Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4138d440b2 | ||
|
|
dcd886c84b | ||
|
|
9248bdcf96 | ||
|
|
613f116b28 | ||
|
|
f06206f125 | ||
|
|
f421e0febf | ||
|
|
34be06663d | ||
|
|
09af4a76e1 | ||
|
|
e233938851 | ||
|
|
4ff0535a4e | ||
|
|
7edc1a01ef | ||
|
|
05451b5e88 | ||
|
|
d4459ea3a3 | ||
|
|
8262db31d8 | ||
|
|
9e6ef70fab | ||
|
|
21db852661 | ||
|
|
c11e261bba | ||
|
|
cff007f161 | ||
|
|
18be725cf3 |
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 59 KiB |
@@ -6,8 +6,8 @@ android {
|
||||
applicationId "com.dosse.airpods"
|
||||
minSdkVersion 23
|
||||
targetSdkVersion 28
|
||||
versionCode 2
|
||||
versionName '0.2'
|
||||
versionCode 5
|
||||
versionName '0.5'
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
buildTypes {
|
||||
|
||||
@@ -45,7 +45,6 @@
|
||||
<activity android:name=".MainActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
@@ -50,7 +50,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
PackageManager p = getPackageManager();
|
||||
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();
|
||||
try{getApplicationContext().openFileOutput("hidden",MODE_PRIVATE).close();}catch(Throwable t){}
|
||||
finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
package com.dosse.airpods;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.Service;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothHeadset;
|
||||
import android.bluetooth.BluetoothManager;
|
||||
import android.bluetooth.BluetoothProfile;
|
||||
import android.bluetooth.le.BluetoothLeScanner;
|
||||
import android.bluetooth.le.ScanCallback;
|
||||
import android.bluetooth.le.ScanResult;
|
||||
@@ -13,12 +16,15 @@ import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.location.LocationManager;
|
||||
import android.os.Build;
|
||||
import android.os.IBinder;
|
||||
import android.os.ParcelUuid;
|
||||
import android.os.SystemClock;
|
||||
import android.provider.Settings;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.RemoteViews;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -49,6 +55,7 @@ public class PodsService extends Service {
|
||||
* What we did to workaround this issue is this:
|
||||
* - When a beacon arrives that looks like a pair of AirPods, look at the other beacons received in the last 10 seconds and get the strongest one
|
||||
* - If the strongest beacon's fake address is the same as this, use this beacon; otherwise use the strongest beacon
|
||||
* - Filter for signals stronger than -60db
|
||||
* - Decode...
|
||||
*
|
||||
* Decoding the beacon:
|
||||
@@ -78,6 +85,8 @@ public class PodsService extends Service {
|
||||
byte[] data = result.getScanRecord().getManufacturerSpecificData(76);
|
||||
if (data == null||data.length!=27) return;
|
||||
recentBeacons.add(result);
|
||||
if(ENABLE_LOGGING) Log.d(TAG,""+result.getRssi()+"db");
|
||||
if(ENABLE_LOGGING) Log.d(TAG, decodeHex(data));
|
||||
ScanResult strongestBeacon=null;
|
||||
for(int i=0;i<recentBeacons.size();i++){
|
||||
if(SystemClock.elapsedRealtimeNanos()-recentBeacons.get(i).getTimestampNanos()>RECENT_BEACONS_MAX_T_NS){
|
||||
@@ -88,6 +97,7 @@ public class PodsService extends Service {
|
||||
}
|
||||
if(strongestBeacon!=null&&strongestBeacon.getDevice().getAddress().equals(result.getDevice().getAddress())) strongestBeacon=result;
|
||||
result=strongestBeacon;
|
||||
if(result.getRssi()<-60) return;
|
||||
String a=decodeHex(result.getScanRecord().getManufacturerSpecificData(76));
|
||||
String str = ""; //left airpod (0-10 batt; 15=disconnected)
|
||||
String str2 = ""; //right airpod (0-10 batt; 15=disconnected)
|
||||
@@ -157,12 +167,18 @@ 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);
|
||||
}
|
||||
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);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { //on oreo and newer, create a notification channel
|
||||
@@ -170,15 +186,14 @@ public class PodsService extends Service {
|
||||
channel.enableVibration(false);
|
||||
channel.enableLights(false);
|
||||
channel.setShowBadge(true);
|
||||
channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
|
||||
mNotifyManager.createNotificationChannel(channel);
|
||||
}
|
||||
mBuilder.setCustomContentView(notificationSmall);
|
||||
mBuilder.setCustomBigContentView(notificationBig);
|
||||
mBuilder.setShowWhen(false);
|
||||
mBuilder.setOngoing(true);
|
||||
mBuilder.setSmallIcon(R.drawable.left_pod);
|
||||
mBuilder.setSmallIcon(R.mipmap.notification_icon);
|
||||
for(;;){
|
||||
if(maybeConnected &&!(leftStatus==15&&rightStatus==15&&caseStatus==15)){
|
||||
if(maybeConnected &&!(leftStatus==15&&rightStatus==15&&caseStatus==15)/*&&System.currentTimeMillis()-lastSeenConnected<TIMEOUT_CONNECTED*/){
|
||||
if(!notificationShowing){
|
||||
if(ENABLE_LOGGING) Log.d(TAG,"Creating notification");
|
||||
notificationShowing=true;
|
||||
@@ -189,27 +204,53 @@ public class PodsService extends Service {
|
||||
if(ENABLE_LOGGING) Log.d(TAG,"Removing notification");
|
||||
notificationShowing=false;
|
||||
mNotifyManager.cancel(1);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if(isLocationEnabled()) {
|
||||
mBuilder.setCustomContentView(notificationSmall);
|
||||
mBuilder.setCustomBigContentView(notificationBig);
|
||||
}else{
|
||||
mBuilder.setCustomContentView(locationDisabledSmall);
|
||||
mBuilder.setCustomBigContentView(locationDisabledBig);
|
||||
}
|
||||
if(notificationShowing){
|
||||
if(ENABLE_LOGGING) Log.d(TAG,"Left: "+leftStatus+(chargeL?"+":"")+" "+"Right: "+rightStatus+(chargeR?"+":"")+" "+"Case: "+caseStatus+(chargeCase?"+":""));
|
||||
notificationBig.setImageViewResource(R.id.leftPodImg,leftStatus<=10?R.drawable.left_pod:R.drawable.left_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);
|
||||
if(System.currentTimeMillis()-lastSeenConnected<TIMEOUT_CONNECTED) {
|
||||
notificationBig.setTextViewText(R.id.leftPodText, (leftStatus <= 10 ? (leftStatus * 10) + "%" : "") + ((chargeL && leftStatus < 10) ? "+" : ""));
|
||||
notificationBig.setTextViewText(R.id.rightPodText, (rightStatus <= 10 ? (rightStatus * 10) + "%" : "") + ((chargeR && rightStatus < 10) ? "+" : ""));
|
||||
notificationBig.setTextViewText(R.id.podCaseText, (caseStatus <= 10 ? (caseStatus * 10) + "%" : "") + (chargeCase ? "+" : ""));
|
||||
notificationSmall.setTextViewText(R.id.leftPodText, (leftStatus <= 10 ? (leftStatus * 10) + "%" : "") + (chargeL ? "+" : ""));
|
||||
notificationSmall.setTextViewText(R.id.rightPodText, (rightStatus <= 10 ? (rightStatus * 10) + "%" : "") + (chargeR ? "+" : ""));
|
||||
notificationSmall.setTextViewText(R.id.podCaseText, (caseStatus <= 10 ? (caseStatus * 10) + "%" : "") + (chargeCase ? "+" : ""));
|
||||
notificationBig.setViewVisibility(R.id.leftPodText, View.VISIBLE);
|
||||
notificationBig.setViewVisibility(R.id.rightPodText, View.VISIBLE);
|
||||
notificationBig.setViewVisibility(R.id.podCaseText, View.VISIBLE);
|
||||
notificationBig.setViewVisibility(R.id.leftPodUpdating, View.INVISIBLE);
|
||||
notificationBig.setViewVisibility(R.id.rightPodUpdating, View.INVISIBLE);
|
||||
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{
|
||||
notificationBig.setTextViewText(R.id.leftPodText, "");
|
||||
notificationBig.setTextViewText(R.id.rightPodText, "");
|
||||
notificationBig.setTextViewText(R.id.podCaseText, "");
|
||||
notificationSmall.setTextViewText(R.id.leftPodText, "");
|
||||
notificationSmall.setTextViewText(R.id.rightPodText, "");
|
||||
notificationSmall.setTextViewText(R.id.podCaseText, "");
|
||||
notificationBig.setViewVisibility(R.id.leftPodText, View.INVISIBLE);
|
||||
notificationBig.setViewVisibility(R.id.rightPodText, View.INVISIBLE);
|
||||
notificationBig.setViewVisibility(R.id.podCaseText, View.INVISIBLE);
|
||||
notificationBig.setViewVisibility(R.id.leftPodUpdating, View.VISIBLE);
|
||||
notificationBig.setViewVisibility(R.id.rightPodUpdating, View.VISIBLE);
|
||||
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());
|
||||
}
|
||||
@@ -256,21 +297,23 @@ public class PodsService extends Service {
|
||||
String action = intent.getAction();
|
||||
if(action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)){
|
||||
int state= intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
|
||||
if(state==BluetoothAdapter.STATE_OFF){ //bluetooth turned off, stop scanner and remove notification
|
||||
if(state==BluetoothAdapter.STATE_OFF||state==BluetoothAdapter.STATE_TURNING_OFF){ //bluetooth turned off, stop scanner and remove notification
|
||||
if(ENABLE_LOGGING) Log.d(TAG,"BT OFF");
|
||||
maybeConnected =false;
|
||||
stopAirPodsScanner();
|
||||
recentBeacons.clear();
|
||||
}
|
||||
if(state==BluetoothAdapter.STATE_ON){ //bluetooth turned on, start/restart scanner
|
||||
if(ENABLE_LOGGING) Log.d(TAG,"BT ON");
|
||||
startAirPodsScanner();
|
||||
}
|
||||
}
|
||||
if (bluetoothDevice != null && action != null && !action.isEmpty()&&checkUUID(bluetoothDevice)){ //airpods filter
|
||||
if(action.equals("android.bluetooth.device.action.ACL_CONNECTED")){ //airpods connected, show notification
|
||||
if(action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)){ //airpods connected, show notification
|
||||
if(ENABLE_LOGGING) Log.d(TAG,"ACL CONNECTED");
|
||||
maybeConnected =true;
|
||||
}
|
||||
if(action.equals("android.bluetooth.device.action.ACL_DISCONNECTED")){ //airpods disconnected, remove notification but leave the scanner going
|
||||
if(action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)||action.equals(BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED)){ //airpods disconnected, remove notification but leave the scanner going
|
||||
if(ENABLE_LOGGING) Log.d(TAG,"ACL DISCONNECTED");
|
||||
maybeConnected =false;
|
||||
recentBeacons.clear();
|
||||
@@ -284,7 +327,34 @@ public class PodsService extends Service {
|
||||
try{
|
||||
registerReceiver(btReceiver,intentFilter);
|
||||
}catch(Throwable t){}
|
||||
if(((BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE)).getAdapter().isEnabled())startAirPodsScanner(); //if BT is already on when the app is started, start the scanner without waiting for an event to happen
|
||||
//this BT Profile Proxy allows us to know if airpods are already connected when the app is started. It also fires an event when BT is turned off, in case the BroadcastReceiver doesn't do its job
|
||||
BluetoothAdapter ba=((BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE)).getAdapter();
|
||||
ba.getProfileProxy(getApplicationContext(), new BluetoothProfile.ServiceListener() {
|
||||
@Override
|
||||
public void onServiceConnected(int i, BluetoothProfile bluetoothProfile) {
|
||||
if(i==BluetoothProfile.HEADSET){
|
||||
if(ENABLE_LOGGING) Log.d(TAG,"BT PROXY SERVICE CONNECTED");
|
||||
BluetoothHeadset h=(BluetoothHeadset)bluetoothProfile;
|
||||
for(BluetoothDevice d:h.getConnectedDevices()){
|
||||
if(checkUUID(d)){
|
||||
if(ENABLE_LOGGING) Log.d(TAG,"BT PROXY: AIRPODS ALREADY CONNECTED");
|
||||
maybeConnected=true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(int i) {
|
||||
if(i==BluetoothProfile.HEADSET){
|
||||
if(ENABLE_LOGGING) Log.d(TAG,"BT PROXY SERVICE DISCONNECTED ");
|
||||
maybeConnected=false;
|
||||
}
|
||||
|
||||
}
|
||||
},BluetoothProfile.HEADSET);
|
||||
if(ba.isEnabled())startAirPodsScanner(); //if BT is already on when the app is started, start the scanner without waiting for an event to happen
|
||||
}
|
||||
|
||||
private boolean checkUUID(BluetoothDevice bluetoothDevice){
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
android:layout_height="120dp"
|
||||
android:src="@drawable/tick"
|
||||
android:layout_alignParentTop="true"
|
||||
android:id="@+id/mainTick"
|
||||
android:id="@+id/tick"
|
||||
android:layout_marginBottom="24dp"
|
||||
/>
|
||||
<TextView
|
||||
@@ -27,14 +27,21 @@
|
||||
android:layout_marginBottom="24dp"
|
||||
android:text="@string/main1"
|
||||
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
|
||||
android:id="@+id/main2"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="80dp"
|
||||
android:layout_below="@id/main1"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:layout_below="@id/supportedDevices"
|
||||
android:text="@string/main2" />
|
||||
<Button
|
||||
android:layout_width="wrap_content"
|
||||
@@ -44,6 +51,7 @@
|
||||
android:id="@+id/mainHide"
|
||||
android:layout_alignParentEnd="true"
|
||||
/>
|
||||
|
||||
</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>
|
||||
@@ -6,6 +6,7 @@
|
||||
android:gravity="center"
|
||||
>
|
||||
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="60dp"
|
||||
android:layout_height="wrap_content"
|
||||
@@ -31,6 +32,16 @@
|
||||
android:id="@+id/leftPodText"
|
||||
/>
|
||||
|
||||
<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
|
||||
@@ -56,6 +67,16 @@
|
||||
android:id="@+id/rightPodText"
|
||||
/>
|
||||
|
||||
<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
|
||||
@@ -81,6 +102,16 @@
|
||||
android:id="@+id/podCaseText"
|
||||
/>
|
||||
|
||||
<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>
|
||||
|
||||
@@ -31,6 +31,15 @@
|
||||
android:id="@+id/leftPodText"
|
||||
/>
|
||||
|
||||
<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
|
||||
@@ -58,6 +67,15 @@
|
||||
android:id="@+id/rightPodText"
|
||||
/>
|
||||
|
||||
<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
|
||||
@@ -85,6 +103,15 @@
|
||||
android:id="@+id/podCaseText"
|
||||
/>
|
||||
|
||||
<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>
|
||||
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 5.8 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 5.5 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 6.0 KiB |
|
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 7.1 KiB |
@@ -0,0 +1,13 @@
|
||||
<resources>
|
||||
<string name="intro1">Esta aplicación te permite ver el estado de tus AirPods en cualquier dispositivo Android</string>
|
||||
<string name="intro2">Para operar, necesitamos permiso para usar Bluetooth LE (Localización) y permiso para ejecutarnos en el fondo</string>
|
||||
<string name="introAllow">Permitir</string>
|
||||
<string name="main1">Esta aplicación mostrará una notificación cuando tus AirPods estén conectados</string>
|
||||
<string name="main2">Puedes ocultar la app si gustas</string>
|
||||
<string name="mainHide">Ocultar app</string>
|
||||
<string name="hideClicked">El ícono desaparecerá pronto</string>
|
||||
<string name="noBtText">Parece que este dispositivo no tiene Bluetooth LE</string>
|
||||
<string name="locationDisabledText">Necesitas encender Localización para permitir que OpenPods lea el estado</string>
|
||||
<string name="supportedDevices">Dispositivos compatibles:\n
|
||||
• Apple AirPods 1st gen</string>
|
||||
</resources>
|
||||
@@ -8,4 +8,6 @@
|
||||
<string name="mainHide">Nascondi app</string>
|
||||
<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>
|
||||
</resources>
|
||||
@@ -8,4 +8,6 @@
|
||||
<string name="mainHide">Hide app</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="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>
|
||||
</resources>
|
||||
|
||||
@@ -6,14 +6,17 @@ The Free and Open Source app for monitoring your AirPods on Android
|
||||
## Download
|
||||
The project is currently in Alpha.
|
||||
|
||||
[Download from F-Droid](https://f-droid.org/repository/browse/?fdid=com.dosse.airpods)
|
||||
[Download APK](https://downloads.fdossena.com/geth.php?r=openpods-apk)
|
||||
|
||||
|
||||
## Features
|
||||
* Shows a notification with AirPods status when they are connected
|
||||
* Respects your privacy
|
||||
* Free and Open Source software
|
||||
|
||||
## Supported devices
|
||||
* Apple AirPods 1st 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.
|
||||
|
||||
|
||||