WIP: Refactoring Bonjour from manage repos to swap.

Implementing the bare bones of a generic "peer finder" framework. This
may or may not eventuate to something which can live in its own library
and be used by other projects. Might go hand in hand with Carries idea
of having a common UI to be shared among projects.

Got Bluetooth and Bonjour kinda working, but the UI is crud,
and it doesn't remove items and ends up with duplicates. Otherwise,
on our way to a proper "nearby peers" screen.
This commit is contained in:
Peter Serwylo 2015-06-22 08:59:20 +10:00
parent a30ec646b2
commit 0100415e3e
14 changed files with 139 additions and 47 deletions

View File

@ -44,6 +44,7 @@
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="18" />

View File

@ -16,7 +16,6 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/activatedBackgroundIndicator"
android:minHeight="?android:attr/listPreferredItemHeight"
android:paddingBottom="2dip"
android:paddingTop="2dip">

View File

@ -181,26 +181,11 @@
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1.00"
android:drawableStart="@drawable/ic_fdroid_grey"
android:drawablePadding="10dp"
android:paddingStart="20dp"
android:background="@android:color/transparent"
android:text="@string/swap_send_fdroid"
android:gravity="start|center_vertical" />
android:layout_height="wrap_content" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1.00"
android:drawableStart="@drawable/ic_qr_grey"
android:drawablePadding="10dp"
android:paddingStart="20dp"
android:background="@android:color/transparent"
android:text="@string/swap_scan_qr"
android:gravity="start|center_vertical" />
</LinearLayout>
android:layout_width="match_parent"/>
</org.fdroid.fdroid.views.swap.StartSwapView>

View File

@ -321,6 +321,10 @@
<string name="swap_dont_show_again">Don\'t show this again</string>
<string name="swap_tap_for_details_and_install">Tap an app for details and to install.</string>
<string name="swap_scan_or_type_url">One person needs to scan the code, or type the URL of the other in a browser.</string>
<string name="swap_choose_apps">Choose Apps</string>
<string name="swap_scan_qr">Scan QR Code</string>
<string name="swap_people_nearby">People Nearby</string>
<!-- WiFi AP status for Swap flow -->
<string name="wifi_ap_public">Public</string>
<string name="wifi_ap_private">Private</string>
@ -328,8 +332,6 @@
<string name="wifi_warning_public">May work</string>
<string name="wifi_warning_private">Promising</string>
<string name="wifi_warning_personal">Best bet</string>
<string name="swap_choose_apps">Choose Apps</string>
<string name="swap_scan_qr">Scan QR Code</string>
<string name="swap_nearby">Nearby Swap</string>
<string name="swap_intro">Connect and trade apps with people near you.</string>
<string name="swap_visible_bluetooth">Visible via Bluetooth</string>

View File

@ -18,9 +18,9 @@ import org.fdroid.fdroid.localrepo.peers.PeerFinder;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class SwapManager {
@ -49,7 +49,7 @@ public class SwapManager {
private Set<String> appsToSwap;
@NonNull
private Collection<Peer> peers;
private List<Peer> peers;
private SwapManager(@NonNull Context context, @NonNull Set<String> appsToSwap) {
this.context = context.getApplicationContext();
@ -73,6 +73,12 @@ public class SwapManager {
// Search for peers to swap
// ==========================================================
private PeerFinder.Listener<Peer> peerListener;
public void setPeerListener(PeerFinder.Listener<Peer> listener) {
this.peerListener = listener;
}
public void scanForPeers() {
if (service != null) {
Log.d(TAG, "Scanning for nearby devices to swap with...");
@ -91,7 +97,17 @@ public class SwapManager {
}
public void onPeerFound(Peer peer) {
peers.add(peer);
if (!peers.contains(peer)) {
peers.add(peer);
if (peerListener != null) {
peerListener.onPeerFound(peer);
}
}
}
@NonNull
public List<Peer> getPeers() {
return peers;
}

View File

@ -1,21 +1,70 @@
package org.fdroid.fdroid.localrepo.peers;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.util.Log;
// TODO: Still to be implemented
public class BluetoothFinder extends PeerFinder<BluetoothPeer> {
private static final String TAG = "BluetoothFinder";
private final Context context;
private final BluetoothAdapter adapter;
public BluetoothFinder(Context context) {
this.context = context;
adapter = BluetoothAdapter.getDefaultAdapter();
}
@Override
public void scan() {
if (adapter == null) {
Log.i(TAG, "Not scanning for bluetooth peers to swap with, couldn't find a bluetooth adapter on this device.");
return;
}
final BroadcastReceiver deviceFoundReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (BluetoothDevice.ACTION_FOUND.equals(intent.getAction())) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
onDeviceFound(device);
}
}
};
final BroadcastReceiver scanCompleteReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "Scan complete: " + intent.getAction());
}
};
context.registerReceiver(deviceFoundReceiver, new IntentFilter(BluetoothDevice.ACTION_FOUND));
context.registerReceiver(scanCompleteReceiver, new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED));
if (!adapter.startDiscovery()) {
Log.e(TAG, "Couldn't start bluetooth scanning.");
}
}
@Override
public void cancel() {
if (adapter != null) {
Log.d(TAG, "Stopping bluetooth discovery.");
adapter.cancelDiscovery();
}
}
private void onDeviceFound(BluetoothDevice device) {
foundPeer(new BluetoothPeer(device));
}
}

View File

@ -21,4 +21,9 @@ public class BluetoothPeer implements Peer {
return android.R.drawable.stat_sys_data_bluetooth;
}
@Override
public boolean equals(Peer peer) {
return peer != null && peer instanceof BluetoothPeer && ((BluetoothPeer)peer).device.getAddress() == device.getAddress();
}
}

View File

@ -62,7 +62,7 @@ public class BonjourFinder extends PeerFinder<BonjourPeer> implements ServiceLis
@Override
protected void onPostExecute(Void result) {
Log.d(TAG, "Cleaning up mDNS service listeners.");
Log.d(TAG, "Adding mDNS service listeners.");
if (mJmdns != null) {
mJmdns.addServiceListener(HTTP_SERVICE_TYPE, BonjourFinder.this);
mJmdns.addServiceListener(HTTPS_SERVICE_TYPE, BonjourFinder.this);
@ -78,6 +78,14 @@ public class BonjourFinder extends PeerFinder<BonjourPeer> implements ServiceLis
@Override
public void serviceAdded(final ServiceEvent event) {
// TODO: Get clarification, but it looks like this is:
// 1) Identifying that there is _a_ bonjour service available
// 2) Adding it to the list to give some sort of feedback to the user
// 3) Requesting more detailed info in an async manner
// 4) If that is in fact an fdroid repo (after requesting info), then add it again
// so that more detailed info can be shown to the user.
//
// If so, when is the old one removed?
addFDroidService(event);
new AsyncTask<Void, Void, Void>() {
@Override

View File

@ -22,4 +22,14 @@ public class BonjourPeer implements Peer {
return R.drawable.wifi;
}
@Override
public boolean equals(Peer peer) {
if (peer != null && peer instanceof BonjourPeer) {
BonjourPeer that = (BonjourPeer)peer;
// TODO: Don't us "name" for comparing, but rather fingerprint of the swap repo.
return that.serviceInfo.getName().equals(this.serviceInfo.getName());
}
return false;
}
}

View File

@ -8,4 +8,6 @@ public interface Peer {
@DrawableRes int getIcon();
boolean equals(Peer peer);
}

View File

@ -28,7 +28,7 @@ public class BonjourType implements SwapType {
@Override
public void start() {
if (Preferences.get().isLocalRepoBonjourEnabled())
if (!Preferences.get().isLocalRepoBonjourEnabled())
return;
/*

View File

@ -72,17 +72,6 @@ public class SelectAppsView extends ListView implements
private AppListAdapter adapter;
private String mCurrentFilterString;
private final Presenter presenter = new Presenter();
public static class Presenter {
private SelectAppsView view;
public void setView(@NonNull SelectAppsView view) {
this.view = view;
}
}
@Override
protected void onFinishInflate() {
@ -101,8 +90,6 @@ public class SelectAppsView extends ListView implements
toggleAppSelected(position);
}
});
presenter.setView(this);
}
@Override

View File

@ -1,26 +1,21 @@
package org.fdroid.fdroid.views.swap;
import android.annotation.TargetApi;
import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.support.annotation.ColorRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.View;
import android.widget.CompoundButton;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.Switch;
import android.widget.TextView;
import org.fdroid.fdroid.R;
import org.fdroid.fdroid.compat.SwitchCompat;
import org.fdroid.fdroid.localrepo.SwapManager;
import org.fdroid.fdroid.localrepo.peers.Peer;
import org.fdroid.fdroid.localrepo.peers.PeerFinder;
public class StartSwapView extends LinearLayout implements SwapWorkflowActivity.InnerView {
@ -48,6 +43,27 @@ public class StartSwapView extends LinearLayout implements SwapWorkflowActivity.
super(context, attrs, defStyleAttr, defStyleRes);
}
private class PeopleNearbyAdapter extends ArrayAdapter<Peer> {
public PeopleNearbyAdapter(Context context) {
super(context, 0, SwapManager.load(context).getPeers());
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = LayoutInflater.from(getContext()).inflate(android.R.layout.two_line_list_item, parent, false);
}
Peer peer = getItem(position);
((TextView)convertView.findViewById(android.R.id.text1)).setText(peer.getName());
return convertView;
}
}
private SwapWorkflowActivity getActivity() {
// TODO: Try and find a better way to get to the SwapActivity, which makes less asumptions.
return (SwapWorkflowActivity)getContext();
@ -104,6 +120,18 @@ public class StartSwapView extends LinearLayout implements SwapWorkflowActivity.
}
});
final PeopleNearbyAdapter adapter = new PeopleNearbyAdapter(getContext());
peopleNearbyList = (ListView)findViewById(R.id.people_nearby);
peopleNearbyList.setAdapter(adapter);
SwapManager.load(getActivity()).setPeerListener(new PeerFinder.Listener<Peer>() {
@Override
public void onPeerFound(Peer peer) {
adapter.notifyDataSetChanged();
}
});
}

View File

@ -49,7 +49,7 @@ public class SwapWorkflowActivity extends ActionBarActivity {
/** @return True if the menu should be shown. */
boolean buildMenu(Menu menu, @NonNull MenuInflater inflater);
/** @return The step that this view represents. */
/** @return The steap that this view represents. */
@SwapManager.SwapStep int getStep();
@SwapManager.SwapStep int getPreviousStep();