diff --git a/res/values/strings.xml b/res/values/strings.xml
index 746e99b0f..8428ece70 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -4,6 +4,7 @@
Found one application matching \'%s\':
No applications were found matching \'%s\'
The new version is signed with a different key to the old one. To install the new version, the old one must be uninstalled first. Please do this and try again. (Note that uninstalling will erase any internal data stored by the application)
+ Android says this package is not compatible with your device. Do you want to try and install it anyway?
Version
%d versions available
%d version available
diff --git a/res/xml/preferences.xml b/res/xml/preferences.xml
index 51f7c5f67..97d8c5eca 100644
--- a/res/xml/preferences.xml
+++ b/res/xml/preferences.xml
@@ -37,6 +37,9 @@
android:key="antiNonFreeDep" />
+
diff --git a/src/org/fdroid/fdroid/AppDetails.java b/src/org/fdroid/fdroid/AppDetails.java
index 787d6966c..0f80d040c 100644
--- a/src/org/fdroid/fdroid/AppDetails.java
+++ b/src/org/fdroid/fdroid/AppDetails.java
@@ -136,6 +136,15 @@ public class AppDetails extends ListActivity {
} else {
added.setVisibility(View.GONE);
}
+
+ // Disable it all if it isn't compatible...
+ if (!apk.compatible) {
+ View[] views = { v, version, status, size, buildtype, added };
+ for (View view : views) {
+ view.setEnabled(false);
+ }
+ }
+
return v;
}
}
@@ -167,8 +176,9 @@ public class AppDetails extends ListActivity {
private DownloadHandler downloadHandler;
private boolean stateRetained;
- private Context mctx = this;
+ LinearLayout headerView;
+ private Context mctx = this;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -190,6 +200,13 @@ public class AppDetails extends ListActivity {
appid = i.getStringExtra("appid");
}
+ // Set up the list...
+ headerView = new LinearLayout(this);
+ ListView lv = (ListView) findViewById(android.R.id.list);
+ lv.addHeaderView(headerView);
+ ApkListAdapter la = new ApkListAdapter(this, null);
+ setListAdapter(la);
+
}
private boolean pref_cacheDownloaded;
@@ -263,8 +280,6 @@ public class AppDetails extends ListActivity {
// place of reset(), so it must initialize all fields normally set
// there.
private void copyState(AppDetails old) {
- ApkListAdapter oldAdapter = (ApkListAdapter) old.getListAdapter();
- setListAdapter(new ApkListAdapter(this, oldAdapter.getItems()));
if (old.downloadHandler != null)
downloadHandler = new DownloadHandler(old.downloadHandler);
app = old.app;
@@ -328,29 +343,27 @@ public class AppDetails extends ListActivity {
private void resetViews() {
- // Clear the listadapter, because we can't mess with the listview's
- // header view while it's set.
- setListAdapter(null);
-
+ // Repopulate the list...
+ ApkListAdapter la = (ApkListAdapter) getListAdapter();
+ la.items.clear();
+ for (DB.Apk apk : app.apks)
+ la.addItem(apk);
+
// Insert the 'infoView' (which contains the summary, various odds and
// ends, and the description) into the appropriate place, if we're in
// landscape mode. In portrait mode, we put it in the listview's
// header..
View infoView = View.inflate(this, R.layout.appinfo, null);
LinearLayout landparent = (LinearLayout) findViewById(R.id.landleft);
- ListView lv = (ListView) findViewById(android.R.id.list);
+ headerView.removeAllViews();
if (landparent != null) {
landparent.addView(infoView);
+ Log.d("FDroid", "Setting landparent infoview");
} else {
- lv.addHeaderView(infoView);
+ headerView.addView(infoView);
+ Log.d("FDroid", "Setting header infoview");
}
- // Set up the list...
- ApkListAdapter la = new ApkListAdapter(this, null);
- for (DB.Apk apk : app.apks)
- la.addItem(apk);
- setListAdapter(la);
-
// Set the icon...
ImageView iv = (ImageView) findViewById(R.id.icon);
File icon = new File(DB.getIconsPath(), app.icon);
@@ -427,7 +440,7 @@ public class AppDetails extends ListActivity {
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
- curapk = app.apks.get(position);
+ curapk = app.apks.get(position - l.getHeaderViewsCount());
if (app.installedVersion != null
&& app.installedVersion.equals(curapk.version)) {
removeApk(app.id);
@@ -525,6 +538,27 @@ public class AppDetails extends ListActivity {
// Install the version of this app denoted by 'curapk'.
private void install() {
+ if(!curapk.compatible) {
+ AlertDialog.Builder ask_alrt = new AlertDialog.Builder(this);
+ ask_alrt.setMessage(getString(R.string.installIncompatible));
+ ask_alrt.setPositiveButton(getString(R.string.yes),
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog,
+ int whichButton) {
+ downloadHandler = new DownloadHandler(curapk);
+ }
+ });
+ ask_alrt.setNegativeButton(getString(R.string.no),
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog,
+ int whichButton) {
+ return;
+ }
+ });
+ AlertDialog alert = ask_alrt.create();
+ alert.show();
+ return;
+ }
if (mInstalledSigID != null && curapk.sig != null
&& !curapk.sig.equals(mInstalledSigID)) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
diff --git a/src/org/fdroid/fdroid/AppListAdapter.java b/src/org/fdroid/fdroid/AppListAdapter.java
index efdb0d18a..1b23273a9 100644
--- a/src/org/fdroid/fdroid/AppListAdapter.java
+++ b/src/org/fdroid/fdroid/AppListAdapter.java
@@ -90,6 +90,14 @@ public class AppListAdapter extends BaseAdapter {
icon.setImageResource(android.R.drawable.sym_def_app_icon);
}
+ // Disable it all if it isn't compatible...
+ if (!app.compatible) {
+ View[] views = { v, status, summary, license, name };
+ for (View view : views) {
+ view.setEnabled(false);
+ }
+ }
+
return v;
}
diff --git a/src/org/fdroid/fdroid/DB.java b/src/org/fdroid/fdroid/DB.java
index 2f629e015..6d1fc3b68 100644
--- a/src/org/fdroid/fdroid/DB.java
+++ b/src/org/fdroid/fdroid/DB.java
@@ -94,7 +94,8 @@ public class DB {
+ "curVersion text," + "curVercode integer,"
+ "antiFeatures string," + "donateURL string,"
+ "requirements string," + "category string," + "added string,"
- + "lastUpdated string," + "primary key(id));";
+ + "lastUpdated string," + "compatible int not null,"
+ + "primary key(id));";
public static class App implements Comparable {
@@ -117,11 +118,15 @@ public class DB {
lastUpdated = null;
apks = new Vector();
detail_Populated = false;
+ compatible = false;
}
// True when all the detail fields are populated, False otherwise.
public boolean detail_Populated;
+ // True if compatible with the device (i.e. if at least one apk is)
+ public boolean compatible;
+
public String id;
public String name;
public String summary;
@@ -224,7 +229,8 @@ public class DB {
+ "size int not null," + "apkSource text," + "sig string,"
+ "srcname string," + "minSdkVersion integer,"
+ "permissions string," + "features string," + "hashType string,"
- + "added string," + "primary key(id,vercode));";
+ + "added string," + "compatible int not null,"
+ + "primary key(id,vercode));";
public static class Apk {
@@ -237,6 +243,7 @@ public class DB {
detail_hash = null;
detail_hashType = null;
detail_permissions = null;
+ compatible = false;
}
public String id;
@@ -256,6 +263,9 @@ public class DB {
// transition to this field existing.
public String sig;
+ // True if compatible with the device.
+ public boolean compatible;
+
public String apkName;
// If null, the apk comes from the same server as the repo index.
@@ -363,7 +373,7 @@ public class DB {
public String pubkey; // null for an unsigned repo
}
- private final int DBVersion = 17;
+ private final int DBVersion = 18;
private static void createAppApk(SQLiteDatabase db) {
db.execSQL(CREATE_TABLE_APP);
@@ -578,7 +588,8 @@ public class DB {
String cols[] = new String[] { "antiFeatures", "requirements",
"id", "name", "summary", "icon", "license", "category",
- "curVersion", "curVercode", "added", "lastUpdated" };
+ "curVersion", "curVercode", "added", "lastUpdated",
+ "compatible" };
c = db.query(TABLE_APP, cols, null, null, null, null, null);
c.moveToFirst();
while (!c.isAfterLast()) {
@@ -601,6 +612,7 @@ public class DB {
app.lastUpdated = (sLastUpdated == null || sLastUpdated
.length() == 0) ? null : mDateFormat
.parse(sLastUpdated);
+ app.compatible = c.getInt(12) == 1;
app.hasUpdates = false;
if (getinstalledinfo && systemApks.containsKey(app.id)) {
@@ -624,7 +636,7 @@ public class DB {
cols = new String[] { "id", "version", "vercode", "sig", "srcname",
"apkName", "apkSource", "minSdkVersion", "added",
- "features" };
+ "features", "compatible" };
c = db.query(TABLE_APK, cols, null, null, null, null,
"vercode desc");
c.moveToFirst();
@@ -642,6 +654,7 @@ public class DB {
apk.added = (sApkAdded == null || sApkAdded.length() == 0) ? null
: mDateFormat.parse(sApkAdded);
apk.features = CommaSeparatedList.make(c.getString(9));
+ apk.compatible = c.getInt(10) == 1;
apps.get(apk.id).apks.add(apk);
c.moveToNext();
}
@@ -821,14 +834,25 @@ public class DB {
if (compatChecker == null)
compatChecker = Apk.CompatibilityChecker.getChecker(mContext);
+ SharedPreferences prefs = PreferenceManager
+ .getDefaultSharedPreferences(mContext);
+ boolean prefCompat = prefs.getBoolean("showIncompatible", false);
+
// See if it's compatible (by which we mean if it has at least one
// compatible apk - if it's not, leave it out)
// Also keep a list of which were compatible, because they're the
- // only ones we'll add.
+ // only ones we'll add, unless the showIncompatible preference is set.
Vector compatibleapks = new Vector();
- for (Apk apk : upapp.apks)
- if (compatChecker.isCompatible(apk))
+ for (Apk apk : upapp.apks) {
+ if (compatChecker.isCompatible(apk)) {
+ apk.compatible = true;
compatibleapks.add(apk);
+ }
+ }
+ if (compatibleapks.size() > 0)
+ upapp.compatible = true;
+ if (prefCompat)
+ compatibleapks = upapp.apks;
if (compatibleapks.size() == 0)
return false;
@@ -902,6 +926,7 @@ public class DB {
values.put("curVercode", upapp.curVercode);
values.put("antiFeatures", CommaSeparatedList.str(upapp.antiFeatures));
values.put("requirements", CommaSeparatedList.str(upapp.requirements));
+ values.put("compatible", upapp.compatible ? 1 : 0);
if (oldapp != null) {
db.update(TABLE_APP, values, "id = ?", new String[] { oldapp.id });
} else {
@@ -934,6 +959,7 @@ public class DB {
values.put("permissions",
CommaSeparatedList.str(upapk.detail_permissions));
values.put("features", CommaSeparatedList.str(upapk.features));
+ values.put("compatible", upapk.compatible ? 1 : 0);
if (oldapk != null) {
db.update(TABLE_APK, values,
"id = ? and vercode = " + Integer.toString(oldapk.vercode),
diff --git a/src/org/fdroid/fdroid/Preferences.java b/src/org/fdroid/fdroid/Preferences.java
index 0639e3ffd..4d2fddf43 100644
--- a/src/org/fdroid/fdroid/Preferences.java
+++ b/src/org/fdroid/fdroid/Preferences.java
@@ -27,62 +27,18 @@ import android.preference.PreferenceActivity;
import android.preference.Preference.OnPreferenceClickListener;
import android.widget.Toast;
-public class Preferences extends PreferenceActivity {
+public class Preferences extends PreferenceActivity implements
+ OnPreferenceClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
-
- Preference r = (Preference) findPreference("reset");
- r.setOnPreferenceClickListener(new OnPreferenceClickListener() {
-
- public boolean onPreferenceClick(Preference preference) {
-
- // TODO: Progress dialog + thread is needed, it can take a
- // while to delete all the icons and cached apks in a long
- // standing install!
-
- // TODO: This is going to cause problems if there is background
- // update in progress at the time!
-
- try {
- DB db = DB.getDB();
- db.reset();
- } finally {
- DB.releaseDB();
- }
- ((FDroidApp) getApplication()).invalidateApps();
-
- File dp = DB.getDataPath();
- deleteAll(dp);
- dp.mkdir();
- DB.getIconsPath().mkdir();
-
- Toast.makeText(getBaseContext(),
- "Local cached data has been cleared", Toast.LENGTH_LONG)
- .show();
- Intent ret = new Intent();
- ret.putExtra("reset", true);
- setResult(RESULT_OK, ret);
- finish();
- return true;
- }
-
- });
-
- r = (Preference) findPreference("ignoreTouchscreen");
- r.setOnPreferenceClickListener(new OnPreferenceClickListener() {
-
- public boolean onPreferenceClick(Preference preference) {
- Intent ret = new Intent();
- ret.putExtra("update", true);
- setResult(RESULT_OK, ret);
- return true;
- }
-
- });
-
+ for (String prefkey : new String[] { "reset", "ignoreTouchscreen",
+ "showIncompatible" }) {
+ Preference pref = findPreference(prefkey);
+ pref.setOnPreferenceClickListener(this);
+ }
}
private void deleteAll(File dir) {
@@ -96,5 +52,45 @@ public class Preferences extends PreferenceActivity {
dir.delete();
}
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ String key = preference.getKey();
+ if (key.equals("ignoreTouchscreen") || key.equals("showIncompatible")) {
+ Intent ret = new Intent();
+ ret.putExtra("update", true);
+ setResult(RESULT_OK, ret);
+ return true;
+ } else if (key.equals("reset")) {
+ // TODO: Progress dialog + thread is needed, it can take a
+ // while to delete all the icons and cached apks in a long
+ // standing install!
+
+ // TODO: This is going to cause problems if there is background
+ // update in progress at the time!
+
+ try {
+ DB db = DB.getDB();
+ db.reset();
+ } finally {
+ DB.releaseDB();
+ }
+ ((FDroidApp) getApplication()).invalidateApps();
+
+ File dp = DB.getDataPath();
+ deleteAll(dp);
+ dp.mkdir();
+ DB.getIconsPath().mkdir();
+
+ Toast.makeText(getBaseContext(),
+ "Local cached data has been cleared", Toast.LENGTH_LONG)
+ .show();
+ Intent ret = new Intent();
+ ret.putExtra("reset", true);
+ setResult(RESULT_OK, ret);
+ finish();
+ return true;
+ }
+ return false;
+ }
}