diff --git a/OpenPods/app/src/main/java/com/dosse/airpods/notification/NotificationBuilder.java b/OpenPods/app/src/main/java/com/dosse/airpods/notification/NotificationBuilder.java index b4d5933..8cdfc60 100644 --- a/OpenPods/app/src/main/java/com/dosse/airpods/notification/NotificationBuilder.java +++ b/OpenPods/app/src/main/java/com/dosse/airpods/notification/NotificationBuilder.java @@ -9,8 +9,10 @@ import android.widget.RemoteViews; import androidx.core.app.NotificationCompat; import com.dosse.airpods.R; -import com.dosse.airpods.pods.Pod; import com.dosse.airpods.pods.PodsStatus; +import com.dosse.airpods.pods.data.IPods; +import com.dosse.airpods.pods.data.RegularPods; +import com.dosse.airpods.pods.data.SinglePods; import com.dosse.airpods.utils.PermissionUtils; public class NotificationBuilder { @@ -46,23 +48,20 @@ public class NotificationBuilder { mBuilder.setCustomBigContentView(notificationLocation[0]); } - if (status.isAirpods()) for (RemoteViews notification : notificationArr) { - notification.setImageViewResource(R.id.leftPodImg, status.getLeftPod().isConnected() ? R.drawable.pod : R.drawable.pod_disconnected); - notification.setImageViewResource(R.id.rightPodImg, status.getRightPod().isConnected() ? R.drawable.pod : R.drawable.pod_disconnected); - notification.setImageViewResource(R.id.podCaseImg, status.getCasePod().isConnected() ? R.drawable.pod_case : R.drawable.pod_case_disconnected); - } - else if (status.isAirpodsPro()) for (RemoteViews notification : notificationArr) { - notification.setImageViewResource(R.id.leftPodImg, status.getLeftPod().isConnected() ? R.drawable.podpro : R.drawable.podpro_disconnected); - notification.setImageViewResource(R.id.rightPodImg, status.getRightPod().isConnected() ? R.drawable.podpro : R.drawable.podpro_disconnected); - notification.setImageViewResource(R.id.podCaseImg, status.getCasePod().isConnected() ? R.drawable.podpro_case : R.drawable.podpro_case_disconnected); - } - else if (status.isAirpodsMax()) for (RemoteViews notification : notificationArr) { - notification.setImageViewResource(R.id.leftPodImg, status.getMaxPod().isConnected() ? R.drawable.podmax : R.drawable.podmax_disconnected); - } + IPods airpods = status.getAirpods(); + boolean single = airpods.isSingle(); for (RemoteViews notification : notificationArr) { - notification.setViewVisibility(R.id.rightPod, status.isAirpodsMax() ? View.GONE : View.VISIBLE); - notification.setViewVisibility(R.id.podCase, status.isAirpodsMax() ? View.GONE : View.VISIBLE); + if (!single) { + notification.setImageViewResource(R.id.leftPodImg, ((RegularPods)airpods).getLeftDrawable()); + notification.setImageViewResource(R.id.rightPodImg, ((RegularPods)airpods).getRightDrawable()); + notification.setImageViewResource(R.id.podCaseImg, ((RegularPods)airpods).getCaseDrawable()); + } else { + notification.setImageViewResource(R.id.leftPodImg, ((SinglePods)airpods).getDrawable()); + } + + notification.setViewVisibility(R.id.rightPod, single ? View.GONE : View.VISIBLE); + notification.setViewVisibility(R.id.podCase, single ? View.GONE : View.VISIBLE); } if (isFreshStatus(status)) for (RemoteViews notification : notificationArr) { @@ -73,20 +72,31 @@ public class NotificationBuilder { notification.setViewVisibility(R.id.rightPodUpdating, View.INVISIBLE); notification.setViewVisibility(R.id.podCaseUpdating, View.INVISIBLE); - notification.setTextViewText(R.id.leftPodText, status.isAirpodsMax() ? status.getMaxPod().parseStatus() : status.getLeftPod().parseStatus()); - notification.setTextViewText(R.id.rightPodText, status.getRightPod().parseStatus()); - notification.setTextViewText(R.id.podCaseText, status.getCasePod().parseStatus()); + if (!single) { + RegularPods regularPods = (RegularPods)airpods; - notification.setImageViewResource(R.id.leftBatImg, batImgSrcId(status.isAirpodsMax() ? status.getMaxPod() : status.getLeftPod())); - notification.setImageViewResource(R.id.rightBatImg, batImgSrcId(status.getRightPod())); - notification.setImageViewResource(R.id.caseBatImg, batImgSrcId(status.getCasePod())); + notification.setTextViewText(R.id.leftPodText, regularPods.getParsedStatus(RegularPods.LEFT)); + notification.setTextViewText(R.id.rightPodText, regularPods.getParsedStatus(RegularPods.RIGHT)); + notification.setTextViewText(R.id.podCaseText, regularPods.getParsedStatus(RegularPods.CASE)); - notification.setViewVisibility(R.id.leftBatImg, batImgVisibility(status.isAirpodsMax() ? status.getMaxPod() : status.getLeftPod())); - notification.setViewVisibility(R.id.rightBatImg, batImgVisibility(status.getRightPod())); - notification.setViewVisibility(R.id.caseBatImg, batImgVisibility(status.getCasePod())); + notification.setImageViewResource(R.id.leftBatImg, regularPods.getBatImgSrcId(RegularPods.LEFT)); + notification.setImageViewResource(R.id.rightBatImg, regularPods.getBatImgSrcId(RegularPods.RIGHT)); + notification.setImageViewResource(R.id.caseBatImg, regularPods.getBatImgSrcId(RegularPods.CASE)); - notification.setViewVisibility(R.id.leftInEarImg, status.getLeftPod().isInEar() ? View.VISIBLE : View.INVISIBLE); - notification.setViewVisibility(R.id.rightInEarImg, status.getRightPod().isInEar() ? View.VISIBLE : View.INVISIBLE); + notification.setViewVisibility(R.id.leftBatImg, regularPods.getBatImgVisibility(RegularPods.LEFT)); + notification.setViewVisibility(R.id.rightBatImg, regularPods.getBatImgVisibility(RegularPods.RIGHT)); + notification.setViewVisibility(R.id.caseBatImg, regularPods.getBatImgVisibility(RegularPods.CASE)); + + notification.setViewVisibility(R.id.leftInEarImg, regularPods.getInEarVisibility(RegularPods.LEFT)); + notification.setViewVisibility(R.id.rightInEarImg, regularPods.getInEarVisibility(RegularPods.RIGHT)); + } else { + SinglePods singlePods = (SinglePods)airpods; + + notification.setTextViewText(R.id.leftPodText, singlePods.getParsedStatus()); + notification.setImageViewResource(R.id.leftBatImg, singlePods.getBatImgSrcId()); + notification.setViewVisibility(R.id.leftBatImg, singlePods.getBatImgVisibility()); + notification.setViewVisibility(R.id.leftInEarImg, singlePods.getInEarVisibility()); + } } else for (RemoteViews notification : notificationArr) { notification.setViewVisibility(R.id.leftPodText, View.INVISIBLE); @@ -109,12 +119,4 @@ public class NotificationBuilder { return System.currentTimeMillis() - status.getTimestamp() < TIMEOUT_CONNECTED; } - private static int batImgSrcId (Pod pod) { - return pod.isCharging() ? R.drawable.ic_battery_charging_full_green_24dp : R.drawable.ic_battery_alert_red_24dp; - } - - private static int batImgVisibility (Pod pod) { - return (pod.isCharging() && pod.isConnected() || pod.isLowBattery()) ? View.VISIBLE : View.GONE; - } - } \ No newline at end of file diff --git a/OpenPods/app/src/main/java/com/dosse/airpods/notification/NotificationThread.java b/OpenPods/app/src/main/java/com/dosse/airpods/notification/NotificationThread.java index 641f9a0..7c79100 100644 --- a/OpenPods/app/src/main/java/com/dosse/airpods/notification/NotificationThread.java +++ b/OpenPods/app/src/main/java/com/dosse/airpods/notification/NotificationThread.java @@ -62,7 +62,7 @@ public abstract class NotificationThread extends Thread { Logger.debug("Creating notification"); notificationShowing = true; } - Logger.debug(status.parseStatusForLogger()); + Logger.debug(status.getAirpods().parseStatusForLogger()); mNotifyManager.notify(NOTIFICATION_ID, builder.build(status)); } else { if (notificationShowing) { diff --git a/OpenPods/app/src/main/java/com/dosse/airpods/pods/PodsStatus.java b/OpenPods/app/src/main/java/com/dosse/airpods/pods/PodsStatus.java index 42c1633..d9114ce 100644 --- a/OpenPods/app/src/main/java/com/dosse/airpods/pods/PodsStatus.java +++ b/OpenPods/app/src/main/java/com/dosse/airpods/pods/PodsStatus.java @@ -1,11 +1,23 @@ package com.dosse.airpods.pods; -import java.util.Locale; +import com.dosse.airpods.pods.data.AirPods1; +import com.dosse.airpods.pods.data.AirPods2; +import com.dosse.airpods.pods.data.AirPodsMax; +import com.dosse.airpods.pods.data.AirPodsPro; +import com.dosse.airpods.pods.data.BeatsFlex; +import com.dosse.airpods.pods.data.BeatsSolo3; +import com.dosse.airpods.pods.data.BeatsStudio3; +import com.dosse.airpods.pods.data.BeatsX; +import com.dosse.airpods.pods.data.IPods; +import com.dosse.airpods.pods.data.Pod; +import com.dosse.airpods.pods.data.Powerbeats3; +import com.dosse.airpods.pods.data.PowerbeatsPro; +import com.dosse.airpods.pods.data.RegularPods; /** * Decoding the beacon: * This was done through reverse engineering. Hopefully it's correct. - * - The beacon coming from a pair of AirPods contains a manufacturer specific data field n°76 of 27 bytes + * - The beacon coming from a pair of AirPods/Beats contains a manufacturer specific data field n°76 of 27 bytes * - We convert this data to a hexadecimal string * - The 12th and 13th characters in the string represent the charge of the left and right pods. * Under unknown circumstances[1], they are right and left instead (see isFlipped). Values between 0 and 10 are battery 0-100%; Value 15 means it's disconnected @@ -13,19 +25,16 @@ import java.util.Locale; * - The 14th character in the string represents the "in charge" status. * Bit 0 (LSB) is the left pod; Bit 1 is the right pod; Bit 2 is the case. Bit 3 might be case open/closed but I'm not sure and it's not used * - The 11th character in the string represents the in-ear detection status. Bit 1 is the left pod; Bit 3 is the right pod. - * - The 7th character in the string represents the AirPods model (E=AirPods pro, A=AirPods max) + * - The 7th character in the string represents the model * * Notes: * 1) - isFlipped set by bit 1 of 10th character in the string; seems to be related to in-ear detection; */ public class PodsStatus { - public static final String MODEL_AIRPODS_NORMAL = "airpods12", MODEL_AIRPODS_PRO = "airpodspro", MODEL_AIRPODS_MAX = "airpodsmax"; - public static final PodsStatus DISCONNECTED = new PodsStatus(); - private Pod leftPod, rightPod, casePod, maxPod; - private String model = MODEL_AIRPODS_NORMAL; + private IPods pods; private final long timestamp = System.currentTimeMillis(); public PodsStatus () { @@ -40,90 +49,66 @@ public class PodsStatus { int leftStatus = Integer.parseInt("" + status.charAt(flip ? 12 : 13), 16); // Left airpod (0-10 batt; 15=disconnected) int rightStatus = Integer.parseInt("" + status.charAt(flip ? 13 : 12), 16); // Right airpod (0-10 batt; 15=disconnected) int caseStatus = Integer.parseInt("" + status.charAt(15), 16); // Case (0-10 batt; 15=disconnected) - int maxStatus = Integer.parseInt("" + status.charAt(13), 16); // Airpods max (0-10 batt; 15=disconnected) + int singleStatus = Integer.parseInt("" + status.charAt(13), 16); // Single (0-10 batt; 15=disconnected) int chargeStatus = Integer.parseInt("" + status.charAt(14), 16); // Charge status (bit 0=left; bit 1=right; bit 2=case) boolean chargeL = (chargeStatus & (flip ? 0b00000010 : 0b00000001)) != 0; boolean chargeR = (chargeStatus & (flip ? 0b00000001 : 0b00000010)) != 0; boolean chargeCase = (chargeStatus & 0b00000100) != 0; - boolean chargeMax = (chargeStatus & 0b00000001) != 0; + boolean chargeSingle = (chargeStatus & 0b00000001) != 0; int inEarStatus = Integer.parseInt("" + status.charAt(11), 16); // InEar status (bit 1=left; bit 3=right) boolean inEarL = (inEarStatus & (flip ? 0b00001000 : 0b00000010)) != 0; boolean inEarR = (inEarStatus & (flip ? 0b00000010 : 0b00001000)) != 0; - switch (status.charAt(7)) { // Detect if these are AirPods Pro/Max or regular ones - case 'E': model = MODEL_AIRPODS_PRO; - break; - case 'A': model = MODEL_AIRPODS_MAX; - break; - default: model = MODEL_AIRPODS_NORMAL; - } + Pod leftPod = new Pod(leftStatus, chargeL, inEarL); + Pod rightPod = new Pod(rightStatus, chargeR, inEarR); + Pod casePod = new Pod(caseStatus, chargeCase, false); + Pod singlePod = new Pod(singleStatus, chargeSingle, false); - leftPod = new Pod(leftStatus, chargeL, inEarL); - rightPod = new Pod(rightStatus, chargeR, inEarR); - casePod = new Pod(caseStatus, chargeCase, false); - maxPod = new Pod(maxStatus, chargeMax, false); + // Detect which model + switch (status.charAt(7)) { + case '2': pods = new AirPods1(leftPod, rightPod, casePod); // Airpods 1st gen + break; + case 'F': pods = new AirPods2(leftPod, rightPod, casePod); // Airpods 2nd gen + break; + case 'E': pods = new AirPodsPro(leftPod, rightPod, casePod); // Airpods Pro + break; + case 'A': pods = new AirPodsMax(singlePod); // Airpods Max + break; + case 'B': pods = new PowerbeatsPro(leftPod, rightPod, casePod); // Powerbeats Pro + break; + case '5': pods = new BeatsX(singlePod); // Beats X + break; + case '0': pods = new BeatsFlex(singlePod); // Beats Flex + break; + case '6': pods = new BeatsSolo3(singlePod); // Beats Solo 3 + break; + case '9': pods = new BeatsStudio3(singlePod); // Beats Studio 3 + break; + case '3': pods = new Powerbeats3(singlePod); // Powerbeats 3 + break; + default: pods = new RegularPods(leftPod, rightPod, casePod); // Unknown + } } public static boolean isFlipped (String str) { return (Integer.parseInt("" + str.charAt(10), 16) & 0x02) == 0; } - //region Pod - public Pod getLeftPod () { - return leftPod; - } - - public Pod getRightPod () { - return rightPod; - } - - public Pod getCasePod () { - return casePod; - } - - public Pod getMaxPod () { - return maxPod; - } - //endregion - - public String parseStatusForLogger () { - return isAirpodsMax() ? - String.format(Locale.getDefault(), "Battery: %d%s Model: %s", - maxPod.getStatus(), maxPod.isCharging() ? "+" : "", model) : - String.format(Locale.getDefault(), "Left: %d%s%s Right: %d%s%s Case: %d%s Model: %s", - leftPod.getStatus(), leftPod.isCharging() ? "+" : "", leftPod.isInEar() ? "$" : "", - rightPod.getStatus(), rightPod.isCharging() ? "+" : "", rightPod.isInEar() ? "$" : "", - casePod.getStatus(), casePod.isCharging() ? "+" : "", model); + public IPods getAirpods () { + return pods; } public boolean isAllDisconnected () { if (this == DISCONNECTED) return true; - return leftPod.isDisconnected() && - rightPod.isDisconnected() && - casePod.isDisconnected() && - maxPod.isDisconnected(); + return pods.isDisconnected(); } - //region Model - public boolean isAirpods () { - return model.equals(MODEL_AIRPODS_NORMAL); - } - - public boolean isAirpodsPro () { - return model.equals(MODEL_AIRPODS_PRO); - } - - public boolean isAirpodsMax () { - return model.equals(MODEL_AIRPODS_MAX); - } - //endregion - public long getTimestamp () { return timestamp; } diff --git a/OpenPods/app/src/main/java/com/dosse/airpods/pods/data/AirPods1.java b/OpenPods/app/src/main/java/com/dosse/airpods/pods/data/AirPods1.java new file mode 100644 index 0000000..b814bf6 --- /dev/null +++ b/OpenPods/app/src/main/java/com/dosse/airpods/pods/data/AirPods1.java @@ -0,0 +1,14 @@ +package com.dosse.airpods.pods.data; + +public class AirPods1 extends RegularPods { + + public AirPods1 (Pod leftPod, Pod rightPod, Pod casePod) { + super(leftPod, rightPod, casePod); + } + + @Override + public String getModel () { + return MODEL_AIRPODS_GEN1; + } + +} \ No newline at end of file diff --git a/OpenPods/app/src/main/java/com/dosse/airpods/pods/data/AirPods2.java b/OpenPods/app/src/main/java/com/dosse/airpods/pods/data/AirPods2.java new file mode 100644 index 0000000..f0d9133 --- /dev/null +++ b/OpenPods/app/src/main/java/com/dosse/airpods/pods/data/AirPods2.java @@ -0,0 +1,14 @@ +package com.dosse.airpods.pods.data; + +public class AirPods2 extends RegularPods { + + public AirPods2 (Pod leftPod, Pod rightPod, Pod casePod) { + super(leftPod, rightPod, casePod); + } + + @Override + public String getModel () { + return MODEL_AIRPODS_GEN2; + } + +} \ No newline at end of file diff --git a/OpenPods/app/src/main/java/com/dosse/airpods/pods/data/AirPodsMax.java b/OpenPods/app/src/main/java/com/dosse/airpods/pods/data/AirPodsMax.java new file mode 100644 index 0000000..8f1c893 --- /dev/null +++ b/OpenPods/app/src/main/java/com/dosse/airpods/pods/data/AirPodsMax.java @@ -0,0 +1,21 @@ +package com.dosse.airpods.pods.data; + +import com.dosse.airpods.R; + +public class AirPodsMax extends SinglePods { + + public AirPodsMax (Pod singlePod) { + super(singlePod); + } + + @Override + public int getDrawable () { + return getPod().isConnected() ? R.drawable.podmax : R.drawable.podmax_disconnected; + } + + @Override + public String getModel () { + return MODEL_AIRPODS_MAX; + } + +} \ No newline at end of file diff --git a/OpenPods/app/src/main/java/com/dosse/airpods/pods/data/AirPodsPro.java b/OpenPods/app/src/main/java/com/dosse/airpods/pods/data/AirPodsPro.java new file mode 100644 index 0000000..86d7227 --- /dev/null +++ b/OpenPods/app/src/main/java/com/dosse/airpods/pods/data/AirPodsPro.java @@ -0,0 +1,31 @@ +package com.dosse.airpods.pods.data; + +import com.dosse.airpods.R; + +public class AirPodsPro extends RegularPods { + + public AirPodsPro (Pod leftPod, Pod rightPod, Pod casePod) { + super(leftPod, rightPod, casePod); + } + + @Override + public int getLeftDrawable () { + return getPod(LEFT).isConnected() ? R.drawable.podpro : R.drawable.podpro_disconnected; + } + + @Override + public int getRightDrawable () { + return getPod(RIGHT).isConnected() ? R.drawable.podpro : R.drawable.podpro_disconnected; + } + + @Override + public int getCaseDrawable () { + return getPod(CASE).isConnected() ? R.drawable.podpro_case : R.drawable.podpro_case_disconnected; + } + + @Override + public String getModel () { + return MODEL_AIRPODS_PRO; + } + +} \ No newline at end of file diff --git a/OpenPods/app/src/main/java/com/dosse/airpods/pods/data/BeatsFlex.java b/OpenPods/app/src/main/java/com/dosse/airpods/pods/data/BeatsFlex.java new file mode 100644 index 0000000..880d2fc --- /dev/null +++ b/OpenPods/app/src/main/java/com/dosse/airpods/pods/data/BeatsFlex.java @@ -0,0 +1,21 @@ +package com.dosse.airpods.pods.data; + +import com.dosse.airpods.R; + +public class BeatsFlex extends SinglePods { + + public BeatsFlex (Pod singlePod) { + super(singlePod); + } + + @Override + public int getDrawable () { + return getPod().isConnected() ? R.drawable.beatsflex : R.drawable.beatsflex_disconnected; + } + + @Override + public String getModel () { + return MODEL_BEATS_FLEX; + } + +} \ No newline at end of file diff --git a/OpenPods/app/src/main/java/com/dosse/airpods/pods/data/BeatsSolo3.java b/OpenPods/app/src/main/java/com/dosse/airpods/pods/data/BeatsSolo3.java new file mode 100644 index 0000000..05bccd7 --- /dev/null +++ b/OpenPods/app/src/main/java/com/dosse/airpods/pods/data/BeatsSolo3.java @@ -0,0 +1,21 @@ +package com.dosse.airpods.pods.data; + +import com.dosse.airpods.R; + +public class BeatsSolo3 extends SinglePods { + + public BeatsSolo3 (Pod singlePod) { + super(singlePod); + } + + @Override + public int getDrawable () { + return getPod().isConnected() ? R.drawable.beatssolo3 : R.drawable.beatssolo3_disconnected; + } + + @Override + public String getModel () { + return MODEL_BEATS_SOLO_3; + } + +} \ No newline at end of file diff --git a/OpenPods/app/src/main/java/com/dosse/airpods/pods/data/BeatsStudio3.java b/OpenPods/app/src/main/java/com/dosse/airpods/pods/data/BeatsStudio3.java new file mode 100644 index 0000000..56f9568 --- /dev/null +++ b/OpenPods/app/src/main/java/com/dosse/airpods/pods/data/BeatsStudio3.java @@ -0,0 +1,21 @@ +package com.dosse.airpods.pods.data; + +import com.dosse.airpods.R; + +public class BeatsStudio3 extends SinglePods { + + public BeatsStudio3 (Pod singlePod) { + super(singlePod); + } + + @Override + public int getDrawable () { + return getPod().isConnected() ? R.drawable.beatsstudio3 : R.drawable.beatsstudio3_disconnected; + } + + @Override + public String getModel () { + return MODEL_BEATS_STUDIO_3; + } + +} \ No newline at end of file diff --git a/OpenPods/app/src/main/java/com/dosse/airpods/pods/data/BeatsX.java b/OpenPods/app/src/main/java/com/dosse/airpods/pods/data/BeatsX.java new file mode 100644 index 0000000..b4c8999 --- /dev/null +++ b/OpenPods/app/src/main/java/com/dosse/airpods/pods/data/BeatsX.java @@ -0,0 +1,21 @@ +package com.dosse.airpods.pods.data; + +import com.dosse.airpods.R; + +public class BeatsX extends SinglePods { + + public BeatsX (Pod singlePod) { + super(singlePod); + } + + @Override + public int getDrawable () { + return getPod().isConnected() ? R.drawable.beatsx : R.drawable.beatsx_disconnected; + } + + @Override + public String getModel () { + return MODEL_BEATS_X; + } + +} \ No newline at end of file diff --git a/OpenPods/app/src/main/java/com/dosse/airpods/pods/data/IPods.java b/OpenPods/app/src/main/java/com/dosse/airpods/pods/data/IPods.java new file mode 100644 index 0000000..6a88132 --- /dev/null +++ b/OpenPods/app/src/main/java/com/dosse/airpods/pods/data/IPods.java @@ -0,0 +1,26 @@ +package com.dosse.airpods.pods.data; + +public interface IPods { + + String MODEL_AIRPODS_GEN1 = "airpods1"; + String MODEL_AIRPODS_GEN2 = "airpods2"; + String MODEL_AIRPODS_PRO = "airpodspro"; + String MODEL_AIRPODS_MAX = "airpodsmax"; + String MODEL_BEATS_X = "beatsx"; + String MODEL_BEATS_FLEX = "beatsflex"; + String MODEL_BEATS_SOLO_3 = "beatssolo3"; + String MODEL_BEATS_STUDIO_3 = "beatsstudio3"; + String MODEL_POWERBEATS_3 = "powerbeats3"; + String MODEL_POWERBEATS_PRO = "powerbeatspro"; + String MODEL_UNKNOWN = "unknown"; + + + String getModel (); + + boolean isSingle (); + + boolean isDisconnected (); + + String parseStatusForLogger (); + +} \ No newline at end of file diff --git a/OpenPods/app/src/main/java/com/dosse/airpods/pods/Pod.java b/OpenPods/app/src/main/java/com/dosse/airpods/pods/data/Pod.java similarity index 66% rename from OpenPods/app/src/main/java/com/dosse/airpods/pods/Pod.java rename to OpenPods/app/src/main/java/com/dosse/airpods/pods/data/Pod.java index 9757a50..29160e8 100644 --- a/OpenPods/app/src/main/java/com/dosse/airpods/pods/Pod.java +++ b/OpenPods/app/src/main/java/com/dosse/airpods/pods/data/Pod.java @@ -1,4 +1,8 @@ -package com.dosse.airpods.pods; +package com.dosse.airpods.pods.data; + +import android.view.View; + +import com.dosse.airpods.R; public class Pod { @@ -37,11 +41,23 @@ public class Pod { } public boolean isDisconnected () { - return status == DISCONNECTED_STATUS; + return status == DISCONNECTED_STATUS; } public boolean isLowBattery () { return status <= LOW_BATTERY_STATUS; } -} + public int inEarVisibility () { + return inEar ? View.VISIBLE : View.INVISIBLE; + } + + public int batImgVisibility () { + return (charging && isConnected() || isLowBattery()) ? View.VISIBLE : View.GONE; + } + + public int batImgSrcId () { + return charging ? R.drawable.ic_battery_charging_full_green_24dp : R.drawable.ic_battery_alert_red_24dp; + } + +} \ No newline at end of file diff --git a/OpenPods/app/src/main/java/com/dosse/airpods/pods/data/Powerbeats3.java b/OpenPods/app/src/main/java/com/dosse/airpods/pods/data/Powerbeats3.java new file mode 100644 index 0000000..9aa3841 --- /dev/null +++ b/OpenPods/app/src/main/java/com/dosse/airpods/pods/data/Powerbeats3.java @@ -0,0 +1,21 @@ +package com.dosse.airpods.pods.data; + +import com.dosse.airpods.R; + +public class Powerbeats3 extends SinglePods { + + public Powerbeats3 (Pod singlePod) { + super(singlePod); + } + + @Override + public int getDrawable () { + return getPod().isConnected() ? R.drawable.powerbeats3 : R.drawable.powerbeats3_disconnected; + } + + @Override + public String getModel () { + return MODEL_POWERBEATS_3; + } + +} \ No newline at end of file diff --git a/OpenPods/app/src/main/java/com/dosse/airpods/pods/data/PowerbeatsPro.java b/OpenPods/app/src/main/java/com/dosse/airpods/pods/data/PowerbeatsPro.java new file mode 100644 index 0000000..aebb782 --- /dev/null +++ b/OpenPods/app/src/main/java/com/dosse/airpods/pods/data/PowerbeatsPro.java @@ -0,0 +1,31 @@ +package com.dosse.airpods.pods.data; + +import com.dosse.airpods.R; + +public class PowerbeatsPro extends RegularPods { + + public PowerbeatsPro (Pod leftPod, Pod rightPod, Pod casePod) { + super(leftPod, rightPod, casePod); + } + + @Override + public int getLeftDrawable () { + return getPod(LEFT).isConnected() ? R.drawable.powerbeatspro : R.drawable.powerbeatspro_disconnected; + } + + @Override + public int getRightDrawable () { + return getPod(RIGHT).isConnected() ? R.drawable.powerbeatspro : R.drawable.powerbeatspro_disconnected; + } + + @Override + public int getCaseDrawable () { + return getPod(CASE).isConnected() ? R.drawable.powerbeatspro_case : R.drawable.powerbeatspro_case_disconnected; + } + + @Override + public String getModel () { + return MODEL_POWERBEATS_PRO; + } + +} \ No newline at end of file diff --git a/OpenPods/app/src/main/java/com/dosse/airpods/pods/data/RegularPods.java b/OpenPods/app/src/main/java/com/dosse/airpods/pods/data/RegularPods.java new file mode 100644 index 0000000..73f2604 --- /dev/null +++ b/OpenPods/app/src/main/java/com/dosse/airpods/pods/data/RegularPods.java @@ -0,0 +1,74 @@ +package com.dosse.airpods.pods.data; + +import com.dosse.airpods.R; + +import java.util.Locale; + +public class RegularPods implements IPods { + + public static final int LEFT = 0, RIGHT = 1, CASE = 2; + + private final Pod[] pods; + + public RegularPods (Pod leftPod, Pod rightPod, Pod casePod) { + this.pods = new Pod[] {leftPod, rightPod, casePod}; + } + + public Pod getPod (int pos) { + return pods[pos]; + } + + public String getParsedStatus (int pos) { + return pods[pos].parseStatus(); + } + + public int getInEarVisibility (int pos) { + return pods[pos].inEarVisibility(); + } + + public int getBatImgVisibility (int pos) { + return pods[pos].batImgVisibility(); + } + + public int getBatImgSrcId (int pos) { + return pods[pos].batImgSrcId(); + } + + public int getLeftDrawable () { + return getPod(LEFT).isConnected() ? R.drawable.pod : R.drawable.pod_disconnected; + } + + public int getRightDrawable () { + return getPod(RIGHT).isConnected() ? R.drawable.pod : R.drawable.pod_disconnected; + } + + public int getCaseDrawable () { + return getPod(CASE).isConnected() ? R.drawable.pod_case : R.drawable.pod_case_disconnected; + } + + @Override + public String getModel () { + return MODEL_UNKNOWN; + } + + @Override + public boolean isSingle () { + return false; + } + + @Override + public boolean isDisconnected () { + return pods[LEFT].isDisconnected() && + pods[RIGHT].isDisconnected() && + pods[CASE].isDisconnected(); + } + + @Override + public String parseStatusForLogger () { + return String.format(Locale.getDefault(), "Left: %d%s%s Right: %d%s%s Case: %d%s Model: %s", + pods[LEFT].getStatus(), pods[LEFT].isCharging() ? "+" : "", pods[LEFT].isInEar() ? "$" : "", + pods[RIGHT].getStatus(), pods[RIGHT].isCharging() ? "+" : "", pods[RIGHT].isInEar() ? "$" : "", + pods[CASE].getStatus(), pods[CASE].isCharging() ? "+" : "", getModel()); + } + +} \ No newline at end of file diff --git a/OpenPods/app/src/main/java/com/dosse/airpods/pods/data/SinglePods.java b/OpenPods/app/src/main/java/com/dosse/airpods/pods/data/SinglePods.java new file mode 100644 index 0000000..b479597 --- /dev/null +++ b/OpenPods/app/src/main/java/com/dosse/airpods/pods/data/SinglePods.java @@ -0,0 +1,50 @@ +package com.dosse.airpods.pods.data; + +import java.util.Locale; + +public abstract class SinglePods implements IPods { + + public abstract int getDrawable (); + + private final Pod pod; + + public SinglePods (Pod pod) { + this.pod = pod; + } + + public Pod getPod () { + return pod; + } + + public String getParsedStatus () { + return pod.parseStatus(); + } + + public int getInEarVisibility () { + return pod.inEarVisibility(); + } + + public int getBatImgVisibility () { + return pod.batImgVisibility(); + } + + public int getBatImgSrcId () { + return pod.batImgSrcId(); + } + + @Override + public boolean isSingle () { + return true; + } + + @Override + public boolean isDisconnected () { + return pod.isDisconnected(); + } + + @Override + public String parseStatusForLogger () { + return String.format(Locale.getDefault(), "Battery: %d%s Model: %s", pod.getStatus(), pod.isCharging() ? "+" : "", getModel()); + } + +} \ No newline at end of file diff --git a/OpenPods/app/src/main/res/drawable/beatsflex.png b/OpenPods/app/src/main/res/drawable/beatsflex.png new file mode 100644 index 0000000..3d65c67 Binary files /dev/null and b/OpenPods/app/src/main/res/drawable/beatsflex.png differ diff --git a/OpenPods/app/src/main/res/drawable/beatsflex_disconnected.png b/OpenPods/app/src/main/res/drawable/beatsflex_disconnected.png new file mode 100644 index 0000000..b22a653 Binary files /dev/null and b/OpenPods/app/src/main/res/drawable/beatsflex_disconnected.png differ diff --git a/OpenPods/app/src/main/res/drawable/beatssolo3.png b/OpenPods/app/src/main/res/drawable/beatssolo3.png new file mode 100644 index 0000000..3d65c67 Binary files /dev/null and b/OpenPods/app/src/main/res/drawable/beatssolo3.png differ diff --git a/OpenPods/app/src/main/res/drawable/beatssolo3_disconnected.png b/OpenPods/app/src/main/res/drawable/beatssolo3_disconnected.png new file mode 100644 index 0000000..b22a653 Binary files /dev/null and b/OpenPods/app/src/main/res/drawable/beatssolo3_disconnected.png differ diff --git a/OpenPods/app/src/main/res/drawable/beatsstudio3.png b/OpenPods/app/src/main/res/drawable/beatsstudio3.png new file mode 100644 index 0000000..3d65c67 Binary files /dev/null and b/OpenPods/app/src/main/res/drawable/beatsstudio3.png differ diff --git a/OpenPods/app/src/main/res/drawable/beatsstudio3_disconnected.png b/OpenPods/app/src/main/res/drawable/beatsstudio3_disconnected.png new file mode 100644 index 0000000..b22a653 Binary files /dev/null and b/OpenPods/app/src/main/res/drawable/beatsstudio3_disconnected.png differ diff --git a/OpenPods/app/src/main/res/drawable/beatsx.png b/OpenPods/app/src/main/res/drawable/beatsx.png new file mode 100644 index 0000000..3d65c67 Binary files /dev/null and b/OpenPods/app/src/main/res/drawable/beatsx.png differ diff --git a/OpenPods/app/src/main/res/drawable/beatsx_disconnected.png b/OpenPods/app/src/main/res/drawable/beatsx_disconnected.png new file mode 100644 index 0000000..b22a653 Binary files /dev/null and b/OpenPods/app/src/main/res/drawable/beatsx_disconnected.png differ diff --git a/OpenPods/app/src/main/res/drawable/powerbeats3.png b/OpenPods/app/src/main/res/drawable/powerbeats3.png new file mode 100644 index 0000000..3d65c67 Binary files /dev/null and b/OpenPods/app/src/main/res/drawable/powerbeats3.png differ diff --git a/OpenPods/app/src/main/res/drawable/powerbeats3_disconnected.png b/OpenPods/app/src/main/res/drawable/powerbeats3_disconnected.png new file mode 100644 index 0000000..b22a653 Binary files /dev/null and b/OpenPods/app/src/main/res/drawable/powerbeats3_disconnected.png differ diff --git a/OpenPods/app/src/main/res/drawable/powerbeatspro.png b/OpenPods/app/src/main/res/drawable/powerbeatspro.png new file mode 100644 index 0000000..986017d Binary files /dev/null and b/OpenPods/app/src/main/res/drawable/powerbeatspro.png differ diff --git a/OpenPods/app/src/main/res/drawable/powerbeatspro_case.png b/OpenPods/app/src/main/res/drawable/powerbeatspro_case.png new file mode 100644 index 0000000..b66bdb0 Binary files /dev/null and b/OpenPods/app/src/main/res/drawable/powerbeatspro_case.png differ diff --git a/OpenPods/app/src/main/res/drawable/powerbeatspro_case_disconnected.png b/OpenPods/app/src/main/res/drawable/powerbeatspro_case_disconnected.png new file mode 100644 index 0000000..39b3040 Binary files /dev/null and b/OpenPods/app/src/main/res/drawable/powerbeatspro_case_disconnected.png differ diff --git a/OpenPods/app/src/main/res/drawable/powerbeatspro_disconnected.png b/OpenPods/app/src/main/res/drawable/powerbeatspro_disconnected.png new file mode 100644 index 0000000..bd7718b Binary files /dev/null and b/OpenPods/app/src/main/res/drawable/powerbeatspro_disconnected.png differ