diff --git a/F-Droid/res/layout/swap_connecting.xml b/F-Droid/res/layout/swap_connecting.xml
new file mode 100644
index 000000000..5df0e4034
--- /dev/null
+++ b/F-Droid/res/layout/swap_connecting.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/F-Droid/res/layout/swap_success.xml b/F-Droid/res/layout/swap_success.xml
new file mode 100644
index 000000000..e00d3c09c
--- /dev/null
+++ b/F-Droid/res/layout/swap_success.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/F-Droid/res/values/strings.xml b/F-Droid/res/values/strings.xml
index 1593feaba..5e22c6ca1 100644
--- a/F-Droid/res/values/strings.xml
+++ b/F-Droid/res/values/strings.xml
@@ -344,4 +344,5 @@
May work
Promising
Best bet
+ Connecting
diff --git a/F-Droid/src/org/fdroid/fdroid/localrepo/SwapManager.java b/F-Droid/src/org/fdroid/fdroid/localrepo/SwapManager.java
index e44ae179d..7f81042fa 100644
--- a/F-Droid/src/org/fdroid/fdroid/localrepo/SwapManager.java
+++ b/F-Droid/src/org/fdroid/fdroid/localrepo/SwapManager.java
@@ -16,6 +16,8 @@ import android.text.TextUtils;
import android.util.Log;
import org.fdroid.fdroid.FDroidApp;
+import org.fdroid.fdroid.ProgressListener;
+import org.fdroid.fdroid.UpdateService;
import org.fdroid.fdroid.localrepo.peers.Peer;
import java.lang.annotation.Retention;
@@ -119,6 +121,7 @@ public class SwapManager {
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;
private @SwapStep int step = STEP_INTRO;
@@ -140,17 +143,48 @@ public class SwapManager {
return appsToSwap;
}
+ 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.");
+ }
+
+ return UpdateService.updateRepoNow(peer.getRepoAddress(), context);
+ }
+
/**
* 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})
+ @IntDef({STEP_INTRO, STEP_SELECT_APPS, STEP_JOIN_WIFI, STEP_SHOW_NFC, STEP_WIFI_QR,
+ STEP_CONNECTING})
@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;
+
+ 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
// ==========================================
diff --git a/F-Droid/src/org/fdroid/fdroid/localrepo/peers/BluetoothPeer.java b/F-Droid/src/org/fdroid/fdroid/localrepo/peers/BluetoothPeer.java
index c15a76a00..e2d7def0f 100644
--- a/F-Droid/src/org/fdroid/fdroid/localrepo/peers/BluetoothPeer.java
+++ b/F-Droid/src/org/fdroid/fdroid/localrepo/peers/BluetoothPeer.java
@@ -12,6 +12,11 @@ public class BluetoothPeer implements Peer {
this.device = device;
}
+ @Override
+ public String toString() {
+ return getName();
+ }
+
@Override
public String getName() {
return "Bluetooth: " + device.getName();
@@ -24,9 +29,14 @@ public class BluetoothPeer implements Peer {
@Override
public boolean equals(Peer peer) {
- return peer != null && peer instanceof BluetoothPeer && ((BluetoothPeer)peer).device.getAddress() == device.getAddress();
+ return peer != null && peer instanceof BluetoothPeer && ((BluetoothPeer)peer).device.getAddress().equals(device.getAddress());
}
-
+
+ @Override
+ public String getRepoAddress() {
+ return "bluetooth://" + device.getAddress() + "/fdroid/repo";
+ }
+
@Override
public int describeContents() {
return 0;
diff --git a/F-Droid/src/org/fdroid/fdroid/localrepo/peers/BonjourPeer.java b/F-Droid/src/org/fdroid/fdroid/localrepo/peers/BonjourPeer.java
index a11a91319..fabb89d66 100644
--- a/F-Droid/src/org/fdroid/fdroid/localrepo/peers/BonjourPeer.java
+++ b/F-Droid/src/org/fdroid/fdroid/localrepo/peers/BonjourPeer.java
@@ -14,6 +14,11 @@ public class BonjourPeer implements Peer {
this.serviceInfo = serviceInfo;
}
+ @Override
+ public String toString() {
+ return getName();
+ }
+
@Override
public String getName() {
return "Bonjour: " + serviceInfo.getName();
@@ -34,6 +39,10 @@ public class BonjourPeer implements Peer {
return false;
}
+ @Override
+ public String getRepoAddress() {
+ return serviceInfo.getURL();
+ }
@Override
public int describeContents() {
diff --git a/F-Droid/src/org/fdroid/fdroid/localrepo/peers/Peer.java b/F-Droid/src/org/fdroid/fdroid/localrepo/peers/Peer.java
index 047fd0fdc..490837bc4 100644
--- a/F-Droid/src/org/fdroid/fdroid/localrepo/peers/Peer.java
+++ b/F-Droid/src/org/fdroid/fdroid/localrepo/peers/Peer.java
@@ -11,4 +11,5 @@ public interface Peer extends Parcelable {
boolean equals(Peer peer);
+ String getRepoAddress();
}
diff --git a/F-Droid/src/org/fdroid/fdroid/views/swap/JoinWifiView.java b/F-Droid/src/org/fdroid/fdroid/views/swap/JoinWifiView.java
index 092ec07d9..b606b3ddf 100644
--- a/F-Droid/src/org/fdroid/fdroid/views/swap/JoinWifiView.java
+++ b/F-Droid/src/org/fdroid/fdroid/views/swap/JoinWifiView.java
@@ -113,7 +113,7 @@ public class JoinWifiView extends RelativeLayout implements SwapWorkflowActivity
next.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
- getActivity().onJoinWifiComplete();
+ getActivity().showSelectApps();
return true;
}
});
@@ -127,7 +127,7 @@ public class JoinWifiView extends RelativeLayout implements SwapWorkflowActivity
@Override
public int getPreviousStep() {
- return SwapManager.STEP_SELECT_APPS;
+ return SwapManager.STEP_INTRO;
}
@ColorRes
diff --git a/F-Droid/src/org/fdroid/fdroid/views/swap/SelectAppsView.java b/F-Droid/src/org/fdroid/fdroid/views/swap/SelectAppsView.java
index 9da360cca..6651cbf19 100644
--- a/F-Droid/src/org/fdroid/fdroid/views/swap/SelectAppsView.java
+++ b/F-Droid/src/org/fdroid/fdroid/views/swap/SelectAppsView.java
@@ -124,7 +124,7 @@ public class SelectAppsView extends ListView implements
@Override
public int getPreviousStep() {
- return SwapManager.STEP_INTRO;
+ return getState().isConnectingWithPeer() ? SwapManager.STEP_JOIN_WIFI : SwapManager.STEP_INTRO;
}
@ColorRes
diff --git a/F-Droid/src/org/fdroid/fdroid/views/swap/StartSwapView.java b/F-Droid/src/org/fdroid/fdroid/views/swap/StartSwapView.java
index 6c0444f0b..a232325cb 100644
--- a/F-Droid/src/org/fdroid/fdroid/views/swap/StartSwapView.java
+++ b/F-Droid/src/org/fdroid/fdroid/views/swap/StartSwapView.java
@@ -18,6 +18,7 @@ import android.view.Menu;
import android.view.MenuInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CompoundButton;
@@ -32,6 +33,8 @@ import org.fdroid.fdroid.R;
import org.fdroid.fdroid.localrepo.SwapManager;
import org.fdroid.fdroid.localrepo.peers.Peer;
+import java.util.ArrayList;
+
public class StartSwapView extends LinearLayout implements SwapWorkflowActivity.InnerView {
private static final String TAG = "StartSwapView";
@@ -63,7 +66,7 @@ public class StartSwapView extends LinearLayout implements SwapWorkflowActivity.
private class PeopleNearbyAdapter extends ArrayAdapter {
public PeopleNearbyAdapter(Context context) {
- super(context, 0, new Peer[] {});
+ super(context, 0, new ArrayList());
}
@Override
@@ -108,6 +111,8 @@ public class StartSwapView extends LinearLayout implements SwapWorkflowActivity.
uiInitBluetooth();
uiInitWifi();
uiInitButtons();
+ uiUpdatePeersInfo();
+
}
private void uiInitButtons() {
@@ -140,6 +145,14 @@ public class StartSwapView extends LinearLayout implements SwapWorkflowActivity.
peopleNearbyList.setAdapter(adapter);
uiUpdatePeersInfo();
+ peopleNearbyList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView> parent, View view, int position, long id) {
+ Peer peer = adapter.getItem(position);
+ onPeerSelected(peer);
+ }
+ });
+
getContext().registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -175,6 +188,9 @@ public class StartSwapView extends LinearLayout implements SwapWorkflowActivity.
viewBluetoothId = (TextView)findViewById(R.id.device_id_bluetooth);
viewBluetoothId.setText(bluetooth.getName());
+ int textResource = getManager().isBluetoothDiscoverable() ? R.string.swap_visible_bluetooth : R.string.swap_not_visible_bluetooth;
+ textBluetoothVisible.setText(textResource);
+
Switch bluetoothSwitch = ((Switch) findViewById(R.id.switch_bluetooth));
bluetoothSwitch.setChecked(getManager().isBluetoothDiscoverable());
bluetoothSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@@ -183,13 +199,13 @@ public class StartSwapView extends LinearLayout implements SwapWorkflowActivity.
if (isChecked) {
getManager().ensureBluetoothDiscoverable();
getManager().scanForPeers();
- textBluetoothVisible.setText(getContext().getString(R.string.swap_visible_bluetooth));
+ textBluetoothVisible.setText(R.string.swap_visible_bluetooth);
uiUpdatePeersInfo();
// TODO: When they deny the request for enabling bluetooth, we need to disable this switch...
} else {
getManager().cancelScanningForPeers();
getManager().makeBluetoothNonDiscoverable();
- textBluetoothVisible.setText(getContext().getString(R.string.swap_not_visible_bluetooth));
+ textBluetoothVisible.setText(R.string.swap_not_visible_bluetooth);
uiUpdatePeersInfo();
}
}
@@ -206,16 +222,19 @@ public class StartSwapView extends LinearLayout implements SwapWorkflowActivity.
viewWifiId = (TextView)findViewById(R.id.device_id_wifi);
viewWifiNetwork = (TextView)findViewById(R.id.wifi_network);
+ int textResource = getManager().isBonjourDiscoverable() ? R.string.swap_visible_wifi : R.string.swap_not_visible_wifi;
+ textWifiVisible.setText(textResource);
+
Switch wifiSwitch = (Switch)findViewById(R.id.switch_wifi);
wifiSwitch.setChecked(getManager().isBonjourDiscoverable());
wifiSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
- textWifiVisible.setText(getContext().getString(R.string.swap_visible_wifi));
+ textWifiVisible.setText(R.string.swap_visible_wifi);
uiUpdatePeersInfo();
} else {
- textWifiVisible.setText(getContext().getString(R.string.swap_not_visible_wifi));
+ textWifiVisible.setText(R.string.swap_not_visible_wifi);
uiUpdatePeersInfo();
}
}
@@ -239,6 +258,10 @@ public class StartSwapView extends LinearLayout implements SwapWorkflowActivity.
}
}
+ private void onPeerSelected(Peer peer) {
+ getActivity().swapWith(peer);
+ }
+
@Override
public boolean buildMenu(Menu menu, @NonNull MenuInflater inflater) {
return false;
diff --git a/F-Droid/src/org/fdroid/fdroid/views/swap/SwapConnecting.java b/F-Droid/src/org/fdroid/fdroid/views/swap/SwapConnecting.java
new file mode 100644
index 000000000..479a5df31
--- /dev/null
+++ b/F-Droid/src/org/fdroid/fdroid/views/swap/SwapConnecting.java
@@ -0,0 +1,108 @@
+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.util.Log;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import org.fdroid.fdroid.Preferences;
+import org.fdroid.fdroid.ProgressListener;
+import org.fdroid.fdroid.R;
+import org.fdroid.fdroid.UpdateService;
+import org.fdroid.fdroid.localrepo.SwapManager;
+import org.fdroid.fdroid.localrepo.peers.Peer;
+
+public class SwapConnecting extends LinearLayout implements SwapWorkflowActivity.InnerView {
+
+ private final static String TAG = "SwapConnecting";
+
+ public SwapConnecting(Context context) {
+ super(context);
+ }
+
+ public SwapConnecting(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public SwapConnecting(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+ public SwapConnecting(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ private SwapWorkflowActivity getActivity() {
+ return (SwapWorkflowActivity)getContext();
+ }
+
+ private SwapManager getManager() {
+ return getActivity().getState();
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ Peer peer = getManager().getPeer();
+ if (peer == null) {
+ Log.e(TAG, "Cannot find the peer to connect to.");
+
+ // TODO: Don't go to the selected apps, rather show a Toast message and then
+ // go to the intro screen.
+ getActivity().showSelectApps();
+ return;
+ }
+
+ String heading = getContext().getString(R.string.status_connecting_to_repo, getActivity().getState().getPeer().getName());
+ ((TextView) findViewById(R.id.heading)).setText(heading);
+
+ UpdateService.UpdateReceiver receiver = getManager().connectTo(peer);
+
+ receiver.hideDialog();
+ receiver.setListener(new ProgressListener() {
+ @Override
+ public void onProgress(Event event) {
+ ((TextView) findViewById(R.id.progress)).setText(event.data.getString(UpdateService.EXTRA_ADDRESS));
+ }
+ });
+ }
+
+ @Override
+ public boolean buildMenu(Menu menu, @NonNull MenuInflater inflater) {
+ return true;
+ }
+
+ @Override
+ public int getStep() {
+ return SwapManager.STEP_CONNECTING;
+ }
+
+ @Override
+ public int getPreviousStep() {
+ return SwapManager.STEP_SELECT_APPS;
+ }
+
+ @ColorRes
+ public int getToolbarColour() {
+ return getResources().getColor(R.color.swap_blue);
+ }
+
+ @Override
+ public String getToolbarTitle() {
+ return getResources().getString(R.string.swap_connecting);
+ }
+}
diff --git a/F-Droid/src/org/fdroid/fdroid/views/swap/SwapWorkflowActivity.java b/F-Droid/src/org/fdroid/fdroid/views/swap/SwapWorkflowActivity.java
index 2118760a9..8a12ef950 100644
--- a/F-Droid/src/org/fdroid/fdroid/views/swap/SwapWorkflowActivity.java
+++ b/F-Droid/src/org/fdroid/fdroid/views/swap/SwapWorkflowActivity.java
@@ -32,6 +32,7 @@ import org.fdroid.fdroid.Utils;
import org.fdroid.fdroid.data.NewRepoConfig;
import org.fdroid.fdroid.localrepo.LocalRepoManager;
import org.fdroid.fdroid.localrepo.SwapManager;
+import org.fdroid.fdroid.localrepo.peers.Peer;
import java.util.Arrays;
import java.util.HashSet;
@@ -191,8 +192,8 @@ public class SwapWorkflowActivity extends AppCompatActivity {
if (updateSwappableAppsTask == null && !hasPreparedLocalRepo) {
updateSwappableAppsTask = new PrepareFullSwapRepo(state.getAppsToSwap());
updateSwappableAppsTask.execute();
- } else {
- showJoinWifi();
+ } else if (!attemptToShowNfc()) {
+ showWifiQr();
}
}
@@ -208,25 +209,30 @@ public class SwapWorkflowActivity extends AppCompatActivity {
/**
* Once the UpdateAsyncTask has finished preparing our repository index, we can
- * show the next screen to the user.
+ * show the next screen to the user. This will be one of two things:
+ * * If we directly selected a peer to swap with initially, we will skip straight to getting
+ * the list of apps from that device.
+ * * Alternatively, if we didn't have a person to connect to, and instead clicked "Scan QR Code",
+ * then we want to show a QR code or NFC dialog.
*/
private void onLocalRepoPrepared() {
updateSwappableAppsTask = null;
hasPreparedLocalRepo = true;
- showJoinWifi();
+ if (state.isConnectingWithPeer()) {
+ startSwappingWithPeer();
+ } else if (!attemptToShowNfc()) {
+ showWifiQr();
+ };
+ }
+
+ private void startSwappingWithPeer() {
+ inflateInnerView(R.layout.swap_connecting);
}
private void showJoinWifi() {
inflateInnerView(R.layout.swap_join_wifi);
}
- public void onJoinWifiComplete() {
- ensureLocalRepoRunning();
- if (!attemptToShowNfc()) {
- showWifiQr();
- }
- }
-
public void showWifiQr() {
inflateInnerView(R.layout.swap_wifi_qr);
}
@@ -247,15 +253,16 @@ public class SwapWorkflowActivity extends AppCompatActivity {
return false;
}
- private void ensureLocalRepoRunning() {
- getState().enableSwapping();
- }
-
public void stopSwapping() {
getState().disableSwapping();
finish();
}
+ public void swapWith(Peer peer) {
+ state.swapWith(peer);
+ showSelectApps();
+ }
+
@Override
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, intent);