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 SCORE = "score";
String[] COLUMNS = {_ID, USER_ID, CREATED_AT, NAME, SCREEN_NAME,
DESCRIPTION_PLAIN, LOCATION, URL, PROFILE_IMAGE_URL, PROFILE_BANNER_URL, IS_PROTECTED,
IS_VERIFIED, IS_FOLLOWING, FOLLOWERS_COUNT, FRIENDS_COUNT, STATUSES_COUNT, FAVORITES_COUNT,
@ -293,6 +295,7 @@ public interface TwidereDataStore {
String ICON = "icon";
String EXTRA_ID = "extra_id";
String EXTRA = "extra";
String VALUE = "value";
String TABLE_NAME = "suggestions";
@ -300,9 +303,9 @@ public interface TwidereDataStore {
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,
TYPE_INT, TYPE_TEXT};
TYPE_INT, TYPE_TEXT, TYPE_TEXT};
interface AutoComplete extends Suggestions {

View File

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

View File

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

View File

@ -32,7 +32,7 @@ import android.view.View;
import android.widget.AutoCompleteTextView;
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.ParseUtils;
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";
private AutoCompleteTextView mEditText;
private UserHashtagAutoCompleteAdapter mUserAutoCompleteAdapter;
private ComposeAutoCompleteAdapter mUserAutoCompleteAdapter;
@Override
public void onClick(final DialogInterface dialog, final int which) {
@ -71,7 +71,7 @@ public class AddUserListMemberDialogFragment extends BaseSupportDialogFragment i
if (savedInstanceState != null) {
mEditText.setText(savedInstanceState.getCharSequence(EXTRA_TEXT));
}
mUserAutoCompleteAdapter = new UserHashtagAutoCompleteAdapter(wrapped);
mUserAutoCompleteAdapter = new ComposeAutoCompleteAdapter(wrapped);
final Bundle args = getArguments();
mUserAutoCompleteAdapter.setAccountId(args.getLong(EXTRA_ACCOUNT_ID));
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.ThemeUtils;
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.holder.StatusViewHolder;
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.item_content).setFocusable(false);
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);
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.ICON).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), "?||'%'", "^");
@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.ICON).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);
@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.PROFILE_IMAGE_URL, Suggestions.Search.ICON).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;
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.in(new Column(CachedUsers.USER_ID), new RawItemArray(nicknameIds)));
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 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);
if (m.matches()) {
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];
@ -868,8 +872,9 @@ public final class TwidereDataProvider extends ContentProvider implements Consta
new Column(CachedUsers.SCREEN_NAME, Suggestions.SUMMARY).getSQL(),
new Column(CachedUsers.USER_ID, Suggestions.EXTRA_ID).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};
final boolean[] ascending = {false, false, true, true};
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("0", Suggestions.EXTRA_ID).getSQL(),
new Column("NULL", Suggestions.ICON).getSQL(),
new Column(CachedHashtags.NAME, Suggestions.VALUE).getSQL(),
};
return query(CachedHashtags.CONTENT_URI, mappedProjection, where.getSQL(),
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.BaseArrayAdapter;
import org.mariotaku.twidere.adapter.BaseRecyclerViewAdapter;
import org.mariotaku.twidere.adapter.ComposeAutoCompleteAdapter;
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.BaseFiltersFragment;
import org.mariotaku.twidere.fragment.BaseFragment;
@ -86,7 +87,9 @@ public interface GeneralComponent {
void inject(AccountsAdapter object);
void inject(UserHashtagAutoCompleteAdapter object);
void inject(ComposeAutoCompleteAdapter object);
void inject(UserAutoCompleteAdapter object);
void inject(AccountsSpinnerAdapter object);

View File

@ -20,17 +20,25 @@
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.Selection;
import android.text.method.ArrowKeyMovementMethod;
import android.text.method.MovementMethod;
import android.util.AttributeSet;
import android.view.View;
import android.widget.AdapterView;
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.view.iface.IThemeBackgroundTintView;
public class ComposeEditText extends AppCompatMultiAutoCompleteTextView {
public class ComposeEditText extends AppCompatMultiAutoCompleteTextView implements IThemeBackgroundTintView {
private UserHashtagAutoCompleteAdapter mAdapter;
private ComposeAutoCompleteAdapter mAdapter;
private long mAccountId;
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) {
super(context, attrs, defStyle);
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
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (!isInEditMode() && mAdapter == null) {
mAdapter = new UserHashtagAutoCompleteAdapter(getContext());
mAdapter = new ComposeAutoCompleteAdapter(getContext());
}
setAdapter(mAdapter);
updateAccountId();
@ -66,13 +96,14 @@ public class ComposeEditText extends AppCompatMultiAutoCompleteTextView {
}
}
public void setAccountId(long accountId) {
mAccountId = accountId;
updateAccountId();
}
private void updateAccountId() {
if (mAdapter == null) return;
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:numColumns="@integer/grid_column_image_preview"
android:stretchMode="columnWidth"
tools:listitem="@layout/grid_item_image_preview" />
tools:listitem="@layout/gallery_item_image_preview" />
<LinearLayout
android:id="@+id/edit_text_container"
@ -58,13 +58,13 @@
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@null"
android:completionThreshold="1"
android:gravity="top"
android:hint="@string/status_hint"
android:inputType="text|textLongMessage|textAutoComplete|textMultiLine"
android:inputType="textMultiLine|textShortMessage"
android:minLines="6"
android:padding="@dimen/element_spacing_normal"
android:scrollbars="vertical" />
android:scrollbars="vertical"
android:singleLine="false" />
<LinearLayout
android:id="@+id/location_container"

View File

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