dragon: Add keyboard battery warning in DragonFirmwareKeyboardUpdater

Add the Pixel C keyboard battery warning notification in
DragonFirmwareKeyboardUpdater. The notification will show up when the keyboard
is connected and its battery level is low.

BUG=b/25369339
TEST=build, run the service and check the display

Change-Id: I117cd59987fe638c5ad77ae35d9731b5053764dd
diff --git a/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/Android.mk b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/Android.mk
index 04771c9..d2c6242 100644
--- a/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/Android.mk
+++ b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/Android.mk
@@ -22,7 +22,7 @@
     $(LOCAL_PATH)/../dfu/res
 LOCAL_CERTIFICATE := platform
 LOCAL_PRIVILEGED_MODULE := true
-LOCAL_STATIC_JAVA_LIBRARIES := dfu-library
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 dfu-library
 LOCAL_PACKAGE_NAME := DragonKeyboardFirmwareUpdater
 LOCAL_AAPT_FLAGS := --auto-add-overlay
 LOCAL_AAPT_FLAGS += --extra-packages no.nordicsemi.android.dfu
diff --git a/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/drawable/ic_battery_warning.xml b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/drawable/ic_battery_warning.xml
new file mode 100644
index 0000000..e01b46b
--- /dev/null
+++ b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/drawable/ic_battery_warning.xml
@@ -0,0 +1,28 @@
+<!--
+   Copyright 2015, The Android Open Source Project
+
+   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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+
+    <path
+        android:pathData="M0 0h24v24H0z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33v15.33C7 21.4 7.6 22 8.33 22h7.33c.74
+0 1.34-.6 1.34-1.33V5.33C17 4.6 16.4 4 15.67 4zM13 18h-2v-2h2v2zm0-4h-2V9h2v5z" />
+</vector>
diff --git a/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/values/strings.xml b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/values/strings.xml
index 330931d..58a4533 100644
--- a/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/values/strings.xml
+++ b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/res/values/strings.xml
@@ -19,9 +19,7 @@
          Settings > Apps and when the user activates the Recents view while in the
          installation confirmation screen.
     -->
-    <string name="app_name">
-        <xliff:g id="product_name">Pixel C</xliff:g> Keyboard Firmware Updater
-    </string>
+    <string name="app_name"><xliff:g id="product_name">Pixel C</xliff:g> Keyboard Firmware Updater</string>
 
     <!-- Static information of the target keyboard. (for service use) -->
     <string name="target_keyboard_name" translatable="false">Pixel C Keyboard</string>
@@ -33,31 +31,42 @@
     <string name="target_firmware_init_file_name" translatable="false">init_151104_mp_signed</string>
     <string name="target_firmware_image_file_name" translatable="false">image_151104_mp_signed</string>
 
-    <!-- The notification message title shown when a keyboard update is
+    <!-- The update notification message title shown when a keyboard update is
          available. Users are given the option to install or postpone the update.
     -->
-    <string name="notification_title">Keyboard update available</string>
+    <string name="notification_update_title">Keyboard update available</string>
 
-    <!-- The notification message subtitle shown when a keyboard update is
-         available. It elaborates more on the title of the notification
+    <!-- The update notification message subtitle shown when a keyboard update
+         is available. It elaborates more on the title of the notification
          ("Keyboard update available") by specifying the product name. Users are
          given the option to install or postpone the update.
     -->
-    <string name="notification_text">
-        <xliff:g id="product_name">Pixel C</xliff:g> Keyboard update is ready
-    </string>
+    <string name="notification_update_text"><xliff:g id="product_name">Pixel C</xliff:g> Keyboard update is ready</string>
 
     <!-- One of the two options provided by the "keyboard update available"
          notification. Tapping on this option will dismiss the notification, and
          the notification is expected to resurface again some time later.
     -->
-    <string name="notification_later">LATER</string>
+    <string name="notification_update_later">LATER</string>
 
     <!-- One of the two options provided by the "keyboard update available"
-         notification. Tapping on this option will send users to the installation
-         screen.
+         notification. Tapping on this option will send users to the
+         installation screen.
     -->
-    <string name="notification_install">INSTALL</string>
+    <string name="notification_update_install">INSTALL</string>
+
+    <!-- The battery warning notification message title shown when the battery
+         level of a keyboard is low.
+    -->
+    <string name="notification_battery_warning_title"><xliff:g id="product_name">Pixel C</xliff:g> Keyboard battery low</string>
+
+    <!-- The battery warning notification message subtitle shown when the
+         battery level of a keyboard is low. It gives the users an instruction
+         on how they charge the keyboard. The most important points to convey to
+         the users are that the Pixel C has to be charged face down on the
+         keyboard, otherwise the keyboard won't charge.
+    -->
+    <string name="notification_battery_warning_text">Charge your <xliff:g id="product_name">Pixel C</xliff:g> while it is face down on the keyboard</string>
 
     <!-- The text shown in the installation confirmation screen. The most
          important points to convey to the user are that the keyboard will not work
@@ -70,8 +79,5 @@
          this will begin the installation process.
     -->
     <string name="confirmation_install">INSTALL</string>
-    <!-- Update preference names (for service use) -->
-    <string name="preference_notification_flag" translatable="false">com.android.dragonkeyboardfirmwareupdater.preference_notification_flag</string>
-    <string name="preference_current_version" translatable="false">com.android.dragonkeyboardfirmwareupdater.preference_current_version</string>
 
 </resources>
diff --git a/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/src/com/android/dragonkeyboardfirmwareupdater/KeyboardFirmwareUpdateService.java b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/src/com/android/dragonkeyboardfirmwareupdater/KeyboardFirmwareUpdateService.java
index 248681b..325abc4 100644
--- a/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/src/com/android/dragonkeyboardfirmwareupdater/KeyboardFirmwareUpdateService.java
+++ b/DragonKeyboardFirmwareUpdater/keyboard_firmware_updater/src/com/android/dragonkeyboardfirmwareupdater/KeyboardFirmwareUpdateService.java
@@ -36,11 +36,13 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.graphics.Color;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.PowerManager;
+import android.support.v4.app.NotificationCompat;
 import android.support.v4.content.LocalBroadcastManager;
 import android.util.Log;
 
@@ -123,6 +125,7 @@
 
     /* Update notification. */
     private static final int UPDATE_NOTIFICATION_ID = 1248;
+    private static final int BATTERY_WARNING_NOTIFICATION_ID = 8421;
     private Notification mUpdateNotification;
 
     /**
@@ -250,13 +253,13 @@
             UUID uuid = characteristic.getUuid();
             if (GattAttributeUUID.UUID_BATTERY_LEVEL_CHARACTERISTIC.equals(uuid)) {
 
-                int batterLevel = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0);
-                if (batterLevel < Integer.parseInt(getString(R.string.target_battery_level))) {
+                int batteryLevel = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0);
+                if (batteryLevel < Integer.parseInt(getString(R.string.target_battery_level))) {
                     Log.w(TAG, "onCharacteristicRead BATTERY_LEVEL_CHARACTERISTIC: " +getKeyboardString()
-                            + " battery level(" + batterLevel + "%) is too low:");
+                            + " battery level(" + batteryLevel + "%) is too low:");
                     changeDfuStatus(DFU_STATE_INFO_NOT_SUITABLE);
 
-                    // TODO(mcchou): show charging notification if the battery level is too low.
+                    showBatteryWarningNotification();
 
                     return;
                 }
@@ -504,7 +507,7 @@
                     return;
                 }
 
-                showNotification();
+                showUpdateNotification();
             } else if (deviceConnectionState == BluetoothAdapter.STATE_DISCONNECTING) {
                 handleGattDisconnection();
             }
@@ -516,6 +519,7 @@
             } else if (adapterState == BluetoothAdapter.STATE_TURNING_OFF) {
                 // Terminate update process and disable Bluetooth connectivity.
                 disableBluetoothConnectivity();
+
                 // Since BluetoothAdapter has been disabled, the callback of disconnection would not
                 // be called. Therefore a separate clean-up of GATT connection is need.
                 cleanUpGattConnection();
@@ -537,7 +541,7 @@
             }
 
         } else if (ACTION_KEYBOARD_UPDATE_CONFIRMED.equals(action)) {
-            dismissNotification();
+            dismissUpdateNotification();
 
             if (mDfuStatus != DFU_STATE_INFO_READY || mDfuStatus == DFU_STATE_UPDATING) {
                 Log.w(TAG, "onHandleIntent: DFP preparation not ready or DFU is in progress. ");
@@ -558,7 +562,7 @@
             changeDfuStatus(DFU_STATE_SWITCHING_TO_DFU_MODE);
 
         } else if (ACTION_KEYBOARD_UPDATE_POSTPONED.equals(action)) {
-            dismissNotification();
+            dismissUpdateNotification();
             // TODO(mcchou): Update the preference when the Settings keyboard entry is available.
         }
     }
@@ -633,9 +637,9 @@
         mBroadcastReceiver = null;
     }
 
-    /* Show the update notification. */
-    private void showNotification() {
-        Log.d(TAG, "showNotification: " + getKeyboardString());
+    /* Shows the update notification. */
+    private void showUpdateNotification() {
+        Log.d(TAG, "showUpdateNotification: " + getKeyboardString());
 
         // Intent for triggering the update confirmation page.
         Intent updateConfirmation = new Intent(this, UpdateConfirmationActivity.class);
@@ -654,15 +658,16 @@
                 this, 0, updateConfirmation, PendingIntent.FLAG_CANCEL_CURRENT);
 
         // Create a notification object with two buttons (actions)
-        mUpdateNotification = new Notification.Builder(this)
-                .setContentTitle(getString(R.string.notification_title))
-                .setContentText(getString(R.string.notification_text))
+        mUpdateNotification = new NotificationCompat.Builder(this)
+                .setCategory(Notification.CATEGORY_SYSTEM)
+                .setContentTitle(getString(R.string.notification_update_title))
+                .setContentText(getString(R.string.notification_update_text))
                 .setSmallIcon(R.drawable.ic_keyboard)
-                .addAction(new Notification.Action.Builder(
-                        R.drawable.ic_later, getString(R.string.notification_later),
+                .addAction(new NotificationCompat.Action.Builder(
+                        R.drawable.ic_later, getString(R.string.notification_update_later),
                         laterIntent).build())
-                .addAction(new Notification.Action.Builder(
-                        R.drawable.ic_install, getString(R.string.notification_install),
+                .addAction(new NotificationCompat.Action.Builder(
+                        R.drawable.ic_install, getString(R.string.notification_update_install),
                         installIntent).build())
                 .setAutoCancel(true)
                 .setOnlyAlertOnce(true)
@@ -675,15 +680,37 @@
     }
 
     /* Dismisses the udpate notification. */
-    private void dismissNotification() {
+    private void dismissUpdateNotification() {
         if (mUpdateNotification == null) return;
-        Log.d(TAG, "dismissNotification");
+        Log.d(TAG, "dismissUpdateNotification");
         NotificationManager notificationManager =
                 (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
         notificationManager.cancel(UPDATE_NOTIFICATION_ID);
         mUpdateNotification = null;
     }
 
+    /* Shows the keyboard battery warning notification. */
+    private void showBatteryWarningNotification() {
+        Log.d(TAG, "showBatteryWarningNotification: " + getKeyboardString());
+
+        Notification batteryWarningNotification = new NotificationCompat.Builder(this)
+                .setContentTitle(getString(R.string.notification_battery_warning_title))
+                .setContentText(getString(R.string.notification_battery_warning_text))
+                .setSmallIcon(R.drawable.ic_battery_warning)
+                .setAutoCancel(true)
+                .setOnlyAlertOnce(true)
+                .setPriority(Notification.PRIORITY_HIGH)
+                .setColor(Color.RED)
+                .setStyle(new NotificationCompat.BigTextStyle().bigText(
+                        getString(R.string.notification_battery_warning_text)))
+                .build();
+
+        // Show the notification via notification manager
+        NotificationManager notificationManager =
+                (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+        notificationManager.notify(BATTERY_WARNING_NOTIFICATION_ID, batteryWarningNotification);
+    }
+
     /* Connects to the GATT server hosted on the given Bluetooth LE device. */
     private boolean connectToKeyboard() {
         if (!isBluetoothEnabled() || !isUpdateServiceInUse()) {
@@ -1098,7 +1125,7 @@
                 break;
             case DFU_STATE_UPDATING:
                 abortDfu();
-                dismissNotification();
+                dismissUpdateNotification();
                 break;
             default:
                 break;