Java tutorial
/* * Copyright 2016 Google Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.physical_web.physicalweb; import org.physical_web.collection.PwsClient; import org.physical_web.collection.PwsResult; import org.physical_web.collection.PwsResultCallback; import org.physical_web.physicalweb.ble.AdvertiseDataUtils; import android.annotation.TargetApi; import android.app.Service; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothManager; import android.bluetooth.le.AdvertiseCallback; import android.bluetooth.le.AdvertiseData; import android.bluetooth.le.AdvertiseSettings; import android.bluetooth.le.BluetoothLeAdvertiser; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.os.Handler; import android.os.IBinder; import android.preference.PreferenceManager; import android.support.v4.app.NotificationManagerCompat; import android.widget.Toast; import java.net.URI; import java.net.URISyntaxException; import java.util.Arrays; import java.util.Collection; /** * Shares URLs via bluetooth. * Also interfaces with PWS to shorten URLs that are too long for Eddystone URLs. * Lastly, it surfaces a persistent notification whenever a URL is currently being broadcast. **/ @TargetApi(21) public class PhysicalWebBroadcastService extends Service { private static final String TAG = PhysicalWebBroadcastService.class.getSimpleName(); private BluetoothLeAdvertiser mBluetoothLeAdvertiser; private static final int BROADCASTING_NOTIFICATION_ID = 6; public static final String DISPLAY_URL_KEY = "displayUrl"; public static final String PREVIOUS_BROADCAST_URL_KEY = "previousUrl"; public static final int MAX_URI_LENGTH = 18; private NotificationManagerCompat mNotificationManager; private Handler mHandler = new Handler(); private String mDisplayUrl; private byte[] mShareUrl; private boolean mStartedByRestart; private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { Log.d(TAG, "in receiver"); String action = intent.getAction(); if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) { int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1); switch (state) { case BluetoothAdapter.STATE_OFF: Log.d(TAG, "stop because BT off"); stopSelf(); break; default: } } } }; ///////////////////////////////// // callbacks ///////////////////////////////// @Override public void onCreate() { super.onCreate(); Log.d(TAG, "SERVICE onCreate"); BluetoothManager bluetoothManager = (BluetoothManager) getApplicationContext() .getSystemService(Context.BLUETOOTH_SERVICE); mBluetoothLeAdvertiser = bluetoothManager.getAdapter().getBluetoothLeAdvertiser(); mNotificationManager = NotificationManagerCompat.from(this); } @Override public int onStartCommand(Intent intent, int flags, int startId) { fetchBroadcastData(intent); if (mDisplayUrl == null) { stopSelf(); return START_STICKY; } Log.d(TAG, "SERVICE onStartCommand"); IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED); registerReceiver(mReceiver, filter); Log.d(TAG, mDisplayUrl); byte[] encodedUrl = AdvertiseDataUtils.encodeUri(mDisplayUrl); if (hasValidUrlLength(encodedUrl.length) && checkAndHandleAsciiUrl(mDisplayUrl)) { // Set the url if we can Log.d(TAG, "valid length"); mShareUrl = encodedUrl; broadcastUrl(); } else { Log.d(TAG, "needs shortening"); UrlShortenerClient.ShortenUrlCallback urlSetter = new UrlShortenerClient.ShortenUrlCallback() { @Override public void onUrlShortened(String newUrl) { Log.d(TAG, "shortening success"); mShareUrl = AdvertiseDataUtils.encodeUri(newUrl); broadcastUrl(); } @Override public void onError(String oldUrl) { Toast.makeText(getApplicationContext(), getString(R.string.shorten_error), Toast.LENGTH_LONG) .show(); stopSelf(); } }; UrlShortenerClient shortenerClient = UrlShortenerClient.getInstance(this); if (mDisplayUrl.contains("goo.gl")) { // find the site URL and reshorten it since // goo.gl will not reshorten other goo.gl links fetchAndShorten(shortenerClient, urlSetter); } else { shortenerClient.shortenUrl(mDisplayUrl, urlSetter, TAG); } } return START_STICKY; } private void fetchAndShorten(UrlShortenerClient shortenerClient, UrlShortenerClient.ShortenUrlCallback urlSetter) { PwsClient pwsClient = new PwsClient(); pwsClient.resolve(Arrays.asList(mDisplayUrl), new PwsResultCallback() { @Override public void onPwsResult(PwsResult pwsResult) { shortenerClient.shortenUrl(pwsResult.getSiteUrl(), urlSetter, TAG); } @Override public void onPwsResultAbsent(String url) { Toast.makeText(getApplicationContext(), getString(R.string.shorten_error), Toast.LENGTH_LONG) .show(); stopSelf(); } @Override public void onPwsResultError(Collection<String> urls, int httpResponseCode, Exception e) { Toast.makeText(getApplicationContext(), getString(R.string.shorten_error), Toast.LENGTH_LONG) .show(); stopSelf(); } @Override public void onResponseReceived(long durationMillis) { } }); } private void fetchBroadcastData(Intent intent) { mStartedByRestart = intent == null; if (intent == null) { SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); mDisplayUrl = sharedPrefs.getString(PREVIOUS_BROADCAST_URL_KEY, null); return; } mDisplayUrl = intent.getStringExtra(DISPLAY_URL_KEY); PreferenceManager.getDefaultSharedPreferences(this).edit() .putString(PREVIOUS_BROADCAST_URL_KEY, mDisplayUrl).commit(); } private static boolean hasValidUrlLength(int uriLength) { return 0 < uriLength && uriLength <= MAX_URI_LENGTH; } private boolean checkAndHandleAsciiUrl(String url) { boolean isCompliant = false; try { URI uri = new URI(url); String urlString = uri.toASCIIString(); isCompliant = url.equals(urlString); } catch (URISyntaxException e) { Toast.makeText(this, getString(R.string.no_url_error), Toast.LENGTH_LONG).show(); } return isCompliant; } @Override public void onDestroy() { Log.d(TAG, "SERVICE onDestroy"); unregisterReceiver(stopServiceReceiver); unregisterReceiver(mReceiver); disableUrlBroadcasting(); super.onDestroy(); } @Override public IBinder onBind(Intent intent) { return null; } // The callbacks for the ble advertisement events private final AdvertiseCallback mAdvertiseCallback = new AdvertiseCallback() { // Fires when the URL is successfully being advertised @Override public void onStartSuccess(AdvertiseSettings advertiseSettings) { Log.d(TAG, "URL is broadcasting"); Utils.createBroadcastNotification(PhysicalWebBroadcastService.this, stopServiceReceiver, BROADCASTING_NOTIFICATION_ID, getString(R.string.broadcast_notif), mDisplayUrl, "myFilter"); if (!mStartedByRestart) { Toast.makeText(getApplicationContext(), getString(R.string.url_broadcast), Toast.LENGTH_LONG) .show(); } } // Fires when the URL could not be advertised @Override public void onStartFailure(int result) { Log.d(TAG, "onStartFailure" + result); } }; ///////////////////////////////// // utilities ///////////////////////////////// // Broadcast via bluetooth the stored URL private void broadcastUrl() { final AdvertiseData advertisementData = AdvertiseDataUtils.getAdvertisementData(mShareUrl); final AdvertiseSettings advertiseSettings = AdvertiseDataUtils.getAdvertiseSettings(false); mBluetoothLeAdvertiser.stopAdvertising(mAdvertiseCallback); mBluetoothLeAdvertiser.startAdvertising(advertiseSettings, advertisementData, mAdvertiseCallback); } // Turn off URL broadcasting public void disableUrlBroadcasting() { Log.d(TAG, "disableUrlBroadcasting"); mBluetoothLeAdvertiser.stopAdvertising(mAdvertiseCallback); mNotificationManager.cancel(BROADCASTING_NOTIFICATION_ID); } protected BroadcastReceiver stopServiceReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { Log.d(TAG, context.toString()); stopSelf(); } }; }