Merge branch 'whatsnew-n-stuff' into 'master'

support new WhatsNew fields in index-v1 (+ l18n fixes)

Closes #910 and #941

See merge request !474
This commit is contained in:
Peter Serwylo 2017-04-17 06:32:04 +00:00
commit c73a65656a
8 changed files with 57 additions and 18 deletions

View File

@ -226,6 +226,7 @@ public final class Languages {
new Locale("da"), new Locale("da"),
new Locale("el"), new Locale("el"),
new Locale("es"), new Locale("es"),
new Locale("eo"),
new Locale("et"), new Locale("et"),
new Locale("eu"), new Locale("eu"),
new Locale("fa"), new Locale("fa"),
@ -267,6 +268,7 @@ public final class Languages {
new Locale("sk"), new Locale("sk"),
new Locale("sl"), new Locale("sl"),
new Locale("sn"), new Locale("sn"),
new Locale("sq"),
new Locale("sr"), new Locale("sr"),
new Locale("sv"), new Locale("sv"),
new Locale("sw"), new Locale("sw"),

View File

@ -110,7 +110,10 @@ public class App extends ValueObject implements Comparable<App>, Parcelable {
public String description; public String description;
public String video; /**
* A descriptive text for what has changed in this version.
*/
public String whatsNew;
public String featureGraphic; public String featureGraphic;
public String promoGraphic; public String promoGraphic;
@ -133,6 +136,8 @@ public class App extends ValueObject implements Comparable<App>, Parcelable {
public String sourceCode; public String sourceCode;
public String video;
public String changelog; public String changelog;
public String donate; public String donate;
@ -230,6 +235,9 @@ public class App extends ValueObject implements Comparable<App>, Parcelable {
case Cols.DESCRIPTION: case Cols.DESCRIPTION:
description = cursor.getString(i); description = cursor.getString(i);
break; break;
case Cols.WHATSNEW:
whatsNew = cursor.getString(i);
break;
case Cols.LICENSE: case Cols.LICENSE:
license = cursor.getString(i); license = cursor.getString(i);
break; break;
@ -248,6 +256,9 @@ public class App extends ValueObject implements Comparable<App>, Parcelable {
case Cols.SOURCE_CODE: case Cols.SOURCE_CODE:
sourceCode = cursor.getString(i); sourceCode = cursor.getString(i);
break; break;
case Cols.VIDEO:
video = cursor.getString(i);
break;
case Cols.CHANGELOG: case Cols.CHANGELOG:
changelog = cursor.getString(i); changelog = cursor.getString(i);
break; break;
@ -387,8 +398,10 @@ public class App extends ValueObject implements Comparable<App>, Parcelable {
} }
} }
// if key starts with Upper case, its set by humans // if key starts with Upper case, its set by humans
// Name, Summary, Description existed before localization so their values can be set directly
video = getLocalizedEntry(localized, localesToUse, "Video"); video = getLocalizedEntry(localized, localesToUse, "Video");
whatsNew = getLocalizedEntry(localized, localesToUse, "WhatsNew");
// Name, Summary, Description existed before localization so they shouldn't replace
// non-localized old data format with a null or blank string
String value = getLocalizedEntry(localized, localesToUse, "Name"); String value = getLocalizedEntry(localized, localesToUse, "Name");
if (!TextUtils.isEmpty(value)) { if (!TextUtils.isEmpty(value)) {
name = value; name = value;
@ -397,7 +410,7 @@ public class App extends ValueObject implements Comparable<App>, Parcelable {
if (!TextUtils.isEmpty(value)) { if (!TextUtils.isEmpty(value)) {
summary = value; summary = value;
} }
description = getLocalizedEntry(localized, localesToUse, "Description"); value = getLocalizedEntry(localized, localesToUse, "Description");
if (!TextUtils.isEmpty(value)) { if (!TextUtils.isEmpty(value)) {
description = value; description = value;
} }
@ -733,12 +746,14 @@ public class App extends ValueObject implements Comparable<App>, Parcelable {
values.put(Cols.ICON_URL, iconUrl); values.put(Cols.ICON_URL, iconUrl);
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.WHATSNEW, whatsNew);
values.put(Cols.LICENSE, license); values.put(Cols.LICENSE, license);
values.put(Cols.AUTHOR_NAME, authorName); values.put(Cols.AUTHOR_NAME, authorName);
values.put(Cols.AUTHOR_EMAIL, authorEmail); values.put(Cols.AUTHOR_EMAIL, authorEmail);
values.put(Cols.WEBSITE, webSite); values.put(Cols.WEBSITE, webSite);
values.put(Cols.ISSUE_TRACKER, issueTracker); values.put(Cols.ISSUE_TRACKER, issueTracker);
values.put(Cols.SOURCE_CODE, sourceCode); values.put(Cols.SOURCE_CODE, sourceCode);
values.put(Cols.VIDEO, video);
values.put(Cols.CHANGELOG, changelog); values.put(Cols.CHANGELOG, changelog);
values.put(Cols.DONATE, donate); values.put(Cols.DONATE, donate);
values.put(Cols.BITCOIN, bitcoin); values.put(Cols.BITCOIN, bitcoin);
@ -887,12 +902,14 @@ public class App extends ValueObject implements Comparable<App>, Parcelable {
dest.writeString(this.summary); dest.writeString(this.summary);
dest.writeString(this.icon); dest.writeString(this.icon);
dest.writeString(this.description); dest.writeString(this.description);
dest.writeString(this.whatsNew);
dest.writeString(this.license); dest.writeString(this.license);
dest.writeString(this.authorName); dest.writeString(this.authorName);
dest.writeString(this.authorEmail); dest.writeString(this.authorEmail);
dest.writeString(this.webSite); dest.writeString(this.webSite);
dest.writeString(this.issueTracker); dest.writeString(this.issueTracker);
dest.writeString(this.sourceCode); dest.writeString(this.sourceCode);
dest.writeString(this.video);
dest.writeString(this.changelog); dest.writeString(this.changelog);
dest.writeString(this.donate); dest.writeString(this.donate);
dest.writeString(this.bitcoin); dest.writeString(this.bitcoin);
@ -932,12 +949,14 @@ public class App extends ValueObject implements Comparable<App>, Parcelable {
this.summary = in.readString(); this.summary = in.readString();
this.icon = in.readString(); this.icon = in.readString();
this.description = in.readString(); this.description = in.readString();
this.whatsNew = in.readString();
this.license = in.readString(); this.license = in.readString();
this.authorName = in.readString(); this.authorName = in.readString();
this.authorEmail = in.readString(); this.authorEmail = in.readString();
this.webSite = in.readString(); this.webSite = in.readString();
this.issueTracker = in.readString(); this.issueTracker = in.readString();
this.sourceCode = in.readString(); this.sourceCode = in.readString();
this.video = in.readString();
this.changelog = in.readString(); this.changelog = in.readString();
this.donate = in.readString(); this.donate = in.readString();
this.bitcoin = in.readString(); this.bitcoin = in.readString();

View File

@ -119,12 +119,14 @@ class DBHelper extends SQLiteOpenHelper {
+ AppMetadataTable.Cols.SUMMARY + " text not null, " + AppMetadataTable.Cols.SUMMARY + " text not null, "
+ AppMetadataTable.Cols.ICON + " text, " + AppMetadataTable.Cols.ICON + " text, "
+ AppMetadataTable.Cols.DESCRIPTION + " text not null, " + AppMetadataTable.Cols.DESCRIPTION + " text not null, "
+ AppMetadataTable.Cols.WHATSNEW + " text, "
+ AppMetadataTable.Cols.LICENSE + " text not null, " + AppMetadataTable.Cols.LICENSE + " text not null, "
+ AppMetadataTable.Cols.AUTHOR_NAME + " text, " + AppMetadataTable.Cols.AUTHOR_NAME + " text, "
+ AppMetadataTable.Cols.AUTHOR_EMAIL + " text, " + AppMetadataTable.Cols.AUTHOR_EMAIL + " text, "
+ AppMetadataTable.Cols.WEBSITE + " text, " + AppMetadataTable.Cols.WEBSITE + " text, "
+ AppMetadataTable.Cols.ISSUE_TRACKER + " text, " + AppMetadataTable.Cols.ISSUE_TRACKER + " text, "
+ AppMetadataTable.Cols.SOURCE_CODE + " text, " + AppMetadataTable.Cols.SOURCE_CODE + " text, "
+ AppMetadataTable.Cols.VIDEO + " string, "
+ AppMetadataTable.Cols.CHANGELOG + " 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,"
@ -190,7 +192,7 @@ class DBHelper extends SQLiteOpenHelper {
+ InstalledAppTable.Cols.HASH + " TEXT NOT NULL" + InstalledAppTable.Cols.HASH + " TEXT NOT NULL"
+ " );"; + " );";
protected static final int DB_VERSION = 68; protected static final int DB_VERSION = 69;
private final Context context; private final Context context;
@ -273,6 +275,21 @@ class DBHelper extends SQLiteOpenHelper {
addIndexV1Fields(db, oldVersion); addIndexV1Fields(db, oldVersion);
addIndexV1AppFields(db, oldVersion); addIndexV1AppFields(db, oldVersion);
recalculatePreferredMetadata(db, oldVersion); recalculatePreferredMetadata(db, oldVersion);
addWhatsNewAndVideo(db, oldVersion);
}
private void addWhatsNewAndVideo(SQLiteDatabase db, int oldVersion) {
if (oldVersion >= 69) {
return;
}
if (!columnExists(db, AppMetadataTable.NAME, AppMetadataTable.Cols.WHATSNEW)) {
Utils.debugLog(TAG, "Adding " + AppMetadataTable.Cols.WHATSNEW + " field to " + AppMetadataTable.NAME + " table in db.");
db.execSQL("alter table " + AppMetadataTable.NAME + " add column " + AppMetadataTable.Cols.WHATSNEW + " text;");
}
if (!columnExists(db, AppMetadataTable.NAME, AppMetadataTable.Cols.VIDEO)) {
Utils.debugLog(TAG, "Adding " + AppMetadataTable.Cols.VIDEO + " field to " + AppMetadataTable.NAME + " table in db.");
db.execSQL("alter table " + AppMetadataTable.NAME + " add column " + AppMetadataTable.Cols.VIDEO + " string;");
}
} }
private void recalculatePreferredMetadata(SQLiteDatabase db, int oldVersion) { private void recalculatePreferredMetadata(SQLiteDatabase db, int oldVersion) {

View File

@ -123,12 +123,14 @@ public interface Schema {
String SUMMARY = "summary"; String SUMMARY = "summary";
String ICON = "icon"; String ICON = "icon";
String DESCRIPTION = "description"; String DESCRIPTION = "description";
String WHATSNEW = "whatsNew";
String LICENSE = "license"; String LICENSE = "license";
String AUTHOR_NAME = "author"; String AUTHOR_NAME = "author";
String AUTHOR_EMAIL = "email"; String AUTHOR_EMAIL = "email";
String WEBSITE = "webURL"; String WEBSITE = "webURL";
String ISSUE_TRACKER = "trackerURL"; String ISSUE_TRACKER = "trackerURL";
String SOURCE_CODE = "sourceURL"; String SOURCE_CODE = "sourceURL";
String VIDEO = "video";
String CHANGELOG = "changelogURL"; String CHANGELOG = "changelogURL";
String DONATE = "donateURL"; String DONATE = "donateURL";
String BITCOIN = "bitcoinAddr"; String BITCOIN = "bitcoinAddr";
@ -184,8 +186,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_NAME, AUTHOR_EMAIL, WEBSITE, ISSUE_TRACKER, SOURCE_CODE, WHATSNEW, LICENSE, AUTHOR_NAME, AUTHOR_EMAIL, WEBSITE, ISSUE_TRACKER, SOURCE_CODE,
CHANGELOG, DONATE, BITCOIN, LITECOIN, FLATTR_ID, VIDEO, 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,
FEATURE_GRAPHIC, PROMO_GRAPHIC, TV_BANNER, PHONE_SCREENSHOTS, FEATURE_GRAPHIC, PROMO_GRAPHIC, TV_BANNER, PHONE_SCREENSHOTS,
@ -200,8 +202,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_NAME, AUTHOR_EMAIL, WEBSITE, ISSUE_TRACKER, SOURCE_CODE, WHATSNEW, LICENSE, AUTHOR_NAME, AUTHOR_EMAIL, WEBSITE, ISSUE_TRACKER, SOURCE_CODE,
CHANGELOG, DONATE, BITCOIN, LITECOIN, FLATTR_ID, VIDEO, 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,
FEATURE_GRAPHIC, PROMO_GRAPHIC, TV_BANNER, PHONE_SCREENSHOTS, FEATURE_GRAPHIC, PROMO_GRAPHIC, TV_BANNER, PHONE_SCREENSHOTS,

View File

@ -410,21 +410,17 @@ public class AppDetailsRecyclerViewAdapter
lastUpdateView.setVisibility(View.GONE); lastUpdateView.setVisibility(View.GONE);
} }
Apk suggestedApk = getSuggestedApk(); if (TextUtils.isEmpty(app.whatsNew)) {
// TODO populate whatsNew with suggestedApk.whatsNew once that exists
String whatsNew = null;
//noinspection ConstantConditions
if (suggestedApk == null || TextUtils.isEmpty(whatsNew)) {
whatsNewView.setVisibility(View.GONE); whatsNewView.setVisibility(View.GONE);
} else { } else {
//noinspection deprecation Ignore deprecation because the suggested way is only available in API 24. //noinspection deprecation Ignore deprecation because the suggested way is only available in API 24.
Locale locale = context.getResources().getConfiguration().locale; Locale locale = context.getResources().getConfiguration().locale;
StringBuilder sbWhatsNew = new StringBuilder(); StringBuilder sbWhatsNew = new StringBuilder();
sbWhatsNew.append(whatsNewView.getContext().getString(R.string.details_new_in_version, suggestedApk.versionName).toUpperCase(locale)); sbWhatsNew.append(whatsNewView.getContext().getString(R.string.details_new_in_version,
getSuggestedApk().versionName).toUpperCase(locale));
sbWhatsNew.append("\n\n"); sbWhatsNew.append("\n\n");
sbWhatsNew.append(whatsNew); sbWhatsNew.append(app.whatsNew);
whatsNewView.setText(sbWhatsNew); whatsNewView.setText(sbWhatsNew);
whatsNewView.setVisibility(View.VISIBLE); whatsNewView.setVisibility(View.VISIBLE);
} }

View File

@ -32,6 +32,7 @@ import org.fdroid.fdroid.data.Schema;
import org.fdroid.fdroid.views.apps.AppListActivity; import org.fdroid.fdroid.views.apps.AppListActivity;
import org.fdroid.fdroid.views.apps.FeatureImage; import org.fdroid.fdroid.views.apps.FeatureImage;
import java.util.Locale;
import java.util.Random; import java.util.Random;
public class CategoryController extends RecyclerView.ViewHolder implements LoaderManager.LoaderCallbacks<Cursor> { public class CategoryController extends RecyclerView.ViewHolder implements LoaderManager.LoaderCallbacks<Cursor> {
@ -108,7 +109,7 @@ public class CategoryController extends RecyclerView.ViewHolder implements Loade
private static int getCategoryResource(Context context, @NonNull String categoryName, String resourceType, boolean requiresLowerCaseId) { private static int getCategoryResource(Context context, @NonNull String categoryName, String resourceType, boolean requiresLowerCaseId) {
String suffix = categoryName.replace(" & ", "_").replace(" ", "_").replace("'", ""); String suffix = categoryName.replace(" & ", "_").replace(" ", "_").replace("'", "");
if (requiresLowerCaseId) { if (requiresLowerCaseId) {
suffix = suffix.toLowerCase(); suffix = suffix.toLowerCase(Locale.ENGLISH);
} }
return context.getResources().getIdentifier("category_" + suffix, resourceType, context.getPackageName()); return context.getResources().getIdentifier("category_" + suffix, resourceType, context.getPackageName());
} }
@ -121,7 +122,7 @@ public class CategoryController extends RecyclerView.ViewHolder implements Loade
// Seed based on the categoryName, so that each time we try to choose a colour for the same // Seed based on the categoryName, so that each time we try to choose a colour for the same
// category it will look the same for each different user, and each different session. // category it will look the same for each different user, and each different session.
Random random = new Random(categoryName.toLowerCase().hashCode()); Random random = new Random(categoryName.toLowerCase(Locale.ENGLISH).hashCode());
float[] hsv = new float[3]; float[] hsv = new float[3];
hsv[0] = random.nextFloat() * 360; hsv[0] = random.nextFloat() * 360;

View File

@ -33,6 +33,7 @@
android:layout_alignParentTop="true" android:layout_alignParentTop="true"
android:paddingBottom="8dp" android:paddingBottom="8dp"
android:paddingRight="8dp" android:paddingRight="8dp"
android:paddingEnd="8dp"
android:src="@drawable/ic_repo_app_default" /> android:src="@drawable/ic_repo_app_default" />
<LinearLayout <LinearLayout

View File

@ -251,6 +251,7 @@ public class IndexV1UpdaterTest extends FDroidProviderTest {
"upstreamVersionCode", "upstreamVersionCode",
"upstreamVersionName", "upstreamVersionName",
"video", "video",
"whatsNew",
"wearScreenshots", "wearScreenshots",
"webSite", "webSite",
}; };