/*
* Copyright (C) 2013 Chris Lacy 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 org.tweetalib.android.fetch;
import android.util.Log;
import org.appdotnet4j.model.AdnInteractions;
import org.appdotnet4j.model.AdnPaging;
import org.appdotnet4j.model.AdnPost;
import org.appdotnet4j.model.AdnPosts;
import org.appdotnet4j.model.AdnUser;
import org.asynctasktex.AsyncTaskEx;
import org.socialnetlib.android.AppdotnetApi;
import org.tweetalib.android.ConnectionStatus;
import org.tweetalib.android.TwitterContentHandle;
import org.tweetalib.android.TwitterFetchResult;
import org.tweetalib.android.TwitterPaging;
import org.tweetalib.android.TwitterUtil;
import org.tweetalib.android.callback.TwitterFetchStatusesFinishedCallback;
import org.tweetalib.android.model.TwitterStatus;
import org.tweetalib.android.model.TwitterStatuses;
import org.tweetalib.android.model.TwitterStatuses.AddUserCallback;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import twitter4j.Paging;
import twitter4j.Query;
import twitter4j.QueryResult;
import twitter4j.ResponseList;
import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.User;
public class TwitterFetchStatuses {
private FetchStatusesWorkerCallbacks mCallbacks;
private final HashMap<String, TwitterStatuses> mStatusesHashMap;
private Integer mFetchStatusesCallbackHandle;
private final HashMap<Integer, TwitterFetchStatusesFinishedCallback> mFinishedCallbackMap;
private final HashMap<String, String> mHashtagMap;
/*
*
*/
public void clearCallbacks() {
if (mFinishedCallbackMap != null) {
for (Integer key : mFinishedCallbackMap.keySet()) {
TwitterFetchStatusesFinishedCallback callback = mFinishedCallbackMap.get(key);
cancel(callback);
}
mFinishedCallbackMap.clear();
}
}
/*
*
*/
public interface FetchStatusesWorkerCallbacks {
public AppdotnetApi getAppdotnetApi();
public Twitter getTwitterInstance();
public void addUser(User user);
public void addUser(AdnUser user);
}
/*
*
*/
public TwitterFetchStatuses() {
mFinishedCallbackMap = new HashMap<Integer, TwitterFetchStatusesFinishedCallback>();
mFetchStatusesCallbackHandle = 0;
mStatusesHashMap = new HashMap<String, TwitterStatuses>();
mHashtagMap = new HashMap<String, String>();
}
/*
*
*/
public void setWorkerCallbacks(FetchStatusesWorkerCallbacks callbacks) {
mCallbacks = callbacks;
}
/*
*
*/
/*
*
*/
TwitterFetchStatusesFinishedCallback getFetchStatusesCallback(Integer callbackHandle) {
return mFinishedCallbackMap.get(callbackHandle);
}
/*
*
*/
void removeFetchStatusesCallback(TwitterFetchStatusesFinishedCallback callback) {
if (mFinishedCallbackMap.containsValue(callback)) {
mFinishedCallbackMap.remove(callback.getHandle());
}
}
/*
*
*/
Twitter getTwitterInstance() {
return mCallbacks.getTwitterInstance();
}
AppdotnetApi getAppdotnetApi() {
return mCallbacks.getAppdotnetApi();
}
/*
*
*/
TwitterStatuses setStatuses(TwitterContentHandle contentHandle, QueryResult result) {
TwitterStatuses feed = getStatuses(contentHandle);
feed.add(result);
return feed;
}
/*
*
*/
TwitterStatuses setStatuses(TwitterContentHandle contentHandle, ResponseList<twitter4j.Status> statuses) {
TwitterStatuses feed = getStatuses(contentHandle);
AddUserCallback addUserCallback = new AddUserCallback() {
@Override
public void addUser(User user) {
mCallbacks.addUser(user);
}
@Override
public void addUser(AdnUser user) {
mCallbacks.addUser(user);
}
};
if (feed == null) {
feed = new TwitterStatuses();
}
feed.add(statuses, addUserCallback);
return feed;
}
/*
*
*/
TwitterStatuses setStatuses(TwitterContentHandle contentHandle, AdnPosts posts) {
TwitterStatuses feed = getStatuses(contentHandle);
AddUserCallback addUserCallback = new AddUserCallback() {
@Override
public void addUser(User user) {
mCallbacks.addUser(user);
}
@Override
public void addUser(AdnUser user) {
mCallbacks.addUser(user);
}
};
if (feed == null) {
feed = new TwitterStatuses();
}
if (posts != null && posts.mPosts != null && posts.mPosts.size() > 0) {
feed.add(posts, addUserCallback);
} else {
feed.setFeedFullyRefreshed();
}
return feed;
}
/*
*
*/
public TwitterStatuses setStatuses(TwitterContentHandle contentHandle, TwitterStatuses statuses,
boolean resetExisting) {
TwitterStatuses feed = getStatuses(contentHandle);
if (resetExisting) {
feed.reset();
}
feed.add(statuses);
return feed;
}
/*
*
*/
public TwitterStatuses getStatuses(TwitterContentHandle handle) {
if (mStatusesHashMap != null) {
TwitterStatuses feed = mStatusesHashMap.get(handle.getKey());
if (feed == null) {
mStatusesHashMap.put(handle.getKey(), new TwitterStatuses());
}
// TODO: This is a bug right? No feed will be returned when it is
// created??
return feed;
}
return null;
}
public void removeFromHashMap(TwitterStatuses removeStatuses) {
if (mStatusesHashMap != null) {
for (String key : mStatusesHashMap.keySet()) {
TwitterStatuses feed = mStatusesHashMap.get(key);
feed.remove(removeStatuses);
}
}
}
/*
*
*/
public void trigger(TwitterContentHandle contentHandle, TwitterPaging paging,
TwitterFetchStatusesFinishedCallback callback, ConnectionStatus connectionStatus, int priorityOffset) {
if (connectionStatus != null && !connectionStatus.isOnline()) {
if (callback != null) {
callback.finished(new TwitterFetchResult(false, connectionStatus.getErrorMessageNoConnection()),
null, contentHandle);
}
return;
}
if (mFinishedCallbackMap.containsValue(callback)) {
throw new RuntimeException("Shouldn't be");
}
mFinishedCallbackMap.put(mFetchStatusesCallbackHandle, callback);
new FetchStatusesTask().execute(AsyncTaskEx.PRIORITY_NOT_QUITE_HIGHEST + priorityOffset, "Fetch Statuses",
new FetchStatusesTaskInput(mFetchStatusesCallbackHandle, contentHandle, paging, connectionStatus));
mFetchStatusesCallbackHandle += 1;
}
/*
*
*/
public void cancel(TwitterFetchStatusesFinishedCallback callback) {
removeFetchStatusesCallback(callback);
}
/*
*
*/
class FetchStatusesTaskInput {
FetchStatusesTaskInput(Integer callbackHandle, TwitterContentHandle contentHandle, TwitterPaging paging,
ConnectionStatus connectionStatus) {
mCallbackHandle = callbackHandle;
mContentHandle = contentHandle;
mPaging = paging;
mConnectionStatus = connectionStatus;
}
final Integer mCallbackHandle;
final TwitterContentHandle mContentHandle;
final TwitterPaging mPaging;
final ConnectionStatus mConnectionStatus;
}
/*
*
*/
class FetchStatusesTaskOutput {
FetchStatusesTaskOutput(TwitterFetchResult result, Integer callbackHandle, TwitterStatuses feed,
TwitterContentHandle contentHandle) {
mResult = result;
mCallbackHandle = callbackHandle;
mContentHandle = contentHandle;
mFeed = feed;
}
final TwitterFetchResult mResult;
final Integer mCallbackHandle;
final TwitterContentHandle mContentHandle;
final TwitterStatuses mFeed;
}
/*
*
*/
class FetchStatusesTask extends AsyncTaskEx<FetchStatusesTaskInput, Void, FetchStatusesTaskOutput> {
@Override
protected FetchStatusesTaskOutput doInBackground(FetchStatusesTaskInput... inputArray) {
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
TwitterStatuses contentFeed = null;
FetchStatusesTaskInput input = inputArray[0];
String errorDescription = null;
if (input.mConnectionStatus != null && !input.mConnectionStatus.isOnline()) {
return new FetchStatusesTaskOutput(
new TwitterFetchResult(false, input.mConnectionStatus.getErrorMessageNoConnection()),
input.mCallbackHandle, null, input.mContentHandle);
}
AppdotnetApi appdotnetApi = getAppdotnetApi();
if (appdotnetApi != null) {
AdnPaging defaultPaging = new AdnPaging(1);
AdnPaging paging;
if (input.mPaging != null) {
paging = input.mPaging.getAdnPaging();
} else {
paging = defaultPaging;
}
switch (input.mContentHandle.getStatusesType()) {
case USER_HOME_TIMELINE: {
AdnPosts posts = appdotnetApi.getAdnStream(paging);
contentFeed = setStatuses(input.mContentHandle, posts);
break;
}
case USER_TIMELINE: {
String userIdAsString = input.mContentHandle.getIdentifier();
try {
int userId = Integer.valueOf(userIdAsString);
AdnPosts posts = appdotnetApi.getAdnUserStream(userId, paging);
contentFeed = setStatuses(input.mContentHandle, posts);
} catch (NumberFormatException e) {
}
break;
}
case RETWEETS_OF_ME: {
AdnInteractions interactions = appdotnetApi.getAdnInteractions();
AdnPosts posts = null;
if (interactions != null) {
posts = interactions.getAsPosts();
}
contentFeed = setStatuses(input.mContentHandle, posts);
break;
}
case SCREEN_NAME_SEARCH:
case USER_MENTIONS: {
String userIdAsString = input.mContentHandle.getIdentifier();
try {
int userId = Integer.valueOf(userIdAsString);
AdnPosts posts = appdotnetApi.getAdnMentions(userId, paging);
contentFeed = setStatuses(input.mContentHandle, posts);
} catch (NumberFormatException e) {
}
break;
}
case USER_FAVORITES: {
String userIdAsString = input.mContentHandle.getIdentifier();
AdnPosts posts = appdotnetApi.getAdnFavorites(userIdAsString, paging);
contentFeed = setStatuses(input.mContentHandle, posts);
break;
}
case STATUS_SEARCH: {
String searchTerm = input.mContentHandle.getScreenName();
if (searchTerm.length() > 1 && searchTerm.charAt(0) == '#') {
searchTerm = searchTerm.substring(1);
}
AdnPosts posts = appdotnetApi.getAdnTagPosts(searchTerm, paging);
contentFeed = setStatuses(input.mContentHandle, posts);
break;
}
case GLOBAL_FEED: {
AdnPosts posts = appdotnetApi.getAdnGlobalStream(paging);
contentFeed = setStatuses(input.mContentHandle, posts);
break;
}
case PREVIOUS_CONVERSATION: {
TwitterStatuses statuses = new TwitterStatuses();
long statusId = Long.parseLong(input.mContentHandle.getIdentifier());
AdnPost post = appdotnetApi.getAdnPost(statusId);
if (post != null) {
TwitterStatus status = new TwitterStatus(post);
if (status.mInReplyToStatusId != null) {
long inReplyToStatusId = status.mInReplyToStatusId;
for (int i = 0; i < 4; i++) {
TwitterStatus reply = new TwitterStatus(appdotnetApi.getAdnPost(inReplyToStatusId));
statuses.add(reply, false);
if (reply.mInReplyToStatusId != null) {
inReplyToStatusId = reply.mInReplyToStatusId;
} else {
break;
}
}
}
statuses.add(status, false);
if (statuses.getStatusCount() > 0) {
statuses.sort();
contentFeed = setStatuses(input.mContentHandle, statuses, false);
}
}
}
case FULL_CONVERSATION: {
long statusId = Long.parseLong(input.mContentHandle.getIdentifier());
AddUserCallback addUserCallback = new AddUserCallback() {
@Override
public void addUser(User user) {
mCallbacks.addUser(user);
}
@Override
public void addUser(AdnUser user) {
mCallbacks.addUser(user);
}
};
AdnPosts conversation = appdotnetApi.getAdnConversation(statusId, paging);
if (conversation != null && conversation.mPosts != null && conversation.mPosts.size() > 0) {
TwitterStatuses statuses = new TwitterStatuses();
statuses.add(conversation, addUserCallback);
contentFeed = setStatuses(input.mContentHandle, statuses, true);
}
}
break;
default:
break;
}
} else {
Twitter twitter = getTwitterInstance();
if (twitter != null) {
Paging defaultPaging = new Paging(1);
defaultPaging.setCount(TwitterPaging.DEFAULT_STATUS_COUNT);
Paging paging;
if (input.mPaging != null) {
paging = input.mPaging.getT4JPaging();
} else {
paging = defaultPaging;
}
try {
switch (input.mContentHandle.getStatusesType()) {
case USER_HOME_TIMELINE: {
Log.d("api-call", "getHomeTimeline");
ResponseList<twitter4j.Status> statuses;
statuses = twitter.getHomeTimeline(paging);
contentFeed = setStatuses(input.mContentHandle, statuses);
break;
}
case USER_TIMELINE: {
Log.d("api-call", "getUserTimeline");
ResponseList<twitter4j.Status> statuses =
twitter.getUserTimeline(input.mContentHandle.getScreenName(), paging);
contentFeed = setStatuses(input.mContentHandle, statuses);
break;
}
case USER_MENTIONS: {
Log.d("api-call", "getMentionsTimeline");
ResponseList<twitter4j.Status> statuses = twitter.getMentionsTimeline(paging);
contentFeed = setStatuses(input.mContentHandle, statuses);
break;
}
case USER_LIST_TIMELINE: {
String listIdAsString = input.mContentHandle.getIdentifier();
try {
Log.d("api-call", "getUserListStatuses");
int listId = Integer.valueOf(listIdAsString);
ResponseList<twitter4j.Status> statuses = twitter.getUserListStatuses(listId, paging);
contentFeed = setStatuses(input.mContentHandle, statuses);
} catch (NumberFormatException e) {
} catch (OutOfMemoryError e) {
errorDescription = "There was an out of memory doing that!";
}
break;
}
case USER_FAVORITES: {
Log.d("api-call", "getFavorites");
ResponseList<twitter4j.Status> statuses =
twitter.getFavorites(input.mContentHandle.getScreenName(), paging);
contentFeed = setStatuses(input.mContentHandle, statuses);
break;
}
case RETWEETS_OF_ME: {
Log.d("api-call", "getRetweetsOfMe");
ResponseList<twitter4j.Status> statuses = twitter.getRetweetsOfMe(paging);
contentFeed = setStatuses(input.mContentHandle, statuses);
break;
}
case SCREEN_NAME_SEARCH: {
Log.d("api-call", "search");
Query query = new Query("@" + input.mContentHandle.getScreenName());
query = TwitterUtil.updateQueryWithPaging(query, paging);
QueryResult result = twitter.search(query);
contentFeed = setStatuses(input.mContentHandle, result);
break;
}
case STATUS_SEARCH: {
Log.d("api-call", "search");
Query query = new Query(input.mContentHandle.getScreenName());
query = TwitterUtil.updateQueryWithPaging(query, paging);
QueryResult result = twitter.search(query);
contentFeed = setStatuses(input.mContentHandle, result);
break;
}
case PREVIOUS_CONVERSATION:
case FULL_CONVERSATION: {
Log.d("api-call", "showStatus");
TwitterStatuses statuses = new TwitterStatuses();
long statusId = Long.parseLong(input.mContentHandle.getIdentifier());
TwitterStatus status = new TwitterStatus(twitter.showStatus(statusId));
if (status.mInReplyToStatusId != null) {
long inReplyToStatusId = status.mInReplyToStatusId;
for (int i = 0; i < 4; i++) {
Log.d("api-call", "showStatus");
TwitterStatus reply = new TwitterStatus(twitter.showStatus(inReplyToStatusId));
statuses.add(reply, false);
if (reply.mInReplyToStatusId != null) {
inReplyToStatusId = reply.mInReplyToStatusId;
} else {
break;
}
}
}
statuses.add(status, false);
if (statuses.getStatusCount() > 0) {
statuses.sort();
contentFeed = setStatuses(input.mContentHandle, statuses, true);
}
break;
}
default:
break;
}
} catch (TwitterException e) {
e.printStackTrace();
errorDescription = e.getErrorMessage();
Log.e("api-call", errorDescription, e);
if (e.getRateLimitStatus() != null && e.getRateLimitStatus().getRemaining() <= 0) {
errorDescription += "\nTry again in " + e.getRateLimitStatus().getSecondsUntilReset()
+ " " + "seconds";
}
} catch (OutOfMemoryError e) {
e.printStackTrace();
errorDescription = e.getMessage();
Log.e("api-call", errorDescription, e);
} catch (Exception e) {
e.printStackTrace();
errorDescription = e.getMessage();
Log.e("api-call", errorDescription, e);
}
}
}
cacheHashtags(contentFeed);
return new FetchStatusesTaskOutput(
new TwitterFetchResult(errorDescription == null, errorDescription),
input.mCallbackHandle, contentFeed, input.mContentHandle);
}
@Override
protected void onPostExecute(FetchStatusesTaskOutput output) {
TwitterFetchStatusesFinishedCallback callback = getFetchStatusesCallback(output.mCallbackHandle);
if (callback != null) {
callback.finished(output.mResult, output.mFeed, output.mContentHandle);
removeFetchStatusesCallback(callback);
}
super.onPostExecute(output);
}
}
public List<String> getCachedHashtags() {
if (mHashtagMap == null) {
return null;
}
return new ArrayList<String>(mHashtagMap.values());
}
public void cacheHashtags(TwitterStatuses statuses) {
if (statuses == null) {
return;
}
for (int i = 0; i < statuses.getStatusCount(); ++i) {
for (String hastag : statuses.getStatus(i).getHashtags()) {
mHashtagMap.put(hastag, hastag);
}
}
}
}