blob: 55c76e3c1a75766010f080ce55fa592d7acaaf9d [file] [log] [blame]
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.bluetooth;
import android.bluetooth.BluetoothAdapter;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.Handler;
import android.os.SystemProperties;
import android.util.Log;
import androidx.preference.Preference;
import com.android.settings.R;
import com.android.settingslib.bluetooth.BluetoothDiscoverableTimeoutReceiver;
import java.time.Duration;
/**
* BluetoothDiscoverableEnabler is a helper to manage the "Discoverable"
* checkbox. It sets/unsets discoverability and keeps track of how much time
* until the the discoverability is automatically turned off.
*/
final class BluetoothDiscoverableEnabler implements Preference.OnPreferenceClickListener {
private static final String TAG = "BluetoothDiscoverableEnabler";
private static final String SYSTEM_PROPERTY_DISCOVERABLE_TIMEOUT =
"debug.bt.discoverable_time";
private static final int DISCOVERABLE_TIMEOUT_TWO_MINUTES = 120;
private static final int DISCOVERABLE_TIMEOUT_FIVE_MINUTES = 300;
private static final int DISCOVERABLE_TIMEOUT_ONE_HOUR = 3600;
static final int DISCOVERABLE_TIMEOUT_NEVER = 0;
// Bluetooth advanced settings screen was replaced with action bar items.
// Use the same preference key for discoverable timeout as the old ListPreference.
private static final String KEY_DISCOVERABLE_TIMEOUT = "bt_discoverable_timeout";
private static final String VALUE_DISCOVERABLE_TIMEOUT_TWO_MINUTES = "twomin";
private static final String VALUE_DISCOVERABLE_TIMEOUT_FIVE_MINUTES = "fivemin";
private static final String VALUE_DISCOVERABLE_TIMEOUT_ONE_HOUR = "onehour";
private static final String VALUE_DISCOVERABLE_TIMEOUT_NEVER = "never";
static final int DEFAULT_DISCOVERABLE_TIMEOUT = DISCOVERABLE_TIMEOUT_TWO_MINUTES;
private Context mContext;
private final Handler mUiHandler;
private final Preference mDiscoveryPreference;
private final BluetoothAdapter mBluetoothAdapter;
private final SharedPreferences mSharedPreferences;
private boolean mDiscoverable;
private int mNumberOfPairedDevices;
private int mTimeoutSecs = -1;
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (BluetoothAdapter.ACTION_SCAN_MODE_CHANGED.equals(intent.getAction())) {
int mode = intent.getIntExtra(BluetoothAdapter.EXTRA_SCAN_MODE,
BluetoothAdapter.ERROR);
if (mode != BluetoothAdapter.ERROR) {
handleModeChanged(mode);
}
}
}
};
private final Runnable mUpdateCountdownSummaryRunnable = new Runnable() {
public void run() {
updateCountdownSummary();
}
};
BluetoothDiscoverableEnabler(Preference discoveryPreference) {
mUiHandler = new Handler();
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
mDiscoveryPreference = discoveryPreference;
mSharedPreferences = discoveryPreference.getSharedPreferences();
discoveryPreference.setPersistent(false);
}
public void resume(Context context) {
if (mBluetoothAdapter == null) {
return;
}
if (mContext != context) {
mContext = context;
}
IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
mContext.registerReceiver(mReceiver, filter);
mDiscoveryPreference.setOnPreferenceClickListener(this);
handleModeChanged(mBluetoothAdapter.getScanMode());
}
public void pause() {
if (mBluetoothAdapter == null) {
return;
}
mUiHandler.removeCallbacks(mUpdateCountdownSummaryRunnable);
mContext.unregisterReceiver(mReceiver);
mDiscoveryPreference.setOnPreferenceClickListener(null);
}
public boolean onPreferenceClick(Preference preference) {
// toggle discoverability
mDiscoverable = !mDiscoverable;
setEnabled(mDiscoverable);
return true;
}
private void setEnabled(boolean enable) {
if (enable) {
int timeout = getDiscoverableTimeout();
long endTimestamp = System.currentTimeMillis() + timeout * 1000L;
LocalBluetoothPreferences.persistDiscoverableEndTimestamp(mContext, endTimestamp);
mBluetoothAdapter.setDiscoverableTimeout(Duration.ofSeconds(timeout));
mBluetoothAdapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE);
updateCountdownSummary();
Log.d(TAG, "setEnabled(): enabled = " + enable + "timeout = " + timeout);
if (timeout > 0) {
BluetoothDiscoverableTimeoutReceiver.setDiscoverableAlarm(mContext, endTimestamp);
} else {
BluetoothDiscoverableTimeoutReceiver.cancelDiscoverableAlarm(mContext);
}
} else {
mBluetoothAdapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE);
BluetoothDiscoverableTimeoutReceiver.cancelDiscoverableAlarm(mContext);
}
}
private void updateTimerDisplay(int timeout) {
if (getDiscoverableTimeout() == DISCOVERABLE_TIMEOUT_NEVER) {
mDiscoveryPreference.setSummary(R.string.bluetooth_is_discoverable_always);
} else {
String textTimeout = formatTimeRemaining(timeout);
mDiscoveryPreference.setSummary(mContext.getString(R.string.bluetooth_is_discoverable,
textTimeout));
}
}
private static String formatTimeRemaining(int timeout) {
StringBuilder sb = new StringBuilder(6); // "mmm:ss"
int min = timeout / 60;
sb.append(min).append(':');
int sec = timeout - (min * 60);
if (sec < 10) {
sb.append('0');
}
sb.append(sec);
return sb.toString();
}
void setDiscoverableTimeout(int index) {
String timeoutValue;
switch (index) {
case 0:
default:
mTimeoutSecs = DISCOVERABLE_TIMEOUT_TWO_MINUTES;
timeoutValue = VALUE_DISCOVERABLE_TIMEOUT_TWO_MINUTES;
break;
case 1:
mTimeoutSecs = DISCOVERABLE_TIMEOUT_FIVE_MINUTES;
timeoutValue = VALUE_DISCOVERABLE_TIMEOUT_FIVE_MINUTES;
break;
case 2:
mTimeoutSecs = DISCOVERABLE_TIMEOUT_ONE_HOUR;
timeoutValue = VALUE_DISCOVERABLE_TIMEOUT_ONE_HOUR;
break;
case 3:
mTimeoutSecs = DISCOVERABLE_TIMEOUT_NEVER;
timeoutValue = VALUE_DISCOVERABLE_TIMEOUT_NEVER;
break;
}
mSharedPreferences.edit().putString(KEY_DISCOVERABLE_TIMEOUT, timeoutValue).apply();
setEnabled(true); // enable discovery and reset timer
}
private int getDiscoverableTimeout() {
if (mTimeoutSecs != -1) {
return mTimeoutSecs;
}
int timeout = SystemProperties.getInt(SYSTEM_PROPERTY_DISCOVERABLE_TIMEOUT, -1);
if (timeout < 0) {
String timeoutValue = mSharedPreferences.getString(KEY_DISCOVERABLE_TIMEOUT,
VALUE_DISCOVERABLE_TIMEOUT_TWO_MINUTES);
if (timeoutValue.equals(VALUE_DISCOVERABLE_TIMEOUT_NEVER)) {
timeout = DISCOVERABLE_TIMEOUT_NEVER;
} else if (timeoutValue.equals(VALUE_DISCOVERABLE_TIMEOUT_ONE_HOUR)) {
timeout = DISCOVERABLE_TIMEOUT_ONE_HOUR;
} else if (timeoutValue.equals(VALUE_DISCOVERABLE_TIMEOUT_FIVE_MINUTES)) {
timeout = DISCOVERABLE_TIMEOUT_FIVE_MINUTES;
} else {
timeout = DISCOVERABLE_TIMEOUT_TWO_MINUTES;
}
}
mTimeoutSecs = timeout;
return timeout;
}
int getDiscoverableTimeoutIndex() {
int timeout = getDiscoverableTimeout();
switch (timeout) {
case DISCOVERABLE_TIMEOUT_TWO_MINUTES:
default:
return 0;
case DISCOVERABLE_TIMEOUT_FIVE_MINUTES:
return 1;
case DISCOVERABLE_TIMEOUT_ONE_HOUR:
return 2;
case DISCOVERABLE_TIMEOUT_NEVER:
return 3;
}
}
void setNumberOfPairedDevices(int pairedDevices) {
mNumberOfPairedDevices = pairedDevices;
handleModeChanged(mBluetoothAdapter.getScanMode());
}
void handleModeChanged(int mode) {
Log.d(TAG, "handleModeChanged(): mode = " + mode);
if (mode == BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
mDiscoverable = true;
updateCountdownSummary();
} else {
mDiscoverable = false;
setSummaryNotDiscoverable();
}
}
private void setSummaryNotDiscoverable() {
if (mNumberOfPairedDevices != 0) {
mDiscoveryPreference.setSummary(R.string.bluetooth_only_visible_to_paired_devices);
} else {
mDiscoveryPreference.setSummary(R.string.bluetooth_not_visible_to_other_devices);
}
}
private void updateCountdownSummary() {
int mode = mBluetoothAdapter.getScanMode();
if (mode != BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
return;
}
long currentTimestamp = System.currentTimeMillis();
long endTimestamp = LocalBluetoothPreferences.getDiscoverableEndTimestamp(mContext);
if (currentTimestamp > endTimestamp) {
// We're still in discoverable mode, but maybe there isn't a timeout.
updateTimerDisplay(0);
return;
}
int timeLeft = (int) ((endTimestamp - currentTimestamp) / 1000L);
updateTimerDisplay(timeLeft);
synchronized (this) {
mUiHandler.removeCallbacks(mUpdateCountdownSummaryRunnable);
mUiHandler.postDelayed(mUpdateCountdownSummaryRunnable, 1000);
}
}
}