| // Copyright 2015 The Vanadium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package io.v.discoverysample; |
| |
| import android.app.Activity; |
| import android.content.Intent; |
| import android.os.Bundle; |
| import android.view.Menu; |
| import android.view.View; |
| import android.widget.Button; |
| import android.widget.ListView; |
| import android.widget.Toast; |
| |
| import com.google.common.base.Function; |
| import com.google.common.util.concurrent.FutureCallback; |
| import com.google.common.util.concurrent.Futures; |
| import com.google.common.util.concurrent.ListenableFuture; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.List; |
| |
| import io.v.android.libs.security.BlessingsManager; |
| import io.v.android.v23.UiThreadExecutor; |
| import io.v.android.v23.V; |
| import io.v.android.v23.services.blessing.BlessingCreationException; |
| import io.v.android.v23.services.blessing.BlessingService; |
| import io.v.v23.InputChannels; |
| import io.v.v23.context.CancelableVContext; |
| import io.v.v23.context.VContext; |
| import io.v.v23.discovery.Attributes; |
| import io.v.v23.discovery.Service; |
| import io.v.v23.discovery.VDiscovery; |
| import io.v.v23.security.BlessingPattern; |
| import io.v.v23.security.Blessings; |
| import io.v.v23.verror.CanceledException; |
| import io.v.v23.verror.VException; |
| import io.v.v23.vom.VomUtil; |
| |
| public class MainActivity extends Activity { |
| private static final Service SERVICE_TO_ADVERTISE = ServiceAdvert.make(); |
| private static final List<BlessingPattern> NO_PATTERNS = new ArrayList<>(); |
| private static final int BLESSINGS_REQUEST = 1; |
| private Button chooseBlessingsButton; |
| private Button scanButton; |
| private Button advertiseButton; |
| private Blessings blessings; |
| private VDiscovery discovery; |
| private VContext rootCtx; |
| private CancelableVContext scanCtx; |
| private CancelableVContext advCtx; |
| private ScanHandlerAdapter adapter; |
| |
| @Override |
| protected void onCreate(Bundle savedInstanceState) { |
| super.onCreate(savedInstanceState); |
| rootCtx = V.init(this); |
| discovery = V.getDiscovery(rootCtx); |
| |
| setContentView(R.layout.activity_main); |
| chooseBlessingsButton = (Button) findViewById(R.id.blessingsButton); |
| chooseBlessingsButton.setOnClickListener(new View.OnClickListener() { |
| @Override |
| public void onClick(View view) { |
| fetchBlessings(); |
| } |
| }); |
| |
| scanButton = (Button) findViewById(R.id.scanForService); |
| scanButton.setEnabled(false); |
| scanButton.setOnClickListener(new View.OnClickListener() { |
| @Override |
| public void onClick(View view) { |
| flipScan(); |
| } |
| }); |
| |
| advertiseButton = (Button) findViewById(R.id.advertiseButton); |
| advertiseButton.setEnabled(false); |
| advertiseButton.setOnClickListener(new View.OnClickListener() { |
| @Override |
| public void onClick(View view) { |
| flipAdvertise(); |
| } |
| }); |
| |
| adapter = new ScanHandlerAdapter(this); |
| ListView devices = (ListView) findViewById(R.id.list_view); |
| devices.setAdapter(adapter); |
| } |
| |
| @Override |
| public boolean onCreateOptionsMenu(Menu menu) { |
| // Inflate the menu; this adds items to the action bar if it is present. |
| getMenuInflater().inflate(R.menu.menu_main, menu); |
| return true; |
| } |
| |
| private void flipAdvertise() { |
| advertiseButton.setEnabled(false); |
| if (advCtx == null) { // not advertising |
| advCtx = rootCtx.withCancel(); |
| Futures.addCallback(discovery.advertise(advCtx, SERVICE_TO_ADVERTISE, NO_PATTERNS), |
| new FutureCallback<ListenableFuture<Void>>() { |
| @Override |
| public void onSuccess(ListenableFuture<Void> result) { |
| // Advertising started. |
| advertiseButton.setText("Stop advertising"); |
| advertiseButton.setEnabled(true); |
| Futures.transform(result, new Function<Void, Void>() { |
| @Override |
| public Void apply(Void input) { |
| // Advertising done. |
| advertiseButton.setText("Advertise"); |
| advertiseButton.setEnabled(true); |
| return null; |
| } |
| }, UiThreadExecutor.INSTANCE); |
| } |
| @Override |
| public void onFailure(final Throwable t) { |
| if (!(t instanceof CanceledException)) { |
| // advertising wasn't stopped via advCtx.cancel() |
| String msg = "Couldn't start advertising: " + t.getMessage(); |
| Toast.makeText(MainActivity.this, msg, Toast.LENGTH_LONG).show(); |
| } |
| advertiseButton.setText("Advertise"); |
| advertiseButton.setEnabled(true); |
| } |
| }, UiThreadExecutor.INSTANCE); |
| } else { // advertising |
| advCtx.cancel(); |
| advCtx = null; |
| } |
| } |
| |
| private void flipScan() { |
| if (scanCtx == null) { // not scanning |
| scanButton.setText("Stop scan"); |
| scanCtx = rootCtx.withCancel(); |
| Futures.addCallback(InputChannels.withCallback( |
| discovery.scan(scanCtx, ServiceAdvert.QUERY), adapter, |
| UiThreadExecutor.INSTANCE), |
| new FutureCallback<Void>() { |
| @Override |
| public void onSuccess(Void result) { |
| MainActivity.this.runOnUiThread(new Runnable() { |
| @Override |
| public void run() { |
| scanButton.setText("Start scan"); |
| scanButton.setEnabled(true); |
| } |
| }); |
| } |
| @Override |
| public void onFailure(Throwable t) { |
| if (!(t instanceof CanceledException)) { |
| // scanning wasn't stopped via scanCtx.cancel() |
| String msg = "Scan failed with error: " + t.getMessage(); |
| Toast.makeText(MainActivity.this, msg, Toast.LENGTH_LONG).show(); |
| } |
| scanButton.setText("Start scan"); |
| scanButton.setEnabled(true); |
| } |
| }, UiThreadExecutor.INSTANCE); |
| } else { // scanning |
| scanButton.setEnabled(false); |
| scanCtx.cancel(); |
| scanCtx = null; |
| } |
| } |
| |
| private void fetchBlessings() { |
| Intent intent = BlessingService.newBlessingIntent(this); |
| startActivityForResult(intent, BLESSINGS_REQUEST); |
| } |
| |
| @Override |
| protected void onActivityResult(int requestCode, int resultCode, Intent data) { |
| switch (requestCode) { |
| case BLESSINGS_REQUEST: |
| try { |
| // The Account Manager will pass us the blessings to use as |
| // an array of bytes. Use VomUtil to decode them... |
| byte[] blessingsVom = |
| BlessingService.extractBlessingReply(resultCode, data); |
| blessings = (Blessings) VomUtil.decode(blessingsVom, Blessings.class); |
| BlessingsManager.addBlessings(this, blessings); |
| Toast.makeText(this, "Success, ready to listen!", |
| Toast.LENGTH_SHORT).show(); |
| scanButton.setEnabled(true); |
| advertiseButton.setEnabled(true); |
| } catch (BlessingCreationException e) { |
| String msg = "Couldn't create blessing: " + e.getMessage(); |
| Toast.makeText(this, msg, Toast.LENGTH_LONG).show(); |
| } catch (VException e) { |
| String msg = "Couldn't store blessing: " + e.getMessage(); |
| Toast.makeText(this, msg, Toast.LENGTH_LONG).show(); |
| } |
| return; |
| } |
| } |
| |
| private static class ServiceAdvert { |
| // Required type/interface name, probably a URL into a web-based |
| // ontology. Necessary for querying. |
| private static final String INTERFACE_NAME = "v.io/x/ref.Thermostat"; |
| |
| // Required service address(es). |
| private static final String[] ADDRESSES = {"localhost:2001", "localhost:2002"}; |
| |
| // Optional universally unique identifier of the service. |
| // If omitted, the discovery service will invent a value. |
| private static final String INSTANCE_UUID = "012"; |
| |
| // Optional service name intended for humans. |
| private static final String INSTANCE_NAME = "livingRoomThermostat"; |
| |
| // Optional arbitrary key value pairs to supplement the service description. |
| private static Attributes makeAttributes() { |
| Attributes attrs = new Attributes(); |
| attrs.put("maxTemp", "451"); |
| return attrs; |
| } |
| |
| // To limit scans to see only this service. |
| static final String QUERY = "v.InterfaceName=\"" + INTERFACE_NAME + "\""; |
| |
| // Make a service description (not an actual service). |
| static Service make() { |
| return new Service( |
| INSTANCE_UUID, INSTANCE_NAME, INTERFACE_NAME, |
| makeAttributes(), Arrays.asList(ADDRESSES)); |
| } |
| } |
| } |