package org.wordpress.android;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import android.app.Application;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.preference.PreferenceManager;
import android.util.Log;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.ImageLoader;
import com.android.volley.toolbox.Volley;
import com.google.android.gcm.GCMRegistrar;
import com.wordpress.rest.Oauth;
import com.wordpress.rest.RestRequest;
import org.json.JSONException;
import org.json.JSONObject;
import org.xmlrpc.android.WPComXMLRPCApi;
import org.wordpress.android.lockmanager.AppLockManager;
import org.wordpress.android.models.Blog;
import org.wordpress.android.models.Comment;
import org.wordpress.android.models.Postable;
import org.wordpress.android.util.BitmapLruCache;
import org.wordpress.android.util.WPRestClient;
public class WordPress extends Application {
public static final String NOTES_CACHE = "notifications.json";
public static final String ACCESS_TOKEN_PREFERENCE = "wp_pref_wpcom_access_token";
public static final String WPCOM_USERNAME_PREFERENCE = "wp_pref_wpcom_username";
public static final String WPCOM_PASSWORD_PREFERENCE = "wp_pref_wpcom_password";
private static final String APP_ID_PROPERTY = "oauth.app_id";
private static final String APP_SECRET_PROPERTY = "oauth.app_secret";
private static final String APP_REDIRECT_PROPERTY = "oauth.redirect_uri";
public static String versionName;
public static Blog currentBlog;
public static Comment currentComment;
private static Postable currentPost;
public static WordPressDB wpDB;
public static OnPostUploadedListener onPostUploadedListener = null;
public static boolean postsShouldRefresh;
public static boolean shouldRestoreSelectedActivity;
public static WPRestClient restClient;
public static RequestQueue requestQueue;
public static ImageLoader imageLoader;
public static JSONObject latestNotes;
public static final String TAG = "WordPress";
@Override
public void onCreate() {
versionName = getVersionName();
wpDB = new WordPressDB(this);
// Volley networking setup
requestQueue = Volley.newRequestQueue(this);
int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
// Use a small slice of available memory for the image cache
int cacheSize = maxMemory / 32;
imageLoader = new ImageLoader(requestQueue, new BitmapLruCache(
cacheSize));
SharedPreferences settings = PreferenceManager
.getDefaultSharedPreferences(this);
if (settings.getInt("wp_pref_last_activity", -1) >= 0)
shouldRestoreSelectedActivity = true;
restClient = new WPRestClient(requestQueue, new OauthAuthenticator(),
settings.getString(ACCESS_TOKEN_PREFERENCE, null));
registerForCloudMessaging(this);
//Uncomment this line if you want to test the app locking feature
//AppLockManager.getInstance().enableDefaultAppLockIfAvailable(this);
loadNotifications(this);
super.onCreate();
}
public static void registerForCloudMessaging(Context ctx) {
if (WordPress.hasValidWPComCredentials(ctx)) {
String token = null;
try {
// Register for Google Cloud Messaging
GCMRegistrar.checkDevice(ctx);
GCMRegistrar.checkManifest(ctx);
token = GCMRegistrar.getRegistrationId(ctx);
String gcmId = Config.GCM_ID;
if (gcmId != null && token.equals("")) {
GCMRegistrar.register(ctx, gcmId);
} else {
// Send the token to WP.com in case it was invalidated
new WPComXMLRPCApi().registerWPComToken(ctx, token);
Log.v("WORDPRESS", "Already registered for GCM");
}
} catch (Exception e) {
Log.v("WORDPRESS",
"Could not register for GCM: " + e.getMessage());
}
}
}
public static Postable getCurrentPost() {
return currentPost;
}
public static void setCurrentPost(Postable currentPost) {
WordPress.currentPost = currentPost;
}
/**
* Get versionName from Manifest.xml
*
* @return versionName
*/
private String getVersionName() {
PackageManager pm = getPackageManager();
try {
PackageInfo pi = pm.getPackageInfo(getPackageName(), 0);
return pi.versionName;
} catch (NameNotFoundException e) {
return "";
}
}
public interface OnPostUploadedListener {
public abstract void OnPostUploaded();
}
public static void setOnPostUploadedListener(OnPostUploadedListener listener) {
onPostUploadedListener = listener;
}
public static void postUploaded() {
if (onPostUploadedListener != null) {
try {
onPostUploadedListener.OnPostUploaded();
} catch (Exception e) {
postsShouldRefresh = true;
}
} else {
postsShouldRefresh = true;
}
}
/**
* Get the currently active blog.
* <p>
* If the current blog is not already set, try and determine the last active
* blog from the last time the application was used. If we're not able to
* determine the last active blog, just select the first one.
*/
public static Blog getCurrentBlog() {
if (currentBlog == null) {
// attempt to restore the last active blog
setCurrentBlogToLastActive();
// fallback to just using the first blog
List<Map<String, Object>> accounts = WordPress.wpDB.getAccounts();
if (currentBlog == null && accounts.size() > 0) {
int id = Integer.valueOf(accounts.get(0).get("id").toString());
setCurrentBlog(id);
wpDB.updateLastBlogId(id);
}
}
return currentBlog;
}
/**
* Get the blog with the specified ID.
*
* @param id
* ID of the blog to retrieve.
* @return the blog with the specified ID, or null if blog could not be
* retrieved.
*/
public static Blog getBlog(int id) {
try {
Blog blog = new Blog(id);
return blog;
} catch (Exception e) {
return null;
}
}
/**
* Set the last active blog as the current blog.
*
* @return the current blog
*/
public static Blog setCurrentBlogToLastActive() {
List<Map<String, Object>> accounts = WordPress.wpDB.getAccounts();
int lastBlogId = WordPress.wpDB.getLastBlogId();
if (lastBlogId != -1) {
for (Map<String, Object> account : accounts) {
int id = Integer.valueOf(account.get("id").toString());
if (id == lastBlogId) {
setCurrentBlog(id);
}
}
}
return currentBlog;
}
/**
* Set the blog with the specified id as the current blog.
*
* @param id
* id of the blog to set as current
* @return the current blog
*/
public static Blog setCurrentBlog(int id) {
try {
currentBlog = new Blog(id);
} catch (Exception e) {
e.printStackTrace();
}
return currentBlog;
}
/**
* Restores notifications from cached file
*/
public static void loadNotifications(Context context) {
File file = new File(context.getCacheDir(), NOTES_CACHE);
BufferedReader buf = null;
StringBuilder json = null;
try {
buf = new BufferedReader(new FileReader(file));
json = new StringBuilder();
String line;
do {
line = buf.readLine();
json.append(line);
} while (line == null);
} catch (java.io.FileNotFoundException e) {
json = null;
Log.e(TAG, "No cached notes", e);
} catch (IOException e) {
json = null;
Log.e(TAG, "Could not read cached notes", e);
} finally {
try {
if (buf != null) {
buf.close();
}
} catch (IOException e) {
Log.e(TAG, "Couldn't close file buffer", e);
}
}
if (json == null) {
Log.d(TAG, "Could not find cached notes");
return;
}
try {
latestNotes = new JSONObject(json.toString());
} catch (JSONException e) {
latestNotes = null;
Log.e(TAG, "Failed to parse note json", e);
return;
}
Log.d(TAG, "Restored notes");
}
/**
* Refreshes latest notes and stores a copy of the response to disk.
*/
public static void refreshNotifications(final Context context,
final RestRequest.Listener listener,
final RestRequest.ErrorListener errorListener) {
restClient.getNotifications(new RestRequest.Listener() {
@Override
public void onResponse(JSONObject response) {
latestNotes = response;
File file = new File(context.getCacheDir(), NOTES_CACHE);
if (file.exists()) {
file.delete();
}
FileWriter writer = null;
try {
String json = response.toString();
writer = new FileWriter(file);
writer.write(json, 0, json.length());
writer.close();
Log.d(TAG, String.format("Wrote notes json to %s", file));
} catch (IOException e) {
Log.d(TAG, String.format(
"Failed to cache notifications to %s", file));
} finally {
try {
if (writer != null) {
writer.close();
}
} catch (IOException e) {
writer = null;
}
}
Log.d(TAG, String.format("Store file here %s", file));
if (listener != null) {
listener.onResponse(response);
}
}
}, new RestRequest.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
if (errorListener != null) {
errorListener.onErrorResponse(error);
}
}
});
}
/**
* Delete cached notifications, usually due to account logout
*/
public static void deleteCachedNotifications(Context context) {
File file = new File(context.getCacheDir(), NOTES_CACHE);
if (file.exists()) {
file.delete();
}
latestNotes = null;
}
/**
* Checks for WordPress.com credentials
*
* @return true if we have credentials or false if not
*/
public static boolean hasValidWPComCredentials(Context context) {
SharedPreferences settings = PreferenceManager
.getDefaultSharedPreferences(context);
String username = settings.getString(WPCOM_USERNAME_PREFERENCE, null);
String password = settings.getString(WPCOM_PASSWORD_PREFERENCE, null);
if (username != null && password != null)
return true;
else
return false;
}
/**
* Returns WordPress.com Auth Token
*
* @return String - The wpcom Auth token, or null if not authenticated.
*/
public static String getWPComAuthToken(Context context) {
SharedPreferences settings = PreferenceManager
.getDefaultSharedPreferences(context);
return settings.getString(WordPress.ACCESS_TOKEN_PREFERENCE, null);
}
class OauthAuthenticator implements WPRestClient.Authenticator {
private final RequestQueue mQueue = Volley
.newRequestQueue(WordPress.this);
@Override
public void authenticate(WPRestClient.Request request) {
// set the access token if we have one
if (!hasValidWPComCredentials(WordPress.this)) {
request.abort(new VolleyError("Missing WordPress.com Account"));
} else {
requestAccessToken(request);
}
}
public void requestAccessToken(final WPRestClient.Request request) {
final SharedPreferences settings = PreferenceManager
.getDefaultSharedPreferences(WordPress.this);
String username = settings.getString(WPCOM_USERNAME_PREFERENCE,
null);
String password = WordPressDB.decryptPassword(settings.getString(
WPCOM_PASSWORD_PREFERENCE, null));
Oauth oauth = new Oauth(Config.OAUTH_APP_ID,
Config.OAUTH_APP_SECRET, Config.OAUTH_REDIRECT_URI);
// make oauth volley request
Request oauthRequest = oauth.makeRequest(username, password,
new Oauth.Listener() {
@Override
public void onResponse(Oauth.Token token) {
settings.edit()
.putString(ACCESS_TOKEN_PREFERENCE,
token.toString()).commit();
request.setAccessToken(token);
request.send();
}
}, new Oauth.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
request.abort(error);
}
});
mQueue.add(oauthRequest);
// add oauth request to the request queue
}
}
/**
* Sign out from all accounts by clearing out the password, which will
* require user to sign in again
*/
public static void signOut(Context context) {
new WPComXMLRPCApi().unregisterWPComToken(context,
GCMRegistrar.getRegistrationId(context));
try {
GCMRegistrar.checkDevice(context);
GCMRegistrar.unregister(context);
} catch (Exception e) {
Log.v("WORDPRESS",
"Could not unregister for GCM: " + e.getMessage());
}
SharedPreferences.Editor editor = PreferenceManager
.getDefaultSharedPreferences(context).edit();
editor.remove(WordPress.WPCOM_USERNAME_PREFERENCE);
editor.remove(WordPress.WPCOM_PASSWORD_PREFERENCE);
editor.remove(WordPress.ACCESS_TOKEN_PREFERENCE);
editor.commit();
wpDB.deactivateAccounts();
wpDB.updateLastBlogId(-1);
currentBlog = null;
restClient.clearAccessToken();
}
}