From 03a8151875d8c148539d378e761df1c98473bb1e Mon Sep 17 00:00:00 2001 From: mvp76 Date: Tue, 21 Mar 2017 10:09:56 +0100 Subject: [PATCH 1/9] Added OverscrollLinearLayoutManager to fix scrolling in AppDetails2 --- .../java/org/fdroid/fdroid/AppDetails2.java | 38 +++++++- .../views/OverscrollLinearLayoutManager.java | 91 +++++++++++++++++++ 2 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/org/fdroid/fdroid/views/OverscrollLinearLayoutManager.java diff --git a/app/src/main/java/org/fdroid/fdroid/AppDetails2.java b/app/src/main/java/org/fdroid/fdroid/AppDetails2.java index 24c9701e1..a705211a4 100644 --- a/app/src/main/java/org/fdroid/fdroid/AppDetails2.java +++ b/app/src/main/java/org/fdroid/fdroid/AppDetails2.java @@ -13,6 +13,7 @@ import android.graphics.Bitmap; import android.graphics.Color; import android.net.Uri; import android.os.Bundle; +import android.support.design.widget.AppBarLayout; import android.support.design.widget.CoordinatorLayout; import android.support.v4.content.LocalBroadcastManager; import android.support.v7.app.AlertDialog; @@ -46,6 +47,7 @@ import org.fdroid.fdroid.installer.InstallerService; import org.fdroid.fdroid.net.Downloader; import org.fdroid.fdroid.net.DownloaderService; import org.fdroid.fdroid.views.AppDetailsRecyclerViewAdapter; +import org.fdroid.fdroid.views.OverscrollLinearLayoutManager; import org.fdroid.fdroid.views.ShareChooserDialog; import org.fdroid.fdroid.views.apps.FeatureImage; @@ -59,6 +61,8 @@ public class AppDetails2 extends AppCompatActivity implements ShareChooserDialog private FDroidApp fdroidApp; private App app; + private CoordinatorLayout coordinatorLayout; + private AppBarLayout appBarLayout; private RecyclerView recyclerView; private AppDetailsRecyclerViewAdapter adapter; private LocalBroadcastManager localBroadcastManager; @@ -91,10 +95,42 @@ public class AppDetails2 extends AppCompatActivity implements ShareChooserDialog localBroadcastManager = LocalBroadcastManager.getInstance(this); + coordinatorLayout = (CoordinatorLayout) findViewById(R.id.rootCoordinator); + appBarLayout = (AppBarLayout) coordinatorLayout.findViewById(R.id.app_bar); recyclerView = (RecyclerView) findViewById(R.id.rvDetails); adapter = new AppDetailsRecyclerViewAdapter(this, app, this); - LinearLayoutManager lm = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false); + OverscrollLinearLayoutManager lm = new OverscrollLinearLayoutManager(this, LinearLayoutManager.VERTICAL, false); lm.setStackFromEnd(false); + + /** The recyclerView/AppBarLayout combo has a bug that prevents a "fling" from the bottom + * to continue all the way to the top by expanding the AppBarLayout. It will instead stop + * with the app bar in a collapsed state. See here: https://code.google.com/p/android/issues/detail?id=177729 + * Not sure this is the exact issue, but it is true that while in a fling the RecyclerView will + * consume the scroll events quietly, without calling the nested scrolling mechanism. + * We fix this behavior by using an OverscrollLinearLayoutManager that will give us information + * of overscroll, i.e. when we have not consumed all of a scroll event, and use this information + * to send the scroll to the app bar layout so that it will expand itself. + */ + lm.setOnOverscrollListener(new OverscrollLinearLayoutManager.OnOverscrollListener() { + @Override + public int onOverscrollX(int overscroll) { + return 0; + } + + @Override + public int onOverscrollY(int overscroll) { + int consumed = 0; + if (overscroll < 0) { + CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) appBarLayout.getLayoutParams(); + CoordinatorLayout.Behavior behavior = lp.getBehavior(); + if (behavior != null && behavior instanceof AppBarLayout.Behavior) { + ((AppBarLayout.Behavior) behavior).onNestedScroll(coordinatorLayout, appBarLayout, recyclerView, 0, 0, 0, overscroll); + consumed = overscroll; // Consume all of it! + } + } + return consumed; + } + }); recyclerView.setLayoutManager(lm); recyclerView.setAdapter(adapter); diff --git a/app/src/main/java/org/fdroid/fdroid/views/OverscrollLinearLayoutManager.java b/app/src/main/java/org/fdroid/fdroid/views/OverscrollLinearLayoutManager.java new file mode 100644 index 000000000..2af5ac1e6 --- /dev/null +++ b/app/src/main/java/org/fdroid/fdroid/views/OverscrollLinearLayoutManager.java @@ -0,0 +1,91 @@ +package org.fdroid.fdroid.views; + +import android.content.Context; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.util.AttributeSet; + +/** + * This class is like a standard LinearLayoutManager but with an option to add an + * overscroll listener. This can be used to consume overscrolls, e.g. to draw custom + * "glows". + */ +public class OverscrollLinearLayoutManager extends LinearLayoutManager { + + /** + * A listener interface to get overscroll infromation. + */ + public interface OnOverscrollListener { + /** + * Notifies the listener that an overscroll has happened in the x direction. + * @param overscroll If negative, the recycler view has been scrolled to the "start" + * position. If positive to the "end" position. + * @return Return the amount of overscroll consumed. Returning 0 will let the + * recycler view handle this in the default way. Return "overscroll" to consume the + * whole event. + */ + int onOverscrollX(int overscroll); + + /** + * Notifies the listener that an overscroll has happened in the y direction. + * @param overscroll If negative, the recycler view has been scrolled to the "top" + * position. If positive to the "bottom" position. + * @return Return the amount of overscroll consumed. Returning 0 will let the + * recycler view handle this in the default way. Return "overscroll" to consume the + * whole event. + */ + + int onOverscrollY(int overscroll); + } + + private OnOverscrollListener overscrollListener = null; + + public OverscrollLinearLayoutManager(Context context) { + super(context); + } + + public OverscrollLinearLayoutManager(Context context, int orientation, boolean reverseLayout) { + super(context, orientation, reverseLayout); + } + + public OverscrollLinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + /** + * Set the {@link OverscrollLinearLayoutManager.OnOverscrollListener} to get information about + * when the parent recyclerview is overscrolled. + * + * @param listener Listener to add + * @see OverscrollLinearLayoutManager.OnOverscrollListener + */ + public void setOnOverscrollListener(OnOverscrollListener listener) { + overscrollListener = listener; + } + + @Override + public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) { + int consumed = super.scrollHorizontallyBy(dx, recycler, state); + int overscrollX = dx - consumed; + if (overscrollX != 0) { + if (overscrollListener != null) { + int consumedByListener = overscrollListener.onOverscrollX(overscrollX); + consumed += consumedByListener; + } + } + return consumed; + } + + @Override + public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) { + int consumed = super.scrollVerticallyBy(dy, recycler, state); + int overscrollY = dy - consumed; + if (overscrollY != 0) { + if (overscrollListener != null) { + int consumedByListener = overscrollListener.onOverscrollY(overscrollY); + consumed += consumedByListener; + } + } + return consumed; + } +} From d457a5ca0a7342a103d8ef1f04dff8f372dd1ec5 Mon Sep 17 00:00:00 2001 From: mvp76 Date: Tue, 21 Mar 2017 10:59:04 +0100 Subject: [PATCH 2/9] Drop the "Summary" from the main header, and instead just go straight to the description --- .../fdroid/views/AppDetailsRecyclerViewAdapter.java | 3 --- app/src/main/res/layout/app_details2_header.xml | 11 ----------- 2 files changed, 14 deletions(-) diff --git a/app/src/main/java/org/fdroid/fdroid/views/AppDetailsRecyclerViewAdapter.java b/app/src/main/java/org/fdroid/fdroid/views/AppDetailsRecyclerViewAdapter.java index 102c22888..3f13c2fbd 100644 --- a/app/src/main/java/org/fdroid/fdroid/views/AppDetailsRecyclerViewAdapter.java +++ b/app/src/main/java/org/fdroid/fdroid/views/AppDetailsRecyclerViewAdapter.java @@ -289,7 +289,6 @@ public class AppDetailsRecyclerViewAdapter final ImageView iconView; final TextView titleView; final TextView authorView; - final TextView summaryView; final TextView descriptionView; final TextView descriptionMoreView; final View buttonLayout; @@ -307,7 +306,6 @@ public class AppDetailsRecyclerViewAdapter iconView = (ImageView) view.findViewById(R.id.icon); titleView = (TextView) view.findViewById(R.id.title); authorView = (TextView) view.findViewById(R.id.author); - summaryView = (TextView) view.findViewById(R.id.summary); descriptionView = (TextView) view.findViewById(R.id.description); descriptionMoreView = (TextView) view.findViewById(R.id.description_more); buttonLayout = view.findViewById(R.id.button_layout); @@ -392,7 +390,6 @@ public class AppDetailsRecyclerViewAdapter } else { authorView.setVisibility(View.GONE); } - summaryView.setText(app.summary); final Spanned desc = Html.fromHtml(app.description, null, new Utils.HtmlTagHandler()); descriptionView.setMovementMethod(LinkMovementMethod.getInstance()); descriptionView.setText(trimTrailingNewlines(desc)); diff --git a/app/src/main/res/layout/app_details2_header.xml b/app/src/main/res/layout/app_details2_header.xml index 823d69ec7..aa752a60b 100755 --- a/app/src/main/res/layout/app_details2_header.xml +++ b/app/src/main/res/layout/app_details2_header.xml @@ -145,17 +145,6 @@ - - Date: Tue, 21 Mar 2017 16:53:14 +0100 Subject: [PATCH 3/9] Add last updated under the title and author Also, added styles for the title and the last updated text. --- .../views/AppDetailsRecyclerViewAdapter.java | 12 +++ .../main/res/layout/app_details2_header.xml | 92 ++++++++++--------- app/src/main/res/values/strings.xml | 6 ++ app/src/main/res/values/styles_detail.xml | 7 ++ 4 files changed, 76 insertions(+), 41 deletions(-) diff --git a/app/src/main/java/org/fdroid/fdroid/views/AppDetailsRecyclerViewAdapter.java b/app/src/main/java/org/fdroid/fdroid/views/AppDetailsRecyclerViewAdapter.java index 3f13c2fbd..5f750a61c 100644 --- a/app/src/main/java/org/fdroid/fdroid/views/AppDetailsRecyclerViewAdapter.java +++ b/app/src/main/java/org/fdroid/fdroid/views/AppDetailsRecyclerViewAdapter.java @@ -50,7 +50,9 @@ import org.fdroid.fdroid.privileged.views.AppSecurityPermissions; import java.text.NumberFormat; import java.util.ArrayList; +import java.util.Calendar; import java.util.List; +import java.util.concurrent.TimeUnit; public class AppDetailsRecyclerViewAdapter extends RecyclerView.Adapter { @@ -289,6 +291,7 @@ public class AppDetailsRecyclerViewAdapter final ImageView iconView; final TextView titleView; final TextView authorView; + final TextView lastUpdateView; final TextView descriptionView; final TextView descriptionMoreView; final View buttonLayout; @@ -306,6 +309,7 @@ public class AppDetailsRecyclerViewAdapter iconView = (ImageView) view.findViewById(R.id.icon); titleView = (TextView) view.findViewById(R.id.title); authorView = (TextView) view.findViewById(R.id.author); + lastUpdateView = (TextView) view.findViewById(R.id.text_last_update); descriptionView = (TextView) view.findViewById(R.id.description); descriptionMoreView = (TextView) view.findViewById(R.id.description_more); buttonLayout = view.findViewById(R.id.button_layout); @@ -390,6 +394,14 @@ public class AppDetailsRecyclerViewAdapter } else { authorView.setVisibility(View.GONE); } + if (app.lastUpdated != null) { + long msDiff = Calendar.getInstance().getTimeInMillis() - app.lastUpdated.getTime(); + int daysDiff = (int) TimeUnit.MILLISECONDS.toDays(msDiff); + lastUpdateView.setText(lastUpdateView.getContext().getResources().getQuantityString(R.plurals.details_last_update_days, daysDiff, daysDiff)); + lastUpdateView.setVisibility(View.VISIBLE); + } else { + lastUpdateView.setVisibility(View.GONE); + } final Spanned desc = Html.fromHtml(app.description, null, new Utils.HtmlTagHandler()); descriptionView.setMovementMethod(LinkMovementMethod.getInstance()); descriptionView.setText(trimTrailingNewlines(desc)); diff --git a/app/src/main/res/layout/app_details2_header.xml b/app/src/main/res/layout/app_details2_header.xml index aa752a60b..5b8360680 100755 --- a/app/src/main/res/layout/app_details2_header.xml +++ b/app/src/main/res/layout/app_details2_header.xml @@ -12,8 +12,8 @@ + android:orientation="vertical" + android:padding="8dp"> - + android:orientation="vertical"> - + + + + + + + + android:layout_alignParentLeft="true" + android:layout_alignParentRight="true" + android:layout_alignParentStart="true" + android:layout_below="@id/icon"> + + android:layout_centerVertical="true" + android:src="@android:drawable/ic_menu_close_clear_cancel" /> + + android:textAppearance="@style/TextAppearance.AppCompat.Small" /> + + android:textAppearance="@style/TextAppearance.AppCompat.Small" /> + + android:layout_below="@id/progress_label" + android:layout_toLeftOf="@id/progress_cancel" + android:layout_toStartOf="@id/progress_cancel" /> @@ -137,7 +149,6 @@ android:layout_marginLeft="8dp" android:layout_marginStart="8dp" android:layout_weight="1" - android:maxLines="1" android:ellipsize="marquee" android:padding="12dp" tools:text="THIS IS 2" /> @@ -150,19 +161,18 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="16dp" - android:textAppearance="@style/TextAppearance.AppCompat.Body1" - tools:text="This is the app description of this awezome app. It can be several lines long, but will be truncated at just a few if it is. A 'read more' button will appear so that you can expand the view and view the full text, if you wish. Yes, it will be blue and beautiful." android:scrollbars="none" - /> + android:textAppearance="@style/TextAppearance.AppCompat.Body1" + tools:text="This is the app description of this awezome app. It can be several lines long, but will be truncated at just a few if it is. A 'read more' button will appear so that you can expand the view and view the full text, if you wish. Yes, it will be blue and beautiful." /> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 77a11336d..8a433336a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -473,4 +473,10 @@ View the single one app in the %2$s category View all %1$d apps from the %2$s category + + + Updated today + Updated %1$s day ago + Updated %1$s days ago + diff --git a/app/src/main/res/values/styles_detail.xml b/app/src/main/res/values/styles_detail.xml index 19fa91439..7bca4da09 100644 --- a/app/src/main/res/values/styles_detail.xml +++ b/app/src/main/res/values/styles_detail.xml @@ -34,4 +34,11 @@ @color/fdroid_blue + + + + \ No newline at end of file From 2d27ba40862096a15aaad63fa3a8f2419eb34ece Mon Sep 17 00:00:00 2001 From: mvp76 Date: Tue, 21 Mar 2017 16:55:39 +0100 Subject: [PATCH 4/9] Display anti-features Issue #878. --- .../views/AppDetailsRecyclerViewAdapter.java | 59 ++++++++++++++++++- .../res/drawable/ic_warning_black_24dp.xml | 9 +++ .../main/res/layout/app_details2_header.xml | 32 ++++++++++ app/src/main/res/values/strings.xml | 2 + app/src/main/res/values/styles_detail.xml | 4 ++ 5 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 app/src/main/res/drawable/ic_warning_black_24dp.xml diff --git a/app/src/main/java/org/fdroid/fdroid/views/AppDetailsRecyclerViewAdapter.java b/app/src/main/java/org/fdroid/fdroid/views/AppDetailsRecyclerViewAdapter.java index 5f750a61c..a9b6ce80c 100644 --- a/app/src/main/java/org/fdroid/fdroid/views/AppDetailsRecyclerViewAdapter.java +++ b/app/src/main/java/org/fdroid/fdroid/views/AppDetailsRecyclerViewAdapter.java @@ -294,6 +294,9 @@ public class AppDetailsRecyclerViewAdapter final TextView lastUpdateView; final TextView descriptionView; final TextView descriptionMoreView; + final TextView antiFeaturesLabelView; + final TextView antiFeaturesView; + final View antiFeaturesWarningView; final View buttonLayout; final Button buttonPrimaryView; final Button buttonSecondaryView; @@ -303,6 +306,7 @@ public class AppDetailsRecyclerViewAdapter final TextView progressPercent; final View progressCancel; final DisplayImageOptions displayImageOptions; + boolean descriptionIsExpanded; HeaderViewHolder(View view) { super(view); @@ -312,6 +316,9 @@ public class AppDetailsRecyclerViewAdapter lastUpdateView = (TextView) view.findViewById(R.id.text_last_update); descriptionView = (TextView) view.findViewById(R.id.description); descriptionMoreView = (TextView) view.findViewById(R.id.description_more); + antiFeaturesLabelView = (TextView) view.findViewById(R.id.label_anti_features); + antiFeaturesView = (TextView) view.findViewById(R.id.text_anti_features); + antiFeaturesWarningView = view.findViewById(R.id.anti_features_warning); buttonLayout = view.findViewById(R.id.button_layout); buttonPrimaryView = (Button) view.findViewById(R.id.primaryButtonView); buttonSecondaryView = (Button) view.findViewById(R.id.secondaryButtonView); @@ -341,10 +348,13 @@ public class AppDetailsRecyclerViewAdapter if (TextViewCompat.getMaxLines(descriptionView) != MAX_LINES) { descriptionView.setMaxLines(MAX_LINES); descriptionMoreView.setText(R.string.more); + descriptionIsExpanded = false; } else { descriptionView.setMaxLines(Integer.MAX_VALUE); descriptionMoreView.setText(R.string.less); + descriptionIsExpanded = true; } + updateAntiFeaturesWarning(); } }); // Set ALL caps (in a way compatible with SDK 10) @@ -421,13 +431,27 @@ public class AppDetailsRecyclerViewAdapter descriptionView.post(new Runnable() { @Override public void run() { - if (descriptionView.getLineCount() <= HeaderViewHolder.MAX_LINES) { + if (descriptionView.getLineCount() <= HeaderViewHolder.MAX_LINES && app.antiFeatures == null) { descriptionMoreView.setVisibility(View.GONE); } else { descriptionMoreView.setVisibility(View.VISIBLE); } } }); + if (app.antiFeatures != null) { + StringBuilder sb = new StringBuilder(); + for (String af : app.antiFeatures) { + String afdesc = descAntiFeature(af); + sb.append("\t• ").append(afdesc).append('\n'); + } + if (sb.length() > 0) { + sb.setLength(sb.length() - 1); + antiFeaturesView.setText(sb.toString()); + } else { + antiFeaturesView.setVisibility(View.GONE); + } + } + updateAntiFeaturesWarning(); buttonSecondaryView.setText(R.string.menu_uninstall); buttonSecondaryView.setVisibility(app.isInstalled() ? View.VISIBLE : View.INVISIBLE); buttonSecondaryView.setOnClickListener(onUnInstallClickListener); @@ -473,6 +497,39 @@ public class AppDetailsRecyclerViewAdapter }); } + + private void updateAntiFeaturesWarning() { + if (app.antiFeatures == null || TextUtils.isEmpty(antiFeaturesView.getText())) { + antiFeaturesLabelView.setVisibility(View.GONE); + antiFeaturesView.setVisibility(View.GONE); + antiFeaturesWarningView.setVisibility(View.GONE); + } else { + antiFeaturesLabelView.setVisibility(descriptionIsExpanded ? View.VISIBLE : View.GONE); + antiFeaturesView.setVisibility(descriptionIsExpanded ? View.VISIBLE : View.GONE); + antiFeaturesWarningView.setVisibility(descriptionIsExpanded ? View.GONE : View.VISIBLE); + } + } + + private String descAntiFeature(String af) { + switch (af) { + case "Ads": + return itemView.getContext().getString(R.string.antiadslist); + case "Tracking": + return itemView.getContext().getString(R.string.antitracklist); + case "NonFreeNet": + return itemView.getContext().getString(R.string.antinonfreenetlist); + case "NonFreeAdd": + return itemView.getContext().getString(R.string.antinonfreeadlist); + case "NonFreeDep": + return itemView.getContext().getString(R.string.antinonfreedeplist); + case "UpstreamNonFree": + return itemView.getContext().getString(R.string.antiupstreamnonfreelist); + case "NonFreeAssets": + return itemView.getContext().getString(R.string.antinonfreeassetslist); + default: + return af; + } + } } @Override diff --git a/app/src/main/res/drawable/ic_warning_black_24dp.xml b/app/src/main/res/drawable/ic_warning_black_24dp.xml new file mode 100644 index 000000000..b3a9e036b --- /dev/null +++ b/app/src/main/res/drawable/ic_warning_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/app_details2_header.xml b/app/src/main/res/layout/app_details2_header.xml index 5b8360680..c14ea082c 100755 --- a/app/src/main/res/layout/app_details2_header.xml +++ b/app/src/main/res/layout/app_details2_header.xml @@ -165,6 +165,26 @@ android:textAppearance="@style/TextAppearance.AppCompat.Body1" tools:text="This is the app description of this awezome app. It can be several lines long, but will be truncated at just a few if it is. A 'read more' button will appear so that you can expand the view and view the full text, if you wish. Yes, it will be blue and beautiful." /> + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8a433336a..ed72fb596 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -170,6 +170,8 @@ Version %s installed Not installed + This app has features you may not like. + Anti-features This app contains advertising This app tracks and reports your activity This app promotes non-free add-ons diff --git a/app/src/main/res/values/styles_detail.xml b/app/src/main/res/values/styles_detail.xml index 7bca4da09..2a6b07c86 100644 --- a/app/src/main/res/values/styles_detail.xml +++ b/app/src/main/res/values/styles_detail.xml @@ -41,4 +41,8 @@ + + \ No newline at end of file From 622a1245dc74553d482950e3b54cc3cad7b1c947 Mon Sep 17 00:00:00 2001 From: mvp76 Date: Tue, 21 Mar 2017 17:55:49 +0100 Subject: [PATCH 5/9] Move "what's new" from its own section to the header section Also, add some styling and placeholder text (randomly set or not set) --- .../main/java/org/fdroid/fdroid/data/Apk.java | 13 +++++ .../views/AppDetailsRecyclerViewAdapter.java | 54 +++++++++---------- .../main/res/layout/app_details2_header.xml | 32 ++++++++++- .../main/res/layout/app_details2_whatsnew.xml | 8 --- app/src/main/res/values/strings.xml | 1 + 5 files changed, 70 insertions(+), 38 deletions(-) delete mode 100755 app/src/main/res/layout/app_details2_whatsnew.xml diff --git a/app/src/main/java/org/fdroid/fdroid/data/Apk.java b/app/src/main/java/org/fdroid/fdroid/data/Apk.java index d1f75ff89..e08078756 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/Apk.java +++ b/app/src/main/java/org/fdroid/fdroid/data/Apk.java @@ -9,6 +9,7 @@ import android.os.Parcel; import android.os.Parcelable; import android.support.annotation.NonNull; +import org.fdroid.fdroid.BuildConfig; import org.fdroid.fdroid.RepoXMLHandler; import org.fdroid.fdroid.Utils; import org.fdroid.fdroid.data.Schema.ApkTable.Cols; @@ -74,6 +75,11 @@ public class Apk extends ValueObject implements Comparable, Parcelable { public String repoAddress; public String[] incompatibleReasons; + /** + * A descriptive text for what has changed in the latest version. + */ + public String whatsNew; + public String[] antiFeatures; /** @@ -211,6 +217,13 @@ public class Apk extends ValueObject implements Comparable, Parcelable { break; } } + + // For now, just populate "what's new" with placeholder (or leave blank) + if (BuildConfig.DEBUG) { + if (Math.random() > 0.5) { + whatsNew = "This section will contain the 'what's new' information for the apk.\n\n\t• Bug fixes."; + } + } } private void checkRepoAddress() { diff --git a/app/src/main/java/org/fdroid/fdroid/views/AppDetailsRecyclerViewAdapter.java b/app/src/main/java/org/fdroid/fdroid/views/AppDetailsRecyclerViewAdapter.java index a9b6ce80c..3fb2e1a9c 100644 --- a/app/src/main/java/org/fdroid/fdroid/views/AppDetailsRecyclerViewAdapter.java +++ b/app/src/main/java/org/fdroid/fdroid/views/AppDetailsRecyclerViewAdapter.java @@ -52,6 +52,7 @@ import java.text.NumberFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.List; +import java.util.Locale; import java.util.concurrent.TimeUnit; public class AppDetailsRecyclerViewAdapter @@ -83,12 +84,11 @@ public class AppDetailsRecyclerViewAdapter private static final int VIEWTYPE_HEADER = 0; private static final int VIEWTYPE_SCREENSHOTS = 1; - private static final int VIEWTYPE_WHATS_NEW = 2; - private static final int VIEWTYPE_DONATE = 3; - private static final int VIEWTYPE_LINKS = 4; - private static final int VIEWTYPE_PERMISSIONS = 5; - private static final int VIEWTYPE_VERSIONS = 6; - private static final int VIEWTYPE_VERSION = 7; + private static final int VIEWTYPE_DONATE = 2; + private static final int VIEWTYPE_LINKS = 3; + private static final int VIEWTYPE_PERMISSIONS = 4; + private static final int VIEWTYPE_VERSIONS = 5; + private static final int VIEWTYPE_VERSION = 6; private final Context context; @NonNull @@ -127,7 +127,6 @@ public class AppDetailsRecyclerViewAdapter } addItem(VIEWTYPE_HEADER); addItem(VIEWTYPE_SCREENSHOTS); - addItem(VIEWTYPE_WHATS_NEW); addItem(VIEWTYPE_DONATE); addItem(VIEWTYPE_LINKS); addItem(VIEWTYPE_PERMISSIONS); @@ -169,6 +168,12 @@ public class AppDetailsRecyclerViewAdapter private boolean shouldShowPermissions() { // Figure out if we should show permissions section + Apk curApk = getSuggestedApk(); + final boolean curApkCompatible = curApk != null && curApk.compatible; + return versions.size() > 0 && (curApkCompatible || Preferences.get().showIncompatibleVersions()); + } + + private Apk getSuggestedApk() { Apk curApk = null; for (int i = 0; i < versions.size(); i++) { final Apk apk = versions.get(i); @@ -177,8 +182,7 @@ public class AppDetailsRecyclerViewAdapter break; } } - final boolean curApkCompatible = curApk != null && curApk.compatible; - return versions.size() > 0 && (curApkCompatible || Preferences.get().showIncompatibleVersions()); + return curApk; } private boolean shouldShowDonate() { @@ -208,9 +212,6 @@ public class AppDetailsRecyclerViewAdapter case VIEWTYPE_SCREENSHOTS: View screenshots = inflater.inflate(R.layout.app_details2_screenshots, parent, false); return new ScreenShotsViewHolder(screenshots); - case VIEWTYPE_WHATS_NEW: - View whatsNew = inflater.inflate(R.layout.app_details2_whatsnew, parent, false); - return new WhatsNewViewHolder(whatsNew); case VIEWTYPE_DONATE: View donate = inflater.inflate(R.layout.app_details2_donate, parent, false); return new DonateViewHolder(donate); @@ -242,9 +243,6 @@ public class AppDetailsRecyclerViewAdapter case VIEWTYPE_SCREENSHOTS: ((ScreenShotsViewHolder) holder).bindModel(); break; - case VIEWTYPE_WHATS_NEW: - ((WhatsNewViewHolder) holder).bindModel(); - break; case VIEWTYPE_DONATE: ((DonateViewHolder) holder).bindModel(); break; @@ -292,6 +290,7 @@ public class AppDetailsRecyclerViewAdapter final TextView titleView; final TextView authorView; final TextView lastUpdateView; + final TextView whatsNewView; final TextView descriptionView; final TextView descriptionMoreView; final TextView antiFeaturesLabelView; @@ -314,6 +313,7 @@ public class AppDetailsRecyclerViewAdapter titleView = (TextView) view.findViewById(R.id.title); authorView = (TextView) view.findViewById(R.id.author); lastUpdateView = (TextView) view.findViewById(R.id.text_last_update); + whatsNewView = (TextView) view.findViewById(R.id.whats_new); descriptionView = (TextView) view.findViewById(R.id.description); descriptionMoreView = (TextView) view.findViewById(R.id.description_more); antiFeaturesLabelView = (TextView) view.findViewById(R.id.label_anti_features); @@ -412,6 +412,17 @@ public class AppDetailsRecyclerViewAdapter } else { lastUpdateView.setVisibility(View.GONE); } + Apk suggestedApk = getSuggestedApk(); + if (suggestedApk == null || TextUtils.isEmpty(suggestedApk.whatsNew)) { + whatsNewView.setVisibility(View.GONE); + } else { + StringBuilder sbWhatsNew = new StringBuilder(); + sbWhatsNew.append(whatsNewView.getContext().getString(R.string.details_new_in_version, suggestedApk.versionName).toUpperCase(Locale.getDefault())); + sbWhatsNew.append("\n\n"); + sbWhatsNew.append(suggestedApk.whatsNew); + whatsNewView.setText(sbWhatsNew); + whatsNewView.setVisibility(View.VISIBLE); + } final Spanned desc = Html.fromHtml(app.description, null, new Utils.HtmlTagHandler()); descriptionView.setMovementMethod(LinkMovementMethod.getInstance()); descriptionView.setText(trimTrailingNewlines(desc)); @@ -569,19 +580,6 @@ public class AppDetailsRecyclerViewAdapter } } - private class WhatsNewViewHolder extends RecyclerView.ViewHolder { - final TextView textView; - - WhatsNewViewHolder(View view) { - super(view); - textView = (TextView) view.findViewById(R.id.text); - } - - public void bindModel() { - textView.setText("WHATS NEW GOES HERE"); - } - } - private class DonateViewHolder extends RecyclerView.ViewHolder { final TextView donateHeading; final GridLayout donationOptionsLayout; diff --git a/app/src/main/res/layout/app_details2_header.xml b/app/src/main/res/layout/app_details2_header.xml index c14ea082c..afe1dfdb2 100755 --- a/app/src/main/res/layout/app_details2_header.xml +++ b/app/src/main/res/layout/app_details2_header.xml @@ -13,12 +13,15 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" - android:padding="8dp"> + android:paddingBottom="8dp" + > + + @@ -190,6 +214,8 @@ style="@style/DetailsMoreButtonStyle" android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_marginStart="8dp" + android:layout_marginEnd="8dp" android:gravity="right|end" android:text="@string/more" android:textAppearance="@style/TextAppearance.AppCompat.Body1" @@ -200,6 +226,8 @@ style="@style/DetailsAntiFeaturesWarningStyle" android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_marginStart="8dp" + android:layout_marginEnd="8dp" android:textAppearance="@style/TextAppearance.AppCompat.Body1" android:drawableStart="@drawable/ic_warning_black_24dp" android:drawableLeft="@drawable/ic_warning_black_24dp" diff --git a/app/src/main/res/layout/app_details2_whatsnew.xml b/app/src/main/res/layout/app_details2_whatsnew.xml deleted file mode 100755 index 3498f984e..000000000 --- a/app/src/main/res/layout/app_details2_whatsnew.xml +++ /dev/null @@ -1,8 +0,0 @@ - - \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ed72fb596..8867b0f75 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -169,6 +169,7 @@ Version %s installed Not installed + New in version %s This app has features you may not like. Anti-features From c059b83f2bd54bd670e9248a16e02bf17af004ca Mon Sep 17 00:00:00 2001 From: mvp76 Date: Thu, 23 Mar 2017 09:37:33 +0100 Subject: [PATCH 6/9] Fix merge conflict --- app/src/main/res/layout/app_details2_header.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/src/main/res/layout/app_details2_header.xml b/app/src/main/res/layout/app_details2_header.xml index afe1dfdb2..f15c02a07 100755 --- a/app/src/main/res/layout/app_details2_header.xml +++ b/app/src/main/res/layout/app_details2_header.xml @@ -123,8 +123,6 @@ android:id="@+id/button_layout" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_alignParentEnd="true" - android:layout_alignParentRight="true" android:layout_below="@id/icon" android:layout_toEndOf="@id/icon" android:layout_toRightOf="@id/icon" From 80e0f84816a917e2a701fee749697ce974078044 Mon Sep 17 00:00:00 2001 From: Peter Serwylo Date: Wed, 22 Mar 2017 10:30:20 +1100 Subject: [PATCH 7/9] Use correct locale when uppercasing text. Locale.getDefault() returns the default for the current JVM (or whatever runtime Android calls it these days). By asking the configuration, we will get the Locale that the user has selected from within the F-Droid preferences. --- .../fdroid/fdroid/views/AppDetailsRecyclerViewAdapter.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/fdroid/fdroid/views/AppDetailsRecyclerViewAdapter.java b/app/src/main/java/org/fdroid/fdroid/views/AppDetailsRecyclerViewAdapter.java index 3fb2e1a9c..e64e3a6a9 100644 --- a/app/src/main/java/org/fdroid/fdroid/views/AppDetailsRecyclerViewAdapter.java +++ b/app/src/main/java/org/fdroid/fdroid/views/AppDetailsRecyclerViewAdapter.java @@ -416,8 +416,11 @@ public class AppDetailsRecyclerViewAdapter if (suggestedApk == null || TextUtils.isEmpty(suggestedApk.whatsNew)) { whatsNewView.setVisibility(View.GONE); } else { + //noinspection deprecation Ignore deprecation because the suggested way is only available in API 24. + Locale locale = context.getResources().getConfiguration().locale; + StringBuilder sbWhatsNew = new StringBuilder(); - sbWhatsNew.append(whatsNewView.getContext().getString(R.string.details_new_in_version, suggestedApk.versionName).toUpperCase(Locale.getDefault())); + sbWhatsNew.append(whatsNewView.getContext().getString(R.string.details_new_in_version, suggestedApk.versionName).toUpperCase(locale)); sbWhatsNew.append("\n\n"); sbWhatsNew.append(suggestedApk.whatsNew); whatsNewView.setText(sbWhatsNew); From 31fdcba9450e422b7d3629211320bbf1ad0338e5 Mon Sep 17 00:00:00 2001 From: Peter Serwylo Date: Wed, 22 Mar 2017 10:31:09 +1100 Subject: [PATCH 8/9] Remove xmlns from inner element --- app/src/main/res/layout/app_details2_header.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/layout/app_details2_header.xml b/app/src/main/res/layout/app_details2_header.xml index f15c02a07..2c14167d6 100755 --- a/app/src/main/res/layout/app_details2_header.xml +++ b/app/src/main/res/layout/app_details2_header.xml @@ -157,7 +157,7 @@ - Date: Thu, 23 Mar 2017 11:12:59 +0100 Subject: [PATCH 9/9] Fix test after adapter change --- .../java/org/fdroid/fdroid/views/AppDetailsAdapterTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/test/java/org/fdroid/fdroid/views/AppDetailsAdapterTest.java b/app/src/test/java/org/fdroid/fdroid/views/AppDetailsAdapterTest.java index 70ac2834f..a075e8783 100644 --- a/app/src/test/java/org/fdroid/fdroid/views/AppDetailsAdapterTest.java +++ b/app/src/test/java/org/fdroid/fdroid/views/AppDetailsAdapterTest.java @@ -47,7 +47,7 @@ public class AppDetailsAdapterTest extends FDroidProviderTest { AppDetailsRecyclerViewAdapter adapter = new AppDetailsRecyclerViewAdapter(context, app, dummyCallbacks); populateViewHolders(adapter); - assertEquals(5, adapter.getItemCount()); + assertEquals(4, adapter.getItemCount()); }