diff --git a/AndroidManifest.xml b/AndroidManifest.xml index c986d3b2d11..80c97667372 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -141,6 +141,13 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/values/arrays.xml b/res/values/arrays.xml index 0aa604d6bc7..12eeb6895c5 100644 --- a/res/values/arrays.xml +++ b/res/values/arrays.xml @@ -227,6 +227,15 @@ 802.1x EAP + + + Open + + WEP + + WPA/WPA2 PSK + + diff --git a/res/values/strings.xml b/res/values/strings.xml index 2c42797f4f7..ceb33f08c02 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -846,6 +846,28 @@ Gateway Netmask + + Wi-fi Access Point for tethering is currently enabled. Turning Wi-fi on will disable Access Point operation. + + + + Wi-Fi + + Wi-Fi AP + + Set as Access Point + + Wi-Fi AP settings + + Set up & manage Access Point + + SSID and Security + + Configure AP + + Channel + + Enabling Wi-fi Access Point for tethering. This will reset any existing Wi-fi connection and stop Wi-fi station mode operation. @@ -1353,6 +1375,7 @@ Tethering settings + USB USB tethering USB connected, select to tether diff --git a/res/xml/tether_prefs.xml b/res/xml/tether_prefs.xml index b903eafa57b..d15003c284a 100644 --- a/res/xml/tether_prefs.xml +++ b/res/xml/tether_prefs.xml @@ -17,14 +17,43 @@ - - + + + + + + + + + + + + + + + + diff --git a/res/xml/wifi_ap_settings.xml b/res/xml/wifi_ap_settings.xml new file mode 100644 index 00000000000..2a403d8c2c4 --- /dev/null +++ b/res/xml/wifi_ap_settings.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + diff --git a/src/com/android/settings/TetherSettings.java b/src/com/android/settings/TetherSettings.java index 3e41bb8e0f2..9430c192b8b 100644 --- a/src/com/android/settings/TetherSettings.java +++ b/src/com/android/settings/TetherSettings.java @@ -16,6 +16,8 @@ package com.android.settings; +import com.android.settings.wifi.WifiApEnabler; + import android.os.Bundle; import android.os.SystemProperties; import android.content.BroadcastReceiver; @@ -32,14 +34,21 @@ import android.provider.Settings; import android.util.Log; import java.util.ArrayList; + /* * Displays preferences for Tethering. */ public class TetherSettings extends PreferenceActivity { private static final String USB_TETHER_SETTINGS = "usb_tether_settings"; + private static final String ENABLE_WIFI_AP = "enable_wifi_ap"; + private static final String WIFI_AP_SETTINGS = "wifi_ap_settings"; private PreferenceScreen mUsbTether; + private CheckBoxPreference mEnableWifiAp; + private PreferenceScreen mWifiApSettings; + private WifiApEnabler mWifiApEnabler; + private BroadcastReceiver mTetherChangeReceiver; private String[] mUsbRegexs; @@ -55,6 +64,8 @@ public class TetherSettings extends PreferenceActivity { addPreferencesFromResource(R.xml.tether_prefs); mUsbTether = (PreferenceScreen) findPreference(USB_TETHER_SETTINGS); + mEnableWifiAp = (CheckBoxPreference) findPreference(ENABLE_WIFI_AP); + mWifiApSettings = (PreferenceScreen) findPreference(WIFI_AP_SETTINGS); ConnectivityManager cm = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE); @@ -62,7 +73,13 @@ public class TetherSettings extends PreferenceActivity { if (mUsbRegexs.length == 0) { getPreferenceScreen().removePreference(mUsbTether); } + mWifiRegexs = cm.getTetherableWifiRegexs(); + if (mWifiRegexs.length == 0) { + getPreferenceScreen().removePreference(mEnableWifiAp); + getPreferenceScreen().removePreference(mWifiApSettings); + } + mWifiApEnabler = new WifiApEnabler(this, mEnableWifiAp); } @@ -91,6 +108,7 @@ public class TetherSettings extends PreferenceActivity { Intent intent = registerReceiver(mTetherChangeReceiver, filter); if (intent != null) mTetherChangeReceiver.onReceive(this, intent); + mWifiApEnabler.resume(); } @Override @@ -98,6 +116,7 @@ public class TetherSettings extends PreferenceActivity { super.onPause(); unregisterReceiver(mTetherChangeReceiver); mTetherChangeReceiver = null; + mWifiApEnabler.pause(); } private void updateState(ArrayList available, ArrayList tethered, diff --git a/src/com/android/settings/wifi/AccessPoint.java b/src/com/android/settings/wifi/AccessPoint.java index 9e40bd0cb56..3ccab21ac8f 100644 --- a/src/com/android/settings/wifi/AccessPoint.java +++ b/src/com/android/settings/wifi/AccessPoint.java @@ -49,7 +49,7 @@ class AccessPoint extends Preference { private DetailedState mState; private ImageView mSignal; - private static int getSecurity(WifiConfiguration config) { + static int getSecurity(WifiConfiguration config) { if (config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) { return SECURITY_PSK; } diff --git a/src/com/android/settings/wifi/WifiApDialog.java b/src/com/android/settings/wifi/WifiApDialog.java new file mode 100644 index 00000000000..79372d0c6eb --- /dev/null +++ b/src/com/android/settings/wifi/WifiApDialog.java @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2010 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.WifiConfiguration; +import android.net.wifi.WifiConfiguration.AuthAlgorithm; +import android.net.wifi.WifiConfiguration.KeyMgmt; +import android.os.Bundle; +import android.text.Editable; +import android.text.InputType; +import android.text.TextWatcher; +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.TextView; + +/** + * Dialog to configure the SSID and security settings + * for Access Point operation + */ +class WifiApDialog extends AlertDialog implements View.OnClickListener, + TextWatcher, AdapterView.OnItemSelectedListener { + + static final int BUTTON_SUBMIT = DialogInterface.BUTTON_POSITIVE; + + private final DialogInterface.OnClickListener mListener; + + private View mView; + private TextView mSsid; + private int mSecurityType = AccessPoint.SECURITY_NONE; + private EditText mPassword; + + WifiConfiguration mWifiConfig; + + public WifiApDialog(Context context, DialogInterface.OnClickListener listener, + WifiConfiguration wifiConfig) { + super(context); + mListener = listener; + mWifiConfig = wifiConfig; + if (wifiConfig != null) + mSecurityType = AccessPoint.getSecurity(wifiConfig); + } + + public WifiConfiguration getConfig() { + + WifiConfiguration config = new WifiConfiguration(); + + config.SSID = mSsid.getText().toString(); + + switch (mSecurityType) { + case AccessPoint.SECURITY_NONE: + config.allowedKeyManagement.set(KeyMgmt.NONE); + return config; + + case AccessPoint.SECURITY_WEP: + config.allowedKeyManagement.set(KeyMgmt.NONE); + config.allowedAuthAlgorithms.set(AuthAlgorithm.SHARED); + if (mPassword.length() != 0) { + int length = mPassword.length(); + String password = mPassword.getText().toString(); + // WEP-40, WEP-104, and 256-bit WEP (WEP-232?) + if ((length == 10 || length == 26 || length == 58) && + password.matches("[0-9A-Fa-f]*")) { + config.wepKeys[0] = password; + } else { + config.wepKeys[0] = '"' + password + '"'; + } + } + return config; + + case AccessPoint.SECURITY_PSK: + config.allowedKeyManagement.set(KeyMgmt.WPA_PSK); + config.allowedAuthAlgorithms.set(AuthAlgorithm.OPEN); + if (mPassword.length() != 0) { + String password = mPassword.getText().toString(); + if (password.matches("[0-9A-Fa-f]{64}")) { + config.preSharedKey = password; + } else { + config.preSharedKey = '"' + password + '"'; + } + } + return config; + } + return null; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + + Spinner mSecurity = ((Spinner) mView.findViewById(R.id.security)); + mView = getLayoutInflater().inflate(R.layout.wifi_ap_dialog, null); + + setView(mView); + setInverseBackgroundForced(true); + + Context context = getContext(); + + setTitle(R.string.wifi_ap_configure_network); + mView.findViewById(R.id.type).setVisibility(View.VISIBLE); + mSsid = (TextView) mView.findViewById(R.id.ssid); + mPassword = (EditText) mView.findViewById(R.id.password); + + setButton(BUTTON_SUBMIT, context.getString(R.string.wifi_save), mListener); + setButton(DialogInterface.BUTTON_NEGATIVE, + context.getString(R.string.wifi_cancel), mListener); + + if (mWifiConfig != null) { + mSsid.setText(mWifiConfig.SSID); + switch (mSecurityType) { + case AccessPoint.SECURITY_WEP: + mPassword.setText(mWifiConfig.wepKeys[0]); + break; + case AccessPoint.SECURITY_PSK: + mPassword.setText(mWifiConfig.preSharedKey); + break; + } + mSecurity.setSelection(mSecurityType); + } + + mSsid.addTextChangedListener(this); + mPassword.addTextChangedListener(this); + ((CheckBox) mView.findViewById(R.id.show_password)).setOnClickListener(this); + mSecurity.setOnItemSelectedListener(this); + + super.onCreate(savedInstanceState); + + showSecurityFields(); + validate(); + } + + private void validate() { + if ((mSsid != null && mSsid.length() == 0) || + (mSecurityType == AccessPoint.SECURITY_WEP && mPassword.length() == 0) || + (mSecurityType == AccessPoint.SECURITY_PSK && mPassword.length() < 8)) { + getButton(BUTTON_SUBMIT).setEnabled(false); + } else { + getButton(BUTTON_SUBMIT).setEnabled(true); + } + } + + public void onClick(View view) { + mPassword.setInputType( + InputType.TYPE_CLASS_TEXT | (((CheckBox) view).isChecked() ? + InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD : + InputType.TYPE_TEXT_VARIATION_PASSWORD)); + } + + public void onTextChanged(CharSequence s, int start, int before, int count) { + } + + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + + public void afterTextChanged(Editable editable) { + validate(); + } + + public void onItemSelected(AdapterView parent, View view, int position, long id) { + mSecurityType = position; + showSecurityFields(); + validate(); + } + + public void onNothingSelected(AdapterView parent) { + } + + private void showSecurityFields() { + if (mSecurityType == AccessPoint.SECURITY_NONE) { + mView.findViewById(R.id.fields).setVisibility(View.GONE); + return; + } + mView.findViewById(R.id.fields).setVisibility(View.VISIBLE); + } +} diff --git a/src/com/android/settings/wifi/WifiApEnabler.java b/src/com/android/settings/wifi/WifiApEnabler.java new file mode 100644 index 00000000000..7e051737eb7 --- /dev/null +++ b/src/com/android/settings/wifi/WifiApEnabler.java @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2010 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 com.android.settings.WirelessSettings; + +import android.app.AlertDialog; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.IntentFilter; +import android.net.NetworkInfo; +import android.net.wifi.SupplicantState; +import android.net.wifi.WifiInfo; +import android.net.wifi.WifiManager; +import android.preference.Preference; +import android.preference.CheckBoxPreference; +import android.provider.Settings; +import android.text.TextUtils; +import android.util.Log; +import android.widget.Toast; + +public class WifiApEnabler implements Preference.OnPreferenceChangeListener, + DialogInterface.OnClickListener { + private final Context mContext; + private final CheckBoxPreference mCheckBox; + private final CharSequence mOriginalSummary; + + private final WifiManager mWifiManager; + private final IntentFilter mIntentFilter; + private AlertDialog mAlertDialog = null; + + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (WifiManager.WIFI_AP_STATE_CHANGED_ACTION.equals(action)) { + handleWifiApStateChanged(intent.getIntExtra( + WifiManager.EXTRA_WIFI_AP_STATE, WifiManager.WIFI_AP_STATE_FAILED)); + } + } + }; + + public WifiApEnabler(Context context, CheckBoxPreference checkBox) { + mContext = context; + mCheckBox = checkBox; + mOriginalSummary = checkBox.getSummary(); + checkBox.setPersistent(false); + + mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); + mIntentFilter = new IntentFilter(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); + } + + public void resume() { + mContext.registerReceiver(mReceiver, mIntentFilter); + mCheckBox.setOnPreferenceChangeListener(this); + } + + public void pause() { + mContext.unregisterReceiver(mReceiver); + mCheckBox.setOnPreferenceChangeListener(null); + if (mAlertDialog != null) { + mAlertDialog.dismiss(); + mAlertDialog = null; + } + } + + public boolean onPreferenceChange(Preference preference, Object value) { + boolean enable = (Boolean) value; + + if (enable && mWifiManager.isWifiEnabled()) { + AlertDialog.Builder builder = new AlertDialog.Builder(mContext); + builder.setMessage(R.string.wifi_ap_tether_message) + .setCancelable(false) + .setPositiveButton(android.R.string.ok, this) + .setNegativeButton(android.R.string.cancel, this); + + mAlertDialog = builder.create(); + mAlertDialog.show(); + } else { + setUpAccessPoint(enable); + } + + return false; + } + + public void onClick(DialogInterface dialog, int id) { + if(id == DialogInterface.BUTTON_POSITIVE ) { + setUpAccessPoint(true); + } else if (id == DialogInterface.BUTTON_NEGATIVE) { + dialog.dismiss(); + mAlertDialog = null; + } + } + + private void setUpAccessPoint(boolean enable) { + if (mWifiManager.setWifiApEnabled(null, enable)) { + mCheckBox.setEnabled(false); + } else { + mCheckBox.setSummary(R.string.wifi_error); + } + } + + private void handleWifiApStateChanged(int state) { + switch (state) { + case WifiManager.WIFI_AP_STATE_ENABLING: + mCheckBox.setSummary(R.string.wifi_starting); + mCheckBox.setEnabled(false); + break; + case WifiManager.WIFI_AP_STATE_ENABLED: + mCheckBox.setChecked(true); + mCheckBox.setSummary(null); + mCheckBox.setEnabled(true); + break; + case WifiManager.WIFI_AP_STATE_DISABLING: + mCheckBox.setSummary(R.string.wifi_stopping); + mCheckBox.setEnabled(false); + break; + case WifiManager.WIFI_AP_STATE_DISABLED: + mCheckBox.setChecked(false); + mCheckBox.setSummary(mOriginalSummary); + mCheckBox.setEnabled(true); + break; + default: + mCheckBox.setChecked(false); + mCheckBox.setSummary(R.string.wifi_error); + mCheckBox.setEnabled(true); + } + } +} diff --git a/src/com/android/settings/wifi/WifiApSettings.java b/src/com/android/settings/wifi/WifiApSettings.java new file mode 100644 index 00000000000..17900fe5f12 --- /dev/null +++ b/src/com/android/settings/wifi/WifiApSettings.java @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2010 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.BroadcastReceiver; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.IntentFilter; +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.net.wifi.WifiConfiguration; +import android.net.wifi.WifiManager; +import android.os.Bundle; + +/* + * Displays preferences for Tethering. + */ +public class WifiApSettings extends PreferenceActivity + implements DialogInterface.OnClickListener, Preference.OnPreferenceChangeListener { + + private static final String WIFI_AP_SSID_AND_SECURITY = "wifi_ap_ssid_and_security"; + private static final String WIFI_AP_CHANNEL = "wifi_ap_channel"; + private static final String ENABLE_WIFI_AP = "enable_wifi_ap"; + + private Preference mCreateNetwork; + private ListPreference mChannel; + private CheckBoxPreference mEnableWifiAp; + + private WifiApDialog mDialog; + private WifiManager mWifiManager; + private WifiApEnabler mWifiApEnabler; + private WifiConfiguration mWifiConfig = null; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE); + + addPreferencesFromResource(R.xml.wifi_ap_settings); + + mCreateNetwork = findPreference(WIFI_AP_SSID_AND_SECURITY); + mChannel = (ListPreference) findPreference(WIFI_AP_CHANNEL); + mEnableWifiAp = (CheckBoxPreference) findPreference(ENABLE_WIFI_AP); + + mWifiApEnabler = new WifiApEnabler(this, mEnableWifiAp); + + initChannels(); + } + + public void initChannels() { + mChannel.setOnPreferenceChangeListener(this); + + int numChannels = mWifiManager.getNumAllowedChannels(); + + String[] entries = new String[numChannels]; + String[] entryValues = new String[numChannels]; + + for (int i = 1; i <= numChannels; i++) { + entries[i-1] = "Channel "+i; + entryValues[i-1] = i+""; + } + + mChannel.setEntries(entries); + mChannel.setEntryValues(entryValues); + mChannel.setEnabled(true); + /** + * TODO: randomize initial channel chosen + */ + mChannel.setValue("2"); + } + + @Override + protected void onResume() { + super.onResume(); + mWifiApEnabler.resume(); + } + + @Override + protected void onPause() { + super.onPause(); + mWifiApEnabler.pause(); + } + + @Override + public boolean onPreferenceTreeClick(PreferenceScreen screen, Preference preference) { + if (preference == mCreateNetwork) { + showDialog(); + } + return true; + } + + private void showDialog() { + if (mDialog != null) { + mDialog.dismiss(); + } + mDialog = new WifiApDialog(this, this, mWifiConfig); + mDialog.show(); + } + + public void onClick(DialogInterface dialogInterface, int button) { + /** + * TODO: Needs work + */ + mWifiConfig = mDialog.getConfig(); + + if(mWifiConfig.SSID != null) + mCreateNetwork.setSummary(mWifiConfig.SSID); + + /** + * TODO: set SSID and security + */ + } + + public boolean onPreferenceChange(Preference preference, Object newValue) { + /** + * TODO: Needs work + */ + + String key = preference.getKey(); + if (key == null) return true; + + if (key.equals(WIFI_AP_CHANNEL)) { + int chosenChannel = Integer.parseInt((String) newValue); + if(newValue != null) + mChannel.setSummary(newValue.toString()); + } + return true; + } +}