/* * 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; import android.util.Log; import org.appdotnet4j.model.AdnUser; import org.appdotnet4j.model.AdnUsers; import org.asynctasktex.AsyncTaskEx; import org.socialnetlib.android.AppdotnetApi; import org.tweetalib.android.TwitterConstant.UsersType; import org.tweetalib.android.model.TwitterIds; import org.tweetalib.android.model.TwitterUser; import org.tweetalib.android.model.TwitterUsers; import java.util.ArrayList; import java.util.HashMap; import twitter4j.IDs; import twitter4j.Paging; import twitter4j.ResponseList; import twitter4j.Twitter; import twitter4j.TwitterException; import twitter4j.User; public class TwitterFetchUsers { private FetchUsersWorkerCallbacks mWorkerCallbacks; private final HashMap<String, TwitterIds> mIdsHashMap; private Integer mFetchUsersCallbackHandle; private final HashMap<Integer, FinishedCallback> mFinishedCallbackMap; /* * */ public void clearCallbacks() { if (mFinishedCallbackMap != null) { mFinishedCallbackMap.clear(); } } /* * */ public interface FetchUsersWorkerCallbacks { public Twitter getTwitterInstance(); public AppdotnetApi getAppdotnetInstance(); public String getCurrentAccountKey(); public void addUser(User user); public void addUser(AdnUser user); public TwitterUser getUser(Long userID); } /* * */ public interface FinishedCallbackInterface { public void finished(TwitterFetchResult result, TwitterUsers users); } /* * */ public abstract class FinishedCallback implements FinishedCallbackInterface { static final int kInvalidHandle = -1; public FinishedCallback() { mHandle = kInvalidHandle; } private int mHandle; } /* * */ public TwitterFetchUsers() { mFinishedCallbackMap = new HashMap<Integer, FinishedCallback>(); mFetchUsersCallbackHandle = 0; mIdsHashMap = new HashMap<String, TwitterIds>(); } /* * */ public void setWorkerCallbacks(FetchUsersWorkerCallbacks callbacks) { mWorkerCallbacks = callbacks; } /* * */ FinishedCallback getFetchUsersCallback(Integer callbackHandle) { return mFinishedCallbackMap.get(callbackHandle); } /* * */ void removeFetchUsersCallback(FinishedCallback callback) { if (mFinishedCallbackMap.containsValue(callback)) { mFinishedCallbackMap.remove(callback.mHandle); } } /* * */ Twitter getTwitterInstance() { return mWorkerCallbacks.getTwitterInstance(); } AppdotnetApi getAppdotnetInstance() { return mWorkerCallbacks.getAppdotnetInstance(); } /* * */ TwitterIds setUsers(TwitterContentHandle contentHandle, IDs ids) { TwitterIds twitterIds = getUserIds(contentHandle); twitterIds.add(ids); return twitterIds; } TwitterIds setUsers(TwitterContentHandle contentHandle, long[] ids) { TwitterIds twitterIds = getUserIds(contentHandle); twitterIds.add(ids); return twitterIds; } /* * */ TwitterIds getUserIds(TwitterContentHandle handle) { if (mIdsHashMap != null) { TwitterIds users = mIdsHashMap.get(handle.getKey()); if (users == null) { mIdsHashMap.put(handle.getKey(), new TwitterIds()); } return users; } return null; } /* * */ public TwitterUsers getUsers(TwitterContentHandle contentHandle, TwitterPaging paging) { TwitterIds ids = getUserIds(contentHandle); if (ids == null || ids.getIdCount() == 0) { return null; } TwitterUsers result = new TwitterUsers(); for (int i = 0; i < ids.getIdCount(); i++) { TwitterUser user = mWorkerCallbacks.getUser(ids.getId(i)); if (user != null) { result.add(user); } } return result; } /* * */ public TwitterUsers getUsers(TwitterContentHandle contentHandle, TwitterPaging paging, FinishedCallback callback, ConnectionStatus connectionStatus) { TwitterUsers result = getUsers(contentHandle, paging); if (result == null) { trigger(contentHandle, paging, callback, connectionStatus); } else { callback.finished(new TwitterFetchResult(true, null), result); } return result; } /* * */ void trigger(TwitterContentHandle contentHandle, TwitterPaging paging, FinishedCallback callback, ConnectionStatus connectionStatus) { if (connectionStatus != null && !connectionStatus.isOnline()) { if (callback != null) { callback.finished(new TwitterFetchResult(false, connectionStatus.getErrorMessageNoConnection()), null); } return; } assert (!mFinishedCallbackMap.containsValue(callback)); mFinishedCallbackMap.put(mFetchUsersCallbackHandle, callback); new FetchUsersTask().execute(AsyncTaskEx.PRIORITY_MEDIUM, "Fetch Users", new FetchUsersTaskInput( mFetchUsersCallbackHandle, contentHandle, connectionStatus, paging)); mFetchUsersCallbackHandle += 1; } /* * */ public void cancel(FinishedCallback callback) { removeFetchUsersCallback(callback); } /* * */ public void updateFriendshipUser(String currentUserScreenName, TwitterUser userToUpdate, boolean create, FinishedCallback callback, ConnectionStatus connectionStatus) { updateFriendshipUsers(currentUserScreenName, new TwitterUsers( userToUpdate), create, callback, connectionStatus); } public void updateFriendshipUsers(String currentUserScreenName, TwitterUsers usersToUpdate, boolean create, FinishedCallback callback, ConnectionStatus connectionStatus) { ArrayList<String> userScreenNames = new ArrayList<String>(); for (int i = 0; i < usersToUpdate.getUserCount(); i++) { userScreenNames.add(usersToUpdate.getUser(i).getScreenName()); } updateFriendshipScreenNames(currentUserScreenName, userScreenNames, create, callback, connectionStatus); } /* * */ public void updateFriendshipScreenName(String currentUserScreenName, String screenNameToUpdate, boolean create, FinishedCallback callback, ConnectionStatus connectionStatus) { ArrayList<String> userScreenNames = new ArrayList<String>(); userScreenNames.add(screenNameToUpdate); updateFriendshipScreenNames(currentUserScreenName, userScreenNames, create, callback, connectionStatus); } private static int _mFriendshipCounter = 0; public void updateFriendshipScreenNames(String currentUserScreenName, ArrayList<String> userScreenNamesToUpdate, boolean create, FinishedCallback callback, ConnectionStatus connectionStatus) { if (connectionStatus != null && !connectionStatus.isOnline()) { if (callback != null) { callback.finished(new TwitterFetchResult(false, connectionStatus.getErrorMessageNoConnection()), null); } return; } _mFriendshipCounter += 1; TwitterContentHandle contentHandle = new TwitterContentHandle( new TwitterContentHandleBase(TwitterConstant.ContentType.USERS, TwitterConstant.UsersType.UPDATE_FRIENDSHIP), currentUserScreenName, Integer.toString(_mFriendshipCounter), mWorkerCallbacks.getCurrentAccountKey()); mFinishedCallbackMap.put(mFetchUsersCallbackHandle, callback); new FetchUsersTask().execute(AsyncTaskEx.PRIORITY_MEDIUM, "Fetch friendship", new FetchUsersTaskInput(mFetchUsersCallbackHandle, contentHandle, connectionStatus, userScreenNamesToUpdate, null, create)); } /* * */ public void updateFriendshipUserId(long currentUserId, Long userIdToUpdate, boolean create, FinishedCallback callback, ConnectionStatus connectionStatus) { ArrayList<Long> userIds = new ArrayList<Long>(); userIds.add(userIdToUpdate); updateFriendshipUserIds(currentUserId, userIds, create, callback, connectionStatus); } public void updateFriendshipUserIds(long currentUserId, ArrayList<Long> userIdsToUpdate, boolean create, FinishedCallback callback, ConnectionStatus connectionStatus) { if (connectionStatus != null && !connectionStatus.isOnline()) { if (callback != null) { callback.finished(new TwitterFetchResult(false, connectionStatus.getErrorMessageNoConnection()), null); } return; } _mFriendshipCounter += 1; TwitterContentHandle contentHandle = new TwitterContentHandle( new TwitterContentHandleBase(TwitterConstant.ContentType.USERS, TwitterConstant.UsersType.UPDATE_FRIENDSHIP), Long.toString(currentUserId), Integer.toString(_mFriendshipCounter), mWorkerCallbacks.getCurrentAccountKey()); mFinishedCallbackMap.put(mFetchUsersCallbackHandle, callback); new FetchUsersTask().execute(AsyncTaskEx.PRIORITY_MEDIUM, "Update Friendships", new FetchUsersTaskInput( mFetchUsersCallbackHandle, contentHandle, connectionStatus, null, userIdsToUpdate, create)); } /* * */ private void createBlockOrReportSpam(UsersType usersType, long currentUserId, Long userId, FinishedCallback callback, ConnectionStatus connectionStatus) { ArrayList<Long> userIds = new ArrayList<Long>(); userIds.add(userId); createBlockOrReportSpam(usersType, currentUserId, userIds, callback, connectionStatus); } private void createBlockOrReportSpam(UsersType usersType, long currentUserId, ArrayList<Long> userIds, FinishedCallback callback, ConnectionStatus connectionStatus) { if (connectionStatus != null && !connectionStatus.isOnline()) { if (callback != null) { callback.finished(new TwitterFetchResult(false, connectionStatus.getErrorMessageNoConnection()), null); } return; } _mFriendshipCounter += 1; TwitterContentHandle contentHandle = new TwitterContentHandle( new TwitterContentHandleBase(TwitterConstant.ContentType.USERS, usersType), Long.toString(currentUserId), Integer.toString(_mFriendshipCounter), mWorkerCallbacks.getCurrentAccountKey()); mFinishedCallbackMap.put(mFetchUsersCallbackHandle, callback); new FetchUsersTask().execute(AsyncTaskEx.PRIORITY_MEDIUM, "Block or Report", new FetchUsersTaskInput( mFetchUsersCallbackHandle, contentHandle, connectionStatus, userIds)); } /* * */ public void reportSpam(long currentUserId, Long userId, FinishedCallback callback, ConnectionStatus connectionStatus) { createBlockOrReportSpam(UsersType.REPORT_SPAM, currentUserId, userId, callback, connectionStatus); } public void reportSpam(long currentUserId, ArrayList<Long> userIds, FinishedCallback callback, ConnectionStatus connectionStatus) { createBlockOrReportSpam(UsersType.REPORT_SPAM, currentUserId, userIds, callback, connectionStatus); } /* * */ public void createBlock(long currentUserId, Long userId, FinishedCallback callback, ConnectionStatus connectionStatus) { createBlockOrReportSpam(UsersType.CREATE_BLOCK, currentUserId, userId, callback, connectionStatus); } public void createBlock(long currentUserId, ArrayList<Long> userIds, FinishedCallback callback, ConnectionStatus connectionStatus) { createBlockOrReportSpam(UsersType.CREATE_BLOCK, currentUserId, userIds, callback, connectionStatus); } /* * */ class FetchUsersTaskInput { FetchUsersTaskInput(Integer callbackHandle, TwitterContentHandle contentHandle, ConnectionStatus connectionStatus, TwitterPaging paging) { mCallbackHandle = callbackHandle; mContentHandle = contentHandle; mConnectionStatus = connectionStatus; mPaging = paging; } FetchUsersTaskInput(Integer callbackHandle, TwitterContentHandle contentHandle, ConnectionStatus connectionStatus, ArrayList<Long> userIds) { mCallbackHandle = callbackHandle; mContentHandle = contentHandle; mConnectionStatus = connectionStatus; mUserIds = userIds != null ? new ArrayList<Long>(userIds) : null; } FetchUsersTaskInput(Integer callbackHandle, TwitterContentHandle contentHandle, ConnectionStatus connectionStatus, ArrayList<String> userScreenNames, ArrayList<Long> userIds, boolean createFriendship) { mCallbackHandle = callbackHandle; mContentHandle = contentHandle; mConnectionStatus = connectionStatus; mScreenNames = userScreenNames != null ? new ArrayList<String>( userScreenNames) : null; mUserIds = userIds != null ? new ArrayList<Long>(userIds) : null; mCreateFriendship = createFriendship; } final Integer mCallbackHandle; final TwitterContentHandle mContentHandle; TwitterPaging mPaging; final ConnectionStatus mConnectionStatus; ArrayList<String> mScreenNames; ArrayList<Long> mUserIds; boolean mCreateFriendship; } /* * */ class FetchUsersTaskOutput { FetchUsersTaskOutput(TwitterFetchResult result, Integer callbackHandle, TwitterUsers users) { mResult = result; mCallbackHandle = callbackHandle; mUsers = users; } final TwitterFetchResult mResult; final Integer mCallbackHandle; final TwitterUsers mUsers; } /* * */ class FetchUsersTask extends AsyncTaskEx<FetchUsersTaskInput, Void, FetchUsersTaskOutput> { @Override protected FetchUsersTaskOutput doInBackground( FetchUsersTaskInput... inputArray) { FetchUsersTaskInput input = inputArray[0]; TwitterUsers twitterUsers = null; Twitter twitter = getTwitterInstance(); AppdotnetApi appdotnet = getAppdotnetInstance(); String errorDescription = null; if (input.mConnectionStatus != null && !input.mConnectionStatus.isOnline()) { return new FetchUsersTaskOutput(new TwitterFetchResult(false, input.mConnectionStatus.getErrorMessageNoConnection()), input.mCallbackHandle, null); } UsersType usersType = input.mContentHandle.getUsersType(); if (appdotnet != null) { long[] ids = null; AdnUsers users = null; switch (usersType) { case FRIENDS: { ids = appdotnet.getAdnFollowing(); setUsers(input.mContentHandle, ids); break; } case FOLLOWERS: { ids = appdotnet.getAdnFollowedBy(); setUsers(input.mContentHandle, ids); break; } case RETWEETED_BY: { try { long postId = Long.valueOf(input.mContentHandle .getIdentifier()); users = appdotnet.getUsersWhoReposted(postId); setUsers(input.mContentHandle, ids); } catch (NumberFormatException e) { } break; } case UPDATE_FRIENDSHIP: { twitterUsers = new TwitterUsers(); if (input.mScreenNames != null) { for (String screenName : input.mScreenNames) { AdnUser user = null; // We can't follow ourself... if (!screenName.toLowerCase().equals( input.mContentHandle.getScreenName() .toLowerCase())) { if (input.mCreateFriendship) { user = appdotnet.setAdnFollow(screenName, true); } else { user = appdotnet.setAdnFollow(screenName, false); } } if (user != null) { twitterUsers.add(new TwitterUser(user)); } } } else if (input.mUserIds != null) { long currentUserId = Long .parseLong(input.mContentHandle.getScreenName()); for (Long userId : input.mUserIds) { AdnUser user = null; // We can't follow ourself... if (currentUserId != userId) { if (input.mCreateFriendship) { user = appdotnet.setAdnFollow(userId, true); } else { user = appdotnet .setAdnFollow(userId, false); } } if (user != null) { twitterUsers.add(new TwitterUser(user)); } } } } } if (ids != null) { int max = input.mPaging == null ? 40 : input.mPaging.getCount(); int numberToFetch = Math.min(max, ids.length); long[] longArray = new long[numberToFetch]; System.arraycopy(ids, 0, longArray, 0, numberToFetch); users = appdotnet.getAdnMultipleUsers(longArray); } if (users != null && users.mUsers != null && users.mUsers.size() > 0 && twitterUsers == null) { twitterUsers = new TwitterUsers(); for (AdnUser user : users.mUsers) { mWorkerCallbacks.addUser(user); twitterUsers.add(new TwitterUser(user)); } } return new FetchUsersTaskOutput(new TwitterFetchResult( errorDescription == null, errorDescription), input.mCallbackHandle, twitterUsers); } else if (twitter != null) { IDs userIds = null; ResponseList<User> users = null; Paging paging = null; if (input.mPaging != null) { paging = input.mPaging.getT4JPaging(); } try { switch (usersType) { case FRIENDS: { Log.d("api-call", "getFriendsIDs"); userIds = twitter.getFriendsIDs(-1); setUsers(input.mContentHandle, userIds); break; } case FOLLOWERS: { Log.d("api-call", "getFollowersIDs"); userIds = twitter.getFollowersIDs(-1); setUsers(input.mContentHandle, userIds); break; } case RETWEETED_BY: { Log.d("api-call", "getRetweets"); long statusId = Long.parseLong(input.mContentHandle .getIdentifier()); ResponseList<twitter4j.Status> statuses = twitter .getRetweets(statusId); if (statuses != null) { twitterUsers = new TwitterUsers(); for (twitter4j.Status status : statuses) { mWorkerCallbacks.addUser(status.getUser()); twitterUsers.add(new TwitterUser(status .getUser())); } } break; } case PEOPLE_SEARCH: { Log.d("api-call", "searchUsers"); String searchTerm = input.mContentHandle .getScreenName(); users = twitter.searchUsers(searchTerm, 0); break; } case UPDATE_FRIENDSHIP: { twitterUsers = new TwitterUsers(); if (input.mScreenNames != null) { for (String screenName : input.mScreenNames) { User user = null; // We can't follow ourself... if (!screenName.toLowerCase().equals( input.mContentHandle.getScreenName() .toLowerCase())) { if (input.mCreateFriendship) { Log.d("api-call", "createFriendship"); user = twitter .createFriendship(screenName); } else { Log.d("api-call", "destroyFriendship"); user = twitter .destroyFriendship(screenName); } } if (user != null) { twitterUsers.add(new TwitterUser(user)); } } } else if (input.mUserIds != null) { long currentUserId = Long .parseLong(input.mContentHandle .getScreenName()); for (Long userId : input.mUserIds) { User user = null; // We can't follow ourself... if (currentUserId != userId) { if (input.mCreateFriendship) { Log.d("api-call", "createFriendship"); user = twitter.createFriendship(userId); } else { Log.d("api-call", "destroyFriendship"); user = twitter .destroyFriendship(userId); } } if (user != null) { twitterUsers.add(new TwitterUser(user)); } } } if (twitterUsers.getUserCount() == 0) { twitterUsers = null; } break; } case CREATE_BLOCK: case REPORT_SPAM: { twitterUsers = new TwitterUsers(); long currentUserId = Long .parseLong(input.mContentHandle.getScreenName()); for (Long userId : input.mUserIds) { User user = null; // We can't act on ourself... if (currentUserId != userId) { if (usersType == UsersType.CREATE_BLOCK) { Log.d("api-call", "createBlock"); user = twitter.createBlock(userId); } else if (usersType == UsersType.REPORT_SPAM) { Log.d("api-call", "reportSpam"); user = twitter.reportSpam(userId); } if (user != null) { twitterUsers.add(new TwitterUser(user)); } } } if (twitterUsers.getUserCount() == 0) { twitterUsers = null; } break; } } if (userIds != null) { long[] ids = userIds.getIDs(); int numberToFetch = paging == null ? 40 : paging.getCount(); int start = 0; int finish = numberToFetch; ArrayList<Long> fetchIds = new ArrayList<Long>(); boolean check = true; while (check) { //Establish ids for this batch for (int i = start; i < finish; i++) { if (ids.length == 0 || ids.length - 1 == i) { check = false; break; } fetchIds.add(ids[i]); } //Mark where to start and end next time round start = start + numberToFetch; finish = finish + numberToFetch; //Convert arraylist into long[] long[] longArray = new long[fetchIds.size()]; for (int i = 0; i < fetchIds.size(); i++) { longArray[i] = fetchIds.get(i); } fetchIds.clear(); //Get this batch of users if (users == null) { users = twitter.lookupUsers(longArray); } else { users.addAll(twitter.lookupUsers(longArray)); } } } } 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); } if (users != null && twitterUsers == null) { twitterUsers = new TwitterUsers(); for (User user : users) { mWorkerCallbacks.addUser(user); twitterUsers.add(new TwitterUser(user)); } } return new FetchUsersTaskOutput(new TwitterFetchResult( errorDescription == null, errorDescription), input.mCallbackHandle, twitterUsers); } return new FetchUsersTaskOutput(new TwitterFetchResult( errorDescription == null, errorDescription), input.mCallbackHandle, twitterUsers); } @Override protected void onPostExecute(FetchUsersTaskOutput output) { FinishedCallback callback = getFetchUsersCallback(output.mCallbackHandle); if (callback != null) { callback.finished(output.mResult, output.mUsers); removeFetchUsersCallback(callback); } super.onPostExecute(output); } } }