1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-12-26 18:45:49 +01:00

Add initial support for Teclast H30

Scan and connection, battery level, firmware version, date and time sync
(along with some other currently hardcoded settings), notification
support, alarm support, and some more.
This commit is contained in:
Sami Alaoui 2017-09-04 07:43:34 +01:00 committed by Andreas Shimokawa
parent b8c5a44709
commit f6ce0c1a0e
15 changed files with 678 additions and 0 deletions

View File

@ -0,0 +1,41 @@
package nodomain.freeyourgadget.gadgetbridge.devices.jyou;
import java.util.UUID;
public final class JYouConstants {
public static final UUID UUID_CHARACTERISTIC_CONTROL = UUID.fromString("000033f3-0000-1000-8000-00805f9b34fb");
public static final UUID UUID_CHARACTERISTIC_MEASURE = UUID.fromString("000033f4-0000-1000-8000-00805f9b34fb");
public static final UUID UUID_SERVICE_JYOU = UUID.fromString("000056ff-0000-1000-8000-00805f9b34fb");
public static final byte CMD_SET_DATE_AND_TIME = 0x08;
public static final byte CMD_SET_HEARTRATE_AUTO = 0x38;
public static final byte CMD_SET_HEARTRATE_WARNING_VALUE = 0x01;
public static final byte CMD_SET_TARGET_STEPS = 0x03;
public static final byte CMD_SET_ALARM_1 = 0x09;
public static final byte CMD_SET_ALARM_2 = 0x22;
public static final byte CMD_SET_ALARM_3 = 0x23;
public static final byte CMD_GET_STEP_COUNT = 0x1D;
public static final byte CMD_GET_SLEEP_TIME = 0x32;
public static final byte CMD_SET_NOON_TIME = 0x26;
public static final byte CMD_SET_SLEEP_TIME = 0x27;
public static final byte CMD_SET_DND_SETTINGS = 0x39;
public static final byte CMD_SET_INACTIVITY_WARNING_TIME = 0x24;
public static final byte CMD_ACTION_HEARTRATE_SWITCH = 0x0D;
public static final byte CMD_ACTION_SHOW_NOTIFICATION = 0x2C;
public static final byte CMD_ACTION_REBOOT_DEVICE = 0x0E;
public static final byte RECEIVE_BATTERY_LEVEL = (byte)0xF7;
public static final byte RECEIVE_DEVICE_INFO = (byte)0xF6;
public static final byte RECEIVE_STEPS_DATA = (byte)0xF9;
public static final byte RECEIVE_HEARTRATE = (byte)0xFC;
public static final byte ICON_CALL = 0;
public static final byte ICON_SMS = 1;
public static final byte ICON_WECHAT = 2;
public static final byte ICON_QQ = 3;
public static final byte ICON_FACEBOOK = 4;
public static final byte ICON_SKYPE = 5;
public static final byte ICON_TWITTER = 6;
public static final byte ICON_WHATSAPP = 7;
public static final byte ICON_LINE = 8;
}

View File

@ -0,0 +1,144 @@
package nodomain.freeyourgadget.gadgetbridge.devices.jyou;
import android.annotation.TargetApi;
import android.app.Activity;
import android.bluetooth.le.ScanFilter;
import android.content.Context;
import android.net.Uri;
import android.os.Build;
import android.os.ParcelUuid;
import android.support.annotation.NonNull;
import de.greenrobot.dao.query.QueryBuilder;
import nodomain.freeyourgadget.gadgetbridge.GBException;
import nodomain.freeyourgadget.gadgetbridge.activities.charts.ChartsActivity;
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collection;
import java.util.Collections;
public class TeclastH30Coordinator extends AbstractDeviceCoordinator {
protected static final Logger LOG = LoggerFactory.getLogger(TeclastH30Coordinator.class);
@NonNull
@Override
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public Collection<? extends ScanFilter> createBLEScanFilters() {
ParcelUuid uuid = new ParcelUuid(JYouConstants.UUID_SERVICE_JYOU);
ScanFilter filter = new ScanFilter.Builder().setServiceUuid(uuid).build();
return Collections.singletonList(filter);
}
@NonNull
@Override
public DeviceType getSupportedType(GBDeviceCandidate candidate) {
String name = candidate.getDevice().getName();
if (name != null && name.startsWith("TECLAST_H30")) {
return DeviceType.TECLASTH30;
}
return DeviceType.UNKNOWN;
}
@Override
public int getBondingStyle(GBDevice deviceCandidate){
return BONDING_STYLE_NONE;
}
@Override
public boolean supportsCalendarEvents() {
return false;
}
@Override
public boolean supportsRealtimeData() {
return true;
}
@Override
public DeviceType getDeviceType() {
return DeviceType.TECLASTH30;
}
@Override
public Class<? extends Activity> getPairingActivity() {
return null;
}
@Override
public Class<? extends Activity> getPrimaryActivity() {
return ChartsActivity.class;
}
@Override
public InstallHandler findInstallHandler(Uri uri, Context context) {
return null;
}
@Override
public boolean supportsActivityDataFetching() {
return false;
}
@Override
public boolean supportsActivityTracking() {
return false;
}
@Override
public SampleProvider<? extends ActivitySample> getSampleProvider(GBDevice device, DaoSession session) {
return null;
}
@Override
public boolean supportsScreenshots() {
return false;
}
@Override
public boolean supportsAlarmConfiguration() {
return true;
}
@Override
public boolean supportsSmartWakeup(GBDevice device) {
return false;
}
@Override
public boolean supportsHeartRateMeasurement(GBDevice device) {
return true;
}
@Override
public String getManufacturer() {
return "Teclast";
}
@Override
public boolean supportsAppsManagement() {
return false;
}
@Override
public Class<? extends Activity> getAppsManagementActivity() {
return null;
}
@Override
protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException {
}
}

View File

@ -38,6 +38,7 @@ public enum DeviceType {
HPLUS(40, R.drawable.ic_device_hplus, R.drawable.ic_device_hplus_disabled),
MAKIBESF68(41, R.drawable.ic_device_hplus, R.drawable.ic_device_hplus_disabled),
NO1F1(50, R.drawable.ic_device_hplus, R.drawable.ic_device_hplus_disabled),
TECLASTH30(60, R.drawable.ic_device_h30_h10, R.drawable.ic_device_h30_h10_disabled),
TEST(1000, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled);
private final int key;

View File

@ -36,6 +36,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.no1f1.No1F1Support;
import nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.PebbleSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.vibratissimo.VibratissimoSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.hplus.HPlusSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.jyou.TeclastH30Support;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
public class DeviceSupportFactory {
@ -129,6 +130,9 @@ public class DeviceSupportFactory {
case NO1F1:
deviceSupport = new ServiceDeviceSupport(new No1F1Support(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case TECLASTH30:
deviceSupport = new ServiceDeviceSupport(new TeclastH30Support(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
}
if (deviceSupport != null) {
deviceSupport.setContext(gbDevice, mBtAdapter, mContext);

View File

@ -0,0 +1,484 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.jyou;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.net.Uri;
import android.widget.Toast;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.UUID;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInfo;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo;
import nodomain.freeyourgadget.gadgetbridge.devices.jyou.JYouConstants;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec;
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
import nodomain.freeyourgadget.gadgetbridge.model.CannedMessagesSpec;
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport;
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.StringUtils;
public class TeclastH30Support extends AbstractBTLEDeviceSupport {
private static final Logger LOG = LoggerFactory.getLogger(TeclastH30Support.class);
public BluetoothGattCharacteristic ctrlCharacteristic = null;
public BluetoothGattCharacteristic measureCharacteristic = null;
private final GBDeviceEventVersionInfo versionCmd = new GBDeviceEventVersionInfo();
private final GBDeviceEventBatteryInfo batteryCmd = new GBDeviceEventBatteryInfo();
public TeclastH30Support() {
super(LOG);
addSupportedService(JYouConstants.UUID_SERVICE_JYOU);
}
@Override
protected TransactionBuilder initializeDevice(TransactionBuilder builder) {
LOG.info("Initializing");
gbDevice.setState(GBDevice.State.INITIALIZING);
gbDevice.sendDeviceUpdateIntent(getContext());
measureCharacteristic = getCharacteristic(JYouConstants.UUID_CHARACTERISTIC_MEASURE);
ctrlCharacteristic = getCharacteristic(JYouConstants.UUID_CHARACTERISTIC_CONTROL);
builder.setGattCallback(this);
builder.notify(measureCharacteristic, true);
syncSettings(builder);
gbDevice.setState(GBDevice.State.INITIALIZED);
gbDevice.sendDeviceUpdateIntent(getContext());
LOG.info("Initialization Done");
return builder;
}
@Override
public boolean onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
if (super.onCharacteristicChanged(gatt, characteristic)) {
return true;
}
UUID characteristicUUID = characteristic.getUuid();
byte[] data = characteristic.getValue();
if (data.length == 0)
return true;
switch (data[0]) {
case JYouConstants.RECEIVE_DEVICE_INFO:
int fwVerNum = data[4] & 0xFF;
versionCmd.fwVersion = (fwVerNum / 100) + "." + ((fwVerNum % 100) / 10) + "." + ((fwVerNum % 100) % 10);
handleGBDeviceEvent(versionCmd);
LOG.info("Firmware version is: " + versionCmd.fwVersion);
return true;
case JYouConstants.RECEIVE_BATTERY_LEVEL:
batteryCmd.level = data[8];
handleGBDeviceEvent(batteryCmd);
LOG.info("Battery level is: " + batteryCmd.level);
return true;
case JYouConstants.RECEIVE_STEPS_DATA:
int steps = ByteBuffer.wrap(data, 5, 4).getInt();
LOG.info("Number of walked steps: " + steps);
return true;
case JYouConstants.RECEIVE_HEARTRATE:
LOG.info("Current heart rate: " + data[8]);
return true;
default:
LOG.info("Unhandled characteristic change: " + characteristicUUID + " code: " + String.format("0x%1x ...", data[0]));
return true;
}
}
private void syncDateAndTime(TransactionBuilder builder) {
Calendar cal = Calendar.getInstance();
String strYear = String.valueOf(cal.get(Calendar.YEAR));
byte year1 = (byte)Integer.parseInt(strYear.substring(0, 2));
byte year2 = (byte)Integer.parseInt(strYear.substring(2, 4));
byte month = (byte)cal.get(Calendar.MONTH);
byte day = (byte)cal.get(Calendar.DAY_OF_MONTH);
byte hour = (byte)cal.get(Calendar.HOUR_OF_DAY);
byte minute = (byte)cal.get(Calendar.MINUTE);
byte second = (byte)cal.get(Calendar.SECOND);
byte weekDay = (byte)cal.get(Calendar.DAY_OF_WEEK);
builder.write(ctrlCharacteristic, commandWithChecksum(
JYouConstants.CMD_SET_DATE_AND_TIME,
(year1 << 24) | (year2 << 16) | (month << 8) | day,
(hour << 24) | (minute << 16) | (second << 8) | weekDay
));
}
private void syncSettings(TransactionBuilder builder) {
syncDateAndTime(builder);
// TODO: unhardcode and separate stuff
builder.write(ctrlCharacteristic, commandWithChecksum(
JYouConstants.CMD_SET_HEARTRATE_WARNING_VALUE, 0, 152
));
builder.write(ctrlCharacteristic, commandWithChecksum(
JYouConstants.CMD_SET_TARGET_STEPS, 0, 10000
));
builder.write(ctrlCharacteristic, commandWithChecksum(
JYouConstants.CMD_GET_STEP_COUNT, 0, 0
));
builder.write(ctrlCharacteristic, commandWithChecksum(
JYouConstants.CMD_GET_SLEEP_TIME, 0, 0
));
builder.write(ctrlCharacteristic, commandWithChecksum(
JYouConstants.CMD_SET_NOON_TIME, 12 * 60 * 60, 14 * 60 * 60 // 12:00 - 14:00
));
builder.write(ctrlCharacteristic, commandWithChecksum(
JYouConstants.CMD_SET_SLEEP_TIME, 21 * 60 * 60, 8 * 60 * 60 // 21:00 - 08:00
));
builder.write(ctrlCharacteristic, commandWithChecksum(
JYouConstants.CMD_SET_INACTIVITY_WARNING_TIME, 0, 0
));
// do not disturb and a couple more features
byte dndStartHour = 22;
byte dndStartMin = 0;
byte dndEndHour = 8;
byte dndEndMin = 0;
boolean dndToggle = false;
boolean vibrationToggle = true;
boolean wakeOnRaiseToggle = true;
builder.write(ctrlCharacteristic, commandWithChecksum(
JYouConstants.CMD_SET_DND_SETTINGS,
(dndStartHour << 24) | (dndStartMin << 16) | (dndEndHour << 8) | dndEndMin,
((dndToggle ? 0 : 1) << 2) | ((vibrationToggle ? 1 : 0) << 1) | (wakeOnRaiseToggle ? 1 : 0)
));
}
private void showNotification(byte icon, String title, String message) {
try {
TransactionBuilder builder = performInitialized("ShowNotification");
byte[] titleBytes = stringToUTF8Bytes(title, 16);
byte[] messageBytes = stringToUTF8Bytes(message, 80);
for (int i = 1; i <= 7; i++)
{
byte[] currentPacket = new byte[20];
currentPacket[0] = JYouConstants.CMD_ACTION_SHOW_NOTIFICATION;
currentPacket[1] = 7;
currentPacket[2] = (byte)i;
switch(i) {
case 1:
currentPacket[4] = icon;
break;
case 2:
if (titleBytes != null) {
System.arraycopy(titleBytes, 0, currentPacket, 3, 6);
System.arraycopy(titleBytes, 6, currentPacket, 10, 10);
}
break;
default:
if (messageBytes != null) {
System.arraycopy(messageBytes, 16 * (i - 3), currentPacket, 3, 6);
System.arraycopy(messageBytes, 6 + 16 * (i - 3), currentPacket, 10, 10);
}
break;
}
builder.write(ctrlCharacteristic, currentPacket);
}
performConnected(builder.getTransaction());
} catch (IOException e) {
LOG.warn(e.getMessage());
}
}
@Override
public boolean useAutoConnect() {
return true;
}
@Override
public void onNotification(NotificationSpec notificationSpec) {
String notificationTitle = StringUtils.getFirstOf(notificationSpec.sender, notificationSpec.title);
byte icon;
switch (notificationSpec.type) {
case GENERIC_SMS:
icon = JYouConstants.ICON_SMS;
break;
case FACEBOOK:
case FACEBOOK_MESSENGER:
icon = JYouConstants.ICON_FACEBOOK;
break;
case TWITTER:
icon = JYouConstants.ICON_TWITTER;
break;
case WHATSAPP:
icon = JYouConstants.ICON_WHATSAPP;
break;
default:
icon = JYouConstants.ICON_LINE;
break;
}
showNotification(icon, notificationTitle, notificationSpec.body);
}
@Override
public void onDeleteNotification(int id) {
}
@Override
public void onSetAlarms(ArrayList<? extends Alarm> alarms) {
try {
TransactionBuilder builder = performInitialized("SetAlarms");
for (int i = 0; i < alarms.size(); i++)
{
byte cmd;
switch (i) {
case 0:
cmd = JYouConstants.CMD_SET_ALARM_1;
break;
case 1:
cmd = JYouConstants.CMD_SET_ALARM_2;
break;
case 2:
cmd = JYouConstants.CMD_SET_ALARM_3;
break;
default:
return;
}
Calendar cal = alarms.get(i).getAlarmCal();
builder.write(ctrlCharacteristic, commandWithChecksum(
cmd,
alarms.get(i).isEnabled() ? cal.get(Calendar.HOUR_OF_DAY) : -1,
alarms.get(i).isEnabled() ? cal.get(Calendar.MINUTE) : -1
));
}
performConnected(builder.getTransaction());
GB.toast(getContext(), "Alarm settings applied - do note that the current device does not support day specification", Toast.LENGTH_LONG, GB.INFO);
} catch(IOException e) {
LOG.warn(e.getMessage());
}
}
@Override
public void onSetTime() {
try {
TransactionBuilder builder = performInitialized("SetTime");
syncDateAndTime(builder);
performConnected(builder.getTransaction());
} catch(IOException e) {
LOG.warn(e.getMessage());
}
}
@Override
public void onSetCallState(CallSpec callSpec) {
switch (callSpec.command) {
case CallSpec.CALL_INCOMING:
showNotification(JYouConstants.ICON_CALL, callSpec.name, callSpec.number);
break;
}
}
@Override
public void onSetCannedMessages(CannedMessagesSpec cannedMessagesSpec) {
}
@Override
public void onSetMusicState(MusicStateSpec stateSpec) {
}
@Override
public void onSetMusicInfo(MusicSpec musicSpec) {
}
@Override
public void onEnableRealtimeSteps(boolean enable) {
onEnableRealtimeHeartRateMeasurement(enable);
}
@Override
public void onInstallApp(Uri uri) {
}
@Override
public void onAppInfoReq() {
}
@Override
public void onAppStart(UUID uuid, boolean start) {
}
@Override
public void onAppDelete(UUID uuid) {
}
@Override
public void onAppConfiguration(UUID appUuid, String config) {
}
@Override
public void onAppReorder(UUID[] uuids) {
}
@Override
public void onFetchActivityData() {
}
@Override
public void onReboot() {
try {
TransactionBuilder builder = performInitialized("Reboot");
builder.write(ctrlCharacteristic, commandWithChecksum(
JYouConstants.CMD_ACTION_REBOOT_DEVICE, 0, 0
));
performConnected(builder.getTransaction());
} catch(Exception e) {
LOG.warn(e.getMessage());
}
}
@Override
public void onHeartRateTest() {
try {
TransactionBuilder builder = performInitialized("HeartRateTest");
builder.write(ctrlCharacteristic, commandWithChecksum(
JYouConstants.CMD_ACTION_HEARTRATE_SWITCH, 0, 1
));
performConnected(builder.getTransaction());
} catch(Exception e) {
LOG.warn(e.getMessage());
}
}
@Override
public void onEnableRealtimeHeartRateMeasurement(boolean enable) {
// TODO: test
try {
TransactionBuilder builder = performInitialized("RealTimeHeartMeasurement");
builder.write(ctrlCharacteristic, commandWithChecksum(
JYouConstants.CMD_SET_HEARTRATE_AUTO, 0, enable ? 1 : 0
));
performConnected(builder.getTransaction());
} catch(Exception e) {
LOG.warn(e.getMessage());
}
}
@Override
public void onFindDevice(boolean start) {
if (start) {
showNotification(JYouConstants.ICON_QQ, "Gadgetbridge", "Bzzt! Bzzt!");
GB.toast(getContext(), "As your device doesn't have sound, it will only vibrate 3 times consecutively", Toast.LENGTH_LONG, GB.INFO);
}
}
@Override
public void onSetConstantVibration(int integer) {
}
@Override
public void onScreenshotReq() {
}
@Override
public void onEnableHeartRateSleepSupport(boolean enable) {
}
@Override
public void onAddCalendarEvent(CalendarEventSpec calendarEventSpec) {
}
@Override
public void onDeleteCalendarEvent(byte type, long id) {
}
@Override
public void onSendConfiguration(String config) {
}
@Override
public void onTestNewFunction() {
}
@Override
public void onSendWeather(WeatherSpec weatherSpec) {
}
private byte[] commandWithChecksum(byte cmd, int argSlot1, int argSlot2)
{
ByteBuffer buf = ByteBuffer.allocate(10);
buf.put(cmd);
buf.putInt(argSlot1);
buf.putInt(argSlot2);
byte[] bytesToWrite = buf.array();
byte checksum = 0;
for (byte b : bytesToWrite) {
checksum += b;
}
bytesToWrite[9] = checksum;
return bytesToWrite;
}
private byte[] stringToUTF8Bytes(String src, int byteCount) {
try {
if (src == null)
return null;
for (int i = src.length(); i > 0; i--) {
String sub = src.substring(0, i);
byte[] subUTF8 = sub.getBytes("UTF-8");
if (subUTF8.length == byteCount) {
return subUTF8;
}
if (subUTF8.length < byteCount) {
byte[] largerSubUTF8 = new byte[byteCount];
System.arraycopy(subUTF8, 0, largerSubUTF8, 0, subUTF8.length);
return largerSubUTF8;
}
}
} catch (UnsupportedEncodingException e) {
LOG.warn(e.getMessage());
}
return null;
}
}

View File

@ -42,6 +42,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.UnknownDeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.amazfitbip.AmazfitBipCooordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.hplus.HPlusCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.hplus.MakibesF68Coordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.jyou.TeclastH30Coordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.liveview.LiveviewCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBand2Coordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst;
@ -195,6 +196,7 @@ public class DeviceHelper {
result.add(new HPlusCoordinator());
result.add(new No1F1Coordinator());
result.add(new MakibesF68Coordinator());
result.add(new TeclastH30Coordinator());
return result;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 980 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 992 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@ -10,6 +10,7 @@
<item android:maxLevel="140" android:drawable="@drawable/ic_device_hplus_disabled" />
<item android:maxLevel="141" android:drawable="@drawable/ic_device_hplus_disabled" />
<item android:maxLevel="150" android:drawable="@drawable/ic_device_hplus_disabled" />
<item android:maxLevel="160" android:drawable="@drawable/ic_device_h30_h10_disabled" />
<item android:maxLevel="199" android:drawable="@drawable/ic_launcher" />
<item android:maxLevel="201" android:drawable="@drawable/ic_device_pebble" />
@ -21,5 +22,6 @@
<item android:maxLevel="240" android:drawable="@drawable/ic_device_hplus" />
<item android:maxLevel="241" android:drawable="@drawable/ic_device_hplus" />
<item android:maxLevel="250" android:drawable="@drawable/ic_device_hplus" />
<item android:maxLevel="260" android:drawable="@drawable/ic_device_h30_h10" />
</level-list>