1
0
mirror of https://github.com/TwidereProject/Twidere-Android synced 2025-02-16 19:50:53 +01:00
This commit is contained in:
Mariotaku Lee 2017-12-25 14:44:09 +08:00
parent 81ec6352fc
commit f9874588bd
No known key found for this signature in database
GPG Key ID: 15C10F89D7C33535
27 changed files with 326 additions and 280 deletions

View File

@ -42,6 +42,7 @@ import android.support.v7.widget.FixedLinearLayoutManager
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.support.v7.widget.RecyclerView.ViewHolder
import android.support.v7.widget.TooltipCompat
import android.support.v7.widget.helper.ItemTouchHelper
import android.text.Editable
import android.text.Spannable
@ -51,7 +52,6 @@ import android.text.method.LinkMovementMethod
import android.text.style.*
import android.view.*
import android.view.View.OnClickListener
import android.view.View.OnLongClickListener
import android.view.animation.AccelerateDecelerateInterpolator
import android.widget.ImageView
import android.widget.Toast
@ -114,7 +114,7 @@ import kotlin.collections.ArrayList
import android.Manifest.permission as AndroidPermission
@SuppressLint("RestrictedApi")
class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener, OnLongClickListener,
class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener,
ActionMode.Callback, PermissionRequestCancelCallback, EditAltTextDialogFragment.EditAltTextCallback {
// Utility classes
@ -200,7 +200,7 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
get() {
val status = inReplyToStatus
if (!isQuote || status == null) return false
return !status.can_retweet && status.account_key != status.user_key
return !status.canRetweet && status.account_key != status.user_key
}
override fun onCreate(savedInstanceState: Bundle?) {
@ -317,8 +317,7 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
ThemeUtils.wrapMenuIcon(menuBar)
updateStatus.setOnClickListener(this)
updateStatus.setOnLongClickListener(this)
TooltipCompat.setTooltipText(updateStatus, updateStatus.contentDescription)
val composeExtensionsIntent = Intent(INTENT_ACTION_EXTENSION_COMPOSE)
val imageExtensionsIntent = Intent(INTENT_ACTION_EXTENSION_EDIT_IMAGE)
@ -527,16 +526,6 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
contentView.paddingRight, contentView.paddingBottom)
}
override fun onLongClick(v: View): Boolean {
when (v) {
updateStatus -> {
Utils.showMenuItemToast(v, getString(R.string.action_send), true)
return true
}
}
return false
}
override fun onMenuItemClick(item: MenuItem): Boolean {
when (item.itemId) {
R.id.take_photo -> {
@ -1356,7 +1345,6 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
menu.setItemAvailability(R.id.location_submenu, hasLocationOption)
ThemeUtils.wrapMenuIcon(menuBar, excludeGroups = *intArrayOf(MENU_GROUP_IMAGE_EXTENSION))
ThemeUtils.resetCheatSheet(menuBar)
}
private fun setProgressVisible(visible: Boolean) {

View File

@ -54,11 +54,13 @@ import android.support.v7.app.AlertDialog
import android.support.v7.app.AppCompatDelegate
import android.support.v7.graphics.drawable.DrawerArrowDrawable
import android.support.v7.widget.TintTypedArray
import android.support.v7.widget.TooltipCompat
import android.util.SparseIntArray
import android.view.*
import android.view.View.OnClickListener
import android.view.View.OnLongClickListener
import android.view.ViewGroup.MarginLayoutParams
import android.widget.Toast
import com.getkeepsafe.taptargetview.TapTarget
import com.getkeepsafe.taptargetview.TapTargetView
import com.squareup.otto.Subscribe
@ -73,6 +75,7 @@ import org.mariotaku.kpreferences.set
import org.mariotaku.ktextension.coerceInOr
import org.mariotaku.ktextension.contains
import org.mariotaku.sqliteqb.library.Expression
import org.mariotaku.twidere.BuildConfig
import org.mariotaku.twidere.Constants.*
import org.mariotaku.twidere.R
import org.mariotaku.twidere.activity.iface.IControlBarActivity.ControlBarShowHideHelper
@ -87,10 +90,7 @@ import org.mariotaku.twidere.fragment.iface.IFloatingActionButtonFragment
import org.mariotaku.twidere.fragment.iface.RefreshScrollTopInterface
import org.mariotaku.twidere.fragment.iface.SupportFragmentCallback
import org.mariotaku.twidere.graphic.EmptyDrawable
import org.mariotaku.twidere.model.AccountDetails
import org.mariotaku.twidere.model.SupportTabSpec
import org.mariotaku.twidere.model.Tab
import org.mariotaku.twidere.model.UserKey
import org.mariotaku.twidere.model.*
import org.mariotaku.twidere.model.event.UnreadCountUpdatedEvent
import org.mariotaku.twidere.model.notification.NotificationChannelSpec
import org.mariotaku.twidere.promise.RefreshPromises
@ -123,7 +123,7 @@ class HomeActivity : BaseActivity(), OnClickListener, OnPageChangeListener, Supp
private var updateUnreadCountTask: UpdateUnreadCountTask? = null
private val readStateChangeListener = OnSharedPreferenceChangeListener { _, _ -> updateUnreadCount() }
private val controlBarShowHideHelper = ControlBarShowHideHelper(this)
private val useTabNavigation get() = pagerAdapter.getCount() > 1
private val useTabNavigation get() = pagerAdapter.count > 1
override val controlBarHeight: Int
get() {
@ -163,7 +163,7 @@ class HomeActivity : BaseActivity(), OnClickListener, OnPageChangeListener, Supp
override val currentVisibleFragment: Fragment?
get() {
val currentItem = mainPager.currentItem
if (currentItem < 0 || currentItem >= pagerAdapter.getCount()) return null
if (currentItem < 0 || currentItem >= pagerAdapter.count) return null
return pagerAdapter.instantiateItem(mainPager, currentItem)
}
@ -408,7 +408,7 @@ class HomeActivity : BaseActivity(), OnClickListener, OnPageChangeListener, Supp
override fun onLongClick(v: View): Boolean {
when (v) {
actionsButton -> {
Utils.showMenuItemToast(v, v.contentDescription, true)
Toast.makeText(v.context, v.contentDescription, Toast.LENGTH_SHORT).show()
return true
}
}
@ -467,7 +467,7 @@ class HomeActivity : BaseActivity(), OnClickListener, OnPageChangeListener, Supp
override fun onNewIntent(intent: Intent) {
val tabPosition = handleIntent(intent, false)
if (tabPosition >= 0) {
mainPager.currentItem = tabPosition.coerceInOr(0 until pagerAdapter.getCount(), 0)
mainPager.currentItem = tabPosition.coerceInOr(0 until pagerAdapter.count, 0)
}
}
@ -530,7 +530,7 @@ class HomeActivity : BaseActivity(), OnClickListener, OnPageChangeListener, Supp
if (previous < 0 && DrawerLayoutAccessor.findDrawerWithGravity(homeMenu, Gravity.START) != null) {
homeMenu.openDrawer(GravityCompat.START)
setControlBarVisibleAnimate(true)
} else if (previous < pagerAdapter.getCount()) {
} else if (previous < pagerAdapter.count) {
if (homeMenu.isDrawerOpen(GravityCompat.END)) {
homeMenu.closeDrawers()
} else {
@ -541,7 +541,7 @@ class HomeActivity : BaseActivity(), OnClickListener, OnPageChangeListener, Supp
}
KeyboardShortcutConstants.ACTION_NAVIGATION_NEXT_TAB -> {
val next = mainPager.currentItem + 1
if (next >= pagerAdapter.getCount() && DrawerLayoutAccessor.findDrawerWithGravity(homeMenu, Gravity.END) != null) {
if (next >= pagerAdapter.count && DrawerLayoutAccessor.findDrawerWithGravity(homeMenu, Gravity.END) != null) {
homeMenu.openDrawer(GravityCompat.END)
setControlBarVisibleAnimate(true)
} else if (next >= 0) {
@ -717,7 +717,7 @@ class HomeActivity : BaseActivity(), OnClickListener, OnPageChangeListener, Supp
if (tabType != null) {
val accountKey = uri?.getQueryParameter(QUERY_PARAM_ACCOUNT_KEY)?.let(UserKey::valueOf)
val adapter = pagerAdapter
for (i in 0 until adapter.getCount()) {
for (i in 0 until adapter.count) {
val tab = adapter.get(i)
if (tabType == Tab.getTypeAlias(tab.type)) {
val args = tab.args
@ -802,10 +802,10 @@ class HomeActivity : BaseActivity(), OnClickListener, OnPageChangeListener, Supp
private fun setTabPosition(initialTab: Int) {
val rememberPosition = preferences.getBoolean(SharedPreferenceConstants.KEY_REMEMBER_POSITION, true)
if (initialTab >= 0) {
mainPager.currentItem = initialTab.coerceInOr(0 until pagerAdapter.getCount(), 0)
mainPager.currentItem = initialTab.coerceInOr(0 until pagerAdapter.count, 0)
} else if (rememberPosition) {
val position = preferences.getInt(SharedPreferenceConstants.KEY_SAVED_TAB_POSITION, 0)
mainPager.currentItem = position.coerceInOr(0 until pagerAdapter.getCount(), 0)
mainPager.currentItem = position.coerceInOr(0 until pagerAdapter.count, 0)
}
}
@ -836,7 +836,7 @@ class HomeActivity : BaseActivity(), OnClickListener, OnPageChangeListener, Supp
private fun setupHomeTabs() {
pagerAdapter.clear()
pagerAdapter.addAll(CustomTabUtils.getHomeTabs(this))
val hasNoTab = pagerAdapter.getCount() == 0
val hasNoTab = pagerAdapter.count == 0
emptyTabHint.visibility = if (hasNoTab) View.VISIBLE else View.GONE
mainPager.visibility = if (hasNoTab) View.GONE else View.VISIBLE
val useTabNavigation = useTabNavigation
@ -909,11 +909,11 @@ class HomeActivity : BaseActivity(), OnClickListener, OnPageChangeListener, Supp
private fun triggerActionsClick() {
val position = mainPager.currentItem
if (pagerAdapter.getCount() == 0) return
if (pagerAdapter.count == 0) return
val fragment = pagerAdapter.instantiateItem(mainPager, position) as? IFloatingActionButtonFragment
val handled = fragment?.onActionClick("home") ?: false
if (!handled) {
startActivity(Intent(INTENT_ACTION_COMPOSE))
startActivity(Intent(INTENT_ACTION_COMPOSE).setPackage(BuildConfig.APPLICATION_ID))
}
}
@ -928,14 +928,12 @@ class HomeActivity : BaseActivity(), OnClickListener, OnPageChangeListener, Supp
}
return@run f
}
val info = fragment?.getActionInfo("home") ?: run {
actionsButton.setImageResource(R.drawable.ic_action_status_compose)
actionsButton.contentDescription = getString(R.string.action_compose)
return
}
val info = fragment?.getActionInfo("home") ?: FloatingActionButtonInfo(R.drawable.ic_action_status_compose,
getString(R.string.action_compose))
actionsButton.setImageResource(info.icon)
actionsButton.contentDescription = info.title
TooltipCompat.setTooltipText(actionsButton, info.title)
}

View File

@ -27,12 +27,12 @@ import android.support.v4.app.FragmentManager
import android.support.v4.view.PagerAdapter
import android.view.View
import android.view.ViewGroup
import org.mariotaku.twidere.extension.announceForAccessibilityCompat
import org.mariotaku.twidere.fragment.iface.RefreshScrollTopInterface
import org.mariotaku.twidere.fragment.iface.SupportFragmentCallback
import org.mariotaku.twidere.model.SupportTabSpec
import org.mariotaku.twidere.model.tab.DrawableHolder
import org.mariotaku.twidere.util.CustomTabUtils.getTabIconDrawable
import org.mariotaku.twidere.util.Utils.announceForAccessibilityCompat
import org.mariotaku.twidere.view.iface.PagerIndicator
import org.mariotaku.twidere.view.iface.PagerIndicator.TabListener
import org.mariotaku.twidere.view.iface.PagerIndicator.TabProvider
@ -77,7 +77,7 @@ class SupportTabsAdapter(
}
override fun getPageWidth(position: Int): Float {
val columnCount = getCount()
val columnCount = count
if (columnCount == 0) return 1f
if (hasMultipleColumns && preferredColumnWidth > 0) {
val resources = context.resources
@ -114,10 +114,10 @@ class SupportTabsAdapter(
}
override fun onTabClick(position: Int) {
if (position < 0 || position >= getCount()) return
if (position < 0 || position >= count) return
val text = getPageTitle(position) ?: return
val view = indicator as? View ?: return
announceForAccessibilityCompat(context, view, text, javaClass)
view.announceForAccessibilityCompat(text)
}
override fun onTabLongClick(position: Int): Boolean {

View File

@ -33,6 +33,8 @@ object TableIds {
const val SEARCH_TIMELINE = 26
const val MEDIA_SEARCH_TIMELINE = 27
val RANGE_CUSTOM_TIMELINE = FAVORITES..MEDIA_SEARCH_TIMELINE
const val ACTIVITIES_ABOUT_ME = 51
const val ACTIVITIES_BY_FRIENDS = 52
const val MESSAGES = 71

View File

@ -20,13 +20,17 @@
package org.mariotaku.twidere.extension
import android.annotation.TargetApi
import android.content.Context
import android.graphics.Rect
import android.graphics.RectF
import android.os.Build
import android.support.annotation.UiThread
import android.support.v4.view.accessibility.AccessibilityEventCompat
import android.text.TextUtils
import android.view.View
import android.view.ViewGroup
import android.view.accessibility.AccessibilityEvent
import android.view.accessibility.AccessibilityManager
import android.widget.TextView
import org.mariotaku.ktextension.empty
@ -114,6 +118,35 @@ fun ViewGroup.showContextMenuForChildCompat(originalView: View, x: Float, y: Flo
return ViewExtensionsN.showContextMenuForChild(this, originalView, x, y)
}
fun View.announceForAccessibilityCompat(text: CharSequence) {
val accessibilityManager = context.getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager
if (!accessibilityManager.isEnabled) return
// Prior to SDK 16, announcements could only be made through FOCUSED
// events. Jelly Bean (SDK 16) added support for speaking text verbatim
// using the ANNOUNCEMENT event type.
val eventType: Int
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
eventType = AccessibilityEvent.TYPE_VIEW_FOCUSED
} else {
eventType = AccessibilityEventCompat.TYPE_ANNOUNCEMENT
}
// Construct an accessibility event with the minimum recommended
// attributes. An event without a class name or package may be dropped.
val event = AccessibilityEvent.obtain(eventType)
event.text.add(text)
event.className = javaClass.name
event.packageName = context.packageName
event.setSource(this)
// Sends the event directly through the accessibility manager. If your
// application only targets SDK 14+, you should just call
// getParent().requestSendAccessibilityEvent(this, event);
accessibilityManager.sendAccessibilityEvent(event)
}
fun View.findViewByText(text: CharSequence?): TextView? {
if (this is TextView && TextUtils.equals(text, this.text))
return this

View File

@ -10,7 +10,6 @@ import org.mariotaku.twidere.model.*
import org.mariotaku.twidere.util.HtmlEscapeHelper
import org.mariotaku.twidere.util.UriUtils
import org.mariotaku.twidere.util.UserColorNameManager
import org.mariotaku.twidere.util.Utils
import org.mariotaku.twidere.view.ShortTimeView
@ -74,10 +73,13 @@ inline val ParcelableStatus.quoted_user_acct: String?
"$quoted_user_screen_name@${quoted_user_key?.host}"
}
inline val ParcelableStatus.is_my_retweet: Boolean
get() = Utils.isMyRetweet(account_key, retweeted_by_user_key, my_retweet_id)
inline val ParcelableStatus.isAccountRetweet: Boolean
get() = account_key == retweeted_by_user_key || my_retweet_id != null
inline val ParcelableStatus.can_retweet: Boolean
inline val ParcelableStatus.isAccountStatus: Boolean
get() = account_key.maybeEquals(user_key)
inline val ParcelableStatus.canRetweet: Boolean
get() {
if (user_key.host == USER_TYPE_FANFOU_COM) return true
val visibility = extras?.visibility ?: return !user_is_protected
@ -203,7 +205,7 @@ fun ParcelableStatus.updateExtraInformation(details: AccountDetails) {
}
fun ParcelableStatus.contentDescription(context: Context, manager: UserColorNameManager,
nameFirst: Boolean, displayInReplyTo: Boolean, showAbsoluteTime: Boolean): String {
displayInReplyTo: Boolean, showAbsoluteTime: Boolean): String {
val displayName = manager.getDisplayName(this)
val displayTime = if (is_retweet) retweet_timestamp else timestamp
val timeLabel = ShortTimeView.getTimeLabel(context, displayTime, showAbsoluteTime)

View File

@ -42,7 +42,7 @@ import org.mariotaku.twidere.constant.IntentConstants.EXTRA_EXTRAS
import org.mariotaku.twidere.constant.localTrendsWoeIdKey
import org.mariotaku.twidere.extension.get
import org.mariotaku.twidere.fragment.iface.IFloatingActionButtonFragment
import org.mariotaku.twidere.fragment.iface.IFloatingActionButtonFragment.ActionInfo
import org.mariotaku.twidere.model.FloatingActionButtonInfo
import org.mariotaku.twidere.model.UserKey
import org.mariotaku.twidere.model.event.TrendsRefreshedEvent
import org.mariotaku.twidere.model.tab.extra.TrendsTabExtras
@ -142,10 +142,10 @@ class TrendsSuggestionsFragment : AbsContentListViewFragment<TrendsAdapter>(), L
refreshing = false
}
override fun getActionInfo(tag: String): ActionInfo? {
override fun getActionInfo(tag: String): FloatingActionButtonInfo? {
when (tag) {
"home" -> {
return ActionInfo(R.drawable.ic_action_search, getString(R.string.action_search))
return FloatingActionButtonInfo(R.drawable.ic_action_search, getString(R.string.action_search))
}
}
return null

View File

@ -1185,10 +1185,10 @@ class UserFragment : BaseFragment(), OnClickListener, OnLinkClickListener,
val locale = Locale.getDefault()
listedCount.primaryText = Utils.getLocalizedNumber(locale, user.listed_count)
groupsCount.primaryText = Utils.getLocalizedNumber(locale, user.groupsCount)
followersCount.primaryText = Utils.getLocalizedNumber(locale, user.followers_count)
friendsCount.primaryText = Utils.getLocalizedNumber(locale, user.friends_count)
listedCount.primaryText = user.listed_count.toString(locale)
groupsCount.primaryText = user.groupsCount.toString(locale)
followersCount.primaryText = user.followers_count.toString(locale)
friendsCount.primaryText = user.friends_count.toString(locale)
listedCount.updateText()
groupsCount.updateText()

View File

@ -19,14 +19,12 @@
package org.mariotaku.twidere.fragment.iface
/**
* Created by mariotaku on 2017/2/15.
*/
import org.mariotaku.twidere.model.FloatingActionButtonInfo
interface IFloatingActionButtonFragment {
fun getActionInfo(tag: String): ActionInfo?
fun onActionClick(tag: String): Boolean
fun getActionInfo(tag: String): FloatingActionButtonInfo? = null
fun onActionClick(tag: String): Boolean = false
data class ActionInfo(val icon: Int, val title: String)
}

View File

@ -59,7 +59,7 @@ import org.mariotaku.twidere.extension.model.user
import org.mariotaku.twidere.extension.promise
import org.mariotaku.twidere.fragment.AbsContentListRecyclerViewFragment
import org.mariotaku.twidere.fragment.iface.IFloatingActionButtonFragment
import org.mariotaku.twidere.fragment.iface.IFloatingActionButtonFragment.ActionInfo
import org.mariotaku.twidere.model.FloatingActionButtonInfo
import org.mariotaku.twidere.model.ParcelableMessageConversation
import org.mariotaku.twidere.model.UserKey
import org.mariotaku.twidere.model.event.GetMessagesTaskEvent
@ -166,8 +166,8 @@ class MessagesEntriesFragment : AbsContentListRecyclerViewFragment<MessagesEntri
IntentUtils.openUserProfile(context!!, user, preferences[newDocumentApiKey])
}
override fun getActionInfo(tag: String): ActionInfo? {
return ActionInfo(R.drawable.ic_action_add, getString(R.string.new_direct_message))
override fun getActionInfo(tag: String): FloatingActionButtonInfo? {
return FloatingActionButtonInfo(R.drawable.ic_action_add, getString(R.string.new_direct_message))
}
override fun onActionClick(tag: String): Boolean {

View File

@ -39,8 +39,8 @@ import org.mariotaku.twidere.annotation.AccountType
import org.mariotaku.twidere.constant.IntentConstants.*
import org.mariotaku.twidere.constant.quickSendKey
import org.mariotaku.twidere.extension.*
import org.mariotaku.twidere.extension.model.can_retweet
import org.mariotaku.twidere.extension.model.is_my_retweet
import org.mariotaku.twidere.extension.model.canRetweet
import org.mariotaku.twidere.extension.model.isAccountRetweet
import org.mariotaku.twidere.fragment.BaseDialogFragment
import org.mariotaku.twidere.model.*
import org.mariotaku.twidere.model.draft.QuoteStatusActionExtras
@ -115,7 +115,7 @@ class RetweetQuoteDialogFragment : AbsStatusDialogFragment() {
}
getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener {
if (!shouldQuoteRetweet(account) && status.is_my_retweet) {
if (!shouldQuoteRetweet(account) && status.isAccountRetweet) {
StatusPromises.getInstance(context).cancelRetweet(account.key, status.id, status.my_retweet_id)
dismiss()
} else if (retweetOrQuote(account, status, showProtectedConfirm)) {
@ -166,19 +166,19 @@ class RetweetQuoteDialogFragment : AbsStatusDialogFragment() {
if (!editComment.empty) {
positiveButton.setText(R.string.action_comment)
positiveButton.isEnabled = true
} else if (status.is_my_retweet) {
} else if (status.isAccountRetweet) {
positiveButton.setText(R.string.action_cancel_retweet)
positiveButton.isEnabled = true
} else {
positiveButton.setText(R.string.action_retweet)
positiveButton.isEnabled = status.can_retweet
positiveButton.isEnabled = status.canRetweet
}
} else if (status.is_my_retweet) {
} else if (status.isAccountRetweet) {
positiveButton.setText(R.string.action_cancel_retweet)
positiveButton.isEnabled = true
} else {
positiveButton.setText(R.string.action_retweet)
positiveButton.isEnabled = status.can_retweet
positiveButton.isEnabled = status.canRetweet
}
textCountView.remaining = StatusTextValidator.calculateRemaining(arrayOf(account),
null, text.toString())

View File

@ -48,6 +48,7 @@ import kotlinx.android.synthetic.main.fragment_content_recyclerview.*
import org.mariotaku.kpreferences.get
import org.mariotaku.ktextension.*
import org.mariotaku.sqliteqb.library.Expression
import org.mariotaku.twidere.BuildConfig
import org.mariotaku.twidere.R
import org.mariotaku.twidere.activity.AccountSelectorActivity
import org.mariotaku.twidere.activity.ComposeActivity
@ -73,6 +74,7 @@ import org.mariotaku.twidere.extension.view.firstVisibleItemPosition
import org.mariotaku.twidere.extension.view.lastVisibleItemPosition
import org.mariotaku.twidere.fragment.AbsContentRecyclerViewFragment
import org.mariotaku.twidere.fragment.BaseFragment
import org.mariotaku.twidere.fragment.iface.IFloatingActionButtonFragment
import org.mariotaku.twidere.fragment.status.FavoriteConfirmDialogFragment
import org.mariotaku.twidere.fragment.status.RetweetQuoteDialogFragment
import org.mariotaku.twidere.graphic.like.LikeAnimationDrawable
@ -97,7 +99,8 @@ import org.mariotaku.twidere.view.holder.TimelineFilterHeaderViewHolder
import org.mariotaku.twidere.view.holder.iface.IStatusViewHolder
import org.mariotaku.twidere.view.holder.status.StatusViewHolder
abstract class AbsTimelineFragment : AbsContentRecyclerViewFragment<ParcelableStatusesAdapter, LayoutManager>() {
abstract class AbsTimelineFragment : AbsContentRecyclerViewFragment<ParcelableStatusesAdapter, LayoutManager>(),
IFloatingActionButtonFragment {
override val reachingStart: Boolean
get() = recyclerView.layoutManager.firstVisibleItemPosition <= 0
@ -306,6 +309,21 @@ abstract class AbsTimelineFragment : AbsContentRecyclerViewFragment<ParcelableSt
return result
}
override fun onActionClick(tag: String): Boolean {
when (tag) {
"home" -> {
val intent = Intent(INTENT_ACTION_COMPOSE).setPackage(BuildConfig.APPLICATION_ID)
val accountKeys = Utils.getAccountKeys(context!!, arguments)
if (accountKeys != null) {
intent.putExtra(EXTRA_ACCOUNT_KEYS, accountKeys)
}
startActivity(intent)
return true
}
}
return false
}
fun reloadAll() {
val controller = dataController
if (controller != null) {

View File

@ -0,0 +1,22 @@
/*
* 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.model
data class FloatingActionButtonInfo(val icon: Int, val title: String)

View File

@ -37,6 +37,7 @@ import com.squareup.otto.Bus
import okhttp3.Dns
import org.mariotaku.ktextension.isNullOrEmpty
import org.mariotaku.ktextension.mapToArray
import org.mariotaku.ktextension.toLongOr
import org.mariotaku.ktextension.toNulls
import org.mariotaku.sqliteqb.library.*
import org.mariotaku.sqliteqb.library.Columns.Column
@ -142,6 +143,7 @@ class TwidereDataProvider : ContentProvider(), LazyLoadCallback {
override fun query(uri: Uri, projection: Array<String>?, selection: String?, selectionArgs: Array<String>?,
sortOrder: String?): Cursor? {
val limit = uri.getQueryParameter(QUERY_PARAM_LIMIT)
var querySelection = selection
try {
val tableId = DataStoreUtils.getTableId(uri)
val table = DataStoreUtils.getTableNameById(tableId)
@ -223,44 +225,21 @@ class TwidereDataProvider : ContentProvider(), LazyLoadCallback {
if (projection != null || selection != null || sortOrder != null) {
throw IllegalArgumentException()
}
val c = databaseWrapper.rawQuery(uri.lastPathSegment, selectionArgs)
uri.getQueryParameter(QUERY_PARAM_NOTIFY_URI)?.let {
c?.setNotificationUri(context.contentResolver, Uri.parse(it))
}
return c
return rawQuery(uri, selectionArgs)
}
TableIds.MESSAGES_CONVERSATIONS -> if (projection?.contains(Messages.Conversations.UNREAD_COUNT) == true) {
val mappedProjection = projection.mapToArray(TwidereQueryBuilder::mapConversationsProjection)
val qb = SQLQueryBuilder.select(Columns(*mappedProjection))
qb.from(Table(Messages.Conversations.TABLE_NAME))
qb.join(Join(false, Join.Operation.LEFT_OUTER, Table(Messages.TABLE_NAME),
Expression.and(
Expression.equals(
Column(Table(Messages.Conversations.TABLE_NAME), Messages.Conversations.CONVERSATION_ID),
Column(Table(Messages.TABLE_NAME), Messages.CONVERSATION_ID)
),
Expression.equals(
Column(Table(Messages.Conversations.TABLE_NAME), Messages.Conversations.ACCOUNT_KEY),
Column(Table(Messages.TABLE_NAME), Messages.ACCOUNT_KEY))
)
))
if (selection != null) {
qb.where(Expression(selection))
return queryMessagesConversationWithUnreadCount(projection, selection, sortOrder, limit, selectionArgs, uri)
}
in TableIds.RANGE_CUSTOM_TIMELINE -> if (uri.pathSegments.size == 3) {
val tabId = uri.lastPathSegment.toLongOr(-1)
if (tabId > 0) {
val tabIdWhere = Expression.equals(Statuses.TAB_ID, tabId).sql
querySelection = if (querySelection == null) tabIdWhere else "$querySelection AND $tabIdWhere"
}
qb.groupBy(Column(Table(Messages.TABLE_NAME), Messages.CONVERSATION_ID))
if (sortOrder != null) {
qb.orderBy(RawSQLLang(sortOrder))
}
if (limit != null) {
qb.limit(RawSQLLang(limit))
}
val c = databaseWrapper.rawQuery(qb.buildSQL(), selectionArgs)
c?.setNotificationUri(context.contentResolver, uri)
return c
}
}
if (table == null) return null
val c = databaseWrapper.query(table, projection, selection, selectionArgs,
val c = databaseWrapper.query(table, projection, querySelection, selectionArgs,
null, null, sortOrder, limit)
c?.setNotificationUri(context.contentResolver, uri)
return c
@ -308,6 +287,45 @@ class TwidereDataProvider : ContentProvider(), LazyLoadCallback {
return null
}
private fun rawQuery(uri: Uri, selectionArgs: Array<String>?): Cursor {
val c = databaseWrapper.rawQuery(uri.lastPathSegment, selectionArgs)
uri.getQueryParameter(QUERY_PARAM_NOTIFY_URI)?.let {
c?.setNotificationUri(context.contentResolver, Uri.parse(it))
}
return c
}
private fun queryMessagesConversationWithUnreadCount(projection: Array<String>, selection: String?,
sortOrder: String?, limit: String?, selectionArgs: Array<String>?, uri: Uri): Cursor? {
val mappedProjection = projection.mapToArray(TwidereQueryBuilder::mapConversationsProjection)
val qb = SQLQueryBuilder.select(Columns(*mappedProjection))
qb.from(Table(Messages.Conversations.TABLE_NAME))
qb.join(Join(false, Join.Operation.LEFT_OUTER, Table(Messages.TABLE_NAME),
Expression.and(
Expression.equals(
Column(Table(Messages.Conversations.TABLE_NAME), Messages.Conversations.CONVERSATION_ID),
Column(Table(Messages.TABLE_NAME), Messages.CONVERSATION_ID)
),
Expression.equals(
Column(Table(Messages.Conversations.TABLE_NAME), Messages.Conversations.ACCOUNT_KEY),
Column(Table(Messages.TABLE_NAME), Messages.ACCOUNT_KEY))
)
))
if (selection != null) {
qb.where(Expression(selection))
}
qb.groupBy(Column(Table(Messages.TABLE_NAME), Messages.CONVERSATION_ID))
if (sortOrder != null) {
qb.orderBy(RawSQLLang(sortOrder))
}
if (limit != null) {
qb.limit(RawSQLLang(limit))
}
val c = databaseWrapper.rawQuery(qb.buildSQL(), selectionArgs)
c?.setNotificationUri(context.contentResolver, uri)
return c
}
private fun handleSQLException(e: SQLException): Boolean {
try {
if (e is SQLiteFullException) {

View File

@ -18,7 +18,8 @@ import org.mariotaku.library.objectcursor.ObjectCursor
import org.mariotaku.microblog.library.MicroBlogException
import org.mariotaku.microblog.library.TwitterUserStream
import org.mariotaku.microblog.library.annotation.twitter.StreamWith
import org.mariotaku.microblog.library.model.microblog.*
import org.mariotaku.microblog.library.model.microblog.DirectMessage
import org.mariotaku.microblog.library.model.microblog.Status
import org.mariotaku.microblog.library.model.twitter.Activity
import org.mariotaku.microblog.library.model.twitter.TwitterStreamObject
import org.mariotaku.sqliteqb.library.Columns
@ -43,10 +44,10 @@ import org.mariotaku.twidere.model.refresh.ContentRefreshParam
import org.mariotaku.twidere.provider.TwidereDataStore.*
import org.mariotaku.twidere.task.twitter.GetActivitiesAboutMeTask
import org.mariotaku.twidere.task.twitter.message.GetMessagesTask
import org.mariotaku.twidere.util.BatteryManagerCompat
import org.mariotaku.twidere.util.DataStoreUtils
import org.mariotaku.twidere.util.DebugLog
import org.mariotaku.twidere.util.IntentUtils
import org.mariotaku.twidere.util.Utils
import org.mariotaku.twidere.util.streaming.TwitterTimelineStreamCallback
import java.util.*
import java.util.concurrent.ExecutorService
@ -125,7 +126,7 @@ class StreamingService : BaseService() {
return false
}
// Quit if not charging (with preference)
val isCharging = Utils.isCharging(this)
val isCharging = BatteryManagerCompat.isCharging(this)
if (preferences[streamingPowerSavingKey] && !isCharging) {
return false
}

View File

@ -0,0 +1,54 @@
/*
* 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.util
import android.annotation.TargetApi
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.BatteryManager
import android.os.Build
object BatteryManagerCompat {
private val chargingPluggedStates = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
arrayOf(BatteryManager.BATTERY_PLUGGED_AC, BatteryManager.BATTERY_PLUGGED_USB, BatteryManager.BATTERY_PLUGGED_WIRELESS)
} else {
arrayOf(BatteryManager.BATTERY_PLUGGED_AC, BatteryManager.BATTERY_PLUGGED_USB)
}
fun isCharging(context: Context): Boolean {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return BatteryManagerM.isCharging(context)
}
val intent = context.registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED)) ?: return false
val plugged = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1)
return plugged in chargingPluggedStates
}
@TargetApi(Build.VERSION_CODES.M)
private object BatteryManagerM {
fun isCharging(context: Context): Boolean {
val bm = context.getSystemService(Context.BATTERY_SERVICE) as BatteryManager
return bm.isCharging
}
}
}

View File

@ -40,6 +40,7 @@ fun Context.deleteDrafts(draftIds: LongArray): Int {
return contentResolver.delete(Drafts.CONTENT_URI, where, whereArgs)
}
@WorkerThread
fun ContentResolver.deleteAccountData(accountKey: UserKey) {
val where = Expression.equalsArgs(AccountSupportColumns.ACCOUNT_KEY).sql
val whereArgs = arrayOf(accountKey.toString())

View File

@ -32,7 +32,9 @@ import android.provider.BaseColumns
import android.support.annotation.WorkerThread
import android.text.TextUtils
import org.mariotaku.kpreferences.get
import org.mariotaku.ktextension.addTo
import org.mariotaku.ktextension.mapToArray
import org.mariotaku.ktextension.toLongOr
import org.mariotaku.library.objectcursor.ObjectCursor
import org.mariotaku.microblog.library.Mastodon
import org.mariotaku.microblog.library.MicroBlog
@ -129,9 +131,16 @@ object DataStoreUtils {
tableMatcher.addPath("${TwidereDataStore.CONTENT_PATH_RAW_QUERY}/*", TableIds.VIRTUAL_RAW_QUERY)
}
fun getStatusTabId(uri: Uri): Long {
if (getTableId(uri) in TableIds.RANGE_CUSTOM_TIMELINE) {
return uri.lastPathSegment.toLongOr(-1)
}
return -1
}
fun getNewestStatusIds(context: Context, uri: Uri, accountKeys: Array<UserKey?>): Array<String?> {
return getStringFieldArray(context, uri, accountKeys, Statuses.ACCOUNT_KEY, Statuses.ID,
OrderBy(SQLFunctions.MAX(Statuses.TIMESTAMP)), null, null)
OrderBy(SQLFunctions.MAX(Statuses.TIMESTAMP)), getStatusesWhere(uri), null)
}
fun getNewestMessageIds(context: Context, uri: Uri, accountKeys: Array<UserKey?>, outgoing: Boolean): Array<String?> {
@ -166,19 +175,19 @@ object DataStoreUtils {
fun getNewestStatusSortIds(context: Context, uri: Uri, accountKeys: Array<UserKey?>): LongArray {
return getLongFieldArray(context, uri, accountKeys, Statuses.ACCOUNT_KEY, Statuses.SORT_ID,
OrderBy(SQLFunctions.MAX(Statuses.TIMESTAMP)), null, null)
OrderBy(SQLFunctions.MAX(Statuses.TIMESTAMP)), getStatusesWhere(uri), null)
}
fun getOldestStatusIds(context: Context, uri: Uri, accountKeys: Array<UserKey?>): Array<String?> {
return getStringFieldArray(context, uri, accountKeys, Statuses.ACCOUNT_KEY, Statuses.ID,
OrderBy(SQLFunctions.MIN(Statuses.TIMESTAMP)), null, null)
OrderBy(SQLFunctions.MIN(Statuses.TIMESTAMP)), getStatusesWhere(uri), null)
}
fun getOldestStatusSortIds(context: Context, uri: Uri, accountKeys: Array<UserKey?>): LongArray {
return getLongFieldArray(context, uri, accountKeys, Statuses.ACCOUNT_KEY, Statuses.SORT_ID,
OrderBy(SQLFunctions.MIN(Statuses.TIMESTAMP)), null, null)
OrderBy(SQLFunctions.MIN(Statuses.TIMESTAMP)), getStatusesWhere(uri), null)
}
fun getNewestActivityMaxPositions(context: Context, uri: Uri, accountKeys: Array<UserKey?>,
@ -297,9 +306,9 @@ object DataStoreUtils {
val expressionArgs = ArrayList<String>()
expressions.add(Expression.inArgs(Column(Statuses.ACCOUNT_KEY), keys.size))
for (accountKey in keys) {
expressionArgs.add(accountKey.toString())
}
keys.mapTo(expressionArgs) { it.toString() }
getStatusesWhere(uri)?.addTo(expressions)
if (greaterThan) {
expressions.add(Expression.greaterThan(compareColumn, compare))
@ -742,14 +751,6 @@ object DataStoreUtils {
}
}
internal interface FieldArrayCreator<T, I> {
fun newArray(size: Int): T
fun newIndex(cur: Cursor): I
fun assign(array: T, arrayIdx: Int, cur: Cursor, colIdx: I)
}
fun getInteractionsCount(context: Context, preferences: SharedPreferences, extraArgs: Bundle?,
accountKeys: Array<UserKey>, since: Long, sinceColumn: String,
@FilterScope filterScopes: Int): Int {
@ -772,6 +773,14 @@ object DataStoreUtils {
extraWhereArgs, sinceColumn, since, followingOnly, accountKeys, filterScopes)
}
internal interface FieldArrayCreator<T, I> {
fun newArray(size: Int): T
fun newIndex(cur: Cursor): I
fun assign(array: T, arrayIdx: Int, cur: Cursor, colIdx: I)
}
fun addToFilter(context: Context, users: Collection<ParcelableUser>, filterAnywhere: Boolean) {
val cr = context.contentResolver
@ -903,4 +912,12 @@ object DataStoreUtils {
private fun UriMatcher.addPath(path: String, code: Int) {
addURI(TwidereDataStore.AUTHORITY, path, code)
}
private fun getStatusesWhere(uri: Uri): Expression? {
val tabId = getStatusTabId(uri)
if (tabId > 0) {
return Expression.equals(Statuses.TAB_ID, tabId)
}
return null
}
}

View File

@ -55,6 +55,8 @@ import org.mariotaku.twidere.constant.favoriteConfirmationKey
import org.mariotaku.twidere.constant.iWantMyStarsBackKey
import org.mariotaku.twidere.extension.getDetails
import org.mariotaku.twidere.extension.model.isOfficial
import org.mariotaku.twidere.extension.model.isAccountRetweet
import org.mariotaku.twidere.extension.model.isAccountStatus
import org.mariotaku.twidere.extension.promise
import org.mariotaku.twidere.fragment.AddStatusFilterDialogFragment
import org.mariotaku.twidere.fragment.BaseFragment
@ -120,9 +122,9 @@ object MenuUtils {
val isMyRetweet = when {
RetweetStatusTask.isRunning(status.account_key, status.id) -> true
DestroyStatusTask.isRunning(status.account_key, status.id) -> false
else -> status.retweeted || Utils.isMyRetweet(status)
else -> status.retweeted || status.isAccountRetweet
}
val isMyStatus = Utils.isMyStatus(status)
val isMyStatus = status.isAccountRetweet || status.isAccountStatus
menu.setItemAvailability(R.id.delete, isMyStatus)
if (isMyStatus) {
val isPinned = status.is_pinned_status

View File

@ -35,13 +35,15 @@ import android.support.v4.graphics.ColorUtils
import android.support.v4.graphics.drawable.DrawableCompat
import android.support.v4.view.MenuItemCompat
import android.support.v7.app.TwilightManagerAccessor
import android.support.v7.view.menu.ActionMenuItemView
import android.support.v7.widget.ActionMenuView
import android.support.v7.widget.TintTypedArray
import android.support.v7.widget.Toolbar
import android.support.v7.widget.TwidereToolbar
import android.util.TypedValue
import android.view.*
import android.view.Menu
import android.view.MenuItem
import android.view.Window
import android.view.WindowManager
import android.widget.FrameLayout
import org.mariotaku.chameleon.ChameleonUtils
import org.mariotaku.kpreferences.get
@ -287,21 +289,6 @@ object ThemeUtils {
return darkColor
}
@SuppressLint("RestrictedApi")
fun resetCheatSheet(menuView: ActionMenuView) {
val listener = View.OnLongClickListener { v ->
if ((v as ActionMenuItemView).hasText()) return@OnLongClickListener false
val menuItem = v.itemData
Utils.showMenuItemToast(v, menuItem.title, true)
return@OnLongClickListener true
}
(0 until menuView.childCount).forEach { i ->
val child = menuView.getChildAt(i) as? ActionMenuItemView ?: return@forEach
if (child.itemData.hasSubMenu()) return@forEach
child.setOnLongClickListener(listener)
}
}
fun getOptimalAccentColor(accentColor: Int, foregroundColor: Int): Int {
val yiq = IntArray(3)
TwidereColorUtils.colorToYIQ(foregroundColor, yiq)

View File

@ -20,14 +20,12 @@
package org.mariotaku.twidere.util
import android.accounts.AccountManager
import android.annotation.SuppressLint
import android.app.ActionBar
import android.app.Activity
import android.content.*
import android.content.pm.ActivityInfo
import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
import android.graphics.Rect
import android.graphics.drawable.Drawable
import android.net.ConnectivityManager
import android.net.Uri
@ -39,23 +37,15 @@ import android.os.Bundle
import android.support.annotation.DrawableRes
import android.support.annotation.StringRes
import android.support.v4.net.ConnectivityManagerCompat
import android.support.v4.view.GravityCompat
import android.support.v4.view.accessibility.AccessibilityEventCompat
import android.support.v7.app.AppCompatActivity
import android.text.format.DateFormat
import android.text.format.DateUtils
import android.util.Log
import android.util.TypedValue
import android.view.Gravity
import android.view.KeyCharacterMap
import android.view.KeyEvent
import android.view.View
import android.view.accessibility.AccessibilityEvent
import android.view.accessibility.AccessibilityManager
import android.widget.Toast
import org.mariotaku.kpreferences.get
import org.mariotaku.ktextension.getNullableTypedArray
import org.mariotaku.ktextension.toString
import org.mariotaku.pickncrop.library.PNCUtils
import org.mariotaku.sqliteqb.library.AllColumns
import org.mariotaku.sqliteqb.library.Columns
@ -87,7 +77,6 @@ import org.mariotaku.twidere.model.UserKey
import org.mariotaku.twidere.provider.TwidereDataStore.CachedUsers
import org.mariotaku.twidere.util.TwidereLinkify.PATTERN_TWITTER_PROFILE_IMAGES
import java.io.File
import java.util.*
import java.util.regex.Pattern
object Utils {
@ -105,40 +94,11 @@ object Utils {
}
fun announceForAccessibilityCompat(context: Context, view: View, text: CharSequence,
cls: Class<*>) {
val accessibilityManager = context
.getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager
if (!accessibilityManager.isEnabled) return
// Prior to SDK 16, announcements could only be made through FOCUSED
// events. Jelly Bean (SDK 16) added support for speaking text verbatim
// using the ANNOUNCEMENT event type.
val eventType: Int
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
eventType = AccessibilityEvent.TYPE_VIEW_FOCUSED
} else {
eventType = AccessibilityEventCompat.TYPE_ANNOUNCEMENT
}
// Construct an accessibility event with the minimum recommended
// attributes. An event without a class name or package may be dropped.
val event = AccessibilityEvent.obtain(eventType)
event.text.add(text)
event.className = cls.name
event.packageName = context.packageName
event.setSource(view)
// Sends the event directly through the accessibility manager. If your
// application only targets SDK 14+, you should just call
// getParent().requestSendAccessibilityEvent(this, event);
accessibilityManager.sendAccessibilityEvent(event)
}
fun deleteMedia(context: Context, uri: Uri): Boolean {
try {
return PNCUtils.deleteMedia(context, uri)
return try {
PNCUtils.deleteMedia(context, uri)
} catch (e: SecurityException) {
return false
false
}
}
@ -162,25 +122,27 @@ object Utils {
fun getAccountKeys(context: Context, args: Bundle?): Array<UserKey>? {
if (args == null) return null
if (args.containsKey(EXTRA_ACCOUNT_KEYS)) {
return args.getNullableTypedArray(EXTRA_ACCOUNT_KEYS)
} else if (args.containsKey(EXTRA_ACCOUNT_KEY)) {
val accountKey = args.getParcelable<UserKey>(EXTRA_ACCOUNT_KEY) ?: return emptyArray()
return arrayOf(accountKey)
} else if (args.containsKey(EXTRA_ACCOUNT_ID)) {
val accountId = args.get(EXTRA_ACCOUNT_ID).toString()
try {
if (java.lang.Long.parseLong(accountId) <= 0) return null
} catch (e: NumberFormatException) {
// Ignore
when {
args.containsKey(EXTRA_ACCOUNT_KEYS) -> return args.getNullableTypedArray(EXTRA_ACCOUNT_KEYS)
args.containsKey(EXTRA_ACCOUNT_KEY) -> {
val accountKey = args.getParcelable<UserKey>(EXTRA_ACCOUNT_KEY) ?: return emptyArray()
return arrayOf(accountKey)
}
args.containsKey(EXTRA_ACCOUNT_ID) -> {
val accountId = args.get(EXTRA_ACCOUNT_ID)?.toString().orEmpty()
try {
if (java.lang.Long.parseLong(accountId) <= 0) return null
} catch (e: NumberFormatException) {
// Ignore
}
val accountKey = DataStoreUtils.findAccountKey(context, accountId)
args.putParcelable(EXTRA_ACCOUNT_KEY, accountKey)
if (accountKey == null) return arrayOf(UserKey(accountId, null))
return arrayOf(accountKey)
val accountKey = DataStoreUtils.findAccountKey(context, accountId)
args.putParcelable(EXTRA_ACCOUNT_KEY, accountKey)
if (accountKey == null) return arrayOf(UserKey(accountId, null))
return arrayOf(accountKey)
}
else -> return null
}
return null
}
fun getAccountKey(context: Context, args: Bundle?): UserKey? {
@ -281,10 +243,6 @@ object Utils {
return null
}
fun getLocalizedNumber(locale: Locale, number: Number): String {
return number.toString(locale)
}
fun getMatchedNicknameKeys(str: String, manager: UserColorNameManager): Array<String> {
if (str.isEmpty()) return emptyArray()
return manager.nicknames.filter { (_, value) ->
@ -346,18 +304,20 @@ object Utils {
@DrawableRes
fun getUserTypeIconRes(isVerified: Boolean, isProtected: Boolean): Int {
if (isVerified)
return R.drawable.ic_user_type_verified
else if (isProtected) return R.drawable.ic_user_type_protected
return 0
return when {
isVerified -> R.drawable.ic_user_type_verified
isProtected -> R.drawable.ic_user_type_protected
else -> 0
}
}
@StringRes
fun getUserTypeDescriptionRes(isVerified: Boolean, isProtected: Boolean): Int {
if (isVerified)
return R.string.user_type_verified
else if (isProtected) return R.string.user_type_protected
return 0
return when {
isVerified -> R.string.user_type_verified
isProtected -> R.string.user_type_protected
else -> 0
}
}
fun isBatteryOkay(context: Context?): Boolean {
@ -388,15 +348,6 @@ object Utils {
return am.findAccount(screenName) != null
}
fun isMyRetweet(status: ParcelableStatus?): Boolean {
return status != null && isMyRetweet(status.account_key, status.retweeted_by_user_key,
status.my_retweet_id)
}
fun isMyRetweet(accountKey: UserKey, retweetedByKey: UserKey?, myRetweetId: String?): Boolean {
return accountKey == retweetedByKey || myRetweetId != null
}
fun matchTabCode(uri: Uri): Int {
return HOME_TABS_URI_MATCHER.match(uri)
}
@ -451,40 +402,6 @@ object Utils {
return top - actionBarHeight
}
fun restartActivity(activity: Activity?) {
if (activity == null) return
val enterAnim = android.R.anim.fade_in
val exitAnim = android.R.anim.fade_out
activity.finish()
activity.overridePendingTransition(enterAnim, exitAnim)
activity.startActivity(activity.intent)
activity.overridePendingTransition(enterAnim, exitAnim)
}
internal fun isMyStatus(status: ParcelableStatus): Boolean {
if (isMyRetweet(status)) return true
return status.account_key.maybeEquals(status.user_key)
}
fun showMenuItemToast(v: View, text: CharSequence, isBottomBar: Boolean) {
val screenPos = IntArray(2)
val displayFrame = Rect()
v.getLocationOnScreen(screenPos)
v.getWindowVisibleDisplayFrame(displayFrame)
val width = v.width
val height = v.height
val screenWidth = v.resources.displayMetrics.widthPixels
val cheatSheet = Toast.makeText(v.context.applicationContext, text, Toast.LENGTH_SHORT)
if (isBottomBar) {
// Show along the bottom center
cheatSheet.setGravity(Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL, 0, height)
} else {
// Show along the top; follow action buttons
cheatSheet.setGravity(Gravity.TOP or GravityCompat.END, screenWidth - screenPos[0] - width / 2, height)
}
cheatSheet.show()
}
internal fun getMetadataDrawable(pm: PackageManager?, info: ActivityInfo?, key: String?): Drawable? {
if (pm == null || info == null || info.metaData == null || key == null || !info.metaData.containsKey(key))
return null
@ -492,8 +409,7 @@ object Utils {
}
internal fun isExtensionUseJSON(info: ResolveInfo?): Boolean {
if (info == null || info.activityInfo == null) return false
val activityInfo = info.activityInfo
val activityInfo = info?.activityInfo ?: return false
if (activityInfo.metaData != null && activityInfo.metaData.containsKey(METADATA_KEY_EXTENSION_USE_JSON))
return activityInfo.metaData.getBoolean(METADATA_KEY_EXTENSION_USE_JSON)
val appInfo = activityInfo.applicationInfo ?: return false
@ -530,16 +446,6 @@ object Utils {
return result
}
@SuppressLint("InlinedApi")
fun isCharging(context: Context?): Boolean {
if (context == null) return false
val intent = context.registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED)) ?: return false
val plugged = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1)
return plugged == BatteryManager.BATTERY_PLUGGED_AC
|| plugged == BatteryManager.BATTERY_PLUGGED_USB
|| plugged == BatteryManager.BATTERY_PLUGGED_WIRELESS
}
fun isMediaPreviewEnabled(context: Context, preferences: SharedPreferences): Boolean {
if (!preferences[mediaPreviewKey])
return false

View File

@ -24,7 +24,7 @@ import android.graphics.Color
import android.support.annotation.ColorInt
import android.support.v7.widget.AppCompatTextView
import android.util.AttributeSet
import org.mariotaku.twidere.util.Utils.getLocalizedNumber
import org.mariotaku.ktextension.toString
import java.util.*
class StatusTextCountView(context: Context, attrs: AttributeSet? = null) : AppCompatTextView(context,
@ -47,7 +47,7 @@ class StatusTextCountView(context: Context, attrs: AttributeSet? = null) : AppCo
text = null
return
}
text = getLocalizedNumber(Locale.getDefault(), remaining)
text = remaining.toString(Locale.getDefault())
val exceededLimit = remaining < 0
val nearLimit = remaining <= warnLimit
val hue = (if (exceededLimit) if (nearLimit) 5 * remaining else 50 else 0).toFloat()

View File

@ -25,11 +25,11 @@ import android.widget.TextView
import kotlinx.android.synthetic.main.list_item_user_list.view.*
import org.mariotaku.ktextension.hideIfEmpty
import org.mariotaku.ktextension.spannable
import org.mariotaku.ktextension.toString
import org.mariotaku.twidere.R
import org.mariotaku.twidere.adapter.iface.IUserListsAdapter
import org.mariotaku.twidere.extension.loadProfileImage
import org.mariotaku.twidere.model.ParcelableUserList
import org.mariotaku.twidere.util.Utils
import org.mariotaku.twidere.view.ColorLabelRelativeLayout
import org.mariotaku.twidere.view.ProfileImageView
import java.util.*
@ -75,8 +75,8 @@ class UserListViewHolder(
}
descriptionView.spannable = userList.description
descriptionView.hideIfEmpty()
membersCountView.text = Utils.getLocalizedNumber(Locale.getDefault(), userList.members_count)
subscribersCountView.text = Utils.getLocalizedNumber(Locale.getDefault(), userList.subscribers_count)
membersCountView.text = userList.members_count.toString(Locale.getDefault())
subscribersCountView.text = userList.subscribers_count.toString(Locale.getDefault())
}
fun setOnClickListeners() {

View File

@ -27,6 +27,7 @@ import android.widget.RelativeLayout
import kotlinx.android.synthetic.main.list_item_user.view.*
import org.mariotaku.ktextension.hideIfEmpty
import org.mariotaku.ktextension.spannable
import org.mariotaku.ktextension.toString
import org.mariotaku.twidere.R
import org.mariotaku.twidere.adapter.iface.IUsersAdapter
import org.mariotaku.twidere.adapter.iface.IUsersAdapter.*
@ -34,7 +35,6 @@ import org.mariotaku.twidere.extension.loadProfileImage
import org.mariotaku.twidere.extension.model.hasSameHost
import org.mariotaku.twidere.model.ParcelableUser
import org.mariotaku.twidere.promise.FriendshipPromises
import org.mariotaku.twidere.util.Utils
import org.mariotaku.twidere.util.Utils.getUserTypeIconRes
import java.util.*
@ -174,9 +174,9 @@ class UserViewHolder(
urlView.spannable = user.url_expanded
urlView.hideIfEmpty()
val locale = Locale.getDefault()
statusesCountView.text = Utils.getLocalizedNumber(locale, user.statuses_count)
followersCountView.text = Utils.getLocalizedNumber(locale, user.followers_count)
friendsCountView.text = Utils.getLocalizedNumber(locale, user.friends_count)
statusesCountView.text = user.statuses_count.toString(locale)
followersCountView.text = user.followers_count.toString(locale)
friendsCountView.text = user.friends_count.toString(locale)
}
}

View File

@ -722,7 +722,7 @@ class DetailStatusViewHolder(
throw UnsupportedOperationException("Unsupported type " + count.type)
}
}
labelView.primaryText = Utils.getLocalizedNumber(Locale.getDefault(), count.count)
labelView.primaryText = count.count.toString(Locale.getDefault())
labelView.secondaryText = label
labelView.updateText(adapter.bidiFormatter)

View File

@ -160,7 +160,7 @@ class LargeMediaStatusViewHolder(private val adapter: IStatusesAdapter, itemView
mediaPreviewAdapter.media = status.media
itemView.contentDescription = status.contentDescription(context, adapter.userColorNameManager,
adapter.nameFirst, false, timeView.showAbsoluteTime)
false, timeView.showAbsoluteTime)
}
override fun onMediaClick(view: View, current: ParcelableMedia, accountKey: UserKey?, id: Long) {

View File

@ -62,7 +62,6 @@ import org.mariotaku.twidere.util.HtmlEscapeHelper.toPlainText
import org.mariotaku.twidere.util.HtmlSpanBuilder
import org.mariotaku.twidere.util.ThemeUtils
import org.mariotaku.twidere.util.UnitConvertUtils
import org.mariotaku.twidere.util.Utils
import org.mariotaku.twidere.util.Utils.getUserTypeIconRes
import org.mariotaku.twidere.view.ShapedImageView
import org.mariotaku.twidere.view.holder.iface.IStatusViewHolder
@ -447,7 +446,7 @@ class StatusViewHolder(private val adapter: IStatusesAdapter, itemView: View) :
itemView.contentDescription = status.contentDescription(context, colorNameManager,
nameFirst, displayInReplyTo, timeView.showAbsoluteTime)
displayInReplyTo, timeView.showAbsoluteTime)
profileImageView.contentDescription = context.getString(R.string.content_description_open_user_name_profile,
colorNameManager.getDisplayName(status))
@ -749,8 +748,8 @@ class StatusViewHolder(private val adapter: IStatusesAdapter, itemView: View) :
fun isRetweetIconActivated(status: ParcelableStatus): Boolean {
return !DestroyStatusTask.isRunning(status.account_key, status.my_retweet_id) &&
(RetweetStatusTask.isRunning(status.account_key, status.id) ||
status.retweeted || Utils.isMyRetweet(status.account_key,
status.retweeted_by_user_key, status.my_retweet_id))
status.retweeted || status.account_key == status.retweeted_by_user_key ||
status.my_retweet_id != null)
}
fun isFavoriteIconActivated(status: ParcelableStatus): Boolean {