Merge branch 'master' into experimental/refactor-update

This commit is contained in:
Peter Serwylo 2014-01-05 09:54:42 +11:00
commit b863802479
15 changed files with 258 additions and 157 deletions

2
.gitignore vendored
View File

@ -1,11 +1,9 @@
/local.properties /local.properties
/build.properties
.classpath .classpath
/bin/ /bin/
/gen/ /gen/
/build /build
/.gradle /.gradle
/proguard.cfg
/build.xml /build.xml
*~ *~
.idea .idea

View File

@ -18,6 +18,14 @@
android:layout_marginRight="4dp" android:layout_marginRight="4dp"
android:orientation="vertical" > android:orientation="vertical" >
<TextView
android:id="@+id/title"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:singleLine="false"
android:textSize="18sp"
android:textStyle="bold" />
<RelativeLayout <RelativeLayout
android:id="@+id/header" android:id="@+id/header"
android:layout_width="fill_parent" android:layout_width="fill_parent"
@ -25,41 +33,48 @@
android:layout_marginBottom="4dp" android:layout_marginBottom="4dp"
android:orientation="horizontal" > android:orientation="horizontal" >
<TextView
android:id="@+id/title"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_marginBottom="4dp"
android:singleLine="false"
android:textSize="18sp"
android:textStyle="bold" />
<ImageView <ImageView
android:id="@+id/icon" android:id="@+id/icon"
android:layout_width="48dp" android:layout_width="56dp"
android:layout_height="48dp" android:layout_height="56dp"
android:layout_below="@id/title" android:padding="4dp"
android:layout_marginRight="6dp"
android:scaleType="fitCenter" /> android:scaleType="fitCenter" />
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="4dp"
android:layout_toRightOf="@id/icon"
android:orientation="vertical" >
<TextView <TextView
android:id="@+id/license" android:id="@+id/license"
android:layout_width="wrap_content" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_below="@id/title" android:layout_alignParentTop="true"
android:layout_toRightOf="@id/icon" android:textSize="13sp" />
<TextView
android:id="@+id/categories"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="@id/license"
android:layout_above="@id/status"
android:layout_centerVertical="true"
android:textSize="13sp" /> android:textSize="13sp" />
<TextView <TextView
android:id="@+id/status" android:id="@+id/status"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_below="@id/license" android:layout_alignParentBottom="true"
android:layout_toRightOf="@id/icon"
android:textSize="13sp" /> android:textSize="13sp" />
</RelativeLayout>
</RelativeLayout> </RelativeLayout>
</LinearLayout> </LinearLayout>
</ScrollView> </ScrollView>
<ListView <ListView

View File

@ -15,7 +15,7 @@
android:textSize="18sp" /> android:textSize="18sp" />
<TextView android:id="@+id/status" <TextView android:id="@+id/status"
android:textSize="12sp" android:textSize="13sp"
android:maxLines="2" android:maxLines="2"
android:ellipsize="end" android:ellipsize="end"
android:layout_below="@id/version" android:layout_below="@id/version"
@ -23,28 +23,36 @@
android:layout_width="wrap_content" /> android:layout_width="wrap_content" />
<TextView android:id="@+id/added" <TextView android:id="@+id/added"
android:textSize="12sp" android:textSize="13sp"
android:layout_below="@id/status" android:layout_below="@id/status"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_width="wrap_content" /> android:layout_width="wrap_content" />
<TextView android:id="@+id/buildtype" <TextView android:id="@+id/buildtype"
android:textSize="12sp" android:textSize="13sp"
android:layout_alignParentRight="true" android:layout_alignParentRight="true"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_marginBottom="4sp" /> android:layout_marginBottom="4sp" />
<TextView android:id="@+id/size" <TextView android:id="@+id/size"
android:textSize="12sp" android:textSize="13sp"
android:layout_below="@id/buildtype" android:layout_below="@id/buildtype"
android:layout_alignParentRight="true" android:layout_alignParentRight="true"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_marginBottom="4sp" /> android:layout_marginBottom="4sp" />
<TextView android:id="@+id/api"
android:textSize="13sp"
android:layout_below="@id/buildtype"
android:layout_toLeftOf="@id/size"
android:layout_marginRight="16sp"
android:layout_height="wrap_content"
android:layout_width="wrap_content" />
<TextView android:id="@+id/nativecode" <TextView android:id="@+id/nativecode"
android:textSize="12sp" android:textSize="13sp"
android:layout_below="@id/size" android:layout_below="@id/size"
android:layout_alignParentRight="true" android:layout_alignParentRight="true"
android:layout_height="wrap_content" android:layout_height="wrap_content"

View File

@ -22,9 +22,8 @@
<RelativeLayout <RelativeLayout
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="fill_parent" android:layout_height="56dp"
android:padding="6dp" android:padding="6dp"
android:layout_centerVertical="true"
android:orientation="vertical" > android:orientation="vertical" >
<TextView <TextView
@ -33,9 +32,9 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentTop="true" android:layout_alignParentTop="true"
android:layout_alignParentRight="true" android:layout_alignParentRight="true"
android:paddingTop="2sp" android:paddingTop="3sp"
android:paddingBottom="2sp" android:paddingBottom="3sp"
android:layout_marginLeft="8dp" android:layout_marginLeft="8sp"
android:textSize="13sp" /> android:textSize="13sp" />
<TextView <TextView
@ -44,7 +43,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentBottom="true" android:layout_alignParentBottom="true"
android:layout_alignParentRight="true" android:layout_alignParentRight="true"
android:layout_marginLeft="8dp" android:layout_marginLeft="8sp"
android:textSize="13sp" /> android:textSize="13sp" />
<TextView <TextView

View File

@ -27,11 +27,11 @@
<TextView android:id="@+id/status" <TextView android:id="@+id/status"
android:singleLine="true" android:singleLine="true"
android:ellipsize="end" android:ellipsize="end"
android:paddingTop="2sp" android:paddingTop="3sp"
android:paddingBottom="2sp" android:paddingBottom="3sp"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginLeft="8dp" android:layout_marginLeft="10sp"
android:layout_alignParentTop="true" android:layout_alignParentTop="true"
android:layout_alignParentRight="true" /> android:layout_alignParentRight="true" />
@ -41,7 +41,7 @@
android:ellipsize="end" android:ellipsize="end"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginLeft="8dp" android:layout_marginLeft="10sp"
android:layout_alignParentBottom="true" android:layout_alignParentBottom="true"
android:layout_alignParentRight="true" /> android:layout_alignParentRight="true" />

View File

@ -163,5 +163,6 @@
<string name="compactlayout_on">Show icons at a smaller size</string> <string name="compactlayout_on">Show icons at a smaller size</string>
<string name="compactlayout_off">Show icons at regular size</string> <string name="compactlayout_off">Show icons at regular size</string>
<string name="theme">Theme</string> <string name="theme">Theme</string>
<string name="minsdk_or_later">Android %s or later</string>
</resources> </resources>

View File

@ -76,6 +76,9 @@ import com.nostra13.universalimageloader.core.display.FadeInBitmapDisplayer;
import com.nostra13.universalimageloader.core.DisplayImageOptions; import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader; import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.assist.ImageScaleType; import com.nostra13.universalimageloader.core.assist.ImageScaleType;
import com.nostra13.universalimageloader.utils.StorageUtils;
import android.os.Environment;
public class AppDetails extends ListActivity { public class AppDetails extends ListActivity {
@ -140,10 +143,11 @@ public class AppDetails extends ListActivity {
tv = (TextView) v.findViewById(R.id.status); tv = (TextView) v.findViewById(R.id.status);
if (apk.vercode == app.installedVerCode if (apk.vercode == app.installedVerCode
&& apk.sig.equals(mInstalledSigID)) && apk.sig.equals(mInstalledSigID)) {
tv.setText(getString(R.string.inst)); tv.setText(getString(R.string.inst));
else } else {
tv.setText(getString(R.string.not_inst)); tv.setText(getString(R.string.not_inst));
}
tv.setEnabled(apk.compatible); tv.setEnabled(apk.compatible);
tv = (TextView) v.findViewById(R.id.size); tv = (TextView) v.findViewById(R.id.size);
@ -153,6 +157,16 @@ public class AppDetails extends ListActivity {
tv.setText(Utils.getFriendlySize(apk.detail_size)); tv.setText(Utils.getFriendlySize(apk.detail_size));
tv.setEnabled(apk.compatible); 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); tv = (TextView) v.findViewById(R.id.buildtype);
if (apk.srcname != null) { if (apk.srcname != null) {
tv.setText("source"); tv.setText("source");
@ -160,6 +174,7 @@ public class AppDetails extends ListActivity {
tv.setText("bin"); tv.setText("bin");
} }
tv.setEnabled(apk.compatible); tv.setEnabled(apk.compatible);
tv = (TextView) v.findViewById(R.id.added); tv = (TextView) v.findViewById(R.id.added);
if (apk.added != null) { if (apk.added != null) {
tv.setVisibility(View.VISIBLE); tv.setVisibility(View.VISIBLE);
@ -168,6 +183,7 @@ public class AppDetails extends ListActivity {
} else { } else {
tv.setVisibility(View.GONE); tv.setVisibility(View.GONE);
} }
tv = (TextView) v.findViewById(R.id.nativecode); tv = (TextView) v.findViewById(R.id.nativecode);
if (pref_expert && apk.nativecode != null) { if (pref_expert && apk.nativecode != null) {
tv.setVisibility(View.VISIBLE); tv.setVisibility(View.VISIBLE);
@ -457,26 +473,6 @@ public class AppDetails extends ListActivity {
tv = (TextView) infoView.findViewById(R.id.description); 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()); tv.setMovementMethod(LinkMovementMethod.getInstance());
// Need this to add the unimplemented support for ordered and unordered // 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, public void onClick(DialogInterface dialog,
int whichButton) { int whichButton) {
downloadHandler = new DownloadHandler(app.curApk, downloadHandler = new DownloadHandler(app.curApk,
repoaddress, DB repoaddress, Utils
.getDataPath(getBaseContext())); .getApkCacheDir(getBaseContext()));
} }
}); });
ask_alrt.setNegativeButton(getString(R.string.no), ask_alrt.setNegativeButton(getString(R.string.no),
@ -879,7 +875,7 @@ public class AppDetails extends ListActivity {
return; return;
} }
downloadHandler = new DownloadHandler(app.curApk, repoaddress, downloadHandler = new DownloadHandler(app.curApk, repoaddress,
DB.getDataPath(this)); Utils.getApkCacheDir(getBaseContext()));
} }
private void removeApk(String id) { private void removeApk(String id) {

View File

@ -105,7 +105,7 @@ public class DB {
+ "lastUpdated string," + "compatible int not null," + "lastUpdated string," + "compatible int not null,"
+ "ignoreAllUpdates int not null," + "ignoreAllUpdates int not null,"
+ "ignoreThisUpdate int not null," + "ignoreThisUpdate int not null,"
+ "provides string," + "primary key(id));"; + "primary key(id));";
public static class App implements Comparable<App> { public static class App implements Comparable<App> {
@ -123,7 +123,6 @@ public class DB {
detail_dogecoinAddr = null; detail_dogecoinAddr = null;
detail_webURL = null; detail_webURL = null;
categories = null; categories = null;
provides = null;
antiFeatures = null; antiFeatures = null;
requirements = null; requirements = null;
hasUpdates = false; hasUpdates = false;
@ -198,9 +197,6 @@ public class DB {
public int installedVerCode; public int installedVerCode;
public boolean userInstalled; 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 // List of categories (as defined in the metadata
// documentation) or null if there aren't any. // documentation) or null if there aren't any.
public CommaSeparatedList categories; public CommaSeparatedList categories;
@ -374,10 +370,11 @@ public class DB {
} }
} }
cpuAbis = new ArrayList<String>(); cpuAbis = new ArrayList<String>(2);
if (hasApi(8))
cpuAbis.add(android.os.Build.CPU_ABI2);
cpuAbis.add(android.os.Build.CPU_ABI); cpuAbis.add(android.os.Build.CPU_ABI);
if (hasApi(8)) {
cpuAbis.add(android.os.Build.CPU_ABI2);
}
Log.d("FDroid", logMsg.toString()); Log.d("FDroid", logMsg.toString());
} }
@ -410,8 +407,7 @@ public class DB {
} }
if (!compatibleApi(apk.nativecode)) { if (!compatibleApi(apk.nativecode)) {
Log.d("FDroid", apk.id + " vercode " + apk.vercode Log.d("FDroid", apk.id + " vercode " + apk.vercode
+ " makes use of incompatible native code: " + " only supports " + CommaSeparatedList.str(apk.nativecode)
+ CommaSeparatedList.str(apk.nativecode)
+ " while your architecture is " + cpuAbis.get(0)); + " while your architecture is " + cpuAbis.get(0));
return false; return false;
} }
@ -434,6 +430,7 @@ public class DB {
public String address; public String address;
public String name; public String name;
public String description; public String description;
public int version; // index version, i.e. what fdroidserver built it - 0 if not specified
public boolean inuse; public boolean inuse;
public int priority; public int priority;
public String pubkey; // null for an unsigned repo public String pubkey; // null for an unsigned repo
@ -442,7 +439,7 @@ public class DB {
public String lastetag; // last etag we updated from, null forces update public String lastetag; // last etag we updated from, null forces update
} }
private final int DBVersion = 32; private final int DBVersion = 34;
private static void createAppApk(SQLiteDatabase db) { private static void createAppApk(SQLiteDatabase db) {
db.execSQL(CREATE_TABLE_APP); db.execSQL(CREATE_TABLE_APP);
@ -508,6 +505,7 @@ public class DB {
mContext.getString(R.string.default_repo_name)); mContext.getString(R.string.default_repo_name));
values.put("description", values.put("description",
mContext.getString(R.string.default_repo_description)); mContext.getString(R.string.default_repo_description));
values.put("version", 0);
String pubkey = mContext.getString(R.string.default_repo_pubkey); String pubkey = mContext.getString(R.string.default_repo_pubkey);
String fingerprint = DB.calcFingerprint(pubkey); String fingerprint = DB.calcFingerprint(pubkey);
values.put("pubkey", pubkey); values.put("pubkey", pubkey);
@ -525,9 +523,11 @@ public class DB {
mContext.getString(R.string.default_repo_name2)); mContext.getString(R.string.default_repo_name2));
values.put("description", values.put("description",
mContext.getString(R.string.default_repo_description2)); mContext.getString(R.string.default_repo_description2));
values.put("version", 0);
// default #2 is /archive which has the same key as /repo // default #2 is /archive which has the same key as /repo
values.put("pubkey", pubkey); values.put("pubkey", pubkey);
values.put("fingerprint", fingerprint); values.put("fingerprint", fingerprint);
values.put("maxage", 0);
values.put("inuse", 0); values.put("inuse", 0);
values.put("priority", 20); values.put("priority", 20);
values.put("lastetag", (String) null); values.put("lastetag", (String) null);
@ -615,18 +615,14 @@ public class DB {
if (oldVersion < 30) { if (oldVersion < 30) {
db.execSQL("alter table " + TABLE_REPO + " add column maxage integer not null default 0"); db.execSQL("alter table " + TABLE_REPO + " add column maxage integer not null default 0");
} }
if (oldVersion < 34) {
db.execSQL("alter table " + TABLE_REPO + " add column version integer not null default 0");
}
} }
} }
/**
* 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 Context mContext;
private Apk.CompatibilityChecker compatChecker = null; private Apk.CompatibilityChecker compatChecker = null;
@ -803,16 +799,23 @@ public class DB {
} }
} }
Map<String, App> apps = new HashMap<String, App>(); // Start the map at the actual number of apps we will have
Cursor c = null; 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<String, App> apps = new HashMap<String, App>(count);
long startTime = System.currentTimeMillis(); long startTime = System.currentTimeMillis();
try { try {
String cols[] = new String[] { "antiFeatures", "requirements", String cols[] = new String[] { "antiFeatures", "requirements",
"categories", "id", "name", "summary", "icon", "license", "categories", "id", "name", "summary", "icon", "license",
"curVersion", "curVercode", "added", "lastUpdated", "curVersion", "curVercode", "added", "lastUpdated",
"compatible", "ignoreAllUpdates", "ignoreThisUpdate", "compatible", "ignoreAllUpdates", "ignoreThisUpdate" };
"provides" };
c = db.query(TABLE_APP, cols, null, null, null, null, null); c = db.query(TABLE_APP, cols, null, null, null, null, null);
c.moveToFirst(); c.moveToFirst();
while (!c.isAfterLast()) { while (!c.isAfterLast()) {
@ -838,7 +841,6 @@ public class DB {
app.compatible = c.getInt(12) == 1; app.compatible = c.getInt(12) == 1;
app.ignoreAllUpdates = c.getInt(13) == 1; app.ignoreAllUpdates = c.getInt(13) == 1;
app.ignoreThisUpdate = c.getInt(14); app.ignoreThisUpdate = c.getInt(14);
app.provides = DB.CommaSeparatedList.make(c.getString(15));
app.hasUpdates = false; app.hasUpdates = false;
if (getinstalledinfo && systemApks.containsKey(app.id)) { if (getinstalledinfo && systemApks.containsKey(app.id)) {
@ -858,18 +860,13 @@ public class DB {
} }
apps.put(app.id, app); apps.put(app.id, app);
if (app.provides != null) {
for (String id : app.provides) {
apps.put(id, app);
}
}
c.moveToNext(); c.moveToNext();
} }
c.close(); c.close();
c = null; c = null;
Log.d("FDroid", "Read app data from database " + " (took " Log.d("FDroid", "Read app data from database (took "
+ (System.currentTimeMillis() - startTime) + " ms)"); + (System.currentTimeMillis() - startTime) + " ms)");
List<Repo> repos = getRepos(); List<Repo> repos = getRepos();
@ -928,7 +925,7 @@ public class DB {
c.close(); 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)"); + (System.currentTimeMillis() - startTime) + " ms)");
} }
@ -1020,7 +1017,7 @@ public class DB {
try { try {
String filter = "%" + query + "%"; String filter = "%" + query + "%";
c = db.query(TABLE_APP, new String[] { "id" }, 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); new String[] { filter, filter, filter, filter }, null, null, null);
c.moveToFirst(); c.moveToFirst();
while (!c.isAfterLast()) { while (!c.isAfterLast()) {
@ -1291,8 +1288,8 @@ public class DB {
Cursor c = null; Cursor c = null;
try { try {
c = db.query(TABLE_REPO, new String[] { "address", "name", c = db.query(TABLE_REPO, new String[] { "address", "name",
"description", "inuse", "priority", "pubkey", "fingerprint", "description", "version", "inuse", "priority", "pubkey",
"maxage", "lastetag" }, "fingerprint", "maxage", "lastetag" },
"id = ?", new String[] { Integer.toString(id) }, null, null, null); "id = ?", new String[] { Integer.toString(id) }, null, null, null);
if (!c.moveToFirst()) if (!c.moveToFirst())
return null; return null;
@ -1301,12 +1298,13 @@ public class DB {
repo.address = c.getString(0); repo.address = c.getString(0);
repo.name = c.getString(1); repo.name = c.getString(1);
repo.description = c.getString(2); repo.description = c.getString(2);
repo.inuse = (c.getInt(3) == 1); repo.version = c.getInt(3);
repo.priority = c.getInt(4); repo.inuse = (c.getInt(4) == 1);
repo.pubkey = c.getString(5); repo.priority = c.getInt(5);
repo.fingerprint = c.getString(6); repo.pubkey = c.getString(6);
repo.maxage = c.getInt(7); repo.fingerprint = c.getString(7);
repo.lastetag = c.getString(8); repo.maxage = c.getInt(8);
repo.lastetag = c.getString(9);
return repo; return repo;
} finally { } finally {
if (c != null) if (c != null)
@ -1320,8 +1318,8 @@ public class DB {
Cursor c = null; Cursor c = null;
try { try {
c = db.query(TABLE_REPO, new String[] { "id", "address", "name", c = db.query(TABLE_REPO, new String[] { "id", "address", "name",
"description", "inuse", "priority", "pubkey", "fingerprint", "description", "version", "inuse", "priority", "pubkey",
"maxage", "lastetag" }, "fingerprint", "maxage", "lastetag" },
null, null, null, null, "priority"); null, null, null, null, "priority");
c.moveToFirst(); c.moveToFirst();
while (!c.isAfterLast()) { while (!c.isAfterLast()) {
@ -1330,12 +1328,13 @@ public class DB {
repo.address = c.getString(1); repo.address = c.getString(1);
repo.name = c.getString(2); repo.name = c.getString(2);
repo.description = c.getString(3); repo.description = c.getString(3);
repo.inuse = (c.getInt(4) == 1); repo.version = c.getInt(4);
repo.priority = c.getInt(5); repo.inuse = (c.getInt(5) == 1);
repo.pubkey = c.getString(6); repo.priority = c.getInt(6);
repo.fingerprint = c.getString(7); repo.pubkey = c.getString(7);
repo.maxage = c.getInt(8); repo.fingerprint = c.getString(8);
repo.lastetag = c.getString(9); repo.maxage = c.getInt(9);
repo.lastetag = c.getString(10);
repos.add(repo); repos.add(repo);
c.moveToNext(); c.moveToNext();
} }
@ -1365,6 +1364,7 @@ public class DB {
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
values.put("name", repo.name); values.put("name", repo.name);
values.put("description", repo.description); values.put("description", repo.description);
values.put("version", repo.version);
values.put("inuse", repo.inuse); values.put("inuse", repo.inuse);
values.put("priority", repo.priority); values.put("priority", repo.priority);
values.put("pubkey", repo.pubkey); values.put("pubkey", repo.pubkey);
@ -1388,12 +1388,14 @@ public class DB {
} }
public void addRepo(String address, String name, String description, 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 { throws SecurityException {
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
values.put("address", address); values.put("address", address);
values.put("name", name); values.put("name", name);
values.put("description", description); values.put("description", description);
values.put("version", version);
values.put("inuse", inuse ? 1 : 0); values.put("inuse", inuse ? 1 : 0);
values.put("priority", priority); values.put("priority", priority);
values.put("pubkey", pubkey); values.put("pubkey", pubkey);

View File

@ -132,8 +132,11 @@ public class FDroid extends FragmentActivity {
public boolean onCreateOptionsMenu(Menu menu) { public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(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); 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( menu.add(Menu.NONE, MANAGE_REPO, 2, R.string.menu_manage).setIcon(
android.R.drawable.ic_menu_agenda); android.R.drawable.ic_menu_agenda);
MenuItem search = menu.add(Menu.NONE, SEARCH, 3, R.string.menu_search).setIcon( MenuItem search = menu.add(Menu.NONE, SEARCH, 3, R.string.menu_search).setIcon(

View File

@ -31,11 +31,13 @@ import android.util.Log;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences; 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.impl.LimitedAgeDiscCache;
import com.nostra13.universalimageloader.cache.disc.naming.FileNameGenerator; import com.nostra13.universalimageloader.cache.disc.naming.FileNameGenerator;
import com.nostra13.universalimageloader.core.ImageLoader; import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.ImageLoaderConfiguration; import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;
import com.nostra13.universalimageloader.utils.StorageUtils;
public class FDroidApp extends Application { public class FDroidApp extends Application {
@ -78,7 +80,7 @@ public class FDroidApp extends Application {
curTheme = Theme.valueOf(prefs.getString("theme", "dark")); curTheme = Theme.valueOf(prefs.getString("theme", "dark"));
if (!prefs.getBoolean("cacheDownloaded", false)) { 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 // Things can be null if the SD card is not ready - we'll just
// ignore that and do it next time. // ignore that and do it next time.
if (local_path != null) { if (local_path != null) {
@ -101,7 +103,8 @@ public class FDroidApp extends Application {
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(ctx) ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(ctx)
.discCache(new LimitedAgeDiscCache( .discCache(new LimitedAgeDiscCache(
new File(StorageUtils.getCacheDirectory(ctx), "icons"), new File(StorageUtils.getCacheDirectory(ctx, true),
"icons"),
new FileNameGenerator() { new FileNameGenerator() {
@Override @Override
public String generate(String imageUri) { public String generate(String imageUri) {

View File

@ -218,7 +218,7 @@ public class ManageRepo extends ListActivity {
protected void addRepo(String repoUri, String fingerprint) { protected void addRepo(String repoUri, String fingerprint) {
try { try {
DB db = DB.getDB(); DB db = DB.getDB();
db.addRepo(repoUri, null, null, 10, null, fingerprint, 0, true); db.addRepo(repoUri, null, null, 0, 10, null, fingerprint, 0, true);
} finally { } finally {
DB.releaseDB(); DB.releaseDB();
} }

View File

@ -89,8 +89,9 @@ public class RepoXMLHandler extends DefaultHandler {
private DB.Apk curapk = null; private DB.Apk curapk = null;
private StringBuilder curchars = new StringBuilder(); private StringBuilder curchars = new StringBuilder();
// After processing the XML, this will be null if the index didn't specify // After processing the XML, these will be null if the index didn't specify
// a maximum age - otherwise it will be the value specified. // them - otherwise it will be the value specified.
private String version;
private String maxage; private String maxage;
// After processing the XML, this will be null if the index specified a // After processing the XML, this will be null if the index specified a
@ -278,8 +279,6 @@ public class RepoXMLHandler extends DefaultHandler {
} catch (NumberFormatException ex) { } catch (NumberFormatException ex) {
curapp.curVercode = -1; curapp.curVercode = -1;
} }
} else if (curel.equals("provides")) {
curapp.provides = DB.CommaSeparatedList.make(str);
} else if (curel.equals("categories")) { } else if (curel.equals("categories")) {
curapp.categories = DB.CommaSeparatedList.make(str); curapp.categories = DB.CommaSeparatedList.make(str);
} else if (curel.equals("antifeatures")) { } else if (curel.equals("antifeatures")) {
@ -294,10 +293,12 @@ public class RepoXMLHandler extends DefaultHandler {
public void startElement(String uri, String localName, String qName, public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException { Attributes attributes) throws SAXException {
super.startElement(uri, localName, qName, attributes); super.startElement(uri, localName, qName, attributes);
if (localName.equals("repo")) { if (localName.equals("repo")) {
String pk = attributes.getValue("", "pubkey"); String pk = attributes.getValue("", "pubkey");
if (pk != null) if (pk != null)
pubkey = pk; pubkey = pk;
version = attributes.getValue("", "version");
maxage = attributes.getValue("", "maxage"); maxage = attributes.getValue("", "maxage");
String nm = attributes.getValue("", "name"); String nm = attributes.getValue("", "name");
if (nm != null) if (nm != null)
@ -305,6 +306,7 @@ public class RepoXMLHandler extends DefaultHandler {
String dc = attributes.getValue("", "description"); String dc = attributes.getValue("", "description");
if (dc != null) if (dc != null)
description = dc; description = dc;
} else if (localName.equals("application") && curapp == null) { } else if (localName.equals("application") && curapp == null) {
curapp = new DB.App(); curapp = new DB.App();
curapp.detail_Populated = true; curapp.detail_Populated = true;
@ -315,11 +317,13 @@ public class RepoXMLHandler extends DefaultHandler {
new ProgressListener.Event( new ProgressListener.Event(
RepoUpdater.PROGRESS_TYPE_PROCESS_XML, progressCounter, RepoUpdater.PROGRESS_TYPE_PROCESS_XML, progressCounter,
totalAppCount, progressData)); totalAppCount, progressData));
} else if (localName.equals("package") && curapp != null && curapk == null) { } else if (localName.equals("package") && curapp != null && curapk == null) {
curapk = new DB.Apk(); curapk = new DB.Apk();
curapk.id = curapp.id; curapk.id = curapp.id;
curapk.repo = repo.id; curapk.repo = repo.id;
hashType = null; hashType = null;
} else if (localName.equals("hash") && curapk != null) { } else if (localName.equals("hash") && curapk != null) {
hashType = attributes.getValue("", "type"); hashType = attributes.getValue("", "type");
} }
@ -726,6 +730,17 @@ public class RepoXMLHandler extends DefaultHandler {
DB.releaseDB(); DB.releaseDB();
} }
} }
boolean updateRepo = false;
if (handler.version != null) {
int version = Integer.parseInt(handler.version);
if (version != repo.version) {
Log.d("FDroid", "Repo specified a new version: from "
+ repo.version + " to " + version);
repo.version = version;
updateRepo = true;
}
}
if (handler.maxage != null) { if (handler.maxage != null) {
int maxage = Integer.parseInt(handler.maxage); int maxage = Integer.parseInt(handler.maxage);
@ -733,6 +748,11 @@ public class RepoXMLHandler extends DefaultHandler {
Log.d("FDroid", Log.d("FDroid",
"Repo specified a new maximum age - updated"); "Repo specified a new maximum age - updated");
repo.maxage = maxage; repo.maxage = maxage;
updateRepo = true;
}
}
if (updateRepo) {
try { try {
DB db = DB.getDB(); DB db = DB.getDB();
db.updateRepoByAddress(repo); db.updateRepoByAddress(repo);
@ -740,7 +760,6 @@ public class RepoXMLHandler extends DefaultHandler {
DB.releaseDB(); DB.releaseDB();
} }
} }
}
} else if (code == 304) { } else if (code == 304) {
// The index is unchanged since we last read it. We just mark // The index is unchanged since we last read it. We just mark

View File

@ -27,6 +27,10 @@ import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import android.content.Context;
import com.nostra13.universalimageloader.utils.StorageUtils;
public final class Utils { public final class Utils {
public static final int BUFFER_SIZE = 4096; public static final int BUFFER_SIZE = 4096;
@ -86,6 +90,30 @@ public final class Utils {
return String.format(FRIENDLY_SIZE_FORMAT[i], s); 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 { public static int countSubstringOccurrence(File file, String substring) throws IOException {
int count = 0; int count = 0;
BufferedReader reader = null; BufferedReader reader = null;
@ -123,4 +151,13 @@ public final class Utils {
return count; return count;
} }
public static File getApkCacheDir(Context context) {
File apkCacheDir = new File(
StorageUtils.getCacheDirectory(context, true), "apks");
if (!apkCacheDir.exists()) {
apkCacheDir.mkdir();
}
return apkCacheDir;
}
} }

View File

@ -27,10 +27,13 @@ abstract public class AppListAdapter extends BaseAdapter {
private List<DB.App> items = new ArrayList<DB.App>(); private List<DB.App> items = new ArrayList<DB.App>();
private Context mContext; private Context mContext;
private LayoutInflater mInflater;
private DisplayImageOptions displayImageOptions; private DisplayImageOptions displayImageOptions;
public AppListAdapter(Context context) { public AppListAdapter(Context context) {
mContext = context; mContext = context;
mInflater = (LayoutInflater) mContext.getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
displayImageOptions = new DisplayImageOptions.Builder() displayImageOptions = new DisplayImageOptions.Builder()
.cacheInMemory(true) .cacheInMemory(true)
@ -76,38 +79,55 @@ abstract public class AppListAdapter extends BaseAdapter {
return position; return position;
} }
static class ViewHolder {
TextView name;
TextView summary;
TextView status;
TextView license;
ImageView icon;
}
@Override @Override
public View getView(int position, View convertView, ViewGroup parent) { public View getView(int position, View convertView, ViewGroup parent) {
boolean compact = Preferences.get().hasCompactLayout(); boolean compact = Preferences.get().hasCompactLayout();
DB.App app = items.get(position); DB.App app = items.get(position);
ViewHolder holder;
if (convertView == null) { if (convertView == null) {
convertView = ((LayoutInflater) mContext.getSystemService( convertView = mInflater.inflate(R.layout.applistitem, null);
Context.LAYOUT_INFLATER_SERVICE)).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); holder.name.setText(app.name);
TextView summary = (TextView) convertView.findViewById(R.id.summary); holder.summary.setText(app.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);
name.setText(app.name); layoutIcon(holder.icon, compact);
summary.setText(app.summary); ImageLoader.getInstance().displayImage(app.iconUrl, holder.icon,
int visibleOnCompact = compact ? View.VISIBLE : View.GONE;
int notVisibleOnCompact = compact ? View.GONE : View.VISIBLE;
layoutIcon(icon, compact);
ImageLoader.getInstance().displayImage(app.iconUrl, icon,
displayImageOptions); displayImageOptions);
status.setText(getVersionInfo(app)); holder.status.setText(getVersionInfo(app));
license.setText(app.license); holder.license.setText(app.license);
// Disable it all if it isn't compatible... // 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) { for (View view : views) {
view.setEnabled(app.compatible && !app.filtered); view.setEnabled(app.compatible && !app.filtered);
} }