Merge branch 'merge-requests/43'

This commit is contained in:
Ciaran Gultnieks 2013-11-24 07:42:41 +00:00
commit 46bcfcf015
13 changed files with 137 additions and 36 deletions

View File

@ -83,6 +83,18 @@
<data android:path="/archive" />
<data android:pathPattern="/repo/*" />
<data android:pathPattern="/archive/*" />
<data android:pathPattern="/.*/repo" />
<data android:pathPattern="/.*/archive" />
<data android:pathPattern="/.*/repo/*" />
<data android:pathPattern="/.*/archive/*" />
<data android:pathPattern="/.*/.*/repo" />
<data android:pathPattern="/.*/.*/archive" />
<data android:pathPattern="/.*/.*/repo/*" />
<data android:pathPattern="/.*/.*/archive/*" />
<data android:pathPattern="/.*/.*/.*/repo" />
<data android:pathPattern="/.*/.*/.*/archive" />
<data android:pathPattern="/.*/.*/.*/repo/*" />
<data android:pathPattern="/.*/.*/.*/archive/*" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />

View File

@ -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();
}

View File

@ -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<String> 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<Repo> oldrepos = new ArrayList<Repo>();
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<String> 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);
}

View File

@ -95,6 +95,7 @@ public class Downloader extends Thread {
return curapk;
}
@Override
public void run() {
InputStream input = null;

View File

@ -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);
}

View File

@ -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);

View File

@ -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;

View File

@ -115,6 +115,7 @@ public class UpdateService extends IntentService implements ProgressListener {
return count;
}
@Override
protected void onHandleIntent(Intent intent) {
receiver = intent.getParcelableExtra("receiver");

View File

@ -38,6 +38,7 @@ public class AppListFragmentPageAdapter extends FragmentPagerAdapter {
return 3;
}
@Override
public String getPageTitle(int i) {
switch(i) {
case 0:

View File

@ -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 {

View File

@ -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();

View File

@ -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();
}

View File

@ -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();
}