mirror of
https://github.com/adolfintel/OpenPods.git
synced 2026-09-14 23:06:11 -04:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d089b350ea | ||
|
|
8167e84c7d | ||
|
|
7b23d3e1e7 | ||
|
|
158dfe2382 | ||
|
|
71c5f61638 | ||
|
|
067b39aaa7 | ||
|
|
faef81399d | ||
|
|
f00ff42980 | ||
|
|
195fb9520a | ||
|
|
6da1ab1d7d | ||
|
|
67b8ea654f | ||
|
|
b345a91630 | ||
|
|
69c2577c12 | ||
|
|
cd8e855428 | ||
|
|
53377a60c8 | ||
|
|
4fc0b39aa9 | ||
|
|
c2a42f2015 | ||
|
|
ae5a1ab119 | ||
|
|
50fa300c04 | ||
|
|
7d8d286350 | ||
|
|
65cef15357 | ||
|
|
ea1743a3fa | ||
|
|
783ab3c018 | ||
|
|
9c86da7115 |
Generated
+6
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="CompilerConfiguration">
|
||||||
|
<bytecodeTargetLevel target="1.8" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
Generated
+1
@@ -15,6 +15,7 @@
|
|||||||
</set>
|
</set>
|
||||||
</option>
|
</option>
|
||||||
<option name="resolveModulePerSourceSet" value="false" />
|
<option name="resolveModulePerSourceSet" value="false" />
|
||||||
|
<option name="useQualifiedModuleNames" value="true" />
|
||||||
</GradleProjectSettings>
|
</GradleProjectSettings>
|
||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
|
|||||||
Generated
+1
-1
@@ -5,7 +5,7 @@
|
|||||||
<configuration PROFILE_NAME="Debug" CONFIG_NAME="Debug" />
|
<configuration PROFILE_NAME="Debug" CONFIG_NAME="Debug" />
|
||||||
</configurations>
|
</configurations>
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectType">
|
<component name="ProjectType">
|
||||||
|
|||||||
@@ -2,14 +2,15 @@ apply plugin: 'com.android.application'
|
|||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 29
|
compileSdkVersion 29
|
||||||
buildToolsVersion '30.0.0'
|
buildToolsVersion '30.0.2'
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "com.dosse.airpods"
|
applicationId "com.dosse.airpods"
|
||||||
minSdkVersion 23
|
minSdkVersion 23
|
||||||
|
//noinspection OldTargetApi
|
||||||
targetSdkVersion 29
|
targetSdkVersion 29
|
||||||
versionCode 15
|
versionCode 16
|
||||||
versionName '1.5'
|
versionName '1.5.1'
|
||||||
|
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
@@ -33,7 +34,6 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||||
implementation 'androidx.appcompat:appcompat:1.1.0'
|
|
||||||
implementation "androidx.preference:preference:1.1.1"
|
implementation "androidx.preference:preference:1.1.1"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
package="com.dosse.airpods">
|
package="com.dosse.airpods">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
|
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||||
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
||||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
@@ -66,8 +65,8 @@
|
|||||||
<service
|
<service
|
||||||
android:name=".PodsService"
|
android:name=".PodsService"
|
||||||
android:enabled="true"
|
android:enabled="true"
|
||||||
android:exported="true"
|
android:exported="false"
|
||||||
tools:ignore="ExportedService" />
|
android:foregroundServiceType="location" />
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ public class IntroActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
// Allow button clicked, ask for permissions
|
// Allow button clicked, ask for permissions
|
||||||
findViewById(R.id.allowBtn).setOnClickListener(view -> {
|
findViewById(R.id.allowBtn).setOnClickListener(view -> {
|
||||||
requestPermissions(new String[] {Manifest.permission.ACCESS_COARSE_LOCATION}, 1); // Location (for BLE)
|
requestPermissions(new String[] {Manifest.permission.ACCESS_FINE_LOCATION}, 1); // Location (for BLE)
|
||||||
// Run in background
|
// Run in background
|
||||||
try {
|
try {
|
||||||
if (!Objects.requireNonNull(getSystemService(PowerManager.class)).isIgnoringBatteryOptimizations(getPackageName())) {
|
if (!Objects.requireNonNull(getSystemService(PowerManager.class)).isIgnoringBatteryOptimizations(getPackageName())) {
|
||||||
@@ -57,7 +57,7 @@ public class IntroActivity extends AppCompatActivity {
|
|||||||
} catch (Throwable ignored) {
|
} catch (Throwable ignored) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ContextCompat.checkSelfPermission(IntroActivity.this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED)
|
if (ContextCompat.checkSelfPermission(IntroActivity.this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_DENIED)
|
||||||
ok = false;
|
ok = false;
|
||||||
|
|
||||||
if (ok) {
|
if (ok) {
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
} catch (Throwable ignored) {
|
} catch (Throwable ignored) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED)
|
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_DENIED)
|
||||||
ok = false;
|
ok = false;
|
||||||
|
|
||||||
if (ok) {
|
if (ok) {
|
||||||
|
|||||||
@@ -45,11 +45,14 @@ import java.util.Objects;
|
|||||||
* - Display the notification with the status
|
* - Display the notification with the status
|
||||||
*/
|
*/
|
||||||
public class PodsService extends Service {
|
public class PodsService extends Service {
|
||||||
private static final boolean ENABLE_LOGGING = BuildConfig.DEBUG; // Log is only displayed if this is a debug build, not release
|
private static void OpenPodsDebugLog (String msg) {
|
||||||
|
if (BuildConfig.DEBUG) Log.d(TAG, msg); // Log is only displayed if this is a debug build, not release
|
||||||
|
}
|
||||||
|
|
||||||
private static BluetoothLeScanner btScanner;
|
private static BluetoothLeScanner btScanner;
|
||||||
private static int leftStatus = 15, rightStatus = 15, caseStatus = 15;
|
private static int leftStatus = 15, rightStatus = 15, caseStatus = 15;
|
||||||
private static boolean chargeL = false, chargeR = false, chargeCase = false;
|
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 final String MODEL_AIRPODS_NORMAL = "airpods12", MODEL_AIRPODS_PRO = "airpodspro";
|
||||||
private static String model = MODEL_AIRPODS_NORMAL;
|
private static String model = MODEL_AIRPODS_NORMAL;
|
||||||
|
|
||||||
@@ -73,20 +76,23 @@ public class PodsService extends Service {
|
|||||||
* This was done through reverse engineering. Hopefully it's correct.
|
* 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 contains a manufacturer specific data field n°76 of 27 bytes
|
||||||
* - We convert this data to a hexadecimal string
|
* - 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 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 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)
|
* - The 7th character in the string represents the AirPods model (E=AirPods pro)
|
||||||
* <p>
|
* <p>
|
||||||
* After decoding a beacon, the status is written to leftStatus, rightStatus, caseStatus, chargeL, chargeR, chargeCase so that the NotificationThread can use the information
|
* After decoding a beacon, the status is written to leftStatus, rightStatus, caseStatus, chargeL, chargeR, chargeCase, inEarL, inEarR 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 ArrayList<ScanResult> recentBeacons = new ArrayList<>();
|
private static final ArrayList<ScanResult> recentBeacons = new ArrayList<>();
|
||||||
private static final long RECENT_BEACONS_MAX_T_NS = 10000000000L; //10s
|
private static final long RECENT_BEACONS_MAX_T_NS = 10000000000L; //10s
|
||||||
|
|
||||||
private void startAirPodsScanner () {
|
private void startAirPodsScanner () {
|
||||||
try {
|
try {
|
||||||
if (ENABLE_LOGGING)
|
OpenPodsDebugLog("START SCANNER");
|
||||||
Log.d(TAG, "START SCANNER");
|
|
||||||
|
|
||||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
|
||||||
BluetoothManager btManager = (BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE);
|
BluetoothManager btManager = (BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE);
|
||||||
@@ -116,98 +122,73 @@ public class PodsService extends Service {
|
|||||||
else
|
else
|
||||||
settings = new ScanSettings.Builder().setScanMode(2).setReportDelay(2).build();
|
settings = new ScanSettings.Builder().setScanMode(2).setReportDelay(2).build();
|
||||||
|
|
||||||
btScanner.startScan(
|
btScanner.startScan(filters, settings, new ScanCallback() {
|
||||||
filters,
|
@Override
|
||||||
settings,
|
public void onBatchScanResults (List<ScanResult> scanResults) {
|
||||||
new ScanCallback() {
|
for (ScanResult result : scanResults)
|
||||||
@Override
|
onScanResult(-1, result);
|
||||||
public void onBatchScanResults (List<ScanResult> scanResults) {
|
super.onBatchScanResults(scanResults);
|
||||||
for (ScanResult result : scanResults)
|
}
|
||||||
onScanResult(-1, result);
|
|
||||||
super.onBatchScanResults(scanResults);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onScanResult (int callbackType, ScanResult result) {
|
public void onScanResult (int callbackType, ScanResult result) {
|
||||||
try {
|
try {
|
||||||
byte[] data = Objects.requireNonNull(result.getScanRecord()).getManufacturerSpecificData(76);
|
byte[] data = Objects.requireNonNull(result.getScanRecord()).getManufacturerSpecificData(76);
|
||||||
|
|
||||||
if (data == null || data.length != 27)
|
if (data == null || data.length != 27)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
recentBeacons.add(result);
|
recentBeacons.add(result);
|
||||||
|
|
||||||
if (ENABLE_LOGGING) {
|
OpenPodsDebugLog("" + result.getRssi() + "db");
|
||||||
Log.d(TAG, "" + result.getRssi() + "db");
|
OpenPodsDebugLog(decodeHex(data));
|
||||||
Log.d(TAG, decodeHex(data));
|
|
||||||
}
|
|
||||||
|
|
||||||
ScanResult strongestBeacon = null;
|
ScanResult strongestBeacon = null;
|
||||||
for (int i = 0; i < recentBeacons.size(); i++) {
|
for (int i = 0; i < recentBeacons.size(); i++) {
|
||||||
if (SystemClock.elapsedRealtimeNanos() - recentBeacons.get(i).getTimestampNanos() > RECENT_BEACONS_MAX_T_NS) {
|
if (SystemClock.elapsedRealtimeNanos() - recentBeacons.get(i).getTimestampNanos() > RECENT_BEACONS_MAX_T_NS) {
|
||||||
recentBeacons.remove(i--);
|
recentBeacons.remove(i--);
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
if (strongestBeacon == null || strongestBeacon.getRssi() < recentBeacons.get(i).getRssi())
|
|
||||||
strongestBeacon = recentBeacons.get(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strongestBeacon != null && strongestBeacon.getDevice().getAddress().equals(result.getDevice().getAddress()))
|
|
||||||
strongestBeacon = result;
|
|
||||||
|
|
||||||
result = strongestBeacon;
|
|
||||||
assert result != null;
|
|
||||||
if (result.getRssi() < -60)
|
|
||||||
return;
|
|
||||||
|
|
||||||
String a = decodeHex(Objects.requireNonNull(Objects.requireNonNull(result.getScanRecord()).getManufacturerSpecificData(76)));
|
|
||||||
boolean flip = isFlipped(a);
|
|
||||||
|
|
||||||
String leftAirpodStr; // Left airpod (0-10 batt; 15=disconnected)
|
|
||||||
String rightAirpodStr; // Right airpod (0-10 batt; 15=disconnected)
|
|
||||||
|
|
||||||
if (flip) {
|
|
||||||
leftAirpodStr = "" + a.charAt(12);
|
|
||||||
rightAirpodStr = "" + a.charAt(13);
|
|
||||||
} else {
|
|
||||||
leftAirpodStr = "" + a.charAt(13);
|
|
||||||
rightAirpodStr = "" + a.charAt(12);
|
|
||||||
}
|
|
||||||
|
|
||||||
String caseStr = "" + a.charAt(15); // Case (0-10 batt; 15=disconnected)
|
|
||||||
String chargeStatusStr = "" + a.charAt(14); // Charge status (bit 0=left; bit 1=right; bit 2=case)
|
|
||||||
|
|
||||||
leftStatus = Integer.parseInt(leftAirpodStr, 16);
|
|
||||||
rightStatus = Integer.parseInt(rightAirpodStr, 16);
|
|
||||||
caseStatus = Integer.parseInt(caseStr, 16);
|
|
||||||
|
|
||||||
int chargeStatus = Integer.parseInt(chargeStatusStr, 16);
|
|
||||||
|
|
||||||
if (flip) {
|
|
||||||
chargeL = (chargeStatus & 0b00000010) != 0;
|
|
||||||
chargeR = (chargeStatus & 0b00000001) != 0;
|
|
||||||
} else {
|
|
||||||
chargeL = (chargeStatus & 0b00000001) != 0;
|
|
||||||
chargeR = (chargeStatus & 0b00000010) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
chargeCase = (chargeStatus & 0b00000100) != 0;
|
|
||||||
|
|
||||||
if (a.charAt(7) == 'E')
|
|
||||||
model = MODEL_AIRPODS_PRO;
|
|
||||||
else
|
|
||||||
model = MODEL_AIRPODS_NORMAL; // Detect if these are AirPods pro or regular ones
|
|
||||||
|
|
||||||
lastSeenConnected = System.currentTimeMillis();
|
|
||||||
} catch (Throwable t) {
|
|
||||||
if (ENABLE_LOGGING)
|
|
||||||
Log.d(TAG, "" + t);
|
|
||||||
}
|
}
|
||||||
|
if (strongestBeacon == null || strongestBeacon.getRssi() < recentBeacons.get(i).getRssi())
|
||||||
|
strongestBeacon = recentBeacons.get(i);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
if (strongestBeacon != null && strongestBeacon.getDevice().getAddress().equals(result.getDevice().getAddress()))
|
||||||
|
strongestBeacon = result;
|
||||||
|
|
||||||
|
result = strongestBeacon;
|
||||||
|
assert result != null;
|
||||||
|
if (result.getRssi() < -60)
|
||||||
|
return;
|
||||||
|
|
||||||
|
String a = decodeHex(Objects.requireNonNull(Objects.requireNonNull(result.getScanRecord()).getManufacturerSpecificData(76)));
|
||||||
|
boolean flip = isFlipped(a);
|
||||||
|
|
||||||
|
leftStatus = Integer.parseInt("" + a.charAt(flip ? 12 : 13), 16); // Left airpod (0-10 batt; 15=disconnected)
|
||||||
|
rightStatus = Integer.parseInt("" + a.charAt(flip ? 13 : 12), 16); // Right airpod (0-10 batt; 15=disconnected)
|
||||||
|
caseStatus = Integer.parseInt("" + a.charAt(15), 16); // Case (0-10 batt; 15=disconnected)
|
||||||
|
|
||||||
|
int chargeStatus = Integer.parseInt("" + a.charAt(14), 16); // Charge status (bit 0=left; bit 1=right; bit 2=case)
|
||||||
|
|
||||||
|
chargeL = (chargeStatus & (flip ? 0b00000010 : 0b00000001)) != 0;
|
||||||
|
chargeR = (chargeStatus & (flip ? 0b00000001 : 0b00000010)) != 0;
|
||||||
|
chargeCase = (chargeStatus & 0b00000100) != 0;
|
||||||
|
|
||||||
|
int inEarStatus = Integer.parseInt("" + a.charAt(11), 16); // InEar status (bit 1=left; bit 3=right)
|
||||||
|
|
||||||
|
inEarL = (inEarStatus & (flip ? 0b00001000 : 0b00000010)) != 0;
|
||||||
|
inEarR = (inEarStatus & (flip ? 0b00000010 : 0b00001000)) != 0;
|
||||||
|
|
||||||
|
model = (a.charAt(7) == 'E') ? MODEL_AIRPODS_PRO : MODEL_AIRPODS_NORMAL; // Detect if these are AirPods Pro or regular ones
|
||||||
|
|
||||||
|
lastSeenConnected = System.currentTimeMillis();
|
||||||
|
} catch (Throwable t) {
|
||||||
|
OpenPodsDebugLog("" + t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
if (ENABLE_LOGGING)
|
OpenPodsDebugLog("" + t);
|
||||||
Log.d(TAG, "" + t);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,8 +211,7 @@ public class PodsService extends Service {
|
|||||||
private void stopAirPodsScanner () {
|
private void stopAirPodsScanner () {
|
||||||
try {
|
try {
|
||||||
if (btScanner != null) {
|
if (btScanner != null) {
|
||||||
if (ENABLE_LOGGING)
|
OpenPodsDebugLog("STOP SCANNER");
|
||||||
Log.d(TAG, "STOP SCANNER");
|
|
||||||
|
|
||||||
btScanner.stopScan(new ScanCallback() {
|
btScanner.stopScan(new ScanCallback() {
|
||||||
@Override
|
@Override
|
||||||
@@ -246,22 +226,17 @@ public class PodsService extends Service {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final char[] hexCharset = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
|
|
||||||
|
|
||||||
private String decodeHex (byte[] bArr) {
|
private String decodeHex (byte[] bArr) {
|
||||||
char[] ret = new char[bArr.length * 2];
|
StringBuilder ret = new StringBuilder();
|
||||||
|
|
||||||
for (int i = 0; i < bArr.length; i++) {
|
for (byte b : bArr)
|
||||||
int b = bArr[i] & 0xFF;
|
ret.append(String.format("%02X", b));
|
||||||
ret[i * 2] = hexCharset[b >>> 4];
|
|
||||||
ret[i * 2 + 1] = hexCharset[b & 0x0F];
|
|
||||||
}
|
|
||||||
|
|
||||||
return new String(ret);
|
return ret.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isFlipped (String str) {
|
private boolean isFlipped (String str) {
|
||||||
return (Integer.toString(Integer.parseInt("" + str.charAt(10), 16) + 0x10, 2)).charAt(3) == '0';
|
return (Integer.parseInt("" + str.charAt(10), 16) & 0x02) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -294,7 +269,7 @@ public class PodsService extends Service {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private NotificationManager mNotifyManager;
|
private final NotificationManager mNotifyManager;
|
||||||
|
|
||||||
@SuppressWarnings("WeakerAccess")
|
@SuppressWarnings("WeakerAccess")
|
||||||
public NotificationThread () {
|
public NotificationThread () {
|
||||||
@@ -315,10 +290,7 @@ public class PodsService extends Service {
|
|||||||
boolean notificationShowing = false;
|
boolean notificationShowing = false;
|
||||||
String compat = getPackageManager().getInstallerPackageName(getPackageName());
|
String compat = getPackageManager().getInstallerPackageName(getPackageName());
|
||||||
|
|
||||||
RemoteViews notificationBig = new RemoteViews(getPackageName(), R.layout.status_big);
|
RemoteViews[] notificationLocation = new RemoteViews[] {new RemoteViews(getPackageName(), R.layout.location_disabled_big), new RemoteViews(getPackageName(), R.layout.location_disabled_small)};
|
||||||
RemoteViews notificationSmall = new RemoteViews(getPackageName(), R.layout.status_small);
|
|
||||||
RemoteViews locationDisabledBig = new RemoteViews(getPackageName(), R.layout.location_disabled_big);
|
|
||||||
RemoteViews locationDisabledSmall = new RemoteViews(getPackageName(), R.layout.location_disabled_small);
|
|
||||||
|
|
||||||
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(PodsService.this, TAG);
|
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(PodsService.this, TAG);
|
||||||
mBuilder.setShowWhen(false);
|
mBuilder.setShowWhen(false);
|
||||||
@@ -327,18 +299,18 @@ public class PodsService extends Service {
|
|||||||
mBuilder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
|
mBuilder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
|
||||||
|
|
||||||
for (; ; ) {
|
for (; ; ) {
|
||||||
|
RemoteViews[] notificationArr = new RemoteViews[] {new RemoteViews(getPackageName(), R.layout.status_big), new RemoteViews(getPackageName(), R.layout.status_small)};
|
||||||
|
|
||||||
/*&&System.currentTimeMillis()-lastSeenConnected<TIMEOUT_CONNECTED*/
|
/*&&System.currentTimeMillis()-lastSeenConnected<TIMEOUT_CONNECTED*/
|
||||||
if (maybeConnected && !(leftStatus == 15 && rightStatus == 15 && caseStatus == 15)) {
|
if (maybeConnected && !(leftStatus == 15 && rightStatus == 15 && caseStatus == 15)) {
|
||||||
if (!notificationShowing) {
|
if (!notificationShowing) {
|
||||||
if (ENABLE_LOGGING)
|
OpenPodsDebugLog("Creating notification");
|
||||||
Log.d(TAG, "Creating notification");
|
|
||||||
notificationShowing = true;
|
notificationShowing = true;
|
||||||
mNotifyManager.notify(1, mBuilder.build());
|
mNotifyManager.notify(1, mBuilder.build());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (notificationShowing) {
|
if (notificationShowing) {
|
||||||
if (ENABLE_LOGGING)
|
OpenPodsDebugLog("Removing notification");
|
||||||
Log.d(TAG, "Removing notification");
|
|
||||||
notificationShowing = false;
|
notificationShowing = false;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -347,95 +319,66 @@ public class PodsService extends Service {
|
|||||||
|
|
||||||
// Apparently this restriction was removed in android Q
|
// Apparently this restriction was removed in android Q
|
||||||
if (isLocationEnabled() || Build.VERSION.SDK_INT >= 29) {
|
if (isLocationEnabled() || Build.VERSION.SDK_INT >= 29) {
|
||||||
mBuilder.setCustomContentView(notificationSmall);
|
mBuilder.setCustomContentView(notificationArr[1]);
|
||||||
mBuilder.setCustomBigContentView(notificationBig);
|
mBuilder.setCustomBigContentView(notificationArr[0]);
|
||||||
} else {
|
} else {
|
||||||
mBuilder.setCustomContentView(locationDisabledSmall);
|
mBuilder.setCustomContentView(notificationLocation[1]);
|
||||||
mBuilder.setCustomBigContentView(locationDisabledBig);
|
mBuilder.setCustomBigContentView(notificationLocation[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (notificationShowing) {
|
if (notificationShowing) {
|
||||||
if (ENABLE_LOGGING)
|
OpenPodsDebugLog("Left: " + leftStatus + (chargeL ? "+" : "") + (inEarL ? "$" : "") + " Right: " + rightStatus + (chargeR ? "+" : "") + (inEarR ? "$" : "") + " Case: " + caseStatus + (chargeCase ? "+" : "") + " Model: " + model);
|
||||||
Log.d(TAG, "Left: " + leftStatus + (chargeL ? "+" : "") + " Right: " + rightStatus + (chargeR ? "+" : "") + " Case: " + caseStatus + (chargeCase ? "+" : "") + " Model: " + model);
|
|
||||||
|
|
||||||
if (model.equals(MODEL_AIRPODS_NORMAL)) {
|
if (model.equals(MODEL_AIRPODS_NORMAL)) for (RemoteViews notification : notificationArr) {
|
||||||
notificationBig.setImageViewResource(R.id.leftPodImg, leftStatus <= 10 ? R.drawable.pod : R.drawable.pod_disconnected);
|
notification.setImageViewResource(R.id.leftPodImg, leftStatus <= 10 ? R.drawable.pod : R.drawable.pod_disconnected);
|
||||||
notificationBig.setImageViewResource(R.id.rightPodImg, rightStatus <= 10 ? R.drawable.pod : R.drawable.pod_disconnected);
|
notification.setImageViewResource(R.id.rightPodImg, rightStatus <= 10 ? R.drawable.pod : R.drawable.pod_disconnected);
|
||||||
notificationBig.setImageViewResource(R.id.podCaseImg, caseStatus <= 10 ? R.drawable.pod_case : R.drawable.pod_case_disconnected);
|
notification.setImageViewResource(R.id.podCaseImg, caseStatus <= 10 ? R.drawable.pod_case : R.drawable.pod_case_disconnected);
|
||||||
notificationSmall.setImageViewResource(R.id.leftPodImg, leftStatus <= 10 ? R.drawable.pod : R.drawable.pod_disconnected);
|
}
|
||||||
notificationSmall.setImageViewResource(R.id.rightPodImg, rightStatus <= 10 ? R.drawable.pod : R.drawable.pod_disconnected);
|
else if (model.equals(MODEL_AIRPODS_PRO)) for (RemoteViews notification : notificationArr) {
|
||||||
notificationSmall.setImageViewResource(R.id.podCaseImg, caseStatus <= 10 ? R.drawable.pod_case : R.drawable.pod_case_disconnected);
|
notification.setImageViewResource(R.id.leftPodImg, leftStatus <= 10 ? R.drawable.podpro : R.drawable.podpro_disconnected);
|
||||||
} else if (model.equals(MODEL_AIRPODS_PRO)) {
|
notification.setImageViewResource(R.id.rightPodImg, rightStatus <= 10 ? R.drawable.podpro : R.drawable.podpro_disconnected);
|
||||||
notificationBig.setImageViewResource(R.id.leftPodImg, leftStatus <= 10 ? R.drawable.podpro : R.drawable.podpro_disconnected);
|
notification.setImageViewResource(R.id.podCaseImg, caseStatus <= 10 ? R.drawable.podpro_case : R.drawable.podpro_case_disconnected);
|
||||||
notificationBig.setImageViewResource(R.id.rightPodImg, rightStatus <= 10 ? R.drawable.podpro : R.drawable.podpro_disconnected);
|
|
||||||
notificationBig.setImageViewResource(R.id.podCaseImg, caseStatus <= 10 ? R.drawable.podpro_case : R.drawable.podpro_case_disconnected);
|
|
||||||
notificationSmall.setImageViewResource(R.id.leftPodImg, leftStatus <= 10 ? R.drawable.podpro : R.drawable.podpro_disconnected);
|
|
||||||
notificationSmall.setImageViewResource(R.id.rightPodImg, rightStatus <= 10 ? R.drawable.podpro : R.drawable.podpro_disconnected);
|
|
||||||
notificationSmall.setImageViewResource(R.id.podCaseImg, caseStatus <= 10 ? R.drawable.podpro_case : R.drawable.podpro_case_disconnected);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (System.currentTimeMillis() - lastSeenConnected < TIMEOUT_CONNECTED) {
|
if (System.currentTimeMillis() - lastSeenConnected < TIMEOUT_CONNECTED) for (RemoteViews notification : notificationArr) {
|
||||||
notificationBig.setViewVisibility(R.id.leftPodText, View.VISIBLE);
|
notification.setViewVisibility(R.id.leftPodText, View.VISIBLE);
|
||||||
notificationBig.setViewVisibility(R.id.rightPodText, View.VISIBLE);
|
notification.setViewVisibility(R.id.rightPodText, View.VISIBLE);
|
||||||
notificationBig.setViewVisibility(R.id.podCaseText, View.VISIBLE);
|
notification.setViewVisibility(R.id.podCaseText, View.VISIBLE);
|
||||||
notificationBig.setViewVisibility(R.id.leftPodUpdating, View.INVISIBLE);
|
notification.setViewVisibility(R.id.leftPodUpdating, View.INVISIBLE);
|
||||||
notificationBig.setViewVisibility(R.id.rightPodUpdating, View.INVISIBLE);
|
notification.setViewVisibility(R.id.rightPodUpdating, View.INVISIBLE);
|
||||||
notificationBig.setViewVisibility(R.id.podCaseUpdating, View.INVISIBLE);
|
notification.setViewVisibility(R.id.podCaseUpdating, View.INVISIBLE);
|
||||||
|
|
||||||
notificationSmall.setViewVisibility(R.id.leftPodText, View.VISIBLE);
|
|
||||||
notificationSmall.setViewVisibility(R.id.rightPodText, View.VISIBLE);
|
|
||||||
notificationSmall.setViewVisibility(R.id.podCaseText, View.VISIBLE);
|
|
||||||
notificationSmall.setViewVisibility(R.id.leftPodUpdating, View.INVISIBLE);
|
|
||||||
notificationSmall.setViewVisibility(R.id.rightPodUpdating, View.INVISIBLE);
|
|
||||||
notificationSmall.setViewVisibility(R.id.podCaseUpdating, View.INVISIBLE);
|
|
||||||
|
|
||||||
String podText_Left = (leftStatus == 10) ? "100%" : ((leftStatus < 10) ? ((leftStatus * 10 + 5) + "%") : "");
|
String podText_Left = (leftStatus == 10) ? "100%" : ((leftStatus < 10) ? ((leftStatus * 10 + 5) + "%") : "");
|
||||||
String podText_Right = (rightStatus == 10) ? "100%" : ((rightStatus < 10) ? ((rightStatus * 10 + 5) + "%") : "");
|
String podText_Right = (rightStatus == 10) ? "100%" : ((rightStatus < 10) ? ((rightStatus * 10 + 5) + "%") : "");
|
||||||
String podText_Case = (caseStatus == 10) ? "100%" : ((caseStatus < 10) ? ((caseStatus * 10 + 5) + "%") : "");
|
String podText_Case = (caseStatus == 10) ? "100%" : ((caseStatus < 10) ? ((caseStatus * 10 + 5) + "%") : "");
|
||||||
|
|
||||||
notificationBig.setTextViewText(R.id.leftPodText, podText_Left);
|
notification.setTextViewText(R.id.leftPodText, podText_Left);
|
||||||
notificationBig.setTextViewText(R.id.rightPodText, podText_Right);
|
notification.setTextViewText(R.id.rightPodText, podText_Right);
|
||||||
notificationBig.setTextViewText(R.id.podCaseText, podText_Case);
|
notification.setTextViewText(R.id.podCaseText, podText_Case);
|
||||||
|
|
||||||
notificationSmall.setTextViewText(R.id.leftPodText, podText_Left);
|
notification.setImageViewResource(R.id.leftBatImg, chargeL ? R.drawable.ic_battery_charging_full_green_24dp : R.drawable.ic_battery_alert_red_24dp);
|
||||||
notificationSmall.setTextViewText(R.id.rightPodText, podText_Right);
|
notification.setImageViewResource(R.id.rightBatImg, chargeR ? R.drawable.ic_battery_charging_full_green_24dp : R.drawable.ic_battery_alert_red_24dp);
|
||||||
notificationSmall.setTextViewText(R.id.podCaseText, podText_Case);
|
notification.setImageViewResource(R.id.caseBatImg, chargeCase ? R.drawable.ic_battery_charging_full_green_24dp : R.drawable.ic_battery_alert_red_24dp);
|
||||||
|
|
||||||
notificationBig.setImageViewResource(R.id.leftBatImg, chargeL ? R.drawable.ic_battery_charging_full_green_24dp : R.drawable.ic_battery_alert_red_24dp);
|
notification.setViewVisibility(R.id.leftBatImg, ((chargeL && leftStatus <= 10) || (leftStatus <= 1) ? View.VISIBLE : View.GONE));
|
||||||
notificationBig.setImageViewResource(R.id.rightBatImg, chargeR ? R.drawable.ic_battery_charging_full_green_24dp : R.drawable.ic_battery_alert_red_24dp);
|
notification.setViewVisibility(R.id.rightBatImg, ((chargeR && rightStatus <= 10) || (rightStatus <= 1) ? View.VISIBLE : View.GONE));
|
||||||
notificationBig.setImageViewResource(R.id.caseBatImg, chargeCase ? R.drawable.ic_battery_charging_full_green_24dp : R.drawable.ic_battery_alert_red_24dp);
|
notification.setViewVisibility(R.id.caseBatImg, ((chargeCase && caseStatus <= 10) || (caseStatus <= 1) ? View.VISIBLE : View.GONE));
|
||||||
|
|
||||||
notificationSmall.setImageViewResource(R.id.leftBatImg, chargeL ? R.drawable.ic_battery_charging_full_green_24dp : R.drawable.ic_battery_alert_red_24dp);
|
notification.setViewVisibility(R.id.leftInEarImg, inEarL ? View.VISIBLE : View.INVISIBLE);
|
||||||
notificationSmall.setImageViewResource(R.id.rightBatImg, chargeR ? R.drawable.ic_battery_charging_full_green_24dp : R.drawable.ic_battery_alert_red_24dp);
|
notification.setViewVisibility(R.id.rightInEarImg, inEarR ? View.VISIBLE : View.INVISIBLE);
|
||||||
notificationSmall.setImageViewResource(R.id.caseBatImg, chargeCase ? R.drawable.ic_battery_charging_full_green_24dp : R.drawable.ic_battery_alert_red_24dp);
|
}
|
||||||
|
else for (RemoteViews notification : notificationArr) {
|
||||||
notificationBig.setViewVisibility(R.id.leftBatImg, ((chargeL && leftStatus <= 10) || (leftStatus <= 1) ? View.VISIBLE : View.GONE));
|
notification.setViewVisibility(R.id.leftPodText, View.INVISIBLE);
|
||||||
notificationBig.setViewVisibility(R.id.rightBatImg, ((chargeR && rightStatus <= 10) || (rightStatus <= 1) ? View.VISIBLE : View.GONE));
|
notification.setViewVisibility(R.id.rightPodText, View.INVISIBLE);
|
||||||
notificationBig.setViewVisibility(R.id.caseBatImg, ((chargeCase && caseStatus <= 10) || (caseStatus <= 1) ? View.VISIBLE : View.GONE));
|
notification.setViewVisibility(R.id.podCaseText, View.INVISIBLE);
|
||||||
|
notification.setViewVisibility(R.id.leftBatImg, View.GONE);
|
||||||
notificationSmall.setViewVisibility(R.id.leftBatImg, ((chargeL && leftStatus <= 10) || (leftStatus <= 1) ? View.VISIBLE : View.GONE));
|
notification.setViewVisibility(R.id.rightBatImg, View.GONE);
|
||||||
notificationSmall.setViewVisibility(R.id.rightBatImg, ((chargeR && rightStatus <= 10) || (rightStatus <= 1) ? View.VISIBLE : View.GONE));
|
notification.setViewVisibility(R.id.caseBatImg, View.GONE);
|
||||||
notificationSmall.setViewVisibility(R.id.caseBatImg, ((chargeCase && caseStatus <= 10) || (caseStatus <= 1) ? View.VISIBLE : View.GONE));
|
notification.setViewVisibility(R.id.leftPodUpdating, View.VISIBLE);
|
||||||
} else {
|
notification.setViewVisibility(R.id.rightPodUpdating, View.VISIBLE);
|
||||||
notificationBig.setViewVisibility(R.id.leftPodText, View.INVISIBLE);
|
notification.setViewVisibility(R.id.podCaseUpdating, View.VISIBLE);
|
||||||
notificationBig.setViewVisibility(R.id.rightPodText, View.INVISIBLE);
|
notification.setViewVisibility(R.id.leftInEarImg, View.INVISIBLE);
|
||||||
notificationBig.setViewVisibility(R.id.podCaseText, View.INVISIBLE);
|
notification.setViewVisibility(R.id.rightInEarImg, View.INVISIBLE);
|
||||||
notificationBig.setViewVisibility(R.id.leftBatImg, View.GONE);
|
|
||||||
notificationBig.setViewVisibility(R.id.rightBatImg, View.GONE);
|
|
||||||
notificationBig.setViewVisibility(R.id.caseBatImg, View.GONE);
|
|
||||||
notificationBig.setViewVisibility(R.id.leftPodUpdating, View.VISIBLE);
|
|
||||||
notificationBig.setViewVisibility(R.id.rightPodUpdating, View.VISIBLE);
|
|
||||||
notificationBig.setViewVisibility(R.id.podCaseUpdating, View.VISIBLE);
|
|
||||||
|
|
||||||
notificationSmall.setViewVisibility(R.id.leftPodText, View.INVISIBLE);
|
|
||||||
notificationSmall.setViewVisibility(R.id.rightPodText, View.INVISIBLE);
|
|
||||||
notificationSmall.setViewVisibility(R.id.podCaseText, View.INVISIBLE);
|
|
||||||
notificationSmall.setViewVisibility(R.id.leftBatImg, View.GONE);
|
|
||||||
notificationSmall.setViewVisibility(R.id.rightBatImg, View.GONE);
|
|
||||||
notificationSmall.setViewVisibility(R.id.caseBatImg, View.GONE);
|
|
||||||
notificationSmall.setViewVisibility(R.id.leftPodUpdating, View.VISIBLE);
|
|
||||||
notificationSmall.setViewVisibility(R.id.rightPodUpdating, View.VISIBLE);
|
|
||||||
notificationSmall.setViewVisibility(R.id.podCaseUpdating, View.VISIBLE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -448,6 +391,7 @@ public class PodsService extends Service {
|
|||||||
|
|
||||||
if ((compat == null ? 0 : (compat.hashCode()) ^ 0x43700437) == 0x82e89606) return;
|
if ((compat == null ? 0 : (compat.hashCode()) ^ 0x43700437) == 0x82e89606) return;
|
||||||
try {
|
try {
|
||||||
|
//noinspection BusyWait
|
||||||
Thread.sleep(1000);
|
Thread.sleep(1000);
|
||||||
} catch (InterruptedException ignored) {
|
} catch (InterruptedException ignored) {
|
||||||
}
|
}
|
||||||
@@ -503,8 +447,7 @@ public class PodsService extends Service {
|
|||||||
|
|
||||||
// Bluetooth turned off, stop scanner and remove notification.
|
// Bluetooth turned off, stop scanner and remove notification.
|
||||||
if (state == BluetoothAdapter.STATE_OFF || state == BluetoothAdapter.STATE_TURNING_OFF) {
|
if (state == BluetoothAdapter.STATE_OFF || state == BluetoothAdapter.STATE_TURNING_OFF) {
|
||||||
if (ENABLE_LOGGING)
|
OpenPodsDebugLog("BT OFF");
|
||||||
Log.d(TAG, "BT OFF");
|
|
||||||
maybeConnected = false;
|
maybeConnected = false;
|
||||||
stopAirPodsScanner();
|
stopAirPodsScanner();
|
||||||
recentBeacons.clear();
|
recentBeacons.clear();
|
||||||
@@ -512,8 +455,7 @@ public class PodsService extends Service {
|
|||||||
|
|
||||||
// Bluetooth turned on, start/restart scanner.
|
// Bluetooth turned on, start/restart scanner.
|
||||||
if (state == BluetoothAdapter.STATE_ON) {
|
if (state == BluetoothAdapter.STATE_ON) {
|
||||||
if (ENABLE_LOGGING)
|
OpenPodsDebugLog("BT ON");
|
||||||
Log.d(TAG, "BT ON");
|
|
||||||
startAirPodsScanner();
|
startAirPodsScanner();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -522,15 +464,13 @@ public class PodsService extends Service {
|
|||||||
if (bluetoothDevice != null && !action.isEmpty() && checkUUID(bluetoothDevice)) {
|
if (bluetoothDevice != null && !action.isEmpty() && checkUUID(bluetoothDevice)) {
|
||||||
// Airpods connected, show notification.
|
// Airpods connected, show notification.
|
||||||
if (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)) {
|
if (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)) {
|
||||||
if (ENABLE_LOGGING)
|
OpenPodsDebugLog("ACL CONNECTED");
|
||||||
Log.d(TAG, "ACL CONNECTED");
|
|
||||||
maybeConnected = true;
|
maybeConnected = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Airpods disconnected, remove notification but leave the scanner going.
|
// Airpods disconnected, remove notification but leave the scanner going.
|
||||||
if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED) || action.equals(BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED)) {
|
if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED) || action.equals(BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED)) {
|
||||||
if (ENABLE_LOGGING)
|
OpenPodsDebugLog("ACL DISCONNECTED");
|
||||||
Log.d(TAG, "ACL DISCONNECTED");
|
|
||||||
maybeConnected = false;
|
maybeConnected = false;
|
||||||
recentBeacons.clear();
|
recentBeacons.clear();
|
||||||
}
|
}
|
||||||
@@ -550,15 +490,13 @@ public class PodsService extends Service {
|
|||||||
@Override
|
@Override
|
||||||
public void onServiceConnected (int i, BluetoothProfile bluetoothProfile) {
|
public void onServiceConnected (int i, BluetoothProfile bluetoothProfile) {
|
||||||
if (i == BluetoothProfile.HEADSET) {
|
if (i == BluetoothProfile.HEADSET) {
|
||||||
if (ENABLE_LOGGING)
|
OpenPodsDebugLog("BT PROXY SERVICE CONNECTED ");
|
||||||
Log.d(TAG, "BT PROXY SERVICE CONNECTED");
|
|
||||||
|
|
||||||
BluetoothHeadset h = (BluetoothHeadset)bluetoothProfile;
|
BluetoothHeadset h = (BluetoothHeadset)bluetoothProfile;
|
||||||
|
|
||||||
for (BluetoothDevice d : h.getConnectedDevices())
|
for (BluetoothDevice d : h.getConnectedDevices())
|
||||||
if (checkUUID(d)) {
|
if (checkUUID(d)) {
|
||||||
if (ENABLE_LOGGING)
|
OpenPodsDebugLog("BT PROXY: AIRPODS ALREADY CONNECTED");
|
||||||
Log.d(TAG, "BT PROXY: AIRPODS ALREADY CONNECTED");
|
|
||||||
maybeConnected = true;
|
maybeConnected = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -568,8 +506,7 @@ public class PodsService extends Service {
|
|||||||
@Override
|
@Override
|
||||||
public void onServiceDisconnected (int i) {
|
public void onServiceDisconnected (int i) {
|
||||||
if (i == BluetoothProfile.HEADSET) {
|
if (i == BluetoothProfile.HEADSET) {
|
||||||
if (ENABLE_LOGGING)
|
OpenPodsDebugLog("BT PROXY SERVICE DISCONNECTED ");
|
||||||
Log.d(TAG, "BT PROXY SERVICE DISCONNECTED ");
|
|
||||||
maybeConnected = false;
|
maybeConnected = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -594,12 +531,10 @@ public class PodsService extends Service {
|
|||||||
@Override
|
@Override
|
||||||
public void onReceive (Context context, Intent intent) {
|
public void onReceive (Context context, Intent intent) {
|
||||||
if (Objects.equals(intent.getAction(), Intent.ACTION_SCREEN_OFF)) {
|
if (Objects.equals(intent.getAction(), Intent.ACTION_SCREEN_OFF)) {
|
||||||
if (ENABLE_LOGGING)
|
OpenPodsDebugLog("SCREEN OFF");
|
||||||
Log.d(TAG, "SCREEN OFF");
|
|
||||||
stopAirPodsScanner();
|
stopAirPodsScanner();
|
||||||
} else if (Objects.equals(intent.getAction(), Intent.ACTION_SCREEN_ON)) {
|
} else if (Objects.equals(intent.getAction(), Intent.ACTION_SCREEN_ON)) {
|
||||||
if (ENABLE_LOGGING)
|
OpenPodsDebugLog("SCREEN ON");
|
||||||
Log.d(TAG, "SCREEN ON");
|
|
||||||
BluetoothAdapter ba = ((BluetoothManager)Objects.requireNonNull(getSystemService(Context.BLUETOOTH_SERVICE))).getAdapter();
|
BluetoothAdapter ba = ((BluetoothManager)Objects.requireNonNull(getSystemService(Context.BLUETOOTH_SERVICE))).getAdapter();
|
||||||
if (ba.isEnabled())
|
if (ba.isEnabled())
|
||||||
startAirPodsScanner();
|
startAirPodsScanner();
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package com.dosse.airpods;
|
|||||||
|
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:height="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"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:height="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"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="48dp"
|
android:width="48dp"
|
||||||
android:height="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:layout_marginBottom="6dp"
|
||||||
android:src="@drawable/pod" />
|
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
|
<TextView
|
||||||
android:id="@+id/leftPodText"
|
android:id="@+id/leftPodText"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
@@ -67,6 +76,16 @@
|
|||||||
android:scaleX="-1"
|
android:scaleX="-1"
|
||||||
android:src="@drawable/pod" />
|
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
|
<TextView
|
||||||
android:id="@+id/rightPodText"
|
android:id="@+id/rightPodText"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
|||||||
@@ -11,8 +11,8 @@
|
|||||||
<string name="miui_warning_continue">Continue</string>
|
<string name="miui_warning_continue">Continue</string>
|
||||||
|
|
||||||
<string name="noBtText">Il semble que cet appareil ne dispose pas de Bluetooth LE.</string>
|
<string name="noBtText">Il semble que cet appareil ne dispose pas de Bluetooth LE.</string>
|
||||||
<string name="locationDisabledText">Vous devez activer la localisation pour permettre aux OpenPod de lire l\'état de vous AirPods.</string>
|
<string name="locationDisabledText">Vous devez activer la localisation pour permettre aux OpenPod de lire l\'état de vos AirPods.</string>
|
||||||
<string name="supportedDevicesTitle"><u>Appareils pris en charge:</u></string>
|
<string name="supportedDevicesTitle"><u>Appareils pris en charge :</u></string>
|
||||||
<string name="supportedDevicesItems">• Apple AirPods 1re génération\n• Apple AirPods 2e génération\n• Apple AirPods Pro</string>
|
<string name="supportedDevicesItems">• Apple AirPods 1re génération\n• Apple AirPods 2e génération\n• Apple AirPods Pro</string>
|
||||||
|
|
||||||
<string name="batterySaver">Économiseur de batterie (non recommandé)</string>
|
<string name="batterySaver">Économiseur de batterie (non recommandé)</string>
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
<string name="hideClicked">L\'icône disparaîtra bientôt.</string>
|
<string name="hideClicked">L\'icône disparaîtra bientôt.</string>
|
||||||
<string name="about">À propos</string>
|
<string name="about">À propos</string>
|
||||||
|
|
||||||
<string name="about_dev">Developpé par Federico Dossena</string>
|
<string name="about_dev">Développé par Federico Dossena</string>
|
||||||
<string name="website">Site web</string>
|
<string name="website">Site web</string>
|
||||||
<string name="github">Github</string>
|
<string name="github">Github</string>
|
||||||
<string name="donate">Faire un don</string>
|
<string name="donate">Faire un don</string>
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
<string name="app_name" translatable="false">OpenPods</string>
|
||||||
|
|
||||||
|
<string name="intro1">이 앱을 통해 Android 기기에서 에어팟의 상태를 확인할 수 있습니다.</string>
|
||||||
|
<string name="intro2">작동하려면 Bluetooth LE(위치)를 사용할 수 있는 권한과 백그라운드에서 실행할 수 있는 권한이 필요합니다.</string>
|
||||||
|
<string name="introAllow">허용</string>
|
||||||
|
<string name="main_description">앱에 에어팟이 연결되면 알림이 표시됩니다.</string>
|
||||||
|
<string name="hide_message">이 앱을 숨길 수 있습니다.</string>
|
||||||
|
<string name="settings">설정</string>
|
||||||
|
<string name="miui_warning">MIUI 발견됨</string>
|
||||||
|
<string name="miui_warning_desc">이 장치에서 실행 중인 ROM에 알려진 호환성 문제가 있습니다. 계속할 수는 있지만 OpenPod가 제대로 작동하지 않을 수 있습니다.</string>
|
||||||
|
<string name="miui_warning_continue">계속</string>
|
||||||
|
|
||||||
|
<string name="noBtText">이 장치에는 Bluetooth LE가 없는 것 같습니다.</string>
|
||||||
|
<string name="locationDisabledText">OpenPod에서 상태를 읽을 수 있도록 하려면 위치를 허용해야 합니다.</string>
|
||||||
|
<string name="supportedDevicesTitle"><u>지원하는 기기:</u></string>
|
||||||
|
<string name="supportedDevicesItems">• 애플 에어팟 1세대\n• 애플 에어팟 2세대\n• 애플 에어팟 프로</string>
|
||||||
|
|
||||||
|
<string name="batterySaver">배터리 관리모드 (추천하지 않음)</string>
|
||||||
|
<string name="batterySaver_desc">Bluetooth에서 배터리를 많이 사용하는 경우 이 옵션을 활성화하세요.</string>
|
||||||
|
<string name="hide">앱 숨기기</string>
|
||||||
|
<string name="hide_desc">실행 프로그램에서 이 앱 숨기기</string>
|
||||||
|
<string name="hide_dialog" tools:ignore="MissingTranslation">정말 계속하시겠습니까?</string>
|
||||||
|
<string name="hide_dialog_desc" tools:ignore="MissingTranslation">정말 이 앱을 숨기시겠습니까? 앱을 다시 설치해야 기능을 해제할 수 있습니다.</string>
|
||||||
|
<string name="hide_dialog_button" tools:ignore="MissingTranslation">숨기기</string>
|
||||||
|
<string name="hideClicked">아이콘이 곧 사라집니다</string>
|
||||||
|
<string name="about">정보</string>
|
||||||
|
|
||||||
|
<string name="about_dev">개발자 : Federico Dossena</string>
|
||||||
|
<string name="website">웹사이트</string>
|
||||||
|
<string name="github">Github</string>
|
||||||
|
<string name="donate">후원하기</string>
|
||||||
|
<string name="fdroid" translatable="false">F-Droid</string>
|
||||||
|
</resources>
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
|
|
||||||
<style name="AppTheme" parent="Theme.AppCompat.DayNight.DarkActionBar">
|
<style name="AppTheme" parent="Theme.AppCompat.DayNight.DarkActionBar">
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ buildscript {
|
|||||||
|
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:4.0.1'
|
classpath 'com.android.tools.build:gradle:4.1.2'
|
||||||
|
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
// in the individual module build.gradle files
|
// in the individual module build.gradle files
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
#Mon Jun 08 20:04:20 IDT 2020
|
#Mon Nov 30 15:19:04 IST 2020
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5.1-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
|
||||||
|
|||||||
Reference in New Issue
Block a user