diff --git a/src/com/fsck/k9/helper/AutoSyncHelper.java b/src/com/fsck/k9/helper/AutoSyncHelper.java new file mode 100644 index 000000000..0cf78d79c --- /dev/null +++ b/src/com/fsck/k9/helper/AutoSyncHelper.java @@ -0,0 +1,135 @@ +package com.fsck.k9.helper; + +import com.fsck.k9.K9; +import android.os.Build; +import android.util.Log; + +/** + * Helper class to get the current state of the auto-sync setting. + */ +public class AutoSyncHelper +{ + /** + * False, if we never tried to load the class for this SDK version. + * True, otherwise. + * + * Note: if sAutoSync is null and sChecked is true, then an error occured + * while loading the class for the SDK version we're running on. + */ + private static boolean sChecked = false; + + /** + * Instance of the SDK specific class that implements the IAutoSync + * interface. + */ + private static IAutoSync sAutoSync = null; + + + /** + * Try loading the class that implements IAutoSync for this SDK version. + * + * @return the IAutoSync object for this SDK version, or null if something + * went wrong. + */ + private static IAutoSync loadAutoSync() + { + /* + * We're trying to load the class for this SDK version. If anything + * goes wrong after this point, we don't want to try again. + */ + sChecked = true; + + /* + * Check the version of the SDK we are running on. Choose an + * implementation class designed for that version of the SDK. + */ + @SuppressWarnings("deprecation") + int sdkVersion = Integer.parseInt(Build.VERSION.SDK); + + String className = null; + if (sdkVersion == Build.VERSION_CODES.CUPCAKE) + { + className = "com.fsck.k9.helper.AutoSyncSdk3"; + } + else if (sdkVersion == Build.VERSION_CODES.DONUT) + { + className = "com.fsck.k9.helper.AutoSyncSdk4"; + } + else if (sdkVersion >= Build.VERSION_CODES.ECLAIR) + { + className = "com.fsck.k9.helper.AutoSyncSdk5"; + } + + /* + * Find the required class by name and instantiate it. + */ + try + { + Class clazz = + Class.forName(className).asSubclass(IAutoSync.class); + + IAutoSync autoSync = clazz.newInstance(); + autoSync.initialize(K9.app); + + return autoSync; + } + catch (ClassNotFoundException e) + { + Log.e(K9.LOG_TAG, "Couldn't find class: " + className, e); + } + catch (InstantiationException e) + { + Log.e(K9.LOG_TAG, "Couldn't instantiate class: " + className, e); + } + catch (IllegalAccessException e) + { + Log.e(K9.LOG_TAG, "Couldn't access class: " + className, e); + } + catch (NoSuchMethodException e) + { + if (K9.DEBUG) + { + Log.d(K9.LOG_TAG, "Couldn't load method to get auto-sync state", e); + } + } + return null; + } + + /** + * Checks whether we can query the auto-sync state using + * getMasterSyncAutomatically() or not. + * + * @return true, if calls to getMasterSyncAutomatically() will return the + * state of the auto-sync setting. false, otherwise. + */ + public static boolean isAvailable() + { + if (!sChecked) + { + sAutoSync = loadAutoSync(); + } + return (sAutoSync != null); + } + + /** + * Query the state of the auto-sync setting. + * + * @return the state of the auto-sync setting. + * @see IAutoSync + */ + public static boolean getMasterSyncAutomatically() + { + if (!sChecked) + { + sAutoSync = loadAutoSync(); + } + + if (sAutoSync == null) + { + throw new RuntimeException( + "Called getMasterSyncAutomatically() before checking if it's available."); + } + + return sAutoSync.getMasterSyncAutomatically(); + } +} diff --git a/src/com/fsck/k9/helper/AutoSyncSdk3.java b/src/com/fsck/k9/helper/AutoSyncSdk3.java new file mode 100644 index 000000000..b79d13c53 --- /dev/null +++ b/src/com/fsck/k9/helper/AutoSyncSdk3.java @@ -0,0 +1,48 @@ +package com.fsck.k9.helper; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import android.content.ContentResolver; +import android.content.Context; +import android.os.Handler; + +public class AutoSyncSdk3 implements IAutoSync +{ + private Method mGetListenForNetworkTickles; + private Object mQueryMap; + + public void initialize(Context context) throws NoSuchMethodException + { + /* + * There's no documented/official way to query the state of the + * auto-sync setting for a normal application in SDK 1.5/API 3. + * + * We use reflection to get an Sync.Settings.QueryMap" object, so we + * can call its getListenForNetworkTickles() method. This will return + * the current auto-sync state. + */ + try + { + Class clazz = Class.forName("android.provider.Sync$Settings$QueryMap"); + Constructor c = clazz.getConstructor(ContentResolver.class, boolean.class, Handler.class); + mQueryMap = c.newInstance(context.getContentResolver(), true, null); + mGetListenForNetworkTickles = mQueryMap.getClass().getMethod("getListenForNetworkTickles"); + } + catch (Exception e) + { + throw new NoSuchMethodException(); + } + } + + public boolean getMasterSyncAutomatically() + { + try + { + return (Boolean) mGetListenForNetworkTickles.invoke(mQueryMap); + } + catch (Exception e) + { + return false; + } + } +} diff --git a/src/com/fsck/k9/helper/AutoSyncSdk4.java b/src/com/fsck/k9/helper/AutoSyncSdk4.java new file mode 100644 index 000000000..76429bbdd --- /dev/null +++ b/src/com/fsck/k9/helper/AutoSyncSdk4.java @@ -0,0 +1,45 @@ +package com.fsck.k9.helper; + +import java.lang.reflect.Method; +import android.content.ContentResolver; +import android.content.Context; + +public class AutoSyncSdk4 implements IAutoSync +{ + private Method mGetListenForNetworkTickles; + private Object mContentService; + + public void initialize(Context context) throws NoSuchMethodException + { + /* + * There's no documented/official way to query the state of the + * auto-sync setting for a normal application in SDK 1.6/API 4. + * + * We use reflection to get an ContentService object, so we can call its + * getListenForNetworkTickles() method. This will return the current + * auto-sync state. + */ + try + { + Method getContentService = ContentResolver.class.getMethod("getContentService"); + mContentService = getContentService.invoke(null); + mGetListenForNetworkTickles = mContentService.getClass().getMethod("getListenForNetworkTickles"); + } + catch (Exception e) + { + throw new NoSuchMethodException(); + } + } + + public boolean getMasterSyncAutomatically() + { + try + { + return (Boolean) mGetListenForNetworkTickles.invoke(mContentService); + } + catch (Exception e) + { + return false; + } + } +} diff --git a/src/com/fsck/k9/helper/AutoSyncSdk5.java b/src/com/fsck/k9/helper/AutoSyncSdk5.java new file mode 100644 index 000000000..88ee6ff8f --- /dev/null +++ b/src/com/fsck/k9/helper/AutoSyncSdk5.java @@ -0,0 +1,21 @@ +package com.fsck.k9.helper; + +import android.content.ContentResolver; +import android.content.Context; + +public class AutoSyncSdk5 implements IAutoSync +{ + public void initialize(Context context) throws NoSuchMethodException + { + // Nothing to do here + } + + public boolean getMasterSyncAutomatically() + { + /* + * SDK 2.0/API 5 introduced an official method to query the auto-sync + * state. + */ + return ContentResolver.getMasterSyncAutomatically(); + } +} diff --git a/src/com/fsck/k9/helper/IAutoSync.java b/src/com/fsck/k9/helper/IAutoSync.java new file mode 100644 index 000000000..b32745c2b --- /dev/null +++ b/src/com/fsck/k9/helper/IAutoSync.java @@ -0,0 +1,27 @@ +package com.fsck.k9.helper; + +import android.content.Context; + +/** + * Classes that implement this interface know how to query the system for the + * current state of the auto-sync setting. This method differs from SDK 3 to + * SDK 5, so there are specialized implementations for each SDK version. + */ +public interface IAutoSync +{ + /** + * Do the necessary reflection magic to get the necessary objects and/or + * methods to later query the state of the auto-sync setting. + * + * @param context The application context object. + * @throws NoSuchMethodException if something went wrong. + */ + public void initialize(Context context) throws NoSuchMethodException; + + /** + * Query the state of the auto-sync setting. + * + * @return the state of the auto-sync setting. + */ + public boolean getMasterSyncAutomatically(); +}