added import filters from blocks/mutes

This commit is contained in:
Mariotaku Lee 2016-12-28 11:17:11 +08:00
parent 6401776c77
commit edc1ccce56
24 changed files with 377 additions and 66 deletions

View File

@ -72,7 +72,7 @@ public class FiltersData {
@JsonObject
@CursorObject(valuesCreator = true, tableInfo = true)
public static class UserItem {
@CursorField(value = Filters.Users._ID, type = TwidereDataStore.TYPE_PRIMARY_KEY)
@CursorField(value = Filters.Users._ID, type = TwidereDataStore.TYPE_PRIMARY_KEY, excludeWrite = true)
long _id;
@CursorField(value = Filters.Users.USER_KEY, converter = UserKeyCursorFieldConverter.class, type = "TEXT NOT NULL UNIQUE")
@JsonField(name = "user_key", typeConverter = UserKeyConverter.class)
@ -88,7 +88,7 @@ public class FiltersData {
*/
@CursorField(value = Filters.Users.SOURCE, type = "INTEGER DEFAULT -1")
@JsonField(name = "source")
long source;
long source = -1;
public UserKey getUserKey() {
return userKey;
@ -135,7 +135,7 @@ public class FiltersData {
@JsonObject
@CursorObject(valuesCreator = true, tableInfo = true)
public static class BaseItem {
@CursorField(value = Filters._ID, type = TwidereDataStore.TYPE_PRIMARY_KEY)
@CursorField(value = Filters._ID, type = TwidereDataStore.TYPE_PRIMARY_KEY, excludeWrite = true)
long _id;
@CursorField(value = Filters.VALUE, type = "TEXT NOT NULL UNIQUE")
@JsonField(name = "value")
@ -145,7 +145,7 @@ public class FiltersData {
*/
@CursorField(value = Filters.Users.SOURCE, type = "INTEGER DEFAULT -1")
@JsonField(name = "source")
long source;
long source = -1;
public String getValue() {
return value;

View File

@ -194,6 +194,9 @@ public class ParcelableUser implements Parcelable, Comparable<ParcelableUser> {
@ParcelableThisPlease
public String nickname;
@ParcelableThisPlease
public boolean is_filtered;
public static final Creator<ParcelableUser> CREATOR = new Creator<ParcelableUser>() {
@Override
public ParcelableUser createFromParcel(Parcel source) {

View File

@ -93,9 +93,11 @@ import org.mariotaku.twidere.provider.TwidereDataStore.Statuses;
import org.mariotaku.twidere.provider.TwidereDataStore.Suggestions;
import org.mariotaku.twidere.provider.TwidereDataStore.Tabs;
import org.mariotaku.twidere.provider.TwidereDataStore.UnreadCounts;
import org.mariotaku.twidere.util.content.ContentResolverUtils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -1021,20 +1023,23 @@ public class DataStoreUtils implements Constants {
since, sinceColumn, followingOnly, accountIds);
}
public static void addToFilter(Context context, ParcelableUser user, boolean filterAnywhere) {
public static void addToFilter(Context context, Collection<ParcelableUser> users, boolean filterAnywhere) {
final ContentResolver cr = context.getContentResolver();
final FiltersData.UserItem userItem = new FiltersData.UserItem();
userItem.setUserKey(user.key);
userItem.setScreenName(user.screen_name);
userItem.setName(user.name);
try {
// Insert to filtered users
cr.insert(Filters.Users.CONTENT_URI, FiltersData$UserItemValuesCreator.create(userItem));
if (filterAnywhere) {
// Insert user mention to keywords
List<ContentValues> userValues = new ArrayList<>();
List<ContentValues> keywordValues = new ArrayList<>();
List<ContentValues> linkValues = new ArrayList<>();
for (ParcelableUser user : users) {
final FiltersData.UserItem userItem = new FiltersData.UserItem();
userItem.setUserKey(user.key);
userItem.setScreenName(user.screen_name);
userItem.setName(user.name);
userValues.add(FiltersData$UserItemValuesCreator.create(userItem));
final FiltersData.BaseItem keywordItem = new FiltersData.BaseItem();
keywordItem.setValue("@" + user.screen_name);
cr.insert(Filters.Keywords.CONTENT_URI, FiltersData$BaseItemValuesCreator.create(keywordItem));
keywordValues.add(FiltersData$BaseItemValuesCreator.create(keywordItem));
// Insert user link (without scheme) to links
final FiltersData.BaseItem linkItem = new FiltersData.BaseItem();
@ -1045,7 +1050,15 @@ public class DataStoreUtils implements Constants {
linkWithoutScheme = linkWithoutScheme.substring(idx + 3);
}
linkItem.setValue(linkWithoutScheme);
cr.insert(Filters.Links.CONTENT_URI, FiltersData$BaseItemValuesCreator.create(linkItem));
linkValues.add(FiltersData$BaseItemValuesCreator.create(linkItem));
}
ContentResolverUtils.bulkInsert(cr, Filters.Users.CONTENT_URI, userValues);
if (filterAnywhere) {
// Insert to filtered users
ContentResolverUtils.bulkInsert(cr, Filters.Keywords.CONTENT_URI, keywordValues);
// Insert user mention to keywords
ContentResolverUtils.bulkInsert(cr, Filters.Links.CONTENT_URI, linkValues);
}
} catch (IOException e) {
throw new RuntimeException(e);

View File

@ -21,9 +21,10 @@ package org.mariotaku.twidere.view;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.CheckBox;
public class ActivatedCheckBox extends CheckBox {
import org.mariotaku.chameleon.view.ChameleonCheckBox;
public class ActivatedCheckBox extends ChameleonCheckBox {
public ActivatedCheckBox(final Context context) {
super(context);

View File

@ -29,7 +29,7 @@ fun Menu.setItemAvailability(id: Int, available: Boolean) {
item.isEnabled = available
}
fun Menu.setMenuGroupAvailability(groupId: Int, available: Boolean) {
fun Menu.setGroupAvailability(groupId: Int, available: Boolean) {
setGroupEnabled(groupId, available)
setGroupVisible(groupId, available)
}

View File

@ -0,0 +1,10 @@
package org.mariotaku.ktextension
import android.util.SparseBooleanArray
/**
* Created by mariotaku on 2016/12/27.
*/
operator fun SparseBooleanArray.set(key: Int, value: Boolean) {
put(key, value)
}

View File

@ -39,7 +39,7 @@ class AddUserFilterDialogFragment : AbsUserMuteBlockDialogFragment() {
}
override fun performUserAction(user: ParcelableUser, filterEverywhere: Boolean) {
DataStoreUtils.addToFilter(context, user, filterEverywhere)
DataStoreUtils.addToFilter(context, listOf(user), filterEverywhere)
bus.post(FriendshipTaskEvent(FriendshipTaskEvent.Action.FILTER, user.account_key, user.key).apply {
isFinished = true
isSucceeded = true

View File

@ -43,6 +43,8 @@ import android.widget.AbsListView.MultiChoiceModeListener
import android.widget.AutoCompleteTextView
import android.widget.ListView
import kotlinx.android.synthetic.main.fragment_content_listview.*
import org.mariotaku.ktextension.setGroupAvailability
import org.mariotaku.ktextension.setItemAvailability
import org.mariotaku.sqliteqb.library.Columns.Column
import org.mariotaku.sqliteqb.library.Expression
import org.mariotaku.sqliteqb.library.RawItemArray
@ -92,11 +94,17 @@ abstract class BaseFiltersFragment : AbsContentListViewFragment<SimpleCursorAdap
actionMode = mode
setControlVisible(true)
mode.menuInflater.inflate(R.menu.action_multi_select_items, menu)
menu.setGroupAvailability(R.id.selection_group, true)
return true
}
override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean {
updateTitle(mode)
val checkedCount = listView.checkedItemCount
val listCount = listView.count
menu.setItemAvailability(R.id.select_none, checkedCount > 0)
menu.setItemAvailability(R.id.select_all, checkedCount < listCount)
menu.setItemAvailability(R.id.invert_selection, checkedCount > 0 && checkedCount < listCount)
return true
}
@ -106,19 +114,28 @@ abstract class BaseFiltersFragment : AbsContentListViewFragment<SimpleCursorAdap
val where = Expression.`in`(Column(Filters._ID),
RawItemArray(listView.checkedItemIds))
context.contentResolver.delete(contentUri, where.sql, null)
mode.finish()
}
R.id.inverse_selection -> {
R.id.select_all -> {
for (i in 0 until listView.count) {
listView.setItemChecked(i, true)
}
}
R.id.select_none -> {
for (i in 0 until listView.count) {
listView.setItemChecked(i, false)
}
}
R.id.invert_selection -> {
val positions = listView.checkedItemPositions
for (i in 0 until listView.count) {
listView.setItemChecked(i, !positions.get(i))
}
return true
}
else -> {
return false
}
}
mode.finish()
return true
}
@ -129,6 +146,7 @@ abstract class BaseFiltersFragment : AbsContentListViewFragment<SimpleCursorAdap
override fun onItemCheckedStateChanged(mode: ActionMode, position: Int, id: Long,
checked: Boolean) {
updateTitle(mode)
mode.invalidate()
}
override fun onScroll(view: AbsListView, firstVisibleItem: Int, visibleItemCount: Int, totalItemCount: Int) {
@ -161,6 +179,7 @@ abstract class BaseFiltersFragment : AbsContentListViewFragment<SimpleCursorAdap
} else {
showEmpty(R.drawable.ic_info_volume_off, getString(R.string.no_rule))
}
actionMode?.invalidate()
}
override fun onLoaderReset(loader: Loader<Cursor?>) {

View File

@ -1,35 +1,54 @@
package org.mariotaku.twidere.fragment.filter
import android.app.Dialog
import android.content.Context
import android.content.DialogInterface
import android.os.Bundle
import android.support.v4.app.DialogFragment
import android.support.v4.app.LoaderManager
import android.support.v4.content.Loader
import android.support.v4.util.ArrayMap
import android.support.v7.app.AlertDialog
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.*
import android.widget.CheckBox
import android.widget.CompoundButton
import android.widget.TextView
import android.widget.Toast
import nl.komponents.kovenant.task
import nl.komponents.kovenant.ui.alwaysUi
import org.mariotaku.kpreferences.get
import org.mariotaku.ktextension.*
import org.mariotaku.twidere.R
import org.mariotaku.twidere.activity.BaseActivity
import org.mariotaku.twidere.adapter.LoadMoreSupportAdapter
import org.mariotaku.twidere.adapter.iface.IContentCardAdapter
import org.mariotaku.twidere.adapter.iface.ILoadMoreSupportAdapter
import org.mariotaku.twidere.adapter.iface.ILoadMoreSupportAdapter.IndicatorPosition
import org.mariotaku.twidere.constant.*
import org.mariotaku.twidere.fragment.AbsContentListRecyclerViewFragment
import org.mariotaku.twidere.fragment.BaseDialogFragment
import org.mariotaku.twidere.fragment.MessageDialogFragment
import org.mariotaku.twidere.fragment.ProgressDialogFragment
import org.mariotaku.twidere.loader.CursorSupportUsersLoader
import org.mariotaku.twidere.loader.iface.IExtendedLoader
import org.mariotaku.twidere.model.ParcelableUser
import org.mariotaku.twidere.model.UserKey
import org.mariotaku.twidere.util.DataStoreUtils
import org.mariotaku.twidere.util.ThemeUtils
import org.mariotaku.twidere.util.support.ViewSupport
import org.mariotaku.twidere.view.holder.LoadIndicatorViewHolder
import org.mariotaku.twidere.view.holder.SimpleUserViewHolder
/**
* Created by mariotaku on 2016/12/26.
*/
private const val EXTRA_COUNT = "count"
abstract class BaseFiltersImportFragment : AbsContentListRecyclerViewFragment<BaseFiltersImportFragment.SelectableUsersAdapter>(),
LoaderManager.LoaderCallbacks<List<ParcelableUser>?> {
protected var nextCursor: Long = -1
private set
protected var prevCursor: Long = -1
@ -39,11 +58,60 @@ abstract class BaseFiltersImportFragment : AbsContentListRecyclerViewFragment<Ba
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
setHasOptionsMenu(true)
val loaderArgs = Bundle(arguments)
loaderArgs.putBoolean(IntentConstants.EXTRA_FROM_USER, true)
loaderManager.initLoader(0, loaderArgs, this)
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.menu_filters_import, menu)
}
override fun onPrepareOptionsMenu(menu: Menu) {
val checkedCount = adapter.checkedCount
val userCount = adapter.userCount
menu.setItemAvailability(R.id.select_none, checkedCount > 0)
menu.setItemAvailability(R.id.select_all, checkedCount < userCount)
menu.setItemAvailability(R.id.invert_selection, checkedCount > 0 && checkedCount < userCount)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.select_none -> {
adapter.clearSelection()
adapter.notifyDataSetChanged()
}
R.id.select_all -> {
for (idx in rangeOfSize(adapter.userStartIndex, adapter.userCount - 1)) {
adapter.setItemChecked(idx, true)
}
adapter.notifyDataSetChanged()
}
R.id.invert_selection -> {
for (idx in rangeOfSize(adapter.userStartIndex, adapter.userCount - 1)) {
adapter.setItemChecked(idx, !adapter.isItemChecked(idx))
}
adapter.notifyDataSetChanged()
}
R.id.perform_import -> {
if (adapter.checkedCount == 0) {
Toast.makeText(context, R.string.message_no_user_selected, Toast.LENGTH_SHORT).show()
return true
}
val df = ImportConfirmDialogFragment()
df.arguments = Bundle {
this[EXTRA_COUNT] = adapter.checkedCount
}
df.show(childFragmentManager, "import_confirm")
}
else -> {
return false
}
}
return true
}
override fun onCreateLoader(id: Int, args: Bundle): Loader<List<ParcelableUser>?> {
val fromUser = args.getBoolean(IntentConstants.EXTRA_FROM_USER)
args.remove(IntentConstants.EXTRA_FROM_USER)
@ -55,22 +123,35 @@ abstract class BaseFiltersImportFragment : AbsContentListRecyclerViewFragment<Ba
}
override fun onLoadFinished(loader: Loader<List<ParcelableUser>?>, data: List<ParcelableUser>?) {
val hasMoreData = run {
val previousCount = adapter.data?.size
if (previousCount != data?.size) return@run true
val previousFirst = adapter.data?.firstOrNull()
val previousLast = adapter.data?.lastOrNull()
// If first and last data not changed, assume no more data
return@run previousFirst != data?.firstOrNull() && previousLast != data?.lastOrNull()
}
adapter.data = data
if (loader !is IExtendedLoader || loader.fromUser) {
adapter.loadMoreSupportedPosition = if (hasMoreData(data)) ILoadMoreSupportAdapter.END else ILoadMoreSupportAdapter.NONE
adapter.loadMoreSupportedPosition = if (hasMoreData) {
ILoadMoreSupportAdapter.END
} else {
ILoadMoreSupportAdapter.NONE
}
refreshEnabled = true
}
if (loader is IExtendedLoader) {
loader.fromUser = false
}
showContent()
refreshEnabled = true
refreshEnabled = data.isNullOrEmpty()
refreshing = false
setLoadMoreIndicatorPosition(ILoadMoreSupportAdapter.NONE)
val cursorLoader = loader as CursorSupportUsersLoader
nextCursor = cursorLoader.nextCursor
prevCursor = cursorLoader.prevCursor
nextPage = cursorLoader.nextPage
activity.supportInvalidateOptionsMenu()
}
override fun onLoadMoreContents(@IndicatorPosition position: Long) {
@ -85,21 +166,72 @@ abstract class BaseFiltersImportFragment : AbsContentListRecyclerViewFragment<Ba
loaderManager.restartLoader(0, loaderArgs, this)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_filters_import, container, false)
}
override fun onCreateAdapter(context: Context): SelectableUsersAdapter {
return SelectableUsersAdapter(context)
val adapter = SelectableUsersAdapter(context)
adapter.itemCheckedListener = { position, checked ->
val count = adapter.checkedCount
val actionBar = (activity as BaseActivity).supportActionBar
actionBar?.subtitle = if (count > 0) {
resources.getQuantityString(R.plurals.Nitems_selected, count, count)
} else {
null
}
activity.supportInvalidateOptionsMenu()
}
return adapter
}
protected abstract fun onCreateUsersLoader(context: Context, args: Bundle, fromUser: Boolean):
Loader<List<ParcelableUser>?>
protected open fun hasMoreData(data: List<ParcelableUser>?): Boolean {
return data == null || !data.isEmpty()
private fun performImport(filterEverywhere: Boolean) {
val selectedUsers = rangeOfSize(adapter.userStartIndex, adapter.userCount - 1)
.filter { adapter.isItemChecked(it) }
.map { adapter.getUser(it)!! }
ProgressDialogFragment.show(childFragmentManager, "import_progress")
task {
DataStoreUtils.addToFilter(context, selectedUsers, filterEverywhere)
}.alwaysUi {
executeAfterFragmentResumed {
(childFragmentManager.findFragmentByTag("import_progress") as? DialogFragment)?.dismiss()
}
}
}
class ImportConfirmDialogFragment : BaseDialogFragment(), DialogInterface.OnClickListener {
override fun onClick(dialog: DialogInterface, which: Int) {
when (which) {
DialogInterface.BUTTON_POSITIVE -> {
val filterEverywhere = ((dialog as Dialog).findViewById(R.id.filterEverywhereToggle) as CheckBox).isChecked
(parentFragment as BaseFiltersImportFragment).performImport(filterEverywhere)
}
}
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val builder = AlertDialog.Builder(context)
builder.setTitle(R.string.add_to_filter)
builder.setView(R.layout.dialog_block_mute_filter_user_confirm)
builder.setPositiveButton(android.R.string.ok, this)
builder.setNegativeButton(android.R.string.cancel, null)
val dialog = builder.create()
dialog.setOnShowListener {
val confirmMessageView = dialog.findViewById(R.id.confirmMessage) as TextView
val filterEverywhereHelp = dialog.findViewById(R.id.filterEverywhereHelp)!!
filterEverywhereHelp.setOnClickListener {
MessageDialogFragment.show(childFragmentManager, title = getString(R.string.filter_everywhere),
message = getString(R.string.filter_everywhere_description), tag = "filter_everywhere_help")
}
val usersCount = arguments.getInt(EXTRA_COUNT)
val nUsers = resources.getQuantityString(R.plurals.N_users, usersCount, usersCount)
confirmMessageView.text = getString(R.string.filter_user_confirm_message, nUsers)
}
return dialog
}
}
class SelectableUsersAdapter(context: Context) : LoadMoreSupportAdapter<RecyclerView.ViewHolder>(context), IContentCardAdapter {
override val isShowAbsoluteTime: Boolean
override val profileImageEnabled: Boolean
@ -109,16 +241,23 @@ abstract class BaseFiltersImportFragment : AbsContentListRecyclerViewFragment<Ba
val ITEM_VIEW_TYPE_USER = 2
private val inflater: LayoutInflater
private val itemStates: MutableMap<UserKey, Boolean>
var itemCheckedListener: ((Int, Boolean) -> Unit)? = null
var data: List<ParcelableUser>? = null
set(value) {
field = value
value?.forEach { item ->
if (item.key !in itemStates && item.is_filtered) {
itemStates[item.key] = true
}
}
notifyDataSetChanged()
}
init {
inflater = LayoutInflater.from(context)
itemStates = ArrayMap<UserKey, Boolean>()
isShowAbsoluteTime = preferences[showAbsoluteTimeKey]
profileImageEnabled = preferences[displayProfileImageKey]
profileImageStyle = preferences[profileImageStyleKey]
@ -157,9 +296,8 @@ abstract class BaseFiltersImportFragment : AbsContentListRecyclerViewFragment<Ba
return start
}
fun getUserId(position: Int): String? {
if (position == userCount) return null
return data!![position].key.id
fun getUserKey(position: Int): UserKey {
return data!![position].key
}
val userCount: Int
@ -229,15 +367,48 @@ abstract class BaseFiltersImportFragment : AbsContentListRecyclerViewFragment<Ba
}
return ITEM_VIEW_TYPE_USER
}
val checkedCount: Int get() {
return itemStates.count { it.value }
}
fun setItemChecked(position: Int, value: Boolean) {
val userKey = getUserKey(position)
itemStates[userKey] = value
itemCheckedListener?.invoke(position, value)
}
fun isItemChecked(position: Int): Boolean {
return itemStates[getUserKey(position)] ?: false
}
fun clearSelection() {
itemStates.clear()
}
}
class SelectableUserViewHolder(
internal class SelectableUserViewHolder(
itemView: View,
adapter: IContentCardAdapter
adapter: SelectableUsersAdapter
) : SimpleUserViewHolder(itemView, adapter) {
val checkChangedListener: CompoundButton.OnCheckedChangeListener
init {
ViewSupport.setBackground(itemView, ThemeUtils.getSelectableItemBackgroundDrawable(itemView.context))
checkBox.visibility = View.VISIBLE
checkChangedListener = CompoundButton.OnCheckedChangeListener { view, value ->
adapter.setItemChecked(layoutPosition, value)
}
itemView.setOnClickListener {
checkBox.toggle()
}
}
override fun displayUser(user: ParcelableUser) {
super.displayUser(user)
checkBox.setOnCheckedChangeListener(null)
checkBox.isChecked = (adapter as SelectableUsersAdapter).isItemChecked(layoutPosition)
checkBox.setOnCheckedChangeListener(checkChangedListener)
}
}

View File

@ -64,13 +64,19 @@ abstract class MicroBlogAPIUsersLoader(
if (hasId(user.id)) {
continue
}
data.add(ParcelableUserUtils.fromUser(user, accountKey, pos.toLong()))
val item = ParcelableUserUtils.fromUser(user, accountKey, pos.toLong())
processUser(item)
data.add(item)
pos++
}
Collections.sort(data)
return ListResponse.getListInstance(data)
}
protected open fun processUser(user: ParcelableUser) {
}
@Throws(MicroBlogException::class)
protected abstract fun getUsers(twitter: MicroBlog, details: AccountDetails): List<User>
}

View File

@ -28,6 +28,7 @@ import org.mariotaku.microblog.library.twitter.model.User
import org.mariotaku.twidere.model.AccountDetails
import org.mariotaku.twidere.model.ParcelableUser
import org.mariotaku.twidere.model.UserKey
import org.mariotaku.twidere.util.DataStoreUtils
class MutesUsersLoader(
context: Context,
@ -36,9 +37,19 @@ class MutesUsersLoader(
fromUser: Boolean
) : CursorSupportUsersLoader(context, accountKey, data, fromUser) {
private var filteredUsers: Array<UserKey>? = null
@Throws(MicroBlogException::class)
override fun getCursoredUsers(twitter: MicroBlog, details: AccountDetails, paging: Paging): PageableResponseList<User> {
return twitter.getMutesUsersList(paging)
}
override fun onLoadInBackground(): List<ParcelableUser> {
filteredUsers = DataStoreUtils.getFilteredUserIds(context)
return super.onLoadInBackground()
}
override fun processUser(user: ParcelableUser) {
user.is_filtered = filteredUsers?.contains(user.key) ?: false
}
}

View File

@ -29,6 +29,7 @@ import org.mariotaku.twidere.annotation.AccountType
import org.mariotaku.twidere.model.AccountDetails
import org.mariotaku.twidere.model.ParcelableUser
import org.mariotaku.twidere.model.UserKey
import org.mariotaku.twidere.util.DataStoreUtils
class UserBlocksLoader(
context: Context,
@ -37,6 +38,8 @@ class UserBlocksLoader(
fromUser: Boolean
) : CursorSupportUsersLoader(context, accountKey, data, fromUser) {
private var filteredUsers: Array<UserKey>? = null
@Throws(MicroBlogException::class)
override fun getCursoredUsers(twitter: MicroBlog,
details: AccountDetails,
@ -49,4 +52,12 @@ class UserBlocksLoader(
return twitter.getBlocksList(paging)
}
override fun onLoadInBackground(): List<ParcelableUser> {
filteredUsers = DataStoreUtils.getFilteredUserIds(context)
return super.onLoadInBackground()
}
override fun processUser(user: ParcelableUser) {
user.is_filtered = filteredUsers?.contains(user.key) ?: false
}
}

View File

@ -0,0 +1,14 @@
package org.mariotaku.twidere.model.analyzer
import org.mariotaku.twidere.annotation.AccountType
import org.mariotaku.twidere.util.Analyzer
/**
* Created by mariotaku on 2016/12/28.
*/
data class UpdateStatus(
@AccountType override val accountType: String? = null
) : Analyzer.Event {
}

View File

@ -66,7 +66,7 @@ open class CreateUserBlockTask(
resolver.insert(CachedRelationships.CONTENT_URI, values)
if (filterEverywhere) {
DataStoreUtils.addToFilter(context, user, true)
DataStoreUtils.addToFilter(context, listOf(user), true)
}
}

View File

@ -59,7 +59,7 @@ class CreateUserMuteTask(
values.put(CachedRelationships.MUTING, true)
resolver.insert(CachedRelationships.CONTENT_URI, values)
if (filterEverywhere) {
DataStoreUtils.addToFilter(context, user, true)
DataStoreUtils.addToFilter(context, listOf(user), true)
}
}

View File

@ -23,6 +23,7 @@ fun AccountManager.removeAccountSupport(
return AccountManagerSupportL.removeAccount(this, account, activity, callback, handler)
}
@Suppress("DEPRECATION")
val future = this.removeAccount(account, { future ->
callback?.run(BooleanToBundleAccountManagerFuture(future))
}, handler)

View File

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include
layout="@layout/fragment_content_recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>

View File

@ -51,7 +51,7 @@
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
@ -61,7 +61,7 @@
<TextView
android:id="@+id/screenName"
android:layout_width="wrap_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"

View File

@ -1,11 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:ignore="AppCompatResource">
<item
android:id="@id/delete"
android:icon="@drawable/ic_action_delete"
app:showAsAction="ifRoom|withText"
android:title="@string/action_delete" />
android:showAsAction="always"
android:title="@string/action_delete"
app:showAsAction="always"
tools:ignore="AlwaysShowAction"/>
<group
android:id="@+id/selection_group"
android:enabled="false"
android:visible="false">
<item
android:id="@+id/select_all"
android:showAsAction="never"
android:title="@string/action_select_all"
app:showAsAction="never"/>
<item
android:id="@+id/select_none"
android:showAsAction="never"
android:title="@string/action_select_none"
app:showAsAction="never"/>
<item
android:id="@+id/invert_selection"
android:showAsAction="never"
android:title="@string/action_invert_selection"
app:showAsAction="never"/>
</group>
</menu>

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/perform_import"
android:icon="@drawable/ic_action_import"
android:title="@string/action_import"
app:showAsAction="always"/>
<item
android:id="@+id/select_all"
android:title="@string/action_select_all"
app:showAsAction="never"/>
<item
android:id="@+id/select_none"
android:title="@string/action_select_none"
app:showAsAction="never"/>
<item
android:id="@+id/invert_selection"
android:title="@string/action_invert_selection"
app:showAsAction="never"/>
</menu>

View File

@ -78,7 +78,6 @@
<item name="progress" type="id"/>
<item name="open_with_account" type="id"/>
<item name="accounts" type="id"/>
<item name="inverse_selection" type="id"/>
<item name="edit_media" type="id"/>
<item name="reset" type="id"/>
<item name="info" type="id"/>

View File

@ -61,5 +61,9 @@
<item quantity="one"><xliff:g id="count">%d</xliff:g> vote</item>
<item quantity="other"><xliff:g id="count">%d</xliff:g> votes</item>
</plurals>
<plurals name="N_users">
<item quantity="one"><xliff:g id="count">%d</xliff:g> user</item>
<item quantity="other"><xliff:g id="count">%d</xliff:g> users</item>
</plurals>
</resources>

View File

@ -578,7 +578,6 @@
<string name="exclude_this_host">Exclude this host</string>
<string name="api_url_format_help">[DOMAIN]: Twitter API domain.\nExample: https://[DOMAIN].twitter.com/ will be replaced to https://api.twitter.com/.</string>
<string name="no_version_suffix">No version suffix</string>
<string name="inverse_selection">Inverse selection</string>
<string name="edit_media">Edit media</string>
<string name="media">Media</string>
<string name="mute_user">Mute <xliff:g id="name">%s</xliff:g></string>
@ -859,4 +858,10 @@
<string name="title_error_invalid_account">Invalid account</string>
<string name="message_error_invalid_account">Some account data are corrupted, Twidere will remove those accounts to prevent crash.</string>
<string name="title_premium_features_name">Twidere ∞</string>
<!-- [verb] Perform import action -->
<string name="action_import">Import</string>
<string name="action_select_all">Select all</string>
<string name="action_select_none">Select none</string>
<string name="action_invert_selection">Invert selection</string>
<string name="message_no_user_selected">No user selected</string>
</resources>

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="32px" height="32px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 41.2 (35397) - http://www.bohemiancoding.com/sketch -->
<title>ic_action_import-mdpi</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Action-Icons" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="ic_action_import-mdpi">
<polyline id="Path-2" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" points="11.7563613 7 7 7 7 25 25 25 25 20.406492"></polyline>
<path d="M16,8.13011457 L18.7765957,10.9042553 L24.6783961,5 L28,8.32160393 L22.0957447,14.2234043 L24.8698854,16.997545 L16,16.997545 L16,8.13011457 Z" id="polygon3-#1-path" fill="#FFFFFF"></path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 907 B