1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-07-21 22:31:16 +02:00

Add support for multiple batteries per device

This commit is contained in:
vanous 2021-10-16 22:40:30 +02:00
parent b0585fc852
commit 788f4d8ae0
11 changed files with 281 additions and 63 deletions

View File

@ -43,7 +43,7 @@ public class GBDaoGenerator {
public static void main(String[] args) throws Exception {
Schema schema = new Schema(34, MAIN_PACKAGE + ".entities");
Schema schema = new Schema(35, MAIN_PACKAGE + ".entities");
Entity userAttributes = addUserAttributes(schema);
Entity user = addUserInfo(schema, userAttributes);
@ -613,6 +613,7 @@ public class GBDaoGenerator {
Property deviceId = batteryLevel.addLongProperty("deviceId").primaryKey().notNull().getProperty();
batteryLevel.addToOne(device, deviceId);
batteryLevel.addIntProperty("level").notNull();
batteryLevel.addIntProperty("batteryIndex").notNull().primaryKey();;
return batteryLevel;
}

View File

@ -22,11 +22,13 @@ import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
public class BatteryInfoActivity extends AbstractGBActivity {
public class
BatteryInfoActivity extends AbstractGBActivity {
private static final Logger LOG = LoggerFactory.getLogger(BatteryInfoActivity.class);
GBDevice gbDevice;
private int timeFrom;
private int timeTo;
private int batteryIndex = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -42,6 +44,7 @@ public class BatteryInfoActivity extends AbstractGBActivity {
Bundle bundle = intent.getExtras();
if (bundle != null) {
gbDevice = bundle.getParcelable(GBDevice.EXTRA_DEVICE);
batteryIndex = bundle.getInt("BATTERY_INDEX", 0);
} else {
throw new IllegalArgumentException("Must provide a device when invoking this activity");
}
@ -55,7 +58,7 @@ public class BatteryInfoActivity extends AbstractGBActivity {
timeTo = (int) (System.currentTimeMillis() / 1000);
batteryInfoChartFragment.setDateAndGetData(gbDevice, timeFrom, timeTo);
batteryInfoChartFragment.setDateAndGetData(gbDevice, batteryIndex, timeFrom, timeTo);
TextView battery_status_device_name_text = (TextView) findViewById(R.id.battery_status_device_name);
TextView battery_status_battery_voltage = (TextView) findViewById(R.id.battery_status_battery_voltage);
@ -105,7 +108,7 @@ public class BatteryInfoActivity extends AbstractGBActivity {
battery_status_time_span_text.setText(text);
battery_status_date_from_text.setText(DateTimeUtils.formatDate(new Date(timeFrom * 1000L)));
battery_status_date_to_text.setText(DateTimeUtils.formatDate(new Date(timeTo * 1000L)));
batteryInfoChartFragment.setDateAndGetData(gbDevice, timeFrom, timeTo);
batteryInfoChartFragment.setDateAndGetData(gbDevice, batteryIndex, timeFrom, timeTo);
}
@ -141,7 +144,7 @@ public class BatteryInfoActivity extends AbstractGBActivity {
battery_status_time_span_seekbar.setProgress(0);
battery_status_time_span_seekbar.setProgress(1);
batteryInfoChartFragment.setDateAndGetData(gbDevice, timeFrom, timeTo);
batteryInfoChartFragment.setDateAndGetData(gbDevice, batteryIndex, timeFrom, timeTo);
}
}, currentDate.get(Calendar.YEAR), currentDate.get(Calendar.MONTH), currentDate.get(Calendar.DATE)).show();
}

View File

@ -74,11 +74,13 @@ public class BatteryInfoChartFragment extends AbstractGBFragment {
private int startTime;
private int endTime;
private GBDevice gbDevice;
private int batteryIndex;
public void setDateAndGetData(GBDevice gbDevice, long startTime, long endTime) {
public void setDateAndGetData(GBDevice gbDevice, int batteryIndex, long startTime, long endTime) {
this.startTime = (int) startTime;
this.endTime = (int) endTime;
this.gbDevice = gbDevice;
this.batteryIndex = batteryIndex;
try {
createRefreshTask("Visualizing data", getActivity()).execute();
} catch (Exception e) {
@ -174,12 +176,13 @@ public class BatteryInfoChartFragment extends AbstractGBFragment {
yAxisRight.setTextColor(CHART_TEXT_COLOR);
}
private List<? extends BatteryLevel> getBatteryLevels(DBHandler db, GBDevice device, int tsFrom, int tsTo) {
private List<? extends BatteryLevel> getBatteryLevels(DBHandler db, GBDevice device, int batteryIndex, int tsFrom, int tsTo) {
BatteryLevelDao batteryLevelDao = db.getDaoSession().getBatteryLevelDao();
Device dbDevice = DBHelper.findDevice(device, db.getDaoSession());
QueryBuilder<BatteryLevel> qb = batteryLevelDao.queryBuilder();
qb.where(BatteryLevelDao.Properties.DeviceId.eq(dbDevice.getId())).orderAsc(BatteryLevelDao.Properties.Timestamp);
qb.where(BatteryLevelDao.Properties.BatteryIndex.eq(batteryIndex));
qb.where(BatteryLevelDao.Properties.Timestamp.gt(tsFrom));
qb.where(BatteryLevelDao.Properties.Timestamp.lt(tsTo));
@ -215,7 +218,7 @@ public class BatteryInfoChartFragment extends AbstractGBFragment {
@Override
protected void doInBackground(DBHandler handler) {
List<? extends BatteryLevel> samples = getBatteryLevels(handler, gbDevice, startTime, endTime);
List<? extends BatteryLevel> samples = getBatteryLevels(handler, gbDevice, batteryIndex, startTime, endTime);
DefaultBatteryChartsData dcd = null;
try {
dcd = fill_dcd(samples);

View File

@ -144,38 +144,51 @@ public class GBDeviceAdapterv2 extends RecyclerView.Adapter<GBDeviceAdapterv2.Vi
//begin of action row
//battery
holder.batteryStatusBox.setVisibility(View.VISIBLE);
short batteryLevel = device.getBatteryLevel();
float batteryVoltage = device.getBatteryVoltage();
BatteryState batteryState = device.getBatteryState();
// multiple battery support: at this point we support up to three batteries
// to support more batteries, the battery UI would need to be extended
if (batteryLevel != GBDevice.BATTERY_UNKNOWN) {
holder.batteryStatusBox.setVisibility(View.VISIBLE);
holder.batteryStatusLabel.setText(device.getBatteryLevel() + "%");
if (BatteryState.BATTERY_CHARGING.equals(batteryState) ||
BatteryState.BATTERY_CHARGING_FULL.equals(batteryState)) {
holder.batteryIcon.setImageLevel(device.getBatteryLevel() + 100);
} else {
holder.batteryIcon.setImageLevel(device.getBatteryLevel());
holder.batteryStatusBox0.setVisibility(coordinator.getBatteryCount() > 0 ? View.VISIBLE : View.GONE);
holder.batteryStatusBox1.setVisibility(coordinator.getBatteryCount() > 1 ? View.VISIBLE : View.GONE);
holder.batteryStatusBox2.setVisibility(coordinator.getBatteryCount() > 2 ? View.VISIBLE : View.GONE);
LinearLayout[] batteryStatusBoxes = {holder.batteryStatusBox0, holder.batteryStatusBox1, holder.batteryStatusBox2};
TextView[] batteryStatusLabels = {holder.batteryStatusLabel0, holder.batteryStatusLabel1, holder.batteryStatusLabel2};
ImageView[] batteryIcons = {holder.batteryIcon0, holder.batteryIcon1, holder.batteryIcon2};
for (int battery = 0; battery < coordinator.getBatteryCount(); battery++) {
int batteryLevel = device.getBatteryLevel(battery);
float batteryVoltage = device.getBatteryVoltage(battery);
BatteryState batteryState = device.getBatteryState();
if (batteryLevel != GBDevice.BATTERY_UNKNOWN) {
batteryStatusBoxes[battery].setVisibility(View.VISIBLE);
batteryStatusLabels[battery].setText(device.getBatteryLevel(battery) + "%");
if (BatteryState.BATTERY_CHARGING.equals(batteryState) ||
BatteryState.BATTERY_CHARGING_FULL.equals(batteryState)) {
batteryIcons[battery].setImageLevel(device.getBatteryLevel(battery) + 100);
} else {
batteryIcons[battery].setImageLevel(device.getBatteryLevel(battery));
}
} else if (BatteryState.NO_BATTERY.equals(batteryState) && batteryVoltage != GBDevice.BATTERY_UNKNOWN) {
batteryStatusBoxes[battery].setVisibility(View.VISIBLE);
batteryStatusLabels[battery].setText(String.format(Locale.getDefault(), "%.2f", batteryVoltage));
batteryIcons[battery].setImageLevel(200);
}
} else if (BatteryState.NO_BATTERY.equals(batteryState) && batteryVoltage != GBDevice.BATTERY_UNKNOWN) {
holder.batteryStatusBox.setVisibility(View.VISIBLE);
holder.batteryStatusLabel.setText(String.format(Locale.getDefault(), "%.2f", batteryVoltage));
holder.batteryIcon.setImageLevel(200);
final int finalBattery = battery;
batteryStatusBoxes[battery].setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent startIntent;
startIntent = new Intent(context, BatteryInfoActivity.class);
startIntent.putExtra(GBDevice.EXTRA_DEVICE, device);
startIntent.putExtra("BATTERY_INDEX", finalBattery);
context.startActivity(startIntent);
}
}
);
}
holder.batteryStatusBox.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v) {
Intent startIntent;
startIntent = new Intent(context, BatteryInfoActivity.class);
startIntent.putExtra(GBDevice.EXTRA_DEVICE, device);
context.startActivity(startIntent);
}
}
);
holder.heartRateStatusBox.setVisibility((device.isInitialized() && coordinator.supportsRealtimeData() && coordinator.supportsHeartRateMeasurement(device)) ? View.VISIBLE : View.GONE);
if (parent.getContext() instanceof ControlCenterv2) {
ActivitySample sample = ((ControlCenterv2) parent.getContext()).getCurrentHRSample();
@ -590,9 +603,15 @@ public class GBDeviceAdapterv2 extends RecyclerView.Adapter<GBDeviceAdapterv2.Vi
TextView deviceStatusLabel;
//actions
LinearLayout batteryStatusBox;
TextView batteryStatusLabel;
ImageView batteryIcon;
LinearLayout batteryStatusBox0;
TextView batteryStatusLabel0;
ImageView batteryIcon0;
LinearLayout batteryStatusBox1;
TextView batteryStatusLabel1;
ImageView batteryIcon1;
LinearLayout batteryStatusBox2;
TextView batteryStatusLabel2;
ImageView batteryIcon2;
ImageView deviceSpecificSettingsView;
LinearLayout fetchActivityDataBox;
ImageView fetchActivityData;
@ -628,9 +647,18 @@ public class GBDeviceAdapterv2 extends RecyclerView.Adapter<GBDeviceAdapterv2.Vi
deviceStatusLabel = view.findViewById(R.id.device_status);
//actions
batteryStatusBox = view.findViewById(R.id.device_battery_status_box);
batteryStatusLabel = view.findViewById(R.id.battery_status);
batteryIcon = view.findViewById(R.id.device_battery_status);
batteryStatusBox0 = view.findViewById(R.id.device_battery_status_box);
batteryStatusLabel0 = view.findViewById(R.id.battery_status);
batteryIcon0 = view.findViewById(R.id.device_battery_status);
batteryStatusBox1 = view.findViewById(R.id.device_battery_status_box1);
batteryStatusLabel1 = view.findViewById(R.id.battery_status1);
batteryIcon1 = view.findViewById(R.id.device_battery_status1);
batteryStatusBox2 = view.findViewById(R.id.device_battery_status_box2);
batteryStatusLabel2 = view.findViewById(R.id.battery_status2);
batteryIcon2 = view.findViewById(R.id.device_battery_status2);
deviceSpecificSettingsView = view.findViewById(R.id.device_specific_settings);
fetchActivityDataBox = view.findViewById(R.id.device_action_fetch_activity_box);
fetchActivityData = view.findViewById(R.id.device_action_fetch_activity);

View File

@ -0,0 +1,54 @@
/* Copyright (C) 2017-2020 Andreas Shimokawa, protomors
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.database.schema;
import android.database.sqlite.SQLiteDatabase;
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
import nodomain.freeyourgadget.gadgetbridge.database.DBUpdateScript;
import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummaryDao;
import nodomain.freeyourgadget.gadgetbridge.entities.BatteryLevelDao;
public class GadgetbridgeUpdate_35 implements DBUpdateScript {
@Override
public void upgradeSchema(SQLiteDatabase db) {
if (!DBHelper.existsColumn(BatteryLevelDao.TABLENAME, BatteryLevelDao.Properties.BatteryIndex.columnName, db)) {
String MOVE_DATA_TO_TEMP_TABLE = "ALTER TABLE battery_level RENAME TO battery_levels_temp;";
db.execSQL(MOVE_DATA_TO_TEMP_TABLE);
String CREATE_TABLE = "CREATE TABLE IF NOT EXISTS \"BATTERY_LEVEL\" (\"TIMESTAMP\" INTEGER NOT NULL ," +
"\"DEVICE_ID\" INTEGER NOT NULL ,\"LEVEL\" INTEGER NOT NULL ,\"BATTERY_INDEX\" INTEGER NOT NULL ," +
"PRIMARY KEY (\"TIMESTAMP\" ,\"DEVICE_ID\" ,\"BATTERY_INDEX\" ) ON CONFLICT REPLACE) WITHOUT ROWID;";
db.execSQL(CREATE_TABLE);
String MIGATE_DATA = "insert into " + BatteryLevelDao.TABLENAME
+ " (" + BatteryLevelDao.Properties.Timestamp.columnName + ","
+ BatteryLevelDao.Properties.DeviceId.columnName + ","
+ BatteryLevelDao.Properties.BatteryIndex.columnName + ","
+ BatteryLevelDao.Properties.Level.columnName + ") "
+ " select Timestamp, Device_ID, 0, Level from battery_levels_temp;";
db.execSQL(MIGATE_DATA);
String DROP_TEMP_TABLE = "drop table if exists battery_levels_temp";
db.execSQL(DROP_TEMP_TABLE);
}
}
@Override
public void downgradeSchema(SQLiteDatabase db) {
}
}

View File

@ -24,7 +24,8 @@ import nodomain.freeyourgadget.gadgetbridge.model.BatteryState;
public class GBDeviceEventBatteryInfo extends GBDeviceEvent {
public GregorianCalendar lastChargeTime = null;
public BatteryState state = BatteryState.UNKNOWN;
public short level = 50;
public int batteryIndex = 0;
public int level = 50;
public int numCharges = -1;
public float voltage = -1f;

View File

@ -246,4 +246,9 @@ public abstract class AbstractDeviceCoordinator implements DeviceCoordinator {
public Class<? extends Activity> getCalibrationActivity() {
return null;
}
@Override
public int getBatteryCount() {
return 1;
} //multiple battery support, default is 1, maximum is 3, 0 will disable the battery in UI
}

View File

@ -348,4 +348,12 @@ public interface DeviceCoordinator {
*/
String[] getSupportedLanguageSettings(GBDevice device);
/**
*
* Multiple battery support: Indicates how many batteries the device has.
* 1 is default, 3 is maximum at the moment (as per UI layout)
* 0 will disable the battery from the UI
*/
int getBatteryCount();
}

View File

@ -17,6 +17,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.impl;
import static nodomain.freeyourgadget.gadgetbridge.model.BatteryState.UNKNOWN;
import android.content.Context;
import android.content.Intent;
import android.os.Parcel;
@ -75,10 +77,13 @@ public class GBDevice implements Parcelable {
private String mFirmwareVersion2;
private String mModel;
private State mState = State.NOT_CONNECTED;
private short mBatteryLevel = BATTERY_UNKNOWN;
private float mBatteryVoltage = BATTERY_UNKNOWN;
// multiple battery support: at this point we support up to three batteries
private int[] mBatteryLevel = {BATTERY_UNKNOWN, BATTERY_UNKNOWN, BATTERY_UNKNOWN};
private float[] mBatteryVoltage = {BATTERY_UNKNOWN, BATTERY_UNKNOWN, BATTERY_UNKNOWN};
private short mBatteryThresholdPercent = BATTERY_THRESHOLD_PERCENT;
private BatteryState mBatteryState;
private BatteryState[] mBatteryState = {UNKNOWN, UNKNOWN, UNKNOWN};
private short mRssi = RSSI_UNKNOWN;
private String mBusyTask;
private List<ItemWithDetails> mDeviceInfos;
@ -111,9 +116,10 @@ public class GBDevice implements Parcelable {
mFirmwareVersion2 = in.readString();
mModel = in.readString();
mState = State.values()[in.readInt()];
mBatteryLevel = (short) in.readInt();
mBatteryLevel = in.createIntArray();
mBatteryVoltage = in.createFloatArray();
mBatteryThresholdPercent = (short) in.readInt();
mBatteryState = (BatteryState) in.readSerializable();
mBatteryState = ordinalsToEnums(in.createIntArray());
mRssi = (short) in.readInt();
mBusyTask = in.readString();
mDeviceInfos = in.readArrayList(getClass().getClassLoader());
@ -136,9 +142,10 @@ public class GBDevice implements Parcelable {
dest.writeString(mFirmwareVersion2);
dest.writeString(mModel);
dest.writeInt(mState.ordinal());
dest.writeInt(mBatteryLevel);
dest.writeIntArray(mBatteryLevel);
dest.writeFloatArray(mBatteryVoltage);
dest.writeInt(mBatteryThresholdPercent);
dest.writeSerializable(mBatteryState);
dest.writeIntArray(enumsToOrdinals(mBatteryState));
dest.writeInt(mRssi);
dest.writeString(mBusyTask);
dest.writeList(mDeviceInfos);
@ -154,6 +161,22 @@ public class GBDevice implements Parcelable {
}
}
private int[] enumsToOrdinals(BatteryState[] arrayEnum) {
int[] ordinals = new int[arrayEnum.length];
for (int i = 0; i < arrayEnum.length; i++) {
ordinals[i] = arrayEnum[i].ordinal();
}
return ordinals;
}
private BatteryState[] ordinalsToEnums(int[] arrayInt){
BatteryState[] enums = new BatteryState[arrayInt.length];
for(int i = 0; i<arrayInt.length; i++){
enums[i]=BatteryState.values()[arrayInt[i]];
}
return enums;
}
public String getName() {
return mName;
@ -324,8 +347,12 @@ public class GBDevice implements Parcelable {
}
private void unsetDynamicState() {
setBatteryLevel(BATTERY_UNKNOWN);
setBatteryState(BatteryState.UNKNOWN);
setBatteryLevel(BATTERY_UNKNOWN, 0);
setBatteryLevel(BATTERY_UNKNOWN, 1);
setBatteryLevel(BATTERY_UNKNOWN, 2);
setBatteryState(UNKNOWN, 0);
setBatteryState(UNKNOWN, 1);
setBatteryState(UNKNOWN, 2);
setFirmwareVersion(null);
setFirmwareVersion2(null);
setRssi(RSSI_UNKNOWN);
@ -471,21 +498,35 @@ public class GBDevice implements Parcelable {
*
* @return the battery level in range 0-100, or -1 if unknown
*/
public short getBatteryLevel() {
return mBatteryLevel;
public int getBatteryLevel() {
return getBatteryLevel(0);
}
public void setBatteryLevel(short batteryLevel) {
public int getBatteryLevel(int index) {
return mBatteryLevel[index];
}
public void setBatteryLevel(int batteryLevel) {
setBatteryLevel(batteryLevel, 0);
}
public void setBatteryLevel(int batteryLevel, int index) {
if ((batteryLevel >= 0 && batteryLevel <= 100) || batteryLevel == BATTERY_UNKNOWN) {
mBatteryLevel = batteryLevel;
mBatteryLevel[index] = batteryLevel;
} else {
LOG.error("Battery level musts be within range 0-100: " + batteryLevel);
}
}
public void setBatteryVoltage(float batteryVoltage) {
setBatteryVoltage(batteryVoltage, 0);
}
public void setBatteryVoltage(float batteryVoltage, int index) {
if (batteryVoltage >= 0 || batteryVoltage == BATTERY_UNKNOWN) {
mBatteryVoltage = batteryVoltage;
mBatteryVoltage[index] = batteryVoltage;
} else {
LOG.error("Battery voltage must be > 0: " + batteryVoltage);
}
@ -497,15 +538,27 @@ public class GBDevice implements Parcelable {
* @return the battery voltage, or -1 if unknown
*/
public float getBatteryVoltage() {
return mBatteryVoltage;
return getBatteryVoltage(0);
}
public float getBatteryVoltage(int index) {
return mBatteryVoltage[index];
}
public BatteryState getBatteryState() {
return mBatteryState;
return getBatteryState(0);
}
public BatteryState getBatteryState(int index) {
return mBatteryState[index];
}
public void setBatteryState(BatteryState mBatteryState) {
this.mBatteryState = mBatteryState;
setBatteryState(mBatteryState, 0);
}
public void setBatteryState(BatteryState mBatteryState, int index) {
this.mBatteryState[index] = mBatteryState;
}
public short getBatteryThresholdPercent() {

View File

@ -395,9 +395,9 @@ public abstract class AbstractDeviceSupport implements DeviceSupport {
protected void handleGBDeviceEvent(GBDeviceEventBatteryInfo deviceEvent) {
Context context = getContext();
LOG.info("Got BATTERY_INFO device event");
gbDevice.setBatteryLevel(deviceEvent.level);
gbDevice.setBatteryLevel(deviceEvent.level, deviceEvent.batteryIndex);
gbDevice.setBatteryState(deviceEvent.state);
gbDevice.setBatteryVoltage(deviceEvent.voltage);
gbDevice.setBatteryVoltage(deviceEvent.voltage, deviceEvent.batteryIndex);
if (deviceEvent.level == GBDevice.BATTERY_UNKNOWN) {
// no level available, just "high" or "low"
@ -455,6 +455,7 @@ public abstract class AbstractDeviceSupport implements DeviceSupport {
int ts = (int) (System.currentTimeMillis() / 1000);
BatteryLevel batteryLevel = new BatteryLevel();
batteryLevel.setTimestamp(ts);
batteryLevel.setBatteryIndex(deviceEvent.batteryIndex);
batteryLevel.setDevice(device);
batteryLevel.setLevel(deviceEvent.level);
handler.getDaoSession().getBatteryLevelDao().insert(batteryLevel);

View File

@ -161,6 +161,67 @@
</LinearLayout>
<LinearLayout
android:id="@+id/device_battery_status_box1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_margin="3dp"
android:orientation="vertical">
<ImageView
android:id="@+id/device_battery_status1"
android:layout_width="40dp"
android:layout_height="40dp"
android:padding="3dp"
android:scaleType="fitXY"
card_view:srcCompat="@drawable/level_list_battery"
card_view:tint="@color/secondarytext" />
<TextView
android:id="@+id/battery_status1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:minWidth="36dp"
android:textColor="@color/secondarytext"
android:textStyle="bold"
tools:text="100%" />
</LinearLayout>
<LinearLayout
android:id="@+id/device_battery_status_box2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_margin="3dp"
android:orientation="vertical">
<ImageView
android:id="@+id/device_battery_status2"
android:layout_width="40dp"
android:layout_height="40dp"
android:padding="3dp"
android:scaleType="fitXY"
card_view:srcCompat="@drawable/level_list_battery"
card_view:tint="@color/secondarytext" />
<TextView
android:id="@+id/battery_status2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:minWidth="36dp"
android:textColor="@color/secondarytext"
android:textStyle="bold"
tools:text="100%" />
</LinearLayout>
<ImageView
android:id="@+id/device_specific_settings"
android:layout_width="40dp"