Java tutorial
/* * Twittnuker - Twitter client for Android * * Copyright (C) 2013-2015 vanita5 <mail@vanita5.de> * * This program incorporates a modified version of Twidere. * 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 de.vanita5.twittnuker.activity.support; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.database.Cursor; import android.graphics.PorterDuff.Mode; import android.net.Uri; import android.os.Bundle; import android.support.v4.app.LoaderManager.LoaderCallbacks; import android.support.v4.content.AsyncTaskLoader; import android.support.v4.content.Loader; import android.text.Editable; import android.text.TextUtils; import android.text.TextWatcher; import android.view.Gravity; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.AdapterView.OnItemSelectedListener; import android.widget.BaseAdapter; import android.widget.EditText; import android.widget.ImageView; import android.widget.ListView; import android.widget.Spinner; import android.widget.TextView; import android.widget.TextView.OnEditorActionListener; import org.mariotaku.querybuilder.Columns.Column; import org.mariotaku.querybuilder.Expression; import org.mariotaku.querybuilder.OrderBy; import de.vanita5.twittnuker.R; import de.vanita5.twittnuker.activity.support.QuickSearchBarActivity.SuggestionItem; import de.vanita5.twittnuker.adapter.AccountsSpinnerAdapter; import de.vanita5.twittnuker.app.TwittnukerApplication; import de.vanita5.twittnuker.model.ParcelableAccount; import de.vanita5.twittnuker.model.ParcelableUser; import de.vanita5.twittnuker.model.ParcelableUser.CachedIndices; import de.vanita5.twittnuker.provider.TwidereDataStore.CachedUsers; import de.vanita5.twittnuker.provider.TwidereDataStore.SavedSearches; import de.vanita5.twittnuker.provider.TwidereDataStore.SearchHistory; import de.vanita5.twittnuker.util.ImageLoaderWrapper; import de.vanita5.twittnuker.util.ParseUtils; import de.vanita5.twittnuker.util.ThemeUtils; import de.vanita5.twittnuker.util.Utils; import java.util.ArrayList; import java.util.List; public class QuickSearchBarActivity extends BaseSupportActivity implements OnClickListener, OnEditorActionListener, LoaderCallbacks<List<SuggestionItem>>, TextWatcher, OnItemSelectedListener, OnItemClickListener { private Spinner mAccountSpinner; private EditText mSearchQuery; private View mSearchSubmit; private ListView mSuggestionsList; private SuggestionsAdapter mUsersSearchAdapter; @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { final SuggestionItem item = mUsersSearchAdapter.getItem(position); item.onItemClick(this, position); } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { getSupportLoaderManager().restartLoader(0, null, this); } @Override public void afterTextChanged(Editable s) { } @Override public int getThemeResourceId() { return ThemeUtils.getQuickSearchBarThemeResource(this); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_quick_search_bar); final List<ParcelableAccount> accounts = ParcelableAccount.getAccountsList(this, false); final AccountsSpinnerAdapter accountsSpinnerAdapter = new AccountsSpinnerAdapter(this, R.layout.spinner_item_account_icon); accountsSpinnerAdapter.setDropDownViewResource(R.layout.list_item_user); accountsSpinnerAdapter.addAll(accounts); mAccountSpinner.setAdapter(accountsSpinnerAdapter); mAccountSpinner.setOnItemSelectedListener(this); if (savedInstanceState == null) { final Intent intent = getIntent(); final int index = accountsSpinnerAdapter.findItemPosition(intent.getLongExtra(EXTRA_ACCOUNT_ID, -1)); if (index != -1) { mAccountSpinner.setSelection(index); } } mUsersSearchAdapter = new SuggestionsAdapter(this); mSuggestionsList.setAdapter(mUsersSearchAdapter); mSuggestionsList.setOnItemClickListener(this); mSearchSubmit.setOnClickListener(this); mSearchQuery.setOnEditorActionListener(this); mSearchQuery.addTextChangedListener(this); getSupportLoaderManager().initLoader(0, null, this); } @Override protected void onResume() { super.onResume(); updateWindowAttributes(); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.search_submit: { doSearch(); break; } } } @Override public void onSupportContentChanged() { super.onSupportContentChanged(); mAccountSpinner = (Spinner) findViewById(R.id.account_spinner); mSearchQuery = (EditText) findViewById(R.id.search_query); mSearchSubmit = findViewById(R.id.search_submit); mSuggestionsList = (ListView) findViewById(R.id.suggestions_list); } @Override public Loader<List<SuggestionItem>> onCreateLoader(int id, Bundle args) { return new SuggestionsLoader(this, mAccountSpinner.getSelectedItemId(), mSearchQuery.getText().toString()); } @Override public void onLoadFinished(Loader<List<SuggestionItem>> loader, List<SuggestionItem> data) { mUsersSearchAdapter.setData(data); } @Override public void onLoaderReset(Loader<List<SuggestionItem>> loader) { mUsersSearchAdapter.setData(null); } @Override public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { if (event == null) return false; switch (event.getKeyCode()) { case KeyEvent.KEYCODE_ENTER: { doSearch(); return true; } } return false; } @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { getSupportLoaderManager().restartLoader(0, null, this); } @Override public void onNothingSelected(AdapterView<?> parent) { } private void doSearch() { if (isFinishing()) return; final String query = ParseUtils.parseString(mSearchQuery.getText()); if (TextUtils.isEmpty(query)) return; final long accountId = mAccountSpinner.getSelectedItemId(); Utils.openSearch(this, accountId, query); finish(); } private long getAccountId() { return mAccountSpinner.getSelectedItemId(); } private void updateWindowAttributes() { final Window window = getWindow(); final WindowManager.LayoutParams attributes = window.getAttributes(); attributes.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL; window.setAttributes(attributes); } static interface SuggestionItem { void bindView(SuggestionsAdapter adapter, View view, int position); int getItemLayoutResource(); int getItemViewType(); boolean isEnabled(); void onItemClick(QuickSearchBarActivity activity, int position); } static class HeaderItem implements SuggestionItem { static final int ITEM_VIEW_TYPE = 1; @Override public void bindView(SuggestionsAdapter adapter, View view, int position) { } @Override public int getItemViewType() { return ITEM_VIEW_TYPE; } @Override public boolean isEnabled() { return false; } @Override public void onItemClick(QuickSearchBarActivity activity, int position) { } @Override public int getItemLayoutResource() { return 0; } } static class SearchHistoryItem extends BaseClickableItem { static final int ITEM_VIEW_TYPE = 1; private final String mQuery; public SearchHistoryItem(String query) { mQuery = query; } @Override public final int getItemLayoutResource() { return R.layout.list_item_suggestion_search; } @Override public int getItemViewType() { return ITEM_VIEW_TYPE; } @Override public void onItemClick(QuickSearchBarActivity activity, int position) { Utils.openSearch(activity, activity.getAccountId(), mQuery); activity.finish(); } @Override public void bindView(SuggestionsAdapter adapter, View view, int position) { final ImageView icon = (ImageView) view.findViewById(android.R.id.icon); final TextView text1 = (TextView) view.findViewById(android.R.id.text1); text1.setText(mQuery); icon.setImageResource(R.drawable.ic_action_history); icon.setColorFilter(text1.getCurrentTextColor(), Mode.SRC_ATOP); } } static abstract class BaseClickableItem implements SuggestionItem { @Override public final boolean isEnabled() { return true; } } static class SavedSearchItem extends BaseClickableItem { static final int ITEM_VIEW_TYPE = 2; private final String mQuery; public SavedSearchItem(String query) { mQuery = query; } @Override public final int getItemLayoutResource() { return R.layout.list_item_suggestion_search; } @Override public int getItemViewType() { return ITEM_VIEW_TYPE; } @Override public void bindView(SuggestionsAdapter adapter, View view, int position) { final ImageView icon = (ImageView) view.findViewById(android.R.id.icon); final TextView text1 = (TextView) view.findViewById(android.R.id.text1); text1.setText(mQuery); icon.setImageResource(R.drawable.ic_action_save); icon.setColorFilter(text1.getCurrentTextColor(), Mode.SRC_ATOP); } @Override public void onItemClick(QuickSearchBarActivity activity, int position) { Utils.openSearch(activity, activity.getAccountId(), mQuery); activity.finish(); } } static class UserSuggestionItem extends BaseClickableItem { static final int ITEM_VIEW_TYPE = 3; private final ParcelableUser mUser; public UserSuggestionItem(Cursor c, CachedIndices i, long accountId) { mUser = new ParcelableUser(c, i, accountId); } @Override public int getItemViewType() { return ITEM_VIEW_TYPE; } @Override public void onItemClick(QuickSearchBarActivity activity, int position) { Utils.openUserProfile(activity, mUser, null); activity.finish(); } @Override public final int getItemLayoutResource() { return R.layout.list_item_suggestion_user; } @Override public void bindView(SuggestionsAdapter adapter, View view, int position) { final ParcelableUser user = mUser; final ImageLoaderWrapper loader = adapter.getImageLoader(); final ImageView icon = (ImageView) view.findViewById(android.R.id.icon); final TextView text1 = (TextView) view.findViewById(android.R.id.text1); final TextView text2 = (TextView) view.findViewById(android.R.id.text2); text1.setText(user.name); text2.setText("@" + user.screen_name); loader.displayProfileImage(icon, user.profile_image_url); } } public static class SuggestionsAdapter extends BaseAdapter { private final Context mContext; private final LayoutInflater mInflater; private final ImageLoaderWrapper mImageLoader; private List<SuggestionItem> mData; SuggestionsAdapter(Context context) { mContext = context; mInflater = LayoutInflater.from(context); mImageLoader = TwittnukerApplication.getInstance(context).getImageLoaderWrapper(); } public Context getContext() { return mContext; } @Override public int getCount() { if (mData == null) return 0; return mData.size(); } @Override public SuggestionItem getItem(int position) { if (mData == null) return null; return mData.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { final View view; final SuggestionItem item = getItem(position); if (convertView == null) { view = mInflater.inflate(item.getItemLayoutResource(), parent, false); } else { view = convertView; } item.bindView(this, view, position); return view; } public ImageLoaderWrapper getImageLoader() { return mImageLoader; } @Override public boolean isEnabled(int position) { return getItem(position).isEnabled(); } @Override public int getItemViewType(int position) { if (mData == null) return IGNORE_ITEM_VIEW_TYPE; return mData.get(position).getItemViewType(); } @Override public int getViewTypeCount() { return 4; } public void setData(List<SuggestionItem> data) { mData = data; notifyDataSetChanged(); } } public static class SuggestionsLoader extends AsyncTaskLoader<List<SuggestionItem>> { private final long mAccountId; private final String mQuery; public SuggestionsLoader(Context context, long accountId, String query) { super(context); mAccountId = accountId; mQuery = query; } @Override public List<SuggestionItem> loadInBackground() { final boolean emptyQuery = TextUtils.isEmpty(mQuery); final Context context = getContext(); final ContentResolver resolver = context.getContentResolver(); final List<SuggestionItem> result = new ArrayList<>(); final String[] historyProjection = { SearchHistory.QUERY }; final Cursor historyCursor = resolver.query(SearchHistory.CONTENT_URI, historyProjection, null, null, SearchHistory.DEFAULT_SORT_ORDER); for (int i = 0, j = Math.min(emptyQuery ? 3 : 2, historyCursor.getCount()); i < j; i++) { historyCursor.moveToPosition(i); result.add(new SearchHistoryItem(historyCursor.getString(0))); } historyCursor.close(); if (!emptyQuery) { final String queryEscaped = mQuery.replace("_", "^_"); final Expression selection = Expression.or( Expression.likeRaw(new Column(CachedUsers.SCREEN_NAME), "?||'%'", "^"), Expression.likeRaw(new Column(CachedUsers.NAME), "?||'%'", "^")); final String[] selectionArgs = new String[] { queryEscaped, queryEscaped }; final OrderBy orderBy = new OrderBy(CachedUsers.LAST_SEEN + " DESC", "score DESC", CachedUsers.SCREEN_NAME, CachedUsers.NAME); final Uri uri = Uri.withAppendedPath(CachedUsers.CONTENT_URI_WITH_SCORE, String.valueOf(mAccountId)); final Cursor usersCursor = context.getContentResolver().query(uri, CachedUsers.COLUMNS, selection != null ? selection.getSQL() : null, selectionArgs, orderBy.getSQL()); final CachedIndices usersIndices = new CachedIndices(usersCursor); for (int i = 0, j = Math.min(5, usersCursor.getCount()); i < j; i++) { usersCursor.moveToPosition(i); result.add(new UserSuggestionItem(usersCursor, usersIndices, mAccountId)); } usersCursor.close(); } else { final String[] savedSearchesProjection = { SavedSearches.QUERY }; final Expression savedSearchesWhere = Expression.equals(SavedSearches.ACCOUNT_ID, mAccountId); final Cursor savedSearchesCursor = resolver.query(SavedSearches.CONTENT_URI, savedSearchesProjection, savedSearchesWhere.getSQL(), null, SavedSearches.DEFAULT_SORT_ORDER); savedSearchesCursor.moveToFirst(); while (!savedSearchesCursor.isAfterLast()) { result.add(new SavedSearchItem(savedSearchesCursor.getString(0))); savedSearchesCursor.moveToNext(); } savedSearchesCursor.close(); } return result; } @Override protected void onStartLoading() { forceLoad(); } } }