diff --git a/F-Droid/res/drawable-hdpi/ic_clear.png b/F-Droid/res/drawable-hdpi/ic_clear.png
new file mode 100644
index 000000000..1a9cd75a0
Binary files /dev/null and b/F-Droid/res/drawable-hdpi/ic_clear.png differ
diff --git a/F-Droid/res/drawable-mdpi/ic_clear.png b/F-Droid/res/drawable-mdpi/ic_clear.png
new file mode 100644
index 000000000..40a1a84e3
Binary files /dev/null and b/F-Droid/res/drawable-mdpi/ic_clear.png differ
diff --git a/F-Droid/res/drawable-xhdpi/ic_clear.png b/F-Droid/res/drawable-xhdpi/ic_clear.png
new file mode 100644
index 000000000..6bc437298
Binary files /dev/null and b/F-Droid/res/drawable-xhdpi/ic_clear.png differ
diff --git a/F-Droid/res/drawable-xxhdpi/ic_clear.png b/F-Droid/res/drawable-xxhdpi/ic_clear.png
new file mode 100644
index 000000000..51b4401ca
Binary files /dev/null and b/F-Droid/res/drawable-xxhdpi/ic_clear.png differ
diff --git a/F-Droid/res/drawable-xxxhdpi/ic_clear.png b/F-Droid/res/drawable-xxxhdpi/ic_clear.png
new file mode 100644
index 000000000..df42feecb
Binary files /dev/null and b/F-Droid/res/drawable-xxxhdpi/ic_clear.png differ
diff --git a/F-Droid/res/layout/app_details_header.xml b/F-Droid/res/layout/app_details_header.xml
index e36319c54..99f2cb936 100644
--- a/F-Droid/res/layout/app_details_header.xml
+++ b/F-Droid/res/layout/app_details_header.xml
@@ -16,7 +16,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-->
-
@@ -109,7 +110,50 @@
-
+
+
+
+
+
+
+
+
+
diff --git a/F-Droid/res/layout/repodetails.xml b/F-Droid/res/layout/repodetails.xml
index 86cb9b1a2..921612d11 100644
--- a/F-Droid/res/layout/repodetails.xml
+++ b/F-Droid/res/layout/repodetails.xml
@@ -1,32 +1,32 @@
-
-
+
+
+
+
-
+ android:textStyle="bold"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:layout_alignParentTop="true" />
-
+
+
+
+
+
diff --git a/F-Droid/res/menu/main.xml b/F-Droid/res/menu/main.xml
index 881e99869..9c8a76dc0 100644
--- a/F-Droid/res/menu/main.xml
+++ b/F-Droid/res/menu/main.xml
@@ -7,11 +7,6 @@
android:icon="@drawable/ic_search_white"
android:title="@string/menu_search"
app:showAsAction="always"/>
-
+
-
+
\ No newline at end of file
diff --git a/F-Droid/res/values/strings.xml b/F-Droid/res/values/strings.xml
index 8fae272f7..770a1b222 100644
--- a/F-Droid/res/values/strings.xml
+++ b/F-Droid/res/values/strings.xml
@@ -210,6 +210,7 @@
- Percentage complete (int between 0-100)
-->
Downloading\n%2$s / %3$s (%4$d%%) from\n%1$s
+ Updating repositories
Processing %2$s / %3$s (%4$d%%) from %1$s
Connecting to\n%1$s
Checking apps compatibility with your deviceā¦
@@ -372,4 +373,13 @@
NEW:
Provided by %1$s.
+ Downloading...
+
+
+
- B
+ - KiB
+ - MiB
+ - GiB
+ - TiB
+
diff --git a/F-Droid/src/org/fdroid/fdroid/AppDetails.java b/F-Droid/src/org/fdroid/fdroid/AppDetails.java
index 76d17a2a0..6500551b0 100644
--- a/F-Droid/src/org/fdroid/fdroid/AppDetails.java
+++ b/F-Droid/src/org/fdroid/fdroid/AppDetails.java
@@ -21,28 +21,30 @@
package org.fdroid.fdroid;
import android.app.Activity;
-import android.app.ProgressDialog;
import android.bluetooth.BluetoothAdapter;
import android.content.ActivityNotFoundException;
+import android.content.BroadcastReceiver;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.database.ContentObserver;
import android.graphics.Bitmap;
import android.net.Uri;
-import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
+import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.support.v4.app.ListFragment;
import android.support.v4.app.NavUtils;
+import android.support.v4.content.LocalBroadcastManager;
import android.support.v4.view.MenuItemCompat;
-import android.support.v7.app.ActionBarActivity;
import android.support.v7.app.AlertDialog;
+import android.support.v7.app.AppCompatActivity;
import android.text.Html;
import android.text.Layout;
import android.text.Selection;
@@ -63,9 +65,11 @@ import android.view.Window;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.FrameLayout;
+import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
+import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
@@ -90,6 +94,7 @@ import org.fdroid.fdroid.net.Downloader;
import java.io.File;
import java.security.NoSuchAlgorithmException;
+import java.text.DecimalFormat;
import java.util.Iterator;
import java.util.List;
@@ -113,7 +118,7 @@ interface AppInstallListener {
void removeApk(String packageName);
}
-public class AppDetails extends ActionBarActivity implements ProgressListener, AppDetailsData, AppInstallListener {
+public class AppDetails extends AppCompatActivity implements ProgressListener, AppDetailsData, AppInstallListener {
private static final String TAG = "AppDetails";
@@ -124,7 +129,6 @@ public class AppDetails extends ActionBarActivity implements ProgressListener, A
private FDroidApp fdroidApp;
private ApkListAdapter adapter;
- private ProgressDialog progressDialog;
private static class ViewHolder {
TextView version;
@@ -316,6 +320,7 @@ public class AppDetails extends ActionBarActivity implements ProgressListener, A
private App app;
private PackageManager mPm;
private ApkDownloader downloadHandler;
+ private LocalBroadcastManager localBroadcastManager;
private boolean startingIgnoreAll;
private int startingIgnoreThis;
@@ -323,11 +328,14 @@ public class AppDetails extends ActionBarActivity implements ProgressListener, A
private final Context mctx = this;
private Installer installer;
+
+ private AppDetailsHeaderFragment mHeaderFragment;
+
/**
* Stores relevant data that we want to keep track of when destroying the activity
* with the expectation of it being recreated straight away (e.g. after an
* orientation change). One of the major things is that we want the download thread
- * to stay active, but for it not to trigger any UI stuff (e.g. progress dialogs)
+ * to stay active, but for it not to trigger any UI stuff (e.g. progress bar)
* between the activity being destroyed and recreated.
*/
private static class ConfigurationChangeHelper {
@@ -418,13 +426,7 @@ public class AppDetails extends ActionBarActivity implements ProgressListener, A
listFragment.removeSummaryHeader();
}
- // Spinner seems to default to visible on Android 4.0.3 and 4.0.4
- // https://gitlab.com/fdroid/fdroidclient/issues/75
- // Can't put this in onResume(), because that is called on return from asking
- // the user permission to use su (in which case we still want to show the
- // progress indicator after returning from that prompt).
- setSupportProgressBarIndeterminateVisibility(false);
-
+ localBroadcastManager = LocalBroadcastManager.getInstance(this);
}
// The signature of the installed version.
@@ -442,39 +444,35 @@ public class AppDetails extends ActionBarActivity implements ProgressListener, A
myAppObserver);
}
- @Override
- protected void onResume() {
- super.onResume();
- if (downloadHandler != null) {
- if (downloadHandler.isComplete()) {
- downloadCompleteInstallApk();
- } else {
- downloadHandler.setProgressListener(this);
-
- // Show the progress dialog, if for no other reason than to prevent them attempting
- // to download again (i.e. we force them to touch 'cancel' before they can access
- // the rest of the activity).
- Log.d(TAG, "Showing dialog to user after resuming app details view, because a download was previously in progress");
- updateProgressDialog();
- }
- }
- }
-
@Override
protected void onResumeFragments() {
super.onResumeFragments();
refreshApkList();
refreshHeader();
supportInvalidateOptionsMenu();
+ if (downloadHandler != null) {
+ if (downloadHandler.isComplete()) {
+ downloadCompleteInstallApk();
+ } else {
+ localBroadcastManager.registerReceiver(downloaderProgressReceiver,
+ new IntentFilter(Downloader.LOCAL_ACTION_PROGRESS));
+ downloadHandler.setProgressListener(this);
+
+ if (downloadHandler.getTotalBytes() == 0)
+ mHeaderFragment.startProgress();
+ else
+ mHeaderFragment.updateProgress(downloadHandler.getBytesRead(), downloadHandler.getTotalBytes());
+ }
+ }
}
/**
- * Remove progress listener, suppress progress dialog, set downloadHandler to null.
+ * Remove progress listener, suppress progress bar, set downloadHandler to null.
*/
private void cleanUpFinishedDownload() {
if (downloadHandler != null) {
downloadHandler.removeProgressListener();
- removeProgressDialog();
+ mHeaderFragment.removeProgress();
downloadHandler = null;
}
}
@@ -485,7 +483,7 @@ public class AppDetails extends ActionBarActivity implements ProgressListener, A
*/
private void downloadCompleteInstallApk() {
if (downloadHandler != null) {
- installApk(downloadHandler.localFile(), downloadHandler.getApk().id);
+ installApk(downloadHandler.localFile());
cleanUpFinishedDownload();
}
}
@@ -504,13 +502,23 @@ public class AppDetails extends ActionBarActivity implements ProgressListener, A
setIgnoreUpdates(app.id, app.ignoreAllUpdates, app.ignoreThisUpdate);
}
+ localBroadcastManager.unregisterReceiver(downloaderProgressReceiver);
if (downloadHandler != null) {
downloadHandler.removeProgressListener();
}
- removeProgressDialog();
+ mHeaderFragment.removeProgress();
}
+ private final BroadcastReceiver downloaderProgressReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (mHeaderFragment != null)
+ mHeaderFragment.updateProgress(intent.getIntExtra(Downloader.EXTRA_BYTES_READ, -1),
+ intent.getIntExtra(Downloader.EXTRA_TOTAL_BYTES, -1));
+ }
+ };
+
private void onAppChanged() {
if (!reset(app.id)) {
AppDetails.this.finish();
@@ -553,13 +561,6 @@ public class AppDetails extends ActionBarActivity implements ProgressListener, A
super.onDestroy();
}
- private void removeProgressDialog() {
- if (progressDialog != null) {
- progressDialog.dismiss();
- progressDialog = null;
- }
- }
-
// Reset the display and list contents. Used when entering the activity, and
// also when something has been installed/uninstalled.
// Return true if the app was found, false otherwise.
@@ -619,9 +620,9 @@ public class AppDetails extends ActionBarActivity implements ProgressListener, A
}
private void refreshHeader() {
- AppDetailsHeaderFragment headerFragment = (AppDetailsHeaderFragment)
+ mHeaderFragment = (AppDetailsHeaderFragment)
getSupportFragmentManager().findFragmentById(R.id.header);
- headerFragment.refresh();
+ mHeaderFragment.updateViews();
}
@Override
@@ -725,8 +726,8 @@ public class AppDetails extends ActionBarActivity implements ProgressListener, A
}
@Override
- public boolean onTouchEvent(TextView widget, Spannable buffer,
- MotionEvent event) {
+ public boolean onTouchEvent(@NonNull TextView widget, @NonNull Spannable buffer,
+ @NonNull MotionEvent event) {
try {
return super.onTouchEvent(widget, buffer, event);
} catch (ActivityNotFoundException ex) {
@@ -805,6 +806,10 @@ public class AppDetails extends ActionBarActivity implements ProgressListener, A
// Install the version of this app denoted by 'app.curApk'.
@Override
public void install(final Apk apk) {
+ // Ignore call if another download is running.
+ if (downloadHandler != null && !downloadHandler.isComplete())
+ return;
+
final String[] projection = { RepoProvider.DataColumns.ADDRESS };
Repo repo = RepoProvider.Helper.findById(this, apk.repo, projection);
if (repo == null || repo.address == null) {
@@ -854,32 +859,28 @@ public class AppDetails extends ActionBarActivity implements ProgressListener, A
private void startDownload(Apk apk, String repoAddress) {
downloadHandler = new ApkDownloader(getBaseContext(), apk, repoAddress);
+ localBroadcastManager.registerReceiver(downloaderProgressReceiver,
+ new IntentFilter(Downloader.LOCAL_ACTION_PROGRESS));
downloadHandler.setProgressListener(this);
if (downloadHandler.download()) {
- updateProgressDialog();
+ mHeaderFragment.startProgress();
}
}
- private void installApk(File file, String packageName) {
- setSupportProgressBarIndeterminateVisibility(true);
-
+ private void installApk(File file) {
try {
installer.installPackage(file);
} catch (AndroidNotCompatibleException e) {
Log.e(TAG, "Android not compatible with this Installer!", e);
- setSupportProgressBarIndeterminateVisibility(false);
}
}
@Override
public void removeApk(String packageName) {
- setSupportProgressBarIndeterminateVisibility(true);
-
try {
installer.deletePackage(packageName);
} catch (AndroidNotCompatibleException e) {
Log.e(TAG, "Android not compatible with this Installer!", e);
- setSupportProgressBarIndeterminateVisibility(false);
}
}
@@ -894,7 +895,6 @@ public class AppDetails extends ActionBarActivity implements ProgressListener, A
PackageManagerCompat.setInstaller(mPm, app.id);
}
- setSupportProgressBarIndeterminateVisibility(false);
onAppChanged();
}
});
@@ -906,7 +906,6 @@ public class AppDetails extends ActionBarActivity implements ProgressListener, A
runOnUiThread(new Runnable() {
@Override
public void run() {
- setSupportProgressBarIndeterminateVisibility(false);
onAppChanged();
}
});
@@ -914,7 +913,6 @@ public class AppDetails extends ActionBarActivity implements ProgressListener, A
runOnUiThread(new Runnable() {
@Override
public void run() {
- setSupportProgressBarIndeterminateVisibility(false);
onAppChanged();
Log.e(TAG, "Installer aborted with errorCode: " + errorCode);
@@ -945,79 +943,6 @@ public class AppDetails extends ActionBarActivity implements ProgressListener, A
startActivity(Intent.createChooser(shareIntent, getString(R.string.menu_share)));
}
- private ProgressDialog getProgressDialog(String file) {
- if (progressDialog == null) {
- final ProgressDialog pd = new ProgressDialog(this);
- pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
- if (Build.VERSION.SDK_INT >= 11) {
- pd.setProgressNumberFormat("%1d/%2d KiB");
- }
- pd.setMessage(getString(R.string.download_server) + ":\n " + file);
- pd.setCancelable(true);
- pd.setCanceledOnTouchOutside(false);
-
- // The indeterminate-ness will get overridden on the first progress event we receive.
- pd.setIndeterminate(true);
-
- pd.setOnCancelListener(new DialogInterface.OnCancelListener() {
- @Override
- public void onCancel(DialogInterface dialog) {
- Log.d(TAG, "User clicked 'cancel' on download, attempting to interrupt download thread.");
- if (downloadHandler != null) {
- downloadHandler.cancel();
- cleanUpFinishedDownload();
- } else {
- Log.e(TAG, "Tried to cancel, but the downloadHandler doesn't exist.");
- }
- progressDialog = null;
- Toast.makeText(AppDetails.this, getString(R.string.download_cancelled), Toast.LENGTH_LONG).show();
- }
- });
- pd.setButton(DialogInterface.BUTTON_NEUTRAL,
- getString(R.string.cancel),
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- pd.cancel();
- }
- }
- );
- progressDialog = pd;
- }
- return progressDialog;
- }
-
- /**
- * Looks at the current downloadHandler
and finds it's size and progress.
- * This is in comparison to {@link org.fdroid.fdroid.AppDetails#updateProgressDialog(int, int)},
- * which is used when you have the details from a freshly received
- * {@link org.fdroid.fdroid.ProgressListener.Event}.
- */
- private void updateProgressDialog() {
- if (downloadHandler != null) {
- updateProgressDialog(downloadHandler.getProgress(), downloadHandler.getTotalSize());
- }
- }
-
- private void updateProgressDialog(int progress, int total) {
- if (downloadHandler != null) {
- ProgressDialog pd = getProgressDialog(downloadHandler.getRemoteAddress());
- if (total > 0) {
- pd.setIndeterminate(false);
- pd.setProgress(progress/1024);
- pd.setMax(total/1024);
- } else {
- pd.setIndeterminate(true);
- pd.setProgress(progress/1024);
- pd.setMax(0);
- }
- if (!pd.isShowing()) {
- Log.d(TAG, "Showing progress dialog for download.");
- pd.show();
- }
- }
- }
-
@Override
public void onProgress(Event event) {
if (downloadHandler == null || !downloadHandler.isEventFromThis(event)) {
@@ -1034,9 +959,6 @@ public class AppDetails extends ActionBarActivity implements ProgressListener, A
boolean finished = false;
switch (event.type) {
- case Downloader.EVENT_PROGRESS:
- updateProgressDialog(event.progress, event.total);
- break;
case ApkDownloader.EVENT_ERROR:
final String text;
if (event.getData().getInt(ApkDownloader.EVENT_DATA_ERROR_TYPE) == ApkDownloader.ERROR_HASH_MISMATCH)
@@ -1054,7 +976,8 @@ public class AppDetails extends ActionBarActivity implements ProgressListener, A
}
if (finished) {
- removeProgressDialog();
+ if (mHeaderFragment != null)
+ mHeaderFragment.removeProgress();
downloadHandler = null;
}
}
@@ -1447,9 +1370,14 @@ public class AppDetails extends ActionBarActivity implements ProgressListener, A
}
}
- public static class AppDetailsHeaderFragment extends Fragment {
+ public static class AppDetailsHeaderFragment extends Fragment implements View.OnClickListener {
private AppDetailsData data;
+ private Button btMain;
+ private ProgressBar progressBar;
+ private TextView progressSize;
+ private TextView progressPercent;
+ private ImageButton cancelButton;
protected final DisplayImageOptions displayImageOptions;
public static boolean installed = false;
public static boolean updateWanted = false;
@@ -1493,35 +1421,120 @@ public class AppDetails extends ActionBarActivity implements ProgressListener, A
TextView tv = (TextView) view.findViewById(R.id.title);
tv.setText(getApp().name);
+ btMain = (Button) view.findViewById(R.id.btn_main);
+ progressBar = (ProgressBar) view.findViewById(R.id.progress_bar);
+ progressSize = (TextView) view.findViewById(R.id.progress_size);
+ progressPercent = (TextView) view.findViewById(R.id.progress_percentage);
+ cancelButton = (ImageButton) view.findViewById(R.id.cancel);
+ progressBar.setIndeterminate(false);
+ cancelButton.setOnClickListener(this);
+
updateViews(view);
}
@Override
public void onResume() {
super.onResume();
- refresh();
+ updateViews();
}
- public void refresh() {
+ /**
+ * Displays empty, indeterminate progress bar and related views.
+ */
+ public void startProgress() {
+ setProgressVisible(true);
+ progressBar.setIndeterminate(true);
+ progressSize.setText("");
+ progressPercent.setText("");
+ updateViews();
+ }
+
+ /**
+ * Updates progress bar and captions to new values (in bytes).
+ */
+ public void updateProgress(long progress, long total) {
+ long percent = progress * 100 / total;
+ setProgressVisible(true);
+ progressBar.setIndeterminate(false);
+ progressBar.setProgress((int) percent);
+ progressBar.setMax(100);
+ progressSize.setText(readableFileSize(progress) + " / " + readableFileSize(total));
+ progressPercent.setText(Long.toString(percent) + " %");
+ }
+
+ /**
+ * Converts a number of bytes to a human readable file size (eg 3.5 GiB).
+ *
+ * Based on http://stackoverflow.com/a/5599842
+ */
+ public String readableFileSize(long bytes) {
+ final String[] units = getResources().getStringArray(R.array.file_size_units);
+ if (bytes <= 0) return "0 " + units[0];
+ int digitGroups = (int) (Math.log10(bytes) / Math.log10(1024));
+ return new DecimalFormat("#,##0.#")
+ .format(bytes / Math.pow(1024, digitGroups)) + " " + units[digitGroups];
+ }
+
+ /**
+ * Shows or hides progress bar and related views.
+ */
+ private void setProgressVisible(boolean visible) {
+ int state = (visible) ? View.VISIBLE : View.GONE;
+ progressBar.setVisibility(state);
+ progressSize.setVisibility(state);
+ progressPercent.setVisibility(state);
+ cancelButton.setVisibility(state);
+ }
+
+ /**
+ * Removes progress bar and related views, invokes {@link #updateViews()}.
+ */
+ public void removeProgress() {
+ setProgressVisible(false);
+ updateViews();
+ }
+
+ /**
+ * Cancels download and hides progress bar.
+ */
+ @Override
+ public void onClick(View view) {
+ AppDetails activity = (AppDetails) getActivity();
+ if (activity == null || activity.downloadHandler == null)
+ return;
+
+ activity.downloadHandler.cancel();
+ activity.cleanUpFinishedDownload();
+ setProgressVisible(false);
+ updateViews();
+ }
+
+ public void updateViews() {
updateViews(getView());
}
public void updateViews(View view) {
TextView statusView = (TextView) view.findViewById(R.id.status);
- Button btMain = (Button) view.findViewById(R.id.btn_main);
btMain.setVisibility(View.VISIBLE);
+ AppDetails activity = (AppDetails) getActivity();
+ if (activity.downloadHandler != null) {
+ btMain.setText(R.string.downloading);
+ btMain.setEnabled(false);
+ }
/*
Check count > 0 due to incompatible apps resulting in an empty list.
If App isn't installed
*/
- if (!getApp().isInstalled() && getApp().suggestedVercode > 0 && ((AppDetails)getActivity()).adapter.getCount() > 0) {
+ else if (!getApp().isInstalled() && getApp().suggestedVercode > 0 &&
+ ((AppDetails)getActivity()).adapter.getCount() > 0) {
installed = false;
statusView.setText(getString(R.string.details_notinstalled));
NfcHelper.disableAndroidBeam(getActivity());
// Set Install button and hide second button
btMain.setText(R.string.menu_install);
btMain.setOnClickListener(mOnClickListener);
+ btMain.setEnabled(true);
}
// If App is installed
else if (getApp().isInstalled()) {
@@ -1541,6 +1554,7 @@ public class AppDetails extends ActionBarActivity implements ProgressListener, A
}
}
btMain.setOnClickListener(mOnClickListener);
+ btMain.setEnabled(true);
}
TextView currentVersion = (TextView) view.findViewById(R.id.current_version);
if (!getApks().isEmpty()) {
@@ -1574,6 +1588,8 @@ public class AppDetails extends ActionBarActivity implements ProgressListener, A
// If not installed, install
else if (getApp().suggestedVercode > 0) {
+ btMain.setEnabled(false);
+ btMain.setText(R.string.system_install_installing);
final Apk apkToInstall = ApkProvider.Helper.find(getActivity(), getApp().id, getApp().suggestedVercode);
((AppDetails)getActivity()).install(apkToInstall);
}
diff --git a/F-Droid/src/org/fdroid/fdroid/FDroid.java b/F-Droid/src/org/fdroid/fdroid/FDroid.java
index 4a525cbf7..92c8cbd8a 100644
--- a/F-Droid/src/org/fdroid/fdroid/FDroid.java
+++ b/F-Droid/src/org/fdroid/fdroid/FDroid.java
@@ -55,7 +55,6 @@ public class FDroid extends ActionBarActivity {
private static final String TAG = "FDroid";
- public static final int REQUEST_MANAGEREPOS = 0;
public static final int REQUEST_PREFS = 1;
public static final int REQUEST_ENABLE_BLUETOOTH = 2;
public static final int REQUEST_SWAP = 3;
@@ -244,12 +243,11 @@ public class FDroid extends ActionBarActivity {
switch (item.getItemId()) {
case R.id.action_update_repo:
- updateRepos();
+ UpdateService.updateNow(this);
return true;
case R.id.action_manage_repos:
- Intent i = new Intent(this, ManageReposActivity.class);
- startActivityForResult(i, REQUEST_MANAGEREPOS);
+ startActivity(new Intent(this, ManageReposActivity.class));
return true;
case R.id.action_settings:
@@ -318,31 +316,6 @@ public class FDroid extends ActionBarActivity {
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
- case REQUEST_MANAGEREPOS:
- if (data != null && data.hasExtra(ManageReposActivity.REQUEST_UPDATE)) {
- AlertDialog.Builder ask_alrt = new AlertDialog.Builder(this);
- ask_alrt.setTitle(getString(R.string.repo_update_title));
- ask_alrt.setMessage(getString(R.string.repo_alrt));
- ask_alrt.setPositiveButton(getString(R.string.yes),
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog,
- int whichButton) {
- updateRepos();
- }
- });
- ask_alrt.setNegativeButton(getString(R.string.no),
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog,
- int whichButton) {
- // do nothing
- }
- });
- AlertDialog alert = ask_alrt.create();
- alert.show();
- }
- break;
case REQUEST_PREFS:
// The automatic update settings may have changed, so reschedule (or
// unschedule) the service accordingly. It's cheap, so no need to
@@ -377,13 +350,6 @@ public class FDroid extends ActionBarActivity {
});
}
- // Force a repo update now. A progress dialog is shown and the UpdateService
- // is told to do the update, which will result in the database changing. The
- // UpdateReceiver class should get told when this is finished.
- public void updateRepos() {
- UpdateService.updateNow(this);
- }
-
private TabManager getTabManager() {
if (tabManager == null) {
tabManager = new TabManager(this, viewPager);
diff --git a/F-Droid/src/org/fdroid/fdroid/RepoUpdater.java b/F-Droid/src/org/fdroid/fdroid/RepoUpdater.java
index f3cff0b8b..936d2e963 100644
--- a/F-Droid/src/org/fdroid/fdroid/RepoUpdater.java
+++ b/F-Droid/src/org/fdroid/fdroid/RepoUpdater.java
@@ -3,7 +3,6 @@ package org.fdroid.fdroid;
import android.content.ContentValues;
import android.content.Context;
import android.content.pm.PackageManager;
-import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
@@ -22,6 +21,8 @@ import org.xml.sax.XMLReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
import java.security.CodeSigner;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
@@ -52,16 +53,14 @@ public class RepoUpdater {
/**
* Updates an app repo as read out of the database into a {@link Repo} instance.
- *
- * @param context
- * @param repo a {@link Repo} read out of the local database
+ * @param repo A {@link Repo} read out of the local database
*/
public RepoUpdater(@NonNull Context context, @NonNull Repo repo) {
this.context = context;
this.repo = repo;
}
- public void setProgressListener(ProgressListener progressListener) {
+ public void setProgressListener(@Nullable ProgressListener progressListener) {
this.progressListener = progressListener;
}
@@ -71,29 +70,23 @@ public class RepoUpdater {
public List getApks() { return apks; }
- protected String getIndexAddress() {
+ protected URL getIndexAddress() throws MalformedURLException {
+ String urlString = repo.address + "/index.jar";
try {
String versionName = context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionName;
- return repo.address + "/index.jar?client_version=" + versionName;
+ urlString += "?client_version=" + versionName;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
- return repo.address + "/index.jar";
+ return new URL(urlString);
}
Downloader downloadIndex() throws UpdateException {
Downloader downloader = null;
try {
- downloader = DownloaderFactory.create(
+ downloader = DownloaderFactory.create(context,
getIndexAddress(), File.createTempFile("index-", "-downloaded", context.getCacheDir()));
downloader.setCacheTag(repo.lastetag);
-
- if (progressListener != null) { // interactive session, show progress
- Bundle data = new Bundle(1);
- data.putString(PROGRESS_DATA_REPO_ADDRESS, repo.address);
- downloader.setProgressListener(progressListener, data);
- }
-
downloader.downloadUninterrupted();
if (downloader.isCached()) {
diff --git a/F-Droid/src/org/fdroid/fdroid/UpdateService.java b/F-Droid/src/org/fdroid/fdroid/UpdateService.java
index ec63a7d49..3e75ff7db 100644
--- a/F-Droid/src/org/fdroid/fdroid/UpdateService.java
+++ b/F-Droid/src/org/fdroid/fdroid/UpdateService.java
@@ -22,11 +22,12 @@ import android.app.AlarmManager;
import android.app.IntentService;
import android.app.NotificationManager;
import android.app.PendingIntent;
-import android.app.ProgressDialog;
+import android.content.BroadcastReceiver;
import android.content.ContentProviderOperation;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.OperationApplicationException;
import android.content.SharedPreferences;
import android.database.Cursor;
@@ -34,14 +35,12 @@ import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.os.Build;
-import android.os.Bundle;
-import android.os.Handler;
import android.os.RemoteException;
-import android.os.ResultReceiver;
import android.os.SystemClock;
import android.preference.PreferenceManager;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.TaskStackBuilder;
+import android.support.v4.content.LocalBroadcastManager;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Toast;
@@ -63,30 +62,28 @@ public class UpdateService extends IntentService implements ProgressListener {
private static final String TAG = "UpdateService";
- public static final String RESULT_MESSAGE = "msg";
- public static final String RESULT_EVENT = "event";
- public static final String RESULT_REPO_ERRORS = "repoErrors";
+ public static final String LOCAL_ACTION_STATUS = "status";
+
+ public static final String EXTRA_MESSAGE = "msg";
+ public static final String EXTRA_REPO_ERRORS = "repoErrors";
+ public static final String EXTRA_STATUS_CODE = "status";
+ public static final String EXTRA_ADDRESS = "address";
+ public static final String EXTRA_MANUAL_UPDATE = "manualUpdate";
public static final int STATUS_COMPLETE_WITH_CHANGES = 0;
- public static final int STATUS_COMPLETE_AND_SAME = 1;
- public static final int STATUS_ERROR_GLOBAL = 2;
- public static final int STATUS_ERROR_LOCAL = 3;
- public static final int STATUS_ERROR_LOCAL_SMALL = 4;
- public static final int STATUS_INFO = 5;
+ public static final int STATUS_COMPLETE_AND_SAME = 1;
+ public static final int STATUS_ERROR_GLOBAL = 2;
+ public static final int STATUS_ERROR_LOCAL = 3;
+ public static final int STATUS_ERROR_LOCAL_SMALL = 4;
+ public static final int STATUS_INFO = 5;
- // I don't like that I've had to dupliacte the statuses above with strings here, however
- // one method of communication/notification is using ResultReceiver (int status codes)
- // while the other uses progress events (string event types).
- public static final String EVENT_COMPLETE_WITH_CHANGES = "repoUpdateComplete (changed)";
- public static final String EVENT_COMPLETE_AND_SAME = "repoUpdateComplete (not changed)";
- public static final String EVENT_FINISHED = "repoUpdateFinished";
- public static final String EVENT_ERROR = "repoUpdateError";
- public static final String EVENT_INFO = "repoUpdateInfo";
+ private LocalBroadcastManager localBroadcastManager;
- public static final String EXTRA_RECEIVER = "receiver";
- public static final String EXTRA_ADDRESS = "address";
+ private static final int NOTIFY_ID_UPDATING = 0;
+ private static final int NOTIFY_ID_UPDATES_AVAILABLE = 1;
- private ResultReceiver receiver = null;
+ private NotificationManager notificationManager;
+ private NotificationCompat.Builder notificationBuilder;
public UpdateService() {
super("UpdateService");
@@ -106,130 +103,17 @@ public class UpdateService extends IntentService implements ProgressListener {
AppProvider.DataColumns.IGNORE_THISUPDATE
};
- // For receiving results from the UpdateService when we've told it to
- // update in response to a user request.
- public static class UpdateReceiver extends ResultReceiver {
-
- private Context context;
- private ProgressDialog dialog;
- private ProgressListener listener;
- private String lastShownMessage = null;
-
- public UpdateReceiver(Handler handler) {
- super(handler);
- }
-
- public UpdateReceiver setDialog(ProgressDialog dialog) {
- this.dialog = dialog;
- return this;
- }
-
- public UpdateReceiver setListener(ProgressListener listener) {
- this.listener = listener;
- return this;
- }
-
- private void forwardEvent(String type) {
- if (listener != null) {
- listener.onProgress(new Event(type));
- }
- }
-
- private void ensureDialog() {
- if (dialog == null) {
- String title = context.getString(R.string.process_wait_title);
- String message = lastShownMessage == null ? context.getString(R.string.process_update_msg) : lastShownMessage;
- dialog = ProgressDialog.show(context, title, message, true, true);
- dialog.setIcon(android.R.drawable.ic_dialog_info);
- dialog.setCanceledOnTouchOutside(false);
- }
- }
-
- public UpdateReceiver showDialog(Context context) {
- this.context = context;
- ensureDialog();
- dialog.show();
- return this;
- }
-
- public void hideDialog() {
- dialog.hide();
- dialog.dismiss();
- dialog = null;
- }
-
- @Override
- protected void onReceiveResult(int resultCode, Bundle resultData) {
- final String message = resultData.getString(RESULT_MESSAGE);
- boolean finished = false;
- switch (resultCode) {
- case STATUS_ERROR_GLOBAL:
- forwardEvent(EVENT_ERROR);
- Toast.makeText(context, context.getString(R.string.global_error_updating_repos) + " " + message, Toast.LENGTH_LONG).show();
- finished = true;
- break;
- case STATUS_ERROR_LOCAL:
- case STATUS_ERROR_LOCAL_SMALL:
- StringBuilder msgB = new StringBuilder();
- List repoErrors = resultData.getCharSequenceArrayList(RESULT_REPO_ERRORS);
- for (CharSequence error : repoErrors) {
- if (msgB.length() > 0) msgB.append('\n');
- msgB.append(error);
- }
- if (resultCode == STATUS_ERROR_LOCAL_SMALL) {
- msgB.append('\n').append(context.getString(R.string.all_other_repos_fine));
- }
- Toast.makeText(context, msgB.toString(), Toast.LENGTH_LONG).show();
- finished = true;
- break;
- case STATUS_COMPLETE_WITH_CHANGES:
- forwardEvent(EVENT_COMPLETE_WITH_CHANGES);
- finished = true;
- break;
- case STATUS_COMPLETE_AND_SAME:
- forwardEvent(EVENT_COMPLETE_AND_SAME);
- Toast.makeText(context, context.getString(R.string.repos_unchanged), Toast.LENGTH_LONG).show();
- finished = true;
- break;
- case STATUS_INFO:
- forwardEvent(EVENT_INFO);
- if (dialog != null) {
- lastShownMessage = message;
- dialog.setMessage(message);
- }
- break;
- }
-
- if (finished) {
- forwardEvent(EVENT_FINISHED);
- if (dialog != null && dialog.isShowing()) {
- try {
- dialog.dismiss();
- } catch (IllegalArgumentException e) {
- // sometimes dialog.isShowing() doesn't work :(
- // https://stackoverflow.com/questions/19538282/view-not-attached-to-window-manager-dialog-dismiss
- e.printStackTrace();
- }
- }
- }
- }
+ public static void updateNow(Context context) {
+ updateRepoNow(null, context);
}
- public static UpdateReceiver updateNow(Context context) {
- return updateRepoNow(null, context);
- }
-
- public static UpdateReceiver updateRepoNow(String address, Context context) {
+ public static void updateRepoNow(String address, Context context) {
Intent intent = new Intent(context, UpdateService.class);
- UpdateReceiver receiver = new UpdateReceiver(new Handler());
- receiver.showDialog(context);
- intent.putExtra(EXTRA_RECEIVER, receiver);
+ intent.putExtra(EXTRA_MANUAL_UPDATE, true);
if (!TextUtils.isEmpty(address)) {
intent.putExtra(EXTRA_ADDRESS, address);
}
context.startService(intent);
-
- return receiver;
}
// Schedule (or cancel schedule for) this service, according to the
@@ -259,36 +143,135 @@ public class UpdateService extends IntentService implements ProgressListener {
}
+ @Override
+ public void onCreate() {
+ super.onCreate();
+
+ localBroadcastManager = LocalBroadcastManager.getInstance(this);
+ localBroadcastManager.registerReceiver(downloadProgressReceiver,
+ new IntentFilter(Downloader.LOCAL_ACTION_PROGRESS));
+ localBroadcastManager.registerReceiver(updateStatusReceiver,
+ new IntentFilter(LOCAL_ACTION_STATUS));
+
+ notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+
+ notificationBuilder = new NotificationCompat.Builder(this)
+ .setSmallIcon(R.drawable.ic_refresh_white)
+ .setOngoing(true)
+ .setCategory(NotificationCompat.CATEGORY_SERVICE)
+ .setContentTitle(getString(R.string.update_notification_title));
+ notificationManager.notify(NOTIFY_ID_UPDATING, notificationBuilder.build());
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ notificationManager.cancel(NOTIFY_ID_UPDATING);
+ localBroadcastManager.unregisterReceiver(downloadProgressReceiver);
+ localBroadcastManager.unregisterReceiver(updateStatusReceiver);
+ }
+
protected void sendStatus(int statusCode) {
sendStatus(statusCode, null);
}
protected void sendStatus(int statusCode, String message) {
- if (receiver != null) {
- Bundle resultData = new Bundle();
- if (!TextUtils.isEmpty(message)) {
- resultData.putString(RESULT_MESSAGE, message);
- }
- receiver.send(statusCode, resultData);
- }
+ Intent intent = new Intent(LOCAL_ACTION_STATUS);
+ intent.putExtra(EXTRA_STATUS_CODE, statusCode);
+ if (!TextUtils.isEmpty(message))
+ intent.putExtra(EXTRA_MESSAGE, message);
+ LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
protected void sendRepoErrorStatus(int statusCode, ArrayList repoErrors) {
- if (receiver != null) {
- Bundle resultData = new Bundle();
- resultData.putCharSequenceArrayList(RESULT_REPO_ERRORS, repoErrors);
- receiver.send(statusCode, resultData);
- }
+ Intent intent = new Intent(LOCAL_ACTION_STATUS);
+ intent.putExtra(EXTRA_STATUS_CODE, statusCode);
+ intent.putExtra(EXTRA_REPO_ERRORS, repoErrors.toArray(new CharSequence[repoErrors.size()]));
+ LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
- /**
- * We might be doing a scheduled run, or we might have been launched by the
- * app in response to a user's request. If we have a receiver, it's the
- * latter...
- */
- private boolean isScheduledRun() {
- return receiver == null;
- }
+ private final BroadcastReceiver downloadProgressReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (TextUtils.isEmpty(action))
+ return;
+
+ if (!action.equals(Downloader.LOCAL_ACTION_PROGRESS))
+ return;
+
+ String repoAddress = intent.getStringExtra(Downloader.EXTRA_ADDRESS);
+ int downloadedSize = intent.getIntExtra(Downloader.EXTRA_BYTES_READ, -1);
+ int totalSize = intent.getIntExtra(Downloader.EXTRA_TOTAL_BYTES, -1);
+ int percent = (int) ((double) downloadedSize / totalSize * 100);
+ sendStatus(STATUS_INFO,
+ getString(R.string.status_download, repoAddress,
+ Utils.getFriendlySize(downloadedSize),
+ Utils.getFriendlySize(totalSize), percent));
+ }
+ };
+
+ // For receiving results from the UpdateService when we've told it to
+ // update in response to a user request.
+ private final BroadcastReceiver updateStatusReceiver = new BroadcastReceiver() {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (TextUtils.isEmpty(action))
+ return;
+
+ if (!action.equals(LOCAL_ACTION_STATUS))
+ return;
+
+ final String message = intent.getStringExtra(EXTRA_MESSAGE);
+ int resultCode = intent.getIntExtra(EXTRA_STATUS_CODE, -1);
+
+ String text;
+ switch (resultCode) {
+ case STATUS_INFO:
+ notificationBuilder.setContentText(message)
+ .setProgress(0, 0, true)
+ .setCategory(NotificationCompat.CATEGORY_SERVICE);
+ notificationManager.notify(NOTIFY_ID_UPDATING, notificationBuilder.build());
+ break;
+ case STATUS_ERROR_GLOBAL:
+ text = context.getString(R.string.global_error_updating_repos) + " " + message;
+ notificationBuilder.setContentText(text)
+ .setCategory(NotificationCompat.CATEGORY_ERROR)
+ .setSmallIcon(android.R.drawable.ic_dialog_alert);
+ notificationManager.notify(NOTIFY_ID_UPDATING, notificationBuilder.build());
+ Toast.makeText(context, text, Toast.LENGTH_LONG).show();
+ break;
+ case STATUS_ERROR_LOCAL:
+ case STATUS_ERROR_LOCAL_SMALL:
+ StringBuilder msgBuilder = new StringBuilder();
+ CharSequence[] repoErrors = intent.getCharSequenceArrayExtra(EXTRA_REPO_ERRORS);
+ for (CharSequence error : repoErrors) {
+ if (msgBuilder.length() > 0) msgBuilder.append('\n');
+ msgBuilder.append(error);
+ }
+ if (resultCode == STATUS_ERROR_LOCAL_SMALL) {
+ msgBuilder.append('\n').append(context.getString(R.string.all_other_repos_fine));
+ }
+ text = msgBuilder.toString();
+ notificationBuilder.setContentText(text)
+ .setCategory(NotificationCompat.CATEGORY_ERROR)
+ .setSmallIcon(android.R.drawable.ic_dialog_info);
+ notificationManager.notify(NOTIFY_ID_UPDATING, notificationBuilder.build());
+ Toast.makeText(context, text, Toast.LENGTH_LONG).show();
+ break;
+ case STATUS_COMPLETE_WITH_CHANGES:
+ break;
+ case STATUS_COMPLETE_AND_SAME:
+ text = context.getString(R.string.repos_unchanged);
+ notificationBuilder.setContentText(text)
+ .setCategory(NotificationCompat.CATEGORY_SERVICE);
+ notificationManager.notify(NOTIFY_ID_UPDATING, notificationBuilder.build());
+ break;
+ }
+ }
+ };
/**
* Check whether it is time to run the scheduled update.
@@ -332,15 +315,14 @@ public class UpdateService extends IntentService implements ProgressListener {
@Override
protected void onHandleIntent(Intent intent) {
- receiver = intent.getParcelableExtra(EXTRA_RECEIVER);
String address = intent.getStringExtra(EXTRA_ADDRESS);
+ boolean manualUpdate = intent.getBooleanExtra(EXTRA_MANUAL_UPDATE, false);
- long startTime = System.currentTimeMillis();
try {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
// See if it's time to actually do anything yet...
- if (!isScheduledRun()) {
+ if (manualUpdate) {
Log.d(TAG, "Unscheduled (manually requested) update");
} else if (!verifyIsTimeForScheduledRun()) {
return;
@@ -460,11 +442,6 @@ public class UpdateService extends IntentService implements ProgressListener {
"Exception during update processing:\n"
+ Log.getStackTraceString(e));
sendStatus(STATUS_ERROR_GLOBAL, e.getMessage());
- } finally {
- Log.d(TAG, "Update took "
- + ((System.currentTimeMillis() - startTime) / 1000)
- + " seconds.");
- receiver = null;
}
}
@@ -557,8 +534,7 @@ public class UpdateService extends IntentService implements ProgressListener {
.setContentText(contentText)
.setStyle(createNotificationBigStyle(hasUpdates));
- NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
- nm.notify(1, builder.build());
+ notificationManager.notify(NOTIFY_ID_UPDATES_AVAILABLE, builder.build());
}
private List getKnownAppIds(List apps) {
@@ -783,9 +759,6 @@ public class UpdateService extends IntentService implements ProgressListener {
String totalSize = Utils.getFriendlySize(event.total);
int percent = (int) ((double) event.progress / event.total * 100);
switch (event.type) {
- case Downloader.EVENT_PROGRESS:
- message = getString(R.string.status_download, repoAddress, downloadedSize, totalSize, percent);
- break;
case RepoUpdater.PROGRESS_TYPE_PROCESS_XML:
message = getString(R.string.status_processing_xml_percent, repoAddress, downloadedSize, totalSize, percent);
break;
diff --git a/F-Droid/src/org/fdroid/fdroid/net/ApkDownloader.java b/F-Droid/src/org/fdroid/fdroid/net/ApkDownloader.java
index 185f55bb1..98e76f0ba 100644
--- a/F-Droid/src/org/fdroid/fdroid/net/ApkDownloader.java
+++ b/F-Droid/src/org/fdroid/fdroid/net/ApkDownloader.java
@@ -52,7 +52,6 @@ public class ApkDownloader implements AsyncDownloadWrapper.Listener {
public static final int ERROR_HASH_MISMATCH = 101;
public static final int ERROR_DOWNLOAD_FAILED = 102;
- public static final int ERROR_UNKNOWN = 103;
private static final String EVENT_SOURCE_ID = "sourceId";
private static long downloadIdCounter = 0;
@@ -63,14 +62,13 @@ public class ApkDownloader implements AsyncDownloadWrapper.Listener {
public static final String EVENT_DATA_ERROR_TYPE = "apkDownloadErrorType";
@NonNull private final Apk curApk;
+ @NonNull private final Context context;
@NonNull private final String repoAddress;
@NonNull private final SanitizedFile localFile;
@NonNull private final SanitizedFile potentiallyCachedFile;
private ProgressListener listener;
private AsyncDownloadWrapper dlWrapper = null;
- private int progress = 0;
- private int totalSize = 0;
private boolean isComplete = false;
private final long id = ++downloadIdCounter;
@@ -84,6 +82,7 @@ public class ApkDownloader implements AsyncDownloadWrapper.Listener {
}
public ApkDownloader(@NonNull final Context context, @NonNull final Apk apk, @NonNull final String repoAddress) {
+ this.context = context;
curApk = apk;
this.repoAddress = repoAddress;
localFile = new SanitizedFile(Utils.getApkDownloadDir(context), apk.apkName);
@@ -191,7 +190,7 @@ public class ApkDownloader implements AsyncDownloadWrapper.Listener {
Log.d(TAG, "Downloading apk from " + remoteAddress + " to " + localFile);
try {
- Downloader downloader = DownloaderFactory.create(remoteAddress, localFile);
+ Downloader downloader = DownloaderFactory.create(context, remoteAddress, localFile);
dlWrapper = new AsyncDownloadWrapper(downloader, this);
dlWrapper.download();
return true;
@@ -214,14 +213,6 @@ public class ApkDownloader implements AsyncDownloadWrapper.Listener {
}
private void sendProgressEvent(Event event) {
- switch (event.type) {
- case Downloader.EVENT_PROGRESS:
- // Keep a copy of these ourselves, so people can interrogate us for the
- // info (in addition to receiving events with the info).
- totalSize = event.total;
- progress = event.progress;
- break;
- }
event.getData().putLong(EVENT_SOURCE_ID, id);
@@ -230,18 +221,6 @@ public class ApkDownloader implements AsyncDownloadWrapper.Listener {
}
}
- @Override
- public void onReceiveTotalDownloadSize(int size) {
- // Do nothing...
- // Rather, we will obtain the total download size from the progress events
- // when they start coming through.
- }
-
- @Override
- public void onReceiveCacheTag(String cacheTag) {
- // Do nothing...
- }
-
@Override
public void onErrorDownloading(String localisedExceptionDetails) {
Log.e(TAG, "Download failed: " + localisedExceptionDetails);
@@ -292,7 +271,7 @@ public class ApkDownloader implements AsyncDownloadWrapper.Listener {
public Apk getApk() { return curApk; }
- public int getProgress() { return progress; }
+ public int getBytesRead() { return dlWrapper != null ? dlWrapper.getBytesRead() : 0; }
- public int getTotalSize() { return totalSize; }
+ public int getTotalBytes() { return dlWrapper != null ? dlWrapper.getTotalBytes() : 0; }
}
diff --git a/F-Droid/src/org/fdroid/fdroid/net/AsyncDownloadWrapper.java b/F-Droid/src/org/fdroid/fdroid/net/AsyncDownloadWrapper.java
index d113f93f3..3588343b8 100644
--- a/F-Droid/src/org/fdroid/fdroid/net/AsyncDownloadWrapper.java
+++ b/F-Droid/src/org/fdroid/fdroid/net/AsyncDownloadWrapper.java
@@ -21,7 +21,6 @@ public class AsyncDownloadWrapper extends Handler {
private static final String TAG = "AsyncDownloadWrapper";
- private static final int MSG_PROGRESS = 1;
private static final int MSG_DOWNLOAD_COMPLETE = 2;
private static final int MSG_DOWNLOAD_CANCELLED = 3;
private static final int MSG_ERROR = 4;
@@ -43,16 +42,6 @@ public class AsyncDownloadWrapper extends Handler {
this.listener = listener;
}
- public void fetchTotalDownloadSize() {
- int size = downloader.totalDownloadSize();
- listener.onReceiveTotalDownloadSize(size);
- }
-
- public void fetchCacheTag() {
- String cacheTag = downloader.getCacheTag();
- listener.onReceiveCacheTag(cacheTag);
- }
-
public void download() {
downloadThread = new DownloadThread();
downloadThread.start();
@@ -64,22 +53,6 @@ public class AsyncDownloadWrapper extends Handler {
}
}
- public static class NotDownloadingException extends Exception {
- public NotDownloadingException(String message) {
- super(message);
- }
- }
-
- public void cancelDownload() throws NotDownloadingException {
- if (downloadThread == null) {
- throw new RuntimeException("Can't cancel download, it hasn't started yet.");
- } else if (!downloadThread.isAlive()) {
- throw new RuntimeException("Can't cancel download, it is already finished.");
- }
-
- downloadThread.interrupt();
- }
-
/**
* Receives "messages" from the download thread, and passes them onto the
* relevant {@link org.fdroid.fdroid.net.AsyncDownloadWrapper.Listener}
@@ -87,11 +60,6 @@ public class AsyncDownloadWrapper extends Handler {
*/
public void handleMessage(Message message) {
switch (message.arg1) {
- case MSG_PROGRESS:
- Bundle data = message.getData();
- ProgressListener.Event event = data.getParcelable(MSG_DATA);
- listener.onProgress(event);
- break;
case MSG_DOWNLOAD_COMPLETE:
listener.onDownloadComplete();
break;
@@ -104,19 +72,24 @@ public class AsyncDownloadWrapper extends Handler {
}
}
+ public int getBytesRead() {
+ return downloader.getBytesRead();
+ }
+
+ public int getTotalBytes() {
+ return downloader.getTotalBytes();
+ }
+
public interface Listener extends ProgressListener {
- void onReceiveTotalDownloadSize(int size);
- void onReceiveCacheTag(String cacheTag);
void onErrorDownloading(String localisedExceptionDetails);
void onDownloadComplete();
void onDownloadCancelled();
}
- private class DownloadThread extends Thread implements ProgressListener {
+ private class DownloadThread extends Thread {
public void run() {
try {
- downloader.setProgressListener(this);
downloader.download();
sendMessage(MSG_DOWNLOAD_COMPLETE);
} catch (InterruptedException e) {
@@ -137,16 +110,5 @@ public class AsyncDownloadWrapper extends Handler {
message.arg1 = messageType;
AsyncDownloadWrapper.this.sendMessage(message);
}
-
- @Override
- public void onProgress(Event event) {
- Message message = new Message();
- Bundle data = new Bundle();
- data.putParcelable(MSG_DATA, event);
- message.setData(data);
- message.arg1 = MSG_PROGRESS;
- AsyncDownloadWrapper.this.sendMessage(message);
- }
}
-
}
diff --git a/F-Droid/src/org/fdroid/fdroid/net/Downloader.java b/F-Droid/src/org/fdroid/fdroid/net/Downloader.java
index cf9dfa170..4802f39f2 100644
--- a/F-Droid/src/org/fdroid/fdroid/net/Downloader.java
+++ b/F-Droid/src/org/fdroid/fdroid/net/Downloader.java
@@ -1,11 +1,10 @@
package org.fdroid.fdroid.net;
import android.content.Context;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
+import android.content.Intent;
+import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
-import org.fdroid.fdroid.ProgressListener;
import org.fdroid.fdroid.Utils;
import java.io.File;
@@ -20,49 +19,31 @@ import java.net.URL;
public abstract class Downloader {
private static final String TAG = "Downloader";
- private OutputStream outputStream;
- private ProgressListener progressListener = null;
- private Bundle eventData = null;
+ public static final String LOCAL_ACTION_PROGRESS = "Downloader.PROGRESS";
+
+ public static final String EXTRA_ADDRESS = "extraAddress";
+ public static final String EXTRA_BYTES_READ = "extraBytesRead";
+ public static final String EXTRA_TOTAL_BYTES = "extraTotalBytes";
+
+ private final OutputStream outputStream;
+
+ private final LocalBroadcastManager localBroadcastManager;
private final File outputFile;
- protected URL sourceUrl;
+ protected final URL sourceUrl;
protected String cacheTag = null;
-
- public static final String EVENT_PROGRESS = "downloadProgress";
+ protected int bytesRead = 0;
+ protected int totalBytes = 0;
public abstract InputStream getInputStream() throws IOException;
- // The context is required for opening the file to write to.
- Downloader(String destFile, @NonNull Context ctx)
- throws FileNotFoundException, MalformedURLException {
- this(new File(ctx.getFilesDir() + File.separator + destFile));
- }
-
- // The context is required for opening the file to write to.
- Downloader(@NonNull Context ctx) throws IOException {
- this(File.createTempFile("dl-", "", ctx.getCacheDir()));
- }
-
- Downloader(File destFile)
+ Downloader(Context context, URL url, File destFile)
throws FileNotFoundException, MalformedURLException {
+ this.sourceUrl = url;
outputFile = destFile;
outputStream = new FileOutputStream(outputFile);
- }
-
- Downloader(OutputStream output)
- throws MalformedURLException {
- outputStream = output;
- outputFile = null;
- }
-
- public void setProgressListener(ProgressListener listener) {
- setProgressListener(listener, null);
- }
-
- public void setProgressListener(ProgressListener listener, Bundle eventData) {
- this.progressListener = listener;
- this.eventData = eventData;
+ localBroadcastManager = LocalBroadcastManager.getInstance(context);
}
/**
@@ -157,11 +138,16 @@ public abstract class Downloader {
}
}
+ /**
+ * This copies the downloaded data from the InputStream to the OutputStream,
+ * keeping track of the number of bytes that have flowed through for the
+ * progress counter.
+ */
protected void copyInputToOutputStream(InputStream input) throws IOException, InterruptedException {
byte[] buffer = new byte[Utils.BUFFER_SIZE];
int bytesRead = 0;
- int totalBytes = totalDownloadSize();
+ this.totalBytes = totalDownloadSize();
// Getting the total download size could potentially take time, depending on how
// it is implemented, so we may as well check this before we proceed.
@@ -185,13 +171,19 @@ public abstract class Downloader {
}
protected void sendProgress(int bytesRead, int totalBytes) {
- sendProgress(new ProgressListener.Event(EVENT_PROGRESS, bytesRead, totalBytes, eventData));
+ this.bytesRead = bytesRead;
+ Intent intent = new Intent(LOCAL_ACTION_PROGRESS);
+ intent.putExtra(EXTRA_ADDRESS, sourceUrl.toString());
+ intent.putExtra(EXTRA_BYTES_READ, bytesRead);
+ intent.putExtra(EXTRA_TOTAL_BYTES, totalBytes);
+ localBroadcastManager.sendBroadcast(intent);
}
- protected void sendProgress(ProgressListener.Event event) {
- if (progressListener != null) {
- progressListener.onProgress(event);
- }
+ public int getBytesRead() {
+ return bytesRead;
}
+ public int getTotalBytes() {
+ return totalBytes;
+ }
}
diff --git a/F-Droid/src/org/fdroid/fdroid/net/DownloaderFactory.java b/F-Droid/src/org/fdroid/fdroid/net/DownloaderFactory.java
index a6cae1438..fdc7b3255 100644
--- a/F-Droid/src/org/fdroid/fdroid/net/DownloaderFactory.java
+++ b/F-Droid/src/org/fdroid/fdroid/net/DownloaderFactory.java
@@ -4,26 +4,46 @@ import android.content.Context;
import java.io.File;
import java.io.IOException;
+import java.net.URL;
public class DownloaderFactory {
- public static Downloader create(String url, Context context)
+ /**
+ * Downloads to a temporary file, which *you must delete yourself when
+ * you are done. It is stored in {@link Context#getCacheDir()} and starts
+ * with the prefix {@code dl-}.
+ */
+ public static Downloader create(Context context, String urlString)
throws IOException {
- if (isOnionAddress(url)) {
- return new TorHttpDownloader(url, context);
- }
- return new HttpDownloader(url, context);
+ return create(context, new URL(urlString));
}
- public static Downloader create(String url, File destFile)
+ /**
+ * Downloads to a temporary file, which *you must delete yourself when
+ * you are done. It is stored in {@link Context#getCacheDir()} and starts
+ * with the prefix {@code dl-}.
+ */
+ public static Downloader create(Context context, URL url)
throws IOException {
- if (isOnionAddress(url)) {
- return new TorHttpDownloader(url, destFile);
- }
- return new HttpDownloader(url, destFile);
+ File destFile = File.createTempFile("dl-", "", context.getCacheDir());
+ destFile.deleteOnExit(); // this probably does nothing, but maybe...
+ return create(context, url, destFile);
}
- private static boolean isOnionAddress(String url) {
- return url.matches("^[a-zA-Z0-9]+://[^/]+\\.onion/.*");
+ public static Downloader create(Context context, String urlString, File destFile)
+ throws IOException {
+ return create(context, new URL(urlString), destFile);
+ }
+
+ public static Downloader create(Context context, URL url, File destFile)
+ throws IOException {
+ if (isOnionAddress(url)) {
+ return new TorHttpDownloader(context, url, destFile);
+ }
+ return new HttpDownloader(context, url, destFile);
+ }
+
+ private static boolean isOnionAddress(URL url) {
+ return url.getHost().endsWith(".onion");
}
}
diff --git a/F-Droid/src/org/fdroid/fdroid/net/HttpDownloader.java b/F-Droid/src/org/fdroid/fdroid/net/HttpDownloader.java
index c1a6a3650..d134c95d0 100644
--- a/F-Droid/src/org/fdroid/fdroid/net/HttpDownloader.java
+++ b/F-Droid/src/org/fdroid/fdroid/net/HttpDownloader.java
@@ -27,21 +27,9 @@ public class HttpDownloader extends Downloader {
protected HttpURLConnection connection;
private int statusCode = -1;
- // The context is required for opening the file to write to.
- HttpDownloader(String source, File destFile)
+ HttpDownloader(Context context, URL url, File destFile)
throws FileNotFoundException, MalformedURLException {
- super(destFile);
- sourceUrl = new URL(source);
- }
-
- /**
- * Downloads to a temporary file, which *you must delete yourself when
- * you are done*.
- * @see org.fdroid.fdroid.net.Downloader#getFile()
- */
- HttpDownloader(String source, Context ctx) throws IOException {
- super(ctx);
- sourceUrl = new URL(source);
+ super(context, url, destFile);
}
@Override
diff --git a/F-Droid/src/org/fdroid/fdroid/net/IconDownloader.java b/F-Droid/src/org/fdroid/fdroid/net/IconDownloader.java
index 0ba464755..b90876df8 100644
--- a/F-Droid/src/org/fdroid/fdroid/net/IconDownloader.java
+++ b/F-Droid/src/org/fdroid/fdroid/net/IconDownloader.java
@@ -22,7 +22,7 @@ public class IconDownloader extends BaseImageDownloader {
switch (Scheme.ofUri(imageUri)) {
case HTTP:
case HTTPS:
- Downloader downloader = DownloaderFactory.create(imageUri, context);
+ Downloader downloader = DownloaderFactory.create(context, imageUri);
return downloader.getInputStream();
default:
return super.getStream(imageUri, extra);
diff --git a/F-Droid/src/org/fdroid/fdroid/net/TorHttpDownloader.java b/F-Droid/src/org/fdroid/fdroid/net/TorHttpDownloader.java
index 70848ac66..c0eaf4d45 100644
--- a/F-Droid/src/org/fdroid/fdroid/net/TorHttpDownloader.java
+++ b/F-Droid/src/org/fdroid/fdroid/net/TorHttpDownloader.java
@@ -10,16 +10,13 @@ import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.Proxy;
import java.net.SocketAddress;
+import java.net.URL;
public class TorHttpDownloader extends HttpDownloader {
- TorHttpDownloader(String url, Context ctx) throws IOException {
- super(url, ctx);
- }
-
- TorHttpDownloader(String url, File destFile)
+ TorHttpDownloader(Context context, URL url, File destFile)
throws FileNotFoundException, MalformedURLException {
- super(url, destFile);
+ super(context, url, destFile);
}
@Override
diff --git a/F-Droid/src/org/fdroid/fdroid/views/ManageReposActivity.java b/F-Droid/src/org/fdroid/fdroid/views/ManageReposActivity.java
index b895f6a14..20ae144bd 100644
--- a/F-Droid/src/org/fdroid/fdroid/views/ManageReposActivity.java
+++ b/F-Droid/src/org/fdroid/fdroid/views/ManageReposActivity.java
@@ -64,7 +64,6 @@ import org.apache.http.impl.client.DefaultHttpClient;
import org.fdroid.fdroid.FDroid;
import org.fdroid.fdroid.FDroidApp;
import org.fdroid.fdroid.Preferences;
-import org.fdroid.fdroid.ProgressListener;
import org.fdroid.fdroid.R;
import org.fdroid.fdroid.UpdateService;
import org.fdroid.fdroid.compat.ClipboardCompat;
@@ -74,7 +73,6 @@ import org.fdroid.fdroid.data.RepoProvider;
import org.fdroid.fdroid.net.MDnsHelper;
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;
@@ -87,13 +85,6 @@ import java.util.Locale;
import javax.jmdns.ServiceInfo;
public class ManageReposActivity extends ActionBarActivity {
-
- /**
- * If we have a new repo added, or the address of a repo has changed, then
- * we when we're finished, we'll set this boolean to true in the intent that
- * 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 = "ManageReposActivity";
private static final String DEFAULT_NEW_REPO_TEXT = "https://";
@@ -104,10 +95,6 @@ public class ManageReposActivity extends ActionBarActivity {
IS_SWAP
}
- private UpdateService.UpdateReceiver updateHandler = null;
-
- private static boolean changed = false;
-
private RepoListFragment listFragment;
/**
@@ -152,21 +139,11 @@ public class ManageReposActivity extends ActionBarActivity {
@Override
protected void onResume() {
super.onResume();
- if (updateHandler != null) {
- updateHandler.showDialog(this);
- }
+
/* let's see if someone is trying to send us a new repo */
addRepoFromIntent(getIntent());
}
- @Override
- protected void onPause() {
- super.onPause();
- if (updateHandler != null) {
- updateHandler.hideDialog();
- }
- }
-
@Override
protected void onNewIntent(Intent intent) {
setIntent(intent);
@@ -175,22 +152,10 @@ public class ManageReposActivity extends ActionBarActivity {
@Override
public void finish() {
Intent ret = new Intent();
- markChangedIfRequired(ret);
setResult(RESULT_OK, ret);
super.finish();
}
- private boolean hasChanged() {
- return changed;
- }
-
- private void markChangedIfRequired(Intent intent) {
- if (hasChanged()) {
- Log.i(TAG, "Repo details have changed, prompting for update.");
- intent.putExtra(REQUEST_UPDATE, true);
- }
- }
-
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.manage_repos, menu);
@@ -202,7 +167,6 @@ public class ManageReposActivity extends ActionBarActivity {
switch (item.getItemId()) {
case android.R.id.home:
Intent destIntent = new Intent(this, FDroid.class);
- markChangedIfRequired(destIntent);
setResult(RESULT_OK, destIntent);
NavUtils.navigateUpTo(this, destIntent);
return true;
@@ -210,7 +174,7 @@ public class ManageReposActivity extends ActionBarActivity {
showAddRepo();
return true;
case R.id.action_update_repo:
- updateRepos();
+ UpdateService.updateNow(this);
return true;
case R.id.action_find_local_repos:
scanForRepos();
@@ -219,26 +183,6 @@ public class ManageReposActivity extends ActionBarActivity {
return super.onOptionsItemSelected(item);
}
- private void updateRepos() {
- updateHandler = UpdateService.updateNow(this).setListener(
- new ProgressListener() {
- @Override
- public void onProgress(Event event) {
- switch (event.type) {
- case UpdateService.EVENT_COMPLETE_AND_SAME:
- case UpdateService.EVENT_COMPLETE_WITH_CHANGES:
- // No need to prompt to update any more, we just
- // did it!
- changed = false;
- break;
- case UpdateService.EVENT_FINISHED:
- updateHandler = null;
- break;
- }
- }
- });
- }
-
private void scanForRepos() {
final RepoScanListAdapter adapter = new RepoScanListAdapter(this);
final MDnsHelper mDnsHelper = new MDnsHelper(this, adapter);
@@ -706,11 +650,11 @@ public class ManageReposActivity extends ActionBarActivity {
/**
* If started by an intent that expects a result (e.g. QR codes) then we
- * will set a result and finish. Otherwise, we'll refresh the list of repos
+ * will set a result and finish. Otherwise, we'll updateViews the list of repos
* to reflect the newly created repo.
*/
private void finishedAddingRepo() {
- changed = true;
+ UpdateService.updateNow(ManageReposActivity.this);
if (addRepoDialog.isShowing()) {
addRepoDialog.dismiss();
}
@@ -783,7 +727,7 @@ public class ManageReposActivity extends ActionBarActivity {
/**
* NOTE: If somebody toggles a repo off then on again, it will have
* removed all apps from the index when it was toggled off, so when it
- * is toggled on again, then it will require a refresh. Previously, I
+ * is toggled on again, then it will require a updateViews. Previously, I
* toyed with the idea of remembering whether they had toggled on or
* off, and then only actually performing the function when the activity
* stopped, but I think that will be problematic. What about when they
@@ -803,7 +747,7 @@ public class ManageReposActivity extends ActionBarActivity {
RepoProvider.Helper.update(getActivity(), repo, values);
if (isEnabled) {
- changed = true;
+ UpdateService.updateNow(getActivity());
} else {
FDroidApp app = (FDroidApp) getActivity().getApplication();
RepoProvider.Helper.purgeApps(getActivity(), repo, app);
@@ -888,7 +832,7 @@ public class ManageReposActivity extends ActionBarActivity {
public void editRepo(Repo repo) {
Intent intent = new Intent(getActivity(), RepoDetailsActivity.class);
- intent.putExtra(RepoDetailsFragment.ARG_REPO_ID, repo.getId());
+ intent.putExtra(RepoDetailsActivity.ARG_REPO_ID, repo.getId());
startActivityForResult(intent, SHOW_REPO_DETAILS);
}
diff --git a/F-Droid/src/org/fdroid/fdroid/views/RepoDetailsActivity.java b/F-Droid/src/org/fdroid/fdroid/views/RepoDetailsActivity.java
index 51e965828..c49325c45 100644
--- a/F-Droid/src/org/fdroid/fdroid/views/RepoDetailsActivity.java
+++ b/F-Droid/src/org/fdroid/fdroid/views/RepoDetailsActivity.java
@@ -1,33 +1,91 @@
package org.fdroid.fdroid.views;
import android.annotation.TargetApi;
+import android.content.BroadcastReceiver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.DialogInterface;
import android.content.Intent;
+import android.content.IntentFilter;
import android.net.Uri;
import android.nfc.NdefMessage;
import android.nfc.NfcAdapter;
import android.os.Build;
import android.os.Bundle;
import android.os.Parcelable;
+import android.support.v7.app.AlertDialog;
import android.support.v4.app.NavUtils;
+import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.app.ActionBarActivity;
+import android.text.TextUtils;
import android.util.Log;
+import android.view.Menu;
import android.view.MenuItem;
-import android.widget.LinearLayout;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
import android.widget.Toast;
import org.fdroid.fdroid.FDroidApp;
import org.fdroid.fdroid.NfcHelper;
+import org.fdroid.fdroid.NfcNotEnabledActivity;
+import org.fdroid.fdroid.QrGenAsyncTask;
+import org.fdroid.fdroid.R;
+import org.fdroid.fdroid.UpdateService;
import org.fdroid.fdroid.Utils;
import org.fdroid.fdroid.data.Repo;
import org.fdroid.fdroid.data.RepoProvider;
-import org.fdroid.fdroid.views.fragments.RepoDetailsFragment;
+
+import java.util.Locale;
public class RepoDetailsActivity extends ActionBarActivity {
private static final String TAG = "RepoDetailsActivity";
- private Repo repo;
+ public static final String MIME_TYPE = "application/vnd.org.fdroid.fdroid.repo";
+ public static final String ARG_REPO_ID = "repo_id";
- static final String MIME_TYPE = "application/vnd.org.fdroid.fdroid.repo";
+ /**
+ * If the repo has been updated at least once, then we will show
+ * all of this info, otherwise they will be hidden.
+ */
+ private static final int[] SHOW_IF_EXISTS = {
+ R.id.label_repo_name,
+ R.id.text_repo_name,
+ R.id.label_description,
+ R.id.text_description,
+ R.id.label_num_apps,
+ R.id.text_num_apps,
+ R.id.label_last_update,
+ R.id.text_last_update,
+ R.id.label_repo_fingerprint,
+ R.id.text_repo_fingerprint,
+ R.id.text_repo_fingerprint_description
+ };
+ /**
+ * If the repo has not been updated yet, then we only show
+ * these, otherwise they are hidden.
+ */
+ private static final int[] HIDE_IF_EXISTS = {
+ R.id.text_not_yet_updated,
+ R.id.btn_update
+ };
+ private Repo repo;
+ private long repoId;
+ private View repoView;
+
+ /**
+ * Help function to make switching between two view states easier.
+ * Perhaps there is a better way to do this. I recall that using Adobe
+ * Flex, there was a thing called "ViewStates" for exactly this. Wonder if
+ * that exists in Android?
+ */
+ private static void setMultipleViewVisibility(View parent,
+ int[] viewIds,
+ int visibility) {
+ for (int viewId : viewIds) {
+ parent.findViewById(viewId).setVisibility(visibility);
+ }
+ }
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -35,27 +93,11 @@ public class RepoDetailsActivity extends ActionBarActivity {
((FDroidApp) getApplication()).applyTheme(this);
super.onCreate(savedInstanceState);
- long repoId = getIntent().getLongExtra(RepoDetailsFragment.ARG_REPO_ID, 0);
-
- if (savedInstanceState == null) {
-
- // Need to set a dummy view (which will get overridden by the fragment manager
- // below) so that we can call setContentView(). This is a work around for
- // a (bug?) thing in 3.0, 3.1 which requires setContentView to be invoked before
- // the actionbar is played with:
- // http://blog.perpetumdesign.com/2011/08/strange-case-of-dr-action-and-mr-bar.html
- if (Build.VERSION.SDK_INT >= 11 && Build.VERSION.SDK_INT <= 13) {
- setContentView(new LinearLayout(this));
- }
-
- RepoDetailsFragment fragment = new RepoDetailsFragment();
- fragment.setArguments(getIntent().getExtras());
- getSupportFragmentManager()
- .beginTransaction()
- .add(android.R.id.content, fragment)
- .commit();
- }
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ setContentView(R.layout.repodetails);
+ repoView = findViewById(R.id.repoView);
+ repoId = getIntent().getLongExtra(ARG_REPO_ID, 0);
final String[] projection = {
RepoProvider.DataColumns.NAME,
RepoProvider.DataColumns.ADDRESS,
@@ -63,8 +105,23 @@ public class RepoDetailsActivity extends ActionBarActivity {
};
repo = RepoProvider.Helper.findById(this, repoId, projection);
- getSupportActionBar().setDisplayHomeAsUpEnabled(true);
- setTitle(repo.getName());
+ setTitle(repo.name);
+
+ TextView inputUrl = (TextView) findViewById(R.id.input_repo_url);
+ inputUrl.setText(repo.address);
+
+ Button update = (Button) repoView.findViewById(R.id.btn_update);
+ update.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ performUpdate();
+ }
+ });
+
+ Uri uri = Uri.parse(repo.address);
+ uri = uri.buildUpon().appendQueryParameter("fingerprint", repo.fingerprint).build();
+ String qrUriString = uri.toString().toUpperCase(Locale.ENGLISH);
+ new QrGenAsyncTask(this, R.id.qr_code).execute(uri.toString());
}
@TargetApi(14)
@@ -82,6 +139,19 @@ public class RepoDetailsActivity extends ActionBarActivity {
@Override
public void onResume() {
super.onResume();
+
+ /*
+ * After, for example, a repo update, the details will have changed in the
+ * database. However, or local reference to the Repo object will not
+ * have been updated. The safest way to deal with this is to reload the
+ * repo object directly from the database.
+ */
+ repo = RepoProvider.Helper.findById(this, repoId);
+ updateRepoView();
+
+ LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver,
+ new IntentFilter(UpdateService.LOCAL_ACTION_STATUS));
+
// FDroid.java and AppDetails set different NFC actions, so reset here
setNfc();
processIntent(getIntent());
@@ -105,21 +175,189 @@ public class RepoDetailsActivity extends ActionBarActivity {
Log.i(TAG, "Got this URL: " + url);
Toast.makeText(this, "Got this URL: " + url, Toast.LENGTH_LONG).show();
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
- String packageName = getPackageName();
- intent.setClassName(packageName, packageName + ".ManageRepo");
+ intent.setClass(this, ManageReposActivity.class);
startActivity(intent);
finish();
}
}
+ private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ int statusCode = intent.getIntExtra(UpdateService.EXTRA_STATUS_CODE, -1);
+ if (statusCode == UpdateService.STATUS_COMPLETE_WITH_CHANGES)
+ updateRepoView();
+ }
+ };
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ LocalBroadcastManager.getInstance(this).unregisterReceiver(broadcastReceiver);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.repo_details_activity, menu);
+ return true;
+ }
+
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
- case android.R.id.home:
- NavUtils.navigateUpFromSameTask(this);
- return true;
+ case android.R.id.home:
+ NavUtils.navigateUpFromSameTask(this);
+ return true;
+ case R.id.menu_update:
+ performUpdate();
+ return true;
+ case R.id.menu_delete:
+ promptForDelete();
+ return true;
+ case R.id.menu_enable_nfc:
+ Intent intent = new Intent(this, NfcNotEnabledActivity.class);
+ startActivity(intent);
+ return true;
}
+
return super.onOptionsItemSelected(item);
}
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ if (Build.VERSION.SDK_INT >= 14) {
+ prepareNfcMenuItems(menu);
+ }
+ return true;
+ }
+
+ @TargetApi(16)
+ private void prepareNfcMenuItems(Menu menu) {
+ NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
+ if (nfcAdapter == null) {
+ return;
+ }
+
+ boolean needsEnableNfcMenuItem;
+ if (Build.VERSION.SDK_INT < 16) {
+ needsEnableNfcMenuItem = !nfcAdapter.isEnabled();
+ } else {
+ needsEnableNfcMenuItem = !nfcAdapter.isNdefPushEnabled();
+ }
+
+ MenuItem menuItem = menu.findItem(R.id.menu_enable_nfc);
+ menuItem.setVisible(needsEnableNfcMenuItem);
+ }
+
+ private void setupDescription(View parent, Repo repo) {
+
+ TextView descriptionLabel = (TextView) parent.findViewById(R.id.label_description);
+ TextView description = (TextView) parent.findViewById(R.id.text_description);
+
+ if (TextUtils.isEmpty(repo.description)) {
+ descriptionLabel.setVisibility(View.GONE);
+ description.setVisibility(View.GONE);
+ description.setText("");
+ } else {
+ descriptionLabel.setVisibility(View.VISIBLE);
+ description.setVisibility(View.VISIBLE);
+ description.setText(repo.description.replaceAll("\n", " "));
+ }
+ }
+
+ private void setupRepoFingerprint(View parent, Repo repo) {
+ TextView repoFingerprintView = (TextView) parent.findViewById(R.id.text_repo_fingerprint);
+ TextView repoFingerprintDescView = (TextView) parent.findViewById(R.id.text_repo_fingerprint_description);
+
+ String repoFingerprint;
+ int repoFingerprintColor;
+
+ // TODO show the current state of the signature check, not just whether there is a key or not
+ if (TextUtils.isEmpty(repo.fingerprint) && TextUtils.isEmpty(repo.pubkey)) {
+ repoFingerprint = getResources().getString(R.string.unsigned);
+ repoFingerprintColor = getResources().getColor(R.color.unsigned);
+ repoFingerprintDescView.setVisibility(View.VISIBLE);
+ repoFingerprintDescView.setText(getResources().getString(R.string.unsigned_description));
+ } else {
+ // this is based on repo.fingerprint always existing, which it should
+ repoFingerprint = Utils.formatFingerprint(this, repo.fingerprint);
+ repoFingerprintColor = getResources().getColor(R.color.signed);
+ repoFingerprintDescView.setVisibility(View.GONE);
+ }
+
+ repoFingerprintView.setText(repoFingerprint);
+ repoFingerprintView.setTextColor(repoFingerprintColor);
+ }
+
+ private void updateRepoView() {
+
+ if (repo.hasBeenUpdated()) {
+ updateViewForExistingRepo(repoView);
+ } else {
+ updateViewForNewRepo(repoView);
+ }
+
+ }
+
+ 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);
+
+ TextView name = (TextView) repoView.findViewById(R.id.text_repo_name);
+ TextView numApps = (TextView) repoView.findViewById(R.id.text_num_apps);
+ TextView lastUpdated = (TextView) repoView.findViewById(R.id.text_last_update);
+
+ name.setText(repo.name);
+
+ int appCount = RepoProvider.Helper.countAppsForRepo(this, repoId);
+ numApps.setText(Integer.toString(appCount));
+
+ setupDescription(repoView, repo);
+ setupRepoFingerprint(repoView, repo);
+
+ // Repos that existed before this feature was supported will have an
+ // "Unknown" last update until next time they update...
+ String lastUpdate = repo.lastUpdated != null
+ ? repo.lastUpdated.toString() : getString(R.string.unknown);
+ lastUpdated.setText(lastUpdate);
+ }
+
+ /**
+ * When an update is performed, notify the listener so that the repo
+ * list can be updated. We will perform the update ourselves though.
+ */
+ private void performUpdate() {
+ // Ensure repo is enabled before updating...
+ ContentValues values = new ContentValues(1);
+ values.put(RepoProvider.DataColumns.IN_USE, 1);
+ RepoProvider.Helper.update(this, repo, values);
+
+ UpdateService.updateRepoNow(repo.address, this);
+ }
+
+ private void promptForDelete() {
+ new AlertDialog.Builder(this)
+ .setTitle(R.string.repo_confirm_delete_title)
+ .setMessage(R.string.repo_confirm_delete_body)
+ .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ RepoProvider.Helper.remove(getApplicationContext(), repoId);
+ finish();
+ }
+ }).setNegativeButton(android.R.string.cancel,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ // Do nothing...
+ }
+ }
+ ).show();
+ }
+
}
diff --git a/F-Droid/src/org/fdroid/fdroid/views/fragments/RepoDetailsFragment.java b/F-Droid/src/org/fdroid/fdroid/views/fragments/RepoDetailsFragment.java
deleted file mode 100644
index 9986b9149..000000000
--- a/F-Droid/src/org/fdroid/fdroid/views/fragments/RepoDetailsFragment.java
+++ /dev/null
@@ -1,392 +0,0 @@
-package org.fdroid.fdroid.views.fragments;
-
-import android.annotation.TargetApi;
-import android.app.Activity;
-import android.content.ContentValues;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.nfc.NfcAdapter;
-import android.os.Build;
-import android.os.Bundle;
-import android.support.v4.app.Fragment;
-import android.support.v4.view.MenuItemCompat;
-import android.support.v7.app.AlertDialog;
-import android.text.Editable;
-import android.text.TextUtils;
-import android.text.TextWatcher;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.Button;
-import android.widget.EditText;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import org.fdroid.fdroid.NfcNotEnabledActivity;
-import org.fdroid.fdroid.ProgressListener;
-import org.fdroid.fdroid.R;
-import org.fdroid.fdroid.UpdateService;
-import org.fdroid.fdroid.Utils;
-import org.fdroid.fdroid.data.Repo;
-import org.fdroid.fdroid.data.RepoProvider;
-
-public class RepoDetailsFragment extends Fragment {
-
- public static final String ARG_REPO_ID = "repo_id";
-
- /**
- * If the repo has been updated at least once, then we will show
- * all of this info, otherwise they will be hidden.
- */
- private static final int[] SHOW_IF_EXISTS = {
- R.id.label_repo_name,
- R.id.text_repo_name,
- R.id.label_description,
- R.id.text_description,
- R.id.label_num_apps,
- R.id.text_num_apps,
- R.id.label_last_update,
- R.id.text_last_update,
- R.id.label_repo_fingerprint,
- R.id.text_repo_fingerprint,
- R.id.text_repo_fingerprint_description
- };
-
- /**
- * If the repo has not been updated yet, then we only show
- * these, otherwise they are hidden.
- */
- private static final int[] HIDE_IF_EXISTS = {
- R.id.text_not_yet_updated,
- R.id.btn_update
- };
-
- private static final int DELETE = 0;
- private static final int UPDATE = 1;
- private static final int ENABLE_NFC = 2;
-
- private static final String TAG = "RepoDetailsFragment";
-
- private MenuItem enableNfc = null;
- private UpdateService.UpdateReceiver updateHandler = null;
-
- // TODO: Currently initialised in onCreateView. Not sure if that is the
- // best way to go about this...
- private Repo repo;
-
- @Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
- if (updateHandler != null) {
- updateHandler.showDialog(getActivity());
- }
- }
-
- @Override
- public void onDetach() {
- super.onDetach();
- if (updateHandler != null) {
- updateHandler.hideDialog();
- }
- }
-
- private long getRepoId() {
- return getArguments().getLong(RepoDetailsFragment.ARG_REPO_ID, 0);
- }
-
- /**
- * After, for example, a repo update, the details will have changed in the
- * database. However, or local reference to the Repo object will not
- * have been updated. The safest way to deal with this is to reload the
- * repo object directly from the database.
- */
- private Repo loadRepoDetails() {
- return RepoProvider.Helper.findById(getActivity(), getRepoId());
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
-
- repo = loadRepoDetails();
-
- if (repo == null) {
- Log.e(TAG, "Error showing details for repo '" + getRepoId() + "'");
- return new LinearLayout(container.getContext());
- }
-
- ViewGroup repoView = (ViewGroup)inflater.inflate(R.layout.repodetails, null);
- updateView(repoView);
-
- // Setup listeners here, rather than in updateView(...),
- // because otherwise we will end up adding multiple listeners with
- // subsequent calls to updateView().
- EditText inputUrl = (EditText)repoView.findViewById(R.id.input_repo_url);
- inputUrl.addTextChangedListener(new UrlWatcher());
-
- Button update = (Button)repoView.findViewById(R.id.btn_update);
- update.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- performUpdate();
- }
- });
-
- return repoView;
- }
-
- /**
- * Populates relevant views with properties from the current repository.
- * Decides which views to show and hide depending on the state of the
- * repository.
- */
- private void updateView(ViewGroup repoView) {
-
- EditText inputUrl = (EditText)repoView.findViewById(R.id.input_repo_url);
- inputUrl.setText(repo.address);
-
- if (repo.hasBeenUpdated()) {
- updateViewForExistingRepo(repoView);
- } else {
- updateViewForNewRepo(repoView);
- }
-
- }
-
- /**
- * Help function to make switching between two view states easier.
- * Perhaps there is a better way to do this. I recall that using Adobe
- * Flex, there was a thing called "ViewStates" for exactly this. Wonder if
- * that exists in Android?
- */
- private static void setMultipleViewVisibility(ViewGroup parent,
- int[] viewIds,
- int visibility) {
- for (int viewId : viewIds) {
- parent.findViewById(viewId).setVisibility(visibility);
- }
- }
-
- private void updateViewForNewRepo(ViewGroup repoView) {
- setMultipleViewVisibility(repoView, HIDE_IF_EXISTS, View.VISIBLE);
- setMultipleViewVisibility(repoView, SHOW_IF_EXISTS, View.GONE);
- }
-
- private void updateViewForExistingRepo(ViewGroup repoView) {
- setMultipleViewVisibility(repoView, SHOW_IF_EXISTS, View.VISIBLE);
- setMultipleViewVisibility(repoView, HIDE_IF_EXISTS, View.GONE);
-
- TextView name = (TextView)repoView.findViewById(R.id.text_repo_name);
- TextView numApps = (TextView)repoView.findViewById(R.id.text_num_apps);
- TextView lastUpdated = (TextView)repoView.findViewById(R.id.text_last_update);
-
- name.setText(repo.getName());
-
- int appCount = RepoProvider.Helper.countAppsForRepo(getActivity(), repo.getId());
- numApps.setText(Integer.toString(appCount));
-
- setupDescription(repoView, repo);
- setupRepoFingerprint(repoView, repo);
-
- // Repos that existed before this feature was supported will have an
- // "Unknown" last update until next time they update...
- String lastUpdate = repo.lastUpdated != null
- ? repo.lastUpdated.toString() : getString(R.string.unknown);
- lastUpdated.setText(lastUpdate);
- }
-
- private void setupDescription(ViewGroup parent, Repo repo) {
-
- TextView descriptionLabel = (TextView)parent.findViewById(R.id.label_description);
- TextView description = (TextView)parent.findViewById(R.id.text_description);
-
- if (repo.description == null || repo.description.length() == 0) {
- descriptionLabel.setVisibility(View.GONE);
- description.setVisibility(View.GONE);
- } else {
- descriptionLabel.setVisibility(View.VISIBLE);
- description.setVisibility(View.VISIBLE);
- }
-
- String descriptionText = repo.description == null
- ? "" : repo.description.replaceAll("\n", " ");
- description.setText(descriptionText);
-
- }
-
- /**
- * When an update is performed, notify the listener so that the repo
- * list can be updated. We will perform the update ourselves though.
- */
- private void performUpdate() {
- // Ensure repo is enabled before updating...
- ContentValues values = new ContentValues(1);
- values.put(RepoProvider.DataColumns.IN_USE, 1);
- RepoProvider.Helper.update(getActivity(), repo, values);
-
- updateHandler = UpdateService.updateRepoNow(repo.address, getActivity()).setListener(new ProgressListener() {
- @Override
- public void onProgress(Event event) {
- switch (event.type) {
- case UpdateService.EVENT_COMPLETE_WITH_CHANGES:
- repo = loadRepoDetails();
- updateView((ViewGroup)getView());
- break;
- case UpdateService.EVENT_FINISHED:
- updateHandler = null;
- break;
- }
- }
- });
- }
-
- /**
- * When the URL is changed, notify the repoChangeListener.
- */
- class UrlWatcher implements TextWatcher {
-
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
-
- @Override
- public void afterTextChanged(Editable s) {}
-
- @Override
- // TODO: This is called each character change, resulting in a DB query.
- // Doesn't exactly cause performance problems,
- // but seems silly not to go for more of a "focus out" event then
- // this "text changed" event.
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- if (!repo.address.equals(s.toString())) {
- ContentValues values = new ContentValues(1);
- values.put(RepoProvider.DataColumns.ADDRESS, s.toString());
- RepoProvider.Helper.update(getActivity(), repo, values);
- }
- }
- }
-
- @Override
- public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
- super.onCreateOptionsMenu(menu, inflater);
- menu.clear();
-
- MenuItem update = menu.add(Menu.NONE, UPDATE, 0, R.string.repo_update);
- update.setIcon(R.drawable.ic_refresh_white);
- MenuItemCompat.setShowAsAction(update,
- MenuItemCompat.SHOW_AS_ACTION_ALWAYS |
- MenuItemCompat.SHOW_AS_ACTION_WITH_TEXT);
-
- MenuItem delete = menu.add(Menu.NONE, DELETE, 0, R.string.delete);
- delete.setIcon(R.drawable.ic_delete_white);
- MenuItemCompat.setShowAsAction(delete,
- MenuItemCompat.SHOW_AS_ACTION_IF_ROOM |
- MenuItemCompat.SHOW_AS_ACTION_WITH_TEXT);
- }
-
- @Override
- public void onPrepareOptionsMenu(Menu menu) {
- if (Build.VERSION.SDK_INT >= 14)
- prepareNfcMenuItems(menu);
- }
-
- @TargetApi(16)
- private void prepareNfcMenuItems(Menu menu) {
- boolean needsEnableNfcMenuItem;
- NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(getActivity());
- if (nfcAdapter == null) {
- return;
- }
- if (Build.VERSION.SDK_INT < 16)
- needsEnableNfcMenuItem = !nfcAdapter.isEnabled();
- else
- needsEnableNfcMenuItem = !nfcAdapter.isNdefPushEnabled();
- if (needsEnableNfcMenuItem) {
- if (enableNfc != null)
- return; // already created
- enableNfc = menu.add(Menu.NONE, ENABLE_NFC, 0, R.string.enable_nfc_send);
- enableNfc.setIcon(R.drawable.ic_nfc_white);
- MenuItemCompat.setShowAsAction(enableNfc,
- MenuItemCompat.SHOW_AS_ACTION_IF_ROOM |
- MenuItemCompat.SHOW_AS_ACTION_WITH_TEXT);
- } else if (enableNfc != null) {
- // remove the existing MenuItem since NFC is now enabled
- menu.removeItem(enableNfc.getItemId());
- enableNfc = null;
- }
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
-
- switch (item.getItemId()) {
- case DELETE:
- promptForDelete();
- return true;
- case UPDATE:
- performUpdate();
- return true;
- case ENABLE_NFC:
- Intent intent = new Intent(getActivity(), NfcNotEnabledActivity.class);
- startActivity(intent);
- return true;
- }
-
- return false;
- }
-
- private void promptForDelete() {
- new AlertDialog.Builder(getActivity())
- .setTitle(R.string.repo_confirm_delete_title)
- .setMessage(R.string.repo_confirm_delete_body)
- .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- Repo repo = RepoDetailsFragment.this.repo;
- RepoProvider.Helper.remove(getActivity(), repo.getId());
- getActivity().finish();
- }
- }).setNegativeButton(android.R.string.cancel,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- // Do nothing...
- }
- }
- ).show();
- }
-
- private void setupRepoFingerprint(ViewGroup parent, Repo repo) {
- TextView repoFingerprintView = (TextView)parent.findViewById(R.id.text_repo_fingerprint);
- TextView repoFingerprintDescView = (TextView)parent.findViewById(R.id.text_repo_fingerprint_description);
-
- String repoFingerprint;
- int repoFingerprintColor;
-
- // TODO show the current state of the signature check, not just whether there is a key or not
- if (TextUtils.isEmpty(repo.fingerprint) && TextUtils.isEmpty(repo.pubkey)) {
- repoFingerprint = getResources().getString(R.string.unsigned);
- repoFingerprintColor = getResources().getColor(R.color.unsigned);
- repoFingerprintDescView.setVisibility(View.VISIBLE);
- repoFingerprintDescView.setText(getResources().getString(R.string.unsigned_description));
- } else {
- // this is based on repo.fingerprint always existing, which it should
- repoFingerprint = Utils.formatFingerprint(getActivity(), repo.fingerprint);
- repoFingerprintColor = getResources().getColor(R.color.signed);
- repoFingerprintDescView.setVisibility(View.GONE);
- }
-
- repoFingerprintView.setText(repoFingerprint);
- repoFingerprintView.setTextColor(repoFingerprintColor);
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setHasOptionsMenu(true);
- setRetainInstance(true);
- }
-
-}
diff --git a/F-Droid/src/org/fdroid/fdroid/views/swap/ConnectSwapActivity.java b/F-Droid/src/org/fdroid/fdroid/views/swap/ConnectSwapActivity.java
index 3eebca77c..2cb2afa6d 100644
--- a/F-Droid/src/org/fdroid/fdroid/views/swap/ConnectSwapActivity.java
+++ b/F-Droid/src/org/fdroid/fdroid/views/swap/ConnectSwapActivity.java
@@ -1,13 +1,17 @@
package org.fdroid.fdroid.views.swap;
import android.app.Activity;
+import android.content.BroadcastReceiver;
import android.content.ContentValues;
+import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.net.Uri;
import android.net.http.AndroidHttpClient;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.Nullable;
+import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.View;
@@ -20,7 +24,6 @@ import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.message.BasicNameValuePair;
import org.fdroid.fdroid.FDroidApp;
-import org.fdroid.fdroid.ProgressListener;
import org.fdroid.fdroid.R;
import org.fdroid.fdroid.UpdateService;
import org.fdroid.fdroid.Utils;
@@ -33,7 +36,7 @@ import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
-public class ConnectSwapActivity extends ActionBarActivity implements ProgressListener {
+public class ConnectSwapActivity extends ActionBarActivity {
private static final String TAG = "ConnectSwapActivity";
private static final String STATE_CONFIRM = "startSwap";
@@ -79,6 +82,10 @@ public class ConnectSwapActivity extends ActionBarActivity implements ProgressLi
@Override
protected void onResume() {
super.onResume();
+
+ LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver,
+ new IntentFilter(UpdateService.LOCAL_ACTION_STATUS));
+
// Only confirm the action, and then return a result...
newRepoConfig = new NewRepoConfig(this, getIntent());
if (newRepoConfig.isValidRepo()) {
@@ -91,40 +98,48 @@ public class ConnectSwapActivity extends ActionBarActivity implements ProgressLi
}
@Override
- @SuppressWarnings("fallthrough")
- public void onProgress(Event event) {
- // TODO: Show progress, but we can worry about that later.
- // Might be nice to have it nicely embedded in the UI, rather than as
- // an additional dialog. E.g. White text on blue, letting the user
- // know what we are up to.
+ protected void onPause() {
+ super.onPause();
+ LocalBroadcastManager.getInstance(this).unregisterReceiver(broadcastReceiver);
+ }
- switch (event.type) {
- case UpdateService.EVENT_COMPLETE_AND_SAME:
- Log.i(TAG, "EVENT_COMPLETE_AND_SAME");
- case UpdateService.EVENT_COMPLETE_WITH_CHANGES:
- Log.i(TAG, "EVENT_COMPLETE_WITH_CHANGES");
- Intent intent = new Intent(this, SwapAppListActivity.class);
- intent.putExtra(SwapAppListActivity.EXTRA_REPO_ID, repo.getId());
- startActivity(intent);
- finish();
+ BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // TODO: Show progress, but we can worry about that later.
+ // Might be nice to have it nicely embedded in the UI, rather than as
+ // an additional dialog. E.g. White text on blue, letting the user
+ // know what we are up to.
+ int statusCode = intent.getIntExtra(UpdateService.EXTRA_STATUS_CODE, -1);
+
+ switch (statusCode) {
+ case UpdateService.STATUS_COMPLETE_AND_SAME:
+ Log.i(TAG, "STATUS_COMPLETE_AND_SAME");
+ case UpdateService.STATUS_COMPLETE_WITH_CHANGES:
+ Log.i(TAG, "STATUS_COMPLETE_WITH_CHANGES");
+ Intent salIntent = new Intent(getBaseContext(), SwapAppListActivity.class);
+ salIntent.putExtra(SwapAppListActivity.EXTRA_REPO_ID, repo.getId());
+ startActivity(salIntent);
+ finish();
/*
// TODO: Load repo from database to get proper name. This is what the category we want to select will be called.
intent.putExtra("category", newRepoConfig.getHost());
getActivity().setResult(Activity.RESULT_OK, intent);
*/
- break;
- case UpdateService.EVENT_ERROR:
- // TODO: Show message on this screen (with a big "okay" button that goes back to F-Droid activity)
- // rather than finishing directly.
- finish();
- break;
+ break;
+ case UpdateService.STATUS_ERROR_GLOBAL:
+ // TODO: Show message on this screen (with a big "okay" button that goes back to F-Droid activity)
+ // rather than finishing directly.
+ finish();
+ break;
+ }
}
- }
+ };
private void confirm() {
repo = ensureRepoExists();
if (repo != null) {
- UpdateService.updateRepoNow(repo.address, this).setListener(this);
+ UpdateService.updateRepoNow(repo.address, this);
}
}