auto import from //depot/cupcake/@135843

This commit is contained in:
The Android Open Source Project
2009-03-03 19:32:34 -08:00
parent 4e14e5ccbf
commit afc4ab2ffb
242 changed files with 36904 additions and 0 deletions

View File

@@ -0,0 +1,58 @@
/*
* Copyright (C) 2009 Google Inc.
*
* 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;
import android.app.LauncherActivity;
import android.content.Intent;
import android.os.Parcelable;
import android.view.View;
import android.widget.ListView;
/**
* Displays a list of all activities matching the incoming {@link Intent.EXTRA_INTENT}
* query, along with any applicable icons.
*/
public class ActivityPicker extends LauncherActivity {
@Override
protected Intent getTargetIntent() {
Intent intent = this.getIntent();
Intent targetIntent = new Intent(Intent.ACTION_MAIN, null);
targetIntent.addCategory(Intent.CATEGORY_DEFAULT);
// Use a custom title for this dialog, if provided
if (intent.hasExtra(Intent.EXTRA_TITLE)) {
String title = intent.getStringExtra(Intent.EXTRA_TITLE);
setTitle(title);
}
Parcelable parcel = intent.getParcelableExtra(Intent.EXTRA_INTENT);
if (parcel instanceof Intent) {
targetIntent = (Intent) parcel;
}
return targetIntent;
}
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
Intent intent = intentForPosition(position);
setResult(RESULT_OK, intent);
finish();
}
}

View File

@@ -0,0 +1,118 @@
/*
* Copyright (C) 2007 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;
import com.android.internal.telephony.PhoneStateIntentReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.Message;
import android.preference.CheckBoxPreference;
import android.preference.Preference;
import android.provider.Settings;
import android.telephony.ServiceState;
public class AirplaneModeEnabler implements Preference.OnPreferenceChangeListener {
private final Context mContext;
private PhoneStateIntentReceiver mPhoneStateReceiver;
private final CheckBoxPreference mCheckBoxPref;
private static final int EVENT_SERVICE_STATE_CHANGED = 3;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case EVENT_SERVICE_STATE_CHANGED:
onAirplaneModeChanged();
break;
}
}
};
public AirplaneModeEnabler(Context context, CheckBoxPreference airplaneModeCheckBoxPreference) {
mContext = context;
mCheckBoxPref = airplaneModeCheckBoxPreference;
airplaneModeCheckBoxPreference.setPersistent(false);
mPhoneStateReceiver = new PhoneStateIntentReceiver(mContext, mHandler);
mPhoneStateReceiver.notifyServiceState(EVENT_SERVICE_STATE_CHANGED);
}
public void resume() {
// This is the widget enabled state, not the preference toggled state
mCheckBoxPref.setEnabled(true);
mCheckBoxPref.setChecked(isAirplaneModeOn(mContext));
mPhoneStateReceiver.registerIntent();
mCheckBoxPref.setOnPreferenceChangeListener(this);
}
public void pause() {
mPhoneStateReceiver.unregisterIntent();
mCheckBoxPref.setOnPreferenceChangeListener(null);
}
static boolean isAirplaneModeOn(Context context) {
return Settings.System.getInt(context.getContentResolver(),
Settings.System.AIRPLANE_MODE_ON, 0) != 0;
}
private void setAirplaneModeOn(boolean enabling) {
mCheckBoxPref.setEnabled(false);
mCheckBoxPref.setSummary(enabling ? R.string.airplane_mode_turning_on
: R.string.airplane_mode_turning_off);
// Change the system setting
Settings.System.putInt(mContext.getContentResolver(), Settings.System.AIRPLANE_MODE_ON,
enabling ? 1 : 0);
// Post the intent
Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
intent.putExtra("state", enabling);
mContext.sendBroadcast(intent);
}
/**
* Called when we've received confirmation that the airplane mode was set.
*/
private void onAirplaneModeChanged() {
ServiceState serviceState = mPhoneStateReceiver.getServiceState();
boolean airplaneModeEnabled = serviceState.getState() == ServiceState.STATE_POWER_OFF;
mCheckBoxPref.setChecked(airplaneModeEnabled);
mCheckBoxPref.setSummary(airplaneModeEnabled ? null :
mContext.getString(R.string.airplane_mode_summary));
mCheckBoxPref.setEnabled(true);
}
/**
* Called when someone clicks on the checkbox preference.
*/
public boolean onPreferenceChange(Preference preference, Object newValue) {
setAirplaneModeOn((Boolean) newValue);
return true;
}
}

View File

@@ -0,0 +1,403 @@
/*
* Copyright (C) 2006 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;
import android.app.AlertDialog;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.SystemProperties;
import android.preference.EditTextPreference;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.provider.Telephony;
import com.android.internal.telephony.TelephonyProperties;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
public class ApnEditor extends PreferenceActivity
implements SharedPreferences.OnSharedPreferenceChangeListener {
private final static String TAG = ApnEditor.class.getSimpleName();
private final static String SAVED_POS = "pos";
private static final int MENU_DELETE = Menu.FIRST;
private static final int MENU_SAVE = Menu.FIRST + 1;
private static final int MENU_CANCEL = Menu.FIRST + 2;
private static String sNotSet;
private EditTextPreference mName;
private EditTextPreference mApn;
private EditTextPreference mProxy;
private EditTextPreference mPort;
private EditTextPreference mUser;
private EditTextPreference mServer;
private EditTextPreference mPassword;
private EditTextPreference mMmsc;
private EditTextPreference mMcc;
private EditTextPreference mMnc;
private EditTextPreference mMmsProxy;
private EditTextPreference mMmsPort;
private EditTextPreference mApnType;
private String mCurMnc;
private String mCurMcc;
private Uri mUri;
private Cursor mCursor;
private boolean mNewApn;
private boolean mFirstTime;
private Resources mRes;
/**
* Standard projection for the interesting columns of a normal note.
*/
private static final String[] sProjection = new String[] {
Telephony.Carriers._ID, // 0
Telephony.Carriers.NAME, // 1
Telephony.Carriers.APN, // 2
Telephony.Carriers.PROXY, // 3
Telephony.Carriers.PORT, // 4
Telephony.Carriers.USER, // 5
Telephony.Carriers.SERVER, // 6
Telephony.Carriers.PASSWORD, // 7
Telephony.Carriers.MMSC, // 8
Telephony.Carriers.MCC, // 9
Telephony.Carriers.MNC, // 10
Telephony.Carriers.NUMERIC, // 11
Telephony.Carriers.MMSPROXY,// 12
Telephony.Carriers.MMSPORT, // 13
Telephony.Carriers.TYPE, // 14
};
private static final int ID_INDEX = 0;
private static final int NAME_INDEX = 1;
private static final int APN_INDEX = 2;
private static final int PROXY_INDEX = 3;
private static final int PORT_INDEX = 4;
private static final int USER_INDEX = 5;
private static final int SERVER_INDEX = 6;
private static final int PASSWORD_INDEX = 7;
private static final int MMSC_INDEX = 8;
private static final int MCC_INDEX = 9;
private static final int MNC_INDEX = 10;
private static final int MMSPROXY_INDEX = 12;
private static final int MMSPORT_INDEX = 13;
private static final int TYPE_INDEX = 14;
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
addPreferencesFromResource(R.xml.apn_editor);
sNotSet = getResources().getString(R.string.apn_not_set);
mName = (EditTextPreference) findPreference("apn_name");
mApn = (EditTextPreference) findPreference("apn_apn");
mProxy = (EditTextPreference) findPreference("apn_http_proxy");
mPort = (EditTextPreference) findPreference("apn_http_port");
mUser = (EditTextPreference) findPreference("apn_user");
mServer = (EditTextPreference) findPreference("apn_server");
mPassword = (EditTextPreference) findPreference("apn_password");
mMmsProxy = (EditTextPreference) findPreference("apn_mms_proxy");
mMmsPort = (EditTextPreference) findPreference("apn_mms_port");
mMmsc = (EditTextPreference) findPreference("apn_mmsc");
mMcc = (EditTextPreference) findPreference("apn_mcc");
mMnc = (EditTextPreference) findPreference("apn_mnc");
mApnType = (EditTextPreference) findPreference("apn_type");
mRes = getResources();
final Intent intent = getIntent();
final String action = intent.getAction();
mFirstTime = icicle == null;
if (action.equals(Intent.ACTION_EDIT)) {
mUri = intent.getData();
} else if (action.equals(Intent.ACTION_INSERT)) {
if (mFirstTime) {
mUri = getContentResolver().insert(intent.getData(), new ContentValues());
} else {
mUri = ContentUris.withAppendedId(Telephony.Carriers.CONTENT_URI,
icicle.getInt(SAVED_POS));
}
mNewApn = true;
// If we were unable to create a new note, then just finish
// this activity. A RESULT_CANCELED will be sent back to the
// original activity if they requested a result.
if (mUri == null) {
Log.w(TAG, "Failed to insert new telephony provider into "
+ getIntent().getData());
finish();
return;
}
// The new entry was created, so assume all will end well and
// set the result to be returned.
setResult(RESULT_OK, (new Intent()).setAction(mUri.toString()));
} else {
finish();
return;
}
mCursor = managedQuery(mUri, sProjection, null, null);
mCursor.moveToFirst();
fillUi();
}
@Override
public void onResume() {
super.onResume();
getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
}
@Override
public void onPause() {
getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
super.onPause();
}
private void fillUi() {
if (mFirstTime) {
mFirstTime = false;
// Fill in all the values from the db in both text editor and summary
mName.setText(mCursor.getString(NAME_INDEX));
mApn.setText(mCursor.getString(APN_INDEX));
mProxy.setText(mCursor.getString(PROXY_INDEX));
mPort.setText(mCursor.getString(PORT_INDEX));
mUser.setText(mCursor.getString(USER_INDEX));
mServer.setText(mCursor.getString(SERVER_INDEX));
mPassword.setText(mCursor.getString(PASSWORD_INDEX));
mMmsProxy.setText(mCursor.getString(MMSPROXY_INDEX));
mMmsPort.setText(mCursor.getString(MMSPORT_INDEX));
mMmsc.setText(mCursor.getString(MMSC_INDEX));
mMcc.setText(mCursor.getString(MCC_INDEX));
mMnc.setText(mCursor.getString(MNC_INDEX));
mApnType.setText(mCursor.getString(TYPE_INDEX));
if (mNewApn) {
String numeric =
SystemProperties.get(TelephonyProperties.PROPERTY_SIM_OPERATOR_NUMERIC);
// MCC is first 3 chars and then in 2 - 3 chars of MNC
if (numeric != null && numeric.length() > 4) {
// Country code
String mcc = numeric.substring(0, 3);
// Network code
String mnc = numeric.substring(3);
// Auto populate MNC and MCC for new entries, based on what SIM reports
mMcc.setText(mcc);
mMnc.setText(mnc);
mCurMnc = mnc;
mCurMcc = mcc;
}
}
}
mName.setSummary(checkNull(mName.getText()));
mApn.setSummary(checkNull(mApn.getText()));
mProxy.setSummary(checkNull(mProxy.getText()));
mPort.setSummary(checkNull(mPort.getText()));
mUser.setSummary(checkNull(mUser.getText()));
mServer.setSummary(checkNull(mServer.getText()));
mPassword.setSummary(starify(mPassword.getText()));
mMmsProxy.setSummary(checkNull(mMmsProxy.getText()));
mMmsPort.setSummary(checkNull(mMmsPort.getText()));
mMmsc.setSummary(checkNull(mMmsc.getText()));
mMcc.setSummary(checkNull(mMcc.getText()));
mMnc.setSummary(checkNull(mMnc.getText()));
mApnType.setSummary(checkNull(mApnType.getText()));
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
// If it's a new APN, then cancel will delete the new entry in onPause
if (!mNewApn) {
menu.add(0, MENU_DELETE, 0, R.string.menu_delete)
.setIcon(android.R.drawable.ic_menu_delete);
}
menu.add(0, MENU_SAVE, 0, R.string.menu_save)
.setIcon(android.R.drawable.ic_menu_save);
menu.add(0, MENU_CANCEL, 0, R.string.menu_cancel)
.setIcon(android.R.drawable.ic_menu_close_clear_cancel);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case MENU_DELETE:
deleteApn();
return true;
case MENU_SAVE:
if (validateAndSave(false)) {
finish();
}
return true;
case MENU_CANCEL:
if (mNewApn) {
getContentResolver().delete(mUri, null, null);
}
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_BACK: {
if (validateAndSave(false)) {
finish();
}
return true;
}
}
return super.onKeyDown(keyCode, event);
}
@Override
protected void onSaveInstanceState(Bundle icicle) {
super.onSaveInstanceState(icicle);
validateAndSave(true);
icicle.putInt(SAVED_POS, mCursor.getInt(ID_INDEX));
}
/**
* Check the key fields' validity and save if valid.
* @param force save even if the fields are not valid, if the app is
* being suspended
* @return true if the data was saved
*/
private boolean validateAndSave(boolean force) {
String name = checkNotSet(mName.getText());
String apn = checkNotSet(mApn.getText());
String mcc = checkNotSet(mMcc.getText());
String mnc = checkNotSet(mMnc.getText());
String errorMsg = null;
if (name.length() < 1) {
errorMsg = mRes.getString(R.string.error_name_empty);
} else if (apn.length() < 1) {
errorMsg = mRes.getString(R.string.error_apn_empty);
} else if (mcc.length() != 3) {
errorMsg = mRes.getString(R.string.error_mcc_not3);
} else if ((mnc.length() & 0xFFFE) != 2) {
errorMsg = mRes.getString(R.string.error_mnc_not23);
}
if (errorMsg != null && !force) {
showErrorMessage(errorMsg);
return false;
}
if (!mCursor.moveToFirst()) {
Log.w(TAG,
"Could not go to the first row in the Cursor when saving data.");
return false;
}
ContentValues values = new ContentValues();
values.put(Telephony.Carriers.NAME, name);
values.put(Telephony.Carriers.APN, apn);
values.put(Telephony.Carriers.PROXY, checkNotSet(mProxy.getText()));
values.put(Telephony.Carriers.PORT, checkNotSet(mPort.getText()));
values.put(Telephony.Carriers.MMSPROXY, checkNotSet(mMmsProxy.getText()));
values.put(Telephony.Carriers.MMSPORT, checkNotSet(mMmsPort.getText()));
values.put(Telephony.Carriers.USER, checkNotSet(mUser.getText()));
values.put(Telephony.Carriers.SERVER, checkNotSet(mServer.getText()));
values.put(Telephony.Carriers.PASSWORD, checkNotSet(mPassword.getText()));
values.put(Telephony.Carriers.MMSC, checkNotSet(mMmsc.getText()));
values.put(Telephony.Carriers.TYPE, checkNotSet(mApnType.getText()));
values.put(Telephony.Carriers.MCC, mcc);
values.put(Telephony.Carriers.MNC, mnc);
values.put(Telephony.Carriers.NUMERIC, mcc + mnc);
if (mCurMnc != null && mCurMcc != null) {
if (mCurMnc.equals(mnc) && mCurMcc.equals(mcc)) {
values.put(Telephony.Carriers.CURRENT, 1);
}
}
getContentResolver().update(mUri, values, null, null);
return true;
}
private void showErrorMessage(String message) {
new AlertDialog.Builder(this)
.setTitle(R.string.error_title)
.setMessage(message)
.setPositiveButton(android.R.string.ok, null)
.show();
}
private void deleteApn() {
getContentResolver().delete(mUri, null, null);
finish();
}
private String starify(String value) {
if (value == null || value.length() == 0) {
return sNotSet;
} else {
char[] password = new char[value.length()];
for (int i = 0; i < password.length; i++) {
password[i] = '*';
}
return new String(password);
}
}
private String checkNull(String value) {
if (value == null || value.length() == 0) {
return sNotSet;
} else {
return value;
}
}
private String checkNotSet(String value) {
if (value == null || value.equals(sNotSet)) {
return "";
} else {
return value;
}
}
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
Preference pref = findPreference(key);
if (pref != null) {
pref.setSummary(checkNull(sharedPreferences.getString(key, "")));
}
}
}

View File

@@ -0,0 +1,229 @@
/*
* 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;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceGroup;
import android.preference.PreferenceScreen;
import android.provider.Telephony;
import android.text.TextUtils;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;
public class ApnSettings extends PreferenceActivity {
public static final String EXTRA_POSITION = "position";
public static final String RESTORE_CARRIERS_URI =
"content://telephony/carriers/restore";
private static final int ID_INDEX = 0;
private static final int NAME_INDEX = 1;
private static final int APN_INDEX = 2;
private static final int MENU_NEW = Menu.FIRST;
private static final int MENU_RESTORE = Menu.FIRST + 1;
private static final int EVENT_RESTORE_DEFAULTAPN_START = 1;
private static final int EVENT_RESTORE_DEFAULTAPN_COMPLETE = 2;
private static final int DIALOG_RESTORE_DEFAULTAPN = 1001;
private static final Uri DEFAULTAPN_URI = Uri.parse(RESTORE_CARRIERS_URI);
private static boolean mRestoreDefaultApnMode;
private RestoreApnUiHandler mRestoreApnUiHandler;
private RestoreApnProcessHandler mRestoreApnProcessHandler;
private Cursor mCursor;
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
addPreferencesFromResource(R.xml.apn_settings);
}
@Override
protected void onResume() {
super.onResume();
if (!mRestoreDefaultApnMode) {
fillList();
} else {
showDialog(DIALOG_RESTORE_DEFAULTAPN);
}
}
private void fillList() {
mCursor = managedQuery(Telephony.Carriers.CONTENT_URI, new String[] {
"_id", "name", "apn"}, null, Telephony.Carriers.DEFAULT_SORT_ORDER);
PreferenceGroup apnList = (PreferenceGroup) findPreference("apn_list");
apnList.removeAll();
mCursor.moveToFirst();
while (!mCursor.isAfterLast()) {
String name = mCursor.getString(NAME_INDEX);
String apn = mCursor.getString(APN_INDEX);
if (name != null && apn != null && TextUtils.getTrimmedLength(name) > 0
&& TextUtils.getTrimmedLength(apn) > 0) {
Preference pref = new Preference((Context) this);
pref.setKey(mCursor.getString(ID_INDEX));
pref.setTitle(name);
pref.setSummary(apn);
pref.setPersistent(false);
apnList.addPreference(pref);
}
mCursor.moveToNext();
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
menu.add(0, MENU_NEW, 0,
getResources().getString(R.string.menu_new))
.setIcon(android.R.drawable.ic_menu_add);
menu.add(0, MENU_RESTORE, 0,
getResources().getString(R.string.menu_restore))
.setIcon(android.R.drawable.ic_menu_upload);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case MENU_NEW:
addNewApn();
return true;
case MENU_RESTORE:
restoreDefaultApn();
return true;
}
return super.onOptionsItemSelected(item);
}
private void addNewApn() {
startActivity(new Intent(Intent.ACTION_INSERT, Telephony.Carriers.CONTENT_URI));
}
@Override
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
int pos = Integer.parseInt(preference.getKey());
Uri url = ContentUris.withAppendedId(Telephony.Carriers.CONTENT_URI, pos);
startActivity(new Intent(Intent.ACTION_EDIT, url));
return true;
}
private boolean restoreDefaultApn() {
showDialog(DIALOG_RESTORE_DEFAULTAPN);
mRestoreDefaultApnMode = true;
if (mRestoreApnUiHandler == null) {
mRestoreApnUiHandler = new RestoreApnUiHandler();
}
if (mRestoreApnProcessHandler == null) {
HandlerThread restoreDefaultApnThread = new HandlerThread(
"Restore default APN Handler: Process Thread");
restoreDefaultApnThread.start();
mRestoreApnProcessHandler = new RestoreApnProcessHandler(
restoreDefaultApnThread.getLooper(), mRestoreApnUiHandler);
}
mRestoreApnProcessHandler
.sendEmptyMessage(EVENT_RESTORE_DEFAULTAPN_START);
return true;
}
private class RestoreApnUiHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case EVENT_RESTORE_DEFAULTAPN_COMPLETE:
fillList();
getPreferenceScreen().setEnabled(true);
mRestoreDefaultApnMode = false;
dismissDialog(DIALOG_RESTORE_DEFAULTAPN);
Toast.makeText(
ApnSettings.this,
getResources().getString(
R.string.restore_default_apn_completed),
Toast.LENGTH_LONG).show();
break;
}
}
}
private class RestoreApnProcessHandler extends Handler {
private Handler mRestoreApnUiHandler;
public RestoreApnProcessHandler(Looper looper, Handler restoreApnUiHandler) {
super(looper);
this.mRestoreApnUiHandler = restoreApnUiHandler;
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case EVENT_RESTORE_DEFAULTAPN_START:
ContentResolver resolver = getContentResolver();
resolver.delete(DEFAULTAPN_URI, null, null);
mRestoreApnUiHandler
.sendEmptyMessage(EVENT_RESTORE_DEFAULTAPN_COMPLETE);
break;
}
}
}
@Override
protected Dialog onCreateDialog(int id) {
if (id == DIALOG_RESTORE_DEFAULTAPN) {
ProgressDialog dialog = new ProgressDialog(this);
dialog.setMessage(getResources().getString(R.string.restore_default_apn));
dialog.setCancelable(false);
return dialog;
}
return null;
}
@Override
protected void onPrepareDialog(int id, Dialog dialog) {
if (id == DIALOG_RESTORE_DEFAULTAPN) {
getPreferenceScreen().setEnabled(false);
}
}
}

View File

@@ -0,0 +1,98 @@
/*
* 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;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.res.Configuration;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceScreen;
import android.provider.Settings;
public class ApplicationSettings extends PreferenceActivity implements
DialogInterface.OnClickListener {
private static final String KEY_TOGGLE_INSTALL_APPLICATIONS = "toggle_install_applications";
private static final String KEY_QUICK_LAUNCH = "quick_launch";
private CheckBoxPreference mToggleAppInstallation;
private DialogInterface mWarnInstallApps;
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
addPreferencesFromResource(R.xml.application_settings);
mToggleAppInstallation = (CheckBoxPreference) findPreference(KEY_TOGGLE_INSTALL_APPLICATIONS);
mToggleAppInstallation.setChecked(isNonMarketAppsAllowed());
if (getResources().getConfiguration().keyboard == Configuration.KEYBOARD_NOKEYS) {
// No hard keyboard, remove the setting for quick launch
Preference quickLaunchSetting = findPreference(KEY_QUICK_LAUNCH);
getPreferenceScreen().removePreference(quickLaunchSetting);
}
}
@Override
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
if (preference == mToggleAppInstallation) {
if (mToggleAppInstallation.isChecked()) {
mToggleAppInstallation.setChecked(false);
warnAppInstallation();
} else {
setNonMarketAppsAllowed(false);
}
}
return super.onPreferenceTreeClick(preferenceScreen, preference);
}
public void onClick(DialogInterface dialog, int which) {
if (dialog == mWarnInstallApps && which == DialogInterface.BUTTON1) {
setNonMarketAppsAllowed(true);
mToggleAppInstallation.setChecked(true);
}
}
private void setNonMarketAppsAllowed(boolean enabled) {
// Change the system setting
Settings.Secure.putInt(getContentResolver(), Settings.Secure.INSTALL_NON_MARKET_APPS,
enabled ? 1 : 0);
}
private boolean isNonMarketAppsAllowed() {
return Settings.Secure.getInt(getContentResolver(),
Settings.Secure.INSTALL_NON_MARKET_APPS, 0) > 0;
}
private void warnAppInstallation() {
mWarnInstallApps = new AlertDialog.Builder(this)
.setTitle(getString(R.string.error_title))
.setIcon(com.android.internal.R.drawable.ic_dialog_alert)
.setMessage(getResources().getString(R.string.install_all_warning))
.setPositiveButton(android.R.string.yes, this)
.setNegativeButton(android.R.string.no, null)
.show();
}
}

View File

@@ -0,0 +1,217 @@
package com.android.settings;
import android.app.Activity;
import android.app.AlertDialog;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneFactory;
import android.os.Bundle;
import android.os.Message;
import android.os.Handler;
import android.os.AsyncResult;
import android.util.Log;
import android.content.DialogInterface;
import android.view.View;
import android.view.WindowManager;
import android.view.Window;
import android.widget.ListView;
import android.widget.ArrayAdapter;
import android.widget.AdapterView;
/**
* Radio Band Mode Selection Class
*
* It will query baseband about all available band modes and display them
* in screen. It will display all six band modes if the query failed.
*
* After user select one band, it will send the selection to baseband.
*
* It will alter user the result of select operation and exit, no matter success
* or not.
*
*/
public class BandMode extends Activity {
private static final String LOG_TAG = "phone";
private static final boolean DBG = false;
private static final int EVENT_BAND_SCAN_COMPLETED = 100;
private static final int EVENT_BAND_SELECTION_DONE = 200;
private static final String[] BAND_NAMES = new String[] {
"Automatic",
"EURO Band",
"USA Band",
"JAPAN Band",
"AUS Band",
"AUS2 Band"
};
private ListView mBandList;
private ArrayAdapter mBandListAdapter;
private BandListItem mTargetBand = null;
private DialogInterface mProgressPanel;
private Phone mPhone = null;
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
setContentView(R.layout.band_mode);
setTitle(getString(R.string.band_mode_title));
getWindow().setLayout(WindowManager.LayoutParams.FILL_PARENT,
WindowManager.LayoutParams.WRAP_CONTENT);
mPhone = PhoneFactory.getDefaultPhone();
mBandList = (ListView) findViewById(R.id.band);
mBandListAdapter = new ArrayAdapter<BandListItem>(this,
android.R.layout.simple_list_item_1);
mBandList.setAdapter(mBandListAdapter);
mBandList.setOnItemClickListener(mBandSelectionHandler);
loadBandList();
}
private AdapterView.OnItemClickListener mBandSelectionHandler =
new AdapterView.OnItemClickListener () {
public void onItemClick(AdapterView parent, View v,
int position, long id) {
getWindow().setFeatureInt(
Window.FEATURE_INDETERMINATE_PROGRESS,
Window.PROGRESS_VISIBILITY_ON);
mTargetBand = (BandListItem) parent.getAdapter().getItem(position);
if (DBG) log("Select band : " + mTargetBand.toString());
Message msg =
mHandler.obtainMessage(EVENT_BAND_SELECTION_DONE);
mPhone.setBandMode(mTargetBand.getBand(), msg);
}
};
private class BandListItem {
private int mBandMode = Phone.BM_UNSPECIFIED;
public BandListItem(int bm) {
mBandMode = bm;
}
public int getBand() {
return mBandMode;
}
public String toString() {
return BAND_NAMES[mBandMode];
}
}
private void loadBandList() {
String str = getString(R.string.band_mode_loading);
if (DBG) log(str);
//ProgressDialog.show(this, null, str, true, true, null);
mProgressPanel = new AlertDialog.Builder(this)
.setMessage(str)
.show();
Message msg = mHandler.obtainMessage(EVENT_BAND_SCAN_COMPLETED);
mPhone.queryAvailableBandMode(msg);
}
private void bandListLoaded(AsyncResult result) {
if (DBG) log("network list loaded");
if (mProgressPanel != null) mProgressPanel.dismiss();
clearList();
boolean addBandSuccess = false;
BandListItem item;
if (result.result != null) {
int bands[] = (int[])result.result;
int size = bands[0];
if (size > 0) {
for (int i=1; i<size; i++) {
item = new BandListItem(bands[i]);
mBandListAdapter.add(item);
if (DBG) log("Add " + item.toString());
}
addBandSuccess = true;
}
}
if (addBandSuccess == false) {
if (DBG) log("Error in query, add default list");
for (int i=0; i<Phone.BM_BOUNDARY; i++) {
item = new BandListItem(i);
mBandListAdapter.add(item);
if (DBG) log("Add default " + item.toString());
}
}
mBandList.requestFocus();
}
private void displayBandSelectionResult(Throwable ex) {
String status = getString(R.string.band_mode_set)
+" [" + mTargetBand.toString() + "] ";
if (ex != null) {
status = status + getString(R.string.band_mode_failed);
} else {
status = status + getString(R.string.band_mode_succeeded);
}
mProgressPanel = new AlertDialog.Builder(this)
.setMessage(status)
.setPositiveButton(android.R.string.ok, null).show();
}
private void clearList() {
while(mBandListAdapter.getCount() > 0) {
mBandListAdapter.remove(
mBandListAdapter.getItem(0));
}
}
private void log(String msg) {
Log.d(LOG_TAG, "[BandsList] " + msg);
}
private Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
AsyncResult ar;
switch (msg.what) {
case EVENT_BAND_SCAN_COMPLETED:
ar = (AsyncResult) msg.obj;
bandListLoaded(ar);
break;
case EVENT_BAND_SELECTION_DONE:
ar = (AsyncResult) msg.obj;
getWindow().setFeatureInt(
Window.FEATURE_INDETERMINATE_PROGRESS,
Window.PROGRESS_VISIBILITY_OFF);
displayBandSelectionResult(ar.exception);
break;
}
}
};
}

View File

@@ -0,0 +1,208 @@
/*
* Copyright (C) 2006 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;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.BatteryManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.IPowerManager;
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.text.format.DateUtils;
import android.widget.TextView;
import com.android.internal.app.IBatteryStats;
public class BatteryInfo extends Activity {
private TextView mStatus;
private TextView mLevel;
private TextView mScale;
private TextView mHealth;
private TextView mVoltage;
private TextView mTemperature;
private TextView mTechnology;
private TextView mUptime;
private TextView mAwakeBattery;
private TextView mAwakePlugged;
private TextView mScreenOn;
private IBatteryStats mBatteryStats;
private IPowerManager mScreenStats;
private static final int EVENT_TICK = 1;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case EVENT_TICK:
updateBatteryStats();
sendEmptyMessageDelayed(EVENT_TICK, 1000);
break;
}
}
};
/**
* Format a number of tenths-units as a decimal string without using a
* conversion to float. E.g. 347 -> "34.7"
*/
private final String tenthsToFixedString(int x) {
int tens = x / 10;
return new String("" + tens + "." + (x - 10*tens));
}
/**
*Listens for intent broadcasts
*/
private IntentFilter mIntentFilter;
private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
int plugType = intent.getIntExtra("plugged", 0);
mLevel.setText("" + intent.getIntExtra("level", 0));
mScale.setText("" + intent.getIntExtra("scale", 0));
mVoltage.setText("" + intent.getIntExtra("voltage", 0) + " "
+ getString(R.string.battery_info_voltage_units));
mTemperature.setText("" + tenthsToFixedString(intent.getIntExtra("temperature", 0))
+ getString(R.string.battery_info_temperature_units));
mTechnology.setText("" + intent.getStringExtra("technology"));
int status = intent.getIntExtra("status", BatteryManager.BATTERY_STATUS_UNKNOWN);
String statusString;
if (status == BatteryManager.BATTERY_STATUS_CHARGING) {
statusString = getString(R.string.battery_info_status_charging);
if (plugType > 0) {
statusString = statusString + " " + getString(
(plugType == BatteryManager.BATTERY_PLUGGED_AC)
? R.string.battery_info_status_charging_ac
: R.string.battery_info_status_charging_usb);
}
} else if (status == BatteryManager.BATTERY_STATUS_DISCHARGING) {
statusString = getString(R.string.battery_info_status_discharging);
} else if (status == BatteryManager.BATTERY_STATUS_NOT_CHARGING) {
statusString = getString(R.string.battery_info_status_not_charging);
} else if (status == BatteryManager.BATTERY_STATUS_FULL) {
statusString = getString(R.string.battery_info_status_full);
} else {
statusString = getString(R.string.battery_info_status_unknown);
}
mStatus.setText(statusString);
int health = intent.getIntExtra("health", BatteryManager.BATTERY_HEALTH_UNKNOWN);
String healthString;
if (health == BatteryManager.BATTERY_HEALTH_GOOD) {
healthString = getString(R.string.battery_info_health_good);
} else if (health == BatteryManager.BATTERY_HEALTH_OVERHEAT) {
healthString = getString(R.string.battery_info_health_overheat);
} else if (health == BatteryManager.BATTERY_HEALTH_DEAD) {
healthString = getString(R.string.battery_info_health_dead);
} else if (health == BatteryManager.BATTERY_HEALTH_OVER_VOLTAGE) {
healthString = getString(R.string.battery_info_health_over_voltage);
} else if (health == BatteryManager.BATTERY_HEALTH_UNSPECIFIED_FAILURE) {
healthString = getString(R.string.battery_info_health_unspecified_failure);
} else {
healthString = getString(R.string.battery_info_health_unknown);
}
mHealth.setText(healthString);
}
}
};
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.battery_info);
// create the IntentFilter that will be used to listen
// to battery status broadcasts
mIntentFilter = new IntentFilter();
mIntentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
}
@Override
public void onResume() {
super.onResume();
mStatus = (TextView)findViewById(R.id.status);
mLevel = (TextView)findViewById(R.id.level);
mScale = (TextView)findViewById(R.id.scale);
mHealth = (TextView)findViewById(R.id.health);
mTechnology = (TextView)findViewById(R.id.technology);
mVoltage = (TextView)findViewById(R.id.voltage);
mTemperature = (TextView)findViewById(R.id.temperature);
mUptime = (TextView) findViewById(R.id.uptime);
mAwakeBattery = (TextView) findViewById(R.id.awakeBattery);
mAwakePlugged = (TextView) findViewById(R.id.awakePlugged);
mScreenOn = (TextView) findViewById(R.id.screenOn);
// Get awake time plugged in and on battery
mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
mScreenStats = IPowerManager.Stub.asInterface(ServiceManager.getService(POWER_SERVICE));
mHandler.sendEmptyMessageDelayed(EVENT_TICK, 1000);
registerReceiver(mIntentReceiver, mIntentFilter);
}
@Override
public void onPause() {
super.onPause();
mHandler.removeMessages(EVENT_TICK);
// we are no longer on the screen stop the observers
unregisterReceiver(mIntentReceiver);
}
private void updateBatteryStats() {
long uptime = SystemClock.elapsedRealtime();
mUptime.setText(DateUtils.formatElapsedTime(uptime / 1000));
if (mBatteryStats != null) {
try {
long awakeTimeBattery = mBatteryStats.getAwakeTimeBattery() / 1000;
long awakeTimePluggedIn = mBatteryStats.getAwakeTimePlugged() / 1000;
mAwakeBattery.setText(DateUtils.formatElapsedTime(awakeTimeBattery / 1000)
+ " (" + (100 * awakeTimeBattery / uptime) + "%)");
mAwakePlugged.setText(DateUtils.formatElapsedTime(awakeTimePluggedIn / 1000)
+ " (" + (100 * awakeTimePluggedIn / uptime) + "%)");
} catch (RemoteException re) {
mAwakeBattery.setText("Unknown");
mAwakePlugged.setText("Unknown");
}
}
if (mScreenStats != null) {
try {
long screenOnTime = mScreenStats.getScreenOnTime();
mScreenOn.setText(DateUtils.formatElapsedTime(screenOnTime / 1000));
} catch (RemoteException re) {
mScreenOn.setText("Unknown");
}
}
}
}

View File

@@ -0,0 +1,103 @@
/*
* 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;
import android.content.Context;
import android.os.RemoteException;
import android.os.IHardwareService;
import android.os.ServiceManager;
import android.preference.SeekBarPreference;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.SeekBar;
import java.util.Map;
public class BrightnessPreference extends SeekBarPreference implements
SeekBar.OnSeekBarChangeListener {
private SeekBar mSeekBar;
private int mOldBrightness;
// Backlight range is from 0 - 255. Need to make sure that user
// doesn't set the backlight to 0 and get stuck
private static final int MINIMUM_BACKLIGHT = android.os.Power.BRIGHTNESS_DIM + 10;
private static final int MAXIMUM_BACKLIGHT = android.os.Power.BRIGHTNESS_ON;
public BrightnessPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onBindDialogView(View view) {
super.onBindDialogView(view);
mSeekBar = getSeekBar(view);
mSeekBar.setOnSeekBarChangeListener(this);
mSeekBar.setMax(MAXIMUM_BACKLIGHT - MINIMUM_BACKLIGHT);
try {
mOldBrightness = Settings.System.getInt(getContext().getContentResolver(),
Settings.System.SCREEN_BRIGHTNESS);
} catch (SettingNotFoundException snfe) {
mOldBrightness = MAXIMUM_BACKLIGHT;
}
mSeekBar.setProgress(mOldBrightness - MINIMUM_BACKLIGHT);
}
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromTouch) {
setBrightness(progress + MINIMUM_BACKLIGHT);
}
public void onStartTrackingTouch(SeekBar seekBar) {
// NA
}
public void onStopTrackingTouch(SeekBar seekBar) {
// NA
}
@Override
protected void onDialogClosed(boolean positiveResult) {
super.onDialogClosed(positiveResult);
if (positiveResult) {
Settings.System.putInt(getContext().getContentResolver(),
Settings.System.SCREEN_BRIGHTNESS,
mSeekBar.getProgress() + MINIMUM_BACKLIGHT);
} else {
setBrightness(mOldBrightness);
}
}
private void setBrightness(int brightness) {
try {
IHardwareService hardware = IHardwareService.Stub.asInterface(
ServiceManager.getService("hardware"));
if (hardware != null) {
hardware.setScreenBacklight(brightness);
}
} catch (RemoteException doe) {
}
}
}

View File

@@ -0,0 +1,497 @@
/*
* Copyright (C) 2007 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;
import com.google.android.collect.Lists;
import com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockPatternView;
import static com.android.internal.widget.LockPatternView.DisplayMode;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.View;
import android.view.Window;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* If the user has a lock pattern set already, makes them confirm the existing one.
*
* Then, prompts the user to choose a lock pattern:
* - prompts for initial pattern
* - asks for confirmation / restart
* - saves chosen password when confirmed
*/
public class ChooseLockPattern extends Activity implements View.OnClickListener{
/**
* Used by the choose lock pattern wizard to indicate the wizard is
* finished, and each activity in the wizard should finish.
* <p>
* Previously, each activity in the wizard would finish itself after
* starting the next activity. However, this leads to broken 'Back'
* behavior. So, now an activity does not finish itself until it gets this
* result.
*/
static final int RESULT_FINISHED = RESULT_FIRST_USER;
// how long after a confirmation message is shown before moving on
static final int INFORMATION_MSG_TIMEOUT_MS = 3000;
// how long we wait to clear a wrong pattern
private static final int WRONG_PATTERN_CLEAR_TIMEOUT_MS = 2000;
private static final int ID_EMPTY_MESSAGE = -1;
protected TextView mHeaderText;
protected LockPatternView mLockPatternView;
protected TextView mFooterText;
private TextView mFooterLeftButton;
private TextView mFooterRightButton;
protected List<LockPatternView.Cell> mChosenPattern = null;
protected LockPatternUtils mLockPatternUtils;
/**
* The patten used during the help screen to show how to draw a pattern.
*/
private final List<LockPatternView.Cell> mAnimatePattern =
Collections.unmodifiableList(
Lists.newArrayList(
LockPatternView.Cell.of(0, 0),
LockPatternView.Cell.of(0, 1),
LockPatternView.Cell.of(1, 1),
LockPatternView.Cell.of(2, 1)
));
/**
* The pattern listener that responds according to a user choosing a new
* lock pattern.
*/
protected LockPatternView.OnPatternListener mChooseNewLockPatternListener = new LockPatternView.OnPatternListener() {
public void onPatternStart() {
mLockPatternView.removeCallbacks(mClearPatternRunnable);
patternInProgress();
}
public void onPatternCleared() {
mLockPatternView.removeCallbacks(mClearPatternRunnable);
}
public void onPatternDetected(List<LockPatternView.Cell> pattern) {
if (mUiStage == Stage.NeedToConfirm || mUiStage == Stage.ConfirmWrong) {
if (mChosenPattern == null) throw new IllegalStateException("null chosen pattern in stage 'need to confirm");
if (mChosenPattern.equals(pattern)) {
updateStage(Stage.ChoiceConfirmed);
} else {
updateStage(Stage.ConfirmWrong);
}
} else if (mUiStage == Stage.Introduction || mUiStage == Stage.ChoiceTooShort){
if (pattern.size() < LockPatternUtils.MIN_LOCK_PATTERN_SIZE) {
updateStage(Stage.ChoiceTooShort);
} else {
mChosenPattern = new ArrayList<LockPatternView.Cell>(pattern);
updateStage(Stage.FirstChoiceValid);
}
} else {
throw new IllegalStateException("Unexpected stage " + mUiStage + " when "
+ "entering the pattern.");
}
}
private void patternInProgress() {
mHeaderText.setText(R.string.lockpattern_recording_inprogress);
mFooterText.setText("");
mFooterLeftButton.setEnabled(false);
mFooterRightButton.setEnabled(false);
}
};
/**
* The states of the left footer button.
*/
enum LeftButtonMode {
Cancel(R.string.cancel, true),
CancelDisabled(R.string.cancel, false),
Retry(R.string.lockpattern_retry_button_text, true),
RetryDisabled(R.string.lockpattern_retry_button_text, false),
Gone(ID_EMPTY_MESSAGE, false);
/**
* @param text The displayed text for this mode.
* @param enabled Whether the button should be enabled.
*/
LeftButtonMode(int text, boolean enabled) {
this.text = text;
this.enabled = enabled;
}
final int text;
final boolean enabled;
}
/**
* The states of the right button.
*/
enum RightButtonMode {
Continue(R.string.lockpattern_continue_button_text, true),
ContinueDisabled(R.string.lockpattern_continue_button_text, false),
Confirm(R.string.lockpattern_confirm_button_text, true),
ConfirmDisabled(R.string.lockpattern_confirm_button_text, false),
Ok(android.R.string.ok, true);
/**
* @param text The displayed text for this mode.
* @param enabled Whether the button should be enabled.
*/
RightButtonMode(int text, boolean enabled) {
this.text = text;
this.enabled = enabled;
}
final int text;
final boolean enabled;
}
/**
* Keep track internally of where the user is in choosing a pattern.
*/
protected enum Stage {
Introduction(
R.string.lockpattern_recording_intro_header,
LeftButtonMode.Cancel, RightButtonMode.ContinueDisabled,
R.string.lockpattern_recording_intro_footer, true),
HelpScreen(
R.string.lockpattern_settings_help_how_to_record,
LeftButtonMode.Gone, RightButtonMode.Ok, ID_EMPTY_MESSAGE, false),
ChoiceTooShort(
R.string.lockpattern_recording_incorrect_too_short,
LeftButtonMode.Retry, RightButtonMode.ContinueDisabled,
ID_EMPTY_MESSAGE, true),
FirstChoiceValid(
R.string.lockpattern_pattern_entered_header,
LeftButtonMode.Retry, RightButtonMode.Continue, ID_EMPTY_MESSAGE, false),
NeedToConfirm(
R.string.lockpattern_need_to_confirm,
LeftButtonMode.CancelDisabled, RightButtonMode.ConfirmDisabled,
ID_EMPTY_MESSAGE, true),
ConfirmWrong(
R.string.lockpattern_need_to_unlock_wrong,
LeftButtonMode.Cancel, RightButtonMode.ConfirmDisabled,
ID_EMPTY_MESSAGE, true),
ChoiceConfirmed(
R.string.lockpattern_pattern_confirmed_header,
LeftButtonMode.Cancel, RightButtonMode.Confirm, ID_EMPTY_MESSAGE, false);
/**
* @param headerMessage The message displayed at the top.
* @param leftMode The mode of the left button.
* @param rightMode The mode of the right button.
* @param footerMessage The footer message.
* @param patternEnabled Whether the pattern widget is enabled.
*/
Stage(int headerMessage,
LeftButtonMode leftMode,
RightButtonMode rightMode,
int footerMessage, boolean patternEnabled) {
this.headerMessage = headerMessage;
this.leftMode = leftMode;
this.rightMode = rightMode;
this.footerMessage = footerMessage;
this.patternEnabled = patternEnabled;
}
final int headerMessage;
final LeftButtonMode leftMode;
final RightButtonMode rightMode;
final int footerMessage;
final boolean patternEnabled;
}
private Stage mUiStage = Stage.Introduction;
private Runnable mClearPatternRunnable = new Runnable() {
public void run() {
mLockPatternView.clearPattern();
}
};
private static final String KEY_UI_STAGE = "uiStage";
private static final String KEY_PATTERN_CHOICE = "chosenPattern";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mLockPatternUtils = new LockPatternUtils(getContentResolver());
requestWindowFeature(Window.FEATURE_NO_TITLE);
setupViews();
// make it so unhandled touch events within the unlock screen go to the
// lock pattern view.
final LinearLayoutWithDefaultTouchRecepient topLayout
= (LinearLayoutWithDefaultTouchRecepient) findViewById(
R.id.topLayout);
topLayout.setDefaultTouchRecepient(mLockPatternView);
if (savedInstanceState == null) {
// first launch
updateStage(Stage.Introduction);
if (mLockPatternUtils.savedPatternExists()) {
confirmPattern();
}
} else {
// restore from previous state
final String patternString = savedInstanceState.getString(KEY_PATTERN_CHOICE);
if (patternString != null) {
mChosenPattern = LockPatternUtils.stringToPattern(patternString);
}
updateStage(Stage.values()[savedInstanceState.getInt(KEY_UI_STAGE)]);
}
}
/**
* Keep all "find view" related stuff confined to this function since in
* case someone needs to subclass and customize.
*/
protected void setupViews() {
setContentView(R.layout.choose_lock_pattern);
mHeaderText = (TextView) findViewById(R.id.headerText);
mLockPatternView = (LockPatternView) findViewById(R.id.lockPattern);
mLockPatternView.setOnPatternListener(mChooseNewLockPatternListener);
mLockPatternView.setTactileFeedbackEnabled(mLockPatternUtils.isTactileFeedbackEnabled());
mFooterText = (TextView) findViewById(R.id.footerText);
mFooterLeftButton = (TextView) findViewById(R.id.footerLeftButton);
mFooterRightButton = (TextView) findViewById(R.id.footerRightButton);
mFooterLeftButton.setOnClickListener(this);
mFooterRightButton.setOnClickListener(this);
}
public void onClick(View v) {
if (v == mFooterLeftButton) {
if (mUiStage.leftMode == LeftButtonMode.Retry) {
mChosenPattern = null;
mLockPatternView.clearPattern();
updateStage(Stage.Introduction);
} else if (mUiStage.leftMode == LeftButtonMode.Cancel) {
// They are canceling the entire wizard
setResult(RESULT_FINISHED);
finish();
} else {
throw new IllegalStateException("left footer button pressed, but stage of " +
mUiStage + " doesn't make sense");
}
} else if (v == mFooterRightButton) {
if (mUiStage.rightMode == RightButtonMode.Continue) {
if (mUiStage != Stage.FirstChoiceValid) {
throw new IllegalStateException("expected ui stage " + Stage.FirstChoiceValid
+ " when button is " + RightButtonMode.Continue);
}
updateStage(Stage.NeedToConfirm);
} else if (mUiStage.rightMode == RightButtonMode.Confirm) {
if (mUiStage != Stage.ChoiceConfirmed) {
throw new IllegalStateException("expected ui stage " + Stage.ChoiceConfirmed
+ " when button is " + RightButtonMode.Confirm);
}
saveChosenPatternAndFinish();
} else if (mUiStage.rightMode == RightButtonMode.Ok) {
if (mUiStage != Stage.HelpScreen) {
throw new IllegalStateException("Help screen is only mode with ok button, but " +
"stage is " + mUiStage);
}
mLockPatternView.clearPattern();
mLockPatternView.setDisplayMode(DisplayMode.Correct);
updateStage(Stage.Introduction);
}
}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) {
if (mUiStage == Stage.HelpScreen) {
updateStage(Stage.Introduction);
return true;
}
}
if (keyCode == KeyEvent.KEYCODE_MENU && mUiStage == Stage.Introduction) {
updateStage(Stage.HelpScreen);
return true;
}
return super.onKeyDown(keyCode, event);
}
/**
* Launch screen to confirm the existing lock pattern.
* @see #onActivityResult(int, int, android.content.Intent)
*/
protected void confirmPattern() {
final Intent intent = new Intent();
intent.setClassName("com.android.settings", "com.android.settings.ConfirmLockPattern");
startActivityForResult(intent, 55);
}
/**
* @see #confirmPattern
*/
@Override
protected void onActivityResult(int requestCode, int resultCode,
Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode != 55) {
return;
}
if (resultCode != Activity.RESULT_OK) {
setResult(RESULT_FINISHED);
finish();
}
updateStage(Stage.Introduction);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(KEY_UI_STAGE, mUiStage.ordinal());
if (mChosenPattern != null) {
outState.putString(KEY_PATTERN_CHOICE, LockPatternUtils.patternToString(mChosenPattern));
}
}
/**
* Updates the messages and buttons appropriate to what stage the user
* is at in choosing a view. This doesn't handle clearing out the pattern;
* the pattern is expected to be in the right state.
* @param stage
*/
protected void updateStage(Stage stage) {
mUiStage = stage;
// header text, footer text, visibility and
// enabled state all known from the stage
if (stage == Stage.ChoiceTooShort) {
mHeaderText.setText(
getResources().getString(
stage.headerMessage,
LockPatternUtils.MIN_LOCK_PATTERN_SIZE));
} else {
mHeaderText.setText(stage.headerMessage);
}
if (stage.footerMessage == ID_EMPTY_MESSAGE) {
mFooterText.setText("");
} else {
mFooterText.setText(stage.footerMessage);
}
if (stage.leftMode == LeftButtonMode.Gone) {
mFooterLeftButton.setVisibility(View.GONE);
} else {
mFooterLeftButton.setVisibility(View.VISIBLE);
mFooterLeftButton.setText(stage.leftMode.text);
mFooterLeftButton.setEnabled(stage.leftMode.enabled);
}
mFooterRightButton.setText(stage.rightMode.text);
mFooterRightButton.setEnabled(stage.rightMode.enabled);
// same for whether the patten is enabled
if (stage.patternEnabled) {
mLockPatternView.enableInput();
} else {
mLockPatternView.disableInput();
}
// the rest of the stuff varies enough that it is easier just to handle
// on a case by case basis.
mLockPatternView.setDisplayMode(DisplayMode.Correct);
switch (mUiStage) {
case Introduction:
mLockPatternView.clearPattern();
break;
case HelpScreen:
mLockPatternView.setPattern(DisplayMode.Animate, mAnimatePattern);
break;
case ChoiceTooShort:
mLockPatternView.setDisplayMode(DisplayMode.Wrong);
postClearPatternRunnable();
break;
case FirstChoiceValid:
break;
case NeedToConfirm:
mLockPatternView.clearPattern();
break;
case ConfirmWrong:
mLockPatternView.setDisplayMode(DisplayMode.Wrong);
postClearPatternRunnable();
break;
case ChoiceConfirmed:
break;
}
}
// clear the wrong pattern unless they have started a new one
// already
private void postClearPatternRunnable() {
mLockPatternView.removeCallbacks(mClearPatternRunnable);
mLockPatternView.postDelayed(mClearPatternRunnable, WRONG_PATTERN_CLEAR_TIMEOUT_MS);
}
private void saveChosenPatternAndFinish() {
boolean patternExistedBefore = mLockPatternUtils.savedPatternExists();
mLockPatternUtils.saveLockPattern(mChosenPattern);
// if setting pattern for first time, enable the lock gesture. otherwise,
// keep the user's setting.
if (!patternExistedBefore) {
mLockPatternUtils.setLockPatternEnabled(true);
mLockPatternUtils.setVisiblePatternEnabled(true);
}
setResult(RESULT_FINISHED);
finish();
}
}

View File

@@ -0,0 +1,104 @@
/*
* 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;
import android.app.Activity;
import android.content.Intent;
import android.graphics.drawable.AnimationDrawable;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.widget.ImageView;
public class ChooseLockPatternExample extends Activity implements View.OnClickListener {
private static final int REQUESTCODE_CHOOSE = 1;
private static final long START_DELAY = 1000;
protected static final String TAG = "Settings";
private View mNextButton;
private View mSkipButton;
private View mImageView;
private AnimationDrawable mAnimation;
private Handler mHandler = new Handler();
private Runnable mRunnable = new Runnable() {
public void run() {
startAnimation(mAnimation);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.choose_lock_pattern_example);
initViews();
}
@Override
protected void onResume() {
super.onResume();
mHandler.postDelayed(mRunnable, START_DELAY);
}
@Override
protected void onPause() {
super.onPause();
stopAnimation(mAnimation);
}
public void onClick(View v) {
if (v == mSkipButton) {
// Canceling, so finish all
setResult(ChooseLockPattern.RESULT_FINISHED);
finish();
} else if (v == mNextButton) {
stopAnimation(mAnimation);
Intent intent = new Intent(this, ChooseLockPattern.class);
startActivityForResult(intent, REQUESTCODE_CHOOSE);
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUESTCODE_CHOOSE && resultCode == ChooseLockPattern.RESULT_FINISHED) {
setResult(resultCode);
finish();
}
}
private void initViews() {
mNextButton = findViewById(R.id.next_button);
mNextButton.setOnClickListener(this);
mSkipButton = findViewById(R.id.skip_button);
mSkipButton.setOnClickListener(this);
mImageView = (ImageView) findViewById(R.id.lock_anim);
mImageView.setBackgroundResource(R.drawable.lock_anim);
mImageView.setOnClickListener(this);
mAnimation = (AnimationDrawable) mImageView.getBackground();
}
protected void startAnimation(final AnimationDrawable animation) {
if (animation != null && !animation.isRunning()) {
animation.run();
}
}
protected void stopAnimation(final AnimationDrawable animation) {
if (animation != null && animation.isRunning()) animation.stop();
}
}

View File

@@ -0,0 +1,75 @@
/*
* 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;
import com.android.internal.widget.LockPatternUtils;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
public class ChooseLockPatternTutorial extends Activity implements View.OnClickListener {
private static final int REQUESTCODE_EXAMPLE = 1;
private View mNextButton;
private View mSkipButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Don't show the tutorial if the user has seen it before.
LockPatternUtils lockPatternUtils = new LockPatternUtils(getContentResolver());
if (savedInstanceState == null && lockPatternUtils.savedPatternExists()) {
Intent intent = new Intent();
intent.setClassName("com.android.settings", "com.android.settings.ChooseLockPattern");
startActivity(intent);
finish();
} else {
initViews();
}
}
private void initViews() {
setContentView(R.layout.choose_lock_pattern_tutorial);
mNextButton = findViewById(R.id.next_button);
mNextButton.setOnClickListener(this);
mSkipButton = findViewById(R.id.skip_button);
mSkipButton.setOnClickListener(this);
}
public void onClick(View v) {
if (v == mSkipButton) {
// Canceling, so finish all
setResult(ChooseLockPattern.RESULT_FINISHED);
finish();
} else if (v == mNextButton) {
startActivityForResult(new Intent(this, ChooseLockPatternExample.class),
REQUESTCODE_EXAMPLE);
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUESTCODE_EXAMPLE && resultCode == ChooseLockPattern.RESULT_FINISHED) {
setResult(resultCode);
finish();
}
}
}

View File

@@ -0,0 +1,260 @@
/*
* 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;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockPatternView;
import com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient;
import android.app.Activity;
import android.content.Intent;
import android.os.CountDownTimer;
import android.os.SystemClock;
import android.os.Bundle;
import android.widget.TextView;
import android.view.Window;
import java.util.List;
/**
* Launch this when you want the user to confirm their lock pattern.
*
* Sets an activity result of {@link Activity#RESULT_OK} when the user
* successfully confirmed their pattern.
*/
public class ConfirmLockPattern extends Activity {
/**
* Names of {@link CharSequence} fields within the originating {@link Intent}
* that are used to configure the keyguard confirmation view's labeling.
* The view will use the system-defined resource strings for any labels that
* the caller does not supply.
*/
public static final String HEADER_TEXT = "com.android.settings.ConfirmLockPattern.header";
public static final String FOOTER_TEXT = "com.android.settings.ConfirmLockPattern.footer";
public static final String HEADER_WRONG_TEXT = "com.android.settings.ConfirmLockPattern.header_wrong";
public static final String FOOTER_WRONG_TEXT = "com.android.settings.ConfirmLockPattern.footer_wrong";
// how long we wait to clear a wrong pattern
private static final int WRONG_PATTERN_CLEAR_TIMEOUT_MS = 2000;
private static final String KEY_NUM_WRONG_ATTEMPTS = "num_wrong_attempts";
private LockPatternView mLockPatternView;
private LockPatternUtils mLockPatternUtils;
private int mNumWrongConfirmAttempts;
private CountDownTimer mCountdownTimer;
private TextView mHeaderTextView;
private TextView mFooterTextView;
// caller-supplied text for various prompts
private CharSequence mHeaderText;
private CharSequence mFooterText;
private CharSequence mHeaderWrongText;
private CharSequence mFooterWrongText;
private enum Stage {
NeedToUnlock,
NeedToUnlockWrong,
LockedOut
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mLockPatternUtils = new LockPatternUtils(getContentResolver());
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.confirm_lock_pattern);
mHeaderTextView = (TextView) findViewById(R.id.headerText);
mLockPatternView = (LockPatternView) findViewById(R.id.lockPattern);
mFooterTextView = (TextView) findViewById(R.id.footerText);
// make it so unhandled touch events within the unlock screen go to the
// lock pattern view.
final LinearLayoutWithDefaultTouchRecepient topLayout
= (LinearLayoutWithDefaultTouchRecepient) findViewById(
R.id.topLayout);
topLayout.setDefaultTouchRecepient(mLockPatternView);
Intent intent = getIntent();
if (intent != null) {
mHeaderText = intent.getCharSequenceExtra(HEADER_TEXT);
mFooterText = intent.getCharSequenceExtra(FOOTER_TEXT);
mHeaderWrongText = intent.getCharSequenceExtra(HEADER_WRONG_TEXT);
mFooterWrongText = intent.getCharSequenceExtra(FOOTER_WRONG_TEXT);
}
mLockPatternView.setTactileFeedbackEnabled(mLockPatternUtils.isTactileFeedbackEnabled());
mLockPatternView.setOnPatternListener(mConfirmExistingLockPatternListener);
updateStage(Stage.NeedToUnlock);
if (savedInstanceState != null) {
mNumWrongConfirmAttempts = savedInstanceState.getInt(KEY_NUM_WRONG_ATTEMPTS);
} else {
// on first launch, if no lock pattern is set, then finish with
// success (don't want user to get stuck confirming something that
// doesn't exist).
if (!mLockPatternUtils.savedPatternExists()) {
setResult(RESULT_OK);
finish();
}
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
// deliberately not calling super since we are managing this in full
outState.putInt(KEY_NUM_WRONG_ATTEMPTS, mNumWrongConfirmAttempts);
}
@Override
protected void onPause() {
super.onPause();
if (mCountdownTimer != null) {
mCountdownTimer.cancel();
}
}
@Override
protected void onResume() {
super.onResume();
// if the user is currently locked out, enforce it.
long deadline = mLockPatternUtils.getLockoutAttemptDeadline();
if (deadline != 0) {
handleAttemptLockout(deadline);
}
}
private void updateStage(Stage stage) {
switch (stage) {
case NeedToUnlock:
if (mHeaderText != null) {
mHeaderTextView.setText(mHeaderText);
} else {
mHeaderTextView.setText(R.string.lockpattern_need_to_unlock);
}
if (mFooterText != null) {
mFooterTextView.setText(mFooterText);
} else {
mFooterTextView.setText(R.string.lockpattern_need_to_unlock_footer);
}
mLockPatternView.setEnabled(true);
mLockPatternView.enableInput();
break;
case NeedToUnlockWrong:
if (mHeaderWrongText != null) {
mHeaderTextView.setText(mHeaderWrongText);
} else {
mHeaderTextView.setText(R.string.lockpattern_need_to_unlock_wrong);
}
if (mFooterWrongText != null) {
mFooterTextView.setText(mFooterWrongText);
} else {
mFooterTextView.setText(R.string.lockpattern_need_to_unlock_wrong_footer);
}
mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Wrong);
mLockPatternView.setEnabled(true);
mLockPatternView.enableInput();
break;
case LockedOut:
mLockPatternView.clearPattern();
// enabled = false means: disable input, and have the
// appearance of being disabled.
mLockPatternView.setEnabled(false); // appearance of being disabled
break;
}
}
private Runnable mClearPatternRunnable = new Runnable() {
public void run() {
mLockPatternView.clearPattern();
}
};
// clear the wrong pattern unless they have started a new one
// already
private void postClearPatternRunnable() {
mLockPatternView.removeCallbacks(mClearPatternRunnable);
mLockPatternView.postDelayed(mClearPatternRunnable, WRONG_PATTERN_CLEAR_TIMEOUT_MS);
}
/**
* The pattern listener that responds according to a user confirming
* an existing lock pattern.
*/
private LockPatternView.OnPatternListener mConfirmExistingLockPatternListener = new LockPatternView.OnPatternListener() {
public void onPatternStart() {
mLockPatternView.removeCallbacks(mClearPatternRunnable);
}
public void onPatternCleared() {
mLockPatternView.removeCallbacks(mClearPatternRunnable);
}
public void onPatternDetected(List<LockPatternView.Cell> pattern) {
if (mLockPatternUtils.checkPattern(pattern)) {
setResult(RESULT_OK);
finish();
} else {
if (pattern.size() >= LockPatternUtils.MIN_PATTERN_REGISTER_FAIL &&
++mNumWrongConfirmAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) {
long deadline = mLockPatternUtils.setLockoutAttemptDeadline();
handleAttemptLockout(deadline);
} else {
updateStage(Stage.NeedToUnlockWrong);
postClearPatternRunnable();
}
}
}
};
private void handleAttemptLockout(long elapsedRealtimeDeadline) {
updateStage(Stage.LockedOut);
long elapsedRealtime = SystemClock.elapsedRealtime();
mCountdownTimer = new CountDownTimer(
elapsedRealtimeDeadline - elapsedRealtime,
LockPatternUtils.FAILED_ATTEMPT_COUNTDOWN_INTERVAL_MS) {
@Override
public void onTick(long millisUntilFinished) {
mHeaderTextView.setText(R.string.lockpattern_too_many_failed_confirmation_attempts_header);
final int secondsCountdown = (int) (millisUntilFinished / 1000);
mFooterTextView.setText(getString(
R.string.lockpattern_too_many_failed_confirmation_attempts_footer,
secondsCountdown));
}
@Override
public void onFinish() {
mNumWrongConfirmAttempts = 0;
updateStage(Stage.NeedToUnlock);
}
}.start();
}
}

View File

@@ -0,0 +1,366 @@
/*
* 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;
import android.app.Dialog;
import android.app.DatePickerDialog;
import android.app.TimePickerDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.os.Bundle;
import android.os.SystemClock;
import android.preference.CheckBoxPreference;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceScreen;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.text.format.DateFormat;
import android.widget.DatePicker;
import android.widget.TimePicker;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
public class DateTimeSettings
extends PreferenceActivity
implements OnSharedPreferenceChangeListener,
TimePickerDialog.OnTimeSetListener , DatePickerDialog.OnDateSetListener {
private static final String HOURS_12 = "12";
private static final String HOURS_24 = "24";
private Calendar mDummyDate;
private static final String KEY_DATE_FORMAT = "date_format";
private static final String KEY_AUTO_TIME = "auto_time";
private static final int DIALOG_DATEPICKER = 0;
private static final int DIALOG_TIMEPICKER = 1;
private CheckBoxPreference mAutoPref;
private Preference mTimePref;
private Preference mTime24Pref;
private Preference mTimeZone;
private Preference mDatePref;
private ListPreference mDateFormat;
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
addPreferencesFromResource(R.xml.date_time_prefs);
initUI();
}
private void initUI() {
boolean autoEnabled = getAutoState();
mDummyDate = Calendar.getInstance();
mDummyDate.set(mDummyDate.get(Calendar.YEAR), 11, 31, 13, 0, 0);
mAutoPref = (CheckBoxPreference) findPreference(KEY_AUTO_TIME);
mAutoPref.setChecked(autoEnabled);
mTimePref = findPreference("time");
mTime24Pref = findPreference("24 hour");
mTimeZone = findPreference("timezone");
mDatePref = findPreference("date");
mDateFormat = (ListPreference) findPreference(KEY_DATE_FORMAT);
int currentFormatIndex = -1;
String [] dateFormats = getResources().getStringArray(R.array.date_format_values);
String [] formattedDates = new String[dateFormats.length];
String currentFormat = getDateFormat();
// Initialize if DATE_FORMAT is not set in the system settings
// This can happen after a factory reset (or data wipe)
if (currentFormat == null) {
currentFormat = getResources().getString(R.string.default_date_format);
setDateFormat(currentFormat);
}
for (int i = 0; i < formattedDates.length; i++) {
formattedDates[i] = DateFormat.format(dateFormats[i], mDummyDate).toString();
if (currentFormat.equals(dateFormats[i])) currentFormatIndex = i;
}
mDateFormat.setEntries(formattedDates);
mDateFormat.setEntryValues(R.array.date_format_values);
mDateFormat.setValue(currentFormat);
mTimePref.setEnabled(!autoEnabled);
mDatePref.setEnabled(!autoEnabled);
mTimeZone.setEnabled(!autoEnabled);
getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
}
@Override
protected void onResume() {
super.onResume();
((CheckBoxPreference)mTime24Pref).setChecked(is24Hour());
// Register for time ticks and other reasons for time change
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_TIME_TICK);
filter.addAction(Intent.ACTION_TIME_CHANGED);
filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
registerReceiver(mIntentReceiver, filter, null, null);
updateTimeAndDateDisplay();
}
@Override
protected void onPause() {
super.onPause();
unregisterReceiver(mIntentReceiver);
}
private void updateTimeAndDateDisplay() {
java.text.DateFormat shortDateFormat = DateFormat.getDateFormat(this);
Date now = Calendar.getInstance().getTime();
Date dummyDate = mDummyDate.getTime();
mTimePref.setSummary(DateFormat.getTimeFormat(this).format(now));
mTimeZone.setSummary(getTimeZoneText());
mDatePref.setSummary(shortDateFormat.format(now));
mDateFormat.setSummary(shortDateFormat.format(dummyDate));
}
public void onDateSet(DatePicker view, int year, int month, int day) {
Calendar c = Calendar.getInstance();
c.set(Calendar.YEAR, year);
c.set(Calendar.MONTH, month);
c.set(Calendar.DAY_OF_MONTH, day);
long when = c.getTimeInMillis();
if (when / 1000 < Integer.MAX_VALUE) {
SystemClock.setCurrentTimeMillis(when);
}
updateTimeAndDateDisplay();
}
public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
Calendar c = Calendar.getInstance();
c.set(Calendar.HOUR_OF_DAY, hourOfDay);
c.set(Calendar.MINUTE, minute);
long when = c.getTimeInMillis();
if (when / 1000 < Integer.MAX_VALUE) {
SystemClock.setCurrentTimeMillis(when);
}
updateTimeAndDateDisplay();
timeUpdated();
}
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
if (key.equals(KEY_DATE_FORMAT)) {
String format = preferences.getString(key,
getResources().getString(R.string.default_date_format));
Settings.System.putString(getContentResolver(),
Settings.System.DATE_FORMAT, format);
updateTimeAndDateDisplay();
} else if (key.equals(KEY_AUTO_TIME)) {
boolean autoEnabled = preferences.getBoolean(key, true);
Settings.System.putInt(getContentResolver(),
Settings.System.AUTO_TIME,
autoEnabled ? 1 : 0);
mTimePref.setEnabled(!autoEnabled);
mDatePref.setEnabled(!autoEnabled);
mTimeZone.setEnabled(!autoEnabled);
}
}
@Override
public Dialog onCreateDialog(int id) {
Dialog d;
switch (id) {
case DIALOG_DATEPICKER: {
final Calendar calendar = Calendar.getInstance();
d = new DatePickerDialog(
this,
this,
calendar.get(Calendar.YEAR),
calendar.get(Calendar.MONTH),
calendar.get(Calendar.DAY_OF_MONTH));
d.setTitle(getResources().getString(R.string.date_time_changeDate_text));
break;
}
case DIALOG_TIMEPICKER: {
final Calendar calendar = Calendar.getInstance();
d = new TimePickerDialog(
this,
this,
calendar.get(Calendar.HOUR_OF_DAY),
calendar.get(Calendar.MINUTE),
DateFormat.is24HourFormat(this));
d.setTitle(getResources().getString(R.string.date_time_changeTime_text));
break;
}
default:
d = null;
break;
}
return d;
}
@Override
public void onPrepareDialog(int id, Dialog d) {
switch (id) {
case DIALOG_DATEPICKER: {
DatePickerDialog datePicker = (DatePickerDialog)d;
final Calendar calendar = Calendar.getInstance();
datePicker.updateDate(
calendar.get(Calendar.YEAR),
calendar.get(Calendar.MONTH),
calendar.get(Calendar.DAY_OF_MONTH));
break;
}
case DIALOG_TIMEPICKER: {
TimePickerDialog timePicker = (TimePickerDialog)d;
final Calendar calendar = Calendar.getInstance();
timePicker.updateTime(
calendar.get(Calendar.HOUR_OF_DAY),
calendar.get(Calendar.MINUTE));
break;
}
default:
break;
}
}
@Override
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
if (preference == mDatePref) {
showDialog(DIALOG_DATEPICKER);
} else if (preference == mTimePref) {
// The 24-hour mode may have changed, so recreate the dialog
removeDialog(DIALOG_TIMEPICKER);
showDialog(DIALOG_TIMEPICKER);
} else if (preference == mTime24Pref) {
set24Hour(((CheckBoxPreference)mTime24Pref).isChecked());
updateTimeAndDateDisplay();
timeUpdated();
} else if (preference == mTimeZone) {
Intent intent = new Intent();
intent.setClass(this, ZoneList.class);
startActivityForResult(intent, 0);
}
return false;
}
@Override
protected void onActivityResult(int requestCode, int resultCode,
Intent data) {
updateTimeAndDateDisplay();
}
private void timeUpdated() {
Intent timeChanged = new Intent(Intent.ACTION_TIME_CHANGED);
sendBroadcast(timeChanged);
}
/* Get & Set values from the system settings */
private boolean is24Hour() {
return DateFormat.is24HourFormat(this);
}
private void set24Hour(boolean is24Hour) {
Settings.System.putString(getContentResolver(),
Settings.System.TIME_12_24,
is24Hour? HOURS_24 : HOURS_12);
}
private String getDateFormat() {
return Settings.System.getString(getContentResolver(),
Settings.System.DATE_FORMAT);
}
private boolean getAutoState() {
try {
return Settings.System.getInt(getContentResolver(),
Settings.System.AUTO_TIME) > 0;
} catch (SettingNotFoundException snfe) {
return true;
}
}
private void setDateFormat(String format) {
Settings.System.putString(getContentResolver(), Settings.System.DATE_FORMAT, format);
}
/* Helper routines to format timezone */
private String getTimeZoneText() {
TimeZone tz = java.util.Calendar.getInstance().getTimeZone();
boolean daylight = tz.inDaylightTime(new Date());
StringBuilder sb = new StringBuilder();
sb.append(formatOffset(tz.getRawOffset() +
(daylight ? tz.getDSTSavings() : 0))).
append(", ").
append(tz.getDisplayName(daylight, TimeZone.LONG));
return sb.toString();
}
private char[] formatOffset(int off) {
off = off / 1000 / 60;
char[] buf = new char[9];
buf[0] = 'G';
buf[1] = 'M';
buf[2] = 'T';
if (off < 0) {
buf[3] = '-';
off = -off;
} else {
buf[3] = '+';
}
int hours = off / 60;
int minutes = off % 60;
buf[4] = (char) ('0' + hours / 10);
buf[5] = (char) ('0' + hours % 10);
buf[6] = ':';
buf[7] = (char) ('0' + minutes / 10);
buf[8] = (char) ('0' + minutes % 10);
return buf;
}
private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
updateTimeAndDateDisplay();
}
};
}

View File

@@ -0,0 +1,41 @@
/*
* 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;
import android.os.Bundle;
import android.view.View;
import android.view.Window;
import android.view.View.OnClickListener;
import android.widget.LinearLayout;
public class DateTimeSettingsSetupWizard extends DateTimeSettings implements OnClickListener {
private View mNextButton;
@Override
protected void onCreate(Bundle icicle) {
requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(icicle);
setContentView(R.layout.date_time_settings_setupwizard);
mNextButton = findViewById(R.id.next_button);
mNextButton.setOnClickListener(this);
}
public void onClick(View v) {
setResult(RESULT_OK);
finish();
}
}

View File

@@ -0,0 +1,88 @@
/*
* Copyright (C) 2006 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;
import android.app.Activity;
import android.widget.EditText;
import android.widget.Button;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.text.TextUtils;
import android.text.Spannable;
import android.text.Selection;
import android.net.Uri;
/**
* A simple activity that provides a UI for sending intents
*/
public class DebugIntentSender extends Activity {
private EditText mIntentField;
private EditText mDataField;
private EditText mAccountField;
private EditText mResourceField;
private Button mSendBroadcastButton;
private Button mStartActivityButton;
private View.OnClickListener mClicked = new View.OnClickListener() {
public void onClick(View v) {
if ((v == mSendBroadcastButton) ||
(v == mStartActivityButton)) {
String intentAction = mIntentField.getText().toString();
String intentData = mDataField.getText().toString();
String account = mAccountField.getText().toString();
String resource = mResourceField.getText().toString();
Intent intent = new Intent(intentAction);
if (!TextUtils.isEmpty(intentData)) {
intent.setData(Uri.parse(intentData));
}
intent.putExtra("account", account);
intent.putExtra("resource", resource);
if (v == mSendBroadcastButton) {
sendBroadcast(intent);
} else {
startActivity(intent);
}
setResult(RESULT_OK);
finish();
}
}
};
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.intent_sender);
mIntentField = (EditText) findViewById(R.id.intent);
mIntentField.setText(Intent.ACTION_SYNC);
Selection.selectAll((Spannable) mIntentField.getText());
mDataField = (EditText) findViewById(R.id.data);
mDataField.setBackgroundResource(android.R.drawable.editbox_background);
mAccountField = (EditText) findViewById(R.id.account);
mResourceField = (EditText) findViewById(R.id.resource);
mSendBroadcastButton = (Button) findViewById(R.id.sendbroadcast);
mSendBroadcastButton.setOnClickListener(mClicked);
mStartActivityButton = (Button) findViewById(R.id.startactivity);
mStartActivityButton.setOnClickListener(mClicked);
}
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright (C) 2007 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;
import android.content.Context;
import android.content.Intent;
import android.media.RingtoneManager;
import android.net.Uri;
import android.preference.RingtonePreference;
import android.util.AttributeSet;
import android.util.Config;
import android.util.Log;
public class DefaultRingtonePreference extends RingtonePreference {
private static final String TAG = "DefaultRingtonePreference";
public DefaultRingtonePreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onPrepareRingtonePickerIntent(Intent ringtonePickerIntent) {
super.onPrepareRingtonePickerIntent(ringtonePickerIntent);
/*
* Since this preference is for choosing the default ringtone, it
* doesn't make sense to show a 'Default' item.
*/
ringtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, false);
/*
* Similarly, 'Silent' shouldn't be shown here.
*/
ringtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, false);
}
@Override
protected void onSaveRingtone(Uri ringtoneUri) {
RingtoneManager.setActualDefaultRingtoneUri(getContext(), getRingtoneType(), ringtoneUri);
}
@Override
protected Uri onRestoreRingtone() {
return RingtoneManager.getActualDefaultRingtoneUri(getContext(), getRingtoneType());
}
}

View File

@@ -0,0 +1,88 @@
/*
* 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;
import android.os.BatteryManager;
import android.os.Bundle;
import android.os.SystemProperties;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceScreen;
import android.preference.CheckBoxPreference;
import android.provider.Settings;
import android.text.TextUtils;
/*
* Displays preferences for application developers.
*/
public class DevelopmentSettings extends PreferenceActivity {
private static final String ENABLE_ADB = "enable_adb";
private static final String KEEP_SCREEN_ON = "keep_screen_on";
private static final String ALLOW_MOCK_LOCATION = "allow_mock_location";
private CheckBoxPreference mEnableAdb;
private CheckBoxPreference mKeepScreenOn;
private CheckBoxPreference mAllowMockLocation;
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
addPreferencesFromResource(R.xml.development_prefs);
mEnableAdb = (CheckBoxPreference) findPreference(ENABLE_ADB);
mKeepScreenOn = (CheckBoxPreference) findPreference(KEEP_SCREEN_ON);
mAllowMockLocation = (CheckBoxPreference) findPreference(ALLOW_MOCK_LOCATION);
}
@Override
protected void onResume() {
super.onResume();
mEnableAdb.setChecked(Settings.Secure.getInt(getContentResolver(),
Settings.Secure.ADB_ENABLED, 0) != 0);
mKeepScreenOn.setChecked(Settings.System.getInt(getContentResolver(),
Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0) != 0);
mAllowMockLocation.setChecked(Settings.Secure.getInt(getContentResolver(),
Settings.Secure.ALLOW_MOCK_LOCATION, 0) != 0);
}
@Override
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
// Those monkeys kept committing suicide, so we add this property
// to disable this functionality
if (!TextUtils.isEmpty(SystemProperties.get("ro.monkey"))) {
return false;
}
if (preference == mEnableAdb) {
Settings.Secure.putInt(getContentResolver(), Settings.Secure.ADB_ENABLED,
mEnableAdb.isChecked() ? 1 : 0);
} else if (preference == mKeepScreenOn) {
Settings.System.putInt(getContentResolver(), Settings.System.STAY_ON_WHILE_PLUGGED_IN,
mKeepScreenOn.isChecked() ?
(BatteryManager.BATTERY_PLUGGED_AC | BatteryManager.BATTERY_PLUGGED_USB) : 0);
} else if (preference == mAllowMockLocation) {
Settings.Secure.putInt(getContentResolver(), Settings.Secure.ALLOW_MOCK_LOCATION,
mAllowMockLocation.isChecked() ? 1 : 0);
}
return false;
}
}

View File

@@ -0,0 +1,153 @@
/*
* 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;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Build;
import android.os.Bundle;
import android.os.SystemProperties;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceGroup;
import android.util.Config;
import android.util.Log;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class DeviceInfoSettings extends PreferenceActivity {
private static final String TAG = "DeviceInfoSettings";
private static final boolean LOGD = false || Config.LOGD;
private static final String KEY_CONTAINER = "container";
private static final String KEY_TEAM = "team";
private static final String KEY_CONTRIBUTORS = "contributors";
private static final String KEY_TERMS = "terms";
private static final String KEY_LICENSE = "license";
private static final String KEY_COPYRIGHT = "copyright";
private static final String KEY_SYSTEM_UPDATE_SETTINGS = "system_update_settings";
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
addPreferencesFromResource(R.xml.device_info_settings);
setStringSummary("firmware_version", Build.VERSION.RELEASE);
setValueSummary("baseband_version", "gsm.version.baseband");
setStringSummary("device_model", Build.MODEL);
setStringSummary("build_number", Build.DISPLAY);
findPreference("kernel_version").setSummary(getFormattedKernelVersion());
/*
* Settings is a generic app and should not contain any device-specific
* info.
*/
// These are contained in the "container" preference group
PreferenceGroup parentPreference = (PreferenceGroup) findPreference(KEY_CONTAINER);
Utils.updatePreferenceToSpecificActivityOrRemove(this, parentPreference, KEY_TERMS,
Utils.UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY);
Utils.updatePreferenceToSpecificActivityOrRemove(this, parentPreference, KEY_LICENSE,
Utils.UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY);
Utils.updatePreferenceToSpecificActivityOrRemove(this, parentPreference, KEY_COPYRIGHT,
Utils.UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY);
Utils.updatePreferenceToSpecificActivityOrRemove(this, parentPreference, KEY_TEAM,
Utils.UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY);
// These are contained by the root preference screen
parentPreference = getPreferenceScreen();
Utils.updatePreferenceToSpecificActivityOrRemove(this, parentPreference,
KEY_SYSTEM_UPDATE_SETTINGS,
Utils.UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY);
Utils.updatePreferenceToSpecificActivityOrRemove(this, parentPreference, KEY_CONTRIBUTORS,
Utils.UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY);
}
private void setStringSummary(String preference, String value) {
try {
findPreference(preference).setSummary(value);
} catch (RuntimeException e) {
findPreference(preference).setSummary(
getResources().getString(R.string.device_info_default));
}
}
private void setValueSummary(String preference, String property) {
try {
findPreference(preference).setSummary(
SystemProperties.get(property,
getResources().getString(R.string.device_info_default)));
} catch (RuntimeException e) {
}
}
private String getFormattedKernelVersion() {
String procVersionStr;
try {
BufferedReader reader = new BufferedReader(new FileReader("/proc/version"), 256);
try {
procVersionStr = reader.readLine();
} finally {
reader.close();
}
final String PROC_VERSION_REGEX =
"\\w+\\s+" + /* ignore: Linux */
"\\w+\\s+" + /* ignore: version */
"([^\\s]+)\\s+" + /* group 1: 2.6.22-omap1 */
"\\(([^\\s@]+(?:@[^\\s.]+)?)[^)]*\\)\\s+" + /* group 2: (xxxxxx@xxxxx.constant) */
"\\([^)]+\\)\\s+" + /* ignore: (gcc ..) */
"([^\\s]+)\\s+" + /* group 3: #26 */
"(?:PREEMPT\\s+)?" + /* ignore: PREEMPT (optional) */
"(.+)"; /* group 4: date */
Pattern p = Pattern.compile(PROC_VERSION_REGEX);
Matcher m = p.matcher(procVersionStr);
if (!m.matches()) {
Log.e(TAG, "Regex did not match on /proc/version: " + procVersionStr);
return "Unavailable";
} else if (m.groupCount() < 4) {
Log.e(TAG, "Regex match on /proc/version only returned " + m.groupCount()
+ " groups");
return "Unavailable";
} else {
return (new StringBuilder(m.group(1)).append("\n").append(
m.group(2)).append(" ").append(m.group(3)).append("\n")
.append(m.group(4))).toString();
}
} catch (IOException e) {
Log.e(TAG,
"IO Exception when getting kernel version for Device Info screen",
e);
return "Unavailable";
}
}
}

View File

@@ -0,0 +1,138 @@
/*
* Copyright (C) 2006 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;
import android.app.Activity;
import android.app.ActivityManagerNative;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.os.Bundle;
import android.os.RemoteException;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.Spinner;
import android.widget.TextView;
public class Display extends Activity implements View.OnClickListener {
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.display);
mFontSize = (Spinner) findViewById(R.id.fontSize);
mFontSize.setOnItemSelectedListener(mFontSizeChanged);
String[] states = new String[3];
Resources r = getResources();
states[0] = r.getString(R.string.small_font);
states[1] = r.getString(R.string.medium_font);
states[2] = r.getString(R.string.large_font);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_spinner_item, states);
adapter.setDropDownViewResource(
android.R.layout.simple_spinner_dropdown_item);
mFontSize.setAdapter(adapter);
mPreview = (TextView) findViewById(R.id.preview);
mPreview.setText(r.getText(R.string.font_size_preview_text));
Button save = (Button) findViewById(R.id.save);
save.setText(r.getText(R.string.font_size_save));
save.setOnClickListener(this);
mTextSizeTyped = new TypedValue();
TypedArray styledAttributes =
obtainStyledAttributes(android.R.styleable.TextView);
styledAttributes.getValue(android.R.styleable.TextView_textSize,
mTextSizeTyped);
DisplayMetrics metrics = getResources().getDisplayMetrics();
mDisplayMetrics = new DisplayMetrics();
mDisplayMetrics.density = metrics.density;
mDisplayMetrics.heightPixels = metrics.heightPixels;
mDisplayMetrics.scaledDensity = metrics.scaledDensity;
mDisplayMetrics.widthPixels = metrics.widthPixels;
mDisplayMetrics.xdpi = metrics.xdpi;
mDisplayMetrics.ydpi = metrics.ydpi;
styledAttributes.recycle();
}
@Override
public void onResume() {
super.onResume();
try {
mCurConfig.updateFrom(
ActivityManagerNative.getDefault().getConfiguration());
} catch (RemoteException e) {
}
if (mCurConfig.fontScale < 1) {
mFontSize.setSelection(0);
} else if (mCurConfig.fontScale > 1) {
mFontSize.setSelection(2);
} else {
mFontSize.setSelection(1);
}
updateFontScale();
}
private void updateFontScale() {
mDisplayMetrics.scaledDensity = mDisplayMetrics.density *
mCurConfig.fontScale;
float size = mTextSizeTyped.getDimension(mDisplayMetrics);
mPreview.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
}
public void onClick(View v) {
try {
ActivityManagerNative.getDefault().updateConfiguration(mCurConfig);
} catch (RemoteException e) {
}
finish();
}
private Spinner.OnItemSelectedListener mFontSizeChanged
= new Spinner.OnItemSelectedListener() {
public void onItemSelected(android.widget.AdapterView av, View v,
int position, long id) {
if (position == 0) {
mCurConfig.fontScale = .75f;
} else if (position == 2) {
mCurConfig.fontScale = 1.25f;
} else {
mCurConfig.fontScale = 1.0f;
}
updateFontScale();
}
public void onNothingSelected(android.widget.AdapterView av) {
}
};
private Spinner mFontSize;
private TextView mPreview;
private TypedValue mTextSizeTyped;
private DisplayMetrics mDisplayMetrics;
private Configuration mCurConfig = new Configuration();
}

View File

@@ -0,0 +1,84 @@
/*
* 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;
import android.content.Context;
import android.preference.EditTextPreference;
import android.text.method.DigitsKeyListener;
import android.text.method.PasswordTransformationMethod;
import android.util.AttributeSet;
import android.view.View;
import android.widget.EditText;
import java.util.Map;
/**
* TODO: Add a soft dialpad for PIN entry.
*/
class EditPinPreference extends EditTextPreference {
private boolean mDialogOpen;
interface OnPinEnteredListener {
void onPinEntered(EditPinPreference preference, boolean positiveResult);
}
private OnPinEnteredListener mPinListener;
public EditPinPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
public EditPinPreference(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public void setOnPinEnteredListener(OnPinEnteredListener listener) {
mPinListener = listener;
}
@Override
protected void onBindDialogView(View view) {
super.onBindDialogView(view);
final EditText editText = (EditText) view.findViewById(android.R.id.edit);
if (editText != null) {
editText.setSingleLine(true);
editText.setTransformationMethod(PasswordTransformationMethod.getInstance());
editText.setKeyListener(DigitsKeyListener.getInstance());
}
}
public boolean isDialogOpen() {
return mDialogOpen;
}
@Override
protected void onDialogClosed(boolean positiveResult) {
super.onDialogClosed(positiveResult);
mDialogOpen = false;
if (mPinListener != null) {
mPinListener.onPinEntered(this, positiveResult);
}
}
public void showPinDialog() {
mDialogOpen = true;
showDialog(null);
}
}

View File

@@ -0,0 +1,131 @@
/*
* 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;
import android.app.LauncherActivity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.gadget.GadgetProviderInfo;
import android.gadget.GadgetManager;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.View;
import android.widget.ListView;
import android.util.Log;
import java.text.Collator;
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
public class GadgetPickActivity extends LauncherActivity
{
private static final String TAG = "GadgetPickActivity";
GadgetManager mGadgetManager;
int mGadgetId;
public GadgetPickActivity() {
mGadgetManager = GadgetManager.getInstance(this);
}
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
Bundle extras = getIntent().getExtras();
mGadgetId = extras.getInt(GadgetManager.EXTRA_GADGET_ID);
setResultData(RESULT_CANCELED);
}
@Override
public void onListItemClick(ListView l, View v, int position, long id)
{
Intent intent = intentForPosition(position);
int result;
try {
mGadgetManager.bindGadgetId(mGadgetId, intent.getComponent());
result = RESULT_OK;
} catch (IllegalArgumentException e) {
// This is thrown if they're already bound, or otherwise somehow
// bogus. Set the result to canceled, and exit. The app *should*
// clean up at this point. We could pass the error along, but
// it's not clear that that's useful -- the gadget will simply not
// appear.
result = RESULT_CANCELED;
}
setResultData(result);
finish();
}
@Override
public List<ListItem> makeListItems() {
List<GadgetProviderInfo> installed = mGadgetManager.getInstalledProviders();
PackageManager pm = getPackageManager();
Drawable defaultIcon = null;
IconResizer resizer = new IconResizer();
ArrayList<ListItem> result = new ArrayList();
final int N = installed.size();
for (int i=0; i<N; i++) {
GadgetProviderInfo info = installed.get(i);
LauncherActivity.ListItem item = new LauncherActivity.ListItem();
item.packageName = info.provider.getPackageName();
item.className = info.provider.getClassName();
item.label = info.label;
if (info.icon != 0) {
Drawable d = pm.getDrawable( item.packageName, info.icon, null);
if (d != null) {
item.icon = resizer.createIconThumbnail(d);
} else {
Log.w(TAG, "Can't load icon drawable 0x" + Integer.toHexString(info.icon)
+ " for package: " + item.packageName);
}
}
if (item.icon == null) {
// (including error case above)
if (defaultIcon == null) {
// TODO: Load standard icon.
}
item.icon = defaultIcon;
}
result.add(item);
}
Collections.sort(result, new Comparator<ListItem>() {
Collator mCollator = Collator.getInstance();
public int compare(ListItem lhs, ListItem rhs) {
return mCollator.compare(lhs.label, rhs.label);
}
});
return result;
}
void setResultData(int code) {
Intent result = new Intent();
result.putExtra(GadgetManager.EXTRA_GADGET_ID, mGadgetId);
setResult(code, result);
}
}

View File

@@ -0,0 +1,191 @@
/*
* 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;
import java.util.HashSet;
import java.util.List;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceScreen;
import android.preference.CheckBoxPreference;
import android.provider.Settings;
import android.text.TextUtils;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;
/*
* Displays preferences for input methods.
*/
public class InputMethodsSettings extends PreferenceActivity {
private List<InputMethodInfo> mInputMethodProperties;
final TextUtils.SimpleStringSplitter mStringColonSplitter
= new TextUtils.SimpleStringSplitter(':');
private String mLastInputMethodId;
private String mLastTickedInputMethodId;
static public String getInputMethodIdFromKey(String key) {
return key;
}
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
addPreferencesFromResource(R.xml.input_methods_prefs);
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
mInputMethodProperties = imm.getInputMethodList();
mLastInputMethodId = Settings.Secure.getString(getContentResolver(),
Settings.Secure.DEFAULT_INPUT_METHOD);
int N = (mInputMethodProperties == null ? 0 : mInputMethodProperties
.size());
for (int i = 0; i < N; ++i) {
InputMethodInfo property = mInputMethodProperties.get(i);
String prefKey = property.getId();
CharSequence label = property.loadLabel(getPackageManager());
// Add a check box.
CheckBoxPreference chkbxPref = new CheckBoxPreference(this);
chkbxPref.setKey(prefKey);
chkbxPref.setTitle(label);
getPreferenceScreen().addPreference(chkbxPref);
// If setting activity is available, add a setting screen entry.
if (null != property.getSettingsActivity()) {
PreferenceScreen prefScreen = new PreferenceScreen(this, null);
prefScreen.setKey(property.getSettingsActivity());
prefScreen.setTitle(getResources().getString(
R.string.input_methods_settings_label_format, label));
getPreferenceScreen().addPreference(prefScreen);
}
}
}
@Override
protected void onResume() {
super.onResume();
final HashSet<String> enabled = new HashSet<String>();
String enabledStr = Settings.Secure.getString(getContentResolver(),
Settings.Secure.ENABLED_INPUT_METHODS);
if (enabledStr != null) {
final TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
splitter.setString(enabledStr);
while (splitter.hasNext()) {
enabled.add(splitter.next());
}
}
// Update the statuses of the Check Boxes.
int N = mInputMethodProperties.size();
for (int i = 0; i < N; ++i) {
final String id = mInputMethodProperties.get(i).getId();
CheckBoxPreference pref = (CheckBoxPreference) findPreference(mInputMethodProperties
.get(i).getId());
pref.setChecked(enabled.contains(id));
}
mLastTickedInputMethodId = null;
}
@Override
protected void onPause() {
super.onPause();
StringBuilder builder = new StringBuilder(256);
boolean haveLastInputMethod = false;
int firstEnabled = -1;
int N = mInputMethodProperties.size();
for (int i = 0; i < N; ++i) {
final String id = mInputMethodProperties.get(i).getId();
CheckBoxPreference pref = (CheckBoxPreference) findPreference(id);
boolean hasIt = id.equals(mLastInputMethodId);
if (pref.isChecked()) {
if (builder.length() > 0) builder.append(':');
builder.append(id);
if (firstEnabled < 0) {
firstEnabled = i;
}
if (hasIt) haveLastInputMethod = true;
} else if (hasIt) {
mLastInputMethodId = mLastTickedInputMethodId;
}
}
// If the last input method is unset, set it as the first enabled one.
if (null == mLastInputMethodId || "".equals(mLastInputMethodId)) {
if (firstEnabled >= 0) {
mLastInputMethodId = mInputMethodProperties.get(firstEnabled).getId();
} else {
mLastInputMethodId = null;
}
}
Settings.Secure.putString(getContentResolver(),
Settings.Secure.ENABLED_INPUT_METHODS, builder.toString());
Settings.Secure.putString(getContentResolver(),
Settings.Secure.DEFAULT_INPUT_METHOD, mLastInputMethodId);
}
@Override
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
Preference preference) {
// Those monkeys kept committing suicide, so we add this property
// to disable this functionality
if (!TextUtils.isEmpty(SystemProperties.get("ro.monkey"))) {
return false;
}
if (preference instanceof CheckBoxPreference) {
CheckBoxPreference chkPref = (CheckBoxPreference) preference;
String id = getInputMethodIdFromKey(chkPref.getKey());
if (chkPref.isChecked()) {
mLastTickedInputMethodId = id;
} else if (id.equals(mLastTickedInputMethodId)) {
mLastTickedInputMethodId = null;
}
} else if (preference instanceof PreferenceScreen) {
if (preference.getIntent() == null) {
PreferenceScreen pref = (PreferenceScreen) preference;
String activityName = pref.getKey();
String packageName = activityName.substring(0, activityName
.lastIndexOf("."));
if (activityName.length() > 0) {
Intent i = new Intent(Intent.ACTION_MAIN);
i.setClassName(packageName, activityName);
startActivity(i);
}
}
}
return false;
}
}

View File

@@ -0,0 +1,478 @@
/**
* Copyright (C) 2007 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;
import com.android.settings.R;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageStatsObserver;
import android.content.pm.PackageManager;
import android.content.pm.PackageStats;
import android.content.pm.PackageManager.NameNotFoundException;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.format.Formatter;
import android.util.Config;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
import android.content.ComponentName;
import android.view.View;
import android.widget.AppSecurityPermissions;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
/**
* Activity to display application information from Settings. This activity presents
* extended information associated with a package like code, data, total size, permissions
* used by the application and also the set of default launchable activities.
* For system applications, an option to clear user data is displayed only if data size is > 0.
* System applications that do not want clear user data do not have this option.
* For non-system applications, there is no option to clear data. Instead there is an option to
* uninstall the application.
*/
public class InstalledAppDetails extends Activity implements View.OnClickListener, DialogInterface.OnClickListener {
private static final String TAG="InstalledAppDetails";
private static final int _UNKNOWN_APP=R.string.unknown;
private ApplicationInfo mAppInfo;
private Button mAppButton;
private Button mActivitiesButton;
private boolean mCanUninstall;
private boolean localLOGV=Config.LOGV || false;
private TextView mAppSnippetSize;
private TextView mTotalSize;
private TextView mAppSize;
private TextView mDataSize;
private PkgSizeObserver mSizeObserver;
private ClearUserDataObserver mClearDataObserver;
// Views related to cache info
private View mCachePanel;
private TextView mCacheSize;
private Button mClearCacheButton;
private ClearCacheObserver mClearCacheObserver;
private Button mForceStopButton;
PackageStats mSizeInfo;
private Button mManageSpaceButton;
private PackageManager mPm;
//internal constants used in Handler
private static final int OP_SUCCESSFUL = 1;
private static final int OP_FAILED = 2;
private static final int CLEAR_USER_DATA = 1;
private static final int GET_PKG_SIZE = 2;
private static final int CLEAR_CACHE = 3;
private static final String ATTR_PACKAGE_STATS="PackageStats";
// invalid size value used initially and also when size retrieval through PackageManager
// fails for whatever reason
private static final int SIZE_INVALID = -1;
// Resource strings
private CharSequence mInvalidSizeStr;
private CharSequence mComputingStr;
private CharSequence mAppButtonText;
// Possible btn states
private enum AppButtonStates {
CLEAR_DATA,
UNINSTALL,
NONE
}
private AppButtonStates mAppButtonState;
private Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case CLEAR_USER_DATA:
processClearMsg(msg);
break;
case GET_PKG_SIZE:
refreshSizeInfo(msg);
break;
case CLEAR_CACHE:
// Refresh size info
mPm.getPackageSizeInfo(mAppInfo.packageName, mSizeObserver);
break;
default:
break;
}
}
};
private boolean isUninstallable() {
if (((mAppInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0) &&
((mAppInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) == 0)) {
return false;
}
return true;
}
class ClearUserDataObserver extends IPackageDataObserver.Stub {
public void onRemoveCompleted(final String packageName, final boolean succeeded) {
final Message msg = mHandler.obtainMessage(CLEAR_USER_DATA);
msg.arg1 = succeeded?OP_SUCCESSFUL:OP_FAILED;
mHandler.sendMessage(msg);
}
}
class PkgSizeObserver extends IPackageStatsObserver.Stub {
public int idx;
public void onGetStatsCompleted(PackageStats pStats, boolean succeeded) {
Message msg = mHandler.obtainMessage(GET_PKG_SIZE);
Bundle data = new Bundle();
data.putParcelable(ATTR_PACKAGE_STATS, pStats);
msg.setData(data);
mHandler.sendMessage(msg);
}
}
class ClearCacheObserver extends IPackageDataObserver.Stub {
public void onRemoveCompleted(final String packageName, final boolean succeeded) {
final Message msg = mHandler.obtainMessage(CLEAR_CACHE);
msg.arg1 = succeeded?OP_SUCCESSFUL:OP_FAILED;
mHandler.sendMessage(msg);
}
}
private String getSizeStr(long size) {
if (size == SIZE_INVALID) {
return mInvalidSizeStr.toString();
}
return Formatter.formatFileSize(this, size);
}
private void setAppBtnState() {
boolean visible = false;
if(mCanUninstall) {
//app can clear user data
if((mAppInfo.flags & ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA)
== ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA) {
mAppButtonText = getText(R.string.clear_user_data_text);
mAppButtonState = AppButtonStates.CLEAR_DATA;
visible = true;
} else {
//hide button if diableClearUserData is set
visible = false;
mAppButtonState = AppButtonStates.NONE;
}
} else {
visible = true;
mAppButtonState = AppButtonStates.UNINSTALL;
mAppButtonText = getText(R.string.uninstall_text);
}
if(visible) {
mAppButton.setText(mAppButtonText);
mAppButton.setVisibility(View.VISIBLE);
} else {
mAppButton.setVisibility(View.GONE);
}
}
/** Called when the activity is first created. */
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
//get package manager
mPm = getPackageManager();
//get application's name from intent
Intent intent = getIntent();
final String packageName = intent.getStringExtra(ManageApplications.APP_PKG_NAME);
mComputingStr = getText(R.string.computing_size);
// Try retrieving package stats again
CharSequence totalSizeStr, appSizeStr, dataSizeStr;
totalSizeStr = appSizeStr = dataSizeStr = mComputingStr;
if(localLOGV) Log.i(TAG, "Have to compute package sizes");
mSizeObserver = new PkgSizeObserver();
mPm.getPackageSizeInfo(packageName, mSizeObserver);
try {
mAppInfo = mPm.getApplicationInfo(packageName,
PackageManager.GET_UNINSTALLED_PACKAGES);
} catch (NameNotFoundException e) {
Log.e(TAG, "Exception when retrieving package:"+packageName, e);
displayErrorDialog(R.string.app_not_found_dlg_text, true, true);
}
setContentView(R.layout.installed_app_details);
((ImageView)findViewById(R.id.app_icon)).setImageDrawable(mAppInfo.loadIcon(mPm));
//set application name TODO version
CharSequence appName = mAppInfo.loadLabel(mPm);
if(appName == null) {
appName = getString(_UNKNOWN_APP);
}
((TextView)findViewById(R.id.app_name)).setText(appName);
mAppSnippetSize = ((TextView)findViewById(R.id.app_size));
mAppSnippetSize.setText(totalSizeStr);
//TODO download str and download url
//set values on views
mTotalSize = (TextView)findViewById(R.id.total_size_text);
mTotalSize.setText(totalSizeStr);
mAppSize = (TextView)findViewById(R.id.application_size_text);
mAppSize.setText(appSizeStr);
mDataSize = (TextView)findViewById(R.id.data_size_text);
mDataSize.setText(dataSizeStr);
mAppButton = ((Button)findViewById(R.id.uninstall_button));
//determine if app is a system app
mCanUninstall = !isUninstallable();
if(localLOGV) Log.i(TAG, "Is systemPackage "+mCanUninstall);
setAppBtnState();
mManageSpaceButton = (Button)findViewById(R.id.manage_space_button);
if(mAppInfo.manageSpaceActivityName != null) {
mManageSpaceButton.setVisibility(View.VISIBLE);
mManageSpaceButton.setOnClickListener(this);
}
// Cache section
mCachePanel = findViewById(R.id.cache_panel);
mCacheSize = (TextView) findViewById(R.id.cache_size_text);
mCacheSize.setText(mComputingStr);
mClearCacheButton = (Button) findViewById(R.id.clear_cache_button);
mForceStopButton = (Button) findViewById(R.id.force_stop_button);
mForceStopButton.setOnClickListener(this);
//clear activities
mActivitiesButton = (Button)findViewById(R.id.clear_activities_button);
List<ComponentName> prefActList = new ArrayList<ComponentName>();
//intent list cannot be null. so pass empty list
List<IntentFilter> intentList = new ArrayList<IntentFilter>();
mPm.getPreferredActivities(intentList, prefActList, packageName);
if(localLOGV) Log.i(TAG, "Have "+prefActList.size()+" number of activities in prefered list");
TextView autoLaunchView = (TextView)findViewById(R.id.auto_launch);
if(prefActList.size() <= 0) {
//disable clear activities button
autoLaunchView.setText(R.string.auto_launch_disable_text);
mActivitiesButton.setEnabled(false);
} else {
autoLaunchView.setText(R.string.auto_launch_enable_text);
mActivitiesButton.setOnClickListener(this);
}
// security permissions section
LinearLayout permsView = (LinearLayout) findViewById(R.id.permissions_section);
AppSecurityPermissions asp = new AppSecurityPermissions(this, packageName);
if(asp.getPermissionCount() > 0) {
permsView.setVisibility(View.VISIBLE);
// Make the security sections header visible
LinearLayout securityList = (LinearLayout) permsView.findViewById(
R.id.security_settings_list);
securityList.addView(asp.getPermissionsView());
} else {
permsView.setVisibility(View.GONE);
}
}
private void displayErrorDialog(int msgId, final boolean finish, final boolean changed) {
//display confirmation dialog
new AlertDialog.Builder(this)
.setTitle(getString(R.string.app_not_found_dlg_title))
.setIcon(android.R.drawable.ic_dialog_alert)
.setMessage(getString(msgId))
.setNeutralButton(getString(R.string.dlg_ok),
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
//force to recompute changed value
setIntentAndFinish(finish, changed);
}
}
)
.show();
}
private void setIntentAndFinish(boolean finish, boolean appChanged) {
if(localLOGV) Log.i(TAG, "appChanged="+appChanged);
Intent intent = new Intent();
intent.putExtra(ManageApplications.APP_CHG, appChanged);
setResult(ManageApplications.RESULT_OK, intent);
mAppButton.setEnabled(false);
if(finish) {
finish();
}
}
/*
* Private method to handle get size info notification from observer when
* the async operation from PackageManager is complete. The current user data
* info has to be refreshed in the manage applications screen as well as the current screen.
*/
private void refreshSizeInfo(Message msg) {
boolean changed = false;
PackageStats newPs = msg.getData().getParcelable(ATTR_PACKAGE_STATS);
long newTot = newPs.cacheSize+newPs.codeSize+newPs.dataSize;
if(mSizeInfo == null) {
mSizeInfo = newPs;
String str = getSizeStr(newTot);
mTotalSize.setText(str);
mAppSnippetSize.setText(str);
mAppSize.setText(getSizeStr(newPs.codeSize));
mDataSize.setText(getSizeStr(newPs.dataSize));
mCacheSize.setText(getSizeStr(newPs.cacheSize));
} else {
long oldTot = mSizeInfo.cacheSize+mSizeInfo.codeSize+mSizeInfo.dataSize;
if(newTot != oldTot) {
String str = getSizeStr(newTot);
mTotalSize.setText(str);
mAppSnippetSize.setText(str);
changed = true;
}
if(newPs.codeSize != mSizeInfo.codeSize) {
mAppSize.setText(getSizeStr(newPs.codeSize));
changed = true;
}
if(newPs.dataSize != mSizeInfo.dataSize) {
mDataSize.setText(getSizeStr(newPs.dataSize));
changed = true;
}
if(newPs.cacheSize != mSizeInfo.cacheSize) {
mCacheSize.setText(getSizeStr(newPs.cacheSize));
changed = true;
}
if(changed) {
mSizeInfo = newPs;
}
}
long data = mSizeInfo.dataSize;
// Disable button if data is 0
if(mAppButtonState != AppButtonStates.NONE){
mAppButton.setText(mAppButtonText);
if((mAppButtonState == AppButtonStates.CLEAR_DATA) && (data == 0)) {
mAppButton.setEnabled(false);
} else {
mAppButton.setEnabled(true);
mAppButton.setOnClickListener(this);
}
}
refreshCacheInfo(newPs.cacheSize);
}
private void refreshCacheInfo(long cacheSize) {
// Set cache info
mCacheSize.setText(getSizeStr(cacheSize));
if (cacheSize <= 0) {
mClearCacheButton.setEnabled(false);
} else {
mClearCacheButton.setOnClickListener(this);
}
}
/*
* Private method to handle clear message notification from observer when
* the async operation from PackageManager is complete
*/
private void processClearMsg(Message msg) {
int result = msg.arg1;
String packageName = mAppInfo.packageName;
if(result == OP_SUCCESSFUL) {
Log.i(TAG, "Cleared user data for system package:"+packageName);
mPm.getPackageSizeInfo(packageName, mSizeObserver);
} else {
mAppButton.setText(R.string.clear_user_data_text);
mAppButton.setEnabled(true);
}
}
/*
* Private method to initiate clearing user data when the user clicks the clear data
* button for a system package
*/
private void initiateClearUserDataForSysPkg() {
mAppButton.setEnabled(false);
//invoke uninstall or clear user data based on sysPackage
String packageName = mAppInfo.packageName;
Log.i(TAG, "Clearing user data for system package");
if(mClearDataObserver == null) {
mClearDataObserver = new ClearUserDataObserver();
}
ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
boolean res = am.clearApplicationUserData(packageName, mClearDataObserver);
if(!res) {
//doesnt initiate clear. some error. should not happen but just log error for now
Log.i(TAG, "Couldnt clear application user data for package:"+packageName);
displayErrorDialog(R.string.clear_data_failed, false, false);
} else {
mAppButton.setText(R.string.recompute_size);
}
}
/*
* Method implementing functionality of buttons clicked
* @see android.view.View.OnClickListener#onClick(android.view.View)
*/
public void onClick(View v) {
String packageName = mAppInfo.packageName;
if(v == mAppButton) {
if(mCanUninstall) {
//display confirmation dialog
new AlertDialog.Builder(this)
.setTitle(getString(R.string.clear_data_dlg_title))
.setIcon(android.R.drawable.ic_dialog_alert)
.setMessage(getString(R.string.clear_data_dlg_text))
.setPositiveButton(R.string.dlg_ok, this)
.setNegativeButton(R.string.dlg_cancel, this)
.show();
} else {
//create new intent to launch Uninstaller activity
Uri packageURI = Uri.parse("package:"+packageName);
Intent uninstallIntent = new Intent(Intent.ACTION_DELETE, packageURI);
startActivity(uninstallIntent);
setIntentAndFinish(true, true);
}
} else if(v == mActivitiesButton) {
mPm.clearPackagePreferredActivities(packageName);
mActivitiesButton.setEnabled(false);
} else if(v == mManageSpaceButton) {
Intent intent = new Intent(Intent.ACTION_DEFAULT);
intent.setClassName(mAppInfo.packageName, mAppInfo.manageSpaceActivityName);
startActivityForResult(intent, -1);
} else if (v == mClearCacheButton) {
// Lazy initialization of observer
if (mClearCacheObserver == null) {
mClearCacheObserver = new ClearCacheObserver();
}
mPm.deleteApplicationCacheFiles(packageName, mClearCacheObserver);
} else if (v == mForceStopButton) {
ActivityManager am = (ActivityManager)getSystemService(
Context.ACTIVITY_SERVICE);
am.restartPackage(packageName);
}
}
public void onClick(DialogInterface dialog, int which) {
if(which == AlertDialog.BUTTON_POSITIVE) {
//invoke uninstall or clear user data based on sysPackage
initiateClearUserDataForSysPkg();
} else {
//cancel do nothing just retain existing screen
}
}
}

View File

@@ -0,0 +1,247 @@
/*
* 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;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.SystemProperties;
import android.preference.CheckBoxPreference;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceGroup;
import android.preference.PreferenceScreen;
import android.provider.Settings;
import android.provider.Settings.System;
import android.text.TextUtils;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;
import java.util.HashSet;
import java.util.List;
public class LanguageSettings extends PreferenceActivity {
private final String[] mSettingsUiKey = {
"auto_caps",
"auto_replace",
"auto_punctuate",
};
// Note: Order of this array should correspond to the order of the above array
private final String[] mSettingsSystemId = {
System.TEXT_AUTO_CAPS,
System.TEXT_AUTO_REPLACE,
System.TEXT_AUTO_PUNCTUATE,
};
// Note: Order of this array should correspond to the order of the above array
private final int[] mSettingsDefault = {
1,
1,
1,
};
private List<InputMethodInfo> mInputMethodProperties;
final TextUtils.SimpleStringSplitter mStringColonSplitter
= new TextUtils.SimpleStringSplitter(':');
private String mLastInputMethodId;
private String mLastTickedInputMethodId;
static public String getInputMethodIdFromKey(String key) {
return key;
}
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
addPreferencesFromResource(R.xml.language_settings);
if (getAssets().getLocales().length == 1) {
getPreferenceScreen().
removePreference(findPreference("language_category"));
}
Configuration config = getResources().getConfiguration();
if (config.keyboard != Configuration.KEYBOARD_QWERTY) {
getPreferenceScreen().removePreference(
getPreferenceScreen().findPreference("hardkeyboard_category"));
} else {
ContentResolver resolver = getContentResolver();
for (int i = 0; i < mSettingsUiKey.length; i++) {
CheckBoxPreference pref = (CheckBoxPreference) findPreference(mSettingsUiKey[i]);
pref.setChecked(System.getInt(resolver, mSettingsSystemId[i],
mSettingsDefault[i]) > 0);
}
}
onCreateIMM();
}
private void onCreateIMM() {
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
mInputMethodProperties = imm.getInputMethodList();
mLastInputMethodId = Settings.Secure.getString(getContentResolver(),
Settings.Secure.DEFAULT_INPUT_METHOD);
PreferenceGroup textCategory = (PreferenceGroup) findPreference("text_category");
int N = (mInputMethodProperties == null ? 0 : mInputMethodProperties
.size());
for (int i = 0; i < N; ++i) {
InputMethodInfo property = mInputMethodProperties.get(i);
String prefKey = property.getId();
CharSequence label = property.loadLabel(getPackageManager());
// Add a check box.
CheckBoxPreference chkbxPref = new CheckBoxPreference(this);
chkbxPref.setKey(prefKey);
chkbxPref.setTitle(label);
textCategory.addPreference(chkbxPref);
// If setting activity is available, add a setting screen entry.
if (null != property.getSettingsActivity()) {
PreferenceScreen prefScreen = new PreferenceScreen(this, null);
prefScreen.setKey(property.getSettingsActivity());
CharSequence settingsLabel = getResources().getString(
R.string.input_methods_settings_label_format, label);
prefScreen.setTitle(settingsLabel);
textCategory.addPreference(prefScreen);
}
}
}
@Override
protected void onResume() {
super.onResume();
final HashSet<String> enabled = new HashSet<String>();
String enabledStr = Settings.Secure.getString(getContentResolver(),
Settings.Secure.ENABLED_INPUT_METHODS);
if (enabledStr != null) {
final TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
splitter.setString(enabledStr);
while (splitter.hasNext()) {
enabled.add(splitter.next());
}
}
// Update the statuses of the Check Boxes.
int N = mInputMethodProperties.size();
for (int i = 0; i < N; ++i) {
final String id = mInputMethodProperties.get(i).getId();
CheckBoxPreference pref = (CheckBoxPreference) findPreference(mInputMethodProperties
.get(i).getId());
pref.setChecked(enabled.contains(id));
}
mLastTickedInputMethodId = null;
}
@Override
protected void onPause() {
super.onPause();
StringBuilder builder = new StringBuilder(256);
boolean haveLastInputMethod = false;
int firstEnabled = -1;
int N = mInputMethodProperties.size();
for (int i = 0; i < N; ++i) {
final String id = mInputMethodProperties.get(i).getId();
CheckBoxPreference pref = (CheckBoxPreference) findPreference(id);
boolean hasIt = id.equals(mLastInputMethodId);
if (pref.isChecked()) {
if (builder.length() > 0) builder.append(':');
builder.append(id);
if (firstEnabled < 0) {
firstEnabled = i;
}
if (hasIt) haveLastInputMethod = true;
} else if (hasIt) {
mLastInputMethodId = mLastTickedInputMethodId;
}
}
// If the last input method is unset, set it as the first enabled one.
if (null == mLastInputMethodId || "".equals(mLastInputMethodId)) {
if (firstEnabled >= 0) {
mLastInputMethodId = mInputMethodProperties.get(firstEnabled).getId();
} else {
mLastInputMethodId = null;
}
}
Settings.Secure.putString(getContentResolver(),
Settings.Secure.ENABLED_INPUT_METHODS, builder.toString());
Settings.Secure.putString(getContentResolver(),
Settings.Secure.DEFAULT_INPUT_METHOD, mLastInputMethodId);
}
@Override
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
// Physical keyboard stuff
for (int i = 0; i < mSettingsUiKey.length; i++) {
if (mSettingsUiKey[i].equals(preference.getKey())) {
System.putInt(getContentResolver(), mSettingsSystemId[i],
((CheckBoxPreference)preference).isChecked()? 1 : 0);
return true;
}
}
// Input Method stuff
// Those monkeys kept committing suicide, so we add this property
// to disable this functionality
if (!TextUtils.isEmpty(SystemProperties.get("ro.monkey"))) {
return false;
}
if (preference instanceof CheckBoxPreference) {
CheckBoxPreference chkPref = (CheckBoxPreference) preference;
String id = getInputMethodIdFromKey(chkPref.getKey());
if (chkPref.isChecked()) {
mLastTickedInputMethodId = id;
} else if (id.equals(mLastTickedInputMethodId)) {
mLastTickedInputMethodId = null;
}
} else if (preference instanceof PreferenceScreen) {
if (preference.getIntent() == null) {
PreferenceScreen pref = (PreferenceScreen) preference;
String activityName = pref.getKey();
String packageName = activityName.substring(0, activityName
.lastIndexOf("."));
if (activityName.length() > 0) {
Intent i = new Intent(Intent.ACTION_MAIN);
i.setClassName(packageName, activityName);
startActivity(i);
}
}
}
return super.onPreferenceTreeClick(preferenceScreen, preference);
}
}

View File

@@ -0,0 +1,225 @@
/*
* 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;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Intent;
import android.content.ComponentName;
import android.database.Cursor;
import android.database.SQLException;
import android.gadget.GadgetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.provider.BaseColumns;
import android.util.Log;
import java.util.ArrayList;
public class LauncherGadgetBinder extends Activity {
private static final String TAG = "LauncherGadgetBinder";
private static final boolean LOGD = true;
static final String AUTHORITY = "com.android.launcher.settings";
static final String TABLE_FAVORITES = "favorites";
static final String EXTRA_BIND_SOURCES = "com.android.launcher.settings.bindsources";
static final String EXTRA_BIND_TARGETS = "com.android.launcher.settings.bindtargets";
static final String EXTRA_GADGET_BITMAPS = "com.android.camera.gadgetbitmaps";
/**
* {@link ContentProvider} constants pulled over from Launcher
*/
static final class LauncherProvider implements BaseColumns {
static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/" + TABLE_FAVORITES);
static final String ITEM_TYPE = "itemType";
static final String GADGET_ID = "gadgetId";
static final String ICON = "icon";
static final int ITEM_TYPE_GADGET = 4;
static final int ITEM_TYPE_WIDGET_CLOCK = 1000;
static final int ITEM_TYPE_WIDGET_SEARCH = 1001;
static final int ITEM_TYPE_WIDGET_PHOTO_FRAME = 1002;
}
static final String[] BIND_PROJECTION = new String[] {
LauncherProvider._ID,
LauncherProvider.ITEM_TYPE,
LauncherProvider.GADGET_ID,
LauncherProvider.ICON,
};
static final int INDEX_ID = 0;
static final int INDEX_ITEM_TYPE = 1;
static final int INDEX_GADGET_ID = 2;
static final int INDEX_ICON = 3;
static final ComponentName BIND_PHOTO_GADGET = new ComponentName("com.android.camera",
"com.android.camera.PhotoGadgetBind");
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
finish();
// This helper reaches into the Launcher database and binds any unlinked
// gadgets. If will remove any items that can't be bound successfully.
// We protect this binder at the manifest level by asserting the caller
// has the Launcher WRITE_SETTINGS permission.
final Intent intent = getIntent();
final Bundle extras = intent.getExtras();
int[] bindSources = null;
ArrayList<ComponentName> bindTargets = null;
Exception exception = null;
try {
bindSources = extras.getIntArray(EXTRA_BIND_SOURCES);
bindTargets = intent.getParcelableArrayListExtra(EXTRA_BIND_TARGETS);
} catch (ClassCastException ex) {
exception = ex;
}
if (exception != null || bindSources == null || bindTargets == null ||
bindSources.length != bindTargets.size()) {
Log.w(TAG, "Problem reading incoming bind request, or invalid request", exception);
return;
}
final String selectWhere = buildOrWhereString(LauncherProvider.ITEM_TYPE, bindSources);
final ContentResolver resolver = getContentResolver();
final GadgetManager gadgetManager = GadgetManager.getInstance(this);
boolean foundPhotoGadgets = false;
final ArrayList<Integer> photoGadgetIds = new ArrayList<Integer>();
final ArrayList<Bitmap> photoBitmaps = new ArrayList<Bitmap>();
Cursor c = null;
try {
c = resolver.query(LauncherProvider.CONTENT_URI,
BIND_PROJECTION, selectWhere, null, null);
if (LOGD) Log.d(TAG, "found bind cursor count="+c.getCount());
final ContentValues values = new ContentValues();
while (c != null && c.moveToNext()) {
long favoriteId = c.getLong(INDEX_ID);
int itemType = c.getInt(INDEX_ITEM_TYPE);
int gadgetId = c.getInt(INDEX_GADGET_ID);
byte[] iconData = c.getBlob(INDEX_ICON);
// Find the binding target for this type
ComponentName targetGadget = null;
for (int i = 0; i < bindSources.length; i++) {
if (bindSources[i] == itemType) {
targetGadget = bindTargets.get(i);
break;
}
}
if (LOGD) Log.d(TAG, "found matching targetGadget="+targetGadget.toString()+" for favoriteId="+favoriteId);
boolean bindSuccess = false;
try {
gadgetManager.bindGadgetId(gadgetId, targetGadget);
bindSuccess = true;
} catch (RuntimeException ex) {
Log.w(TAG, "Problem binding gadget", ex);
}
// Handle special case of photo gadget by loading bitmap and
// preparing for later binding
if (bindSuccess && iconData != null &&
itemType == LauncherProvider.ITEM_TYPE_WIDGET_PHOTO_FRAME) {
Bitmap bitmap = BitmapFactory.decodeByteArray(iconData, 0, iconData.length);
photoGadgetIds.add(gadgetId);
photoBitmaps.add(bitmap);
foundPhotoGadgets = true;
}
if (LOGD) Log.d(TAG, "after finished, success="+bindSuccess);
// Depending on success, update launcher or remove item
Uri favoritesUri = ContentUris.withAppendedId(LauncherProvider.CONTENT_URI, favoriteId);
if (bindSuccess) {
values.clear();
values.put(LauncherProvider.ITEM_TYPE, LauncherProvider.ITEM_TYPE_GADGET);
values.putNull(LauncherProvider.ICON);
resolver.update(favoritesUri, values, null, null);
} else {
resolver.delete(favoritesUri, null, null);
}
}
} catch (SQLException ex) {
Log.w(TAG, "Problem while binding gadgetIds for Launcher", ex);
} finally {
if (c != null) {
c.close();
}
}
if (foundPhotoGadgets) {
// Convert gadgetIds into int[]
final int N = photoGadgetIds.size();
final int[] photoGadgetIdsArray = new int[N];
for (int i = 0; i < N; i++) {
photoGadgetIdsArray[i] = photoGadgetIds.get(i);
}
// Launch intent over to handle bitmap binding, but we don't need to
// wait around for the result.
final Intent bindIntent = new Intent();
bindIntent.setComponent(BIND_PHOTO_GADGET);
final Bundle bindExtras = new Bundle();
bindExtras.putIntArray(GadgetManager.EXTRA_GADGET_IDS, photoGadgetIdsArray);
bindExtras.putParcelableArrayList(EXTRA_GADGET_BITMAPS, photoBitmaps);
bindIntent.putExtras(bindExtras);
startActivity(bindIntent);
}
if (LOGD) Log.d(TAG, "completely finished with binding for Launcher");
}
/**
* Build a query string that will match any row where the column matches
* anything in the values list.
*/
static String buildOrWhereString(String column, int[] values) {
StringBuilder selectWhere = new StringBuilder();
for (int i = values.length - 1; i >= 0; i--) {
selectWhere.append(column).append("=").append(values[i]);
if (i > 0) {
selectWhere.append(" OR ");
}
}
return selectWhere.toString();
}
}

View File

@@ -0,0 +1,153 @@
/*
* Copyright (C) 2007 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;
import android.app.ActivityManagerNative;
import android.app.IActivityManager;
import android.app.ListActivity;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.util.Log;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.util.Arrays;
import java.util.Locale;
public class LocalePicker extends ListActivity {
private static final String TAG = "LocalePicker";
Loc[] mLocales;
private static class Loc {
String label;
Locale locale;
public Loc(String label, Locale locale) {
this.label = label;
this.locale = locale;
}
@Override
public String toString() {
return this.label;
}
}
int getContentView() {
return R.layout.locale_picker;
}
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(getContentView());
String[] locales = getAssets().getLocales();
Arrays.sort(locales);
final int origSize = locales.length;
Loc[] preprocess = new Loc[origSize];
int finalSize = 0;
for (int i = 0 ; i < origSize; i++ ) {
String s = locales[i];
int len = s.length();
if (len == 2) {
Locale l = new Locale(s);
preprocess[finalSize++] = new Loc(toTitleCase(l.getDisplayLanguage()), l);
} else if (len == 5) {
String language = s.substring(0, 2);
String country = s.substring(3, 5);
Locale l = new Locale(language, country);
if (finalSize == 0) {
preprocess[finalSize++] = new Loc(toTitleCase(l.getDisplayLanguage()), l);
} else {
// check previous entry:
// same lang and no country -> overwrite it with a lang-only name
// same lang and a country -> upgrade to full name and
// insert ours with full name
// diff lang -> insert ours with lang-only name
if (preprocess[finalSize-1].locale.getLanguage().equals(language)) {
String prevCountry = preprocess[finalSize-1].locale.getCountry();
if (prevCountry.length() == 0) {
preprocess[finalSize-1].locale = l;
preprocess[finalSize-1].label = toTitleCase(l.getDisplayLanguage());
} else {
preprocess[finalSize-1].label = toTitleCase(preprocess[finalSize-1].locale.getDisplayName());
preprocess[finalSize++] = new Loc(toTitleCase(l.getDisplayName()), l);
}
} else {
String displayName;
if (s.equals("zz_ZZ")) {
displayName = "Pseudo...";
} else {
displayName = toTitleCase(l.getDisplayLanguage());
}
preprocess[finalSize++] = new Loc(displayName, l);
}
}
}
}
mLocales = new Loc[finalSize];
for (int i = 0; i < finalSize ; i++) {
mLocales[i] = preprocess[i];
}
int layoutId = R.layout.locale_picker_item;
int fieldId = R.id.locale;
ArrayAdapter<Loc> adapter = new ArrayAdapter<Loc>(this, layoutId, fieldId, mLocales);
getListView().setAdapter(adapter);
}
private static String toTitleCase(String s) {
if (s.length() == 0) {
return s;
}
return Character.toUpperCase(s.charAt(0)) + s.substring(1);
}
@Override
public void onResume() {
super.onResume();
getListView().requestFocus();
}
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
try {
IActivityManager am = ActivityManagerNative.getDefault();
Configuration config = am.getConfiguration();
Loc loc = mLocales[position];
config.locale = loc.locale;
// indicate this isn't some passing default - the user wants this remembered
config.userSetLocale = true;
am.updateConfiguration(config);
} catch (RemoteException e) {
// Intentionally left blank
}
finish();
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright (C) 2007 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;
import android.app.ActivityManagerNative;
import android.app.IActivityManager;
import android.app.ListActivity;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.util.Log;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.util.Arrays;
import java.util.Locale;
public class LocalePickerInSetupWizard extends LocalePicker {
@Override
int getContentView() {
return R.layout.locale_picker_in_setupwizard;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,205 @@
/*
* 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;
import com.android.internal.widget.LockPatternUtils;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.ICheckinService;
import android.os.ServiceManager;
import android.os.SystemProperties;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
/**
* Confirm and execute a reset of the device to a clean "just out of the box"
* state. Multiple confirmations are required: first, a general "are you sure
* you want to do this?" prompt, followed by a keyguard pattern trace if the user
* has defined one, followed by a final strongly-worded "THIS WILL ERASE EVERYTHING
* ON THE PHONE" prompt. If at any time the phone is allowed to go to sleep, is
* locked, et cetera, then the confirmation sequence is abandoned.
*/
public class MasterClear extends Activity {
private static final int KEYGUARD_REQUEST = 55;
private LayoutInflater mInflater;
private LockPatternUtils mLockUtils;
private View mInitialView;
private Button mInitiateButton;
private View mFinalView;
private Button mFinalButton;
/**
* The user has gone through the multiple confirmation, so now we go ahead
* and invoke the Checkin Service to reset the device to its factory-default
* state (rebooting in the process).
*/
private Button.OnClickListener mFinalClickListener = new Button.OnClickListener() {
public void onClick(View v) {
// Those monkeys kept committing suicide, so we add this property
// to disable going through with the master clear
if (!TextUtils.isEmpty(SystemProperties.get("ro.monkey"))) {
return;
}
ICheckinService service =
ICheckinService.Stub.asInterface(ServiceManager.getService("checkin"));
if (service != null) {
try {
// This RPC should never return
service.masterClear();
} catch (android.os.RemoteException e) {
// Intentionally blank - there's nothing we can do here
Log.w("MasterClear", "Unable to invoke ICheckinService.masterClear()");
}
} else {
Log.w("MasterClear", "Unable to locate ICheckinService");
}
/* If we reach this point, the master clear didn't happen -- the
* service might have been unregistered with the ServiceManager,
* the RPC might have thrown an exception, or for some reason
* the implementation of masterClear() may have returned instead
* of resetting the device.
*/
new AlertDialog.Builder(MasterClear.this)
.setMessage(getText(R.string.master_clear_failed))
.setPositiveButton(getText(android.R.string.ok), null)
.show();
}
};
/**
* Keyguard validation is run using the standard {@link ConfirmLockPattern}
* component as a subactivity
*/
private void runKeyguardConfirmation() {
final Intent intent = new Intent();
intent.setClassName("com.android.settings",
"com.android.settings.ConfirmLockPattern");
// supply header and footer text in the intent
intent.putExtra(ConfirmLockPattern.HEADER_TEXT,
getText(R.string.master_clear_gesture_prompt));
intent.putExtra(ConfirmLockPattern.FOOTER_TEXT,
getText(R.string.master_clear_gesture_explanation));
startActivityForResult(intent, KEYGUARD_REQUEST);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode != KEYGUARD_REQUEST) {
return;
}
// If the user entered a valid keyguard trace, present the final
// confirmation prompt; otherwise, go back to the initial state.
if (resultCode == Activity.RESULT_OK) {
establishFinalConfirmationState();
} else {
establishInitialState();
}
}
/**
* If the user clicks to begin the reset sequence, we next require a
* keyguard confirmation if the user has currently enabled one. If there
* is no keyguard available, we simply go to the final confirmation prompt.
*/
private Button.OnClickListener mInitiateListener = new Button.OnClickListener() {
public void onClick(View v) {
if (mLockUtils.isLockPatternEnabled()) {
runKeyguardConfirmation();
} else {
establishFinalConfirmationState();
}
}
};
/**
* Configure the UI for the final confirmation interaction
*/
private void establishFinalConfirmationState() {
if (mFinalView == null) {
mFinalView = mInflater.inflate(R.layout.master_clear_final, null);
mFinalButton =
(Button) mFinalView.findViewById(R.id.execute_master_clear);
mFinalButton.setOnClickListener(mFinalClickListener);
}
setContentView(mFinalView);
}
/**
* In its initial state, the activity presents a button for the user to
* click in order to initiate a confirmation sequence. This method is
* called from various other points in the code to reset the activity to
* this base state.
*
* <p>Reinflating views from resources is expensive and prevents us from
* caching widget pointers, so we use a single-inflate pattern: we lazy-
* inflate each view, caching all of the widget pointers we'll need at the
* time, then simply reuse the inflated views directly whenever we need
* to change contents.
*/
private void establishInitialState() {
if (mInitialView == null) {
mInitialView = mInflater.inflate(R.layout.master_clear_primary, null);
mInitiateButton =
(Button) mInitialView.findViewById(R.id.initiate_master_clear);
mInitiateButton.setOnClickListener(mInitiateListener);
}
setContentView(mInitialView);
}
@Override
protected void onCreate(Bundle savedState) {
super.onCreate(savedState);
mInitialView = null;
mFinalView = null;
mInflater = LayoutInflater.from(this);
mLockUtils = new LockPatternUtils(getContentResolver());
establishInitialState();
}
/** Abandon all progress through the confirmation sequence by returning
* to the initial view any time the activity is interrupted (e.g. by
* idle timeout).
*/
@Override
public void onPause() {
super.onPause();
establishInitialState();
}
}

View File

@@ -0,0 +1,193 @@
/*
* 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;
import com.android.internal.widget.LockPatternUtils;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.IMountService;
import android.os.ServiceManager;
import android.os.SystemProperties;
import android.os.Environment;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
/**
* Confirm and execute a format of the sdcard.
* Multiple confirmations are required: first, a general "are you sure
* you want to do this?" prompt, followed by a keyguard pattern trace if the user
* has defined one, followed by a final strongly-worded "THIS WILL ERASE EVERYTHING
* ON THE SD CARD" prompt. If at any time the phone is allowed to go to sleep, is
* locked, et cetera, then the confirmation sequence is abandoned.
*/
public class MediaFormat extends Activity {
private static final int KEYGUARD_REQUEST = 55;
private LayoutInflater mInflater;
private LockPatternUtils mLockUtils;
private View mInitialView;
private Button mInitiateButton;
private View mFinalView;
private Button mFinalButton;
/**
* The user has gone through the multiple confirmation, so now we go ahead
* and invoke the Mount Service to format the SD card.
*/
private Button.OnClickListener mFinalClickListener = new Button.OnClickListener() {
public void onClick(View v) {
// Those monkeys kept committing suicide, so we add this property
// to disable going through with the format
if (!TextUtils.isEmpty(SystemProperties.get("ro.monkey"))) {
return;
}
IMountService service =
IMountService.Stub.asInterface(ServiceManager.getService("mount"));
if (service != null) {
try {
service.formatMedia(Environment.getExternalStorageDirectory().toString());
} catch (android.os.RemoteException e) {
// Intentionally blank - there's nothing we can do here
Log.w("MediaFormat", "Unable to invoke IMountService.formatMedia()");
}
} else {
Log.w("MediaFormat", "Unable to locate IMountService");
}
finish();
}
};
/**
* Keyguard validation is run using the standard {@link ConfirmLockPattern}
* component as a subactivity
*/
private void runKeyguardConfirmation() {
final Intent intent = new Intent();
intent.setClassName("com.android.settings",
"com.android.settings.ConfirmLockPattern");
// supply header and footer text in the intent
intent.putExtra(ConfirmLockPattern.HEADER_TEXT,
getText(R.string.media_format_gesture_prompt));
intent.putExtra(ConfirmLockPattern.FOOTER_TEXT,
getText(R.string.media_format_gesture_explanation));
startActivityForResult(intent, KEYGUARD_REQUEST);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode != KEYGUARD_REQUEST) {
return;
}
// If the user entered a valid keyguard trace, present the final
// confirmation prompt; otherwise, go back to the initial state.
if (resultCode == Activity.RESULT_OK) {
establishFinalConfirmationState();
} else {
establishInitialState();
}
}
/**
* If the user clicks to begin the reset sequence, we next require a
* keyguard confirmation if the user has currently enabled one. If there
* is no keyguard available, we simply go to the final confirmation prompt.
*/
private Button.OnClickListener mInitiateListener = new Button.OnClickListener() {
public void onClick(View v) {
if (mLockUtils.isLockPatternEnabled()) {
runKeyguardConfirmation();
} else {
establishFinalConfirmationState();
}
}
};
/**
* Configure the UI for the final confirmation interaction
*/
private void establishFinalConfirmationState() {
if (mFinalView == null) {
mFinalView = mInflater.inflate(R.layout.media_format_final, null);
mFinalButton =
(Button) mFinalView.findViewById(R.id.execute_media_format);
mFinalButton.setOnClickListener(mFinalClickListener);
}
setContentView(mFinalView);
}
/**
* In its initial state, the activity presents a button for the user to
* click in order to initiate a confirmation sequence. This method is
* called from various other points in the code to reset the activity to
* this base state.
*
* <p>Reinflating views from resources is expensive and prevents us from
* caching widget pointers, so we use a single-inflate pattern: we lazy-
* inflate each view, caching all of the widget pointers we'll need at the
* time, then simply reuse the inflated views directly whenever we need
* to change contents.
*/
private void establishInitialState() {
if (mInitialView == null) {
mInitialView = mInflater.inflate(R.layout.media_format_primary, null);
mInitiateButton =
(Button) mInitialView.findViewById(R.id.initiate_media_format);
mInitiateButton.setOnClickListener(mInitiateListener);
}
setContentView(mInitialView);
}
@Override
protected void onCreate(Bundle savedState) {
super.onCreate(savedState);
mInitialView = null;
mFinalView = null;
mInflater = LayoutInflater.from(this);
mLockUtils = new LockPatternUtils(getContentResolver());
establishInitialState();
}
/** Abandon all progress through the confirmation sequence by returning
* to the initial view any time the activity is interrupted (e.g. by
* idle timeout).
*/
@Override
public void onPause() {
super.onPause();
establishInitialState();
}
}

View File

@@ -0,0 +1,55 @@
/*
* 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;
import android.content.Context;
import android.preference.PreferenceCategory;
import android.util.AttributeSet;
import android.view.View;
import java.util.Map;
public class ProgressCategory extends PreferenceCategory {
private boolean mProgress = false;
public ProgressCategory(Context context, AttributeSet attrs) {
super(context, attrs);
setLayoutResource(R.layout.preference_progress_category);
}
@Override
public void onBindView(View view) {
super.onBindView(view);
View textView = view.findViewById(R.id.scanning_text);
View progressBar = view.findViewById(R.id.scanning_progress);
int visibility = mProgress ? View.VISIBLE : View.INVISIBLE;
textView.setVisibility(visibility);
progressBar.setVisibility(visibility);
}
/**
* Turn on/off the progress indicator and text on the right.
* @param progressOn whether or not the progress should be displayed
*/
public void setProgress(boolean progressOn) {
mProgress = progressOn;
notifyChanged();
}
}

View File

@@ -0,0 +1,260 @@
/*
* Copyright (C) 2006 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;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ContentResolver;
import android.content.Intent;
import android.net.Proxy;
import android.os.Bundle;
import android.provider.Settings;
import android.text.Selection;
import android.text.Spannable;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnFocusChangeListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* To start the Proxy Selector activity, create the following intent.
*
* <code>
* Intent intent = new Intent();
* intent.setClassName("com.android.browser.ProxySelector");
* startActivity(intent);
* </code>
*
* you can add extra options to the intent by using
*
* <code>
* intent.putExtra(key, value);
* </code>
*
* the extra options are:
*
* button-label: a string label to display for the okay button
* title: the title of the window
* error-text: If not null, will be used as the label of the error message.
*/
public class ProxySelector extends Activity
{
private final static String LOGTAG = "Settings";
EditText mHostnameField;
EditText mPortField;
Button mOKButton;
// Matches blank input, ips, and domain names
private static final String HOSTNAME_REGEXP = "^$|^[a-zA-Z0-9]+(\\-[a-zA-Z0-9]+)*(\\.[a-zA-Z0-9]+(\\-[a-zA-Z0-9]+)*)*$";
private static final Pattern HOSTNAME_PATTERN;
static {
HOSTNAME_PATTERN = Pattern.compile(HOSTNAME_REGEXP);
}
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
if (android.util.Config.LOGV) Log.v(LOGTAG, "[ProxySelector] onStart");
setContentView(R.layout.proxy);
initView();
populateFields(false);
}
protected void showError(int error) {
new AlertDialog.Builder(this)
.setTitle(R.string.proxy_error)
.setMessage(error)
.setPositiveButton(R.string.proxy_error_dismiss, null)
.show();
}
void initView() {
mHostnameField = (EditText)findViewById(R.id.hostname);
mHostnameField.setOnFocusChangeListener(mOnFocusChangeHandler);
mPortField = (EditText)findViewById(R.id.port);
mPortField.setOnClickListener(mOKHandler);
mPortField.setOnFocusChangeListener(mOnFocusChangeHandler);
mOKButton = (Button)findViewById(R.id.action);
mOKButton.setOnClickListener(mOKHandler);
Button b = (Button)findViewById(R.id.clear);
b.setOnClickListener(mClearHandler);
b = (Button)findViewById(R.id.defaultView);
b.setOnClickListener(mDefaultHandler);
}
void populateFields(boolean useDefault) {
String hostname = null;
int port = -1;
if (useDefault) {
// Use the default proxy settings provided by the carrier
hostname = Proxy.getDefaultHost();
port = Proxy.getDefaultPort();
} else {
// Use the last setting given by the user
hostname = Proxy.getHost(this);
port = Proxy.getPort(this);
}
if (hostname == null) {
hostname = "";
}
mHostnameField.setText(hostname);
String portStr = port == -1 ? "" : Integer.toString(port);
mPortField.setText(portStr);
Intent intent = getIntent();
String buttonLabel = intent.getStringExtra("button-label");
if (!TextUtils.isEmpty(buttonLabel)) {
mOKButton.setText(buttonLabel);
}
String title = intent.getStringExtra("title");
if (!TextUtils.isEmpty(title)) {
setTitle(title);
}
}
/**
* validate syntax of hostname and port entries
* @return 0 on success, string resource ID on failure
*/
int validate(String hostname, String port) {
Matcher match = HOSTNAME_PATTERN.matcher(hostname);
if (!match.matches()) return R.string.proxy_error_invalid_host;
if (hostname.length() > 0 && port.length() == 0) {
return R.string.proxy_error_empty_port;
}
if (port.length() > 0) {
if (hostname.length() == 0) {
return R.string.proxy_error_empty_host_set_port;
}
int portVal = -1;
try {
portVal = Integer.parseInt(port);
} catch (NumberFormatException ex) {
return R.string.proxy_error_invalid_port;
}
if (portVal <= 0 || portVal > 0xFFFF) {
return R.string.proxy_error_invalid_port;
}
}
return 0;
}
/**
* returns true on success, false if the user must correct something
*/
boolean saveToDb() {
String hostname = mHostnameField.getText().toString().trim();
String portStr = mPortField.getText().toString().trim();
int port = -1;
int result = validate(hostname, portStr);
if (result > 0) {
showError(result);
return false;
}
if (portStr.length() > 0) {
try {
port = Integer.parseInt(portStr);
} catch (NumberFormatException ex) {
return false;
}
}
// FIXME: The best solution would be to make a better UI that would
// disable editing of the text boxes if the user chooses to use the
// default settings. i.e. checking a box to always use the default
// carrier. http:/b/issue?id=756480
// FIXME: This currently will not work if the default host is blank and
// the user has cleared the input boxes in order to not use a proxy.
// This is a UI problem and can be solved with some better form
// controls.
// FIXME: If the user types in a proxy that matches the default, should
// we keep that setting? Can be fixed with a new UI.
ContentResolver res = getContentResolver();
if (hostname.equals(Proxy.getDefaultHost())
&& port == Proxy.getDefaultPort()) {
// If the user hit the default button and didn't change any of
// the input boxes, treat it as if the user has not specified a
// proxy.
hostname = null;
}
if (!TextUtils.isEmpty(hostname)) {
hostname += ':' + portStr;
}
Settings.Secure.putString(res, Settings.Secure.HTTP_PROXY, hostname);
sendBroadcast(new Intent(Proxy.PROXY_CHANGE_ACTION));
return true;
}
OnClickListener mOKHandler = new OnClickListener() {
public void onClick(View v) {
if (saveToDb()) {
finish();
}
}
};
OnClickListener mClearHandler = new OnClickListener() {
public void onClick(View v) {
mHostnameField.setText("");
mPortField.setText("");
}
};
OnClickListener mDefaultHandler = new OnClickListener() {
public void onClick(View v) {
populateFields(true);
}
};
OnFocusChangeListener mOnFocusChangeHandler = new OnFocusChangeListener() {
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
TextView textView = (TextView) v;
Selection.selectAll((Spannable) textView.getText());
}
}
};
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,129 @@
/*
* 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;
import android.content.Context;
import android.media.AudioManager;
import android.preference.VolumePreference;
import android.preference.VolumePreference.SeekBarVolumizer;
import android.provider.Settings;
import android.util.AttributeSet;
import android.view.View;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.SeekBar;
import android.widget.TextView;
/**
* Special preference type that allows configuration of both the ring volume and
* notification volume.
*/
public class RingerVolumePreference extends VolumePreference implements
CheckBox.OnCheckedChangeListener {
private static final String TAG = "RingerVolumePreference";
private CheckBox mNotificationsUseRingVolumeCheckbox;
private SeekBarVolumizer mNotificationSeekBarVolumizer;
private TextView mNotificationVolumeTitle;
public RingerVolumePreference(Context context, AttributeSet attrs) {
super(context, attrs);
// The always visible seekbar is for ring volume
setStreamType(AudioManager.STREAM_RING);
setDialogLayoutResource(R.layout.preference_dialog_ringervolume);
}
@Override
protected void onBindDialogView(View view) {
super.onBindDialogView(view);
mNotificationsUseRingVolumeCheckbox =
(CheckBox) view.findViewById(R.id.same_notification_volume);
mNotificationsUseRingVolumeCheckbox.setOnCheckedChangeListener(this);
mNotificationsUseRingVolumeCheckbox.setChecked(Settings.System.getInt(
getContext().getContentResolver(),
Settings.System.NOTIFICATIONS_USE_RING_VOLUME, 1) == 1);
final SeekBar seekBar = (SeekBar) view.findViewById(R.id.notification_volume_seekbar);
mNotificationSeekBarVolumizer = new SeekBarVolumizer(getContext(), seekBar,
AudioManager.STREAM_NOTIFICATION);
mNotificationVolumeTitle = (TextView) view.findViewById(R.id.notification_volume_title);
setNotificationVolumeVisibility(!mNotificationsUseRingVolumeCheckbox.isChecked());
}
@Override
protected void onDialogClosed(boolean positiveResult) {
super.onDialogClosed(positiveResult);
if (!positiveResult && mNotificationSeekBarVolumizer != null) {
mNotificationSeekBarVolumizer.revertVolume();
}
cleanup();
}
@Override
public void onActivityStop() {
super.onActivityStop();
cleanup();
}
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
setNotificationVolumeVisibility(!isChecked);
Settings.System.putInt(getContext().getContentResolver(),
Settings.System.NOTIFICATIONS_USE_RING_VOLUME, isChecked ? 1 : 0);
if (isChecked) {
// The user wants the notification to be same as ring, so do a
// one-time sync right now
AudioManager audioManager = (AudioManager) getContext()
.getSystemService(Context.AUDIO_SERVICE);
audioManager.setStreamVolume(AudioManager.STREAM_NOTIFICATION,
audioManager.getStreamVolume(AudioManager.STREAM_RING), 0);
}
}
@Override
protected void onSampleStarting(SeekBarVolumizer volumizer) {
super.onSampleStarting(volumizer);
if (mNotificationSeekBarVolumizer != null && volumizer != mNotificationSeekBarVolumizer) {
mNotificationSeekBarVolumizer.stopSample();
}
}
private void setNotificationVolumeVisibility(boolean visible) {
if (mNotificationSeekBarVolumizer != null) {
mNotificationSeekBarVolumizer.getSeekBar().setVisibility(
visible ? View.VISIBLE : View.GONE);
mNotificationVolumeTitle.setVisibility(visible ? View.VISIBLE : View.GONE);
}
}
private void cleanup() {
if (mNotificationSeekBarVolumizer != null) {
mNotificationSeekBarVolumizer.stop();
mNotificationSeekBarVolumizer = null;
}
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright (C) 2007 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;
import android.app.Notification;
import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent;
import android.content.BroadcastReceiver;
import android.util.Config;
import android.util.Log;
/**
*
*/
public class SdCardIntentReceiver extends BroadcastReceiver {
private static final int SDCARD_STATUS = 1;
private static final String TAG = "SdCardIntentReceiver";
@Override
public void onReceive(Context context, Intent intent) {
NotificationManager nm = (NotificationManager) context
.getSystemService(Context.NOTIFICATION_SERVICE);
String action = intent.getAction();
if (Config.LOGD) Log.d(TAG, "onReceiveIntent " + action);
if (action.equals(Intent.ACTION_MEDIA_MOUNTED)) {
nm.cancel(SDCARD_STATUS);
Intent statusIntent = new Intent(Intent.ACTION_MAIN, null);
statusIntent.setClass(context, SdCardSettings.class);
nm.notify(SDCARD_STATUS, new Notification(context,
android.R.drawable.stat_notify_sdcard,
null,
System.currentTimeMillis(),
context.getText(R.string.sdcard_setting),
null,
statusIntent));
} else if (action.equals(Intent.ACTION_MEDIA_REMOVED)) {
nm.cancel(SDCARD_STATUS);
} else if (action.equals(Intent.ACTION_MEDIA_SHARED)) {
nm.cancel(SDCARD_STATUS);
Intent statusIntent = new Intent(Intent.ACTION_MAIN, null);
statusIntent.setClass(context, SdCardSettings.class);
nm.notify(SDCARD_STATUS, new Notification(context,
android.R.drawable.stat_notify_sdcard_usb,
null,
System.currentTimeMillis(),
"SD Card",
null,
statusIntent));
}
}
}

View File

@@ -0,0 +1,232 @@
/*
* Copyright (C) 2007 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;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.Environment;
import android.os.IMountService;
import android.os.ServiceManager;
import android.os.StatFs;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.TextView;
import java.io.File;
public class SdCardSettings extends Activity
{
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.sdcard_settings_screen);
mMountService = IMountService.Stub.asInterface(ServiceManager.getService("mount"));
mRemovedLayout = findViewById(R.id.removed);
mMountedLayout = findViewById(R.id.mounted);
mUnmountedLayout = findViewById(R.id.unmounted);
mScanningLayout = findViewById(R.id.scanning);
mSharedLayout = findViewById(R.id.shared);
mBadRemovalLayout = findViewById(R.id.bad_removal);
mReadOnlyStatus = findViewById(R.id.read_only);
mMassStorage = (CheckBox)findViewById(R.id.mass_storage);
mMassStorage.setOnClickListener(mMassStorageListener);
Button unmountButton = (Button)findViewById(R.id.sdcard_unmount);
unmountButton.setOnClickListener(mUnmountButtonHandler);
Button formatButton = (Button)findViewById(R.id.sdcard_format);
formatButton.setOnClickListener(mFormatButtonHandler);
mTotalSize = (TextView)findViewById(R.id.total);
mUsedSize = (TextView)findViewById(R.id.used);
mAvailableSize = (TextView)findViewById(R.id.available);
// install an intent filter to receive SD card related events.
IntentFilter intentFilter = new IntentFilter(Intent.ACTION_MEDIA_REMOVED);
intentFilter.addAction(Intent.ACTION_MEDIA_UNMOUNTED);
intentFilter.addAction(Intent.ACTION_MEDIA_MOUNTED);
intentFilter.addAction(Intent.ACTION_MEDIA_SHARED);
intentFilter.addAction(Intent.ACTION_MEDIA_CHECKING);
intentFilter.addAction(Intent.ACTION_MEDIA_NOFS);
intentFilter.addAction(Intent.ACTION_MEDIA_BAD_REMOVAL);
intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_STARTED);
intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED);
intentFilter.addDataScheme("file");
registerReceiver(mReceiver, intentFilter);
}
@Override
protected void onDestroy() {
super.onDestroy();
}
@Override
public void onResume() {
super.onResume();
update();
}
private void setLayout(View layout) {
mRemovedLayout.setVisibility(layout == mRemovedLayout ? View.VISIBLE : View.GONE);
mMountedLayout.setVisibility(layout == mMountedLayout ? View.VISIBLE : View.GONE);
mUnmountedLayout.setVisibility(layout == mUnmountedLayout ? View.VISIBLE : View.GONE);
mScanningLayout.setVisibility(layout == mScanningLayout ? View.VISIBLE : View.GONE);
mSharedLayout.setVisibility(layout == mSharedLayout ? View.VISIBLE : View.GONE);
mBadRemovalLayout.setVisibility(layout == mBadRemovalLayout ? View.VISIBLE : View.GONE);
}
private void update() {
try {
mMassStorage.setChecked(mMountService.getMassStorageEnabled());
} catch (RemoteException ex) {
}
String scanVolume = null; // this no longer exists: SystemProperties.get(MediaScanner.CURRENT_VOLUME_PROPERTY, "");
boolean scanning = "external".equals(scanVolume);
if (scanning) {
setLayout(mScanningLayout);
} else {
String status = Environment.getExternalStorageState();
boolean readOnly = false;
if (status.equals(Environment.MEDIA_MOUNTED_READ_ONLY)) {
status = Environment.MEDIA_MOUNTED;
readOnly = true;
}
if (status.equals(Environment.MEDIA_MOUNTED)) {
try {
File path = Environment.getExternalStorageDirectory();
StatFs stat = new StatFs(path.getPath());
long blockSize = stat.getBlockSize();
long totalBlocks = stat.getBlockCount();
long availableBlocks = stat.getAvailableBlocks();
mTotalSize.setText(formatSize(totalBlocks * blockSize));
mUsedSize.setText(formatSize((totalBlocks - availableBlocks) * blockSize));
mAvailableSize.setText(formatSize(availableBlocks * blockSize));
} catch (IllegalArgumentException e) {
// this can occur if the SD card is removed, but we haven't received the
// ACTION_MEDIA_REMOVED Intent yet.
status = Environment.MEDIA_REMOVED;
}
mReadOnlyStatus.setVisibility(readOnly ? View.VISIBLE : View.GONE);
setLayout(mMountedLayout);
} else if (status.equals(Environment.MEDIA_UNMOUNTED)) {
setLayout(mUnmountedLayout);
} else if (status.equals(Environment.MEDIA_REMOVED)) {
setLayout(mRemovedLayout);
} else if (status.equals(Environment.MEDIA_SHARED)) {
setLayout(mSharedLayout);
} else if (status.equals(Environment.MEDIA_BAD_REMOVAL)) {
setLayout(mBadRemovalLayout);
}
}
}
private String formatSize(long size) {
String suffix = null;
// add K or M suffix if size is greater than 1K or 1M
if (size >= 1024) {
suffix = "K";
size /= 1024;
if (size >= 1024) {
suffix = "M";
size /= 1024;
}
}
StringBuilder resultBuffer = new StringBuilder(Long.toString(size));
int commaOffset = resultBuffer.length() - 3;
while (commaOffset > 0) {
resultBuffer.insert(commaOffset, ',');
commaOffset -= 3;
}
if (suffix != null)
resultBuffer.append(suffix);
return resultBuffer.toString();
}
OnClickListener mMassStorageListener = new OnClickListener() {
public void onClick(View v) {
try {
mMountService.setMassStorageEnabled(mMassStorage.isChecked());
} catch (RemoteException ex) {
}
}
};
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
update();
}
};
OnClickListener mUnmountButtonHandler = new OnClickListener() {
public void onClick(View v) {
try {
mMountService.unmountMedia(Environment.getExternalStorageDirectory().toString());
} catch (RemoteException ex) {
}
}
};
OnClickListener mFormatButtonHandler = new OnClickListener() {
public void onClick(View v) {
try {
mMountService.formatMedia(Environment.getExternalStorageDirectory().toString());
} catch (RemoteException ex) {
}
}
};
private int mStatus;
private IMountService mMountService;
private CheckBox mMassStorage;
private TextView mTotalSize;
private TextView mUsedSize;
private TextView mAvailableSize;
private View mRemovedLayout;
private View mMountedLayout;
private View mUnmountedLayout;
private View mScanningLayout;
private View mSharedLayout;
private View mBadRemovalLayout;
private View mReadOnlyStatus;
}

View File

@@ -0,0 +1,304 @@
/*
* Copyright (C) 2007 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;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.location.LocationManager;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceCategory;
import android.preference.PreferenceScreen;
import android.provider.Settings;
import android.util.Config;
import android.util.Log;
import com.android.internal.widget.LockPatternUtils;
/**
* Gesture lock pattern settings.
*/
public class SecuritySettings extends PreferenceActivity
implements SharedPreferences.OnSharedPreferenceChangeListener {
// Lock Settings
private static final String KEY_LOCK_ENABLED = "lockenabled";
private static final String KEY_VISIBLE_PATTERN = "visiblepattern";
private static final String KEY_TACTILE_FEEDBACK_ENABLED = "tactilefeedback";
private static final int CONFIRM_PATTERN_THEN_DISABLE_REQUEST_CODE = 55;
private static final int CONFIRM_PATTERN_THEN_ENABLE_REQUEST_CODE = 56;
private LockPatternUtils mLockPatternUtils;
private CheckBoxPreference mLockEnabled;
private CheckBoxPreference mVisiblePattern;
private CheckBoxPreference mTactileFeedback;
private Preference mChoosePattern;
private CheckBoxPreference mShowPassword;
// Location Settings
private static final String LOCATION_NETWORK = "location_network";
private static final String LOCATION_GPS = "location_gps";
private CheckBoxPreference mNetwork;
private CheckBoxPreference mGps;
private LocationManager mLocationManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.security_settings);
mLockPatternUtils = new LockPatternUtils(getContentResolver());
createPreferenceHierarchy();
// Get the available location providers
mLocationManager = (LocationManager)
getSystemService(Context.LOCATION_SERVICE);
mNetwork = (CheckBoxPreference) getPreferenceScreen().findPreference(LOCATION_NETWORK);
mGps = (CheckBoxPreference) getPreferenceScreen().findPreference(LOCATION_GPS);
getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
updateToggles();
}
private PreferenceScreen createPreferenceHierarchy() {
// Root
PreferenceScreen root = this.getPreferenceScreen();
// Inline preferences
PreferenceCategory inlinePrefCat = new PreferenceCategory(this);
inlinePrefCat.setTitle(R.string.lock_settings_title);
root.addPreference(inlinePrefCat);
// autolock toggle
mLockEnabled = new LockEnabledPref(this);
mLockEnabled.setTitle(R.string.lockpattern_settings_enable_title);
mLockEnabled.setSummary(R.string.lockpattern_settings_enable_summary);
mLockEnabled.setKey(KEY_LOCK_ENABLED);
inlinePrefCat.addPreference(mLockEnabled);
// visible pattern
mVisiblePattern = new CheckBoxPreference(this);
mVisiblePattern.setKey(KEY_VISIBLE_PATTERN);
mVisiblePattern.setTitle(R.string.lockpattern_settings_enable_visible_pattern_title);
inlinePrefCat.addPreference(mVisiblePattern);
// tactile feedback
mTactileFeedback = new CheckBoxPreference(this);
mTactileFeedback.setKey(KEY_TACTILE_FEEDBACK_ENABLED);
mTactileFeedback.setTitle(R.string.lockpattern_settings_enable_tactile_feedback_title);
inlinePrefCat.addPreference(mTactileFeedback);
// change pattern lock
Intent intent = new Intent();
intent.setClassName("com.android.settings",
"com.android.settings.ChooseLockPatternTutorial");
mChoosePattern = getPreferenceManager().createPreferenceScreen(this);
mChoosePattern.setIntent(intent);
inlinePrefCat.addPreference(mChoosePattern);
PreferenceScreen simLockPreferences = getPreferenceManager()
.createPreferenceScreen(this);
simLockPreferences.setTitle(R.string.sim_lock_settings_category);
// Intent to launch SIM lock settings
intent = new Intent();
intent.setClassName("com.android.settings", "com.android.settings.SimLockSettings");
simLockPreferences.setIntent(intent);
PreferenceCategory simLockCat = new PreferenceCategory(this);
simLockCat.setTitle(R.string.sim_lock_settings_title);
root.addPreference(simLockCat);
simLockCat.addPreference(simLockPreferences);
// Passwords
PreferenceCategory passwordsCat = new PreferenceCategory(this);
passwordsCat.setTitle(R.string.security_passwords_title);
root.addPreference(passwordsCat);
CheckBoxPreference showPassword = mShowPassword = new CheckBoxPreference(this);
showPassword.setKey("show_password");
showPassword.setTitle(R.string.show_password);
showPassword.setSummary(R.string.show_password_summary);
showPassword.setPersistent(false);
passwordsCat.addPreference(showPassword);
return root;
}
@Override
protected void onResume() {
super.onResume();
boolean patternExists = mLockPatternUtils.savedPatternExists();
mLockEnabled.setEnabled(patternExists);
mVisiblePattern.setEnabled(patternExists);
mTactileFeedback.setEnabled(patternExists);
mLockEnabled.setChecked(mLockPatternUtils.isLockPatternEnabled());
mVisiblePattern.setChecked(mLockPatternUtils.isVisiblePatternEnabled());
mTactileFeedback.setChecked(mLockPatternUtils.isTactileFeedbackEnabled());
int chooseStringRes = mLockPatternUtils.savedPatternExists() ?
R.string.lockpattern_settings_change_lock_pattern :
R.string.lockpattern_settings_choose_lock_pattern;
mChoosePattern.setTitle(chooseStringRes);
mShowPassword
.setChecked(Settings.System.getInt(getContentResolver(),
Settings.System.TEXT_SHOW_PASSWORD, 1) != 0);
}
@Override
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
Preference preference) {
final String key = preference.getKey();
if (KEY_LOCK_ENABLED.equals(key)) {
mLockPatternUtils.setLockPatternEnabled(isToggled(preference));
} else if (KEY_VISIBLE_PATTERN.equals(key)) {
mLockPatternUtils.setVisiblePatternEnabled(isToggled(preference));
} else if (KEY_TACTILE_FEEDBACK_ENABLED.equals(key)) {
mLockPatternUtils.setTactileFeedbackEnabled(isToggled(preference));
} else if (preference == mShowPassword) {
Settings.System.putInt(getContentResolver(), Settings.System.TEXT_SHOW_PASSWORD,
mShowPassword.isChecked() ? 1 : 0);
}
return false;
}
/*
* Creates toggles for each available location provider
*/
private void updateToggles() {
String providers = getAllowedProviders();
mNetwork.setChecked(providers.contains(LocationManager.NETWORK_PROVIDER));
mGps.setChecked(providers.contains(LocationManager.GPS_PROVIDER));
}
private void updateProviders() {
String preferredProviders = "";
if (mNetwork.isChecked()) {
preferredProviders += LocationManager.NETWORK_PROVIDER;
}
if (mGps.isChecked()) {
preferredProviders += "," + LocationManager.GPS_PROVIDER;
}
setProviders(preferredProviders);
}
private void setProviders(String providers) {
// Update the secure setting LOCATION_PROVIDERS_ALLOWED
Settings.Secure.putString(getContentResolver(),
Settings.Secure.LOCATION_PROVIDERS_ALLOWED, providers);
if (Config.LOGV) {
Log.v("Location Accuracy", "Setting LOCATION_PROVIDERS_ALLOWED = " + providers);
}
// Inform the location manager about the changes
mLocationManager.updateProviders();
}
/**
* @return string containing a list of providers that have been enabled for use
*/
private String getAllowedProviders() {
String allowedProviders =
Settings.Secure.getString(getContentResolver(),
Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
if (allowedProviders == null) {
allowedProviders = "";
}
return allowedProviders;
}
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
if (LOCATION_NETWORK.equals(key) || LOCATION_GPS.equals(key)) {
updateProviders();
}
}
private boolean isToggled(Preference pref) {
return ((CheckBoxPreference) pref).isChecked();
}
/**
* For the user to disable keyguard, we first make them verify their
* existing pattern.
*/
private class LockEnabledPref extends CheckBoxPreference {
public LockEnabledPref(Context context) {
super(context);
}
@Override
protected void onClick() {
if (mLockPatternUtils.savedPatternExists()) {
if (isChecked()) {
confirmPatternThenDisable();
} else {
confirmPatternThenEnable();
}
} else {
super.onClick();
}
}
}
private void confirmPatternThenEnable() {
final Intent intent = new Intent();
intent.setClassName("com.android.settings", "com.android.settings.ConfirmLockPattern");
startActivityForResult(intent, CONFIRM_PATTERN_THEN_ENABLE_REQUEST_CODE);
}
/**
* Launch screen to confirm the existing lock pattern.
* @see #onActivityResult(int, int, android.content.Intent)
*/
private void confirmPatternThenDisable() {
final Intent intent = new Intent();
intent.setClassName("com.android.settings", "com.android.settings.ConfirmLockPattern");
startActivityForResult(intent, CONFIRM_PATTERN_THEN_DISABLE_REQUEST_CODE);
}
/**
* @see #confirmPatternThenDisable
*/
@Override
protected void onActivityResult(int requestCode, int resultCode,
Intent data) {
super.onActivityResult(requestCode, resultCode, data);
final boolean resultOk = resultCode == Activity.RESULT_OK;
if ((requestCode == CONFIRM_PATTERN_THEN_DISABLE_REQUEST_CODE) && resultOk) {
mLockPatternUtils.setLockPatternEnabled(false);
} else if ((requestCode == CONFIRM_PATTERN_THEN_ENABLE_REQUEST_CODE) && resultOk) {
mLockPatternUtils.setLockPatternEnabled(true);
}
}
}

View File

@@ -0,0 +1,46 @@
/*
* 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;
import android.os.Bundle;
import android.preference.PreferenceActivity;
import android.preference.PreferenceGroup;
import android.provider.Settings.System;
public class Settings extends PreferenceActivity {
private static final String KEY_PARENT = "parent";
private static final String KEY_CALL_SETTINGS = "call_settings";
private static final String KEY_SYNC_SETTINGS = "sync_settings";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.settings);
PreferenceGroup parent = (PreferenceGroup) findPreference(KEY_PARENT);
Utils.updatePreferenceToSpecificActivityOrRemove(this, parent, KEY_SYNC_SETTINGS, 0);
}
@Override
protected void onResume() {
super.onResume();
findPreference(KEY_CALL_SETTINGS).setEnabled(!AirplaneModeEnabler.isAirplaneModeOn(this));
}
}

View File

@@ -0,0 +1,123 @@
/*
* Copyright (C) 2007 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;
import android.os.Bundle;
import android.os.SystemProperties;
import android.text.TextUtils;
import android.util.Config;
import android.util.Log;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Toast;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.zip.GZIPInputStream;
import com.android.internal.app.AlertActivity;
import com.android.internal.app.AlertController;
/**
* The "dialog" that shows from "License" in the Settings app.
*/
public class SettingsLicenseActivity extends AlertActivity {
private static final String TAG = "SettingsLicenseActivity";
private static final boolean LOGV = false || Config.LOGV;
private static final String DEFAULT_LICENSE_PATH = "/system/etc/NOTICE.html.gz";
private static final String PROPERTY_LICENSE_PATH = "ro.config.license_path";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String fileName = SystemProperties.get(PROPERTY_LICENSE_PATH, DEFAULT_LICENSE_PATH);
if (TextUtils.isEmpty(fileName)) {
Log.e(TAG, "The system property for the license file is empty.");
showErrorAndFinish();
return;
}
InputStreamReader inputReader = null;
StringBuilder data = null;
try {
data = new StringBuilder(2048);
char tmp[] = new char[2048];
int numRead;
if (fileName.endsWith(".gz")) {
inputReader = new InputStreamReader(
new GZIPInputStream(new FileInputStream(fileName)));
} else {
inputReader = new FileReader(fileName);
}
while ((numRead = inputReader.read(tmp)) >= 0) {
data.append(tmp, 0, numRead);
}
} catch (FileNotFoundException e) {
Log.e(TAG, "License HTML file not found at " + fileName, e);
showErrorAndFinish();
return;
} catch (IOException e) {
Log.e(TAG, "Error reading license HTML file at " + fileName, e);
showErrorAndFinish();
return;
} finally {
try {
if (inputReader != null) {
inputReader.close();
}
} catch (IOException e) {
}
}
if (TextUtils.isEmpty(data)) {
Log.e(TAG, "License HTML is empty (from " + fileName + ")");
showErrorAndFinish();
return;
}
WebView webView = new WebView(this);
// Begin the loading. This will be done in a separate thread in WebView.
webView.loadDataWithBaseURL(null, data.toString(), "text/html", "utf-8", null);
webView.setWebViewClient(new WebViewClient() {
@Override
public void onPageFinished(WebView view, String url) {
// Change from 'Loading...' to the real title
mAlert.setTitle(getString(R.string.settings_license_activity_title));
}
});
final AlertController.AlertParams p = mAlertParams;
p.mTitle = getString(R.string.settings_license_activity_loading);
p.mView = webView;
p.mForceInverseBackground = true;
setupAlert();
}
private void showErrorAndFinish() {
Toast.makeText(this, R.string.settings_license_activity_unavailable, Toast.LENGTH_LONG)
.show();
finish();
}
}

View File

@@ -0,0 +1,321 @@
/*
* 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;
import android.content.Context;
import android.content.res.Resources;
import android.os.AsyncResult;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.CheckBoxPreference;
import android.preference.PreferenceScreen;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneFactory;
import android.widget.Toast;
/**
* Implements the preference screen to enable/disable SIM lock and
* also the dialogs to change the SIM PIN. In the former case, enabling/disabling
* the SIM lock will prompt the user for the current PIN.
* In the Change PIN case, it prompts the user for old pin, new pin and new pin
* again before attempting to change it. Calls the SimCard interface to execute
* these operations.
*
*/
public class SimLockSettings extends PreferenceActivity
implements EditPinPreference.OnPinEnteredListener {
private static final int OFF_MODE = 0;
// State when enabling/disabling SIM lock
private static final int SIM_LOCK_MODE = 1;
// State when entering the old pin
private static final int SIM_OLD_MODE = 2;
// State when entering the new pin - first time
private static final int SIM_NEW_MODE = 3;
// State when entering the new pin - second time
private static final int SIM_REENTER_MODE = 4;
// Keys in xml file
private static final String PIN_DIALOG = "sim_pin";
private static final String PIN_TOGGLE = "sim_toggle";
// Keys in icicle
private static final String DIALOG_STATE = "dialogState";
private static final String DIALOG_PIN = "dialogPin";
private static final String DIALOG_ERROR = "dialogError";
private static final String ENABLE_TO_STATE = "enableState";
private static final int MIN_PIN_LENGTH = 4;
private static final int MAX_PIN_LENGTH = 8;
// Which dialog to show next when popped up
private int mDialogState = OFF_MODE;
private String mPin;
private String mOldPin;
private String mNewPin;
private String mError;
// Are we trying to enable or disable SIM lock?
private boolean mToState;
private Phone mPhone;
private EditPinPreference mPinDialog;
private CheckBoxPreference mPinToggle;
private Resources mRes;
// For async handler to identify request type
private static final int ENABLE_SIM_PIN_COMPLETE = 100;
private static final int CHANGE_SIM_PIN_COMPLETE = 101;
// For replies from SimCard interface
private Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
AsyncResult ar = (AsyncResult) msg.obj;
switch (msg.what) {
case ENABLE_SIM_PIN_COMPLETE:
simLockChanged(ar.exception == null);
break;
case CHANGE_SIM_PIN_COMPLETE:
simPinChanged(ar.exception == null);
break;
}
return;
}
};
// For top-level settings screen to query
static boolean isSimLockEnabled() {
return PhoneFactory.getDefaultPhone().getSimCard().getSimLockEnabled();
}
static String getSummary(Context context) {
Resources res = context.getResources();
String summary = isSimLockEnabled()
? res.getString(R.string.sim_lock_on)
: res.getString(R.string.sim_lock_off);
return summary;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.sim_lock_settings);
mPinDialog = (EditPinPreference) findPreference(PIN_DIALOG);
mPinToggle = (CheckBoxPreference) findPreference(PIN_TOGGLE);
if (savedInstanceState != null && savedInstanceState.containsKey(DIALOG_STATE)) {
mDialogState = savedInstanceState.getInt(DIALOG_STATE);
mPin = savedInstanceState.getString(DIALOG_PIN);
mError = savedInstanceState.getString(DIALOG_ERROR);
mToState = savedInstanceState.getBoolean(ENABLE_TO_STATE);
}
mPinDialog.setOnPinEnteredListener(this);
// Don't need any changes to be remembered
getPreferenceScreen().setPersistent(false);
mPhone = PhoneFactory.getDefaultPhone();
mRes = getResources();
}
@Override
protected void onResume() {
super.onResume();
mPinToggle.setChecked(mPhone.getSimCard().getSimLockEnabled());
if (mDialogState != OFF_MODE) {
showPinDialog();
} else {
// Prep for standard click on "Change PIN"
resetDialogState();
}
}
@Override
protected void onSaveInstanceState(Bundle out) {
// Need to store this state for slider open/close
// There is one case where the dialog is popped up by the preference
// framework. In that case, let the preference framework store the
// dialog state. In other cases, where this activity manually launches
// the dialog, store the state of the dialog.
if (mPinDialog.isDialogOpen()) {
out.putInt(DIALOG_STATE, mDialogState);
out.putString(DIALOG_PIN, mPinDialog.getEditText().getText().toString());
out.putString(DIALOG_ERROR, mError);
out.putBoolean(ENABLE_TO_STATE, mToState);
} else {
super.onSaveInstanceState(out);
}
}
private void showPinDialog() {
if (mDialogState == OFF_MODE) {
return;
}
setDialogValues();
mPinDialog.showPinDialog();
}
private void setDialogValues() {
mPinDialog.setText(mPin);
String message = "";
switch (mDialogState) {
case SIM_LOCK_MODE:
message = mRes.getString(R.string.sim_enter_pin);
mPinDialog.setDialogTitle(mToState
? mRes.getString(R.string.sim_enable_sim_lock)
: mRes.getString(R.string.sim_disable_sim_lock));
break;
case SIM_OLD_MODE:
message = mRes.getString(R.string.sim_enter_old);
mPinDialog.setDialogTitle(mRes.getString(R.string.sim_change_pin));
break;
case SIM_NEW_MODE:
message = mRes.getString(R.string.sim_enter_new);
mPinDialog.setDialogTitle(mRes.getString(R.string.sim_change_pin));
break;
case SIM_REENTER_MODE:
message = mRes.getString(R.string.sim_reenter_new);
mPinDialog.setDialogTitle(mRes.getString(R.string.sim_change_pin));
break;
}
if (mError != null) {
message = mError + "\n" + message;
mError = null;
}
mPinDialog.setDialogMessage(message);
}
public void onPinEntered(EditPinPreference preference, boolean positiveResult) {
if (!positiveResult) {
resetDialogState();
return;
}
mPin = preference.getText();
if (!reasonablePin(mPin)) {
// inject error message and display dialog again
mError = mRes.getString(R.string.sim_bad_pin);
showPinDialog();
return;
}
switch (mDialogState) {
case SIM_LOCK_MODE:
tryChangeSimLockState();
break;
case SIM_OLD_MODE:
mOldPin = mPin;
mDialogState = SIM_NEW_MODE;
mError = null;
mPin = null;
showPinDialog();
break;
case SIM_NEW_MODE:
mNewPin = mPin;
mDialogState = SIM_REENTER_MODE;
mPin = null;
showPinDialog();
break;
case SIM_REENTER_MODE:
if (!mPin.equals(mNewPin)) {
mError = mRes.getString(R.string.sim_pins_dont_match);
mDialogState = SIM_NEW_MODE;
mPin = null;
showPinDialog();
} else {
mError = null;
tryChangePin();
}
break;
}
}
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
if (preference == mPinToggle) {
// Get the new, preferred state
mToState = mPinToggle.isChecked();
// Flip it back and pop up pin dialog
mPinToggle.setChecked(!mToState);
mDialogState = SIM_LOCK_MODE;
showPinDialog();
}
return true;
}
private void tryChangeSimLockState() {
// Try to change sim lock. If it succeeds, toggle the lock state and
// reset dialog state. Else inject error message and show dialog again.
Message callback = Message.obtain(mHandler, ENABLE_SIM_PIN_COMPLETE);
mPhone.getSimCard().setSimLockEnabled(mToState, mPin, callback);
}
private void simLockChanged(boolean success) {
if (success) {
mPinToggle.setChecked(mToState);
} else {
// TODO: I18N
Toast.makeText(this, mRes.getString(R.string.sim_lock_failed), Toast.LENGTH_SHORT)
.show();
}
resetDialogState();
}
private void simPinChanged(boolean success) {
if (!success) {
// TODO: I18N
Toast.makeText(this, mRes.getString(R.string.sim_change_failed),
Toast.LENGTH_SHORT)
.show();
} else {
Toast.makeText(this, mRes.getString(R.string.sim_change_succeeded),
Toast.LENGTH_SHORT)
.show();
}
resetDialogState();
}
private void tryChangePin() {
Message callback = Message.obtain(mHandler, CHANGE_SIM_PIN_COMPLETE);
mPhone.getSimCard().changeSimLockPassword(mOldPin,
mNewPin, callback);
}
private boolean reasonablePin(String pin) {
if (pin == null || pin.length() < MIN_PIN_LENGTH || pin.length() > MAX_PIN_LENGTH) {
return false;
} else {
return true;
}
}
private void resetDialogState() {
mError = null;
mDialogState = SIM_OLD_MODE; // Default for when Change PIN is clicked
mPin = "";
setDialogValues();
}
}

View File

@@ -0,0 +1,245 @@
/*
* Copyright (C) 2007 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;
import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioManager;
import android.os.Bundle;
import android.os.IMountService;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceScreen;
import android.preference.CheckBoxPreference;
import android.provider.Settings;
import android.util.Log;
import android.view.IWindowManager;
public class SoundAndDisplaySettings extends PreferenceActivity implements
Preference.OnPreferenceChangeListener {
private static final String TAG = "SoundAndDisplaysSettings";
/** If there is no setting in the provider, use this. */
private static final int FALLBACK_SCREEN_TIMEOUT_VALUE = 30000;
private static final String KEY_SILENT = "silent";
private static final String KEY_VIBRATE = "vibrate";
private static final String KEY_SCREEN_TIMEOUT = "screen_timeout";
private static final String KEY_DTMF_TONE = "dtmf_tone";
private static final String KEY_SOUND_EFFECTS = "sound_effects";
private static final String KEY_ANIMATIONS = "animations";
private static final String KEY_PLAY_MEDIA_NOTIFICATION_SOUNDS = "play_media_notification_sounds";
private CheckBoxPreference mSilent;
private CheckBoxPreference mPlayMediaNotificationSounds;
private IMountService mMountService = null;
/*
* If we are currently in one of the silent modes (the ringer mode is set to either
* "silent mode" or "vibrate mode"), then toggling the "Phone vibrate"
* preference will switch between "silent mode" and "vibrate mode".
* Otherwise, it will adjust the normal ringer mode's ring or ring+vibrate
* setting.
*/
private CheckBoxPreference mVibrate;
private CheckBoxPreference mDtmfTone;
private CheckBoxPreference mSoundEffects;
private CheckBoxPreference mAnimations;
private float[] mAnimationScales;
private AudioManager mAudioManager;
private IWindowManager mWindowManager;
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
updateState(false);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ContentResolver resolver = getContentResolver();
mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
mWindowManager = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
mMountService = IMountService.Stub.asInterface(ServiceManager.getService("mount"));
addPreferencesFromResource(R.xml.sound_and_display_settings);
mSilent = (CheckBoxPreference) findPreference(KEY_SILENT);
mPlayMediaNotificationSounds = (CheckBoxPreference) findPreference(KEY_PLAY_MEDIA_NOTIFICATION_SOUNDS);
mVibrate = (CheckBoxPreference) findPreference(KEY_VIBRATE);
mDtmfTone = (CheckBoxPreference) findPreference(KEY_DTMF_TONE);
mDtmfTone.setPersistent(false);
mDtmfTone.setChecked(Settings.System.getInt(resolver,
Settings.System.DTMF_TONE_WHEN_DIALING, 1) != 0);
mSoundEffects = (CheckBoxPreference) findPreference(KEY_SOUND_EFFECTS);
mSoundEffects.setPersistent(false);
mSoundEffects.setChecked(Settings.System.getInt(resolver,
Settings.System.SOUND_EFFECTS_ENABLED, 0) != 0);
mAnimations = (CheckBoxPreference) findPreference(KEY_ANIMATIONS);
mAnimations.setPersistent(false);
ListPreference screenTimeoutPreference =
(ListPreference) findPreference(KEY_SCREEN_TIMEOUT);
screenTimeoutPreference.setValue(String.valueOf(Settings.System.getInt(
resolver, SCREEN_OFF_TIMEOUT, FALLBACK_SCREEN_TIMEOUT_VALUE)));
screenTimeoutPreference.setOnPreferenceChangeListener(this);
}
@Override
protected void onResume() {
super.onResume();
updateState(true);
IntentFilter filter = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION);
registerReceiver(mReceiver, filter);
}
@Override
protected void onPause() {
super.onPause();
unregisterReceiver(mReceiver);
}
private void updateState(boolean force) {
final int ringerMode = mAudioManager.getRingerMode();
final boolean silentOrVibrateMode =
ringerMode != AudioManager.RINGER_MODE_NORMAL;
if (silentOrVibrateMode != mSilent.isChecked() || force) {
mSilent.setChecked(silentOrVibrateMode);
}
try {
mPlayMediaNotificationSounds.setChecked(mMountService.getPlayNotificationSounds());
} catch (RemoteException e) {
}
boolean vibrateSetting;
if (silentOrVibrateMode) {
vibrateSetting = ringerMode == AudioManager.RINGER_MODE_VIBRATE;
} else {
vibrateSetting = mAudioManager.getVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER)
== AudioManager.VIBRATE_SETTING_ON;
}
if (vibrateSetting != mVibrate.isChecked() || force) {
mVibrate.setChecked(vibrateSetting);
}
boolean animations = true;
try {
mAnimationScales = mWindowManager.getAnimationScales();
} catch (RemoteException e) {
}
if (mAnimationScales != null) {
for (int i=0; i<mAnimationScales.length; i++) {
if (mAnimationScales[i] == 0) {
animations = false;
break;
}
}
}
if (animations != mAnimations.isChecked() || force) {
mAnimations.setChecked(animations);
}
}
@Override
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
if (preference == mSilent) {
final boolean silent = mSilent.isChecked();
mAudioManager.setRingerMode(silent ? AudioManager.RINGER_MODE_SILENT
: AudioManager.RINGER_MODE_NORMAL);
updateState(false);
} else if (preference == mPlayMediaNotificationSounds) {
try {
mMountService.setPlayNotificationSounds(mPlayMediaNotificationSounds.isChecked());
} catch (RemoteException e) {
}
} else if (preference == mVibrate) {
final boolean vibrate = mVibrate.isChecked();
final boolean silent = mSilent.isChecked();
if (silent) {
mAudioManager.setRingerMode(vibrate ? AudioManager.RINGER_MODE_VIBRATE :
AudioManager.RINGER_MODE_SILENT);
} else {
mAudioManager.setVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER,
vibrate ? AudioManager.VIBRATE_SETTING_ON
: AudioManager.VIBRATE_SETTING_OFF);
}
} else if (preference == mDtmfTone) {
Settings.System.putInt(getContentResolver(), Settings.System.DTMF_TONE_WHEN_DIALING,
mDtmfTone.isChecked() ? 1 : 0);
} else if (preference == mSoundEffects) {
if (mSoundEffects.isChecked()) {
mAudioManager.loadSoundEffects();
} else {
mAudioManager.unloadSoundEffects();
}
Settings.System.putInt(getContentResolver(), Settings.System.SOUND_EFFECTS_ENABLED,
mSoundEffects.isChecked() ? 1 : 0);
} else if (preference == mAnimations) {
for (int i=0; i<mAnimationScales.length; i++) {
mAnimationScales[i] = mAnimations.isChecked() ? 1 : 0;
}
try {
mWindowManager.setAnimationScales(mAnimationScales);
} catch (RemoteException e) {
}
}
return true;
}
public boolean onPreferenceChange(Preference preference, Object objValue) {
if (KEY_SCREEN_TIMEOUT.equals(preference.getKey())) {
int value = Integer.parseInt((String) objValue);
try {
Settings.System.putInt(getContentResolver(),
SCREEN_OFF_TIMEOUT, value);
} catch (NumberFormatException e) {
Log.e(TAG, "could not persist screen timeout setting", e);
}
}
return true;
}
}

View File

@@ -0,0 +1,31 @@
/*
* 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;
import android.os.Bundle;
import android.preference.PreferenceActivity;
public class TestingSettings extends PreferenceActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.testing_settings);
}
}

View File

@@ -0,0 +1,28 @@
package com.android.settings;
import android.provider.Telephony;
import static android.provider.Telephony.Intents.SECRET_CODE_ACTION;
import android.content.Context;
import android.content.Intent;
import android.content.BroadcastReceiver;
import android.util.Config;
import android.util.Log;
import android.view.KeyEvent;
public class TestingSettingsBroadcastReceiver extends BroadcastReceiver {
public TestingSettingsBroadcastReceiver() {
}
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(SECRET_CODE_ACTION)) {
Intent i = new Intent(Intent.ACTION_MAIN);
i.setClass(context, TestingSettings.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
}
}
}

View File

@@ -0,0 +1,249 @@
/**
* Copyright (C) 2007 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;
import com.android.internal.app.IUsageStats;
import com.android.settings.R;
import android.app.Activity;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.ServiceManager;
import com.android.internal.os.PkgUsageStats;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.AdapterView.OnItemSelectedListener;
/**
* Activity to display package usage statistics.
*/
public class UsageStats extends Activity implements OnItemSelectedListener {
private static final String TAG="UsageStatsActivity";
private static final boolean localLOGV = true;
private Spinner mTypeSpinner;
private ListView mListView;
private IUsageStats mUsageStatsService;
private LayoutInflater mInflater;
private UsageStatsAdapter mAdapter;
private PackageManager mPm;
public static class AppNameComparator implements Comparator<PkgUsageStats> {
Map<String, CharSequence> mAppLabelList;
AppNameComparator(Map<String, CharSequence> appList) {
mAppLabelList = appList;
}
public final int compare(PkgUsageStats a, PkgUsageStats b) {
String alabel = mAppLabelList.get(a.packageName).toString();
String blabel = mAppLabelList.get(b.packageName).toString();
return alabel.compareTo(blabel);
}
}
public static class LaunchCountComparator implements Comparator<PkgUsageStats> {
public final int compare(PkgUsageStats a, PkgUsageStats b) {
// return by descending order
return b.launchCount - a.launchCount;
}
}
public static class UsageTimeComparator implements Comparator<PkgUsageStats> {
public final int compare(PkgUsageStats a, PkgUsageStats b) {
long ret = a.usageTime-b.usageTime;
if (ret == 0) {
return 0;
}
if (ret < 0) {
return 1;
}
return -1;
}
}
// View Holder used when displaying views
static class AppViewHolder {
TextView pkgName;
TextView launchCount;
TextView usageTime;
}
class UsageStatsAdapter extends BaseAdapter {
// Constants defining order for display order
private static final int _DISPLAY_ORDER_USAGE_TIME = 0;
private static final int _DISPLAY_ORDER_LAUNCH_COUNT = 1;
private static final int _DISPLAY_ORDER_APP_NAME = 2;
private int mDisplayOrder = _DISPLAY_ORDER_USAGE_TIME;
private List<PkgUsageStats> mUsageStats;
private LaunchCountComparator mLaunchCountComparator;
private UsageTimeComparator mUsageTimeComparator;
private AppNameComparator mAppLabelComparator;
private HashMap<String, CharSequence> mAppLabelMap;
UsageStatsAdapter() {
mUsageStats = new ArrayList<PkgUsageStats>();
mAppLabelMap = new HashMap<String, CharSequence>();
PkgUsageStats[] stats;
try {
stats = mUsageStatsService.getAllPkgUsageStats();
} catch (RemoteException e) {
Log.e(TAG, "Failed initializing usage stats service");
return;
}
if (stats == null) {
return;
}
for (PkgUsageStats ps : stats) {
mUsageStats.add(ps);
// load application labels for each application
CharSequence label;
try {
ApplicationInfo appInfo = mPm.getApplicationInfo(ps.packageName, 0);
label = appInfo.loadLabel(mPm);
} catch (NameNotFoundException e) {
label = ps.packageName;
}
mAppLabelMap.put(ps.packageName, label);
}
// Sort list
mLaunchCountComparator = new LaunchCountComparator();
mUsageTimeComparator = new UsageTimeComparator();
mAppLabelComparator = new AppNameComparator(mAppLabelMap);
sortList();
}
public int getCount() {
return mUsageStats.size();
}
public Object getItem(int position) {
return mUsageStats.get(position);
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
// A ViewHolder keeps references to children views to avoid unneccessary calls
// to findViewById() on each row.
AppViewHolder holder;
// When convertView is not null, we can reuse it directly, there is no need
// to reinflate it. We only inflate a new View when the convertView supplied
// by ListView is null.
if (convertView == null) {
convertView = mInflater.inflate(R.layout.usage_stats_item, null);
// Creates a ViewHolder and store references to the two children views
// we want to bind data to.
holder = new AppViewHolder();
holder.pkgName = (TextView) convertView.findViewById(R.id.package_name);
holder.launchCount = (TextView) convertView.findViewById(R.id.launch_count);
holder.usageTime = (TextView) convertView.findViewById(R.id.usage_time);
convertView.setTag(holder);
} else {
// Get the ViewHolder back to get fast access to the TextView
// and the ImageView.
holder = (AppViewHolder) convertView.getTag();
}
// Bind the data efficiently with the holder
PkgUsageStats pkgStats = mUsageStats.get(position);
if (pkgStats != null) {
CharSequence label = mAppLabelMap.get(pkgStats.packageName);
holder.pkgName.setText(label);
holder.launchCount.setText(String.valueOf(pkgStats.launchCount));
holder.usageTime.setText(String.valueOf(pkgStats.usageTime)+" ms");
} else {
Log.w(TAG, "No usage stats info for package:"+pkgStats.packageName);
}
return convertView;
}
void sortList(int sortOrder) {
if (mDisplayOrder == sortOrder) {
// do nothing
return;
}
mDisplayOrder= sortOrder;
sortList();
}
private void sortList() {
if (mDisplayOrder == _DISPLAY_ORDER_USAGE_TIME) {
if (localLOGV) Log.i(TAG, "Sorting by usage time");
Collections.sort(mUsageStats, mUsageTimeComparator);
} else if (mDisplayOrder == _DISPLAY_ORDER_LAUNCH_COUNT) {
if (localLOGV) Log.i(TAG, "Sorting launch count");
Collections.sort(mUsageStats, mLaunchCountComparator);
} else if (mDisplayOrder == _DISPLAY_ORDER_APP_NAME) {
if (localLOGV) Log.i(TAG, "Sorting by application name");
Collections.sort(mUsageStats, mAppLabelComparator);
}
notifyDataSetChanged();
}
}
/** Called when the activity is first created. */
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
mUsageStatsService = IUsageStats.Stub.asInterface(ServiceManager.getService("usagestats"));
if (mUsageStatsService == null) {
Log.e(TAG, "Failed to retrieve usagestats service");
return;
}
mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mPm = getPackageManager();
setContentView(R.layout.usage_stats);
mTypeSpinner = (Spinner) findViewById(R.id.typeSpinner);
mTypeSpinner.setOnItemSelectedListener(this);
mListView = (ListView) findViewById(R.id.pkg_list);
// Initialize the inflater
mAdapter = new UsageStatsAdapter();
mListView.setAdapter(mAdapter);
}
public void onItemSelected(AdapterView<?> parent, View view, int position,
long id) {
mAdapter.sortList(position);
}
public void onNothingSelected(AdapterView<?> parent) {
// do nothing
}
}

View File

@@ -0,0 +1,271 @@
/**
* Copyright (C) 2007 Google Inc.
*
* 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;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ListActivity;
import android.content.Context;
import android.content.DialogInterface;
import android.database.Cursor;
import android.os.Bundle;
import android.provider.UserDictionary;
import android.text.InputType;
import android.view.ContextMenu;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ContextMenu.ContextMenuInfo;
import android.widget.AlphabetIndexer;
import android.widget.EditText;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.SectionIndexer;
import android.widget.SimpleCursorAdapter;
import android.widget.TextView;
import android.widget.AdapterView.AdapterContextMenuInfo;
import java.util.Locale;
public class UserDictionarySettings extends ListActivity {
private static final String INSTANCE_KEY_DIALOG_EDITING_WORD = "DIALOG_EDITING_WORD";
private static final String INSTANCE_KEY_ADDED_WORD = "DIALOG_ADDED_WORD";
private static final String[] QUERY_PROJECTION = {
UserDictionary.Words._ID, UserDictionary.Words.WORD
};
// Either the locale is empty (means the word is applicable to all locales)
// or the word equals our current locale
private static final String QUERY_SELECTION = UserDictionary.Words.LOCALE + "=? OR "
+ UserDictionary.Words.LOCALE + " is null";
private static final String DELETE_SELECTION = UserDictionary.Words.WORD + "=?";
private static final String EXTRA_WORD = "word";
private static final int CONTEXT_MENU_EDIT = Menu.FIRST;
private static final int CONTEXT_MENU_DELETE = Menu.FIRST + 1;
private static final int OPTIONS_MENU_ADD = Menu.FIRST;
private static final int DIALOG_ADD_OR_EDIT = 0;
/** The word being edited in the dialog (null means the user is adding a word). */
private String mDialogEditingWord;
private Cursor mCursor;
private boolean mAddedWordAlready;
private boolean mAutoReturn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.list_content_with_empty_view);
mCursor = createCursor();
setListAdapter(createAdapter());
TextView emptyView = (TextView) findViewById(R.id.empty);
emptyView.setText(R.string.user_dict_settings_empty_text);
ListView listView = getListView();
listView.setFastScrollEnabled(true);
listView.setEmptyView(emptyView);
registerForContextMenu(listView);
}
@Override
protected void onResume() {
super.onResume();
if (!mAddedWordAlready
&& getIntent().getAction().equals("com.android.settings.USER_DICTIONARY_INSERT")) {
String word = getIntent().getStringExtra(EXTRA_WORD);
mAutoReturn = true;
if (word != null) {
showAddOrEditDialog(word);
}
}
}
@Override
protected void onRestoreInstanceState(Bundle state) {
super.onRestoreInstanceState(state);
mDialogEditingWord = state.getString(INSTANCE_KEY_DIALOG_EDITING_WORD);
mAddedWordAlready = state.getBoolean(INSTANCE_KEY_ADDED_WORD, false);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString(INSTANCE_KEY_DIALOG_EDITING_WORD, mDialogEditingWord);
outState.putBoolean(INSTANCE_KEY_ADDED_WORD, mAddedWordAlready);
}
private Cursor createCursor() {
String currentLocale = Locale.getDefault().toString();
// Case-insensitive sort
return managedQuery(UserDictionary.Words.CONTENT_URI, QUERY_PROJECTION,
QUERY_SELECTION, new String[] { currentLocale },
"UPPER(" + UserDictionary.Words.WORD + ")");
}
private ListAdapter createAdapter() {
return new MyAdapter(this,
android.R.layout.simple_list_item_1, mCursor,
new String[] { UserDictionary.Words.WORD },
new int[] { android.R.id.text1 });
}
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
showAddOrEditDialog(getWord(position));
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
if (!(menuInfo instanceof AdapterContextMenuInfo)) return;
AdapterContextMenuInfo adapterMenuInfo = (AdapterContextMenuInfo) menuInfo;
menu.setHeaderTitle(getWord(adapterMenuInfo.position));
menu.add(0, CONTEXT_MENU_EDIT, 0, R.string.user_dict_settings_context_menu_edit_title);
menu.add(0, CONTEXT_MENU_DELETE, 0, R.string.user_dict_settings_context_menu_delete_title);
}
@Override
public boolean onContextItemSelected(MenuItem item) {
ContextMenuInfo menuInfo = item.getMenuInfo();
if (!(menuInfo instanceof AdapterContextMenuInfo)) return false;
AdapterContextMenuInfo adapterMenuInfo = (AdapterContextMenuInfo) menuInfo;
String word = getWord(adapterMenuInfo.position);
switch (item.getItemId()) {
case CONTEXT_MENU_DELETE:
deleteWord(word);
return true;
case CONTEXT_MENU_EDIT:
showAddOrEditDialog(word);
return true;
}
return false;
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
menu.add(0, OPTIONS_MENU_ADD, 0, R.string.user_dict_settings_add_menu_title)
.setIcon(R.drawable.ic_menu_add);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
showAddOrEditDialog(null);
return true;
}
private void showAddOrEditDialog(String editingWord) {
mDialogEditingWord = editingWord;
showDialog(DIALOG_ADD_OR_EDIT);
}
private String getWord(int position) {
mCursor.moveToPosition(position);
return mCursor.getString(
mCursor.getColumnIndexOrThrow(UserDictionary.Words.WORD));
}
@Override
protected Dialog onCreateDialog(int id) {
View content = getLayoutInflater().inflate(R.layout.dialog_edittext, null);
final EditText editText = (EditText) content.findViewById(R.id.edittext);
// No prediction in soft keyboard mode. TODO: Create a better way to disable prediction
editText.setInputType(InputType.TYPE_CLASS_TEXT
| InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE);
return new AlertDialog.Builder(this)
.setTitle(R.string.user_dict_settings_add_dialog_title)
.setView(content)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
onAddOrEditFinished(editText.getText().toString());
if (mAutoReturn) finish();
}})
.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
if (mAutoReturn) finish();
}})
.create();
}
@Override
protected void onPrepareDialog(int id, Dialog d) {
AlertDialog dialog = (AlertDialog) d;
EditText editText = (EditText) dialog.findViewById(R.id.edittext);
editText.setText(mDialogEditingWord);
}
private void onAddOrEditFinished(String word) {
if (mDialogEditingWord != null) {
// The user was editing a word, so do a delete/add
deleteWord(mDialogEditingWord);
}
// Disallow duplicates
deleteWord(word);
// TODO: present UI for picking whether to add word to all locales, or current.
UserDictionary.Words.addWord(this, word.toString(),
250, UserDictionary.Words.LOCALE_TYPE_ALL);
mCursor.requery();
mAddedWordAlready = true;
}
private void deleteWord(String word) {
getContentResolver().delete(UserDictionary.Words.CONTENT_URI, DELETE_SELECTION,
new String[] { word });
}
private static class MyAdapter extends SimpleCursorAdapter implements SectionIndexer {
private AlphabetIndexer mIndexer;
public MyAdapter(Context context, int layout, Cursor c, String[] from, int[] to) {
super(context, layout, c, from, to);
int wordColIndex = c.getColumnIndexOrThrow(UserDictionary.Words.WORD);
String alphabet = context.getString(com.android.internal.R.string.fast_scroll_alphabet);
mIndexer = new AlphabetIndexer(c, wordColIndex, alphabet);
}
public int getPositionForSection(int section) {
return mIndexer.getPositionForSection(section);
}
public int getSectionForPosition(int position) {
return mIndexer.getSectionForPosition(position);
}
public Object[] getSections() {
return mIndexer.getSections();
}
}
}

View File

@@ -0,0 +1,91 @@
/**
* Copyright (C) 2007 Google Inc.
*
* 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;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.preference.Preference;
import android.preference.PreferenceGroup;
import java.util.List;
public class Utils {
/**
* Set the preference's title to the matching activity's label.
*/
public static final int UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY = 1;
/**
* Finds a matching activity for a preference's intent. If a matching
* activity is not found, it will remove the preference.
*
* @param context The context.
* @param parentPreferenceGroup The preference group that contains the
* preference whose intent is being resolved.
* @param preferenceKey The key of the preference whose intent is being
* resolved.
* @param flags 0 or one or more of
* {@link #UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY}
* .
* @return Whether an activity was found. If false, the preference was
* removed.
*/
public static boolean updatePreferenceToSpecificActivityOrRemove(Context context,
PreferenceGroup parentPreferenceGroup, String preferenceKey, int flags) {
Preference preference = parentPreferenceGroup.findPreference(preferenceKey);
if (preference == null) {
return false;
}
Intent intent = preference.getIntent();
if (intent != null) {
// Find the activity that is in the system image
PackageManager pm = context.getPackageManager();
List<ResolveInfo> list = pm.queryIntentActivities(intent, 0);
int listSize = list.size();
for (int i = 0; i < listSize; i++) {
ResolveInfo resolveInfo = list.get(i);
if ((resolveInfo.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
!= 0) {
// Replace the intent with this specific activity
preference.setIntent(new Intent().setClassName(
resolveInfo.activityInfo.packageName,
resolveInfo.activityInfo.name));
if ((flags & UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY) != 0) {
// Set the preference title to the activity's label
preference.setTitle(resolveInfo.loadLabel(pm));
}
return true;
}
}
}
// Did not find a matching activity, so remove the preference
parentPreferenceGroup.removePreference(preference);
return true;
}
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright (C) 2007 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;
import com.android.settings.bluetooth.BluetoothEnabler;
import com.android.settings.wifi.WifiEnabler;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.preference.PreferenceActivity;
import android.preference.CheckBoxPreference;
public class WirelessSettings extends PreferenceActivity {
private static final String KEY_TOGGLE_AIRPLANE = "toggle_airplane";
private static final String KEY_TOGGLE_BLUETOOTH = "toggle_bluetooth";
private static final String KEY_TOGGLE_WIFI = "toggle_wifi";
private WifiEnabler mWifiEnabler;
private AirplaneModeEnabler mAirplaneModeEnabler;
private BluetoothEnabler mBtEnabler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.wireless_settings);
initToggles();
}
@Override
protected void onResume() {
super.onResume();
mWifiEnabler.resume();
mAirplaneModeEnabler.resume();
mBtEnabler.resume();
}
@Override
protected void onPause() {
super.onPause();
mWifiEnabler.pause();
mAirplaneModeEnabler.pause();
mBtEnabler.pause();
}
private void initToggles() {
mWifiEnabler = new WifiEnabler(
this,
(WifiManager) getSystemService(WIFI_SERVICE),
(CheckBoxPreference) findPreference(KEY_TOGGLE_WIFI));
mAirplaneModeEnabler = new AirplaneModeEnabler(
this,
(CheckBoxPreference) findPreference(KEY_TOGGLE_AIRPLANE));
mBtEnabler = new BluetoothEnabler(
this,
(CheckBoxPreference) findPreference(KEY_TOGGLE_BLUETOOTH));
}
}

View File

@@ -0,0 +1,273 @@
/*
* Copyright (C) 2006 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;
import android.app.AlarmManager;
import android.app.ListActivity;
import android.content.Context;
import android.content.res.XmlResourceParser;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import org.xmlpull.v1.XmlPullParserException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
/**
* This activity displays a list of time zones that match a filter string
* such as "Africa", "Europe", etc. Choosing an item from the list will set
* the time zone. Pressing Back without choosing from the list will not
* result in a change in the time zone setting.
*/
public class ZoneList extends ListActivity {
private static final String TAG = "ZoneList";
private static final String KEY_ID = "id";
private static final String KEY_DISPLAYNAME = "name";
private static final String KEY_GMT = "gmt";
private static final String KEY_OFFSET = "offset";
private static final String XMLTAG_TIMEZONE = "timezone";
private static final int HOURS_1 = 60 * 60000;
private static final int HOURS_24 = 24 * HOURS_1;
private static final int HOURS_HALF = HOURS_1 / 2;
private static final int MENU_TIMEZONE = Menu.FIRST+1;
private static final int MENU_ALPHABETICAL = Menu.FIRST;
// Initial focus position
private int mDefault;
private boolean mSortedByTimezone;
private SimpleAdapter mTimezoneSortedAdapter;
private SimpleAdapter mAlphabeticalAdapter;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
String[] from = new String[] {KEY_DISPLAYNAME, KEY_GMT};
int[] to = new int[] {android.R.id.text1, android.R.id.text2};
MyComparator comparator = new MyComparator(KEY_OFFSET);
List<HashMap> timezoneSortedList = getZones();
Collections.sort(timezoneSortedList, comparator);
mTimezoneSortedAdapter = new SimpleAdapter(this,
(List) timezoneSortedList,
android.R.layout.simple_list_item_2,
from,
to);
List<HashMap> alphabeticalList = new ArrayList<HashMap>(timezoneSortedList);
comparator.setSortingKey(KEY_DISPLAYNAME);
Collections.sort(alphabeticalList, comparator);
mAlphabeticalAdapter = new SimpleAdapter(this,
(List) alphabeticalList,
android.R.layout.simple_list_item_2,
from,
to);
// Sets the adapter
setSorting(true);
// If current timezone is in this list, move focus to it
setSelection(mDefault);
// Assume user may press Back
setResult(RESULT_CANCELED);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
menu.add(0, MENU_ALPHABETICAL, 0, R.string.zone_list_menu_sort_alphabetically)
.setIcon(android.R.drawable.ic_menu_sort_alphabetically);
menu.add(0, MENU_TIMEZONE, 0, R.string.zone_list_menu_sort_by_timezone)
.setIcon(R.drawable.ic_menu_3d_globe);
return true;
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
if (mSortedByTimezone) {
menu.findItem(MENU_TIMEZONE).setVisible(false);
menu.findItem(MENU_ALPHABETICAL).setVisible(true);
} else {
menu.findItem(MENU_TIMEZONE).setVisible(true);
menu.findItem(MENU_ALPHABETICAL).setVisible(false);
}
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case MENU_TIMEZONE:
setSorting(true);
return true;
case MENU_ALPHABETICAL:
setSorting(false);
return true;
default:
return false;
}
}
private void setSorting(boolean timezone) {
setListAdapter(timezone ? mTimezoneSortedAdapter : mAlphabeticalAdapter);
mSortedByTimezone = timezone;
}
private List<HashMap> getZones() {
List<HashMap> myData = new ArrayList<HashMap>();
long date = Calendar.getInstance().getTimeInMillis();
try {
XmlResourceParser xrp = getResources().getXml(R.xml.timezones);
while (xrp.next() != XmlResourceParser.START_TAG)
;
xrp.next();
while (xrp.getEventType() != XmlResourceParser.END_TAG) {
while (xrp.getEventType() != XmlResourceParser.START_TAG) {
if (xrp.getEventType() == XmlResourceParser.END_DOCUMENT) {
return myData;
}
xrp.next();
}
if (xrp.getName().equals(XMLTAG_TIMEZONE)) {
String id = xrp.getAttributeValue(0);
String displayName = xrp.nextText();
addItem(myData, id, displayName, date);
}
while (xrp.getEventType() != XmlResourceParser.END_TAG) {
xrp.next();
}
xrp.next();
}
xrp.close();
} catch (XmlPullParserException xppe) {
Log.e(TAG, "Ill-formatted timezones.xml file");
} catch (java.io.IOException ioe) {
Log.e(TAG, "Unable to read timezones.xml file");
}
return myData;
}
protected void addItem(List<HashMap> myData, String id, String displayName,
long date) {
HashMap map = new HashMap();
map.put(KEY_ID, id);
map.put(KEY_DISPLAYNAME, displayName);
TimeZone tz = TimeZone.getTimeZone(id);
int offset = tz.getOffset(date);
int p = Math.abs(offset);
StringBuilder name = new StringBuilder();
name.append("GMT");
if (offset < 0) {
name.append('-');
} else {
name.append('+');
}
name.append(p / (HOURS_1));
name.append(':');
int min = p / 60000;
min %= 60;
if (min < 10) {
name.append('0');
}
name.append(min);
map.put(KEY_GMT, name.toString());
map.put(KEY_OFFSET, offset);
if (id.equals(TimeZone.getDefault().getID())) {
mDefault = myData.size();
}
myData.add(map);
}
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
Map map = (Map) l.getItemAtPosition(position);
// Update the system timezone value
AlarmManager alarm = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarm.setTimeZone((String) map.get(KEY_ID));
setResult(RESULT_OK);
finish();
}
private static class MyComparator implements Comparator<HashMap> {
private String mSortingKey;
public MyComparator(String sortingKey) {
mSortingKey = sortingKey;
}
public void setSortingKey(String sortingKey) {
mSortingKey = sortingKey;
}
public int compare(HashMap map1, HashMap map2) {
Object value1 = map1.get(mSortingKey);
Object value2 = map2.get(mSortingKey);
/*
* This should never happen, but just in-case, put non-comparable
* items at the end.
*/
if (!isComparable(value1)) {
return isComparable(value2) ? 1 : 0;
} else if (!isComparable(value2)) {
return -1;
}
return ((Comparable) value1).compareTo(value2);
}
private boolean isComparable(Object value) {
return (value != null) && (value instanceof Comparable);
}
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright (C) 2006 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;
import android.app.ListActivity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ZonePicker extends ListActivity {
private ArrayAdapter<CharSequence> mFilterAdapter;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
mFilterAdapter = ArrayAdapter.createFromResource(this,
R.array.timezone_filters, android.R.layout.simple_list_item_1);
setListAdapter(mFilterAdapter);
}
protected void addItem(List<Map> data, String name, String zone) {
HashMap temp = new HashMap();
temp.put("title", name);
temp.put("zone", zone);
data.add(temp);
}
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
String filter = (String) mFilterAdapter.getItem(position);
// If All is chosen, reset the filter
if (filter.equals("All")) {
filter = null;
}
Intent zoneList = new Intent();
zoneList.setClass(this, ZoneList.class);
zoneList.putExtra("filter", filter);
startActivityForResult(zoneList, 0);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// If subactivity has resulted in a timezone selection, close this act.
if (resultCode == RESULT_OK) {
finish();
}
}
}

View File

@@ -0,0 +1,861 @@
/*
* Copyright (C) 2006 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.battery_history;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import com.android.internal.app.IBatteryStats;
import com.android.settings.R;
import android.app.Activity;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.BatteryStats;
import android.os.Bundle;
import android.os.Parcel;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.BatteryStats.Timer;
import android.os.BatteryStats.Uid;
import android.util.Log;
import android.util.LogPrinter;
import android.util.SparseArray;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.AdapterView.OnItemSelectedListener;
public class BatteryHistory extends Activity implements OnClickListener, OnItemSelectedListener {
private static final String TAG = "BatteryHistory";
private static final int SECONDS_PER_MINUTE = 60;
private static final int SECONDS_PER_HOUR = 60 * 60;
private static final int SECONDS_PER_DAY = 24 * 60 * 60;
// Must be in sync with the values in res/values/array.xml (id battery_history_type_spinner)
private static final int CPU_USAGE = 0;
private static final int NETWORK_USAGE = 1;
private static final int GPS_USAGE = 2;
private static final int SENSOR_USAGE = 3;
private static final int WAKELOCK_USAGE = 4;
private static final int MISC_USAGE = 5;
// Must be in sync with the values in res/values/array.xml (id battery_history_which_spinner)
private static final int UNPLUGGED = 0;
private static final int CURRENT = 1;
private static final int TOTAL = 2;
private BatteryStats mStats;
private int mWhich = BatteryStats.STATS_UNPLUGGED;
private int mType = CPU_USAGE;
private GraphableButton[] mButtons;
IBatteryStats mBatteryInfo;
private List<CpuUsage> mCpuUsage = new ArrayList<CpuUsage>();
private List<NetworkUsage> mNetworkUsage = new ArrayList<NetworkUsage>();
private List<SensorUsage> mSensorUsage = new ArrayList<SensorUsage>();
private List<SensorUsage> mGpsUsage = new ArrayList<SensorUsage>();
private List<WakelockUsage> mWakelockUsage = new ArrayList<WakelockUsage>();
private List<MiscUsage> mMiscUsage = new ArrayList<MiscUsage>();
private boolean mHaveCpuUsage, mHaveNetworkUsage, mHaveSensorUsage,
mHaveWakelockUsage, mHaveMiscUsage;
private LinearLayout mGraphLayout;
private LinearLayout mTextLayout;
private TextView mMessageText;
private TextView mDetailsText;
private Button mDetailsBackButton;
private Spinner mTypeSpinner;
private Spinner mWhichSpinner;
private boolean mDetailsShown = false;
private static String getLabel(String packageName, PackageManager pm) {
try {
ApplicationInfo ai = pm.getApplicationInfo(packageName, 0);
CharSequence label = ai.loadLabel(pm);
if (label != null) {
return label.toString();
}
} catch (NameNotFoundException e) {
return packageName;
}
return "";
}
void formatTime(double millis, StringBuilder sb) {
int seconds = (int) Math.floor(millis / 1000);
int days = 0, hours = 0, minutes = 0;
if (seconds > SECONDS_PER_DAY) {
days = seconds / SECONDS_PER_DAY;
seconds -= days * SECONDS_PER_DAY;
}
if (seconds > SECONDS_PER_HOUR) {
hours = seconds / SECONDS_PER_HOUR;
seconds -= hours * SECONDS_PER_HOUR;
}
if (seconds > SECONDS_PER_MINUTE) {
minutes = seconds / SECONDS_PER_MINUTE;
seconds -= minutes * SECONDS_PER_MINUTE;
}
if (days > 0) {
sb.append(getString(R.string.battery_history_days, days, hours, minutes, seconds));
} else if (hours > 0) {
sb.append(getString(R.string.battery_history_hours, hours, minutes, seconds));
} else if (minutes > 0) {
sb.append(getString(R.string.battery_history_minutes, minutes, seconds));
} else {
sb.append(getString(R.string.battery_history_seconds, seconds));
}
}
abstract class Graphable implements Comparable<Graphable> {
protected String mName;
protected String mNamePackage;
protected boolean mUniqueName;
protected String[] mPackages;
protected String[] mPackageNames;
public abstract String getLabel();
public abstract double getSortValue();
public abstract double[] getValues();
public abstract void getInfo(StringBuilder info);
public double getMaxValue() {
return -Double.MAX_VALUE;
}
public int compareTo(Graphable o) {
double t = getSortValue();
double ot = o.getSortValue();
if (t < ot) {
// Largest first
return 1;
} else if (t > ot) {
return -1;
} else {
return 0;
}
}
// Side effects: sets mName and mUniqueName
void getNameForUid(int uid) {
PackageManager pm = getPackageManager();
mPackages = pm.getPackagesForUid(uid);
if (mPackages == null) {
mName = Integer.toString(uid);
mNamePackage = null;
return;
}
mPackageNames = new String[mPackages.length];
System.arraycopy(mPackages, 0, mPackageNames, 0, mPackages.length);
// Convert package names to user-facing labels where possible
for (int i = 0; i < mPackageNames.length; i++) {
mPackageNames[i] = BatteryHistory.getLabel(mPackageNames[i], pm);
}
if (mPackageNames.length == 1) {
mNamePackage = mPackages[0];
mName = mPackageNames[0];
mUniqueName = true;
} else {
mName = getString(R.string.battery_history_uid, uid); // Default name
// Look for an official name for this UID.
for (String name : mPackages) {
try {
PackageInfo pi = pm.getPackageInfo(name, 0);
if (pi.sharedUserLabel != 0) {
CharSequence nm = pm.getText(name,
pi.sharedUserLabel, pi.applicationInfo);
if (nm != null) {
mName = nm.toString();
break;
}
}
} catch (PackageManager.NameNotFoundException e) {
}
}
}
}
}
class CpuUsage extends Graphable {
String mProcess;
double[] mUsage;
double mTotalRuntime;
long mStarts;
public CpuUsage(int uid, String process, long userTime, long systemTime,
long starts, long totalRuntime) {
getNameForUid(uid);
mProcess = process;
PackageManager pm = BatteryHistory.this.getPackageManager();
mName = BatteryHistory.getLabel(process, pm);
mUsage = new double[2];
mUsage[0] = userTime;
mUsage[1] = userTime + systemTime;
mTotalRuntime = totalRuntime;
mStarts = starts;
}
public String getLabel() {
return mName;
}
public double getSortValue() {
return mUsage[1];
}
public double[] getValues() {
return mUsage;
}
public double getMaxValue() {
return mTotalRuntime;
}
public void getInfo(StringBuilder info) {
info.append(getString(R.string.battery_history_cpu_usage, mProcess));
info.append("\n\n");
info.append(getString(R.string.battery_history_user_time));
formatTime(mUsage[0] * 10, info);
info.append('\n');
info.append(getString(R.string.battery_history_system_time));
formatTime((mUsage[1] - mUsage[0]) * 10, info);
info.append('\n');
info.append(getString(R.string.battery_history_total_time));
formatTime((mUsage[1]) * 10, info);
info.append('\n');
info.append(getString(R.string.battery_history_starts, mStarts));
}
}
class NetworkUsage extends Graphable {
double[] mUsage;
public NetworkUsage(int uid, long received, long sent) {
getNameForUid(uid);
mUsage = new double[2];
mUsage[0] = received;
mUsage[1] = received + sent;
}
public String getLabel() {
return mName;
}
public double getSortValue() {
return mUsage[1];
}
public double[] getValues() {
return mUsage;
}
public void getInfo(StringBuilder info) {
info.append(getString(R.string.battery_history_network_usage, mName));
info.append("\n\n");
info.append(getString(R.string.battery_history_bytes_received, (long) mUsage[0]));
info.append('\n');
info.append(getString(R.string.battery_history_bytes_sent,
(long) mUsage[1] - (long) mUsage[0]));
info.append('\n');
info.append(getString(R.string.battery_history_bytes_total, (long) mUsage[1]));
if (!mUniqueName) {
info.append("\n\n");
info.append(getString(R.string.battery_history_packages_sharing_this_uid));
info.append('\n');
PackageManager pm = BatteryHistory.this.getPackageManager();
List<String> names = new ArrayList<String>();
for (String name : mPackageNames) {
names.add(BatteryHistory.getLabel(name, pm));
}
Collections.sort(names);
for (String name : names) {
info.append(" ");
info.append(name);
info.append('\n');
}
}
}
}
class SensorUsage extends Graphable {
double[] mUsage;
double mTotalRealtime;
int mCount;
public SensorUsage(int uid, long time, int count, long totalRealtime) {
getNameForUid(uid);
mUsage = new double[1];
mUsage[0] = time;
mTotalRealtime = totalRealtime;
mCount = count;
}
public String getLabel() {
return mName;
}
public double getSortValue() {
return mUsage[0];
}
public double[] getValues() {
return mUsage;
}
public double getMaxValue() {
return mTotalRealtime;
}
public void getInfo(StringBuilder info) {
info.append(getString(R.string.battery_history_sensor));
info.append(mName);
info.append("\n\n");
info.append(getString(R.string.battery_history_total_time));
formatTime(mUsage[0], info);
info.append("\n\n");
}
}
class WakelockUsage extends Graphable {
double[] mUsage;
double mTotalRealtime;
int mCount;
public WakelockUsage(int uid, long time, int count, long totalRealtime) {
getNameForUid(uid);
mUsage = new double[1];
mUsage[0] = time;
mTotalRealtime = totalRealtime;
mCount = count;
}
public String getLabel() {
return mName;
}
public double getSortValue() {
return mUsage[0];
}
public double[] getValues() {
return mUsage;
}
public double getMaxValue() {
return mTotalRealtime;
}
public void getInfo(StringBuilder info) {
info.append(getString(R.string.battery_history_wakelock));
info.append(mName);
info.append("\n\n");
info.append(getString(R.string.battery_history_total_time));
formatTime(mUsage[0], info);
info.append("\n\n");
}
}
class MiscUsage extends Graphable {
int mInfoLabelRes;
double[] mUsage;
double mTotalRealtime;
public MiscUsage(String name, int infoLabelRes, long value,
long totalRealtime) {
mName = name;
mInfoLabelRes = infoLabelRes;
mUsage = new double[2];
mUsage[0] = value;
mTotalRealtime = totalRealtime;
}
public String getLabel() {
return mName;
}
public double getSortValue() {
return mUsage[1];
}
public double[] getValues() {
return mUsage;
}
public double getMaxValue() {
return mTotalRealtime;
}
public void getInfo(StringBuilder info) {
info.append(getString(mInfoLabelRes));
info.append(' ');
formatTime(mUsage[0], info);
info.append(" (");
info.append((mUsage[0]*100)/mTotalRealtime);
info.append("%)");
}
}
private List<? extends Graphable> getGraphRecords() {
switch (mType) {
case CPU_USAGE: return mCpuUsage;
case NETWORK_USAGE : return mNetworkUsage;
case SENSOR_USAGE: return mSensorUsage;
case GPS_USAGE: return mGpsUsage;
case WAKELOCK_USAGE: return mWakelockUsage;
case MISC_USAGE: return mMiscUsage;
default:
return (List<? extends Graphable>) null; // TODO
}
}
private void displayGraph() {
Log.i(TAG, "displayGraph");
collectStatistics();
// Hide the UI and selectively enable it below
mMessageText.setVisibility(View.GONE);
for (int i = 0; i < mButtons.length; i++) {
mButtons[i].setVisibility(View.INVISIBLE);
}
double maxValue = -Double.MAX_VALUE;
List<? extends Graphable> records = getGraphRecords();
for (Graphable g : records) {
double[] values = g.getValues();
maxValue = Math.max(maxValue, values[values.length - 1]);
maxValue = Math.max(maxValue, g.getMaxValue());
}
int[] colors = new int[2];
colors[0] = 0xff0000ff;
colors[1] = 0xffff0000;
for (int i = 0; i < mButtons.length; i++) {
mButtons[i].setVisibility(View.INVISIBLE);
}
int numRecords = Math.min(records.size(), mButtons.length);
if (numRecords == 0) {
mMessageText.setVisibility(View.VISIBLE);
mMessageText.setText(R.string.battery_history_no_data);
} else {
for (int i = 0; i < numRecords; i++) {
Graphable r = records.get(i);
mButtons[i].setText(r.getLabel());
mButtons[i].setValues(r.getValues(), maxValue);
mButtons[i].setVisibility(View.VISIBLE);
}
}
}
private void hideDetails() {
mTextLayout.setVisibility(View.GONE);
mGraphLayout.setVisibility(View.VISIBLE);
mDetailsShown = false;
}
private void showDetails(int id) {
mGraphLayout.setVisibility(View.GONE);
mTextLayout.setVisibility(View.VISIBLE);
StringBuilder info = new StringBuilder();
List<? extends Graphable> records = getGraphRecords();
if (id < records.size()) {
Graphable record = records.get(id);
record.getInfo(info);
} else {
info.append(getString(R.string.battery_history_details_for, id));
}
mDetailsText.setText(info.toString());
mDetailsShown = true;
}
private void processCpuUsage() {
mCpuUsage.clear();
long uSecTime = SystemClock.uptimeMillis() * 1000;
final long uSecNow = mStats.computeBatteryUptime(uSecTime, mWhich) / 1000;
SparseArray<? extends Uid> uidStats = mStats.getUidStats();
final int NU = uidStats.size();
for (int iu = 0; iu < NU; iu++) {
Uid u = uidStats.valueAt(iu);
Map<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats();
if (processStats.size() > 0) {
for (Map.Entry<String, ? extends BatteryStats.Uid.Proc> ent
: processStats.entrySet()) {
Uid.Proc ps = ent.getValue();
long userTime = ps.getUserTime(mWhich);
long systemTime = ps.getSystemTime(mWhich);
long starts = ps.getStarts(mWhich);
if (userTime != 0 || systemTime != 0) {
mCpuUsage.add(new CpuUsage(u.getUid(), ent.getKey(),
userTime, systemTime, starts, uSecNow));
}
}
}
}
Collections.sort(mCpuUsage);
}
private void processNetworkUsage() {
mNetworkUsage.clear();
SparseArray<? extends Uid> uidStats = mStats.getUidStats();
final int NU = uidStats.size();
for (int iu = 0; iu < NU; iu++) {
Uid u = uidStats.valueAt(iu);
long received = u.getTcpBytesReceived(mWhich);
long sent = u.getTcpBytesSent(mWhich);
if (received + sent > 0) {
mNetworkUsage.add(new NetworkUsage(u.getUid(), received, sent));
}
}
Collections.sort(mNetworkUsage);
}
private void processSensorUsage() {
mGpsUsage.clear();
mSensorUsage.clear();
long uSecTime = SystemClock.elapsedRealtime() * 1000;
final long uSecNow = mStats.computeBatteryRealtime(uSecTime, mWhich) / 1000;
SparseArray<? extends Uid> uidStats = mStats.getUidStats();
final int NU = uidStats.size();
for (int iu = 0; iu < NU; iu++) {
Uid u = uidStats.valueAt(iu);
int uid = u.getUid();
Map<Integer, ? extends BatteryStats.Uid.Sensor> sensorStats = u.getSensorStats();
long timeGps = 0;
int countGps = 0;
long timeOther = 0;
int countOther = 0;
if (sensorStats.size() > 0) {
for (Map.Entry<Integer, ? extends BatteryStats.Uid.Sensor> ent
: sensorStats.entrySet()) {
Uid.Sensor se = ent.getValue();
int handle = se.getHandle();
Timer timer = se.getSensorTime();
if (timer != null) {
// Convert from microseconds to milliseconds with rounding
long totalTime = (timer.getTotalTime(uSecNow, mWhich) + 500) / 1000;
int count = timer.getCount(mWhich);
if (handle == BatteryStats.Uid.Sensor.GPS) {
timeGps += totalTime;
countGps += count;
} else {
timeOther += totalTime;
countOther += count;
}
}
}
}
if (timeGps > 0) {
mGpsUsage.add(new SensorUsage(uid, timeGps, countGps, uSecNow));
}
if (timeOther > 0) {
mSensorUsage.add(new SensorUsage(uid, timeOther, countOther, uSecNow));
}
}
Collections.sort(mGpsUsage);
Collections.sort(mSensorUsage);
}
private void processWakelockUsage() {
mWakelockUsage.clear();
long uSecTime = SystemClock.elapsedRealtime() * 1000;
final long uSecNow = mStats.computeBatteryRealtime(uSecTime, mWhich) / 1000;
SparseArray<? extends Uid> uidStats = mStats.getUidStats();
final int NU = uidStats.size();
for (int iu = 0; iu < NU; iu++) {
Uid u = uidStats.valueAt(iu);
int uid = u.getUid();
Map<String, ? extends BatteryStats.Uid.Wakelock> wakelockStats = u.getWakelockStats();
long time = 0;
int count = 0;
if (wakelockStats.size() > 0) {
for (Map.Entry<String, ? extends BatteryStats.Uid.Wakelock> ent
: wakelockStats.entrySet()) {
Uid.Wakelock wl = ent.getValue();
Timer timer = wl.getWakeTime(BatteryStats.WAKE_TYPE_PARTIAL);
if (timer != null) {
// Convert from microseconds to milliseconds with rounding
time += (timer.getTotalTime(uSecNow, mWhich) + 500) / 1000;
count += timer.getCount(mWhich);
}
}
}
if (time > 0) {
mWakelockUsage.add(new WakelockUsage(uid, time, count, uSecNow));
}
}
Collections.sort(mWakelockUsage);
}
private void processMiscUsage() {
mMiscUsage.clear();
long rawRealtime = SystemClock.elapsedRealtime() * 1000;
final long batteryRealtime = mStats.getBatteryRealtime(rawRealtime);
final long whichRealtime = mStats.computeBatteryRealtime(rawRealtime, mWhich) / 1000;
long time = mStats.computeBatteryUptime(SystemClock.uptimeMillis() * 1000, mWhich) / 1000;
if (time > 0) {
mMiscUsage.add(new MiscUsage(getString(
R.string.battery_history_awake_label),
R.string.battery_history_awake,
time, whichRealtime));
}
time = mStats.getScreenOnTime(batteryRealtime, mWhich) / 1000;
if (time > 0) {
mMiscUsage.add(new MiscUsage(getString(
R.string.battery_history_screen_on_label),
R.string.battery_history_screen_on,
time, whichRealtime));
}
time = mStats.getPhoneOnTime(batteryRealtime, mWhich) / 1000;
if (time > 0) {
mMiscUsage.add(new MiscUsage(getString(
R.string.battery_history_phone_on_label),
R.string.battery_history_phone_on,
time, whichRealtime));
}
Collections.sort(mMiscUsage);
}
private void collectStatistics() {
if (mType == CPU_USAGE) {
if (!mHaveCpuUsage) {
mHaveCpuUsage = true;
processCpuUsage();
}
}
if (mType == NETWORK_USAGE) {
if (!mHaveNetworkUsage) {
mHaveNetworkUsage = true;
processNetworkUsage();
}
}
if (mType == GPS_USAGE || mType == SENSOR_USAGE) {
if (!mHaveSensorUsage) {
mHaveSensorUsage = true;
processSensorUsage();
}
}
if (mType == WAKELOCK_USAGE) {
if (!mHaveWakelockUsage) {
mHaveWakelockUsage = true;
processWakelockUsage();
}
}
if (mType == MISC_USAGE) {
if (!mHaveMiscUsage) {
mHaveMiscUsage = true;
processMiscUsage();
}
}
}
private void load() {
try {
byte[] data = mBatteryInfo.getStatistics();
Parcel parcel = Parcel.obtain();
//Log.i(TAG, "Got data: " + data.length + " bytes");
parcel.unmarshall(data, 0, data.length);
parcel.setDataPosition(0);
mStats = com.android.internal.os.BatteryStatsImpl.CREATOR
.createFromParcel(parcel);
//Log.i(TAG, "RECEIVED BATTERY INFO:");
//mStats.dumpLocked(new LogPrinter(Log.INFO, TAG));
mHaveCpuUsage = mHaveNetworkUsage = mHaveSensorUsage
= mHaveWakelockUsage = mHaveMiscUsage = false;
} catch (RemoteException e) {
Log.e(TAG, "RemoteException:", e);
}
}
public void onClick(View v) {
if (v == mDetailsBackButton) {
hideDetails();
return;
}
int id = ((Integer) v.getTag()).intValue();
showDetails(id);
}
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK && mDetailsShown) {
hideDetails();
return true;
}
return super.onKeyDown(keyCode, event);
}
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
int oldWhich = mWhich;
if (parent.equals(mTypeSpinner)) {
mType = position;
} else if (parent.equals(mWhichSpinner)) {
switch (position) {
case UNPLUGGED:
mWhich = BatteryStats.STATS_UNPLUGGED;
break;
case CURRENT:
mWhich = BatteryStats.STATS_CURRENT;
break;
case TOTAL:
mWhich = BatteryStats.STATS_TOTAL;
break;
}
}
if (oldWhich != mWhich) {
mHaveCpuUsage = mHaveNetworkUsage = mHaveSensorUsage
= mHaveWakelockUsage = mHaveMiscUsage = false;
}
displayGraph();
}
public void onNothingSelected(AdapterView<?> parent) {
// Do nothing
}
@Override
public Object onRetainNonConfigurationInstance() {
BatteryStats stats = mStats;
mStats = null;
return stats;
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (mStats != null) {
outState.putParcelable("stats", mStats);
}
outState.putInt("type", mType);
outState.putInt("which", mWhich);
}
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
Log.i(TAG, "onCreate");
setContentView(R.layout.battery_history);
mGraphLayout = (LinearLayout) findViewById(R.id.graphLayout);
mTextLayout = (LinearLayout) findViewById(R.id.textLayout);
mDetailsText = (TextView) findViewById(R.id.detailsText);
mMessageText = (TextView) findViewById(R.id.messageText);
mTypeSpinner = (Spinner) findViewById(R.id.typeSpinner);
mTypeSpinner.setOnItemSelectedListener(this);
mWhichSpinner = (Spinner) findViewById(R.id.whichSpinner);
mWhichSpinner.setOnItemSelectedListener(this);
mWhichSpinner.setEnabled(true);
mButtons = new GraphableButton[8];
mButtons[0] = (GraphableButton) findViewById(R.id.button0);
mButtons[1] = (GraphableButton) findViewById(R.id.button1);
mButtons[2] = (GraphableButton) findViewById(R.id.button2);
mButtons[3] = (GraphableButton) findViewById(R.id.button3);
mButtons[4] = (GraphableButton) findViewById(R.id.button4);
mButtons[5] = (GraphableButton) findViewById(R.id.button5);
mButtons[6] = (GraphableButton) findViewById(R.id.button6);
mButtons[7] = (GraphableButton) findViewById(R.id.button7);
for (int i = 0; i < mButtons.length; i++) {
mButtons[i].setTag(i);
mButtons[i].setOnClickListener(this);
}
mBatteryInfo = IBatteryStats.Stub.asInterface(
ServiceManager.getService("batteryinfo"));
mStats = (BatteryStats)getLastNonConfigurationInstance();
if (icicle != null) {
if (mStats == null) {
mStats = (BatteryStats)icicle.getParcelable("stats");
}
mType = icicle.getInt("type");
mWhich = icicle.getInt("which");
}
if (mStats == null) {
load();
}
displayGraph();
}
}

View File

@@ -0,0 +1,55 @@
package com.android.settings.battery_history;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.Button;
public class GraphableButton extends Button {
private static final String TAG = "GraphableButton";
static Paint[] sPaint = new Paint[2];
static {
sPaint[0] = new Paint();
sPaint[0].setStyle(Paint.Style.FILL);
sPaint[0].setColor(Color.BLUE);
sPaint[1] = new Paint();
sPaint[1].setStyle(Paint.Style.FILL);
sPaint[1].setColor(Color.RED);
}
double[] mValues;
public GraphableButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setValues(double[] values, double maxValue) {
mValues = values.clone();
for (int i = 0; i < values.length; i++) {
mValues[i] /= maxValue;
}
}
@Override
public void onDraw(Canvas canvas) {
Log.i(TAG, "onDraw: w = " + getWidth() + ", h = " + getHeight());
int xmin = getPaddingLeft();
int xmax = getWidth() - getPaddingRight();
int ymin = getPaddingTop();
int ymax = getHeight() - getPaddingBottom();
int startx = xmin;
for (int i = 0; i < mValues.length; i++) {
int endx = xmin + (int) (mValues[i] * (xmax - xmin));
canvas.drawRect(startx, ymin, endx, ymax, sPaint[i]);
startx = endx;
}
super.onDraw(canvas);
}
}

View File

@@ -0,0 +1,122 @@
/*
* 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 com.android.settings.R;
import android.content.Context;
import android.preference.Preference;
import android.util.TypedValue;
import android.view.View;
import android.widget.ImageView;
/**
* BluetoothDevicePreference is the preference type used to display each remote
* Bluetooth device in the Bluetooth Settings screen.
*/
public class BluetoothDevicePreference extends Preference implements LocalBluetoothDevice.Callback {
private static final String TAG = "BluetoothDevicePreference";
private static int sDimAlpha = Integer.MIN_VALUE;
private LocalBluetoothDevice mLocalDevice;
/**
* Cached local copy of whether the device is busy. This is only updated
* from {@link #onDeviceAttributesChanged(LocalBluetoothDevice)}.
*/
private boolean mIsBusy;
public BluetoothDevicePreference(Context context, LocalBluetoothDevice localDevice) {
super(context);
if (sDimAlpha == Integer.MIN_VALUE) {
TypedValue outValue = new TypedValue();
context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, outValue, true);
sDimAlpha = (int) (outValue.getFloat() * 255);
}
mLocalDevice = localDevice;
setLayoutResource(R.layout.preference_bluetooth);
localDevice.registerCallback(this);
onDeviceAttributesChanged(localDevice);
}
public LocalBluetoothDevice getDevice() {
return mLocalDevice;
}
@Override
protected void onPrepareForRemoval() {
super.onPrepareForRemoval();
mLocalDevice.unregisterCallback(this);
}
public void onDeviceAttributesChanged(LocalBluetoothDevice device) {
/*
* The preference framework takes care of making sure the value has
* changed before proceeding.
*/
setTitle(mLocalDevice.getName());
/*
* TODO: Showed "Paired" even though it was "Connected". This may be
* related to BluetoothHeadset not bound to the actual
* BluetoothHeadsetService when we got here.
*/
setSummary(mLocalDevice.getSummary());
// Used to gray out the item
mIsBusy = mLocalDevice.isBusy();
// Data has changed
notifyChanged();
// This could affect ordering, so notify that also
notifyHierarchyChanged();
}
@Override
public boolean isEnabled() {
return super.isEnabled() && !mIsBusy;
}
@Override
protected void onBindView(View view) {
super.onBindView(view);
ImageView btClass = (ImageView) view.findViewById(R.id.btClass);
btClass.setImageResource(mLocalDevice.getBtClassDrawable());
btClass.setAlpha(isEnabled() ? 255 : sDimAlpha);
}
@Override
public int compareTo(Preference another) {
if (!(another instanceof BluetoothDevicePreference)) {
// Put other preference types above us
return 1;
}
return mLocalDevice.compareTo(((BluetoothDevicePreference) another).mLocalDevice);
}
}

View File

@@ -0,0 +1,198 @@
/*
* 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 com.android.settings.R;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothError;
import android.bluetooth.BluetoothIntent;
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.preference.Preference;
import android.preference.CheckBoxPreference;
import android.util.Log;
/**
* 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.
*/
public class BluetoothDiscoverableEnabler implements Preference.OnPreferenceChangeListener {
private static final String TAG = "BluetoothDiscoverableEnabler";
private static final boolean V = LocalBluetoothManager.V;
private static final String SYSTEM_PROPERTY_DISCOVERABLE_TIMEOUT =
"debug.bt.discoverable_time";
private static final int DISCOVERABLE_TIMEOUT = 120;
private static final String SHARED_PREFERENCES_KEY_DISCOVERABLE_END_TIMESTAMP =
"discoverable_end_timestamp";
private final Context mContext;
private final Handler mUiHandler;
private final CheckBoxPreference mCheckBoxPreference;
private final LocalBluetoothManager mLocalManager;
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (BluetoothIntent.SCAN_MODE_CHANGED_ACTION.equals(intent.getAction())) {
int mode = intent.getIntExtra(BluetoothIntent.SCAN_MODE, BluetoothError.ERROR);
if (mode != BluetoothError.ERROR) {
handleModeChanged(mode);
}
}
}
};
private final Runnable mUpdateCountdownSummaryRunnable = new Runnable() {
public void run() {
updateCountdownSummary();
}
};
public BluetoothDiscoverableEnabler(Context context, CheckBoxPreference checkBoxPreference) {
mContext = context;
mUiHandler = new Handler();
mCheckBoxPreference = checkBoxPreference;
checkBoxPreference.setPersistent(false);
mLocalManager = LocalBluetoothManager.getInstance(context);
if (mLocalManager == null) {
// Bluetooth not supported
checkBoxPreference.setEnabled(false);
}
}
public void resume() {
if (mLocalManager == null) {
return;
}
IntentFilter filter = new IntentFilter(BluetoothIntent.SCAN_MODE_CHANGED_ACTION);
filter.addAction(BluetoothIntent.DISABLED_ACTION);
mContext.registerReceiver(mReceiver, filter);
mCheckBoxPreference.setOnPreferenceChangeListener(this);
handleModeChanged(mLocalManager.getBluetoothManager().getScanMode());
}
public void pause() {
if (mLocalManager == null) {
return;
}
mUiHandler.removeCallbacks(mUpdateCountdownSummaryRunnable);
mCheckBoxPreference.setOnPreferenceChangeListener(null);
mContext.unregisterReceiver(mReceiver);
}
public boolean onPreferenceChange(Preference preference, Object value) {
if (V) {
Log.v(TAG, "Preference changed to " + value);
}
// Turn on/off BT discoverability
setEnabled((Boolean) value);
return true;
}
private void setEnabled(final boolean enable) {
BluetoothDevice manager = mLocalManager.getBluetoothManager();
if (enable) {
int timeout = getDiscoverableTimeout();
manager.setDiscoverableTimeout(timeout);
long endTimestamp = System.currentTimeMillis() + timeout * 1000;
persistDiscoverableEndTimestamp(endTimestamp);
manager.setScanMode(BluetoothDevice.SCAN_MODE_CONNECTABLE_DISCOVERABLE);
} else {
manager.setScanMode(BluetoothDevice.SCAN_MODE_CONNECTABLE);
}
}
private int getDiscoverableTimeout() {
int timeout = SystemProperties.getInt(SYSTEM_PROPERTY_DISCOVERABLE_TIMEOUT, -1);
if (timeout <= 0) {
timeout = DISCOVERABLE_TIMEOUT;
}
return timeout;
}
private void persistDiscoverableEndTimestamp(long endTimestamp) {
SharedPreferences.Editor editor = mLocalManager.getSharedPreferences().edit();
editor.putLong(SHARED_PREFERENCES_KEY_DISCOVERABLE_END_TIMESTAMP, endTimestamp);
editor.commit();
}
private void handleModeChanged(int mode) {
if (V) {
Log.v(TAG, "Got mode changed: " + mode);
}
if (mode == BluetoothDevice.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
mCheckBoxPreference.setChecked(true);
updateCountdownSummary();
} else {
mCheckBoxPreference.setChecked(false);
}
}
private void updateCountdownSummary() {
int mode = mLocalManager.getBluetoothManager().getScanMode();
if (mode != BluetoothDevice.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
return;
}
long currentTimestamp = System.currentTimeMillis();
long endTimestamp = mLocalManager.getSharedPreferences().getLong(
SHARED_PREFERENCES_KEY_DISCOVERABLE_END_TIMESTAMP, 0);
if (currentTimestamp > endTimestamp) {
// We're still in discoverable mode, but maybe there isn't a timeout.
mCheckBoxPreference.setSummaryOn(null);
return;
}
String formattedTimeLeft = String.valueOf((endTimestamp - currentTimestamp) / 1000);
mCheckBoxPreference.setSummaryOn(
mContext.getResources().getString(R.string.bluetooth_is_discoverable,
formattedTimeLeft));
synchronized (this) {
mUiHandler.removeCallbacks(mUpdateCountdownSummaryRunnable);
mUiHandler.postDelayed(mUpdateCountdownSummaryRunnable, 1000);
}
}
}

View File

@@ -0,0 +1,149 @@
/*
* 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 com.android.settings.R;
import com.android.settings.bluetooth.LocalBluetoothManager.ExtendedBluetoothState;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.preference.Preference;
import android.preference.CheckBoxPreference;
import android.text.TextUtils;
import android.util.Config;
/**
* BluetoothEnabler is a helper to manage the Bluetooth on/off checkbox
* preference. It is turns on/off Bluetooth and ensures the summary of the
* preference reflects the current state.
*/
public class BluetoothEnabler implements Preference.OnPreferenceChangeListener {
private static final boolean LOCAL_LOGD = Config.LOGD || false;
private static final String TAG = "BluetoothEnabler";
private final Context mContext;
private final CheckBoxPreference mCheckBoxPreference;
private final CharSequence mOriginalSummary;
private final LocalBluetoothManager mLocalManager;
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
handleStateChanged(mLocalManager.getBluetoothState());
}
};
public BluetoothEnabler(Context context, CheckBoxPreference checkBoxPreference) {
mContext = context;
mCheckBoxPreference = checkBoxPreference;
mOriginalSummary = checkBoxPreference.getSummary();
checkBoxPreference.setPersistent(false);
mLocalManager = LocalBluetoothManager.getInstance(context);
if (mLocalManager == null) {
// Bluetooth not supported
checkBoxPreference.setEnabled(false);
}
}
public void resume() {
if (mLocalManager == null) {
return;
}
ExtendedBluetoothState state = mLocalManager.getBluetoothState();
// This is the widget enabled state, not the preference toggled state
mCheckBoxPreference.setEnabled(state == ExtendedBluetoothState.ENABLED ||
state == ExtendedBluetoothState.DISABLED);
// BT state is not a sticky broadcast, so set it manually
handleStateChanged(state);
mContext.registerReceiver(mReceiver,
new IntentFilter(LocalBluetoothManager.EXTENDED_BLUETOOTH_STATE_CHANGED_ACTION));
mCheckBoxPreference.setOnPreferenceChangeListener(this);
}
public void pause() {
if (mLocalManager == null) {
return;
}
mContext.unregisterReceiver(mReceiver);
mCheckBoxPreference.setOnPreferenceChangeListener(null);
}
public boolean onPreferenceChange(Preference preference, Object value) {
// Turn on/off BT
setEnabled((Boolean) value);
// Don't update UI to opposite state until we're sure
return false;
}
private void setEnabled(final boolean enable) {
// Disable preference
mCheckBoxPreference.setEnabled(false);
mLocalManager.setBluetoothEnabled(enable);
}
private void handleStateChanged(ExtendedBluetoothState state) {
if (state == ExtendedBluetoothState.DISABLED || state == ExtendedBluetoothState.ENABLED) {
mCheckBoxPreference.setChecked(state == ExtendedBluetoothState.ENABLED);
mCheckBoxPreference
.setSummary(state == ExtendedBluetoothState.DISABLED ? mOriginalSummary : null);
mCheckBoxPreference.setEnabled(isEnabledByDependency());
} else if (state == ExtendedBluetoothState.ENABLING ||
state == ExtendedBluetoothState.DISABLING) {
mCheckBoxPreference.setSummary(state == ExtendedBluetoothState.ENABLING
? R.string.wifi_starting
: R.string.wifi_stopping);
} else if (state == ExtendedBluetoothState.UNKNOWN) {
mCheckBoxPreference.setChecked(false);
mCheckBoxPreference.setSummary(R.string.wifi_error);
mCheckBoxPreference.setEnabled(true);
}
}
private boolean isEnabledByDependency() {
Preference dep = getDependencyPreference();
if (dep == null) {
return true;
}
return !dep.shouldDisableDependents();
}
private Preference getDependencyPreference() {
String depKey = mCheckBoxPreference.getDependency();
if (TextUtils.isEmpty(depKey)) {
return null;
}
return mCheckBoxPreference.getPreferenceManager().findPreference(depKey);
}
}

View File

@@ -0,0 +1,147 @@
/*
* 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 com.android.settings.bluetooth.LocalBluetoothManager.ExtendedBluetoothState;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothError;
import android.bluetooth.BluetoothIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.util.Log;
/**
* BluetoothEventRedirector receives broadcasts and callbacks from the Bluetooth
* API and dispatches the event on the UI thread to the right class in the
* Settings.
*/
public class BluetoothEventRedirector {
private static final String TAG = "BluetoothEventRedirector";
private static final boolean V = LocalBluetoothManager.V;
private LocalBluetoothManager mManager;
private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (V) {
Log.v(TAG, "Received " + intent.getAction());
}
String action = intent.getAction();
String address = intent.getStringExtra(BluetoothIntent.ADDRESS);
if (action.equals(BluetoothIntent.ENABLED_ACTION)) {
mManager.setBluetoothStateInt(ExtendedBluetoothState.ENABLED);
} else if (action.equals(BluetoothIntent.DISABLED_ACTION)) {
mManager.setBluetoothStateInt(ExtendedBluetoothState.DISABLED);
} else if (action.equals(BluetoothIntent.DISCOVERY_STARTED_ACTION)) {
mManager.onScanningStateChanged(true);
} else if (action.equals(BluetoothIntent.DISCOVERY_COMPLETED_ACTION)) {
mManager.onScanningStateChanged(false);
} else if (action.equals(BluetoothIntent.REMOTE_DEVICE_FOUND_ACTION)) {
short rssi = intent.getShortExtra(BluetoothIntent.RSSI, Short.MIN_VALUE);
mManager.getLocalDeviceManager().onDeviceAppeared(address, rssi);
} else if (action.equals(BluetoothIntent.REMOTE_DEVICE_DISAPPEARED_ACTION)) {
mManager.getLocalDeviceManager().onDeviceDisappeared(address);
} else if (action.equals(BluetoothIntent.REMOTE_NAME_UPDATED_ACTION)) {
mManager.getLocalDeviceManager().onDeviceNameUpdated(address);
} else if (action.equals(BluetoothIntent.BOND_STATE_CHANGED_ACTION)) {
int bondState = intent.getIntExtra(BluetoothIntent.BOND_STATE,
BluetoothError.ERROR);
mManager.getLocalDeviceManager().onBondingStateChanged(address, bondState);
if (bondState == BluetoothDevice.BOND_NOT_BONDED) {
int reason = intent.getIntExtra(BluetoothIntent.REASON, BluetoothError.ERROR);
if (reason == BluetoothDevice.UNBOND_REASON_AUTH_FAILED ||
reason == BluetoothDevice.UNBOND_REASON_AUTH_REJECTED ||
reason == BluetoothDevice.UNBOND_REASON_REMOTE_DEVICE_DOWN) {
mManager.getLocalDeviceManager().onBondingError(address, reason);
}
}
} else if (action.equals(BluetoothIntent.HEADSET_STATE_CHANGED_ACTION)) {
mManager.getLocalDeviceManager().onProfileStateChanged(address);
int newState = intent.getIntExtra(BluetoothIntent.HEADSET_STATE, 0);
int oldState = intent.getIntExtra(BluetoothIntent.HEADSET_PREVIOUS_STATE, 0);
if (newState == BluetoothHeadset.STATE_DISCONNECTED &&
oldState == BluetoothHeadset.STATE_CONNECTING) {
Log.i(TAG, "Failed to connect BT headset");
}
} else if (action.equals(BluetoothA2dp.SINK_STATE_CHANGED_ACTION)) {
mManager.getLocalDeviceManager().onProfileStateChanged(address);
int newState = intent.getIntExtra(BluetoothA2dp.SINK_STATE, 0);
int oldState = intent.getIntExtra(BluetoothA2dp.SINK_PREVIOUS_STATE, 0);
if (newState == BluetoothA2dp.STATE_DISCONNECTED &&
oldState == BluetoothA2dp.STATE_CONNECTING) {
Log.i(TAG, "Failed to connect BT A2DP");
}
} else if (action.equals(BluetoothIntent.REMOTE_DEVICE_CLASS_UPDATED_ACTION)) {
mManager.getLocalDeviceManager().onBtClassChanged(address);
}
}
};
public BluetoothEventRedirector(LocalBluetoothManager localBluetoothManager) {
mManager = localBluetoothManager;
}
public void start() {
IntentFilter filter = new IntentFilter();
// Bluetooth on/off broadcasts
filter.addAction(BluetoothIntent.ENABLED_ACTION);
filter.addAction(BluetoothIntent.DISABLED_ACTION);
// Discovery broadcasts
filter.addAction(BluetoothIntent.DISCOVERY_STARTED_ACTION);
filter.addAction(BluetoothIntent.DISCOVERY_COMPLETED_ACTION);
filter.addAction(BluetoothIntent.REMOTE_DEVICE_DISAPPEARED_ACTION);
filter.addAction(BluetoothIntent.REMOTE_DEVICE_FOUND_ACTION);
filter.addAction(BluetoothIntent.REMOTE_NAME_UPDATED_ACTION);
// Pairing broadcasts
filter.addAction(BluetoothIntent.BOND_STATE_CHANGED_ACTION);
// Fine-grained state broadcasts
filter.addAction(BluetoothA2dp.SINK_STATE_CHANGED_ACTION);
filter.addAction(BluetoothIntent.HEADSET_STATE_CHANGED_ACTION);
filter.addAction(BluetoothIntent.REMOTE_DEVICE_CLASS_UPDATED_ACTION);
mManager.getContext().registerReceiver(mBroadcastReceiver, filter);
}
public void stop() {
mManager.getContext().unregisterReceiver(mBroadcastReceiver);
}
}

View File

@@ -0,0 +1,79 @@
/*
* 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.BluetoothDevice;
import android.bluetooth.BluetoothIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.preference.EditTextPreference;
import android.preference.PreferenceManager;
import android.util.AttributeSet;
/**
* BluetoothNamePreference is the preference type for editing the device's
* Bluetooth name. It asks the user for a name, and persists it via the
* Bluetooth API.
*/
public class BluetoothNamePreference extends EditTextPreference {
private static final String TAG = "BluetoothNamePreference";
private LocalBluetoothManager mLocalManager;
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
setSummaryToName();
}
};
public BluetoothNamePreference(Context context, AttributeSet attrs) {
super(context, attrs);
mLocalManager = LocalBluetoothManager.getInstance(context);
setSummaryToName();
}
public void resume() {
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothIntent.ENABLED_ACTION);
filter.addAction(BluetoothIntent.NAME_CHANGED_ACTION);
getContext().registerReceiver(mReceiver, filter);
}
public void pause() {
getContext().unregisterReceiver(mReceiver);
}
private void setSummaryToName() {
BluetoothDevice manager = mLocalManager.getBluetoothManager();
if (manager.isEnabled()) {
setSummary(manager.getName());
}
}
@Override
protected boolean persistString(String value) {
BluetoothDevice manager = mLocalManager.getBluetoothManager();
manager.setName(value);
return true;
}
}

View File

@@ -0,0 +1,202 @@
/*
* 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.BluetoothDevice;
import android.bluetooth.BluetoothIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.text.Editable;
import android.text.InputFilter;
import android.text.TextWatcher;
import android.text.InputFilter.LengthFilter;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import com.android.internal.app.AlertActivity;
import com.android.internal.app.AlertController;
import com.android.settings.R;
/**
* BluetoothPinDialog asks the user to enter a PIN for pairing with a remote
* Bluetooth device. It is an activity that appears as a dialog.
*/
public class BluetoothPinDialog extends AlertActivity implements DialogInterface.OnClickListener,
TextWatcher {
private static final String TAG = "BluetoothPinDialog";
private LocalBluetoothManager mLocalManager;
private String mAddress;
private EditText mPinView;
private Button mOkButton;
private static final String INSTANCE_KEY_PAIRING_CANCELED = "received_pairing_canceled";
private boolean mReceivedPairingCanceled;
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (!BluetoothIntent.PAIRING_CANCEL_ACTION.equals(intent.getAction())) {
return;
}
String address = intent.getStringExtra(BluetoothIntent.ADDRESS);
if (address == null || address.equals(mAddress)) {
onReceivedPairingCanceled();
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
if (!intent.getAction().equals(BluetoothIntent.PAIRING_REQUEST_ACTION))
{
Log.e(TAG,
"Error: this activity may be started only with intent " +
BluetoothIntent.PAIRING_REQUEST_ACTION);
finish();
}
mLocalManager = LocalBluetoothManager.getInstance(this);
mAddress = intent.getStringExtra(BluetoothIntent.ADDRESS);
// Set up the "dialog"
final AlertController.AlertParams p = mAlertParams;
p.mIconId = android.R.drawable.ic_dialog_info;
p.mTitle = getString(R.string.bluetooth_pin_entry);
p.mView = createView();
p.mPositiveButtonText = getString(android.R.string.ok);
p.mPositiveButtonListener = this;
p.mNegativeButtonText = getString(android.R.string.cancel);
p.mNegativeButtonListener = this;
setupAlert();
mOkButton = mAlert.getButton(DialogInterface.BUTTON_POSITIVE);
mOkButton.setEnabled(false);
/*
* Leave this registered through pause/resume since we still want to
* finish the activity in the background if pairing is canceled.
*/
registerReceiver(mReceiver, new IntentFilter(BluetoothIntent.PAIRING_CANCEL_ACTION));
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
mReceivedPairingCanceled = savedInstanceState.getBoolean(INSTANCE_KEY_PAIRING_CANCELED);
if (mReceivedPairingCanceled) {
onReceivedPairingCanceled();
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(INSTANCE_KEY_PAIRING_CANCELED, mReceivedPairingCanceled);
}
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(mReceiver);
}
private View createView() {
View view = getLayoutInflater().inflate(R.layout.bluetooth_pin_entry, null);
String name = mLocalManager.getLocalDeviceManager().getName(mAddress);
TextView messageView = (TextView) view.findViewById(R.id.message);
messageView.setText(getString(R.string.bluetooth_enter_pin_msg, name));
mPinView = (EditText) view.findViewById(R.id.text);
mPinView.addTextChangedListener(this);
// Maximum of 10 characters in a PIN
mPinView.setFilters(new InputFilter[] { new LengthFilter(10) });
return view;
}
public void afterTextChanged(Editable s) {
if (s.length() > 0) {
mOkButton.setEnabled(true);
}
}
private void onReceivedPairingCanceled() {
mReceivedPairingCanceled = true;
TextView messageView = (TextView) findViewById(R.id.message);
messageView.setText(getString(R.string.bluetooth_pairing_error_message,
mLocalManager.getLocalDeviceManager().getName(mAddress)));
mPinView.setVisibility(View.GONE);
mPinView.clearFocus();
mPinView.removeTextChangedListener(this);
mOkButton.setEnabled(true);
mAlert.getButton(DialogInterface.BUTTON_NEGATIVE).setVisibility(View.GONE);
}
private void onPair(String pin) {
byte[] pinBytes = BluetoothDevice.convertPinToBytes(pin);
if (pinBytes == null) {
return;
}
mLocalManager.getBluetoothManager().setPin(mAddress, pinBytes);
}
private void onCancel() {
mLocalManager.getBluetoothManager().cancelBondProcess(mAddress);
}
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case DialogInterface.BUTTON_POSITIVE:
onPair(mPinView.getText().toString());
break;
case DialogInterface.BUTTON_NEGATIVE:
onCancel();
break;
}
}
/* Not used */
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
/* Not used */
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
}

View File

@@ -0,0 +1,96 @@
/*
* 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 com.android.settings.R;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.text.TextUtils;
/**
* BluetoothPinRequest is a receiver for any Bluetooth pairing PIN request. It
* checks if the Bluetooth Settings is currently visible and brings up the PIN
* entry dialog. Otherwise it puts a Notification in the status bar, which can
* be clicked to bring up the PIN entry dialog.
*/
public class BluetoothPinRequest extends BroadcastReceiver {
public static final int NOTIFICATION_ID = android.R.drawable.stat_sys_data_bluetooth;
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(BluetoothIntent.PAIRING_REQUEST_ACTION)) {
LocalBluetoothManager localManager = LocalBluetoothManager.getInstance(context);
String address = intent.getStringExtra(BluetoothIntent.ADDRESS);
Intent pinIntent = new Intent();
pinIntent.setClass(context, BluetoothPinDialog.class);
pinIntent.putExtra(BluetoothIntent.ADDRESS, address);
pinIntent.setAction(BluetoothIntent.PAIRING_REQUEST_ACTION);
pinIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (localManager.getForegroundActivity() != null) {
// Since the BT-related activity is in the foreground, just open the dialog
context.startActivity(pinIntent);
} else {
// Put up a notification that leads to the dialog
Resources res = context.getResources();
Notification notification = new Notification(
android.R.drawable.stat_sys_data_bluetooth,
res.getString(R.string.bluetooth_notif_ticker),
System.currentTimeMillis());
PendingIntent pending = PendingIntent.getActivity(context, 0,
pinIntent, PendingIntent.FLAG_ONE_SHOT);
String name = intent.getStringExtra(BluetoothIntent.NAME);
if (TextUtils.isEmpty(name)) {
name = localManager.getLocalDeviceManager().getName(address);
}
notification.setLatestEventInfo(context,
res.getString(R.string.bluetooth_notif_title),
res.getString(R.string.bluetooth_notif_message) + name,
pending);
notification.flags |= Notification.FLAG_AUTO_CANCEL;
NotificationManager manager = (NotificationManager)
context.getSystemService(Context.NOTIFICATION_SERVICE);
manager.notify(NOTIFICATION_ID, notification);
}
} else if (action.equals(BluetoothIntent.PAIRING_CANCEL_ACTION)) {
// Remove the notification
NotificationManager manager = (NotificationManager) context
.getSystemService(Context.NOTIFICATION_SERVICE);
manager.cancel(NOTIFICATION_ID);
}
}
}

View File

@@ -0,0 +1,260 @@
/*
* 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 com.android.settings.ProgressCategory;
import com.android.settings.R;
import com.android.settings.bluetooth.LocalBluetoothManager.ExtendedBluetoothState;
import java.util.List;
import java.util.WeakHashMap;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceScreen;
import android.view.ContextMenu;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ContextMenu.ContextMenuInfo;
import android.widget.AdapterView.AdapterContextMenuInfo;
/**
* BluetoothSettings is the Settings screen for Bluetooth configuration and
* connection management.
*/
public class BluetoothSettings extends PreferenceActivity
implements LocalBluetoothManager.Callback {
private static final String TAG = "BluetoothSettings";
private static final int MENU_SCAN = Menu.FIRST;
private static final String KEY_BT_CHECKBOX = "bt_checkbox";
private static final String KEY_BT_DISCOVERABLE = "bt_discoverable";
private static final String KEY_BT_DEVICE_LIST = "bt_device_list";
private static final String KEY_BT_NAME = "bt_name";
private static final String KEY_BT_SCAN = "bt_scan";
private LocalBluetoothManager mLocalManager;
private BluetoothEnabler mEnabler;
private BluetoothDiscoverableEnabler mDiscoverableEnabler;
private BluetoothNamePreference mNamePreference;
private ProgressCategory mDeviceList;
private WeakHashMap<LocalBluetoothDevice, BluetoothDevicePreference> mDevicePreferenceMap =
new WeakHashMap<LocalBluetoothDevice, BluetoothDevicePreference>();
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// TODO: put this in callback instead of receiving
onBluetoothStateChanged(mLocalManager.getBluetoothState());
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mLocalManager = LocalBluetoothManager.getInstance(this);
if (mLocalManager == null) finish();
addPreferencesFromResource(R.xml.bluetooth_settings);
mEnabler = new BluetoothEnabler(
this,
(CheckBoxPreference) findPreference(KEY_BT_CHECKBOX));
mDiscoverableEnabler = new BluetoothDiscoverableEnabler(
this,
(CheckBoxPreference) findPreference(KEY_BT_DISCOVERABLE));
mNamePreference = (BluetoothNamePreference) findPreference(KEY_BT_NAME);
mDeviceList = (ProgressCategory) findPreference(KEY_BT_DEVICE_LIST);
registerForContextMenu(getListView());
}
@Override
protected void onResume() {
super.onResume();
// Repopulate (which isn't too bad since it's cached in the settings
// bluetooth manager
mDevicePreferenceMap.clear();
mDeviceList.removeAll();
addDevices();
mEnabler.resume();
mDiscoverableEnabler.resume();
mNamePreference.resume();
mLocalManager.registerCallback(this);
mLocalManager.startScanning(false);
registerReceiver(mReceiver,
new IntentFilter(LocalBluetoothManager.EXTENDED_BLUETOOTH_STATE_CHANGED_ACTION));
mLocalManager.setForegroundActivity(this);
}
@Override
protected void onPause() {
super.onPause();
mLocalManager.setForegroundActivity(null);
unregisterReceiver(mReceiver);
mLocalManager.unregisterCallback(this);
mNamePreference.pause();
mDiscoverableEnabler.pause();
mEnabler.pause();
}
private void addDevices() {
List<LocalBluetoothDevice> devices = mLocalManager.getLocalDeviceManager().getDevicesCopy();
for (LocalBluetoothDevice device : devices) {
onDeviceAdded(device);
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
menu.add(0, MENU_SCAN, 0, R.string.bluetooth_scan_for_devices)
.setIcon(com.android.internal.R.drawable.ic_menu_refresh)
.setAlphabeticShortcut('r');
return true;
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
menu.findItem(MENU_SCAN).setEnabled(mLocalManager.getBluetoothManager().isEnabled());
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case MENU_SCAN:
mLocalManager.startScanning(true);
return true;
default:
return false;
}
}
@Override
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
Preference preference) {
if (KEY_BT_SCAN.equals(preference.getKey())) {
mLocalManager.startScanning(true);
return true;
}
if (preference instanceof BluetoothDevicePreference) {
BluetoothDevicePreference btPreference = (BluetoothDevicePreference) preference;
btPreference.getDevice().onClicked();
return true;
}
return super.onPreferenceTreeClick(preferenceScreen, preference);
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
LocalBluetoothDevice device = getDeviceFromMenuInfo(menuInfo);
if (device == null) return;
device.onCreateContextMenu(menu);
}
@Override
public boolean onContextItemSelected(MenuItem item) {
LocalBluetoothDevice device = getDeviceFromMenuInfo(item.getMenuInfo());
if (device == null) return false;
device.onContextItemSelected(item);
return true;
}
private LocalBluetoothDevice getDeviceFromMenuInfo(ContextMenuInfo menuInfo) {
if ((menuInfo == null) || !(menuInfo instanceof AdapterContextMenuInfo)) {
return null;
}
AdapterContextMenuInfo adapterMenuInfo = (AdapterContextMenuInfo) menuInfo;
Preference pref = (Preference) getPreferenceScreen().getRootAdapter().getItem(
adapterMenuInfo.position);
if (pref == null || !(pref instanceof BluetoothDevicePreference)) {
return null;
}
return ((BluetoothDevicePreference) pref).getDevice();
}
public void onDeviceAdded(LocalBluetoothDevice device) {
if (mDevicePreferenceMap.get(device) != null) {
throw new IllegalStateException("Got onDeviceAdded, but device already exists");
}
createDevicePreference(device);
}
private void createDevicePreference(LocalBluetoothDevice device) {
BluetoothDevicePreference preference = new BluetoothDevicePreference(this, device);
mDeviceList.addPreference(preference);
mDevicePreferenceMap.put(device, preference);
}
public void onDeviceDeleted(LocalBluetoothDevice device) {
BluetoothDevicePreference preference = mDevicePreferenceMap.remove(device);
if (preference != null) {
mDeviceList.removePreference(preference);
}
}
public void onScanningStateChanged(boolean started) {
mDeviceList.setProgress(started);
}
private void onBluetoothStateChanged(ExtendedBluetoothState bluetoothState) {
// When bluetooth is enabled (and we are in the activity, which we are),
// we should start a scan
if (bluetoothState == ExtendedBluetoothState.ENABLED) {
mLocalManager.startScanning(false);
} else if (bluetoothState == ExtendedBluetoothState.DISABLED) {
mDeviceList.setProgress(false);
}
}
}

View File

@@ -0,0 +1,298 @@
/*
* 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 com.android.settings.R;
import com.android.settings.bluetooth.LocalBluetoothProfileManager.Profile;
import android.content.Intent;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceGroup;
import android.text.TextUtils;
import android.util.Log;
/**
* ConnectSpecificProfilesActivity presents the user with all of the profiles
* for a particular device, and allows him to choose which should be connected
* (or disconnected).
*/
public class ConnectSpecificProfilesActivity extends PreferenceActivity
implements LocalBluetoothDevice.Callback, Preference.OnPreferenceChangeListener {
private static final String TAG = "ConnectSpecificProfilesActivity";
private static final String KEY_ONLINE_MODE = "online_mode";
private static final String KEY_TITLE = "title";
private static final String KEY_PROFILE_CONTAINER = "profile_container";
public static final String EXTRA_ADDRESS = "address";
private LocalBluetoothManager mManager;
private LocalBluetoothDevice mDevice;
private PreferenceGroup mProfileContainer;
private CheckBoxPreference mOnlineModePreference;
/**
* The current mode of this activity and its checkboxes (either online mode
* or offline mode). In online mode, user interactions with the profile
* checkboxes will also toggle the profile's connectivity. In offline mode,
* they will not, and only the preferred state will be saved for the
* profile.
*/
private boolean mOnlineMode;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String address;
if (savedInstanceState != null) {
address = savedInstanceState.getString(EXTRA_ADDRESS);
} else {
Intent intent = getIntent();
address = intent.getStringExtra(EXTRA_ADDRESS);
}
if (TextUtils.isEmpty(address)) {
Log.w(TAG, "Activity started without address");
finish();
}
mManager = LocalBluetoothManager.getInstance(this);
mDevice = mManager.getLocalDeviceManager().findDevice(address);
if (mDevice == null) {
Log.w(TAG, "Device not found, cannot connect to it");
finish();
}
addPreferencesFromResource(R.xml.bluetooth_device_advanced);
mProfileContainer = (PreferenceGroup) findPreference(KEY_PROFILE_CONTAINER);
// Set the title of the screen
findPreference(KEY_TITLE).setTitle(
getString(R.string.bluetooth_device_advanced_title, mDevice.getName()));
// Listen for check/uncheck of the online mode checkbox
mOnlineModePreference = (CheckBoxPreference) findPreference(KEY_ONLINE_MODE);
mOnlineModePreference.setOnPreferenceChangeListener(this);
// Add a preference for each profile
addPreferencesForProfiles();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString(EXTRA_ADDRESS, mDevice.getAddress());
}
@Override
protected void onResume() {
super.onResume();
mManager.setForegroundActivity(this);
mDevice.registerCallback(this);
refresh();
}
@Override
protected void onPause() {
super.onPause();
mDevice.unregisterCallback(this);
mManager.setForegroundActivity(null);
}
private void addPreferencesForProfiles() {
for (Profile profile : mDevice.getProfiles()) {
Preference pref = createProfilePreference(profile);
mProfileContainer.addPreference(pref);
}
}
/**
* Creates a checkbox preference for the particular profile. The key will be
* the profile's name.
*
* @param profile The profile for which the preference controls.
* @return A preference that allows the user to choose whether this profile
* will be connected to.
*/
private CheckBoxPreference createProfilePreference(Profile profile) {
CheckBoxPreference pref = new CheckBoxPreference(this);
pref.setKey(profile.toString());
pref.setTitle(profile.localizedString);
pref.setPersistent(false);
pref.setOnPreferenceChangeListener(this);
refreshProfilePreference(pref, profile);
return pref;
}
public boolean onPreferenceChange(Preference preference, Object newValue) {
String key = preference.getKey();
if (TextUtils.isEmpty(key) || newValue == null) return true;
if (key.equals(KEY_ONLINE_MODE)) {
onOnlineModeCheckedStateChanged((Boolean) newValue);
} else {
Profile profile = getProfileOf(preference);
if (profile == null) return false;
onProfileCheckedStateChanged(profile, (Boolean) newValue);
}
return true;
}
private void onOnlineModeCheckedStateChanged(boolean checked) {
setOnlineMode(checked, true);
}
private void onProfileCheckedStateChanged(Profile profile, boolean checked) {
if (mOnlineMode) {
if (checked) {
mDevice.connect(profile);
} else {
mDevice.disconnect(profile);
}
}
LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager
.getProfileManager(mManager, profile);
profileManager.setPreferred(mDevice.getAddress(), checked);
}
public void onDeviceAttributesChanged(LocalBluetoothDevice device) {
refresh();
}
private void refresh() {
// We are in 'online mode' if we are connected, connecting, or disconnecting
setOnlineMode(mDevice.isConnected() || mDevice.isBusy(), false);
refreshProfiles();
}
/**
* Switches between online/offline mode.
*
* @param onlineMode Whether to be in online mode, or offline mode.
* @param takeAction Whether to take action (i.e., connect or disconnect)
* based on the new online mode.
*/
private void setOnlineMode(boolean onlineMode, boolean takeAction) {
mOnlineMode = onlineMode;
if (takeAction) {
if (onlineMode) {
mDevice.connect();
} else {
mDevice.disconnect();
}
}
refreshOnlineModePreference();
}
private void refreshOnlineModePreference() {
mOnlineModePreference.setChecked(mOnlineMode);
/* Gray out checkbox while connecting and disconnecting */
mOnlineModePreference.setEnabled(!mDevice.isBusy());
/**
* If the device is online, show status. Otherwise, show a summary that
* describes what the checkbox does.
*/
mOnlineModePreference.setSummary(mOnlineMode ? mDevice.getSummary()
: R.string.bluetooth_device_advanced_online_mode_summary);
}
private void refreshProfiles() {
for (Profile profile : mDevice.getProfiles()) {
CheckBoxPreference profilePref =
(CheckBoxPreference) findPreference(profile.toString());
if (profilePref == null) {
profilePref = createProfilePreference(profile);
mProfileContainer.addPreference(profilePref);
} else {
refreshProfilePreference(profilePref, profile);
}
}
}
private void refreshProfilePreference(CheckBoxPreference profilePref, Profile profile) {
String address = mDevice.getAddress();
LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager
.getProfileManager(mManager, profile);
int connectionStatus = profileManager.getConnectionStatus(address);
/* Gray out checkbox while connecting and disconnecting */
profilePref.setEnabled(!mDevice.isBusy());
profilePref.setSummary(getProfileSummary(profileManager, profile, address,
connectionStatus, mOnlineMode));
profilePref.setChecked(profileManager.isPreferred(address));
}
private Profile getProfileOf(Preference pref) {
if (!(pref instanceof CheckBoxPreference)) return null;
String key = pref.getKey();
if (TextUtils.isEmpty(key)) return null;
try {
return Profile.valueOf(pref.getKey());
} catch (IllegalArgumentException e) {
return null;
}
}
private static int getProfileSummary(LocalBluetoothProfileManager profileManager,
Profile profile, String address, int connectionStatus, boolean onlineMode) {
if (!onlineMode || connectionStatus == SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED) {
return getProfileSummaryForSettingPreference(profile);
} else {
return profileManager.getSummary(address);
}
}
/**
* Gets the summary that describes when checked, it will become a preferred profile.
*
* @param profile The profile to get the summary for.
* @return The summary.
*/
private static final int getProfileSummaryForSettingPreference(Profile profile) {
switch (profile) {
case A2DP:
return R.string.bluetooth_a2dp_profile_summary_use_for;
case HEADSET:
return R.string.bluetooth_headset_profile_summary_use_for;
default:
return 0;
}
}
}

View File

@@ -0,0 +1,576 @@
/*
* 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 com.android.settings.R;
import com.android.settings.bluetooth.LocalBluetoothProfileManager.Profile;
import android.app.AlertDialog;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothClass;
import android.bluetooth.IBluetoothDeviceCallback;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Resources;
import android.os.IBinder;
import android.os.RemoteException;
import android.text.TextUtils;
import android.util.Log;
import android.view.ContextMenu;
import android.view.Menu;
import android.view.MenuItem;
import java.util.ArrayList;
import java.util.List;
/**
* LocalBluetoothDevice represents a remote Bluetooth device. It contains
* attributes of the device (such as the address, name, RSSI, etc.) and
* functionality that can be performed on the device (connect, pair, disconnect,
* etc.).
*/
public class LocalBluetoothDevice implements Comparable<LocalBluetoothDevice> {
private static final String TAG = "LocalBluetoothDevice";
private static final int CONTEXT_ITEM_CONNECT = Menu.FIRST + 1;
private static final int CONTEXT_ITEM_DISCONNECT = Menu.FIRST + 2;
private static final int CONTEXT_ITEM_UNPAIR = Menu.FIRST + 3;
private static final int CONTEXT_ITEM_CONNECT_ADVANCED = Menu.FIRST + 4;
private final String mAddress;
private String mName;
private short mRssi;
private int mBtClass = BluetoothClass.ERROR;
private List<Profile> mProfiles = new ArrayList<Profile>();
private boolean mVisible;
private final LocalBluetoothManager mLocalManager;
private List<Callback> mCallbacks = new ArrayList<Callback>();
/**
* When we connect to multiple profiles, we only want to display a single
* error even if they all fail. This tracks that state.
*/
private boolean mIsConnectingErrorPossible;
LocalBluetoothDevice(Context context, String address) {
mLocalManager = LocalBluetoothManager.getInstance(context);
if (mLocalManager == null) {
throw new IllegalStateException(
"Cannot use LocalBluetoothDevice without Bluetooth hardware");
}
mAddress = address;
fillData();
}
public void onClicked() {
int bondState = getBondState();
if (isConnected()) {
askDisconnect();
} else if (bondState == BluetoothDevice.BOND_BONDED) {
connect();
} else if (bondState == BluetoothDevice.BOND_NOT_BONDED) {
pair();
}
}
public void disconnect() {
for (Profile profile : mProfiles) {
disconnect(profile);
}
}
public void disconnect(Profile profile) {
LocalBluetoothProfileManager profileManager =
LocalBluetoothProfileManager.getProfileManager(mLocalManager, profile);
int status = profileManager.getConnectionStatus(mAddress);
if (SettingsBtStatus.isConnectionStatusConnected(status)) {
profileManager.disconnect(mAddress);
}
}
public void askDisconnect() {
Context context = mLocalManager.getForegroundActivity();
if (context == null) {
// Cannot ask, since we need an activity context
disconnect();
return;
}
Resources res = context.getResources();
String name = getName();
if (TextUtils.isEmpty(name)) {
name = res.getString(R.string.bluetooth_device);
}
String message = res.getString(R.string.bluetooth_disconnect_blank, name);
DialogInterface.OnClickListener disconnectListener = new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
disconnect();
}
};
AlertDialog ad = new AlertDialog.Builder(context)
.setTitle(getName())
.setMessage(message)
.setPositiveButton(android.R.string.ok, disconnectListener)
.setNegativeButton(android.R.string.cancel, null)
.show();
}
public void connect() {
if (!ensurePaired()) return;
// Reset the only-show-one-error-dialog tracking variable
mIsConnectingErrorPossible = true;
Context context = mLocalManager.getContext();
boolean hasAtLeastOnePreferredProfile = false;
for (Profile profile : mProfiles) {
LocalBluetoothProfileManager profileManager =
LocalBluetoothProfileManager.getProfileManager(mLocalManager, profile);
if (profileManager.isPreferred(mAddress)) {
hasAtLeastOnePreferredProfile = true;
connectInt(profile);
}
}
if (!hasAtLeastOnePreferredProfile) {
connectAndPreferAllProfiles();
}
}
private void connectAndPreferAllProfiles() {
if (!ensurePaired()) return;
// Reset the only-show-one-error-dialog tracking variable
mIsConnectingErrorPossible = true;
Context context = mLocalManager.getContext();
for (Profile profile : mProfiles) {
LocalBluetoothProfileManager profileManager =
LocalBluetoothProfileManager.getProfileManager(mLocalManager, profile);
profileManager.setPreferred(mAddress, true);
connectInt(profile);
}
}
public void connect(Profile profile) {
// Reset the only-show-one-error-dialog tracking variable
mIsConnectingErrorPossible = true;
connectInt(profile);
}
public void connectInt(Profile profile) {
if (!ensurePaired()) return;
LocalBluetoothProfileManager profileManager =
LocalBluetoothProfileManager.getProfileManager(mLocalManager, profile);
int status = profileManager.getConnectionStatus(mAddress);
if (!SettingsBtStatus.isConnectionStatusConnected(status)) {
if (profileManager.connect(mAddress) != BluetoothDevice.RESULT_SUCCESS) {
Log.i(TAG, "Failed to connect " + profile.toString() + " to " + mName);
}
}
}
public void showConnectingError() {
if (!mIsConnectingErrorPossible) return;
mIsConnectingErrorPossible = false;
mLocalManager.showError(mAddress, R.string.bluetooth_error_title,
R.string.bluetooth_connecting_error_message);
}
private boolean ensurePaired() {
if (getBondState() == BluetoothDevice.BOND_NOT_BONDED) {
pair();
return false;
} else {
return true;
}
}
public void pair() {
BluetoothDevice manager = mLocalManager.getBluetoothManager();
// Pairing is unreliable while scanning, so cancel discovery
if (manager.isDiscovering()) {
manager.cancelDiscovery();
}
if (!mLocalManager.getBluetoothManager().createBond(mAddress)) {
mLocalManager.showError(mAddress, R.string.bluetooth_error_title,
R.string.bluetooth_pairing_error_message);
}
}
public void unpair() {
BluetoothDevice manager = mLocalManager.getBluetoothManager();
switch (getBondState()) {
case BluetoothDevice.BOND_BONDED:
manager.removeBond(mAddress);
break;
case BluetoothDevice.BOND_BONDING:
manager.cancelBondProcess(mAddress);
break;
}
}
private void fillData() {
BluetoothDevice manager = mLocalManager.getBluetoothManager();
fetchName();
fetchBtClass();
mVisible = false;
dispatchAttributesChanged();
}
public String getAddress() {
return mAddress;
}
public String getName() {
return mName;
}
public void refreshName() {
fetchName();
dispatchAttributesChanged();
}
private void fetchName() {
mName = mLocalManager.getBluetoothManager().getRemoteName(mAddress);
if (TextUtils.isEmpty(mName)) {
mName = mAddress;
}
}
public void refresh() {
dispatchAttributesChanged();
}
public boolean isVisible() {
return mVisible;
}
void setVisible(boolean visible) {
if (mVisible != visible) {
mVisible = visible;
dispatchAttributesChanged();
}
}
public int getBondState() {
return mLocalManager.getBluetoothManager().getBondState(mAddress);
}
void setRssi(short rssi) {
if (mRssi != rssi) {
mRssi = rssi;
dispatchAttributesChanged();
}
}
/**
* Checks whether we are connected to this device (any profile counts).
*
* @return Whether it is connected.
*/
public boolean isConnected() {
for (Profile profile : mProfiles) {
int status = LocalBluetoothProfileManager.getProfileManager(mLocalManager, profile)
.getConnectionStatus(mAddress);
if (SettingsBtStatus.isConnectionStatusConnected(status)) {
return true;
}
}
return false;
}
public boolean isBusy() {
for (Profile profile : mProfiles) {
int status = LocalBluetoothProfileManager.getProfileManager(mLocalManager, profile)
.getConnectionStatus(mAddress);
if (SettingsBtStatus.isConnectionStatusBusy(status)) {
return true;
}
}
if (getBondState() == BluetoothDevice.BOND_BONDING) {
return true;
}
return false;
}
public int getBtClassDrawable() {
// First try looking at profiles
if (mProfiles.contains(Profile.A2DP)) {
return R.drawable.ic_bt_headphones_a2dp;
} else if (mProfiles.contains(Profile.HEADSET)) {
return R.drawable.ic_bt_headset_hfp;
}
// Fallback on class
switch (BluetoothClass.Device.Major.getDeviceMajor(mBtClass)) {
case BluetoothClass.Device.Major.COMPUTER:
return R.drawable.ic_bt_laptop;
case BluetoothClass.Device.Major.PHONE:
return R.drawable.ic_bt_cellphone;
default:
return 0;
}
}
/**
* Fetches a new value for the cached BT class.
*/
private void fetchBtClass() {
mBtClass = mLocalManager.getBluetoothManager().getRemoteClass(mAddress);
mProfiles.clear();
LocalBluetoothProfileManager.fill(mBtClass, mProfiles);
}
/**
* Refreshes the UI for the BT class, including fetching the latest value
* for the class.
*/
public void refreshBtClass() {
fetchBtClass();
dispatchAttributesChanged();
}
public int getSummary() {
// TODO: clean up
int oneOffSummary = getOneOffSummary();
if (oneOffSummary != 0) {
return oneOffSummary;
}
for (Profile profile : mProfiles) {
LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager
.getProfileManager(mLocalManager, profile);
int connectionStatus = profileManager.getConnectionStatus(mAddress);
if (SettingsBtStatus.isConnectionStatusConnected(connectionStatus) ||
connectionStatus == SettingsBtStatus.CONNECTION_STATUS_CONNECTING ||
connectionStatus == SettingsBtStatus.CONNECTION_STATUS_DISCONNECTING) {
return SettingsBtStatus.getConnectionStatusSummary(connectionStatus);
}
}
return SettingsBtStatus.getPairingStatusSummary(getBondState());
}
/**
* We have special summaries when particular profiles are connected. This
* checks for those states and returns an applicable summary.
*
* @return A one-off summary that is applicable for the current state, or 0.
*/
private int getOneOffSummary() {
boolean isA2dpConnected = false, isHeadsetConnected = false, isConnecting = false;
if (mProfiles.contains(Profile.A2DP)) {
LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager
.getProfileManager(mLocalManager, Profile.A2DP);
isConnecting = profileManager.getConnectionStatus(mAddress) ==
SettingsBtStatus.CONNECTION_STATUS_CONNECTING;
isA2dpConnected = profileManager.isConnected(mAddress);
}
if (mProfiles.contains(Profile.HEADSET)) {
LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager
.getProfileManager(mLocalManager, Profile.HEADSET);
isConnecting |= profileManager.getConnectionStatus(mAddress) ==
SettingsBtStatus.CONNECTION_STATUS_CONNECTING;
isHeadsetConnected = profileManager.isConnected(mAddress);
}
if (isConnecting) {
// If any of these important profiles is connecting, prefer that
return SettingsBtStatus.getConnectionStatusSummary(
SettingsBtStatus.CONNECTION_STATUS_CONNECTING);
} else if (isA2dpConnected && isHeadsetConnected) {
return R.string.bluetooth_summary_connected_to_a2dp_headset;
} else if (isA2dpConnected) {
return R.string.bluetooth_summary_connected_to_a2dp;
} else if (isHeadsetConnected) {
return R.string.bluetooth_summary_connected_to_headset;
} else {
return 0;
}
}
public List<Profile> getProfiles() {
return new ArrayList<Profile>(mProfiles);
}
public void onCreateContextMenu(ContextMenu menu) {
// No context menu if it is busy (none of these items are applicable if busy)
if (isBusy()) return;
int bondState = getBondState();
boolean isConnected = isConnected();
boolean hasProfiles = mProfiles.size() > 0;
menu.setHeaderTitle(getName());
if (isConnected) {
menu.add(0, CONTEXT_ITEM_DISCONNECT, 0, R.string.bluetooth_device_context_disconnect);
} else if (hasProfiles) {
// For connection action, show either "Connect" or "Pair & connect"
int connectString = (bondState == BluetoothDevice.BOND_NOT_BONDED)
? R.string.bluetooth_device_context_pair_connect
: R.string.bluetooth_device_context_connect;
menu.add(0, CONTEXT_ITEM_CONNECT, 0, connectString);
}
if (bondState == BluetoothDevice.BOND_BONDED) {
// For unpair action, show either "Unpair" or "Disconnect & unpair"
int unpairString = isConnected
? R.string.bluetooth_device_context_disconnect_unpair
: R.string.bluetooth_device_context_unpair;
menu.add(0, CONTEXT_ITEM_UNPAIR, 0, unpairString);
// Show the connection options item
menu.add(0, CONTEXT_ITEM_CONNECT_ADVANCED, 0,
R.string.bluetooth_device_context_connect_advanced);
}
}
/**
* Called when a context menu item is clicked.
*
* @param item The item that was clicked.
*/
public void onContextItemSelected(MenuItem item) {
switch (item.getItemId()) {
case CONTEXT_ITEM_DISCONNECT:
disconnect();
break;
case CONTEXT_ITEM_CONNECT:
connect();
break;
case CONTEXT_ITEM_UNPAIR:
mLocalManager.getBluetoothManager().disconnectRemoteDeviceAcl(mAddress);
unpair();
break;
case CONTEXT_ITEM_CONNECT_ADVANCED:
Intent intent = new Intent();
// Need an activity context to open this in our task
Context context = mLocalManager.getForegroundActivity();
if (context == null) {
// Fallback on application context, and open in a new task
context = mLocalManager.getContext();
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
intent.setClass(context, ConnectSpecificProfilesActivity.class);
intent.putExtra(ConnectSpecificProfilesActivity.EXTRA_ADDRESS, mAddress);
context.startActivity(intent);
break;
}
}
public void registerCallback(Callback callback) {
synchronized (mCallbacks) {
mCallbacks.add(callback);
}
}
public void unregisterCallback(Callback callback) {
synchronized (mCallbacks) {
mCallbacks.remove(callback);
}
}
private void dispatchAttributesChanged() {
synchronized (mCallbacks) {
for (Callback callback : mCallbacks) {
callback.onDeviceAttributesChanged(this);
}
}
}
@Override
public String toString() {
return mAddress;
}
@Override
public boolean equals(Object o) {
if ((o == null) || !(o instanceof LocalBluetoothDevice)) {
throw new ClassCastException();
}
return mAddress.equals(((LocalBluetoothDevice) o).mAddress);
}
@Override
public int hashCode() {
return mAddress.hashCode();
}
public int compareTo(LocalBluetoothDevice another) {
int comparison;
// Connected above not connected
comparison = (another.isConnected() ? 1 : 0) - (isConnected() ? 1 : 0);
if (comparison != 0) return comparison;
// Paired above not paired
comparison = (another.getBondState() == BluetoothDevice.BOND_BONDED ? 1 : 0) -
(getBondState() == BluetoothDevice.BOND_BONDED ? 1 : 0);
if (comparison != 0) return comparison;
// Visible above not visible
comparison = (another.mVisible ? 1 : 0) - (mVisible ? 1 : 0);
if (comparison != 0) return comparison;
// Stronger signal above weaker signal
comparison = another.mRssi - mRssi;
if (comparison != 0) return comparison;
// Fallback on name
return getName().compareTo(another.getName());
}
public interface Callback {
void onDeviceAttributesChanged(LocalBluetoothDevice device);
}
}

View File

@@ -0,0 +1,229 @@
/*
* 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.app.AlertDialog;
import android.bluetooth.BluetoothDevice;
import android.util.Log;
import android.widget.Toast;
import android.content.Context;
import com.android.settings.R;
import com.android.settings.bluetooth.LocalBluetoothManager.Callback;
import java.util.ArrayList;
import java.util.List;
/**
* LocalBluetoothDeviceManager manages the set of remote Bluetooth devices.
*/
public class LocalBluetoothDeviceManager {
private static final String TAG = "LocalBluetoothDeviceManager";
final LocalBluetoothManager mLocalManager;
final List<Callback> mCallbacks;
final List<LocalBluetoothDevice> mDevices = new ArrayList<LocalBluetoothDevice>();
public LocalBluetoothDeviceManager(LocalBluetoothManager localManager) {
mLocalManager = localManager;
mCallbacks = localManager.getCallbacks();
readPairedDevices();
}
private synchronized boolean readPairedDevices() {
BluetoothDevice manager = mLocalManager.getBluetoothManager();
String[] bondedAddresses = manager.listBonds();
if (bondedAddresses == null) return false;
boolean deviceAdded = false;
for (String address : bondedAddresses) {
LocalBluetoothDevice device = findDevice(address);
if (device == null) {
device = new LocalBluetoothDevice(mLocalManager.getContext(), address);
mDevices.add(device);
dispatchDeviceAdded(device);
deviceAdded = true;
}
}
return deviceAdded;
}
public synchronized List<LocalBluetoothDevice> getDevicesCopy() {
return new ArrayList<LocalBluetoothDevice>(mDevices);
}
void onBluetoothStateChanged(boolean enabled) {
if (enabled) {
readPairedDevices();
}
}
public synchronized void onDeviceAppeared(String address, short rssi) {
boolean deviceAdded = false;
LocalBluetoothDevice device = findDevice(address);
if (device == null) {
device = new LocalBluetoothDevice(mLocalManager.getContext(), address);
mDevices.add(device);
deviceAdded = true;
}
device.setRssi(rssi);
device.setVisible(true);
if (deviceAdded) {
dispatchDeviceAdded(device);
}
}
public synchronized void onDeviceDisappeared(String address) {
LocalBluetoothDevice device = findDevice(address);
if (device == null) return;
device.setVisible(false);
checkForDeviceRemoval(device);
}
private void checkForDeviceRemoval(LocalBluetoothDevice device) {
if (device.getBondState() == BluetoothDevice.BOND_NOT_BONDED &&
!device.isVisible()) {
// If device isn't paired, remove it altogether
mDevices.remove(device);
dispatchDeviceDeleted(device);
}
}
public synchronized void onDeviceNameUpdated(String address) {
LocalBluetoothDevice device = findDevice(address);
if (device != null) {
device.refreshName();
}
}
public synchronized LocalBluetoothDevice findDevice(String address) {
for (int i = mDevices.size() - 1; i >= 0; i--) {
LocalBluetoothDevice device = mDevices.get(i);
if (device.getAddress().equals(address)) {
return device;
}
}
return null;
}
/**
* Attempts to get the name of a remote device, otherwise returns the address.
*
* @param address The address.
* @return The name, or if unavailable, the address.
*/
public String getName(String address) {
LocalBluetoothDevice device = findDevice(address);
return device != null ? device.getName() : address;
}
private void dispatchDeviceAdded(LocalBluetoothDevice device) {
synchronized (mCallbacks) {
for (Callback callback : mCallbacks) {
callback.onDeviceAdded(device);
}
}
// TODO: divider between prev paired/connected and scanned
}
private void dispatchDeviceDeleted(LocalBluetoothDevice device) {
synchronized (mCallbacks) {
for (Callback callback : mCallbacks) {
callback.onDeviceDeleted(device);
}
}
}
public synchronized void onBondingStateChanged(String address, int bondState) {
LocalBluetoothDevice device = findDevice(address);
if (device == null) {
if (!readPairedDevices()) {
Log.e(TAG, "Got bonding state changed for " + address +
", but we have no record of that device.");
}
return;
}
device.refresh();
if (bondState == BluetoothDevice.BOND_BONDED) {
// Auto-connect after pairing
device.connect();
}
}
/**
* Called when there is a bonding error.
*
* @param address The address of the remote device.
* @param reason The reason, one of the error reasons from
* BluetoothDevice.UNBOND_REASON_*
*/
public synchronized void onBondingError(String address, int reason) {
mLocalManager.showError(address, R.string.bluetooth_error_title,
(reason == BluetoothDevice.UNBOND_REASON_AUTH_FAILED) ?
R.string.bluetooth_pairing_pin_error_message :
R.string.bluetooth_pairing_error_message);
}
public synchronized void onProfileStateChanged(String address) {
LocalBluetoothDevice device = findDevice(address);
if (device == null) return;
device.refresh();
}
public synchronized void onConnectingError(String address) {
LocalBluetoothDevice device = findDevice(address);
if (device == null) return;
/*
* Go through the device's delegate so we don't spam the user with
* errors connecting to different profiles, and instead make sure the
* user sees a single error for his single 'connect' action.
*/
device.showConnectingError();
}
public synchronized void onScanningStateChanged(boolean started) {
if (!started) return;
// If starting a new scan, clear old visibility
for (int i = mDevices.size() - 1; i >= 0; i--) {
LocalBluetoothDevice device = mDevices.get(i);
device.setVisible(false);
checkForDeviceRemoval(device);
}
}
public synchronized void onBtClassChanged(String address) {
LocalBluetoothDevice device = findDevice(address);
if (device != null) {
device.refreshBtClass();
}
}
}

View File

@@ -0,0 +1,279 @@
/*
* 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 com.android.settings.R;
import java.util.ArrayList;
import java.util.List;
import android.app.Activity;
import android.app.AlertDialog;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.util.Log;
import android.widget.Toast;
// TODO: have some notion of shutting down. Maybe a minute after they leave BT settings?
/**
* LocalBluetoothManager provides a simplified interface on top of a subset of
* the Bluetooth API.
*/
public class LocalBluetoothManager {
private static final String TAG = "LocalBluetoothManager";
static final boolean V = true;
public static final String EXTENDED_BLUETOOTH_STATE_CHANGED_ACTION =
"com.android.settings.bluetooth.intent.action.EXTENDED_BLUETOOTH_STATE_CHANGED";
private static final String SHARED_PREFERENCES_NAME = "bluetooth_settings";
private static LocalBluetoothManager INSTANCE;
/** Used when obtaining a reference to the singleton instance. */
private static Object INSTANCE_LOCK = new Object();
private boolean mInitialized;
private Context mContext;
/** If a BT-related activity is in the foreground, this will be it. */
private Activity mForegroundActivity;
private AlertDialog mErrorDialog = null;
private BluetoothDevice mManager;
private LocalBluetoothDeviceManager mLocalDeviceManager;
private BluetoothEventRedirector mEventRedirector;
private BluetoothA2dp mBluetoothA2dp;
public static enum ExtendedBluetoothState { ENABLED, ENABLING, DISABLED, DISABLING, UNKNOWN }
private ExtendedBluetoothState mState = ExtendedBluetoothState.UNKNOWN;
private List<Callback> mCallbacks = new ArrayList<Callback>();
private static final int SCAN_EXPIRATION_MS = 5 * 60 * 1000; // 5 mins
private long mLastScan;
public static LocalBluetoothManager getInstance(Context context) {
synchronized (INSTANCE_LOCK) {
if (INSTANCE == null) {
INSTANCE = new LocalBluetoothManager();
}
if (!INSTANCE.init(context)) {
return null;
}
return INSTANCE;
}
}
private boolean init(Context context) {
if (mInitialized) return true;
mInitialized = true;
// This will be around as long as this process is
mContext = context.getApplicationContext();
mManager = (BluetoothDevice) context.getSystemService(Context.BLUETOOTH_SERVICE);
if (mManager == null) {
return false;
}
mLocalDeviceManager = new LocalBluetoothDeviceManager(this);
mEventRedirector = new BluetoothEventRedirector(this);
mEventRedirector.start();
mBluetoothA2dp = new BluetoothA2dp(context);
return true;
}
public BluetoothDevice getBluetoothManager() {
return mManager;
}
public Context getContext() {
return mContext;
}
public Activity getForegroundActivity() {
return mForegroundActivity;
}
public void setForegroundActivity(Activity activity) {
if (mErrorDialog != null) {
mErrorDialog.dismiss();
mErrorDialog = null;
}
mForegroundActivity = activity;
}
public SharedPreferences getSharedPreferences() {
return mContext.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
}
public LocalBluetoothDeviceManager getLocalDeviceManager() {
return mLocalDeviceManager;
}
List<Callback> getCallbacks() {
return mCallbacks;
}
public void registerCallback(Callback callback) {
synchronized (mCallbacks) {
mCallbacks.add(callback);
}
}
public void unregisterCallback(Callback callback) {
synchronized (mCallbacks) {
mCallbacks.remove(callback);
}
}
public void startScanning(boolean force) {
if (mManager.isDiscovering()) {
/*
* Already discovering, but give the callback that information.
* Note: we only call the callbacks, not the same path as if the
* scanning state had really changed (in that case the device
* manager would clear its list of unpaired scanned devices).
*/
dispatchScanningStateChanged(true);
} else {
if (!force) {
// Don't scan more than frequently than SCAN_EXPIRATION_MS,
// unless forced
if (mLastScan + SCAN_EXPIRATION_MS > System.currentTimeMillis()) {
return;
}
// If we are playing music, don't scan unless forced.
List<String> sinks = mBluetoothA2dp.listConnectedSinks();
if (sinks != null) {
for (String address : sinks) {
if (mBluetoothA2dp.getSinkState(address) == BluetoothA2dp.STATE_PLAYING) {
return;
}
}
}
}
if (mManager.startDiscovery(true)) {
mLastScan = System.currentTimeMillis();
}
}
}
public ExtendedBluetoothState getBluetoothState() {
if (mState == ExtendedBluetoothState.UNKNOWN) {
syncBluetoothState();
}
return mState;
}
void setBluetoothStateInt(ExtendedBluetoothState state) {
mState = state;
/*
* TODO: change to callback method. originally it was broadcast to
* parallel the framework's method, but it just complicates things here.
*/
// If this were a real API, I'd add as an extra
mContext.sendBroadcast(new Intent(EXTENDED_BLUETOOTH_STATE_CHANGED_ACTION));
if (state == ExtendedBluetoothState.ENABLED || state == ExtendedBluetoothState.DISABLED) {
mLocalDeviceManager.onBluetoothStateChanged(state == ExtendedBluetoothState.ENABLED);
}
}
private void syncBluetoothState() {
setBluetoothStateInt(mManager.isEnabled()
? ExtendedBluetoothState.ENABLED
: ExtendedBluetoothState.DISABLED);
}
public void setBluetoothEnabled(boolean enabled) {
boolean wasSetStateSuccessful = enabled
? mManager.enable()
: mManager.disable();
if (wasSetStateSuccessful) {
setBluetoothStateInt(enabled
? ExtendedBluetoothState.ENABLING
: ExtendedBluetoothState.DISABLING);
} else {
if (V) {
Log.v(TAG,
"setBluetoothEnabled call, manager didn't return success for enabled: "
+ enabled);
}
syncBluetoothState();
}
}
/**
* @param started True if scanning started, false if scanning finished.
*/
void onScanningStateChanged(boolean started) {
// TODO: have it be a callback (once we switch bluetooth state changed to callback)
mLocalDeviceManager.onScanningStateChanged(started);
dispatchScanningStateChanged(started);
}
private void dispatchScanningStateChanged(boolean started) {
synchronized (mCallbacks) {
for (Callback callback : mCallbacks) {
callback.onScanningStateChanged(started);
}
}
}
public void showError(String address, int titleResId, int messageResId) {
LocalBluetoothDevice device = mLocalDeviceManager.findDevice(address);
if (device == null) return;
String name = device.getName();
String message = mContext.getString(messageResId, name);
if (mForegroundActivity != null) {
// Need an activity context to show a dialog
mErrorDialog = new AlertDialog.Builder(mForegroundActivity)
.setIcon(android.R.drawable.ic_dialog_alert)
.setTitle(titleResId)
.setMessage(message)
.setPositiveButton(android.R.string.ok, null)
.show();
} else {
// Fallback on a toast
Toast.makeText(mContext, message, Toast.LENGTH_SHORT).show();
}
}
public interface Callback {
void onScanningStateChanged(boolean started);
void onDeviceAdded(LocalBluetoothDevice device);
void onDeviceDeleted(LocalBluetoothDevice device);
}
}

View File

@@ -0,0 +1,280 @@
/*
* 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 com.android.settings.R;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothError;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothClass;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Handler;
import android.text.TextUtils;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* LocalBluetoothProfileManager is an abstract class defining the basic
* functionality related to a profile.
*/
public abstract class LocalBluetoothProfileManager {
// TODO: close profiles when we're shutting down
private static Map<Profile, LocalBluetoothProfileManager> sProfileMap =
new HashMap<Profile, LocalBluetoothProfileManager>();
protected LocalBluetoothManager mLocalManager;
public static LocalBluetoothProfileManager getProfileManager(LocalBluetoothManager localManager,
Profile profile) {
LocalBluetoothProfileManager profileManager;
synchronized (sProfileMap) {
profileManager = sProfileMap.get(profile);
if (profileManager == null) {
switch (profile) {
case A2DP:
profileManager = new A2dpProfileManager(localManager);
break;
case HEADSET:
profileManager = new HeadsetProfileManager(localManager);
break;
}
sProfileMap.put(profile, profileManager);
}
}
return profileManager;
}
/**
* Temporary method to fill profiles based on a device's class.
*
* @param btClass The class
* @param profiles The list of profiles to fill
*/
public static void fill(int btClass, List<Profile> profiles) {
profiles.clear();
if (BluetoothA2dp.doesClassMatchSink(btClass)) {
profiles.add(Profile.A2DP);
}
if (BluetoothHeadset.doesClassMatch(btClass)) {
profiles.add(Profile.HEADSET);
}
}
protected LocalBluetoothProfileManager(LocalBluetoothManager localManager) {
mLocalManager = localManager;
}
public abstract int connect(String address);
public abstract int disconnect(String address);
public abstract int getConnectionStatus(String address);
public abstract int getSummary(String address);
public abstract boolean isPreferred(String address);
public abstract void setPreferred(String address, boolean preferred);
public boolean isConnected(String address) {
return SettingsBtStatus.isConnectionStatusConnected(getConnectionStatus(address));
}
// TODO: int instead of enum
public enum Profile {
HEADSET(R.string.bluetooth_profile_headset),
A2DP(R.string.bluetooth_profile_a2dp);
public final int localizedString;
private Profile(int localizedString) {
this.localizedString = localizedString;
}
}
/**
* A2dpProfileManager is an abstraction for the {@link BluetoothA2dp} service.
*/
private static class A2dpProfileManager extends LocalBluetoothProfileManager {
private BluetoothA2dp mService;
public A2dpProfileManager(LocalBluetoothManager localManager) {
super(localManager);
mService = new BluetoothA2dp(localManager.getContext());
}
@Override
public int connect(String address) {
return mService.connectSink(address);
}
@Override
public int disconnect(String address) {
return mService.disconnectSink(address);
}
@Override
public int getConnectionStatus(String address) {
return convertState(mService.getSinkState(address));
}
@Override
public int getSummary(String address) {
int connectionStatus = getConnectionStatus(address);
if (SettingsBtStatus.isConnectionStatusConnected(connectionStatus)) {
return R.string.bluetooth_a2dp_profile_summary_connected;
} else {
return SettingsBtStatus.getConnectionStatusSummary(connectionStatus);
}
}
@Override
public boolean isPreferred(String address) {
return mService.getSinkPriority(address) > BluetoothA2dp.PRIORITY_OFF;
}
@Override
public void setPreferred(String address, boolean preferred) {
mService.setSinkPriority(address,
preferred ? BluetoothA2dp.PRIORITY_AUTO : BluetoothA2dp.PRIORITY_OFF);
}
private static int convertState(int a2dpState) {
switch (a2dpState) {
case BluetoothA2dp.STATE_CONNECTED:
return SettingsBtStatus.CONNECTION_STATUS_CONNECTED;
case BluetoothA2dp.STATE_CONNECTING:
return SettingsBtStatus.CONNECTION_STATUS_CONNECTING;
case BluetoothA2dp.STATE_DISCONNECTED:
return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED;
case BluetoothA2dp.STATE_DISCONNECTING:
return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTING;
case BluetoothA2dp.STATE_PLAYING:
return SettingsBtStatus.CONNECTION_STATUS_ACTIVE;
default:
return SettingsBtStatus.CONNECTION_STATUS_UNKNOWN;
}
}
}
/**
* HeadsetProfileManager is an abstraction for the {@link BluetoothHeadset} service.
*/
private static class HeadsetProfileManager extends LocalBluetoothProfileManager
implements BluetoothHeadset.ServiceListener {
private BluetoothHeadset mService;
private Handler mUiHandler = new Handler();
public HeadsetProfileManager(LocalBluetoothManager localManager) {
super(localManager);
mService = new BluetoothHeadset(localManager.getContext(), this);
}
public void onServiceConnected() {
// This could be called on a non-UI thread, funnel to UI thread.
mUiHandler.post(new Runnable() {
public void run() {
/*
* We just bound to the service, so refresh the UI of the
* headset device.
*/
String address = mService.getHeadsetAddress();
if (TextUtils.isEmpty(address)) return;
mLocalManager.getLocalDeviceManager().onProfileStateChanged(address);
}
});
}
public void onServiceDisconnected() {
}
@Override
public int connect(String address) {
// Since connectHeadset fails if already connected to a headset, we
// disconnect from any headset first
mService.disconnectHeadset();
return mService.connectHeadset(address)
? BluetoothError.SUCCESS : BluetoothError.ERROR;
}
@Override
public int disconnect(String address) {
if (mService.getHeadsetAddress().equals(address)) {
return mService.disconnectHeadset() ? BluetoothError.SUCCESS : BluetoothError.ERROR;
} else {
return BluetoothError.SUCCESS;
}
}
@Override
public int getConnectionStatus(String address) {
String headsetAddress = mService.getHeadsetAddress();
return headsetAddress != null && headsetAddress.equals(address)
? convertState(mService.getState())
: SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED;
}
@Override
public int getSummary(String address) {
int connectionStatus = getConnectionStatus(address);
if (SettingsBtStatus.isConnectionStatusConnected(connectionStatus)) {
return R.string.bluetooth_headset_profile_summary_connected;
} else {
return SettingsBtStatus.getConnectionStatusSummary(connectionStatus);
}
}
@Override
public boolean isPreferred(String address) {
return mService.getPriority(address) > BluetoothHeadset.PRIORITY_OFF;
}
@Override
public void setPreferred(String address, boolean preferred) {
mService.setPriority(address,
preferred ? BluetoothHeadset.PRIORITY_AUTO : BluetoothHeadset.PRIORITY_OFF);
}
private static int convertState(int headsetState) {
switch (headsetState) {
case BluetoothHeadset.STATE_CONNECTED:
return SettingsBtStatus.CONNECTION_STATUS_CONNECTED;
case BluetoothHeadset.STATE_CONNECTING:
return SettingsBtStatus.CONNECTION_STATUS_CONNECTING;
case BluetoothHeadset.STATE_DISCONNECTED:
return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED;
default:
return SettingsBtStatus.CONNECTION_STATUS_UNKNOWN;
}
}
}
}

View File

@@ -0,0 +1,81 @@
/*
* 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.BluetoothDevice;
import com.android.settings.R;
/**
* SettingsBtStatus is a helper class that contains constants for various status
* codes.
*/
public class SettingsBtStatus {
private static final String TAG = "SettingsBtStatus";
// Connection status
public static final int CONNECTION_STATUS_UNKNOWN = 0;
public static final int CONNECTION_STATUS_ACTIVE = 1;
/** Use {@link #isConnected} to check for the connected state */
public static final int CONNECTION_STATUS_CONNECTED = 2;
public static final int CONNECTION_STATUS_CONNECTING = 3;
public static final int CONNECTION_STATUS_DISCONNECTED = 4;
public static final int CONNECTION_STATUS_DISCONNECTING = 5;
public static final int getConnectionStatusSummary(int connectionStatus) {
switch (connectionStatus) {
case CONNECTION_STATUS_ACTIVE:
return R.string.bluetooth_connected;
case CONNECTION_STATUS_CONNECTED:
return R.string.bluetooth_connected;
case CONNECTION_STATUS_CONNECTING:
return R.string.bluetooth_connecting;
case CONNECTION_STATUS_DISCONNECTED:
return R.string.bluetooth_disconnected;
case CONNECTION_STATUS_DISCONNECTING:
return R.string.bluetooth_disconnecting;
case CONNECTION_STATUS_UNKNOWN:
return R.string.bluetooth_unknown;
default:
return 0;
}
}
public static final boolean isConnectionStatusConnected(int connectionStatus) {
return connectionStatus == CONNECTION_STATUS_ACTIVE
|| connectionStatus == CONNECTION_STATUS_CONNECTED;
}
public static final boolean isConnectionStatusBusy(int connectionStatus) {
return connectionStatus == CONNECTION_STATUS_CONNECTING
|| connectionStatus == CONNECTION_STATUS_DISCONNECTING;
}
public static final int getPairingStatusSummary(int bondState) {
switch (bondState) {
case BluetoothDevice.BOND_BONDED:
return R.string.bluetooth_paired;
case BluetoothDevice.BOND_BONDING:
return R.string.bluetooth_pairing;
case BluetoothDevice.BOND_NOT_BONDED:
return R.string.bluetooth_not_connected;
default:
return 0;
}
}
}

View File

@@ -0,0 +1,218 @@
/*
* 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.deviceinfo;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.Environment;
import android.os.IMountService;
import android.os.ServiceManager;
import android.os.StatFs;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceScreen;
import android.util.Log;
import com.android.settings.R;
import java.io.File;
import java.text.DecimalFormat;
public class Memory extends PreferenceActivity {
private static final String TAG = "Memory";
private static final String MEMORY_SD_SIZE = "memory_sd_size";
private static final String MEMORY_SD_AVAIL = "memory_sd_avail";
private static final String MEMORY_SD_UNMOUNT = "memory_sd_unmount";
private static final String MEMORY_SD_FORMAT = "memory_sd_format";
private Resources mRes;
private Preference mSdSize;
private Preference mSdAvail;
private Preference mSdUnmount;
private Preference mSdFormat;
// Access using getMountService()
private IMountService mMountService = null;
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
addPreferencesFromResource(R.xml.device_info_memory);
mRes = getResources();
mSdSize = findPreference(MEMORY_SD_SIZE);
mSdAvail = findPreference(MEMORY_SD_AVAIL);
mSdUnmount = findPreference(MEMORY_SD_UNMOUNT);
mSdFormat = findPreference(MEMORY_SD_FORMAT);
}
@Override
protected void onResume() {
super.onResume();
IntentFilter intentFilter = new IntentFilter(Intent.ACTION_MEDIA_REMOVED);
intentFilter.addAction(Intent.ACTION_MEDIA_UNMOUNTED);
intentFilter.addAction(Intent.ACTION_MEDIA_MOUNTED);
intentFilter.addAction(Intent.ACTION_MEDIA_SHARED);
intentFilter.addAction(Intent.ACTION_MEDIA_BAD_REMOVAL);
intentFilter.addAction(Intent.ACTION_MEDIA_UNMOUNTABLE);
intentFilter.addAction(Intent.ACTION_MEDIA_NOFS);
intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_STARTED);
intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED);
intentFilter.addDataScheme("file");
registerReceiver(mReceiver, intentFilter);
updateMemoryStatus();
}
@Override
protected void onPause() {
super.onPause();
unregisterReceiver(mReceiver);
}
private synchronized IMountService getMountService() {
if (mMountService == null) {
IBinder service = ServiceManager.getService("mount");
if (service != null) {
mMountService = IMountService.Stub.asInterface(service);
} else {
Log.e(TAG, "Can't get mount service");
}
}
return mMountService;
}
@Override
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
if (preference == mSdUnmount) {
unmount();
return true;
} else if (preference == mSdFormat) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setClass(this, com.android.settings.MediaFormat.class);
startActivity(intent);
return true;
}
return false;
}
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
updateMemoryStatus();
}
};
private void unmount() {
IMountService mountService = getMountService();
try {
if (mountService != null) {
mountService.unmountMedia(Environment.getExternalStorageDirectory().toString());
} else {
Log.e(TAG, "Mount service is null, can't unmount");
}
} catch (RemoteException ex) {
// Failed for some reason, try to update UI to actual state
updateMemoryStatus();
}
}
private void updateMemoryStatus() {
String status = Environment.getExternalStorageState();
String readOnly = "";
if (status.equals(Environment.MEDIA_MOUNTED_READ_ONLY)) {
status = Environment.MEDIA_MOUNTED;
readOnly = mRes.getString(R.string.read_only);
}
mSdFormat.setEnabled(false);
if (status.equals(Environment.MEDIA_MOUNTED)) {
try {
File path = Environment.getExternalStorageDirectory();
StatFs stat = new StatFs(path.getPath());
long blockSize = stat.getBlockSize();
long totalBlocks = stat.getBlockCount();
long availableBlocks = stat.getAvailableBlocks();
mSdSize.setSummary(formatSize(totalBlocks * blockSize));
mSdAvail.setSummary(formatSize(availableBlocks * blockSize) + readOnly);
mSdUnmount.setEnabled(true);
} catch (IllegalArgumentException e) {
// this can occur if the SD card is removed, but we haven't received the
// ACTION_MEDIA_REMOVED Intent yet.
status = Environment.MEDIA_REMOVED;
}
} else {
mSdSize.setSummary(mRes.getString(R.string.sd_unavailable));
mSdAvail.setSummary(mRes.getString(R.string.sd_unavailable));
mSdUnmount.setEnabled(false);
if (status.equals(Environment.MEDIA_UNMOUNTED) ||
status.equals(Environment.MEDIA_NOFS) ||
status.equals(Environment.MEDIA_UNMOUNTABLE) ) {
mSdFormat.setEnabled(true);
}
}
File path = Environment.getDataDirectory();
StatFs stat = new StatFs(path.getPath());
long blockSize = stat.getBlockSize();
long availableBlocks = stat.getAvailableBlocks();
findPreference("memory_internal_avail").setSummary(formatSize(availableBlocks * blockSize));
}
private String formatSize(long size) {
String suffix = null;
// add KB or MB suffix if size is greater than 1K or 1M
if (size >= 1024) {
suffix = " KB";
size /= 1024;
if (size >= 1024) {
suffix = " MB";
size /= 1024;
}
}
DecimalFormat formatter = new DecimalFormat();
formatter.setGroupingSize(3);
String result = formatter.format(size);
if (suffix != null)
result = result + suffix;
return result;
}
}

View File

@@ -0,0 +1,392 @@
/*
* 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.deviceinfo;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.BatteryManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.NetStat;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.PhoneStateIntentReceiver;
import com.android.internal.telephony.TelephonyProperties;
import com.android.settings.R;
import java.lang.ref.WeakReference;
/**
* Display the following information
* # Phone Number
* # Network
* # Roaming
* # IMEI
* # Network type
* # Signal Strength
* # Battery Strength : TODO
* # Uptime
* # Awake Time
* # XMPP/buzz/tickle status : TODO
*
*/
public class Status extends PreferenceActivity {
private static final String KEY_WIFI_MAC_ADDRESS = "wifi_mac_address";
private static final String KEY_BT_ADDRESS = "bt_address";
private static final String KEY_NETWORK_TRAFFIC_STATS = "network_traffic_stats";
private static final int EVENT_SIGNAL_STRENGTH_CHANGED = 200;
private static final int EVENT_SERVICE_STATE_CHANGED = 300;
private static final int EVENT_UPDATE_STATS = 500;
private TelephonyManager mTelephonyManager;
private Phone mPhone = null;
private PhoneStateIntentReceiver mPhoneStateReceiver;
private Resources mRes;
private Preference mSignalStrength;
private Preference mUptime;
private Preference mAwakeTime;
private static String sUnknown;
private Preference mBatteryStatus;
private Preference mBatteryLevel;
private Handler mHandler;
private static class MyHandler extends Handler {
private WeakReference<Status> mStatus;
public MyHandler(Status activity) {
mStatus = new WeakReference<Status>(activity);
}
@Override
public void handleMessage(Message msg) {
Status status = mStatus.get();
if (status == null) {
return;
}
switch (msg.what) {
case EVENT_SIGNAL_STRENGTH_CHANGED:
status.updateSignalStrength();
break;
case EVENT_SERVICE_STATE_CHANGED:
ServiceState serviceState = status.mPhoneStateReceiver.getServiceState();
status.updateServiceState(serviceState);
break;
case EVENT_UPDATE_STATS:
status.updateTimes();
status.setNetworkTrafficStats();
sendEmptyMessageDelayed(EVENT_UPDATE_STATS, 1000);
break;
}
}
}
private BroadcastReceiver mBatteryInfoReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
int level = intent.getIntExtra("level", 0);
int scale = intent.getIntExtra("scale", 100);
mBatteryLevel.setSummary(String.valueOf(level * 100 / scale) + "%");
int plugType = intent.getIntExtra("plugged", 0);
int status = intent.getIntExtra("status", BatteryManager.BATTERY_STATUS_UNKNOWN);
String statusString;
if (status == BatteryManager.BATTERY_STATUS_CHARGING) {
statusString = getString(R.string.battery_info_status_charging);
if (plugType > 0) {
statusString = statusString + " " + getString(
(plugType == BatteryManager.BATTERY_PLUGGED_AC)
? R.string.battery_info_status_charging_ac
: R.string.battery_info_status_charging_usb);
}
} else if (status == BatteryManager.BATTERY_STATUS_DISCHARGING) {
statusString = getString(R.string.battery_info_status_discharging);
} else if (status == BatteryManager.BATTERY_STATUS_NOT_CHARGING) {
statusString = getString(R.string.battery_info_status_not_charging);
} else if (status == BatteryManager.BATTERY_STATUS_FULL) {
statusString = getString(R.string.battery_info_status_full);
} else {
statusString = getString(R.string.battery_info_status_unknown);
}
mBatteryStatus.setSummary(statusString);
}
}
};
private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
@Override
public void onDataConnectionStateChanged(int state) {
updateDataState();
updateNetworkType();
}
};
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
mHandler = new MyHandler(this);
mTelephonyManager = (TelephonyManager)getSystemService(TELEPHONY_SERVICE);
addPreferencesFromResource(R.xml.device_info_status);
mBatteryLevel = findPreference("battery_level");
mBatteryStatus = findPreference("battery_status");
mRes = getResources();
if (sUnknown == null) {
sUnknown = mRes.getString(R.string.device_info_default);
}
mPhone = PhoneFactory.getDefaultPhone();
mSignalStrength = findPreference("signal_strength");
mUptime = findPreference("up_time");
mAwakeTime = findPreference("awake_time");
setSummaryText("imei", mPhone.getDeviceId());
setSummaryText("imei_sv",
((TelephonyManager) getSystemService(TELEPHONY_SERVICE))
.getDeviceSoftwareVersion());
setSummaryText("number", mPhone.getLine1Number());
mPhoneStateReceiver = new PhoneStateIntentReceiver(this, mHandler);
mPhoneStateReceiver.notifySignalStrength(EVENT_SIGNAL_STRENGTH_CHANGED);
mPhoneStateReceiver.notifyServiceState(EVENT_SERVICE_STATE_CHANGED);
setWifiStatus();
setBtStatus();
}
@Override
protected void onResume() {
super.onResume();
mPhoneStateReceiver.registerIntent();
registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
updateSignalStrength();
updateServiceState(mPhone.getServiceState());
updateDataState();
mTelephonyManager.listen(mPhoneStateListener,
PhoneStateListener.LISTEN_DATA_CONNECTION_STATE);
mHandler.sendEmptyMessage(EVENT_UPDATE_STATS);
}
@Override
public void onPause() {
super.onPause();
mPhoneStateReceiver.unregisterIntent();
mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
unregisterReceiver(mBatteryInfoReceiver);
mHandler.removeMessages(EVENT_UPDATE_STATS);
}
/**
* @param preference The key for the Preference item
* @param property The system property to fetch
* @param alt The default value, if the property doesn't exist
*/
private void setSummary(String preference, String property, String alt) {
try {
findPreference(preference).setSummary(
SystemProperties.get(property, alt));
} catch (RuntimeException e) {
}
}
private void setSummaryText(String preference, String text) {
if (TextUtils.isEmpty(text)) {
text = sUnknown;
}
findPreference(preference).setSummary(text);
}
private void updateNetworkType() {
// Whether EDGE, UMTS, etc...
setSummary("network_type", TelephonyProperties.PROPERTY_DATA_NETWORK_TYPE, sUnknown);
}
private void updateDataState() {
int state = mTelephonyManager.getDataState();
String display = mRes.getString(R.string.radioInfo_unknown);
switch (state) {
case TelephonyManager.DATA_CONNECTED:
display = mRes.getString(R.string.radioInfo_data_connected);
break;
case TelephonyManager.DATA_SUSPENDED:
display = mRes.getString(R.string.radioInfo_data_suspended);
break;
case TelephonyManager.DATA_CONNECTING:
display = mRes.getString(R.string.radioInfo_data_connecting);
break;
case TelephonyManager.DATA_DISCONNECTED:
display = mRes.getString(R.string.radioInfo_data_disconnected);
break;
}
setSummaryText("data_state", display);
}
private void updateServiceState(ServiceState serviceState) {
int state = serviceState.getState();
String display = mRes.getString(R.string.radioInfo_unknown);
switch (state) {
case ServiceState.STATE_IN_SERVICE:
display = mRes.getString(R.string.radioInfo_service_in);
break;
case ServiceState.STATE_OUT_OF_SERVICE:
case ServiceState.STATE_EMERGENCY_ONLY:
display = mRes.getString(R.string.radioInfo_service_out);
break;
case ServiceState.STATE_POWER_OFF:
display = mRes.getString(R.string.radioInfo_service_off);
break;
}
setSummaryText("service_state", display);
if (serviceState.getRoaming()) {
setSummaryText("roaming_state", mRes.getString(R.string.radioInfo_roaming_in));
} else {
setSummaryText("roaming_state", mRes.getString(R.string.radioInfo_roaming_not));
}
setSummaryText("operator_name", serviceState.getOperatorAlphaLong());
}
void updateSignalStrength() {
int state =
mPhoneStateReceiver.getServiceState().getState();
Resources r = getResources();
if ((ServiceState.STATE_OUT_OF_SERVICE == state) ||
(ServiceState.STATE_POWER_OFF == state)) {
mSignalStrength.setSummary("0");
}
int signalDbm = mPhoneStateReceiver.getSignalStrengthDbm();
if (-1 == signalDbm) signalDbm = 0;
int signalAsu = mPhoneStateReceiver.getSignalStrength();
if (-1 == signalAsu) signalAsu = 0;
mSignalStrength.setSummary(String.valueOf(signalDbm) + " "
+ r.getString(R.string.radioInfo_display_dbm) + " "
+ String.valueOf(signalAsu) + " "
+ r.getString(R.string.radioInfo_display_asu));
}
private void setWifiStatus() {
WifiManager wifiManager = (WifiManager) getSystemService(WIFI_SERVICE);
WifiInfo wifiInfo = wifiManager.getConnectionInfo();
Preference wifiMacAddressPref = findPreference(KEY_WIFI_MAC_ADDRESS);
String macAddress = wifiInfo == null ? null : wifiInfo.getMacAddress();
wifiMacAddressPref.setSummary(!TextUtils.isEmpty(macAddress) ? macAddress
: getString(R.string.status_unavailable));
}
private void setBtStatus() {
BluetoothDevice bluetooth = (BluetoothDevice) getSystemService(BLUETOOTH_SERVICE);
Preference btAddressPref = findPreference(KEY_BT_ADDRESS);
if (bluetooth == null) {
// device not BT capable
getPreferenceScreen().removePreference(btAddressPref);
} else {
String address = bluetooth.isEnabled() ? bluetooth.getAddress() : null;
btAddressPref.setSummary(!TextUtils.isEmpty(address) ? address
: getString(R.string.status_unavailable));
}
}
private void setNetworkTrafficStats() {
long txPkts = NetStat.getTotalTxPkts();
long txBytes = NetStat.getTotalTxBytes();
long rxPkts = NetStat.getTotalRxPkts();
long rxBytes = NetStat.getTotalRxBytes();
Preference netStatsPref = findPreference(KEY_NETWORK_TRAFFIC_STATS);
netStatsPref.setSummary(getString(R.string.status_network_traffic_summary,
txPkts, txBytes, rxPkts, rxBytes));
}
void updateTimes() {
long at = SystemClock.uptimeMillis() / 1000;
long ut = SystemClock.elapsedRealtime() / 1000;
long st = ut - at;
if (ut == 0) {
ut = 1;
}
mUptime.setSummary(convert(ut));
mAwakeTime.setSummary(convert(at) + " (" + (((1000 * at / ut) + 5) / 10) + "%)");
}
private String pad(int n) {
if (n >= 10) {
return String.valueOf(n);
} else {
return "0" + String.valueOf(n);
}
}
private String convert(long t) {
int s = (int)(t % 60);
int m = (int)((t / 60) % 60);
int h = (int)((t / 3600));
return h + ":" + pad(m) + ":" + pad(s);
}
}

View File

@@ -0,0 +1,331 @@
/*
* Copyright (C) 2007 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.quicklaunch;
import com.android.settings.R;
import android.app.ListActivity;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
/**
* Activity to pick a bookmark that will be returned to the caller.
* <p>
* Currently, bookmarks are either:
* <li> Activities that are in the launcher
* <li> Activities that are within an app that is capable of being launched with
* the {@link Intent#ACTION_CREATE_SHORTCUT}.
*/
public class BookmarkPicker extends ListActivity implements SimpleAdapter.ViewBinder {
private static final String TAG = "BookmarkPicker";
/** Extra in the returned intent from this activity. */
public static final String EXTRA_TITLE = "com.android.settings.quicklaunch.TITLE";
/** Extra that should be provided, and will be returned. */
public static final String EXTRA_SHORTCUT = "com.android.settings.quicklaunch.SHORTCUT";
/**
* The request code for the screen to create a bookmark that is WITHIN an
* application. For example, Gmail can return a bookmark for the inbox
* folder.
*/
private static final int REQUEST_CREATE_SHORTCUT = 1;
/** Intent used to get all the activities that are launch-able */
private static Intent sLaunchIntent;
/** Intent used to get all the activities that are {@link #REQUEST_CREATE_SHORTCUT}-able */
private static Intent sShortcutIntent;
/**
* List of ResolveInfo for activities that we can bookmark (either directly
* to the activity, or by launching the activity and it returning a bookmark
* WITHIN that application).
*/
private List<ResolveInfo> mResolveList;
// List adapter stuff
private static final String KEY_TITLE = "TITLE";
private static final String KEY_RESOLVE_INFO = "RESOLVE_INFO";
private static final String sKeys[] = new String[] { KEY_TITLE, KEY_RESOLVE_INFO };
private static final int sResourceIds[] = new int[] { R.id.title, R.id.icon };
private SimpleAdapter mMyAdapter;
/** Display those activities that are launch-able */
private static final int DISPLAY_MODE_LAUNCH = 0;
/** Display those activities that are able to have bookmarks WITHIN the application */
private static final int DISPLAY_MODE_SHORTCUT = 1;
private int mDisplayMode = DISPLAY_MODE_LAUNCH;
private Handler mUiHandler = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
updateListAndAdapter();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
menu.add(0, DISPLAY_MODE_LAUNCH, 0, R.string.quick_launch_display_mode_applications)
.setIcon(com.android.internal.R.drawable.ic_menu_archive);
menu.add(0, DISPLAY_MODE_SHORTCUT, 0, R.string.quick_launch_display_mode_shortcuts)
.setIcon(com.android.internal.R.drawable.ic_menu_goto);
return true;
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
menu.findItem(DISPLAY_MODE_LAUNCH).setVisible(mDisplayMode != DISPLAY_MODE_LAUNCH);
menu.findItem(DISPLAY_MODE_SHORTCUT).setVisible(mDisplayMode != DISPLAY_MODE_SHORTCUT);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case DISPLAY_MODE_LAUNCH:
mDisplayMode = DISPLAY_MODE_LAUNCH;
break;
case DISPLAY_MODE_SHORTCUT:
mDisplayMode = DISPLAY_MODE_SHORTCUT;
break;
default:
return false;
}
updateListAndAdapter();
return true;
}
private void ensureIntents() {
if (sLaunchIntent == null) {
sLaunchIntent = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_LAUNCHER);
sShortcutIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
}
}
/**
* This should be called from the UI thread.
*/
private void updateListAndAdapter() {
// Get the activities in a separate thread
new Thread("data updater") {
@Override
public void run() {
synchronized (BookmarkPicker.this) {
/*
* Don't touch any of the lists that are being used by the
* adapter in this thread!
*/
ArrayList<ResolveInfo> newResolveList = new ArrayList<ResolveInfo>();
ArrayList<Map<String, ?>> newAdapterList = new ArrayList<Map<String, ?>>();
fillResolveList(newResolveList);
Collections.sort(newResolveList,
new ResolveInfo.DisplayNameComparator(getPackageManager()));
fillAdapterList(newAdapterList, newResolveList);
updateAdapterToUseNewLists(newAdapterList, newResolveList);
}
}
}.start();
}
private void updateAdapterToUseNewLists(final ArrayList<Map<String, ?>> newAdapterList,
final ArrayList<ResolveInfo> newResolveList) {
// Post this back on the UI thread
mUiHandler.post(new Runnable() {
public void run() {
/*
* SimpleAdapter does not support changing the lists after it
* has been created. We just create a new instance.
*/
mMyAdapter = createResolveAdapter(newAdapterList);
mResolveList = newResolveList;
setListAdapter(mMyAdapter);
}
});
}
/**
* Gets all activities matching our current display mode.
*
* @param list The list to fill.
*/
private void fillResolveList(List<ResolveInfo> list) {
ensureIntents();
PackageManager pm = getPackageManager();
list.clear();
if (mDisplayMode == DISPLAY_MODE_LAUNCH) {
list.addAll(pm.queryIntentActivities(sLaunchIntent, 0));
} else if (mDisplayMode == DISPLAY_MODE_SHORTCUT) {
list.addAll(pm.queryIntentActivities(sShortcutIntent, 0));
}
}
private SimpleAdapter createResolveAdapter(List<Map<String, ?>> list) {
SimpleAdapter adapter = new SimpleAdapter(this, list,
R.layout.bookmark_picker_item, sKeys, sResourceIds);
adapter.setViewBinder(this);
return adapter;
}
private void fillAdapterList(List<Map<String, ?>> list,
List<ResolveInfo> resolveList) {
list.clear();
int resolveListSize = resolveList.size();
for (int i = 0; i < resolveListSize; i++) {
ResolveInfo info = resolveList.get(i);
/*
* Simple adapter craziness. For each item, we need to create a map
* from a key to its value (the value can be any object--the view
* binder will take care of filling the View with a representation
* of that object).
*/
Map<String, Object> map = new TreeMap<String, Object>();
map.put(KEY_TITLE, getResolveInfoTitle(info));
map.put(KEY_RESOLVE_INFO, info);
list.add(map);
}
}
/** Get the title for a resolve info. */
private String getResolveInfoTitle(ResolveInfo info) {
CharSequence label = info.loadLabel(getPackageManager());
if (label == null) label = info.activityInfo.name;
return label != null ? label.toString() : null;
}
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
if (position >= mResolveList.size()) return;
ResolveInfo info = mResolveList.get(position);
switch (mDisplayMode) {
case DISPLAY_MODE_LAUNCH:
// We can go ahead and return the clicked info's intent
Intent intent = getIntentForResolveInfo(info, Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
finish(intent, getResolveInfoTitle(info));
break;
case DISPLAY_MODE_SHORTCUT:
// Start the shortcut activity so the user can pick the actual intent
// (example: Gmail's shortcut activity shows a list of mailboxes)
startShortcutActivity(info);
break;
}
}
private static Intent getIntentForResolveInfo(ResolveInfo info, String action) {
Intent intent = new Intent(action);
ActivityInfo ai = info.activityInfo;
intent.setClassName(ai.packageName, ai.name);
return intent;
}
/**
* Starts an activity to get a shortcut.
* <p>
* For example, Gmail has an activity that lists the available labels. It
* returns a shortcut intent for going directly to this label.
*/
private void startShortcutActivity(ResolveInfo info) {
Intent intent = getIntentForResolveInfo(info, Intent.ACTION_CREATE_SHORTCUT);
startActivityForResult(intent, REQUEST_CREATE_SHORTCUT);
// Will get a callback to onActivityResult
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode != RESULT_OK) {
return;
}
switch (requestCode) {
case REQUEST_CREATE_SHORTCUT:
if (data != null) {
finish((Intent) data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT),
data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME));
}
break;
default:
super.onActivityResult(requestCode, resultCode, data);
break;
}
}
/**
* Finishes the activity and returns the given data.
*/
private void finish(Intent intent, String title) {
// Give back what was given to us (it will have the shortcut, for example)
intent.putExtras(getIntent());
// Put our information
intent.putExtra(EXTRA_TITLE, title);
setResult(RESULT_OK, intent);
finish();
}
/**
* {@inheritDoc}
*/
public boolean setViewValue(View view, Object data, String textRepresentation) {
if (view.getId() == R.id.icon) {
Drawable icon = ((ResolveInfo) data).loadIcon(getPackageManager());
if (icon != null) {
((ImageView) view).setImageDrawable(icon);
}
return true;
} else {
return false;
}
}
}

View File

@@ -0,0 +1,347 @@
/*
* 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.quicklaunch;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.ContentObserver;
import android.database.Cursor;
import android.os.Bundle;
import android.os.Handler;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceGroup;
import android.preference.PreferenceScreen;
import android.provider.Settings.Bookmarks;
import android.util.Log;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.View;
import android.widget.AdapterView;
import com.android.settings.R;
/**
* Settings activity for quick launch.
* <p>
* Shows a list of possible shortcuts, the current application each is bound to,
* and allows choosing a new bookmark for a shortcut.
*/
public class QuickLaunchSettings extends PreferenceActivity implements
AdapterView.OnItemLongClickListener, DialogInterface.OnClickListener {
private static final String TAG = "QuickLaunchSettings";
private static final String KEY_SHORTCUT_CATEGORY = "shortcut_category";
private static final int DIALOG_CLEAR_SHORTCUT = 0;
private static final int REQUEST_PICK_BOOKMARK = 1;
private static final int COLUMN_SHORTCUT = 0;
private static final int COLUMN_TITLE = 1;
private static final int COLUMN_INTENT = 2;
private static final String[] sProjection = new String[] {
Bookmarks.SHORTCUT, Bookmarks.TITLE, Bookmarks.INTENT
};
private static final String sShortcutSelection = Bookmarks.SHORTCUT + "=?";
private Handler mUiHandler = new Handler();
private static final String DEFAULT_BOOKMARK_FOLDER = "@quicklaunch";
/** Cursor for Bookmarks provider. */
private Cursor mBookmarksCursor;
/** Listens for changes to Bookmarks provider. */
private BookmarksObserver mBookmarksObserver;
/** Used to keep track of which shortcuts have bookmarks. */
private SparseBooleanArray mBookmarkedShortcuts;
/** Preference category to hold the shortcut preferences. */
private PreferenceGroup mShortcutGroup;
/** Mapping of a shortcut to its preference. */
private SparseArray<ShortcutPreference> mShortcutToPreference;
/** The bookmark title of the shortcut that is being cleared. */
private CharSequence mClearDialogBookmarkTitle;
private static final String CLEAR_DIALOG_BOOKMARK_TITLE = "CLEAR_DIALOG_BOOKMARK_TITLE";
/** The shortcut that is being cleared. */
private char mClearDialogShortcut;
private static final String CLEAR_DIALOG_SHORTCUT = "CLEAR_DIALOG_SHORTCUT";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.quick_launch_settings);
mShortcutGroup = (PreferenceGroup) findPreference(KEY_SHORTCUT_CATEGORY);
mShortcutToPreference = new SparseArray<ShortcutPreference>();
mBookmarksObserver = new BookmarksObserver(mUiHandler);
initShortcutPreferences();
mBookmarksCursor = managedQuery(Bookmarks.CONTENT_URI, sProjection, null, null);
getListView().setOnItemLongClickListener(this);
}
@Override
protected void onResume() {
super.onResume();
getContentResolver().registerContentObserver(Bookmarks.CONTENT_URI, true,
mBookmarksObserver);
refreshShortcuts();
}
@Override
protected void onPause() {
super.onPause();
getContentResolver().unregisterContentObserver(mBookmarksObserver);
}
@Override
protected void onRestoreInstanceState(Bundle state) {
super.onRestoreInstanceState(state);
// Restore the clear dialog's info
mClearDialogBookmarkTitle = state.getString(CLEAR_DIALOG_BOOKMARK_TITLE);
mClearDialogShortcut = (char) state.getInt(CLEAR_DIALOG_SHORTCUT, 0);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// Save the clear dialog's info
outState.putCharSequence(CLEAR_DIALOG_BOOKMARK_TITLE, mClearDialogBookmarkTitle);
outState.putInt(CLEAR_DIALOG_SHORTCUT, mClearDialogShortcut);
}
@Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case DIALOG_CLEAR_SHORTCUT: {
// Create the dialog for clearing a shortcut
return new AlertDialog.Builder(this)
.setTitle(getString(R.string.quick_launch_clear_dialog_title))
.setIcon(android.R.drawable.ic_dialog_alert)
.setMessage(getString(R.string.quick_launch_clear_dialog_message,
mClearDialogShortcut, mClearDialogBookmarkTitle))
.setPositiveButton(R.string.quick_launch_clear_ok_button, this)
.setNegativeButton(R.string.quick_launch_clear_cancel_button, this)
.create();
}
}
return super.onCreateDialog(id);
}
@Override
protected void onPrepareDialog(int id, Dialog dialog) {
switch (id) {
case DIALOG_CLEAR_SHORTCUT: {
AlertDialog alertDialog = (AlertDialog) dialog;
alertDialog.setMessage(getString(R.string.quick_launch_clear_dialog_message,
mClearDialogShortcut, mClearDialogBookmarkTitle));
}
}
}
private void showClearDialog(ShortcutPreference pref) {
if (!pref.hasBookmark()) return;
mClearDialogBookmarkTitle = pref.getTitle();
mClearDialogShortcut = pref.getShortcut();
showDialog(DIALOG_CLEAR_SHORTCUT);
}
public void onClick(DialogInterface dialog, int which) {
if (mClearDialogShortcut > 0 && which == AlertDialog.BUTTON1) {
// Clear the shortcut
clearShortcut(mClearDialogShortcut);
}
mClearDialogBookmarkTitle = null;
mClearDialogShortcut = 0;
}
private void clearShortcut(char shortcut) {
getContentResolver().delete(Bookmarks.CONTENT_URI, sShortcutSelection,
new String[] { String.valueOf((int) shortcut) });
}
@Override
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
if (!(preference instanceof ShortcutPreference)) return false;
// Open the screen to pick a bookmark for this shortcut
ShortcutPreference pref = (ShortcutPreference) preference;
Intent intent = new Intent(this, BookmarkPicker.class);
intent.putExtra(BookmarkPicker.EXTRA_SHORTCUT, pref.getShortcut());
startActivityForResult(intent, REQUEST_PICK_BOOKMARK);
return true;
}
public boolean onItemLongClick(AdapterView parent, View view, int position, long id) {
// Open the clear shortcut dialog
Preference pref = (Preference) getPreferenceScreen().getRootAdapter().getItem(position);
if (!(pref instanceof ShortcutPreference)) return false;
showClearDialog((ShortcutPreference) pref);
return true;
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode != RESULT_OK) {
return;
}
if (requestCode == REQUEST_PICK_BOOKMARK) {
// Returned from the 'pick bookmark for this shortcut' screen
if (data == null) {
Log.w(TAG, "Result from bookmark picker does not have an intent.");
return;
}
String title = data.getStringExtra(BookmarkPicker.EXTRA_TITLE);
char shortcut = data.getCharExtra(BookmarkPicker.EXTRA_SHORTCUT, (char) 0);
updateShortcut(shortcut, data);
} else {
super.onActivityResult(requestCode, resultCode, data);
}
}
private void updateShortcut(char shortcut, Intent intent) {
// Update the bookmark for a shortcut
// Pass an empty title so it gets resolved each time this bookmark is
// displayed (since the locale could change after we insert into the provider).
Bookmarks.add(getContentResolver(), intent, "", DEFAULT_BOOKMARK_FOLDER, shortcut, 0);
}
private ShortcutPreference getOrCreatePreference(char shortcut) {
ShortcutPreference pref = mShortcutToPreference.get(shortcut);
if (pref != null) {
return pref;
} else {
Log.w(TAG, "Unknown shortcut '" + shortcut + "', creating preference anyway");
return createPreference(shortcut);
}
}
private ShortcutPreference createPreference(char shortcut) {
ShortcutPreference pref = new ShortcutPreference(QuickLaunchSettings.this, shortcut);
mShortcutGroup.addPreference(pref);
mShortcutToPreference.put(shortcut, pref);
return pref;
}
private void initShortcutPreferences() {
/** Whether the shortcut has been seen already. The array index is the shortcut. */
SparseBooleanArray shortcutSeen = new SparseBooleanArray();
KeyCharacterMap keyMap = KeyCharacterMap.load(KeyCharacterMap.BUILT_IN_KEYBOARD);
// Go through all the key codes and create a preference for the appropriate keys
for (int keyCode = KeyEvent.getMaxKeyCode() - 1; keyCode >= 0; keyCode--) {
// Get the label for the primary char on the key that produces this key code
char shortcut = (char) Character.toLowerCase(keyMap.getDisplayLabel(keyCode));
if (shortcut == 0 || shortcutSeen.get(shortcut, false)) continue;
// TODO: need a to tell if the current keyboard can produce this key code, for now
// only allow the letter or digits
if (!Character.isLetterOrDigit(shortcut)) continue;
shortcutSeen.put(shortcut, true);
createPreference(shortcut);
}
}
private synchronized void refreshShortcuts() {
Cursor c = mBookmarksCursor;
if (c == null) {
// Haven't finished querying yet
return;
}
if (!c.requery()) {
Log.e(TAG, "Could not requery cursor when refreshing shortcuts.");
return;
}
/**
* We use the previous bookmarked shortcuts array to filter out those
* shortcuts that had bookmarks before this method call, and don't after
* (so we can set the preferences to be without bookmarks).
*/
SparseBooleanArray noLongerBookmarkedShortcuts = mBookmarkedShortcuts;
SparseBooleanArray newBookmarkedShortcuts = new SparseBooleanArray();
while (c.moveToNext()) {
char shortcut = Character.toLowerCase((char) c.getInt(COLUMN_SHORTCUT));
if (shortcut == 0) continue;
ShortcutPreference pref = getOrCreatePreference(shortcut);
pref.setTitle(Bookmarks.getTitle(this, c));
pref.setSummary(getString(R.string.quick_launch_shortcut,
String.valueOf(shortcut)));
pref.setHasBookmark(true);
newBookmarkedShortcuts.put(shortcut, true);
if (noLongerBookmarkedShortcuts != null) {
// After this loop, the shortcuts with value true in this array
// will no longer have bookmarks
noLongerBookmarkedShortcuts.put(shortcut, false);
}
}
if (noLongerBookmarkedShortcuts != null) {
for (int i = noLongerBookmarkedShortcuts.size() - 1; i >= 0; i--) {
if (noLongerBookmarkedShortcuts.valueAt(i)) {
// True, so there is no longer a bookmark for this shortcut
char shortcut = (char) noLongerBookmarkedShortcuts.keyAt(i);
ShortcutPreference pref = mShortcutToPreference.get(shortcut);
if (pref != null) {
pref.setHasBookmark(false);
}
}
}
}
mBookmarkedShortcuts = newBookmarkedShortcuts;
c.deactivate();
}
private class BookmarksObserver extends ContentObserver {
public BookmarksObserver(Handler handler) {
super(handler);
}
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
refreshShortcuts();
}
}
}

View File

@@ -0,0 +1,151 @@
/*
* Copyright (C) 2007 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.quicklaunch;
import com.android.settings.R;
import android.content.Context;
import android.content.res.ColorStateList;
import android.preference.Preference;
import android.util.TypedValue;
import android.view.View;
import android.widget.TextView;
/**
* Preference type for a shortcut in {@link QuickLaunchSettings}.
*/
public class ShortcutPreference extends Preference implements Comparable<Preference> {
private static Object sStaticVarsLock = new Object();
// These static fields are used across all instances of ShortcutPreference.
// There will be many ShortcutPreference instances (~36 for US).
private static String STRING_ASSIGN_APPLICATION;
private static String STRING_NO_SHORTCUT;
private static int sDimAlpha;
private static ColorStateList sRegularTitleColor;
private static ColorStateList sDimTitleColor;
private static ColorStateList sRegularSummaryColor;
private static ColorStateList sDimSummaryColor;
private char mShortcut;
private boolean mHasBookmark;
public ShortcutPreference(Context context, char shortcut) {
super(context);
synchronized (sStaticVarsLock) {
// Init statics. This should only happen for the first ShortcutPreference created,
// the rest will already have them initialized.
if (STRING_ASSIGN_APPLICATION == null) {
STRING_ASSIGN_APPLICATION = context.getString(R.string.quick_launch_assign_application);
STRING_NO_SHORTCUT = context.getString(R.string.quick_launch_no_shortcut);
TypedValue outValue = new TypedValue();
context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, outValue, true);
sDimAlpha = (int) (outValue.getFloat() * 255);
}
}
mShortcut = shortcut;
setWidgetLayoutResource(R.layout.preference_widget_shortcut);
}
public char getShortcut() {
return mShortcut;
}
public void setShortcut(char shortcut) {
if (shortcut != mShortcut) {
mShortcut = shortcut;
notifyChanged();
}
}
public boolean hasBookmark() {
return mHasBookmark;
}
public void setHasBookmark(boolean hasBookmark) {
if (hasBookmark != mHasBookmark) {
mHasBookmark = hasBookmark;
notifyChanged();
}
}
@Override
public CharSequence getTitle() {
return mHasBookmark ? super.getTitle() : STRING_ASSIGN_APPLICATION;
}
@Override
public CharSequence getSummary() {
return mHasBookmark ? super.getSummary() : STRING_NO_SHORTCUT;
}
@Override
protected void onBindView(View view) {
super.onBindView(view);
TextView shortcutView = (TextView) view.findViewById(R.id.shortcut);
if (shortcutView != null) {
shortcutView.setText(String.valueOf(mShortcut));
}
TextView titleView = (TextView) view.findViewById(android.R.id.title);
synchronized (sStaticVarsLock) {
if (sRegularTitleColor == null) {
sRegularTitleColor = titleView.getTextColors();
sDimTitleColor = sRegularTitleColor.withAlpha(sDimAlpha);
}
}
ColorStateList color = mHasBookmark ? sRegularTitleColor : sDimTitleColor;
if (color != null) {
titleView.setTextColor(color);
}
TextView summaryView = (TextView) view.findViewById(android.R.id.summary);
synchronized (sStaticVarsLock) {
if (sRegularSummaryColor == null) {
sRegularSummaryColor = summaryView.getTextColors();
sDimSummaryColor = sRegularSummaryColor.withAlpha(sDimAlpha);
}
}
color = mHasBookmark ? sRegularSummaryColor : sDimSummaryColor;
if (color != null) {
summaryView.setTextColor(color);
}
}
public int compareTo(Preference another) {
if (!(another instanceof ShortcutPreference)) return super.compareTo(another);
// Letters before digits
char other = ((ShortcutPreference) another).mShortcut;
if (Character.isDigit(mShortcut) && Character.isLetter(other)) return 1;
else if (Character.isDigit(other) && Character.isLetter(mShortcut)) return -1;
else return mShortcut - other;
}
}

View File

@@ -0,0 +1,600 @@
/*
* Copyright (C) 2007 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.wifi;
import com.android.settings.R;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.text.TextUtils;
import android.text.format.Formatter;
import android.text.method.PasswordTransformationMethod;
import android.text.method.TransformationMethod;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TableLayout;
import android.widget.TextView;
public class AccessPointDialog extends AlertDialog implements DialogInterface.OnClickListener,
AdapterView.OnItemSelectedListener, View.OnClickListener {
private static final String TAG = "AccessPointDialog";
private static final String INSTANCE_KEY_ACCESS_POINT_STATE =
"com.android.settings.wifi.AccessPointDialog:accessPointState";
private static final String INSTANCE_KEY_MODE =
"com.android.settings.wifi.AccessPointDialog:mode";
private static final String INSTANCE_KEY_CUSTOM_TITLE =
"com.android.settings.wifi.AccessPointDialog:customTitle";
private static final String INSTANCE_KEY_AUTO_SECURITY_ALLOWED =
"com.android.settings.wifi.AccessPointDialog:autoSecurityAllowed";
private static final int POSITIVE_BUTTON = BUTTON1;
private static final int NEGATIVE_BUTTON = BUTTON2;
private static final int NEUTRAL_BUTTON = BUTTON3;
/** The dialog should show info connectivity functionality */
public static final int MODE_INFO = 0;
/** The dialog should configure the detailed AP properties */
public static final int MODE_CONFIGURE = 1;
/** The dialog should have the password field and connect/cancel */
public static final int MODE_RETRY_PASSWORD = 2;
// These should be matched with the XML. Both arrays in XML depend on this
// ordering!
private static final int SECURITY_AUTO = 0;
private static final int SECURITY_NONE = 1;
private static final int SECURITY_WEP = 2;
private static final int SECURITY_WPA_PERSONAL = 3;
private static final int SECURITY_WPA2_PERSONAL = 4;
private static final int[] WEP_TYPE_VALUES = {
AccessPointState.WEP_PASSWORD_AUTO, AccessPointState.WEP_PASSWORD_ASCII,
AccessPointState.WEP_PASSWORD_HEX
};
// Button positions, default to impossible values
private int mConnectButtonPos = Integer.MAX_VALUE;
private int mForgetButtonPos = Integer.MAX_VALUE;
private int mSaveButtonPos = Integer.MAX_VALUE;
// Client configurable items. Generally, these should be saved in instance state
private int mMode = MODE_INFO;
private boolean mAutoSecurityAllowed = true;
private CharSequence mCustomTitle;
// This does not need to be saved in instance state.
private WifiLayer mWifiLayer;
private AccessPointState mState;
// General views
private View mView;
private TextView mPasswordText;
private EditText mPasswordEdit;
private CheckBox mShowPasswordCheckBox;
// Info-specific views
private ViewGroup mTable;
// Configure-specific views
private EditText mSsidEdit;
private Spinner mSecuritySpinner;
private Spinner mWepTypeSpinner;
public AccessPointDialog(Context context, WifiLayer wifiLayer) {
super(context);
mWifiLayer = wifiLayer;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
onLayout();
onFill();
super.onCreate(savedInstanceState);
}
@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
// Set to a class loader that can find AccessPointState
savedInstanceState.setClassLoader(getClass().getClassLoader());
mState = savedInstanceState.getParcelable(INSTANCE_KEY_ACCESS_POINT_STATE);
mState.setContext(getContext());
mMode = savedInstanceState.getInt(INSTANCE_KEY_MODE, mMode);
mAutoSecurityAllowed = savedInstanceState.getBoolean(INSTANCE_KEY_AUTO_SECURITY_ALLOWED,
mAutoSecurityAllowed);
mCustomTitle = savedInstanceState.getCharSequence(INSTANCE_KEY_CUSTOM_TITLE);
if (mCustomTitle != null) {
setTitle(mCustomTitle);
}
// This is called last since it depends on the above values
super.onRestoreInstanceState(savedInstanceState);
if (mShowPasswordCheckBox != null) {
// Restore the show-password-state on the edit text
setShowPassword(mShowPasswordCheckBox.isChecked());
}
}
@Override
public Bundle onSaveInstanceState() {
Bundle bundle = super.onSaveInstanceState();
bundle.putParcelable(INSTANCE_KEY_ACCESS_POINT_STATE, mState);
bundle.putInt(INSTANCE_KEY_MODE, mMode);
bundle.putBoolean(INSTANCE_KEY_AUTO_SECURITY_ALLOWED, mAutoSecurityAllowed);
bundle.putCharSequence(INSTANCE_KEY_CUSTOM_TITLE, mCustomTitle);
return bundle;
}
/**
* Sets state to show in this dialog.
*
* @param state The state.
*/
public void setState(AccessPointState state) {
mState = state;
}
/**
* Sets the dialog mode.
* @param mode One of {@link #MODE_CONFIGURE} or {@link #MODE_INFO}
*/
public void setMode(int mode) {
mMode = mode;
}
public void setAutoSecurityAllowed(boolean autoSecurityAllowed) {
mAutoSecurityAllowed = autoSecurityAllowed;
}
@Override
public void setTitle(CharSequence title) {
super.setTitle(title);
mCustomTitle = title;
}
@Override
public void setTitle(int titleId) {
setTitle(getContext().getString(titleId));
}
/** Called after flags are set, the dialog's layout/etc should be set up here */
private void onLayout() {
final Context context = getContext();
final String ssid = mState.getHumanReadableSsid();
int positiveButtonResId = 0;
int negativeButtonResId = R.string.cancel;
int neutralButtonResId = 0;
if (mCustomTitle == null) {
// Generic title is the SSID
// We don't want to trigger this as a custom title, so call super's
super.setTitle(ssid);
}
setInverseBackgroundForced(true);
boolean defaultPasswordVisibility = true;
if (mMode == MODE_CONFIGURE) {
setLayout(R.layout.wifi_ap_configure);
positiveButtonResId = R.string.wifi_save_config;
mSaveButtonPos = POSITIVE_BUTTON;
} else if (mMode == MODE_INFO) {
setLayout(R.layout.wifi_ap_info);
if (mState.isConnectable()) {
if (mCustomTitle == null) {
// We don't want to trigger this as a custom title, so call super's
super.setTitle(context.getString(R.string.connect_to_blank, ssid));
}
positiveButtonResId = R.string.connect;
mConnectButtonPos = POSITIVE_BUTTON;
}
if (mState.isForgetable()) {
if (positiveButtonResId == 0) {
positiveButtonResId = R.string.forget_network;
mForgetButtonPos = POSITIVE_BUTTON;
} else {
neutralButtonResId = R.string.forget_network;
mForgetButtonPos = NEUTRAL_BUTTON;
}
}
} else if (mMode == MODE_RETRY_PASSWORD) {
setLayout(R.layout.wifi_ap_retry_password);
positiveButtonResId = R.string.connect;
mConnectButtonPos = POSITIVE_BUTTON;
setGenericPasswordVisible(true);
defaultPasswordVisibility = false;
}
if (defaultPasswordVisibility) {
if (!mState.configured && mState.seen && mState.hasSecurity()) {
setGenericPasswordVisible(true);
} else {
setGenericPasswordVisible(false);
}
}
setButtons(positiveButtonResId, negativeButtonResId, neutralButtonResId);
}
/** Called when we need to set our member variables to point to the views. */
private void onReferenceViews(View view) {
mPasswordText = (TextView) view.findViewById(R.id.password_text);
mPasswordEdit = (EditText) view.findViewById(R.id.password_edit);
mShowPasswordCheckBox = (CheckBox) view.findViewById(R.id.show_password_checkbox);
if (mShowPasswordCheckBox != null) {
mShowPasswordCheckBox.setOnClickListener(this);
}
if (mMode == MODE_CONFIGURE) {
mSsidEdit = (EditText) view.findViewById(R.id.ssid_edit);
mSecuritySpinner = (Spinner) view.findViewById(R.id.security_spinner);
mSecuritySpinner.setOnItemSelectedListener(this);
setSecuritySpinnerAdapter();
mWepTypeSpinner = (Spinner) view.findViewById(R.id.wep_type_spinner);
} else if (mMode == MODE_INFO) {
mTable = (ViewGroup) view.findViewById(R.id.table);
}
}
private void setSecuritySpinnerAdapter() {
Context context = getContext();
int arrayResId = mAutoSecurityAllowed ? R.array.wifi_security_entries
: R.array.wifi_security_without_auto_entries;
ArrayAdapter<CharSequence> adapter = new ArrayAdapter<CharSequence>(context,
android.R.layout.simple_spinner_item,
context.getResources().getStringArray(arrayResId));
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mSecuritySpinner.setAdapter(adapter);
}
/** Called when the widgets are in-place waiting to be filled with data */
private void onFill() {
// Appears in the order added
if (mMode == MODE_INFO) {
if (mState.primary) {
addInfoRow(R.string.wifi_status, mState.getSummarizedStatus());
addInfoRow(R.string.wifi_link_speed, mState.linkSpeed + WifiInfo.LINK_SPEED_UNITS);
}
if (mState.seen) {
addInfoRow(R.string.signal, getSignalResId(mState.signal));
}
if (mState.security != null) {
addInfoRow(R.string.security, mState.getHumanReadableSecurity());
}
if (mState.primary && mState.ipAddress != 0) {
addInfoRow(R.string.ip_address, Formatter.formatIpAddress(mState.ipAddress));
}
} else if (mMode == MODE_CONFIGURE) {
String ssid = mState.getHumanReadableSsid();
if (!TextUtils.isEmpty(ssid)) {
mSsidEdit.setText(ssid);
}
mPasswordEdit.setHint(R.string.wifi_password_unchanged);
}
updatePasswordCaption(mState.security);
}
private void updatePasswordCaption(String security) {
if (mPasswordText != null && security != null
&& security.equals(AccessPointState.WEP)) {
mPasswordText.setText(R.string.please_type_hex_key);
} else {
mPasswordText.setText(R.string.please_type_passphrase);
}
}
private void addInfoRow(int nameResId, String value) {
View rowView = getLayoutInflater().inflate(R.layout.wifi_ap_info_row, mTable, false);
((TextView) rowView.findViewById(R.id.name)).setText(nameResId);
((TextView) rowView.findViewById(R.id.value)).setText(value);
mTable.addView(rowView);
}
private void addInfoRow(int nameResId, int valueResId) {
addInfoRow(nameResId, getContext().getString(valueResId));
}
private void setButtons(int positiveResId, int negativeResId, int neutralResId) {
final Context context = getContext();
if (positiveResId > 0) {
setButton(context.getString(positiveResId), this);
}
if (negativeResId > 0) {
setButton2(context.getString(negativeResId), this);
}
if (neutralResId > 0) {
setButton3(context.getString(neutralResId), this);
}
}
private void setLayout(int layoutResId) {
setView(mView = getLayoutInflater().inflate(layoutResId, null));
onReferenceViews(mView);
}
public void onClick(DialogInterface dialog, int which) {
if (which == mForgetButtonPos) {
handleForget();
} else if (which == mConnectButtonPos) {
handleConnect();
} else if (which == mSaveButtonPos) {
handleSave();
}
}
private void handleForget() {
if (!replaceStateWithWifiLayerInstance()) return;
mWifiLayer.forgetNetwork(mState);
}
private void handleConnect() {
if (!replaceStateWithWifiLayerInstance()) {
Log.w(TAG, "Assuming connecting to a new network.");
}
/*
* If the network is secured and they haven't entered a password, popup
* an error. Allow empty passwords if the state already has a password
* set (since in that scenario, an empty password means keep the old
* password).
*/
String password = getEnteredPassword();
boolean passwordIsEmpty = TextUtils.isEmpty(password);
/*
* When 'retry password', they can not enter a blank password. In any
* other mode, we let them enter a blank password if the state already
* has a password.
*/
if (passwordIsEmpty && (!mState.hasPassword() || mMode == MODE_RETRY_PASSWORD)
&& (mState.security != null) && !mState.security.equals(AccessPointState.OPEN)) {
new AlertDialog.Builder(getContext())
.setTitle(R.string.error_title)
.setIcon(android.R.drawable.ic_dialog_alert)
.setMessage(R.string.wifi_password_incorrect_error)
.setPositiveButton(android.R.string.ok, null)
.show();
return;
}
if (!passwordIsEmpty) {
mState.setPassword(password);
}
mWifiLayer.connectToNetwork(mState);
}
private void handleSave() {
replaceStateWithWifiLayerInstance();
String ssid = mSsidEdit.getText().toString();
String password = mPasswordEdit.getText().toString();
mState.setSsid(ssid);
int securityType = getSecurityTypeFromSpinner();
if (!TextUtils.isEmpty(password)) {
switch (securityType) {
case SECURITY_WPA_PERSONAL: {
mState.setSecurity(AccessPointState.WPA);
mState.setPassword(password);
break;
}
case SECURITY_WPA2_PERSONAL: {
mState.setSecurity(AccessPointState.WPA2);
mState.setPassword(password);
break;
}
case SECURITY_AUTO: {
mState.setPassword(password);
break;
}
case SECURITY_WEP: {
mState.setSecurity(AccessPointState.WEP);
mState.setPassword(password,
WEP_TYPE_VALUES[mWepTypeSpinner.getSelectedItemPosition()]);
break;
}
}
} else {
mState.setSecurity(AccessPointState.OPEN);
}
if (securityType == SECURITY_NONE) {
mState.setSecurity(AccessPointState.OPEN);
}
if (!mWifiLayer.saveNetwork(mState)) {
return;
}
// Connect right away if they've touched it
if (!mWifiLayer.connectToNetwork(mState)) {
return;
}
}
/**
* Replaces our {@link #mState} with the equal WifiLayer instance. This is useful after
* we unparceled the state previously and before we are calling methods on {@link #mWifiLayer}.
*
* @return Whether WifiLayer was able to find an equal state in its set.
*/
private boolean replaceStateWithWifiLayerInstance() {
AccessPointState state = mWifiLayer.getWifiLayerApInstance(mState);
if (state == null) {
return false;
}
mState = state;
return true;
}
private int getSecurityTypeFromSpinner() {
int position = mSecuritySpinner.getSelectedItemPosition();
// If there is no AUTO choice, the position needs 1 added to get
// to the proper spinner position -> security constants mapping
return mAutoSecurityAllowed ? position : position + 1;
}
private String getEnteredPassword() {
return mPasswordEdit != null ? mPasswordEdit.getText().toString() : null;
}
/**
* Call the one you want to hide first.
*/
private void setWepVisible(boolean visible) {
setGenericPasswordVisible(visible);
int visibility = visible ? View.VISIBLE : View.GONE;
mWepTypeSpinner.setVisibility(visibility);
}
/**
* @see #setWepVisible(boolean)
*/
private void setGenericPasswordVisible(boolean visible) {
int visibility = visible ? View.VISIBLE : View.GONE;
mPasswordText.setVisibility(visibility);
mPasswordEdit.setVisibility(visibility);
mShowPasswordCheckBox.setVisibility(visibility);
}
public void onItemSelected(AdapterView parent, View view, int position, long id) {
if (parent == mSecuritySpinner) {
handleSecurityChange(getSecurityTypeFromSpinner());
}
}
public void onNothingSelected(AdapterView parent) {
}
private void handleSecurityChange(int security) {
switch (security) {
case SECURITY_NONE: {
setWepVisible(false);
setGenericPasswordVisible(false);
break;
}
case SECURITY_WEP: {
setGenericPasswordVisible(false);
setWepVisible(true);
updatePasswordCaption(AccessPointState.WEP);
break;
}
case SECURITY_AUTO: {
setWepVisible(false);
setGenericPasswordVisible(mState.hasSecurity());
// Shows the generic 'wireless password'
updatePasswordCaption(AccessPointState.WPA);
break;
}
case SECURITY_WPA_PERSONAL:
case SECURITY_WPA2_PERSONAL: {
setWepVisible(false);
setGenericPasswordVisible(true);
// Both WPA and WPA2 show the same caption, so either is ok
updatePasswordCaption(AccessPointState.WPA);
break;
}
}
}
private static int getSignalResId(int signal) {
switch (WifiManager.calculateSignalLevel(signal, 4)) {
case 0: {
return R.string.wifi_signal_0;
}
case 1: {
return R.string.wifi_signal_1;
}
case 2: {
return R.string.wifi_signal_2;
}
case 3: {
return R.string.wifi_signal_3;
}
}
return 0;
}
public void onClick(View v) {
if (v == mShowPasswordCheckBox) {
setShowPassword(mShowPasswordCheckBox.isChecked());
}
}
private void setShowPassword(boolean showPassword) {
if (mPasswordEdit != null) {
// Toggle password
mPasswordEdit.setTransformationMethod(
showPassword ?
null :
PasswordTransformationMethod.getInstance());
}
}
}

View File

@@ -0,0 +1,108 @@
/*
* Copyright (C) 2007 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.wifi;
import com.android.settings.R;
import android.net.wifi.WifiManager;
import android.preference.Preference;
import android.view.View;
import android.widget.ImageView;
public class AccessPointPreference extends Preference implements
AccessPointState.AccessPointStateCallback {
// UI states
private static final int[] STATE_ENCRYPTED = { R.attr.state_encrypted };
private static final int[] STATE_EMPTY = { };
// Signal strength indicator
private static final int UI_SIGNAL_LEVELS = 4;
private WifiSettings mWifiSettings;
private AccessPointState mState;
public AccessPointPreference(WifiSettings wifiSettings, AccessPointState state) {
super(wifiSettings, null);
mWifiSettings = wifiSettings;
mState = state;
setWidgetLayoutResource(R.layout.preference_widget_wifi_signal);
state.setCallback(this);
refresh();
}
public void refresh() {
setTitle(mState.getHumanReadableSsid());
setSummary(mState.getSummarizedStatus());
notifyChanged();
}
public void refreshAccessPointState() {
refresh();
// The ordering of access points could have changed due to the state change, so
// re-evaluate ordering
notifyHierarchyChanged();
}
@Override
protected void onBindView(View view) {
super.onBindView(view);
ImageView signal = (ImageView) view.findViewById(R.id.signal);
if (mState.seen) {
signal.setImageResource(R.drawable.wifi_signal);
signal.setImageState(mState.hasSecurity() ? STATE_ENCRYPTED : STATE_EMPTY, true);
signal.setImageLevel(getUiSignalLevel());
} else {
signal.setImageDrawable(null);
}
}
private int getUiSignalLevel() {
return mState != null ? WifiManager.calculateSignalLevel(mState.signal, UI_SIGNAL_LEVELS)
: 0;
}
/**
* Returns the {@link AccessPointState} associated with this preference.
* @return The {@link AccessPointState}.
*/
public AccessPointState getAccessPointState() {
return mState;
}
@Override
public int compareTo(Preference another) {
if (!(another instanceof AccessPointPreference)) {
// Let normal preferences go before us.
// NOTE: we should only be compared to Preference in our
// category.
return 1;
}
return mState.compareTo(((AccessPointPreference) another).mState);
}
}

View File

@@ -0,0 +1,879 @@
/*
* Copyright (C) 2007 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.wifi;
import com.android.settings.R;
import android.content.Context;
import android.net.NetworkInfo;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiConfiguration.AuthAlgorithm;
import android.net.wifi.WifiConfiguration.GroupCipher;
import android.net.wifi.WifiConfiguration.KeyMgmt;
import android.net.wifi.WifiConfiguration.PairwiseCipher;
import android.net.wifi.WifiConfiguration.Protocol;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.Log;
public final class AccessPointState implements Comparable<AccessPointState>, Parcelable {
private static final String TAG = "AccessPointState";
// Constants used for different security types
public static final String WPA2 = "WPA2";
public static final String WPA = "WPA";
public static final String WEP = "WEP";
public static final String OPEN = "Open";
/** String present in capabilities if the scan result is ad-hoc */
private static final String ADHOC_CAPABILITY = "[IBSS]";
/** String present in capabilities if the scan result is enterprise secured */
private static final String ENTERPRISE_CAPABILITY = "-EAP-";
// Localized strings for different security types
private static String LOCALIZED_WPA2;
private static String LOCALIZED_WPA;
private static String LOCALIZED_WEP;
private static String LOCALIZED_OPEN;
private static String LOCALIZED_UNKNOWN;
private static String LOCALIZED_VERBOSE_WPA2;
private static String LOCALIZED_VERBOSE_WPA;
private static String LOCALIZED_VERBOSE_WEP;
private static String LOCALIZED_VERBOSE_OPEN;
// Localized strings for various messages
private static String SUMMARY_NOT_IN_RANGE;
private static String SUMMARY_REMEMBERED;
private static String SUMMARY_CONNECTION_FAILED;
public static final String BSSID_ANY = "any";
public static final int NETWORK_ID_NOT_SET = -1;
/** This should be used with care! */
static final int NETWORK_ID_ANY = -2;
public static final int MATCH_NONE = 0;
public static final int MATCH_WEAK = 1;
public static final int MATCH_STRONG = 2;
public static final int MATCH_EXACT = 3;
// Don't set these directly, use the setters.
public int networkId;
public int priority;
public boolean hiddenSsid;
public int linkSpeed;
public int ipAddress;
public String bssid;
public String ssid;
public int signal;
public boolean primary;
public boolean seen;
public boolean configured;
public NetworkInfo.DetailedState status;
public String security;
public boolean disabled;
/**
* Use this for sorting based on signal strength. It is a heavily-damped
* time-averaged weighted signal.
*/
private float signalForSorting = Float.MIN_VALUE;
private static final float DAMPING_FACTOR = 0.2f;
/**
* This will be a user entered password, and NOT taken from wpa_supplicant
* (since it would give us *)
*/
private String mPassword;
private boolean mConfigHadPassword;
public static final int WEP_PASSWORD_AUTO = 0;
public static final int WEP_PASSWORD_ASCII = 1;
public static final int WEP_PASSWORD_HEX = 2;
private int mWepPasswordType;
private Context mContext;
/**
* If > 0, don't refresh (changes are being batched), use
* {@link #blockRefresh()} and {@link #unblockRefresh()} only.
*/
private int mBlockRefresh;
/**
* This will be set by {@link #requestRefresh} and shouldn't be written to
* elsewhere.
*/
private boolean mNeedsRefresh;
private AccessPointStateCallback mCallback;
private StringBuilder mSummaryBuilder = new StringBuilder();
interface AccessPointStateCallback {
void refreshAccessPointState();
}
public AccessPointState(Context context) {
this();
setContext(context);
}
private AccessPointState() {
bssid = BSSID_ANY;
ssid = "";
networkId = NETWORK_ID_NOT_SET;
hiddenSsid = false;
}
void setContext(Context context) {
mContext = context;
setStrings();
}
private void setStrings() {
final Context c = mContext;
if (SUMMARY_NOT_IN_RANGE == null && c != null) {
SUMMARY_NOT_IN_RANGE = c.getString(R.string.summary_not_in_range);
SUMMARY_REMEMBERED = c.getString(R.string.summary_remembered);
SUMMARY_CONNECTION_FAILED = c.getString(R.string.summary_connection_failed);
LOCALIZED_OPEN = c.getString(R.string.wifi_security_open);
LOCALIZED_WEP = c.getString(R.string.wifi_security_wep);
LOCALIZED_WPA = c.getString(R.string.wifi_security_wpa);
LOCALIZED_WPA2 = c.getString(R.string.wifi_security_wpa2);
LOCALIZED_VERBOSE_OPEN = c.getString(R.string.wifi_security_verbose_open);
LOCALIZED_VERBOSE_WEP = c.getString(R.string.wifi_security_verbose_wep);
LOCALIZED_VERBOSE_WPA = c.getString(R.string.wifi_security_verbose_wpa);
LOCALIZED_VERBOSE_WPA2 = c.getString(R.string.wifi_security_verbose_wpa2);
LOCALIZED_UNKNOWN = c.getString(R.string.wifi_security_unknown);
}
}
public void setNetworkId(int networkId) {
this.networkId = networkId;
}
public void setBssid(String bssid) {
if (bssid != null) {
// If the BSSID is a wildcard, do NOT let a specific BSSID replace it
if (!this.bssid.equals(BSSID_ANY)) {
this.bssid = bssid;
}
}
}
private String getWpaSupplicantBssid() {
return bssid.equals(BSSID_ANY) ? null : bssid;
}
public static String convertToQuotedString(String string) {
if (TextUtils.isEmpty(string)) {
return "";
}
final int lastPos = string.length() - 1;
if (lastPos < 0 || (string.charAt(0) == '"' && string.charAt(lastPos) == '"')) {
return string;
}
return "\"" + string + "\"";
}
public void setPrimary(boolean primary) {
if (this.primary != primary) {
this.primary = primary;
requestRefresh();
}
}
public void setSeen(boolean seen) {
if (this.seen != seen) {
this.seen = seen;
requestRefresh();
}
}
public void setDisabled(boolean disabled) {
if (this.disabled != disabled) {
this.disabled = disabled;
requestRefresh();
}
}
public void setSignal(int signal) {
if (signalForSorting == Float.MIN_VALUE) {
signalForSorting = signal;
} else {
signalForSorting = (DAMPING_FACTOR * signal) + ((1-DAMPING_FACTOR) * signalForSorting);
}
if (this.signal != signal) {
this.signal = signal;
requestRefresh();
}
}
public String getHumanReadableSsid() {
if (TextUtils.isEmpty(ssid)) {
return "";
}
final int lastPos = ssid.length() - 1;
if (ssid.charAt(0) == '"' && ssid.charAt(lastPos) == '"') {
return ssid.substring(1, lastPos);
}
return ssid;
}
public void setSsid(String ssid) {
if (ssid != null) {
this.ssid = convertToQuotedString(ssid);
requestRefresh();
}
}
public void setPriority(int priority) {
if (this.priority != priority) {
this.priority = priority;
requestRefresh();
}
}
public void setHiddenSsid(boolean hiddenSsid) {
if (this.hiddenSsid != hiddenSsid) {
this.hiddenSsid = hiddenSsid;
requestRefresh();
}
}
public void setLinkSpeed(int linkSpeed) {
if (this.linkSpeed != linkSpeed) {
this.linkSpeed = linkSpeed;
requestRefresh();
}
}
public void setIpAddress(int address) {
if (ipAddress != address) {
ipAddress = address;
requestRefresh();
}
}
public void setConfigured(boolean configured) {
if (this.configured != configured) {
this.configured = configured;
requestRefresh();
}
}
public void setStatus(NetworkInfo.DetailedState status) {
if (this.status != status) {
this.status = status;
requestRefresh();
}
}
public void setSecurity(String security) {
if (TextUtils.isEmpty(this.security) || !this.security.equals(security)) {
this.security = security;
requestRefresh();
}
}
public boolean hasSecurity() {
return security != null && !security.contains(OPEN);
}
public String getHumanReadableSecurity() {
if (security.equals(OPEN)) return LOCALIZED_OPEN;
else if (security.equals(WEP)) return LOCALIZED_WEP;
else if (security.equals(WPA)) return LOCALIZED_WPA;
else if (security.equals(WPA2)) return LOCALIZED_WPA2;
return LOCALIZED_UNKNOWN;
}
public void updateFromScanResult(ScanResult scanResult) {
blockRefresh();
// We don't keep specific AP BSSIDs and instead leave that as wildcard
setSeen(true);
setSsid(scanResult.SSID);
if (networkId == NETWORK_ID_NOT_SET) {
// Since ScanResults don't cross-reference network ID, we set it as a wildcard
setNetworkId(NETWORK_ID_ANY);
}
setSignal(scanResult.level);
setSecurity(getScanResultSecurity(scanResult));
unblockRefresh();
}
/**
* @return The security of a given {@link ScanResult}.
*/
public static String getScanResultSecurity(ScanResult scanResult) {
final String cap = scanResult.capabilities;
final String[] securityModes = { WEP, WPA, WPA2 };
for (int i = securityModes.length - 1; i >= 0; i--) {
if (cap.contains(securityModes[i])) {
return securityModes[i];
}
}
return OPEN;
}
/**
* @return Whether the given ScanResult represents an adhoc network.
*/
public static boolean isAdhoc(ScanResult scanResult) {
return scanResult.capabilities.contains(ADHOC_CAPABILITY);
}
/**
* @return Whether the given ScanResult has enterprise security.
*/
public static boolean isEnterprise(ScanResult scanResult) {
return scanResult.capabilities.contains(ENTERPRISE_CAPABILITY);
}
public void updateFromWifiConfiguration(WifiConfiguration wifiConfig) {
if (wifiConfig != null) {
blockRefresh();
setBssid(wifiConfig.BSSID);
setNetworkId(wifiConfig.networkId);
setPriority(wifiConfig.priority);
setHiddenSsid(wifiConfig.hiddenSSID);
setSsid(wifiConfig.SSID);
setConfigured(true);
setDisabled(wifiConfig.status == WifiConfiguration.Status.DISABLED);
parseWifiConfigurationSecurity(wifiConfig);
unblockRefresh();
}
}
public void setPassword(String password) {
setPassword(password, WEP_PASSWORD_AUTO);
}
public void setPassword(String password, int wepPasswordType) {
mPassword = password;
mWepPasswordType = wepPasswordType;
}
public boolean hasPassword() {
return !TextUtils.isEmpty(mPassword) || mConfigHadPassword;
}
private static boolean hasPassword(WifiConfiguration wifiConfig) {
return !TextUtils.isEmpty(wifiConfig.preSharedKey)
|| !TextUtils.isEmpty(wifiConfig.wepKeys[0])
|| !TextUtils.isEmpty(wifiConfig.wepKeys[1])
|| !TextUtils.isEmpty(wifiConfig.wepKeys[2])
|| !TextUtils.isEmpty(wifiConfig.wepKeys[3]);
}
private void parseWifiConfigurationSecurity(WifiConfiguration wifiConfig) {
setSecurity(getWifiConfigurationSecurity(wifiConfig));
mConfigHadPassword = hasPassword(wifiConfig);
}
/**
* @return The security of a given {@link WifiConfiguration}.
*/
public static String getWifiConfigurationSecurity(WifiConfiguration wifiConfig) {
if (wifiConfig.allowedKeyManagement.get(KeyMgmt.NONE)) {
// If we never set group ciphers, wpa_supplicant puts all of them.
// For open, we don't set group ciphers.
// For WEP, we specifically only set WEP40 and WEP104, so CCMP
// and TKIP should not be there.
if (!wifiConfig.allowedGroupCiphers.get(GroupCipher.CCMP)
&& (wifiConfig.allowedGroupCiphers.get(GroupCipher.WEP40)
|| wifiConfig.allowedGroupCiphers.get(GroupCipher.WEP104))) {
return WEP;
} else {
return OPEN;
}
} else if (wifiConfig.allowedProtocols.get(Protocol.RSN)) {
return WPA2;
} else if (wifiConfig.allowedProtocols.get(Protocol.WPA)) {
return WPA;
} else {
Log.w(TAG, "Unknown security type from WifiConfiguration, falling back on open.");
return OPEN;
}
}
public void updateFromWifiInfo(WifiInfo wifiInfo, NetworkInfo.DetailedState state) {
if (wifiInfo != null) {
blockRefresh();
setBssid(wifiInfo.getBSSID());
setLinkSpeed(wifiInfo.getLinkSpeed());
setNetworkId(wifiInfo.getNetworkId());
setIpAddress(wifiInfo.getIpAddress());
setSsid(wifiInfo.getSSID());
if (state != null) {
setStatus(state);
}
setHiddenSsid(wifiInfo.getHiddenSSID());
unblockRefresh();
}
}
/**
* @return Whether this AP can be connected to at the moment.
*/
public boolean isConnectable() {
return !primary && seen;
}
/**
* @return Whether this AP can be forgotten at the moment.
*/
public boolean isForgetable() {
return configured;
}
/**
* Updates the state as if it were never configured.
* <p>
* Note: This will not pass the forget call to the Wi-Fi API.
*/
public void forget() {
blockRefresh();
setConfigured(false);
setNetworkId(NETWORK_ID_NOT_SET);
setPrimary(false);
setStatus(null);
setDisabled(false);
unblockRefresh();
}
public void updateWifiConfiguration(WifiConfiguration config) {
config.BSSID = getWpaSupplicantBssid();
config.priority = priority;
config.hiddenSSID = hiddenSsid;
config.SSID = convertToQuotedString(ssid);
setupSecurity(config);
}
private void setupSecurity(WifiConfiguration config) {
config.allowedAuthAlgorithms.clear();
config.allowedGroupCiphers.clear();
config.allowedKeyManagement.clear();
config.allowedPairwiseCiphers.clear();
config.allowedProtocols.clear();
if (TextUtils.isEmpty(security)) {
security = OPEN;
Log.w(TAG, "Empty security, assuming open");
}
if (security.equals(WEP)) {
// If password is empty, it should be left untouched
if (!TextUtils.isEmpty(mPassword)) {
if (mWepPasswordType == WEP_PASSWORD_AUTO) {
if (isHexWepKey(mPassword)) {
config.wepKeys[0] = mPassword;
} else {
config.wepKeys[0] = convertToQuotedString(mPassword);
}
} else {
config.wepKeys[0] = mWepPasswordType == WEP_PASSWORD_ASCII
? convertToQuotedString(mPassword)
: mPassword;
}
}
config.wepTxKeyIndex = 0;
config.allowedAuthAlgorithms.set(AuthAlgorithm.OPEN);
config.allowedAuthAlgorithms.set(AuthAlgorithm.SHARED);
config.allowedKeyManagement.set(KeyMgmt.NONE);
config.allowedGroupCiphers.set(GroupCipher.WEP40);
config.allowedGroupCiphers.set(GroupCipher.WEP104);
} else if (security.equals(WPA) || security.equals(WPA2)){
config.allowedGroupCiphers.set(GroupCipher.TKIP);
config.allowedGroupCiphers.set(GroupCipher.CCMP);
config.allowedKeyManagement.set(KeyMgmt.WPA_PSK);
config.allowedPairwiseCiphers.set(PairwiseCipher.CCMP);
config.allowedPairwiseCiphers.set(PairwiseCipher.TKIP);
config.allowedProtocols.set(security.equals(WPA2) ? Protocol.RSN : Protocol.WPA);
// If password is empty, it should be left untouched
if (!TextUtils.isEmpty(mPassword)) {
if (mPassword.length() == 64 && isHex(mPassword)) {
// Goes unquoted as hex
config.preSharedKey = mPassword;
} else {
// Goes quoted as ASCII
config.preSharedKey = convertToQuotedString(mPassword);
}
}
} else if (security.equals(OPEN)) {
config.allowedKeyManagement.set(KeyMgmt.NONE);
}
}
private static boolean isHexWepKey(String wepKey) {
final int len = wepKey.length();
// WEP-40, WEP-104, and some vendors using 256-bit WEP (WEP-232?)
if (len != 10 && len != 26 && len != 58) {
return false;
}
return isHex(wepKey);
}
private static boolean isHex(String key) {
for (int i = key.length() - 1; i >= 0; i--) {
final char c = key.charAt(i);
if (!(c >= '0' && c <= '9' || c >= 'A' && c <= 'F' || c >= 'a' && c <= 'f')) {
return false;
}
}
return true;
}
public void setCallback(AccessPointStateCallback callback) {
mCallback = callback;
}
void blockRefresh() {
mBlockRefresh++;
}
void unblockRefresh() {
if (--mBlockRefresh == 0 && mNeedsRefresh) {
requestRefresh();
}
}
private void requestRefresh() {
if (mBlockRefresh > 0) {
mNeedsRefresh = true;
return;
}
if (mCallback != null) {
mCallback.refreshAccessPointState();
}
mNeedsRefresh = false;
}
/**
* {@inheritDoc}
* @see #hashCode()
* @see #equals(Object)
*/
public int matches(int otherNetworkId, String otherBssid, String otherSsid,
String otherSecurity) {
// Whenever this method is touched, please ensure #equals and #hashCode
// still work with the changes here!
if (otherSsid == null) {
if (WifiLayer.LOGV) {
Log.w(TAG, "BSSID: " + otherBssid + ", SSID: " + otherSsid);
}
return MATCH_NONE;
}
/*
* If we both have 'security' set, it must match (an open network still
* has 'security' set to OPEN)
*/
if (security != null && otherSecurity != null) {
if (!security.equals(otherSecurity)) {
return MATCH_NONE;
}
}
// WifiConfiguration gives an empty bssid as a BSSID wildcard
if (TextUtils.isEmpty(otherBssid)) {
otherBssid = AccessPointState.BSSID_ANY;
}
final boolean networkIdMatches = networkId == otherNetworkId;
if (!networkIdMatches && networkId != NETWORK_ID_ANY && otherNetworkId != NETWORK_ID_ANY) {
// Network IDs don't match (e.g., 1 & 2 or unset & 1) and neither is a wildcard
return MATCH_NONE;
}
if (networkIdMatches && otherNetworkId != NETWORK_ID_NOT_SET
&& otherNetworkId != NETWORK_ID_ANY) {
// Network ID matches (they're set to the same ID)
return MATCH_EXACT;
}
// So now, network IDs aren't set or at least one is a wildcard
final boolean bssidMatches = bssid.equals(otherBssid);
final boolean otherBssidIsWildcard = otherBssid.equals(BSSID_ANY);
if (bssidMatches && !otherBssidIsWildcard) {
// BSSID matches (and neither is a wildcard)
return MATCH_STRONG;
}
if (!bssidMatches && !bssid.equals(BSSID_ANY) && !otherBssidIsWildcard) {
// BSSIDs don't match (e.g., 00:24:21:21:42:12 & 42:12:44:21:22:52)
// and neither is a wildcard
return MATCH_NONE;
}
// So now, BSSIDs are both wildcards
final boolean ssidMatches = ssid.equals(otherSsid);
if (ssidMatches) {
// SSID matches
return MATCH_WEAK;
}
return MATCH_NONE;
}
/**
* {@inheritDoc}
* @see #matches(int, String, String)
* @see #equals(Object)
*/
@Override
public int hashCode() {
// Two equal() objects must have same hashCode.
// With Wi-Fi, the broadest match is if two SSIDs are the same. The finer-grained matches
// imply this (for example, the same network IDs means the same WifiConfiguration which
// means the same SSID).
// See #matches for the exact matching algorithm we use.
return ssid != null ? ssid.hashCode() : 0;
}
/**
* {@inheritDoc}
* @see #matches(int, String, String)
* @see #hashCode()
*/
@Override
public boolean equals(Object o) {
if (!o.getClass().equals(getClass())) {
return false;
}
final AccessPointState other = (AccessPointState) o;
// To see which conditions cause two AccessPointStates to be equal, see
// where #matches returns MATCH_WEAK or greater.
return matches(other.networkId, other.bssid, other.ssid, other.security) >= MATCH_WEAK;
}
public int matchesWifiConfiguration(WifiConfiguration wifiConfig) {
String security = getWifiConfigurationSecurity(wifiConfig);
return matches(wifiConfig.networkId, wifiConfig.BSSID, wifiConfig.SSID, security);
}
String getSummarizedStatus() {
StringBuilder sb = mSummaryBuilder;
sb.delete(0, sb.length());
if (primary && status != null) {
buildSummary(sb, WifiStatus.getPrintable(mContext, status), true);
} else if (!seen) {
buildSummary(sb, SUMMARY_NOT_IN_RANGE, true);
// Remembered comes second in this case
if (!primary && configured) {
buildSummary(sb, SUMMARY_REMEMBERED, true);
}
} else {
if (configured && disabled) {
// The connection failure overrides all in this case
return SUMMARY_CONNECTION_FAILED;
}
// Remembered comes first in this case
if (!primary && configured) {
buildSummary(sb, SUMMARY_REMEMBERED, true);
}
// If it is seen (and not the primary), show the security type
String verboseSecurity = getVerboseSecurity();
if (verboseSecurity != null) {
buildSummary(sb, verboseSecurity, true);
}
}
return sb.toString();
}
private String getVerboseSecurity() {
if (WEP.equals(security)) {
return LOCALIZED_VERBOSE_WEP;
} else if (WPA.equals(security)) {
return LOCALIZED_VERBOSE_WPA;
} else if (WPA2.equals(security)) {
return LOCALIZED_VERBOSE_WPA2;
} else if (OPEN.equals(security)) {
return LOCALIZED_VERBOSE_OPEN;
} else {
return null;
}
}
private void buildSummary(StringBuilder sb, String string, boolean autoLowerCaseFirstLetter) {
if (sb.length() == 0) {
sb.append(string);
} else {
sb.append(", ");
if (autoLowerCaseFirstLetter) {
// Convert first letter to lowercase
sb.append(Character.toLowerCase(string.charAt(0))).append(string, 1,
string.length());
} else {
sb.append(string);
}
}
}
public int compareTo(AccessPointState other) {
// This ranks the states for displaying in the AP list, not for
// connecting to (wpa_supplicant does that using the WifiConfiguration's
// priority field).
// Clarity > efficiency, of this logic:
int comparison;
// Primary
comparison = (other.primary ? 1 : 0) - (primary ? 1 : 0);
if (comparison != 0) return comparison;
// Currently seen (similar to, but not always the same as within range)
comparison = (other.seen ? 1 : 0) - (seen ? 1 : 0);
if (comparison != 0) return comparison;
// Configured
comparison = (other.configured ? 1 : 0) - (configured ? 1 : 0);
if (comparison != 0) return comparison;
if (!configured) {
// Neither are configured
// Open network
comparison = (hasSecurity() ? 1 : 0) - (other.hasSecurity() ? 1 : 0);
if (comparison != 0) return comparison;
}
// Signal strength
comparison = (int) (other.signalForSorting - signalForSorting);
if (comparison != 0) return comparison;
// Alphabetical
return ssid.compareToIgnoreCase(other.ssid);
}
public String toString() {
return ssid + " (" + bssid + ", " + networkId + ", " + super.toString() + ")";
}
/** Implement the Parcelable interface */
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(bssid);
dest.writeInt(configured ? 1 : 0);
dest.writeInt(ipAddress);
dest.writeInt(linkSpeed);
dest.writeInt(networkId);
dest.writeInt(primary ? 1 : 0);
dest.writeInt(priority);
dest.writeInt(hiddenSsid ? 1 : 0);
dest.writeString(security);
dest.writeInt(seen ? 1 : 0);
dest.writeInt(disabled ? 1 : 0);
dest.writeInt(signal);
dest.writeString(ssid);
dest.writeString(status != null ? status.toString() : null);
dest.writeString(mPassword);
dest.writeInt(mConfigHadPassword ? 1 : 0);
dest.writeInt(mWepPasswordType);
}
/** Implement the Parcelable interface */
public int describeContents() {
return 0;
}
/** Implement the Parcelable interface */
public static final Creator<AccessPointState> CREATOR =
new Creator<AccessPointState>() {
public AccessPointState createFromParcel(Parcel in) {
AccessPointState state = new AccessPointState();
state.bssid = in.readString();
state.configured = in.readInt() == 1;
state.ipAddress = in.readInt();
state.linkSpeed = in.readInt();
state.networkId = in.readInt();
state.primary = in.readInt() == 1;
state.priority = in.readInt();
state.hiddenSsid = in.readInt() == 1;
state.security = in.readString();
state.seen = in.readInt() == 1;
state.disabled = in.readInt() == 1;
state.signal = in.readInt();
state.ssid = in.readString();
String statusStr = in.readString();
if (statusStr != null) {
state.status = NetworkInfo.DetailedState.valueOf(statusStr);
}
state.mPassword = in.readString();
state.mConfigHadPassword = in.readInt() == 1;
state.mWepPasswordType = in.readInt();
return state;
}
public AccessPointState[] newArray(int size) {
return new AccessPointState[size];
}
};
}

View File

@@ -0,0 +1,274 @@
/*
* Copyright (C) 2007 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.wifi;
import com.android.settings.R;
import android.content.ContentResolver;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.EditTextPreference;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.provider.Settings;
import android.provider.Settings.System;
import android.text.TextUtils;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;
public class AdvancedSettings extends PreferenceActivity
implements Preference.OnPreferenceChangeListener {
private static final String KEY_MAC_ADDRESS = "mac_address";
private static final String KEY_USE_STATIC_IP = "use_static_ip";
private static final String KEY_NUM_CHANNELS = "num_channels";
private static final String KEY_SLEEP_POLICY = "sleep_policy";
private String[] mSettingNames = {
System.WIFI_STATIC_IP, System.WIFI_STATIC_GATEWAY, System.WIFI_STATIC_NETMASK,
System.WIFI_STATIC_DNS1, System.WIFI_STATIC_DNS2
};
private String[] mPreferenceKeys = {
"ip_address", "gateway", "netmask", "dns1", "dns2"
};
private CheckBoxPreference mUseStaticIpCheckBox;
private static final int MENU_ITEM_SAVE = Menu.FIRST;
private static final int MENU_ITEM_CANCEL = Menu.FIRST + 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.wifi_advanced_settings);
mUseStaticIpCheckBox = (CheckBoxPreference) findPreference(KEY_USE_STATIC_IP);
for (int i = 0; i < mPreferenceKeys.length; i++) {
Preference preference = findPreference(mPreferenceKeys[i]);
preference.setOnPreferenceChangeListener(this);
}
}
@Override
protected void onResume() {
super.onResume();
updateUi();
initNumChannelsPreference();
initSleepPolicyPreference();
refreshMacAddress();
}
private void initNumChannelsPreference() {
ListPreference pref = (ListPreference) findPreference(KEY_NUM_CHANNELS);
pref.setOnPreferenceChangeListener(this);
WifiManager wifiManager = (WifiManager) getSystemService(WIFI_SERVICE);
/*
* Generate the list of valid channel counts to show in the ListPreference.
* The values are numerical, so the only text to be localized is the
* "channel_word" resource.
*/
int[] validChannelCounts = wifiManager.getValidChannelCounts();
if (validChannelCounts == null) {
Toast.makeText(this, R.string.wifi_setting_num_channels_error,
Toast.LENGTH_SHORT).show();
pref.setEnabled(false);
return;
}
String[] entries = new String[validChannelCounts.length];
String[] entryValues = new String[validChannelCounts.length];
for (int i = 0; i < validChannelCounts.length; i++) {
entryValues[i] = String.valueOf(validChannelCounts[i]);
entries[i] = getString(R.string.wifi_setting_num_channels_channel_phrase,
validChannelCounts[i]);
}
pref.setEntries(entries);
pref.setEntryValues(entryValues);
pref.setEnabled(true);
int numChannels = wifiManager.getNumAllowedChannels();
if (numChannels >= 0) {
pref.setValue(String.valueOf(numChannels));
}
}
private void initSleepPolicyPreference() {
ListPreference pref = (ListPreference) findPreference(KEY_SLEEP_POLICY);
pref.setOnPreferenceChangeListener(this);
int value = Settings.System.getInt(getContentResolver(),
Settings.System.WIFI_SLEEP_POLICY,Settings. System.WIFI_SLEEP_POLICY_DEFAULT);
pref.setValue(String.valueOf(value));
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
updateSettingsProvider();
}
return super.onKeyDown(keyCode, event);
}
public boolean onPreferenceChange(Preference preference, Object newValue) {
String key = preference.getKey();
if (key == null) return true;
if (key.equals(KEY_NUM_CHANNELS)) {
try {
int numChannels = Integer.parseInt((String) newValue);
WifiManager wifiManager = (WifiManager) getSystemService(WIFI_SERVICE);
if (!wifiManager.setNumAllowedChannels(numChannels)) {
Toast.makeText(this, R.string.wifi_setting_num_channels_error,
Toast.LENGTH_SHORT).show();
}
} catch (NumberFormatException e) {
Toast.makeText(this, R.string.wifi_setting_num_channels_error,
Toast.LENGTH_SHORT).show();
return false;
}
} else if (key.equals(KEY_SLEEP_POLICY)) {
try {
Settings.System.putInt(getContentResolver(),
Settings.System.WIFI_SLEEP_POLICY, Integer.parseInt(((String) newValue)));
} catch (NumberFormatException e) {
Toast.makeText(this, R.string.wifi_setting_sleep_policy_error,
Toast.LENGTH_SHORT).show();
return false;
}
} else {
String value = (String) newValue;
if (!isIpAddress(value)) {
Toast.makeText(this, R.string.wifi_ip_settings_invalid_ip, Toast.LENGTH_LONG).show();
return false;
}
preference.setSummary(value);
}
return true;
}
private boolean isIpAddress(String value) {
int start = 0;
int end = value.indexOf('.');
int numBlocks = 0;
while (start < value.length()) {
if (end == -1) {
end = value.length();
}
try {
int block = Integer.parseInt(value.substring(start, end));
if ((block > 255) || (block < 0)) {
return false;
}
} catch (NumberFormatException e) {
return false;
}
numBlocks++;
start = end + 1;
end = value.indexOf('.', start);
}
return numBlocks == 4;
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
menu.add(0, MENU_ITEM_SAVE, 0, R.string.wifi_ip_settings_menu_save)
.setIcon(android.R.drawable.ic_menu_save);
menu.add(0, MENU_ITEM_CANCEL, 0, R.string.wifi_ip_settings_menu_cancel)
.setIcon(android.R.drawable.ic_menu_close_clear_cancel);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case MENU_ITEM_SAVE:
updateSettingsProvider();
finish();
return true;
case MENU_ITEM_CANCEL:
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
private void updateUi() {
ContentResolver contentResolver = getContentResolver();
mUseStaticIpCheckBox.setChecked(System.getInt(contentResolver,
System.WIFI_USE_STATIC_IP, 0) != 0);
for (int i = 0; i < mSettingNames.length; i++) {
EditTextPreference preference = (EditTextPreference) findPreference(mPreferenceKeys[i]);
String settingValue = System.getString(contentResolver, mSettingNames[i]);
preference.setText(settingValue);
preference.setSummary(settingValue);
}
}
private void updateSettingsProvider() {
ContentResolver contentResolver = getContentResolver();
System.putInt(contentResolver, System.WIFI_USE_STATIC_IP,
mUseStaticIpCheckBox.isChecked() ? 1 : 0);
for (int i = 0; i < mSettingNames.length; i++) {
EditTextPreference preference = (EditTextPreference) findPreference(mPreferenceKeys[i]);
System.putString(contentResolver, mSettingNames[i], preference.getText());
}
}
private void refreshMacAddress() {
WifiManager wifiManager = (WifiManager) getSystemService(WIFI_SERVICE);
WifiInfo wifiInfo = wifiManager.getConnectionInfo();
Preference wifiMacAddressPref = findPreference(KEY_MAC_ADDRESS);
String macAddress = wifiInfo == null ? null : wifiInfo.getMacAddress();
wifiMacAddressPref.setSummary(!TextUtils.isEmpty(macAddress) ? macAddress
: getString(R.string.status_unavailable));
}
}

View File

@@ -0,0 +1,188 @@
/*
* Copyright (C) 2007 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.wifi;
import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED;
import static android.net.wifi.WifiManager.WIFI_STATE_DISABLING;
import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED;
import static android.net.wifi.WifiManager.WIFI_STATE_ENABLING;
import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN;
import com.android.settings.R;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.NetworkInfo;
import android.net.wifi.WifiManager;
import android.preference.Preference;
import android.preference.CheckBoxPreference;
import android.text.TextUtils;
import android.util.Config;
import android.util.Log;
public class WifiEnabler implements Preference.OnPreferenceChangeListener {
private static final boolean LOCAL_LOGD = Config.LOGD || WifiLayer.LOGV;
private static final String TAG = "SettingsWifiEnabler";
private final Context mContext;
private final WifiManager mWifiManager;
private final CheckBoxPreference mWifiCheckBoxPref;
private final CharSequence mOriginalSummary;
private final IntentFilter mWifiStateFilter;
private final BroadcastReceiver mWifiStateReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
handleWifiStateChanged(
intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, WIFI_STATE_UNKNOWN),
intent.getIntExtra(WifiManager.EXTRA_PREVIOUS_WIFI_STATE,
WIFI_STATE_UNKNOWN));
} else if (intent.getAction().equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
handleNetworkStateChanged(
(NetworkInfo) intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO));
}
}
};
public WifiEnabler(Context context, WifiManager wifiManager,
CheckBoxPreference wifiCheckBoxPreference) {
mContext = context;
mWifiCheckBoxPref = wifiCheckBoxPreference;
mWifiManager = wifiManager;
mOriginalSummary = wifiCheckBoxPreference.getSummary();
wifiCheckBoxPreference.setPersistent(false);
mWifiStateFilter = new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION);
mWifiStateFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
}
public void resume() {
int state = mWifiManager.getWifiState();
// This is the widget enabled state, not the preference toggled state
mWifiCheckBoxPref.setEnabled(state == WIFI_STATE_ENABLED || state == WIFI_STATE_DISABLED
|| state == WIFI_STATE_UNKNOWN);
mContext.registerReceiver(mWifiStateReceiver, mWifiStateFilter);
mWifiCheckBoxPref.setOnPreferenceChangeListener(this);
}
public void pause() {
mContext.unregisterReceiver(mWifiStateReceiver);
mWifiCheckBoxPref.setOnPreferenceChangeListener(null);
}
public boolean onPreferenceChange(Preference preference, Object value) {
// Turn on/off Wi-Fi
setWifiEnabled((Boolean) value);
// Don't update UI to opposite state until we're sure
return false;
}
private void setWifiEnabled(final boolean enable) {
// Disable button
mWifiCheckBoxPref.setEnabled(false);
if (!mWifiManager.setWifiEnabled(enable)) {
mWifiCheckBoxPref.setSummary(enable ? R.string.error_starting : R.string.error_stopping);
}
}
private void handleWifiStateChanged(int wifiState, int previousWifiState) {
if (LOCAL_LOGD) {
Log.d(TAG, "Received wifi state changed from "
+ getHumanReadableWifiState(previousWifiState) + " to "
+ getHumanReadableWifiState(wifiState));
}
if (wifiState == WIFI_STATE_DISABLED || wifiState == WIFI_STATE_ENABLED) {
mWifiCheckBoxPref.setChecked(wifiState == WIFI_STATE_ENABLED);
mWifiCheckBoxPref
.setSummary(wifiState == WIFI_STATE_DISABLED ? mOriginalSummary : null);
mWifiCheckBoxPref.setEnabled(isEnabledByDependency());
} else if (wifiState == WIFI_STATE_DISABLING || wifiState == WIFI_STATE_ENABLING) {
mWifiCheckBoxPref.setSummary(wifiState == WIFI_STATE_ENABLING ? R.string.wifi_starting
: R.string.wifi_stopping);
} else if (wifiState == WIFI_STATE_UNKNOWN) {
int message = R.string.wifi_error;
if (previousWifiState == WIFI_STATE_ENABLING) message = R.string.error_starting;
else if (previousWifiState == WIFI_STATE_DISABLING) message = R.string.error_stopping;
mWifiCheckBoxPref.setChecked(false);
mWifiCheckBoxPref.setSummary(message);
mWifiCheckBoxPref.setEnabled(true);
}
}
private void handleNetworkStateChanged(NetworkInfo networkInfo) {
if (LOCAL_LOGD) {
Log.d(TAG, "Received network state changed to " + networkInfo);
}
if (mWifiManager.isWifiEnabled()) {
String summary = WifiStatus.getStatus(mContext,
mWifiManager.getConnectionInfo().getSSID(), networkInfo.getDetailedState());
mWifiCheckBoxPref.setSummary(summary);
}
}
private boolean isEnabledByDependency() {
Preference dep = getDependencyPreference();
if (dep == null) {
return true;
}
return !dep.shouldDisableDependents();
}
private Preference getDependencyPreference() {
String depKey = mWifiCheckBoxPref.getDependency();
if (TextUtils.isEmpty(depKey)) {
return null;
}
return mWifiCheckBoxPref.getPreferenceManager().findPreference(depKey);
}
private static String getHumanReadableWifiState(int wifiState) {
switch (wifiState) {
case WIFI_STATE_DISABLED:
return "Disabled";
case WIFI_STATE_DISABLING:
return "Disabling";
case WIFI_STATE_ENABLED:
return "Enabled";
case WIFI_STATE_ENABLING:
return "Enabling";
case WIFI_STATE_UNKNOWN:
return "Unknown";
default:
return "Some other state!";
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,438 @@
/*
* Copyright (C) 2007 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.wifi;
import com.android.settings.ProgressCategory;
import com.android.settings.R;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceScreen;
import android.preference.CheckBoxPreference;
import android.provider.Settings;
import android.util.Log;
import android.view.ContextMenu;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ContextMenu.ContextMenuInfo;
import android.widget.AdapterView;
import android.widget.Toast;
import android.widget.AdapterView.AdapterContextMenuInfo;
import java.util.Set;
import java.util.WeakHashMap;
/**
* Settings screen for WiFi. This will be launched from the main system settings.
*/
public class WifiSettings extends PreferenceActivity implements WifiLayer.Callback,
DialogInterface.OnDismissListener {
private static final String TAG = "WifiSettings";
//============================
// Preference/activity member variables
//============================
private static final String INSTANCE_KEY_DIALOG_BUNDLE =
"com.android.settings.wifi.WifiSettings:dialogBundle";
/*
* We don't use Activity's dialog management because AlertDialog isn't fully
* able to change many of its features after it's been created, and the
* dialog management only creates once.
*/
private Dialog mDialog;
private static final String KEY_ADD_OTHER_NETWORK = "add_other_network";
private static final int CONTEXT_MENU_ID_CONNECT = Menu.FIRST;
private static final int CONTEXT_MENU_ID_FORGET = Menu.FIRST + 1;
private static final int CONTEXT_MENU_ID_CHANGE_PASSWORD = Menu.FIRST + 2;
private static final int MENU_ID_SCAN = Menu.FIRST;
private static final int MENU_ID_ADVANCED = Menu.FIRST + 1;
private static final String KEY_WIFI_ENABLED = "wifi_enabled";
private static final String KEY_OPEN_NETWORK_NOTIFICATIONS_ENABLED =
"open_network_notifications_enabled";
private static final String KEY_ACCESS_POINTS = "access_points";
private ProgressCategory mApCategory;
private CheckBoxPreference mWifiEnabled;
private WifiEnabler mWifiEnabler;
private CheckBoxPreference mOpenNetworkNotificationsEnabled;
private Preference mAddOtherNetwork;
private WeakHashMap<AccessPointState, AccessPointPreference> mAps;
//============================
// Wifi member variables
//============================
private WifiLayer mWifiLayer;
//============================
// Activity lifecycle
//============================
public WifiSettings() {
mAps = new WeakHashMap<AccessPointState, AccessPointPreference>();
mWifiLayer = new WifiLayer(this, this);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
onCreatePreferences();
mWifiLayer.onCreate();
onCreatedWifi();
mWifiLayer.onCreatedCallback();
}
/**
* Shouldn't have any dependency on the wifi layer.
*/
private void onCreatePreferences() {
addPreferencesFromResource(R.xml.wifi_settings);
final PreferenceScreen preferenceScreen = getPreferenceScreen();
mApCategory = (ProgressCategory) preferenceScreen.findPreference(KEY_ACCESS_POINTS);
// We don't want the ordering to be the order preferences are added,
// instead we want*:
// 1) preferred, visible APs
// 2) visible APs
// 3) preferred, APs out of range
// * this ordering logic is in AccessPointPreference's compareTo
mApCategory.setOrderingAsAdded(false);
mWifiEnabled = (CheckBoxPreference) preferenceScreen.findPreference(KEY_WIFI_ENABLED);
mWifiEnabler = new WifiEnabler(this, (WifiManager) getSystemService(WIFI_SERVICE),
mWifiEnabled);
mOpenNetworkNotificationsEnabled = (CheckBoxPreference) preferenceScreen
.findPreference(KEY_OPEN_NETWORK_NOTIFICATIONS_ENABLED);
mOpenNetworkNotificationsEnabled.setChecked(Settings.Secure.getInt(getContentResolver(),
Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 0) == 1);
mAddOtherNetwork = preferenceScreen.findPreference(KEY_ADD_OTHER_NETWORK);
registerForContextMenu(getListView());
}
private void onCreatedWifi() {
}
@Override
protected void onResume() {
super.onResume();
mWifiLayer.onResume();
mWifiEnabler.resume();
}
@Override
protected void onPause() {
super.onPause();
mWifiLayer.onPause();
mWifiEnabler.pause();
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mDialog != null) {
mDialog.dismiss();
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
menu.add(0, MENU_ID_SCAN, 0, R.string.scan_wifi)
.setIcon(R.drawable.ic_menu_scan_network);
menu.add(0, MENU_ID_ADVANCED, 0, R.string.wifi_menu_advanced)
.setIcon(android.R.drawable.ic_menu_manage);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
super.onOptionsItemSelected(item);
switch (item.getItemId()) {
case MENU_ID_SCAN:
mWifiLayer.attemptScan();
return true;
case MENU_ID_ADVANCED:
Intent intent = new Intent(this, AdvancedSettings.class);
startActivity(intent);
return true;
default:
return false;
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (mDialog != null) {
Bundle dialogBundle = mDialog.onSaveInstanceState();
outState.putBundle(INSTANCE_KEY_DIALOG_BUNDLE, dialogBundle);
}
}
@Override
protected void onRestoreInstanceState(Bundle state) {
super.onRestoreInstanceState(state);
Bundle dialogBundle = state.getBundle(INSTANCE_KEY_DIALOG_BUNDLE);
if (dialogBundle != null) {
mDialog = new AccessPointDialog(this, mWifiLayer);
mDialog.onRestoreInstanceState(dialogBundle);
showDialog(mDialog);
}
}
/**
* {@inheritDoc}
*/
public void onDismiss(DialogInterface dialog) {
if (dialog == mDialog) {
mDialog = null;
}
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
AccessPointState state = getStateFromMenuInfo(menuInfo);
if (state == null) {
return;
}
menu.setHeaderTitle(state.getHumanReadableSsid());
if (state.isConnectable()) {
menu.add(0, CONTEXT_MENU_ID_CONNECT, 0, R.string.wifi_context_menu_connect);
}
if (state.isForgetable()) {
menu.add(0, CONTEXT_MENU_ID_FORGET, 1, R.string.wifi_context_menu_forget);
if (state.hasPassword()) {
menu.add(0, CONTEXT_MENU_ID_CHANGE_PASSWORD, 2,
R.string.wifi_context_menu_change_password);
}
}
}
@Override
public boolean onContextItemSelected(MenuItem item) {
AccessPointState state = getStateFromMenuInfo(item.getMenuInfo());
if (state == null) {
return false;
}
switch (item.getItemId()) {
case CONTEXT_MENU_ID_CONNECT:
connectToNetwork(state);
return true;
case CONTEXT_MENU_ID_FORGET:
mWifiLayer.forgetNetwork(state);
return true;
case CONTEXT_MENU_ID_CHANGE_PASSWORD:
showAccessPointDialog(state, AccessPointDialog.MODE_CONFIGURE);
return true;
default:
return false;
}
}
/**
* Decides what needs to happen to connect to a particular access point. If
* it is secured and doesn't already have a password, it will bring up a
* password box. Otherwise it will just connect.
*/
private void connectToNetwork(AccessPointState state) {
if (state.hasSecurity() && !state.hasPassword()) {
showAccessPointDialog(state, AccessPointDialog.MODE_INFO);
} else {
mWifiLayer.connectToNetwork(state);
}
}
private AccessPointState getStateFromMenuInfo(ContextMenuInfo menuInfo) {
if ((menuInfo == null) || !(menuInfo instanceof AdapterContextMenuInfo)) {
return null;
}
AdapterContextMenuInfo adapterMenuInfo = (AdapterContextMenuInfo) menuInfo;
Preference pref = (Preference) getPreferenceScreen().getRootAdapter().getItem(
adapterMenuInfo.position);
if (pref == null || !(pref instanceof AccessPointPreference)) {
return null;
}
return ((AccessPointPreference) pref).getAccessPointState();
}
//============================
// Preference callbacks
//============================
@Override
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
super.onPreferenceTreeClick(preferenceScreen, preference);
if (preference == mAddOtherNetwork) {
showAddOtherNetworkDialog();
} else if (preference == mOpenNetworkNotificationsEnabled) {
Settings.Secure.putInt(getContentResolver(),
Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON,
mOpenNetworkNotificationsEnabled.isChecked() ? 1 : 0);
} else if (preference instanceof AccessPointPreference) {
AccessPointState state = ((AccessPointPreference) preference).getAccessPointState();
showAccessPointDialog(state, AccessPointDialog.MODE_INFO);
}
return false;
}
//============================
// Wifi-related
//============================
public WifiLayer getWifiLayer() {
return mWifiLayer;
}
private void showAddOtherNetworkDialog() {
AccessPointDialog dialog = new AccessPointDialog(this, mWifiLayer);
dialog.setState(new AccessPointState(this));
dialog.setMode(AccessPointDialog.MODE_CONFIGURE);
dialog.setTitle(R.string.wifi_add_other_network);
dialog.setAutoSecurityAllowed(false);
showDialog(dialog);
}
public void showAccessPointDialog(AccessPointState state, int mode) {
AccessPointDialog dialog = new AccessPointDialog(this, mWifiLayer);
dialog.setMode(mode);
dialog.setState(state);
showDialog(dialog);
}
private void showDialog(Dialog dialog) {
// Have only one dialog open at a time
if (mDialog != null) {
mDialog.dismiss();
}
mDialog = dialog;
dialog.setOnDismissListener(this);
if (dialog != null) {
dialog.show();
}
}
//============================
// Wifi callbacks
//============================
public void onError(int messageResId) {
Toast.makeText(this, messageResId, Toast.LENGTH_LONG).show();
}
public void onScanningStatusChanged(boolean started) {
mApCategory.setProgress(started);
}
public void onAccessPointSetChanged(AccessPointState ap, boolean added) {
AccessPointPreference pref = mAps.get(ap);
if (WifiLayer.LOGV) {
Log.v(TAG, "onAccessPointSetChanged with " + ap + " and "
+ (added ? "added" : "removed") + ", found pref " + pref);
}
if (added) {
if (pref == null) {
pref = new AccessPointPreference(this, ap);
mAps.put(ap, pref);
} else {
pref.setEnabled(true);
}
mApCategory.addPreference(pref);
} else {
mAps.remove(ap);
if (pref != null) {
mApCategory.removePreference(pref);
}
}
}
public void onAccessPointsStateChanged(boolean enabled) {
if (enabled) {
mApCategory.setEnabled(true);
} else {
mApCategory.removeAll();
mAps.clear();
}
mAddOtherNetwork.setEnabled(enabled);
}
public void onRetryPassword(AccessPointState ap) {
if ((mDialog != null) && mDialog.isShowing()) {
// If we're already showing a dialog, ignore this request
return;
}
showAccessPointDialog(ap, AccessPointDialog.MODE_RETRY_PASSWORD);
}
}

View File

@@ -0,0 +1,152 @@
/*
* Copyright (C) 2007 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.wifi;
import com.android.settings.R;
import android.content.Context;
import android.net.NetworkInfo;
import android.net.wifi.WifiInfo;
import android.text.TextUtils;
public class WifiStatus {
// e.g., "Connecting"
public static String sScanning;
public static String sConnecting;
public static String sAuthenticating;
public static String sObtainingIp;
public static String sConnected;
public static String sDisconnecting;
public static String sDisconnected;
public static String sFailed;
// e.g., "Connecting to %1$s"
public static String sScanningFragment;
public static String sConnectingFragment;
public static String sAuthenticatingFragment;
public static String sObtainingIpFragment;
public static String sConnectedFragment;
public static String sDisconnectingFragment;
public static String sDisconnectedFragment;
public static String sFailedFragment;
private static void fillStrings(Context context) {
sScanning = context.getString(R.string.status_scanning);
sConnecting = context.getString(R.string.status_connecting);
sAuthenticating = context.getString(R.string.status_authenticating);
sObtainingIp = context.getString(R.string.status_obtaining_ip);
sConnected = context.getString(R.string.status_connected);
sDisconnecting = context.getString(R.string.status_disconnecting);
sDisconnected = context.getString(R.string.status_disconnected);
sFailed = context.getString(R.string.status_failed);
sScanningFragment = context.getString(R.string.fragment_status_scanning);
sConnectingFragment = context.getString(R.string.fragment_status_connecting);
sAuthenticatingFragment = context.getString(R.string.fragment_status_authenticating);
sObtainingIpFragment = context.getString(R.string.fragment_status_obtaining_ip);
sConnectedFragment = context.getString(R.string.fragment_status_connected);
sDisconnectingFragment = context.getString(R.string.fragment_status_disconnecting);
sDisconnectedFragment = context.getString(R.string.fragment_status_disconnected);
sFailedFragment = context.getString(R.string.fragment_status_failed);
}
public static String getStatus(Context context, String ssid,
NetworkInfo.DetailedState detailedState) {
if (!TextUtils.isEmpty(ssid) && isLiveConnection(detailedState)) {
return getPrintableFragment(context, detailedState, ssid);
} else {
return getPrintable(context, detailedState);
}
}
public static boolean isLiveConnection(NetworkInfo.DetailedState detailedState) {
return detailedState != NetworkInfo.DetailedState.DISCONNECTED
&& detailedState != NetworkInfo.DetailedState.FAILED
&& detailedState != NetworkInfo.DetailedState.IDLE
&& detailedState != NetworkInfo.DetailedState.SCANNING;
}
public static String getPrintable(Context context,
NetworkInfo.DetailedState detailedState) {
if (sScanning == null) {
fillStrings(context);
}
switch (detailedState) {
case AUTHENTICATING:
return sAuthenticating;
case CONNECTED:
return sConnected;
case CONNECTING:
return sConnecting;
case DISCONNECTED:
return sDisconnected;
case DISCONNECTING:
return sDisconnecting;
case FAILED:
return sFailed;
case OBTAINING_IPADDR:
return sObtainingIp;
case SCANNING:
return sScanning;
default:
return null;
}
}
public static String getPrintableFragment(Context context,
NetworkInfo.DetailedState detailedState, String apName) {
if (sScanningFragment == null) {
fillStrings(context);
}
String fragment = null;
switch (detailedState) {
case AUTHENTICATING:
fragment = sAuthenticatingFragment;
break;
case CONNECTED:
fragment = sConnectedFragment;
break;
case CONNECTING:
fragment = sConnectingFragment;
break;
case DISCONNECTED:
fragment = sDisconnectedFragment;
break;
case DISCONNECTING:
fragment = sDisconnectingFragment;
break;
case FAILED:
fragment = sFailedFragment;
break;
case OBTAINING_IPADDR:
fragment = sObtainingIpFragment;
break;
case SCANNING:
fragment = sScanningFragment;
break;
}
return String.format(fragment, apName);
}
}