Merge branch 'prep-for-new-index' into 'master'

prep for new index-v1

Closes #819

See merge request !439
This commit is contained in:
Peter Serwylo 2017-02-28 02:35:15 +00:00
commit 6ac62d791a
14 changed files with 257 additions and 174 deletions

View File

@ -10,10 +10,16 @@ before_script:
test: test:
script: script:
- cd app
- ./tools/langs-list-check.py
- ./tools/check-string-format.py
- cd ..
- ./gradlew assemble -PdisablePreDex - ./gradlew assemble -PdisablePreDex
# always report on lint errors to the build log # always report on lint errors to the build log
- sed -i -e 's,textReport .*,textReport true,' app/build.gradle - sed -i -e 's,textReport .*,textReport true,' app/build.gradle
- ./gradlew lint -PdisablePreDex - ./gradlew lint -PdisablePreDex
- ./gradlew pmd -PdisablePreDex
- ./gradlew checkstyle -PdisablePreDex
- ./gradlew test -PdisablePreDex || { - ./gradlew test -PdisablePreDex || {
for log in app/build/reports/*ests/*/*ml; do for log in app/build/reports/*ests/*/*ml; do
echo "read $log here:"; echo "read $log here:";
@ -64,24 +70,6 @@ connected24:
done done
- exit $EXITVALUE - exit $EXITVALUE
pmd:
script:
- ./gradlew pmd -PdisablePreDex
checkstyle:
script:
- ./gradlew checkstyle -PdisablePreDex
tools:
before_script:
- echo "ignored, no gradle needed"
script:
- cd app
- ./tools/langs-list-check.py
- ./tools/check-string-format.py
after_script:
- echo "ignored, no gradle needed"
after_script: after_script:
# this file changes every time but should not be cached # this file changes every time but should not be cached
- rm -f $GRADLE_USER_HOME/caches/modules-2/modules-2.lock - rm -f $GRADLE_USER_HOME/caches/modules-2/modules-2.lock

View File

@ -1163,23 +1163,23 @@ public class AppDetails extends AppCompatActivity {
App app = appDetails.getApp(); App app = appDetails.getApp();
switch (v.getId()) { switch (v.getId()) {
case R.id.website: case R.id.website:
url = app.webURL; url = app.webSite;
break; break;
case R.id.email: case R.id.email:
final String subject = Uri.encode(getString(R.string.app_details_subject, app.name)); final String subject = Uri.encode(getString(R.string.app_details_subject, app.name));
url = "mailto:" + app.email + "?subject=" + subject; url = "mailto:" + app.authorEmail + "?subject=" + subject;
break; break;
case R.id.source: case R.id.source:
url = app.sourceURL; url = app.sourceCode;
break; break;
case R.id.issues: case R.id.issues:
url = app.trackerURL; url = app.issueTracker;
break; break;
case R.id.changelog: case R.id.changelog:
url = app.changelogURL; url = app.changelog;
break; break;
case R.id.donate: case R.id.donate:
url = app.donateURL; url = app.donate;
break; break;
case R.id.bitcoin: case R.id.bitcoin:
url = app.getBitcoinUri(); url = app.getBitcoinUri();
@ -1267,7 +1267,7 @@ public class AppDetails extends AppCompatActivity {
// Website button // Website button
View tv = view.findViewById(R.id.website); View tv = view.findViewById(R.id.website);
if (!TextUtils.isEmpty(app.webURL)) { if (!TextUtils.isEmpty(app.webSite)) {
tv.setOnClickListener(onClickListener); tv.setOnClickListener(onClickListener);
} else { } else {
tv.setVisibility(View.GONE); tv.setVisibility(View.GONE);
@ -1275,7 +1275,7 @@ public class AppDetails extends AppCompatActivity {
// Email button // Email button
tv = view.findViewById(R.id.email); tv = view.findViewById(R.id.email);
if (!TextUtils.isEmpty(app.email)) { if (!TextUtils.isEmpty(app.authorEmail)) {
tv.setOnClickListener(onClickListener); tv.setOnClickListener(onClickListener);
} else { } else {
tv.setVisibility(View.GONE); tv.setVisibility(View.GONE);
@ -1283,7 +1283,7 @@ public class AppDetails extends AppCompatActivity {
// Source button // Source button
tv = view.findViewById(R.id.source); tv = view.findViewById(R.id.source);
if (!TextUtils.isEmpty(app.sourceURL)) { if (!TextUtils.isEmpty(app.sourceCode)) {
tv.setOnClickListener(onClickListener); tv.setOnClickListener(onClickListener);
} else { } else {
tv.setVisibility(View.GONE); tv.setVisibility(View.GONE);
@ -1291,7 +1291,7 @@ public class AppDetails extends AppCompatActivity {
// Issues button // Issues button
tv = view.findViewById(R.id.issues); tv = view.findViewById(R.id.issues);
if (!TextUtils.isEmpty(app.trackerURL)) { if (!TextUtils.isEmpty(app.issueTracker)) {
tv.setOnClickListener(onClickListener); tv.setOnClickListener(onClickListener);
} else { } else {
tv.setVisibility(View.GONE); tv.setVisibility(View.GONE);
@ -1299,7 +1299,7 @@ public class AppDetails extends AppCompatActivity {
// Changelog button // Changelog button
tv = view.findViewById(R.id.changelog); tv = view.findViewById(R.id.changelog);
if (!TextUtils.isEmpty(app.changelogURL)) { if (!TextUtils.isEmpty(app.changelog)) {
tv.setOnClickListener(onClickListener); tv.setOnClickListener(onClickListener);
} else { } else {
tv.setVisibility(View.GONE); tv.setVisibility(View.GONE);
@ -1307,7 +1307,7 @@ public class AppDetails extends AppCompatActivity {
// Donate button // Donate button
tv = view.findViewById(R.id.donate); tv = view.findViewById(R.id.donate);
if (!TextUtils.isEmpty(app.donateURL)) { if (!TextUtils.isEmpty(app.donate)) {
tv.setOnClickListener(onClickListener); tv.setOnClickListener(onClickListener);
} else { } else {
tv.setVisibility(View.GONE); tv.setVisibility(View.GONE);
@ -1315,7 +1315,7 @@ public class AppDetails extends AppCompatActivity {
// Bitcoin // Bitcoin
tv = view.findViewById(R.id.bitcoin); tv = view.findViewById(R.id.bitcoin);
if (!TextUtils.isEmpty(app.bitcoinAddr)) { if (!TextUtils.isEmpty(app.bitcoin)) {
tv.setOnClickListener(onClickListener); tv.setOnClickListener(onClickListener);
} else { } else {
tv.setVisibility(View.GONE); tv.setVisibility(View.GONE);
@ -1323,7 +1323,7 @@ public class AppDetails extends AppCompatActivity {
// Litecoin // Litecoin
tv = view.findViewById(R.id.litecoin); tv = view.findViewById(R.id.litecoin);
if (!TextUtils.isEmpty(app.litecoinAddr)) { if (!TextUtils.isEmpty(app.litecoin)) {
tv.setOnClickListener(onClickListener); tv.setOnClickListener(onClickListener);
} else { } else {
tv.setVisibility(View.GONE); tv.setVisibility(View.GONE);
@ -1642,8 +1642,8 @@ public class AppDetails extends AppCompatActivity {
btMain.setEnabled(true); btMain.setEnabled(true);
} }
TextView author = (TextView) view.findViewById(R.id.author); TextView author = (TextView) view.findViewById(R.id.author);
if (!TextUtils.isEmpty(app.author)) { if (!TextUtils.isEmpty(app.authorName)) {
author.setText(getString(R.string.by_author) + " " + app.author); author.setText(getString(R.string.by_author) + " " + app.authorName);
author.setVisibility(View.VISIBLE); author.setVisibility(View.VISIBLE);
} }
TextView currentVersion = (TextView) view.findViewById(R.id.current_version); TextView currentVersion = (TextView) view.findViewById(R.id.current_version);

View File

@ -58,6 +58,7 @@ import java.security.CodeSigner;
import java.security.cert.Certificate; import java.security.cert.Certificate;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.jar.JarEntry; import java.util.jar.JarEntry;
@ -195,9 +196,11 @@ public class RepoUpdater {
private RepoXMLHandler.IndexReceiver createIndexReceiver() { private RepoXMLHandler.IndexReceiver createIndexReceiver() {
return new RepoXMLHandler.IndexReceiver() { return new RepoXMLHandler.IndexReceiver() {
@Override @Override
public void receiveRepo(String name, String description, String signingCert, int maxAge, int version, long timestamp) { public void receiveRepo(String name, String description, String signingCert, int maxAge,
int version, long timestamp, String icon, String[] mirrors) {
signingCertFromIndexXml = signingCert; signingCertFromIndexXml = signingCert;
repoDetailsToSave = prepareRepoDetailsForSaving(name, description, maxAge, version, timestamp); repoDetailsToSave = prepareRepoDetailsForSaving(name, description, maxAge, version,
timestamp, icon, mirrors);
} }
@Override @Override
@ -294,7 +297,9 @@ public class RepoUpdater {
* Update tracking data for the repo represented by this instance (index version, etag, * Update tracking data for the repo represented by this instance (index version, etag,
* description, human-readable name, etc. * description, human-readable name, etc.
*/ */
private ContentValues prepareRepoDetailsForSaving(String name, String description, int maxAge, int version, long timestamp) { private ContentValues prepareRepoDetailsForSaving(String name, String description, int maxAge,
int version, long timestamp, String icon,
String[] mirrors) {
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
values.put(RepoTable.Cols.LAST_UPDATED, Utils.formatTime(new Date(), "")); values.put(RepoTable.Cols.LAST_UPDATED, Utils.formatTime(new Date(), ""));
@ -329,6 +334,14 @@ public class RepoUpdater {
// timestamp. // timestamp.
values.put(RepoTable.Cols.TIMESTAMP, timestamp); values.put(RepoTable.Cols.TIMESTAMP, timestamp);
if (icon != null && !icon.equals(repo.icon)) {
values.put(RepoTable.Cols.ICON, icon);
}
if (mirrors != null && mirrors.length > 0 && !Arrays.equals(mirrors, repo.mirrors)) {
values.put(RepoTable.Cols.MIRRORS, Utils.serializeCommaSeparatedString(mirrors));
}
return values; return values;
} }

View File

@ -59,6 +59,8 @@ public class RepoXMLHandler extends DefaultHandler {
private long repoTimestamp; private long repoTimestamp;
private String repoDescription; private String repoDescription;
private String repoName; private String repoName;
private String repoIcon;
private final ArrayList<String> repoMirrors = new ArrayList<>();
/** /**
* Set of requested permissions per package/APK * Set of requested permissions per package/APK
@ -73,7 +75,8 @@ public class RepoXMLHandler extends DefaultHandler {
private final StringBuilder curchars = new StringBuilder(); private final StringBuilder curchars = new StringBuilder();
public interface IndexReceiver { public interface IndexReceiver {
void receiveRepo(String name, String description, String signingCert, int maxage, int version, long timestamp); void receiveRepo(String name, String description, String signingCert, int maxage, int version,
long timestamp, String icon, String[] mirrors);
void receiveApp(App app, List<Apk> packages); void receiveApp(App app, List<Apk> packages);
@ -205,34 +208,34 @@ public class RepoXMLHandler extends DefaultHandler {
curapp.license = str; curapp.license = str;
break; break;
case "author": case "author":
curapp.author = str; curapp.authorName = str;
break; break;
case "email": case "email":
curapp.email = str; curapp.authorEmail = str;
break; break;
case "source": case "source":
curapp.sourceURL = str; curapp.sourceCode = str;
break; break;
case "changelog": case "changelog":
curapp.changelogURL = str; curapp.changelog = str;
break; break;
case "donate": case "donate":
curapp.donateURL = str; curapp.donate = str;
break; break;
case "bitcoin": case "bitcoin":
curapp.bitcoinAddr = str; curapp.bitcoin = str;
break; break;
case "litecoin": case "litecoin":
curapp.litecoinAddr = str; curapp.litecoin = str;
break; break;
case "flattr": case "flattr":
curapp.flattrID = str; curapp.flattrID = str;
break; break;
case "web": case "web":
curapp.webURL = str; curapp.webSite = str;
break; break;
case "tracker": case "tracker":
curapp.trackerURL = str; curapp.issueTracker = str;
break; break;
case "added": case "added":
curapp.added = Utils.parseDate(str, null); curapp.added = Utils.parseDate(str, null);
@ -258,6 +261,8 @@ public class RepoXMLHandler extends DefaultHandler {
} }
} else if ("description".equals(localName)) { } else if ("description".equals(localName)) {
repoDescription = cleanWhiteSpace(str); repoDescription = cleanWhiteSpace(str);
} else if ("mirror".equals(localName)) {
repoMirrors.add(str);
} }
} }
@ -312,7 +317,8 @@ public class RepoXMLHandler extends DefaultHandler {
} }
private void onRepoParsed() { private void onRepoParsed() {
receiver.receiveRepo(repoName, repoDescription, repoSigningCert, repoMaxAge, repoVersion, repoTimestamp); receiver.receiveRepo(repoName, repoDescription, repoSigningCert, repoMaxAge, repoVersion,
repoTimestamp, repoIcon, repoMirrors.toArray(new String[repoMirrors.size()]));
} }
private void onRepoPushRequestParsed(RepoPushRequest repoPushRequest) { private void onRepoPushRequestParsed(RepoPushRequest repoPushRequest) {
@ -331,6 +337,7 @@ public class RepoXMLHandler extends DefaultHandler {
repoName = cleanWhiteSpace(attributes.getValue("", "name")); repoName = cleanWhiteSpace(attributes.getValue("", "name"));
repoDescription = cleanWhiteSpace(attributes.getValue("", "description")); repoDescription = cleanWhiteSpace(attributes.getValue("", "description"));
repoTimestamp = parseLong(attributes.getValue("", "timestamp"), 0); repoTimestamp = parseLong(attributes.getValue("", "timestamp"), 0);
repoIcon = attributes.getValue("", "icon");
} else if (RepoPushRequest.INSTALL.equals(localName) } else if (RepoPushRequest.INSTALL.equals(localName)
|| RepoPushRequest.UNINSTALL.equals(localName)) { || RepoPushRequest.UNINSTALL.equals(localName)) {
if (repo.pushRequests == Repo.PUSH_REQUEST_ACCEPT_ALWAYS) { if (repo.pushRequests == Repo.PUSH_REQUEST_ACCEPT_ALWAYS) {

View File

@ -2,6 +2,7 @@ package org.fdroid.fdroid.data;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.content.ContentValues; import android.content.ContentValues;
import android.content.pm.PackageInfo;
import android.database.Cursor; import android.database.Cursor;
import android.os.Build; import android.os.Build;
import android.os.Parcel; import android.os.Parcel;
@ -73,6 +74,8 @@ public class Apk extends ValueObject implements Comparable<Apk>, Parcelable {
public String repoAddress; public String repoAddress;
public String[] incompatibleReasons; public String[] incompatibleReasons;
public String[] antiFeatures;
/** /**
* The numeric primary key of the Metadata table, which is used to join apks. * The numeric primary key of the Metadata table, which is used to join apks.
*/ */
@ -203,6 +206,9 @@ public class Apk extends ValueObject implements Comparable<Apk>, Parcelable {
case Cols.Repo.ADDRESS: case Cols.Repo.ADDRESS:
repoAddress = cursor.getString(i); repoAddress = cursor.getString(i);
break; break;
case Cols.ANTI_FEATURES:
antiFeatures = Utils.parseCommaSeparatedString(cursor.getString(i));
break;
} }
} }
} }
@ -303,6 +309,7 @@ public class Apk extends ValueObject implements Comparable<Apk>, Parcelable {
values.put(Cols.FEATURES, Utils.serializeCommaSeparatedString(features)); values.put(Cols.FEATURES, Utils.serializeCommaSeparatedString(features));
values.put(Cols.NATIVE_CODE, Utils.serializeCommaSeparatedString(nativecode)); values.put(Cols.NATIVE_CODE, Utils.serializeCommaSeparatedString(nativecode));
values.put(Cols.INCOMPATIBLE_REASONS, Utils.serializeCommaSeparatedString(incompatibleReasons)); values.put(Cols.INCOMPATIBLE_REASONS, Utils.serializeCommaSeparatedString(incompatibleReasons));
values.put(Cols.ANTI_FEATURES, Utils.serializeCommaSeparatedString(antiFeatures));
values.put(Cols.IS_COMPATIBLE, compatible ? 1 : 0); values.put(Cols.IS_COMPATIBLE, compatible ? 1 : 0);
return values; return values;
} }
@ -349,6 +356,7 @@ public class Apk extends ValueObject implements Comparable<Apk>, Parcelable {
dest.writeInt(this.repoVersion); dest.writeInt(this.repoVersion);
dest.writeString(this.repoAddress); dest.writeString(this.repoAddress);
dest.writeStringArray(this.incompatibleReasons); dest.writeStringArray(this.incompatibleReasons);
dest.writeStringArray(this.antiFeatures);
dest.writeLong(this.appId); dest.writeLong(this.appId);
} }
@ -380,6 +388,7 @@ public class Apk extends ValueObject implements Comparable<Apk>, Parcelable {
this.repoVersion = in.readInt(); this.repoVersion = in.readInt();
this.repoAddress = in.readString(); this.repoAddress = in.readString();
this.incompatibleReasons = in.createStringArray(); this.incompatibleReasons = in.createStringArray();
this.antiFeatures = in.createStringArray();
this.appId = in.readLong(); this.appId = in.readLong();
} }

View File

@ -65,22 +65,22 @@ public class App extends ValueObject implements Comparable<App>, Parcelable {
public String license = "Unknown"; public String license = "Unknown";
public String author; public String authorName;
public String email; public String authorEmail;
public String webURL; public String webSite;
public String trackerURL; public String issueTracker;
public String sourceURL; public String sourceCode;
public String changelogURL; public String changelog;
public String donateURL; public String donate;
public String bitcoinAddr; public String bitcoin;
public String litecoinAddr; public String litecoin;
public String flattrID; public String flattrID;
@ -113,8 +113,9 @@ public class App extends ValueObject implements Comparable<App>, Parcelable {
public String[] antiFeatures; public String[] antiFeatures;
/** /**
* List of special requirements (such as root privileges) or null if there aren't any. * Requires root access (only ever used for root)
*/ */
@Deprecated
public String[] requirements; public String[] requirements;
private AppPrefs prefs; private AppPrefs prefs;
@ -185,32 +186,32 @@ public class App extends ValueObject implements Comparable<App>, Parcelable {
case Cols.LICENSE: case Cols.LICENSE:
license = cursor.getString(i); license = cursor.getString(i);
break; break;
case Cols.AUTHOR: case Cols.AUTHOR_NAME:
author = cursor.getString(i); authorName = cursor.getString(i);
break; break;
case Cols.EMAIL: case Cols.AUTHOR_EMAIL:
email = cursor.getString(i); authorEmail = cursor.getString(i);
break; break;
case Cols.WEB_URL: case Cols.WEBSITE:
webURL = cursor.getString(i); webSite = cursor.getString(i);
break; break;
case Cols.TRACKER_URL: case Cols.ISSUE_TRACKER:
trackerURL = cursor.getString(i); issueTracker = cursor.getString(i);
break; break;
case Cols.SOURCE_URL: case Cols.SOURCE_CODE:
sourceURL = cursor.getString(i); sourceCode = cursor.getString(i);
break; break;
case Cols.CHANGELOG_URL: case Cols.CHANGELOG:
changelogURL = cursor.getString(i); changelog = cursor.getString(i);
break; break;
case Cols.DONATE_URL: case Cols.DONATE:
donateURL = cursor.getString(i); donate = cursor.getString(i);
break; break;
case Cols.BITCOIN_ADDR: case Cols.BITCOIN:
bitcoinAddr = cursor.getString(i); bitcoin = cursor.getString(i);
break; break;
case Cols.LITECOIN_ADDR: case Cols.LITECOIN:
litecoinAddr = cursor.getString(i); litecoin = cursor.getString(i);
break; break;
case Cols.FLATTR_ID: case Cols.FLATTR_ID:
flattrID = cursor.getString(i); flattrID = cursor.getString(i);
@ -489,15 +490,15 @@ public class App extends ValueObject implements Comparable<App>, Parcelable {
values.put(Cols.ICON_URL_LARGE, iconUrlLarge); values.put(Cols.ICON_URL_LARGE, iconUrlLarge);
values.put(Cols.DESCRIPTION, description); values.put(Cols.DESCRIPTION, description);
values.put(Cols.LICENSE, license); values.put(Cols.LICENSE, license);
values.put(Cols.AUTHOR, author); values.put(Cols.AUTHOR_NAME, authorName);
values.put(Cols.EMAIL, email); values.put(Cols.AUTHOR_EMAIL, authorEmail);
values.put(Cols.WEB_URL, webURL); values.put(Cols.WEBSITE, webSite);
values.put(Cols.TRACKER_URL, trackerURL); values.put(Cols.ISSUE_TRACKER, issueTracker);
values.put(Cols.SOURCE_URL, sourceURL); values.put(Cols.SOURCE_CODE, sourceCode);
values.put(Cols.CHANGELOG_URL, changelogURL); values.put(Cols.CHANGELOG, changelog);
values.put(Cols.DONATE_URL, donateURL); values.put(Cols.DONATE, donate);
values.put(Cols.BITCOIN_ADDR, bitcoinAddr); values.put(Cols.BITCOIN, bitcoin);
values.put(Cols.LITECOIN_ADDR, litecoinAddr); values.put(Cols.LITECOIN, litecoin);
values.put(Cols.FLATTR_ID, flattrID); values.put(Cols.FLATTR_ID, flattrID);
values.put(Cols.ADDED, Utils.formatDate(added, "")); values.put(Cols.ADDED, Utils.formatDate(added, ""));
values.put(Cols.LAST_UPDATED, Utils.formatDate(lastUpdated, "")); values.put(Cols.LAST_UPDATED, Utils.formatDate(lastUpdated, ""));
@ -555,12 +556,12 @@ public class App extends ValueObject implements Comparable<App>, Parcelable {
@Nullable @Nullable
public String getBitcoinUri() { public String getBitcoinUri() {
return TextUtils.isEmpty(bitcoinAddr) ? null : "bitcoin:" + bitcoinAddr; return TextUtils.isEmpty(bitcoin) ? null : "bitcoin:" + bitcoin;
} }
@Nullable @Nullable
public String getLitecoinUri() { public String getLitecoinUri() {
return TextUtils.isEmpty(litecoinAddr) ? null : "litecoin:" + litecoinAddr; return TextUtils.isEmpty(bitcoin) ? null : "litecoin:" + bitcoin;
} }
@Nullable @Nullable
@ -631,15 +632,15 @@ public class App extends ValueObject implements Comparable<App>, Parcelable {
dest.writeString(this.icon); dest.writeString(this.icon);
dest.writeString(this.description); dest.writeString(this.description);
dest.writeString(this.license); dest.writeString(this.license);
dest.writeString(this.author); dest.writeString(this.authorName);
dest.writeString(this.email); dest.writeString(this.authorEmail);
dest.writeString(this.webURL); dest.writeString(this.webSite);
dest.writeString(this.trackerURL); dest.writeString(this.issueTracker);
dest.writeString(this.sourceURL); dest.writeString(this.sourceCode);
dest.writeString(this.changelogURL); dest.writeString(this.changelog);
dest.writeString(this.donateURL); dest.writeString(this.donate);
dest.writeString(this.bitcoinAddr); dest.writeString(this.bitcoin);
dest.writeString(this.litecoinAddr); dest.writeString(this.litecoin);
dest.writeString(this.flattrID); dest.writeString(this.flattrID);
dest.writeString(this.upstreamVersionName); dest.writeString(this.upstreamVersionName);
dest.writeInt(this.upstreamVersionCode); dest.writeInt(this.upstreamVersionCode);
@ -668,15 +669,15 @@ public class App extends ValueObject implements Comparable<App>, Parcelable {
this.icon = in.readString(); this.icon = in.readString();
this.description = in.readString(); this.description = in.readString();
this.license = in.readString(); this.license = in.readString();
this.author = in.readString(); this.authorName = in.readString();
this.email = in.readString(); this.authorEmail = in.readString();
this.webURL = in.readString(); this.webSite = in.readString();
this.trackerURL = in.readString(); this.issueTracker = in.readString();
this.sourceURL = in.readString(); this.sourceCode = in.readString();
this.changelogURL = in.readString(); this.changelog = in.readString();
this.donateURL = in.readString(); this.donate = in.readString();
this.bitcoinAddr = in.readString(); this.bitcoin = in.readString();
this.litecoinAddr = in.readString(); this.litecoin = in.readString();
this.flattrID = in.readString(); this.flattrID = in.readString();
this.upstreamVersionName = in.readString(); this.upstreamVersionName = in.readString();
this.upstreamVersionCode = in.readInt(); this.upstreamVersionCode = in.readInt();

View File

@ -12,9 +12,9 @@ import android.util.Log;
import org.fdroid.fdroid.Preferences; import org.fdroid.fdroid.Preferences;
import org.fdroid.fdroid.Utils; import org.fdroid.fdroid.Utils;
import org.fdroid.fdroid.data.Schema.ApkTable; import org.fdroid.fdroid.data.Schema.ApkTable;
import org.fdroid.fdroid.data.Schema.AppPrefsTable;
import org.fdroid.fdroid.data.Schema.AppMetadataTable; import org.fdroid.fdroid.data.Schema.AppMetadataTable;
import org.fdroid.fdroid.data.Schema.AppMetadataTable.Cols; import org.fdroid.fdroid.data.Schema.AppMetadataTable.Cols;
import org.fdroid.fdroid.data.Schema.AppPrefsTable;
import org.fdroid.fdroid.data.Schema.CatJoinTable; import org.fdroid.fdroid.data.Schema.CatJoinTable;
import org.fdroid.fdroid.data.Schema.CategoryTable; import org.fdroid.fdroid.data.Schema.CategoryTable;
import org.fdroid.fdroid.data.Schema.InstalledAppTable; import org.fdroid.fdroid.data.Schema.InstalledAppTable;
@ -837,6 +837,11 @@ public class AppProvider extends FDroidProvider {
values.remove(Cols.Package.PACKAGE_NAME); values.remove(Cols.Package.PACKAGE_NAME);
values.put(Cols.PACKAGE_ID, packageId); values.put(Cols.PACKAGE_ID, packageId);
if (!values.containsKey(Cols.DESCRIPTION) || values.getAsString(Cols.DESCRIPTION) == null) {
// the current structure assumes that description is always present and non-null
values.put(Cols.DESCRIPTION, "");
}
String[] categories = null; String[] categories = null;
boolean saveCategories = false; boolean saveCategories = false;
if (values.containsKey(Cols.ForWriting.Categories.CATEGORIES)) { if (values.containsKey(Cols.ForWriting.Categories.CATEGORIES)) {

View File

@ -76,6 +76,8 @@ class DBHelper extends SQLiteOpenHelper {
+ RepoTable.Cols.USERNAME + " string, " + RepoTable.Cols.USERNAME + " string, "
+ RepoTable.Cols.PASSWORD + " string," + RepoTable.Cols.PASSWORD + " string,"
+ RepoTable.Cols.TIMESTAMP + " integer not null default 0, " + RepoTable.Cols.TIMESTAMP + " integer not null default 0, "
+ RepoTable.Cols.ICON + " string, "
+ RepoTable.Cols.MIRRORS + " string, "
+ RepoTable.Cols.PUSH_REQUESTS + " integer not null default " + Repo.PUSH_REQUEST_IGNORE + RepoTable.Cols.PUSH_REQUESTS + " integer not null default " + Repo.PUSH_REQUEST_IGNORE
+ ");"; + ");";
@ -104,6 +106,7 @@ class DBHelper extends SQLiteOpenHelper {
+ ApkTable.Cols.ADDED_DATE + " string, " + ApkTable.Cols.ADDED_DATE + " string, "
+ ApkTable.Cols.IS_COMPATIBLE + " int not null, " + ApkTable.Cols.IS_COMPATIBLE + " int not null, "
+ ApkTable.Cols.INCOMPATIBLE_REASONS + " text, " + ApkTable.Cols.INCOMPATIBLE_REASONS + " text, "
+ ApkTable.Cols.ANTI_FEATURES + " string, "
+ "PRIMARY KEY (" + ApkTable.Cols.APP_ID + ", " + ApkTable.Cols.VERSION_CODE + ", " + ApkTable.Cols.REPO_ID + ")" + "PRIMARY KEY (" + ApkTable.Cols.APP_ID + ", " + ApkTable.Cols.VERSION_CODE + ", " + ApkTable.Cols.REPO_ID + ")"
+ ");"; + ");";
@ -116,19 +119,19 @@ class DBHelper extends SQLiteOpenHelper {
+ AppMetadataTable.Cols.ICON + " text, " + AppMetadataTable.Cols.ICON + " text, "
+ AppMetadataTable.Cols.DESCRIPTION + " text not null, " + AppMetadataTable.Cols.DESCRIPTION + " text not null, "
+ AppMetadataTable.Cols.LICENSE + " text not null, " + AppMetadataTable.Cols.LICENSE + " text not null, "
+ AppMetadataTable.Cols.AUTHOR + " text, " + AppMetadataTable.Cols.AUTHOR_NAME + " text, "
+ AppMetadataTable.Cols.EMAIL + " text, " + AppMetadataTable.Cols.AUTHOR_EMAIL + " text, "
+ AppMetadataTable.Cols.WEB_URL + " text, " + AppMetadataTable.Cols.WEBSITE + " text, "
+ AppMetadataTable.Cols.TRACKER_URL + " text, " + AppMetadataTable.Cols.ISSUE_TRACKER + " text, "
+ AppMetadataTable.Cols.SOURCE_URL + " text, " + AppMetadataTable.Cols.SOURCE_CODE + " text, "
+ AppMetadataTable.Cols.CHANGELOG_URL + " text, " + AppMetadataTable.Cols.CHANGELOG + " text, "
+ AppMetadataTable.Cols.SUGGESTED_VERSION_CODE + " text," + AppMetadataTable.Cols.SUGGESTED_VERSION_CODE + " text,"
+ AppMetadataTable.Cols.UPSTREAM_VERSION_NAME + " text," + AppMetadataTable.Cols.UPSTREAM_VERSION_NAME + " text,"
+ AppMetadataTable.Cols.UPSTREAM_VERSION_CODE + " integer," + AppMetadataTable.Cols.UPSTREAM_VERSION_CODE + " integer,"
+ AppMetadataTable.Cols.ANTI_FEATURES + " string," + AppMetadataTable.Cols.ANTI_FEATURES + " string,"
+ AppMetadataTable.Cols.DONATE_URL + " string," + AppMetadataTable.Cols.DONATE + " string,"
+ AppMetadataTable.Cols.BITCOIN_ADDR + " string," + AppMetadataTable.Cols.BITCOIN + " string,"
+ AppMetadataTable.Cols.LITECOIN_ADDR + " string," + AppMetadataTable.Cols.LITECOIN + " string,"
+ AppMetadataTable.Cols.FLATTR_ID + " string," + AppMetadataTable.Cols.FLATTR_ID + " string,"
+ AppMetadataTable.Cols.REQUIREMENTS + " string," + AppMetadataTable.Cols.REQUIREMENTS + " string,"
+ AppMetadataTable.Cols.ADDED + " string," + AppMetadataTable.Cols.ADDED + " string,"
@ -178,7 +181,7 @@ class DBHelper extends SQLiteOpenHelper {
+ InstalledAppTable.Cols.HASH + " TEXT NOT NULL" + InstalledAppTable.Cols.HASH + " TEXT NOT NULL"
+ " );"; + " );";
protected static final int DB_VERSION = 65; protected static final int DB_VERSION = 66;
private final Context context; private final Context context;
@ -258,6 +261,27 @@ class DBHelper extends SQLiteOpenHelper {
migrateToPackageTable(db, oldVersion); migrateToPackageTable(db, oldVersion);
addObbFiles(db, oldVersion); addObbFiles(db, oldVersion);
addCategoryTables(db, oldVersion); addCategoryTables(db, oldVersion);
addIndexV1Fields(db, oldVersion);
}
private void addIndexV1Fields(SQLiteDatabase db, int oldVersion) {
if (oldVersion >= 66) {
return;
}
if (!columnExists(db, Schema.RepoTable.NAME, RepoTable.Cols.ICON)) {
Utils.debugLog(TAG, "Adding " + RepoTable.Cols.ICON + " field to " + RepoTable.NAME + " table in db.");
db.execSQL("alter table " + RepoTable.NAME + " add column " + RepoTable.Cols.ICON + " string;");
}
if (!columnExists(db, RepoTable.NAME, RepoTable.Cols.MIRRORS)) {
Utils.debugLog(TAG, "Adding " + RepoTable.Cols.MIRRORS + " field to " + RepoTable.NAME + " table in db.");
db.execSQL("alter table " + RepoTable.NAME + " add column " + RepoTable.Cols.MIRRORS + " string;");
}
if (!columnExists(db, ApkTable.NAME, ApkTable.Cols.ANTI_FEATURES)) {
Utils.debugLog(TAG, "Adding " + ApkTable.Cols.ANTI_FEATURES + " field to " + ApkTable.NAME + " table in db.");
db.execSQL("alter table " + ApkTable.NAME + " add column " + ApkTable.Cols.ANTI_FEATURES + " string;");
}
} }
/** /**
@ -756,11 +780,11 @@ class DBHelper extends SQLiteOpenHelper {
} }
private void addChangelogToApp(SQLiteDatabase db, int oldVersion) { private void addChangelogToApp(SQLiteDatabase db, int oldVersion) {
if (oldVersion >= 48 || columnExists(db, AppMetadataTable.NAME, AppMetadataTable.Cols.CHANGELOG_URL)) { if (oldVersion >= 48 || columnExists(db, AppMetadataTable.NAME, AppMetadataTable.Cols.CHANGELOG)) {
return; return;
} }
Utils.debugLog(TAG, "Adding " + AppMetadataTable.Cols.CHANGELOG_URL + " column to " + AppMetadataTable.NAME); Utils.debugLog(TAG, "Adding " + AppMetadataTable.Cols.CHANGELOG + " column to " + AppMetadataTable.NAME);
db.execSQL("alter table " + AppMetadataTable.NAME + " add column " + AppMetadataTable.Cols.CHANGELOG_URL + " text"); db.execSQL("alter table " + AppMetadataTable.NAME + " add column " + AppMetadataTable.Cols.CHANGELOG + " text");
} }
private void addIconUrlLargeToApp(SQLiteDatabase db, int oldVersion) { private void addIconUrlLargeToApp(SQLiteDatabase db, int oldVersion) {
@ -809,13 +833,13 @@ class DBHelper extends SQLiteOpenHelper {
if (oldVersion >= 53) { if (oldVersion >= 53) {
return; return;
} }
if (!columnExists(db, AppMetadataTable.NAME, AppMetadataTable.Cols.AUTHOR)) { if (!columnExists(db, AppMetadataTable.NAME, AppMetadataTable.Cols.AUTHOR_NAME)) {
Utils.debugLog(TAG, "Adding " + AppMetadataTable.Cols.AUTHOR + " column to " + AppMetadataTable.NAME); Utils.debugLog(TAG, "Adding " + AppMetadataTable.Cols.AUTHOR_NAME + " column to " + AppMetadataTable.NAME);
db.execSQL("alter table " + AppMetadataTable.NAME + " add column " + AppMetadataTable.Cols.AUTHOR + " text"); db.execSQL("alter table " + AppMetadataTable.NAME + " add column " + AppMetadataTable.Cols.AUTHOR_NAME + " text");
} }
if (!columnExists(db, AppMetadataTable.NAME, AppMetadataTable.Cols.EMAIL)) { if (!columnExists(db, AppMetadataTable.NAME, AppMetadataTable.Cols.AUTHOR_EMAIL)) {
Utils.debugLog(TAG, "Adding " + AppMetadataTable.Cols.EMAIL + " column to " + AppMetadataTable.NAME); Utils.debugLog(TAG, "Adding " + AppMetadataTable.Cols.AUTHOR_EMAIL + " column to " + AppMetadataTable.NAME);
db.execSQL("alter table " + AppMetadataTable.NAME + " add column " + AppMetadataTable.Cols.EMAIL + " text"); db.execSQL("alter table " + AppMetadataTable.NAME + " add column " + AppMetadataTable.Cols.AUTHOR_EMAIL + " text");
} }
} }

View File

@ -124,13 +124,21 @@ public class InstalledAppProviderService extends IntentService {
* is in sync with what the {@link PackageManager} tells us is installed. Once * is in sync with what the {@link PackageManager} tells us is installed. Once
* completed, the relevant {@link android.content.ContentProvider}s will be * completed, the relevant {@link android.content.ContentProvider}s will be
* notified of any changes to installed statuses. * notified of any changes to installed statuses.
* <p/> * <p>
* The installed app cache could get out of sync, e.g. if F-Droid crashed/ or * The installed app cache could get out of sync, e.g. if F-Droid crashed/ or
* ran out of battery half way through responding to {@link Intent#ACTION_PACKAGE_ADDED}. * ran out of battery half way through responding to {@link Intent#ACTION_PACKAGE_ADDED}.
* This method returns immediately, and will continue to work in an * This method returns immediately, and will continue to work in an
* {@link IntentService}. It doesn't really matter where we put this in the * {@link IntentService}. It doesn't really matter where we put this in the
* bootstrap process, because it runs in its own thread, at the lowest priority: * bootstrap process, because it runs in its own thread, at the lowest priority:
* {@link Process#THREAD_PRIORITY_LOWEST}. * {@link Process#THREAD_PRIORITY_LOWEST}.
* <p>
* APKs installed in {@code /system} will often have zeroed out timestamps, like
* 2008-01-01 (ziptime) or 2009-01-01. So instead anything older than 2010 every
* time since we have no way to know whether an APK wasn't changed as part of an
* OTA update. An OTA update could change the APK without changing the
* {@link PackageInfo#versionCode} or {@link PackageInfo#lastUpdateTime}.
*
* @see <a href="https://gitlab.com/fdroid/fdroidclient/issues/819>issue #819</a>
*/ */
public static void compareToPackageManager(Context context) { public static void compareToPackageManager(Context context) {
Map<String, Long> cachedInfo = InstalledAppProvider.Helper.all(context); Map<String, Long> cachedInfo = InstalledAppProvider.Helper.all(context);
@ -139,7 +147,8 @@ public class InstalledAppProviderService extends IntentService {
.getInstalledPackages(PackageManager.GET_SIGNATURES); .getInstalledPackages(PackageManager.GET_SIGNATURES);
for (PackageInfo packageInfo : packageInfoList) { for (PackageInfo packageInfo : packageInfoList) {
if (cachedInfo.containsKey(packageInfo.packageName)) { if (cachedInfo.containsKey(packageInfo.packageName)) {
if (packageInfo.lastUpdateTime > cachedInfo.get(packageInfo.packageName)) { if (packageInfo.lastUpdateTime < 1262300400000L // 2010-01-01 00:00
|| packageInfo.lastUpdateTime > cachedInfo.get(packageInfo.packageName)) {
insert(context, packageInfo); insert(context, packageInfo);
} }
cachedInfo.remove(packageInfo.packageName); cachedInfo.remove(packageInfo.packageName);
@ -207,7 +216,7 @@ public class InstalledAppProviderService extends IntentService {
* broadcast. In the first case, it will already have a {@link PackageInfo} for us. However if * broadcast. In the first case, it will already have a {@link PackageInfo} for us. However if
* it is from the later case, we'll need to query the {@link PackageManager} ourselves to get * it is from the later case, we'll need to query the {@link PackageManager} ourselves to get
* this info. * this info.
* * <p>
* Can still return null, as there is potentially race conditions to do with uninstalling apps * Can still return null, as there is potentially race conditions to do with uninstalling apps
* such that querying the {@link PackageManager} for a given package may throw an exception. * such that querying the {@link PackageManager} for a given package may throw an exception.
*/ */

View File

@ -47,6 +47,7 @@ public class Repo extends ValueObject {
public String address; public String address;
public String name; public String name;
public String description; public String description;
public String icon;
/** index version, i.e. what fdroidserver built it - 0 if not specified */ /** index version, i.e. what fdroidserver built it - 0 if not specified */
public int version; public int version;
public boolean inuse; public boolean inuse;
@ -71,6 +72,9 @@ public class Repo extends ValueObject {
/** When the signed repo index was generated, used to protect against replay attacks */ /** When the signed repo index was generated, used to protect against replay attacks */
public long timestamp; public long timestamp;
/** Official mirrors of this repo, considered automatically interchangeable */
public String[] mirrors;
/** How to treat push requests included in this repo's index XML */ /** How to treat push requests included in this repo's index XML */
public int pushRequests = PUSH_REQUEST_IGNORE; public int pushRequests = PUSH_REQUEST_IGNORE;
@ -131,6 +135,12 @@ public class Repo extends ValueObject {
case Cols.TIMESTAMP: case Cols.TIMESTAMP:
timestamp = cursor.getLong(i); timestamp = cursor.getLong(i);
break; break;
case Cols.ICON:
icon = cursor.getString(i);
break;
case Cols.MIRRORS:
mirrors = Utils.parseCommaSeparatedString(cursor.getString(i));
break;
case Cols.PUSH_REQUESTS: case Cols.PUSH_REQUESTS:
pushRequests = cursor.getInt(i); pushRequests = cursor.getInt(i);
break; break;
@ -257,6 +267,14 @@ public class Repo extends ValueObject {
timestamp = toInt(values.getAsInteger(Cols.TIMESTAMP)); timestamp = toInt(values.getAsInteger(Cols.TIMESTAMP));
} }
if (values.containsKey(Cols.ICON)) {
icon = values.getAsString(Cols.ICON);
}
if (values.containsKey(Cols.MIRRORS)) {
mirrors = Utils.parseCommaSeparatedString(values.getAsString(Cols.MIRRORS));
}
if (values.containsKey(Cols.PUSH_REQUESTS)) { if (values.containsKey(Cols.PUSH_REQUESTS)) {
pushRequests = toInt(values.getAsInteger(Cols.PUSH_REQUESTS)); pushRequests = toInt(values.getAsInteger(Cols.PUSH_REQUESTS));
} }

View File

@ -124,15 +124,15 @@ public interface Schema {
String ICON = "icon"; String ICON = "icon";
String DESCRIPTION = "description"; String DESCRIPTION = "description";
String LICENSE = "license"; String LICENSE = "license";
String AUTHOR = "author"; String AUTHOR_NAME = "author";
String EMAIL = "email"; String AUTHOR_EMAIL = "email";
String WEB_URL = "webURL"; String WEBSITE = "webURL";
String TRACKER_URL = "trackerURL"; String ISSUE_TRACKER = "trackerURL";
String SOURCE_URL = "sourceURL"; String SOURCE_CODE = "sourceURL";
String CHANGELOG_URL = "changelogURL"; String CHANGELOG = "changelogURL";
String DONATE_URL = "donateURL"; String DONATE = "donateURL";
String BITCOIN_ADDR = "bitcoinAddr"; String BITCOIN = "bitcoinAddr";
String LITECOIN_ADDR = "litecoinAddr"; String LITECOIN = "litecoinAddr";
String FLATTR_ID = "flattrID"; String FLATTR_ID = "flattrID";
String SUGGESTED_VERSION_CODE = "suggestedVercode"; String SUGGESTED_VERSION_CODE = "suggestedVercode";
String UPSTREAM_VERSION_NAME = "upstreamVersion"; String UPSTREAM_VERSION_NAME = "upstreamVersion";
@ -176,8 +176,8 @@ public interface Schema {
*/ */
String[] ALL_COLS = { String[] ALL_COLS = {
ROW_ID, PACKAGE_ID, REPO_ID, IS_COMPATIBLE, NAME, SUMMARY, ICON, DESCRIPTION, ROW_ID, PACKAGE_ID, REPO_ID, IS_COMPATIBLE, NAME, SUMMARY, ICON, DESCRIPTION,
LICENSE, AUTHOR, EMAIL, WEB_URL, TRACKER_URL, SOURCE_URL, LICENSE, AUTHOR_NAME, AUTHOR_EMAIL, WEBSITE, ISSUE_TRACKER, SOURCE_CODE,
CHANGELOG_URL, DONATE_URL, BITCOIN_ADDR, LITECOIN_ADDR, FLATTR_ID, CHANGELOG, DONATE, BITCOIN, LITECOIN, FLATTR_ID,
UPSTREAM_VERSION_NAME, UPSTREAM_VERSION_CODE, ADDED, LAST_UPDATED, UPSTREAM_VERSION_NAME, UPSTREAM_VERSION_CODE, ADDED, LAST_UPDATED,
ANTI_FEATURES, REQUIREMENTS, ICON_URL, ICON_URL_LARGE, ANTI_FEATURES, REQUIREMENTS, ICON_URL, ICON_URL_LARGE,
SUGGESTED_VERSION_CODE, SUGGESTED_VERSION_CODE,
@ -190,8 +190,8 @@ public interface Schema {
*/ */
String[] ALL = { String[] ALL = {
_ID, ROW_ID, REPO_ID, IS_COMPATIBLE, NAME, SUMMARY, ICON, DESCRIPTION, _ID, ROW_ID, REPO_ID, IS_COMPATIBLE, NAME, SUMMARY, ICON, DESCRIPTION,
LICENSE, AUTHOR, EMAIL, WEB_URL, TRACKER_URL, SOURCE_URL, LICENSE, AUTHOR_NAME, AUTHOR_EMAIL, WEBSITE, ISSUE_TRACKER, SOURCE_CODE,
CHANGELOG_URL, DONATE_URL, BITCOIN_ADDR, LITECOIN_ADDR, FLATTR_ID, CHANGELOG, DONATE, BITCOIN, LITECOIN, FLATTR_ID,
UPSTREAM_VERSION_NAME, UPSTREAM_VERSION_CODE, ADDED, LAST_UPDATED, UPSTREAM_VERSION_NAME, UPSTREAM_VERSION_CODE, ADDED, LAST_UPDATED,
ANTI_FEATURES, REQUIREMENTS, ICON_URL, ICON_URL_LARGE, ANTI_FEATURES, REQUIREMENTS, ICON_URL, ICON_URL_LARGE,
SUGGESTED_VERSION_CODE, SuggestedApk.VERSION_NAME, SUGGESTED_VERSION_CODE, SuggestedApk.VERSION_NAME,
@ -240,6 +240,7 @@ public interface Schema {
String ADDED_DATE = "added"; String ADDED_DATE = "added";
String IS_COMPATIBLE = "compatible"; String IS_COMPATIBLE = "compatible";
String INCOMPATIBLE_REASONS = "incompatibleReasons"; String INCOMPATIBLE_REASONS = "incompatibleReasons";
String ANTI_FEATURES = "antiFeatures";
interface Repo { interface Repo {
String VERSION = "repoVersion"; String VERSION = "repoVersion";
@ -258,7 +259,7 @@ public interface Schema {
SIZE, SIGNATURE, SOURCE_NAME, MIN_SDK_VERSION, TARGET_SDK_VERSION, MAX_SDK_VERSION, SIZE, SIGNATURE, SOURCE_NAME, MIN_SDK_VERSION, TARGET_SDK_VERSION, MAX_SDK_VERSION,
OBB_MAIN_FILE, OBB_MAIN_FILE_SHA256, OBB_PATCH_FILE, OBB_PATCH_FILE_SHA256, OBB_MAIN_FILE, OBB_MAIN_FILE_SHA256, OBB_PATCH_FILE, OBB_PATCH_FILE_SHA256,
REQUESTED_PERMISSIONS, FEATURES, NATIVE_CODE, HASH_TYPE, ADDED_DATE, REQUESTED_PERMISSIONS, FEATURES, NATIVE_CODE, HASH_TYPE, ADDED_DATE,
IS_COMPATIBLE, INCOMPATIBLE_REASONS, IS_COMPATIBLE, INCOMPATIBLE_REASONS, ANTI_FEATURES,
}; };
/** /**
@ -270,6 +271,7 @@ public interface Schema {
OBB_MAIN_FILE, OBB_MAIN_FILE_SHA256, OBB_PATCH_FILE, OBB_PATCH_FILE_SHA256, OBB_MAIN_FILE, OBB_MAIN_FILE_SHA256, OBB_PATCH_FILE, OBB_PATCH_FILE_SHA256,
REQUESTED_PERMISSIONS, FEATURES, NATIVE_CODE, HASH_TYPE, ADDED_DATE, REQUESTED_PERMISSIONS, FEATURES, NATIVE_CODE, HASH_TYPE, ADDED_DATE,
IS_COMPATIBLE, Repo.VERSION, Repo.ADDRESS, INCOMPATIBLE_REASONS, IS_COMPATIBLE, Repo.VERSION, Repo.ADDRESS, INCOMPATIBLE_REASONS,
ANTI_FEATURES,
}; };
} }
} }
@ -295,12 +297,14 @@ public interface Schema {
String USERNAME = "username"; String USERNAME = "username";
String PASSWORD = "password"; String PASSWORD = "password";
String TIMESTAMP = "timestamp"; String TIMESTAMP = "timestamp";
String ICON = "icon";
String MIRRORS = "mirrors";
String PUSH_REQUESTS = "pushRequests"; String PUSH_REQUESTS = "pushRequests";
String[] ALL = { String[] ALL = {
_ID, ADDRESS, NAME, DESCRIPTION, IN_USE, PRIORITY, SIGNING_CERT, _ID, ADDRESS, NAME, DESCRIPTION, IN_USE, PRIORITY, SIGNING_CERT,
FINGERPRINT, MAX_AGE, LAST_UPDATED, LAST_ETAG, VERSION, IS_SWAP, FINGERPRINT, MAX_AGE, LAST_UPDATED, LAST_ETAG, VERSION, IS_SWAP,
USERNAME, PASSWORD, TIMESTAMP, PUSH_REQUESTS, USERNAME, PASSWORD, TIMESTAMP, ICON, MIRRORS, PUSH_REQUESTS,
}; };
} }
} }

View File

@ -176,7 +176,7 @@ public class AppDetailsRecyclerViewAdapter
} }
private boolean shouldShowDonate() { private boolean shouldShowDonate() {
return uriIsSetAndCanBeOpened(app.donateURL) || return uriIsSetAndCanBeOpened(app.donate) ||
uriIsSetAndCanBeOpened(app.getBitcoinUri()) || uriIsSetAndCanBeOpened(app.getBitcoinUri()) ||
uriIsSetAndCanBeOpened(app.getLitecoinUri()) || uriIsSetAndCanBeOpened(app.getLitecoinUri()) ||
uriIsSetAndCanBeOpened(app.getFlattrUri()); uriIsSetAndCanBeOpened(app.getFlattrUri());
@ -382,8 +382,8 @@ public class AppDetailsRecyclerViewAdapter
public void bindModel() { public void bindModel() {
ImageLoader.getInstance().displayImage(app.iconUrlLarge, iconView, displayImageOptions); ImageLoader.getInstance().displayImage(app.iconUrlLarge, iconView, displayImageOptions);
titleView.setText(app.name); titleView.setText(app.name);
if (!TextUtils.isEmpty(app.author)) { if (!TextUtils.isEmpty(app.authorName)) {
authorView.setText(context.getString(R.string.by_author) + " " + app.author); authorView.setText(context.getString(R.string.by_author) + " " + app.authorName);
authorView.setVisibility(View.VISIBLE); authorView.setVisibility(View.VISIBLE);
} else { } else {
authorView.setVisibility(View.GONE); authorView.setVisibility(View.GONE);
@ -523,18 +523,18 @@ public class AppDetailsRecyclerViewAdapter
} }
public void bindModel() { public void bindModel() {
if (TextUtils.isEmpty(app.author)) { if (TextUtils.isEmpty(app.authorName)) {
donateHeading.setText(context.getString(R.string.app_details_donate_prompt_unknown_author, app.name)); donateHeading.setText(context.getString(R.string.app_details_donate_prompt_unknown_author, app.name));
} else { } else {
String author = "<strong>" + app.author + "</strong>"; String author = "<strong>" + app.authorName + "</strong>";
donateHeading.setText(Html.fromHtml(context.getString(R.string.app_details_donate_prompt, app.name, author))); donateHeading.setText(Html.fromHtml(context.getString(R.string.app_details_donate_prompt, app.name, author)));
} }
donationOptionsLayout.removeAllViews(); donationOptionsLayout.removeAllViews();
// Donate button // Donate button
if (uriIsSetAndCanBeOpened(app.donateURL)) { if (uriIsSetAndCanBeOpened(app.donate)) {
addDonateOption(R.layout.donate_generic, app.donateURL); addDonateOption(R.layout.donate_generic, app.donate);
} }
// Bitcoin // Bitcoin
@ -639,28 +639,28 @@ public class AppDetailsRecyclerViewAdapter
contentView.removeAllViews(); contentView.removeAllViews();
// Source button // Source button
if (uriIsSetAndCanBeOpened(app.sourceURL)) { if (uriIsSetAndCanBeOpened(app.sourceCode)) {
addLinkItemView(contentView, R.string.menu_source, R.drawable.ic_source_code, app.sourceURL); addLinkItemView(contentView, R.string.menu_source, R.drawable.ic_source_code, app.sourceCode);
} }
// Issues button // Issues button
if (uriIsSetAndCanBeOpened(app.trackerURL)) { if (uriIsSetAndCanBeOpened(app.issueTracker)) {
addLinkItemView(contentView, R.string.menu_issues, R.drawable.ic_issues, app.trackerURL); addLinkItemView(contentView, R.string.menu_issues, R.drawable.ic_issues, app.issueTracker);
} }
// Changelog button // Changelog button
if (uriIsSetAndCanBeOpened(app.changelogURL)) { if (uriIsSetAndCanBeOpened(app.changelog)) {
addLinkItemView(contentView, R.string.menu_changelog, R.drawable.ic_changelog, app.changelogURL); addLinkItemView(contentView, R.string.menu_changelog, R.drawable.ic_changelog, app.changelog);
} }
// Website button // Website button
if (uriIsSetAndCanBeOpened(app.webURL)) { if (uriIsSetAndCanBeOpened(app.webSite)) {
addLinkItemView(contentView, R.string.menu_website, R.drawable.ic_website, app.webURL); addLinkItemView(contentView, R.string.menu_website, R.drawable.ic_website, app.webSite);
} }
// Email button // Email button
final String subject = Uri.encode(context.getString(R.string.app_details_subject, app.name)); final String subject = Uri.encode(context.getString(R.string.app_details_subject, app.name));
String emailUrl = app.email == null ? null : ("mailto:" + app.email + "?subject=" + subject); String emailUrl = app.authorEmail == null ? null : ("mailto:" + app.authorEmail + "?subject=" + subject);
if (uriIsSetAndCanBeOpened(emailUrl)) { if (uriIsSetAndCanBeOpened(emailUrl)) {
addLinkItemView(contentView, R.string.menu_email, R.drawable.ic_email, emailUrl); addLinkItemView(contentView, R.string.menu_email, R.drawable.ic_email, emailUrl);
} }

View File

@ -274,9 +274,9 @@ public class ProperMultiRepoUpdaterTest extends MultiRepoUpdaterTest {
assertEquals("2048", a2048.name); assertEquals("2048", a2048.name);
assertEquals(String.format("<p>2048 from %s repo.</p>", id), a2048.description); assertEquals(String.format("<p>2048 from %s repo.</p>", id), a2048.description);
assertEquals(String.format("Puzzle game (%s)", id), a2048.summary); assertEquals(String.format("Puzzle game (%s)", id), a2048.summary);
assertEquals(String.format("https://github.com/uberspot/2048-android?%s", id), a2048.webURL); assertEquals(String.format("https://github.com/uberspot/2048-android?%s", id), a2048.webSite);
assertEquals(String.format("https://github.com/uberspot/2048-android?code&%s", id), a2048.sourceURL); assertEquals(String.format("https://github.com/uberspot/2048-android?code&%s", id), a2048.sourceCode);
assertEquals(String.format("https://github.com/uberspot/2048-android/issues?%s", id), a2048.trackerURL); assertEquals(String.format("https://github.com/uberspot/2048-android/issues?%s", id), a2048.issueTracker);
} }
private void assertAdAwayMetadata(Repo repo, @RepoIdentifier String id) { private void assertAdAwayMetadata(Repo repo, @RepoIdentifier String id) {
@ -290,11 +290,11 @@ public class ProperMultiRepoUpdaterTest extends MultiRepoUpdaterTest {
assertEquals(String.format("AdAway", id), adaway.name); assertEquals(String.format("AdAway", id), adaway.name);
assertEquals(String.format("<p>AdAway from %s repo.</p>", id), adaway.description); assertEquals(String.format("<p>AdAway from %s repo.</p>", id), adaway.description);
assertEquals(String.format("Block advertisements (%s)", id), adaway.summary); assertEquals(String.format("Block advertisements (%s)", id), adaway.summary);
assertEquals(String.format("http://sufficientlysecure.org/index.php/adaway?%s", id), adaway.webURL); assertEquals(String.format("http://sufficientlysecure.org/index.php/adaway?%s", id), adaway.webSite);
assertEquals(String.format("https://github.com/dschuermann/ad-away?%s", id), adaway.sourceURL); assertEquals(String.format("https://github.com/dschuermann/ad-away?%s", id), adaway.sourceCode);
assertEquals(String.format("https://github.com/dschuermann/ad-away/issues?%s", id), adaway.trackerURL); assertEquals(String.format("https://github.com/dschuermann/ad-away/issues?%s", id), adaway.issueTracker);
assertEquals(String.format("https://github.com/dschuermann/ad-away/raw/HEAD/CHANGELOG?%s", id), adaway.changelogURL); assertEquals(String.format("https://github.com/dschuermann/ad-away/raw/HEAD/CHANGELOG?%s", id), adaway.changelog);
assertEquals(String.format("http://sufficientlysecure.org/index.php/adaway?%s", id), adaway.donateURL); assertEquals(String.format("http://sufficientlysecure.org/index.php/adaway?%s", id), adaway.donate);
assertEquals(String.format("369138", id), adaway.flattrID); assertEquals(String.format("369138", id), adaway.flattrID);
} }
@ -309,9 +309,9 @@ public class ProperMultiRepoUpdaterTest extends MultiRepoUpdaterTest {
assertEquals("adbWireless", adb.name); assertEquals("adbWireless", adb.name);
assertEquals(String.format("<p>adbWireless from %s repo.</p>", id), adb.description); assertEquals(String.format("<p>adbWireless from %s repo.</p>", id), adb.description);
assertEquals(String.format("Wireless adb (%s)", id), adb.summary); assertEquals(String.format("Wireless adb (%s)", id), adb.summary);
assertEquals(String.format("https://adbwireless.example.com?%s", id), adb.webURL); assertEquals(String.format("https://adbwireless.example.com?%s", id), adb.webSite);
assertEquals(String.format("https://adbwireless.example.com/source?%s", id), adb.sourceURL); assertEquals(String.format("https://adbwireless.example.com/source?%s", id), adb.sourceCode);
assertEquals(String.format("https://adbwireless.example.com/issues?%s", id), adb.trackerURL); assertEquals(String.format("https://adbwireless.example.com/issues?%s", id), adb.issueTracker);
} }
private void assertCalendarMetadata(Repo repo, @RepoIdentifier String id) { private void assertCalendarMetadata(Repo repo, @RepoIdentifier String id) {
@ -325,9 +325,9 @@ public class ProperMultiRepoUpdaterTest extends MultiRepoUpdaterTest {
assertEquals("Add to calendar", calendar.name); assertEquals("Add to calendar", calendar.name);
assertEquals(String.format("<p>Add to calendar from %s repo.</p>", id), calendar.description); assertEquals(String.format("<p>Add to calendar from %s repo.</p>", id), calendar.description);
assertEquals(String.format("Import .ics files into calendar (%s)", id), calendar.summary); assertEquals(String.format("Import .ics files into calendar (%s)", id), calendar.summary);
assertEquals(String.format("https://github.com/danielegobbetti/ICSImport/blob/HEAD/README.md?%s", id), calendar.webURL); assertEquals(String.format("https://github.com/danielegobbetti/ICSImport/blob/HEAD/README.md?%s", id), calendar.webSite);
assertEquals(String.format("https://github.com/danielegobbetti/ICSImport?%s", id), calendar.sourceURL); assertEquals(String.format("https://github.com/danielegobbetti/ICSImport?%s", id), calendar.sourceCode);
assertEquals(String.format("https://github.com/danielegobbetti/ICSImport/issues?%s", id), calendar.trackerURL); assertEquals(String.format("https://github.com/danielegobbetti/ICSImport/issues?%s", id), calendar.issueTracker);
assertEquals("2225390", calendar.flattrID); assertEquals("2225390", calendar.flattrID);
} }

View File

@ -31,19 +31,24 @@ public class RepoDetails implements RepoXMLHandler.IndexReceiver {
public int maxAge; public int maxAge;
public int version; public int version;
public long timestamp; public long timestamp;
public String icon;
public String[] mirrors;
public List<Apk> apks = new ArrayList<>(); public List<Apk> apks = new ArrayList<>();
public List<App> apps = new ArrayList<>(); public List<App> apps = new ArrayList<>();
public List<RepoPushRequest> repoPushRequestList = new ArrayList<>(); public List<RepoPushRequest> repoPushRequestList = new ArrayList<>();
@Override @Override
public void receiveRepo(String name, String description, String signingCert, int maxage, int version, long timestamp) { public void receiveRepo(String name, String description, String signingCert, int maxage,
int version, long timestamp, String icon, String[] mirrors) {
this.name = name; this.name = name;
this.description = description; this.description = description;
this.signingCert = signingCert; this.signingCert = signingCert;
this.maxAge = maxage; this.maxAge = maxage;
this.version = version; this.version = version;
this.timestamp = timestamp; this.timestamp = timestamp;
this.icon = icon;
this.mirrors = mirrors;
} }
@Override @Override