11 Commits
13 changed files with 82 additions and 33 deletions
+6 -3
View File
@@ -5,9 +5,9 @@ android {
defaultConfig { defaultConfig {
applicationId "com.dosse.airpods" applicationId "com.dosse.airpods"
minSdkVersion 23 minSdkVersion 23
targetSdkVersion 28 targetSdkVersion 23
versionCode 7 versionCode 9
versionName '0.7' versionName '0.9'
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
} }
buildTypes { buildTypes {
@@ -18,6 +18,9 @@ android {
} }
productFlavors { productFlavors {
} }
lintOptions {
abortOnError false
}
} }
dependencies { dependencies {
@@ -7,6 +7,7 @@
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_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-feature <uses-feature
android:name="android.hardware.bluetooth_le" android:name="android.hardware.bluetooth_le"
@@ -38,7 +38,7 @@ public class MainActivity extends AppCompatActivity {
}catch(Throwable t){} }catch(Throwable t){}
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) ok=false; if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) ok=false;
if(ok){ if(ok){
startService(new Intent(getApplicationContext(),PodsService.class)); Starter.startPodsService(getApplicationContext());
}else{ }else{
Intent i=new Intent(this,IntroActivity.class); Intent i=new Intent(this,IntroActivity.class);
startActivity(i); startActivity(i);
@@ -11,7 +11,10 @@ import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothProfile;
import android.bluetooth.le.BluetoothLeScanner; import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback; import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanFilter.Builder;
import android.bluetooth.le.ScanResult; import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
@@ -26,8 +29,9 @@ import android.support.v4.app.NotificationCompat;
import android.util.Log; import android.util.Log;
import android.view.View; import android.view.View;
import android.widget.RemoteViews; import android.widget.RemoteViews;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/** /**
* This is the class that does most of the work. It has 3 functions: * This is the class that does most of the work. It has 3 functions:
@@ -78,7 +82,20 @@ public class PodsService extends Service {
btScanner = btAdapter.getBluetoothLeScanner(); btScanner = btAdapter.getBluetoothLeScanner();
if (btAdapter == null) throw new Exception("No BT"); if (btAdapter == null) throw new Exception("No BT");
if (!btAdapter.isEnabled()) throw new Exception("BT Off"); if (!btAdapter.isEnabled()) throw new Exception("BT Off");
btScanner.startScan(new ScanCallback() {
List<ScanFilter> filters = getScanFilters();
ScanSettings settings = new ScanSettings.Builder().setScanMode(2).setReportDelay(2).build();
btScanner.startScan(
filters,
settings,
new ScanCallback() {
@Override
public void onBatchScanResults(List<ScanResult> 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 {
@@ -128,6 +145,21 @@ public class PodsService extends Service {
} }
} }
private List<ScanFilter> getScanFilters() {
byte[] manufacturerData = new byte[27];
byte[] manufacturerDataMask = new byte[27];
manufacturerData[0] = 7;
manufacturerData[1] = 25;
manufacturerDataMask[0] = -1;
manufacturerDataMask[1] = -1;
Builder builder = new Builder();
builder.setManufacturerData(76, manufacturerData, manufacturerDataMask);
return Collections.singletonList(builder.build());
}
private void stopAirPodsScanner(){ private void stopAirPodsScanner(){
try{ try{
if(btScanner!=null){ if(btScanner!=null){
@@ -170,17 +202,20 @@ public class PodsService extends Service {
private static boolean maybeConnected =false; private static boolean maybeConnected =false;
private class NotificationThread extends Thread{ private class NotificationThread extends Thread{
private boolean isLocationEnabled(){ private boolean isLocationEnabled(){
LocationManager service = (LocationManager) getSystemService(LOCATION_SERVICE); if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.P){
return service!=null&&service.isProviderEnabled(LocationManager.NETWORK_PROVIDER); LocationManager service = (LocationManager) getSystemService(getApplicationContext().LOCATION_SERVICE);
return service!=null&&service.isLocationEnabled();
}else{
try {
return Settings.Secure.getInt(getContentResolver(), Settings.Secure.LOCATION_MODE) != Settings.Secure.LOCATION_MODE_OFF;
}catch(Throwable t){
return true;
} }
public void run(){ }
boolean notificationShowing=false; }
RemoteViews notificationBig=new RemoteViews(getPackageName(),R.layout.status_big); private NotificationManager mNotifyManager;
RemoteViews notificationSmall=new RemoteViews(getPackageName(),R.layout.status_small); public NotificationThread(){
RemoteViews locationDisabledBig=new RemoteViews(getPackageName(),R.layout.location_disabled_big); mNotifyManager=(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
RemoteViews locationDisabledSmall=new RemoteViews(getPackageName(),R.layout.location_disabled_small);
NotificationManager mNotifyManager=(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
NotificationCompat.Builder mBuilder=new NotificationCompat.Builder(PodsService.this,TAG);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { //on oreo and newer, create a notification channel if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { //on oreo and newer, create a notification channel
NotificationChannel channel = new NotificationChannel(TAG, TAG, NotificationManager.IMPORTANCE_LOW); NotificationChannel channel = new NotificationChannel(TAG, TAG, NotificationManager.IMPORTANCE_LOW);
channel.enableVibration(false); channel.enableVibration(false);
@@ -189,6 +224,14 @@ public class PodsService extends Service {
channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC); channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
mNotifyManager.createNotificationChannel(channel); mNotifyManager.createNotificationChannel(channel);
} }
}
public void run(){
boolean notificationShowing=false;
RemoteViews notificationBig=new RemoteViews(getPackageName(),R.layout.status_big);
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);
mBuilder.setShowWhen(false); mBuilder.setShowWhen(false);
mBuilder.setOngoing(true); mBuilder.setOngoing(true);
mBuilder.setSmallIcon(R.mipmap.notification_icon); mBuilder.setSmallIcon(R.mipmap.notification_icon);
@@ -290,6 +333,9 @@ public class PodsService extends Service {
intentFilter.addAction("android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED"); intentFilter.addAction("android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED");
intentFilter.addAction("android.bluetooth.a2dp.profile.action.PLAYING_STATE_CHANGED"); intentFilter.addAction("android.bluetooth.a2dp.profile.action.PLAYING_STATE_CHANGED");
intentFilter.addCategory("android.bluetooth.headset.intent.category.companyid.76"); intentFilter.addCategory("android.bluetooth.headset.intent.category.companyid.76");
try{
unregisterReceiver(btReceiver);
}catch (Throwable t){}
btReceiver=new BroadcastReceiver() { btReceiver=new BroadcastReceiver() {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
@@ -321,9 +367,6 @@ public class PodsService extends Service {
} }
} }
}; };
try{
unregisterReceiver(btReceiver);
}catch (Throwable t){}
try{ try{
registerReceiver(btReceiver,intentFilter); registerReceiver(btReceiver,intentFilter);
}catch(Throwable t){} }catch(Throwable t){}
@@ -387,5 +430,4 @@ public class PodsService extends Service {
return START_STICKY; return START_STICKY;
} }
} }
@@ -3,6 +3,7 @@ package com.dosse.airpods;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Build;
import android.util.Log; import android.util.Log;
/** /**
@@ -11,6 +12,10 @@ import android.util.Log;
public class Starter extends BroadcastReceiver { public class Starter extends BroadcastReceiver {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
startPodsService(context);
}
public static final void startPodsService(Context context){
context.startService(new Intent(context, PodsService.class)); context.startService(new Intent(context, PodsService.class));
} }
} }
@@ -9,5 +9,5 @@
<string name="hideClicked">Das Icon wird bald verschwinden</string> <string name="hideClicked">Das Icon wird bald verschwinden</string>
<string name="noBtText">Es sieht so aus als hätte dieses Gerät kein Bluetooth LE</string> <string name="noBtText">Es sieht so aus als hätte dieses Gerät kein Bluetooth LE</string>
<string name="locationDisabledText">Du musst Standortinformationen aktivieren, damit OpenPods den Status deiner AirPods auslesen kann</string> <string name="locationDisabledText">Du musst Standortinformationen aktivieren, damit OpenPods den Status deiner AirPods auslesen kann</string>
<string name="supportedDevices">Unterstützte Geräte:\n&#8226; Apple AirPods 1st gen</string> <string name="supportedDevices">Unterstützte Geräte:\n&#8226; Apple AirPods 1st gen\n&#8226; Apple AirPods 2nd gen</string>
</resources> </resources>
@@ -8,6 +8,5 @@
<string name="hideClicked">El ícono desaparecerá pronto</string> <string name="hideClicked">El ícono desaparecerá pronto</string>
<string name="noBtText">Parece que este dispositivo no tiene Bluetooth LE</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="locationDisabledText">Necesitas encender Localización para permitir que OpenPods lea el estado</string>
<string name="supportedDevices">Dispositivos compatibles:\n <string name="supportedDevices">Dispositivos compatibles:\n&#8226; Apple AirPods 1st gen\n&#8226; Apple AirPods 2nd gen</string>
&#8226; Apple AirPods 1st gen</string>
</resources> </resources>
@@ -9,5 +9,5 @@
<string name="hideClicked">L\'icona sarà rimossa presto</string> <string name="hideClicked">L\'icona sarà rimossa presto</string>
<string name="noBtText">Questo dispositivo non supporta Bluetooth LE</string> <string name="noBtText">Questo dispositivo non supporta Bluetooth LE</string>
<string name="locationDisabledText">Attiva la posizione per permttere a OpenPods di leggere lo stato</string> <string name="locationDisabledText">Attiva la posizione per permttere a OpenPods di leggere lo stato</string>
<string name="supportedDevices">Dispositivi supportati:\n&#8226; Apple AirPods 1st gen</string> <string name="supportedDevices">Dispositivi supportati:\n&#8226; Apple AirPods 1st gen\n&#8226; Apple AirPods 2nd gen</string>
</resources> </resources>
@@ -9,5 +9,5 @@
<string name="hideClicked">桌面图标很快就会隐藏掉</string> <string name="hideClicked">桌面图标很快就会隐藏掉</string>
<string name="noBtText">看起来您的设备并不支持蓝牙</string> <string name="noBtText">看起来您的设备并不支持蓝牙</string>
<string name="locationDisabledText">您需要开启定位来让 OpenPods 读取状态</string> <string name="locationDisabledText">您需要开启定位来让 OpenPods 读取状态</string>
<string name="supportedDevices">支持的设备:\n&#8226; Apple AirPods 一代</string> <string name="supportedDevices">支持的设备:\n&#8226; Apple AirPods 1st gen\n&#8226; Apple AirPods 2nd gen</string>
</resources> </resources>
+1 -1
View File
@@ -9,5 +9,5 @@
<string name="hideClicked">The icon will disappear soon</string> <string name="hideClicked">The icon will disappear soon</string>
<string name="noBtText">It looks like this device doesn\'t have Bluetooth LE</string> <string name="noBtText">It looks like this device doesn\'t have Bluetooth LE</string>
<string name="locationDisabledText">You need to turn on Location to allow OpenPods to read the status</string> <string name="locationDisabledText">You need to turn on Location to allow OpenPods to read the status</string>
<string name="supportedDevices">Supported devices:\n&#8226; Apple AirPods 1st gen</string> <string name="supportedDevices">Supported devices:\n&#8226; Apple AirPods 1st gen\n&#8226; Apple AirPods 2nd gen</string>
</resources> </resources>
+1 -1
View File
@@ -7,7 +7,7 @@ buildscript {
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.4.0' classpath 'com.android.tools.build:gradle:3.4.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
+4 -5
View File
@@ -4,8 +4,6 @@
The Free and Open Source app for monitoring your AirPods on Android The Free and Open Source app for monitoring your AirPods on Android
## Download ## Download
The project is currently in Alpha.
[Download from F-Droid](https://f-droid.org/repository/browse/?fdid=com.dosse.airpods) [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) [Download APK](https://downloads.fdossena.com/geth.php?r=openpods-apk)
@@ -16,11 +14,12 @@ The project is currently in Alpha.
## Supported devices ## Supported devices
* Apple AirPods 1st gen * Apple AirPods 1st gen
* Apple AirPods 2nd gen
## Project status ## DO NOT REUPLOAD TO GOOGLE PLAY
Right now, this project should be considered in alpha state. It was not extensively tested, it may not be stable or work at all on your device. **This app violates Google Play policies and is designed to break if you try to fix that unless you really know what you're doing.** You have been warned. I do NOT want this app to be on Google Play.
**Feedback is appreciated** I had to do this because several people were redistributing modified proprietary versions of this app on Google Play in violation of the GNU GPLv3 license.
## Donate ## Donate
[Donate with PayPal](https://www.paypal.me/sineisochronic) [Donate with PayPal](https://www.paypal.me/sineisochronic)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 63 KiB