package de.bsd.zwitscher;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import android.content.ContentValues;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.preference.PreferenceManager;
import android.text.format.DateUtils;
import android.util.Log;
import com.bugsense.trace.BugSenseHandler;
import de.bsd.zwitscher.account.Account;
import de.bsd.zwitscher.helper.ExpandUrlRunner;
import de.bsd.zwitscher.helper.MetaList;
import twitter4j.*;
import twitter4j.auth.AccessToken;
import twitter4j.auth.BasicAuthorization;
import twitter4j.auth.OAuthAuthorization;
import twitter4j.auth.RequestToken;
import twitter4j.conf.Configuration;
import twitter4j.conf.ConfigurationBuilder;
import twitter4j.conf.PropertyConfiguration;
import twitter4j.json.DataObjectFactory;
import twitter4j.media.ImageUpload;
import twitter4j.media.ImageUploadFactory;
import twitter4j.media.MediaProvider;
public class TwitterHelper {
private Context context;
private TweetDB tweetDB;
private Twitter twitter;
private int accountId;
private Account account;
public TwitterHelper(Context context, Account account) {
this.context = context;
this.account = account;
if (account!=null)
accountId = account.getId();
else
accountId = -1; // default if no account selected.
tweetDB = TweetDB.getInstance(context.getApplicationContext());
twitter = getTwitter();
}
/**
* Get a timeline for Status (Home time line, mentions) or lists
* @param paging paging object to control fetching
* @param list_id The list to fetch (0 = home time line , -1 = mentions
* @param fromDbOnly Should fetching only come from the DB or should a remote server be contacted?
* @return List of Status objects
* @see twitter4j.Status
*/
public MetaList<Status> getTimeline(Paging paging, int list_id, boolean fromDbOnly) {
List<Status> statuses = null;
try {
switch (list_id) {
case 0:
if (!fromDbOnly)
statuses = twitter.getHomeTimeline(paging ); //like friends + including retweet
break;
case -1:
if (!fromDbOnly) {
// if (!account.isStatusNet())
statuses = twitter.getMentionsTimeline(paging);
// else
// statuses = twitter.getSNMentions(paging);
}
break;
// -2 is directs
case -3 :
if (!fromDbOnly)
statuses = twitter.getUserTimeline(paging);
break;
case -4 :
if (!fromDbOnly)
statuses = twitter.getFavorites(); // TODO paging? TODO favorites only live - or how to sync them?
break;
default:
statuses = new ArrayList<Status>();
}
if (statuses==null)
statuses=new ArrayList<Status>();
persistStatus(statuses,list_id);
}
catch (TwitterException e) {
Log.e("TwitterHelper","getTimeline: Got exception: " + e.getMessage() );
if (e.getCause()!=null)
System.err.println(" " + e.getCause().getMessage());
statuses = new ArrayList<Status>();
}
int numStatuses = statuses.size();
int filled = fillUpStatusesFromDB(list_id,statuses, -1);
Log.i("getTimeline","Now we have " + statuses.size());
MetaList<Status> metaList = new MetaList<Status>(statuses,numStatuses,filled);
Log.i("getTimeline","returning " + metaList);
return metaList ;
}
OAuthAuthorization getOAuth() {
return (OAuthAuthorization) twitter.getAuthorization();
}
/**
* Return direct messages.
* The "fetch from the server" part is a bit tricky. as Twitter4J keeps
* the JSON representation of the objects in a ThreadLocal. So to obtain
* it, it must be accessed before the next call to twitter.
*
* @param fromDbOnly If true, only messages that are found in the DB are returned
* @param paging A paging object to tell how many items to fetch.
* @return A list of direct messages
*/
public MetaList<DirectMessage> getDirectMessages(boolean fromDbOnly, Paging paging) {
List<DirectMessage> ret;
List<DirectMessage> ret2 = new ArrayList<DirectMessage>();
if (!fromDbOnly) {
// Get direct messages sent to us
try {
ret = twitter.getDirectMessages(paging);
} catch (TwitterException e) {
Log.e("getDirects", "Got exception: " + e.getMessage());
ret = Collections.emptyList();
}
long max = -1;
// persist directs
if (ret.size()>0) {
persistDirects(ret);
if (ret.get(0).getId()>max)
max = ret.get(0).getId();
ret2.addAll(ret);
}
// get direct messages we sent
try {
ret = twitter.getSentDirectMessages(paging);
} catch (TwitterException e) {
Log.e("getDirects", "Got exception: " + e.getMessage());
ret = Collections.emptyList();
}
if (ret.size()>0) {
persistDirects(ret);
if (ret.get(0).getId()>max)
max = ret.get(0).getId();
ret2.addAll(ret);
}
if (max > -1) {
tweetDB.updateOrInsertLastRead(accountId, -2,max);
}
}
int numDirects = ret2.size();
int filled = fillUpDirectsFromDb(ret2);
// Now sort the two collections we've got to form a linear
// timeline.
Collections.sort(ret2,new Comparator<DirectMessage>() {
public int compare(DirectMessage directMessage, DirectMessage directMessage1) {
return directMessage1.getCreatedAt().compareTo(directMessage.getCreatedAt());
}
});
MetaList<DirectMessage> result = new MetaList<DirectMessage>(ret2,numDirects,filled);
return result;
}
/**
* Read status objects from the database only.
* @param sinceId The oldest Id that should
* @param howMany How many items should be retrieved
* @param list_id THe id of the list to retrieve the items for
* @return List of Statuses
*/
public List<Status> getStatuesFromDb(long sinceId, int howMany, long list_id) {
List<Status> ret = new ArrayList<Status>();
List<String> responseList = tweetDB.getStatusesObjsOlderThan(accountId, sinceId,howMany,list_id);
for (String json : responseList) {
Status status = materializeStatus(json);
ret.add(status);
}
return ret;
}
public List<DirectMessage> getDirectsFromDb(long sinceId, int num) {
List<DirectMessage> ret = new ArrayList<DirectMessage>();
List<String> responses = tweetDB.getDirectsOlderThan(accountId, sinceId,num);
for (String json : responses) {
DirectMessage msg = materializeDirect(json);
ret.add(msg);
}
Log.i("Get directs","Got " + ret.size() + " messages");
return ret;
}
public List<UserList> getUserListsFromServer() {
List<UserList> userLists;
try {
String username = account.getName();
userLists = twitter.getUserLists(username); // Lists I created
userLists.addAll( twitter.getUserListSubscriptions(username, -1) ); // Lists from other users
return userLists;
} catch (TwitterException e) {
// called from background task, so no toast allowed
userLists = Collections.emptyList();
}
return userLists;
}
/**
* Retrieve the lists, the passed user is subscribed to. The returned
* list is filtered down to the lists the current account owns
* @param screenName Name of the user to investigate
* @return List of list names
*/
public List<String> getListMembershipFromServer(String screenName) {
List<String> userLists;
List<UserList> tmp;
try {
tmp = twitter.getUserListMemberships(screenName, -1, true);
userLists = new ArrayList<String>(tmp.size());
for (UserList ul : tmp) {
userLists.add(ul.getName());
}
}
catch (TwitterException te) {
te.printStackTrace();
userLists = Collections.emptyList();
}
return userLists;
}
/**
* Get the default account from the tweet db.
* @return Default account
*/
private Account getDefaultAccount() {
Account acc = tweetDB.getDefaultAccount();
return acc;
}
/**
* Get an authorized org.twitter4j.Twitter instance. If the global
* account object is not set, the default account is used.
* @return authorized Twitter instance.
*/
public Twitter getTwitter() {
if (account==null)
account = getDefaultAccount();
MediaProvider mediaProvider = getMediaProvider();
if (account!=null) {
if (account.getServerType()== Account.Type.TWITTER) {
ConfigurationBuilder cb = new ConfigurationBuilder();
cb.setIncludeEntitiesEnabled(true);
cb.setJSONStoreEnabled(true);
cb.setMediaProvider(mediaProvider.toString());
cb.setHttpConnectionTimeout(60*1000);
cb.setHttpReadTimeout(240*1000); // 4 min
cb.setHttpRetryCount(3);
Configuration conf = cb.build();
OAuthAuthorization auth = new OAuthAuthorization(conf);
auth.setOAuthAccessToken(new AccessToken(account.getAccessTokenKey(), account.getAccessTokenSecret()));
auth.setOAuthConsumer(Tokens.consumerKey, Tokens.consumerSecret);
Twitter twitterInstance = new TwitterFactory(conf).getInstance(auth);
return twitterInstance;
}
else if (account.getServerType()== Account.Type.IDENTICA || account.getServerType()== Account.Type.STATUSNET) {
String base = account.getServerUrl();
base += "/api/";
ConfigurationBuilder cb = new ConfigurationBuilder();
cb.setRestBaseURL(base);
// cb.setSearchBaseURL(HTTP_IDENTI_CA_API);
cb.setOAuthAccessTokenURL(base + "oauth/access_token");
cb.setOAuthAuthorizationURL(base + "oauth/authorize");
cb.setOAuthRequestTokenURL(base + "oauth/request_token");
cb.setIncludeEntitiesEnabled(true);
cb.setJSONStoreEnabled(true);
Configuration conf = cb.build() ;
BasicAuthorization auth = new BasicAuthorization(account.getName(),account.getPassword());
Twitter twitterInstance = new TwitterFactory(conf).getInstance(auth);
return twitterInstance;
}
}
return null;
}
public String getAuthUrl() throws Exception {
// TODO use fresh token for the first call
RequestToken requestToken = getRequestToken(true);
String authUrl = requestToken.getAuthorizationURL();
return authUrl;
}
public RequestToken getRequestToken(boolean useFresh) throws Exception {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
if (!useFresh) {
if (preferences.contains("requestToken")) {
String rt = preferences.getString("requestToken", null);
String rts = preferences.getString("requestTokenSecret", null);
RequestToken token = new RequestToken(rt, rts);
return token;
}
}
Twitter twitterInstance = new TwitterFactory().getInstance();
twitterInstance.setOAuthConsumer(Tokens.consumerKey, Tokens.consumerSecret);
RequestToken requestToken = twitterInstance.getOAuthRequestToken();
Editor editor = preferences.edit();
editor.putString("requestToken", requestToken.getToken());
editor.putString("requestTokenSecret", requestToken.getTokenSecret());
editor.commit();
return requestToken;
}
/**
* Get an auth token the classical OAuth way
*
* @param pin Pin obtained from Twitter needed to create the oauth tokens
* @return the newly created account
* @throws Exception
*/
public Account generateAccountWithOauth(String pin) throws Exception{
Twitter twitterInstance = new TwitterFactory().getInstance();
twitterInstance.setOAuthConsumer(Tokens.consumerKey, Tokens.consumerSecret);
RequestToken requestToken = getRequestToken(false); // twitterInstance.getOAuthRequestToken();
AccessToken accessToken = twitterInstance.getOAuthAccessToken(requestToken, pin);
int newId = tweetDB.getNewAccountId();
Account account = new Account(newId,accessToken.getScreenName(),accessToken.getToken(),accessToken.getTokenSecret(),null, Account.Type.TWITTER,true);
tweetDB.insertOrUpdateAccount(account);
return account;
}
/**
* Generate an account the xAuth way for Twitter. This only works if especially enabled by Twitter
* This also works for identi.ca and generic status.net instances.
*
* @param username Username to get the token for
* @param password password of that user and service
* @param serviceType service to use. Currently supported are twitter and identi.ca
* @param makeDefault @throws Exception If the server can not be reached or the credentials are not valid
* @param baseUrl Base url of the server to connect to (mostly applies to generic status.net instances)
* @return id of the account
* @throws Exception when anything goes wrong (e.g wrong username/password etc.)
*/
public Account generateAccountWithXauth(String username, String password, Account.Type serviceType, boolean makeDefault, String baseUrl) throws Exception {
ConfigurationBuilder cb = new ConfigurationBuilder();
Twitter twitterInstance;
Account account = null;
if (serviceType== Account.Type.TWITTER) {
cb.setOAuthConsumerKey(Tokens.consumerKey);
cb.setOAuthConsumerSecret(Tokens.consumerSecret);
cb.setIncludeEntitiesEnabled(true);
cb.setJSONStoreEnabled(true);
Configuration conf = cb.build() ;
BasicAuthorization auth = new BasicAuthorization(username,password);
twitterInstance = new TwitterFactory(conf).getInstance(auth);
AccessToken accessToken = twitterInstance.getOAuthAccessToken();
int newId = tweetDB.getNewAccountId();
account = new Account(newId,username,accessToken.getToken(),accessToken.getTokenSecret(),null, Account.Type.TWITTER,makeDefault);
// TODO determine account id via db sequence?
tweetDB.insertOrUpdateAccount(account);
if (makeDefault)
tweetDB.setDefaultAccount(newId);
}
else if (serviceType== Account.Type.IDENTICA || serviceType == Account.Type.STATUSNET) {
Log.i("TwitterHelper","generateAccount with baseUrl="+baseUrl);
cb.setRestBaseURL(baseUrl + "api/");
cb.setOAuthAccessTokenURL(baseUrl + "api/oauth/access_token");
cb.setOAuthAuthorizationURL(baseUrl + "api/oauth/authorize");
cb.setOAuthRequestTokenURL(baseUrl + "api/oauth/request_token");
cb.setJSONStoreEnabled(true);
Configuration conf = cb.build() ;
TwitterFactory twitterFactory = new TwitterFactory(conf);
BasicAuthorization auth = new BasicAuthorization(username,password);
twitterInstance = twitterFactory.getInstance(auth);
// Trigger a fetch to validate the credentials
Paging paging = new Paging();
paging.setCount(1);
twitterInstance.getHomeTimeline(paging);
// TODO determine account id via db sequence?
int newId = tweetDB.getNewAccountId();
account = new Account(newId,username,baseUrl, serviceType,makeDefault,password);
tweetDB.insertOrUpdateAccount(account);
if (makeDefault)
tweetDB.setDefaultAccount(newId);
}
return account;
}
public UpdateResponse updateStatus(UpdateRequest request) {
UpdateResponse updateResponse = new UpdateResponse(request.updateType,request.statusUpdate);
updateResponse.setPicturePath(request.picturePath);
Log.i("TwitterHelper", "Sending update: " + request.statusUpdate);
try {
twitter.updateStatus(request.statusUpdate);
String sentMessage;
if (account.isStatusNet())
sentMessage = context.getString(R.string.dent_sent);
else
sentMessage = context.getString((R.string.tweet_sent));
updateResponse.setMessage(sentMessage);
updateResponse.setSuccess();
} catch (TwitterException e) {
updateResponse.setMessage( e.getLocalizedMessage());
updateResponse.setFailure();
updateResponse.setStatusCode(e.getStatusCode());
Log.e("TH::updateStatus","Update failed",e);
}
return updateResponse;
}
public UpdateResponse retweet(UpdateRequest request) {
UpdateResponse response = new UpdateResponse(request.updateType,request.id);
try {
twitter.retweetStatus(request.id);
response.setSuccess();
response.setMessage("Retweeted successfully");
} catch (TwitterException e) {
response.setFailure();
response.setMessage(e.getLocalizedMessage());
response.setStatusCode(e.getStatusCode());
}
return response;
}
public UpdateResponse favorite(UpdateRequest request) {
Status status = request.status;
UpdateResponse updateResponse = new UpdateResponse(request.updateType, status);
updateResponse.view = request.view;
try {
if (status.isFavorited()) {
twitter.destroyFavorite(status.getId());
}
else {
twitter.createFavorite(status.getId());
}
// reload tweet and update in DB - twitter4j should have some status.setFav()..
status = getStatusById(status.getId(),null, true, false, false); // no list id, don't persist
updateStatus(status); // explicitly update in DB - we know it is there.
updateResponse.setSuccess();
updateResponse.setMessage("(Un)favorite set");
} catch (TwitterException e) {
updateResponse.setFailure();
updateResponse.setMessage(e.getLocalizedMessage());
updateResponse.setStatusCode(e.getStatusCode());
}
updateResponse.setStatus(status);
return updateResponse;
}
public UpdateResponse direct(UpdateRequest request) {
UpdateResponse updateResponse = new UpdateResponse(request.updateType, (Status) null); // TODO
try {
twitter.sendDirectMessage((int)request.id,request.message);
updateResponse.setSuccess();
String msg = context.getString(R.string.direct_message_sent);
updateResponse.setMessage(msg);
} catch (TwitterException e) {
updateResponse.setFailure();
updateResponse.setMessage(e.getLocalizedMessage());
updateResponse.setOrigMessage(request.message);
updateResponse.setStatusCode(e.getStatusCode());
}
return updateResponse;
}
public MetaList<Status> getUserList(Paging paging, int listId, boolean fromDbOnly, int unreadCount) {
List<Status> statuses;
if (!fromDbOnly) {
try {
statuses = twitter.getUserListStatuses( listId, paging);
int size = statuses.size();
Log.i("getUserList","Got " + size + " statuses from Twitter");
persistStatus(statuses,listId);
} catch (TwitterException e) {
statuses = new ArrayList<Status>();
System.err.println("Got exception: " + e.getMessage() );
if (e.getCause()!=null)
System.err.println(" " + e.getCause().getMessage());
}
} else
statuses = new ArrayList<Status>();
int numOriginal = statuses.size();
int filled = fillUpStatusesFromDB(listId, statuses, unreadCount);
Log.i("getUserList","Now we have " + statuses.size());
MetaList<Status> metaList = new MetaList<Status>(statuses,numOriginal,filled);
return metaList;
}
/**
* Fill the passed status list with old tweets from the DB. This is wanted in
* two occasions:<ul>
* <li>No tweets came from the server, so we want to show something</li>
* <li>A small number is fetched, we want to show more (to have some timely context)</li>
* </ul>
* For a given number of passed statuses, we
* <ul>
* <li>Always add minOld tweets from the DB</li>
* <li>If no incoming tweets, show maxOld</li>
* </ul>
* See also preferences.xml
*
* @param listId The list for which tweets are fetched
* @param statuses The list of incoming statuses to fill up
* @param unreadCount Number of unread messages the use has in the list. Provides a minimum to fetch. Set to -1 to ignore.
* @return number of added statuses
*/
private int fillUpStatusesFromDB(int listId, List<Status> statuses, int unreadCount) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
int minOld = Integer.valueOf(preferences.getString("minOldTweets","5"));
int maxOld = Integer.valueOf(preferences.getString("maxOldTweets","10"));
if (unreadCount>-1 && unreadCount > maxOld)
maxOld=unreadCount+3; // Fetch some 'context' too. Also prevents ArrayIndexOOB exceptions later
int size = statuses.size();
if (size==0)
statuses.addAll(getStatuesFromDb(-1,maxOld,listId));
else {
int num = (size+minOld< maxOld) ? maxOld-size : minOld;
statuses.addAll(getStatuesFromDb(statuses.get(size-1).getId(),num,listId));
}
int size2 = statuses.size();
int i = size2 - size;
return i;
}
private int fillUpDirectsFromDb(List<DirectMessage> messages) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
int minOld = Integer.valueOf(preferences.getString("minOldTweets","5"));
int maxOld = Integer.valueOf(preferences.getString("maxOldTweets","10"));
int size = messages.size();
if (size==0)
messages.addAll(getDirectsFromDb(-1,maxOld));
else {
int num = (size+minOld < maxOld) ? maxOld-size : minOld;
messages.addAll(getDirectsFromDb(messages.get(size-1).getId(),num));
}
int size2 = messages.size();
return size2 - size;
}
/**
* Get a single status. If directOnly is false, we first look in the local
* db if it is already present. Otherwise we directly go to the server.
*
*
* @param statusId Id of the status to fetch
* @param list_id id of the timeline
* @param directOnly If true only call out to the server. Otherwise try to look up in local db.
* @param alsoPersist Should the fetched status be persisted? Required directOnly = false and no db hit.
* @param fromDbOnly If true, do not call out to the server.
* @return the obtained Status
*/
public Status getStatusById(long statusId, Long list_id, boolean directOnly, boolean alsoPersist, boolean fromDbOnly) {
Status status = null;
if (!directOnly) {
String obj = tweetDB.getStatusObjectById(accountId, statusId, list_id);
if (obj!=null) {
status = materializeStatus(obj);
if (status!=null)
return status;
}
}
if (fromDbOnly)
return null;
try {
status = twitter.showStatus(statusId);
if (alsoPersist) {
long id;
if (list_id==null)
id = 0;
else
id = list_id;
List<Status> sList = new ArrayList<Status>(1);
sList.add(status);
persistStatus(sList, id);
}
} catch (TwitterException e) {
e.printStackTrace(); // TODO: Customise this generated block
}
return status;
}
public List<Status> getThreadForStatus(long startid) {
List<String> jsons = tweetDB.getThreadForStatus(accountId, startid);
List<Status> ret = new ArrayList<Status>(jsons.size());
for (String json : jsons) {
Status status = materializeStatus(json);
if (!ret.contains(status)) // Filter duplicates, as we search over all timelines
ret.add(status);
}
Collections.sort(ret,new Comparator<Status>() {
@Override
public int compare(Status status1, Status status2) {
return status2.getCreatedAt().compareTo(status1.getCreatedAt());
}
});
return ret;
}
public List<Status> getRepliesToStatus(long statusId) {
List<Status> ret = new ArrayList<Status>();
List<String> replies = tweetDB.getReplies(accountId, statusId);
for (String reply : replies) {
Status status = materializeStatus(reply);
ret.add(status);
}
return ret;
}
/**
* Search the status table for statuses of the current user
* that match the passed query entry. If the query starts with
* 'from:', statuses from the passed user are looked up
* @param query Query [from:user |�searchTerm ]
* @return Statuses found or an empty list.
*/
public List<Status> searchStatues(String query) {
Log.i("searchStatuses", "Query= " + query);
String what=null;
if (query.contains(":")) {
int pos = query.indexOf(':');
what = query.substring(0, pos);
query = query.substring(pos +1);
}
List<String> jsons = tweetDB.searchStatuses(accountId, query);
List<Status> ret = new ArrayList<Status>(jsons.size());
String qtl = query.toLowerCase();
for (String s : jsons ) {
Status st = materializeStatus(s);
if (what!=null) {
if ("from".equalsIgnoreCase(what)) { // Todo limit options
if (st.getUser().getName().contains(qtl) && !ret.contains(st))
ret.add(st);
if (st.getUser().getScreenName().contains(qtl) && !ret.contains(st))
ret.add(st);
}
} else {
if (st.getText().toLowerCase().contains(qtl) && !ret.contains(st))
ret.add(st);
}
}
return ret;
}
/**
* Retrieve a User object for the passed userId.
* @param userId Id of the user to look up
* @param cachedOnly If true, only a cached version of the object or null is returned. Otherwise a request to the server is made
* @return User object or null, if it can not be obtained.
*/
public User getUserById(long userId, boolean cachedOnly) {
User user = null;
boolean existing = false;
try {
String userJson = tweetDB.getUserById(accountId, userId);
if (userJson!=null) {
existing = true;
user = DataObjectFactory.createUser(userJson);
}
if (cachedOnly) {
return user;
}
// not cached only -> go to the server
user = twitter.showUser(userId);
userJson = DataObjectFactory.getRawJSON(user);
if (!existing)
tweetDB.insertUser(accountId, userId,userJson, user.getScreenName());
else
tweetDB.updateUser(accountId, userId,userJson);
} catch (TwitterException e) {
e.printStackTrace(); // TODO: Customise this generated block
}
return user;
}
/**
* Retrieve a User object of the passed screen name
* @param screenName Screen name of the user
* @param cachedOnly If true, only a cached version of the object or null is returned. Otherwise a request to the server is made
* @return User object or null, if it can not be obtained.
*/
public User getUserByScreenName(String screenName, boolean cachedOnly) {
User user = null;
boolean existing = false;
try {
String userJson = tweetDB.getUserByName(accountId, screenName);
if (userJson!=null) {
existing=true;
user = DataObjectFactory.createUser(userJson);
}
if (cachedOnly)
return user;
// not cached only -> go to the server
user = twitter.showUser(screenName);
userJson = DataObjectFactory.getRawJSON(user);
if (!existing)
tweetDB.insertUser(accountId, user.getId(),userJson, user.getScreenName());
else
tweetDB.updateUser(accountId, user.getId(),userJson);
} catch (TwitterException e) {
e.printStackTrace(); // TODO: Customise this generated block
}
return user;
}
/**
* Send a request to follow or unfollow a user
* @param userId Id of the user to follow
* @param doFollow If true, send a follow request; unfollow otherwise.
*/
public void followUnfollowUser(long userId, boolean doFollow ) throws TwitterException {
if (doFollow)
twitter.createFriendship(userId);
else
twitter.destroyFriendship(userId);
}
/**
* Add a user to some of our user lists
* @param userId User to add
* @param listId ids of the list to put the user on
*/
public void addUserToLists(long userId, int listId) throws TwitterException {
twitter.createUserListMember(listId, userId);
}
/**
* Remove a user to some of our user lists
* @param userId User to add
* @param listId ids of the list to put the user on
*/
public void removeUserFromList(long userId, int listId) throws TwitterException {
twitter.destroyUserListMember(listId, userId);
}
public List<Status> getUserTweets(Long userId) {
Paging paging = new Paging();
paging.setCount(30);
List<Status> ret;
try {
ret = twitter.getUserTimeline(userId, paging);
} catch (TwitterException e) {
e.printStackTrace(); // TODO: Customise this generated block
return Collections.emptyList();
}
return ret;
}
public void persistStatus(Collection<Status> statuses, long list_id) {
List<ContentValues> values = new ArrayList<ContentValues>(statuses.size());
long now = System.currentTimeMillis();
List<String> urls = new ArrayList<String>();
for (Status status : statuses) {
String json = DataObjectFactory.getRawJSON(status);
ContentValues cv = new ContentValues(6);
cv.put("ID", status.getId());
cv.put("I_REP_TO", status.getInReplyToStatusId());
cv.put("LIST_ID", list_id);
cv.put("ACCOUNT_ID",accountId);
cv.put("STATUS", json);
cv.put("ctime", now);
values.add(cv);
// urls.addAll(parseUrls(status.getText()));
/*
if (status.getURLEntities()!=null) {
for (URLEntity ue: status.getURLEntities()) {
urls.add(ue.getExpandedURL().toString());
}
}
*/
}
tweetDB.storeValues(TweetDB.TABLE_STATUSES,values);
Thread urlFetchThread = new Thread(new ExpandUrlRunner(context,urls));
urlFetchThread.start();
}
private List<String> parseUrls(String text) {
List<String> ret = new ArrayList<String>();
String[] tokens = text.split(" ");
for (String token : tokens) {
if (token.startsWith("http://t.co")) {
ret.add(token);
}
}
return ret;
}
private void persistDirects(Collection<DirectMessage> directs) {
if (directs.isEmpty())
return;
List<ContentValues> values = new ArrayList<ContentValues>(directs.size());
for (DirectMessage directMessage : directs) {
String json = DataObjectFactory.getRawJSON(directMessage);
ContentValues cv = new ContentValues(4);
cv.put("id",directMessage.getId());
cv.put("created_at", directMessage.getCreatedAt().getTime());
cv.put("ACCOUNT_ID",accountId);
cv.put("message_json",json);
values.add(cv);
}
tweetDB.storeValues(TweetDB.TABLE_DIRECTS,values);
}
/**
* Update an existing status object in the database with the passed one.
*
* @param status Updated status object
*/
private void updateStatus(Status status){
// Serialize and then store in DB
String json = DataObjectFactory.getRawJSON(status);
tweetDB.updateStatus(accountId, status.getId(), json);
}
private Status materializeStatus(String obj) {
Status response;
try {
response = DataObjectFactory.createStatus(obj);
} catch (TwitterException e) {
e.printStackTrace(); // TODO: Customise this generated block
response = null;
}
return response;
}
private User materializeUser(String obj) {
User response;
try {
response = DataObjectFactory.createUser(obj);
} catch (TwitterException e) {
e.printStackTrace(); // TODO: Customise this generated block
response = null;
}
return response;
}
private DirectMessage materializeDirect(String obj) {
DirectMessage response;
try {
response = DataObjectFactory.createDirectMessage(obj);
} catch (TwitterException e) {
e.printStackTrace(); // TODO: Customise this generated block
response = null;
}
return response;
}
/**
* Return a string representation of the date the passed status was created.
* @param status Response object from the server (Status or DirectMessage)
* @return String showing the date
*/
public String getStatusDate(TwitterResponse status) {
Date date;
if (status instanceof Status)
date = ((Status)status).getCreatedAt();
else if (status instanceof DirectMessage)
date = ((DirectMessage)status).getCreatedAt();
else
throw new IllegalArgumentException("Type of " + status + " unknown");
long time = date.getTime();
return (String) DateUtils.getRelativeDateTimeString(context,
time,
DateUtils.SECOND_IN_MILLIS,
DateUtils.DAY_IN_MILLIS,
DateUtils.FORMAT_ABBREV_ALL);
}
/**
* Upload a picture to a remote picture service like yfrog and post it to twitter.
*
* @param fileName Path on file system to the picture
* @param message Message of the update
* @return Url where this was stored on the remote service or null on error
*/
public String postPicture(String fileName, String message) {
try {
File file = new File(fileName);
MediaProvider mProvider =getMediaProvider();
String accessTokenToken = account.getAccessTokenKey();
String accessTokenSecret = account.getAccessTokenSecret();
if (accessTokenSecret == null || accessTokenToken == null) {
BugSenseHandler.sendEvent("postPicture: Token was null for account " + account);
return null;
}
Properties props = new Properties();
props.put(PropertyConfiguration.MEDIA_PROVIDER,mProvider);
props.put(PropertyConfiguration.OAUTH_ACCESS_TOKEN,accessTokenToken);
props.put(PropertyConfiguration.OAUTH_ACCESS_TOKEN_SECRET,accessTokenSecret);
props.put(PropertyConfiguration.OAUTH_CONSUMER_KEY, Tokens.consumerKey);
props.put(PropertyConfiguration.OAUTH_CONSUMER_SECRET, Tokens.consumerSecret);
props.put(PropertyConfiguration.HTTP_READ_TIMEOUT,240*1000); // 4mins
Configuration conf = new PropertyConfiguration(props);
ImageUploadFactory factory = new ImageUploadFactory(conf);
ImageUpload upload = factory.getInstance(mProvider);
String url;
url = upload.upload(file,message);
return url;
} catch (TwitterException e) {
BugSenseHandler.sendExceptionMessage("postPicture", getMediaProvider().name().toString(), e);
}
return null;
}
MediaProvider getMediaProvider() {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
String provider = preferences.getString("pictureService","yfrog");
MediaProvider mProvider ;
if (provider.equals("yfrog"))
mProvider = MediaProvider.YFROG;
else if (provider.equals("twitpic"))
mProvider = MediaProvider.TWITPIC;
else if (provider.equals("twitter"))
mProvider = MediaProvider.TWITTER;
else
throw new IllegalArgumentException("Picture provider " + provider + " unknown");
return mProvider;
}
/**
* Determine if the current user is following the passed one
* @param userId id of the user to verify
* @return true if we are following, false otherwise
*/
public boolean areWeFollowing(Long userId) {
try {
long myId = twitter.getId();
Relationship rel = twitter.showFriendship(myId,userId);
return rel.isSourceFollowingTarget();
} catch (TwitterException e) {
e.printStackTrace(); // TODO: Customise this generated block
return false;
}
}
/**
* Reports the passed user as spammer
* @param userId id of the user
*/
public void reportAsSpammer(long userId) throws TwitterException {
twitter.reportSpam(userId);
}
public List<SavedSearch> getSavedSearchesFromServer() {
List<SavedSearch> searches;
try {
searches = twitter.getSavedSearches();
} catch (TwitterException e) {
searches = Collections.emptyList();
}
return searches; // TODO: Customise this generated block
}
public List<SavedSearch> getSavedSearchesFromDb() {
List<String> jsons = tweetDB.getSavedSearches(accountId);
List<SavedSearch> searches = new ArrayList<SavedSearch>(jsons.size());
for (String json : jsons) {
try {
SavedSearch search = DataObjectFactory.createSavedSearch(json);
searches.add(search);
} catch (TwitterException e) {
e.printStackTrace(); // TODO Customize ...
}
}
return searches;
}
public List<User> getUsersFromDb() {
List<String> jsons = tweetDB.getUsers(accountId);
List<User> users = new ArrayList<User>(jsons.size());
for (String json : jsons) {
try {
User user = DataObjectFactory.createUser(json);
users.add(user);
} catch (TwitterException e) {
e.printStackTrace(); // TODO: Customise this generated block
}
}
return users;
}
public MetaList<Status> getSavedSearchesTweets(int searchId, boolean fromDbOnly, Paging paging) {
List<SavedSearch> searches = getSavedSearchesFromDb();
for (SavedSearch search : searches) {
if (search.getId()==searchId) {
String queryString = search.getQuery();
Query query = new Query(queryString); // TODO paging - probably not needed as default is 15
// TODO set some restriction like language or such
try {
QueryResult queryResult = twitter.search(query);
List<Status> tweets = queryResult.getTweets();
MetaList metaList = new MetaList(tweets,tweets.size(),0);
return metaList;
} catch (TwitterException e) {
e.printStackTrace(); // TODO: Customise this generated block
return new MetaList<Status>();
}
}
}
return new MetaList<Status>();
}
public void persistSavedSearch(SavedSearch search) {
String json = DataObjectFactory.getRawJSON(search);
tweetDB.storeSavedSearch(accountId, search.getName(),search.getQuery(),search.getId(),json);
}
public void markStatusesAsOld(Set<Long> ids) {
tweetDB.addReadIds(accountId,ids);
}
public List<Long> getReadIds(List<Long> idsToCheck) {
return tweetDB.getReads(accountId,idsToCheck);
}
public void deleteStatus(long id) throws TwitterException {
twitter.destroyStatus(id);
// TODO remove from tweetdb see also OneTweetActivity.deleteStatus()
}
}