Refactored SwapService + SwapManager into single SwapService
This commit is contained in:
parent
a3af6b8b9f
commit
0b1e2732c2
26
F-Droid/res/layout/swap_initial_loading.xml
Normal file
26
F-Droid/res/layout/swap_initial_loading.xml
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<org.fdroid.fdroid.views.swap.InitialLoadingView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/swap_blue"
|
||||||
|
android:paddingTop="38.8dp"> <!-- 69px * 96dpi / 160dpi -->
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/progress"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_centerInParent="true"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/loading"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:layout_below="@+id/progress"
|
||||||
|
android:textColor="@android:color/white"
|
||||||
|
android:layout_centerHorizontal="true" />
|
||||||
|
|
||||||
|
</org.fdroid.fdroid.views.swap.InitialLoadingView>
|
@ -347,4 +347,5 @@
|
|||||||
<string name="wifi_warning_public">May work</string>
|
<string name="wifi_warning_public">May work</string>
|
||||||
<string name="wifi_warning_private">Promising</string>
|
<string name="wifi_warning_private">Promising</string>
|
||||||
<string name="wifi_warning_personal">Best bet</string>
|
<string name="wifi_warning_personal">Best bet</string>
|
||||||
|
<string name="loading">Loading...</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -56,7 +56,7 @@ import java.util.jar.JarEntry;
|
|||||||
import java.util.jar.JarOutputStream;
|
import java.util.jar.JarOutputStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The {@link SwapManager} deals with managing the entire workflow from selecting apps to
|
* The {@link SwapService} deals with managing the entire workflow from selecting apps to
|
||||||
* swap, to invoking this class to prepare the webroot, to enabling various communication protocols.
|
* swap, to invoking this class to prepare the webroot, to enabling various communication protocols.
|
||||||
* This class deals specifically with the webroot side of things, ensuring we have a valid index.jar
|
* This class deals specifically with the webroot side of things, ensuring we have a valid index.jar
|
||||||
* and the relevant .apk and icon files available.
|
* and the relevant .apk and icon files available.
|
||||||
|
@ -1,461 +0,0 @@
|
|||||||
package org.fdroid.fdroid.localrepo;
|
|
||||||
|
|
||||||
import android.content.BroadcastReceiver;
|
|
||||||
import android.content.ComponentName;
|
|
||||||
import android.content.ContentValues;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.IntentFilter;
|
|
||||||
import android.content.ServiceConnection;
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.IBinder;
|
|
||||||
import android.support.annotation.IntDef;
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import org.fdroid.fdroid.FDroidApp;
|
|
||||||
import org.fdroid.fdroid.UpdateService;
|
|
||||||
import org.fdroid.fdroid.data.App;
|
|
||||||
import org.fdroid.fdroid.data.Repo;
|
|
||||||
import org.fdroid.fdroid.data.RepoProvider;
|
|
||||||
import org.fdroid.fdroid.localrepo.peers.Peer;
|
|
||||||
|
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import java.lang.annotation.RetentionPolicy;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
public class SwapManager {
|
|
||||||
|
|
||||||
private static final String TAG = "SwapState";
|
|
||||||
private static final String SHARED_PREFERENCES = "swap-state";
|
|
||||||
private static final String KEY_APPS_TO_SWAP = "appsToSwap";
|
|
||||||
|
|
||||||
private static SwapManager instance;
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
public static SwapManager load(@NonNull Context context) {
|
|
||||||
if (instance == null) {
|
|
||||||
SharedPreferences preferences = context.getSharedPreferences(SHARED_PREFERENCES, Context.MODE_PRIVATE);
|
|
||||||
Set<String> appsToSwap = deserializePackages(preferences.getString(KEY_APPS_TO_SWAP, ""));
|
|
||||||
instance = new SwapManager(context, appsToSwap);
|
|
||||||
}
|
|
||||||
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
private final Context context;
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
private Set<String> appsToSwap;
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
private List<Peer> peers;
|
|
||||||
|
|
||||||
private SwapManager(@NonNull Context context, @NonNull Set<String> appsToSwap) {
|
|
||||||
this.context = context.getApplicationContext();
|
|
||||||
this.appsToSwap = appsToSwap;
|
|
||||||
this.peers = new ArrayList<>();
|
|
||||||
|
|
||||||
context.registerReceiver(new BroadcastReceiver() {
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
Peer peer = (Peer) intent.getSerializableExtra(EXTRA_PEER);
|
|
||||||
if (!peers.contains(peer)) {
|
|
||||||
peers.add(peer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, new IntentFilter(ACTION_PEER_FOUND));
|
|
||||||
|
|
||||||
setupService();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Where relevant, the state of the swap process will be saved to disk using preferences.
|
|
||||||
* Note that this is not always useful, for example saving the "current wifi network" is
|
|
||||||
* bound to cause trouble when the user opens the swap process again and is connected to
|
|
||||||
* a different network.
|
|
||||||
*/
|
|
||||||
private SharedPreferences persistence() {
|
|
||||||
return context.getSharedPreferences(SHARED_PREFERENCES, Context.MODE_APPEND);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==========================================================
|
|
||||||
// Search for peers to swap
|
|
||||||
// ==========================================================
|
|
||||||
|
|
||||||
public void scanForPeers() {
|
|
||||||
if (service != null) {
|
|
||||||
Log.d(TAG, "Scanning for nearby devices to swap with...");
|
|
||||||
service.scanForPeers();
|
|
||||||
} else {
|
|
||||||
Log.e(TAG, "Couldn't scan for peers, because service was not running.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void cancelScanningForPeers() {
|
|
||||||
if (service != null) {
|
|
||||||
service.cancelScanningForPeers();
|
|
||||||
} else {
|
|
||||||
Log.e(TAG, "Couldn't cancel scanning for peers, because service was not running.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
public List<Peer> getPeers() {
|
|
||||||
return peers;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// ==========================================================
|
|
||||||
// Manage the current step
|
|
||||||
// ("Step" refers to the current view being shown in the UI)
|
|
||||||
// ==========================================================
|
|
||||||
|
|
||||||
public static final int STEP_INTRO = 1;
|
|
||||||
public static final int STEP_SELECT_APPS = 2;
|
|
||||||
public static final int STEP_JOIN_WIFI = 3;
|
|
||||||
public static final int STEP_SHOW_NFC = 4;
|
|
||||||
public static final int STEP_WIFI_QR = 5;
|
|
||||||
public static final int STEP_CONNECTING = 6;
|
|
||||||
public static final int STEP_SUCCESS = 7;
|
|
||||||
public static final int STEP_CONFIRM_SWAP = 8;
|
|
||||||
|
|
||||||
private @SwapStep int step = STEP_INTRO;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Current screen that the swap process is up to.
|
|
||||||
* Will be one of the SwapState.STEP_* values.
|
|
||||||
*/
|
|
||||||
@SwapStep
|
|
||||||
public int getStep() {
|
|
||||||
return step;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SwapManager setStep(@SwapStep int step) {
|
|
||||||
this.step = step;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NonNull Set<String> getAppsToSwap() {
|
|
||||||
return appsToSwap;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public UpdateService.UpdateReceiver refreshSwap() {
|
|
||||||
return this.peer != null ? connectTo(peer) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
public UpdateService.UpdateReceiver connectTo(@NonNull Peer peer) {
|
|
||||||
if (peer != this.peer) {
|
|
||||||
Log.e(TAG, "Oops, got a different peer to swap with than initially planned.");
|
|
||||||
}
|
|
||||||
|
|
||||||
peerRepo = ensureRepoExists(peer);
|
|
||||||
|
|
||||||
// Only ask server to swap with us, if we are actually running a local repo service.
|
|
||||||
// It is possible to have a swap initiated without first starting a swap, in which
|
|
||||||
// case swapping back is pointless.
|
|
||||||
/*if (!newRepoConfig.preventFurtherSwaps() && isEnabled()) {
|
|
||||||
askServerToSwapWithUs();
|
|
||||||
}*/
|
|
||||||
|
|
||||||
return UpdateService.updateRepoNow(peer.getRepoAddress(), context, false);
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
private void askServerToSwapWithUs() {
|
|
||||||
if (!newRepoConfig.isValidRepo()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
new AsyncTask<Void, Void, Void>() {
|
|
||||||
@Override
|
|
||||||
protected Void doInBackground(Void... args) {
|
|
||||||
Uri repoUri = newRepoConfig.getRepoUri();
|
|
||||||
String swapBackUri = Utils.getLocalRepoUri(FDroidApp.repo).toString();
|
|
||||||
|
|
||||||
AndroidHttpClient client = AndroidHttpClient.newInstance("F-Droid", ConnectSwapActivity.this);
|
|
||||||
HttpPost request = new HttpPost("/request-swap");
|
|
||||||
HttpHost host = new HttpHost(repoUri.getHost(), repoUri.getPort(), repoUri.getScheme());
|
|
||||||
|
|
||||||
try {
|
|
||||||
Log.d(TAG, "Asking server at " + newRepoConfig.getRepoUriString() + " to swap with us in return (by POSTing to \"/request-swap\" with repo \"" + swapBackUri + "\")...");
|
|
||||||
populatePostParams(swapBackUri, request);
|
|
||||||
client.execute(host, request);
|
|
||||||
} catch (IOException e) {
|
|
||||||
notifyOfErrorOnUiThread();
|
|
||||||
Log.e(TAG, "Error while asking server to swap with us: " + e.getMessage());
|
|
||||||
} finally {
|
|
||||||
client.close();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void populatePostParams(String swapBackUri, HttpPost request) throws UnsupportedEncodingException {
|
|
||||||
List<NameValuePair> params = new ArrayList<>();
|
|
||||||
params.add(new BasicNameValuePair("repo", swapBackUri));
|
|
||||||
UrlEncodedFormEntity encodedParams = new UrlEncodedFormEntity(params);
|
|
||||||
request.setEntity(encodedParams);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void notifyOfErrorOnUiThread() {
|
|
||||||
runOnUiThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
Toast.makeText(
|
|
||||||
ConnectSwapActivity.this,
|
|
||||||
R.string.swap_reciprocate_failed,
|
|
||||||
Toast.LENGTH_LONG
|
|
||||||
).show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}.execute();
|
|
||||||
}*/
|
|
||||||
private Repo ensureRepoExists(@NonNull Peer peer) {
|
|
||||||
// TODO: newRepoConfig.getParsedUri() will include a fingerprint, which may not match with
|
|
||||||
// the repos address in the database. Not sure on best behaviour in this situation.
|
|
||||||
Repo repo = RepoProvider.Helper.findByAddress(context, peer.getRepoAddress());
|
|
||||||
if (repo == null) {
|
|
||||||
ContentValues values = new ContentValues(6);
|
|
||||||
|
|
||||||
// TODO: i18n and think about most appropriate name. Although it wont be visible in
|
|
||||||
// the "Manage repos" UI after being marked as a swap repo here...
|
|
||||||
values.put(RepoProvider.DataColumns.NAME, peer.getName());
|
|
||||||
values.put(RepoProvider.DataColumns.ADDRESS, peer.getRepoAddress());
|
|
||||||
values.put(RepoProvider.DataColumns.DESCRIPTION, ""); // TODO;
|
|
||||||
values.put(RepoProvider.DataColumns.FINGERPRINT, peer.getFingerprint());
|
|
||||||
values.put(RepoProvider.DataColumns.IN_USE, true);
|
|
||||||
values.put(RepoProvider.DataColumns.IS_SWAP, true);
|
|
||||||
Uri uri = RepoProvider.Helper.insert(context, values);
|
|
||||||
repo = RepoProvider.Helper.findByUri(context, uri);
|
|
||||||
}
|
|
||||||
|
|
||||||
return repo;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public Repo getPeerRepo() {
|
|
||||||
return peerRepo;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void install(@NonNull final App app) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ensure that we don't get put into an incorrect state, by forcing people to pass valid
|
|
||||||
* states to setStep. Ideally this would be done by requiring an enum or something to
|
|
||||||
* be passed rather than in integer, however that is harder to persist on disk than an int.
|
|
||||||
* This is the same as, e.g. {@link Context#getSystemService(String)}
|
|
||||||
*/
|
|
||||||
@IntDef({STEP_INTRO, STEP_SELECT_APPS, STEP_JOIN_WIFI, STEP_SHOW_NFC, STEP_WIFI_QR,
|
|
||||||
STEP_CONNECTING, STEP_SUCCESS, STEP_CONFIRM_SWAP})
|
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
|
||||||
public @interface SwapStep {}
|
|
||||||
|
|
||||||
|
|
||||||
// =================================================
|
|
||||||
// Have selected a specific peer to swap with
|
|
||||||
// (Rather than showing a generic QR code to scan)
|
|
||||||
// =================================================
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private Peer peer;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private Repo peerRepo;
|
|
||||||
|
|
||||||
public void swapWith(Peer peer) {
|
|
||||||
this.peer = peer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isConnectingWithPeer() {
|
|
||||||
return peer != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public Peer getPeer() {
|
|
||||||
return peer;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// ==========================================
|
|
||||||
// Remember apps user wants to swap
|
|
||||||
// ==========================================
|
|
||||||
|
|
||||||
private void persistAppsToSwap() {
|
|
||||||
persistence().edit().putString(KEY_APPS_TO_SWAP, serializePackages(appsToSwap)).commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Replacement for {@link android.content.SharedPreferences.Editor#putStringSet(String, Set)}
|
|
||||||
* which is only available in API >= 11.
|
|
||||||
* Package names are reverse-DNS-style, so they should only have alpha numeric values. Thus,
|
|
||||||
* this uses a comma as the separator.
|
|
||||||
* @see SwapManager#deserializePackages(String)
|
|
||||||
*/
|
|
||||||
private static String serializePackages(Set<String> packages) {
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
for (String pkg : packages) {
|
|
||||||
if (sb.length() > 0) {
|
|
||||||
sb.append(',');
|
|
||||||
}
|
|
||||||
sb.append(pkg);
|
|
||||||
}
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see SwapManager#deserializePackages(String)
|
|
||||||
*/
|
|
||||||
private static Set<String> deserializePackages(String packages) {
|
|
||||||
Set<String> set = new HashSet<>();
|
|
||||||
if (!TextUtils.isEmpty(packages)) {
|
|
||||||
Collections.addAll(set, packages.split(","));
|
|
||||||
}
|
|
||||||
return set;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ensureFDroidSelected() {
|
|
||||||
String fdroid = context.getPackageName();
|
|
||||||
if (!hasSelectedPackage(fdroid)) {
|
|
||||||
selectPackage(fdroid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasSelectedPackage(String packageName) {
|
|
||||||
return appsToSwap.contains(packageName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void selectPackage(String packageName) {
|
|
||||||
appsToSwap.add(packageName);
|
|
||||||
persistAppsToSwap();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void deselectPackage(String packageName) {
|
|
||||||
if (appsToSwap.contains(packageName)) {
|
|
||||||
appsToSwap.remove(packageName);
|
|
||||||
}
|
|
||||||
persistAppsToSwap();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// ==========================================
|
|
||||||
// Local repo stop/start/restart handling
|
|
||||||
// ==========================================
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private SwapService service = null;
|
|
||||||
|
|
||||||
private void setupService() {
|
|
||||||
|
|
||||||
ServiceConnection serviceConnection = new ServiceConnection() {
|
|
||||||
@Override
|
|
||||||
public void onServiceConnected(ComponentName className, IBinder binder) {
|
|
||||||
Log.d(TAG, "Swap service connected, enabling SwapManager to communicate with SwapService.");
|
|
||||||
service = ((SwapService.Binder)binder).getService();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onServiceDisconnected(ComponentName className) {
|
|
||||||
Log.d(TAG, "Swap service disconnected");
|
|
||||||
service = null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// The server should not be doing anything or occupying any (noticable) resources
|
|
||||||
// until we actually ask it to enable swapping. Therefore, we will start it nice and
|
|
||||||
// early so we don't have to wait until it is connected later.
|
|
||||||
Intent service = new Intent(context, SwapService.class);
|
|
||||||
if (context.bindService(service, serviceConnection, Context.BIND_AUTO_CREATE)) {
|
|
||||||
context.startService(service);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public void enableSwapping() {
|
|
||||||
if (service != null) {
|
|
||||||
service.enableSwapping();
|
|
||||||
} else {
|
|
||||||
Log.e(TAG, "Couldn't enable swap, because service was not running.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void disableSwapping() {
|
|
||||||
if (service != null) {
|
|
||||||
service.disableSwapping();
|
|
||||||
} else {
|
|
||||||
Log.e(TAG, "Couldn't disable swap, because service was not running.");
|
|
||||||
}
|
|
||||||
setStep(STEP_INTRO);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles checking if the {@link SwapService} is running, and only restarts it if it was running.
|
|
||||||
*/
|
|
||||||
public void restartIfEnabled() {
|
|
||||||
if (service != null) {
|
|
||||||
service.restartIfEnabled();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isEnabled() {
|
|
||||||
return service != null && service.isEnabled();
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==========================================
|
|
||||||
// Interacting with Bluetooth adapter
|
|
||||||
// ==========================================
|
|
||||||
|
|
||||||
public boolean isBluetoothDiscoverable() {
|
|
||||||
return service != null && service.getBluetooth().isConnected();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ensureBluetoothDiscoverable() {
|
|
||||||
if (service != null) {
|
|
||||||
service.getBluetooth().start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void makeBluetoothNonDiscoverable() {
|
|
||||||
if (service != null) {
|
|
||||||
service.getBluetooth().stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isWifiConnected() {
|
|
||||||
return !TextUtils.isEmpty(FDroidApp.ssid);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isBonjourDiscoverable() {
|
|
||||||
return isWifiConnected() && service != null && service.isEnabled();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ensureBonjourDiscoverable() {
|
|
||||||
if (!isBonjourDiscoverable()) {
|
|
||||||
// TODO: Enable bonjour (currently it is enabled by default when the service starts)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void makeBonjourNotDiscoverable() {
|
|
||||||
if (service != null) {
|
|
||||||
// TODO: Disable bonjour (currently it is enabled by default when the service starts)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isScanningForPeers() {
|
|
||||||
return service != null && service.isScanningForPeers();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final String ACTION_PEER_FOUND = "org.fdroid.fdroid.SwapManager.ACTION_PEER_FOUND";
|
|
||||||
public static final String EXTRA_PEER = "EXTRA_PEER";
|
|
||||||
}
|
|
@ -4,20 +4,32 @@ import android.app.Notification;
|
|||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.app.Service;
|
import android.app.Service;
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.ContentValues;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
|
import android.support.annotation.IntDef;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.v4.app.NotificationCompat;
|
import android.support.v4.app.NotificationCompat;
|
||||||
import android.support.v4.content.LocalBroadcastManager;
|
import android.support.v4.content.LocalBroadcastManager;
|
||||||
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import org.fdroid.fdroid.FDroidApp;
|
||||||
import org.fdroid.fdroid.Preferences;
|
import org.fdroid.fdroid.Preferences;
|
||||||
import org.fdroid.fdroid.R;
|
import org.fdroid.fdroid.R;
|
||||||
|
import org.fdroid.fdroid.UpdateService;
|
||||||
|
import org.fdroid.fdroid.data.App;
|
||||||
|
import org.fdroid.fdroid.data.Repo;
|
||||||
|
import org.fdroid.fdroid.data.RepoProvider;
|
||||||
import org.fdroid.fdroid.localrepo.peers.BluetoothFinder;
|
import org.fdroid.fdroid.localrepo.peers.BluetoothFinder;
|
||||||
import org.fdroid.fdroid.localrepo.peers.BonjourFinder;
|
import org.fdroid.fdroid.localrepo.peers.BonjourFinder;
|
||||||
|
import org.fdroid.fdroid.localrepo.peers.Peer;
|
||||||
import org.fdroid.fdroid.localrepo.type.BluetoothType;
|
import org.fdroid.fdroid.localrepo.type.BluetoothType;
|
||||||
import org.fdroid.fdroid.localrepo.type.BonjourType;
|
import org.fdroid.fdroid.localrepo.type.BonjourType;
|
||||||
import org.fdroid.fdroid.localrepo.type.SwapType;
|
import org.fdroid.fdroid.localrepo.type.SwapType;
|
||||||
@ -25,6 +37,12 @@ import org.fdroid.fdroid.localrepo.type.WebServerType;
|
|||||||
import org.fdroid.fdroid.net.WifiStateChangeService;
|
import org.fdroid.fdroid.net.WifiStateChangeService;
|
||||||
import org.fdroid.fdroid.views.swap.SwapWorkflowActivity;
|
import org.fdroid.fdroid.views.swap.SwapWorkflowActivity;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.Timer;
|
import java.util.Timer;
|
||||||
import java.util.TimerTask;
|
import java.util.TimerTask;
|
||||||
|
|
||||||
@ -35,105 +53,290 @@ import java.util.TimerTask;
|
|||||||
*/
|
*/
|
||||||
public class SwapService extends Service {
|
public class SwapService extends Service {
|
||||||
|
|
||||||
private static final String TAG = "SwapService";
|
private static final String TAG = "SwapManager";
|
||||||
|
private static final String SHARED_PREFERENCES = "swap-state";
|
||||||
|
private static final String KEY_APPS_TO_SWAP = "appsToSwap";
|
||||||
|
|
||||||
public static final String BONJOUR_STATE_CHANGE = "org.fdroid.fdroid.BONJOUR_STATE_CHANGE";
|
@NonNull
|
||||||
public static final String BLUETOOTH_STATE_CHANGE = "org.fdroid.fdroid.BLUETOOTH_STATE_CHANGE";
|
private Set<String> appsToSwap = new HashSet<>();
|
||||||
public static final String EXTRA_STARTING = "STARTING";
|
|
||||||
public static final String EXTRA_STARTED = "STARTED";
|
|
||||||
public static final String EXTRA_STOPPED = "STOPPED";
|
|
||||||
|
|
||||||
private static final int NOTIFICATION = 1;
|
public SwapService() {
|
||||||
|
super();
|
||||||
private final Binder binder = new Binder();
|
}
|
||||||
private SwapType bonjourType;
|
|
||||||
private SwapType bluetoothType;
|
|
||||||
private SwapType webServerType;
|
|
||||||
|
|
||||||
private BonjourFinder bonjourFinder;
|
|
||||||
private BluetoothFinder bluetoothFinder;
|
|
||||||
|
|
||||||
private final static int TIMEOUT = 900000; // 15 mins
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to automatically turn of swapping after a defined amount of time (15 mins).
|
* Where relevant, the state of the swap process will be saved to disk using preferences.
|
||||||
|
* Note that this is not always useful, for example saving the "current wifi network" is
|
||||||
|
* bound to cause trouble when the user opens the swap process again and is connected to
|
||||||
|
* a different network.
|
||||||
*/
|
*/
|
||||||
@Nullable
|
private SharedPreferences persistence() {
|
||||||
private Timer timer;
|
return getSharedPreferences(SHARED_PREFERENCES, MODE_APPEND);
|
||||||
|
|
||||||
public boolean isScanningForPeers() {
|
|
||||||
return bonjourFinder.isScanning() || bluetoothFinder.isScanning();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public SwapType getBluetooth() {
|
// ==========================================================
|
||||||
return bluetoothType;
|
// Search for peers to swap
|
||||||
}
|
// ==========================================================
|
||||||
|
|
||||||
public SwapType getBonjour() {
|
|
||||||
return bluetoothType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Binder extends android.os.Binder {
|
|
||||||
public SwapService getService() {
|
|
||||||
return SwapService.this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onCreate() {
|
|
||||||
super.onCreate();
|
|
||||||
|
|
||||||
bonjourType = new BonjourType(this);
|
|
||||||
bluetoothType = BluetoothType.create(this);
|
|
||||||
webServerType = new WebServerType(this);
|
|
||||||
bonjourFinder = new BonjourFinder(this);
|
|
||||||
bluetoothFinder = new BluetoothFinder(this);
|
|
||||||
|
|
||||||
Log.d(TAG, "Creating service, will register appropriate listeners.");
|
|
||||||
Preferences.get().registerLocalRepoBonjourListeners(bonjourEnabledListener);
|
|
||||||
Preferences.get().registerLocalRepoHttpsListeners(httpsEnabledListener);
|
|
||||||
|
|
||||||
LocalBroadcastManager.getInstance(this).registerReceiver(onWifiChange,
|
|
||||||
new IntentFilter(WifiStateChangeService.BROADCAST));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IBinder onBind(Intent intent) {
|
|
||||||
return binder;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroy() {
|
|
||||||
super.onDestroy();
|
|
||||||
Log.d(TAG, "Destroying service, will disable swapping if required, and unregister listeners.");
|
|
||||||
disableSwapping();
|
|
||||||
Preferences.get().unregisterLocalRepoBonjourListeners(bonjourEnabledListener);
|
|
||||||
Preferences.get().unregisterLocalRepoHttpsListeners(httpsEnabledListener);
|
|
||||||
LocalBroadcastManager.getInstance(this).unregisterReceiver(onWifiChange);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Notification createNotification() {
|
|
||||||
Intent intent = new Intent(this, SwapWorkflowActivity.class);
|
|
||||||
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
|
||||||
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
|
|
||||||
return new NotificationCompat.Builder(this)
|
|
||||||
.setContentTitle(getText(R.string.local_repo_running))
|
|
||||||
.setContentText(getText(R.string.touch_to_configure_local_repo))
|
|
||||||
.setSmallIcon(R.drawable.ic_swap)
|
|
||||||
.setContentIntent(contentIntent)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void scanForPeers() {
|
public void scanForPeers() {
|
||||||
|
Log.d(TAG, "Scanning for nearby devices to swap with...");
|
||||||
bonjourFinder.scan();
|
bonjourFinder.scan();
|
||||||
bluetoothFinder.scan();
|
bluetoothFinder.scan();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void cancelScanningForPeers() {
|
public void stopScanningForPeers() {
|
||||||
bonjourFinder.cancel();
|
bonjourFinder.cancel();
|
||||||
bluetoothFinder.cancel();
|
bluetoothFinder.cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean enabled = false;
|
|
||||||
|
// ==========================================================
|
||||||
|
// Manage the current step
|
||||||
|
// ("Step" refers to the current view being shown in the UI)
|
||||||
|
// ==========================================================
|
||||||
|
|
||||||
|
public static final int STEP_INTRO = 1;
|
||||||
|
public static final int STEP_SELECT_APPS = 2;
|
||||||
|
public static final int STEP_JOIN_WIFI = 3;
|
||||||
|
public static final int STEP_SHOW_NFC = 4;
|
||||||
|
public static final int STEP_WIFI_QR = 5;
|
||||||
|
public static final int STEP_CONNECTING = 6;
|
||||||
|
public static final int STEP_SUCCESS = 7;
|
||||||
|
public static final int STEP_CONFIRM_SWAP = 8;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Special view, that we don't really want to actually store against the
|
||||||
|
* {@link SwapService#step}. Rather, we use it for the purpose of specifying
|
||||||
|
* we are in the state waiting for the {@link SwapService} to get started and
|
||||||
|
* bound to the {@link SwapWorkflowActivity}.
|
||||||
|
*/
|
||||||
|
public static final int STEP_INITIAL_LOADING = 9;
|
||||||
|
|
||||||
|
private @SwapStep int step = STEP_INTRO;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current screen that the swap process is up to.
|
||||||
|
* Will be one of the SwapState.STEP_* values.
|
||||||
|
*/
|
||||||
|
@SwapStep
|
||||||
|
public int getStep() {
|
||||||
|
return step;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SwapService setStep(@SwapStep int step) {
|
||||||
|
this.step = step;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NonNull Set<String> getAppsToSwap() {
|
||||||
|
return appsToSwap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public UpdateService.UpdateReceiver refreshSwap() {
|
||||||
|
return this.peer != null ? connectTo(peer) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public UpdateService.UpdateReceiver connectTo(@NonNull Peer peer) {
|
||||||
|
if (peer != this.peer) {
|
||||||
|
Log.e(TAG, "Oops, got a different peer to swap with than initially planned.");
|
||||||
|
}
|
||||||
|
|
||||||
|
peerRepo = ensureRepoExists(peer);
|
||||||
|
|
||||||
|
// Only ask server to swap with us, if we are actually running a local repo service.
|
||||||
|
// It is possible to have a swap initiated without first starting a swap, in which
|
||||||
|
// case swapping back is pointless.
|
||||||
|
/*if (!newRepoConfig.preventFurtherSwaps() && isEnabled()) {
|
||||||
|
askServerToSwapWithUs();
|
||||||
|
}*/
|
||||||
|
|
||||||
|
return UpdateService.updateRepoNow(peer.getRepoAddress(), this, false);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
private void askServerToSwapWithUs() {
|
||||||
|
if (!newRepoConfig.isValidRepo()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
new AsyncTask<Void, Void, Void>() {
|
||||||
|
@Override
|
||||||
|
protected Void doInBackground(Void... args) {
|
||||||
|
Uri repoUri = newRepoConfig.getRepoUri();
|
||||||
|
String swapBackUri = Utils.getLocalRepoUri(FDroidApp.repo).toString();
|
||||||
|
|
||||||
|
AndroidHttpClient client = AndroidHttpClient.newInstance("F-Droid", ConnectSwapActivity.this);
|
||||||
|
HttpPost request = new HttpPost("/request-swap");
|
||||||
|
HttpHost host = new HttpHost(repoUri.getHost(), repoUri.getPort(), repoUri.getScheme());
|
||||||
|
|
||||||
|
try {
|
||||||
|
Log.d(TAG, "Asking server at " + newRepoConfig.getRepoUriString() + " to swap with us in return (by POSTing to \"/request-swap\" with repo \"" + swapBackUri + "\")...");
|
||||||
|
populatePostParams(swapBackUri, request);
|
||||||
|
client.execute(host, request);
|
||||||
|
} catch (IOException e) {
|
||||||
|
notifyOfErrorOnUiThread();
|
||||||
|
Log.e(TAG, "Error while asking server to swap with us: " + e.getMessage());
|
||||||
|
} finally {
|
||||||
|
client.close();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void populatePostParams(String swapBackUri, HttpPost request) throws UnsupportedEncodingException {
|
||||||
|
List<NameValuePair> params = new ArrayList<>();
|
||||||
|
params.add(new BasicNameValuePair("repo", swapBackUri));
|
||||||
|
UrlEncodedFormEntity encodedParams = new UrlEncodedFormEntity(params);
|
||||||
|
request.setEntity(encodedParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void notifyOfErrorOnUiThread() {
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
Toast.makeText(
|
||||||
|
ConnectSwapActivity.this,
|
||||||
|
R.string.swap_reciprocate_failed,
|
||||||
|
Toast.LENGTH_LONG
|
||||||
|
).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}.execute();
|
||||||
|
}*/
|
||||||
|
|
||||||
|
private Repo ensureRepoExists(@NonNull Peer peer) {
|
||||||
|
// TODO: newRepoConfig.getParsedUri() will include a fingerprint, which may not match with
|
||||||
|
// the repos address in the database. Not sure on best behaviour in this situation.
|
||||||
|
Repo repo = RepoProvider.Helper.findByAddress(this, peer.getRepoAddress());
|
||||||
|
if (repo == null) {
|
||||||
|
ContentValues values = new ContentValues(6);
|
||||||
|
|
||||||
|
// TODO: i18n and think about most appropriate name. Although it wont be visible in
|
||||||
|
// the "Manage repos" UI after being marked as a swap repo here...
|
||||||
|
values.put(RepoProvider.DataColumns.NAME, peer.getName());
|
||||||
|
values.put(RepoProvider.DataColumns.ADDRESS, peer.getRepoAddress());
|
||||||
|
values.put(RepoProvider.DataColumns.DESCRIPTION, ""); // TODO;
|
||||||
|
values.put(RepoProvider.DataColumns.FINGERPRINT, peer.getFingerprint());
|
||||||
|
values.put(RepoProvider.DataColumns.IN_USE, true);
|
||||||
|
values.put(RepoProvider.DataColumns.IS_SWAP, true);
|
||||||
|
Uri uri = RepoProvider.Helper.insert(this, values);
|
||||||
|
repo = RepoProvider.Helper.findByUri(this, uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
return repo;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Repo getPeerRepo() {
|
||||||
|
return peerRepo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void install(@NonNull final App app) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure that we don't get put into an incorrect state, by forcing people to pass valid
|
||||||
|
* states to setStep. Ideally this would be done by requiring an enum or something to
|
||||||
|
* be passed rather than in integer, however that is harder to persist on disk than an int.
|
||||||
|
* This is the same as, e.g. {@link Context#getSystemService(String)}
|
||||||
|
*/
|
||||||
|
@IntDef({STEP_INTRO, STEP_SELECT_APPS, STEP_JOIN_WIFI, STEP_SHOW_NFC, STEP_WIFI_QR,
|
||||||
|
STEP_CONNECTING, STEP_SUCCESS, STEP_CONFIRM_SWAP, STEP_INITIAL_LOADING})
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
public @interface SwapStep {}
|
||||||
|
|
||||||
|
|
||||||
|
// =================================================
|
||||||
|
// Have selected a specific peer to swap with
|
||||||
|
// (Rather than showing a generic QR code to scan)
|
||||||
|
// =================================================
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private Peer peer;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private Repo peerRepo;
|
||||||
|
|
||||||
|
public void swapWith(Peer peer) {
|
||||||
|
this.peer = peer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isConnectingWithPeer() {
|
||||||
|
return peer != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Peer getPeer() {
|
||||||
|
return peer;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// Remember apps user wants to swap
|
||||||
|
// ==========================================
|
||||||
|
|
||||||
|
private void persistAppsToSwap() {
|
||||||
|
persistence().edit().putString(KEY_APPS_TO_SWAP, serializePackages(appsToSwap)).commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replacement for {@link android.content.SharedPreferences.Editor#putStringSet(String, Set)}
|
||||||
|
* which is only available in API >= 11.
|
||||||
|
* Package names are reverse-DNS-style, so they should only have alpha numeric values. Thus,
|
||||||
|
* this uses a comma as the separator.
|
||||||
|
* @see SwapService#deserializePackages(String)
|
||||||
|
*/
|
||||||
|
private static String serializePackages(Set<String> packages) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (String pkg : packages) {
|
||||||
|
if (sb.length() > 0) {
|
||||||
|
sb.append(',');
|
||||||
|
}
|
||||||
|
sb.append(pkg);
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see SwapService#deserializePackages(String)
|
||||||
|
*/
|
||||||
|
private static Set<String> deserializePackages(String packages) {
|
||||||
|
Set<String> set = new HashSet<>();
|
||||||
|
if (!TextUtils.isEmpty(packages)) {
|
||||||
|
Collections.addAll(set, packages.split(","));
|
||||||
|
}
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ensureFDroidSelected() {
|
||||||
|
String fdroid = getPackageName();
|
||||||
|
if (!hasSelectedPackage(fdroid)) {
|
||||||
|
selectPackage(fdroid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasSelectedPackage(String packageName) {
|
||||||
|
return appsToSwap.contains(packageName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void selectPackage(String packageName) {
|
||||||
|
appsToSwap.add(packageName);
|
||||||
|
persistAppsToSwap();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deselectPackage(String packageName) {
|
||||||
|
if (appsToSwap.contains(packageName)) {
|
||||||
|
appsToSwap.remove(packageName);
|
||||||
|
}
|
||||||
|
persistAppsToSwap();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// Local repo stop/start/restart handling
|
||||||
|
// ==========================================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ensures that the webserver is running, as are the other services which make swap work.
|
* Ensures that the webserver is running, as are the other services which make swap work.
|
||||||
@ -165,17 +368,6 @@ public class SwapService extends Service {
|
|||||||
initTimer();
|
initTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* The guts of this class - responsible for enabling the relevant services for swapping.
|
|
||||||
* Doesn't know anything about enabled/disabled state, you should check that before invoking
|
|
||||||
* this method so it doesn't start something that is already started.
|
|
||||||
* Runs asynchronously on several background threads.
|
|
||||||
*/
|
|
||||||
private void enableSwappingAsynchronous() {
|
|
||||||
webServerType.startInBackground();
|
|
||||||
bonjourType.startInBackground();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void disableSwapping() {
|
public void disableSwapping() {
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
new AsyncTask<Void, Void, Void>() {
|
new AsyncTask<Void, Void, Void>() {
|
||||||
@ -206,18 +398,8 @@ public class SwapService extends Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see SwapService#enableSwappingAsynchronous()
|
* Handles checking if the {@link SwapService} is running, and only restarts it if it was running.
|
||||||
*/
|
*/
|
||||||
private void disableSwappingSynchronous() {
|
|
||||||
Log.d(TAG, "Disabling SwapService (bonjour, webserver, etc)");
|
|
||||||
bonjourType.stop();
|
|
||||||
webServerType.stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isEnabled() {
|
|
||||||
return enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void restartIfEnabled() {
|
public void restartIfEnabled() {
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
new AsyncTask<Void, Void, Void>() {
|
new AsyncTask<Void, Void, Void>() {
|
||||||
@ -232,6 +414,169 @@ public class SwapService extends Service {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// Interacting with Bluetooth adapter
|
||||||
|
// ==========================================
|
||||||
|
|
||||||
|
public boolean isBluetoothDiscoverable() {
|
||||||
|
return bluetoothType.isConnected();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ensureBluetoothDiscoverable() {
|
||||||
|
bluetoothType.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void makeBluetoothNonDiscoverable() {
|
||||||
|
bluetoothType.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isWifiConnected() {
|
||||||
|
return !TextUtils.isEmpty(FDroidApp.ssid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isBonjourDiscoverable() {
|
||||||
|
return isWifiConnected() && isEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ensureBonjourDiscoverable() {
|
||||||
|
if (!isBonjourDiscoverable()) {
|
||||||
|
// TODO: Enable bonjour (currently it is enabled by default when the service starts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void makeBonjourNotDiscoverable() {
|
||||||
|
// TODO: Disable bonjour (currently it is enabled by default when the service starts)
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isScanningForPeers() {
|
||||||
|
return bonjourFinder.isScanning() || bluetoothFinder.isScanning();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final String ACTION_PEER_FOUND = "org.fdroid.fdroid.SwapManager.ACTION_PEER_FOUND";
|
||||||
|
public static final String EXTRA_PEER = "EXTRA_PEER";
|
||||||
|
|
||||||
|
|
||||||
|
// ===============================================================
|
||||||
|
// Old SwapService stuff being merged into that.
|
||||||
|
// ===============================================================
|
||||||
|
|
||||||
|
public static final String BONJOUR_STATE_CHANGE = "org.fdroid.fdroid.BONJOUR_STATE_CHANGE";
|
||||||
|
public static final String BLUETOOTH_STATE_CHANGE = "org.fdroid.fdroid.BLUETOOTH_STATE_CHANGE";
|
||||||
|
public static final String EXTRA_STARTING = "STARTING";
|
||||||
|
public static final String EXTRA_STARTED = "STARTED";
|
||||||
|
public static final String EXTRA_STOPPED = "STOPPED";
|
||||||
|
|
||||||
|
private static final int NOTIFICATION = 1;
|
||||||
|
|
||||||
|
private final Binder binder = new Binder();
|
||||||
|
private SwapType bonjourType;
|
||||||
|
private SwapType bluetoothType;
|
||||||
|
private SwapType webServerType;
|
||||||
|
|
||||||
|
private BonjourFinder bonjourFinder;
|
||||||
|
private BluetoothFinder bluetoothFinder;
|
||||||
|
|
||||||
|
private final static int TIMEOUT = 900000; // 15 mins
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to automatically turn of swapping after a defined amount of time (15 mins).
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
private Timer timer;
|
||||||
|
|
||||||
|
public SwapType getBluetooth() {
|
||||||
|
return bluetoothType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SwapType getBonjour() {
|
||||||
|
return bluetoothType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Binder extends android.os.Binder {
|
||||||
|
public SwapService getService() {
|
||||||
|
return SwapService.this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onCreate() {
|
||||||
|
super.onCreate();
|
||||||
|
|
||||||
|
Log.d(TAG, "Creating swap service.");
|
||||||
|
|
||||||
|
SharedPreferences preferences = getSharedPreferences(SHARED_PREFERENCES, Context.MODE_PRIVATE);
|
||||||
|
|
||||||
|
appsToSwap.addAll(deserializePackages(preferences.getString(KEY_APPS_TO_SWAP, "")));
|
||||||
|
bonjourType = new BonjourType(this);
|
||||||
|
bluetoothType = BluetoothType.create(this);
|
||||||
|
webServerType = new WebServerType(this);
|
||||||
|
bonjourFinder = new BonjourFinder(this);
|
||||||
|
bluetoothFinder = new BluetoothFinder(this);
|
||||||
|
|
||||||
|
Preferences.get().registerLocalRepoBonjourListeners(bonjourEnabledListener);
|
||||||
|
Preferences.get().registerLocalRepoHttpsListeners(httpsEnabledListener);
|
||||||
|
|
||||||
|
LocalBroadcastManager.getInstance(this).registerReceiver(onWifiChange, new IntentFilter(WifiStateChangeService.BROADCAST));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||||
|
|
||||||
|
return START_STICKY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IBinder onBind(Intent intent) {
|
||||||
|
return binder;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
Log.d(TAG, "Destroying service, will disable swapping if required, and unregister listeners.");
|
||||||
|
disableSwapping();
|
||||||
|
Preferences.get().unregisterLocalRepoBonjourListeners(bonjourEnabledListener);
|
||||||
|
Preferences.get().unregisterLocalRepoHttpsListeners(httpsEnabledListener);
|
||||||
|
LocalBroadcastManager.getInstance(this).unregisterReceiver(onWifiChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Notification createNotification() {
|
||||||
|
Intent intent = new Intent(this, SwapWorkflowActivity.class);
|
||||||
|
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||||
|
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
|
||||||
|
return new NotificationCompat.Builder(this)
|
||||||
|
.setContentTitle(getText(R.string.local_repo_running))
|
||||||
|
.setContentText(getText(R.string.touch_to_configure_local_repo))
|
||||||
|
.setSmallIcon(R.drawable.ic_swap)
|
||||||
|
.setContentIntent(contentIntent)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean enabled = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The guts of this class - responsible for enabling the relevant services for swapping.
|
||||||
|
* Doesn't know anything about enabled/disabled state, you should check that before invoking
|
||||||
|
* this method so it doesn't start something that is already started.
|
||||||
|
* Runs asynchronously on several background threads.
|
||||||
|
*/
|
||||||
|
private void enableSwappingAsynchronous() {
|
||||||
|
webServerType.startInBackground();
|
||||||
|
bonjourType.startInBackground();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see SwapService#enableSwappingAsynchronous()
|
||||||
|
*/
|
||||||
|
private void disableSwappingSynchronous() {
|
||||||
|
Log.d(TAG, "Disabling SwapService (bonjour, webserver, etc)");
|
||||||
|
bonjourType.stop();
|
||||||
|
webServerType.stop();
|
||||||
|
}
|
||||||
|
|
||||||
private void initTimer() {
|
private void initTimer() {
|
||||||
if (timer != null) {
|
if (timer != null) {
|
||||||
Log.d(TAG, "Cancelling existing timer");
|
Log.d(TAG, "Cancelling existing timer");
|
||||||
|
@ -2,14 +2,13 @@ package org.fdroid.fdroid.localrepo.peers;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import org.fdroid.fdroid.localrepo.SwapManager;
|
import org.fdroid.fdroid.localrepo.SwapService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Searches for other devices in the vicinity, using specific technologies.
|
* Searches for other devices in the vicinity, using specific technologies.
|
||||||
* Once found, sends an {@link SwapManager#ACTION_PEER_FOUND} intent with the {@link SwapManager#EXTRA_PEER}
|
* Once found, sends an {@link SwapService#ACTION_PEER_FOUND} intent with the {@link SwapService#EXTRA_PEER}
|
||||||
* extra attribute set to the subclass of {@link Peer} that was found.
|
* extra attribute set to the subclass of {@link Peer} that was found.
|
||||||
*/
|
*/
|
||||||
public abstract class PeerFinder<T extends Peer> {
|
public abstract class PeerFinder<T extends Peer> {
|
||||||
@ -32,8 +31,8 @@ public abstract class PeerFinder<T extends Peer> {
|
|||||||
|
|
||||||
protected void foundPeer(T peer) {
|
protected void foundPeer(T peer) {
|
||||||
Log.i(TAG, "Found peer " + peer.getName());
|
Log.i(TAG, "Found peer " + peer.getName());
|
||||||
Intent intent = new Intent(SwapManager.ACTION_PEER_FOUND);
|
Intent intent = new Intent(SwapService.ACTION_PEER_FOUND);
|
||||||
intent.putExtra(SwapManager.EXTRA_PEER, peer);
|
intent.putExtra(SwapService.EXTRA_PEER, peer);
|
||||||
context.sendBroadcast(intent);
|
context.sendBroadcast(intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,7 +3,6 @@ package org.fdroid.fdroid.localrepo.type;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import org.fdroid.fdroid.FDroid;
|
|
||||||
import org.fdroid.fdroid.FDroidApp;
|
import org.fdroid.fdroid.FDroidApp;
|
||||||
import org.fdroid.fdroid.Preferences;
|
import org.fdroid.fdroid.Preferences;
|
||||||
import org.fdroid.fdroid.Utils;
|
import org.fdroid.fdroid.Utils;
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
package org.fdroid.fdroid.net;
|
package org.fdroid.fdroid.net;
|
||||||
|
|
||||||
import android.app.Service;
|
import android.app.Service;
|
||||||
|
import android.content.ComponentName;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.ServiceConnection;
|
||||||
import android.net.NetworkInfo;
|
import android.net.NetworkInfo;
|
||||||
import android.net.wifi.WifiInfo;
|
import android.net.wifi.WifiInfo;
|
||||||
import android.net.wifi.WifiManager;
|
import android.net.wifi.WifiManager;
|
||||||
@ -17,7 +19,7 @@ import org.fdroid.fdroid.Preferences;
|
|||||||
import org.fdroid.fdroid.Utils;
|
import org.fdroid.fdroid.Utils;
|
||||||
import org.fdroid.fdroid.localrepo.LocalRepoKeyStore;
|
import org.fdroid.fdroid.localrepo.LocalRepoKeyStore;
|
||||||
import org.fdroid.fdroid.localrepo.LocalRepoManager;
|
import org.fdroid.fdroid.localrepo.LocalRepoManager;
|
||||||
import org.fdroid.fdroid.localrepo.SwapManager;
|
import org.fdroid.fdroid.localrepo.SwapService;
|
||||||
|
|
||||||
import java.net.Inet6Address;
|
import java.net.Inet6Address;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
@ -159,7 +161,17 @@ public class WifiStateChangeService extends Service {
|
|||||||
Intent intent = new Intent(BROADCAST);
|
Intent intent = new Intent(BROADCAST);
|
||||||
LocalBroadcastManager.getInstance(WifiStateChangeService.this).sendBroadcast(intent);
|
LocalBroadcastManager.getInstance(WifiStateChangeService.this).sendBroadcast(intent);
|
||||||
WifiStateChangeService.this.stopSelf();
|
WifiStateChangeService.this.stopSelf();
|
||||||
SwapManager.load(WifiStateChangeService.this).restartIfEnabled();
|
|
||||||
|
Intent swapService = new Intent(WifiStateChangeService.this, SwapService.class);
|
||||||
|
bindService(swapService, new ServiceConnection() {
|
||||||
|
@Override
|
||||||
|
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||||
|
((SwapService.Binder)service).getService().restartIfEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onServiceDisconnected(ComponentName name) {}
|
||||||
|
}, BIND_AUTO_CREATE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,18 +5,13 @@ import android.content.Context;
|
|||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.support.annotation.ColorRes;
|
import android.support.annotation.ColorRes;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.v4.view.MenuItemCompat;
|
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
|
||||||
import android.widget.CheckBox;
|
|
||||||
import android.widget.CompoundButton;
|
|
||||||
import android.widget.RelativeLayout;
|
import android.widget.RelativeLayout;
|
||||||
|
|
||||||
import org.fdroid.fdroid.Preferences;
|
|
||||||
import org.fdroid.fdroid.R;
|
import org.fdroid.fdroid.R;
|
||||||
import org.fdroid.fdroid.localrepo.SwapManager;
|
import org.fdroid.fdroid.localrepo.SwapService;
|
||||||
|
|
||||||
public class ConfirmReceive extends RelativeLayout implements SwapWorkflowActivity.InnerView {
|
public class ConfirmReceive extends RelativeLayout implements SwapWorkflowActivity.InnerView {
|
||||||
|
|
||||||
@ -41,7 +36,7 @@ public class ConfirmReceive extends RelativeLayout implements SwapWorkflowActivi
|
|||||||
return (SwapWorkflowActivity)getContext();
|
return (SwapWorkflowActivity)getContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
private SwapManager getManager() {
|
private SwapService getManager() {
|
||||||
return getActivity().getState();
|
return getActivity().getState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,12 +54,12 @@ public class ConfirmReceive extends RelativeLayout implements SwapWorkflowActivi
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getStep() {
|
public int getStep() {
|
||||||
return SwapManager.STEP_CONFIRM_SWAP;
|
return SwapService.STEP_CONFIRM_SWAP;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getPreviousStep() {
|
public int getPreviousStep() {
|
||||||
return SwapManager.STEP_INTRO;
|
return SwapService.STEP_INTRO;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ColorRes
|
@ColorRes
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
package org.fdroid.fdroid.views.swap;
|
package org.fdroid.fdroid.views.swap;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
import android.content.ComponentName;
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.ServiceConnection;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.net.http.AndroidHttpClient;
|
import android.net.http.AndroidHttpClient;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.IBinder;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.v7.app.ActionBarActivity;
|
import android.support.v7.app.ActionBarActivity;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
@ -27,7 +30,7 @@ import org.fdroid.fdroid.Utils;
|
|||||||
import org.fdroid.fdroid.data.NewRepoConfig;
|
import org.fdroid.fdroid.data.NewRepoConfig;
|
||||||
import org.fdroid.fdroid.data.Repo;
|
import org.fdroid.fdroid.data.Repo;
|
||||||
import org.fdroid.fdroid.data.RepoProvider;
|
import org.fdroid.fdroid.data.RepoProvider;
|
||||||
import org.fdroid.fdroid.localrepo.SwapManager;
|
import org.fdroid.fdroid.localrepo.SwapService;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
@ -152,16 +155,39 @@ public class ConnectSwapActivity extends ActionBarActivity implements ProgressLi
|
|||||||
repo = RepoProvider.Helper.findByUri(this, uri);
|
repo = RepoProvider.Helper.findByUri(this, uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only ask server to swap with us, if we are actually running a local repo service.
|
attemptSwapBack();
|
||||||
// It is possible to have a swap initiated without first starting a swap, in which
|
|
||||||
// case swapping back is pointless.
|
|
||||||
if (!newRepoConfig.preventFurtherSwaps() && SwapManager.load(this).isEnabled()) {
|
|
||||||
askServerToSwapWithUs();
|
|
||||||
}
|
|
||||||
|
|
||||||
return repo;
|
return repo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only ask server to swap with us, if we are actually running a local repo service.
|
||||||
|
* It is possible to have a swap initiated without first starting a swap, in which
|
||||||
|
* case swapping back is pointless.
|
||||||
|
*/
|
||||||
|
private void attemptSwapBack() {
|
||||||
|
|
||||||
|
if (newRepoConfig.preventFurtherSwaps()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ServiceConnection connection = new ServiceConnection() {
|
||||||
|
@Override
|
||||||
|
public void onServiceConnected(ComponentName name, IBinder binder) {
|
||||||
|
SwapService service = ((SwapService.Binder) binder).getService();
|
||||||
|
if (service.isEnabled()) {
|
||||||
|
askServerToSwapWithUs();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onServiceDisconnected(ComponentName name) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
Intent intent = new Intent(this, SwapService.class);
|
||||||
|
bindService(intent, connection, BIND_AUTO_CREATE);
|
||||||
|
}
|
||||||
|
|
||||||
private void askServerToSwapWithUs() {
|
private void askServerToSwapWithUs() {
|
||||||
if (!newRepoConfig.isValidRepo()) {
|
if (!newRepoConfig.isValidRepo()) {
|
||||||
return;
|
return;
|
||||||
|
@ -0,0 +1,68 @@
|
|||||||
|
package org.fdroid.fdroid.views.swap;
|
||||||
|
|
||||||
|
import android.annotation.TargetApi;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.support.annotation.ColorRes;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.v4.view.MenuItemCompat;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.widget.CheckBox;
|
||||||
|
import android.widget.CompoundButton;
|
||||||
|
import android.widget.RelativeLayout;
|
||||||
|
|
||||||
|
import org.fdroid.fdroid.Preferences;
|
||||||
|
import org.fdroid.fdroid.R;
|
||||||
|
import org.fdroid.fdroid.localrepo.SwapService;
|
||||||
|
|
||||||
|
public class InitialLoadingView extends RelativeLayout implements SwapWorkflowActivity.InnerView {
|
||||||
|
|
||||||
|
public InitialLoadingView(Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public InitialLoadingView(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public InitialLoadingView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||||
|
super(context, attrs, defStyleAttr);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||||
|
public InitialLoadingView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||||
|
super(context, attrs, defStyleAttr, defStyleRes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private SwapWorkflowActivity getActivity() {
|
||||||
|
return (SwapWorkflowActivity)getContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean buildMenu(Menu menu, @NonNull MenuInflater inflater) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getStep() {
|
||||||
|
return SwapService.STEP_INITIAL_LOADING;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPreviousStep() {
|
||||||
|
return SwapService.STEP_JOIN_WIFI;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ColorRes
|
||||||
|
public int getToolbarColour() {
|
||||||
|
return getResources().getColor(R.color.swap_blue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getToolbarTitle() {
|
||||||
|
return getResources().getString(R.string.swap);
|
||||||
|
}
|
||||||
|
}
|
@ -23,7 +23,7 @@ import android.widget.TextView;
|
|||||||
|
|
||||||
import org.fdroid.fdroid.FDroidApp;
|
import org.fdroid.fdroid.FDroidApp;
|
||||||
import org.fdroid.fdroid.R;
|
import org.fdroid.fdroid.R;
|
||||||
import org.fdroid.fdroid.localrepo.SwapManager;
|
import org.fdroid.fdroid.localrepo.SwapService;
|
||||||
import org.fdroid.fdroid.net.WifiStateChangeService;
|
import org.fdroid.fdroid.net.WifiStateChangeService;
|
||||||
|
|
||||||
public class JoinWifiView extends RelativeLayout implements SwapWorkflowActivity.InnerView {
|
public class JoinWifiView extends RelativeLayout implements SwapWorkflowActivity.InnerView {
|
||||||
@ -122,12 +122,12 @@ public class JoinWifiView extends RelativeLayout implements SwapWorkflowActivity
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getStep() {
|
public int getStep() {
|
||||||
return SwapManager.STEP_JOIN_WIFI;
|
return SwapService.STEP_JOIN_WIFI;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getPreviousStep() {
|
public int getPreviousStep() {
|
||||||
return SwapManager.STEP_INTRO;
|
return SwapService.STEP_INTRO;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ColorRes
|
@ColorRes
|
||||||
|
@ -16,7 +16,7 @@ import android.widget.RelativeLayout;
|
|||||||
|
|
||||||
import org.fdroid.fdroid.Preferences;
|
import org.fdroid.fdroid.Preferences;
|
||||||
import org.fdroid.fdroid.R;
|
import org.fdroid.fdroid.R;
|
||||||
import org.fdroid.fdroid.localrepo.SwapManager;
|
import org.fdroid.fdroid.localrepo.SwapService;
|
||||||
|
|
||||||
public class NfcView extends RelativeLayout implements SwapWorkflowActivity.InnerView {
|
public class NfcView extends RelativeLayout implements SwapWorkflowActivity.InnerView {
|
||||||
|
|
||||||
@ -70,12 +70,12 @@ public class NfcView extends RelativeLayout implements SwapWorkflowActivity.Inne
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getStep() {
|
public int getStep() {
|
||||||
return SwapManager.STEP_SHOW_NFC;
|
return SwapService.STEP_SHOW_NFC;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getPreviousStep() {
|
public int getPreviousStep() {
|
||||||
return SwapManager.STEP_JOIN_WIFI;
|
return SwapService.STEP_JOIN_WIFI;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ColorRes
|
@ColorRes
|
||||||
|
@ -36,7 +36,7 @@ import android.widget.TextView;
|
|||||||
|
|
||||||
import org.fdroid.fdroid.R;
|
import org.fdroid.fdroid.R;
|
||||||
import org.fdroid.fdroid.data.InstalledAppProvider;
|
import org.fdroid.fdroid.data.InstalledAppProvider;
|
||||||
import org.fdroid.fdroid.localrepo.SwapManager;
|
import org.fdroid.fdroid.localrepo.SwapService;
|
||||||
|
|
||||||
public class SelectAppsView extends ListView implements
|
public class SelectAppsView extends ListView implements
|
||||||
SwapWorkflowActivity.InnerView,
|
SwapWorkflowActivity.InnerView,
|
||||||
@ -64,7 +64,7 @@ public class SelectAppsView extends ListView implements
|
|||||||
return (SwapWorkflowActivity)getContext();
|
return (SwapWorkflowActivity)getContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
private SwapManager getState() {
|
private SwapService getState() {
|
||||||
return getActivity().getState();
|
return getActivity().getState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,12 +119,12 @@ public class SelectAppsView extends ListView implements
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getStep() {
|
public int getStep() {
|
||||||
return SwapManager.STEP_SELECT_APPS;
|
return SwapService.STEP_SELECT_APPS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getPreviousStep() {
|
public int getPreviousStep() {
|
||||||
return getState().isConnectingWithPeer() ? SwapManager.STEP_JOIN_WIFI : SwapManager.STEP_INTRO;
|
return getState().isConnectingWithPeer() ? SwapService.STEP_JOIN_WIFI : SwapService.STEP_INTRO;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ColorRes
|
@ColorRes
|
||||||
|
@ -6,8 +6,6 @@ import android.content.BroadcastReceiver;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.graphics.PorterDuff;
|
|
||||||
import android.graphics.PorterDuffColorFilter;
|
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.support.annotation.ColorRes;
|
import android.support.annotation.ColorRes;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
@ -23,10 +21,8 @@ import android.view.View;
|
|||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.AdapterView;
|
import android.widget.AdapterView;
|
||||||
import android.widget.ArrayAdapter;
|
import android.widget.ArrayAdapter;
|
||||||
import android.widget.Button;
|
|
||||||
import android.widget.CompoundButton;
|
import android.widget.CompoundButton;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.LinearLayout;
|
|
||||||
import android.widget.ListView;
|
import android.widget.ListView;
|
||||||
import android.widget.ProgressBar;
|
import android.widget.ProgressBar;
|
||||||
import android.widget.ScrollView;
|
import android.widget.ScrollView;
|
||||||
@ -35,7 +31,6 @@ import android.widget.TextView;
|
|||||||
|
|
||||||
import org.fdroid.fdroid.FDroidApp;
|
import org.fdroid.fdroid.FDroidApp;
|
||||||
import org.fdroid.fdroid.R;
|
import org.fdroid.fdroid.R;
|
||||||
import org.fdroid.fdroid.localrepo.SwapManager;
|
|
||||||
import org.fdroid.fdroid.localrepo.SwapService;
|
import org.fdroid.fdroid.localrepo.SwapService;
|
||||||
import org.fdroid.fdroid.localrepo.peers.Peer;
|
import org.fdroid.fdroid.localrepo.peers.Peer;
|
||||||
import org.fdroid.fdroid.net.WifiStateChangeService;
|
import org.fdroid.fdroid.net.WifiStateChangeService;
|
||||||
@ -97,7 +92,7 @@ public class StartSwapView extends ScrollView implements SwapWorkflowActivity.In
|
|||||||
return (SwapWorkflowActivity)getContext();
|
return (SwapWorkflowActivity)getContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
private SwapManager getManager() {
|
private SwapService getManager() {
|
||||||
return getActivity().getState();
|
return getActivity().getState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,7 +170,7 @@ public class StartSwapView extends ScrollView implements SwapWorkflowActivity.In
|
|||||||
getContext().registerReceiver(new BroadcastReceiver() {
|
getContext().registerReceiver(new BroadcastReceiver() {
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
Peer peer = intent.getParcelableExtra(SwapManager.EXTRA_PEER);
|
Peer peer = intent.getParcelableExtra(SwapService.EXTRA_PEER);
|
||||||
if (adapter.getPosition(peer) >= 0) {
|
if (adapter.getPosition(peer) >= 0) {
|
||||||
Log.d(TAG, "Found peer: " + peer + ", ignoring though, because it is already in our list.");
|
Log.d(TAG, "Found peer: " + peer + ", ignoring though, because it is already in our list.");
|
||||||
} else {
|
} else {
|
||||||
@ -184,7 +179,7 @@ public class StartSwapView extends ScrollView implements SwapWorkflowActivity.In
|
|||||||
uiUpdatePeersInfo();
|
uiUpdatePeersInfo();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, new IntentFilter(SwapManager.ACTION_PEER_FOUND));
|
}, new IntentFilter(SwapService.ACTION_PEER_FOUND));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -241,12 +236,12 @@ public class StartSwapView extends ScrollView implements SwapWorkflowActivity.In
|
|||||||
if (intent.hasExtra(SwapService.EXTRA_STARTING)) {
|
if (intent.hasExtra(SwapService.EXTRA_STARTING)) {
|
||||||
Log.d(TAG, "Bluetooth service is starting...");
|
Log.d(TAG, "Bluetooth service is starting...");
|
||||||
bluetoothSwitch.setEnabled(false);
|
bluetoothSwitch.setEnabled(false);
|
||||||
bluetoothSwitch.setChecked(true);
|
// bluetoothSwitch.setChecked(true);
|
||||||
} else {
|
} else {
|
||||||
bluetoothSwitch.setEnabled(true);
|
bluetoothSwitch.setEnabled(true);
|
||||||
if (intent.hasExtra(SwapService.EXTRA_STARTED)) {
|
if (intent.hasExtra(SwapService.EXTRA_STARTED)) {
|
||||||
Log.d(TAG, "Bluetooth service has started.");
|
Log.d(TAG, "Bluetooth service has started.");
|
||||||
bluetoothSwitch.setChecked(true);
|
// bluetoothSwitch.setChecked(true);
|
||||||
} else {
|
} else {
|
||||||
Log.d(TAG, "Bluetooth service has stopped.");
|
Log.d(TAG, "Bluetooth service has stopped.");
|
||||||
bluetoothSwitch.setChecked(false);
|
bluetoothSwitch.setChecked(false);
|
||||||
@ -336,7 +331,7 @@ public class StartSwapView extends ScrollView implements SwapWorkflowActivity.In
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getStep() {
|
public int getStep() {
|
||||||
return SwapManager.STEP_INTRO;
|
return SwapService.STEP_INTRO;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -345,7 +340,7 @@ public class StartSwapView extends ScrollView implements SwapWorkflowActivity.In
|
|||||||
// if getStep is STEP_INTRO, don't even bother asking for getPreviousStep. But that is a
|
// if getStep is STEP_INTRO, don't even bother asking for getPreviousStep. But that is a
|
||||||
// bit messy. It would be nicer if this was handled using the same mechanism as everything
|
// bit messy. It would be nicer if this was handled using the same mechanism as everything
|
||||||
// else.
|
// else.
|
||||||
return SwapManager.STEP_INTRO;
|
return SwapService.STEP_INTRO;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -20,7 +20,6 @@ import android.support.v7.widget.SearchView;
|
|||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.ContextThemeWrapper;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
@ -39,7 +38,7 @@ import org.fdroid.fdroid.UpdateService;
|
|||||||
import org.fdroid.fdroid.data.App;
|
import org.fdroid.fdroid.data.App;
|
||||||
import org.fdroid.fdroid.data.AppProvider;
|
import org.fdroid.fdroid.data.AppProvider;
|
||||||
import org.fdroid.fdroid.data.Repo;
|
import org.fdroid.fdroid.data.Repo;
|
||||||
import org.fdroid.fdroid.localrepo.SwapManager;
|
import org.fdroid.fdroid.localrepo.SwapService;
|
||||||
|
|
||||||
import java.util.Timer;
|
import java.util.Timer;
|
||||||
import java.util.TimerTask;
|
import java.util.TimerTask;
|
||||||
@ -70,7 +69,7 @@ public class SwapAppsView extends ListView implements
|
|||||||
return (SwapWorkflowActivity)getContext();
|
return (SwapWorkflowActivity)getContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
private SwapManager getState() {
|
private SwapService getState() {
|
||||||
return getActivity().getState();
|
return getActivity().getState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,12 +174,12 @@ public class SwapAppsView extends ListView implements
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getStep() {
|
public int getStep() {
|
||||||
return SwapManager.STEP_SUCCESS;
|
return SwapService.STEP_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getPreviousStep() {
|
public int getPreviousStep() {
|
||||||
return SwapManager.STEP_INTRO;
|
return SwapService.STEP_INTRO;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ColorRes
|
@ColorRes
|
||||||
|
@ -5,28 +5,19 @@ import android.content.Context;
|
|||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.support.annotation.ColorRes;
|
import android.support.annotation.ColorRes;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.v4.view.MenuItemCompat;
|
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
|
||||||
import android.widget.CheckBox;
|
|
||||||
import android.widget.CompoundButton;
|
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
import android.widget.RelativeLayout;
|
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import org.fdroid.fdroid.Preferences;
|
|
||||||
import org.fdroid.fdroid.ProgressListener;
|
import org.fdroid.fdroid.ProgressListener;
|
||||||
import org.fdroid.fdroid.R;
|
import org.fdroid.fdroid.R;
|
||||||
import org.fdroid.fdroid.UpdateService;
|
import org.fdroid.fdroid.UpdateService;
|
||||||
import org.fdroid.fdroid.localrepo.SwapManager;
|
import org.fdroid.fdroid.localrepo.SwapService;
|
||||||
import org.fdroid.fdroid.localrepo.peers.Peer;
|
import org.fdroid.fdroid.localrepo.peers.Peer;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class SwapConnecting extends LinearLayout implements SwapWorkflowActivity.InnerView {
|
public class SwapConnecting extends LinearLayout implements SwapWorkflowActivity.InnerView {
|
||||||
|
|
||||||
private final static String TAG = "SwapConnecting";
|
private final static String TAG = "SwapConnecting";
|
||||||
@ -52,7 +43,7 @@ public class SwapConnecting extends LinearLayout implements SwapWorkflowActivity
|
|||||||
return (SwapWorkflowActivity)getContext();
|
return (SwapWorkflowActivity)getContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
private SwapManager getManager() {
|
private SwapService getManager() {
|
||||||
return getActivity().getState();
|
return getActivity().getState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,12 +107,12 @@ public class SwapConnecting extends LinearLayout implements SwapWorkflowActivity
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getStep() {
|
public int getStep() {
|
||||||
return SwapManager.STEP_CONNECTING;
|
return SwapService.STEP_CONNECTING;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getPreviousStep() {
|
public int getPreviousStep() {
|
||||||
return SwapManager.STEP_SELECT_APPS;
|
return SwapService.STEP_SELECT_APPS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ColorRes
|
@ColorRes
|
||||||
|
@ -2,14 +2,18 @@ package org.fdroid.fdroid.views.swap;
|
|||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.ProgressDialog;
|
import android.app.ProgressDialog;
|
||||||
|
import android.content.ComponentName;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.ServiceConnection;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.IBinder;
|
||||||
import android.support.annotation.ColorRes;
|
import android.support.annotation.ColorRes;
|
||||||
import android.support.annotation.LayoutRes;
|
import android.support.annotation.LayoutRes;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
import android.support.v7.widget.Toolbar;
|
import android.support.v7.widget.Toolbar;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
@ -31,7 +35,7 @@ import org.fdroid.fdroid.R;
|
|||||||
import org.fdroid.fdroid.Utils;
|
import org.fdroid.fdroid.Utils;
|
||||||
import org.fdroid.fdroid.data.NewRepoConfig;
|
import org.fdroid.fdroid.data.NewRepoConfig;
|
||||||
import org.fdroid.fdroid.localrepo.LocalRepoManager;
|
import org.fdroid.fdroid.localrepo.LocalRepoManager;
|
||||||
import org.fdroid.fdroid.localrepo.SwapManager;
|
import org.fdroid.fdroid.localrepo.SwapService;
|
||||||
import org.fdroid.fdroid.localrepo.peers.Peer;
|
import org.fdroid.fdroid.localrepo.peers.Peer;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@ -45,7 +49,7 @@ public class SwapWorkflowActivity extends AppCompatActivity {
|
|||||||
/**
|
/**
|
||||||
* A UI component (subclass of {@link View}) which forms part of the swap workflow.
|
* A UI component (subclass of {@link View}) which forms part of the swap workflow.
|
||||||
* There is a one to one mapping between an {@link org.fdroid.fdroid.views.swap.SwapWorkflowActivity.InnerView}
|
* There is a one to one mapping between an {@link org.fdroid.fdroid.views.swap.SwapWorkflowActivity.InnerView}
|
||||||
* and a {@link org.fdroid.fdroid.localrepo.SwapManager.SwapStep}, and these views know what
|
* and a {@link SwapService.SwapStep}, and these views know what
|
||||||
* the previous view before them should be.
|
* the previous view before them should be.
|
||||||
*/
|
*/
|
||||||
public interface InnerView {
|
public interface InnerView {
|
||||||
@ -53,9 +57,9 @@ public class SwapWorkflowActivity extends AppCompatActivity {
|
|||||||
boolean buildMenu(Menu menu, @NonNull MenuInflater inflater);
|
boolean buildMenu(Menu menu, @NonNull MenuInflater inflater);
|
||||||
|
|
||||||
/** @return The step that this view represents. */
|
/** @return The step that this view represents. */
|
||||||
@SwapManager.SwapStep int getStep();
|
@SwapService.SwapStep int getStep();
|
||||||
|
|
||||||
@SwapManager.SwapStep int getPreviousStep();
|
@SwapService.SwapStep int getPreviousStep();
|
||||||
|
|
||||||
@ColorRes int getToolbarColour();
|
@ColorRes int getToolbarColour();
|
||||||
|
|
||||||
@ -66,18 +70,57 @@ public class SwapWorkflowActivity extends AppCompatActivity {
|
|||||||
private static final int CONNECT_TO_SWAP = 1;
|
private static final int CONNECT_TO_SWAP = 1;
|
||||||
|
|
||||||
private Toolbar toolbar;
|
private Toolbar toolbar;
|
||||||
private SwapManager state;
|
|
||||||
private InnerView currentView;
|
private InnerView currentView;
|
||||||
private boolean hasPreparedLocalRepo = false;
|
private boolean hasPreparedLocalRepo = false;
|
||||||
private PrepareSwapRepo updateSwappableAppsTask = null;
|
private PrepareSwapRepo updateSwappableAppsTask = null;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private SwapService service = null;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public SwapService getService() {
|
||||||
|
if (service == null) {
|
||||||
|
// *Slightly* more informative than a null-pointer error that would otherwise happen.
|
||||||
|
throw new IllegalStateException("Trying to access swap service before it was initialized.");
|
||||||
|
}
|
||||||
|
return service;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupService() {
|
||||||
|
|
||||||
|
ServiceConnection serviceConnection = new ServiceConnection() {
|
||||||
|
@Override
|
||||||
|
public void onServiceConnected(ComponentName className, IBinder binder) {
|
||||||
|
Log.d(TAG, "Swap service connected, enabling SwapManager to communicate with SwapService.");
|
||||||
|
service = ((SwapService.Binder)binder).getService();
|
||||||
|
showRelevantView();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onServiceDisconnected(ComponentName className) {
|
||||||
|
Log.d(TAG, "Swap service disconnected");
|
||||||
|
service = null;
|
||||||
|
// TODO: What to do about the UI in this instance?
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// The server should not be doing anything or occupying any (noticeable) resources
|
||||||
|
// until we actually ask it to enable swapping. Therefore, we will start it nice and
|
||||||
|
// early so we don't have to wait until it is connected later.
|
||||||
|
Intent service = new Intent(this, SwapService.class);
|
||||||
|
if (bindService(service, serviceConnection, Context.BIND_AUTO_CREATE)) {
|
||||||
|
startService(service);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBackPressed() {
|
public void onBackPressed() {
|
||||||
if (currentView.getStep() == SwapManager.STEP_INTRO) {
|
if (currentView.getStep() == SwapService.STEP_INTRO) {
|
||||||
finish();
|
finish();
|
||||||
} else {
|
} else {
|
||||||
int nextStep = currentView.getPreviousStep();
|
int nextStep = currentView.getPreviousStep();
|
||||||
state.setStep(nextStep);
|
getService().setStep(nextStep);
|
||||||
showRelevantView();
|
showRelevantView();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -85,7 +128,9 @@ public class SwapWorkflowActivity extends AppCompatActivity {
|
|||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
state = SwapManager.load(this);
|
|
||||||
|
setupService();
|
||||||
|
|
||||||
setContentView(R.layout.swap_activity);
|
setContentView(R.layout.swap_activity);
|
||||||
|
|
||||||
toolbar = (Toolbar) findViewById(R.id.toolbar);
|
toolbar = (Toolbar) findViewById(R.id.toolbar);
|
||||||
@ -93,14 +138,13 @@ public class SwapWorkflowActivity extends AppCompatActivity {
|
|||||||
setSupportActionBar(toolbar);
|
setSupportActionBar(toolbar);
|
||||||
|
|
||||||
container = (ViewGroup) findViewById(R.id.fragment_container);
|
container = (ViewGroup) findViewById(R.id.fragment_container);
|
||||||
showRelevantView();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||||
menu.clear();
|
menu.clear();
|
||||||
boolean parent = super.onPrepareOptionsMenu(menu);
|
boolean parent = super.onPrepareOptionsMenu(menu);
|
||||||
boolean inner = currentView.buildMenu(menu, getMenuInflater());
|
boolean inner = currentView != null && currentView.buildMenu(menu, getMenuInflater());
|
||||||
return parent || inner;
|
return parent || inner;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,32 +155,38 @@ public class SwapWorkflowActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void showRelevantView() {
|
private void showRelevantView() {
|
||||||
if (currentView != null && currentView.getStep() == state.getStep()) {
|
|
||||||
// Already showing the currect step, so don't bother changing anything.
|
if (service == null) {
|
||||||
|
showInitialLoading();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch(state.getStep()) {
|
if (container.getVisibility() == View.GONE || currentView != null && currentView.getStep() == service.getStep()) {
|
||||||
case SwapManager.STEP_INTRO:
|
// Already showing the correct step, so don't bother changing anything.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(service.getStep()) {
|
||||||
|
case SwapService.STEP_INTRO:
|
||||||
showIntro();
|
showIntro();
|
||||||
break;
|
break;
|
||||||
case SwapManager.STEP_SELECT_APPS:
|
case SwapService.STEP_SELECT_APPS:
|
||||||
showSelectApps();
|
showSelectApps();
|
||||||
break;
|
break;
|
||||||
case SwapManager.STEP_SHOW_NFC:
|
case SwapService.STEP_SHOW_NFC:
|
||||||
showNfc();
|
showNfc();
|
||||||
break;
|
break;
|
||||||
case SwapManager.STEP_JOIN_WIFI:
|
case SwapService.STEP_JOIN_WIFI:
|
||||||
showJoinWifi();
|
showJoinWifi();
|
||||||
break;
|
break;
|
||||||
case SwapManager.STEP_WIFI_QR:
|
case SwapService.STEP_WIFI_QR:
|
||||||
showWifiQr();
|
showWifiQr();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public SwapManager getState() {
|
public SwapService getState() {
|
||||||
return state;
|
return service;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showNfc() {
|
private void showNfc() {
|
||||||
@ -149,7 +199,16 @@ public class SwapWorkflowActivity extends AppCompatActivity {
|
|||||||
container.removeAllViews();
|
container.removeAllViews();
|
||||||
View view = ((LayoutInflater)getSystemService(LAYOUT_INFLATER_SERVICE)).inflate(viewRes, container, false);
|
View view = ((LayoutInflater)getSystemService(LAYOUT_INFLATER_SERVICE)).inflate(viewRes, container, false);
|
||||||
currentView = (InnerView)view;
|
currentView = (InnerView)view;
|
||||||
state.setStep(currentView.getStep());
|
|
||||||
|
// Don't actually set the step to STEP_INITIAL_LOADING, as we are going to use this view
|
||||||
|
// purely as a placeholder for _whatever view is meant to be shown_.
|
||||||
|
if (currentView.getStep() != SwapService.STEP_INITIAL_LOADING) {
|
||||||
|
if (service == null) {
|
||||||
|
throw new IllegalStateException("We are not in the STEP_INITIAL_LOADING state, but the service is not ready.");
|
||||||
|
}
|
||||||
|
service.setStep(currentView.getStep());
|
||||||
|
}
|
||||||
|
|
||||||
toolbar.setBackgroundColor(currentView.getToolbarColour());
|
toolbar.setBackgroundColor(currentView.getToolbarColour());
|
||||||
toolbar.setTitle(currentView.getToolbarTitle());
|
toolbar.setTitle(currentView.getToolbarTitle());
|
||||||
toolbar.setNavigationIcon(R.drawable.ic_close_white);
|
toolbar.setNavigationIcon(R.drawable.ic_close_white);
|
||||||
@ -164,15 +223,19 @@ public class SwapWorkflowActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void onToolbarCancel() {
|
private void onToolbarCancel() {
|
||||||
SwapManager.load(this).disableSwapping();
|
getService().disableSwapping();
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void showInitialLoading() {
|
||||||
|
inflateInnerView(R.layout.swap_initial_loading);
|
||||||
|
}
|
||||||
|
|
||||||
private void showIntro() {
|
private void showIntro() {
|
||||||
if (!state.isEnabled()) {
|
if (!getService().isEnabled()) {
|
||||||
prepareInitialRepo();
|
prepareInitialRepo();
|
||||||
}
|
}
|
||||||
SwapManager.load(this).scanForPeers();
|
getService().scanForPeers();
|
||||||
inflateInnerView(R.layout.swap_blank);
|
inflateInnerView(R.layout.swap_blank);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,7 +253,7 @@ public class SwapWorkflowActivity extends AppCompatActivity {
|
|||||||
// Otherwise, probably will need to scan the file system.
|
// Otherwise, probably will need to scan the file system.
|
||||||
public void onAppsSelected() {
|
public void onAppsSelected() {
|
||||||
if (updateSwappableAppsTask == null && !hasPreparedLocalRepo) {
|
if (updateSwappableAppsTask == null && !hasPreparedLocalRepo) {
|
||||||
updateSwappableAppsTask = new PrepareFullSwapRepo(state.getAppsToSwap());
|
updateSwappableAppsTask = new PrepareFullSwapRepo(getService().getAppsToSwap());
|
||||||
updateSwappableAppsTask.execute();
|
updateSwappableAppsTask.execute();
|
||||||
} else if (!attemptToShowNfc()) {
|
} else if (!attemptToShowNfc()) {
|
||||||
showWifiQr();
|
showWifiQr();
|
||||||
@ -218,11 +281,11 @@ public class SwapWorkflowActivity extends AppCompatActivity {
|
|||||||
private void onLocalRepoPrepared() {
|
private void onLocalRepoPrepared() {
|
||||||
updateSwappableAppsTask = null;
|
updateSwappableAppsTask = null;
|
||||||
hasPreparedLocalRepo = true;
|
hasPreparedLocalRepo = true;
|
||||||
if (state.isConnectingWithPeer()) {
|
if (getService().isConnectingWithPeer()) {
|
||||||
startSwappingWithPeer();
|
startSwappingWithPeer();
|
||||||
} else if (!attemptToShowNfc()) {
|
} else if (!attemptToShowNfc()) {
|
||||||
showWifiQr();
|
showWifiQr();
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startSwappingWithPeer() {
|
private void startSwappingWithPeer() {
|
||||||
@ -263,7 +326,7 @@ public class SwapWorkflowActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void swapWith(Peer peer) {
|
public void swapWith(Peer peer) {
|
||||||
state.swapWith(peer);
|
getService().swapWith(peer);
|
||||||
showSelectApps();
|
showSelectApps();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -291,7 +354,7 @@ public class SwapWorkflowActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPreExecute() {
|
protected void onPreExecute() {
|
||||||
state.enableSwapping();
|
getService().enableSwapping();
|
||||||
super.onPreExecute();
|
super.onPreExecute();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ import org.fdroid.fdroid.Preferences;
|
|||||||
import org.fdroid.fdroid.QrGenAsyncTask;
|
import org.fdroid.fdroid.QrGenAsyncTask;
|
||||||
import org.fdroid.fdroid.R;
|
import org.fdroid.fdroid.R;
|
||||||
import org.fdroid.fdroid.Utils;
|
import org.fdroid.fdroid.Utils;
|
||||||
import org.fdroid.fdroid.localrepo.SwapManager;
|
import org.fdroid.fdroid.localrepo.SwapService;
|
||||||
import org.fdroid.fdroid.net.WifiStateChangeService;
|
import org.fdroid.fdroid.net.WifiStateChangeService;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
@ -114,13 +114,13 @@ public class WifiQrView extends ScrollView implements SwapWorkflowActivity.Inner
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getStep() {
|
public int getStep() {
|
||||||
return SwapManager.STEP_WIFI_QR;
|
return SwapService.STEP_WIFI_QR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getPreviousStep() {
|
public int getPreviousStep() {
|
||||||
// TODO: Find a way to make this optionally go back to the NFC screen if appropriate.
|
// TODO: Find a way to make this optionally go back to the NFC screen if appropriate.
|
||||||
return SwapManager.STEP_JOIN_WIFI;
|
return SwapService.STEP_JOIN_WIFI;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ColorRes
|
@ColorRes
|
||||||
|
Loading…
x
Reference in New Issue
Block a user