23 Commits
Author SHA1 Message Date
dosse91 952c11ba21 Fixed typo 2019-08-18 12:17:21 +02:00
dosse91 9bea2c5e6e Fixed nagging lint 2019-08-18 12:05:47 +02:00
dosse91 88c344d4ff Updated README.md 2019-08-18 12:02:16 +02:00
dosse91 6f3b43e84c Updated to latest gradle, android studio, ... 2019-08-18 11:58:12 +02:00
dosse91 8a41df7c51 Fixed a bug where the app thought location was disabled but was actually set to device only; Fixed rare bug where the app wouldn't start on boot; Lowered Target SDK to 23 so the app cannot be uploaded to Google Play; Bumped version to Alpha 9 2019-08-17 10:12:46 +02:00
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
29 changed files with 128 additions and 69 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 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>
+6 -3
View File
@@ -5,9 +5,9 @@ android {
defaultConfig {
applicationId "com.dosse.airpods"
minSdkVersion 23
targetSdkVersion 28
versionCode 5
versionName '0.5'
targetSdkVersion 23
versionCode 9
versionName '0.9'
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
@@ -18,6 +18,9 @@ android {
}
productFlavors {
}
lintOptions {
abortOnError false
}
}
dependencies {
@@ -7,6 +7,7 @@
<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.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-feature
android:name="android.hardware.bluetooth_le"
@@ -38,7 +38,7 @@ public class MainActivity extends AppCompatActivity {
}catch(Throwable t){}
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) ok=false;
if(ok){
startService(new Intent(getApplicationContext(),PodsService.class));
Starter.startPodsService(getApplicationContext());
}else{
Intent i=new Intent(this,IntroActivity.class);
startActivity(i);
@@ -11,7 +11,10 @@ import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.le.BluetoothLeScanner;
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.ScanSettings;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -26,8 +29,9 @@ import android.support.v4.app.NotificationCompat;
import android.util.Log;
import android.view.View;
import android.widget.RemoteViews;
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:
@@ -78,7 +82,20 @@ public class PodsService extends Service {
btScanner = btAdapter.getBluetoothLeScanner();
if (btAdapter == null) throw new Exception("No BT");
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
public void onScanResult(int callbackType, ScanResult result) {
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(){
try{
if(btScanner!=null){
@@ -170,17 +202,20 @@ public class PodsService extends Service {
private static boolean maybeConnected =false;
private class NotificationThread extends Thread{
private boolean isLocationEnabled(){
LocationManager service = (LocationManager) getSystemService(LOCATION_SERVICE);
return service!=null&&service.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.P){
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);
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);
NotificationCompat.Builder mBuilder=new NotificationCompat.Builder(PodsService.this,TAG);
private NotificationManager mNotifyManager;
public NotificationThread(){
mNotifyManager=(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
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);
channel.enableVibration(false);
@@ -189,6 +224,14 @@ public class PodsService extends Service {
channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
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.setOngoing(true);
mBuilder.setSmallIcon(R.mipmap.notification_icon);
@@ -203,9 +246,9 @@ public class PodsService extends Service {
if(notificationShowing){
if(ENABLE_LOGGING) Log.d(TAG,"Removing notification");
notificationShowing=false;
mNotifyManager.cancel(1);
continue;
}
mNotifyManager.cancel(1);
}
if(isLocationEnabled()) {
mBuilder.setCustomContentView(notificationSmall);
@@ -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.PLAYING_STATE_CHANGED");
intentFilter.addCategory("android.bluetooth.headset.intent.category.companyid.76");
try{
unregisterReceiver(btReceiver);
}catch (Throwable t){}
btReceiver=new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -321,9 +367,6 @@ public class PodsService extends Service {
}
}
};
try{
unregisterReceiver(btReceiver);
}catch (Throwable t){}
try{
registerReceiver(btReceiver,intentFilter);
}catch(Throwable t){}
@@ -387,5 +430,4 @@ public class PodsService extends Service {
return START_STICKY;
}
}
@@ -3,6 +3,7 @@ package com.dosse.airpods;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.util.Log;
/**
@@ -11,6 +12,10 @@ import android.util.Log;
public class Starter extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
context.startService(new Intent(context,PodsService.class));
startPodsService(context);
}
public static final void startPodsService(Context context){
context.startService(new Intent(context, PodsService.class));
}
}
@@ -4,6 +4,7 @@
android:layout_height="wrap_content"
android:padding="12dp"
android:gravity="center"
android:background="@color/white"
>
@@ -30,6 +31,7 @@
android:gravity="center"
android:text="100%+"
android:id="@+id/leftPodText"
android:textColor="@color/black"
/>
<ProgressBar
@@ -65,6 +67,7 @@
android:gravity="center"
android:text="100%+"
android:id="@+id/rightPodText"
android:textColor="@color/black"
/>
<ProgressBar
@@ -100,6 +103,7 @@
android:gravity="center"
android:text="100%+"
android:id="@+id/podCaseText"
android:textColor="@color/black"
/>
<ProgressBar
@@ -4,6 +4,7 @@
android:layout_height="wrap_content"
android:padding="12dp"
android:gravity="center"
android:background="@color/white"
>
<RelativeLayout
@@ -29,6 +30,7 @@
android:gravity="center_vertical"
android:text="100%+"
android:id="@+id/leftPodText"
android:textColor="@color/black"
/>
<ProgressBar
@@ -65,6 +67,7 @@
android:gravity="center_vertical"
android:text="100%+"
android:id="@+id/rightPodText"
android:textColor="@color/black"
/>
<ProgressBar
@@ -101,6 +104,7 @@
android:gravity="center_vertical"
android:text="100%+"
android:id="@+id/podCaseText"
android:textColor="@color/black"
/>
<ProgressBar
Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 658 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 341 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

After

Width:  |  Height:  |  Size: 684 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

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>
@@ -8,6 +8,5 @@
<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>
<string name="supportedDevices">Dispositivos compatibles:\n&#8226; Apple AirPods 1st gen\n&#8226; Apple AirPods 2nd gen</string>
</resources>
@@ -9,5 +9,5 @@
<string name="hideClicked">L\'icona sarà rimossa presto</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</string>
<string name="supportedDevices">Dispositivi supportati:\n&#8226; Apple AirPods 1st gen\n&#8226; Apple AirPods 2nd gen</string>
</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="colorPrimaryDark">#0d47a1</color>
<color name="colorAccent">#448aff</color>
<color name="white">#ffffff</color>
<color name="black">#000000</color>
</resources>
+1 -1
View File
@@ -9,5 +9,5 @@
<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="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>
+1 -1
View File
@@ -7,7 +7,7 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:3.3.2'
classpath 'com.android.tools.build:gradle:3.4.2'
// NOTE: Do not place your application dependencies here; they belong
// 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
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
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
+9 -10
View File
@@ -1,11 +1,9 @@
![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
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)
@@ -16,20 +14,21 @@ The project is currently in Alpha.
## Supported devices
* Apple AirPods 1st gen
* Apple AirPods 2nd gen
## 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.
## DO NOT REUPLOAD TO GOOGLE PLAY
**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 with PayPal](https://www.paypal.me/sineisochronic)
## Screenshots
![Screenshot: notification](https://github.com/adolfintel/OpenPods/blob/master/.github/screen1.png?raw=true)
![Screenshot: notification](https://github.com/adolfintel/OpenPods/blob/master/.github/screen2.png?raw=true)
![Screenshot: permissions](https://github.com/adolfintel/OpenPods/blob/master/.github/screen3.png?raw=true)
![Screenshot: main app](https://github.com/adolfintel/OpenPods/blob/master/.github/screen4.png?raw=true)
![Screenshot: notification](fastlane/metadata/android/en-US/images/phoneScreenshots/screen1.png)
![Screenshot: notification](fastlane/metadata/android/en-US/images/phoneScreenshots/screen2.png)
![Screenshot: permissions](fastlane/metadata/android/en-US/images/phoneScreenshots/screen3.png)
![Screenshot: main app](fastlane/metadata/android/en-US/images/phoneScreenshots/screen4.png)
## License
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