Merge branch 'fix/issue-5-guess-repo-path' of https://gitlab.com/pserwylo/fdroidclient

This commit is contained in:
Daniel Martí 2015-03-15 16:58:06 +01:00
commit 06ae1bc044
3 changed files with 299 additions and 151 deletions

View File

@ -1,10 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="6dp">
<LinearLayout
android:id="@+id/add_repo_form"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -44,7 +50,18 @@
android:textAppearance="@android:style/TextAppearance.Medium"
android:visibility="gone" />
</LinearLayout><!--
</LinearLayout>
<TextView
android:padding="10dp"
android:textSize="16sp"
android:id="@+id/text_searching_for_repo"
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:text="Searching for repository at\nhttps://www.example.com/fdroid/repo/" />
</RelativeLayout><!--
* Copyright (C) 2009 Roberto Jacinto
* roberto.jacinto@caixamagica.pt
*

View File

@ -251,6 +251,8 @@
<string name="repo_disabled_notification">Disabled "%1$s".\n\nYou will
need to re-enable this repository to install apps from it.
</string>
<string name="repo_added">Saved F-Droid repository %1$s</string>
<string name="repo_searching_address">Looking for F-Droid repository at\n%1$s</string>
<string name="minsdk_or_later">%s or later</string>
<string name="up_to_maxsdk">up to %s</string>
<string name="minsdk_up_to_maxsdk">%1$s up to %2$s</string>

View File

@ -30,6 +30,7 @@ import android.database.Cursor;
import android.net.Uri;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v4.app.FragmentManager;
@ -53,6 +54,9 @@ import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.impl.client.DefaultHttpClient;
import org.fdroid.fdroid.FDroid;
import org.fdroid.fdroid.FDroidApp;
import org.fdroid.fdroid.Preferences;
@ -68,6 +72,7 @@ import org.fdroid.fdroid.net.MDnsHelper.DiscoveredRepo;
import org.fdroid.fdroid.net.MDnsHelper.RepoScanListAdapter;
import org.fdroid.fdroid.views.fragments.RepoDetailsFragment;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Date;
@ -83,9 +88,8 @@ public class ManageReposActivity extends ActionBarActivity {
* we finish with, to signify that we want the main list of apps updated.
*/
public static final String REQUEST_UPDATE = "update";
private static final String TAG = "org.fdroid.fdroid.views.ManageReposActivity";
private RepoListFragment listFragment;
private AlertDialog addRepoDialog;
private static final String DEFAULT_NEW_REPO_TEXT = "https://";
private enum PositiveAction {
@ -123,9 +127,8 @@ public class ManageReposActivity extends ActionBarActivity {
*/
setContentView(new LinearLayout(this));
listFragment = new RepoListFragment();
fm.beginTransaction()
.add(android.R.id.content, listFragment)
.add(android.R.id.content, new RepoListFragment())
.commit();
}
@ -306,42 +309,40 @@ public class ManageReposActivity extends ActionBarActivity {
}
private void showAddRepo(String newAddress, String newFingerprint) {
final View view = getLayoutInflater().inflate(R.layout.addrepo, null);
addRepoDialog = new AlertDialog.Builder(this).setView(view).create();
final EditText uriEditText = (EditText) view.findViewById(R.id.edit_uri);
final EditText fingerprintEditText = (EditText) view
.findViewById(R.id.edit_fingerprint);
new AddRepo(newAddress, newFingerprint);
}
/*
* If the "add new repo" dialog is launched by an action outside of
* FDroid, i.e. a URL, then check to see if any existing repos match,
* and change the action accordingly.
/**
* Utility class to encapsulate the process of adding a new repo (or an existing one,
* depending on if the incoming address is the same as a previous repo). It is responsible
* for managing the lifecycle of adding a repo:
* * Showing the add dialog
* * Deciding whether to add a new repo or update an existing one
* * Search for repos at common suffixes (/, /fdroid/repo, /repo)
*/
private class AddRepo {
private final Context context;
private final AlertDialog addRepoDialog;
public AddRepo(String newAddress, String newFingerprint) {
context = ManageReposActivity.this;
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);
final EditText fingerprintEditText = (EditText) view.findViewById(R.id.edit_fingerprint);
// If the "add new repo" dialog is launched by an action outside of
// FDroid, i.e. a URL, then check to see if any existing repos match,
// and change the action accordingly.
final Repo repo = (newAddress != null && isImportingRepo)
? RepoProvider.Helper.findByAddress(this, newAddress)
? RepoProvider.Helper.findByAddress(context, newAddress)
: null;
addRepoDialog.setIcon(android.R.drawable.ic_menu_add);
addRepoDialog.setTitle(getString(R.string.repo_add_title));
addRepoDialog.setButton(DialogInterface.BUTTON_POSITIVE,
getString(R.string.repo_add_add),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
String fp = fingerprintEditText.getText().toString();
// the DB uses null for no fingerprint but the above
// code returns "" rather than null if its blank
if (fp.equals(""))
fp = null;
if (positiveAction == PositiveAction.ADD_NEW)
createNewRepo(uriEditText.getText().toString(), fp);
else if (positiveAction == PositiveAction.ENABLE)
createNewRepo(repo);
}
});
addRepoDialog.setButton(DialogInterface.BUTTON_NEGATIVE,
getString(R.string.cancel),
@ -351,8 +352,54 @@ public class ManageReposActivity extends ActionBarActivity {
dialog.dismiss();
}
});
// HACK:
// After adding a new repo, need to show feedback to the user.
// This could use either a fresh dialog with some status messages,
// or modify the existing one. Either way is hard with the default API.
// A fresh dialog is impossible until after the dialog is dismissed,
// which happens after calling our OnClickListener. Thus we'd have to
// remember which button was pressed, wait for the dialog to be dismissed,
// then create a new one.
// Editing the existing dialog is preferable, but the dialog is dismissed
// after our onclick listener. We don't want this, we want the dialog to
// hang around so we can show further info on it.
//
// Thus, the hack described at http://stackoverflow.com/a/15619098 is implemented.
addRepoDialog.setButton(DialogInterface.BUTTON_POSITIVE,
getString(R.string.repo_add_add),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
addRepoDialog.show();
// This must be *after* addRepoDialog.show() otherwise getButtion() returns null:
// https://code.google.com/p/android/issues/detail?id=6360
addRepoDialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View v) {
String fp = fingerprintEditText.getText().toString();
// the DB uses null for no fingerprint but the above
// code returns "" rather than null if its blank
if (fp.equals(""))
fp = null;
if (positiveAction == PositiveAction.ADD_NEW)
prepareToCreateNewRepo(uriEditText.getText().toString(), fp);
else if (positiveAction == PositiveAction.ENABLE) {
enableExistingRepo(repo);
finishedAddingRepo();
}
}
}
);
final TextView overwriteMessage = (TextView) view.findViewById(R.id.overwrite_message);
overwriteMessage.setVisibility(View.GONE);
if (repo == null) {
@ -371,13 +418,10 @@ public class ManageReposActivity extends ActionBarActivity {
addButton.setText(R.string.add_key);
positiveAction = PositiveAction.ADD_NEW;
} else if (newFingerprint == null || newFingerprint.equals(repo.fingerprint)) {
// this entry already exists and is not enabled, offer to
// enable
// it
// this entry already exists and is not enabled, offer to enable it
if (repo.inuse) {
addRepoDialog.dismiss();
Toast.makeText(this, R.string.repo_exists_and_enabled,
Toast.LENGTH_LONG).show();
Toast.makeText(context, R.string.repo_exists_and_enabled, Toast.LENGTH_LONG).show();
return;
} else {
overwriteMessage.setText(R.string.repo_exists_enable);
@ -400,9 +444,8 @@ public class ManageReposActivity extends ActionBarActivity {
fingerprintEditText.setText(newFingerprint);
if (newAddress != null) {
// This trick of emptying text then appending,
// rather than just setting in the first place,
// is neccesary to move the cursor to the end of the input.
// This trick of emptying text then appending, rather than just setting in
// the first place, is necessary to move the cursor to the end of the input.
uriEditText.setText("");
uriEditText.append(newAddress);
}
@ -411,24 +454,106 @@ public class ManageReposActivity extends ActionBarActivity {
/**
* Adds a new repo to the database.
*/
private void prepareToCreateNewRepo(final String originalAddress, final String fingerprint) {
addRepoDialog.findViewById(R.id.add_repo_form).setVisibility(View.GONE);
addRepoDialog.getButton(AlertDialog.BUTTON_POSITIVE).setVisibility(View.GONE);
final TextView textSearching = (TextView) addRepoDialog.findViewById(R.id.text_searching_for_repo);
textSearching.setText(getString(R.string.repo_searching_address, originalAddress));
final AsyncTask<String, String, String> checker = new AsyncTask<String, String, String>() {
@Override
protected String doInBackground(String... params) {
String originalAddress = params[0];
String[] pathsToCheck = {"", "fdroid/repo", "repo"};
for (String path : pathsToCheck) {
Log.d(TAG, "Checking for repo at " + originalAddress + " with suffix \"" + path + "\".");
Uri.Builder builder = Uri.parse(originalAddress).buildUpon().appendEncodedPath(path);
String addressWithoutIndex = builder.build().toString();
publishProgress(addressWithoutIndex);
Uri uri = builder.appendPath("index.jar").build();
try {
if (checkForRepository(uri)) {
Log.i(TAG, "Found F-Droid repo at " + addressWithoutIndex);
return addressWithoutIndex;
}
} catch (IOException e) {
Log.e(TAG, "Error while searching for repo at " + addressWithoutIndex + ": " + e.getMessage());
return originalAddress;
}
if (isCancelled()) {
Log.d(TAG, "Not checking any more repo addresses, because process was skipped.");
break;
}
}
return originalAddress;
}
private boolean checkForRepository(Uri indexUri) throws IOException {
HttpClient client = new DefaultHttpClient();
HttpHead head = new HttpHead(indexUri.toString());
return client.execute(head).getStatusLine().getStatusCode() == 200;
}
@Override
protected void onProgressUpdate(String... values) {
String address = values[0];
textSearching.setText(getString(R.string.repo_searching_address, address));
}
@Override
protected void onPostExecute(String newAddress) {
if (addRepoDialog.isShowing()) {
createNewRepo(newAddress, fingerprint);
}
}
};
Button skip = addRepoDialog.getButton(AlertDialog.BUTTON_NEGATIVE);
skip.setText(getString(R.string.skip));
skip.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Still proceed with adding the repo, just don't bother searching for
// a better alternative than the one provided.
// The reason for this is that if they are not connected to the internet,
// or their internet is playing up, then you'd have to wait for several
// connection timeouts before being able to proceed.
createNewRepo(originalAddress, fingerprint);
checker.cancel(false);
}
});
checker.execute(originalAddress);
}
private void createNewRepo(String address, String fingerprint) {
ContentValues values = new ContentValues(2);
values.put(RepoProvider.DataColumns.ADDRESS, address);
if (fingerprint != null && fingerprint.length() > 0) {
values.put(RepoProvider.DataColumns.FINGERPRINT,
fingerprint.toUpperCase(Locale.ENGLISH));
values.put(RepoProvider.DataColumns.FINGERPRINT, fingerprint.toUpperCase(Locale.ENGLISH));
}
RepoProvider.Helper.insert(this, values);
RepoProvider.Helper.insert(context, values);
finishedAddingRepo();
Toast.makeText(ManageReposActivity.this, getString(R.string.repo_added, address), Toast.LENGTH_SHORT).show();
}
/**
* Seeing as this repo already exists, we will force it to be enabled again.
*/
private void createNewRepo(Repo repo) {
private void enableExistingRepo(Repo repo) {
ContentValues values = new ContentValues(1);
values.put(RepoProvider.DataColumns.IN_USE, 1);
RepoProvider.Helper.update(this, repo, values);
RepoProvider.Helper.update(context, repo, values);
repo.inuse = true;
finishedAddingRepo();
}
@ -440,13 +565,17 @@ public class ManageReposActivity extends ActionBarActivity {
*/
private void finishedAddingRepo() {
changed = true;
addRepoDialog = null;
if (addRepoDialog.isShowing()) {
addRepoDialog.dismiss();
}
if (isImportingRepo) {
setResult(Activity.RESULT_OK);
finish();
}
}
}
private void addRepoFromIntent(Intent intent) {
/* an URL from a click, NFC, QRCode scan, etc */
NewRepoConfig newRepoConfig = new NewRepoConfig(this, intent);
@ -543,7 +672,7 @@ public class ManageReposActivity extends ActionBarActivity {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
TextView textLastUpdate = new TextView(getActivity());
long lastUpdate = prefs.getLong(Preferences.PREF_UPD_LAST, 0);
String lastUpdateCheck = "";
String lastUpdateCheck;
if (lastUpdate == 0) {
lastUpdateCheck = getString(R.string.never);
} else {