support adding custom mirrors to any existing repo, via "App Repo"
This lets people add any URL as a mirror to an existing repo. The UX is people add URLs via any of the normal ways of adding a new repo via Intents, like clicking URLs, QRCodes, etc.
This commit is contained in:
		
							parent
							
								
									89e04cc078
								
							
						
					
					
						commit
						2f0cb30ad0
					
				| @ -88,35 +88,36 @@ public class DBHelper extends SQLiteOpenHelper { | ||||
|             + RepoTable.Cols.TIMESTAMP + " integer not null default 0, " | ||||
|             + RepoTable.Cols.ICON + " string, " | ||||
|             + RepoTable.Cols.MIRRORS + " string, " | ||||
|             + RepoTable.Cols.USER_MIRRORS + " string, " | ||||
|             + RepoTable.Cols.PUSH_REQUESTS + " integer not null default " + Repo.PUSH_REQUEST_IGNORE | ||||
|             + ");"; | ||||
| 
 | ||||
|     static final String CREATE_TABLE_APK = | ||||
|             "CREATE TABLE " + ApkTable.NAME + " ( " | ||||
|             + ApkTable.Cols.APP_ID + " integer not null, " | ||||
|             + ApkTable.Cols.VERSION_NAME + " text not null, " | ||||
|             + ApkTable.Cols.REPO_ID + " integer not null, " | ||||
|             + ApkTable.Cols.HASH + " text not null, " | ||||
|             + ApkTable.Cols.VERSION_CODE + " int not null," | ||||
|             + ApkTable.Cols.NAME + " text not null, " | ||||
|             + ApkTable.Cols.SIZE + " int not null, " | ||||
|             + ApkTable.Cols.SIGNATURE + " string, " | ||||
|             + ApkTable.Cols.SOURCE_NAME + " string, " | ||||
|             + ApkTable.Cols.MIN_SDK_VERSION + " integer, " | ||||
|             + ApkTable.Cols.TARGET_SDK_VERSION + " integer, " | ||||
|             + ApkTable.Cols.MAX_SDK_VERSION + " integer, " | ||||
|             + ApkTable.Cols.OBB_MAIN_FILE + " string, " | ||||
|             + ApkTable.Cols.OBB_MAIN_FILE_SHA256 + " string, " | ||||
|             + ApkTable.Cols.OBB_PATCH_FILE + " string, " | ||||
|             + ApkTable.Cols.OBB_PATCH_FILE_SHA256 + " string, " | ||||
|             + ApkTable.Cols.REQUESTED_PERMISSIONS + " string, " | ||||
|             + ApkTable.Cols.FEATURES + " string, " | ||||
|             + ApkTable.Cols.NATIVE_CODE + " string, " | ||||
|             + ApkTable.Cols.HASH_TYPE + " string, " | ||||
|             + ApkTable.Cols.ADDED_DATE + " string, " | ||||
|             + ApkTable.Cols.IS_COMPATIBLE + " int not null, " | ||||
|             + ApkTable.Cols.INCOMPATIBLE_REASONS + " text" | ||||
|             + ");"; | ||||
|                     + ApkTable.Cols.APP_ID + " integer not null, " | ||||
|                     + ApkTable.Cols.VERSION_NAME + " text not null, " | ||||
|                     + ApkTable.Cols.REPO_ID + " integer not null, " | ||||
|                     + ApkTable.Cols.HASH + " text not null, " | ||||
|                     + ApkTable.Cols.VERSION_CODE + " int not null," | ||||
|                     + ApkTable.Cols.NAME + " text not null, " | ||||
|                     + ApkTable.Cols.SIZE + " int not null, " | ||||
|                     + ApkTable.Cols.SIGNATURE + " string, " | ||||
|                     + ApkTable.Cols.SOURCE_NAME + " string, " | ||||
|                     + ApkTable.Cols.MIN_SDK_VERSION + " integer, " | ||||
|                     + ApkTable.Cols.TARGET_SDK_VERSION + " integer, " | ||||
|                     + ApkTable.Cols.MAX_SDK_VERSION + " integer, " | ||||
|                     + ApkTable.Cols.OBB_MAIN_FILE + " string, " | ||||
|                     + ApkTable.Cols.OBB_MAIN_FILE_SHA256 + " string, " | ||||
|                     + ApkTable.Cols.OBB_PATCH_FILE + " string, " | ||||
|                     + ApkTable.Cols.OBB_PATCH_FILE_SHA256 + " string, " | ||||
|                     + ApkTable.Cols.REQUESTED_PERMISSIONS + " string, " | ||||
|                     + ApkTable.Cols.FEATURES + " string, " | ||||
|                     + ApkTable.Cols.NATIVE_CODE + " string, " | ||||
|                     + ApkTable.Cols.HASH_TYPE + " string, " | ||||
|                     + ApkTable.Cols.ADDED_DATE + " string, " | ||||
|                     + ApkTable.Cols.IS_COMPATIBLE + " int not null, " | ||||
|                     + ApkTable.Cols.INCOMPATIBLE_REASONS + " text" | ||||
|                     + ");"; | ||||
| 
 | ||||
|     static final String CREATE_TABLE_APP_METADATA = "CREATE TABLE " + AppMetadataTable.NAME | ||||
|             + " ( " | ||||
| @ -181,7 +182,7 @@ public class DBHelper extends SQLiteOpenHelper { | ||||
|      * app metadata id, because it can instead look through the primary key index. This can be | ||||
|      * observed by flipping the order of the primary key columns, and noting the resulting sqlite | ||||
|      * logs along the lines of: | ||||
|      *   E/SQLiteLog(14164): (284) automatic index on fdroid_categoryAppMetadataJoin(appMetadataId) | ||||
|      * E/SQLiteLog(14164): (284) automatic index on fdroid_categoryAppMetadataJoin(appMetadataId) | ||||
|      */ | ||||
|     static final String CREATE_TABLE_CAT_JOIN = "CREATE TABLE " + CatJoinTable.NAME | ||||
|             + " ( " | ||||
| @ -214,7 +215,7 @@ public class DBHelper extends SQLiteOpenHelper { | ||||
|             + "primary key(" + ApkAntiFeatureJoinTable.Cols.APK_ID + ", " + ApkAntiFeatureJoinTable.Cols.ANTI_FEATURE_ID + ") " | ||||
|             + " );"; | ||||
| 
 | ||||
|     protected static final int DB_VERSION = 77; | ||||
|     protected static final int DB_VERSION = 78; | ||||
| 
 | ||||
|     private final Context context; | ||||
| 
 | ||||
| @ -321,6 +322,17 @@ public class DBHelper extends SQLiteOpenHelper { | ||||
|         addApkAntiFeatures(db, oldVersion); | ||||
|         addIgnoreVulnPref(db, oldVersion); | ||||
|         addLiberapayID(db, oldVersion); | ||||
|         addUserMirrorsFields(db, oldVersion); | ||||
|     } | ||||
| 
 | ||||
|     private void addUserMirrorsFields(SQLiteDatabase db, int oldVersion) { | ||||
|         if (oldVersion >= 78) { | ||||
|             return; | ||||
|         } | ||||
|         if (!columnExists(db, RepoTable.NAME, RepoTable.Cols.USER_MIRRORS)) { | ||||
|             Utils.debugLog(TAG, "Adding " + RepoTable.Cols.USER_MIRRORS + " field to " + RepoTable.NAME + " table in db."); | ||||
|             db.execSQL("alter table " + RepoTable.NAME + " add column " + RepoTable.Cols.USER_MIRRORS + " string;"); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private void addLiberapayID(SQLiteDatabase db, int oldVersion) { | ||||
| @ -581,7 +593,7 @@ public class DBHelper extends SQLiteOpenHelper { | ||||
|         updateRepoPriority(db, gpPubKey, gpArchiveAddress, 4); | ||||
| 
 | ||||
|         int priority = 5; | ||||
|         String[] projection = new String[] {RepoTable.Cols.SIGNING_CERT, RepoTable.Cols.ADDRESS}; | ||||
|         String[] projection = new String[]{RepoTable.Cols.SIGNING_CERT, RepoTable.Cols.ADDRESS}; | ||||
| 
 | ||||
|         // Order by ID, because that is a good analogy for the order in which they were added. | ||||
|         // The order in which they were added is likely the order they present in the ManageRepos activity. | ||||
| @ -606,7 +618,7 @@ public class DBHelper extends SQLiteOpenHelper { | ||||
|                 RepoTable.NAME, | ||||
|                 values, | ||||
|                 RepoTable.Cols.SIGNING_CERT + " = ? AND " + RepoTable.Cols.ADDRESS + " = ?", | ||||
|                 new String[] {signingCert, address} | ||||
|                 new String[]{signingCert, address} | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
| @ -630,15 +642,15 @@ public class DBHelper extends SQLiteOpenHelper { | ||||
|         Utils.debugLog(TAG, "Migrating app preferences to separate table"); | ||||
|         db.execSQL( | ||||
|                 "INSERT INTO " + AppPrefsTable.NAME + " (" | ||||
|                 + AppPrefsTable.Cols.PACKAGE_NAME + ", " | ||||
|                 + AppPrefsTable.Cols.IGNORE_THIS_UPDATE + ", " | ||||
|                 + AppPrefsTable.Cols.IGNORE_ALL_UPDATES | ||||
|                 + ") SELECT " | ||||
|                 + "id, " | ||||
|                 + "ignoreThisUpdate, " | ||||
|                 + "ignoreAllUpdates " | ||||
|                 + "FROM " + AppMetadataTable.NAME + " " | ||||
|                 + "WHERE ignoreThisUpdate > 0 OR ignoreAllUpdates > 0" | ||||
|                         + AppPrefsTable.Cols.PACKAGE_NAME + ", " | ||||
|                         + AppPrefsTable.Cols.IGNORE_THIS_UPDATE + ", " | ||||
|                         + AppPrefsTable.Cols.IGNORE_ALL_UPDATES | ||||
|                         + ") SELECT " | ||||
|                         + "id, " | ||||
|                         + "ignoreThisUpdate, " | ||||
|                         + "ignoreAllUpdates " | ||||
|                         + "FROM " + AppMetadataTable.NAME + " " | ||||
|                         + "WHERE ignoreThisUpdate > 0 OR ignoreAllUpdates > 0" | ||||
|         ); | ||||
| 
 | ||||
|         resetTransient(db); | ||||
| @ -687,7 +699,7 @@ public class DBHelper extends SQLiteOpenHelper { | ||||
| 
 | ||||
|                 db.execSQL(createTableDdl); | ||||
| 
 | ||||
|                 String nonPackageNameFields = TextUtils.join(", ", new String[] { | ||||
|                 String nonPackageNameFields = TextUtils.join(", ", new String[]{ | ||||
|                         ApkTable.Cols.APP_ID, | ||||
|                         ApkTable.Cols.VERSION_NAME, | ||||
|                         ApkTable.Cols.REPO_ID, | ||||
| @ -766,7 +778,7 @@ public class DBHelper extends SQLiteOpenHelper { | ||||
|         } | ||||
|         List<Repo> oldrepos = new ArrayList<>(); | ||||
|         Cursor cursor = db.query(RepoTable.NAME, | ||||
|                 new String[] {RepoTable.Cols.ADDRESS, RepoTable.Cols.IN_USE, RepoTable.Cols.SIGNING_CERT}, | ||||
|                 new String[]{RepoTable.Cols.ADDRESS, RepoTable.Cols.IN_USE, RepoTable.Cols.SIGNING_CERT}, | ||||
|                 null, null, null, null, null); | ||||
|         if (cursor != null) { | ||||
|             if (cursor.getCount() > 0) { | ||||
| @ -847,7 +859,7 @@ public class DBHelper extends SQLiteOpenHelper { | ||||
|         } | ||||
|         List<Repo> oldrepos = new ArrayList<>(); | ||||
|         Cursor cursor = db.query(RepoTable.NAME, | ||||
|                 new String[] {RepoTable.Cols.ADDRESS, RepoTable.Cols.SIGNING_CERT}, | ||||
|                 new String[]{RepoTable.Cols.ADDRESS, RepoTable.Cols.SIGNING_CERT}, | ||||
|                 null, null, null, null, null); | ||||
|         if (cursor != null) { | ||||
|             if (cursor.getCount() > 0) { | ||||
| @ -865,7 +877,7 @@ public class DBHelper extends SQLiteOpenHelper { | ||||
|         for (final Repo repo : oldrepos) { | ||||
|             ContentValues values = new ContentValues(); | ||||
|             values.put(RepoTable.Cols.FINGERPRINT, Utils.calcFingerprint(repo.signingCertificate)); | ||||
|             db.update(RepoTable.NAME, values, RepoTable.Cols.ADDRESS + " = ?", new String[] {repo.address}); | ||||
|             db.update(RepoTable.NAME, values, RepoTable.Cols.ADDRESS + " = ?", new String[]{repo.address}); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| @ -954,7 +966,7 @@ public class DBHelper extends SQLiteOpenHelper { | ||||
| 
 | ||||
|             db.execSQL(createTableDdl); | ||||
| 
 | ||||
|             String nonIdFields = TextUtils.join(", ", new String[] { | ||||
|             String nonIdFields = TextUtils.join(", ", new String[]{ | ||||
|                     RepoTable.Cols.ADDRESS, | ||||
|                     RepoTable.Cols.NAME, | ||||
|                     RepoTable.Cols.DESCRIPTION, | ||||
| @ -1235,8 +1247,8 @@ public class DBHelper extends SQLiteOpenHelper { | ||||
|     } | ||||
| 
 | ||||
|     private static boolean tableExists(SQLiteDatabase db, String table) { | ||||
|         Cursor cursor = db.query("sqlite_master", new String[] {"name"}, | ||||
|                 "type = 'table' AND name = ?", new String[] {table}, null, null, null); | ||||
|         Cursor cursor = db.query("sqlite_master", new String[]{"name"}, | ||||
|                 "type = 'table' AND name = ?", new String[]{table}, null, null, null); | ||||
| 
 | ||||
|         boolean exists = cursor.getCount() > 0; | ||||
|         cursor.close(); | ||||
|  | ||||
| @ -26,13 +26,13 @@ package org.fdroid.fdroid.data; | ||||
| import android.content.ContentValues; | ||||
| import android.database.Cursor; | ||||
| import android.text.TextUtils; | ||||
| 
 | ||||
| import org.fdroid.fdroid.FDroidApp; | ||||
| import org.fdroid.fdroid.Utils; | ||||
| import org.fdroid.fdroid.data.Schema.RepoTable.Cols; | ||||
| 
 | ||||
| import java.net.MalformedURLException; | ||||
| import java.net.URL; | ||||
| import java.util.ArrayList; | ||||
| import java.util.Arrays; | ||||
| import java.util.Collections; | ||||
| import java.util.Date; | ||||
| @ -94,6 +94,11 @@ public class Repo extends ValueObject { | ||||
|     /** Official mirrors of this repo, considered automatically interchangeable */ | ||||
|     public String[] mirrors; | ||||
| 
 | ||||
|     /** | ||||
|      * Mirrors added by the user, either by UI input or by attaching removeable storage | ||||
|      */ | ||||
|     public String[] userMirrors; | ||||
| 
 | ||||
|     /** How to treat push requests included in this repo's index XML */ | ||||
|     public int pushRequests = PUSH_REQUEST_IGNORE; | ||||
| 
 | ||||
| @ -160,6 +165,9 @@ public class Repo extends ValueObject { | ||||
|                 case Cols.MIRRORS: | ||||
|                     mirrors = Utils.parseCommaSeparatedString(cursor.getString(i)); | ||||
|                     break; | ||||
|                 case Cols.USER_MIRRORS: | ||||
|                     userMirrors = Utils.parseCommaSeparatedString(cursor.getString(i)); | ||||
|                     break; | ||||
|                 case Cols.PUSH_REQUESTS: | ||||
|                     pushRequests = cursor.getInt(i); | ||||
|                     break; | ||||
| @ -297,26 +305,43 @@ public class Repo extends ValueObject { | ||||
|             mirrors = Utils.parseCommaSeparatedString(values.getAsString(Cols.MIRRORS)); | ||||
|         } | ||||
| 
 | ||||
|         if (values.containsKey(Cols.USER_MIRRORS)) { | ||||
|             userMirrors = Utils.parseCommaSeparatedString(values.getAsString(Cols.USER_MIRRORS)); | ||||
|         } | ||||
| 
 | ||||
|         if (values.containsKey(Cols.PUSH_REQUESTS)) { | ||||
|             pushRequests = toInt(values.getAsInteger(Cols.PUSH_REQUESTS)); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public boolean hasMirrors() { | ||||
|         return mirrors != null && mirrors.length > 1; | ||||
|         return (mirrors != null && mirrors.length > 1) | ||||
|                 || (userMirrors != null && userMirrors.length > 0); | ||||
|     } | ||||
| 
 | ||||
|     public List<String> getMirrorList() { | ||||
|         final ArrayList<String> allMirrors = new ArrayList<String>(); | ||||
|         if (userMirrors != null) { | ||||
|             allMirrors.addAll(Arrays.asList(userMirrors)); | ||||
|         } | ||||
|         if (mirrors != null) { | ||||
|             allMirrors.addAll(Arrays.asList(mirrors)); | ||||
|         } | ||||
|         return allMirrors; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the number of available mirrors, including the canonical repo. | ||||
|      */ | ||||
|     public int getMirrorCount() { | ||||
|         int count = 0; | ||||
|         if (mirrors != null && mirrors.length > 1) { | ||||
|             for (String m: mirrors) { | ||||
|                 if (!m.equals(address)) { | ||||
|                     if (FDroidApp.isUsingTor()) { | ||||
|         for (String m : getMirrorList()) { | ||||
|             if (!m.equals(address)) { | ||||
|                 if (FDroidApp.isUsingTor()) { | ||||
|                     count++; | ||||
|                 } else { | ||||
|                     if (!m.contains(".onion")) { | ||||
|                         count++; | ||||
|                     } else { | ||||
|                         if (!m.contains(".onion")) { | ||||
|                             count++; | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| @ -328,7 +353,7 @@ public class Repo extends ValueObject { | ||||
|         if (TextUtils.isEmpty(lastWorkingMirror)) { | ||||
|             lastWorkingMirror = address; | ||||
|         } | ||||
|         List<String> shuffledMirrors = Arrays.asList(mirrors); | ||||
|         List<String> shuffledMirrors = getMirrorList(); | ||||
|         Collections.shuffle(shuffledMirrors); | ||||
|         if (shuffledMirrors.size() > 1) { | ||||
|             for (String m : shuffledMirrors) { | ||||
|  | ||||
| @ -362,12 +362,13 @@ public interface Schema { | ||||
|             String TIMESTAMP    = "timestamp"; | ||||
|             String ICON         = "icon"; | ||||
|             String MIRRORS      = "mirrors"; | ||||
|             String USER_MIRRORS = "userMirrors"; | ||||
|             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, PUSH_REQUESTS, | ||||
|                     USERNAME, PASSWORD, TIMESTAMP, ICON, MIRRORS, USER_MIRRORS, PUSH_REQUESTS, | ||||
|             }; | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @ -61,12 +61,15 @@ import org.fdroid.fdroid.data.Repo; | ||||
| import org.fdroid.fdroid.data.RepoProvider; | ||||
| import org.fdroid.fdroid.data.Schema.RepoTable; | ||||
| 
 | ||||
| import java.io.File; | ||||
| import java.io.IOException; | ||||
| import java.net.HttpURLConnection; | ||||
| import java.net.MalformedURLException; | ||||
| import java.net.URI; | ||||
| import java.net.URISyntaxException; | ||||
| import java.net.URL; | ||||
| import java.util.Arrays; | ||||
| import java.util.HashMap; | ||||
| import java.util.Locale; | ||||
| 
 | ||||
| @SuppressWarnings("LineLength") | ||||
| @ -76,7 +79,7 @@ public class ManageReposActivity extends AppCompatActivity implements LoaderMana | ||||
|     private static final String DEFAULT_NEW_REPO_TEXT = "https://"; | ||||
| 
 | ||||
|     private enum AddRepoState { | ||||
|         DOESNT_EXIST, EXISTS_FINGERPRINT_MISMATCH, EXISTS_FINGERPRINT_MATCH, | ||||
|         DOESNT_EXIST, EXISTS_FINGERPRINT_MISMATCH, EXISTS_ADD_MIRROR, | ||||
|         EXISTS_DISABLED, EXISTS_ENABLED, EXISTS_UPGRADABLE_TO_SIGNED, INVALID_URL, | ||||
|         IS_SWAP | ||||
|     } | ||||
| @ -213,18 +216,35 @@ public class ManageReposActivity extends AppCompatActivity implements LoaderMana | ||||
|     private class AddRepo { | ||||
| 
 | ||||
|         private final Context context; | ||||
|         private final HashMap<String, Repo> urlRepoMap = new HashMap<>(); | ||||
|         private final HashMap<String, Repo> fingerprintRepoMap = new HashMap<>(); | ||||
|         private final AlertDialog addRepoDialog; | ||||
| 
 | ||||
|         private final TextView overwriteMessage; | ||||
|         private final ColorStateList defaultTextColour; | ||||
|         private final Button addButton; | ||||
| 
 | ||||
|         private AddRepoState addRepoState; | ||||
| 
 | ||||
|         /** | ||||
|          * Create new instance, setup GUI, and build maps for quickly looking | ||||
|          * up repos based on URL or fingerprint.  These need to be in maps | ||||
|          * since the user input is validated as they are typing.  This also | ||||
|          * checks that the repo type matches, e.g. "repo" or "archive". | ||||
|          */ | ||||
|         AddRepo(String newAddress, String newFingerprint, final String username, final String password) { | ||||
| 
 | ||||
|             context = ManageReposActivity.this; | ||||
| 
 | ||||
|             for (Repo repo : RepoProvider.Helper.all(context)) { | ||||
|                 urlRepoMap.put(repo.address, repo); | ||||
|                 for (String url : repo.getMirrorList()) { | ||||
|                     urlRepoMap.put(url, repo); | ||||
|                 } | ||||
|                 if (TextUtils.equals(getRepoType(newAddress), getRepoType(repo.address))) { | ||||
|                     fingerprintRepoMap.put(repo.fingerprint, repo); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             final View view = getLayoutInflater().inflate(R.layout.addrepo, null); | ||||
|             addRepoDialog = new AlertDialog.Builder(context).setView(view).create(); | ||||
|             final EditText uriEditText = (EditText) view.findViewById(R.id.edit_uri); | ||||
| @ -297,7 +317,7 @@ public class ManageReposActivity extends AppCompatActivity implements LoaderMana | ||||
| 
 | ||||
|                                 case EXISTS_DISABLED: | ||||
|                                 case EXISTS_UPGRADABLE_TO_SIGNED: | ||||
|                                 case EXISTS_FINGERPRINT_MATCH: | ||||
|                                 case EXISTS_ADD_MIRROR: | ||||
|                                     updateAndEnableExistingRepo(url, fp); | ||||
|                                     finishedAddingRepo(); | ||||
|                                     break; | ||||
| @ -347,9 +367,31 @@ public class ManageReposActivity extends AppCompatActivity implements LoaderMana | ||||
|             validateRepoDetails(newAddress == null ? "" : newAddress, newFingerprint == null ? "" : newFingerprint); | ||||
|         } | ||||
| 
 | ||||
|         /** | ||||
|          * Gets the repo type as represented by the final segment of the path. This is | ||||
|          * a bit trickier with {@code content://} URLs, since they might have | ||||
|          * encoded "/" chars in it, for example: | ||||
|          * {@code content://authority/tree/313E-1F1C%3A/document/313E-1F1C%3Aguardianproject.info%2Ffdroid%2Frepo} | ||||
|          */ | ||||
|         private String getRepoType(String url) { | ||||
|             String last = Uri.parse(url).getLastPathSegment(); | ||||
|             if (last == null) { | ||||
|                 return ""; | ||||
|             } else { | ||||
|                 return new File(last).getName(); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         /** | ||||
|          * Compare the repo and the fingerprint against existing repositories, to see if this | ||||
|          * repo matches and display a relevant message to the user if that is the case. | ||||
|          * repo matches and display a relevant message to the user if that is the case. There | ||||
|          * are many different cases to handle: | ||||
|          * <ul> | ||||
|          * <li> a signed repo with a {@link Repo#address URL} and fingerprint that matches | ||||
|          * <li> a signed repo with a matching fingerprint and URL that matches a mirror | ||||
|          * <li> a signed repo with a matching fingerprint, but the URL doesn't match any known mirror | ||||
|          * <li>an unsigned repo and no fingerprint was supplied | ||||
|          * </ul> | ||||
|          */ | ||||
|         private void validateRepoDetails(@NonNull String uri, @NonNull String fingerprint) { | ||||
| 
 | ||||
| @ -361,7 +403,10 @@ public class ManageReposActivity extends AppCompatActivity implements LoaderMana | ||||
|                 // to the user until they try to save the repo. | ||||
|             } | ||||
| 
 | ||||
|             final Repo repo = !TextUtils.isEmpty(uri) ? RepoProvider.Helper.findByAddress(context, uri) : null; | ||||
|             Repo repo = fingerprintRepoMap.get(fingerprint); | ||||
|             if (repo == null) { | ||||
|                 repo = urlRepoMap.get(uri); | ||||
|             } | ||||
| 
 | ||||
|             if (repo == null) { | ||||
|                 repoDoesntExist(repo); | ||||
| @ -373,9 +418,10 @@ public class ManageReposActivity extends AppCompatActivity implements LoaderMana | ||||
|                 } else if (repo.fingerprint != null && !repo.fingerprint.equalsIgnoreCase(fingerprint)) { | ||||
|                     repoFingerprintDoesntMatch(repo); | ||||
|                 } else { | ||||
|                     // Could be either an unsigned repo, and no fingerprint was supplied, | ||||
|                     // or it could be a signed repo with a matching fingerprint. | ||||
|                     if (repo.inuse) { | ||||
|                     if (!TextUtils.equals(repo.address, uri) | ||||
|                             && !repo.getMirrorList().contains(uri)) { | ||||
|                         repoExistsAddMirror(repo); | ||||
|                     } else if (repo.inuse) { | ||||
|                         repoExistsAndEnabled(repo); | ||||
|                     } else { | ||||
|                         repoExistsAndDisabled(repo); | ||||
| @ -659,11 +705,34 @@ public class ManageReposActivity extends AppCompatActivity implements LoaderMana | ||||
|             } | ||||
| 
 | ||||
|             Utils.debugLog(TAG, "Enabling existing repo: " + url); | ||||
|             Repo repo = RepoProvider.Helper.findByAddress(context, url); | ||||
|             Repo repo = fingerprintRepoMap.get(fingerprint); | ||||
|             if (repo == null) { | ||||
|                 repo = RepoProvider.Helper.findByAddress(context, url); | ||||
|             } | ||||
| 
 | ||||
|             ContentValues values = new ContentValues(2); | ||||
|             values.put(RepoTable.Cols.IN_USE, 1); | ||||
|             values.put(RepoTable.Cols.FINGERPRINT, fingerprint); | ||||
|             if (!TextUtils.equals(url, repo.address)) { | ||||
|                 boolean addUserMirror = true; | ||||
|                 for (String mirror : repo.getMirrorList()) { | ||||
|                     if (TextUtils.equals(mirror, url)) { | ||||
|                         addUserMirror = false; | ||||
|                     } | ||||
|                 } | ||||
|                 if (addUserMirror) { | ||||
|                     if (repo.userMirrors == null) { | ||||
|                         repo.userMirrors = new String[]{url}; | ||||
|                     } else { | ||||
|                         int last = repo.userMirrors.length; | ||||
|                         repo.userMirrors = Arrays.copyOf(repo.userMirrors, last); | ||||
|                         repo.userMirrors[last] = url; | ||||
|                     } | ||||
|                     values.put(RepoTable.Cols.USER_MIRRORS, Utils.serializeCommaSeparatedString(repo.userMirrors)); | ||||
|                 } | ||||
|             } | ||||
|             RepoProvider.Helper.update(context, repo, values); | ||||
| 
 | ||||
|             notifyDataSetChanged(); | ||||
|             finishedAddingRepo(); | ||||
|         } | ||||
|  | ||||
| @ -109,6 +109,7 @@ public class RepoDetailsActivity extends ActionBarActivity { | ||||
|                 RepoTable.Cols.ADDRESS, | ||||
|                 RepoTable.Cols.FINGERPRINT, | ||||
|                 RepoTable.Cols.MIRRORS, | ||||
|                 RepoTable.Cols.USER_MIRRORS, | ||||
|         }; | ||||
|         repo = RepoProvider.Helper.findById(this, repoId, projection); | ||||
| 
 | ||||
| @ -340,6 +341,19 @@ public class RepoDetailsActivity extends ActionBarActivity { | ||||
|             } | ||||
|             officialMirrorsText.setText(builder.toString()); | ||||
|         } | ||||
|         if (repo.userMirrors != null) { | ||||
|             TextView userMirrorsLabel = (TextView) repoView.findViewById(R.id.label_user_mirrors); | ||||
|             userMirrorsLabel.setVisibility(View.VISIBLE); | ||||
|             TextView userMirrorsText = (TextView) 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'); | ||||
|             } | ||||
|             userMirrorsText.setText(builder.toString()); | ||||
|         } | ||||
| 
 | ||||
|         name.setText(repo.name); | ||||
| 
 | ||||
|  | ||||
| @ -116,6 +116,7 @@ This often occurs with apps installed via Google Play or other sources, if they | ||||
|     <string name="no">No</string> | ||||
|     <string name="repo_add_title">Add new repository</string> | ||||
|     <string name="repo_add_add">Add</string> | ||||
|     <string name="repo_add_mirror">Add mirror</string> | ||||
|     <string name="links">Links</string> | ||||
|     <string name="versions">Versions</string> | ||||
|     <string name="more">More</string> | ||||
| @ -138,6 +139,7 @@ This often occurs with apps installed via Google Play or other sources, if they | ||||
|     <string name="repo_exists_enable">%1$s is already setup, confirm that you want to re-enable it.</string> | ||||
|     <string name="repo_exists_and_enabled">%1$s is already setup and enabled.</string> | ||||
|     <string name="repo_delete_to_overwrite">First delete %1$s in order to add this with a conflicting key.</string> | ||||
|     <string name="repo_exists_add_mirror">This is a copy of %1$s, add it as a mirror?</string> | ||||
|     <string name="bad_fingerprint">Bad fingerprint</string> | ||||
|     <string name="invalid_url">This is not a valid URL.</string> | ||||
|     <string name="malformed_repo_uri">Ignoring malformed repo URI: %s</string> | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Hans-Christoph Steiner
						Hans-Christoph Steiner