diff --git a/app/build.gradle b/app/build.gradle index 6c9e5e0..7b14486 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,12 +1,12 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 25 - buildToolsVersion "25.0.1" + compileSdkVersion rootProject.ext.compile_sdk_version + buildToolsVersion rootProject.ext.build_tools_version defaultConfig { applicationId "za.co.riggaroo.androidthings.distributedpiano" minSdkVersion 24 - targetSdkVersion 25 + targetSdkVersion rootProject.ext.target_sdk_version versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" @@ -20,14 +20,14 @@ android { } dependencies { - compile fileTree(dir: 'libs', include: ['*.jar']) - androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { + implementation fileTree(dir: 'libs', include: ['*.jar']) + androidTestImplementation("com.android.support.test.espresso:espresso-core:$espresso_version", { exclude group: 'com.android.support', module: 'support-annotations' }) - compile 'com.android.support:appcompat-v7:25.1.0' + implementation "com.android.support:appcompat-v7:$support_version" - compile 'com.google.android.things.contrib:driver-pwmspeaker:0.1' - provided 'com.google.android.things:androidthings:0.1-devpreview' - compile 'com.google.android.gms:play-services-nearby:10.0.1' - testCompile 'junit:junit:4.12' + implementation 'com.google.android.things.contrib:driver-pwmspeaker:0.1' + compileOnly "com.google.android.things:androidthings:$things_version" + implementation "com.google.android.gms:play-services-nearby:$play_services_version" + testImplementation "junit:junit:$junit_version" } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 71f0023..4509ad4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,7 +2,12 @@ - + + + + + + - - diff --git a/app/src/main/java/za/co/riggaroo/androidthings/distributedpiano/PianoActivity.java b/app/src/main/java/za/co/riggaroo/androidthings/distributedpiano/PianoActivity.java index 556555f..0d8274f 100644 --- a/app/src/main/java/za/co/riggaroo/androidthings/distributedpiano/PianoActivity.java +++ b/app/src/main/java/za/co/riggaroo/androidthings/distributedpiano/PianoActivity.java @@ -1,8 +1,9 @@ package za.co.riggaroo.androidthings.distributedpiano; import android.app.Activity; -import android.net.ConnectivityManager; +import android.content.pm.PackageManager; import android.os.Bundle; +import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.util.Log; @@ -10,9 +11,12 @@ import java.io.IOException; +import static android.Manifest.permission.ACCESS_COARSE_LOCATION; + public class PianoActivity extends Activity implements PianoContract.View { private static final String TAG = "PianoActivity"; + private final static int REQUEST_PERMISSION_REQ_CODE = 33; private Speaker speaker; private PianoContract.Presenter presenter; @@ -21,10 +25,44 @@ public class PianoActivity extends Activity implements PianoContract.View { protected void onCreate(@Nullable final Bundle savedInstanceState) { super.onCreate(savedInstanceState); + if (hasRequiredPermissions()) { + startComponents(); + } else { + requestRequiredPermissions(); + } + } + + @Override + public void onRequestPermissionsResult(final int requestCode, final @NonNull String[] permissions, final @NonNull int[] grantResults) { + switch (requestCode) { + case REQUEST_PERMISSION_REQ_CODE: { + if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { + // We have been granted the Manifest.permission.ACCESS_COARSE_LOCATION permission. Now we may proceed with advertising. + startComponents(); + presenter.attachView(this); + } else { + Log.w(TAG, "Required permissions not granted"); + } + break; + } + } + } + + private boolean hasRequiredPermissions() { + return checkSelfPermission(ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED; + } + + private void requestRequiredPermissions() { + if (shouldShowRequestPermissionRationale(ACCESS_COARSE_LOCATION)) { + Log.w(TAG, "Location permission is required for this application"); + } + requestPermissions(new String[]{ACCESS_COARSE_LOCATION}, REQUEST_PERMISSION_REQ_CODE); + } + + private void startComponents() { try { speaker = new Speaker(BoardDefaults.getPwmPin()); - presenter = new PianoPresenter(this, (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE), - getString(R.string.service_id), getPackageName()); + presenter = new PianoPresenter(this, getString(R.string.service_id)); } catch (IOException e) { throw new IllegalArgumentException("Piezo can't be opened, lets end this here."); } @@ -34,7 +72,8 @@ protected void onCreate(@Nullable final Bundle savedInstanceState) { public void onStart() { super.onStart(); Log.d(TAG, "onStart"); - presenter.attachView(this); + if (presenter != null) + presenter.attachView(this); } @@ -42,7 +81,8 @@ public void onStart() { public void onStop() { super.onStop(); Log.d(TAG, "onStop"); - presenter.detachView(); + if (presenter != null) + presenter.detachView(); } diff --git a/app/src/main/java/za/co/riggaroo/androidthings/distributedpiano/PianoPresenter.java b/app/src/main/java/za/co/riggaroo/androidthings/distributedpiano/PianoPresenter.java index 9ebe766..e7320b1 100644 --- a/app/src/main/java/za/co/riggaroo/androidthings/distributedpiano/PianoPresenter.java +++ b/app/src/main/java/za/co/riggaroo/androidthings/distributedpiano/PianoPresenter.java @@ -1,9 +1,6 @@ package za.co.riggaroo.androidthings.distributedpiano; - import android.content.Context; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; @@ -12,42 +9,91 @@ import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.api.GoogleApiClient; import com.google.android.gms.common.api.ResultCallback; -import com.google.android.gms.common.api.Status; import com.google.android.gms.nearby.Nearby; -import com.google.android.gms.nearby.connection.AppIdentifier; -import com.google.android.gms.nearby.connection.AppMetadata; +import com.google.android.gms.nearby.connection.AdvertisingOptions; +import com.google.android.gms.nearby.connection.ConnectionInfo; +import com.google.android.gms.nearby.connection.ConnectionLifecycleCallback; +import com.google.android.gms.nearby.connection.ConnectionResolution; import com.google.android.gms.nearby.connection.Connections; import com.google.android.gms.nearby.connection.ConnectionsStatusCodes; +import com.google.android.gms.nearby.connection.Payload; +import com.google.android.gms.nearby.connection.PayloadCallback; +import com.google.android.gms.nearby.connection.PayloadTransferUpdate; +import com.google.android.gms.nearby.connection.Strategy; import java.io.IOException; import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.List; class PianoPresenter implements PianoContract.Presenter, GoogleApiClient.ConnectionCallbacks, - GoogleApiClient.OnConnectionFailedListener, Connections.ConnectionRequestListener, Connections.MessageListener { + GoogleApiClient.OnConnectionFailedListener { private static final String TAG = "PianoPresenter"; private final GoogleApiClient googleApiClient; + private static final String DEVICE_NAME = "DistributedPiano"; private final String serviceId; - private final String packageName; private PianoContract.View view; - private ConnectivityManager connectivityManager; - PianoPresenter(Context context, ConnectivityManager connectivityManager, String serviceId, - String packageName) throws IOException { + private final ConnectionLifecycleCallback connectionLifecycleCallback = new ConnectionLifecycleCallback() { + @Override + public void onConnectionInitiated(String endpointId, ConnectionInfo connectionInfo) { + Log.d(TAG, "Connection initiated from " + endpointId + " " + connectionInfo.getEndpointName()); + Nearby.Connections.acceptConnection(googleApiClient, endpointId, payloadCallback); + } + + @Override + public void onConnectionResult(String endpointId, ConnectionResolution connectionResolution) { + Log.d(TAG, "onConnectionResult"); + if (connectionResolution.getStatus().isSuccess()) { + Log.d(TAG, "Endpoint " + endpointId + " connected"); + } else { + Log.w(TAG, "Endpoint " + endpointId + " already connected"); + } + } + + @Override + public void onDisconnected(String endpointId) { + Log.d(TAG, endpointId + " disconnected"); + } + }; + + private final PayloadCallback payloadCallback = new PayloadCallback() { + @Override + public void onPayloadReceived(String endpointId, Payload payload) { + Log.d(TAG, "onPayloadReceived"); + double frequency = toDouble(payload.asBytes()); + if (frequency == -1) { + view.stopPlayingNote(); + return; + } + view.playNote(frequency); + } + + @Override + public void onPayloadTransferUpdate(String endpointId, PayloadTransferUpdate payloadTransferUpdate) { + switch (payloadTransferUpdate.getStatus()) { + case PayloadTransferUpdate.Status.IN_PROGRESS: + Log.d(TAG, "onPayloadTransferUpdate " + payloadTransferUpdate.getBytesTransferred() + " bytes transferred"); + break; + case PayloadTransferUpdate.Status.SUCCESS: + Log.d(TAG, "onPayloadTransferUpdate completed"); + break; + case PayloadTransferUpdate.Status.FAILURE: + Log.d(TAG, "onPayloadTransferUpdate failed"); + break; + } + } + }; + + PianoPresenter(Context context, String serviceId) throws IOException { googleApiClient = new GoogleApiClient.Builder(context).addConnectionCallbacks(this) .addOnConnectionFailedListener(this).addApi(Nearby.CONNECTIONS_API).build(); this.serviceId = serviceId; - this.connectivityManager = connectivityManager; - this.packageName = packageName; } @Override public void onConnected(@Nullable final Bundle bundle) { Log.d(TAG, "onConnected!"); startAdvertising(); - } @Override @@ -60,25 +106,11 @@ public void onConnectionFailed(@NonNull final ConnectionResult connectionResult) Log.d(TAG, "onConnectionFailed"); } - private boolean isConnectedToNetwork() { - NetworkInfo info = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI); - NetworkInfo info1 = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_ETHERNET); - - return (info != null && info.isConnectedOrConnecting()) || (info1 != null && info1.isConnectedOrConnecting()); - } - - private void startAdvertising() { Log.d(TAG, "startAdvertising"); - if (!isConnectedToNetwork()) { - Log.d(TAG, "startAdvertising: not connected to WiFi network."); - return; - } - - List appIdentifierList = new ArrayList<>(); - appIdentifierList.add(new AppIdentifier(packageName)); - AppMetadata appMetadata = new AppMetadata(appIdentifierList); - Nearby.Connections.startAdvertising(googleApiClient, serviceId, appMetadata, 0L, this) + AdvertisingOptions advertisingOptions = new AdvertisingOptions(Strategy.P2P_CLUSTER); + Nearby.Connections + .startAdvertising(googleApiClient, DEVICE_NAME, serviceId, connectionLifecycleCallback, advertisingOptions) .setResultCallback(new ResultCallback() { @Override public void onResult(@NonNull Connections.StartAdvertisingResult result) { @@ -99,44 +131,10 @@ public void onResult(@NonNull Connections.StartAdvertisingResult result) { }); } - @Override - public void onConnectionRequest(final String endpointId, String deviceId, String endpointName, byte[] payload) { - Log.d(TAG, "onConnectionRequest"); - - Nearby.Connections.acceptConnectionRequest(googleApiClient, endpointId, payload, this) - .setResultCallback(new ResultCallback() { - @Override - public void onResult(@NonNull Status status) { - if (status.isSuccess()) { - Log.d(TAG, "acceptConnectionRequest: SUCCESS"); - - } else { - Log.d(TAG, "acceptConnectionRequest: FAILURE"); - } - } - }); - } - - @Override - public void onMessageReceived(final String s, final byte[] bytes, final boolean b) { - Log.d(TAG, "onMessageReceived"); - double frequency = toDouble(bytes); - if (frequency == -1) { - view.stopPlayingNote(); - return; - } - view.playNote(frequency); - } - private static double toDouble(byte[] bytes) { return ByteBuffer.wrap(bytes).getDouble(); } - @Override - public void onDisconnected(final String s) { - - } - @Override public void detachView() { this.view = null; diff --git a/build.gradle b/build.gradle index 3384c4b..033c103 100644 --- a/build.gradle +++ b/build.gradle @@ -3,9 +3,10 @@ buildscript { repositories { jcenter() + maven { url 'https://maven.google.com' } } dependencies { - classpath 'com.android.tools.build:gradle:2.3.0-beta2' + classpath 'com.android.tools.build:gradle:3.0.0-alpha4' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files @@ -15,9 +16,19 @@ buildscript { allprojects { repositories { jcenter() + maven { url 'https://maven.google.com' } } } task clean(type: Delete) { delete rootProject.buildDir } + +ext.compile_sdk_version = 26 +ext.build_tools_version = "26.0.0" +ext.target_sdk_version = 26 +ext.support_version = "26.0.0-beta2" +ext.play_services_version = "11.0.1" +ext.things_version = "0.4.1-devpreview" +ext.junit_version = "4.12" +ext.espresso_version = "2.2.2" \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 6205586..bbaf65f 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Sun Jan 22 17:32:18 SAST 2017 +#Mon Jun 19 08:39:43 BST 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.0-rc-1-all.zip diff --git a/pianoplayer/build.gradle b/pianoplayer/build.gradle index f2f5550..e0b8c37 100644 --- a/pianoplayer/build.gradle +++ b/pianoplayer/build.gradle @@ -1,13 +1,13 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 25 - buildToolsVersion "25.0.1" + compileSdkVersion rootProject.ext.compile_sdk_version + buildToolsVersion rootProject.ext.build_tools_version defaultConfig { applicationId "za.co.riggaroo.androidthings.pianoplayer" minSdkVersion 18 - targetSdkVersion 25 + targetSdkVersion rootProject.ext.target_sdk_version versionCode 1 versionName "1.0" @@ -23,12 +23,12 @@ android { } dependencies { - compile fileTree(dir: 'libs', include: ['*.jar']) - androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { + implementation fileTree(dir: 'libs', include: ['*.jar']) + androidTestImplementation("com.android.support.test.espresso:espresso-core:$espresso_version", { exclude group: 'com.android.support', module: 'support-annotations' }) - compile 'com.android.support:appcompat-v7:25.1.0' - compile 'com.google.android.gms:play-services-nearby:10.0.1' - testCompile 'junit:junit:4.12' - compile 'com.android.support:recyclerview-v7:25.1.0' + implementation "com.android.support:appcompat-v7:$support_version" + implementation "com.google.android.gms:play-services-nearby:$play_services_version" + testImplementation "junit:junit:$junit_version" + implementation "com.android.support:recyclerview-v7:$support_version" } diff --git a/pianoplayer/src/main/AndroidManifest.xml b/pianoplayer/src/main/AndroidManifest.xml index db71e45..a22d84c 100644 --- a/pianoplayer/src/main/AndroidManifest.xml +++ b/pianoplayer/src/main/AndroidManifest.xml @@ -2,8 +2,12 @@ - - + + + + + + - () { @Override - public void onConnectionResponse(String endpointId, Status status, byte[] bytes) { - Log.d(TAG, "onConnectionResponse:" + endpointId + ":" + status); + public void onResult(@NonNull Status status) { if (!isViewAttached()) { return; } if (status.isSuccess()) { Log.d(TAG, "onConnectionResponse: " + endpointName + " SUCCESS"); view.showConnectedToMessage(endpointName); - - otherEndpointId = endpointId; } else { Log.d(TAG, "onConnectionResponse: " + endpointName + " FAILURE"); } } - }, this); - } - - private boolean isConnectedToNetwork() { - ConnectivityManager connManager = (ConnectivityManager) context.getSystemService(CONNECTIVITY_SERVICE); - NetworkInfo info = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI); - NetworkInfo info1 = connManager.getNetworkInfo(ConnectivityManager.TYPE_ETHERNET); - return (info != null && info.isConnectedOrConnecting()) || (info1 != null && info1.isConnectedOrConnecting()); + }); } - private void startDiscovery() { Log.d(TAG, "startDiscovery"); - if (!isConnectedToNetwork()) { - Log.d(TAG, "startDiscovery: not connected to WiFi network."); - return; - } - - Nearby.Connections.startDiscovery(googleApiClient, serviceId, TIMEOUT_DISCOVER, this) + Nearby.Connections + .startDiscovery(googleApiClient, serviceId, endpointDiscoveryCallback, new DiscoveryOptions(Strategy.P2P_CLUSTER)) .setResultCallback(new ResultCallback() { @Override public void onResult(@NonNull Status status) { @@ -139,19 +187,6 @@ public void onResult(@NonNull Status status) { }); } - @Override - public void onEndpointFound(final String endpointId, String deviceId, String serviceId, final String endpointName) { - Log.d(TAG, "onEndpointFound:" + endpointId + ":" + endpointName); - - connectTo(endpointId, endpointName); - - } - - @Override - public void onEndpointLost(String endpointId) { - Log.d(TAG, "onEndpointLost:" + endpointId); - } - /** * The function for calculating the frequency that should be played for a certain note. * More information about the formula can be found here: https://en.wikipedia.org/wiki/Piano_key_frequencies @@ -169,7 +204,8 @@ private void sendNote(final double frequency) { return; } - Nearby.Connections.sendReliableMessage(googleApiClient, otherEndpointId, toByteArray(frequency)); + Payload payload = Payload.fromBytes(toByteArray(frequency)); + Nearby.Connections.sendPayload(googleApiClient, otherEndpointId, payload); } private static byte[] toByteArray(double value) { @@ -183,17 +219,7 @@ private void sendStop() { view.showApiNotConnected(); return; } - Nearby.Connections.sendReliableMessage(googleApiClient, otherEndpointId, toByteArray(-1)); - - } - - @Override - public void onMessageReceived(final String s, final byte[] bytes, final boolean b) { - - } - - @Override - public void onDisconnected(final String s) { - + Payload payload = Payload.fromBytes(toByteArray(-1)); + Nearby.Connections.sendPayload(googleApiClient, otherEndpointId, payload); } }