37 Commits
Author SHA1 Message Date
Federico Dossena 35a9d96398 2nd gen Airpods support, bumped version to Alpha 8
2nd gen Airpods support, bumped version to Alpha 8
2019-07-24 15:58:23 +02:00
Federico Dossena 298ce85990 Merge pull request #18 from maxtauro/Gen2Support
Added support for gen 2 airpods
2019-07-24 15:57:23 +02:00
dosse91 18158d70b2 Fixed a typo 2019-07-24 13:53:44 +02:00
dosse91 0f74a0607e Updated README.md and screenshots 2019-07-19 07:02:55 +02:00
dosse91 3bd3ec74e7 Added initial gen2 support 2019-07-19 06:59:27 +02:00
maxwelltauro 5c39fb37f7 Added support for gen 2 airpods 2019-07-18 22:34:41 -04:00
adolfintel 243a947110 Release Alpha 7 2019-05-16 16:29:01 +02:00
Federico Dossena e299e5e346 Changed notification icon
2 airpods as notification icon
2019-05-16 16:24:02 +02:00
2xlink aaab15ee87 2 airpods as notification icon 2019-05-16 16:19:46 +02:00
adolfintel 5fb7c3a39a Scaled notification icon; forced notification color scheme; Updated project to latest gradle 2019-05-14 18:08:46 +02:00
adolfintel 6c65cb36f9 Release Alpha 6 2019-04-12 19:38:58 +02:00
Federico Dossena 24963be8bd Merge pull request #12 from nichbar/master
Add chinese translation
2019-04-12 19:35:10 +02:00
nich 5a82530057 Add chinese translation 2019-04-12 22:24:58 +08:00
Federico Dossena d6d8b3a586 Merge pull request #11 from jplitza/metadata
Move screenshots to fastlane location
2019-04-12 07:11:23 +02:00
Jan-Philipp Litza ae4f6ea1af Move screenshots to fastlane location
This will make F-Droid find and display them.

References:
https://staging.f-droid.org/en/docs/All_About_Descriptions_Graphics_and_Screenshots/
https://docs.fastlane.tools/actions/supply/#images-and-screenshots
2019-04-11 20:09:15 +02:00
Federico Dossena f771481a2d Merge pull request #10 from jplitza/german
Add German translation
2019-04-11 19:41:32 +02:00
Jan-Philipp Litza 44580f2f2a Add German translation 2019-04-11 19:27:05 +02:00
adolfintel c043ade2d9 Added possible workaround for #7. More testing required 2019-04-07 10:07:51 +02:00
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
adolfintel 21db852661 Release Alpha 3 2019-03-18 12:10:38 +01:00
adolfintel c11e261bba Aesthetic changes 2019-03-18 12:07:15 +01:00
adolfintel cff007f161 Added warning if location disabled 2019-03-16 10:48:05 +01:00
adolfintel 18be725cf3 Improved looks of notification 2019-03-15 19:31:09 +01:00
36 changed files with 299 additions and 71 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

-29
View File
@@ -1,29 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<Objective-C-extensions>
<file>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" />
</file>
<class>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" />
</class>
<extensions>
<pair source="cpp" header="h" fileNamingConvention="NONE" />
<pair source="c" header="h" fileNamingConvention="NONE" />
</extensions>
</Objective-C-extensions>
</code_scheme>
</component>
+4
View File
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" addBOMForNewFiles="with NO BOM" />
</project>
+2 -2
View File
@@ -6,8 +6,8 @@ android {
applicationId "com.dosse.airpods" applicationId "com.dosse.airpods"
minSdkVersion 23 minSdkVersion 23
targetSdkVersion 28 targetSdkVersion 28
versionCode 2 versionCode 8
versionName '0.2' versionName '0.8'
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
} }
buildTypes { buildTypes {
@@ -45,7 +45,6 @@
<activity android:name=".MainActivity"> <activity android:name=".MainActivity">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
@@ -50,7 +50,7 @@ public class MainActivity extends AppCompatActivity {
PackageManager p = getPackageManager(); PackageManager p = getPackageManager();
p.setComponentEnabledSetting(new ComponentName(MainActivity.this,MainActivity.class), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP); p.setComponentEnabledSetting(new ComponentName(MainActivity.this,MainActivity.class), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
Toast.makeText(getApplicationContext(),getString(R.string.hideClicked), Toast.LENGTH_LONG).show(); Toast.makeText(getApplicationContext(),getString(R.string.hideClicked), Toast.LENGTH_LONG).show();
try{getApplicationContext().openFileOutput("hidden",MODE_PRIVATE).close();}catch(Throwable t){} finish();
} }
}); });
} }
@@ -1,27 +1,36 @@
package com.dosse.airpods; package com.dosse.airpods;
import android.app.Notification;
import android.app.NotificationChannel; import android.app.NotificationChannel;
import android.app.NotificationManager; import android.app.NotificationManager;
import android.app.Service; import android.app.Service;
import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothManager; import android.bluetooth.BluetoothManager;
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;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.location.LocationManager;
import android.os.Build; import android.os.Build;
import android.os.IBinder; import android.os.IBinder;
import android.os.ParcelUuid; import android.os.ParcelUuid;
import android.os.SystemClock; import android.os.SystemClock;
import android.support.v4.app.NotificationCompat; import android.support.v4.app.NotificationCompat;
import android.util.Log; import android.util.Log;
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:
@@ -49,6 +58,7 @@ public class PodsService extends Service {
* What we did to workaround this issue is this: * 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 * - 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 * - 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... * - Decode...
* *
* Decoding the beacon: * Decoding the beacon:
@@ -71,13 +81,28 @@ 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 {
byte[] data = result.getScanRecord().getManufacturerSpecificData(76); byte[] data = result.getScanRecord().getManufacturerSpecificData(76);
if (data == null||data.length!=27) return; if (data == null||data.length!=27) return;
recentBeacons.add(result); recentBeacons.add(result);
if(ENABLE_LOGGING) Log.d(TAG,""+result.getRssi()+"db");
if(ENABLE_LOGGING) 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){
@@ -88,6 +113,7 @@ public class PodsService extends Service {
} }
if(strongestBeacon!=null&&strongestBeacon.getDevice().getAddress().equals(result.getDevice().getAddress())) strongestBeacon=result; if(strongestBeacon!=null&&strongestBeacon.getDevice().getAddress().equals(result.getDevice().getAddress())) strongestBeacon=result;
result=strongestBeacon; result=strongestBeacon;
if(result.getRssi()<-60) return;
String a=decodeHex(result.getScanRecord().getManufacturerSpecificData(76)); String a=decodeHex(result.getScanRecord().getManufacturerSpecificData(76));
String str = ""; //left airpod (0-10 batt; 15=disconnected) String str = ""; //left airpod (0-10 batt; 15=disconnected)
String str2 = ""; //right airpod (0-10 batt; 15=disconnected) String str2 = ""; //right airpod (0-10 batt; 15=disconnected)
@@ -118,6 +144,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){
@@ -157,12 +198,18 @@ public class PodsService extends Service {
private static final String TAG="AirPods"; private static final String TAG="AirPods";
private static long lastSeenConnected=0; private static long lastSeenConnected=0;
private static final long TIMEOUT_CONNECTED=30000; private static final long TIMEOUT_CONNECTED=30000;
private static boolean maybeConnected =true; private static boolean maybeConnected =false;
private class NotificationThread extends Thread{ private class NotificationThread extends Thread{
private boolean isLocationEnabled(){
LocationManager service = (LocationManager) getSystemService(LOCATION_SERVICE);
return service!=null&&service.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
}
public void run(){ public void run(){
boolean notificationShowing=false; boolean notificationShowing=false;
RemoteViews notificationBig=new RemoteViews(getPackageName(),R.layout.status_big); RemoteViews notificationBig=new RemoteViews(getPackageName(),R.layout.status_big);
RemoteViews notificationSmall=new RemoteViews(getPackageName(),R.layout.status_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);
NotificationManager mNotifyManager=(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); NotificationManager mNotifyManager=(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
NotificationCompat.Builder mBuilder=new NotificationCompat.Builder(PodsService.this,TAG); 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
@@ -170,15 +217,14 @@ public class PodsService extends Service {
channel.enableVibration(false); channel.enableVibration(false);
channel.enableLights(false); channel.enableLights(false);
channel.setShowBadge(true); channel.setShowBadge(true);
channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
mNotifyManager.createNotificationChannel(channel); mNotifyManager.createNotificationChannel(channel);
} }
mBuilder.setCustomContentView(notificationSmall);
mBuilder.setCustomBigContentView(notificationBig);
mBuilder.setShowWhen(false); mBuilder.setShowWhen(false);
mBuilder.setOngoing(true); mBuilder.setOngoing(true);
mBuilder.setSmallIcon(R.drawable.left_pod); mBuilder.setSmallIcon(R.mipmap.notification_icon);
for(;;){ 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(!notificationShowing){
if(ENABLE_LOGGING) Log.d(TAG,"Creating notification"); if(ENABLE_LOGGING) Log.d(TAG,"Creating notification");
notificationShowing=true; notificationShowing=true;
@@ -188,8 +234,16 @@ public class PodsService extends Service {
if(notificationShowing){ if(notificationShowing){
if(ENABLE_LOGGING) Log.d(TAG,"Removing notification"); if(ENABLE_LOGGING) Log.d(TAG,"Removing notification");
notificationShowing=false; notificationShowing=false;
continue;
}
mNotifyManager.cancel(1); mNotifyManager.cancel(1);
} }
if(isLocationEnabled()) {
mBuilder.setCustomContentView(notificationSmall);
mBuilder.setCustomBigContentView(notificationBig);
}else{
mBuilder.setCustomContentView(locationDisabledSmall);
mBuilder.setCustomBigContentView(locationDisabledBig);
} }
if(notificationShowing){ if(notificationShowing){
if(ENABLE_LOGGING) Log.d(TAG,"Left: "+leftStatus+(chargeL?"+":"")+" "+"Right: "+rightStatus+(chargeR?"+":"")+" "+"Case: "+caseStatus+(chargeCase?"+":"")); if(ENABLE_LOGGING) Log.d(TAG,"Left: "+leftStatus+(chargeL?"+":"")+" "+"Right: "+rightStatus+(chargeR?"+":"")+" "+"Case: "+caseStatus+(chargeCase?"+":""));
@@ -197,19 +251,37 @@ public class PodsService extends Service {
notificationBig.setImageViewResource(R.id.rightPodImg,rightStatus<=10?R.drawable.right_pod:R.drawable.right_pod_disconnected); notificationBig.setImageViewResource(R.id.rightPodImg,rightStatus<=10?R.drawable.right_pod:R.drawable.right_pod_disconnected);
notificationBig.setImageViewResource(R.id.podCaseImg,caseStatus<=10?R.drawable.pod_case:R.drawable.pod_case_disconnected); notificationBig.setImageViewResource(R.id.podCaseImg,caseStatus<=10?R.drawable.pod_case:R.drawable.pod_case_disconnected);
if(System.currentTimeMillis()-lastSeenConnected<TIMEOUT_CONNECTED) { if(System.currentTimeMillis()-lastSeenConnected<TIMEOUT_CONNECTED) {
notificationBig.setTextViewText(R.id.leftPodText, (leftStatus <= 10 ? (leftStatus * 10) + "%" : "") + ((chargeL && leftStatus < 10) ? "+" : "")); notificationBig.setViewVisibility(R.id.leftPodText, View.VISIBLE);
notificationBig.setTextViewText(R.id.rightPodText, (rightStatus <= 10 ? (rightStatus * 10) + "%" : "") + ((chargeR && rightStatus < 10) ? "+" : "")); notificationBig.setViewVisibility(R.id.rightPodText, View.VISIBLE);
notificationBig.setTextViewText(R.id.podCaseText, (caseStatus <= 10 ? (caseStatus * 10) + "%" : "") + (chargeCase ? "+" : "")); notificationBig.setViewVisibility(R.id.podCaseText, View.VISIBLE);
notificationSmall.setTextViewText(R.id.leftPodText, (leftStatus <= 10 ? (leftStatus * 10) + "%" : "") + (chargeL ? "+" : "")); notificationBig.setViewVisibility(R.id.leftPodUpdating, View.INVISIBLE);
notificationSmall.setTextViewText(R.id.rightPodText, (rightStatus <= 10 ? (rightStatus * 10) + "%" : "") + (chargeR ? "+" : "")); notificationBig.setViewVisibility(R.id.rightPodUpdating, View.INVISIBLE);
notificationSmall.setTextViewText(R.id.podCaseText, (caseStatus <= 10 ? (caseStatus * 10) + "%" : "") + (chargeCase ? "+" : "")); notificationBig.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);
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{ }else{
notificationBig.setTextViewText(R.id.leftPodText, ""); notificationBig.setViewVisibility(R.id.leftPodText, View.INVISIBLE);
notificationBig.setTextViewText(R.id.rightPodText, ""); notificationBig.setViewVisibility(R.id.rightPodText, View.INVISIBLE);
notificationBig.setTextViewText(R.id.podCaseText, ""); notificationBig.setViewVisibility(R.id.podCaseText, View.INVISIBLE);
notificationSmall.setTextViewText(R.id.leftPodText, ""); notificationBig.setViewVisibility(R.id.leftPodUpdating, View.VISIBLE);
notificationSmall.setTextViewText(R.id.rightPodText, ""); notificationBig.setViewVisibility(R.id.rightPodUpdating, View.VISIBLE);
notificationSmall.setTextViewText(R.id.podCaseText, ""); 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.leftPodUpdating, View.VISIBLE);
notificationSmall.setViewVisibility(R.id.rightPodUpdating, View.VISIBLE);
notificationSmall.setViewVisibility(R.id.podCaseUpdating, View.VISIBLE);
} }
mNotifyManager.notify(1,mBuilder.build()); mNotifyManager.notify(1,mBuilder.build());
} }
@@ -256,21 +328,23 @@ public class PodsService extends Service {
String action = intent.getAction(); String action = intent.getAction();
if(action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)){ if(action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)){
int state= intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR); 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; maybeConnected =false;
stopAirPodsScanner(); stopAirPodsScanner();
recentBeacons.clear(); recentBeacons.clear();
} }
if(state==BluetoothAdapter.STATE_ON){ //bluetooth turned on, start/restart scanner if(state==BluetoothAdapter.STATE_ON){ //bluetooth turned on, start/restart scanner
if(ENABLE_LOGGING) Log.d(TAG,"BT ON");
startAirPodsScanner(); startAirPodsScanner();
} }
} }
if (bluetoothDevice != null && action != null && !action.isEmpty()&&checkUUID(bluetoothDevice)){ //airpods filter 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"); if(ENABLE_LOGGING) Log.d(TAG,"ACL CONNECTED");
maybeConnected =true; 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"); if(ENABLE_LOGGING) Log.d(TAG,"ACL DISCONNECTED");
maybeConnected =false; maybeConnected =false;
recentBeacons.clear(); recentBeacons.clear();
@@ -284,7 +358,34 @@ public class PodsService extends Service {
try{ try{
registerReceiver(btReceiver,intentFilter); registerReceiver(btReceiver,intentFilter);
}catch(Throwable t){} }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){ private boolean checkUUID(BluetoothDevice bluetoothDevice){
@@ -17,7 +17,7 @@
android:layout_height="120dp" android:layout_height="120dp"
android:src="@drawable/tick" android:src="@drawable/tick"
android:layout_alignParentTop="true" android:layout_alignParentTop="true"
android:id="@+id/mainTick" android:id="@+id/tick"
android:layout_marginBottom="24dp" android:layout_marginBottom="24dp"
/> />
<TextView <TextView
@@ -27,14 +27,21 @@
android:layout_marginBottom="24dp" android:layout_marginBottom="24dp"
android:text="@string/main1" android:text="@string/main1"
android:textAppearance="@android:style/TextAppearance.DeviceDefault.Large" android:textAppearance="@android:style/TextAppearance.DeviceDefault.Large"
android:layout_below="@id/mainTick" android:layout_below="@id/tick"
/> />
<TextView
android:id="@+id/supportedDevices"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="60dp"
android:layout_below="@id/main1"
android:text="@string/supportedDevices" />
<TextView <TextView
android:id="@+id/main2" android:id="@+id/main2"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="80dp" android:layout_marginBottom="24dp"
android:layout_below="@id/main1" android:layout_below="@id/supportedDevices"
android:text="@string/main2" /> android:text="@string/main2" />
<Button <Button
android:layout_width="wrap_content" android:layout_width="wrap_content"
@@ -44,6 +51,7 @@
android:id="@+id/mainHide" android:id="@+id/mainHide"
android:layout_alignParentEnd="true" android:layout_alignParentEnd="true"
/> />
</RelativeLayout> </RelativeLayout>
</RelativeLayout> </RelativeLayout>
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="120dp"
android:padding="12dp"
android:gravity="center"
>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="@string/locationDisabledText"
/>
</RelativeLayout>
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="70dp"
android:padding="12dp"
android:gravity="center"
>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="@string/locationDisabledText"
/>
</RelativeLayout>
@@ -4,8 +4,10 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:padding="12dp" android:padding="12dp"
android:gravity="center" android:gravity="center"
android:background="@color/white"
> >
<RelativeLayout <RelativeLayout
android:layout_width="60dp" android:layout_width="60dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -29,6 +31,17 @@
android:gravity="center" android:gravity="center"
android:text="100%+" android:text="100%+"
android:id="@+id/leftPodText" android:id="@+id/leftPodText"
android:textColor="@color/black"
/>
<ProgressBar
android:id="@+id/leftPodUpdating"
style="?android:attr/progressBarStyle"
android:layout_width="match_parent"
android:layout_height="18dp"
android:layout_below="@id/leftPodImg"
android:gravity="center"
android:indeterminate="true"
/> />
</RelativeLayout> </RelativeLayout>
@@ -54,6 +67,17 @@
android:gravity="center" android:gravity="center"
android:text="100%+" android:text="100%+"
android:id="@+id/rightPodText" android:id="@+id/rightPodText"
android:textColor="@color/black"
/>
<ProgressBar
android:id="@+id/rightPodUpdating"
style="?android:attr/progressBarStyle"
android:layout_width="match_parent"
android:layout_height="18dp"
android:layout_below="@id/rightPodImg"
android:gravity="center"
android:indeterminate="true"
/> />
</RelativeLayout> </RelativeLayout>
@@ -79,6 +103,17 @@
android:gravity="center" android:gravity="center"
android:text="100%+" android:text="100%+"
android:id="@+id/podCaseText" android:id="@+id/podCaseText"
android:textColor="@color/black"
/>
<ProgressBar
android:id="@+id/podCaseUpdating"
style="?android:attr/progressBarStyle"
android:layout_width="match_parent"
android:layout_height="18dp"
android:layout_below="@id/podCaseImg"
android:gravity="center"
android:indeterminate="true"
/> />
</RelativeLayout> </RelativeLayout>
@@ -4,6 +4,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:padding="12dp" android:padding="12dp"
android:gravity="center" android:gravity="center"
android:background="@color/white"
> >
<RelativeLayout <RelativeLayout
@@ -29,6 +30,16 @@
android:gravity="center_vertical" android:gravity="center_vertical"
android:text="100%+" android:text="100%+"
android:id="@+id/leftPodText" android:id="@+id/leftPodText"
android:textColor="@color/black"
/>
<ProgressBar
android:id="@+id/leftPodUpdating"
style="?android:attr/progressBarStyle"
android:layout_width="18dp"
android:layout_height="30dp"
android:layout_toRightOf="@id/leftPodImg"
android:indeterminate="true"
/> />
</RelativeLayout> </RelativeLayout>
@@ -56,6 +67,16 @@
android:gravity="center_vertical" android:gravity="center_vertical"
android:text="100%+" android:text="100%+"
android:id="@+id/rightPodText" android:id="@+id/rightPodText"
android:textColor="@color/black"
/>
<ProgressBar
android:id="@+id/rightPodUpdating"
style="?android:attr/progressBarStyle"
android:layout_width="18dp"
android:layout_height="30dp"
android:layout_toRightOf="@id/rightPodImg"
android:indeterminate="true"
/> />
</RelativeLayout> </RelativeLayout>
@@ -83,6 +104,16 @@
android:gravity="center_vertical" android:gravity="center_vertical"
android:text="100%+" android:text="100%+"
android:id="@+id/podCaseText" android:id="@+id/podCaseText"
android:textColor="@color/black"
/>
<ProgressBar
android:id="@+id/podCaseUpdating"
style="?android:attr/progressBarStyle"
android:layout_width="18dp"
android:layout_height="30dp"
android:layout_toRightOf="@id/podCaseImg"
android:indeterminate="true"
/> />
</RelativeLayout> </RelativeLayout>
Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 658 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 341 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 684 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="intro1">Diese App erlaubt es dir, den Status deiner AirPods auf einem beliebigen Android-Gerät zu überprüfen</string>
<string name="intro2">Um zu funktionieren, benötigen wir die Berechtigungen um Bluetooth LE (Standort) zu benutzen und im Hintergrund zu laufen</string>
<string name="introAllow">Erlauben</string>
<string name="main1">Die App zeigt eine Benachrichtigung wenn deine AirPods verbunden sind</string>
<string name="main2">Du kannst die App jetzt verstecken wenn du möchtest</string>
<string name="mainHide">App verstecken</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="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\n&#8226; Apple AirPods 2nd gen</string>
</resources>
@@ -0,0 +1,12 @@
<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\n&#8226; Apple AirPods 2nd gen</string>
</resources>
@@ -8,4 +8,6 @@
<string name="mainHide">Nascondi app</string> <string name="mainHide">Nascondi app</string>
<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="supportedDevices">Dispositivi supportati:\n&#8226; Apple AirPods 1st gen\n&#8226; Apple AirPods 2nd gen</string>
</resources> </resources>
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="intro1">此应用可以让您在任何安卓设备上查看 AirPods 的状态</string>
<string name="intro2">我们需要蓝牙、定位以及在后台运行的权限来正常运作</string>
<string name="introAllow">允许</string>
<string name="main1">当您的 AirPods 接入时,我们会在通知栏显示通知</string>
<string name="main2">您可以隐藏此应用</string>
<string name="mainHide">隐藏</string>
<string name="hideClicked">桌面图标很快就会隐藏掉</string>
<string name="noBtText">看起来您的设备并不支持蓝牙</string>
<string name="locationDisabledText">您需要开启定位来让 OpenPods 读取状态</string>
<string name="supportedDevices">支持的设备:\n&#8226; Apple AirPods 1st gen\n&#8226; Apple AirPods 2nd gen</string>
</resources>
+2 -1
View File
@@ -3,5 +3,6 @@
<color name="colorPrimary">#448aff</color> <color name="colorPrimary">#448aff</color>
<color name="colorPrimaryDark">#0d47a1</color> <color name="colorPrimaryDark">#0d47a1</color>
<color name="colorAccent">#448aff</color> <color name="colorAccent">#448aff</color>
<color name="white">#ffffff</color>
<color name="black">#000000</color>
</resources> </resources>
@@ -8,4 +8,6 @@
<string name="mainHide">Hide app</string> <string name="mainHide">Hide app</string>
<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="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.3.2' classpath 'com.android.tools.build:gradle:3.4.0'
// 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
View File
@@ -1,6 +1,6 @@
#Tue Mar 12 08:48:48 CET 2019 #Tue May 14 16:46:59 CEST 2019
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-4.10.1-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
+10 -6
View File
@@ -1,4 +1,4 @@
![OpenPods Logo](https://github.com/adolfintel/OpenPods/blob/master/.github/logo.png?raw=true) ![OpenPods Logo](fastlane/metadata/android/en-US/images/featureGraphic.png)
# OpenPods # OpenPods
The Free and Open Source app for monitoring your AirPods on Android The Free and Open Source app for monitoring your AirPods on Android
@@ -6,14 +6,18 @@ The Free and Open Source app for monitoring your AirPods on Android
## Download ## Download
The project is currently in Alpha. 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) [Download APK](https://downloads.fdossena.com/geth.php?r=openpods-apk)
## Features ## Features
* Shows a notification with AirPods status when they are connected * Shows a notification with AirPods status when they are connected
* Respects your privacy * Respects your privacy
* Free and Open Source software * Free and Open Source software
## Supported devices
* Apple AirPods 1st gen
* Apple AirPods 2nd gen
## Project status ## Project status
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. 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.
@@ -23,10 +27,10 @@ Right now, this project should be considered in alpha state. It was not extensiv
[Donate with PayPal](https://www.paypal.me/sineisochronic) [Donate with PayPal](https://www.paypal.me/sineisochronic)
## Screenshots ## Screenshots
![Screenshot: notification](https://github.com/adolfintel/OpenPods/blob/master/.github/screen1.png?raw=true) ![Screenshot: notification](fastlane/metadata/android/en-US/images/phoneScreenshots/screen1.png)
![Screenshot: notification](https://github.com/adolfintel/OpenPods/blob/master/.github/screen2.png?raw=true) ![Screenshot: notification](fastlane/metadata/android/en-US/images/phoneScreenshots/screen2.png)
![Screenshot: permissions](https://github.com/adolfintel/OpenPods/blob/master/.github/screen3.png?raw=true) ![Screenshot: permissions](fastlane/metadata/android/en-US/images/phoneScreenshots/screen3.png)
![Screenshot: main app](https://github.com/adolfintel/OpenPods/blob/master/.github/screen4.png?raw=true) ![Screenshot: main app](fastlane/metadata/android/en-US/images/phoneScreenshots/screen4.png)
## License ## License
Copyright (C) 2019 Federico Dossena Copyright (C) 2019 Federico Dossena

Before

Width:  |  Height:  |  Size: 111 KiB

After

Width:  |  Height:  |  Size: 111 KiB

Before

Width:  |  Height:  |  Size: 815 KiB

After

Width:  |  Height:  |  Size: 815 KiB

Before

Width:  |  Height:  |  Size: 994 KiB

After

Width:  |  Height:  |  Size: 994 KiB

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB