/** * Copyright 2009 Joe LaPenna */ package com.joelapenna.foursquared; import com.joelapenna.foursquare.Foursquare; import com.joelapenna.foursquare.error.FoursquareError; import com.joelapenna.foursquare.error.FoursquareException; import com.joelapenna.foursquare.types.User; import com.joelapenna.foursquare.util.IconUtils; import com.joelapenna.foursquared.app.FoursquaredService; import com.joelapenna.foursquared.error.LocationException; import com.joelapenna.foursquared.location.BestLocationListener; import com.joelapenna.foursquared.location.LocationUtils; import com.joelapenna.foursquared.preferences.Preferences; import com.joelapenna.foursquared.util.JavaLoggingHandler; import com.joelapenna.foursquared.util.NullDiskCache; import com.joelapenna.foursquared.util.RemoteResourceManager; import android.app.Application; import android.appwidget.AppWidgetManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.location.Location; import android.location.LocationManager; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; import android.os.Message; import android.preference.PreferenceManager; import android.text.TextUtils; import android.util.Log; import java.io.File; import java.io.IOException; import java.util.Observer; import java.util.logging.Level; import java.util.logging.Logger; /** * @author Joe LaPenna (joe@joelapenna.com) */ public class Foursquared extends Application { private static final String TAG = "Foursquared"; private static final boolean DEBUG = FoursquaredSettings.DEBUG; static { Logger.getLogger("com.joelapenna.foursquare").addHandler(new JavaLoggingHandler()); Logger.getLogger("com.joelapenna.foursquare").setLevel(Level.ALL); } public static final String PACKAGE_NAME = "com.joelapenna.foursquared"; public static final String INTENT_ACTION_LOGGED_OUT = "com.joelapenna.foursquared.intent.action.LOGGED_OUT"; public static final String INTENT_ACTION_LOGGED_IN = "com.joelapenna.foursquared.intent.action.LOGGED_IN"; private String mVersion = null; private TaskHandler mTaskHandler; private HandlerThread mTaskThread; private SharedPreferences mPrefs; private RemoteResourceManager mRemoteResourceManager; private Foursquare mFoursquare; private BestLocationListener mBestLocationListener = new BestLocationListener(); private boolean mIsFirstRun; @Override public void onCreate() { Log.i(TAG, "Using Debug Server:\t" + FoursquaredSettings.USE_DEBUG_SERVER); Log.i(TAG, "Using Dumpcatcher:\t" + FoursquaredSettings.USE_DUMPCATCHER); Log.i(TAG, "Using Debug Log:\t" + DEBUG); mVersion = getVersionString(this); // Check if this is a new install by seeing if our preference file exists on disk. mIsFirstRun = checkIfIsFirstRun(); // Setup Prefs (to load dumpcatcher) mPrefs = PreferenceManager.getDefaultSharedPreferences(this); // Setup some defaults in our preferences if not set yet. Preferences.setupDefaults(mPrefs, getResources()); // If we're on a high density device, request higher res images. This singleton // is picked up by the parsers to replace their icon urls with high res versions. float screenDensity = getApplicationContext().getResources().getDisplayMetrics().density; IconUtils.get().setRequestHighDensityIcons(screenDensity > 1.0f); // Setup Dumpcatcher - We've outgrown this infrastructure but we'll // leave its calls in place for the day that someone pays for some // appengine quota. // if (FoursquaredSettings.USE_DUMPCATCHER) { // Resources resources = getResources(); // new DumpcatcherHelper(Preferences.createUniqueId(mPrefs), resources); // } // Sometimes we want the application to do some work on behalf of the // Activity. Lets do that // asynchronously. mTaskThread = new HandlerThread(TAG + "-AsyncThread"); mTaskThread.start(); mTaskHandler = new TaskHandler(mTaskThread.getLooper()); // Set up storage cache. loadResourceManagers(); // Catch sdcard state changes new MediaCardStateBroadcastReceiver().register(); // Catch logins or logouts. new LoggedInOutBroadcastReceiver().register(); // Log into Foursquare, if we can. loadFoursquare(); } public boolean isReady() { return getFoursquare().hasLoginAndPassword() && !TextUtils.isEmpty(getUserId()); } public Foursquare getFoursquare() { return mFoursquare; } public String getUserId() { return Preferences.getUserId(mPrefs); } public String getUserName() { return Preferences.getUserName(mPrefs); } public String getUserEmail() { return Preferences.getUserEmail(mPrefs); } public String getUserGender() { return Preferences.getUserGender(mPrefs); } public String getVersion() { if (mVersion != null) { return mVersion; } else { return ""; } } public String getLastSeenChangelogVersion() { return Preferences.getLastSeenChangelogVersion(mPrefs); } public void storeLastSeenChangelogVersion(String version) { Preferences.storeLastSeenChangelogVersion(mPrefs.edit(), version); } public boolean getUseNativeImageViewerForFullScreenImages() { return Preferences.getUseNativeImageViewerForFullScreenImages(mPrefs); } public RemoteResourceManager getRemoteResourceManager() { return mRemoteResourceManager; } public BestLocationListener requestLocationUpdates(boolean gps) { mBestLocationListener.register( (LocationManager) getSystemService(Context.LOCATION_SERVICE), gps); return mBestLocationListener; } public BestLocationListener requestLocationUpdates(Observer observer) { mBestLocationListener.addObserver(observer); mBestLocationListener.register( (LocationManager) getSystemService(Context.LOCATION_SERVICE), true); return mBestLocationListener; } public void removeLocationUpdates() { mBestLocationListener .unregister((LocationManager) getSystemService(Context.LOCATION_SERVICE)); } public void removeLocationUpdates(Observer observer) { mBestLocationListener.deleteObserver(observer); this.removeLocationUpdates(); } public Location getLastKnownLocation() { return mBestLocationListener.getLastKnownLocation(); } public Location getLastKnownLocationOrThrow() throws LocationException { Location location = mBestLocationListener.getLastKnownLocation(); if (location == null) { throw new LocationException(); } return location; } public void clearLastKnownLocation() { mBestLocationListener.clearLastKnownLocation(); } public void requestStartService() { mTaskHandler.sendMessage( // mTaskHandler.obtainMessage(TaskHandler.MESSAGE_START_SERVICE)); } public void requestUpdateUser() { mTaskHandler.sendEmptyMessage(TaskHandler.MESSAGE_UPDATE_USER); } private void loadFoursquare() { // Try logging in and setting up foursquare oauth, then user // credentials. if (FoursquaredSettings.USE_DEBUG_SERVER) { mFoursquare = new Foursquare(Foursquare.createHttpApi(FoursquaredSettings.DEBUG_SERVER, mVersion, false)); } else { mFoursquare = new Foursquare(Foursquare.createHttpApi(mVersion, false)); } if (FoursquaredSettings.DEBUG) Log.d(TAG, "loadCredentials()"); String phoneNumber = mPrefs.getString(Preferences.PREFERENCE_LOGIN, null); String password = mPrefs.getString(Preferences.PREFERENCE_PASSWORD, null); mFoursquare.setCredentials(phoneNumber, password); if (mFoursquare.hasLoginAndPassword()) { sendBroadcast(new Intent(INTENT_ACTION_LOGGED_IN)); } else { sendBroadcast(new Intent(INTENT_ACTION_LOGGED_OUT)); } } /** * Provides static access to a Foursquare instance. This instance is * initiated without user credentials. * * @param context the context to use when constructing the Foursquare * instance * @return the Foursquare instace */ public static Foursquare createFoursquare(Context context) { String version = getVersionString(context); if (FoursquaredSettings.USE_DEBUG_SERVER) { return new Foursquare(Foursquare.createHttpApi(FoursquaredSettings.DEBUG_SERVER, version, false)); } else { return new Foursquare(Foursquare.createHttpApi(version, false)); } } /** * Constructs the version string of the application. * * @param context the context to use for getting package info * @return the versions string of the application */ private static String getVersionString(Context context) { // Get a version string for the app. try { PackageManager pm = context.getPackageManager(); PackageInfo pi = pm.getPackageInfo(PACKAGE_NAME, 0); return PACKAGE_NAME + ":" + String.valueOf(pi.versionCode); } catch (NameNotFoundException e) { if (DEBUG) Log.d(TAG, "Could not retrieve package info", e); throw new RuntimeException(e); } } private void loadResourceManagers() { // We probably don't have SD card access if we get an // IllegalStateException. If it did, lets // at least have some sort of disk cache so that things don't npe when // trying to access the // resource managers. try { if (DEBUG) Log.d(TAG, "Attempting to load RemoteResourceManager(cache)"); mRemoteResourceManager = new RemoteResourceManager("cache"); } catch (IllegalStateException e) { if (DEBUG) Log.d(TAG, "Falling back to NullDiskCache for RemoteResourceManager"); mRemoteResourceManager = new RemoteResourceManager(new NullDiskCache()); } } public boolean getIsFirstRun() { return mIsFirstRun; } private boolean checkIfIsFirstRun() { File file = new File( "/data/data/com.joelapenna.foursquared/shared_prefs/com.joelapenna.foursquared_preferences.xml"); return !file.exists(); } /** * Set up resource managers on the application depending on SD card state. * * @author Joe LaPenna (joe@joelapenna.com) */ private class MediaCardStateBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (DEBUG) Log .d(TAG, "Media state changed, reloading resource managers:" + intent.getAction()); if (Intent.ACTION_MEDIA_UNMOUNTED.equals(intent.getAction())) { getRemoteResourceManager().shutdown(); loadResourceManagers(); } else if (Intent.ACTION_MEDIA_MOUNTED.equals(intent.getAction())) { loadResourceManagers(); } } public void register() { // Register our media card broadcast receiver so we can // enable/disable the cache as // appropriate. IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(Intent.ACTION_MEDIA_UNMOUNTED); intentFilter.addAction(Intent.ACTION_MEDIA_MOUNTED); // intentFilter.addAction(Intent.ACTION_MEDIA_REMOVED); // intentFilter.addAction(Intent.ACTION_MEDIA_SHARED); // intentFilter.addAction(Intent.ACTION_MEDIA_BAD_REMOVAL); // intentFilter.addAction(Intent.ACTION_MEDIA_UNMOUNTABLE); // intentFilter.addAction(Intent.ACTION_MEDIA_NOFS); // intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_STARTED); // intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED); intentFilter.addDataScheme("file"); registerReceiver(this, intentFilter); } } private class LoggedInOutBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (INTENT_ACTION_LOGGED_IN.equals(intent.getAction())) { requestUpdateUser(); } } public void register() { // Register our media card broadcast receiver so we can // enable/disable the cache as // appropriate. IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(INTENT_ACTION_LOGGED_IN); intentFilter.addAction(INTENT_ACTION_LOGGED_OUT); registerReceiver(this, intentFilter); } } private class TaskHandler extends Handler { private static final int MESSAGE_UPDATE_USER = 1; private static final int MESSAGE_START_SERVICE = 2; public TaskHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { super.handleMessage(msg); if (DEBUG) Log.d(TAG, "handleMessage: " + msg.what); switch (msg.what) { case MESSAGE_UPDATE_USER: try { // Update user info Log.d(TAG, "Updating user."); // Use location when requesting user information, if we // have it. Foursquare.Location location = LocationUtils .createFoursquareLocation(getLastKnownLocation()); User user = getFoursquare().user( null, false, false, false, location); Editor editor = mPrefs.edit(); Preferences.storeUser(editor, user); editor.commit(); if (location == null) { // Pump the location listener, we don't have a // location in our listener yet. Log.d(TAG, "Priming Location from user city."); Location primeLocation = new Location("foursquare"); // Very inaccurate, right? primeLocation.setTime(System.currentTimeMillis()); mBestLocationListener.updateLocation(primeLocation); } } catch (FoursquareError e) { if (DEBUG) Log.d(TAG, "FoursquareError", e); // TODO Auto-generated catch block } catch (FoursquareException e) { if (DEBUG) Log.d(TAG, "FoursquareException", e); // TODO Auto-generated catch block } catch (IOException e) { if (DEBUG) Log.d(TAG, "IOException", e); // TODO Auto-generated catch block } return; case MESSAGE_START_SERVICE: Intent serviceIntent = new Intent(Foursquared.this, FoursquaredService.class); serviceIntent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE); startService(serviceIntent); return; } } } }