Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
952c11ba21 | ||
|
|
9bea2c5e6e | ||
|
|
88c344d4ff | ||
|
|
6f3b43e84c | ||
|
|
8a41df7c51 | ||
|
|
35a9d96398 | ||
|
|
298ce85990 | ||
|
|
18158d70b2 | ||
|
|
0f74a0607e | ||
|
|
3bd3ec74e7 | ||
|
|
5c39fb37f7 | ||
|
|
243a947110 | ||
|
|
e299e5e346 | ||
|
|
aaab15ee87 | ||
|
|
5fb7c3a39a | ||
|
|
6c65cb36f9 | ||
|
|
24963be8bd | ||
|
|
5a82530057 | ||
|
|
d6d8b3a586 | ||
|
|
ae4f6ea1af | ||
|
|
f771481a2d | ||
|
|
44580f2f2a | ||
|
|
c043ade2d9 | ||
|
|
4138d440b2 | ||
|
|
dcd886c84b | ||
|
|
9248bdcf96 |
|
Before Width: | Height: | Size: 59 KiB |
@@ -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>
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding" addBOMForNewFiles="with NO BOM" />
|
||||
</project>
|
||||
@@ -5,9 +5,9 @@ android {
|
||||
defaultConfig {
|
||||
applicationId "com.dosse.airpods"
|
||||
minSdkVersion 23
|
||||
targetSdkVersion 28
|
||||
versionCode 4
|
||||
versionName '0.4'
|
||||
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);
|
||||
|
||||
@@ -6,10 +6,15 @@ 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.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;
|
||||
@@ -24,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:
|
||||
@@ -53,7 +59,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 -70db
|
||||
* - Filter for signals stronger than -60db
|
||||
* - Decode...
|
||||
*
|
||||
* Decoding the beacon:
|
||||
@@ -76,13 +82,28 @@ 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 {
|
||||
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){
|
||||
@@ -93,7 +114,7 @@ public class PodsService extends Service {
|
||||
}
|
||||
if(strongestBeacon!=null&&strongestBeacon.getDevice().getAddress().equals(result.getDevice().getAddress())) strongestBeacon=result;
|
||||
result=strongestBeacon;
|
||||
if(result.getRssi()<-70) return;
|
||||
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)
|
||||
@@ -124,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){
|
||||
@@ -163,20 +199,23 @@ 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);
|
||||
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);
|
||||
@@ -185,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);
|
||||
@@ -199,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);
|
||||
@@ -286,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) {
|
||||
@@ -294,11 +344,13 @@ public class PodsService extends Service {
|
||||
if(action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)){
|
||||
int state= intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -315,13 +367,37 @@ public class PodsService extends Service {
|
||||
}
|
||||
}
|
||||
};
|
||||
try{
|
||||
unregisterReceiver(btReceiver);
|
||||
}catch (Throwable t){}
|
||||
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){
|
||||
@@ -354,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
|
||||
|
||||
|
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 658 B |
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 341 B |
|
Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 684 B |
|
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 1.4 KiB |
|
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• Apple AirPods 1st gen\n• 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
|
||||
• Apple AirPods 1st gen</string>
|
||||
<string name="supportedDevices">Dispositivos compatibles:\n• Apple AirPods 1st gen\n• 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• Apple AirPods 1st gen</string>
|
||||
<string name="supportedDevices">Dispositivi supportati:\n• Apple AirPods 1st gen\n• 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• Apple AirPods 1st gen\n• Apple AirPods 2nd gen</string>
|
||||
</resources>
|
||||
@@ -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>
|
||||
|
||||
@@ -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• Apple AirPods 1st gen</string>
|
||||
<string name="supportedDevices">Supported devices:\n• Apple AirPods 1st gen\n• Apple AirPods 2nd gen</string>
|
||||
</resources>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||

|
||||

|
||||
|
||||
# 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
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
## 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 |
|
After Width: | Height: | Size: 63 KiB |