improved auto complete, A LOT

This commit is contained in:
Mariotaku Lee 2015-11-21 13:45:55 +08:00
parent a10b83eb01
commit 75c21cdf51
13 changed files with 242 additions and 150 deletions

View File

@ -270,6 +270,8 @@ public interface TwidereDataStore {
String LAST_SEEN = "last_seen"; String LAST_SEEN = "last_seen";
String SCORE = "score";
String[] COLUMNS = {_ID, USER_ID, CREATED_AT, NAME, SCREEN_NAME, String[] COLUMNS = {_ID, USER_ID, CREATED_AT, NAME, SCREEN_NAME,
DESCRIPTION_PLAIN, LOCATION, URL, PROFILE_IMAGE_URL, PROFILE_BANNER_URL, IS_PROTECTED, DESCRIPTION_PLAIN, LOCATION, URL, PROFILE_IMAGE_URL, PROFILE_BANNER_URL, IS_PROTECTED,
IS_VERIFIED, IS_FOLLOWING, FOLLOWERS_COUNT, FRIENDS_COUNT, STATUSES_COUNT, FAVORITES_COUNT, IS_VERIFIED, IS_FOLLOWING, FOLLOWERS_COUNT, FRIENDS_COUNT, STATUSES_COUNT, FAVORITES_COUNT,
@ -293,6 +295,7 @@ public interface TwidereDataStore {
String ICON = "icon"; String ICON = "icon";
String EXTRA_ID = "extra_id"; String EXTRA_ID = "extra_id";
String EXTRA = "extra"; String EXTRA = "extra";
String VALUE = "value";
String TABLE_NAME = "suggestions"; String TABLE_NAME = "suggestions";
@ -300,9 +303,9 @@ public interface TwidereDataStore {
Uri CONTENT_URI = Uri.withAppendedPath(BASE_CONTENT_URI, CONTENT_PATH); Uri CONTENT_URI = Uri.withAppendedPath(BASE_CONTENT_URI, CONTENT_PATH);
String[] COLUMNS = {_ID, TYPE, TITLE, SUMMARY, ICON, EXTRA_ID, EXTRA}; String[] COLUMNS = {_ID, TYPE, TITLE, SUMMARY, ICON, EXTRA_ID, EXTRA, VALUE};
String[] TYPES = {TYPE_PRIMARY_KEY, TYPE_TEXT_NOT_NULL, TYPE_TEXT, TYPE_TEXT, TYPE_TEXT, String[] TYPES = {TYPE_PRIMARY_KEY, TYPE_TEXT_NOT_NULL, TYPE_TEXT, TYPE_TEXT, TYPE_TEXT,
TYPE_INT, TYPE_TEXT}; TYPE_INT, TYPE_TEXT, TYPE_TEXT};
interface AutoComplete extends Suggestions { interface AutoComplete extends Suggestions {

View File

@ -38,7 +38,7 @@ import android.widget.ListView;
import org.mariotaku.twidere.R; import org.mariotaku.twidere.R;
import org.mariotaku.twidere.adapter.SimpleParcelableUserListsAdapter; import org.mariotaku.twidere.adapter.SimpleParcelableUserListsAdapter;
import org.mariotaku.twidere.adapter.SimpleParcelableUsersAdapter; import org.mariotaku.twidere.adapter.SimpleParcelableUsersAdapter;
import org.mariotaku.twidere.adapter.UserHashtagAutoCompleteAdapter; import org.mariotaku.twidere.adapter.UserAutoCompleteAdapter;
import org.mariotaku.twidere.api.twitter.Twitter; import org.mariotaku.twidere.api.twitter.Twitter;
import org.mariotaku.twidere.api.twitter.TwitterException; import org.mariotaku.twidere.api.twitter.TwitterException;
import org.mariotaku.twidere.api.twitter.http.HttpResponseCode; import org.mariotaku.twidere.api.twitter.http.HttpResponseCode;
@ -169,7 +169,7 @@ public class UserListSelectorActivity extends BaseSupportDialogActivity implemen
getUserLists(mScreenName); getUserLists(mScreenName);
} }
} }
final UserHashtagAutoCompleteAdapter adapter = new UserHashtagAutoCompleteAdapter(this); final UserAutoCompleteAdapter adapter = new UserAutoCompleteAdapter(this);
adapter.setAccountId(getAccountId()); adapter.setAccountId(getAccountId());
mEditScreenName.setAdapter(adapter); mEditScreenName.setAdapter(adapter);
mEditScreenName.setText(mScreenName); mEditScreenName.setText(mScreenName);

View File

@ -29,6 +29,7 @@ import android.view.View;
import android.widget.FilterQueryProvider; import android.widget.FilterQueryProvider;
import android.widget.TextView; import android.widget.TextView;
import org.apache.commons.lang3.StringUtils;
import org.mariotaku.twidere.Constants; import org.mariotaku.twidere.Constants;
import org.mariotaku.twidere.R; import org.mariotaku.twidere.R;
import org.mariotaku.twidere.provider.TwidereDataStore.Suggestions; import org.mariotaku.twidere.provider.TwidereDataStore.Suggestions;
@ -42,7 +43,7 @@ import org.mariotaku.twidere.view.ProfileImageView;
import javax.inject.Inject; import javax.inject.Inject;
public class UserHashtagAutoCompleteAdapter extends SimpleCursorAdapter implements Constants { public class ComposeAutoCompleteAdapter extends SimpleCursorAdapter implements Constants {
private static final String[] FROM = new String[0]; private static final String[] FROM = new String[0];
private static final int[] TO = new int[0]; private static final int[] TO = new int[0];
@ -56,11 +57,11 @@ public class UserHashtagAutoCompleteAdapter extends SimpleCursorAdapter implemen
private final boolean mDisplayProfileImage; private final boolean mDisplayProfileImage;
private int mTypeIdx, mIconIdx, mTitleIdx, mSummaryIdx, mExtraIdIdx; private int mTypeIdx, mIconIdx, mTitleIdx, mSummaryIdx, mExtraIdIdx, mValueIdx;
private long mAccountId; private long mAccountId;
private char mToken; private char mToken;
public UserHashtagAutoCompleteAdapter(final Context context) { public ComposeAutoCompleteAdapter(final Context context) {
super(context, R.layout.list_item_auto_complete, null, FROM, TO, 0); super(context, R.layout.list_item_auto_complete, null, FROM, TO, 0);
DaggerGeneralComponent.builder().applicationModule(ApplicationModule.get(context)).build().inject(this); DaggerGeneralComponent.builder().applicationModule(ApplicationModule.get(context)).build().inject(this);
mDisplayProfileImage = mPreferences.getBoolean(KEY_DISPLAY_PROFILE_IMAGE, true); mDisplayProfileImage = mPreferences.getBoolean(KEY_DISPLAY_PROFILE_IMAGE, true);
@ -68,7 +69,6 @@ public class UserHashtagAutoCompleteAdapter extends SimpleCursorAdapter implemen
@Override @Override
public void bindView(final View view, final Context context, final Cursor cursor) { public void bindView(final View view, final Context context, final Cursor cursor) {
if (isCursorClosed()) return;
final TextView text1 = (TextView) view.findViewById(android.R.id.text1); final TextView text1 = (TextView) view.findViewById(android.R.id.text1);
final TextView text2 = (TextView) view.findViewById(android.R.id.text2); final TextView text2 = (TextView) view.findViewById(android.R.id.text2);
final ProfileImageView icon = (ProfileImageView) view.findViewById(android.R.id.icon); final ProfileImageView icon = (ProfileImageView) view.findViewById(android.R.id.icon);
@ -78,7 +78,7 @@ public class UserHashtagAutoCompleteAdapter extends SimpleCursorAdapter implemen
if (Suggestions.AutoComplete.TYPE_USERS.equals(cursor.getString(mTypeIdx))) { if (Suggestions.AutoComplete.TYPE_USERS.equals(cursor.getString(mTypeIdx))) {
text1.setText(mUserColorNameManager.getUserNickname(cursor.getLong(mExtraIdIdx), cursor.getString(mTitleIdx))); text1.setText(mUserColorNameManager.getUserNickname(cursor.getLong(mExtraIdIdx), cursor.getString(mTitleIdx)));
text2.setText("@" + cursor.getString(mSummaryIdx)); text2.setText('@' + cursor.getString(mSummaryIdx));
if (mDisplayProfileImage) { if (mDisplayProfileImage) {
final String profileImageUrl = cursor.getString(mIconIdx); final String profileImageUrl = cursor.getString(mIconIdx);
mProfileImageLoader.displayProfileImage(icon, profileImageUrl); mProfileImageLoader.displayProfileImage(icon, profileImageUrl);
@ -88,7 +88,7 @@ public class UserHashtagAutoCompleteAdapter extends SimpleCursorAdapter implemen
icon.clearColorFilter(); icon.clearColorFilter();
} else { } else {
text1.setText("#" + cursor.getString(mTitleIdx)); text1.setText('#' + cursor.getString(mTitleIdx));
text2.setText(R.string.hashtag); text2.setText(R.string.hashtag);
icon.setImageResource(R.drawable.ic_action_hashtag); icon.setImageResource(R.drawable.ic_action_hashtag);
@ -99,7 +99,7 @@ public class UserHashtagAutoCompleteAdapter extends SimpleCursorAdapter implemen
} }
public void closeCursor() { public void closeCursor() {
final Cursor cursor = getCursor(); final Cursor cursor = swapCursor(null);
if (cursor == null) return; if (cursor == null) return;
if (!cursor.isClosed()) { if (!cursor.isClosed()) {
cursor.close(); cursor.close();
@ -108,13 +108,15 @@ public class UserHashtagAutoCompleteAdapter extends SimpleCursorAdapter implemen
@Override @Override
public CharSequence convertToString(final Cursor cursor) { public CharSequence convertToString(final Cursor cursor) {
if (isCursorClosed()) return null; switch (StringUtils.defaultIfEmpty(cursor.getString(mTypeIdx), "")) {
return cursor.getString(mSummaryIdx != -1 ? mSummaryIdx : mTitleIdx); case Suggestions.AutoComplete.TYPE_HASHTAGS: {
return '#' + cursor.getString(mValueIdx);
} }
case Suggestions.AutoComplete.TYPE_USERS: {
public boolean isCursorClosed() { return '@' + cursor.getString(mValueIdx);
final Cursor cursor = getCursor(); }
return cursor == null || cursor.isClosed(); }
return cursor.getString(mValueIdx);
} }
@Override @Override
@ -159,6 +161,7 @@ public class UserHashtagAutoCompleteAdapter extends SimpleCursorAdapter implemen
mSummaryIdx = cursor.getColumnIndex(Suggestions.AutoComplete.SUMMARY); mSummaryIdx = cursor.getColumnIndex(Suggestions.AutoComplete.SUMMARY);
mExtraIdIdx = cursor.getColumnIndex(Suggestions.AutoComplete.EXTRA_ID); mExtraIdIdx = cursor.getColumnIndex(Suggestions.AutoComplete.EXTRA_ID);
mIconIdx = cursor.getColumnIndex(Suggestions.AutoComplete.ICON); mIconIdx = cursor.getColumnIndex(Suggestions.AutoComplete.ICON);
mValueIdx = cursor.getColumnIndex(Suggestions.AutoComplete.VALUE);
} }
return super.swapCursor(cursor); return super.swapCursor(cursor);
} }

View File

@ -0,0 +1,147 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2015 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.adapter;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.support.v4.widget.SimpleCursorAdapter;
import android.text.TextUtils;
import android.view.View;
import android.widget.FilterQueryProvider;
import android.widget.TextView;
import org.mariotaku.sqliteqb.library.Columns;
import org.mariotaku.sqliteqb.library.Expression;
import org.mariotaku.sqliteqb.library.OrderBy;
import org.mariotaku.sqliteqb.library.RawItemArray;
import org.mariotaku.twidere.Constants;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.provider.TwidereDataStore.CachedUsers;
import org.mariotaku.twidere.util.MediaLoaderWrapper;
import org.mariotaku.twidere.util.SharedPreferencesWrapper;
import org.mariotaku.twidere.util.UserColorNameManager;
import org.mariotaku.twidere.util.Utils;
import org.mariotaku.twidere.util.dagger.ApplicationModule;
import org.mariotaku.twidere.util.dagger.DaggerGeneralComponent;
import org.mariotaku.twidere.view.ProfileImageView;
import javax.inject.Inject;
public class UserAutoCompleteAdapter extends SimpleCursorAdapter implements Constants {
private static final String[] FROM = new String[0];
private static final int[] TO = new int[0];
@Inject
MediaLoaderWrapper mProfileImageLoader;
@Inject
SharedPreferencesWrapper mPreferences;
@Inject
UserColorNameManager mUserColorNameManager;
private final boolean mDisplayProfileImage;
private int mIdIdx, mNameIdx, mScreenNameIdx, mProfileImageIdx;
private long mAccountId;
private char mToken;
public UserAutoCompleteAdapter(final Context context) {
super(context, R.layout.list_item_auto_complete, null, FROM, TO, 0);
DaggerGeneralComponent.builder().applicationModule(ApplicationModule.get(context)).build().inject(this);
mDisplayProfileImage = mPreferences.getBoolean(KEY_DISPLAY_PROFILE_IMAGE, true);
}
@Override
public void bindView(final View view, final Context context, final Cursor cursor) {
final TextView text1 = (TextView) view.findViewById(android.R.id.text1);
final TextView text2 = (TextView) view.findViewById(android.R.id.text2);
final ProfileImageView icon = (ProfileImageView) view.findViewById(android.R.id.icon);
// Clear images in order to prevent images in recycled view shown.
icon.setImageDrawable(null);
text1.setText(mUserColorNameManager.getUserNickname(cursor.getLong(mIdIdx), cursor.getString(mNameIdx)));
text2.setText('@' + cursor.getString(mScreenNameIdx));
if (mDisplayProfileImage) {
final String profileImageUrl = cursor.getString(mProfileImageIdx);
mProfileImageLoader.displayProfileImage(icon, profileImageUrl);
} else {
mProfileImageLoader.cancelDisplayTask(icon);
}
icon.setVisibility(mDisplayProfileImage ? View.VISIBLE : View.GONE);
super.bindView(view, context, cursor);
}
public void closeCursor() {
final Cursor cursor = swapCursor(null);
if (cursor == null) return;
if (!cursor.isClosed()) {
cursor.close();
}
}
@Override
public CharSequence convertToString(final Cursor cursor) {
return cursor.getString(mScreenNameIdx);
}
@Override
public Cursor runQueryOnBackgroundThread(final CharSequence constraint) {
if (TextUtils.isEmpty(constraint)) return null;
final FilterQueryProvider filter = getFilterQueryProvider();
if (filter != null) return filter.runQuery(constraint);
final String query = constraint.toString();
final String queryEscaped = query.replace("_", "^_");
final long[] nicknameIds = Utils.getMatchedNicknameIds(query, mUserColorNameManager);
final Expression usersSelection = Expression.or(
Expression.likeRaw(new Columns.Column(CachedUsers.SCREEN_NAME), "?||'%'", "^"),
Expression.likeRaw(new Columns.Column(CachedUsers.NAME), "?||'%'", "^"),
Expression.in(new Columns.Column(CachedUsers.USER_ID), new RawItemArray(nicknameIds)));
final String[] selectionArgs = new String[]{queryEscaped, queryEscaped};
final String[] order = {CachedUsers.LAST_SEEN, CachedUsers.SCORE, CachedUsers.SCREEN_NAME,
CachedUsers.NAME};
final boolean[] ascending = {false, false, true, true};
final OrderBy orderBy = new OrderBy(order, ascending);
final Uri uri = Uri.withAppendedPath(CachedUsers.CONTENT_URI_WITH_SCORE, String.valueOf(mAccountId));
return mContext.getContentResolver().query(uri, CachedUsers.COLUMNS, usersSelection.getSQL(),
selectionArgs, orderBy.getSQL());
}
public void setAccountId(long accountId) {
mAccountId = accountId;
}
@Override
public Cursor swapCursor(final Cursor cursor) {
if (cursor != null) {
mIdIdx = cursor.getColumnIndex(CachedUsers.USER_ID);
mNameIdx = cursor.getColumnIndex(CachedUsers.NAME);
mScreenNameIdx = cursor.getColumnIndex(CachedUsers.SCREEN_NAME);
mProfileImageIdx = cursor.getColumnIndex(CachedUsers.PROFILE_IMAGE_URL);
}
return super.swapCursor(cursor);
}
}

View File

@ -59,8 +59,8 @@ import org.mariotaku.sqliteqb.library.Expression;
import org.mariotaku.sqliteqb.library.RawItemArray; import org.mariotaku.sqliteqb.library.RawItemArray;
import org.mariotaku.twidere.R; import org.mariotaku.twidere.R;
import org.mariotaku.twidere.activity.support.UserListSelectorActivity; import org.mariotaku.twidere.activity.support.UserListSelectorActivity;
import org.mariotaku.twidere.adapter.ComposeAutoCompleteAdapter;
import org.mariotaku.twidere.adapter.SourceAutoCompleteAdapter; import org.mariotaku.twidere.adapter.SourceAutoCompleteAdapter;
import org.mariotaku.twidere.adapter.UserHashtagAutoCompleteAdapter;
import org.mariotaku.twidere.fragment.support.AbsContentListViewFragment; import org.mariotaku.twidere.fragment.support.AbsContentListViewFragment;
import org.mariotaku.twidere.fragment.support.BaseSupportDialogFragment; import org.mariotaku.twidere.fragment.support.BaseSupportDialogFragment;
import org.mariotaku.twidere.model.ParcelableUser; import org.mariotaku.twidere.model.ParcelableUser;
@ -292,7 +292,7 @@ public abstract class BaseFiltersFragment extends AbsContentListViewFragment<Sim
if (auto_complete_type == AUTO_COMPLETE_TYPE_SOURCES) { if (auto_complete_type == AUTO_COMPLETE_TYPE_SOURCES) {
mUserAutoCompleteAdapter = new SourceAutoCompleteAdapter(activity); mUserAutoCompleteAdapter = new SourceAutoCompleteAdapter(activity);
} else { } else {
final UserHashtagAutoCompleteAdapter adapter = new UserHashtagAutoCompleteAdapter(activity); final ComposeAutoCompleteAdapter adapter = new ComposeAutoCompleteAdapter(activity);
adapter.setAccountId(Utils.getDefaultAccountId(activity)); adapter.setAccountId(Utils.getDefaultAccountId(activity));
mUserAutoCompleteAdapter = adapter; mUserAutoCompleteAdapter = adapter;
} }

View File

@ -32,7 +32,7 @@ import android.view.View;
import android.widget.AutoCompleteTextView; import android.widget.AutoCompleteTextView;
import org.mariotaku.twidere.R; import org.mariotaku.twidere.R;
import org.mariotaku.twidere.adapter.UserHashtagAutoCompleteAdapter; import org.mariotaku.twidere.adapter.ComposeAutoCompleteAdapter;
import org.mariotaku.twidere.util.AsyncTwitterWrapper; import org.mariotaku.twidere.util.AsyncTwitterWrapper;
import org.mariotaku.twidere.util.ParseUtils; import org.mariotaku.twidere.util.ParseUtils;
import org.mariotaku.twidere.util.ThemeUtils; import org.mariotaku.twidere.util.ThemeUtils;
@ -42,7 +42,7 @@ public class AddUserListMemberDialogFragment extends BaseSupportDialogFragment i
public static final String FRAGMENT_TAG = "add_user_list_member"; public static final String FRAGMENT_TAG = "add_user_list_member";
private AutoCompleteTextView mEditText; private AutoCompleteTextView mEditText;
private UserHashtagAutoCompleteAdapter mUserAutoCompleteAdapter; private ComposeAutoCompleteAdapter mUserAutoCompleteAdapter;
@Override @Override
public void onClick(final DialogInterface dialog, final int which) { public void onClick(final DialogInterface dialog, final int which) {
@ -71,7 +71,7 @@ public class AddUserListMemberDialogFragment extends BaseSupportDialogFragment i
if (savedInstanceState != null) { if (savedInstanceState != null) {
mEditText.setText(savedInstanceState.getCharSequence(EXTRA_TEXT)); mEditText.setText(savedInstanceState.getCharSequence(EXTRA_TEXT));
} }
mUserAutoCompleteAdapter = new UserHashtagAutoCompleteAdapter(wrapped); mUserAutoCompleteAdapter = new ComposeAutoCompleteAdapter(wrapped);
final Bundle args = getArguments(); final Bundle args = getArguments();
mUserAutoCompleteAdapter.setAccountId(args.getLong(EXTRA_ACCOUNT_ID)); mUserAutoCompleteAdapter.setAccountId(args.getLong(EXTRA_ACCOUNT_ID));
mEditText.setAdapter(mUserAutoCompleteAdapter); mEditText.setAdapter(mUserAutoCompleteAdapter);

View File

@ -49,7 +49,7 @@ import org.mariotaku.twidere.util.LinkCreator;
import org.mariotaku.twidere.util.MenuUtils; import org.mariotaku.twidere.util.MenuUtils;
import org.mariotaku.twidere.util.ThemeUtils; import org.mariotaku.twidere.util.ThemeUtils;
import org.mariotaku.twidere.util.TwidereValidator; import org.mariotaku.twidere.util.TwidereValidator;
import org.mariotaku.twidere.view.ComposeMaterialEditText; import org.mariotaku.twidere.view.ComposeEditText;
import org.mariotaku.twidere.view.StatusTextCountView; import org.mariotaku.twidere.view.StatusTextCountView;
import org.mariotaku.twidere.view.holder.StatusViewHolder; import org.mariotaku.twidere.view.holder.StatusViewHolder;
import org.mariotaku.twidere.view.holder.StatusViewHolder.DummyStatusHolderAdapter; import org.mariotaku.twidere.view.holder.StatusViewHolder.DummyStatusHolderAdapter;
@ -121,7 +121,7 @@ public class RetweetQuoteDialogFragment extends BaseSupportDialogFragment implem
view.findViewById(R.id.action_buttons).setVisibility(View.GONE); view.findViewById(R.id.action_buttons).setVisibility(View.GONE);
view.findViewById(R.id.item_content).setFocusable(false); view.findViewById(R.id.item_content).setFocusable(false);
view.findViewById(R.id.comment_container).setVisibility(status.user_is_protected ? View.GONE : View.VISIBLE); view.findViewById(R.id.comment_container).setVisibility(status.user_is_protected ? View.GONE : View.VISIBLE);
final ComposeMaterialEditText mEditComment = (ComposeMaterialEditText) view.findViewById(R.id.edit_comment); final ComposeEditText mEditComment = (ComposeEditText) view.findViewById(R.id.edit_comment);
mEditComment.setAccountId(status.account_id); mEditComment.setAccountId(status.account_id);
final boolean sendByEnter = mPreferences.getBoolean(KEY_QUICK_SEND); final boolean sendByEnter = mPreferences.getBoolean(KEY_QUICK_SEND);

View File

@ -780,7 +780,8 @@ public final class TwidereDataProvider extends ContentProvider implements Consta
new Column(SQLConstants.NULL, Suggestions.Search.SUMMARY).getSQL(), new Column(SQLConstants.NULL, Suggestions.Search.SUMMARY).getSQL(),
new Column(SQLConstants.NULL, Suggestions.Search.ICON).getSQL(), new Column(SQLConstants.NULL, Suggestions.Search.ICON).getSQL(),
new Column("0", Suggestions.Search.EXTRA_ID).getSQL(), new Column("0", Suggestions.Search.EXTRA_ID).getSQL(),
new Column(SQLConstants.NULL, Suggestions.Search.EXTRA).getSQL() new Column(SQLConstants.NULL, Suggestions.Search.EXTRA).getSQL(),
new Column(SearchHistory.QUERY, Suggestions.Search.VALUE).getSQL(),
}; };
final Expression historySelection = Expression.likeRaw(new Column(SearchHistory.QUERY), "?||'%'", "^"); final Expression historySelection = Expression.likeRaw(new Column(SearchHistory.QUERY), "?||'%'", "^");
@SuppressLint("Recycle") final Cursor historyCursor = mDatabaseWrapper.query(true, @SuppressLint("Recycle") final Cursor historyCursor = mDatabaseWrapper.query(true,
@ -795,7 +796,8 @@ public final class TwidereDataProvider extends ContentProvider implements Consta
new Column(SQLConstants.NULL, Suggestions.Search.SUMMARY).getSQL(), new Column(SQLConstants.NULL, Suggestions.Search.SUMMARY).getSQL(),
new Column(SQLConstants.NULL, Suggestions.Search.ICON).getSQL(), new Column(SQLConstants.NULL, Suggestions.Search.ICON).getSQL(),
new Column("0", Suggestions.Search.EXTRA_ID).getSQL(), new Column("0", Suggestions.Search.EXTRA_ID).getSQL(),
new Column(SQLConstants.NULL, Suggestions.Search.EXTRA).getSQL() new Column(SQLConstants.NULL, Suggestions.Search.EXTRA).getSQL(),
new Column(SavedSearches.QUERY, Suggestions.Search.VALUE).getSQL()
}; };
final Expression savedSearchesWhere = Expression.equals(SavedSearches.ACCOUNT_ID, accountId); final Expression savedSearchesWhere = Expression.equals(SavedSearches.ACCOUNT_ID, accountId);
@SuppressLint("Recycle") final Cursor savedSearchesCursor = mDatabaseWrapper.query(true, @SuppressLint("Recycle") final Cursor savedSearchesCursor = mDatabaseWrapper.query(true,
@ -811,7 +813,8 @@ public final class TwidereDataProvider extends ContentProvider implements Consta
new Column(CachedUsers.SCREEN_NAME, Suggestions.Search.SUMMARY).getSQL(), new Column(CachedUsers.SCREEN_NAME, Suggestions.Search.SUMMARY).getSQL(),
new Column(CachedUsers.PROFILE_IMAGE_URL, Suggestions.Search.ICON).getSQL(), new Column(CachedUsers.PROFILE_IMAGE_URL, Suggestions.Search.ICON).getSQL(),
new Column(CachedUsers.USER_ID, Suggestions.Search.EXTRA_ID).getSQL(), new Column(CachedUsers.USER_ID, Suggestions.Search.EXTRA_ID).getSQL(),
new Column(SQLConstants.NULL, Suggestions.Search.EXTRA).getSQL() new Column(SQLConstants.NULL, Suggestions.Search.EXTRA).getSQL(),
new Column(CachedUsers.SCREEN_NAME, Suggestions.Search.VALUE).getSQL(),
}; };
String queryTrimmed = queryEscaped.startsWith("@") ? queryEscaped.substring(1) : queryEscaped; String queryTrimmed = queryEscaped.startsWith("@") ? queryEscaped.substring(1) : queryEscaped;
final long[] nicknameIds = Utils.getMatchedNicknameIds(query, mUserColorNameManager); final long[] nicknameIds = Utils.getMatchedNicknameIds(query, mUserColorNameManager);
@ -820,7 +823,8 @@ public final class TwidereDataProvider extends ContentProvider implements Consta
Expression.likeRaw(new Column(CachedUsers.NAME), "?||'%'", "^"), Expression.likeRaw(new Column(CachedUsers.NAME), "?||'%'", "^"),
Expression.in(new Column(CachedUsers.USER_ID), new RawItemArray(nicknameIds))); Expression.in(new Column(CachedUsers.USER_ID), new RawItemArray(nicknameIds)));
final String[] selectionArgs = new String[]{queryTrimmed, queryTrimmed}; final String[] selectionArgs = new String[]{queryTrimmed, queryTrimmed};
final String[] order = {CachedUsers.LAST_SEEN, "score", CachedUsers.SCREEN_NAME, CachedUsers.NAME}; final String[] order = {CachedUsers.LAST_SEEN, CachedUsers.SCORE, CachedUsers.SCREEN_NAME,
CachedUsers.NAME};
final boolean[] ascending = {false, false, true, true}; final boolean[] ascending = {false, false, true, true};
final OrderBy orderBy = new OrderBy(order, ascending); final OrderBy orderBy = new OrderBy(order, ascending);
@ -838,7 +842,7 @@ public final class TwidereDataProvider extends ContentProvider implements Consta
final Matcher m = PATTERN_SCREEN_NAME.matcher(query); final Matcher m = PATTERN_SCREEN_NAME.matcher(query);
if (m.matches()) { if (m.matches()) {
screenNameCursor.addRow(new Object[]{0, Suggestions.Search.TYPE_SCREEN_NAME, screenNameCursor.addRow(new Object[]{0, Suggestions.Search.TYPE_SCREEN_NAME,
query, null, null, 0, null}); query, null, null, 0, null, query});
} }
} }
cursors = new Cursor[3]; cursors = new Cursor[3];
@ -868,8 +872,9 @@ public final class TwidereDataProvider extends ContentProvider implements Consta
new Column(CachedUsers.SCREEN_NAME, Suggestions.SUMMARY).getSQL(), new Column(CachedUsers.SCREEN_NAME, Suggestions.SUMMARY).getSQL(),
new Column(CachedUsers.USER_ID, Suggestions.EXTRA_ID).getSQL(), new Column(CachedUsers.USER_ID, Suggestions.EXTRA_ID).getSQL(),
new Column(CachedUsers.PROFILE_IMAGE_URL, Suggestions.ICON).getSQL(), new Column(CachedUsers.PROFILE_IMAGE_URL, Suggestions.ICON).getSQL(),
new Column(CachedUsers.SCREEN_NAME, Suggestions.VALUE).getSQL(),
}; };
final String[] orderBy = {"score", CachedUsers.LAST_SEEN, CachedUsers.SCREEN_NAME, final String[] orderBy = {CachedUsers.SCORE, CachedUsers.LAST_SEEN, CachedUsers.SCREEN_NAME,
CachedUsers.NAME}; CachedUsers.NAME};
final boolean[] ascending = {false, false, true, true}; final boolean[] ascending = {false, false, true, true};
return query(Uri.withAppendedPath(CachedUsers.CONTENT_URI_WITH_SCORE, accountId), return query(Uri.withAppendedPath(CachedUsers.CONTENT_URI_WITH_SCORE, accountId),
@ -884,6 +889,7 @@ public final class TwidereDataProvider extends ContentProvider implements Consta
new Column("NULL", Suggestions.SUMMARY).getSQL(), new Column("NULL", Suggestions.SUMMARY).getSQL(),
new Column("0", Suggestions.EXTRA_ID).getSQL(), new Column("0", Suggestions.EXTRA_ID).getSQL(),
new Column("NULL", Suggestions.ICON).getSQL(), new Column("NULL", Suggestions.ICON).getSQL(),
new Column(CachedHashtags.NAME, Suggestions.VALUE).getSQL(),
}; };
return query(CachedHashtags.CONTENT_URI, mappedProjection, where.getSQL(), return query(CachedHashtags.CONTENT_URI, mappedProjection, where.getSQL(),
whereArgs, null); whereArgs, null);

View File

@ -29,8 +29,9 @@ import org.mariotaku.twidere.adapter.AccountsAdapter;
import org.mariotaku.twidere.adapter.AccountsSpinnerAdapter; import org.mariotaku.twidere.adapter.AccountsSpinnerAdapter;
import org.mariotaku.twidere.adapter.BaseArrayAdapter; import org.mariotaku.twidere.adapter.BaseArrayAdapter;
import org.mariotaku.twidere.adapter.BaseRecyclerViewAdapter; import org.mariotaku.twidere.adapter.BaseRecyclerViewAdapter;
import org.mariotaku.twidere.adapter.ComposeAutoCompleteAdapter;
import org.mariotaku.twidere.adapter.DraftsAdapter; import org.mariotaku.twidere.adapter.DraftsAdapter;
import org.mariotaku.twidere.adapter.UserHashtagAutoCompleteAdapter; import org.mariotaku.twidere.adapter.UserAutoCompleteAdapter;
import org.mariotaku.twidere.fragment.BaseDialogFragment; import org.mariotaku.twidere.fragment.BaseDialogFragment;
import org.mariotaku.twidere.fragment.BaseFiltersFragment; import org.mariotaku.twidere.fragment.BaseFiltersFragment;
import org.mariotaku.twidere.fragment.BaseFragment; import org.mariotaku.twidere.fragment.BaseFragment;
@ -86,7 +87,9 @@ public interface GeneralComponent {
void inject(AccountsAdapter object); void inject(AccountsAdapter object);
void inject(UserHashtagAutoCompleteAdapter object); void inject(ComposeAutoCompleteAdapter object);
void inject(UserAutoCompleteAdapter object);
void inject(AccountsSpinnerAdapter object); void inject(AccountsSpinnerAdapter object);

View File

@ -20,17 +20,25 @@
package org.mariotaku.twidere.view; package org.mariotaku.twidere.view;
import android.content.Context; import android.content.Context;
import android.content.res.ColorStateList;
import android.support.annotation.NonNull;
import android.support.v7.widget.AppCompatMultiAutoCompleteTextView; import android.support.v7.widget.AppCompatMultiAutoCompleteTextView;
import android.text.InputType;
import android.text.Selection;
import android.text.method.ArrowKeyMovementMethod; import android.text.method.ArrowKeyMovementMethod;
import android.text.method.MovementMethod;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.View;
import android.widget.AdapterView;
import org.mariotaku.twidere.R; import org.mariotaku.twidere.R;
import org.mariotaku.twidere.adapter.UserHashtagAutoCompleteAdapter; import org.mariotaku.twidere.adapter.ComposeAutoCompleteAdapter;
import org.mariotaku.twidere.util.widget.StatusTextTokenizer; import org.mariotaku.twidere.util.widget.StatusTextTokenizer;
import org.mariotaku.twidere.view.iface.IThemeBackgroundTintView;
public class ComposeEditText extends AppCompatMultiAutoCompleteTextView { public class ComposeEditText extends AppCompatMultiAutoCompleteTextView implements IThemeBackgroundTintView {
private UserHashtagAutoCompleteAdapter mAdapter; private ComposeAutoCompleteAdapter mAdapter;
private long mAccountId; private long mAccountId;
public ComposeEditText(final Context context) { public ComposeEditText(final Context context) {
@ -44,14 +52,36 @@ public class ComposeEditText extends AppCompatMultiAutoCompleteTextView {
public ComposeEditText(final Context context, final AttributeSet attrs, final int defStyle) { public ComposeEditText(final Context context, final AttributeSet attrs, final int defStyle) {
super(context, attrs, defStyle); super(context, attrs, defStyle);
setTokenizer(new StatusTextTokenizer()); setTokenizer(new StatusTextTokenizer());
setMovementMethod(ArrowKeyMovementMethod.getInstance()); setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
removeIMESuggestions();
}
});
// HACK: remove AUTO_COMPLETE flag to force IME show auto completion
setRawInputType(getInputType() & ~InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE);
}
@Override
public void setBackgroundTintColor(@NonNull ColorStateList color) {
setSupportBackgroundTintList(color);
}
public void setAccountId(long accountId) {
mAccountId = accountId;
updateAccountId();
}
@Override
protected MovementMethod getDefaultMovementMethod() {
return ArrowKeyMovementMethod.getInstance();
} }
@Override @Override
protected void onAttachedToWindow() { protected void onAttachedToWindow() {
super.onAttachedToWindow(); super.onAttachedToWindow();
if (!isInEditMode() && mAdapter == null) { if (!isInEditMode() && mAdapter == null) {
mAdapter = new UserHashtagAutoCompleteAdapter(getContext()); mAdapter = new ComposeAutoCompleteAdapter(getContext());
} }
setAdapter(mAdapter); setAdapter(mAdapter);
updateAccountId(); updateAccountId();
@ -66,13 +96,14 @@ public class ComposeEditText extends AppCompatMultiAutoCompleteTextView {
} }
} }
public void setAccountId(long accountId) {
mAccountId = accountId;
updateAccountId();
}
private void updateAccountId() { private void updateAccountId() {
if (mAdapter == null) return; if (mAdapter == null) return;
mAdapter.setAccountId(mAccountId); mAdapter.setAccountId(mAccountId);
} }
private void removeIMESuggestions() {
final int selectionEnd = getSelectionEnd(), selectionStart = getSelectionStart();
Selection.removeSelection(getText());
setSelection(selectionStart, selectionEnd);
}
} }

View File

@ -1,101 +0,0 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2014 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.view;
import android.content.Context;
import android.content.res.ColorStateList;
import android.support.annotation.NonNull;
import android.support.v7.widget.AppCompatMultiAutoCompleteTextView;
import android.text.InputType;
import android.text.method.ArrowKeyMovementMethod;
import android.util.AttributeSet;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.adapter.UserHashtagAutoCompleteAdapter;
import org.mariotaku.twidere.util.widget.StatusTextTokenizer;
import org.mariotaku.twidere.view.iface.IThemeBackgroundTintView;
public class ComposeMaterialEditText extends AppCompatMultiAutoCompleteTextView implements IThemeBackgroundTintView {
private UserHashtagAutoCompleteAdapter mAdapter;
private long mAccountId;
public ComposeMaterialEditText(final Context context) {
this(context, null);
}
public ComposeMaterialEditText(final Context context, final AttributeSet attrs) {
this(context, attrs, R.attr.autoCompleteTextViewStyle);
}
public ComposeMaterialEditText(final Context context, final AttributeSet attrs, final int defStyle) {
super(context, attrs, defStyle);
setTokenizer(new StatusTextTokenizer());
setMovementMethod(ArrowKeyMovementMethod.getInstance());
setupComposeInputType();
}
private void setupComposeInputType() {
int rawInputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES;
rawInputType |= InputType.TYPE_TEXT_FLAG_MULTI_LINE;
setRawInputType(rawInputType);
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (!isInEditMode() && mAdapter == null) {
mAdapter = new UserHashtagAutoCompleteAdapter(getContext());
}
setAdapter(mAdapter);
updateAccountId();
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if (mAdapter != null) {
mAdapter.closeCursor();
mAdapter = null;
}
}
@Override
public void setBackgroundTintColor(@NonNull ColorStateList color) {
setSupportBackgroundTintList(color);
}
public void setAccountId(long accountId) {
mAccountId = accountId;
updateAccountId();
}
private void updateAccountId() {
if (mAdapter == null) return;
mAdapter.setAccountId(mAccountId);
}
@Override
protected void replaceText(final CharSequence text) {
super.replaceText(text);
append(" ");
}
}

View File

@ -44,7 +44,7 @@
android:alpha="0.2" android:alpha="0.2"
android:numColumns="@integer/grid_column_image_preview" android:numColumns="@integer/grid_column_image_preview"
android:stretchMode="columnWidth" android:stretchMode="columnWidth"
tools:listitem="@layout/grid_item_image_preview" /> tools:listitem="@layout/gallery_item_image_preview" />
<LinearLayout <LinearLayout
android:id="@+id/edit_text_container" android:id="@+id/edit_text_container"
@ -58,13 +58,13 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:background="@null" android:background="@null"
android:completionThreshold="1"
android:gravity="top" android:gravity="top"
android:hint="@string/status_hint" android:hint="@string/status_hint"
android:inputType="text|textLongMessage|textAutoComplete|textMultiLine" android:inputType="textMultiLine|textShortMessage"
android:minLines="6" android:minLines="6"
android:padding="@dimen/element_spacing_normal" android:padding="@dimen/element_spacing_normal"
android:scrollbars="vertical" /> android:scrollbars="vertical"
android:singleLine="false" />
<LinearLayout <LinearLayout
android:id="@+id/location_container" android:id="@+id/location_container"

View File

@ -37,7 +37,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<org.mariotaku.twidere.view.ComposeMaterialEditText <org.mariotaku.twidere.view.ComposeEditText
android:id="@+id/edit_comment" android:id="@+id/edit_comment"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -49,7 +49,7 @@
android:visibility="visible"> android:visibility="visible">
<requestFocus /> <requestFocus />
</org.mariotaku.twidere.view.ComposeMaterialEditText> </org.mariotaku.twidere.view.ComposeEditText>
<org.mariotaku.twidere.view.StatusTextCountView <org.mariotaku.twidere.view.StatusTextCountView
android:id="@+id/comment_text_count" android:id="@+id/comment_text_count"