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);