Java tutorial
/* * Tweetings - Twitter client for Android * * Copyright (C) 2012-2013 RBD Solutions Limited <apps@tweetings.net> * Copyright (C) 2012 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 com.dwdesign.tweetings.fragment; import static com.dwdesign.tweetings.util.Utils.buildQueryUri; import static com.dwdesign.tweetings.util.Utils.getActivatedAccountIds; import static com.dwdesign.tweetings.util.Utils.getQuoteStatus; import static com.dwdesign.tweetings.util.Utils.isMyRetweet; import static com.dwdesign.tweetings.util.Utils.openConversation; import static com.dwdesign.tweetings.util.Utils.openStatus; import static com.dwdesign.tweetings.util.Utils.setMenuForStatus; import static com.dwdesign.tweetings.util.Utils.getDefaultAccountId; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.URLEncoder; import java.util.List; import java.util.Locale; import org.apache.http.HttpResponse; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.DefaultHttpClient; import com.dwdesign.popupmenu.PopupMenu; import com.dwdesign.popupmenu.PopupMenu.OnMenuItemClickListener; import com.dwdesign.tweetings.R; import com.dwdesign.tweetings.app.TweetingsApplication; import com.dwdesign.tweetings.activity.ComposeActivity; import com.dwdesign.tweetings.activity.HomeActivity; import com.dwdesign.tweetings.adapter.CursorStatusesAdapter; import com.dwdesign.tweetings.model.Panes; import com.dwdesign.tweetings.model.ParcelableStatus; import com.dwdesign.tweetings.model.StatusViewHolder; import com.dwdesign.tweetings.provider.TweetStore.Filters; import com.dwdesign.tweetings.provider.TweetStore.Statuses; import com.dwdesign.tweetings.util.AsyncTaskManager; import com.dwdesign.tweetings.util.NoDuplicatesLinkedList; import com.dwdesign.tweetings.util.ServiceInterface; import com.dwdesign.tweetings.util.StatusesAdapterInterface; import com.dwdesign.tweetings.util.ClipboardUtils; import android.content.BroadcastReceiver; import android.app.AlertDialog; import android.app.Dialog; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.graphics.drawable.Drawable; import android.graphics.Color; import android.graphics.PorterDuff; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.StrictMode; import android.os.SystemClock; import android.os.StrictMode.ThreadPolicy; import android.support.v4.app.LoaderManager.LoaderCallbacks; import android.support.v4.content.Loader; import android.text.Html; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.AbsListView; import android.widget.Toast; import android.widget.AbsListView.OnScrollListener; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.AdapterView.OnItemLongClickListener; import android.widget.ListView; import com.handmark.pulltorefresh.library.PullToRefreshBase.Mode; import com.twitter.Extractor; import android.view.Menu; abstract class BaseStatusesListFragment<Data> extends PullToRefreshListFragment implements LoaderCallbacks<Data>, OnScrollListener, OnItemClickListener, OnItemLongClickListener, OnMenuItemClickListener, Panes.Left { private static final long TICKER_DURATION = 5000L; private ServiceInterface mService; private TweetingsApplication mApplication; protected SharedPreferences mPreferences; private AsyncTaskManager mAsyncTaskManager; private Handler mHandler; private Runnable mTicker; protected ListView mListView; private StatusesAdapterInterface mAdapter; private PopupMenu mPopupMenu; protected Data mData; private ParcelableStatus mSelectedStatus; private boolean mLoadMoreAutomatically; protected long gapStatusId = -1; private volatile boolean mBusy, mTickerStopped, mReachedBottom, mActivityFirstCreated, mNotReachedBottomBefore = true; private final BroadcastReceiver mStateReceiver = new BroadcastReceiver() { @Override public void onReceive(final Context context, final Intent intent) { final String action = intent.getAction(); if (BROADCAST_MULTI_SELECT_STATE_CHANGED.equals(action)) { mAdapter.setMultiSelectEnabled(mApplication.isMultiSelectActive()); } else if (BROADCAST_MULTI_SELECT_ITEM_CHANGED.equals(action)) { mAdapter.notifyDataSetChanged(); } } }; protected String syncPositionReceived; protected String syncPosition; protected String scrollStatusId; public AsyncTaskManager getAsyncTaskManager() { return mAsyncTaskManager; } public final Data getData() { return mData; } public abstract long[] getLastStatusIds(); public abstract long[] getNewestStatusIds(); public abstract void scrollToStatusId(long statusId); @Override public abstract StatusesAdapterInterface getListAdapter(); public ParcelableStatus getSelectedStatus() { return mSelectedStatus; } public SharedPreferences getSharedPreferences() { return mPreferences; } public abstract int getStatuses(long[] account_ids, long[] max_ids, long[] since_ids); public boolean isActivityFirstCreated() { return mActivityFirstCreated; } @Override public void onActivityCreated(final Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); mApplication = getApplication(); mAsyncTaskManager = getAsyncTaskManager(); mPreferences = getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE); mService = getServiceInterface(); mAdapter = getListAdapter(); setListAdapter(mAdapter); mListView = getListView(); mListView.setOnScrollListener(this); mListView.setOnItemClickListener(this); mListView.setOnItemLongClickListener(this); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { mListView.setCacheColorHint(0); } setMode(Mode.BOTH); getLoaderManager().initLoader(0, getArguments(), this); setListShown(false); } @Override public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); mActivityFirstCreated = true; // Tell the framework to try to keep this fragment around // during a configuration change. setRetainInstance(true); } @Override public abstract Loader<Data> onCreateLoader(int id, Bundle args); @Override public void onItemClick(final AdapterView<?> adapter, final View view, final int position, final long id) { mSelectedStatus = null; final Object tag = view.getTag(); if (tag instanceof StatusViewHolder) { final boolean click_to_open_menu = mPreferences.getBoolean(PREFERENCE_KEY_CLICK_TO_OPEN_MENU, false); final ParcelableStatus status = mSelectedStatus = getListAdapter().findStatus(id); if (status == null) return; final StatusViewHolder holder = (StatusViewHolder) tag; if (holder.show_as_gap) { try { if (position + 2 < mAdapter.getCount()) { final long itemId = mAdapter.findItemIdByPosition(position + 1); gapStatusId = itemId; } } catch (Exception e) { e.printStackTrace(); } if (gapStatusId > 0) { getStatuses(new long[] { status.account_id }, new long[] { status.status_id }, new long[] { gapStatusId }); } else { getStatuses(new long[] { status.account_id }, new long[] { status.status_id }, null); } } else { gapStatusId = -1; if (mApplication.isMultiSelectActive()) { final NoDuplicatesLinkedList<Object> list = mApplication.getSelectedItems(); if (!list.contains(status)) { list.add(status); } else { list.remove(status); } return; } if (click_to_open_menu) { openMenu(view, status); } else { openStatus(getActivity(), status); } } } } @Override public boolean onItemLongClick(final AdapterView<?> adapter, final View view, final int position, final long id) { mSelectedStatus = null; final Object tag = view.getTag(); if (tag instanceof StatusViewHolder) { final boolean click_to_open_menu = mPreferences.getBoolean(PREFERENCE_KEY_CLICK_TO_OPEN_MENU, false); final StatusViewHolder holder = (StatusViewHolder) tag; if (holder.show_as_gap) return false; final ParcelableStatus status = mSelectedStatus = getListAdapter() .getStatus(position - mListView.getHeaderViewsCount()); if (mApplication.isMultiSelectActive()) { final NoDuplicatesLinkedList<Object> list = mApplication.getSelectedItems(); if (!list.contains(mSelectedStatus)) { list.add(mSelectedStatus); } else { list.remove(mSelectedStatus); } return true; } if (click_to_open_menu) { if (!mApplication.isMultiSelectActive()) { mApplication.startMultiSelect(); } final NoDuplicatesLinkedList<Object> list = mApplication.getSelectedItems(); if (!list.contains(status)) { list.add(status); } } else { openMenu(view, status); } return true; } return false; } public void openMenu(View view, ParcelableStatus status) { mPopupMenu = PopupMenu.getInstance(getActivity(), view); mPopupMenu.inflate(R.menu.action_status); final int activated_color = getResources().getColor(R.color.holo_blue_bright); final boolean click_to_open_menu = mPreferences.getBoolean(PREFERENCE_KEY_CLICK_TO_OPEN_MENU, false); final String buffer_authorised = mPreferences.getString(PREFERENCE_KEY_BUFFERAPP_ACCESS_TOKEN, null); final Menu menu = mPopupMenu.getMenu(); final MenuItem itemView = menu.findItem(MENU_VIEW); if (itemView != null) { itemView.setVisible(click_to_open_menu); } final MenuItem direct_retweet = menu.findItem(MENU_RETWEET); if (direct_retweet != null) { final Drawable icon = direct_retweet.getIcon().mutate(); direct_retweet.setVisible( (!status.is_protected && status.account_id != status.user_id) || isMyRetweet(status)); if (isMyRetweet(status)) { icon.setColorFilter(activated_color, PorterDuff.Mode.MULTIPLY); direct_retweet.setTitle(R.string.cancel_retweet); } else { icon.clearColorFilter(); direct_retweet.setTitle(R.string.retweet); } } final MenuItem bufferView = menu.findItem(MENU_ADD_TO_BUFFER); if (bufferView != null) { if (buffer_authorised != null && !buffer_authorised.equals("")) { bufferView.setVisible(true); } else { bufferView.setVisible(false); } } setMenuForStatus(getActivity(), menu, status); mPopupMenu.setOnMenuItemClickListener(this); mPopupMenu.show(); } @Override public void onLoaderReset(final Loader<Data> loader) { mData = null; } @Override public void onLoadFinished(final Loader<Data> loader, final Data data) { mData = data; mAdapter.setShowAccountColor(getActivatedAccountIds(getActivity()).length > 1); setListShown(true); } protected void translate(final ParcelableStatus status) { ThreadPolicy tp = ThreadPolicy.LAX; StrictMode.setThreadPolicy(tp); String language = Locale.getDefault().getLanguage(); String url = "http://api.microsofttranslator.com/v2/Http.svc/Translate?contentType=" + URLEncoder.encode("text/plain") + "&appId=" + BING_TRANSLATE_API_KEY + "&from=&to=" + language + "&text="; url = url + URLEncoder.encode(status.text_plain); try { HttpClient httpClient = new DefaultHttpClient(); HttpResponse response = httpClient.execute(new HttpGet(url)); BufferedReader reader = new BufferedReader( new InputStreamReader(response.getEntity().getContent(), "UTF-8")); String sResponse; StringBuilder s = new StringBuilder(); while ((sResponse = reader.readLine()) != null) { s = s.append(sResponse); } String finalString = s.toString(); finalString = finalString .replace("<string xmlns=\"http://schemas.microsoft.com/2003/10/Serialization/\">", ""); finalString = finalString.replace("</string>", ""); AlertDialog.Builder builder = new AlertDialog.Builder(this.getActivity()); builder.setTitle(getString(R.string.translate)); builder.setMessage(finalString); builder.setCancelable(true); builder.setPositiveButton(getString(R.string.ok), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { dialog.cancel(); } }); AlertDialog alert = builder.create(); alert.show(); //Toast.makeText(getActivity(), finalString, Toast.LENGTH_LONG).show(); } catch (ClientProtocolException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Override public boolean onMenuItemClick(final MenuItem item) { final ParcelableStatus status = mSelectedStatus; if (status == null) return false; final long account_id = getDefaultAccountId(mApplication); switch (item.getItemId()) { case MENU_VIEW: { openStatus(getActivity(), status); break; } case MENU_SHARE: { final Intent intent = new Intent(Intent.ACTION_SEND); intent.setType("text/plain"); intent.putExtra(Intent.EXTRA_TEXT, "@" + status.screen_name + ": " + status.text_plain); startActivity(Intent.createChooser(intent, getString(R.string.share))); break; } case MENU_TRANSLATE: { translate(status); break; } case MENU_RETWEET: { if (isMyRetweet(status)) { mService.destroyStatus(status.account_id, status.status_id); } else { final long id_to_retweet = mSelectedStatus.is_retweet && mSelectedStatus.retweet_id > 0 ? mSelectedStatus.retweet_id : mSelectedStatus.status_id; mService.retweetStatus(status.account_id, id_to_retweet); } break; } case MENU_QUOTE: { final Intent intent = new Intent(INTENT_ACTION_COMPOSE); final Bundle bundle = new Bundle(); bundle.putLong(INTENT_KEY_ACCOUNT_ID, status.account_id); bundle.putBoolean(INTENT_KEY_IS_QUOTE, true); bundle.putString(INTENT_KEY_TEXT, getQuoteStatus(getActivity(), status.screen_name, status.text_plain)); intent.putExtras(bundle); startActivity(intent); break; } case MENU_QUOTE_REPLY: { final Intent intent = new Intent(INTENT_ACTION_COMPOSE); final Bundle bundle = new Bundle(); bundle.putLong(INTENT_KEY_ACCOUNT_ID, status.account_id); bundle.putLong(INTENT_KEY_IN_REPLY_TO_ID, status.status_id); bundle.putString(INTENT_KEY_IN_REPLY_TO_SCREEN_NAME, status.screen_name); bundle.putString(INTENT_KEY_IN_REPLY_TO_NAME, status.name); bundle.putBoolean(INTENT_KEY_IS_QUOTE, true); bundle.putString(INTENT_KEY_TEXT, getQuoteStatus(getActivity(), status.screen_name, status.text_plain)); intent.putExtras(bundle); startActivity(intent); break; } case MENU_ADD_TO_BUFFER: { final Intent intent = new Intent(INTENT_ACTION_COMPOSE); final Bundle bundle = new Bundle(); bundle.putLong(INTENT_KEY_ACCOUNT_ID, status.account_id); bundle.putBoolean(INTENT_KEY_IS_BUFFER, true); bundle.putString(INTENT_KEY_TEXT, getQuoteStatus(getActivity(), status.screen_name, status.text_plain)); intent.putExtras(bundle); startActivity(intent); break; } case MENU_REPLY: { final Intent intent = new Intent(INTENT_ACTION_COMPOSE); final Bundle bundle = new Bundle(); final List<String> mentions = new Extractor().extractMentionedScreennames(status.text_plain); mentions.remove(status.screen_name); mentions.add(0, status.screen_name); bundle.putStringArray(INTENT_KEY_MENTIONS, mentions.toArray(new String[mentions.size()])); bundle.putLong(INTENT_KEY_ACCOUNT_ID, status.account_id); bundle.putLong(INTENT_KEY_IN_REPLY_TO_ID, status.status_id); bundle.putString(INTENT_KEY_IN_REPLY_TO_TWEET, status.text_plain); bundle.putString(INTENT_KEY_IN_REPLY_TO_SCREEN_NAME, status.screen_name); bundle.putString(INTENT_KEY_IN_REPLY_TO_NAME, status.name); intent.putExtras(bundle); startActivity(intent); break; } case MENU_FAV: { if (mSelectedStatus.is_favorite) { mService.destroyFavorite(status.account_id, status.status_id); } else { mService.createFavorite(status.account_id, status.status_id); } break; } case MENU_CONVERSATION: { openConversation(getActivity(), status.account_id, status.status_id); break; } case MENU_DELETE: { mService.destroyStatus(status.account_id, status.status_id); break; } case MENU_EXTENSIONS: { final Intent intent = new Intent(INTENT_ACTION_EXTENSION_OPEN_STATUS); final Bundle extras = new Bundle(); extras.putParcelable(INTENT_KEY_STATUS, status); intent.putExtras(extras); startActivity(Intent.createChooser(intent, getString(R.string.open_with_extensions))); break; } case MENU_MULTI_SELECT: { if (!mApplication.isMultiSelectActive()) { mApplication.startMultiSelect(); } final NoDuplicatesLinkedList<Object> list = mApplication.getSelectedItems(); if (!list.contains(status)) { list.add(status); } break; } case MENU_BLOCK: { mService.createBlock(account_id, status.user_id); break; } case MENU_REPORT_SPAM: { mService.reportSpam(account_id, status.user_id); break; } case MENU_MUTE_USER: { final String screen_name = status.screen_name; final Uri uri = Filters.Users.CONTENT_URI; final ContentValues values = new ContentValues(); final SharedPreferences.Editor editor = getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE).edit(); final ContentResolver resolver = getContentResolver(); values.put(Filters.Users.TEXT, screen_name); resolver.delete(uri, Filters.Users.TEXT + " = '" + screen_name + "'", null); resolver.insert(uri, values); editor.putBoolean(PREFERENCE_KEY_ENABLE_FILTER, true).commit(); Toast.makeText(getActivity(), R.string.user_muted, Toast.LENGTH_SHORT).show(); break; } case MENU_MAKE_GAP: { Uri uri = Statuses.CONTENT_URI; final Uri query_uri = buildQueryUri(uri, false); ContentResolver mResolver = getContentResolver(); final ContentValues values = new ContentValues(); values.put(Statuses.IS_GAP, 1); final StringBuilder where = new StringBuilder(); where.append(Statuses.ACCOUNT_ID + "=" + account_id); where.append(" AND " + Statuses.STATUS_ID + "=" + status.status_id); mResolver.update(query_uri, values, where.toString(), null); getActivity().sendBroadcast(new Intent(BROADCAST_FILTERS_UPDATED).putExtra(INTENT_KEY_SUCCEED, true)); break; } case MENU_COPY: { final CharSequence text = Html.fromHtml(status.text_html); if (ClipboardUtils.setText(getActivity(), text)) { Toast.makeText(getActivity(), R.string.text_copied, Toast.LENGTH_SHORT).show(); } break; } } return true; } public abstract void onPostStart(); @Override public abstract void onPullDownToRefresh(); @Override public void onResume() { super.onResume(); mLoadMoreAutomatically = mPreferences.getBoolean(PREFERENCE_KEY_LOAD_MORE_AUTOMATICALLY, false); final float text_size = mPreferences.getFloat(PREFERENCE_KEY_TEXT_SIZE, PREFERENCE_DEFAULT_TEXT_SIZE); final boolean display_profile_image = mPreferences.getBoolean(PREFERENCE_KEY_DISPLAY_PROFILE_IMAGE, true); final String inline_image_preview_display_option = mPreferences.getString( PREFERENCE_KEY_INLINE_IMAGE_PREVIEW_DISPLAY_OPTION, INLINE_IMAGE_PREVIEW_DISPLAY_OPTION_SMALL); final boolean display_name = mPreferences.getBoolean(PREFERENCE_KEY_DISPLAY_NAME, false); final boolean display_name_both = mPreferences.getBoolean(PREFERENCE_KEY_DISPLAY_NAME_BOTH, true); final boolean show_absolute_time = mPreferences.getBoolean(PREFERENCE_KEY_SHOW_ABSOLUTE_TIME, false); final boolean display_sensitive_contents = mPreferences .getBoolean(PREFERENCE_KEY_DISPLAY_SENSITIVE_CONTENTS, false); final boolean fast_processing = mPreferences.getBoolean(PREFERENCE_KEY_FAST_LIST_PROCESSING, false); final boolean show_links = mPreferences.getBoolean(PREFERENCE_KEY_SHOW_LINKS, true); final boolean show_fast_scroll = mPreferences.getBoolean(PREFERENCE_FAST_SCROLL, false); mAdapter.setMultiSelectEnabled(mApplication.isMultiSelectActive()); mAdapter.setDisplayProfileImage(display_profile_image); mAdapter.setInlineImagePreviewDisplayOption(inline_image_preview_display_option); mAdapter.setShowLinks(show_links); mAdapter.setFastProcessingEnabled(fast_processing); mAdapter.setDisplayName(display_name); mAdapter.setDisplayNameBoth(display_name_both); mAdapter.setDisplaySensitiveContents(display_sensitive_contents); mAdapter.setTextSize(text_size); mAdapter.setShowAbsoluteTime(show_absolute_time); mListView.setFastScrollEnabled(show_fast_scroll); } @Override public void onScroll(final AbsListView view, final int firstVisibleItem, final int visibleItemCount, final int totalItemCount) { final boolean reached = firstVisibleItem + visibleItemCount >= totalItemCount && totalItemCount >= visibleItemCount; if (mReachedBottom != reached) { mReachedBottom = reached; if (mReachedBottom && mNotReachedBottomBefore) { mNotReachedBottomBefore = false; return; } if (mLoadMoreAutomatically && mReachedBottom && getListAdapter().getCount() > visibleItemCount) { if (!isRefreshing()) { onPullUpToRefresh(); } } } } @Override public void onScrollStateChanged(final AbsListView view, final int scrollState) { switch (scrollState) { case SCROLL_STATE_FLING: case SCROLL_STATE_TOUCH_SCROLL: mBusy = true; break; case SCROLL_STATE_IDLE: mBusy = false; break; } } @Override public void onStart() { super.onStart(); mTickerStopped = false; mHandler = new Handler(); mTicker = new Runnable() { @Override public void run() { if (mTickerStopped) return; if (mListView != null && !mBusy) { getListAdapter().notifyDataSetChanged(); } final long now = SystemClock.uptimeMillis(); final long next = now + TICKER_DURATION - now % TICKER_DURATION; mHandler.postAtTime(mTicker, next); } }; mTicker.run(); final IntentFilter filter = new IntentFilter(); filter.addAction(BROADCAST_MULTI_SELECT_STATE_CHANGED); filter.addAction(BROADCAST_MULTI_SELECT_ITEM_CHANGED); registerReceiver(mStateReceiver, filter); onPostStart(); } @Override public void onStop() { mTickerStopped = true; mActivityFirstCreated = false; if (mPopupMenu != null) { mPopupMenu.dismiss(); } unregisterReceiver(mStateReceiver); super.onStop(); } }