package gws.grottworkshop.gwsholmeswatson;
import gws.grottworkshop.gwsholmeswatson.cache.ImageCache;
import gws.grottworkshop.gwsholmeswatson.cache.TwoLevelLruCache.Converter;
import java.io.File;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
import org.holoeverywhere.app.Application;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.android.LogcatAppender;
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.FileAppender;
import uk.co.senab.bitmapcache.BitmapLruCache;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Configuration;
import android.os.Environment;
// TODO: Auto-generated Javadoc
/**
* The Class GWSApplication,ie on main thread,
* to set up the singletons we need for
* high performance based applications.
*
* To use you extend this class in your own project
* and remember you can override most of the methods.
*
* Borrowing concepts/code from Kaeppler and Mottier and AOSP,
* modifications and additions are also under Apache License 2.0
* and by:
* @author fredgrott
*
*/
public class GWSApplication extends Application {
/** The context objects. */
private HashMap<String, WeakReference<Context>> contextObjects = new HashMap<String, WeakReference<Context>>();
/** The context. */
private static Context context;
/** The m executor service. */
private ExecutorService mExecutorService;
/** The m low memory listeners. */
private ArrayList<WeakReference<OnLowMemoryListener>> mLowMemoryListeners;
/** The gwslog. */
private Logger GWSLOG = LoggerFactory.getLogger(GWSApplication.class);
/** The is landscape. */
public static boolean isLandscape;
/** The m cache. */
private BitmapLruCache mCache;
/** The i cache. */
public static ImageCache iCache;
/** The m converter. */
private Converter<byte[]> mConverter;
/** The max mem size. */
public static int maxMemSize;
/** The max disk size. */
public static long maxDiskSize;
/**
* Return the class of the home Activity. The home Activity is the main
* entrance point of your application. This is usually where the
* dashboard/general menu is displayed. You will supply that
* in the extension of this class by overriding this method with:
* <code>
* Public lass<?> getHomeActivityClass() {
* return YourHomeActivity.class;
* }
* </code>
*
* @return The Class of the home Activity
*/
public Class<?> getHomeActivityClass() {
return null;
}
/**
* Gets the main application intent. Will be null if you
* do not use and non-null if you override it in your
* application class extending this application class
* with whatever you fill in for method.
*
* @return the main application intent
*/
public Intent getMainApplicationIntent() {
return null;
}
/** The Constant CORE_POOL_SIZE. */
private static final int CORE_POOL_SIZE = 5;
/** The Constant sThreadFactory. */
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread newThread(Runnable r) {
return new Thread(r, "GWS App Thread #" + mCount.getAndIncrement());
}
};
/**
* Gets the executor,touse if we have a long running task.
*
* @return the executor
*/
public ExecutorService getExecutor() {
if (mExecutorService == null) {
mExecutorService = Executors.newFixedThreadPool(CORE_POOL_SIZE, sThreadFactory);
}
return mExecutorService;
}
/**
* The listener interface for receiving onLowMemory events.
* The class that is interested in processing a onLowMemory
* event implements this interface, and the object created
* with that class is registered with a component using the
* component's <code>addOnLowMemoryListener<code> method. When
* the onLowMemory event occurs, that object's appropriate
* method is invoked.
*
* @see OnLowMemoryEvent
*/
public static interface OnLowMemoryListener {
/**
* Callback to be invoked when the system needs memory.
*/
public void onLowMemoryReceived();
}
/**
* Instantiates a new gWS application.
*/
public GWSApplication() {
mLowMemoryListeners = new ArrayList<WeakReference<OnLowMemoryListener>>();
}
/**
* Register on low memory listener.
*
* @param listener the listener
*/
public void registerOnLowMemoryListener(OnLowMemoryListener listener) {
if (listener != null) {
mLowMemoryListeners.add(new WeakReference<OnLowMemoryListener>(listener));
}
}
/**
* Unregister on low memory listener.
*
* @param listener the listener
*/
public void unregisterOnLowMemoryListener(OnLowMemoryListener listener) {
if (listener != null) {
int i = 0;
while (i < mLowMemoryListeners.size()) {
final OnLowMemoryListener l = mLowMemoryListeners.get(i).get();
if (l == null || l == listener) {
mLowMemoryListeners.remove(i);
} else {
i++;
}
}
}
}
/**
* onLowMemory().
*
* @see android.app.Application#onLowMemory()
*/
public void onLowMemory() {
super.onLowMemory();
clearCaches();
int i = 0;
while (i < mLowMemoryListeners.size()) {
final OnLowMemoryListener listener = mLowMemoryListeners.get(i).get();
if (listener == null) {
mLowMemoryListeners.remove(i);
} else {
listener.onLowMemoryReceived();
i++;
}
}
}
/**
* Gets the active context.
*
* @param className the class name
* @return the active context
*/
public synchronized Context getActiveContext(String className) {
WeakReference<Context> ref = contextObjects.get(className);
if (ref == null) {
return null;
}
final Context c = ref.get();
if (c == null) // If the WeakReference is no longer valid, ensure it is removed.
contextObjects.remove(className);
return c;
}
/**
* Sets the active context.
*
* @param className the class name
* @param context the context
*/
public synchronized void setActiveContext(String className, Context context) {
WeakReference<Context> ref = new WeakReference<Context>(context);
this.contextObjects.put(className, ref);
}
/**
* Reset active context.
*
* @param className the class name
*/
public synchronized void resetActiveContext(String className) {
contextObjects.remove(className);
}
/**
* Gets the application info.
*
* @param packageName the package name
* @return the application info
* @see android.content.ContextWrapper#getApplicationInfo()
*/
public ApplicationInfo getApplicationInfo(String packageName) {
// TODO Auto-generated method stub
ApplicationInfo app = null;
try {
app = context.getPackageManager().getApplicationInfo(packageName, 0);
} catch (NameNotFoundException e) {
// TODO Auto-generated catch block
GWSLOG.error("packageName not found");
}
return app;
}
/**
* Gets the app context.
*
* @return the app context
*/
public static Context getAppContext() {
return GWSApplication.context;
}
/**
* Is triggered by a back button event
* that exits the application from the handleApplicationClosing
* method in GWSActivity.
*/
public void onClose() {
clearCaches();
}
/**
* On configuration changed.
* Remember in manifest set:
* <code>
* <activity android:name=".MyActivity"
* android:configChanges="orientation|keyboardHidden"
* android:label="@string/app_name">
* </code>
*
* The default without the manifest setting is
* to be ignored by app and restart activity upon
* configuration change.If special addition handling is required,
* override the method.
*
* @param newConfig the new config
* @see android.app.Application#onConfigurationChanged(android.content.res.Configuration)
*/
@Override
public void onConfigurationChanged(Configuration newConfig) {
// TODO Auto-generated method stub
super.onConfigurationChanged(newConfig);
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
isLandscape = true;
}else{
isLandscape =false;
}
}
/**
* On create.
*
* @see org.holoeverywhere.app.Application#onCreate()
*/
@Override
public void onCreate() {
// TODO Auto-generated method stub
GWSApplication.context = getApplicationContext();
configureLogbackDirectly();
GWSLOG.info("GWSApplicaiton class created");
setCachePrep();
setCaches();
setID();
super.onCreate();
}
/**
* On terminate.
*
* @see android.app.Application#onTerminate()
*/
@Override
public void onTerminate() {
// TODO Auto-generated method stub
clearCaches();
super.onTerminate();
}
/**
* Gets the app name.
*
* @return the app name
*/
public String getAppName() {
final PackageManager pm = getApplicationContext().getPackageManager();
ApplicationInfo ai;
try {
ai = pm.getApplicationInfo( this.getPackageName(), 0);
} catch (final NameNotFoundException e) {
ai = null;
}
final String applicationName = (String) (ai != null ? pm.getApplicationLabel(ai) : "(unknown)");
return applicationName;
}
/**
* Gets the app version.
*
* @return the app version
*/
public String getAppVersion() {
PackageInfo pInfo;
String version;
try {
pInfo = getPackageManager().getPackageInfo(getPackageName(), 0);
version= pInfo.versionName;
} catch (NameNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
version = "namenotfound";
}
return version;
}
/**
* Sets the cache prep.
*/
public void setCachePrep() {
maxMemSize = 3072000;
maxDiskSize= 10240000L;
}
/**
* Sets the caches, normally we overrride this
* method.
*/
public void setCaches(){
File cacheLocation;
File cacheMeLocation;
// If we have external storage use it for the disk cache. Otherwise we use
// the cache dir
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
cacheLocation = new File(
Environment.getExternalStorageDirectory() + "/" + getAppName() + "-Android-BitmapCache");
cacheMeLocation= new File(Environment.getExternalStorageDirectory() + "/" + getAppName() + "-GWSCache");
} else {
cacheLocation = new File(getFilesDir() + "/" + getAppName() + "-Android-BitmapCache");
cacheMeLocation = new File(getFilesDir() + "/" + getAppName() + "-GWSCache");
}
cacheLocation.mkdirs();
BitmapLruCache.Builder builder = new BitmapLruCache.Builder(this);
builder.setMemoryCacheEnabled(true).setMemoryCacheMaxSizeUsingHeapSize();
builder.setDiskCacheEnabled(true).setDiskCacheLocation(cacheLocation);
mCache = builder.build();
try {
iCache = new ImageCache(cacheMeLocation, Integer.parseInt(getAppVersion()), maxMemSize, maxDiskSize, mConverter);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* clearCaches, normally we overrride this.
*/
public void clearCaches() {
//clears mCache of all bitmaps that are not displayed.
mCache.trimMemory();
}
/**
* Gets the bitmap cache.
*
* @return the bitmap cache
*/
public BitmapLruCache getBitmapCache() {
return mCache;
}
/**
* Gets the image cache.
*
* @return the image cache
*/
public static ImageCache getImageCache(){
return iCache;
}
/**
* Set theID if its not stored yet in shared prefs or if
* in shared prefs than set to that value.
*/
public void setID() {
if(!GWSPreferences.getIDState(getApplicationContext())){
GWSPreferences.setIDState(getApplicationContext(), true);
GWSPreferences.setIDString(getApplicationContext(), PseudoID.ePseudoID);
} else{
GWSPreferences.setIDState(getApplicationContext(), false);
}
}
/**
* Configure logback directly.
*/
private void configureLogbackDirectly() {
// reset the default context (which may already have been initialized)
// since we want to reconfigure it
LoggerContext lc = (LoggerContext)LoggerFactory.getILoggerFactory();
lc.reset();
// setup FileAppender
PatternLayoutEncoder encoder1 = new PatternLayoutEncoder();
encoder1.setContext(lc);
encoder1.setPattern("%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n");
encoder1.start();
FileAppender<ILoggingEvent> fileAppender = new FileAppender<ILoggingEvent>();
fileAppender.setContext(lc);
fileAppender.setFile(this.getFileStreamPath("app.log").getAbsolutePath());
fileAppender.setEncoder(encoder1);
fileAppender.start();
// setup LogcatAppender
PatternLayoutEncoder encoder2 = new PatternLayoutEncoder();
encoder2.setContext(lc);
encoder2.setPattern("[%thread] %msg%n");
encoder2.start();
LogcatAppender logcatAppender = new LogcatAppender();
logcatAppender.setContext(lc);
logcatAppender.setEncoder(encoder2);
logcatAppender.start();
// add the newly created appenders to the root logger;
// qualify Logger to disambiguate from org.slf4j.Logger
ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
root.addAppender(fileAppender);
root.addAppender(logcatAppender);
}
}