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
This commit is contained in:
		
							parent
							
								
									a81a61be61
								
							
						
					
					
						commit
						c3ec29df93
					
				@ -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) {
 | 
			
		||||
 | 
			
		||||
@ -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<String> 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 <b>minus</b> the mirrors that have been disabled by the
 | 
			
		||||
     * user preference, e.g. {@link #disabledMirrors}.
 | 
			
		||||
     * <p>
 | 
			
		||||
     * 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<String> 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.
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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,
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -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<MirrorAdapter.MirrorViewHolder> {
 | 
			
		||||
        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<String> 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);
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user