/* * Copyright (C) 2008 Torgny Bjers * * 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.xorcode.andtweet; import org.json.JSONObject; import android.app.SearchManager; import android.content.ContentUris; import android.content.Context; import android.content.Intent; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.provider.SearchRecentSuggestions; import android.text.Editable; import android.text.TextUtils; import android.text.TextWatcher; import android.util.Log; import android.view.ContextMenu; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ContextMenu.ContextMenuInfo; import android.view.inputmethod.InputMethodManager; import android.widget.AbsListView; import android.widget.AdapterView; import android.widget.Button; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.SimpleCursorAdapter; import android.widget.TextView; import android.widget.Toast; import com.xorcode.andtweet.AndTweetService.CommandData; import com.xorcode.andtweet.AndTweetService.CommandEnum; import com.xorcode.andtweet.TwitterUser.CredentialsVerified; import com.xorcode.andtweet.data.AndTweetDatabase; import com.xorcode.andtweet.data.AndTweetPreferences; import com.xorcode.andtweet.data.PagedCursorAdapter; import com.xorcode.andtweet.data.TimelineSearchSuggestionProvider; import com.xorcode.andtweet.data.TweetBinder; import com.xorcode.andtweet.data.AndTweetDatabase.Tweets; import com.xorcode.andtweet.util.SelectionAndArgs; /** * @author torgny.bjers */ public class TweetListActivity extends TimelineActivity { private static final String TAG = TweetListActivity.class.getSimpleName(); // Context menu items public static final int CONTEXT_MENU_ITEM_REPLY = Menu.FIRST + 2; public static final int CONTEXT_MENU_ITEM_FAVORITE = Menu.FIRST + 3; public static final int CONTEXT_MENU_ITEM_DIRECT_MESSAGE = Menu.FIRST + 4; public static final int CONTEXT_MENU_ITEM_UNFOLLOW = Menu.FIRST + 5; public static final int CONTEXT_MENU_ITEM_BLOCK = Menu.FIRST + 6; public static final int CONTEXT_MENU_ITEM_RETWEET = Menu.FIRST + 7; public static final int CONTEXT_MENU_ITEM_PROFILE = Menu.FIRST + 8; public static final int CONTEXT_MENU_ITEM_DESTROY_FAVORITE = Menu.FIRST + 9; public static final int CONTEXT_MENU_ITEM_DESTROY_STATUS = Menu.FIRST + 10; // Views and widgets private Button mSendButton; private EditText mEditText; private TextView mCharsLeftText; // Text limits private int mCurrentChars = 0; private int mLimitChars = 140; private boolean mInitializing = false; /** * Id of the Tweet to which we are replying */ protected long mReplyId = 0; /** * Id of the Tweet that was selected (clicked, or whose context menu item * was selected) TODO: clicked, restore position... */ protected long mCurrentId = 0; // Table columns to use for the tweets data private static final String[] PROJECTION = new String[] { Tweets._ID, Tweets.AUTHOR_ID, Tweets.MESSAGE, Tweets.IN_REPLY_TO_AUTHOR_ID, Tweets.FAVORITED, Tweets.SENT_DATE }; /** * Called when the activity is first created. * * @see android.app.Activity#onCreate(android.os.Bundle) * @param savedInstanceState */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (Log.isLoggable(AndTweetService.APPTAG, Log.VERBOSE)) { Log.v(TAG, "onCreate"); } if (savedInstanceState != null) { if (savedInstanceState.containsKey(AndTweetService.EXTRA_INREPLYTOID)) { mReplyId = savedInstanceState.getLong(AndTweetService.EXTRA_INREPLYTOID); } if (savedInstanceState.containsKey(BUNDLE_KEY_IS_LOADING)) { mIsLoading = savedInstanceState.getBoolean(BUNDLE_KEY_IS_LOADING); } if (savedInstanceState.containsKey(AndTweetService.EXTRA_TWEETID)) { mCurrentId = savedInstanceState.getLong(AndTweetService.EXTRA_TWEETID); } } // Set up views mSendButton = (Button) findViewById(R.id.messageEditSendButton); mEditText = (EditText) findViewById(R.id.edtTweetInput); mCharsLeftText = (TextView) findViewById(R.id.messageEditCharsLeftTextView); // Create list footer for loading messages mListFooter = new LinearLayout(getApplicationContext()); mListFooter.setClickable(false); getListView().addFooterView(mListFooter); LayoutInflater inflater = LayoutInflater.from(getApplicationContext()); View tv = inflater.inflate(R.layout.item_loading, null); mListFooter.addView(tv, new LinearLayout.LayoutParams( LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)); mListFooter.setVisibility(View.INVISIBLE); getListView().setOnScrollListener(this); initUI(); } @Override public boolean onSearchRequested() { Bundle appDataBundle = new Bundle(); appDataBundle.putParcelable("content_uri", AndTweetDatabase.Tweets.SEARCH_URI); startSearch(null, false, appDataBundle, false); return true; } @Override public void onNewIntent(Intent newIntent) { super.onNewIntent(newIntent); // All actions are actually search actions... // So get and process search query here queryListData(newIntent, false, false); } /** * Prepare query to the ContentProvider (to the database) and load List of Tweets with data * @param queryIntent * @param otherThread This method is being accessed from other thread * @param loadOneMorePage load one more page of tweets */ protected void queryListData(Intent queryIntent, boolean otherThread, boolean loadOneMorePage) { // The search query is provided as an "extra" string in the query intent // TODO maybe use mQueryString here... String queryString = queryIntent.getStringExtra(SearchManager.QUERY); Intent intent = getIntent(); if (Log.isLoggable(AndTweetService.APPTAG, Log.VERBOSE)) { Log.v(TAG, "doSearchQuery; queryString=\"" + queryString + "\"; TimelineType=" + mTimelineType); } Uri contentUri = Tweets.CONTENT_URI; SelectionAndArgs sa = new SelectionAndArgs(); String sortOrder = Tweets.DEFAULT_SORT_ORDER; // Id of the last (oldest) tweet to retrieve long lastItemId = -1; if (queryString != null && queryString.length() > 0) { // Record the query string in the recent queries suggestions // provider SearchRecentSuggestions suggestions = new SearchRecentSuggestions(this, TimelineSearchSuggestionProvider.AUTHORITY, TimelineSearchSuggestionProvider.MODE); suggestions.saveRecentQuery(queryString, null); contentUri = Tweets.SEARCH_URI; contentUri = Uri.withAppendedPath(contentUri, Uri.encode(queryString)); } intent.putExtra(SearchManager.QUERY, queryString); if (!contentUri.equals(intent.getData())) { intent.setData(contentUri); } if (sa.nArgs == 0) { // In fact this is needed every time you want to load next page of // tweets. // So we have to duplicate here everything we set in // com.xorcode.andtweet.TimelineActivity.onOptionsItemSelected() sa.clear(); sa.addSelection( Tweets.TWEET_TYPE + " IN (?, ?)", new String[] { String.valueOf(Tweets.TIMELINE_TYPE_FRIENDS), String.valueOf(Tweets.TIMELINE_TYPE_MENTIONS) }); if (mTimelineType == Tweets.TIMELINE_TYPE_FAVORITES) { sa.addSelection(AndTweetDatabase.Tweets.FAVORITED + " = ?", new String[] { "1" }); } if (mTimelineType == Tweets.TIMELINE_TYPE_MENTIONS) { sa.addSelection(Tweets.MESSAGE + " LIKE ?", new String[] { "%@" + TwitterUser.getTwitterUser().getUsername() + "%" }); } } if (!positionRestored) { // We have to ensure that saved position will be // loaded from database into the list lastItemId = getSavedPosition(true); } int nTweets = 0; if (mCursor != null && !mCursor.isClosed()) { if (positionRestored) { // If position is NOT loaded - this cursor is from other // timeline/search // and we shouldn't care how much rows are there. nTweets = mCursor.getCount(); } if (!otherThread) { mCursor.close(); } } if (lastItemId > 0) { if (sa.nArgs == 0) { sa.addSelection( "AndTweetDatabase.Tweets.TWEET_TYPE" + " IN (?, ?)" + ")", new String[] { String.valueOf(Tweets.TIMELINE_TYPE_FRIENDS), String.valueOf(Tweets.TIMELINE_TYPE_MENTIONS) }); } sa.addSelection(Tweets._ID + " >= ?", new String[] { String.valueOf(lastItemId) }); } else { if (loadOneMorePage) { nTweets += PAGE_SIZE; } else if (nTweets < PAGE_SIZE) { nTweets = PAGE_SIZE; } sortOrder += " LIMIT 0," + nTweets; } // This is for testing pruneOldRecords // try { // FriendTimeline fl = new FriendTimeline(TweetListActivity.this, // AndTweetDatabase.Tweets.TIMELINE_TYPE_FRIENDS); // fl.pruneOldRecords(); // // } catch (Exception e) { // e.printStackTrace(); // } mCursor = getContentResolver().query(contentUri, PROJECTION, sa.selection, sa.selectionArgs, sortOrder); if (!otherThread) { createAdapters(); } } @Override protected void onStart() { super.onStart(); if (Log.isLoggable(AndTweetService.APPTAG, Log.VERBOSE)) { Log.v(TAG, "onStart"); } Intent intent = getIntent(); queryListData(intent, false, false); if (hasHardwareKeyboard()) { // TODO: Only if the EditText is visible! mEditText.requestFocus(); } } @Override protected void onResume() { super.onResume(); if (Log.isLoggable(AndTweetService.APPTAG, Log.VERBOSE)) { Log.v(TAG, "onResume"); } if (TwitterUser.getTwitterUser().getCredentialsVerified() == CredentialsVerified.SUCCEEDED) { if (!TwitterUser.getTwitterUser().getSharedPreferences().getBoolean("loadedOnce", false)) { TwitterUser.getTwitterUser().getSharedPreferences().edit().putBoolean("loadedOnce", true).commit(); // One-time "manually" load tweets from the Internet for the new TwitterUser manualReload(); } } mCharsLeftText.setText(String.valueOf(mLimitChars - mEditText.length())); } @Override protected void onStop() { super.onStop(); if (mCursor != null && !mCursor.isClosed()) { mCursor.close(); } //disconnectService(); } @Override protected void onSaveInstanceState(Bundle outState) { outState.putLong(AndTweetService.EXTRA_INREPLYTOID, mReplyId); outState.putBoolean(BUNDLE_KEY_IS_LOADING, mIsLoading); outState.putLong(AndTweetService.EXTRA_TWEETID, mCurrentId); super.onSaveInstanceState(outState); } @Override public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) { super.onCreateContextMenu(menu, view, menuInfo); // Get the adapter context menu information AdapterView.AdapterContextMenuInfo info; try { info = (AdapterView.AdapterContextMenuInfo) menuInfo; } catch (ClassCastException e) { Log.e(TAG, "bad menuInfo", e); return; } int m = 0; // Add menu items menu.add(0, CONTEXT_MENU_ITEM_REPLY, m++, R.string.menu_item_reply); menu.add(0, CONTEXT_MENU_ITEM_RETWEET, m++, R.string.menu_item_retweet); // menu.add(0, CONTEXT_MENU_ITEM_DIRECT_MESSAGE, m++, // R.string.menu_item_direct_message); // menu.add(0, CONTEXT_MENU_ITEM_UNFOLLOW, m++, // R.string.menu_item_unfollow); // menu.add(0, CONTEXT_MENU_ITEM_BLOCK, m++, R.string.menu_item_block); // menu.add(0, CONTEXT_MENU_ITEM_PROFILE, m++, // R.string.menu_item_view_profile); // Get the record for the currently selected item Uri uri = ContentUris.withAppendedId(Tweets.CONTENT_URI, info.id); Cursor c = getContentResolver().query(uri, new String[] { Tweets._ID, Tweets.MESSAGE, Tweets.AUTHOR_ID, Tweets.FAVORITED }, null, null, null); try { c.moveToFirst(); menu.setHeaderTitle(c.getString(c.getColumnIndex(Tweets.MESSAGE))); if (c.getInt(c.getColumnIndex(Tweets.FAVORITED)) == 1) { menu.add(0, CONTEXT_MENU_ITEM_DESTROY_FAVORITE, m++, R.string.menu_item_destroy_favorite); } else { menu.add(0, CONTEXT_MENU_ITEM_FAVORITE, m++, R.string.menu_item_favorite); } if (AndTweetPreferences.getDefaultSharedPreferences().getString(PreferencesActivity.KEY_TWITTER_USERNAME, null).equals( c.getString(c.getColumnIndex(Tweets.AUTHOR_ID)))) { menu.add(0, CONTEXT_MENU_ITEM_DESTROY_STATUS, m++, R.string.menu_item_destroy_status); } } catch (Exception e) { Log.e(TAG, "onCreateContextMenu: " + e.toString()); } finally { if (c != null && !c.isClosed()) c.close(); } } @Override public boolean onContextItemSelected(MenuItem item) { super.onContextItemSelected(item); AdapterView.AdapterContextMenuInfo info; try { info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo(); } catch (ClassCastException e) { Log.e(TAG, "bad menuInfo", e); return false; } mCurrentId = info.id; Uri uri; Cursor c; switch (item.getItemId()) { case CONTEXT_MENU_ITEM_REPLY: uri = ContentUris.withAppendedId(Tweets.CONTENT_URI, info.id); c = getContentResolver().query(uri, new String[] { Tweets._ID, Tweets.AUTHOR_ID }, null, null, null); try { c.moveToFirst(); if (hasHardwareKeyboard()) { mEditText.requestFocus(); } String reply = "@" + c.getString(c.getColumnIndex(Tweets.AUTHOR_ID)) + " "; mEditText.setText(""); mEditText.append(reply, 0, reply.length()); mReplyId = c.getLong(c.getColumnIndex(Tweets._ID)); } catch (Exception e) { Log.e(TAG, "onContextItemSelected: " + e.toString()); return false; } finally { if (c != null && !c.isClosed()) c.close(); } return true; case CONTEXT_MENU_ITEM_RETWEET: uri = ContentUris.withAppendedId(Tweets.CONTENT_URI, info.id); c = getContentResolver().query(uri, new String[] { Tweets._ID, Tweets.AUTHOR_ID, Tweets.MESSAGE }, null, null, null); try { c.moveToFirst(); if (hasHardwareKeyboard()) { mEditText.requestFocus(); } StringBuilder message = new StringBuilder(); String reply = "RT @" + c.getString(c.getColumnIndex(Tweets.AUTHOR_ID)) + " "; message.append(reply); CharSequence text = c.getString(c.getColumnIndex(Tweets.MESSAGE)); int len = 140 - reply.length() - 3; if (text.length() < len) { len = text.length(); } message.append(text, 0, len); if (message.length() == 137) { message.append("..."); } mEditText.setText(""); mEditText.append(message, 0, message.length()); } catch (Exception e) { Log.e(TAG, "onContextItemSelected: " + e.toString()); return false; } finally { if (c != null && !c.isClosed()) c.close(); } return true; case CONTEXT_MENU_ITEM_DESTROY_STATUS: sendCommand( new CommandData(CommandEnum.DESTROY_STATUS, mCurrentId)); return true; case CONTEXT_MENU_ITEM_FAVORITE: sendCommand( new CommandData(CommandEnum.CREATE_FAVORITE, mCurrentId)); return true; case CONTEXT_MENU_ITEM_DESTROY_FAVORITE: sendCommand( new CommandData(CommandEnum.DESTROY_FAVORITE, mCurrentId)); return true; case CONTEXT_MENU_ITEM_UNFOLLOW: case CONTEXT_MENU_ITEM_BLOCK: case CONTEXT_MENU_ITEM_DIRECT_MESSAGE: case CONTEXT_MENU_ITEM_PROFILE: Toast.makeText(this, R.string.unimplemented, Toast.LENGTH_SHORT).show(); return true; } return false; } /** * Handles threaded sending of the message, typed in the mEditText text box. * Queued message sending is supported (if initial sending * failed for some reason). */ void updateStatus() { String status = mEditText.getText().toString(); if (TextUtils.isEmpty(status.trim())) { Toast.makeText(TweetListActivity.this, R.string.cannot_send_empty_message, Toast.LENGTH_SHORT).show(); } else { CommandData commandData = new CommandData(CommandEnum.UPDATE_STATUS); commandData.bundle.putString(AndTweetService.EXTRA_STATUS, status); commandData.bundle.putLong(AndTweetService.EXTRA_INREPLYTOID, mReplyId); sendCommand(commandData); closeSoftKeyboard(); // Let's assume that everything will be Ok // so we may clear the text box with the sent tweet // text... mReplyId = 0; mEditText.setText(""); if (hasHardwareKeyboard()) { mEditText.requestFocus(); } } } /** * Close the on-screen keyboard. */ private void closeSoftKeyboard() { InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); inputMethodManager.hideSoftInputFromWindow(mEditText.getWindowToken(), 0); } /** * Initialize UI */ @Override protected void initUI() { super.initUI(); mSendButton.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { updateStatus(); } }); mEditText.addTextChangedListener(new TextWatcher() { public void afterTextChanged(Editable s) { mCurrentChars = s.length(); mCharsLeftText.setText(String.valueOf(mLimitChars - mCurrentChars)); } public void beforeTextChanged(CharSequence s, int start, int count, int after) { } public void onTextChanged(CharSequence s, int start, int before, int count) { } }); mEditText.setOnKeyListener(new View.OnKeyListener() { public boolean onKey(View v, int keyCode, KeyEvent event) { if (event.getAction() == KeyEvent.ACTION_DOWN) { mCurrentChars = mEditText.length(); if (mCurrentChars == 0) { mReplyId = 0; } switch (keyCode) { case KeyEvent.KEYCODE_DPAD_CENTER: updateStatus(); return true; case KeyEvent.KEYCODE_ENTER: if (event.isAltPressed()) { mEditText.append("\n"); return true; } default: if (keyCode != KeyEvent.KEYCODE_DEL && mCurrentChars > mLimitChars) { return true; } mCharsLeftText.setText(String.valueOf(mLimitChars - mCurrentChars)); break; } } return false; } }); mEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() { public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { if (event != null) { if (event.isAltPressed()) { return false; } } updateStatus(); return true; } }); } /** * Create adapters */ private void createAdapters() { int listItemId = R.layout.tweetlist_item; if (AndTweetPreferences.getDefaultSharedPreferences().getBoolean("appearance_use_avatars", false)) { listItemId = R.layout.tweetlist_item_avatar; } PagedCursorAdapter tweetsAdapter = new PagedCursorAdapter(TweetListActivity.this, listItemId, mCursor, new String[] { Tweets.AUTHOR_ID, Tweets.MESSAGE, Tweets.SENT_DATE, Tweets.FAVORITED }, new int[] { R.id.tweet_screen_name, R.id.tweet_message, R.id.tweet_sent, R.id.tweet_favorite }, getIntent().getData(), PROJECTION, Tweets.DEFAULT_SORT_ORDER); tweetsAdapter.setViewBinder(new TweetBinder()); setListAdapter(tweetsAdapter); } /** * Listener that checks for clicks on the main list view. * * @param adapterView * @param view * @param position * @param id */ @Override public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) { if (Log.isLoggable(AndTweetService.APPTAG, Log.VERBOSE)) { Log.v(TAG, "onItemClick, id=" + id); } if (id <= 0) { return; } Uri uri = ContentUris.withAppendedId(Tweets.CONTENT_URI, id); String action = getIntent().getAction(); if (Intent.ACTION_PICK.equals(action) || Intent.ACTION_GET_CONTENT.equals(action)) { if (Log.isLoggable(AndTweetService.APPTAG, Log.DEBUG)) { Log.d(TAG, "onItemClick, setData=" + uri); } setResult(RESULT_OK, new Intent().setData(uri)); } else { if (Log.isLoggable(AndTweetService.APPTAG, Log.DEBUG)) { Log.d(TAG, "onItemClick, startActivity=" + uri); } startActivity(new Intent(Intent.ACTION_VIEW, uri)); } } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { mTotalItemCount = totalItemCount; if (positionRestored && !mIsLoading) { // Idea from // http://stackoverflow.com/questions/1080811/android-endless-list boolean loadMore = (visibleItemCount > 0) && (firstVisibleItem > 0) && (firstVisibleItem + visibleItemCount >= totalItemCount); if (loadMore) { mIsLoading = true; Log.d(TAG, "Start Loading more items, total=" + totalItemCount); // setProgressBarIndeterminateVisibility(true); mListFooter.setVisibility(View.VISIBLE); Thread thread = new Thread(mLoadListItems); thread.start(); } } } /** * Updates the activity title. */ @Override public void updateTitle() { // First set less detailed title super.updateTitle(); // Then start asynchronous task that will set detailed info sendCommand(new CommandData(CommandEnum.RATE_LIMIT_STATUS)); } { /** * Message handler for messages from threads. */ mHandler = new Handler() { /** * Message handler * * @param msg */ @Override public void handleMessage(Message msg) { JSONObject result = null; switch (msg.what) { case MSG_TWEETS_CHANGED: int numTweets = msg.arg1; if (numTweets > 0) { mNM.cancelAll(); } break; case MSG_DATA_LOADING: mIsLoading = (msg.arg1 == 1) ? true : false; if (!mIsLoading) { Toast.makeText(TweetListActivity.this, R.string.timeline_reloaded, Toast.LENGTH_SHORT).show(); } break; case MSG_UPDATE_STATUS: result = (JSONObject) msg.obj; if (result == null) { Toast.makeText(TweetListActivity.this, R.string.error_connection_error, Toast.LENGTH_LONG).show(); } else if (result.optString("error").length() > 0) { Toast.makeText(TweetListActivity.this, (CharSequence) result.optString("error"), Toast.LENGTH_LONG).show(); } else { // The tweet was sent successfully Toast.makeText(TweetListActivity.this, R.string.message_sent, Toast.LENGTH_SHORT).show(); } break; case MSG_AUTHENTICATION_ERROR: mListFooter.setVisibility(View.INVISIBLE); showDialog(DIALOG_AUTHENTICATION_FAILED); break; case MSG_SERVICE_UNAVAILABLE_ERROR: mListFooter.setVisibility(View.INVISIBLE); showDialog(DIALOG_SERVICE_UNAVAILABLE); break; case MSG_MANUAL_RELOAD: mIsLoading = false; Toast.makeText(TweetListActivity.this, R.string.timeline_reloaded, Toast.LENGTH_SHORT).show(); mListFooter.setVisibility(View.INVISIBLE); updateTitle(); if (mInitializing) { mInitializing = false; bindToService(); } break; case MSG_LOAD_ITEMS: mListFooter.setVisibility(View.INVISIBLE); switch (msg.arg1) { case STATUS_LOAD_ITEMS_SUCCESS: updateTitle(); mListFooter.setVisibility(View.INVISIBLE); if (positionRestored) { // This will prevent continuous loading... if (mCursor.getCount() > getListAdapter().getCount()) { ((SimpleCursorAdapter) getListAdapter()).changeCursor(mCursor); } } mIsLoading = false; // setProgressBarIndeterminateVisibility(false); break; case STATUS_LOAD_ITEMS_FAILURE: break; } break; case MSG_UPDATED_TITLE: if (msg.arg1 > 0) { TweetListActivity.super.updateTitle(msg.arg1 + "/" + msg.arg2); } break; case MSG_CONNECTION_TIMEOUT_EXCEPTION: mListFooter.setVisibility(View.INVISIBLE); showDialog(DIALOG_CONNECTION_TIMEOUT); break; case MSG_STATUS_DESTROY: result = (JSONObject) msg.obj; if (result == null) { Toast.makeText(TweetListActivity.this, R.string.error_connection_error, Toast.LENGTH_LONG).show(); } else if (result.optString("error").length() > 0) { Toast.makeText(TweetListActivity.this, (CharSequence) result.optString("error"), Toast.LENGTH_LONG).show(); } else { Toast.makeText(TweetListActivity.this, R.string.status_destroyed, Toast.LENGTH_SHORT).show(); mCurrentId = 0; } break; case MSG_FAVORITE_CREATE: result = (JSONObject) msg.obj; if (result == null) { Toast.makeText(TweetListActivity.this, R.string.error_connection_error, Toast.LENGTH_LONG).show(); } else if (result.optString("error").length() > 0) { Toast.makeText(TweetListActivity.this, (CharSequence) result.optString("error"), Toast.LENGTH_LONG).show(); } else { Toast.makeText(TweetListActivity.this, R.string.favorite_created, Toast.LENGTH_SHORT).show(); mCurrentId = 0; } break; case MSG_FAVORITE_DESTROY: result = (JSONObject) msg.obj; if (result == null) { Toast.makeText(TweetListActivity.this, R.string.error_connection_error, Toast.LENGTH_LONG).show(); } else if (result.optString("error").length() > 0) { Toast.makeText(TweetListActivity.this, (CharSequence) result.optString("error"), Toast.LENGTH_LONG).show(); } else { Toast.makeText(TweetListActivity.this, R.string.favorite_destroyed, Toast.LENGTH_SHORT).show(); mCurrentId = 0; } break; case MSG_CONNECTION_EXCEPTION: switch (msg.arg1) { case MSG_FAVORITE_CREATE: case MSG_FAVORITE_DESTROY: case MSG_STATUS_DESTROY: try { dismissDialog(DIALOG_EXECUTING_COMMAND); } catch (IllegalArgumentException e) { AndTweetService.d(TAG, "", e); } break; } Toast.makeText(TweetListActivity.this, R.string.error_connection_error, Toast.LENGTH_SHORT).show(); break; default: super.handleMessage(msg); } } }; } /** * Load more items from the database into the list. This procedure doesn't * download any new tweets from the Internet */ protected Runnable mLoadListItems = new Runnable() { public void run() { if (Log.isLoggable(AndTweetService.APPTAG, Log.VERBOSE)) { Log.v(TAG, "mLoadListItems run"); } queryListData(TweetListActivity.this.getIntent(), true, true); mHandler.sendMessageDelayed( mHandler.obtainMessage(MSG_LOAD_ITEMS, STATUS_LOAD_ITEMS_SUCCESS, 0), 400); } }; }