From c8fa5303c4b7d3231827ced45d3be2e06e28fad8 Mon Sep 17 00:00:00 2001 From: Peter Serwylo Date: Sun, 21 Jul 2013 08:14:57 +1000 Subject: [PATCH] Fixed issues with api version differences. I moved the Utils.hasApi to the protected method in Compatibility. It was misleading having it in Utils, because it is not as simple as putting a "if(Utils.hasApi(11)) callApi_11_method()" there. The problem isn't when the method is executed during runtime, it is when the class is loaded into the VM using the classloader. At that point, it tries to verify that indeed every method you call from your class exists, so the conditional check doesn't work, hence VerifyError's and breakage. The appropriate way to do it is the same way as the Android support library does it. The goal is to have an interface which only one implementation is every loaded at runtime. Any implementations for versions that your device doesn't support will never get loaded, so no VerifyErrors. If you have the support library installed in your Android SDK, check out extras/android/support/v4/src/java/android/support/v4/view/MenuItemCompat.java to see how the pattern works. --- src/org/fdroid/fdroid/AppDetails.java | 8 +-- src/org/fdroid/fdroid/DB.java | 31 ++++------ src/org/fdroid/fdroid/Preferences.java | 4 +- src/org/fdroid/fdroid/SearchResults.java | 4 +- src/org/fdroid/fdroid/Utils.java | 10 --- .../fdroid/fdroid/compat/ActionBarCompat.java | 62 +++++++++++++++++++ .../fdroid/fdroid/compat/Compatibility.java | 16 +++++ .../fdroid/fdroid/compat/ContextCompat.java | 59 ++++++++++++++++++ .../fdroid/fdroid/compat/LayoutCompat.java | 39 ++++++++++++ src/org/fdroid/fdroid/compat/MenuManager.java | 4 +- src/org/fdroid/fdroid/compat/TabManager.java | 4 +- .../fdroid/fdroid/views/AppListAdapter.java | 6 +- 12 files changed, 202 insertions(+), 45 deletions(-) create mode 100644 src/org/fdroid/fdroid/compat/ActionBarCompat.java create mode 100644 src/org/fdroid/fdroid/compat/Compatibility.java create mode 100644 src/org/fdroid/fdroid/compat/ContextCompat.java create mode 100644 src/org/fdroid/fdroid/compat/LayoutCompat.java diff --git a/src/org/fdroid/fdroid/AppDetails.java b/src/org/fdroid/fdroid/AppDetails.java index 3b309e1cb..287ca5905 100644 --- a/src/org/fdroid/fdroid/AppDetails.java +++ b/src/org/fdroid/fdroid/AppDetails.java @@ -26,6 +26,7 @@ import java.util.Iterator; import java.util.List; import android.support.v4.view.MenuItemCompat; +import org.fdroid.fdroid.compat.ActionBarCompat; import org.fdroid.fdroid.compat.MenuManager; import org.fdroid.fdroid.DB.CommaSeparatedList; import org.xml.sax.XMLReader; @@ -182,10 +183,9 @@ public class AppDetails extends ListActivity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - if (Utils.hasApi(11)) { - getActionBar().setDisplayShowTitleEnabled(false); - getActionBar().setDisplayHomeAsUpEnabled(true); - } + ActionBarCompat abCompat = ActionBarCompat.create(this); + abCompat.setDisplayHomeAsUpEnabled(true); + abCompat.setDisplayShowTitleEnabled(false); setContentView(R.layout.appdetails); diff --git a/src/org/fdroid/fdroid/DB.java b/src/org/fdroid/fdroid/DB.java index da83f8994..37b35a783 100644 --- a/src/org/fdroid/fdroid/DB.java +++ b/src/org/fdroid/fdroid/DB.java @@ -45,6 +45,8 @@ import android.os.Environment; import android.preference.PreferenceManager; import android.text.TextUtils.SimpleStringSplitter; import android.util.Log; +import org.fdroid.fdroid.compat.Compatibility; +import org.fdroid.fdroid.compat.ContextCompat; public class DB { @@ -275,25 +277,25 @@ public class DB { // Call isCompatible(apk) on an instance of this class to // check if an APK is compatible with the user's device. - public static abstract class CompatibilityChecker { + public static abstract class CompatibilityChecker extends Compatibility { public abstract boolean isCompatible(Apk apk); public static CompatibilityChecker getChecker(Context ctx) { CompatibilityChecker checker; - if (Utils.hasApi(5)) + if (hasApi(5)) checker = new EclairChecker(ctx); else checker = new BasicChecker(); Log.d("FDroid", "Compatibility checker for API level " - + Utils.getApi() + ": " + checker.getClass().getName()); + + getApi() + ": " + checker.getClass().getName()); return checker; } } private static class BasicChecker extends CompatibilityChecker { public boolean isCompatible(Apk apk) { - return (apk.minSdkVersion <= Utils.getApi()); + return hasApi(apk.minSdkVersion); } } @@ -323,7 +325,7 @@ public class DB { } public boolean isCompatible(Apk apk) { - if (apk.minSdkVersion > Utils.getApi()) + if (!hasApi(apk.minSdkVersion)) return false; if (apk.features != null) { for (String feat : apk.features) { @@ -485,21 +487,12 @@ public class DB { } - // Get the local storage (cache) path. This will also create it if - // it doesn't exist. It can return null if it's currently unavailable. + /** + * Get the local storage (cache) path. This will also create it if + * it doesn't exist. It can return null if it's currently unavailable. + */ public static File getDataPath(Context ctx) { - File f; - if (Utils.hasApi(8)) { - f = ctx.getExternalCacheDir(); - } else { - f = new File(Environment.getExternalStorageDirectory(), - "Android/data/org.fdroid.fdroid/cache"); - if(f != null) { - if(!f.exists()) - f.mkdirs(); - } - } - return f; + return ContextCompat.create(ctx).getExternalCacheDir(); } public static File getIconsPath(Context ctx) { diff --git a/src/org/fdroid/fdroid/Preferences.java b/src/org/fdroid/fdroid/Preferences.java index 9e5083cd3..7098f8b98 100644 --- a/src/org/fdroid/fdroid/Preferences.java +++ b/src/org/fdroid/fdroid/Preferences.java @@ -27,6 +27,7 @@ import android.preference.Preference; import android.preference.PreferenceActivity; import android.preference.Preference.OnPreferenceClickListener; import android.widget.Toast; +import org.fdroid.fdroid.compat.ActionBarCompat; public class Preferences extends PreferenceActivity implements OnPreferenceClickListener { @@ -34,8 +35,7 @@ public class Preferences extends PreferenceActivity implements @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - if (Utils.hasApi(11)) - getActionBar().setDisplayHomeAsUpEnabled(true); + ActionBarCompat.create(this).setDisplayHomeAsUpEnabled(true); addPreferencesFromResource(R.xml.preferences); for (String prefkey : new String[] { "reset", "ignoreTouchscreen", "showIncompatible" }) { diff --git a/src/org/fdroid/fdroid/SearchResults.java b/src/org/fdroid/fdroid/SearchResults.java index 365181d06..2d9ef51e0 100644 --- a/src/org/fdroid/fdroid/SearchResults.java +++ b/src/org/fdroid/fdroid/SearchResults.java @@ -34,6 +34,7 @@ import android.view.MenuItem; import android.view.View; import android.widget.ListView; import android.widget.TextView; +import org.fdroid.fdroid.compat.ActionBarCompat; import org.fdroid.fdroid.views.AppListAdapter; import org.fdroid.fdroid.views.AvailableAppListAdapter; @@ -50,8 +51,7 @@ public class SearchResults extends ListActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - if (Utils.hasApi(11)) - getActionBar().setDisplayHomeAsUpEnabled(true); + ActionBarCompat.create(this).setDisplayHomeAsUpEnabled(true); applist = new AvailableAppListAdapter(this); setContentView(R.layout.searchresults); diff --git a/src/org/fdroid/fdroid/Utils.java b/src/org/fdroid/fdroid/Utils.java index d1679cbce..4726e2f32 100644 --- a/src/org/fdroid/fdroid/Utils.java +++ b/src/org/fdroid/fdroid/Utils.java @@ -18,8 +18,6 @@ package org.fdroid.fdroid; -import android.os.Build; - import java.io.BufferedReader; import java.io.Closeable; import java.io.File; @@ -73,14 +71,6 @@ public final class Utils { } } - public static boolean hasApi(int apiLevel) { - return Build.VERSION.SDK_INT >= apiLevel; - } - - public static int getApi() { - return Build.VERSION.SDK_INT; - } - public static String getFriendlySize(int size) { double s = size; int i = 0; diff --git a/src/org/fdroid/fdroid/compat/ActionBarCompat.java b/src/org/fdroid/fdroid/compat/ActionBarCompat.java new file mode 100644 index 000000000..3360a7167 --- /dev/null +++ b/src/org/fdroid/fdroid/compat/ActionBarCompat.java @@ -0,0 +1,62 @@ +package org.fdroid.fdroid.compat; + +import android.app.ActionBar; +import android.app.Activity; + +public abstract class ActionBarCompat extends Compatibility { + + public static ActionBarCompat create(Activity activity) { + if (hasApi(11)) { + return new HoneycombActionBarCompatImpl(activity); + } else { + return new OldActionBarCompatImpl(activity); + } + } + + protected final Activity activity; + + public ActionBarCompat(Activity activity) { + this.activity = activity; + } + + public abstract void setDisplayHomeAsUpEnabled(boolean value); + public abstract void setDisplayShowTitleEnabled(boolean value); + +} + +class OldActionBarCompatImpl extends ActionBarCompat { + + public OldActionBarCompatImpl(Activity activity) { + super(activity); + } + + @Override + public void setDisplayHomeAsUpEnabled(boolean value) { + // Do nothing... + } + + @Override + public void setDisplayShowTitleEnabled(boolean value) { + // Do nothing... + } +} + +class HoneycombActionBarCompatImpl extends ActionBarCompat { + + private final ActionBar actionBar; + + public HoneycombActionBarCompatImpl(Activity activity) { + super(activity); + this.actionBar = activity.getActionBar(); + } + + @Override + public void setDisplayHomeAsUpEnabled(boolean value) { + actionBar.setDisplayHomeAsUpEnabled(value); + } + + @Override + public void setDisplayShowTitleEnabled(boolean value) { + actionBar.setDisplayShowTitleEnabled(value); + } +} diff --git a/src/org/fdroid/fdroid/compat/Compatibility.java b/src/org/fdroid/fdroid/compat/Compatibility.java new file mode 100644 index 000000000..9cd8d1344 --- /dev/null +++ b/src/org/fdroid/fdroid/compat/Compatibility.java @@ -0,0 +1,16 @@ +package org.fdroid.fdroid.compat; + +import android.os.Build; +import org.fdroid.fdroid.Utils; + +public abstract class Compatibility { + + protected static boolean hasApi(int apiLevel) { + return getApi() >= apiLevel; + } + + protected static int getApi() { + return Build.VERSION.SDK_INT; + } + +} diff --git a/src/org/fdroid/fdroid/compat/ContextCompat.java b/src/org/fdroid/fdroid/compat/ContextCompat.java new file mode 100644 index 000000000..fde80e365 --- /dev/null +++ b/src/org/fdroid/fdroid/compat/ContextCompat.java @@ -0,0 +1,59 @@ +package org.fdroid.fdroid.compat; + +import android.content.Context; +import android.os.Environment; + +import java.io.File; + +public abstract class ContextCompat extends Compatibility { + + public static ContextCompat create(Context context) { + if (hasApi(8)) { + return new FroyoContextCompatImpl(context); + } else { + return new OldContextCompatImpl(context); + } + } + + protected final Context context; + + public ContextCompat(Context context ) { + this.context = context; + } + + /** + * @see android.content.Context#getExternalCacheDir() + */ + public abstract File getExternalCacheDir(); + +} + +class OldContextCompatImpl extends ContextCompat { + + public OldContextCompatImpl(Context context) { + super(context); + } + + @Override + public File getExternalCacheDir() { + File file = new File(Environment.getExternalStorageDirectory(), + "Android/data/org.fdroid.fdroid/cache"); + if(!file.exists()) + file.mkdirs(); + return file; + } + +} + +class FroyoContextCompatImpl extends ContextCompat { + + public FroyoContextCompatImpl(Context context) { + super(context); + } + + @Override + public File getExternalCacheDir() { + return context.getExternalCacheDir(); + } + +} diff --git a/src/org/fdroid/fdroid/compat/LayoutCompat.java b/src/org/fdroid/fdroid/compat/LayoutCompat.java new file mode 100644 index 000000000..ab02d8443 --- /dev/null +++ b/src/org/fdroid/fdroid/compat/LayoutCompat.java @@ -0,0 +1,39 @@ +package org.fdroid.fdroid.compat; + +import android.widget.RelativeLayout; + +public abstract class LayoutCompat extends Compatibility { + + public static LayoutCompat create() { + if (hasApi(17)) { + return new JellyBeanMr1LayoutCompatImpl(); + } else { + return new OldLayoutCompatImpl(); + } + } + + private static final LayoutCompat impl = LayoutCompat.create(); + + protected abstract int relativeLayoutEndOf(); + + public static class RelativeLayout { + public static final int END_OF = impl.relativeLayoutEndOf(); + } + +} + +class OldLayoutCompatImpl extends LayoutCompat { + + @Override + protected int relativeLayoutEndOf() { + return android.widget.RelativeLayout.RIGHT_OF; + } +} + +class JellyBeanMr1LayoutCompatImpl extends LayoutCompat { + + @Override + protected int relativeLayoutEndOf() { + return android.widget.RelativeLayout.END_OF; + } +} diff --git a/src/org/fdroid/fdroid/compat/MenuManager.java b/src/org/fdroid/fdroid/compat/MenuManager.java index 9a60b595e..df8e9ddbd 100644 --- a/src/org/fdroid/fdroid/compat/MenuManager.java +++ b/src/org/fdroid/fdroid/compat/MenuManager.java @@ -3,10 +3,10 @@ package org.fdroid.fdroid.compat; import android.app.Activity; import org.fdroid.fdroid.Utils; -abstract public class MenuManager { +abstract public class MenuManager extends Compatibility { public static MenuManager create(Activity activity) { - if (Utils.hasApi(11)) { + if (hasApi(11)) { return new HoneycombMenuManagerImpl(activity); } else { return new OldMenuManagerImpl(activity); diff --git a/src/org/fdroid/fdroid/compat/TabManager.java b/src/org/fdroid/fdroid/compat/TabManager.java index fce2e2ce5..35b966545 100644 --- a/src/org/fdroid/fdroid/compat/TabManager.java +++ b/src/org/fdroid/fdroid/compat/TabManager.java @@ -14,14 +14,14 @@ import org.fdroid.fdroid.Utils; import java.util.ArrayList; import java.util.List; -public abstract class TabManager { +public abstract class TabManager extends Compatibility { public static final int INDEX_AVAILABLE = 0; public static final int INDEX_INSTALLED = 1; public static final int INDEX_CAN_UPDATE = 2; public static TabManager create(FDroid parent, ViewPager pager) { - if (Utils.hasApi(11)) { + if (hasApi(11)) { return new HoneycombTabManagerImpl(parent, pager); } else { return new OldTabManagerImpl(parent, pager); diff --git a/src/org/fdroid/fdroid/views/AppListAdapter.java b/src/org/fdroid/fdroid/views/AppListAdapter.java index 5842287d1..85c4e2d95 100644 --- a/src/org/fdroid/fdroid/views/AppListAdapter.java +++ b/src/org/fdroid/fdroid/views/AppListAdapter.java @@ -16,6 +16,7 @@ import android.view.ViewGroup; import android.widget.*; import org.fdroid.fdroid.DB; import org.fdroid.fdroid.R; +import org.fdroid.fdroid.compat.LayoutCompat; abstract public class AppListAdapter extends BaseAdapter { @@ -110,10 +111,7 @@ abstract public class AppListAdapter extends BaseAdapter { RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT); summaryLayout.addRule(RelativeLayout.BELOW, R.id.name); - if (Utils.hasApi(17)) - summaryLayout.addRule(RelativeLayout.END_OF, R.id.icon); - else - summaryLayout.addRule(RelativeLayout.RIGHT_OF, R.id.icon); + summaryLayout.addRule(LayoutCompat.RelativeLayout.END_OF, R.id.icon); summary.setLayoutParams(summaryLayout); summary.setPadding(0,0,0,0);