From 5f981665c6ca6d6e928c5e06c6eb361add85a09d Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Thu, 1 Jan 2026 20:31:25 -0500
Subject: [PATCH 01/67] Updated BTReceiver.java
With relation to a known issue.
https://github.com/Mystro256/autooffbluetooth/issues/7
---
.../autooffbluetooth/BTReceiver.java | 63 ++++++++++++++-----
1 file changed, 47 insertions(+), 16 deletions(-)
diff --git a/app/src/main/java/com/mystro256/autooffbluetooth/BTReceiver.java b/app/src/main/java/com/mystro256/autooffbluetooth/BTReceiver.java
index 24fd979..7864018 100644
--- a/app/src/main/java/com/mystro256/autooffbluetooth/BTReceiver.java
+++ b/app/src/main/java/com/mystro256/autooffbluetooth/BTReceiver.java
@@ -5,28 +5,59 @@
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.os.Handler;
+import android.os.Looper;
public class BTReceiver extends BroadcastReceiver {
- // "OnReceive" is an abstract function called when there's BT activity
- // or state change; we can use this to try to catch a disconnection. When
- // called, if no devices are connected then we can disable BT.
- // See AndroidManifest.xml for the intent triggers.
+ // A static handler ensures the timer persists across multiple broadcast triggers
+ private static final Handler handler = new Handler(Looper.getMainLooper());
+
+ // The "Runnable" defines the task to turn off Bluetooth
+ private static final Runnable shutdownTask = new Runnable() {
+ @Override
+ public void run() {
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ if (adapter != null && adapter.isEnabled()) {
+ adapter.disable();
+ }
+ }
+ };
+
@Override
public void onReceive(Context context, Intent intent) {
- // Check if BT adapter is valid and hasn't already been disabled
- if (BluetoothAdapter.getDefaultAdapter() != null && BluetoothAdapter.getDefaultAdapter().isEnabled()) {
- int[] profiles = {BluetoothProfile.A2DP, BluetoothProfile.HEADSET, BluetoothProfile.HEALTH};
- for (int profileId : profiles) {
- // If any device is connected, return to avoid disabling BT
- if (BluetoothAdapter.getDefaultAdapter().getProfileConnectionState(profileId) ==
- BluetoothProfile.STATE_CONNECTED) {
- return;
- }
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+
+ // Safety check: if BT is already off or invalid, do nothing
+ if (adapter == null || !adapter.isEnabled()) {
+ return;
+ }
+
+ boolean isConnectedOrConnecting = false;
+
+ // Check standard profiles
+ int[] profiles = {BluetoothProfile.A2DP, BluetoothProfile.HEADSET, BluetoothProfile.HEALTH};
+
+ for (int profileId : profiles) {
+ int state = adapter.getProfileConnectionState(profileId);
+
+ // IMPROVEMENT 1: Also check if we are currently "CONNECTING"
+ // This prevents cutting off the radio while the car is trying to handshake
+ if (state == BluetoothProfile.STATE_CONNECTED || state == BluetoothProfile.STATE_CONNECTING) {
+ isConnectedOrConnecting = true;
+ break;
}
- // This point should only be reacted if nothing is connected
- BluetoothAdapter.getDefaultAdapter().disable();
}
- }
+ if (isConnectedOrConnecting) {
+ // IMPROVEMENT 2: Connection found! Cancel any pending shutdown command.
+ handler.removeCallbacks(shutdownTask);
+ } else {
+ // IMPROVEMENT 3: No connection found.
+ // Don't turn off immediately. Wait 20 seconds (20000ms).
+ // First, remove any existing callbacks to reset the timer (debounce)
+ handler.removeCallbacks(shutdownTask);
+ handler.postDelayed(shutdownTask, 20000);
+ }
+ }
}
From f40dd693272881a12e2cc8c690f12160eb3bb0d6 Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Thu, 1 Jan 2026 20:33:14 -0500
Subject: [PATCH 02/67] Updated build.gradle
Prepared for the next release.
---
app/build.gradle | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index 0078838..e2f739d 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -6,8 +6,8 @@ android {
applicationId "com.mystro256.autooffbluetooth"
minSdkVersion 15
targetSdkVersion 28
- versionCode 5
- versionName "1.4"
+ versionCode 6
+ versionName "1.5"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
From 72fbad9973e72dd5fe9c76d8ef4f8b7daf98a114 Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Thu, 1 Jan 2026 20:44:48 -0500
Subject: [PATCH 03/67] Updated AndroidManifest.xml
---
app/src/main/AndroidManifest.xml | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 164027a..7511a37 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -24,8 +24,9 @@
+
-
\ No newline at end of file
+
From 1b8153d0de7631f7bc5b3c307714912a7bb66717 Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Sat, 3 Jan 2026 14:53:24 -0500
Subject: [PATCH 04/67] Create android.yml
---
.github/workflows/android.yml | 34 ++++++++++++++++++++++++++++++++++
1 file changed, 34 insertions(+)
create mode 100644 .github/workflows/android.yml
diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml
new file mode 100644
index 0000000..7163114
--- /dev/null
+++ b/.github/workflows/android.yml
@@ -0,0 +1,34 @@
+name: Android CI Build APK
+
+on:
+ push:
+ branches: [ "master" ]
+ pull_request:
+ branches: [ "master" ]
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+
+ # Set up JDK
+ - name: Set up JDK 11
+ uses: actions/setup-java@v4
+ with:
+ java-version: '11'
+ distribution: 'temurin'
+ cache: gradle
+
+ - name: Grant execute permission for gradlew
+ run: chmod +x gradlew
+
+ - name: Build Debug APK
+ run: ./gradlew assembleDebug --no-daemon
+
+ - name: Upload Debug APK Artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: app-debug-apk-from-cloud
+ path: app/build/outputs/apk/debug/*.apk
From e922547821c362590ac2290249948111fcef0a13 Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Sun, 4 Jan 2026 14:13:18 -0500
Subject: [PATCH 05/67] Delete app/src/test/java/com/mystro256/autooffbluetooth
directory
---
.../autooffbluetooth/ExampleUnitTest.java | 17 -----------------
1 file changed, 17 deletions(-)
delete mode 100644 app/src/test/java/com/mystro256/autooffbluetooth/ExampleUnitTest.java
diff --git a/app/src/test/java/com/mystro256/autooffbluetooth/ExampleUnitTest.java b/app/src/test/java/com/mystro256/autooffbluetooth/ExampleUnitTest.java
deleted file mode 100644
index 7f229e2..0000000
--- a/app/src/test/java/com/mystro256/autooffbluetooth/ExampleUnitTest.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package com.mystro256.autooffbluetooth;
-
-import org.junit.Test;
-
-import static org.junit.Assert.*;
-
-/**
- * Example local unit test, which will execute on the development machine (host).
- *
- * @see Testing documentation
- */
-public class ExampleUnitTest {
- @Test
- public void addition_isCorrect() {
- assertEquals(4, 2 + 2);
- }
-}
\ No newline at end of file
From 61097445ba02494dc927758d606c0f7814460084 Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Sun, 4 Jan 2026 14:13:28 -0500
Subject: [PATCH 06/67] Delete
app/src/androidTest/java/com/mystro256/autooffbluetooth directory
---
.../ExampleInstrumentedTest.java | 26 -------------------
1 file changed, 26 deletions(-)
delete mode 100644 app/src/androidTest/java/com/mystro256/autooffbluetooth/ExampleInstrumentedTest.java
diff --git a/app/src/androidTest/java/com/mystro256/autooffbluetooth/ExampleInstrumentedTest.java b/app/src/androidTest/java/com/mystro256/autooffbluetooth/ExampleInstrumentedTest.java
deleted file mode 100644
index af1b326..0000000
--- a/app/src/androidTest/java/com/mystro256/autooffbluetooth/ExampleInstrumentedTest.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package com.mystro256.autooffbluetooth;
-
-import android.content.Context;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import static org.junit.Assert.*;
-
-/**
- * Instrumented test, which will execute on an Android device.
- *
- * @see Testing documentation
- */
-@RunWith(AndroidJUnit4.class)
-public class ExampleInstrumentedTest {
- @Test
- public void useAppContext() {
- // Context of the app under test.
- Context appContext = InstrumentationRegistry.getTargetContext();
-
- assertEquals("com.mystro256.autooffbluetooth", appContext.getPackageName());
- }
-}
From b0d83365b98ecbd7f7fef9517919428110f9d1d8 Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Sun, 4 Jan 2026 14:24:01 -0500
Subject: [PATCH 07/67] Update BTReceiver.java
---
.../autooffbluetooth/BTReceiver.java | 19 ++++++++++++-------
1 file changed, 12 insertions(+), 7 deletions(-)
diff --git a/app/src/main/java/com/mystro256/autooffbluetooth/BTReceiver.java b/app/src/main/java/com/mystro256/autooffbluetooth/BTReceiver.java
index 7864018..9ff0893 100644
--- a/app/src/main/java/com/mystro256/autooffbluetooth/BTReceiver.java
+++ b/app/src/main/java/com/mystro256/autooffbluetooth/BTReceiver.java
@@ -35,14 +35,20 @@ public void onReceive(Context context, Intent intent) {
boolean isConnectedOrConnecting = false;
- // Check standard profiles
- int[] profiles = {BluetoothProfile.A2DP, BluetoothProfile.HEADSET, BluetoothProfile.HEALTH};
+ // Add GATT (Low Energy) profiles to the check list.
+ int[] profiles = {
+ BluetoothProfile.A2DP,
+ BluetoothProfile.HEADSET,
+ BluetoothProfile.HEALTH,
+ BluetoothProfile.GATT, // Added for BLE devices (Watches)
+ BluetoothProfile.GATT_SERVER // Added for BLE server role
+ };
for (int profileId : profiles) {
+ // Note: getProfileConnectionState is deprecated in API 31+,
+ // but is the correct method for this approach on older/mixed APIs.
int state = adapter.getProfileConnectionState(profileId);
- // IMPROVEMENT 1: Also check if we are currently "CONNECTING"
- // This prevents cutting off the radio while the car is trying to handshake
if (state == BluetoothProfile.STATE_CONNECTED || state == BluetoothProfile.STATE_CONNECTING) {
isConnectedOrConnecting = true;
break;
@@ -50,11 +56,10 @@ public void onReceive(Context context, Intent intent) {
}
if (isConnectedOrConnecting) {
- // IMPROVEMENT 2: Connection found! Cancel any pending shutdown command.
+ // Connection found! Cancel any pending shutdown command.
handler.removeCallbacks(shutdownTask);
} else {
- // IMPROVEMENT 3: No connection found.
- // Don't turn off immediately. Wait 20 seconds (20000ms).
+ // No connection found. Wait 20 seconds.
// First, remove any existing callbacks to reset the timer (debounce)
handler.removeCallbacks(shutdownTask);
handler.postDelayed(shutdownTask, 20000);
From caf6c1374a0829db130a664f95bd0f11ae60a7dc Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Sun, 4 Jan 2026 14:34:37 -0500
Subject: [PATCH 08/67] Update BTReceiver.java
---
.../autooffbluetooth/BTReceiver.java | 55 +++++++++++--------
1 file changed, 33 insertions(+), 22 deletions(-)
diff --git a/app/src/main/java/com/mystro256/autooffbluetooth/BTReceiver.java b/app/src/main/java/com/mystro256/autooffbluetooth/BTReceiver.java
index 9ff0893..d66667e 100644
--- a/app/src/main/java/com/mystro256/autooffbluetooth/BTReceiver.java
+++ b/app/src/main/java/com/mystro256/autooffbluetooth/BTReceiver.java
@@ -1,19 +1,20 @@
package com.mystro256.autooffbluetooth;
import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.Looper;
+import java.lang.reflect.Method;
+import java.util.Set;
public class BTReceiver extends BroadcastReceiver {
- // A static handler ensures the timer persists across multiple broadcast triggers
private static final Handler handler = new Handler(Looper.getMainLooper());
-
- // The "Runnable" defines the task to turn off Bluetooth
+
private static final Runnable shutdownTask = new Runnable() {
@Override
public void run() {
@@ -28,41 +29,51 @@ public void run() {
public void onReceive(Context context, Intent intent) {
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- // Safety check: if BT is already off or invalid, do nothing
if (adapter == null || !adapter.isEnabled()) {
return;
}
- boolean isConnectedOrConnecting = false;
+ boolean activeConnectionFound = false;
- // Add GATT (Low Energy) profiles to the check list.
- int[] profiles = {
- BluetoothProfile.A2DP,
- BluetoothProfile.HEADSET,
- BluetoothProfile.HEALTH,
- BluetoothProfile.GATT, // Added for BLE devices (Watches)
- BluetoothProfile.GATT_SERVER // Added for BLE server role
- };
-
+ // Check Classic Profiles (Audio/Headset)
+ int[] profiles = {BluetoothProfile.A2DP, BluetoothProfile.HEADSET, BluetoothProfile.HEALTH};
for (int profileId : profiles) {
- // Note: getProfileConnectionState is deprecated in API 31+,
- // but is the correct method for this approach on older/mixed APIs.
int state = adapter.getProfileConnectionState(profileId);
-
if (state == BluetoothProfile.STATE_CONNECTED || state == BluetoothProfile.STATE_CONNECTING) {
- isConnectedOrConnecting = true;
+ activeConnectionFound = true;
break;
}
}
- if (isConnectedOrConnecting) {
- // Connection found! Cancel any pending shutdown command.
+ // Iterate Paired Devices to catch BLE
+ if (!activeConnectionFound) {
+ Set pairedDevices = adapter.getBondedDevices();
+ if (pairedDevices != null) {
+ for (BluetoothDevice device : pairedDevices) {
+ if (isDeviceConnected(device)) {
+ activeConnectionFound = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if (activeConnectionFound) {
+ // Device found! Cancel shutdown.
handler.removeCallbacks(shutdownTask);
} else {
- // No connection found. Wait 20 seconds.
- // First, remove any existing callbacks to reset the timer (debounce)
+ // No active device found. Schedule shutdown in 20s.
handler.removeCallbacks(shutdownTask);
handler.postDelayed(shutdownTask, 20000);
}
}
+
+ private boolean isDeviceConnected(BluetoothDevice device) {
+ try {
+ Method m = device.getClass().getMethod("isConnected");
+ return (boolean) m.invoke(device);
+ } catch (Exception e) {
+ return false;
+ }
+ }
}
From 574f039c0c14a335544378d32fa5b206268947a3 Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Sun, 4 Jan 2026 14:38:19 -0500
Subject: [PATCH 09/67] Update AndroidManifest.xml
---
app/src/main/AndroidManifest.xml | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 7511a37..5fb72ab 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -2,8 +2,9 @@
-
-
+
+
+
+
+
+
From b42601914e0ad33087839ceb91c85e60643db29f Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Sun, 4 Jan 2026 14:49:45 -0500
Subject: [PATCH 10/67] Update BTReceiver.java
---
.../autooffbluetooth/BTReceiver.java | 59 +++++++++++--------
1 file changed, 33 insertions(+), 26 deletions(-)
diff --git a/app/src/main/java/com/mystro256/autooffbluetooth/BTReceiver.java b/app/src/main/java/com/mystro256/autooffbluetooth/BTReceiver.java
index d66667e..b019e56 100644
--- a/app/src/main/java/com/mystro256/autooffbluetooth/BTReceiver.java
+++ b/app/src/main/java/com/mystro256/autooffbluetooth/BTReceiver.java
@@ -8,6 +8,8 @@
import android.content.Intent;
import android.os.Handler;
import android.os.Looper;
+import android.util.Log;
+
import java.lang.reflect.Method;
import java.util.Set;
@@ -19,7 +21,8 @@ public class BTReceiver extends BroadcastReceiver {
@Override
public void run() {
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- if (adapter != null && adapter.isEnabled()) {
+ // Final check right before turning off.
+ if (adapter != null && adapter.isEnabled() && !isAnyDeviceConnected(adapter)) {
adapter.disable();
}
}
@@ -27,48 +30,52 @@ public void run() {
@Override
public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
if (adapter == null || !adapter.isEnabled()) {
return;
}
- boolean activeConnectionFound = false;
+ // If connected to something, CANCEL the timer immediately.
+ if (BluetoothDevice.ACTION_ACL_CONNECTED.equals(action)) {
+ handler.removeCallbacks(shutdownTask);
+ return;
+ }
- // Check Classic Profiles (Audio/Headset)
+ // If disconnected, or something else happened, check if ANYTHING is still alive.
+ if (isAnyDeviceConnected(adapter)) {
+ handler.removeCallbacks(shutdownTask);
+ } else {
+ // Nothing found. Start the countdown.
+ handler.removeCallbacks(shutdownTask);
+ handler.postDelayed(shutdownTask, 20000); // 20 seconds
+ }
+ }
+
+ // Helper to check connections.
+ private static boolean isAnyDeviceConnected(BluetoothAdapter adapter) {
+ // Check Standard Profiles (Headsets, Audio).
int[] profiles = {BluetoothProfile.A2DP, BluetoothProfile.HEADSET, BluetoothProfile.HEALTH};
for (int profileId : profiles) {
- int state = adapter.getProfileConnectionState(profileId);
- if (state == BluetoothProfile.STATE_CONNECTED || state == BluetoothProfile.STATE_CONNECTING) {
- activeConnectionFound = true;
- break;
+ if (adapter.getProfileConnectionState(profileId) == BluetoothProfile.STATE_CONNECTED) {
+ return true;
}
}
- // Iterate Paired Devices to catch BLE
- if (!activeConnectionFound) {
- Set pairedDevices = adapter.getBondedDevices();
- if (pairedDevices != null) {
- for (BluetoothDevice device : pairedDevices) {
- if (isDeviceConnected(device)) {
- activeConnectionFound = true;
- break;
- }
+ // Check Bonded Devices. This catches devices that don't report standard profiles.
+ Set bondedDevices = adapter.getBondedDevices();
+ if (bondedDevices != null) {
+ for (BluetoothDevice device : bondedDevices) {
+ if (isConnectedReflection(device)) {
+ return true;
}
}
}
-
- if (activeConnectionFound) {
- // Device found! Cancel shutdown.
- handler.removeCallbacks(shutdownTask);
- } else {
- // No active device found. Schedule shutdown in 20s.
- handler.removeCallbacks(shutdownTask);
- handler.postDelayed(shutdownTask, 20000);
- }
+ return false;
}
- private boolean isDeviceConnected(BluetoothDevice device) {
+ private static boolean isConnectedReflection(BluetoothDevice device) {
try {
Method m = device.getClass().getMethod("isConnected");
return (boolean) m.invoke(device);
From 67e4ce907268de5e286aa3c7c45c6aba0ec4bd77 Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Sun, 4 Jan 2026 14:53:24 -0500
Subject: [PATCH 11/67] Update AndroidManifest.xml
---
app/src/main/AndroidManifest.xml | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 5fb72ab..aaae529 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -2,9 +2,10 @@
-
+
+
-
+
+
-
From 00968172abe312c6d2925971a5cf1a86cf3c0b24 Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Sun, 4 Jan 2026 14:56:48 -0500
Subject: [PATCH 12/67] Update MainActivity.java
---
.../autooffbluetooth/MainActivity.java | 102 ++++++++++--------
1 file changed, 60 insertions(+), 42 deletions(-)
diff --git a/app/src/main/java/com/mystro256/autooffbluetooth/MainActivity.java b/app/src/main/java/com/mystro256/autooffbluetooth/MainActivity.java
index fb0f2c0..ff6382d 100644
--- a/app/src/main/java/com/mystro256/autooffbluetooth/MainActivity.java
+++ b/app/src/main/java/com/mystro256/autooffbluetooth/MainActivity.java
@@ -1,64 +1,82 @@
package com.mystro256.autooffbluetooth;
+import android.Manifest;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
+import android.os.Bundle;
import android.os.PowerManager;
import android.provider.Settings;
-import android.support.v7.app.AppCompatActivity;
-import android.os.Bundle;
-import android.view.View;
-import android.widget.Button;
-import android.widget.TextView;
-
-public class MainActivity extends AppCompatActivity {
-
- static final int DISABLE_BATTERY_OPTIMIZATION = 0;
+import android.widget.Toast;
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.core.app.ActivityCompat;
+import androidx.core.content.ContextCompat;
- protected void onActivityResult(int requestCode, int resultCode,
- Intent data) {
- // Call back if user agrees to disabling battery optimization
- if (requestCode == DISABLE_BATTERY_OPTIMIZATION) {
- if (resultCode == RESULT_OK) {
- hideBatteryOptimization();
- }
- }
- }
+import java.util.ArrayList;
+import java.util.List;
- // By default, a battery optimization warning amd button is shown; this
- // function hides the warning/button and shows the welcome text instead
- private void hideBatteryOptimization() {
- TextView WelcomeText = findViewById(R.id.WelcomeText);
- TextView batteryOptimizationText = findViewById(R.id.batteryOptimizationText);
- Button batteryOptimizationButton = findViewById(R.id.batteryOptimizationButton);
+public class MainActivity extends AppCompatActivity {
- WelcomeText.setVisibility(View.VISIBLE);
- batteryOptimizationText.setVisibility(View.GONE);
- batteryOptimizationButton.setVisibility(View.GONE);
- }
+ private static final int PERMISSION_REQUEST_CODE = 101;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
- // Hide battery optimization warning if battery optimization is
- // already disabled or user is running something older than android M
- PowerManager powMan = (PowerManager) getSystemService(POWER_SERVICE);
- String packageName = getPackageName();
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M ||
- powMan.isIgnoringBatteryOptimizations(packageName)) {
- hideBatteryOptimization();
+ // Check and request Bluetooth permissions (Required for Android 12+)
+ checkAndRequestPermissions();
+
+ // Request Battery Optimization Exemption
+ requestIgnoreBatteryOptimizations();
+ }
+
+ private void checkAndRequestPermissions() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ List permissionsNeeded = new ArrayList<>();
+
+ if (ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
+ permissionsNeeded.add(Manifest.permission.BLUETOOTH_CONNECT);
+ }
+ if (ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) {
+ permissionsNeeded.add(Manifest.permission.BLUETOOTH_SCAN);
+ }
+
+ if (!permissionsNeeded.isEmpty()) {
+ ActivityCompat.requestPermissions(this, permissionsNeeded.toArray(new String[0]), PERMISSION_REQUEST_CODE);
+ }
}
}
- public void disableBatteryOptimization(View v) {
- Intent intent = new Intent();
- String packageName = getPackageName();
+ private void requestIgnoreBatteryOptimizations() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ Intent intent = new Intent();
+ String packageName = getPackageName();
+ PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
+ if (pm != null && !pm.isIgnoringBatteryOptimizations(packageName)) {
+ intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
+ intent.setData(Uri.parse("package:" + packageName));
+ startActivity(intent);
+ }
+ }
+ }
- // Request disabling battery optimization and request a call back
- intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
- intent.setData(Uri.parse("package:" + packageName));
- startActivityForResult(intent, DISABLE_BATTERY_OPTIMIZATION);
+ @Override
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ if (requestCode == PERMISSION_REQUEST_CODE) {
+ boolean allGranted = true;
+ for (int result : grantResults) {
+ if (result != PackageManager.PERMISSION_GRANTED) {
+ allGranted = false;
+ break;
+ }
+ }
+ if (!allGranted) {
+ Toast.makeText(this, "Bluetooth permissions are required to monitor your watch.", Toast.LENGTH_LONG).show();
+ }
+ }
}
}
From ef4d1fc07492594e6001bd8412efef3303b983a9 Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Sun, 4 Jan 2026 15:02:19 -0500
Subject: [PATCH 13/67] Update build.gradle
---
build.gradle | 12 ++++--------
1 file changed, 4 insertions(+), 8 deletions(-)
diff --git a/build.gradle b/build.gradle
index 9313af6..db9293b 100644
--- a/build.gradle
+++ b/build.gradle
@@ -3,22 +3,18 @@
buildscript {
repositories {
google()
- jcenter()
-
+ mavenCentral() // Replaced jcenter() as it is deprecated
}
dependencies {
- classpath 'com.android.tools.build:gradle:4.1.1'
-
- // NOTE: Do not place your application dependencies here; they belong
- // in the individual module build.gradle files
+ // Updated from 4.1.1 to 7.0.2 to support Android 12+ features
+ classpath 'com.android.tools.build:gradle:7.0.2'
}
}
allprojects {
repositories {
google()
- jcenter()
-
+ mavenCentral()
}
}
From d18509476f60b15a525bcadefb25051d5d7bbb32 Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Sun, 4 Jan 2026 15:07:32 -0500
Subject: [PATCH 14/67] Update build.gradle
---
app/build.gradle | 33 ++++++++++++++++++++++++---------
1 file changed, 24 insertions(+), 9 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index e2f739d..3229030 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -1,28 +1,43 @@
apply plugin: 'com.android.application'
android {
- compileSdkVersion 28
+ // Required to support Android 12+ Bluetooth features
+ compileSdkVersion 31
+
defaultConfig {
applicationId "com.mystro256.autooffbluetooth"
- minSdkVersion 15
- targetSdkVersion 28
+ minSdkVersion 21 // Increased for better modern compatibility
+ targetSdkVersion 31
versionCode 6
versionName "1.5"
- testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+
+ // Updated for AndroidX testing
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
+
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
+
+ // Required for AGP 7.0+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_11
+ targetCompatibility JavaVersion.VERSION_11
+ }
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
- implementation 'com.android.support:appcompat-v7:28.0.0'
- implementation 'com.android.support.constraint:constraint-layout:1.1.3'
- testImplementation 'junit:junit:4.12'
- androidTestImplementation 'com.android.support.test:runner:1.0.2'
- androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
+
+ // MIGRATED TO ANDROIDX: Replacements for com.android.support libraries
+ implementation 'androidx.appcompat:appcompat:1.4.1'
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
+
+ // Testing dependencies updated for AndroidX
+ testImplementation 'junit:junit:4.13.2'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.3'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}
From 2fb7fa40bed0eb6c882653c93167165ed2786b90 Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Sun, 4 Jan 2026 15:14:08 -0500
Subject: [PATCH 15/67] Update build.gradle
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 3229030..7d7bb74 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -6,7 +6,7 @@ android {
defaultConfig {
applicationId "com.mystro256.autooffbluetooth"
- minSdkVersion 21 // Increased for better modern compatibility
+ minSdkVersion 15
targetSdkVersion 31
versionCode 6
versionName "1.5"
From 445671d1a91430a8d5cd210afa098f3802e27662 Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Sun, 4 Jan 2026 15:15:48 -0500
Subject: [PATCH 16/67] Update gradle-wrapper.properties
---
gradle/wrapper/gradle-wrapper.properties | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 3eb1008..a7c289e 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
+# Updated to version 7.0.2 to support Android 12+ features
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionSha256Sum=23e7d37e9bb4f8dabb8a3ea7fdee9dd0428b9b1a71d298aefd65b11dccea220f
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
From 3f6b1fd4afc0bab054cb83836692b83ad31dd174 Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Sun, 4 Jan 2026 15:20:45 -0500
Subject: [PATCH 17/67] Update gradle-wrapper.properties
---
gradle/wrapper/gradle-wrapper.properties | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index a7c289e..2ec77e5 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,5 @@
-# Updated to version 7.0.2 to support Android 12+ features
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
From d09263ee876345b3736dae1307e527983d8db631 Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Sun, 4 Jan 2026 15:21:27 -0500
Subject: [PATCH 18/67] Update build.gradle
---
build.gradle | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)
diff --git a/build.gradle b/build.gradle
index db9293b..e970f21 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,13 +1,10 @@
-// Top-level build file where you can add configuration options common to all sub-projects/modules.
-
buildscript {
repositories {
google()
- mavenCentral() // Replaced jcenter() as it is deprecated
+ mavenCentral()
}
dependencies {
- // Updated from 4.1.1 to 7.0.2 to support Android 12+ features
- classpath 'com.android.tools.build:gradle:7.0.2'
+ classpath 'com.android.tools.build:gradle:7.2.2'
}
}
From 2c9e5b3f87a84de891b62731233174f013bb31dd Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Sun, 4 Jan 2026 15:26:48 -0500
Subject: [PATCH 19/67] Update gradle.properties
---
gradle.properties | 15 +++------------
1 file changed, 3 insertions(+), 12 deletions(-)
diff --git a/gradle.properties b/gradle.properties
index 82618ce..e694a99 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,15 +1,6 @@
# Project-wide Gradle settings.
-# IDE (e.g. Android Studio) users:
-# Gradle settings configured through the IDE *will override*
-# any settings specified in this file.
-# For more details on how to configure your build environment visit
-# http://www.gradle.org/docs/current/userguide/build_environment.html
-# Specifies the JVM arguments used for the daemon process.
-# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
-# When configured, Gradle will run in incubating parallel mode.
-# This option should only be used with decoupled projects. More details, visit
-# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
-# org.gradle.parallel=true
-
+# Mandatory fix for AndroidX dependencies
+android.useAndroidX=true
+android.enableJetifier=true
From 84a7e83da947f6e3083ad3b6b2aef8d998421293 Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Sun, 4 Jan 2026 15:27:38 -0500
Subject: [PATCH 20/67] Update build.gradle
From 40972f07488a74a7c8e2dee6169f9b10f1094c38 Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Sun, 4 Jan 2026 15:31:02 -0500
Subject: [PATCH 21/67] Delete .idea directory
---
.idea/.gitignore | 3 ---
.idea/codeStyles/Project.xml | 29 --------------------------
.idea/compiler.xml | 6 ------
.idea/jarRepositories.xml | 40 ------------------------------------
.idea/misc.xml | 6 ------
5 files changed, 84 deletions(-)
delete mode 100644 .idea/.gitignore
delete mode 100644 .idea/codeStyles/Project.xml
delete mode 100644 .idea/compiler.xml
delete mode 100644 .idea/jarRepositories.xml
delete mode 100644 .idea/misc.xml
diff --git a/.idea/.gitignore b/.idea/.gitignore
deleted file mode 100644
index 26d3352..0000000
--- a/.idea/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-# Default ignored files
-/shelf/
-/workspace.xml
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
deleted file mode 100644
index 30aa626..0000000
--- a/.idea/codeStyles/Project.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
deleted file mode 100644
index 61a9130..0000000
--- a/.idea/compiler.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
deleted file mode 100644
index 17fd017..0000000
--- a/.idea/jarRepositories.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
deleted file mode 100644
index bd152bd..0000000
--- a/.idea/misc.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
From b54b96f018c3e82895808eb8aed56044cae07bce Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Sun, 4 Jan 2026 15:31:33 -0500
Subject: [PATCH 22/67] Update AndroidManifest.xml
---
app/src/main/AndroidManifest.xml | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index aaae529..1e5294a 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -16,14 +16,17 @@
android:supportsRtl="true"
android:theme="@style/AppTheme">
-
+
-
From abd6599fc4c8cb3827deb80117c3f5653960fcb3 Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Sun, 4 Jan 2026 15:39:46 -0500
Subject: [PATCH 23/67] Update BTReceiver.java
---
.../autooffbluetooth/BTReceiver.java | 56 ++++++++++---------
1 file changed, 31 insertions(+), 25 deletions(-)
diff --git a/app/src/main/java/com/mystro256/autooffbluetooth/BTReceiver.java b/app/src/main/java/com/mystro256/autooffbluetooth/BTReceiver.java
index b019e56..bcdae2a 100644
--- a/app/src/main/java/com/mystro256/autooffbluetooth/BTReceiver.java
+++ b/app/src/main/java/com/mystro256/autooffbluetooth/BTReceiver.java
@@ -1,15 +1,16 @@
package com.mystro256.autooffbluetooth;
+import android.Manifest;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.os.Handler;
import android.os.Looper;
-import android.util.Log;
-
+import androidx.core.content.ContextCompat;
import java.lang.reflect.Method;
import java.util.Set;
@@ -21,9 +22,11 @@ public class BTReceiver extends BroadcastReceiver {
@Override
public void run() {
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- // Final check right before turning off.
- if (adapter != null && adapter.isEnabled() && !isAnyDeviceConnected(adapter)) {
- adapter.disable();
+ if (adapter != null && adapter.isEnabled()) {
+ try {
+ adapter.disable();
+ } catch (SecurityException e) {
+ }
}
}
};
@@ -33,45 +36,48 @@ public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- if (adapter == null || !adapter.isEnabled()) {
- return;
+ if (adapter == null || !adapter.isEnabled()) return;
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
+ if (ContextCompat.checkSelfPermission(context, Manifest.permission.BLUETOOTH_CONNECT)
+ != PackageManager.PERMISSION_GRANTED) {
+ return;
+ }
}
- // If connected to something, CANCEL the timer immediately.
if (BluetoothDevice.ACTION_ACL_CONNECTED.equals(action)) {
handler.removeCallbacks(shutdownTask);
return;
}
- // If disconnected, or something else happened, check if ANYTHING is still alive.
- if (isAnyDeviceConnected(adapter)) {
+ if (isAnyDeviceConnected(context, adapter)) {
handler.removeCallbacks(shutdownTask);
} else {
- // Nothing found. Start the countdown.
handler.removeCallbacks(shutdownTask);
- handler.postDelayed(shutdownTask, 20000); // 20 seconds
+ handler.postDelayed(shutdownTask, 20000);
}
}
- // Helper to check connections.
- private static boolean isAnyDeviceConnected(BluetoothAdapter adapter) {
- // Check Standard Profiles (Headsets, Audio).
+ private static boolean isAnyDeviceConnected(Context context, BluetoothAdapter adapter) {
+ // Standard Profiles
int[] profiles = {BluetoothProfile.A2DP, BluetoothProfile.HEADSET, BluetoothProfile.HEALTH};
for (int profileId : profiles) {
- if (adapter.getProfileConnectionState(profileId) == BluetoothProfile.STATE_CONNECTED) {
- return true;
- }
+ try {
+ if (adapter.getProfileConnectionState(profileId) == BluetoothProfile.STATE_CONNECTED) {
+ return true;
+ }
+ } catch (SecurityException e) { return false; }
}
- // Check Bonded Devices. This catches devices that don't report standard profiles.
- Set bondedDevices = adapter.getBondedDevices();
- if (bondedDevices != null) {
- for (BluetoothDevice device : bondedDevices) {
- if (isConnectedReflection(device)) {
- return true;
+ // Bonded Devices
+ try {
+ Set bondedDevices = adapter.getBondedDevices();
+ if (bondedDevices != null) {
+ for (BluetoothDevice device : bondedDevices) {
+ if (isConnectedReflection(device)) return true;
}
}
- }
+ } catch (SecurityException e) { return false; }
+
return false;
}
From 5a93bce4fdc70c176053b59c44a8df718986fb83 Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Sun, 4 Jan 2026 15:43:22 -0500
Subject: [PATCH 24/67] Update activity_main.xml
---
app/src/main/res/layout/activity_main.xml | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 9764ca0..7c5f68d 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -1,5 +1,6 @@
-
-
\ No newline at end of file
+
From 4d3b05bde6d972d2f26d0156b8c3f19f07b72e64 Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Sun, 4 Jan 2026 15:45:46 -0500
Subject: [PATCH 25/67] Update MainActivity.java
---
.../com/mystro256/autooffbluetooth/MainActivity.java | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/app/src/main/java/com/mystro256/autooffbluetooth/MainActivity.java b/app/src/main/java/com/mystro256/autooffbluetooth/MainActivity.java
index ff6382d..1791ead 100644
--- a/app/src/main/java/com/mystro256/autooffbluetooth/MainActivity.java
+++ b/app/src/main/java/com/mystro256/autooffbluetooth/MainActivity.java
@@ -8,6 +8,7 @@
import android.os.Bundle;
import android.os.PowerManager;
import android.provider.Settings;
+import android.view.View; // Added for the button click
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
@@ -25,11 +26,10 @@ public class MainActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
-
- // Check and request Bluetooth permissions (Required for Android 12+)
checkAndRequestPermissions();
+ }
- // Request Battery Optimization Exemption
+ public void disableBatteryOptimization(View view) {
requestIgnoreBatteryOptimizations();
}
@@ -52,13 +52,15 @@ private void checkAndRequestPermissions() {
private void requestIgnoreBatteryOptimizations() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- Intent intent = new Intent();
String packageName = getPackageName();
PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
if (pm != null && !pm.isIgnoringBatteryOptimizations(packageName)) {
+ Intent intent = new Intent();
intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
intent.setData(Uri.parse("package:" + packageName));
startActivity(intent);
+ } else {
+ Toast.makeText(this, "Battery optimization is already disabled.", Toast.LENGTH_SHORT).show();
}
}
}
@@ -75,7 +77,7 @@ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permis
}
}
if (!allGranted) {
- Toast.makeText(this, "Bluetooth permissions are required to monitor your watch.", Toast.LENGTH_LONG).show();
+ Toast.makeText(this, "Nearby Devices permission is required for watch detection.", Toast.LENGTH_LONG).show();
}
}
}
From 3333c53f0d504c931f5264531e64217624bc1242 Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Sun, 4 Jan 2026 16:03:59 -0500
Subject: [PATCH 26/67] Update BTReceiver.java
---
.../autooffbluetooth/BTReceiver.java | 49 +++++++++++++++----
1 file changed, 39 insertions(+), 10 deletions(-)
diff --git a/app/src/main/java/com/mystro256/autooffbluetooth/BTReceiver.java b/app/src/main/java/com/mystro256/autooffbluetooth/BTReceiver.java
index bcdae2a..675e5df 100644
--- a/app/src/main/java/com/mystro256/autooffbluetooth/BTReceiver.java
+++ b/app/src/main/java/com/mystro256/autooffbluetooth/BTReceiver.java
@@ -10,12 +10,14 @@
import android.content.pm.PackageManager;
import android.os.Handler;
import android.os.Looper;
+import android.util.Log;
import androidx.core.content.ContextCompat;
import java.lang.reflect.Method;
import java.util.Set;
public class BTReceiver extends BroadcastReceiver {
+ private static final String TAG = "BTReceiver";
private static final Handler handler = new Handler(Looper.getMainLooper());
private static final Runnable shutdownTask = new Runnable() {
@@ -23,9 +25,11 @@ public class BTReceiver extends BroadcastReceiver {
public void run() {
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
if (adapter != null && adapter.isEnabled()) {
+ Log.d(TAG, "Countdown finished. Turning off Bluetooth...");
try {
adapter.disable();
} catch (SecurityException e) {
+ Log.e(TAG, "Permission denied while disabling BT");
}
}
}
@@ -44,39 +48,64 @@ public void onReceive(Context context, Intent intent) {
}
}
- if (BluetoothDevice.ACTION_ACL_CONNECTED.equals(action)) {
- handler.removeCallbacks(shutdownTask);
- return;
+ // Handle Bluetooth manually turned off elsewhere
+ if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
+ int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
+ if (state == BluetoothAdapter.STATE_TURNING_OFF || state == BluetoothAdapter.STATE_OFF) {
+ handler.removeCallbacks(shutdownTask);
+ return;
+ }
}
+ Log.d(TAG, "Action received: " + action);
+
+ // Logic check: if anything connected.
if (isAnyDeviceConnected(context, adapter)) {
+ Log.d(TAG, "Device detected. Cancelling/Preventing shutdown.");
handler.removeCallbacks(shutdownTask);
} else {
+ Log.d(TAG, "No devices detected. Starting 20s countdown.");
handler.removeCallbacks(shutdownTask);
handler.postDelayed(shutdownTask, 20000);
}
}
private static boolean isAnyDeviceConnected(Context context, BluetoothAdapter adapter) {
- // Standard Profiles
- int[] profiles = {BluetoothProfile.A2DP, BluetoothProfile.HEADSET, BluetoothProfile.HEALTH};
+ // Check Standard Profiles (Audio, Input, Data)
+ int[] profiles = {
+ BluetoothProfile.A2DP, // Standard Audio
+ BluetoothProfile.HEADSET, // Voice Calls
+ BluetoothProfile.GATT, // Low Energy Data (Watches/App-linked headphones)
+ BluetoothProfile.HID_HOST // Input (Keyboards/Media buttons)
+ };
+
for (int profileId : profiles) {
try {
- if (adapter.getProfileConnectionState(profileId) == BluetoothProfile.STATE_CONNECTED) {
+ int state = adapter.getProfileConnectionState(profileId);
+ // If connected OR in the middle of connecting, consider it active
+ if (state == BluetoothProfile.STATE_CONNECTED || state == BluetoothProfile.STATE_CONNECTING) {
+ Log.d(TAG, "Active connection found in profile: " + profileId);
return true;
}
- } catch (SecurityException e) { return false; }
+ } catch (Exception e) {
+ // Profile not supported on this device, ignore
+ }
}
- // Bonded Devices
+ // Check Bonded Devices via Reflection
try {
Set bondedDevices = adapter.getBondedDevices();
if (bondedDevices != null) {
for (BluetoothDevice device : bondedDevices) {
- if (isConnectedReflection(device)) return true;
+ if (isConnectedReflection(device)) {
+ Log.d(TAG, "Active connection detected on: " + device.getName());
+ return true;
+ }
}
}
- } catch (SecurityException e) { return false; }
+ } catch (SecurityException e) {
+ Log.e(TAG, "Permission error during Bonded Device check");
+ }
return false;
}
From 7dd40d668159e80c4ead946e08506fe4e1b7ea41 Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Sun, 4 Jan 2026 16:11:56 -0500
Subject: [PATCH 27/67] Update BTReceiver.java
---
.../autooffbluetooth/BTReceiver.java | 38 +++++++++++--------
1 file changed, 22 insertions(+), 16 deletions(-)
diff --git a/app/src/main/java/com/mystro256/autooffbluetooth/BTReceiver.java b/app/src/main/java/com/mystro256/autooffbluetooth/BTReceiver.java
index 675e5df..6e30ec7 100644
--- a/app/src/main/java/com/mystro256/autooffbluetooth/BTReceiver.java
+++ b/app/src/main/java/com/mystro256/autooffbluetooth/BTReceiver.java
@@ -20,16 +20,18 @@ public class BTReceiver extends BroadcastReceiver {
private static final String TAG = "BTReceiver";
private static final Handler handler = new Handler(Looper.getMainLooper());
+ // Task that actually turns off Bluetooth
private static final Runnable shutdownTask = new Runnable() {
@Override
public void run() {
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
if (adapter != null && adapter.isEnabled()) {
- Log.d(TAG, "Countdown finished. Turning off Bluetooth...");
+ // Final double-check: ensure nothing connected in the last 20 seconds
+ Log.d(TAG, "Countdown finished. Disabling Bluetooth...");
try {
adapter.disable();
} catch (SecurityException e) {
- Log.e(TAG, "Permission denied while disabling BT");
+ Log.e(TAG, "SecurityException: Cannot disable BT. Ensure permission is granted.");
}
}
}
@@ -41,14 +43,17 @@ public void onReceive(Context context, Intent intent) {
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
if (adapter == null || !adapter.isEnabled()) return;
+
+ // Security Check for Android 12+
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
if (ContextCompat.checkSelfPermission(context, Manifest.permission.BLUETOOTH_CONNECT)
!= PackageManager.PERMISSION_GRANTED) {
+ Log.e(TAG, "Missing BLUETOOTH_CONNECT permission. Cannot run logic.");
return;
}
}
- // Handle Bluetooth manually turned off elsewhere
+ // Stop timer if Bluetooth is already being turned off manually
if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
if (state == BluetoothAdapter.STATE_TURNING_OFF || state == BluetoothAdapter.STATE_OFF) {
@@ -59,36 +64,37 @@ public void onReceive(Context context, Intent intent) {
Log.d(TAG, "Action received: " + action);
- // Logic check: if anything connected.
+ // Check if any device is currently active
if (isAnyDeviceConnected(context, adapter)) {
- Log.d(TAG, "Device detected. Cancelling/Preventing shutdown.");
+ Log.d(TAG, "Device(s) currently connected. Cancelling countdown.");
handler.removeCallbacks(shutdownTask);
} else {
- Log.d(TAG, "No devices detected. Starting 20s countdown.");
+ Log.d(TAG, "No active connections detected. Starting 20s countdown.");
handler.removeCallbacks(shutdownTask);
handler.postDelayed(shutdownTask, 20000);
}
}
private static boolean isAnyDeviceConnected(Context context, BluetoothAdapter adapter) {
- // Check Standard Profiles (Audio, Input, Data)
+ // Check Standard Profiles
int[] profiles = {
- BluetoothProfile.A2DP, // Standard Audio
- BluetoothProfile.HEADSET, // Voice Calls
- BluetoothProfile.GATT, // Low Energy Data (Watches/App-linked headphones)
- BluetoothProfile.HID_HOST // Input (Keyboards/Media buttons)
+ BluetoothProfile.A2DP, // 2: Standard Media Audio
+ BluetoothProfile.HEADSET, // 1: Phone Calls
+ BluetoothProfile.GATT, // 7: Data Sync (Watches/Apps)
+ 4, // HID_HOST: Keyboards/Headphone Buttons
+ 5 // PAN: Personal Area Networking
};
for (int profileId : profiles) {
try {
int state = adapter.getProfileConnectionState(profileId);
- // If connected OR in the middle of connecting, consider it active
+ // If connected or connecting, consider a device present
if (state == BluetoothProfile.STATE_CONNECTED || state == BluetoothProfile.STATE_CONNECTING) {
- Log.d(TAG, "Active connection found in profile: " + profileId);
+ Log.d(TAG, "Connection active on profile ID: " + profileId);
return true;
}
} catch (Exception e) {
- // Profile not supported on this device, ignore
+ // Profile not supported on this specific hardware
}
}
@@ -98,13 +104,13 @@ private static boolean isAnyDeviceConnected(Context context, BluetoothAdapter ad
if (bondedDevices != null) {
for (BluetoothDevice device : bondedDevices) {
if (isConnectedReflection(device)) {
- Log.d(TAG, "Active connection detected on: " + device.getName());
+ Log.d(TAG, "Device connected (Reflection): " + device.getName());
return true;
}
}
}
} catch (SecurityException e) {
- Log.e(TAG, "Permission error during Bonded Device check");
+ Log.e(TAG, "SecurityException checking bonded devices.");
}
return false;
From f8b7ffb56c5fc716f21545e5df20a5ee922d2be9 Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Sun, 4 Jan 2026 16:31:59 -0500
Subject: [PATCH 28/67] Update BTReceiver.java
---
.../autooffbluetooth/BTReceiver.java | 90 +++++++++----------
1 file changed, 40 insertions(+), 50 deletions(-)
diff --git a/app/src/main/java/com/mystro256/autooffbluetooth/BTReceiver.java b/app/src/main/java/com/mystro256/autooffbluetooth/BTReceiver.java
index 6e30ec7..f385cb3 100644
--- a/app/src/main/java/com/mystro256/autooffbluetooth/BTReceiver.java
+++ b/app/src/main/java/com/mystro256/autooffbluetooth/BTReceiver.java
@@ -20,18 +20,21 @@ public class BTReceiver extends BroadcastReceiver {
private static final String TAG = "BTReceiver";
private static final Handler handler = new Handler(Looper.getMainLooper());
- // Task that actually turns off Bluetooth
private static final Runnable shutdownTask = new Runnable() {
@Override
public void run() {
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
if (adapter != null && adapter.isEnabled()) {
- // Final double-check: ensure nothing connected in the last 20 seconds
- Log.d(TAG, "Countdown finished. Disabling Bluetooth...");
- try {
- adapter.disable();
- } catch (SecurityException e) {
- Log.e(TAG, "SecurityException: Cannot disable BT. Ensure permission is granted.");
+ // If nothing has reconnected in 20 seconds, disable Bluetooth.
+ if (!isAnyDeviceConnected(adapter)) {
+ Log.d(TAG, "Confirmed: No devices. Turning off Bluetooth.");
+ try {
+ adapter.disable();
+ } catch (SecurityException e) {
+ Log.e(TAG, "Permission denied for disable()");
+ }
+ } else {
+ Log.d(TAG, "Device reconnected during countdown. Aborting shutdown.");
}
}
}
@@ -43,75 +46,62 @@ public void onReceive(Context context, Intent intent) {
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
if (adapter == null || !adapter.isEnabled()) return;
-
- // Security Check for Android 12+
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
if (ContextCompat.checkSelfPermission(context, Manifest.permission.BLUETOOTH_CONNECT)
- != PackageManager.PERMISSION_GRANTED) {
- Log.e(TAG, "Missing BLUETOOTH_CONNECT permission. Cannot run logic.");
- return;
- }
- }
-
- // Stop timer if Bluetooth is already being turned off manually
- if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
- int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
- if (state == BluetoothAdapter.STATE_TURNING_OFF || state == BluetoothAdapter.STATE_OFF) {
- handler.removeCallbacks(shutdownTask);
- return;
- }
+ != PackageManager.PERMISSION_GRANTED) return;
}
- Log.d(TAG, "Action received: " + action);
+ Log.d(TAG, "Received Broadcast: " + action);
- // Check if any device is currently active
- if (isAnyDeviceConnected(context, adapter)) {
- Log.d(TAG, "Device(s) currently connected. Cancelling countdown.");
+ // If a device connects, always stop the timer immediately.
+ if (BluetoothDevice.ACTION_ACL_CONNECTED.equals(action)) {
+ Log.d(TAG, "ACL_CONNECTED: Stopping timer.");
handler.removeCallbacks(shutdownTask);
- } else {
- Log.d(TAG, "No active connections detected. Starting 20s countdown.");
- handler.removeCallbacks(shutdownTask);
- handler.postDelayed(shutdownTask, 20000);
+ return;
+ }
+
+ // If a device disconnects.
+ if (BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(action) ||
+ BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED.equals(action)) {
+
+ Log.d(TAG, "ACL_DISCONNECTED: Triggering 20s countdown.");
+
+ // We wait 2 seconds before checking "isAnyDeviceConnected" to let the stack finish the disconnection process.
+ handler.postDelayed(() -> {
+ if (!isAnyDeviceConnected(adapter)) {
+ handler.removeCallbacks(shutdownTask);
+ handler.postDelayed(shutdownTask, 20000);
+ }
+ }, 2000);
}
}
- private static boolean isAnyDeviceConnected(Context context, BluetoothAdapter adapter) {
- // Check Standard Profiles
- int[] profiles = {
- BluetoothProfile.A2DP, // 2: Standard Media Audio
- BluetoothProfile.HEADSET, // 1: Phone Calls
- BluetoothProfile.GATT, // 7: Data Sync (Watches/Apps)
- 4, // HID_HOST: Keyboards/Headphone Buttons
- 5 // PAN: Personal Area Networking
- };
+ private static boolean isAnyDeviceConnected(BluetoothAdapter adapter) {
+ // Standard Profiles: 1=Headset, 2=A2DP, 7=GATT, 4=HID, 5=PAN.
+ int[] profiles = {1, 2, 7, 4, 5};
for (int profileId : profiles) {
try {
int state = adapter.getProfileConnectionState(profileId);
- // If connected or connecting, consider a device present
- if (state == BluetoothProfile.STATE_CONNECTED || state == BluetoothProfile.STATE_CONNECTING) {
- Log.d(TAG, "Connection active on profile ID: " + profileId);
+ if (state == BluetoothProfile.STATE_CONNECTED) {
+ Log.d(TAG, "Active Profile Found: " + profileId);
return true;
}
- } catch (Exception e) {
- // Profile not supported on this specific hardware
- }
+ } catch (Exception e) { }
}
- // Check Bonded Devices via Reflection
+ // Bonded Reflection.
try {
Set bondedDevices = adapter.getBondedDevices();
if (bondedDevices != null) {
for (BluetoothDevice device : bondedDevices) {
if (isConnectedReflection(device)) {
- Log.d(TAG, "Device connected (Reflection): " + device.getName());
+ Log.d(TAG, "Active Device Found (Reflection): " + device.getName());
return true;
}
}
}
- } catch (SecurityException e) {
- Log.e(TAG, "SecurityException checking bonded devices.");
- }
+ } catch (SecurityException e) { }
return false;
}
From 1fdf282f1565a06a2780085f92ec48348ade42c3 Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Sun, 4 Jan 2026 17:08:21 -0500
Subject: [PATCH 29/67] Update MainActivity.java
---
.../autooffbluetooth/MainActivity.java | 30 ++++++++++---------
1 file changed, 16 insertions(+), 14 deletions(-)
diff --git a/app/src/main/java/com/mystro256/autooffbluetooth/MainActivity.java b/app/src/main/java/com/mystro256/autooffbluetooth/MainActivity.java
index 1791ead..5ee65a4 100644
--- a/app/src/main/java/com/mystro256/autooffbluetooth/MainActivity.java
+++ b/app/src/main/java/com/mystro256/autooffbluetooth/MainActivity.java
@@ -8,7 +8,7 @@
import android.os.Bundle;
import android.os.PowerManager;
import android.provider.Settings;
-import android.view.View; // Added for the button click
+import android.view.View;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
@@ -54,11 +54,18 @@ private void requestIgnoreBatteryOptimizations() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
String packageName = getPackageName();
PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
+
+ // Check if already allowed
if (pm != null && !pm.isIgnoringBatteryOptimizations(packageName)) {
- Intent intent = new Intent();
- intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
- intent.setData(Uri.parse("package:" + packageName));
- startActivity(intent);
+ try {
+ Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
+ intent.setData(Uri.parse("package:" + packageName));
+ startActivity(intent);
+ } catch (Exception e) {
+ // Fallback for devices that block direct intent
+ Intent intent = new Intent(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS);
+ startActivity(intent);
+ }
} else {
Toast.makeText(this, "Battery optimization is already disabled.", Toast.LENGTH_SHORT).show();
}
@@ -69,15 +76,10 @@ private void requestIgnoreBatteryOptimizations() {
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == PERMISSION_REQUEST_CODE) {
- boolean allGranted = true;
- for (int result : grantResults) {
- if (result != PackageManager.PERMISSION_GRANTED) {
- allGranted = false;
- break;
- }
- }
- if (!allGranted) {
- Toast.makeText(this, "Nearby Devices permission is required for watch detection.", Toast.LENGTH_LONG).show();
+ if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ Toast.makeText(this, "Bluetooth permissions granted!", Toast.LENGTH_SHORT).show();
+ } else {
+ Toast.makeText(this, "Permissions are required to detect your watch/headphones.", Toast.LENGTH_LONG).show();
}
}
}
From 76e0775ab1f69a794b8ff35c54c5e5704968a19b Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Sun, 4 Jan 2026 20:33:06 -0500
Subject: [PATCH 30/67] Delete fastlane/metadata/android directory
---
fastlane/metadata/android/de/short_description.txt | 1 -
fastlane/metadata/android/en-US/changelogs/2.txt | 1 -
fastlane/metadata/android/en-US/changelogs/3.txt | 2 --
fastlane/metadata/android/en-US/changelogs/4.txt | 2 --
fastlane/metadata/android/en-US/changelogs/5.txt | 2 --
fastlane/metadata/android/en-US/full_description.txt | 5 -----
fastlane/metadata/android/en-US/short_description.txt | 1 -
fastlane/metadata/android/en-US/title.txt | 1 -
fastlane/metadata/android/es-ES/full_description.txt | 6 ------
fastlane/metadata/android/es-ES/short_description.txt | 1 -
fastlane/metadata/android/es-ES/title.txt | 1 -
fastlane/metadata/android/fr/short_description.txt | 1 -
fastlane/metadata/android/fr/title.txt | 1 -
fastlane/metadata/android/pt/full_description.txt | 5 -----
fastlane/metadata/android/pt/short_description.txt | 1 -
fastlane/metadata/android/pt/title.txt | 1 -
16 files changed, 32 deletions(-)
delete mode 100644 fastlane/metadata/android/de/short_description.txt
delete mode 100644 fastlane/metadata/android/en-US/changelogs/2.txt
delete mode 100644 fastlane/metadata/android/en-US/changelogs/3.txt
delete mode 100644 fastlane/metadata/android/en-US/changelogs/4.txt
delete mode 100644 fastlane/metadata/android/en-US/changelogs/5.txt
delete mode 100644 fastlane/metadata/android/en-US/full_description.txt
delete mode 100644 fastlane/metadata/android/en-US/short_description.txt
delete mode 100644 fastlane/metadata/android/en-US/title.txt
delete mode 100644 fastlane/metadata/android/es-ES/full_description.txt
delete mode 100644 fastlane/metadata/android/es-ES/short_description.txt
delete mode 100644 fastlane/metadata/android/es-ES/title.txt
delete mode 100644 fastlane/metadata/android/fr/short_description.txt
delete mode 100644 fastlane/metadata/android/fr/title.txt
delete mode 100644 fastlane/metadata/android/pt/full_description.txt
delete mode 100644 fastlane/metadata/android/pt/short_description.txt
delete mode 100644 fastlane/metadata/android/pt/title.txt
diff --git a/fastlane/metadata/android/de/short_description.txt b/fastlane/metadata/android/de/short_description.txt
deleted file mode 100644
index cfa5809..0000000
--- a/fastlane/metadata/android/de/short_description.txt
+++ /dev/null
@@ -1 +0,0 @@
-Bluetooth automatisch bei Disconnect abschalten
\ No newline at end of file
diff --git a/fastlane/metadata/android/en-US/changelogs/2.txt b/fastlane/metadata/android/en-US/changelogs/2.txt
deleted file mode 100644
index 713a46a..0000000
--- a/fastlane/metadata/android/en-US/changelogs/2.txt
+++ /dev/null
@@ -1 +0,0 @@
-* Changing from leanback launcher to regular launcher to fix on Android Pie
diff --git a/fastlane/metadata/android/en-US/changelogs/3.txt b/fastlane/metadata/android/en-US/changelogs/3.txt
deleted file mode 100644
index dcbd1b9..0000000
--- a/fastlane/metadata/android/en-US/changelogs/3.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-* Detects and suggests disabling battery optimization if available
-* Built with newer tools
diff --git a/fastlane/metadata/android/en-US/changelogs/4.txt b/fastlane/metadata/android/en-US/changelogs/4.txt
deleted file mode 100644
index 1f8d5b1..0000000
--- a/fastlane/metadata/android/en-US/changelogs/4.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-* French, Spanish, and Portuguese translations
-* Fix bug: don't hide battery optimization warning if user denies disabling it
diff --git a/fastlane/metadata/android/en-US/changelogs/5.txt b/fastlane/metadata/android/en-US/changelogs/5.txt
deleted file mode 100644
index 7cc4e51..0000000
--- a/fastlane/metadata/android/en-US/changelogs/5.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-* Drop unnecessary wake lock permission
-* Add new icon
diff --git a/fastlane/metadata/android/en-US/full_description.txt b/fastlane/metadata/android/en-US/full_description.txt
deleted file mode 100644
index 0cc2bf6..0000000
--- a/fastlane/metadata/android/en-US/full_description.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-A simple Android app to automatically turn off bluetooth if disconnected.
-
-No configuration, should work as-is, no guarentees and might need you to disable battery optimization for the app. For recent versions of android, it seems to need to be launched at least once for it to work.
-
-If BT is disconnected and reconnected quickly, the app will avoid disabling unnecessarily.
diff --git a/fastlane/metadata/android/en-US/short_description.txt b/fastlane/metadata/android/en-US/short_description.txt
deleted file mode 100644
index 7a91b18..0000000
--- a/fastlane/metadata/android/en-US/short_description.txt
+++ /dev/null
@@ -1 +0,0 @@
-Automatically turn off bluetooth if disconnected
diff --git a/fastlane/metadata/android/en-US/title.txt b/fastlane/metadata/android/en-US/title.txt
deleted file mode 100644
index e326e10..0000000
--- a/fastlane/metadata/android/en-US/title.txt
+++ /dev/null
@@ -1 +0,0 @@
-Auto Off Bluetooth
diff --git a/fastlane/metadata/android/es-ES/full_description.txt b/fastlane/metadata/android/es-ES/full_description.txt
deleted file mode 100644
index d8fe4fa..0000000
--- a/fastlane/metadata/android/es-ES/full_description.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-Una aplicación simple de Android para apagar automáticamente la conectividad blutooth en caso que este sea desconectado.
-
-No se necesita configuración, deberia de funcionar tal cual. No se garantiza su funcionamiento y puede que se llegue a necesitar la desactivación de la optimización de energÃa para la aplicación. Para versiones recientes de android parece que se necesita inicializar el programa al menos una véz.
-
-Si el bluetooth se desconecta y reconecta de forma rápida, la aplicación evitará ser activada de forma innecesaria.
-
diff --git a/fastlane/metadata/android/es-ES/short_description.txt b/fastlane/metadata/android/es-ES/short_description.txt
deleted file mode 100644
index 9ca6904..0000000
--- a/fastlane/metadata/android/es-ES/short_description.txt
+++ /dev/null
@@ -1 +0,0 @@
-Apagar el blutooth automáticamente en caso que sea desconectado.
\ No newline at end of file
diff --git a/fastlane/metadata/android/es-ES/title.txt b/fastlane/metadata/android/es-ES/title.txt
deleted file mode 100644
index 1396bfc..0000000
--- a/fastlane/metadata/android/es-ES/title.txt
+++ /dev/null
@@ -1 +0,0 @@
-Auto apagador de Bluetooth
\ No newline at end of file
diff --git a/fastlane/metadata/android/fr/short_description.txt b/fastlane/metadata/android/fr/short_description.txt
deleted file mode 100644
index 9efd4a5..0000000
--- a/fastlane/metadata/android/fr/short_description.txt
+++ /dev/null
@@ -1 +0,0 @@
-Désactiver automatiquement Bluetooth lors de la déconnexion
diff --git a/fastlane/metadata/android/fr/title.txt b/fastlane/metadata/android/fr/title.txt
deleted file mode 100644
index b1d46bd..0000000
--- a/fastlane/metadata/android/fr/title.txt
+++ /dev/null
@@ -1 +0,0 @@
-Auto-Désactivé Bluetooth
diff --git a/fastlane/metadata/android/pt/full_description.txt b/fastlane/metadata/android/pt/full_description.txt
deleted file mode 100644
index b07a0b7..0000000
--- a/fastlane/metadata/android/pt/full_description.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-Um aplicativo Android simples para desactivar automaticamente o bluetooth quando desconectado.
-
-Sem configuração, deve funcionar como está, sem garantias e pode ser necessário desativar a otimização da bateria para o aplicativo. Para versões recentes do Android, parece que ele precisa ser iniciado pelo menos uma vez para funcionar.
-
-Se o BT for desconectado e reconectado rapidamente, o aplicativo evitará a desativação desnecessária.
diff --git a/fastlane/metadata/android/pt/short_description.txt b/fastlane/metadata/android/pt/short_description.txt
deleted file mode 100644
index 8d5ac51..0000000
--- a/fastlane/metadata/android/pt/short_description.txt
+++ /dev/null
@@ -1 +0,0 @@
-Desactivar automaticamente o bluetooth se desconectado
diff --git a/fastlane/metadata/android/pt/title.txt b/fastlane/metadata/android/pt/title.txt
deleted file mode 100644
index f619081..0000000
--- a/fastlane/metadata/android/pt/title.txt
+++ /dev/null
@@ -1 +0,0 @@
-Desligamento automático do bluetooth
From a5a3cbc4451630e846d9b0ff1ed96bd6040eda19 Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Mon, 5 Jan 2026 16:29:56 -0500
Subject: [PATCH 31/67] Update build.gradle
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 7d7bb74..a3433b5 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -5,7 +5,7 @@ android {
compileSdkVersion 31
defaultConfig {
- applicationId "com.mystro256.autooffbluetooth"
+ applicationId "com.mine.autooffbluetooth"
minSdkVersion 15
targetSdkVersion 31
versionCode 6
From dc7537da0f643d47e35b60ae9c14dde4c6366bf1 Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Mon, 5 Jan 2026 16:30:40 -0500
Subject: [PATCH 32/67] Update AndroidManifest.xml
---
app/src/main/AndroidManifest.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 1e5294a..5141865 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,6 +1,6 @@
+ package="com.mine.autooffbluetooth">
From a6886614cba1dbdc838fdd8eb454a30b7776e8f8 Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Mon, 5 Jan 2026 16:31:20 -0500
Subject: [PATCH 33/67] Update BTReceiver.java
---
.../main/java/com/mystro256/autooffbluetooth/BTReceiver.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/java/com/mystro256/autooffbluetooth/BTReceiver.java b/app/src/main/java/com/mystro256/autooffbluetooth/BTReceiver.java
index f385cb3..ecc1ff7 100644
--- a/app/src/main/java/com/mystro256/autooffbluetooth/BTReceiver.java
+++ b/app/src/main/java/com/mystro256/autooffbluetooth/BTReceiver.java
@@ -1,4 +1,4 @@
-package com.mystro256.autooffbluetooth;
+package com.mine.autooffbluetooth;
import android.Manifest;
import android.bluetooth.BluetoothAdapter;
From e2ba9b937860fbc01b4134f4a877259170d93e8c Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Mon, 5 Jan 2026 16:31:38 -0500
Subject: [PATCH 34/67] Update MainActivity.java
---
.../main/java/com/mystro256/autooffbluetooth/MainActivity.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/java/com/mystro256/autooffbluetooth/MainActivity.java b/app/src/main/java/com/mystro256/autooffbluetooth/MainActivity.java
index 5ee65a4..b433670 100644
--- a/app/src/main/java/com/mystro256/autooffbluetooth/MainActivity.java
+++ b/app/src/main/java/com/mystro256/autooffbluetooth/MainActivity.java
@@ -1,4 +1,4 @@
-package com.mystro256.autooffbluetooth;
+package com.mine.autooffbluetooth;
import android.Manifest;
import android.content.Intent;
From 1f4993c2c13bf00c169806c3f63c0759ede7a0ce Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Mon, 5 Jan 2026 16:32:24 -0500
Subject: [PATCH 35/67] Create read.md
---
app/src/main/java/com/mine/read.md | 1 +
1 file changed, 1 insertion(+)
create mode 100644 app/src/main/java/com/mine/read.md
diff --git a/app/src/main/java/com/mine/read.md b/app/src/main/java/com/mine/read.md
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/app/src/main/java/com/mine/read.md
@@ -0,0 +1 @@
+
From ccaaab8f40dbe6cdebe636258dbf5aa0c56e9d5c Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Mon, 5 Jan 2026 16:33:13 -0500
Subject: [PATCH 36/67] Create read.md
---
app/src/main/java/com/mine/autooffbluetooth/read.md | 1 +
1 file changed, 1 insertion(+)
create mode 100644 app/src/main/java/com/mine/autooffbluetooth/read.md
diff --git a/app/src/main/java/com/mine/autooffbluetooth/read.md b/app/src/main/java/com/mine/autooffbluetooth/read.md
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/app/src/main/java/com/mine/autooffbluetooth/read.md
@@ -0,0 +1 @@
+
From 9b987b5e66bd17550a86229133db676f2b636e65 Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Mon, 5 Jan 2026 16:34:00 -0500
Subject: [PATCH 37/67] Delete app/src/main/java/com/mine directory
---
app/src/main/java/com/mine/autooffbluetooth/read.md | 1 -
app/src/main/java/com/mine/read.md | 1 -
2 files changed, 2 deletions(-)
delete mode 100644 app/src/main/java/com/mine/autooffbluetooth/read.md
delete mode 100644 app/src/main/java/com/mine/read.md
diff --git a/app/src/main/java/com/mine/autooffbluetooth/read.md b/app/src/main/java/com/mine/autooffbluetooth/read.md
deleted file mode 100644
index 8b13789..0000000
--- a/app/src/main/java/com/mine/autooffbluetooth/read.md
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/app/src/main/java/com/mine/read.md b/app/src/main/java/com/mine/read.md
deleted file mode 100644
index 8b13789..0000000
--- a/app/src/main/java/com/mine/read.md
+++ /dev/null
@@ -1 +0,0 @@
-
From 1ed216c3f836e81c075f794f4db40f662b2d80f8 Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Mon, 5 Jan 2026 16:35:23 -0500
Subject: [PATCH 38/67] Create read.md
---
app/src/main/java/com/mine/autooffbluetooth/read.md | 1 +
1 file changed, 1 insertion(+)
create mode 100644 app/src/main/java/com/mine/autooffbluetooth/read.md
diff --git a/app/src/main/java/com/mine/autooffbluetooth/read.md b/app/src/main/java/com/mine/autooffbluetooth/read.md
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/app/src/main/java/com/mine/autooffbluetooth/read.md
@@ -0,0 +1 @@
+
From 0924f317364d386dd374fe45a2c7fe59ec2381d8 Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Mon, 5 Jan 2026 16:35:38 -0500
Subject: [PATCH 39/67] Add files via upload
---
.../com/mine/autooffbluetooth/BTReceiver.java | 117 ++++++++++++++++++
.../mine/autooffbluetooth/MainActivity.java | 86 +++++++++++++
2 files changed, 203 insertions(+)
create mode 100644 app/src/main/java/com/mine/autooffbluetooth/BTReceiver.java
create mode 100644 app/src/main/java/com/mine/autooffbluetooth/MainActivity.java
diff --git a/app/src/main/java/com/mine/autooffbluetooth/BTReceiver.java b/app/src/main/java/com/mine/autooffbluetooth/BTReceiver.java
new file mode 100644
index 0000000..ecc1ff7
--- /dev/null
+++ b/app/src/main/java/com/mine/autooffbluetooth/BTReceiver.java
@@ -0,0 +1,117 @@
+package com.mine.autooffbluetooth;
+
+import android.Manifest;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+import androidx.core.content.ContextCompat;
+import java.lang.reflect.Method;
+import java.util.Set;
+
+public class BTReceiver extends BroadcastReceiver {
+
+ private static final String TAG = "BTReceiver";
+ private static final Handler handler = new Handler(Looper.getMainLooper());
+
+ private static final Runnable shutdownTask = new Runnable() {
+ @Override
+ public void run() {
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ if (adapter != null && adapter.isEnabled()) {
+ // If nothing has reconnected in 20 seconds, disable Bluetooth.
+ if (!isAnyDeviceConnected(adapter)) {
+ Log.d(TAG, "Confirmed: No devices. Turning off Bluetooth.");
+ try {
+ adapter.disable();
+ } catch (SecurityException e) {
+ Log.e(TAG, "Permission denied for disable()");
+ }
+ } else {
+ Log.d(TAG, "Device reconnected during countdown. Aborting shutdown.");
+ }
+ }
+ }
+ };
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+
+ if (adapter == null || !adapter.isEnabled()) return;
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
+ if (ContextCompat.checkSelfPermission(context, Manifest.permission.BLUETOOTH_CONNECT)
+ != PackageManager.PERMISSION_GRANTED) return;
+ }
+
+ Log.d(TAG, "Received Broadcast: " + action);
+
+ // If a device connects, always stop the timer immediately.
+ if (BluetoothDevice.ACTION_ACL_CONNECTED.equals(action)) {
+ Log.d(TAG, "ACL_CONNECTED: Stopping timer.");
+ handler.removeCallbacks(shutdownTask);
+ return;
+ }
+
+ // If a device disconnects.
+ if (BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(action) ||
+ BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED.equals(action)) {
+
+ Log.d(TAG, "ACL_DISCONNECTED: Triggering 20s countdown.");
+
+ // We wait 2 seconds before checking "isAnyDeviceConnected" to let the stack finish the disconnection process.
+ handler.postDelayed(() -> {
+ if (!isAnyDeviceConnected(adapter)) {
+ handler.removeCallbacks(shutdownTask);
+ handler.postDelayed(shutdownTask, 20000);
+ }
+ }, 2000);
+ }
+ }
+
+ private static boolean isAnyDeviceConnected(BluetoothAdapter adapter) {
+ // Standard Profiles: 1=Headset, 2=A2DP, 7=GATT, 4=HID, 5=PAN.
+ int[] profiles = {1, 2, 7, 4, 5};
+
+ for (int profileId : profiles) {
+ try {
+ int state = adapter.getProfileConnectionState(profileId);
+ if (state == BluetoothProfile.STATE_CONNECTED) {
+ Log.d(TAG, "Active Profile Found: " + profileId);
+ return true;
+ }
+ } catch (Exception e) { }
+ }
+
+ // Bonded Reflection.
+ try {
+ Set bondedDevices = adapter.getBondedDevices();
+ if (bondedDevices != null) {
+ for (BluetoothDevice device : bondedDevices) {
+ if (isConnectedReflection(device)) {
+ Log.d(TAG, "Active Device Found (Reflection): " + device.getName());
+ return true;
+ }
+ }
+ }
+ } catch (SecurityException e) { }
+
+ return false;
+ }
+
+ private static boolean isConnectedReflection(BluetoothDevice device) {
+ try {
+ Method m = device.getClass().getMethod("isConnected");
+ return (boolean) m.invoke(device);
+ } catch (Exception e) {
+ return false;
+ }
+ }
+}
diff --git a/app/src/main/java/com/mine/autooffbluetooth/MainActivity.java b/app/src/main/java/com/mine/autooffbluetooth/MainActivity.java
new file mode 100644
index 0000000..b433670
--- /dev/null
+++ b/app/src/main/java/com/mine/autooffbluetooth/MainActivity.java
@@ -0,0 +1,86 @@
+package com.mine.autooffbluetooth;
+
+import android.Manifest;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.PowerManager;
+import android.provider.Settings;
+import android.view.View;
+import android.widget.Toast;
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.core.app.ActivityCompat;
+import androidx.core.content.ContextCompat;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class MainActivity extends AppCompatActivity {
+
+ private static final int PERMISSION_REQUEST_CODE = 101;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+ checkAndRequestPermissions();
+ }
+
+ public void disableBatteryOptimization(View view) {
+ requestIgnoreBatteryOptimizations();
+ }
+
+ private void checkAndRequestPermissions() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ List permissionsNeeded = new ArrayList<>();
+
+ if (ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
+ permissionsNeeded.add(Manifest.permission.BLUETOOTH_CONNECT);
+ }
+ if (ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) {
+ permissionsNeeded.add(Manifest.permission.BLUETOOTH_SCAN);
+ }
+
+ if (!permissionsNeeded.isEmpty()) {
+ ActivityCompat.requestPermissions(this, permissionsNeeded.toArray(new String[0]), PERMISSION_REQUEST_CODE);
+ }
+ }
+ }
+
+ private void requestIgnoreBatteryOptimizations() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ String packageName = getPackageName();
+ PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
+
+ // Check if already allowed
+ if (pm != null && !pm.isIgnoringBatteryOptimizations(packageName)) {
+ try {
+ Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
+ intent.setData(Uri.parse("package:" + packageName));
+ startActivity(intent);
+ } catch (Exception e) {
+ // Fallback for devices that block direct intent
+ Intent intent = new Intent(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS);
+ startActivity(intent);
+ }
+ } else {
+ Toast.makeText(this, "Battery optimization is already disabled.", Toast.LENGTH_SHORT).show();
+ }
+ }
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ if (requestCode == PERMISSION_REQUEST_CODE) {
+ if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ Toast.makeText(this, "Bluetooth permissions granted!", Toast.LENGTH_SHORT).show();
+ } else {
+ Toast.makeText(this, "Permissions are required to detect your watch/headphones.", Toast.LENGTH_LONG).show();
+ }
+ }
+ }
+}
From 47f3a33a8ddb5a9c3f2255c8a98f884df74e4b23 Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Mon, 5 Jan 2026 16:36:34 -0500
Subject: [PATCH 40/67] Update read.md
---
app/src/main/java/com/mine/autooffbluetooth/read.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/java/com/mine/autooffbluetooth/read.md b/app/src/main/java/com/mine/autooffbluetooth/read.md
index 8b13789..190a180 100644
--- a/app/src/main/java/com/mine/autooffbluetooth/read.md
+++ b/app/src/main/java/com/mine/autooffbluetooth/read.md
@@ -1 +1 @@
-
+123
From 14c038575b053b6a66412eee43efd7d41bbc727e Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Mon, 5 Jan 2026 16:36:43 -0500
Subject: [PATCH 41/67] Delete
app/src/main/java/com/mine/autooffbluetooth/read.md
---
app/src/main/java/com/mine/autooffbluetooth/read.md | 1 -
1 file changed, 1 deletion(-)
delete mode 100644 app/src/main/java/com/mine/autooffbluetooth/read.md
diff --git a/app/src/main/java/com/mine/autooffbluetooth/read.md b/app/src/main/java/com/mine/autooffbluetooth/read.md
deleted file mode 100644
index 190a180..0000000
--- a/app/src/main/java/com/mine/autooffbluetooth/read.md
+++ /dev/null
@@ -1 +0,0 @@
-123
From 12477c635b8c34f57fb21fbca6545a0d3c3bb875 Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Mon, 5 Jan 2026 16:37:06 -0500
Subject: [PATCH 42/67] Delete app/src/main/java/com/mystro256 directory
---
.../autooffbluetooth/BTReceiver.java | 117 ------------------
.../autooffbluetooth/MainActivity.java | 86 -------------
2 files changed, 203 deletions(-)
delete mode 100644 app/src/main/java/com/mystro256/autooffbluetooth/BTReceiver.java
delete mode 100644 app/src/main/java/com/mystro256/autooffbluetooth/MainActivity.java
diff --git a/app/src/main/java/com/mystro256/autooffbluetooth/BTReceiver.java b/app/src/main/java/com/mystro256/autooffbluetooth/BTReceiver.java
deleted file mode 100644
index ecc1ff7..0000000
--- a/app/src/main/java/com/mystro256/autooffbluetooth/BTReceiver.java
+++ /dev/null
@@ -1,117 +0,0 @@
-package com.mine.autooffbluetooth;
-
-import android.Manifest;
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothProfile;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.os.Handler;
-import android.os.Looper;
-import android.util.Log;
-import androidx.core.content.ContextCompat;
-import java.lang.reflect.Method;
-import java.util.Set;
-
-public class BTReceiver extends BroadcastReceiver {
-
- private static final String TAG = "BTReceiver";
- private static final Handler handler = new Handler(Looper.getMainLooper());
-
- private static final Runnable shutdownTask = new Runnable() {
- @Override
- public void run() {
- BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- if (adapter != null && adapter.isEnabled()) {
- // If nothing has reconnected in 20 seconds, disable Bluetooth.
- if (!isAnyDeviceConnected(adapter)) {
- Log.d(TAG, "Confirmed: No devices. Turning off Bluetooth.");
- try {
- adapter.disable();
- } catch (SecurityException e) {
- Log.e(TAG, "Permission denied for disable()");
- }
- } else {
- Log.d(TAG, "Device reconnected during countdown. Aborting shutdown.");
- }
- }
- }
- };
-
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
-
- if (adapter == null || !adapter.isEnabled()) return;
- if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
- if (ContextCompat.checkSelfPermission(context, Manifest.permission.BLUETOOTH_CONNECT)
- != PackageManager.PERMISSION_GRANTED) return;
- }
-
- Log.d(TAG, "Received Broadcast: " + action);
-
- // If a device connects, always stop the timer immediately.
- if (BluetoothDevice.ACTION_ACL_CONNECTED.equals(action)) {
- Log.d(TAG, "ACL_CONNECTED: Stopping timer.");
- handler.removeCallbacks(shutdownTask);
- return;
- }
-
- // If a device disconnects.
- if (BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(action) ||
- BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED.equals(action)) {
-
- Log.d(TAG, "ACL_DISCONNECTED: Triggering 20s countdown.");
-
- // We wait 2 seconds before checking "isAnyDeviceConnected" to let the stack finish the disconnection process.
- handler.postDelayed(() -> {
- if (!isAnyDeviceConnected(adapter)) {
- handler.removeCallbacks(shutdownTask);
- handler.postDelayed(shutdownTask, 20000);
- }
- }, 2000);
- }
- }
-
- private static boolean isAnyDeviceConnected(BluetoothAdapter adapter) {
- // Standard Profiles: 1=Headset, 2=A2DP, 7=GATT, 4=HID, 5=PAN.
- int[] profiles = {1, 2, 7, 4, 5};
-
- for (int profileId : profiles) {
- try {
- int state = adapter.getProfileConnectionState(profileId);
- if (state == BluetoothProfile.STATE_CONNECTED) {
- Log.d(TAG, "Active Profile Found: " + profileId);
- return true;
- }
- } catch (Exception e) { }
- }
-
- // Bonded Reflection.
- try {
- Set bondedDevices = adapter.getBondedDevices();
- if (bondedDevices != null) {
- for (BluetoothDevice device : bondedDevices) {
- if (isConnectedReflection(device)) {
- Log.d(TAG, "Active Device Found (Reflection): " + device.getName());
- return true;
- }
- }
- }
- } catch (SecurityException e) { }
-
- return false;
- }
-
- private static boolean isConnectedReflection(BluetoothDevice device) {
- try {
- Method m = device.getClass().getMethod("isConnected");
- return (boolean) m.invoke(device);
- } catch (Exception e) {
- return false;
- }
- }
-}
diff --git a/app/src/main/java/com/mystro256/autooffbluetooth/MainActivity.java b/app/src/main/java/com/mystro256/autooffbluetooth/MainActivity.java
deleted file mode 100644
index b433670..0000000
--- a/app/src/main/java/com/mystro256/autooffbluetooth/MainActivity.java
+++ /dev/null
@@ -1,86 +0,0 @@
-package com.mine.autooffbluetooth;
-
-import android.Manifest;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.PowerManager;
-import android.provider.Settings;
-import android.view.View;
-import android.widget.Toast;
-import androidx.annotation.NonNull;
-import androidx.appcompat.app.AppCompatActivity;
-import androidx.core.app.ActivityCompat;
-import androidx.core.content.ContextCompat;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class MainActivity extends AppCompatActivity {
-
- private static final int PERMISSION_REQUEST_CODE = 101;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- checkAndRequestPermissions();
- }
-
- public void disableBatteryOptimization(View view) {
- requestIgnoreBatteryOptimizations();
- }
-
- private void checkAndRequestPermissions() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
- List permissionsNeeded = new ArrayList<>();
-
- if (ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
- permissionsNeeded.add(Manifest.permission.BLUETOOTH_CONNECT);
- }
- if (ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) {
- permissionsNeeded.add(Manifest.permission.BLUETOOTH_SCAN);
- }
-
- if (!permissionsNeeded.isEmpty()) {
- ActivityCompat.requestPermissions(this, permissionsNeeded.toArray(new String[0]), PERMISSION_REQUEST_CODE);
- }
- }
- }
-
- private void requestIgnoreBatteryOptimizations() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- String packageName = getPackageName();
- PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
-
- // Check if already allowed
- if (pm != null && !pm.isIgnoringBatteryOptimizations(packageName)) {
- try {
- Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
- intent.setData(Uri.parse("package:" + packageName));
- startActivity(intent);
- } catch (Exception e) {
- // Fallback for devices that block direct intent
- Intent intent = new Intent(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS);
- startActivity(intent);
- }
- } else {
- Toast.makeText(this, "Battery optimization is already disabled.", Toast.LENGTH_SHORT).show();
- }
- }
- }
-
- @Override
- public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
- super.onRequestPermissionsResult(requestCode, permissions, grantResults);
- if (requestCode == PERMISSION_REQUEST_CODE) {
- if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
- Toast.makeText(this, "Bluetooth permissions granted!", Toast.LENGTH_SHORT).show();
- } else {
- Toast.makeText(this, "Permissions are required to detect your watch/headphones.", Toast.LENGTH_LONG).show();
- }
- }
- }
-}
From f31dc07f9d6211510fef6022eeb59a597dca27c2 Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Mon, 5 Jan 2026 17:09:12 -0500
Subject: [PATCH 43/67] Update README.md
---
README.md | 38 ++++++++++++++++++++++++++++++--------
1 file changed, 30 insertions(+), 8 deletions(-)
diff --git a/README.md b/README.md
index ca94c59..93384d3 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,34 @@
-# autooffbluetooth
-A simple Android app to automatically turn off bluetooth if disconnected.
+# Auto Off Bluetooth
-No configuration, should work as-is, no guarentees and might need you to disable battery optimization for the app. For recent versions of android, it seems to need to be launched at least once for it to work.
+Auto Off Bluetooth is a lightweight utility for Android designed to preserve battery life and increase device security. The app monitors your Bluetooth connection status and automatically turns off the Bluetooth radio after 20 seconds if no devices are connected.
-If BT is disconnected and reconnected quickly, the app will avoid disabling unnecessarily.
+## How it works
-[
](https://f-droid.org/packages/com.mystro256.autooffbluetooth/)
+The app listens for Bluetooth state changes in the stack and for Asynchronous Connection-Less disconnection events in the background. When a device disconnects, the app starts a timer. If the timer expires without a reconnection, the app automatically disables the Bluetooth adapter to save power. If a Bluetooth device reconnects, the app postpones the Auto Off task until the next disconnect.
+
+## Screenshots
-Use at your own risk, all code is licensed GPLv3, see [LICENSE file](https://github.com/Mystro256/autooffbluetooth/blob/master/LICENSE) for details.
+
+

+
+
+## Permissions
+
+The app requires the following permissions to manage your Bluetooth hardware:
+
+* `BLUETOOTH`: Allows the app to see the status of connections.
+* `BLUETOOTH_ADMIN`: Allows the app to toggle the Bluetooth radio on/off.
+* `BLUETOOTH_CONNECT`: To interact with paired devices (required for Android 12+).
+
+## Installation & License
+
+
+
+
+---
+
+This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+---
From 74818a3496a48924b62d3f732ff57cf04895af0a Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Mon, 5 Jan 2026 17:16:05 -0500
Subject: [PATCH 44/67] Update README.md
---
README.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 93384d3..73775aa 100644
--- a/README.md
+++ b/README.md
@@ -22,8 +22,8 @@ The app requires the following permissions to manage your Bluetooth hardware:
## Installation & License
-
-
+
+
---
From 7bfa3c9b0d226b1cf57407c7b27e43034aa7e41a Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Mon, 5 Jan 2026 17:22:09 -0500
Subject: [PATCH 45/67] Create release.yml
---
.github/workflows/release.yml | 57 +++++++++++++++++++++++++++++++++++
1 file changed, 57 insertions(+)
create mode 100644 .github/workflows/release.yml
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..a945fdf
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,57 @@
+name: Android CI Build Release
+
+on:
+ push:
+ tags:
+ - '[0-9]+.[0-9]+*'
+ workflow_dispatch:
+
+permissions:
+ contents: write
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: set up JDK 11
+ uses: actions/setup-java@v3
+ with:
+ java-version: '11'
+ distribution: 'temurin'
+ cache: gradle
+
+ - name: Build Release APK
+ run: ./gradlew assembleRelease
+
+ - name: Sign APK
+ uses: r0adkll/sign-android-release@v1
+ id: sign_app
+ with:
+ releaseDirectory: app/build/outputs/apk/release
+ signingKeyBase64: ${{ secrets.SIGNING_KEY }}
+ alias: ${{ secrets.ALIAS }}
+ keyStorePassword: ${{ secrets.KEY_STORE_PASSWORD }}
+ keyPassword: ${{ secrets.KEY_PASSWORD }}
+ env:
+ BUILD_TOOLS_VERSION: "34.0.0"
+
+ - name: Rename and Strip Dots
+ id: rename_apk
+ run: |
+
+ raw_tag=${GITHUB_REF#refs/tags/}
+ clean_tag="${raw_tag//./}"
+ signed_file="${{ steps.sign_app.outputs.signedReleaseFile }}"
+ new_name="auto-off-bluetooth-${clean_tag}.apk"
+ mv "$signed_file" "app/build/outputs/apk/release/$new_name"
+ echo "new_path=app/build/outputs/apk/release/$new_name" >> $GITHUB_OUTPUT
+
+ - name: Upload Signed APK to GitHub Release
+ uses: softprops/action-gh-release@v1
+ with:
+ files: ${{ steps.rename_apk.outputs.new_path }}
+ generate_release_notes: true
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
From 8a3cab557162310e89b245daf767daa31fd4fe44 Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Mon, 5 Jan 2026 19:28:47 -0500
Subject: [PATCH 46/67] Create full_description.txt
---
metadata /en-US/full_description.txt | 1 +
1 file changed, 1 insertion(+)
create mode 100644 metadata /en-US/full_description.txt
diff --git a/metadata /en-US/full_description.txt b/metadata /en-US/full_description.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/metadata /en-US/full_description.txt
@@ -0,0 +1 @@
+1
From 588ca9555f35d965520f075e08a9fd61bc20d8e8 Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Mon, 5 Jan 2026 19:29:42 -0500
Subject: [PATCH 47/67] Create short_description.txt
---
metadata /en-US/short_description.txt | 1 +
1 file changed, 1 insertion(+)
create mode 100644 metadata /en-US/short_description.txt
diff --git a/metadata /en-US/short_description.txt b/metadata /en-US/short_description.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/metadata /en-US/short_description.txt
@@ -0,0 +1 @@
+1
From 4853cea265563df3483780787791c48282b25109 Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Mon, 5 Jan 2026 19:30:00 -0500
Subject: [PATCH 48/67] Create title.txt
---
metadata /en-US/title.txt | 1 +
1 file changed, 1 insertion(+)
create mode 100644 metadata /en-US/title.txt
diff --git a/metadata /en-US/title.txt b/metadata /en-US/title.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/metadata /en-US/title.txt
@@ -0,0 +1 @@
+1
From 27ed7136100f4531aff214f69391c7759b24b912 Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Mon, 5 Jan 2026 19:31:54 -0500
Subject: [PATCH 49/67] Update full_description.txt
---
metadata /en-US/full_description.txt | 16 +++++++++++++++-
1 file changed, 15 insertions(+), 1 deletion(-)
diff --git a/metadata /en-US/full_description.txt b/metadata /en-US/full_description.txt
index d00491f..78e3e67 100644
--- a/metadata /en-US/full_description.txt
+++ b/metadata /en-US/full_description.txt
@@ -1 +1,15 @@
-1
+Auto Off Bluetooth
+
+Auto Off Bluetooth is a lightweight utility for Android designed to preserve battery life and increase device security. The app monitors your Bluetooth connection status and automatically turns off the Bluetooth radio after 20 seconds if no devices are connected.
+
+How it works
+
+The app listens for Bluetooth state changes in the stack and for Asynchronous Connection-Less disconnection events in the background. When a device disconnects, the app starts a timer. If the timer expires without a reconnection, the app automatically disables the Bluetooth adapter to save power. If a Bluetooth device reconnects, the app postpones the Auto Off task until the next disconnect.
+
+Permissions
+
+The app requires the following permissions to manage your Bluetooth hardware:
+
+* BLUETOOTH: Allows the app to see the status of connections.
+* BLUETOOTH_ADMIN: Allows the app to toggle the Bluetooth radio on/off.
+* BLUETOOTH_CONNECT: To interact with paired devices (required for Android 12+).
From 4a616164386f2f36aefc8a94869f8d583b5ce01d Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Mon, 5 Jan 2026 19:36:21 -0500
Subject: [PATCH 50/67] Update short_description.txt
---
metadata /en-US/short_description.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/metadata /en-US/short_description.txt b/metadata /en-US/short_description.txt
index d00491f..9dc90c7 100644
--- a/metadata /en-US/short_description.txt
+++ b/metadata /en-US/short_description.txt
@@ -1 +1 @@
-1
+The app monitors Bluetooth connection status and automatically turns it off.
From d2ea4cdeef21082f515a1f5a5fb54082114a9598 Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Mon, 5 Jan 2026 19:37:07 -0500
Subject: [PATCH 51/67] Update title.txt
---
metadata /en-US/title.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/metadata /en-US/title.txt b/metadata /en-US/title.txt
index d00491f..e326e10 100644
--- a/metadata /en-US/title.txt
+++ b/metadata /en-US/title.txt
@@ -1 +1 @@
-1
+Auto Off Bluetooth
From ecb424fe5c9d905310562664a1345b6ee0b97f34 Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Mon, 5 Jan 2026 19:38:18 -0500
Subject: [PATCH 52/67] Create read.md
---
metadata /en-US/images/read.md | 1 +
1 file changed, 1 insertion(+)
create mode 100644 metadata /en-US/images/read.md
diff --git a/metadata /en-US/images/read.md b/metadata /en-US/images/read.md
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/metadata /en-US/images/read.md
@@ -0,0 +1 @@
+1
From 39972ab46a08db57b095115487b1dab6a9022492 Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Mon, 5 Jan 2026 19:39:18 -0500
Subject: [PATCH 53/67] Add files via upload
---
metadata /en-US/images/icon.png | Bin 0 -> 19791 bytes
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 metadata /en-US/images/icon.png
diff --git a/metadata /en-US/images/icon.png b/metadata /en-US/images/icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..22b9a954da98dec0581b05398698900d8117a8b1
GIT binary patch
literal 19791
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
Date: Mon, 5 Jan 2026 19:39:36 -0500
Subject: [PATCH 54/67] Delete metadata /en-US/images/read.md
---
metadata /en-US/images/read.md | 1 -
1 file changed, 1 deletion(-)
delete mode 100644 metadata /en-US/images/read.md
diff --git a/metadata /en-US/images/read.md b/metadata /en-US/images/read.md
deleted file mode 100644
index d00491f..0000000
--- a/metadata /en-US/images/read.md
+++ /dev/null
@@ -1 +0,0 @@
-1
From 26b935aa1611ae58dd8bc4e736a3211551f650cd Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Mon, 5 Jan 2026 19:40:15 -0500
Subject: [PATCH 55/67] Create read.md
---
metadata /en-US/images/phoneScreenshots/read.md | 1 +
1 file changed, 1 insertion(+)
create mode 100644 metadata /en-US/images/phoneScreenshots/read.md
diff --git a/metadata /en-US/images/phoneScreenshots/read.md b/metadata /en-US/images/phoneScreenshots/read.md
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/metadata /en-US/images/phoneScreenshots/read.md
@@ -0,0 +1 @@
+1
From 5bc4531d3eddc09d77d941f8159be70352f6ec70 Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Mon, 5 Jan 2026 19:47:23 -0500
Subject: [PATCH 56/67] Create 1.txt
---
metadata /en-US/changelogs/1.txt | 1 +
1 file changed, 1 insertion(+)
create mode 100644 metadata /en-US/changelogs/1.txt
diff --git a/metadata /en-US/changelogs/1.txt b/metadata /en-US/changelogs/1.txt
new file mode 100644
index 0000000..37b509f
--- /dev/null
+++ b/metadata /en-US/changelogs/1.txt
@@ -0,0 +1 @@
+Initial release of the Auto Off Bluetooth application that is available in F-Droid repository, but seems being abandoned by its author. The app is built from the source code based on the 1.4 codebase with improved algorithm logic and Android newer versions compatibility.
From 16964a460b5ef8c15b1b68f8e124b9fbdd2eb63e Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Mon, 5 Jan 2026 19:51:03 -0500
Subject: [PATCH 57/67] Delete metadata directory
---
metadata /en-US/changelogs/1.txt | 1 -
metadata /en-US/full_description.txt | 15 ---------------
metadata /en-US/images/icon.png | Bin 19791 -> 0 bytes
.../en-US/images/phoneScreenshots/read.md | 1 -
metadata /en-US/short_description.txt | 1 -
metadata /en-US/title.txt | 1 -
6 files changed, 19 deletions(-)
delete mode 100644 metadata /en-US/changelogs/1.txt
delete mode 100644 metadata /en-US/full_description.txt
delete mode 100644 metadata /en-US/images/icon.png
delete mode 100644 metadata /en-US/images/phoneScreenshots/read.md
delete mode 100644 metadata /en-US/short_description.txt
delete mode 100644 metadata /en-US/title.txt
diff --git a/metadata /en-US/changelogs/1.txt b/metadata /en-US/changelogs/1.txt
deleted file mode 100644
index 37b509f..0000000
--- a/metadata /en-US/changelogs/1.txt
+++ /dev/null
@@ -1 +0,0 @@
-Initial release of the Auto Off Bluetooth application that is available in F-Droid repository, but seems being abandoned by its author. The app is built from the source code based on the 1.4 codebase with improved algorithm logic and Android newer versions compatibility.
diff --git a/metadata /en-US/full_description.txt b/metadata /en-US/full_description.txt
deleted file mode 100644
index 78e3e67..0000000
--- a/metadata /en-US/full_description.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-Auto Off Bluetooth
-
-Auto Off Bluetooth is a lightweight utility for Android designed to preserve battery life and increase device security. The app monitors your Bluetooth connection status and automatically turns off the Bluetooth radio after 20 seconds if no devices are connected.
-
-How it works
-
-The app listens for Bluetooth state changes in the stack and for Asynchronous Connection-Less disconnection events in the background. When a device disconnects, the app starts a timer. If the timer expires without a reconnection, the app automatically disables the Bluetooth adapter to save power. If a Bluetooth device reconnects, the app postpones the Auto Off task until the next disconnect.
-
-Permissions
-
-The app requires the following permissions to manage your Bluetooth hardware:
-
-* BLUETOOTH: Allows the app to see the status of connections.
-* BLUETOOTH_ADMIN: Allows the app to toggle the Bluetooth radio on/off.
-* BLUETOOTH_CONNECT: To interact with paired devices (required for Android 12+).
diff --git a/metadata /en-US/images/icon.png b/metadata /en-US/images/icon.png
deleted file mode 100644
index 22b9a954da98dec0581b05398698900d8117a8b1..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 19791
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
Date: Mon, 5 Jan 2026 19:53:10 -0500
Subject: [PATCH 58/67] Create 1.txt
---
metadata/en-US/changelogs/1.txt | 1 +
1 file changed, 1 insertion(+)
create mode 100644 metadata/en-US/changelogs/1.txt
diff --git a/metadata/en-US/changelogs/1.txt b/metadata/en-US/changelogs/1.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/metadata/en-US/changelogs/1.txt
@@ -0,0 +1 @@
+1
From c1e6d708db5f1774168f358bc5d2c72f25ce550b Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Mon, 5 Jan 2026 19:53:33 -0500
Subject: [PATCH 59/67] Add files via upload
---
metadata/en-US/changelogs/1.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/metadata/en-US/changelogs/1.txt b/metadata/en-US/changelogs/1.txt
index d00491f..37b509f 100644
--- a/metadata/en-US/changelogs/1.txt
+++ b/metadata/en-US/changelogs/1.txt
@@ -1 +1 @@
-1
+Initial release of the Auto Off Bluetooth application that is available in F-Droid repository, but seems being abandoned by its author. The app is built from the source code based on the 1.4 codebase with improved algorithm logic and Android newer versions compatibility.
From c1a7c06d408d4289284e6540276432f70dc9f843 Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Mon, 5 Jan 2026 19:54:43 -0500
Subject: [PATCH 60/67] Create read.md
---
metadata/en-US/images/phoneScreenshots/read.md | 1 +
1 file changed, 1 insertion(+)
create mode 100644 metadata/en-US/images/phoneScreenshots/read.md
diff --git a/metadata/en-US/images/phoneScreenshots/read.md b/metadata/en-US/images/phoneScreenshots/read.md
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/metadata/en-US/images/phoneScreenshots/read.md
@@ -0,0 +1 @@
+1
From 5663f4271bb42cd31bc5ca604632d32fb3dcf39f Mon Sep 17 00:00:00 2001
From: The-First-King <59452275+The-First-King@users.noreply.github.com>
Date: Mon, 5 Jan 2026 19:55:01 -0500
Subject: [PATCH 61/67] Add files via upload
---
metadata/en-US/images/icon.png | Bin 0 -> 19791 bytes
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 metadata/en-US/images/icon.png
diff --git a/metadata/en-US/images/icon.png b/metadata/en-US/images/icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..22b9a954da98dec0581b05398698900d8117a8b1
GIT binary patch
literal 19791
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