package andraus.bluetoothhidemu.settings;
import java.util.Set;
import andraus.bluetoothhidemu.BluetoothHidEmuActivity;
import andraus.bluetoothhidemu.R;
import andraus.bluetoothhidemu.spoof.Spoof;
import andraus.bluetoothhidemu.spoof.Spoof.SpoofMode;
import andraus.bluetoothhidemu.util.DoLog;
import andraus.bluetoothhidemu.view.BluetoothDeviceView;
import android.app.AlertDialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.preference.CheckBoxPreference;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceChangeListener;
import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceActivity;
import android.preference.PreferenceCategory;
import android.preference.PreferenceManager;
/**
* Main Settings screen.
*
* TODO: Implement mechanism to "rename" bluetooth devices.
*
*/
public class Settings extends PreferenceActivity {
private static final String TAG = BluetoothHidEmuActivity.TAG;
public static final int BLUETOOTH_REQUEST_OK = 1;
public static final int BLUETOOTH_REQUEST_DISCOVERABLE_FOR_PS3_OK = 2;
public static final int BLUETOOTH_DISCOVERABLE_DURATION_100 = 100;
public static final int BLUETOOTH_DISCOVERABLE_DURATION_5 = 5;
public static final String FILE_PREF_DEVICES = "bt_devices";
/* package */ final static String PREF_LAST_DEVICE = "last_device";
/* package */ final static String PREF_EMULATION_MODE = "emulation_mode";
private final static String PREF_BT_DISCOVERABLE = "bt_discoverable";
private final static String PREF_DEVICE_LIST = "bt_device_list";
private CheckBoxPreference mBtDiscoverablePreference = null;
private ListPreference mEmulationModeListPreference = null;
private PreferenceCategory mDeviceListCategory = null;
// Handler to update screen elements
private Handler mUiUpdateHandler = null;
// counter for bluetooth discoverability timeout
private int mCountdown = 0;
// workaround for onResume() being called twice after bluetooth dialog
private boolean mIsResumingFromDialog = false;
private BluetoothDeviceStateReceiver mBluetoothDeviceReceiver = null;
private BluetoothAdapterStateReceiver mBluetoothAdapterStateReceiver = null;
// Runnable used with mUiUpdateHandler to display discoverability countdown
private final Runnable mUpdateCountdownSummaryRunnable = new Runnable() {
public void run() {
if (BluetoothAdapter.getDefaultAdapter().getScanMode() == BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
if (mCountdown != Integer.MAX_VALUE && mCountdown > 0) {
mBtDiscoverablePreference.setSummary(
getResources().getQuantityString(
R.plurals.msg_pref_summary_bluetooth_discoverable_timeout,
mCountdown,
mCountdown));
mCountdown--;
} else if (mCountdown == Integer.MAX_VALUE) {
mBtDiscoverablePreference.setSummary(R.string.msg_pref_summary_bluetooth_discoverable_no_timeout);
}
mUiUpdateHandler.postDelayed(this, 1000 /* ms */);
} else {
setBluetoothDiscoverableCheck(false);
}
}
};
/**
* onCreate
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DoLog.d(TAG, "onCreate()");
addPreferencesFromResource(R.xml.main_preferences);
mBtDiscoverablePreference = (CheckBoxPreference) findPreference(PREF_BT_DISCOVERABLE);
mEmulationModeListPreference = (ListPreference) findPreference(PREF_EMULATION_MODE);
mDeviceListCategory = (PreferenceCategory) findPreference(PREF_DEVICE_LIST);
populateDeviceList(mDeviceListCategory);
mBluetoothDeviceReceiver = new BluetoothDeviceStateReceiver(mDeviceListCategory);
registerReceiver(mBluetoothDeviceReceiver, new IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED));
mBluetoothAdapterStateReceiver = new BluetoothAdapterStateReceiver(this);
registerReceiver(mBluetoothAdapterStateReceiver, new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED));
mUiUpdateHandler = new Handler();
mBtDiscoverablePreference.setOnPreferenceClickListener(new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
// Do not enable the preference right away, need to fire the bluetooth discoverability intent first
setBluetoothDiscoverableCheck(!mBtDiscoverablePreference.isChecked());
if (!mBtDiscoverablePreference.isChecked()) {
startActivityForResult(createBluetoothDiscoverableIntent(Settings.BLUETOOTH_DISCOVERABLE_DURATION_100), Settings.BLUETOOTH_REQUEST_OK);
}
return false;
}
});
mEmulationModeListPreference.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
mEmulationModeListPreference.setSummary(getEmulationModeSummary(getApplicationContext(), Integer.valueOf((String) newValue)));
return true;
}
});
}
/**
*
*/
@Override
protected void onDestroy() {
if (mBluetoothDeviceReceiver != null) {
unregisterReceiver(mBluetoothDeviceReceiver);
}
if (mBluetoothAdapterStateReceiver != null) {
unregisterReceiver(mBluetoothAdapterStateReceiver);
}
super.onDestroy();
}
/**
* onResume
*/
@Override
protected void onResume() {
super.onResume();
if (!mIsResumingFromDialog) {
setBluetoothDiscoverableCheck(BluetoothAdapter.getDefaultAdapter().getScanMode() == BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE);
mEmulationModeListPreference.setSummary(
getEmulationModeSummary(this, Integer.valueOf(
PreferenceManager.getDefaultSharedPreferences(this).getString(PREF_EMULATION_MODE, "-1"))));
if (mBtDiscoverablePreference.isChecked()) {
mCountdown = Integer.MAX_VALUE;
mUiUpdateHandler.post(mUpdateCountdownSummaryRunnable);
}
} else {
mIsResumingFromDialog = false;
}
}
/**
* onActivityResult - used to monitor bluetooth discoverable states
*/
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
DoLog.d(TAG, "onActivityResult: " + requestCode + " " + resultCode);
mIsResumingFromDialog = true;
if (requestCode == BLUETOOTH_REQUEST_OK && resultCode == BLUETOOTH_DISCOVERABLE_DURATION_100) {
setBluetoothDiscoverableCheck(true);
mUiUpdateHandler.removeCallbacksAndMessages(null);
mCountdown = BLUETOOTH_DISCOVERABLE_DURATION_100;
mUiUpdateHandler.post(mUpdateCountdownSummaryRunnable);
} else if (requestCode == BLUETOOTH_REQUEST_OK && resultCode == RESULT_CANCELED) {
setBluetoothDiscoverableCheck(false);
}
super.onActivityResult(requestCode, resultCode, data);
}
/**
* getEmulationMode
*
* @param context
* @return
*/
public static SpoofMode getPrefEmulationMode(Context context) {
int value = Integer.valueOf(PreferenceManager.getDefaultSharedPreferences(context).getString(PREF_EMULATION_MODE, "-1"));
return Spoof.fromInt(value);
}
/**
* getLastConnectedDevice
*
* @param context
* @return
*/
public static String getLastConnectedDevice(Context context) {
String value = PreferenceManager.getDefaultSharedPreferences(context).getString(PREF_LAST_DEVICE, null);
return value;
}
/**
* setLastDevice
*
* @param context
* @param value
*/
public static void setLastDevice(Context context, String value) {
savePref(context, PREF_LAST_DEVICE, value);
}
/**
* Returns emulation mode for the specified device
*
* @param context
* @param device
* @return
*/
public static SpoofMode getEmulationMode(Context context, BluetoothDevice device) {
SharedPreferences devicesPref = context.getSharedPreferences(Settings.FILE_PREF_DEVICES, Context.MODE_PRIVATE);
SpoofMode mode = Spoof.fromInt(devicesPref.getInt(device.getAddress(), Spoof.intValue(SpoofMode.INVALID)));
return mode;
}
/**
* Store emulation mode for the paired device as a shared preference
*
* @param context
* @param device
*/
public static void storeDeviceEmulationMode(Context context, BluetoothDevice device, SpoofMode spoofMode) {
SharedPreferences devicesPref = context.getSharedPreferences(Settings.FILE_PREF_DEVICES, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = devicesPref.edit();
editor.putInt(device.getAddress(), Spoof.intValue(spoofMode));
editor.apply();
}
/**
* savePref
*
* @param context
* @param key
* @param value
*/
private static void savePref(Context context, String key, String value) {
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor editor = sharedPref.edit();
editor.putString(key, value);
editor.apply();
}
/**
* getEmulationModeSummary
*
* @param modeIndex
*/
public static String getEmulationModeSummary(Context context, int modeIndex) {
String[] modeNames = context.getResources().getStringArray(R.array.emulation_mode_names);
return modeNames[modeIndex];
}
/**
* Toggle state for Bluetooth discoverable item
* @param state
*/
private void setBluetoothDiscoverableCheck(boolean state) {
mBtDiscoverablePreference.setChecked(state);
mBtDiscoverablePreference.setEnabled(!state);
mEmulationModeListPreference.setEnabled(!state);
if (!state) {
mBtDiscoverablePreference.setSummary(getResources().getString(R.string.msg_pref_summary_bluetooth_discoverable_click));
}
}
/**
*
*/
private void populateDeviceList(PreferenceCategory deviceListCategory) {
deviceListCategory.removeAll();
Set<BluetoothDevice> deviceSet = BluetoothAdapter.getDefaultAdapter().getBondedDevices();
for (BluetoothDevice device: deviceSet) {
Preference devicePref = new Preference(this);
devicePref.setTitle(device.getName().equals("") ? device.getAddress() : device.getName());
BluetoothDeviceView.isBluetoothDevicePs3(device);
SpoofMode spoofMode = getEmulationMode(this, device);
String emulationSummary = (spoofMode == SpoofMode.INVALID) ?
getResources().getString(R.string.msg_pref_summary_device_emulation_mode_invalid) :
getEmulationModeSummary(this, Spoof.intValue(spoofMode));
devicePref.setSummary(device.getAddress() + "\n"
+ String.format(getResources().getString(R.string.msg_pref_summary_device_emulation_mode),
emulationSummary));
deviceListCategory.addPreference(devicePref);
}
}
/**
*
* @param context
*/
public static void showPs3InvalidModeDialog(Context context) {
AlertDialog dialog = new AlertDialog.Builder(context).create();
dialog.setTitle(context.getResources().getString(R.string.msg_dialog_invalid_ps3_bonding_title));
dialog.setMessage(String.format(
context.getResources().getString(R.string.msg_dialog_invalid_ps3_bonding_text),
getEmulationModeSummary(context, Spoof.intValue(SpoofMode.HID_GENERIC)),
getEmulationModeSummary(context, Spoof.intValue(SpoofMode.HID_PS3KEYPAD))
));
dialog.setButton(DialogInterface.BUTTON_NEUTRAL, context.getResources().getString(android.R.string.ok), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
dialog.show();
}
/**
*
* @param duration
* @return
*/
public static Intent createBluetoothDiscoverableIntent(final int duration) {
Intent bluetoothIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
bluetoothIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, duration);
return bluetoothIntent;
}
}