diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index ac8ee383a..b23692682 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -83,6 +83,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/org/fdroid/fdroid/AppDetails.java b/src/org/fdroid/fdroid/AppDetails.java
index 7759bf80e..7f25bff85 100644
--- a/src/org/fdroid/fdroid/AppDetails.java
+++ b/src/org/fdroid/fdroid/AppDetails.java
@@ -433,14 +433,14 @@ public class AppDetails extends ListActivity {
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());
@@ -598,6 +598,7 @@ public class AppDetails extends ListActivity {
ask_alrt.setMessage(getString(R.string.installDowngrade));
ask_alrt.setPositiveButton(getString(R.string.yes),
new DialogInterface.OnClickListener() {
+ @Override
public void onClick(DialogInterface dialog,
int whichButton) {
install();
@@ -605,6 +606,7 @@ public class AppDetails extends ListActivity {
});
ask_alrt.setNegativeButton(getString(R.string.no),
new DialogInterface.OnClickListener() {
+ @Override
public void onClick(DialogInterface dialog,
int whichButton) {
return;
@@ -807,6 +809,7 @@ public class AppDetails extends ListActivity {
ask_alrt.setMessage(getString(R.string.installIncompatible));
ask_alrt.setPositiveButton(getString(R.string.yes),
new DialogInterface.OnClickListener() {
+ @Override
public void onClick(DialogInterface dialog,
int whichButton) {
downloadHandler = new DownloadHandler(app.curApk,
@@ -816,6 +819,7 @@ public class AppDetails extends ListActivity {
});
ask_alrt.setNegativeButton(getString(R.string.no),
new DialogInterface.OnClickListener() {
+ @Override
public void onClick(DialogInterface dialog,
int whichButton) {
return;
@@ -831,6 +835,7 @@ public class AppDetails extends ListActivity {
builder.setMessage(R.string.SignatureMismatch).setPositiveButton(
getString(R.string.ok),
new DialogInterface.OnClickListener() {
+ @Override
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
@@ -890,6 +895,7 @@ public class AppDetails extends ListActivity {
pd.setCancelable(true);
pd.setCanceledOnTouchOutside(false);
pd.setOnCancelListener(new DialogInterface.OnCancelListener() {
+ @Override
public void onCancel(DialogInterface dialog) {
downloadHandler.cancel();
}
@@ -897,6 +903,7 @@ public class AppDetails extends ListActivity {
pd.setButton(DialogInterface.BUTTON_NEUTRAL,
getString(R.string.cancel),
new DialogInterface.OnClickListener() {
+ @Override
public void onClick(DialogInterface dialog, int which) {
pd.cancel();
}
diff --git a/src/org/fdroid/fdroid/DB.java b/src/org/fdroid/fdroid/DB.java
index 531c22d04..3b950bd5d 100644
--- a/src/org/fdroid/fdroid/DB.java
+++ b/src/org/fdroid/fdroid/DB.java
@@ -19,11 +19,14 @@
package org.fdroid.fdroid;
+import android.annotation.SuppressLint;
import java.io.File;
+import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
+import java.util.Formatter;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -350,6 +353,7 @@ public class DB {
}
private static class BasicChecker extends CompatibilityChecker {
+ @Override
public boolean isCompatible(Apk apk) {
return hasApi(apk.minSdkVersion);
}
@@ -362,6 +366,7 @@ public class DB {
private List cpuAbis;
private boolean ignoreTouchscreen;
+ @SuppressLint("NewApi")
public EclairChecker(Context ctx) {
SharedPreferences prefs = PreferenceManager
@@ -397,6 +402,7 @@ public class DB {
return false;
}
+ @Override
public boolean isCompatible(Apk apk) {
if (!hasApi(apk.minSdkVersion))
return false;
@@ -430,7 +436,8 @@ public class DB {
private static final String CREATE_TABLE_REPO = "create table "
+ TABLE_REPO + " (id integer primary key, address text not null, "
+ "name text, description text, inuse integer not null, "
- + "priority integer not null, pubkey text, lastetag text);";
+ + "priority integer not null, pubkey text, fingerprint text, "
+ + "lastetag text);";
public static class Repo {
public int id;
@@ -440,10 +447,11 @@ public class DB {
public boolean inuse;
public int priority;
public String pubkey; // null for an unsigned repo
+ public String fingerprint; // always null for an unsigned repo
public String lastetag; // last etag we updated from, null forces update
}
- private final int DBVersion = 28;
+ private final int DBVersion = 29;
private static void createAppApk(SQLiteDatabase db) {
db.execSQL(CREATE_TABLE_APP);
@@ -453,6 +461,26 @@ public class DB {
db.execSQL("create index apk_id on " + TABLE_APK + " (id);");
}
+ public static String calcFingerprint(String pubkey) {
+ String ret = null;
+ try {
+ // keytool -list -v gives you the SHA-256 fingerprint
+ MessageDigest digest = MessageDigest.getInstance("SHA-256");
+ digest.update(Hasher.unhex(pubkey));
+ byte[] fingerprint = digest.digest();
+ Formatter formatter = new Formatter(new StringBuilder());
+ for (int i = 1; i < fingerprint.length; i++) {
+ formatter.format("%02X", fingerprint[i]);
+ }
+ ret = formatter.toString();
+ formatter.close();
+ } catch (Exception e) {
+ Log.w("FDroid", "Unable to get certificate fingerprint.\n"
+ + Log.getStackTraceString(e));
+ }
+ return ret;
+ }
+
public void resetTransient(SQLiteDatabase db) {
mContext.getSharedPreferences("FDroid", Context.MODE_PRIVATE).edit()
.putBoolean("triedEmptyUpdate", false).commit();
@@ -487,8 +515,10 @@ public class DB {
mContext.getString(R.string.default_repo_name));
values.put("description",
mContext.getString(R.string.default_repo_description));
- values.put("pubkey",
- mContext.getString(R.string.default_repo_pubkey));
+ String pubkey = mContext.getString(R.string.default_repo_pubkey);
+ String fingerprint = DB.calcFingerprint(pubkey);
+ values.put("pubkey", pubkey);
+ values.put("fingerprint", fingerprint);
values.put("inuse", 1);
values.put("priority", 10);
values.put("lastetag", (String) null);
@@ -501,8 +531,9 @@ public class DB {
mContext.getString(R.string.default_repo_name2));
values.put("description",
mContext.getString(R.string.default_repo_description2));
- values.put("pubkey",
- mContext.getString(R.string.default_repo_pubkey));
+ // default #2 is /archive which has the same key as /repo
+ values.put("pubkey", pubkey);
+ values.put("fingerprint", fingerprint);
values.put("inuse", 0);
values.put("priority", 20);
values.put("lastetag", (String) null);
@@ -564,6 +595,28 @@ public class DB {
mContext.getString(R.string.default_repo_address2) });
}
+ if (oldVersion < 29) {
+ if (!columnExists(db, TABLE_REPO, "fingerprint"))
+ db.execSQL("alter table " + TABLE_REPO + " add column fingerprint text");
+ List oldrepos = new ArrayList();
+ Cursor c = db.query(TABLE_REPO,
+ new String[] { "address", "pubkey" },
+ null, null, null, null, null);
+ c.moveToFirst();
+ while (!c.isAfterLast()) {
+ Repo repo = new Repo();
+ repo.address = c.getString(0);
+ repo.pubkey = c.getString(1);
+ oldrepos.add(repo);
+ c.moveToNext();
+ }
+ c.close();
+ for (Repo repo : oldrepos) {
+ ContentValues values = new ContentValues();
+ values.put("fingerprint", DB.calcFingerprint(repo.pubkey));
+ db.update(TABLE_REPO, values, "address = ?", new String[] { repo.address });
+ }
+ }
}
}
@@ -1002,10 +1055,12 @@ public class DB {
return (instance == null ? null : instance.toString());
}
+ @Override
public String toString() {
return value;
}
+ @Override
public Iterator iterator() {
SimpleStringSplitter splitter = new SimpleStringSplitter(',');
splitter.setString(value);
@@ -1239,7 +1294,8 @@ public class DB {
Cursor c = null;
try {
c = db.query(TABLE_REPO, new String[] { "address", "name",
- "description", "inuse", "priority", "pubkey", "lastetag" },
+ "description", "inuse", "priority", "pubkey", "fingerprint",
+ "lastetag" },
"id = ?", new String[] { Integer.toString(id) }, null, null, null);
if (!c.moveToFirst())
return null;
@@ -1251,7 +1307,8 @@ public class DB {
repo.inuse = (c.getInt(3) == 1);
repo.priority = c.getInt(4);
repo.pubkey = c.getString(5);
- repo.lastetag = c.getString(6);
+ repo.fingerprint = c.getString(6);
+ repo.lastetag = c.getString(7);
return repo;
} finally {
if (c != null)
@@ -1265,7 +1322,8 @@ public class DB {
Cursor c = null;
try {
c = db.query(TABLE_REPO, new String[] { "id", "address", "name",
- "description", "inuse", "priority", "pubkey", "lastetag" },
+ "description", "inuse", "priority", "pubkey", "fingerprint",
+ "lastetag" },
null, null, null, null, "priority");
c.moveToFirst();
while (!c.isAfterLast()) {
@@ -1277,7 +1335,8 @@ public class DB {
repo.inuse = (c.getInt(4) == 1);
repo.priority = c.getInt(5);
repo.pubkey = c.getString(6);
- repo.lastetag = c.getString(7);
+ repo.fingerprint = c.getString(7);
+ repo.lastetag = c.getString(8);
repos.add(repo);
c.moveToNext();
}
@@ -1310,6 +1369,12 @@ public class DB {
values.put("inuse", repo.inuse);
values.put("priority", repo.priority);
values.put("pubkey", repo.pubkey);
+ if (repo.pubkey != null && repo.fingerprint == null) {
+ // we got a new pubkey, so calc the fingerprint
+ values.put("fingerprint", DB.calcFingerprint(repo.pubkey));
+ } else {
+ values.put("fingerprint", repo.fingerprint);
+ }
values.put("lastetag", (String) null);
db.update(TABLE_REPO, values, "address = ?",
new String[] { repo.address });
@@ -1323,7 +1388,8 @@ public class DB {
}
public void addRepo(String address, String name, String description,
- int priority, String pubkey, boolean inuse) {
+ int priority, String pubkey, String fingerprint, boolean inuse)
+ throws SecurityException {
ContentValues values = new ContentValues();
values.put("address", address);
values.put("name", name);
@@ -1331,6 +1397,17 @@ public class DB {
values.put("inuse", inuse ? 1 : 0);
values.put("priority", priority);
values.put("pubkey", pubkey);
+ String calcedFingerprint = DB.calcFingerprint(pubkey);
+ if (fingerprint == null) {
+ fingerprint = calcedFingerprint;
+ } else {
+ fingerprint = fingerprint.toUpperCase();
+ if (!fingerprint.equals(calcedFingerprint)) {
+ throw new SecurityException("Given fingerprint does not match calculated one! ("
+ + fingerprint + " != " + calcedFingerprint);
+ }
+ }
+ values.put("fingerprint", fingerprint);
values.put("lastetag", (String) null);
db.insert(TABLE_REPO, null, values);
}
diff --git a/src/org/fdroid/fdroid/Downloader.java b/src/org/fdroid/fdroid/Downloader.java
index ca7a5a987..e0cb96ad6 100644
--- a/src/org/fdroid/fdroid/Downloader.java
+++ b/src/org/fdroid/fdroid/Downloader.java
@@ -95,6 +95,7 @@ public class Downloader extends Thread {
return curapk;
}
+ @Override
public void run() {
InputStream input = null;
diff --git a/src/org/fdroid/fdroid/FDroid.java b/src/org/fdroid/fdroid/FDroid.java
index 5068adada..63353ffec 100644
--- a/src/org/fdroid/fdroid/FDroid.java
+++ b/src/org/fdroid/fdroid/FDroid.java
@@ -23,6 +23,7 @@ import android.content.*;
import android.content.res.Configuration;
import android.support.v4.view.MenuItemCompat;
+import android.annotation.TargetApi;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.app.NotificationManager;
@@ -123,6 +124,7 @@ public class FDroid extends FragmentActivity {
manager.repopulateLists();
}
+ @Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
getTabManager().onConfigurationChanged(newConfig);
@@ -205,6 +207,7 @@ public class FDroid extends FragmentActivity {
alrt.setButton(AlertDialog.BUTTON_NEUTRAL,
getString(R.string.about_website),
new DialogInterface.OnClickListener() {
+ @Override
public void onClick(DialogInterface dialog,
int whichButton) {
Uri uri = Uri.parse("https://f-droid.org");
@@ -213,6 +216,7 @@ public class FDroid extends FragmentActivity {
});
alrt.setButton(AlertDialog.BUTTON_NEGATIVE, getString(R.string.ok),
new DialogInterface.OnClickListener() {
+ @Override
public void onClick(DialogInterface dialog,
int whichButton) {
}
@@ -223,6 +227,7 @@ public class FDroid extends FragmentActivity {
return super.onOptionsItemSelected(item);
}
+ @TargetApi(5)
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
@@ -237,6 +242,7 @@ public class FDroid extends FragmentActivity {
ask_alrt.setMessage(getString(R.string.repo_alrt));
ask_alrt.setPositiveButton(getString(R.string.yes),
new DialogInterface.OnClickListener() {
+ @Override
public void onClick(DialogInterface dialog,
int whichButton) {
updateRepos();
@@ -244,6 +250,7 @@ public class FDroid extends FragmentActivity {
});
ask_alrt.setNegativeButton(getString(R.string.no),
new DialogInterface.OnClickListener() {
+ @Override
public void onClick(DialogInterface dialog,
int whichButton) {
return;
@@ -264,7 +271,7 @@ public class FDroid extends FragmentActivity {
} else if ((resultCode & PreferencesActivity.RESULT_REFILTER) != 0) {
((FDroidApp) getApplication()).filterApps();
}
-
+
if ((resultCode & PreferencesActivity.RESULT_RESTART) != 0) {
((FDroidApp) getApplication()).reloadTheme();
final Intent intent = getIntent();
@@ -284,6 +291,7 @@ public class FDroid extends FragmentActivity {
viewPageAdapter = new AppListFragmentPageAdapter(this);
viewPager.setAdapter(viewPageAdapter);
viewPager.setOnPageChangeListener( new ViewPager.SimpleOnPageChangeListener() {
+ @Override
public void onPageSelected(int position) {
getTabManager().selectTab(position);
}
diff --git a/src/org/fdroid/fdroid/FDroidApp.java b/src/org/fdroid/fdroid/FDroidApp.java
index 9d401f92c..b7dea120d 100644
--- a/src/org/fdroid/fdroid/FDroidApp.java
+++ b/src/org/fdroid/fdroid/FDroidApp.java
@@ -117,6 +117,7 @@ public class FDroidApp extends Application {
.discCache(new UnlimitedDiscCache(
new File(StorageUtils.getCacheDirectory(ctx), "icons"),
new FileNameGenerator() {
+ @Override
public String generate(String imageUri) {
return imageUri.substring(
imageUri.lastIndexOf('/') + 1);
diff --git a/src/org/fdroid/fdroid/ManageRepo.java b/src/org/fdroid/fdroid/ManageRepo.java
index fae936183..60ad9c656 100644
--- a/src/org/fdroid/fdroid/ManageRepo.java
+++ b/src/org/fdroid/fdroid/ManageRepo.java
@@ -19,10 +19,8 @@
package org.fdroid.fdroid;
-import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Date;
-import java.util.Formatter;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
@@ -157,23 +155,8 @@ public class ManageRepo extends ListActivity {
} else {
server_line.put("inuse", R.drawable.btn_check_off);
}
- if (repo.pubkey != null) {
- try {
- MessageDigest digest = MessageDigest.getInstance("SHA-1");
- digest.update(Hasher.unhex(repo.pubkey));
- byte[] fingerprint = digest.digest();
- Formatter formatter = new Formatter(new StringBuilder());
- formatter.format("%02X", fingerprint[0]);
- for (int i = 1; i < fingerprint.length; i++) {
- formatter.format(i % 5 == 0 ? " %02X" : ":%02X",
- fingerprint[i]);
- }
- server_line.put("fingerprint", formatter.toString());
- formatter.close();
- } catch (Exception e) {
- Log.w("FDroid", "Unable to get certificate fingerprint.\n"
- + Log.getStackTraceString(e));
- }
+ if (repo.fingerprint != null) {
+ server_line.put("fingerprint", repo.fingerprint);
}
result.add(server_line);
}
@@ -202,6 +185,7 @@ public class ManageRepo extends ListActivity {
redraw();
}
+ @Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
@@ -215,10 +199,10 @@ public class ManageRepo extends ListActivity {
return true;
}
- protected void addRepo(String repoUri) {
+ protected void addRepo(String repoUri, String fingerprint) {
try {
DB db = DB.getDB();
- db.addRepo(repoUri, null, null, 10, null, true);
+ db.addRepo(repoUri, null, null, 10, null, fingerprint, true);
} finally {
DB.releaseDB();
}
@@ -268,7 +252,8 @@ public class ManageRepo extends ListActivity {
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
- addRepo(uriEditText.getText().toString());
+ addRepo(uriEditText.getText().toString(),
+ fingerprintEditText.getText().toString());
changed = true;
redraw();
}
@@ -332,6 +317,7 @@ public class ManageRepo extends ListActivity {
builder.setIcon(android.R.drawable.ic_menu_close_clear_cancel);
builder.setMultiChoiceItems(b, null,
new DialogInterface.OnMultiChoiceClickListener() {
+ @Override
public void onClick(DialogInterface dialog,
int whichButton, boolean isChecked) {
if (isChecked) {
@@ -343,6 +329,7 @@ public class ManageRepo extends ListActivity {
});
builder.setPositiveButton(getString(R.string.ok),
new DialogInterface.OnClickListener() {
+ @Override
public void onClick(DialogInterface dialog,
int whichButton) {
try {
@@ -357,6 +344,7 @@ public class ManageRepo extends ListActivity {
});
builder.setNegativeButton(getString(R.string.cancel),
new DialogInterface.OnClickListener() {
+ @Override
public void onClick(DialogInterface dialog,
int whichButton) {
return;
diff --git a/src/org/fdroid/fdroid/UpdateService.java b/src/org/fdroid/fdroid/UpdateService.java
index a7c07787c..5323d0e60 100644
--- a/src/org/fdroid/fdroid/UpdateService.java
+++ b/src/org/fdroid/fdroid/UpdateService.java
@@ -115,6 +115,7 @@ public class UpdateService extends IntentService implements ProgressListener {
return count;
}
+ @Override
protected void onHandleIntent(Intent intent) {
receiver = intent.getParcelableExtra("receiver");
diff --git a/src/org/fdroid/fdroid/views/AppListFragmentPageAdapter.java b/src/org/fdroid/fdroid/views/AppListFragmentPageAdapter.java
index 575839865..cd8946490 100644
--- a/src/org/fdroid/fdroid/views/AppListFragmentPageAdapter.java
+++ b/src/org/fdroid/fdroid/views/AppListFragmentPageAdapter.java
@@ -38,6 +38,7 @@ public class AppListFragmentPageAdapter extends FragmentPagerAdapter {
return 3;
}
+ @Override
public String getPageTitle(int i) {
switch(i) {
case 0:
diff --git a/src/org/fdroid/fdroid/views/fragments/AppListFragment.java b/src/org/fdroid/fdroid/views/fragments/AppListFragment.java
index b8102f320..5177c4ac9 100644
--- a/src/org/fdroid/fdroid/views/fragments/AppListFragment.java
+++ b/src/org/fdroid/fdroid/views/fragments/AppListFragment.java
@@ -32,6 +32,7 @@ abstract class AppListFragment extends Fragment implements AdapterView.OnItemCli
Preferences.get().unregisterCompactLayoutChangeListener(this);
}
+ @Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
diff --git a/src/org/fdroid/fdroid/views/fragments/AvailableAppsFragment.java b/src/org/fdroid/fdroid/views/fragments/AvailableAppsFragment.java
index f7cd4263d..26f34e363 100644
--- a/src/org/fdroid/fdroid/views/fragments/AvailableAppsFragment.java
+++ b/src/org/fdroid/fdroid/views/fragments/AvailableAppsFragment.java
@@ -12,6 +12,7 @@ import org.fdroid.fdroid.views.AppListView;
public class AvailableAppsFragment extends AppListFragment implements AdapterView.OnItemSelectedListener {
+ @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
AppListView view = new AppListView(getActivity());
view.setOrientation(LinearLayout.VERTICAL);
@@ -40,6 +41,7 @@ public class AvailableAppsFragment extends AppListFragment implements AdapterVie
return view;
}
+ @Override
public void onItemSelected(AdapterView> parent, View view, int pos,
long id) {
String category = parent.getItemAtPosition(pos).toString();
diff --git a/src/org/fdroid/fdroid/views/fragments/CanUpdateAppsFragment.java b/src/org/fdroid/fdroid/views/fragments/CanUpdateAppsFragment.java
index a233f61e7..a390f365c 100644
--- a/src/org/fdroid/fdroid/views/fragments/CanUpdateAppsFragment.java
+++ b/src/org/fdroid/fdroid/views/fragments/CanUpdateAppsFragment.java
@@ -9,6 +9,7 @@ import org.fdroid.fdroid.views.AppListAdapter;
public class CanUpdateAppsFragment extends AppListFragment {
+ @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return createPlainAppList();
}
diff --git a/src/org/fdroid/fdroid/views/fragments/InstalledAppsFragment.java b/src/org/fdroid/fdroid/views/fragments/InstalledAppsFragment.java
index 8eb4bf04c..db3a93cfc 100644
--- a/src/org/fdroid/fdroid/views/fragments/InstalledAppsFragment.java
+++ b/src/org/fdroid/fdroid/views/fragments/InstalledAppsFragment.java
@@ -9,6 +9,7 @@ import org.fdroid.fdroid.views.AppListAdapter;
public class InstalledAppsFragment extends AppListFragment {
+ @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return createPlainAppList();
}