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 6c8b24cb8..85f9e2dc1 100644 --- a/app/src/main/java/org/fdroid/fdroid/views/AppDetailsRecyclerViewAdapter.java +++ b/app/src/main/java/org/fdroid/fdroid/views/AppDetailsRecyclerViewAdapter.java @@ -53,6 +53,7 @@ import org.fdroid.fdroid.data.RepoProvider; import org.fdroid.fdroid.installer.Installer; import org.fdroid.fdroid.privileged.views.AppDiff; import org.fdroid.fdroid.privileged.views.AppSecurityPermissions; +import org.fdroid.fdroid.views.appdetails.AntiFeaturesListingView; import org.fdroid.fdroid.views.main.MainActivity; import java.io.File; @@ -379,9 +380,10 @@ public class AppDetailsRecyclerViewAdapter final TextView whatsNewView; final TextView descriptionView; final TextView descriptionMoreView; + final View antiFeaturesSectionView; final TextView antiFeaturesLabelView; - final TextView antiFeaturesView; final View antiFeaturesWarningView; + final AntiFeaturesListingView antiFeaturesListingView; final View buttonLayout; final Button buttonPrimaryView; final Button buttonSecondaryView; @@ -402,9 +404,10 @@ public class AppDetailsRecyclerViewAdapter whatsNewView = (TextView) view.findViewById(R.id.whats_new); descriptionView = (TextView) view.findViewById(R.id.description); descriptionMoreView = (TextView) view.findViewById(R.id.description_more); + antiFeaturesSectionView = view.findViewById(R.id.anti_features_section); 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); + antiFeaturesListingView = view.findViewById(R.id.anti_features_full_listing); buttonLayout = view.findViewById(R.id.button_layout); buttonPrimaryView = (Button) view.findViewById(R.id.primaryButtonView); buttonSecondaryView = (Button) view.findViewById(R.id.secondaryButtonView); @@ -545,23 +548,10 @@ public class AppDetailsRecyclerViewAdapter } } }); - if (app.antiFeatures != null && app.antiFeatures.length > 0) { - StringBuilder sb = new StringBuilder(); - for (String af : app.antiFeatures) { - String afdesc = descAntiFeature(af); - sb.append("

") - .append(afdesc) - .append("

"); - } - antiFeaturesView.setText(trimTrailingNewlines(Html.fromHtml(sb.toString()))); - antiFeaturesView.setMovementMethod(LinkMovementMethod.getInstance()); - } else { - antiFeaturesView.setVisibility(View.GONE); - } + antiFeaturesListingView.setApp(app); updateAntiFeaturesWarning(); + buttonPrimaryView.setText(R.string.menu_install); buttonPrimaryView.setVisibility(versions.isEmpty() ? View.GONE : View.VISIBLE); buttonSecondaryView.setText(R.string.menu_uninstall); @@ -664,41 +654,16 @@ public class AppDetailsRecyclerViewAdapter } private void updateAntiFeaturesWarning() { - if (app.antiFeatures == null || TextUtils.isEmpty(antiFeaturesView.getText())) { - antiFeaturesLabelView.setVisibility(View.GONE); - antiFeaturesView.setVisibility(View.GONE); + if (app.antiFeatures == null || app.antiFeatures.length == 0) { + antiFeaturesSectionView.setVisibility(View.GONE); + } else if (descriptionIsExpanded) { antiFeaturesWarningView.setVisibility(View.GONE); + antiFeaturesLabelView.setVisibility(View.VISIBLE); + antiFeaturesListingView.setVisibility(View.VISIBLE); } 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); - case "DisabledAlgorithm": - return itemView.getContext().getString(R.string.antidisabledalgorithmlist); - case "KnownVuln": - return itemView.getContext().getString(R.string.antiknownvulnlist); - case "NoSourceSince": - return itemView.getContext().getString(R.string.antinosourcesince); - default: - return af; + antiFeaturesWarningView.setVisibility(View.VISIBLE); + antiFeaturesLabelView.setVisibility(View.GONE); + antiFeaturesListingView.setVisibility(View.GONE); } } } diff --git a/app/src/main/java/org/fdroid/fdroid/views/appdetails/AntiFeaturesListingView.java b/app/src/main/java/org/fdroid/fdroid/views/appdetails/AntiFeaturesListingView.java new file mode 100644 index 000000000..2da361829 --- /dev/null +++ b/app/src/main/java/org/fdroid/fdroid/views/appdetails/AntiFeaturesListingView.java @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2019 Michael Pöhn + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 3 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package org.fdroid.fdroid.views.appdetails; + +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.Build; +import android.support.annotation.DrawableRes; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.content.ContextCompat; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import org.fdroid.fdroid.R; +import org.fdroid.fdroid.data.App; + +public class AntiFeaturesListingView extends RecyclerView { + + public AntiFeaturesListingView(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + public void setApp(final App app) { + + setHasFixedSize(true); + + LinearLayoutManager layoutManager = new LinearLayoutManager(getContext()); + setLayoutManager(layoutManager); + + swapAdapter(new RecyclerView.Adapter() { + + @NonNull + @Override + public AntiFeatureItemViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + LayoutInflater inflater = LayoutInflater.from(parent.getContext()); + View view = inflater.inflate(R.layout.listitem_antifeaturelisting, null); + return new AntiFeatureItemViewHolder(view); + } + + @Override + public void onBindViewHolder(@NonNull AntiFeatureItemViewHolder holder, int position) { + final String antiFeatureName = app.antiFeatures[position]; + holder.antiFeatureIcon.setBackgroundDrawable( + ContextCompat.getDrawable(getContext(), antiFeatureIcon(antiFeatureName))); + holder.antiFeatureText.setText( + getAntiFeatureDescriptionText(holder.antiFeatureText.getContext(), antiFeatureName)); + holder.entireView.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + Intent i = new Intent(Intent.ACTION_VIEW); + if (Build.VERSION.SDK_INT >= 21) { + i.setFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT); + } else { + i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); + } + i.setData(Uri.parse("https://f-droid.org/docs/Anti-Features#" + antiFeatureName)); + getContext().startActivity(i); + } + }); + } + + @Override + public int getItemCount() { + return app == null || app.antiFeatures == null ? 0 : app.antiFeatures.length; + } + }, false); + } + + class AntiFeatureItemViewHolder extends RecyclerView.ViewHolder { + + private final View entireView; + private final View antiFeatureIcon; + private final TextView antiFeatureText; + + AntiFeatureItemViewHolder(View itemView) { + super(itemView); + entireView = itemView; + antiFeatureIcon = itemView.findViewById(R.id.anti_feature_icon); + antiFeatureText = itemView.findViewById(R.id.anti_feature_text); + } + + } + + public static String getAntiFeatureDescriptionText(Context context, String antiFeatureName) { + switch (antiFeatureName) { + case "Ads": + return context.getString(R.string.antiadslist); + case "Tracking": + return context.getString(R.string.antitracklist); + case "NonFreeNet": + return context.getString(R.string.antinonfreenetlist); + case "NonFreeAdd": + return context.getString(R.string.antinonfreeadlist); + case "NonFreeDep": + return context.getString(R.string.antinonfreedeplist); + case "UpstreamNonFree": + return context.getString(R.string.antiupstreamnonfreelist); + case "NonFreeAssets": + return context.getString(R.string.antinonfreeassetslist); + case "DisabledAlgorithm": + return context.getString(R.string.antidisabledalgorithmlist); + case "KnownVuln": + return context.getString(R.string.antiknownvulnlist); + case "NoSourceSince": + return context.getString(R.string.antinosourcesince); + default: + return antiFeatureName; + } + } + + public static @DrawableRes int antiFeatureIcon(String antiFeatureName) { + switch (antiFeatureName) { + case "Ads": + return R.drawable.ic_antifeature_ads; + case "Tracking": + return R.drawable.ic_antifeature_tracking; + case "NonFreeNet": + return R.drawable.ic_antifeature_nonfreenet; + case "NonFreeAdd": + return R.drawable.ic_antifeature_nonfreeadd; + case "NonFreeDep": + return R.drawable.ic_antifeature_nonfreedep; + case "UpstreamNonFree": + return R.drawable.ic_antifeature_upstreamnonfree; + case "NonFreeAssets": + return R.drawable.ic_antifeature_nonfreeassets; + case "DisabledAlgorithm": + return R.drawable.ic_antifeature_disabledalgorithm; + case "KnownVuln": + return R.drawable.ic_antifeature_knownvuln; + case "NoSourceSince": + return R.drawable.ic_antifeature_nosourcesince; + default: + return R.drawable.ic_cancel; + } + } +} diff --git a/app/src/main/res/drawable/ic_antifeature_ads.xml b/app/src/main/res/drawable/ic_antifeature_ads.xml new file mode 100644 index 000000000..0e64a7776 --- /dev/null +++ b/app/src/main/res/drawable/ic_antifeature_ads.xml @@ -0,0 +1,5 @@ + + + + diff --git a/app/src/main/res/drawable/ic_antifeature_disabledalgorithm.xml b/app/src/main/res/drawable/ic_antifeature_disabledalgorithm.xml new file mode 100644 index 000000000..c44d42931 --- /dev/null +++ b/app/src/main/res/drawable/ic_antifeature_disabledalgorithm.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_antifeature_knownvuln.xml b/app/src/main/res/drawable/ic_antifeature_knownvuln.xml new file mode 100644 index 000000000..2e52f1d7d --- /dev/null +++ b/app/src/main/res/drawable/ic_antifeature_knownvuln.xml @@ -0,0 +1,5 @@ + + + + diff --git a/app/src/main/res/drawable/ic_antifeature_nonfreeadd.xml b/app/src/main/res/drawable/ic_antifeature_nonfreeadd.xml new file mode 100644 index 000000000..880b5efbe --- /dev/null +++ b/app/src/main/res/drawable/ic_antifeature_nonfreeadd.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_antifeature_nonfreeassets.xml b/app/src/main/res/drawable/ic_antifeature_nonfreeassets.xml new file mode 100644 index 000000000..f95fb2689 --- /dev/null +++ b/app/src/main/res/drawable/ic_antifeature_nonfreeassets.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_antifeature_nonfreedep.xml b/app/src/main/res/drawable/ic_antifeature_nonfreedep.xml new file mode 100644 index 000000000..5ae690aa5 --- /dev/null +++ b/app/src/main/res/drawable/ic_antifeature_nonfreedep.xml @@ -0,0 +1,8 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_antifeature_nonfreenet.xml b/app/src/main/res/drawable/ic_antifeature_nonfreenet.xml new file mode 100644 index 000000000..98d48a4e4 --- /dev/null +++ b/app/src/main/res/drawable/ic_antifeature_nonfreenet.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_antifeature_nosourcesince.xml b/app/src/main/res/drawable/ic_antifeature_nosourcesince.xml new file mode 100644 index 000000000..1637ee001 --- /dev/null +++ b/app/src/main/res/drawable/ic_antifeature_nosourcesince.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_antifeature_tracking.xml b/app/src/main/res/drawable/ic_antifeature_tracking.xml new file mode 100644 index 000000000..f5c38d6b0 --- /dev/null +++ b/app/src/main/res/drawable/ic_antifeature_tracking.xml @@ -0,0 +1,8 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_antifeature_upstreamnonfree.xml b/app/src/main/res/drawable/ic_antifeature_upstreamnonfree.xml new file mode 100644 index 000000000..56cfb73d6 --- /dev/null +++ b/app/src/main/res/drawable/ic_antifeature_upstreamnonfree.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/app/src/main/res/layout/app_details2_header.xml b/app/src/main/res/layout/app_details2_header.xml index b65d4454e..6785d3e85 100644 --- a/app/src/main/res/layout/app_details2_header.xml +++ b/app/src/main/res/layout/app_details2_header.xml @@ -7,14 +7,15 @@ android:layout_margin="@dimen/details_activity_padding" app:cardBackgroundColor="?attr/appDetailsCardBackground" app:cardCornerRadius="3dp" - app:cardElevation="3dp"> + app:cardElevation="3dp" + android:animateLayoutChanges="true"> + android:paddingBottom="8dp"> + android:src="@drawable/ic_repo_app_default" + android:transitionName="@string/transition_app_item_icon" /> + android:layout_toLeftOf="@id/progress_cancel" + android:textAppearance="@style/TextAppearance.AppCompat.Small" + tools:text="500%" /> + android:layout_alignParentStart="true" + android:layout_alignParentLeft="true" + android:layout_toStartOf="@id/progress_cancel" + android:layout_toLeftOf="@id/progress_cancel" /> @@ -136,10 +137,10 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/icon_and_name" - android:paddingBottom="4dp" android:clipToPadding="false" - android:visibility="visible" - android:gravity="end"> + android:gravity="end" + android:paddingBottom="4dp" + android:visibility="visible"> + tools:text="Uninstall" /> + tools:text="Open" /> @@ -184,75 +185,73 @@ android:id="@+id/whats_new" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginTop="2dp" + android:background="?attr/detailPanel" android:paddingStart="8dp" android:paddingLeft="8dp" + android:paddingTop="16dp" android:paddingEnd="8dp" android:paddingRight="8dp" - android:paddingTop="16dp" android:paddingBottom="16dp" android:textAppearance="@style/TextAppearance.AppCompat.Body1" - android:background="?attr/detailPanel" tools:text="NEW IN VERSION 1.0.2233\n\nA lot has happened since the last build:\n\n\t• Improved UI\n\t• Bug fixes" /> + - + android:layout_marginTop="16dp" + android:orientation="vertical"> - + - + + + + + + + diff --git a/app/src/main/res/layout/listitem_antifeaturelisting.xml b/app/src/main/res/layout/listitem_antifeaturelisting.xml new file mode 100644 index 000000000..7f0e8c207 --- /dev/null +++ b/app/src/main/res/layout/listitem_antifeaturelisting.xml @@ -0,0 +1,28 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index 34c4a5a03..06d94df96 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -14,5 +14,6 @@ + diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index c28975a84..218c2c48f 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -25,6 +25,7 @@ @drawable/ic_back_white_24dp @drawable/ic_close_white_24dp @style/PreferenceThemeOverlay.v14.Material + #ffffff