Java tutorial
/* * Copyright 2012 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.conferenceengineer.android.iosched.ui; import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.support.v4.app.ListFragment; import android.support.v4.app.LoaderManager; import android.support.v4.app.ShareCompat; import android.support.v4.content.AsyncTaskLoader; import android.support.v4.content.Loader; import android.text.TextUtils; import android.text.format.DateUtils; import android.view.*; import android.widget.*; import com.conferenceengineer.android.iosched.conference686.R; import com.conferenceengineer.android.iosched.io.model.TweetResponse; import com.conferenceengineer.android.iosched.io.model.Tweets; import com.conferenceengineer.android.iosched.io.model.TweetsResponse; import com.conferenceengineer.android.iosched.Config; import com.conferenceengineer.android.iosched.util.ImageLoader; import com.conferenceengineer.android.iosched.util.UIUtils; import java.io.IOException; import java.util.ArrayList; import java.util.List; import static com.conferenceengineer.android.iosched.util.LogUtils.LOGD; import static com.conferenceengineer.android.iosched.util.LogUtils.makeLogTag; /** * A fragment that renders Google+ search results for a given query, provided as the * {@link SocialStreamFragment#EXTRA_QUERY} extra in the fragment arguments. If no * search query is provided, the conference hashtag is used as the default query. */ public class SocialStreamFragment extends ListFragment implements AbsListView.OnScrollListener, LoaderManager.LoaderCallbacks<List<TweetResponse>> { private static final String TAG = makeLogTag(SocialStreamFragment.class); public static final String EXTRA_QUERY = "com.conferenceengineer.android.iosched.extra.QUERY"; public static final String EXTRA_ADD_VERTICAL_MARGINS = "com.conferenceengineer.android.iosched.extra.ADD_VERTICAL_MARGINS"; private static final String STATE_POSITION = "position"; private static final String STATE_TOP = "top"; private static final int STREAM_LOADER_ID = 0; private String mSearchString; private ImageLoader mImageLoader; private List<TweetResponse> mStream = new ArrayList<TweetResponse>(); private StreamAdapter mStreamAdapter = new StreamAdapter(); private int mListViewStatePosition; private int mListViewStateTop; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setHasOptionsMenu(true); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { if (savedInstanceState != null) { mListViewStatePosition = savedInstanceState.getInt(STATE_POSITION, -1); mListViewStateTop = savedInstanceState.getInt(STATE_TOP, 0); } else { mListViewStatePosition = -1; mListViewStateTop = 0; } return super.onCreateView(inflater, container, savedInstanceState); } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); setEmptyText(getString(R.string.empty_social_stream)); // In support library r8, calling initLoader for a fragment in a FragmentPagerAdapter // in the fragment's onCreate may cause the same LoaderManager to be dealt to multiple // fragments because their mIndex is -1 (haven't been added to the activity yet). Thus, // we do this in onActivityCreated. getLoaderManager().initLoader(STREAM_LOADER_ID, null, this); if (getActivity() instanceof ImageLoader.ImageLoaderProvider) { mImageLoader = ((ImageLoader.ImageLoaderProvider) getActivity()).getImageLoaderInstance(); } } @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); final ListView lv = getListView(); /*if (!UIUtils.isTablet(getActivity())) { view.setBackgroundColor(getResources().getColor(R.color.stream_spacer_color)); }*/ // Add some padding if the parent layout is too wide to avoid stretching the items too much // emulating the activity_letterboxed_when_large layout behaviour if (getArguments() != null && getArguments().getBoolean(EXTRA_ADD_VERTICAL_MARGINS, false)) { int verticalMargin = getResources().getDimensionPixelSize(R.dimen.social_stream_padding_vertical); if (verticalMargin > 0) { lv.setClipToPadding(false); lv.setPadding(0, verticalMargin, 0, verticalMargin); } } lv.setOnScrollListener(this); lv.setDrawSelectorOnTop(true); lv.setDivider(getResources().getDrawable(R.drawable.stream_list_divider)); lv.setDividerHeight(getResources().getDimensionPixelSize(R.dimen.stream_divider_height)); //view.setBackgroundColor(getResources().getColor(R.color.stream_list_bg_color)); setListAdapter(mStreamAdapter); } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { inflater.inflate(R.menu.social_stream, menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.menu_compose: Intent intent = ShareCompat.IntentBuilder.from(getActivity()).setType("text/plain") .setText(Config.CONFERENCE_HASHTAG + "\n\n").getIntent(); UIUtils.preferPackageForIntent(getActivity(), intent, UIUtils.TWITTER_PACKAGE_NAME); startActivity(intent); LOGD("Tracker", "Home Screen Dashboard: Click, post to g+"); return true; } return false; } @Override public void onPrepareOptionsMenu(Menu menu) { } @Override public void onDestroyOptionsMenu() { } @Override public void onSaveInstanceState(Bundle outState) { if (isAdded()) { View v = getListView().getChildAt(0); int top = (v == null) ? 0 : v.getTop(); outState.putInt(STATE_POSITION, getListView().getFirstVisiblePosition()); outState.putInt(STATE_TOP, top); } super.onSaveInstanceState(outState); } public void refresh(String newQuery) { mSearchString = newQuery; refresh(true); } public void refresh() { refresh(false); } public void refresh(boolean forceRefresh) { if (isStreamLoading() && !forceRefresh) { return; } // clear current items mStream.clear(); mStreamAdapter.notifyDataSetInvalidated(); if (isAdded()) { Loader loader = getLoaderManager().getLoader(STREAM_LOADER_ID); ((StreamLoader) loader).init(); } loadMoreResults(); } public void loadMoreResults() { if (isAdded()) { Loader loader = getLoaderManager().getLoader(STREAM_LOADER_ID); if (loader != null) { loader.forceLoad(); } } } @Override public void onListItemClick(ListView l, View v, int position, long id) { TweetResponse activity = mStream.get(position); Intent postDetailIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(activity.getUrl())); postDetailIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); UIUtils.preferPackageForIntent(getActivity(), postDetailIntent, UIUtils.TWITTER_PACKAGE_NAME); startActivity(postDetailIntent); } @Override public void onScrollStateChanged(AbsListView listView, int scrollState) { // Pause disk cache access to ensure smoother scrolling if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_FLING) { mImageLoader.stopProcessingQueue(); } else { mImageLoader.startProcessingQueue(); } } @Override public void onScroll(AbsListView absListView, int firstVisibleItem, int visibleItemCount, int totalItemCount) { if (!isStreamLoading() && visibleItemCount != 0 && firstVisibleItem + visibleItemCount >= totalItemCount - 1 && streamHasMoreResults()) { loadMoreResults(); } } @Override public Loader<List<TweetResponse>> onCreateLoader(int id, Bundle args) { return new StreamLoader(getActivity()); } @Override public void onLoadFinished(Loader<List<TweetResponse>> listLoader, List<TweetResponse> activities) { if (activities != null) { mStream = activities; } mStreamAdapter.notifyDataSetChanged(); if (mListViewStatePosition != -1 && isAdded()) { getListView().setSelectionFromTop(mListViewStatePosition, mListViewStateTop); mListViewStatePosition = -1; } } @Override public void onLoaderReset(Loader<List<TweetResponse>> listLoader) { } private boolean isStreamLoading() { if (isAdded()) { final Loader loader = getLoaderManager().getLoader(STREAM_LOADER_ID); if (loader != null) { return ((StreamLoader) loader).isLoading(); } } return true; } private boolean streamHasMoreResults() { if (isAdded()) { final Loader loader = getLoaderManager().getLoader(STREAM_LOADER_ID); if (loader != null) { return ((StreamLoader) loader).hasMoreResults(); } } return false; } private boolean streamHasError() { if (isAdded()) { final Loader loader = getLoaderManager().getLoader(STREAM_LOADER_ID); if (loader != null) { return ((StreamLoader) loader).hasError(); } } return false; } private static class StreamLoader extends AsyncTaskLoader<List<TweetResponse>> { List<TweetResponse> mActivities; private String mNextPageToken; private boolean mIsLoading; private boolean mHasError; public StreamLoader(Context context) { super(context); init(); } private void init() { mHasError = false; mNextPageToken = null; mIsLoading = true; mActivities = null; } @Override public List<TweetResponse> loadInBackground() { mIsLoading = true; try { TweetsResponse tweets = new Tweets().list(Config.EVENT_ID).execute(); mHasError = false; return tweets.getTweets(); } catch (IOException e) { e.printStackTrace(); mHasError = true; } return null; } @Override public void deliverResult(List<TweetResponse> activities) { mIsLoading = false; if (activities != null) { if (mActivities == null) { mActivities = activities; } else { mActivities.addAll(activities); } } if (isStarted()) { // Need to return new ArrayList for some reason or onLoadFinished() is not called super.deliverResult(mActivities == null ? null : new ArrayList<TweetResponse>(mActivities)); } } @Override protected void onStartLoading() { if (mActivities != null) { // If we already have results and are starting up, deliver what we already have. deliverResult(null); } else { forceLoad(); } } @Override protected void onStopLoading() { mIsLoading = false; cancelLoad(); } @Override protected void onReset() { super.onReset(); onStopLoading(); mActivities = null; } public boolean isLoading() { return mIsLoading; } public boolean hasMoreResults() { return mNextPageToken != null; } public boolean hasError() { return mHasError; } public void setSearchString(String searchString) { } public void refresh() { reset(); startLoading(); } } private class StreamAdapter extends BaseAdapter { private static final int VIEW_TYPE_ACTIVITY = 0; private static final int VIEW_TYPE_LOADING = 1; @Override public boolean areAllItemsEnabled() { return false; } @Override public boolean isEnabled(int position) { return getItemViewType(position) == VIEW_TYPE_ACTIVITY; } @Override public int getViewTypeCount() { return 2; } @Override public boolean hasStableIds() { return true; } @Override public int getCount() { return mStream.size() + ( // show the status list row if... ((isStreamLoading() && mStream.size() == 0) // ...this is the first load || streamHasMoreResults() // ...or there's another page || streamHasError()) // ...or there's an error ? 1 : 0); } @Override public int getItemViewType(int position) { return (position >= mStream.size()) ? VIEW_TYPE_LOADING : VIEW_TYPE_ACTIVITY; } @Override public Object getItem(int position) { return (getItemViewType(position) == VIEW_TYPE_ACTIVITY) ? mStream.get(position) : null; } @Override public long getItemId(int position) { // TODO: better unique ID heuristic /* return (getItemViewType(position) == VIEW_TYPE_ACTIVITY) ? mStream.get(position).getId().hashCode() : -1; */ return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { if (getItemViewType(position) == VIEW_TYPE_LOADING) { if (convertView == null) { convertView = getLayoutInflater(null).inflate(R.layout.list_item_stream_status, parent, false); } if (streamHasError()) { convertView.findViewById(android.R.id.progress).setVisibility(View.GONE); ((TextView) convertView.findViewById(android.R.id.text1)).setText(R.string.stream_error); } else { convertView.findViewById(android.R.id.progress).setVisibility(View.VISIBLE); ((TextView) convertView.findViewById(android.R.id.text1)).setText(R.string.loading); } return convertView; } else { TweetResponse tweet = (TweetResponse) getItem(position); if (convertView == null) { convertView = getLayoutInflater(null).inflate(R.layout.list_item_stream_activity, parent, false); } TextView textView = (TextView) convertView.findViewById(R.id.stream_user_name); textView.setText(tweet.getName()); textView.setCompoundDrawablesWithIntrinsicBounds( tweet.getVerified() ? R.drawable.ic_verified_badge : 0, 0, 0, 0); ((TextView) convertView.findViewById(R.id.stream_user_handle)).setText("@" + tweet.getScreenName()); ((TextView) convertView.findViewById(R.id.stream_timestamp)) .setText(DateUtils.getRelativeTimeSpanString(tweet.getCreatedAt())); textView = (TextView) convertView.findViewById(R.id.stream_content); textView.setText(tweet.getText()); textView = (TextView) convertView.findViewById(R.id.stream_retweets); final Long retweetCount = tweet.getRetweetCount(); if (retweetCount > 0) { textView.setVisibility(View.VISIBLE); textView.setText(getString(R.string.stream_retweets, retweetCount)); } else { textView.setVisibility(View.GONE); } if (!TextUtils.isEmpty(tweet.getProfileImageURL()) && mImageLoader != null) { mImageLoader.get(tweet.getProfileImageURL(), (ImageView) convertView.findViewById(R.id.stream_user_pic)); } return convertView; } } } }