diff --git a/.gitignore b/.gitignore
index 023b57957..4b35a9282 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,11 +1,9 @@
/local.properties
-/build.properties
.classpath
/bin/
/gen/
/build
/.gradle
-/proguard.cfg
/build.xml
*~
.idea
diff --git a/res/layout-land/appdetails.xml b/res/layout-land/appdetails.xml
index 592971970..53c06af43 100644
--- a/res/layout-land/appdetails.xml
+++ b/res/layout-land/appdetails.xml
@@ -18,6 +18,14 @@
android:layout_marginRight="4dp"
android:orientation="vertical" >
+
+
-
-
-
+
+
+
+
+
+
+
+
+
-
+
+
+
@@ -41,7 +41,7 @@
android:ellipsize="end"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginLeft="8dp"
+ android:layout_marginLeft="10sp"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true" />
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 325556670..957f269b6 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -194,5 +194,6 @@
Disabled "%1$s".\n\nYou will
need to re-enable this repository to install apps from it.
+ Android %s or later
diff --git a/src/org/fdroid/fdroid/AppDetails.java b/src/org/fdroid/fdroid/AppDetails.java
index f5a056e7e..b09f724c1 100644
--- a/src/org/fdroid/fdroid/AppDetails.java
+++ b/src/org/fdroid/fdroid/AppDetails.java
@@ -76,6 +76,9 @@ import com.nostra13.universalimageloader.core.display.FadeInBitmapDisplayer;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.assist.ImageScaleType;
+import com.nostra13.universalimageloader.utils.StorageUtils;
+
+import android.os.Environment;
public class AppDetails extends ListActivity {
@@ -140,10 +143,11 @@ public class AppDetails extends ListActivity {
tv = (TextView) v.findViewById(R.id.status);
if (apk.vercode == app.installedVerCode
- && apk.sig.equals(mInstalledSigID))
+ && apk.sig.equals(mInstalledSigID)) {
tv.setText(getString(R.string.inst));
- else
+ } else {
tv.setText(getString(R.string.not_inst));
+ }
tv.setEnabled(apk.compatible);
tv = (TextView) v.findViewById(R.id.size);
@@ -153,6 +157,16 @@ public class AppDetails extends ListActivity {
tv.setText(Utils.getFriendlySize(apk.detail_size));
tv.setEnabled(apk.compatible);
}
+
+ tv = (TextView) v.findViewById(R.id.api);
+ if (apk.minSdkVersion == 0) {
+ tv.setText("");
+ } else {
+ tv.setText(getString(R.string.minsdk_or_later,
+ Utils.getAndroidVersionName(apk.minSdkVersion)));
+ tv.setEnabled(apk.compatible);
+ }
+
tv = (TextView) v.findViewById(R.id.buildtype);
if (apk.srcname != null) {
tv.setText("source");
@@ -160,6 +174,7 @@ public class AppDetails extends ListActivity {
tv.setText("bin");
}
tv.setEnabled(apk.compatible);
+
tv = (TextView) v.findViewById(R.id.added);
if (apk.added != null) {
tv.setVisibility(View.VISIBLE);
@@ -168,6 +183,7 @@ public class AppDetails extends ListActivity {
} else {
tv.setVisibility(View.GONE);
}
+
tv = (TextView) v.findViewById(R.id.nativecode);
if (pref_expert && apk.nativecode != null) {
tv.setVisibility(View.VISIBLE);
@@ -457,26 +473,6 @@ public class AppDetails extends ListActivity {
tv = (TextView) infoView.findViewById(R.id.description);
- /*
- The following is a quick solution to enable both text selection and
- links. Causes glitches and crashes:
- java.lang.IndexOutOfBoundsException: setSpan (-1 ... -1) starts before 0
-
- class CustomMovementMethod extends LinkMovementMethod {
- @Override
- public boolean canSelectArbitrarily () {
- return true;
- }
- }
-
- if (Utils.hasApi(11)) {
- tv.setTextIsSelectable(true);
- tv.setMovementMethod(new CustomMovementMethod());
- } else {
- tv.setMovementMethod(LinkMovementMethod.getInstance());
- }
- */
-
tv.setMovementMethod(LinkMovementMethod.getInstance());
// Need this to add the unimplemented support for ordered and unordered
@@ -848,8 +844,8 @@ public class AppDetails extends ListActivity {
public void onClick(DialogInterface dialog,
int whichButton) {
downloadHandler = new DownloadHandler(app.curApk,
- repoaddress, DB
- .getDataPath(getBaseContext()));
+ repoaddress, Utils
+ .getApkCacheDir(getBaseContext()));
}
});
ask_alrt.setNegativeButton(getString(R.string.no),
@@ -879,7 +875,7 @@ public class AppDetails extends ListActivity {
return;
}
downloadHandler = new DownloadHandler(app.curApk, repoaddress,
- DB.getDataPath(this));
+ Utils.getApkCacheDir(getBaseContext()));
}
private void removeApk(String id) {
diff --git a/src/org/fdroid/fdroid/DB.java b/src/org/fdroid/fdroid/DB.java
index ed39f71e9..415feffef 100644
--- a/src/org/fdroid/fdroid/DB.java
+++ b/src/org/fdroid/fdroid/DB.java
@@ -108,7 +108,7 @@ public class DB {
+ "lastUpdated string," + "compatible int not null,"
+ "ignoreAllUpdates int not null,"
+ "ignoreThisUpdate int not null,"
- + "provides string," + "primary key(id));";
+ + "primary key(id));";
public static class App implements Comparable {
@@ -126,7 +126,6 @@ public class DB {
detail_dogecoinAddr = null;
detail_webURL = null;
categories = null;
- provides = null;
antiFeatures = null;
requirements = null;
hasUpdates = false;
@@ -201,9 +200,6 @@ public class DB {
public int installedVerCode;
public boolean userInstalled;
- // List of app IDs that this app provides or null if there aren't any.
- public CommaSeparatedList provides;
-
// List of categories (as defined in the metadata
// documentation) or null if there aren't any.
public CommaSeparatedList categories;
@@ -377,10 +373,11 @@ public class DB {
}
}
- cpuAbis = new ArrayList();
- if (hasApi(8))
- cpuAbis.add(android.os.Build.CPU_ABI2);
+ cpuAbis = new ArrayList(2);
cpuAbis.add(android.os.Build.CPU_ABI);
+ if (hasApi(8)) {
+ cpuAbis.add(android.os.Build.CPU_ABI2);
+ }
Log.d("FDroid", logMsg.toString());
}
@@ -413,8 +410,7 @@ public class DB {
}
if (!compatibleApi(apk.nativecode)) {
Log.d("FDroid", apk.id + " vercode " + apk.vercode
- + " makes use of incompatible native code: "
- + CommaSeparatedList.str(apk.nativecode)
+ + " only supports " + CommaSeparatedList.str(apk.nativecode)
+ " while your architecture is " + cpuAbis.get(0));
return false;
}
@@ -437,6 +433,7 @@ public class DB {
public String address;
public String name;
public String description;
+ public int version; // index version, i.e. what fdroidserver built it - 0 if not specified
public boolean inuse;
public int priority;
public String pubkey; // null for an unsigned repo
@@ -530,7 +527,7 @@ public class DB {
}
}
- private final int DBVersion = 33;
+ private final int DBVersion = 35;
private int countAppsForRepo(int id) {
String[] selection = { "COUNT(distinct id)" };
@@ -609,6 +606,7 @@ public class DB {
mContext.getString(R.string.default_repo_name));
values.put("description",
mContext.getString(R.string.default_repo_description));
+ values.put("version", 0);
String pubkey = mContext.getString(R.string.default_repo_pubkey);
String fingerprint = DB.calcFingerprint(pubkey);
values.put("pubkey", pubkey);
@@ -626,9 +624,11 @@ public class DB {
mContext.getString(R.string.default_repo_name2));
values.put("description",
mContext.getString(R.string.default_repo_description2));
+ values.put("version", 0);
// default #2 is /archive which has the same key as /repo
values.put("pubkey", pubkey);
values.put("fingerprint", fingerprint);
+ values.put("maxage", 0);
values.put("inuse", 0);
values.put("priority", 20);
values.put("lastetag", (String) null);
@@ -720,23 +720,14 @@ public class DB {
db.execSQL("alter table " + TABLE_REPO + " add column maxage integer not null default 0");
}
- if (oldVersion < 33) {
+ if (oldVersion < 35) {
if (!columnExists(db, TABLE_REPO, "lastUpdated"))
db.execSQL("Alter table " + TABLE_REPO + " add column lastUpdated string");
}
-
}
}
- /**
- * 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) {
- return ContextCompat.create(ctx).getExternalCacheDir();
- }
-
private Context mContext;
private Apk.CompatibilityChecker compatChecker = null;
@@ -924,16 +915,23 @@ public class DB {
}
}
- Map apps = new HashMap();
- Cursor c = null;
+ // Start the map at the actual number of apps we will have
+ Cursor c = db.rawQuery("select count(*) from "+TABLE_APP, null);
+ c.moveToFirst();
+ int count = c.getInt(0);
+ c.close();
+ c = null;
+
+ Log.d("FDroid", "Will be fetching " + count + " apps, and this took us ");
+
+ Map apps = new HashMap(count);
long startTime = System.currentTimeMillis();
try {
String cols[] = new String[] { "antiFeatures", "requirements",
"categories", "id", "name", "summary", "icon", "license",
"curVersion", "curVercode", "added", "lastUpdated",
- "compatible", "ignoreAllUpdates", "ignoreThisUpdate",
- "provides" };
+ "compatible", "ignoreAllUpdates", "ignoreThisUpdate" };
c = db.query(TABLE_APP, cols, null, null, null, null, null);
c.moveToFirst();
while (!c.isAfterLast()) {
@@ -959,7 +957,6 @@ public class DB {
app.compatible = c.getInt(12) == 1;
app.ignoreAllUpdates = c.getInt(13) == 1;
app.ignoreThisUpdate = c.getInt(14);
- app.provides = DB.CommaSeparatedList.make(c.getString(15));
app.hasUpdates = false;
if (getinstalledinfo && systemApks.containsKey(app.id)) {
@@ -979,18 +976,13 @@ public class DB {
}
apps.put(app.id, app);
- if (app.provides != null) {
- for (String id : app.provides) {
- apps.put(id, app);
- }
- }
c.moveToNext();
}
c.close();
c = null;
- Log.d("FDroid", "Read app data from database " + " (took "
+ Log.d("FDroid", "Read app data from database (took "
+ (System.currentTimeMillis() - startTime) + " ms)");
List repos = getRepos();
@@ -1049,7 +1041,7 @@ public class DB {
c.close();
}
- Log.d("FDroid", "Read app and apk data from database " + " (took "
+ Log.d("FDroid", "Read app and apk data from database (took "
+ (System.currentTimeMillis() - startTime) + " ms)");
}
@@ -1141,7 +1133,7 @@ public class DB {
try {
String filter = "%" + query + "%";
c = db.query(TABLE_APP, new String[] { "id" },
- "id like ? or provides like ? or name like ? or summary like ? or description like ?",
+ "id like ? or name like ? or summary like ? or description like ?",
new String[] { filter, filter, filter, filter }, null, null, null);
c.moveToFirst();
while (!c.isAfterLast()) {
@@ -1412,8 +1404,8 @@ public class DB {
Cursor c = null;
try {
c = db.query(TABLE_REPO, new String[] { "address", "name",
- "description", "inuse", "priority", "pubkey", "fingerprint",
- "maxage", "lastetag", "lastUpdated" },
+ "description", "version", "inuse", "priority", "pubkey",
+ "fingerprint", "maxage", "lastetag", "lastUpdated" },
"id = ?", new String[] { Integer.toString(id) }, null, null, null);
if (!c.moveToFirst())
return null;
@@ -1422,19 +1414,19 @@ public class DB {
repo.address = c.getString(0);
repo.name = c.getString(1);
repo.description = c.getString(2);
- repo.inuse = (c.getInt(3) == 1);
- repo.priority = c.getInt(4);
- repo.pubkey = c.getString(5);
- repo.fingerprint = c.getString(6);
- repo.maxage = c.getInt(7);
- repo.lastetag = c.getString(8);
-
+ repo.version = c.getInt(3);
+ repo.inuse = (c.getInt(4) == 1);
+ repo.priority = c.getInt(5);
+ repo.pubkey = c.getString(6);
+ repo.fingerprint = c.getString(7);
+ repo.maxage = c.getInt(8);
+ repo.lastetag = c.getString(9);
try {
- repo.lastUpdated = c.getString(9) != null ?
- mDateFormat.parse( c.getString(9)) :
+ repo.lastUpdated = c.getString(10) != null ?
+ mDateFormat.parse( c.getString(10)) :
null;
} catch (ParseException e) {
- Log.e("FDroid", "Error parsing date " + c.getString(9));
+ Log.e("FDroid", "Error parsing date " + c.getString(10));
}
return repo;
} finally {
@@ -1449,8 +1441,8 @@ public class DB {
Cursor c = null;
try {
c = db.query(TABLE_REPO, new String[] { "id", "address", "name",
- "description", "inuse", "priority", "pubkey", "fingerprint",
- "maxage", "lastetag" },
+ "description", "version", "inuse", "priority", "pubkey",
+ "fingerprint", "maxage", "lastetag" },
null, null, null, null, "priority");
c.moveToFirst();
while (!c.isAfterLast()) {
@@ -1459,12 +1451,13 @@ public class DB {
repo.address = c.getString(1);
repo.name = c.getString(2);
repo.description = c.getString(3);
- repo.inuse = (c.getInt(4) == 1);
- repo.priority = c.getInt(5);
- repo.pubkey = c.getString(6);
- repo.fingerprint = c.getString(7);
- repo.maxage = c.getInt(8);
- repo.lastetag = c.getString(9);
+ repo.version = c.getInt(4);
+ repo.inuse = (c.getInt(5) == 1);
+ repo.priority = c.getInt(6);
+ repo.pubkey = c.getString(7);
+ repo.fingerprint = c.getString(8);
+ repo.maxage = c.getInt(9);
+ repo.lastetag = c.getString(10);
repos.add(repo);
c.moveToNext();
}
@@ -1524,6 +1517,7 @@ public class DB {
values.put("name", repo.name);
values.put("address", repo.address);
values.put("description", repo.description);
+ values.put("version", repo.version);
values.put("inuse", repo.inuse);
values.put("priority", repo.priority);
values.put("pubkey", repo.pubkey);
@@ -1558,12 +1552,14 @@ public class DB {
}
public void addRepo(String address, String name, String description,
- int priority, String pubkey, String fingerprint, int maxage, boolean inuse)
+ int version, int priority, String pubkey, String fingerprint,
+ int maxage, boolean inuse)
throws SecurityException {
ContentValues values = new ContentValues();
values.put("address", address);
values.put("name", name);
values.put("description", description);
+ values.put("version", version);
values.put("inuse", inuse ? 1 : 0);
values.put("priority", priority);
values.put("pubkey", pubkey);
diff --git a/src/org/fdroid/fdroid/FDroid.java b/src/org/fdroid/fdroid/FDroid.java
index 30a629925..0bc843cc4 100644
--- a/src/org/fdroid/fdroid/FDroid.java
+++ b/src/org/fdroid/fdroid/FDroid.java
@@ -127,8 +127,11 @@ public class FDroid extends FragmentActivity {
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
- menu.add(Menu.NONE, UPDATE_REPO, 1, R.string.menu_update_repo).setIcon(
+ MenuItem update = menu.add(Menu.NONE, UPDATE_REPO, 1, R.string.menu_update_repo).setIcon(
android.R.drawable.ic_menu_rotate);
+ MenuItemCompat.setShowAsAction(update,
+ MenuItemCompat.SHOW_AS_ACTION_ALWAYS |
+ MenuItemCompat.SHOW_AS_ACTION_WITH_TEXT);
menu.add(Menu.NONE, MANAGE_REPO, 2, R.string.menu_manage).setIcon(
android.R.drawable.ic_menu_agenda);
MenuItem search = menu.add(Menu.NONE, SEARCH, 3, R.string.menu_search).setIcon(
diff --git a/src/org/fdroid/fdroid/FDroidApp.java b/src/org/fdroid/fdroid/FDroidApp.java
index f2949ae88..7523c5245 100644
--- a/src/org/fdroid/fdroid/FDroidApp.java
+++ b/src/org/fdroid/fdroid/FDroidApp.java
@@ -31,11 +31,13 @@ import android.util.Log;
import android.content.Context;
import android.content.SharedPreferences;
-import com.nostra13.universalimageloader.utils.StorageUtils;
+import org.fdroid.fdroid.Utils;
+
import com.nostra13.universalimageloader.cache.disc.impl.LimitedAgeDiscCache;
import com.nostra13.universalimageloader.cache.disc.naming.FileNameGenerator;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;
+import com.nostra13.universalimageloader.utils.StorageUtils;
public class FDroidApp extends Application {
@@ -78,14 +80,14 @@ public class FDroidApp extends Application {
curTheme = Theme.valueOf(prefs.getString("theme", "dark"));
if (!prefs.getBoolean("cacheDownloaded", false)) {
- File local_path = DB.getDataPath(this);
+ File local_path = Utils.getApkCacheDir(this);
// Things can be null if the SD card is not ready - we'll just
// ignore that and do it next time.
- if(local_path != null) {
+ if (local_path != null) {
File[] files = local_path.listFiles();
- if(files != null) {
- for(File f : files) {
- if(f.getName().endsWith(".apk")) {
+ if (files != null) {
+ for (File f : files) {
+ if (f.getName().endsWith(".apk")) {
f.delete();
}
}
@@ -101,7 +103,8 @@ public class FDroidApp extends Application {
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(ctx)
.discCache(new LimitedAgeDiscCache(
- new File(StorageUtils.getCacheDirectory(ctx), "icons"),
+ new File(StorageUtils.getCacheDirectory(ctx, true),
+ "icons"),
new FileNameGenerator() {
@Override
public String generate(String imageUri) {
diff --git a/src/org/fdroid/fdroid/RepoXMLHandler.java b/src/org/fdroid/fdroid/RepoXMLHandler.java
index bbd3b523e..a95a7338e 100644
--- a/src/org/fdroid/fdroid/RepoXMLHandler.java
+++ b/src/org/fdroid/fdroid/RepoXMLHandler.java
@@ -64,8 +64,9 @@ public class RepoXMLHandler extends DefaultHandler {
private DB.Apk curapk = null;
private StringBuilder curchars = new StringBuilder();
- // After processing the XML, this will be -1 if the index didn't specify
- // a maximum age - otherwise it will be the value specified.
+ // After processing the XML, these will be -1 if the index didn't specify
+ // them - otherwise it will be the value specified.
+ private int version = -1;
private int maxage = -1;
// After processing the XML, this will be null if the index specified a
@@ -242,8 +243,6 @@ public class RepoXMLHandler extends DefaultHandler {
} catch (NumberFormatException ex) {
curapp.curVercode = -1;
}
- } else if (curel.equals("provides")) {
- curapp.provides = DB.CommaSeparatedList.make(str);
} else if (curel.equals("categories")) {
curapp.categories = DB.CommaSeparatedList.make(str);
} else if (curel.equals("antifeatures")) {
@@ -266,10 +265,12 @@ public class RepoXMLHandler extends DefaultHandler {
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
super.startElement(uri, localName, qName, attributes);
+
if (localName.equals("repo")) {
String pk = attributes.getValue("", "pubkey");
if (pk != null)
pubkey = pk;
+
String maxAgeAttr = attributes.getValue("", "maxage");
if (maxAgeAttr != null) {
try {
@@ -277,12 +278,20 @@ public class RepoXMLHandler extends DefaultHandler {
} catch (NumberFormatException nfe) {}
}
+ String versionAttr = attributes.getValue("", "version");
+ if (versionAttr != null) {
+ try {
+ version = Integer.parseInt(versionAttr);
+ } catch (NumberFormatException nfe) {}
+ }
+
String nm = attributes.getValue("", "name");
if (nm != null)
name = nm;
String dc = attributes.getValue("", "description");
if (dc != null)
description = dc;
+
} else if (localName.equals("application") && curapp == null) {
curapp = new DB.App();
curapp.detail_Populated = true;
@@ -293,11 +302,13 @@ public class RepoXMLHandler extends DefaultHandler {
new ProgressListener.Event(
RepoXMLHandler.PROGRESS_TYPE_PROCESS_XML, progressCounter,
totalAppCount, progressData));
+
} else if (localName.equals("package") && curapp != null && curapk == null) {
curapk = new DB.Apk();
curapk.id = curapp.id;
curapk.repo = repo.id;
hashType = null;
+
} else if (localName.equals("hash") && curapk != null) {
hashType = attributes.getValue("", "type");
}
@@ -511,6 +522,13 @@ public class RepoXMLHandler extends DefaultHandler {
repoChanged = true;
}
+ if (version != -1 && version != repo.version) {
+ Log.d("FDroid", "Repo specified a new version: from "
+ + repo.version + " to " + version);
+ repo.version = version;
+ repoChanged = true;
+ }
+
if (maxage != -1 && maxage != repo.maxage) {
Log.d("FDroid",
"Repo specified a new maximum age - updated");
diff --git a/src/org/fdroid/fdroid/Utils.java b/src/org/fdroid/fdroid/Utils.java
index 6f17c3445..a04492e9a 100644
--- a/src/org/fdroid/fdroid/Utils.java
+++ b/src/org/fdroid/fdroid/Utils.java
@@ -31,6 +31,10 @@ import java.io.OutputStream;
import java.security.MessageDigest;
import java.util.Formatter;
+import android.content.Context;
+
+import com.nostra13.universalimageloader.utils.StorageUtils;
+
public final class Utils {
public static final int BUFFER_SIZE = 4096;
@@ -86,6 +90,30 @@ public final class Utils {
return String.format(FRIENDLY_SIZE_FORMAT[i], s);
}
+ public static String getAndroidVersionName(int sdkLevel) {
+ switch (sdkLevel) {
+ case 19: return "4.4";
+ case 18: return "4.3";
+ case 17: return "4.2";
+ case 16: return "4.1";
+ case 15: return "4.0.3";
+ case 14: return "4.0";
+ case 13: return "3.2";
+ case 12: return "3.1";
+ case 11: return "3.0";
+ case 10: return "2.3.3";
+ case 9: return "2.3";
+ case 8: return "2.2";
+ case 7: return "2.1";
+ case 6: return "2.0.1";
+ case 5: return "2.0";
+ case 4: return "1.6";
+ case 3: return "1.5";
+ case 2: return "1.1";
+ default: return "1.0";
+ }
+ }
+
public static int countSubstringOccurrence(File file, String substring) throws IOException {
int count = 0;
BufferedReader reader = null;
@@ -152,4 +180,14 @@ public final class Utils {
}
return fingerprintString;
}
+
+ public static File getApkCacheDir(Context context) {
+ File apkCacheDir = new File(
+ StorageUtils.getCacheDirectory(context, true), "apks");
+ if (!apkCacheDir.exists()) {
+ apkCacheDir.mkdir();
+ }
+ return apkCacheDir;
+ }
+
}
diff --git a/src/org/fdroid/fdroid/compat/ContextCompat.java b/src/org/fdroid/fdroid/compat/ContextCompat.java
index dde5c5ca3..0daeabf5d 100644
--- a/src/org/fdroid/fdroid/compat/ContextCompat.java
+++ b/src/org/fdroid/fdroid/compat/ContextCompat.java
@@ -39,7 +39,7 @@ class OldContextCompatImpl extends ContextCompat {
public File getExternalCacheDir() {
File file = new File(Environment.getExternalStorageDirectory(),
"Android/data/org.fdroid.fdroid/cache");
- if(!file.exists())
+ if (!file.exists())
file.mkdirs();
return file;
}
diff --git a/src/org/fdroid/fdroid/views/AppListAdapter.java b/src/org/fdroid/fdroid/views/AppListAdapter.java
index 78d60e8f7..9783ab843 100644
--- a/src/org/fdroid/fdroid/views/AppListAdapter.java
+++ b/src/org/fdroid/fdroid/views/AppListAdapter.java
@@ -27,10 +27,13 @@ abstract public class AppListAdapter extends BaseAdapter {
private List items = new ArrayList();
private Context mContext;
+ private LayoutInflater mInflater;
private DisplayImageOptions displayImageOptions;
public AppListAdapter(Context context) {
mContext = context;
+ mInflater = (LayoutInflater) mContext.getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
displayImageOptions = new DisplayImageOptions.Builder()
.cacheInMemory(true)
@@ -76,38 +79,55 @@ abstract public class AppListAdapter extends BaseAdapter {
return position;
}
+ static class ViewHolder {
+ TextView name;
+ TextView summary;
+ TextView status;
+ TextView license;
+ ImageView icon;
+ }
+
@Override
public View getView(int position, View convertView, ViewGroup parent) {
boolean compact = Preferences.get().hasCompactLayout();
DB.App app = items.get(position);
+ ViewHolder holder;
if (convertView == null) {
- convertView = ((LayoutInflater) mContext.getSystemService(
- Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout.applistitem, null);
+ convertView = mInflater.inflate(R.layout.applistitem, null);
+
+ holder = new ViewHolder();
+ holder.name = (TextView) convertView.findViewById(R.id.name);
+ holder.summary = (TextView) convertView.findViewById(R.id.summary);
+ holder.status = (TextView) convertView.findViewById(R.id.status);
+ holder.license = (TextView) convertView.findViewById(R.id.license);
+ holder.icon = (ImageView) convertView.findViewById(R.id.icon);
+
+ convertView.setTag(holder);
+ } else {
+ holder = (ViewHolder) convertView.getTag();
}
- TextView name = (TextView) convertView.findViewById(R.id.name);
- TextView summary = (TextView) convertView.findViewById(R.id.summary);
- TextView status = (TextView) convertView.findViewById(R.id.status);
- TextView license = (TextView) convertView.findViewById(R.id.license);
- ImageView icon = (ImageView) convertView.findViewById(R.id.icon);
+ holder.name.setText(app.name);
+ holder.summary.setText(app.summary);
- name.setText(app.name);
- summary.setText(app.summary);
-
- int visibleOnCompact = compact ? View.VISIBLE : View.GONE;
- int notVisibleOnCompact = compact ? View.GONE : View.VISIBLE;
-
- layoutIcon(icon, compact);
- ImageLoader.getInstance().displayImage(app.iconUrl, icon,
+ layoutIcon(holder.icon, compact);
+ ImageLoader.getInstance().displayImage(app.iconUrl, holder.icon,
displayImageOptions);
- status.setText(getVersionInfo(app));
- license.setText(app.license);
+ holder.status.setText(getVersionInfo(app));
+ holder.license.setText(app.license);
// Disable it all if it isn't compatible...
- View[] views = { convertView, status, summary, license, name };
+ View[] views = {
+ convertView,
+ holder.status,
+ holder.summary,
+ holder.license,
+ holder.name
+ };
+
for (View view : views) {
view.setEnabled(app.compatible && !app.filtered);
}