15 Commits
Author SHA1 Message Date
adolfintel 4138d440b2 Release Alpha 5 2019-03-29 19:20:25 +01:00
adolfintel dcd886c84b Added more debug logging 2019-03-28 07:36:29 +01:00
adolfintel 9248bdcf96 Improved event handling and filtering (hopefully) 2019-03-27 20:05:07 +01:00
adolfintel 613f116b28 Release Alpha 4 2019-03-25 18:57:11 +01:00
adolfintel f06206f125 Fixed mipmaps for ic_launcher 2019-03-25 18:54:15 +01:00
adolfintel f421e0febf Fixed heug notification icon if phone has a notch 2019-03-25 18:49:21 +01:00
Federico Dossena 34be06663d Merge pull request #3 from Mauville/stringlate-es-9625
Added spanish translation
2019-03-24 20:52:59 +01:00
Federico Dossena 09af4a76e1 Forgot the ; 2019-03-24 18:50:40 +01:00
Mauville e233938851 Update strings.xml 2019-03-24 11:44:20 -06:00
Mauville 4ff0535a4e Updated es (Spanish) translation 2019-03-24 03:29:32 -05:00
adolfintel 7edc1a01ef Removed unused attribute in manifest 2019-03-23 16:37:06 +01:00
adolfintel 05451b5e88 Oof 2019-03-23 16:32:22 +01:00
adolfintel d4459ea3a3 Improvements to BLE scanner, event detections, decoding 2019-03-23 10:17:07 +01:00
adolfintel 8262db31d8 Fixed a typo 2019-03-21 08:16:46 +01:00
adolfintel 9e6ef70fab Added F-Droid link 2019-03-21 08:15:48 +01:00
15 changed files with 67 additions and 17 deletions
+2 -2
View File
@@ -6,8 +6,8 @@ android {
applicationId "com.dosse.airpods"
minSdkVersion 23
targetSdkVersion 28
versionCode 3
versionName '0.3'
versionCode 5
versionName '0.5'
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
@@ -45,7 +45,6 @@
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
@@ -1,11 +1,14 @@
package com.dosse.airpods;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Service;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanResult;
@@ -52,6 +55,7 @@ public class PodsService extends Service {
* What we did to workaround this issue is this:
* - When a beacon arrives that looks like a pair of AirPods, look at the other beacons received in the last 10 seconds and get the strongest one
* - If the strongest beacon's fake address is the same as this, use this beacon; otherwise use the strongest beacon
* - Filter for signals stronger than -60db
* - Decode...
*
* Decoding the beacon:
@@ -81,6 +85,8 @@ public class PodsService extends Service {
byte[] data = result.getScanRecord().getManufacturerSpecificData(76);
if (data == null||data.length!=27) return;
recentBeacons.add(result);
if(ENABLE_LOGGING) Log.d(TAG,""+result.getRssi()+"db");
if(ENABLE_LOGGING) Log.d(TAG, decodeHex(data));
ScanResult strongestBeacon=null;
for(int i=0;i<recentBeacons.size();i++){
if(SystemClock.elapsedRealtimeNanos()-recentBeacons.get(i).getTimestampNanos()>RECENT_BEACONS_MAX_T_NS){
@@ -91,6 +97,7 @@ public class PodsService extends Service {
}
if(strongestBeacon!=null&&strongestBeacon.getDevice().getAddress().equals(result.getDevice().getAddress())) strongestBeacon=result;
result=strongestBeacon;
if(result.getRssi()<-60) return;
String a=decodeHex(result.getScanRecord().getManufacturerSpecificData(76));
String str = ""; //left airpod (0-10 batt; 15=disconnected)
String str2 = ""; //right airpod (0-10 batt; 15=disconnected)
@@ -160,7 +167,7 @@ public class PodsService extends Service {
private static final String TAG="AirPods";
private static long lastSeenConnected=0;
private static final long TIMEOUT_CONNECTED=30000;
private static boolean maybeConnected =true;
private static boolean maybeConnected =false;
private class NotificationThread extends Thread{
private boolean isLocationEnabled(){
LocationManager service = (LocationManager) getSystemService(LOCATION_SERVICE);
@@ -179,13 +186,14 @@ public class PodsService extends Service {
channel.enableVibration(false);
channel.enableLights(false);
channel.setShowBadge(true);
channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
mNotifyManager.createNotificationChannel(channel);
}
mBuilder.setShowWhen(false);
mBuilder.setOngoing(true);
mBuilder.setSmallIcon(R.drawable.left_pod);
mBuilder.setSmallIcon(R.mipmap.notification_icon);
for(;;){
if(maybeConnected &&!(leftStatus==15&&rightStatus==15&&caseStatus==15)){
if(maybeConnected &&!(leftStatus==15&&rightStatus==15&&caseStatus==15)/*&&System.currentTimeMillis()-lastSeenConnected<TIMEOUT_CONNECTED*/){
if(!notificationShowing){
if(ENABLE_LOGGING) Log.d(TAG,"Creating notification");
notificationShowing=true;
@@ -196,6 +204,7 @@ public class PodsService extends Service {
if(ENABLE_LOGGING) Log.d(TAG,"Removing notification");
notificationShowing=false;
mNotifyManager.cancel(1);
continue;
}
}
if(isLocationEnabled()) {
@@ -223,12 +232,12 @@ public class PodsService extends Service {
notificationSmall.setViewVisibility(R.id.leftPodUpdating, View.INVISIBLE);
notificationSmall.setViewVisibility(R.id.rightPodUpdating, View.INVISIBLE);
notificationSmall.setViewVisibility(R.id.podCaseUpdating, View.INVISIBLE);
notificationBig.setTextViewText(R.id.leftPodText, (leftStatus <= 10 ? (leftStatus * 10) + "%" : "") + ((chargeL && leftStatus < 10) ? "+" : ""));
notificationBig.setTextViewText(R.id.rightPodText, (rightStatus <= 10 ? (rightStatus * 10) + "%" : "") + ((chargeR && rightStatus < 10) ? "+" : ""));
notificationBig.setTextViewText(R.id.podCaseText, (caseStatus <= 10 ? (caseStatus * 10) + "%" : "") + (chargeCase ? "+" : ""));
notificationSmall.setTextViewText(R.id.leftPodText, (leftStatus <= 10 ? (leftStatus * 10) + "%" : "") + (chargeL ? "+" : ""));
notificationSmall.setTextViewText(R.id.rightPodText, (rightStatus <= 10 ? (rightStatus * 10) + "%" : "") + (chargeR ? "+" : ""));
notificationSmall.setTextViewText(R.id.podCaseText, (caseStatus <= 10 ? (caseStatus * 10) + "%" : "") + (chargeCase ? "+" : ""));
notificationBig.setTextViewText(R.id.leftPodText, (leftStatus==10?"100%":leftStatus<10?((leftStatus+1)*10+"%"):"") + ((chargeL && leftStatus < 10) ? "+" : ""));
notificationBig.setTextViewText(R.id.rightPodText, (rightStatus==10?"100%":rightStatus<10?((rightStatus+1)*10+"%"):"") + ((chargeR && rightStatus < 10) ? "+" : ""));
notificationBig.setTextViewText(R.id.podCaseText, (caseStatus==10?"100%":caseStatus<10?((caseStatus+1)*10+"%"):"") + ((chargeCase && caseStatus < 10) ? "+" : ""));
notificationSmall.setTextViewText(R.id.leftPodText, (leftStatus==10?"100%":leftStatus<10?((leftStatus+1)*10+"%"):"") + ((chargeL && leftStatus < 10) ? "+" : ""));
notificationSmall.setTextViewText(R.id.rightPodText, (rightStatus==10?"100%":rightStatus<10?((rightStatus+1)*10+"%"):"") + ((chargeR && rightStatus < 10) ? "+" : ""));
notificationSmall.setTextViewText(R.id.podCaseText, (caseStatus==10?"100%":caseStatus<10?((caseStatus+1)*10+"%"):"") + ((chargeCase && caseStatus < 10) ? "+" : ""));
}else{
notificationBig.setViewVisibility(R.id.leftPodText, View.INVISIBLE);
notificationBig.setViewVisibility(R.id.rightPodText, View.INVISIBLE);
@@ -288,21 +297,23 @@ public class PodsService extends Service {
String action = intent.getAction();
if(action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)){
int state= intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
if(state==BluetoothAdapter.STATE_OFF){ //bluetooth turned off, stop scanner and remove notification
if(state==BluetoothAdapter.STATE_OFF||state==BluetoothAdapter.STATE_TURNING_OFF){ //bluetooth turned off, stop scanner and remove notification
if(ENABLE_LOGGING) Log.d(TAG,"BT OFF");
maybeConnected =false;
stopAirPodsScanner();
recentBeacons.clear();
}
if(state==BluetoothAdapter.STATE_ON){ //bluetooth turned on, start/restart scanner
if(ENABLE_LOGGING) Log.d(TAG,"BT ON");
startAirPodsScanner();
}
}
if (bluetoothDevice != null && action != null && !action.isEmpty()&&checkUUID(bluetoothDevice)){ //airpods filter
if(action.equals("android.bluetooth.device.action.ACL_CONNECTED")){ //airpods connected, show notification
if(action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)){ //airpods connected, show notification
if(ENABLE_LOGGING) Log.d(TAG,"ACL CONNECTED");
maybeConnected =true;
}
if(action.equals("android.bluetooth.device.action.ACL_DISCONNECTED")){ //airpods disconnected, remove notification but leave the scanner going
if(action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)||action.equals(BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED)){ //airpods disconnected, remove notification but leave the scanner going
if(ENABLE_LOGGING) Log.d(TAG,"ACL DISCONNECTED");
maybeConnected =false;
recentBeacons.clear();
@@ -316,7 +327,34 @@ public class PodsService extends Service {
try{
registerReceiver(btReceiver,intentFilter);
}catch(Throwable t){}
if(((BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE)).getAdapter().isEnabled())startAirPodsScanner(); //if BT is already on when the app is started, start the scanner without waiting for an event to happen
//this BT Profile Proxy allows us to know if airpods are already connected when the app is started. It also fires an event when BT is turned off, in case the BroadcastReceiver doesn't do its job
BluetoothAdapter ba=((BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE)).getAdapter();
ba.getProfileProxy(getApplicationContext(), new BluetoothProfile.ServiceListener() {
@Override
public void onServiceConnected(int i, BluetoothProfile bluetoothProfile) {
if(i==BluetoothProfile.HEADSET){
if(ENABLE_LOGGING) Log.d(TAG,"BT PROXY SERVICE CONNECTED");
BluetoothHeadset h=(BluetoothHeadset)bluetoothProfile;
for(BluetoothDevice d:h.getConnectedDevices()){
if(checkUUID(d)){
if(ENABLE_LOGGING) Log.d(TAG,"BT PROXY: AIRPODS ALREADY CONNECTED");
maybeConnected=true;
break;
}
}
}
}
@Override
public void onServiceDisconnected(int i) {
if(i==BluetoothProfile.HEADSET){
if(ENABLE_LOGGING) Log.d(TAG,"BT PROXY SERVICE DISCONNECTED ");
maybeConnected=false;
}
}
},BluetoothProfile.HEADSET);
if(ba.isEnabled())startAirPodsScanner(); //if BT is already on when the app is started, start the scanner without waiting for an event to happen
}
private boolean checkUUID(BluetoothDevice bluetoothDevice){
Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

@@ -0,0 +1,13 @@
<resources>
<string name="intro1">Esta aplicación te permite ver el estado de tus AirPods en cualquier dispositivo Android</string>
<string name="intro2">Para operar, necesitamos permiso para usar Bluetooth LE (Localización) y permiso para ejecutarnos en el fondo</string>
<string name="introAllow">Permitir</string>
<string name="main1">Esta aplicación mostrará una notificación cuando tus AirPods estén conectados</string>
<string name="main2">Puedes ocultar la app si gustas</string>
<string name="mainHide">Ocultar app</string>
<string name="hideClicked">El ícono desaparecerá pronto</string>
<string name="noBtText">Parece que este dispositivo no tiene Bluetooth LE</string>
<string name="locationDisabledText">Necesitas encender Localización para permitir que OpenPods lea el estado</string>
<string name="supportedDevices">Dispositivos compatibles:\n
&#8226; Apple AirPods 1st gen</string>
</resources>
+1 -1
View File
@@ -6,9 +6,9 @@ The Free and Open Source app for monitoring your AirPods on Android
## Download
The project is currently in Alpha.
[Download from F-Droid](https://f-droid.org/repository/browse/?fdid=com.dosse.airpods)
[Download APK](https://downloads.fdossena.com/geth.php?r=openpods-apk)
## Features
* Shows a notification with AirPods status when they are connected
* Respects your privacy