Commit a06cbf10 authored by Aubrey Colter's avatar Aubrey Colter Committed by Gerrit Review System

Replacing the twitter4j jar required some minor code tweaks:

Removing all of the synchronization (and thus the unnecessary variables myTwitter and myUserId)
Search is no longer anonymous in twitter4j, so I only create one instance of twitter and use it in Search as well.
Fixing a few of the blocks that broke because of method signatures that changed.
CheckAuthorized() (checkAccessCode())
Follow()
StopFollowing()
RequestFollowers()
Added the @UsesLibraries annotation that was missing in the broken code. Code is fixed, ready to publish.
Fixed the formatting to be compatible with app-inventor style.
Removed the volatile keyword on the variables because it is no longer necessary.
Moved the setOAuth back into the try block at Jose's suggestion.
Added catch (IllegalStateException) block. Andrew and I determined this should return true because the user is valid, twitter4j does not allow the OAuth to be set again.
Updated documentation to reflect changes in code.
Change-Id: Ibaa8564ac0a154992340c8fef2e6c3e84a0ff042
parent 8ef1932d
......@@ -72,7 +72,7 @@
<pathelement location="lib/keyczar/keyczar-0.66-080111-java-1.6.jar" />
<pathelement location="lib/objectify-3.0/objectify-3.0.jar" />
<pathelement location="lib/tablelayout/TableLayout-jdk1.5-2007-04-21.jar" />
<pathelement location="lib/twitter/twitter4j-2.0.10-SNAPSHOT.jar" />
<pathelement location="lib/twitter/twitter4j-core-android-2.2.6.jar" />
<fileset dir="buildserver/lib" includes="**/*.jar"/>
<!-- gwt libs -->
<pathelement location="lib/gwt/2.3.0/gwt-dev.jar"/>
......
......@@ -128,7 +128,7 @@
</copy>
<copy toFile="${classes.files.dir}/YailGenerator.jar" file="${local.build.dir}/YailGenerator.jar" />
<copy toFile="${classes.files.dir}/kawa.jar" file="${lib.dir}/kawa/kawa-1.11-modified.jar" />
<copy toFile="${classes.files.dir}/twitter4j.jar" file="${lib.dir}/twitter/twitter4j-2.0.10-SNAPSHOT.jar" />
<copy toFile="${classes.files.dir}/twitter4j.jar" file="${lib.dir}/twitter/twitter4j-core-android-2.2.6.jar" />
<copy toFile="${classes.files.dir}/fusiontables.jar" file="${lib.dir}/fusiontables/fusiontables.jar" />
<copy toFile="${classes.files.dir}/google-api-client-beta.jar" file="${lib.dir}/oauth/google-api-client-1.10.3-beta.jar" />
<copy toFile="${classes.files.dir}/google-http-client-beta.jar" file="${lib.dir}/oauth/google-http-client-1.10.3-beta.jar" />
......
......@@ -118,7 +118,7 @@
<pathelement location="${public.build.dir}/CommonConstants.jar" />
<pathelement location="${lib.dir}/android/2.2/android.jar" />
<pathelement location="${lib.dir}/kawa/kawa-1.11-modified.jar" />
<pathelement location="${lib.dir}/twitter/twitter4j-2.0.10-SNAPSHOT.jar" />
<pathelement location="${lib.dir}/twitter/twitter4j-core-android-2.2.6.jar" />
<pathelement location="${lib.dir}/fusiontables/fusiontables.jar" />
<pathelement location="${lib.dir}/oauth/google-api-client-1.10.3-beta.jar" />
<pathelement location="${lib.dir}/oauth/google-api-client-android2-1.10.3-beta.jar" />
......@@ -212,7 +212,7 @@
<pathelement location="${lib.dir}/android/2.2/android.jar" />
<pathelement location="${lib.dir}/guava/guava-11.0.1.jar" />
<pathelement location="${lib.dir}/kawa/kawa-1.11-modified.jar" />
<pathelement location="${lib.dir}/twitter/twitter4j-2.0.10-SNAPSHOT.jar" />
<pathelement location="${lib.dir}/twitter/twitter4j-core-android-2.2.6.jar" />
<pathelement location="${lib.dir}/fusiontables/fusiontables.jar" />
<pathelement location="${lib.dir}/oauth/google-api-client-1.10.3-beta.jar" />
<pathelement location="${lib.dir}/oauth/google-api-client-android2-1.10.3-beta.jar" />
......
......@@ -5,6 +5,27 @@
package com.google.appinventor.components.runtime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import twitter4j.DirectMessage;
import twitter4j.IDs;
import twitter4j.Query;
import twitter4j.Status;
import twitter4j.Tweet;
import twitter4j.TwitterException;
import twitter4j.TwitterFactory;
import twitter4j.User;
import twitter4j.auth.AccessToken;
import twitter4j.auth.RequestToken;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Handler;
import android.util.Log;
import com.google.appinventor.components.annotations.DesignerComponent;
import com.google.appinventor.components.annotations.DesignerProperty;
import com.google.appinventor.components.annotations.PropertyCategory;
......@@ -12,128 +33,98 @@ import com.google.appinventor.components.annotations.SimpleEvent;
import com.google.appinventor.components.annotations.SimpleFunction;
import com.google.appinventor.components.annotations.SimpleObject;
import com.google.appinventor.components.annotations.SimpleProperty;
import com.google.appinventor.components.annotations.UsesPermissions;
import com.google.appinventor.components.annotations.UsesLibraries;
import com.google.appinventor.components.annotations.UsesPermissions;
import com.google.appinventor.components.common.ComponentCategory;
import com.google.appinventor.components.common.PropertyTypeConstants;
import com.google.appinventor.components.common.YaVersion;
import com.google.appinventor.components.runtime.util.AsynchUtil;
import com.google.appinventor.components.runtime.util.ErrorMessages;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Handler;
import android.util.Log;
import twitter4j.DirectMessage;
import twitter4j.Paging;
import twitter4j.Query;
import twitter4j.Status;
import twitter4j.Tweet;
import twitter4j.TwitterException;
import twitter4j.User;
import twitter4j.http.AccessToken;
import twitter4j.http.RequestToken;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Component for accessing Twitter.
*
*
* @author sharon@google.com (Sharon Perl) - added OAuth support
* @author ajcolter@gmail.com (Aubrey Colter) - added the twitter4j 2.2.6 jars
*/
@DesignerComponent(version = YaVersion.TWITTER_COMPONENT_VERSION,
description = "<p>A non-visible component that enables communication " +
"with <a href=\"http://www.twitter.com\" target=\"_blank\">Twitter</a>. " +
"Methods are included to enabling searching (<code>SearchTwitter</code>) " +
"or logging into (<code>Authorize</code>) Twitter. Once a user has authorized " +
"their Twitter account (and the authorization has been confirmed successful by the " +
"<code>IsAuthorized</code> event), many more operations are available:<ul>" +
"<li> Setting the status of the logged-in user (<code>SetStatus</code>)" +
" </li>" +
"<li> Directing a message to a specific user " +
" (<code>DirectMessage</code>)</li> " +
"<li> Receiving the most recent messages directed to the logged-in user " +
" (<code>RequestDirectMessages</code>)</li> " +
"<li> Following a specific user (<code>Follow</code>)</li>" +
"<li> Ceasing to follow a specific user (<code>StopFollowing</code>)</li>" +
"<li> Getting a list of users following the logged-in user " +
" (<code>RequestFollowers</code>)</li> " +
"<li> Getting the most recent messages of users followed by the " +
" logged-in user (<code>RequestFriendTimeline</code>)</li> " +
"<li> Getting the most recent mentions of the logged-in user " +
" (<code>RequestMentions</code>)</li></ul></p> " +
"<p>You must obtain a Comsumer Key and Consumer Secret for Twitter authorization " +
" specific to your app from http://twitter.com/oauth_clients/new </p>",
category = ComponentCategory.SOCIAL,
nonVisible = true,
iconName = "images/twitter.png")
@DesignerComponent(version = YaVersion.TWITTER_COMPONENT_VERSION, description = "<p>A non-visible component that enables communication "
+ "with <a href=\"http://www.twitter.com\" target=\"_blank\">Twitter</a>. "
+ "Once a user has logged into their Twitter account (and the authorization has been confirmed successful by the "
+ "<code>IsAuthorized</code> event), many more operations are available:<ul>"
+ "<li> Searching Twitter for tweets or labels (<code>SearchTwitter</code>)</li>"
+ "<li> Setting the status of the logged-in user (<code>SetStatus</code>)"
+ " </li>"
+ "<li> Directing a message to a specific user "
+ " (<code>DirectMessage</code>)</li> "
+ "<li> Receiving the most recent messages directed to the logged-in user "
+ " (<code>RequestDirectMessages</code>)</li> "
+ "<li> Following a specific user (<code>Follow</code>)</li>"
+ "<li> Ceasing to follow a specific user (<code>StopFollowing</code>)</li>"
+ "<li> Getting a list of users following the logged-in user "
+ " (<code>RequestFollowers</code>)</li> "
+ "<li> Getting the most recent messages of users followed by the "
+ " logged-in user (<code>RequestFriendTimeline</code>)</li> "
+ "<li> Getting the most recent mentions of the logged-in user "
+ " (<code>RequestMentions</code>)</li></ul></p> "
+ "<p>You must obtain a Comsumer Key and Consumer Secret for Twitter authorization "
+ " specific to your app from http://twitter.com/oauth_clients/new </p>", category = ComponentCategory.SOCIAL, nonVisible = true, iconName = "images/twitter.png")
@SimpleObject
@UsesPermissions(permissionNames = "android.permission.INTERNET")
@UsesLibraries(libraries = "twitter4j.jar")
public final class Twitter extends AndroidNonvisibleComponent
implements ActivityResultListener, Component {
public final class Twitter extends AndroidNonvisibleComponent implements
ActivityResultListener, Component {
private static final String ACCESS_TOKEN_TAG = "TwitterOauthAccessToken";
private static final String ACCESS_SECRET_TAG = "TwitterOauthAccessSecret";
private static final String MAX_CHARACTERS = "160";
private static final String URL_HOST = "twitter";
private static final String CALLBACK_URL = Form.APPINVENTOR_URL_SCHEME + "://"
+ URL_HOST;
private static final String WEBVIEW_ACTIVITY_CLASS = WebViewActivity.class.getName();
// lock protects fields twitter, requestToken, accessToken, userId,
// sharedPreferences
private final Object lock = new Object();
private volatile twitter4j.Twitter twitter;
private volatile RequestToken requestToken;
private volatile AccessToken accessToken;
private volatile String userName = "";
private final SharedPreferences sharedPreferences;
// twitterLock synchronizes uses of any/all twitter4j.Twitter objects
// in this class. As far as I can tell, these objects are not thread-safe
private final Object twitterLock = new Object();
private static final String CALLBACK_URL = Form.APPINVENTOR_URL_SCHEME
+ "://" + URL_HOST;
private static final String WEBVIEW_ACTIVITY_CLASS = WebViewActivity.class
.getName();
// the following fields should only be accessed from the UI thread
private volatile String consumerKey = "";
private volatile String consumerSecret = "";
private String consumerKey = "";
private String consumerSecret = "";
private final List<String> mentions;
private final List<String> followers;
private final List<List<String>> timeline;
private final List<String> directMessages;
private final List<String> searchResults;
// the following final fields are not synchronized
// the following final fields are not synchronized -- twitter4j is thread
// safe as of 2.2.6
private twitter4j.Twitter twitter;
private RequestToken requestToken;
private AccessToken accessToken;
private String userName = "";
private final SharedPreferences sharedPreferences;
private final int requestCode;
private final ComponentContainer container;
private final Handler handler;
// TODO(sharon): twitter4j apparently has an asynchronous interface (AsynchTwitter).
// TODO(sharon): twitter4j apparently has an asynchronous interface
// (AsynchTwitter).
// We should consider whether it has any advantages over AsynchUtil.
/**
* The maximum number of mentions returned by the following methods:
*
* The maximum number of mentions returned by the following methods:
*
* <table>
* <tr>
* <td>component</td>
* <td>twitter4j library</td>
* <td>twitter API</td>
* </tr>
* <tr>
* <td>RequestMentions</td>
* <td>getMentions</td>
* <td>statuses/mentions</td>
* </tr>
* <tr>
* <td>RequestDirectMessages</td>
* <td>getDirectMessages</td>
* <td>direct_messages</td>
* </tr>
* <tr>
* <td>component</td>
* <td>twitter4j library</td>
* <td>twitter API</td>
* </tr>
* <tr>
* <td>RequestMentions</td>
* <td>getMentions</td>
* <td>statuses/mentions</td>
* </tr>
* <tr>
* <td>RequestDirectMessages</td>
* <td>getDirectMessages</td>
* <td>direct_messages</td>
* </tr>
* </table>
*/
private static final String MAX_MENTIONS_RETURNED = "20";
......@@ -154,14 +145,13 @@ public final class Twitter extends AndroidNonvisibleComponent
accessToken = retrieveAccessToken();
requestCode = form.registerForActivityResult(this);
}
}
/**
* Logs in to Twitter with a username and password.
*/
@SimpleFunction(userVisible = false,
description = "Twitter's API no longer supports login via username and " +
"password. Use the Authorize call instead.")
@SimpleFunction(userVisible = false, description = "Twitter's API no longer supports login via username and "
+ "password. Use the Authorize call instead.")
public void Login(String username, String password) {
form.dispatchErrorOccurredEvent(this, "Login",
ErrorMessages.ERROR_TWITTER_UNSUPPORTED_LOGIN_FUNCTION);
......@@ -170,44 +160,38 @@ public final class Twitter extends AndroidNonvisibleComponent
/**
* Indicates when the login has been successful.
*/
@SimpleEvent(description =
"This event is raised after the program calls " +
"<code>Authorize</code> if the authorization was successful. " +
"It is also called after a call to <code>CheckAuthorized</code> " +
"if we already have a valid access token. " +
"After this event has been raised, any other method for this " +
"component can be called.")
@SimpleEvent(description = "This event is raised after the program calls "
+ "<code>Authorize</code> if the authorization was successful. "
+ "It is also called after a call to <code>CheckAuthorized</code> "
+ "if we already have a valid access token. "
+ "After this event has been raised, any other method for this "
+ "component can be called.")
public void IsAuthorized() {
EventDispatcher.dispatchEvent(this, "IsAuthorized");
}
@SimpleProperty(
category = PropertyCategory.BEHAVIOR,
description = "The user name of the authorized user. Empty if " +
"there is no authorized user.")
@SimpleProperty(category = PropertyCategory.BEHAVIOR, description = "The user name of the authorized user. Empty if "
+ "there is no authorized user.")
public String Username() {
synchronized (lock) {
return userName;
}
return userName;
}
/**
* ConsumerKey property getter method.
*/
@SimpleProperty(
category = PropertyCategory.BEHAVIOR)
@SimpleProperty(category = PropertyCategory.BEHAVIOR)
public String ConsumerKey() {
return consumerKey;
}
/**
* ConsumerKey property setter method: sets the consumer key to be used
* when authorizing with Twitter via OAuth.
*
* @param consumerKey the key for use in Twitter OAuth
* ConsumerKey property setter method: sets the consumer key to be used when
* authorizing with Twitter via OAuth.
*
* @param consumerKey
* the key for use in Twitter OAuth
*/
@DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_STRING,
defaultValue = "")
@DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_STRING, defaultValue = "")
@SimpleProperty
public void ConsumerKey(String consumerKey) {
this.consumerKey = consumerKey;
......@@ -216,8 +200,7 @@ public final class Twitter extends AndroidNonvisibleComponent
/**
* ConsumerSecret property getter method.
*/
@SimpleProperty(
category = PropertyCategory.BEHAVIOR)
@SimpleProperty(category = PropertyCategory.BEHAVIOR)
public String ConsumerSecret() {
return consumerSecret;
}
......@@ -225,11 +208,11 @@ public final class Twitter extends AndroidNonvisibleComponent
/**
* ConsumerSecret property setter method: sets the consumer secret to be used
* when authorizing with Twitter via OAuth.
*
* @param consumerSecret the secret for use in Twitter OAuth
*
* @param consumerSecret
* the secret for use in Twitter OAuth
*/
@DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_STRING,
defaultValue = "")
@DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_STRING, defaultValue = "")
@SimpleProperty
public void ConsumerSecret(String consumerSecret) {
this.consumerSecret = consumerSecret;
......@@ -238,9 +221,8 @@ public final class Twitter extends AndroidNonvisibleComponent
/**
* Authenticate to Twitter using OAuth
*/
@SimpleFunction(
description = "Redirects user to login to Twitter via the Web browser using " +
"the OAuth protocol if we don't already have authorization.")
@SimpleFunction(description = "Redirects user to login to Twitter via the Web browser using "
+ "the OAuth protocol if we don't already have authorization.")
public void Authorize() {
if (consumerKey.length() == 0 || consumerSecret.length() == 0) {
form.dispatchErrorOccurredEvent(this, "Authorize",
......@@ -249,12 +231,8 @@ public final class Twitter extends AndroidNonvisibleComponent
}
final String myConsumerKey = consumerKey;
final String myConsumerSecret = consumerSecret;
final twitter4j.Twitter myTwitter;
synchronized (lock) {
if (twitter == null) {
twitter = new twitter4j.Twitter();
}
myTwitter = twitter;
if (twitter == null) {
twitter = new TwitterFactory().getInstance();
}
AsynchUtil.runAsynchronously(new Runnable() {
public void run() {
......@@ -270,23 +248,23 @@ public final class Twitter extends AndroidNonvisibleComponent
try {
// potentially time-consuming calls
RequestToken newRequestToken;
synchronized (twitterLock) {
myTwitter.setOAuthConsumer(myConsumerKey, myConsumerSecret);
newRequestToken = myTwitter.getOAuthRequestToken(CALLBACK_URL);
}
twitter.setOAuthConsumer(myConsumerKey, myConsumerSecret);
newRequestToken = twitter.getOAuthRequestToken(CALLBACK_URL);
String authURL = newRequestToken.getAuthorizationURL();
synchronized (lock) {
requestToken = newRequestToken; // request token will be needed to get access token
}
Intent browserIntent = new Intent(Intent.ACTION_MAIN, Uri.parse(authURL));
browserIntent.setClassName(container.$context(), WEBVIEW_ACTIVITY_CLASS);
container.$context().startActivityForResult(browserIntent, requestCode);
requestToken = newRequestToken; // request token will be
// needed to get access token
Intent browserIntent = new Intent(Intent.ACTION_MAIN, Uri
.parse(authURL));
browserIntent.setClassName(container.$context(),
WEBVIEW_ACTIVITY_CLASS);
container.$context().startActivityForResult(browserIntent,
requestCode);
} catch (TwitterException e) {
Log.i("Twitter", "Got exception: " + e.getMessage());
e.printStackTrace();
form.dispatchErrorOccurredEvent(Twitter.this, "Authorize",
ErrorMessages.ERROR_TWITTER_EXCEPTION, e.getMessage());
DeAuthorize(); // clean up
DeAuthorize(); // clean up
}
}
});
......@@ -295,9 +273,8 @@ public final class Twitter extends AndroidNonvisibleComponent
/**
* Check whether we already have a valid Twitter access token
*/
@SimpleFunction(
description = "Checks whether we already have access, and if so, causes "
+ "IsAuthorized event handler to be called.")
@SimpleFunction(description = "Checks whether we already have access, and if so, causes "
+ "IsAuthorized event handler to be called.")
public void CheckAuthorized() {
final String myConsumerKey = consumerKey;
final String myConsumerSecret = consumerSecret;
......@@ -326,33 +303,24 @@ public final class Twitter extends AndroidNonvisibleComponent
if (uri != null) {
Log.i("Twitter", "Intent URI: " + uri.toString());
final String oauthVerifier = uri.getQueryParameter("oauth_verifier");
final RequestToken myRequestToken;
final twitter4j.Twitter myTwitter;
synchronized (lock) {
myRequestToken = requestToken;
myTwitter = twitter;
}
if (myTwitter == null) {
if (twitter == null) {
Log.e("Twitter", "twitter field is unexpectedly null");
new RuntimeException().printStackTrace();
form.dispatchErrorOccurredEvent(this, "Authorize",
ErrorMessages.ERROR_TWITTER_UNABLE_TO_GET_ACCESS_TOKEN,
"internal error: can't access Twitter library");
}
if (myRequestToken != null && oauthVerifier != null && oauthVerifier.length() != 0) {
if (requestToken != null && oauthVerifier != null
&& oauthVerifier.length() != 0) {
AsynchUtil.runAsynchronously(new Runnable() {
public void run() {
try {
AccessToken resultAccessToken;
synchronized (twitterLock) {
resultAccessToken = myTwitter.getOAuthAccessToken(myRequestToken,
oauthVerifier);
}
synchronized (lock) {
accessToken = resultAccessToken;
userName = accessToken.getScreenName();
saveAccessToken(resultAccessToken);
}
resultAccessToken = twitter.getOAuthAccessToken(requestToken,
oauthVerifier);
accessToken = resultAccessToken;
userName = accessToken.getScreenName();
saveAccessToken(resultAccessToken);
handler.post(new Runnable() {
@Override
public void run() {
......@@ -363,25 +331,27 @@ public final class Twitter extends AndroidNonvisibleComponent
Log.e("Twitter", "Got exception: " + e.getMessage());
e.printStackTrace();
form.dispatchErrorOccurredEvent(Twitter.this, "Authorize",
ErrorMessages.ERROR_TWITTER_UNABLE_TO_GET_ACCESS_TOKEN, e.getMessage());
DeAuthorize(); // clean up
ErrorMessages.ERROR_TWITTER_UNABLE_TO_GET_ACCESS_TOKEN,
e.getMessage());
DeAuthorize(); // clean up
}
}
});
} else {
form.dispatchErrorOccurredEvent(this, "Authorize",
ErrorMessages.ERROR_TWITTER_AUTHORIZATION_FAILED);
DeAuthorize(); // clean up
DeAuthorize(); // clean up
}
} else {
Log.e("Twitter", "uri retured from WebView activity was unexpectedly null");
Log.e("Twitter",
"uri retured from WebView activity was unexpectedly null");
}
} else {
Log.e("Twitter", "intent retured from WebView activity was unexpectedly null");
Log.e("Twitter",
"intent retured from WebView activity was unexpectedly null");
}
}
// Call with twitterLock held
private void saveAccessToken(AccessToken accessToken) {
final SharedPreferences.Editor sharedPrefsEditor = sharedPreferences.edit();
if (accessToken == null) {
......@@ -389,12 +359,12 @@ public final class Twitter extends AndroidNonvisibleComponent
sharedPrefsEditor.remove(ACCESS_SECRET_TAG);
} else {
sharedPrefsEditor.putString(ACCESS_TOKEN_TAG, accessToken.getToken());
sharedPrefsEditor.putString(ACCESS_SECRET_TAG, accessToken.getTokenSecret());
sharedPrefsEditor.putString(ACCESS_SECRET_TAG,
accessToken.getTokenSecret());
}
sharedPrefsEditor.commit();
}
// call with twitterLock held
private AccessToken retrieveAccessToken() {
String token = sharedPreferences.getString(ACCESS_TOKEN_TAG, "");
String secret = sharedPreferences.getString(ACCESS_SECRET_TAG, "");
......@@ -407,49 +377,43 @@ public final class Twitter extends AndroidNonvisibleComponent
/**
* Remove authentication for this app instance
*/
@SimpleFunction(
description = "Removes Twitter authorization from this running app instance")
@SimpleFunction(description = "Removes Twitter authorization from this running app instance")
public void DeAuthorize() {
final twitter4j.Twitter oldTwitter;
synchronized (lock) {
requestToken = null;
accessToken = null;
userName = "";
oldTwitter = twitter;
twitter = null; // setting twitter to null gives us a quick check
// that we don't have an authorized version around.
saveAccessToken(accessToken);
}
requestToken = null;
accessToken = null;
userName = "";
oldTwitter = twitter;
twitter = null; // setting twitter to null gives us a quick check
// that we don't have an authorized version around.
saveAccessToken(accessToken);
// clear the access token from the old twitter instance, just in case
// someone stashed it away.
if (oldTwitter != null) {
synchronized (twitterLock) {
oldTwitter.setOAuthAccessToken(null);
}
oldTwitter.setOAuthAccessToken(null);
}
}
/**
* Sets the status of the currently logged in user.
*/
@SimpleFunction(
description = "This updates the logged-in user's status to the " +
"specified Text, which will be trimmed if it exceeds " +
MAX_CHARACTERS + " characters. " +
"<p><u>Requirements</u>: This should only be called after the " +
"<code>IsAuthorized</code> event has been raised, indicating that the " +
"user has successfully logged in to Twitter.</p>")
@SimpleFunction(description = "This updates the logged-in user's status to the "
+ "specified Text, which will be trimmed if it exceeds "
+ MAX_CHARACTERS
+ " characters. "
+ "<p><u>Requirements</u>: This should only be called after the "
+ "<code>IsAuthorized</code> event has been raised, indicating that the "
+ "user has successfully logged in to Twitter.</p>")
public void SetStatus(final String status) {
final twitter4j.Twitter myTwitter;
synchronized (lock) {
myTwitter = twitter;
}
if (myTwitter == null) {
if (twitter == null) {
form.dispatchErrorOccurredEvent(this, "SetStatus",
ErrorMessages.ERROR_TWITTER_SET_STATUS_FAILED, "Need to login?");
return;
}
// TODO(sharon): note that if the user calls DeAuthorize immediately after
// TODO(sharon): note that if the user calls DeAuthorize immediately
// after
// SetStatus it is possible that the DeAuthorize call can slip in
// and invalidate the authorization credentials for myTwitter, causing
// the call below to fail. If we want to prevent this we could consider
......@@ -457,9 +421,7 @@ public final class Twitter extends AndroidNonvisibleComponent
AsynchUtil.runAsynchronously(new Runnable() {
public void run() {
try {
synchronized (twitterLock) {
myTwitter.updateStatus(status);
}
twitter.updateStatus(status);
} catch (TwitterException e) {
form.dispatchErrorOccurredEvent(Twitter.this, "SetStatus",
ErrorMessages.ERROR_TWITTER_SET_STATUS_FAILED, e.getMessage());
......@@ -471,41 +433,38 @@ public final class Twitter extends AndroidNonvisibleComponent
/**
* Gets the most recent messages where your username is mentioned.
*/
@SimpleFunction(
description = "Requests the " + MAX_MENTIONS_RETURNED + " most " +
"recent mentions of the logged-in user. When the mentions have been " +
"retrieved, the system will raise the <code>MentionsReceived</code> " +
"event and set the <code>Mentions</code> property to the list of " +
"mentions." +
"<p><u>Requirements</u>: This should only be called after the " +
"<code>IsAuthorized</code> event has been raised, indicating that the " +
"user has successfully logged in to Twitter.</p>")
@SimpleFunction(description = "Requests the " + MAX_MENTIONS_RETURNED
+ " most "
+ "recent mentions of the logged-in user. When the mentions have been "
+ "retrieved, the system will raise the <code>MentionsReceived</code> "
+ "event and set the <code>Mentions</code> property to the list of "
+ "mentions."
+ "<p><u>Requirements</u>: This should only be called after the "
+ "<code>IsAuthorized</code> event has been raised, indicating that the "
+ "user has successfully logged in to Twitter.</p>")
public void RequestMentions() {
final twitter4j.Twitter myTwitter;
synchronized (lock) {
myTwitter = twitter;
}
if (myTwitter == null) {
if (twitter == null) {
form.dispatchErrorOccurredEvent(this, "RequestMentions",
ErrorMessages.ERROR_TWITTER_REQUEST_MENTIONS_FAILED, "Need to login?");
return;
}
AsynchUtil.runAsynchronously(new Runnable() {
List<Status> replies = Collections.emptyList();
public void run() {
try {
synchronized (twitterLock) {
replies = myTwitter.getMentions();
}
replies = twitter.getMentions();
} catch (TwitterException e) {
form.dispatchErrorOccurredEvent(Twitter.this, "RequestMentions",
ErrorMessages.ERROR_TWITTER_REQUEST_MENTIONS_FAILED, e.getMessage());
ErrorMessages.ERROR_TWITTER_REQUEST_MENTIONS_FAILED,
e.getMessage());
} finally {
handler.post(new Runnable() {
public void run() {
mentions.clear();
for (Status status : replies) {
mentions.add(status.getUser().getScreenName() + " " + status.getText());
mentions.add(status.getUser().getScreenName() + " "
+ status.getText());
}
MentionsReceived(mentions);
}
......@@ -519,28 +478,24 @@ public final class Twitter extends AndroidNonvisibleComponent
* Indicates when all the mentions requested through
* {@link #RequestMentions()} have been received.
*/
@SimpleEvent(
description =
"This event is raised when the mentions of the logged-in user " +
"requested through <code>RequestMentions</code> have been retrieved. " +
"A list of the mentions can then be found in the <code>mentions</code> " +
"parameter or the <code>Mentions</code> property.")
@SimpleEvent(description = "This event is raised when the mentions of the logged-in user "
+ "requested through <code>RequestMentions</code> have been retrieved. "
+ "A list of the mentions can then be found in the <code>mentions</code> "
+ "parameter or the <code>Mentions</code> property.")
public void MentionsReceived(final List<String> mentions) {
EventDispatcher.dispatchEvent(this, "MentionsReceived", mentions);
}
@SimpleProperty(
category = PropertyCategory.BEHAVIOR,
description = "This property contains a list of mentions of the " +
"logged-in user. Initially, the list is empty. To set it, the " +
"program must: <ol> " +
"<li> Call the <code>Authorize</code> method.</li> " +
"<li> Wait for the <code>IsAuthorized</code> event.</li> " +
"<li> Call the <code>RequestMentions</code> method.</li> " +
"<li> Wait for the <code>MentionsReceived</code> event.</li></ol>\n" +
"The value of this property will then be set to the list of mentions " +
"(and will maintain its value until any subsequent calls to " +
"<code>RequestMentions</code>).")
@SimpleProperty(category = PropertyCategory.BEHAVIOR, description = "This property contains a list of mentions of the "
+ "logged-in user. Initially, the list is empty. To set it, the "
+ "program must: <ol> "
+ "<li> Call the <code>Authorize</code> method.</li> "
+ "<li> Wait for the <code>IsAuthorized</code> event.</li> "
+ "<li> Call the <code>RequestMentions</code> method.</li> "
+ "<li> Wait for the <code>MentionsReceived</code> event.</li></ol>\n"
+ "The value of this property will then be set to the list of mentions "
+ "(and will maintain its value until any subsequent calls to "
+ "<code>RequestMentions</code>).")
public List<String> Mentions() {
return mentions;
}
......@@ -550,28 +505,26 @@ public final class Twitter extends AndroidNonvisibleComponent
*/
@SimpleFunction
public void RequestFollowers() {
final String myUserId;
final twitter4j.Twitter myTwitter;
synchronized (lock) {
myUserId = userName;
myTwitter = twitter;
}
if (myTwitter == null || myUserId.length() == 0) {
if (twitter == null || userName.length() == 0) {
form.dispatchErrorOccurredEvent(this, "RequestFollowers",
ErrorMessages.ERROR_TWITTER_REQUEST_FOLLOWERS_FAILED, "Need to login?");
ErrorMessages.ERROR_TWITTER_REQUEST_FOLLOWERS_FAILED,
"Need to login?");
return;
}
AsynchUtil.runAsynchronously(new Runnable() {
List<User> friends = Collections.emptyList();
List<User> friends = new ArrayList<User>();
public void run() {
try {
synchronized (twitterLock) {
friends = myTwitter.getFollowersStatuses(myUserId, new Paging());
IDs followerIDs = twitter.getFollowersIDs(-1);
for (long id : followerIDs.getIDs()) {
// convert from the IDs returned to the User
friends.add(twitter.showUser(id));
}
} catch (TwitterException e) {
form.dispatchErrorOccurredEvent(Twitter.this, "RequestFollowers",
ErrorMessages.ERROR_TWITTER_REQUEST_FOLLOWERS_FAILED, e.getMessage());
ErrorMessages.ERROR_TWITTER_REQUEST_FOLLOWERS_FAILED,
e.getMessage());
} finally {
handler.post(new Runnable() {
public void run() {
......@@ -591,28 +544,25 @@ public final class Twitter extends AndroidNonvisibleComponent
* Indicates when all of your followers requested through
* {@link #RequestFollowers()} have been received.
*/
@SimpleEvent(
description = "This event is raised when all of the followers of the " +
"logged-in user requested through <code>RequestFollowers</code> have " +
"been retrieved. A list of the followers can then be found in the " +
"<code>followers</code> parameter or the <code>Followers</code> " +
"property.")
public void FollowersReceived(final List<String> followers) {
EventDispatcher.dispatchEvent(this, "FollowersReceived", followers);
}
@SimpleProperty(
category = PropertyCategory.BEHAVIOR,
description = "This property contains a list of the followers of the " +
"logged-in user. Initially, the list is empty. To set it, the " +
"program must: <ol> " +
"<li> Call the <code>Authorize</code> method.</li> " +
"<li> Wait for the <code>IsAuthorized</code> event.</li> " +
"<li> Call the <code>RequestFollowers</code> method.</li> " +
"<li> Wait for the <code>FollowersReceived</code> event.</li></ol>\n" +
"The value of this property will then be set to the list of " +
"followers (and maintain its value until any subsequent call to " +
"<code>RequestFollowers</code>).")
@SimpleEvent(description = "This event is raised when all of the followers of the "
+ "logged-in user requested through <code>RequestFollowers</code> have "
+ "been retrieved. A list of the followers can then be found in the "
+ "<code>followers</code> parameter or the <code>Followers</code> "
+ "property.")
public void FollowersReceived(final List<String> followers2) {
EventDispatcher.dispatchEvent(this, "FollowersReceived", followers2);
}
@SimpleProperty(category = PropertyCategory.BEHAVIOR, description = "This property contains a list of the followers of the "
+ "logged-in user. Initially, the list is empty. To set it, the "
+ "program must: <ol> "
+ "<li> Call the <code>Authorize</code> method.</li> "
+ "<li> Wait for the <code>IsAuthorized</code> event.</li> "
+ "<li> Call the <code>RequestFollowers</code> method.</li> "
+ "<li> Wait for the <code>FollowersReceived</code> event.</li></ol>\n"
+ "The value of this property will then be set to the list of "
+ "followers (and maintain its value until any subsequent call to "
+ "<code>RequestFollowers</code>).")
public List<String> Followers() {
return followers;
}
......@@ -620,23 +570,20 @@ public final class Twitter extends AndroidNonvisibleComponent
/**
* Gets the most recent messages sent directly to you.
*/
@SimpleFunction(
description = "Requests the " + MAX_MENTIONS_RETURNED + " most " +
"recent direct messages sent to the logged-in user. When the " +
"messages have been retrieved, the system will raise the " +
"<code>DirectMessagesReceived</code> event and set the " +
"<code>DirectMessages</code> property to the list of messages." +
"<p><u>Requirements</u>: This should only be called after the " +
"<code>IsAuthorized</code> event has been raised, indicating that the " +
"user has successfully logged in to Twitter.</p>")
@SimpleFunction(description = "Requests the " + MAX_MENTIONS_RETURNED
+ " most "
+ "recent direct messages sent to the logged-in user. When the "
+ "messages have been retrieved, the system will raise the "
+ "<code>DirectMessagesReceived</code> event and set the "
+ "<code>DirectMessages</code> property to the list of messages."
+ "<p><u>Requirements</u>: This should only be called after the "
+ "<code>IsAuthorized</code> event has been raised, indicating that the "
+ "user has successfully logged in to Twitter.</p>")
public void RequestDirectMessages() {
final twitter4j.Twitter myTwitter;
synchronized (lock) {
myTwitter = twitter;
}
if (myTwitter == null) {
if (twitter == null) {
form.dispatchErrorOccurredEvent(this, "RequestDirectMessages",
ErrorMessages.ERROR_TWITTER_REQUEST_DIRECT_MESSAGES_FAILED, "Need to login?");
ErrorMessages.ERROR_TWITTER_REQUEST_DIRECT_MESSAGES_FAILED,
"Need to login?");
return;
}
AsynchUtil.runAsynchronously(new Runnable() {
......@@ -645,19 +592,20 @@ public final class Twitter extends AndroidNonvisibleComponent
@Override
public void run() {
try {
synchronized (twitterLock) {
messages = myTwitter.getDirectMessages();
}
messages = twitter.getDirectMessages();
} catch (TwitterException e) {
form.dispatchErrorOccurredEvent(Twitter.this, "RequestDirectMessages",
ErrorMessages.ERROR_TWITTER_REQUEST_DIRECT_MESSAGES_FAILED, e.getMessage());
form.dispatchErrorOccurredEvent(Twitter.this,
"RequestDirectMessages",
ErrorMessages.ERROR_TWITTER_REQUEST_DIRECT_MESSAGES_FAILED,
e.getMessage());
} finally {
handler.post(new Runnable() {
@Override
public void run() {
directMessages.clear();
for (DirectMessage message : messages) {
directMessages.add(message.getSenderScreenName() + " " + message.getText());
directMessages.add(message.getSenderScreenName() + " "
+ message.getText());
}
DirectMessagesReceived(directMessages);
}
......@@ -672,29 +620,26 @@ public final class Twitter extends AndroidNonvisibleComponent
* Indicates when all the direct messages requested through
* {@link #RequestDirectMessages()} have been received.
*/
@SimpleEvent(
description = "This event is raised when the recent messages " +
"requested through <code>RequestDirectMessages</code> have " +
"been retrieved. A list of the messages can then be found in the " +
"<code>messages</code> parameter or the <code>Messages</code> " +
"property.")
@SimpleEvent(description = "This event is raised when the recent messages "
+ "requested through <code>RequestDirectMessages</code> have "
+ "been retrieved. A list of the messages can then be found in the "
+ "<code>messages</code> parameter or the <code>Messages</code> "
+ "property.")
public void DirectMessagesReceived(final List<String> messages) {
EventDispatcher.dispatchEvent(this, "DirectMessagesReceived", messages);
}
@SimpleProperty(
category = PropertyCategory.BEHAVIOR,
description = "This property contains a list of the most recent " +
"messages mentioning the logged-in user. Initially, the list is " +
"empty. To set it, the program must: <ol> " +
"<li> Call the <code>Authorize</code> method.</li> " +
"<li> Wait for the <code>Authorized</code> event.</li> " +
"<li> Call the <code>RequestDirectMessages</code> method.</li> " +
"<li> Wait for the <code>DirectMessagesReceived</code> event.</li>" +
"</ol>\n" +
"The value of this property will then be set to the list of direct " +
"messages retrieved (and maintain that value until any subsequent " +
"call to <code>RequestDirectMessages</code>).")
@SimpleProperty(category = PropertyCategory.BEHAVIOR, description = "This property contains a list of the most recent "
+ "messages mentioning the logged-in user. Initially, the list is "
+ "empty. To set it, the program must: <ol> "
+ "<li> Call the <code>Authorize</code> method.</li> "
+ "<li> Wait for the <code>Authorized</code> event.</li> "
+ "<li> Call the <code>RequestDirectMessages</code> method.</li> "
+ "<li> Wait for the <code>DirectMessagesReceived</code> event.</li>"
+ "</ol>\n"
+ "The value of this property will then be set to the list of direct "
+ "messages retrieved (and maintain that value until any subsequent "
+ "call to <code>RequestDirectMessages</code>).")
public List<String> DirectMessages() {
return directMessages;
}
......@@ -702,29 +647,23 @@ public final class Twitter extends AndroidNonvisibleComponent
/**
* Sends a direct message to a specified username.
*/
@SimpleFunction(
description = "This sends a direct (private) message to the specified " +
"user. The message will be trimmed if it exceeds " + MAX_CHARACTERS +
"characters. " +
"<p><u>Requirements</u>: This should only be called after the " +
"<code>IsAuthorized</code> event has been raised, indicating that the " +
"user has successfully logged in to Twitter.</p>")
@SimpleFunction(description = "This sends a direct (private) message to the specified "
+ "user. The message will be trimmed if it exceeds "
+ MAX_CHARACTERS
+ "characters. "
+ "<p><u>Requirements</u>: This should only be called after the "
+ "<code>IsAuthorized</code> event has been raised, indicating that the "
+ "user has successfully logged in to Twitter.</p>")
public void DirectMessage(final String user, final String message) {
final twitter4j.Twitter myTwitter;
synchronized (lock) {
if (twitter == null) {
form.dispatchErrorOccurredEvent(this, "DirectMessage",
ErrorMessages.ERROR_TWITTER_DIRECT_MESSAGE_FAILED, "Need to login?");
return;
}
myTwitter = twitter;
if (twitter == null) {
form.dispatchErrorOccurredEvent(this, "DirectMessage",
ErrorMessages.ERROR_TWITTER_DIRECT_MESSAGE_FAILED, "Need to login?");
return;
}
AsynchUtil.runAsynchronously(new Runnable() {
public void run() {
try {
synchronized (twitterLock) {
myTwitter.sendDirectMessage(user, message);
}
twitter.sendDirectMessage(user, message);
} catch (TwitterException e) {
form.dispatchErrorOccurredEvent(Twitter.this, "DirectMessage",
ErrorMessages.ERROR_TWITTER_DIRECT_MESSAGE_FAILED, e.getMessage());
......@@ -738,21 +677,19 @@ public final class Twitter extends AndroidNonvisibleComponent
*/
@SimpleFunction
public void Follow(final String user) {
final twitter4j.Twitter myTwitter;
synchronized (lock) {
if (twitter == null) {
form.dispatchErrorOccurredEvent(this, "Follow",
ErrorMessages.ERROR_TWITTER_FOLLOW_FAILED, "Need to login?");
return;
}
myTwitter = twitter;
if (twitter == null) {
form.dispatchErrorOccurredEvent(this, "Follow",
ErrorMessages.ERROR_TWITTER_FOLLOW_FAILED, "Need to login?");
return;
}
AsynchUtil.runAsynchronously(new Runnable() {
public void run() {
try {
synchronized (twitterLock) {
myTwitter.enableNotification(user);
// existsFrienship tests whether userName follows user
if (!twitter.existsFriendship(userName, user)) {
twitter.createFriendship(user);
}
} catch (TwitterException e) {
form.dispatchErrorOccurredEvent(Twitter.this, "Follow",
ErrorMessages.ERROR_TWITTER_FOLLOW_FAILED, e.getMessage());
......@@ -766,11 +703,7 @@ public final class Twitter extends AndroidNonvisibleComponent
*/
@SimpleFunction
public void StopFollowing(final String user) {
final twitter4j.Twitter myTwitter;
synchronized (lock) {
myTwitter = twitter;
}
if (myTwitter == null) {
if (twitter == null) {
form.dispatchErrorOccurredEvent(this, "StopFollowing",
ErrorMessages.ERROR_TWITTER_STOP_FOLLOWING_FAILED, "Need to login?");
return;
......@@ -778,8 +711,9 @@ public final class Twitter extends AndroidNonvisibleComponent
AsynchUtil.runAsynchronously(new Runnable() {
public void run() {
try {
synchronized (twitterLock) {
myTwitter.disableNotification(user);
// existsFrienship tests whether userName follows user
if (twitter.existsFriendship(userName, user)) {
twitter.destroyFriendship(user);
}
} catch (TwitterException e) {
form.dispatchErrorOccurredEvent(Twitter.this, "StopFollowing",
......@@ -790,17 +724,14 @@ public final class Twitter extends AndroidNonvisibleComponent
}
/**
* Gets the most recent 20 messages of usernames that you follow.
* Gets the most recent 20 messages in the user's timeline.
*/
@SimpleFunction
public void RequestFriendTimeline() {
final twitter4j.Twitter myTwitter;
synchronized (lock) {
myTwitter = twitter;
}
if (myTwitter == null) {
if (twitter == null) {
form.dispatchErrorOccurredEvent(this, "RequestFriendTimeline",
ErrorMessages.ERROR_TWITTER_REQUEST_FRIEND_TIMELINE_FAILED, "Need to login?");
ErrorMessages.ERROR_TWITTER_REQUEST_FRIEND_TIMELINE_FAILED,
"Need to login?");
return;
}
AsynchUtil.runAsynchronously(new Runnable() {
......@@ -808,10 +739,12 @@ public final class Twitter extends AndroidNonvisibleComponent
public void run() {
try {
messages = myTwitter.getFriendsTimeline();
messages = twitter.getHomeTimeline();
} catch (TwitterException e) {
form.dispatchErrorOccurredEvent(Twitter.this, "RequestFriendTimeline",
ErrorMessages.ERROR_TWITTER_REQUEST_FRIEND_TIMELINE_FAILED, e.getMessage());
form.dispatchErrorOccurredEvent(Twitter.this,
"RequestFriendTimeline",
ErrorMessages.ERROR_TWITTER_REQUEST_FRIEND_TIMELINE_FAILED,
e.getMessage());
} finally {
handler.post(new Runnable() {
public void run() {
......@@ -834,31 +767,28 @@ public final class Twitter extends AndroidNonvisibleComponent
* Indicates when the friend timeline requested through
* {@link #RequestFriendTimeline()} has been received.
*/
@SimpleEvent(
description = "This event is raised when the messages " +
"requested through <code>RequestFriendTimeline</code> have " +
"been retrieved. The <code>timeline</code> parameter and the " +
"<code>Timeline</code> property will contain a list of lists, where " +
"each sub-list contains a status update of the form (username message)")
@SimpleEvent(description = "This event is raised when the messages "
+ "requested through <code>RequestFriendTimeline</code> have "
+ "been retrieved. The <code>timeline</code> parameter and the "
+ "<code>Timeline</code> property will contain a list of lists, where "
+ "each sub-list contains a status update of the form (username message)")
public void FriendTimelineReceived(final List<List<String>> timeline) {
EventDispatcher.dispatchEvent(this, "FriendTimelineReceived", timeline);
}
@SimpleProperty(
category = PropertyCategory.BEHAVIOR,
description = "This property contains the 20 most recent messages of " +
"users being followed. Initially, the list is empty. To set it, " +
"the program must: <ol> " +
"<li> Call the <code>Authorize</code> method.</li> " +
"<li> Wait for the <code>IsAuthorized</code> event.</li> " +
"<li> Specify users to follow with one or more calls to the " +
"<code>Follow</code> method.</li> " +
"<li> Call the <code>RequestFriendTimeline</code> method.</li> " +
"<li> Wait for the <code>FriendTimelineReceived</code> event.</li> " +
"</ol>\n" +
"The value of this property will then be set to the list of messages " +
"(and maintain its value until any subsequent call to " +
"<code>RequestFriendTimeline</code>.")
@SimpleProperty(category = PropertyCategory.BEHAVIOR, description = "This property contains the 20 most recent messages of "
+ "users being followed. Initially, the list is empty. To set it, "
+ "the program must: <ol> "
+ "<li> Call the <code>Authorize</code> method.</li> "
+ "<li> Wait for the <code>IsAuthorized</code> event.</li> "
+ "<li> Specify users to follow with one or more calls to the "
+ "<code>Follow</code> method.</li> "
+ "<li> Call the <code>RequestFriendTimeline</code> method.</li> "
+ "<li> Wait for the <code>FriendTimelineReceived</code> event.</li> "
+ "</ol>\n"
+ "The value of this property will then be set to the list of messages "
+ "(and maintain its value until any subsequent call to "
+ "<code>RequestFriendTimeline</code>.")
public List<List<String>> FriendTimeline() {
return timeline;
}
......@@ -868,24 +798,12 @@ public final class Twitter extends AndroidNonvisibleComponent
*/
@SimpleFunction
public void SearchTwitter(final String query) {
final twitter4j.Twitter myTwitter;
synchronized (lock) {
if (twitter == null) {
// We don't need to login so open an anonymous Twitter just for this
// operation
myTwitter = new twitter4j.Twitter();
} else {
myTwitter = twitter;
}
}
AsynchUtil.runAsynchronously(new Runnable() {
List<Tweet> tweets = Collections.emptyList();
public void run() {
try {
synchronized (twitterLock) {
tweets = myTwitter.search(new Query(query)).getTweets();
}
tweets = twitter.search(new Query(query)).getTweets();
} catch (TwitterException e) {
form.dispatchErrorOccurredEvent(Twitter.this, "SearchTwitter",
ErrorMessages.ERROR_TWITTER_SEARCH_FAILED, e.getMessage());
......@@ -908,69 +826,60 @@ public final class Twitter extends AndroidNonvisibleComponent
* Indicates when the search requested through {@link #SearchTwitter(String)}
* has completed.
*/
@SimpleEvent(
description = "This event is raised when the results of the search " +
"requested through <code>SearchSuccessful</code> have " +
"been retrieved. A list of the results can then be found in the " +
"<code>results</code> parameter or the <code>Results</code> " +
"property.")
@SimpleEvent(description = "This event is raised when the results of the search "
+ "requested through <code>SearchSuccessful</code> have "
+ "been retrieved. A list of the results can then be found in the "
+ "<code>results</code> parameter or the <code>Results</code> "
+ "property.")
public void SearchSuccessful(final List<String> searchResults) {
EventDispatcher.dispatchEvent(this, "SearchSuccessful", searchResults);
}
@SimpleProperty(
category = PropertyCategory.BEHAVIOR,
description = "This property, which is initially empty, is set to a " +
"list of search results after the program: <ol>" +
"<li>Calls the <code>SearchTwitter</code> method.</li> " +
"<li>Waits for the <code>SearchSuccessful</code> event.</li></ol>\n" +
"The value of the property will then be the same as the parameter to " +
"<code>SearchSuccessful</code>. Note that it is not necessary to " +
"call the <code>Authorize</code> method before calling " +
"<code>SearchTwitter</code>.")
@SimpleProperty(category = PropertyCategory.BEHAVIOR, description = "This property, which is initially empty, is set to a "
+ "list of search results after the program: <ol>"
+ "<li>Calls the <code>SearchTwitter</code> method.</li> "
+ "<li>Waits for the <code>SearchSuccessful</code> event.</li></ol>\n"
+ "The value of the property will then be the same as the parameter to "
+ "<code>SearchSuccessful</code>. Note that it is not necessary to "
+ "call the <code>Authorize</code> method before calling "
+ "<code>SearchTwitter</code>.")
public List<String> SearchResults() {
return searchResults;
}
/**
* Check whether accessToken is valid. This call can take a while
* because it makes a request to Twitter, so it should be called from a
* non-UI thread.
* Check whether accessToken is valid. This call can take a while because it
* makes a request to Twitter, so it should be called from a non-UI thread.
*
* @return true if accessToken is valid, false otherwise.
*/
private boolean checkAccessToken(String myConsumerKey, String myConsumerSecret) {
twitter4j.Twitter myTwitter;
AccessToken myAccessToken;
synchronized (lock) {
if (accessToken == null) {
return false;
}
myAccessToken = accessToken;
if (twitter == null) {
twitter = new twitter4j.Twitter();
}
myTwitter = twitter;
if (accessToken == null) {
return false;
}
if (twitter == null) {
twitter = new TwitterFactory().getInstance();
}
try {
User user;
synchronized (twitterLock) {
myTwitter.setOAuthConsumer(myConsumerKey, myConsumerSecret);
myTwitter.setOAuthAccessToken(myAccessToken);
user = myTwitter.verifyCredentials();
}
synchronized (lock) {
userName = user.getScreenName();
}
user = twitter.verifyCredentials();
userName = user.getScreenName();
twitter.setOAuthConsumer(myConsumerKey, myConsumerSecret);
twitter.setOAuthAccessToken(accessToken);
Log.i("Twitter", "Saved accessToken is valid. UserId is " + userName);
return true; // already have access
return true; // already have access
} catch (TwitterException e) {
synchronized (lock) {
accessToken = null; // clear invalid token
userName = "";
saveAccessToken(accessToken);
}
Log.i("Twitter", "Saved access token is not valid---clearing it in shared prefs");
accessToken = null; // clear invalid token
userName = "";
saveAccessToken(accessToken);
Log.i("Twitter",
"Saved access token is not valid---clearing it in shared prefs");
return false;
} catch (IllegalStateException e) {
Log.i("Twitter",
"OAuth tokens already saved. Saved accessToken is valid. UserId is "
+ userName);
return true;
}
}
......
......@@ -944,17 +944,11 @@
This component allows users to interact with Twitter.
</p>
<p>
This non-visible component enables communication with Twitter. Methods are included
to enable searching (
<code>
SearchTwitter
</code>
) and logging in to Twitter
(
This non-visible component enables communication with Twitter. Once a user has logged in (
<code>
Authorize
</code>
). Once a user has logged in and the login has been
) and the login has been
confirmed successful by the
<code>
IsAuthorized
......@@ -963,6 +957,13 @@
following methods:
</p>
<ul>
<li>
Search Twitter for tweets or labels (
<code>
SearchTwitter
</code>
).
</li>
<li>
Set the status of the logged-in user (
<code>
......@@ -1007,7 +1008,7 @@
).
</li>
<li>
Get the most recent messages of followed users
Get the most recent messages in the user's timeline.
(
<code>
RequestFriendTimeline
......@@ -1024,8 +1025,8 @@
</li>
</ul>
<p>
In general, you invoke one of these methods and the result will bcome available
when the corresponding recipt event is signalled. For example, if you call
In general, you invoke one of these methods and the result will become available
when the corresponding receipt event is signaled. For example, if you call
<code>
RequestFollowers
</code>
......@@ -1034,10 +1035,9 @@
FollowersReceived
</code>
event will
be signalled when the list of followers is available. This might take time, because
the request is going over the web; the result might even never become available,
for example, if your device is not connected to the web or the Twitter web site is
down.
be signaled when the list of followers is available. This might take time, because
the request is going over the web; the result might even never become available if,
for example, your device is not connected to the web or the Twitter web site is down.
</p>
<p>
Login is handled using the OAuth protocol, as required by the Twitter API (
......@@ -1050,19 +1050,12 @@
<code>
Authorize
</code>
. This causes a Twitter login page to
be presented that will allow the user to login with their username and password if
necessary, and then return appropriate credentials to the Twitter component in the
. This loads a Twitter login page that will allow the user to login with their username and password, and then return appropriate credentials to the Twitter component in the
app. Once the Twitter component has the credentials the
<code>
IsAuthorized
</code>
event is raised to let the app know that it can proceed with Twitter API calls. The
only Twitter component method that does not require the user to be authenticated is
<code>
SearchTwitter
</code>
. An app retains Twitter credentials across invocations,
event is raised to let the app know that it can proceed with Twitter API calls. An app retains Twitter credentials across invocations,
so it may not be necessary for the user to login each time he or she uses the app.
Reinstalling an app will clear the credentials, as will calling the
<code>
......@@ -1085,7 +1078,7 @@
Important:
</b>
Most of the operation of the OAuth protocol is hidden inside the
Twitter component and you do not need to understand how it works in order to use
Twitter component and you do not need to understand how it works to use
the component. However, it is necessary for anyone building an app with the Twitter
component to supply a
<i>
......@@ -1320,7 +1313,7 @@
<code>
RequestMentions
</code>
has
have
been retrieved.
</dd>
<dt>
......@@ -1367,11 +1360,7 @@
</dt>
<dd>
Removes authorization from this running app instance. A user will have to
login again to use any Twitter method other than
<code>
SearchTwitter
</code>
.
login again to use any Twitter method.
</dd>
<dt>
<code>
......@@ -1412,7 +1401,7 @@
</dt>
<dd>
Retrieves the 20 most recent messages in your timeline. Returns a list of
tuples (sub-lists), where each tuple contains username and status message.
tuples (sub-lists), where each tuple contains a username and status message.
</dd>
<dt>
<code>
......@@ -1420,7 +1409,7 @@
</code>
</dt>
<dd>
Retrieves the most mentions of the user.
Retrieves the most recent mentions of the user.
</dd>
<dt>
<code>
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment