From a81a61be6108c275dd8ae0cca95d137597ef43d6 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Wed, 13 Feb 2019 20:16:46 +0100 Subject: [PATCH 1/4] RepoDetails: convert mirror lists to have on/off switches --- .../fdroid/views/RepoDetailsActivity.java | 85 ++++++++++++++----- .../main/res/layout/activity_repo_details.xml | 19 +++-- 2 files changed, 76 insertions(+), 28 deletions(-) diff --git a/app/src/main/java/org/fdroid/fdroid/views/RepoDetailsActivity.java b/app/src/main/java/org/fdroid/fdroid/views/RepoDetailsActivity.java index 7d2fbac08..0f043c35d 100644 --- a/app/src/main/java/org/fdroid/fdroid/views/RepoDetailsActivity.java +++ b/app/src/main/java/org/fdroid/fdroid/views/RepoDetailsActivity.java @@ -13,17 +13,23 @@ import android.nfc.NfcAdapter; import android.os.Build; import android.os.Bundle; import android.os.Parcelable; +import android.support.annotation.NonNull; import android.support.v4.app.NavUtils; import android.support.v4.content.LocalBroadcastManager; import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; import android.support.v7.widget.Toolbar; import android.text.TextUtils; import android.text.format.DateUtils; +import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; +import android.view.ViewGroup; import android.widget.Button; +import android.widget.CompoundButton; import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; @@ -115,6 +121,14 @@ public class RepoDetailsActivity extends AppCompatActivity { TextView inputUrl = findViewById(R.id.input_repo_url); inputUrl.setText(repo.address); + RecyclerView officialMirrorListView = findViewById(R.id.official_mirror_list); + officialMirrorListView.setLayoutManager(new LinearLayoutManager(this)); + officialMirrorListView.setAdapter(new MirrorAdapter(repo.mirrors)); + + RecyclerView userMirrorListView = findViewById(R.id.user_mirror_list); + userMirrorListView.setLayoutManager(new LinearLayoutManager(this)); + userMirrorListView.setAdapter(new MirrorAdapter(repo.userMirrors)); + if (repo.address.startsWith("content://")) { // no need to show a QR Code, it is not shareable return; @@ -347,30 +361,14 @@ public class RepoDetailsActivity extends AppCompatActivity { if (repo.mirrors != null) { TextView officialMirrorsLabel = repoView.findViewById(R.id.label_official_mirrors); officialMirrorsLabel.setVisibility(View.VISIBLE); - TextView officialMirrorsText = repoView.findViewById(R.id.text_official_mirrors); - officialMirrorsText.setVisibility(View.VISIBLE); - StringBuilder builder = new StringBuilder(); - for (String url : repo.mirrors) { - builder.append("• "); - builder.append(url); - builder.append('\n'); - } - builder.setLength(Math.max(builder.length() - 1, 0)); - officialMirrorsText.setText(builder.toString()); + RecyclerView officialMirrorList = repoView.findViewById(R.id.official_mirror_list); + officialMirrorList.setVisibility(View.VISIBLE); } if (repo.userMirrors != null) { TextView userMirrorsLabel = repoView.findViewById(R.id.label_user_mirrors); userMirrorsLabel.setVisibility(View.VISIBLE); - TextView userMirrorsText = repoView.findViewById(R.id.text_user_mirrors); - userMirrorsText.setVisibility(View.VISIBLE); - StringBuilder builder = new StringBuilder(); - for (String url : repo.userMirrors) { - builder.append("• "); - builder.append(url); - builder.append('\n'); - } - builder.setLength(Math.max(builder.length() - 1, 0)); - userMirrorsText.setText(builder.toString()); + RecyclerView userMirrorList = repoView.findViewById(R.id.user_mirror_list); + userMirrorList.setVisibility(View.VISIBLE); } name.setText(repo.name); @@ -464,4 +462,51 @@ public class RepoDetailsActivity extends AppCompatActivity { credentialsDialog.show(); } + + private class MirrorAdapter extends RecyclerView.Adapter { + private String[] mirrors; + + class MirrorViewHolder extends RecyclerView.ViewHolder { + View view; + + MirrorViewHolder(View view) { + super(view); + this.view = view; + } + } + + MirrorAdapter(String[] mirrors) { + this.mirrors = mirrors; + } + + @NonNull + @Override + public MirrorAdapter.MirrorViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.repo_item, parent, false); + return new MirrorViewHolder(itemView); + } + + @Override + public void onBindViewHolder(@NonNull MirrorViewHolder holder, int position) { + TextView repoNameTextView = holder.view.findViewById(R.id.repo_name); + repoNameTextView.setText(mirrors[position]); + + CompoundButton switchView = holder.view.findViewById(R.id.repo_switch); + switchView.setChecked(true); + + View repoUnverified = holder.view.findViewById(R.id.repo_unverified); + repoUnverified.setVisibility(View.GONE); + + View repoUnsigned = holder.view.findViewById(R.id.repo_unsigned); + repoUnsigned.setVisibility(View.GONE); + } + + @Override + public int getItemCount() { + if (mirrors == null) { + return 0; + } + return mirrors.length; + } + } } diff --git a/app/src/main/res/layout/activity_repo_details.xml b/app/src/main/res/layout/activity_repo_details.xml index 68de5e0d0..60b11ed72 100644 --- a/app/src/main/res/layout/activity_repo_details.xml +++ b/app/src/main/res/layout/activity_repo_details.xml @@ -1,6 +1,7 @@ @@ -83,10 +84,11 @@ android:text="@string/repo_official_mirrors" android:visibility="gone" /> - + - + Date: Wed, 20 Feb 2019 21:10:34 +0100 Subject: [PATCH 2/4] RepoDetails: add switches to disable any or all mirrors This also needs to handle mirror lists with 1 element, since mirrors can now be disabled. If the user disables all mirrors, then there will be only one URL in the list of mirrors. Asking for a random mirror in that case should not return null, but the one enabled mirror. closes #1696 --- .../java/org/fdroid/fdroid/data/DBHelper.java | 14 +++- .../java/org/fdroid/fdroid/data/Repo.java | 37 ++++++++-- .../java/org/fdroid/fdroid/data/Schema.java | 3 +- .../fdroid/views/RepoDetailsActivity.java | 73 +++++++++++++++---- 4 files changed, 105 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/org/fdroid/fdroid/data/DBHelper.java b/app/src/main/java/org/fdroid/fdroid/data/DBHelper.java index 07df7502d..4444c88f3 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/DBHelper.java +++ b/app/src/main/java/org/fdroid/fdroid/data/DBHelper.java @@ -97,6 +97,7 @@ public class DBHelper extends SQLiteOpenHelper { + RepoTable.Cols.ICON + " string, " + RepoTable.Cols.MIRRORS + " string, " + RepoTable.Cols.USER_MIRRORS + " string, " + + RepoTable.Cols.DISABLED_MIRRORS + " string, " + RepoTable.Cols.PUSH_REQUESTS + " integer not null default " + Repo.PUSH_REQUEST_IGNORE + ");"; @@ -223,7 +224,7 @@ public class DBHelper extends SQLiteOpenHelper { + "primary key(" + ApkAntiFeatureJoinTable.Cols.APK_ID + ", " + ApkAntiFeatureJoinTable.Cols.ANTI_FEATURE_ID + ") " + " );"; - protected static final int DB_VERSION = 79; + protected static final int DB_VERSION = 80; private final Context context; @@ -448,6 +449,17 @@ public class DBHelper extends SQLiteOpenHelper { addLiberapayID(db, oldVersion); addUserMirrorsFields(db, oldVersion); removeNotNullFromVersionName(db, oldVersion); + addDisabledMirrorsFields(db, oldVersion); + } + + private void addDisabledMirrorsFields(SQLiteDatabase db, int oldVersion) { + if (oldVersion >= 80) { + return; + } + if (!columnExists(db, RepoTable.NAME, RepoTable.Cols.DISABLED_MIRRORS)) { + Utils.debugLog(TAG, "Adding " + RepoTable.Cols.DISABLED_MIRRORS + " field to " + RepoTable.NAME + " table in db."); + db.execSQL("alter table " + RepoTable.NAME + " add column " + RepoTable.Cols.DISABLED_MIRRORS + " string;"); + } } private void removeNotNullFromVersionName(SQLiteDatabase db, int oldVersion) { diff --git a/app/src/main/java/org/fdroid/fdroid/data/Repo.java b/app/src/main/java/org/fdroid/fdroid/data/Repo.java index 2bebb1854..fa92350ca 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/Repo.java +++ b/app/src/main/java/org/fdroid/fdroid/data/Repo.java @@ -86,6 +86,9 @@ public class Repo extends ValueObject { @JsonIgnore public int pushRequests = PUSH_REQUEST_IGNORE; + /** + * The canonical URL of the repo. + */ public String address; public String name; public String description; @@ -125,8 +128,15 @@ public class Repo extends ValueObject { /** * Mirrors added by the user, either by UI input or by attaching removeable storage */ + @JsonIgnore public String[] userMirrors; + /** + * Mirrors that have been manually disabled by the user. + */ + @JsonIgnore + public String[] disabledMirrors; + public Repo() { } @@ -193,6 +203,9 @@ public class Repo extends ValueObject { case Cols.USER_MIRRORS: userMirrors = Utils.parseCommaSeparatedString(cursor.getString(i)); break; + case Cols.DISABLED_MIRRORS: + disabledMirrors = Utils.parseCommaSeparatedString(cursor.getString(i)); + break; case Cols.PUSH_REQUESTS: pushRequests = cursor.getInt(i); break; @@ -334,6 +347,10 @@ public class Repo extends ValueObject { userMirrors = Utils.parseCommaSeparatedString(values.getAsString(Cols.USER_MIRRORS)); } + if (values.containsKey(Cols.DISABLED_MIRRORS)) { + disabledMirrors = Utils.parseCommaSeparatedString(values.getAsString(Cols.DISABLED_MIRRORS)); + } + if (values.containsKey(Cols.PUSH_REQUESTS)) { pushRequests = toInt(values.getAsInteger(Cols.PUSH_REQUESTS)); } @@ -345,8 +362,9 @@ public class Repo extends ValueObject { * mirror list. */ public boolean hasMirrors() { - return (mirrors != null && mirrors.length > 1) - || (userMirrors != null && userMirrors.length > 0); + List mirrorList = getMirrorList(); + int size = mirrorList.size(); + return size > 1 || (size == 1 && !mirrorList.contains(address)); } /** @@ -361,6 +379,9 @@ public class Repo extends ValueObject { allMirrors.addAll(Arrays.asList(mirrors)); } allMirrors.add(address); + if (disabledMirrors != null) { + allMirrors.removeAll(Arrays.asList(disabledMirrors)); + } return new ArrayList<>(allMirrors); } @@ -382,9 +403,11 @@ public class Repo extends ValueObject { /** * Get a random mirror URL from the list of mirrors for this repo. It will * remove the URL in {@code mirrorToSkip} from consideration before choosing - * which mirror to return. + * which mirror to return. {@link #getMirrorList()} returns a list of all + * known mirrors minus the mirrors that have been disabled by the + * user preference, e.g. {@link #disabledMirrors}. *

- * The mirror logic assumes that it has a mirrors list with at least once + * The mirror logic assumes that it has a mirrors list with at least one * valid entry in it. In the index format as defined by {@code fdroid update}, * there is always at least one valid URL: the canonical URL. That also means * if there is only one item in the mirrors list, there are no other URLs to try. @@ -394,6 +417,8 @@ public class Repo extends ValueObject { * update. That makes it possible to do the first index update via SD Card * or USB OTG drive. * + * @see #getMirrorList() + * @see #disabledMirrors * @see FDroidApp#resetMirrorVars() * @see FDroidApp#switchUrlToNewMirror(String, Repo) * @see FDroidApp#getTimeout() @@ -403,7 +428,7 @@ public class Repo extends ValueObject { mirrorToSkip = address; } List shuffledMirrors = getMirrorList(); - if (shuffledMirrors.size() > 1) { + if (shuffledMirrors.size() > 0) { Collections.shuffle(shuffledMirrors); for (String m : shuffledMirrors) { // Return a non default, and not last used mirror @@ -419,6 +444,6 @@ public class Repo extends ValueObject { } } } - return null; // In case we are out of mirrors. + return address; // In case we are out of mirrors. } } diff --git a/app/src/main/java/org/fdroid/fdroid/data/Schema.java b/app/src/main/java/org/fdroid/fdroid/data/Schema.java index 3642ebcd9..bdb8c01d8 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/Schema.java +++ b/app/src/main/java/org/fdroid/fdroid/data/Schema.java @@ -363,12 +363,13 @@ public interface Schema { String ICON = "icon"; String MIRRORS = "mirrors"; String USER_MIRRORS = "userMirrors"; + String DISABLED_MIRRORS = "disabledMirrors"; String PUSH_REQUESTS = "pushRequests"; String[] ALL = { _ID, ADDRESS, NAME, DESCRIPTION, IN_USE, PRIORITY, SIGNING_CERT, FINGERPRINT, MAX_AGE, LAST_UPDATED, LAST_ETAG, VERSION, IS_SWAP, - USERNAME, PASSWORD, TIMESTAMP, ICON, MIRRORS, USER_MIRRORS, PUSH_REQUESTS, + USERNAME, PASSWORD, TIMESTAMP, ICON, MIRRORS, USER_MIRRORS, DISABLED_MIRRORS, PUSH_REQUESTS, }; } } diff --git a/app/src/main/java/org/fdroid/fdroid/views/RepoDetailsActivity.java b/app/src/main/java/org/fdroid/fdroid/views/RepoDetailsActivity.java index 0f043c35d..10f39964b 100644 --- a/app/src/main/java/org/fdroid/fdroid/views/RepoDetailsActivity.java +++ b/app/src/main/java/org/fdroid/fdroid/views/RepoDetailsActivity.java @@ -44,6 +44,8 @@ import org.fdroid.fdroid.data.Repo; import org.fdroid.fdroid.data.RepoProvider; import org.fdroid.fdroid.data.Schema.RepoTable; +import java.util.Arrays; +import java.util.HashSet; import java.util.Locale; public class RepoDetailsActivity extends AppCompatActivity { @@ -82,6 +84,8 @@ public class RepoDetailsActivity extends AppCompatActivity { private View repoView; private String shareUrl; + private MirrorAdapter adapterToNotify; + /** * Help function to make switching between two view states easier. * Perhaps there is a better way to do this. I recall that using Adobe @@ -109,25 +113,19 @@ public class RepoDetailsActivity extends AppCompatActivity { repoView = findViewById(R.id.repo_view); repoId = getIntent().getLongExtra(ARG_REPO_ID, 0); - final String[] projection = { - RepoTable.Cols.NAME, - RepoTable.Cols.ADDRESS, - RepoTable.Cols.FINGERPRINT, - RepoTable.Cols.MIRRORS, - RepoTable.Cols.USER_MIRRORS, - }; - repo = RepoProvider.Helper.findById(this, repoId, projection); + repo = RepoProvider.Helper.findById(this, repoId); TextView inputUrl = findViewById(R.id.input_repo_url); inputUrl.setText(repo.address); RecyclerView officialMirrorListView = findViewById(R.id.official_mirror_list); officialMirrorListView.setLayoutManager(new LinearLayoutManager(this)); - officialMirrorListView.setAdapter(new MirrorAdapter(repo.mirrors)); + adapterToNotify = new MirrorAdapter(repo, repo.mirrors); + officialMirrorListView.setAdapter(adapterToNotify); RecyclerView userMirrorListView = findViewById(R.id.user_mirror_list); userMirrorListView.setLayoutManager(new LinearLayoutManager(this)); - userMirrorListView.setAdapter(new MirrorAdapter(repo.userMirrors)); + userMirrorListView.setAdapter(new MirrorAdapter(repo, repo.userMirrors)); if (repo.address.startsWith("content://")) { // no need to show a QR Code, it is not shareable @@ -464,7 +462,8 @@ public class RepoDetailsActivity extends AppCompatActivity { } private class MirrorAdapter extends RecyclerView.Adapter { - private String[] mirrors; + private final Repo repo; + private final String[] mirrors; class MirrorViewHolder extends RecyclerView.ViewHolder { View view; @@ -475,7 +474,8 @@ public class RepoDetailsActivity extends AppCompatActivity { } } - MirrorAdapter(String[] mirrors) { + MirrorAdapter(Repo repo, String[] mirrors) { + this.repo = repo; this.mirrors = mirrors; } @@ -487,12 +487,57 @@ public class RepoDetailsActivity extends AppCompatActivity { } @Override - public void onBindViewHolder(@NonNull MirrorViewHolder holder, int position) { + public void onBindViewHolder(@NonNull MirrorViewHolder holder, final int position) { TextView repoNameTextView = holder.view.findViewById(R.id.repo_name); repoNameTextView.setText(mirrors[position]); + final String itemMirror = mirrors[position]; + boolean enabled = true; + if (repo.disabledMirrors != null) { + for (String disabled : repo.disabledMirrors) { + if (TextUtils.equals(itemMirror, disabled)) { + enabled = false; + break; + } + } + } CompoundButton switchView = holder.view.findViewById(R.id.repo_switch); - switchView.setChecked(true); + switchView.setChecked(enabled); + switchView.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + HashSet disabledMirrors; + if (repo.disabledMirrors == null) { + disabledMirrors = new HashSet<>(1); + } else { + disabledMirrors = new HashSet<>(Arrays.asList(repo.disabledMirrors)); + } + + if (isChecked) { + disabledMirrors.remove(itemMirror); + } else { + disabledMirrors.add(itemMirror); + } + + int totalMirrors = (repo.mirrors == null ? 0 : repo.mirrors.length) + + (repo.userMirrors == null ? 0 : repo.userMirrors.length); + if (disabledMirrors.size() == totalMirrors) { + // if all mirrors are disabled, re-enable canonical repo as mirror + disabledMirrors.remove(repo.address); + adapterToNotify.notifyItemChanged(0); + } + + if (disabledMirrors.size() == 0) { + repo.disabledMirrors = null; + } else { + repo.disabledMirrors = disabledMirrors.toArray(new String[disabledMirrors.size()]); + } + final ContentValues values = new ContentValues(1); + values.put(RepoTable.Cols.DISABLED_MIRRORS, + Utils.serializeCommaSeparatedString(repo.disabledMirrors)); + RepoProvider.Helper.update(RepoDetailsActivity.this, repo, values); + } + }); View repoUnverified = holder.view.findViewById(R.id.repo_unverified); repoUnverified.setVisibility(View.GONE); From b1569795a20ca1c8f95f8e5ddbca80a36f524e2e Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Wed, 20 Feb 2019 21:12:16 +0100 Subject: [PATCH 3/4] IndexV1Updater: use LinkedHashSet to preserve mirror ordering --- .../main/java/org/fdroid/fdroid/IndexV1Updater.java | 4 ++-- .../fdroid/fdroid/updater/IndexV1UpdaterTest.java | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/org/fdroid/fdroid/IndexV1Updater.java b/app/src/main/java/org/fdroid/fdroid/IndexV1Updater.java index c53b52f3e..82a00247f 100644 --- a/app/src/main/java/org/fdroid/fdroid/IndexV1Updater.java +++ b/app/src/main/java/org/fdroid/fdroid/IndexV1Updater.java @@ -65,9 +65,9 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; -import java.util.TreeSet; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -295,7 +295,7 @@ public class IndexV1Updater extends IndexUpdater { repo.description = getStringRepoValue(repoMap, "description"); // ensure the canonical URL is included in the "mirrors" list as the first entry - TreeSet mirrors = new TreeSet<>(); + LinkedHashSet mirrors = new LinkedHashSet<>(); mirrors.add(repo.address); mirrors.addAll(getStringListRepoValue(repoMap, "mirrors")); repo.mirrors = mirrors.toArray(new String[mirrors.size()]); diff --git a/app/src/test/java/org/fdroid/fdroid/updater/IndexV1UpdaterTest.java b/app/src/test/java/org/fdroid/fdroid/updater/IndexV1UpdaterTest.java index 3a8d231ef..986e74285 100644 --- a/app/src/test/java/org/fdroid/fdroid/updater/IndexV1UpdaterTest.java +++ b/app/src/test/java/org/fdroid/fdroid/updater/IndexV1UpdaterTest.java @@ -39,8 +39,8 @@ import java.io.InputStream; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashSet; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -67,6 +67,7 @@ public class IndexV1UpdaterTest extends FDroidProviderTest { public static final String TAG = "IndexV1UpdaterTest"; private static final long FAKE_REPO_ID = 0xdeadbeef; + private static final String TESTY_CANONICAL_URL = "http://testy.at.or.at/fdroid/repo"; private static final String TESTY_JAR = "testy.at.or.at_index-v1.jar"; private static final String TESTY_CERT = "308204e1308202c9a0030201020204483450fa300d06092a864886f70d01010b050030213110300e060355040b1307462d44726f6964310d300b06035504031304736f7661301e170d3136303832333133333131365a170d3434303130393133333131365a30213110300e060355040b1307462d44726f6964310d300b06035504031304736f766130820222300d06092a864886f70d01010105000382020f003082020a0282020100dfdcd120f3ab224999dddf4ea33ea588d295e4d7130bef48c143e9d76e5c0e0e9e5d45e64208e35feebc79a83f08939dd6a343b7d1e2179930a105a1249ccd36d88ff3feffc6e4dc53dae0163a7876dd45ecc1ddb0adf5099aa56c1a84b52affcd45d0711ffa4de864f35ac0333ebe61ea8673eeda35a88f6af678cc4d0f80b089338ac8f2a8279a64195c611d19445cab3fd1a020afed9bd739bb95142fb2c00a8f847db5ef3325c814f8eb741bacf86ed3907bfe6e4564d2de5895df0c263824e0b75407589bae2d3a4666c13b92102d8781a8ee9bb4a5a1a78c4a9c21efdaf5584da42e84418b28f5a81d0456a3dc5b420991801e6b21e38c99bbe018a5b2d690894a114bc860d35601416aa4dc52216aff8a288d4775cddf8b72d45fd2f87303a8e9c0d67e442530be28eaf139894337266e0b33d57f949256ab32083bcc545bc18a83c9ab8247c12aea037e2b68dee31c734cb1f04f241d3b94caa3a2b258ffaf8e6eae9fbbe029a934dc0a0859c5f120334812693a1c09352340a39f2a678dbc1afa2a978bfee43afefcb7e224a58af2f3d647e5745db59061236b8af6fcfd93b3602f9e456978534f3a7851e800071bf56da80401c81d91c45f82568373af0576b1cc5eef9b85654124b6319770be3cdba3fbebe3715e8918fb6c8966624f3d0e815effac3d2ee06dd34ab9c693218b2c7c06ba99d6b74d4f17b8c3cb0203010001a321301f301d0603551d0e04160414d62bee9f3798509546acc62eb1de14b08b954d4f300d06092a864886f70d01010b05000382020100743f7c5692085895f9d1fffad390fb4202c15f123ed094df259185960fd6dadf66cb19851070f180297bba4e6996a4434616573b375cfee94fee73a4505a7ec29136b7e6c22e6436290e3686fe4379d4e3140ec6a08e70cfd3ed5b634a5eb5136efaaabf5f38e0432d3d79568a556970b8cfba2972f5d23a3856d8a981b9e9bbbbb88f35e708bde9cbc5f681cbd974085b9da28911296fe2579fa64bbe9fa0b93475a7a8db051080b0c5fade0d1c018e7858cd4cbe95145b0620e2f632cbe0f8af9cbf22e2fdaa72245ae31b0877b07181cc69dd2df74454251d8de58d25e76354abe7eb690f22e59b08795a8f2c98c578e0599503d9085927634072c82c9f82abd50fd12b8fd1a9d1954eb5cc0b4cfb5796b5aaec0356643b4a65a368442d92ef94edd3ac6a2b7fe3571b8cf9f462729228aab023ef9183f73792f5379633ccac51079177d604c6bc1873ada6f07d8da6d68c897e88a5fa5d63fdb8df820f46090e0716e7562dd3c140ba279a65b996f60addb0abe29d4bf2f5abe89480771d492307b926d91f02f341b2148502903c43d40f3c6c86a811d060711f0698b384acdcc0add44eb54e42962d3d041accc715afd49407715adc09350cb55e8d9281a3b0b6b5fcd91726eede9b7c8b13afdebb2c2b377629595f1096ba62fb14946dbac5f3c5f0b4e5b712e7acc7dcf6c46cdc5e6d6dfdeee55a0c92c2d70f080ac6"; // NOCHECKSTYLE LineLength @@ -83,7 +84,7 @@ public class IndexV1UpdaterTest extends FDroidProviderTest { } assertEquals("No repos present", 0, RepoProvider.Helper.all(context).size()); assertEquals("No apps present", 0, AppProvider.Helper.all(context.getContentResolver()).size()); - Repo repo = MultiIndexUpdaterTest.createRepo("Testy", TESTY_JAR, context, TESTY_CERT); + Repo repo = MultiIndexUpdaterTest.createRepo("Testy", TESTY_CANONICAL_URL, context, TESTY_CERT); repo.timestamp = 1481222110; IndexV1Updater updater = new IndexV1Updater(context, repo); JarFile jarFile = new JarFile(TestUtils.copyResourceToTempFile(TESTY_JAR), true); @@ -125,10 +126,9 @@ public class IndexV1UpdaterTest extends FDroidProviderTest { assertEquals("repo.icon should be set", "fdroid-icon.png", repoFromDb.icon); String description = "This is a repository of apps to be used with F-Droid. Applications in this repository are either official binaries built by the original application developers, or are binaries built from source by the admin of f-droid.org using the tools on https://gitlab.com/u/fdroid. "; // NOCHECKSTYLE LineLength assertEquals("repo.description should be set", description, repoFromDb.description); - assertEquals("repo.mirrors should have items", 3, repo.mirrors.length); - assertEquals("repo.mirrors first URL", "http://frkcchxlcvnb4m5a.onion/fdroid/repo", repo.mirrors[0]); - assertEquals("repo.mirrors second URL", "http://testy.at.or.at/fdroid/repo", repo.mirrors[1]); - assertEquals("repo.mirrors third URL", "testy.at.or.at_index-v1.jar", repo.mirrors[2]); + assertEquals("repo.mirrors should have items", 2, repo.mirrors.length); + assertEquals("second URL in index-v1, but canonical is first", TESTY_CANONICAL_URL, repo.mirrors[0]); + assertEquals("first URL in index-v1", "http://frkcchxlcvnb4m5a.onion/fdroid/repo", repo.mirrors[1]); // Make sure the per-apk anti features which are new in index v1 get added correctly. assertEquals(0, AppProvider.Helper.findInstalledAppsWithKnownVulns(context).size()); From 6a18ba1b8551edd298777eb13812d70230aa6c7a Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Wed, 20 Feb 2019 22:20:05 +0100 Subject: [PATCH 4/4] RepoDetails: hide mirror lists if they are not useful e.g., empty lists or the official mirror list just repeating the canonical URL. --- .../fdroid/views/RepoDetailsActivity.java | 43 +++++++++++-------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/org/fdroid/fdroid/views/RepoDetailsActivity.java b/app/src/main/java/org/fdroid/fdroid/views/RepoDetailsActivity.java index 10f39964b..55846ac36 100644 --- a/app/src/main/java/org/fdroid/fdroid/views/RepoDetailsActivity.java +++ b/app/src/main/java/org/fdroid/fdroid/views/RepoDetailsActivity.java @@ -336,18 +336,36 @@ public class RepoDetailsActivity extends AppCompatActivity { } private void updateRepoView() { + TextView officialMirrorsLabel = repoView.findViewById(R.id.label_official_mirrors); + RecyclerView officialMirrorList = repoView.findViewById(R.id.official_mirror_list); + if ((repo.mirrors != null && repo.mirrors.length > 1) + || (repo.userMirrors != null && repo.userMirrors.length > 0)) { + // don't show this if there is only the canonical URL available, and no other mirrors + officialMirrorsLabel.setVisibility(View.VISIBLE); + officialMirrorList.setVisibility(View.VISIBLE); + } else { + officialMirrorsLabel.setVisibility(View.GONE); + officialMirrorList.setVisibility(View.GONE); + } + + TextView userMirrorsLabel = repoView.findViewById(R.id.label_user_mirrors); + RecyclerView userMirrorList = repoView.findViewById(R.id.user_mirror_list); + if (repo.userMirrors != null && repo.userMirrors.length > 0) { + userMirrorsLabel.setVisibility(View.VISIBLE); + userMirrorList.setVisibility(View.VISIBLE); + } else { + userMirrorsLabel.setVisibility(View.GONE); + userMirrorList.setVisibility(View.GONE); + } + if (repo.hasBeenUpdated()) { updateViewForExistingRepo(repoView); } else { - updateViewForNewRepo(repoView); + setMultipleViewVisibility(repoView, HIDE_IF_EXISTS, View.VISIBLE); + setMultipleViewVisibility(repoView, SHOW_IF_EXISTS, View.GONE); } } - private void updateViewForNewRepo(View repoView) { - setMultipleViewVisibility(repoView, HIDE_IF_EXISTS, View.VISIBLE); - setMultipleViewVisibility(repoView, SHOW_IF_EXISTS, View.GONE); - } - private void updateViewForExistingRepo(View repoView) { setMultipleViewVisibility(repoView, SHOW_IF_EXISTS, View.VISIBLE); setMultipleViewVisibility(repoView, HIDE_IF_EXISTS, View.GONE); @@ -356,19 +374,6 @@ public class RepoDetailsActivity extends AppCompatActivity { TextView numApps = repoView.findViewById(R.id.text_num_apps); TextView lastUpdated = repoView.findViewById(R.id.text_last_update); - if (repo.mirrors != null) { - TextView officialMirrorsLabel = repoView.findViewById(R.id.label_official_mirrors); - officialMirrorsLabel.setVisibility(View.VISIBLE); - RecyclerView officialMirrorList = repoView.findViewById(R.id.official_mirror_list); - officialMirrorList.setVisibility(View.VISIBLE); - } - if (repo.userMirrors != null) { - TextView userMirrorsLabel = repoView.findViewById(R.id.label_user_mirrors); - userMirrorsLabel.setVisibility(View.VISIBLE); - RecyclerView userMirrorList = repoView.findViewById(R.id.user_mirror_list); - userMirrorList.setVisibility(View.VISIBLE); - } - name.setText(repo.name); int appCount = RepoProvider.Helper.countAppsForRepo(this, repoId);