mirror of
https://github.com/adolfintel/OpenPods.git
synced 2026-09-14 14:56:11 -04:00
Added in ear detection
This commit is contained in:
@@ -52,6 +52,7 @@ public class PodsService extends Service {
|
||||
private static BluetoothLeScanner btScanner;
|
||||
private static int leftStatus = 15, rightStatus = 15, caseStatus = 15;
|
||||
private static boolean chargeL = false, chargeR = false, chargeCase = false;
|
||||
private static boolean inEarL = false, inEarR = false;
|
||||
private static final String MODEL_AIRPODS_NORMAL = "airpods12", MODEL_AIRPODS_PRO = "airpodspro";
|
||||
private static String model = MODEL_AIRPODS_NORMAL;
|
||||
|
||||
@@ -75,12 +76,16 @@ public class PodsService extends Service {
|
||||
* 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
|
||||
* - 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, they are right and left instead (see isFlipped). Values between 0 and 10 are battery 0-100%; Value 15 means it's disconnected
|
||||
* - 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
|
||||
* - The 15th character in the string represents the charge of the case. Values between 0 and 10 are battery 0-100%; Value 15 means it's disconnected
|
||||
* - 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 7th character in the string represents the AirPods model (E=AirPods pro)
|
||||
* - The 11th character in the string represents the in-ear detection; TODO: finish documentation on in-ear detection
|
||||
* <p>
|
||||
* After decoding a beacon, the status is written to leftStatus, rightStatus, caseStatus, chargeL, chargeR, chargeCase so that the NotificationThread can use the information
|
||||
* <p>
|
||||
* Notes:
|
||||
* 1) - isFlipped set by bit 1 of 10th character in the string; seems to be related to in-ear detection;
|
||||
*/
|
||||
private static final ArrayList<ScanResult> recentBeacons = new ArrayList<>();
|
||||
private static final long RECENT_BEACONS_MAX_T_NS = 10000000000L; //10s
|
||||
@@ -174,6 +179,11 @@ public class PodsService extends Service {
|
||||
|
||||
model = (a.charAt(7) == 'E') ? MODEL_AIRPODS_PRO : MODEL_AIRPODS_NORMAL; // Detect if these are AirPods Pro or regular ones
|
||||
|
||||
int inEarStatus = Integer.parseInt("" + a.charAt(11), 16);
|
||||
|
||||
inEarL = (inEarStatus & (flip ? 0b00001000 : 0b00000010)) != 0;
|
||||
inEarR = (inEarStatus & (flip ? 0b00000010 : 0b00001000)) != 0;
|
||||
|
||||
lastSeenConnected = System.currentTimeMillis();
|
||||
} catch (Throwable t) {
|
||||
OpenPodsDebugLog("" + t);
|
||||
@@ -319,7 +329,7 @@ public class PodsService extends Service {
|
||||
}
|
||||
|
||||
if (notificationShowing) {
|
||||
OpenPodsDebugLog("Left: " + leftStatus + (chargeL ? "+" : "") + " Right: " + rightStatus + (chargeR ? "+" : "") + " Case: " + caseStatus + (chargeCase ? "+" : "") + " Model: " + model);
|
||||
OpenPodsDebugLog("Left: " + leftStatus + (chargeL ? "+" : "") + (inEarL ? "$" : "") + " Right: " + rightStatus + (chargeR ? "+" : "") + (inEarR ? "$" : "") + " Case: " + caseStatus + (chargeCase ? "+" : "") + " Model: " + model);
|
||||
|
||||
if (model.equals(MODEL_AIRPODS_NORMAL)) for (RemoteViews notification : notificationArr) {
|
||||
notification.setImageViewResource(R.id.leftPodImg, leftStatus <= 10 ? R.drawable.pod : R.drawable.pod_disconnected);
|
||||
@@ -355,6 +365,9 @@ public class PodsService extends Service {
|
||||
notification.setViewVisibility(R.id.leftBatImg, ((chargeL && leftStatus <= 10) || (leftStatus <= 1) ? View.VISIBLE : View.GONE));
|
||||
notification.setViewVisibility(R.id.rightBatImg, ((chargeR && rightStatus <= 10) || (rightStatus <= 1) ? View.VISIBLE : View.GONE));
|
||||
notification.setViewVisibility(R.id.caseBatImg, ((chargeCase && caseStatus <= 10) || (caseStatus <= 1) ? View.VISIBLE : View.GONE));
|
||||
|
||||
notification.setViewVisibility(R.id.leftInEarImg, inEarL ? View.VISIBLE : View.INVISIBLE);
|
||||
notification.setViewVisibility(R.id.rightInEarImg, inEarR ? View.VISIBLE : View.INVISIBLE);
|
||||
}
|
||||
else for (RemoteViews notification : notificationArr) {
|
||||
notification.setViewVisibility(R.id.leftPodText, View.INVISIBLE);
|
||||
@@ -366,6 +379,8 @@ public class PodsService extends Service {
|
||||
notification.setViewVisibility(R.id.leftPodUpdating, View.VISIBLE);
|
||||
notification.setViewVisibility(R.id.rightPodUpdating, View.VISIBLE);
|
||||
notification.setViewVisibility(R.id.podCaseUpdating, View.VISIBLE);
|
||||
notification.setViewVisibility(R.id.leftInEarImg, View.INVISIBLE);
|
||||
notification.setViewVisibility(R.id.rightInEarImg, View.INVISIBLE);
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="48dp"
|
||||
android:height="48dp"
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@color/textColorPrimary"
|
||||
android:pathData="M15,20c-0.3,0 -0.6,-0.1 -0.8,-0.1c-0.7,-0.4 -1.2,-0.9 -1.7,-2.4c-0.5,-1.6 -1.5,-2.3 -2.4,-3c-0.8,-0.6 -1.6,-1.2 -2.3,-2.5C7.3,11 7,9.9 7,9c0,-2.8 2.2,-5 5,-5s5,2.2 5,5h2c0,-3.9 -3.1,-7 -7,-7S5,5.1 5,9c0,1.3 0.4,2.6 1.1,3.9C7,14.5 8,15.4 8.9,16c0.8,0.6 1.4,1.1 1.7,2c0.6,1.8 1.4,2.8 2.7,3.5c0.5,0.2 1.1,0.4 1.6,0.4c2.2,0 4,-1.8 4,-4h-2C17,19.1 16.1,20 15,20zM9.5,9c0,1.4 1.1,2.5 2.5,2.5s2.5,-1.1 2.5,-2.5S13.4,6.5 12,6.5S9.5,7.6 9.5,9z" />
|
||||
</vector>
|
||||
@@ -23,6 +23,15 @@
|
||||
android:layout_marginBottom="6dp"
|
||||
android:src="@drawable/pod" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/leftInEarImg"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignBottom="@id/leftPodImg"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:src="@drawable/ic_inear_24dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/leftPodText"
|
||||
android:layout_width="wrap_content"
|
||||
@@ -67,6 +76,16 @@
|
||||
android:scaleX="-1"
|
||||
android:src="@drawable/pod" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/rightInEarImg"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignBottom="@id/rightPodImg"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:scaleX="-1"
|
||||
android:src="@drawable/ic_inear_24dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/rightPodText"
|
||||
android:layout_width="wrap_content"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<style name="AppTheme" parent="Theme.AppCompat.DayNight.DarkActionBar">
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
#Tue Nov 03 09:53:40 IST 2020
|
||||
#Mon Nov 30 15:19:04 IST 2020
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
|
||||
|
||||
Reference in New Issue
Block a user