Skip to content

Commit 654706c

Browse files
author
Dave Smith
committed
Local UI and byte fixes.
Include local UI to display time. Fix byte casts and time offset values. Change-Id: I1f1871a0adbe14666b010fa8e5ce828c6d6be2e2 Signed-off-by: Dave Smith <[email protected]>
1 parent eed3a4d commit 654706c

File tree

3 files changed

+100
-40
lines changed

3 files changed

+100
-40
lines changed

app/src/main/java/com/example/androidthings/gattserver/GattServerActivity.java

Lines changed: 46 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -37,16 +37,23 @@
3737
import android.content.pm.PackageManager;
3838
import android.os.Bundle;
3939
import android.os.ParcelUuid;
40+
import android.text.format.DateFormat;
4041
import android.util.Log;
42+
import android.view.WindowManager;
43+
import android.widget.TextView;
4144

4245
import java.util.Arrays;
46+
import java.util.Date;
4347
import java.util.HashSet;
4448
import java.util.Set;
4549

4650
public class GattServerActivity extends Activity {
4751
private static final String TAG = GattServerActivity.class.getSimpleName();
4852

53+
/* Local UI */
54+
private TextView mLocalTimeView;
4955
/* Bluetooth API */
56+
private BluetoothManager mBluetoothManager;
5057
private BluetoothGattServer mBluetoothGattServer;
5158
private BluetoothLeAdvertiser mBluetoothLeAdvertiser;
5259
/* Collection of notification subscribers */
@@ -55,10 +62,15 @@ public class GattServerActivity extends Activity {
5562
@Override
5663
protected void onCreate(Bundle savedInstanceState) {
5764
super.onCreate(savedInstanceState);
65+
setContentView(R.layout.activity_server);
5866

59-
BluetoothManager manager =
60-
(BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
61-
BluetoothAdapter bluetoothAdapter = manager.getAdapter();
67+
mLocalTimeView = (TextView) findViewById(R.id.text_time);
68+
69+
// Devices with a display should not go to sleep
70+
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
71+
72+
mBluetoothManager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
73+
BluetoothAdapter bluetoothAdapter = mBluetoothManager.getAdapter();
6274
// We can't continue without proper Bluetooth support
6375
if (!checkBluetoothSupport(bluetoothAdapter)) {
6476
finish();
@@ -72,8 +84,8 @@ protected void onCreate(Bundle savedInstanceState) {
7284
bluetoothAdapter.enable();
7385
} else {
7486
Log.d(TAG, "Bluetooth enabled...starting services");
75-
startAdvertising(manager);
76-
startServer(manager);
87+
startAdvertising();
88+
startServer();
7789
}
7890
}
7991

@@ -98,9 +110,7 @@ protected void onStop() {
98110
protected void onDestroy() {
99111
super.onDestroy();
100112

101-
BluetoothManager manager =
102-
(BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
103-
BluetoothAdapter bluetoothAdapter = manager.getAdapter();
113+
BluetoothAdapter bluetoothAdapter = mBluetoothManager.getAdapter();
104114
if (bluetoothAdapter.isEnabled()) {
105115
stopServer();
106116
stopAdvertising();
@@ -136,7 +146,7 @@ private boolean checkBluetoothSupport(BluetoothAdapter bluetoothAdapter) {
136146
private BroadcastReceiver mTimeReceiver = new BroadcastReceiver() {
137147
@Override
138148
public void onReceive(Context context, Intent intent) {
139-
int adjustReason;
149+
byte adjustReason;
140150
switch (intent.getAction()) {
141151
case Intent.ACTION_TIME_CHANGED:
142152
adjustReason = TimeProfile.ADJUST_MANUAL;
@@ -149,7 +159,9 @@ public void onReceive(Context context, Intent intent) {
149159
adjustReason = TimeProfile.ADJUST_NONE;
150160
break;
151161
}
152-
notifyRegisteredDevices(adjustReason);
162+
long now = System.currentTimeMillis();
163+
notifyRegisteredDevices(now, adjustReason);
164+
updateLocalUi(now);
153165
}
154166
};
155167

@@ -162,12 +174,10 @@ public void onReceive(Context context, Intent intent) {
162174
public void onReceive(Context context, Intent intent) {
163175
int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF);
164176

165-
BluetoothManager manager =
166-
(BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
167177
switch (state) {
168178
case BluetoothAdapter.STATE_ON:
169-
startAdvertising(manager);
170-
startServer(manager);
179+
startAdvertising();
180+
startServer();
171181
break;
172182
case BluetoothAdapter.STATE_OFF:
173183
stopServer();
@@ -184,8 +194,8 @@ public void onReceive(Context context, Intent intent) {
184194
* Begin advertising over Bluetooth that this device is connectable
185195
* and supports the Current Time Service.
186196
*/
187-
private void startAdvertising(BluetoothManager bluetoothManager) {
188-
BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter();
197+
private void startAdvertising() {
198+
BluetoothAdapter bluetoothAdapter = mBluetoothManager.getAdapter();
189199
mBluetoothLeAdvertiser = bluetoothAdapter.getBluetoothLeAdvertiser();
190200
if (mBluetoothLeAdvertiser == null) {
191201
Log.w(TAG, "Failed to create advertiser");
@@ -222,14 +232,17 @@ private void stopAdvertising() {
222232
* Initialize the GATT server instance with the services/characteristics
223233
* from the Time Profile.
224234
*/
225-
private void startServer(BluetoothManager bluetoothManager) {
226-
mBluetoothGattServer = bluetoothManager.openGattServer(this, mGattServerCallback);
235+
private void startServer() {
236+
mBluetoothGattServer = mBluetoothManager.openGattServer(this, mGattServerCallback);
227237
if (mBluetoothGattServer == null) {
228238
Log.w(TAG, "Unable to create GATT server");
229239
return;
230240
}
231241

232242
mBluetoothGattServer.addService(TimeProfile.createTimeService());
243+
244+
// Initialize the local UI
245+
updateLocalUi(System.currentTimeMillis());
233246
}
234247

235248
/**
@@ -260,12 +273,12 @@ public void onStartFailure(int errorCode) {
260273
* Send a time service notification to any devices that are subscribed
261274
* to the characteristic.
262275
*/
263-
public void notifyRegisteredDevices(int adjustReason) {
276+
private void notifyRegisteredDevices(long timestamp, byte adjustReason) {
264277
if (mRegisteredDevices.isEmpty()) {
265278
Log.i(TAG, "No subscribers registered");
266279
return;
267280
}
268-
byte[] exactTime = TimeProfile.getExactTime(System.currentTimeMillis(), adjustReason);
281+
byte[] exactTime = TimeProfile.getExactTime(timestamp, adjustReason);
269282

270283
Log.i(TAG, "Sending update to " + mRegisteredDevices.size() + " subscribers");
271284
for (BluetoothDevice device : mRegisteredDevices) {
@@ -277,6 +290,17 @@ public void notifyRegisteredDevices(int adjustReason) {
277290
}
278291
}
279292

293+
/**
294+
* Update graphical UI on devices that support it with the current time.
295+
*/
296+
private void updateLocalUi(long timestamp) {
297+
Date date = new Date(timestamp);
298+
String displayDate = DateFormat.getMediumDateFormat(this).format(date)
299+
+ "\n"
300+
+ DateFormat.getTimeFormat(this).format(date);
301+
mLocalTimeView.setText(displayDate);
302+
}
303+
280304
/**
281305
* Callback to handle incoming requests to the GATT server.
282306
* All read/write requests for characteristics and descriptors are handled here.
@@ -289,6 +313,8 @@ public void onConnectionStateChange(BluetoothDevice device, int status, int newS
289313
Log.i(TAG, "BluetoothDevice CONNECTED: " + device);
290314
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
291315
Log.i(TAG, "BluetoothDevice DISCONNECTED: " + device);
316+
//Remove device from any active subscriptions
317+
mRegisteredDevices.remove(device);
292318
}
293319
}
294320

app/src/main/java/com/example/androidthings/gattserver/TimeProfile.java

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,10 @@ public static BluetoothGattService createTimeService() {
7777
}
7878

7979
/**
80-
* Return the current time as an Exact Time 256 Field
81-
* for the current time characteristic.
80+
* Construct the field values for a Current Time characteristic
81+
* from the given epoch timestamp and adjustment reason.
8282
*/
83-
public static byte[] getExactTime(long timestamp, int adjustReason) {
83+
public static byte[] getExactTime(long timestamp, byte adjustReason) {
8484
Calendar time = Calendar.getInstance();
8585
time.setTimeInMillis(timestamp);
8686

@@ -91,32 +91,32 @@ public static byte[] getExactTime(long timestamp, int adjustReason) {
9191
field[0] = (byte) (year & 0xFF);
9292
field[1] = (byte) ((year >> 8) & 0xFF);
9393
// Month
94-
field[2] = (byte) ((time.get(Calendar.MONTH) + 1) & 0xFF);
94+
field[2] = (byte) (time.get(Calendar.MONTH) + 1);
9595
// Day
96-
field[3] = (byte) (time.get(Calendar.DATE) & 0xFF);
96+
field[3] = (byte) time.get(Calendar.DATE);
9797
// Hours
98-
field[4] = (byte) (time.get(Calendar.HOUR_OF_DAY) & 0xFF);
98+
field[4] = (byte) time.get(Calendar.HOUR_OF_DAY);
9999
// Minutes
100-
field[5] = (byte) (time.get(Calendar.MINUTE) & 0xFF);
100+
field[5] = (byte) time.get(Calendar.MINUTE);
101101
// Seconds
102-
field[6] = (byte) (time.get(Calendar.SECOND) & 0xFF);
102+
field[6] = (byte) time.get(Calendar.SECOND);
103103
// Day of Week (1-7)
104104
field[7] = getDayOfWeekCode(time.get(Calendar.DAY_OF_WEEK));
105105
// Fractions256
106-
field[8] = (byte) ((time.get(Calendar.MILLISECOND) / 256) & 0xFF);
106+
field[8] = (byte) (time.get(Calendar.MILLISECOND) / 256);
107107

108-
field[9] = (byte) (adjustReason & 0xFF);
108+
field[9] = adjustReason;
109109

110110
return field;
111111
}
112112

113113
/* Time bucket constants for local time information */
114114
private static final int FIFTEEN_MINUTE_MILLIS = 900000;
115-
private static final int ONE_HOUR_MILLIS = 3600000;
115+
private static final int HALF_HOUR_MILLIS = 1800000;
116116

117117
/**
118-
* Return the time zone and DST offset fields that make up
119-
* the local time information characteristic.
118+
* Construct the field values for a Local Time Information characteristic
119+
* from the given epoch timestamp.
120120
*/
121121
public static byte[] getLocalTimeInfo(long timestamp) {
122122
Calendar time = Calendar.getInstance();
@@ -125,12 +125,12 @@ public static byte[] getLocalTimeInfo(long timestamp) {
125125
byte[] field = new byte[2];
126126

127127
// Time zone
128-
int rawOffset = time.get(Calendar.ZONE_OFFSET) / FIFTEEN_MINUTE_MILLIS; // 15 minute increments
129-
field[0] = (byte) rawOffset;
128+
int zoneOffset = time.get(Calendar.ZONE_OFFSET) / FIFTEEN_MINUTE_MILLIS; // 15 minute intervals
129+
field[0] = (byte) zoneOffset;
130130

131131
// DST Offset
132-
rawOffset = time.get(Calendar.DST_OFFSET) / ONE_HOUR_MILLIS;
133-
field[1] = getDstOffsetCode(rawOffset);
132+
int dstOffset = time.get(Calendar.DST_OFFSET) / HALF_HOUR_MILLIS; // 30 minute intervals
133+
field[1] = getDstOffsetCode(dstOffset);
134134

135135
return field;
136136
}
@@ -178,16 +178,18 @@ private static byte getDayOfWeekCode(int dayOfWeek) {
178178
private static final byte DST_UNKNOWN = (byte) 0xFF;
179179

180180
/**
181-
* Convert a raw DST offset (in hours) to the corresponding
182-
* Bluetooth DST offset code.
181+
* Convert a raw DST offset (in 30 minute intervals) to the
182+
* corresponding Bluetooth DST offset code.
183183
*/
184184
private static byte getDstOffsetCode(int rawOffset) {
185185
switch (rawOffset) {
186186
case 0:
187187
return DST_STANDARD;
188188
case 1:
189-
return DST_SINGLE;
189+
return DST_HALF;
190190
case 2:
191+
return DST_SINGLE;
192+
case 4:
191193
return DST_DOUBLE;
192194
default:
193195
return DST_UNKNOWN;
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<!--
3+
Copyright (C) 2017 The Android Open Source Project
4+
5+
Licensed under the Apache License, Version 2.0 (the "License");
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
-->
17+
<FrameLayout
18+
xmlns:android="http://schemas.android.com/apk/res/android"
19+
xmlns:tools="http://schemas.android.com/tools"
20+
android:layout_width="match_parent"
21+
android:layout_height="match_parent">
22+
23+
<TextView
24+
android:id="@+id/text_time"
25+
android:layout_width="wrap_content"
26+
android:layout_height="wrap_content"
27+
android:layout_gravity="center"
28+
android:gravity="center"
29+
android:textSize="56dp"
30+
tools:text="01/01/1970\n12:00"/>
31+
32+
</FrameLayout>

0 commit comments

Comments
 (0)