diff options
Diffstat (limited to 'src/com')
29 files changed, 1104 insertions, 2984 deletions
diff --git a/src/com/android/settings/DevelopmentSettings.java b/src/com/android/settings/DevelopmentSettings.java index 2508454..9692dd5 100644 --- a/src/com/android/settings/DevelopmentSettings.java +++ b/src/com/android/settings/DevelopmentSettings.java @@ -16,13 +16,20 @@ package com.android.settings; +import android.app.ActivityManagerNative; import android.app.AlertDialog; import android.app.Dialog; import android.content.ContentResolver; import android.content.DialogInterface; +import android.content.Intent; import android.os.BatteryManager; import android.os.Build; import android.os.Bundle; +import android.os.IBinder; +import android.os.Parcel; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.StrictMode; import android.os.SystemProperties; import android.preference.CheckBoxPreference; import android.preference.ListPreference; @@ -31,6 +38,8 @@ import android.preference.PreferenceFragment; import android.preference.PreferenceScreen; import android.preference.Preference.OnPreferenceChangeListener; import android.provider.Settings; +import android.text.TextUtils; +import android.view.IWindowManager; /* * Displays preferences for application developers. @@ -45,10 +54,33 @@ public class DevelopmentSettings extends PreferenceFragment private static final String HDCP_CHECKING_KEY = "hdcp_checking"; private static final String HDCP_CHECKING_PROPERTY = "persist.sys.hdcp_checking"; + private static final String STRICT_MODE_KEY = "strict_mode"; + private static final String POINTER_LOCATION_KEY = "pointer_location"; + private static final String SHOW_SCREEN_UPDATES_KEY = "show_screen_updates"; + private static final String SHOW_CPU_USAGE_KEY = "show_cpu_usage"; + private static final String WINDOW_ANIMATION_SCALE_KEY = "window_animation_scale"; + private static final String TRANSITION_ANIMATION_SCALE_KEY = "transition_animation_scale"; + + private static final String IMMEDIATELY_DESTROY_ACTIVITIES_KEY + = "immediately_destroy_activities"; + private static final String APP_PROCESS_LIMIT_KEY = "app_process_limit"; + + private IWindowManager mWindowManager; + private CheckBoxPreference mEnableAdb; private CheckBoxPreference mKeepScreenOn; private CheckBoxPreference mAllowMockLocation; + private CheckBoxPreference mStrictMode; + private CheckBoxPreference mPointerLocation; + private CheckBoxPreference mShowScreenUpdates; + private CheckBoxPreference mShowCpuUsage; + private ListPreference mWindowAnimationScale; + private ListPreference mTransitionAnimationScale; + + private CheckBoxPreference mImmediatelyDestroyActivities; + private ListPreference mAppProcessLimit; + // To track whether Yes was clicked in the adb warning dialog private boolean mOkClicked; @@ -58,12 +90,25 @@ public class DevelopmentSettings extends PreferenceFragment public void onCreate(Bundle icicle) { super.onCreate(icicle); + mWindowManager = IWindowManager.Stub.asInterface(ServiceManager.getService("window")); + addPreferencesFromResource(R.xml.development_prefs); mEnableAdb = (CheckBoxPreference) findPreference(ENABLE_ADB); mKeepScreenOn = (CheckBoxPreference) findPreference(KEEP_SCREEN_ON); mAllowMockLocation = (CheckBoxPreference) findPreference(ALLOW_MOCK_LOCATION); + mStrictMode = (CheckBoxPreference) findPreference(STRICT_MODE_KEY); + mPointerLocation = (CheckBoxPreference) findPreference(POINTER_LOCATION_KEY); + mShowScreenUpdates = (CheckBoxPreference) findPreference(SHOW_SCREEN_UPDATES_KEY); + mShowCpuUsage = (CheckBoxPreference) findPreference(SHOW_CPU_USAGE_KEY); + mWindowAnimationScale = (ListPreference) findPreference(WINDOW_ANIMATION_SCALE_KEY); + mTransitionAnimationScale = (ListPreference) findPreference(TRANSITION_ANIMATION_SCALE_KEY); + + mImmediatelyDestroyActivities = (CheckBoxPreference) findPreference( + IMMEDIATELY_DESTROY_ACTIVITIES_KEY); + mAppProcessLimit = (ListPreference) findPreference(APP_PROCESS_LIMIT_KEY); + removeHdcpOptionsForProduction(); } @@ -89,6 +134,13 @@ public class DevelopmentSettings extends PreferenceFragment mAllowMockLocation.setChecked(Settings.Secure.getInt(cr, Settings.Secure.ALLOW_MOCK_LOCATION, 0) != 0); updateHdcpValues(); + updateStrictModeVisualOptions(); + updatePointerLocationOptions(); + updateFlingerOptions(); + updateCpuUsageOptions(); + updateAnimationScaleOptions(); + updateImmediatelyDestroyActivitiesOptions(); + updateAppProcessLimitOptions(); } private void updateHdcpValues() { @@ -110,6 +162,165 @@ public class DevelopmentSettings extends PreferenceFragment } } + // Returns the current state of the system property that controls + // strictmode flashes. One of: + // 0: not explicitly set one way or another + // 1: on + // 2: off + private int currentStrictModeActiveIndex() { + if (TextUtils.isEmpty(SystemProperties.get(StrictMode.VISUAL_PROPERTY))) { + return 0; + } + boolean enabled = SystemProperties.getBoolean(StrictMode.VISUAL_PROPERTY, false); + return enabled ? 1 : 2; + } + + private void writeStrictModeVisualOptions() { + try { + mWindowManager.setStrictModeVisualIndicatorPreference(mStrictMode.isChecked() + ? "1" : ""); + } catch (RemoteException e) { + } + } + + private void updateStrictModeVisualOptions() { + mStrictMode.setChecked(currentStrictModeActiveIndex() == 1); + } + + private void writePointerLocationOptions() { + Settings.System.putInt(getActivity().getContentResolver(), + Settings.System.POINTER_LOCATION, mPointerLocation.isChecked() ? 1 : 0); + } + + private void updatePointerLocationOptions() { + mPointerLocation.setChecked(Settings.System.getInt(getActivity().getContentResolver(), + Settings.System.POINTER_LOCATION, 0) != 0); + } + + private void updateFlingerOptions() { + // magic communication with surface flinger. + try { + IBinder flinger = ServiceManager.getService("SurfaceFlinger"); + if (flinger != null) { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken("android.ui.ISurfaceComposer"); + flinger.transact(1010, data, reply, 0); + @SuppressWarnings("unused") + int showCpu = reply.readInt(); + @SuppressWarnings("unused") + int enableGL = reply.readInt(); + int showUpdates = reply.readInt(); + mShowScreenUpdates.setChecked(showUpdates != 0); + @SuppressWarnings("unused") + int showBackground = reply.readInt(); + reply.recycle(); + data.recycle(); + } + } catch (RemoteException ex) { + } + } + + private void writeFlingerOptions() { + try { + IBinder flinger = ServiceManager.getService("SurfaceFlinger"); + if (flinger != null) { + Parcel data = Parcel.obtain(); + data.writeInterfaceToken("android.ui.ISurfaceComposer"); + data.writeInt(mShowScreenUpdates.isChecked() ? 1 : 0); + flinger.transact(1002, data, null, 0); + data.recycle(); + + updateFlingerOptions(); + } + } catch (RemoteException ex) { + } + } + + private void updateCpuUsageOptions() { + mShowCpuUsage.setChecked(Settings.System.getInt(getActivity().getContentResolver(), + Settings.System.SHOW_PROCESSES, 0) != 0); + } + + private void writeCpuUsageOptions() { + boolean value = mShowCpuUsage.isChecked(); + Settings.System.putInt(getActivity().getContentResolver(), + Settings.System.SHOW_PROCESSES, value ? 1 : 0); + Intent service = (new Intent()) + .setClassName("android", "com.android.server.LoadAverageService"); + if (value) { + getActivity().startService(service); + } else { + getActivity().stopService(service); + } + } + + private void writeImmediatelyDestroyActivitiesOptions() { + try { + ActivityManagerNative.getDefault().setAlwaysFinish( + mImmediatelyDestroyActivities.isChecked()); + } catch (RemoteException ex) { + } + } + + private void updateImmediatelyDestroyActivitiesOptions() { + mImmediatelyDestroyActivities.setChecked(Settings.System.getInt( + getActivity().getContentResolver(), Settings.System.ALWAYS_FINISH_ACTIVITIES, 0) != 0); + } + + private void updateAnimationScaleValue(int which, ListPreference pref) { + try { + float scale = mWindowManager.getAnimationScale(which); + CharSequence[] values = pref.getEntryValues(); + for (int i=0; i<values.length; i++) { + float val = Float.parseFloat(values[i].toString()); + if (scale <= val) { + pref.setValueIndex(i); + return; + } + } + pref.setValueIndex(values.length-1); + } catch (RemoteException e) { + } + } + + private void updateAnimationScaleOptions() { + updateAnimationScaleValue(0, mWindowAnimationScale); + updateAnimationScaleValue(1, mTransitionAnimationScale); + } + + private void writeAnimationScaleOption(int which, ListPreference pref) { + try { + float scale = Float.parseFloat(pref.getValue().toString()); + mWindowManager.setAnimationScale(which, scale); + } catch (RemoteException e) { + } + } + + private void updateAppProcessLimitOptions() { + try { + int limit = ActivityManagerNative.getDefault().getProcessLimit(); + CharSequence[] values = mAppProcessLimit.getEntryValues(); + for (int i=0; i<values.length; i++) { + int val = Integer.parseInt(values[i].toString()); + if (val >= limit) { + mAppProcessLimit.setValueIndex(i); + return; + } + } + mAppProcessLimit.setValueIndex(0); + } catch (RemoteException e) { + } + } + + private void writeAppProcessLimitOptions() { + try { + int limit = Integer.parseInt(mAppProcessLimit.getValue().toString()); + ActivityManagerNative.getDefault().setProcessLimit(limit); + } catch (RemoteException e) { + } + } + @Override public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { @@ -142,6 +353,22 @@ public class DevelopmentSettings extends PreferenceFragment Settings.Secure.putInt(getActivity().getContentResolver(), Settings.Secure.ALLOW_MOCK_LOCATION, mAllowMockLocation.isChecked() ? 1 : 0); + } else if (preference == mStrictMode) { + writeStrictModeVisualOptions(); + } else if (preference == mPointerLocation) { + writePointerLocationOptions(); + } else if (preference == mShowScreenUpdates) { + writeFlingerOptions(); + } else if (preference == mShowCpuUsage) { + writeCpuUsageOptions(); + } else if (preference == mWindowAnimationScale) { + writeAnimationScaleOption(0, mWindowAnimationScale); + } else if (preference == mTransitionAnimationScale) { + writeAnimationScaleOption(1, mTransitionAnimationScale); + } else if (preference == mImmediatelyDestroyActivities) { + writeImmediatelyDestroyActivitiesOptions(); + } else if (preference == mAppProcessLimit) { + writeAppProcessLimitOptions(); } return false; diff --git a/src/com/android/settings/ProgressCategory.java b/src/com/android/settings/ProgressCategory.java index bedcc98..eee19bc 100644 --- a/src/com/android/settings/ProgressCategory.java +++ b/src/com/android/settings/ProgressCategory.java @@ -17,12 +17,15 @@ package com.android.settings; import android.content.Context; +import android.preference.Preference; import android.util.AttributeSet; import android.view.View; +import android.widget.TextView; public class ProgressCategory extends ProgressCategoryBase { private boolean mProgress = false; + private Preference mNoDeviceFoundPreference; public ProgressCategory(Context context, AttributeSet attrs) { super(context, attrs); @@ -32,12 +35,27 @@ public class ProgressCategory extends ProgressCategoryBase { @Override public void onBindView(View view) { super.onBindView(view); - final View textView = view.findViewById(R.id.scanning_text); + final TextView textView = (TextView) view.findViewById(R.id.scanning_text); final View progressBar = view.findViewById(R.id.scanning_progress); - final int visibility = mProgress ? View.VISIBLE : View.INVISIBLE; - textView.setVisibility(visibility); - progressBar.setVisibility(visibility); + textView.setText(mProgress ? R.string.progress_scanning : R.string.progress_tap_to_pair); + boolean noDeviceFound = getPreferenceCount() == 0; + textView.setVisibility(noDeviceFound ? View.INVISIBLE : View.VISIBLE); + progressBar.setVisibility(mProgress ? View.VISIBLE : View.INVISIBLE); + + if (mProgress) { + if (mNoDeviceFoundPreference != null) { + removePreference(mNoDeviceFoundPreference); + } + } else { + if (noDeviceFound) { + if (mNoDeviceFoundPreference == null) { + mNoDeviceFoundPreference = new Preference(getContext()); + mNoDeviceFoundPreference.setSummary(R.string.bluetooth_no_devices_found); + } + addPreference(mNoDeviceFoundPreference); + } + } } @Override diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java index 69e6fad..16531e3 100644 --- a/src/com/android/settings/Settings.java +++ b/src/com/android/settings/Settings.java @@ -544,7 +544,6 @@ public class Settings extends PreferenceActivity implements ButtonBarHandler { public static class StorageSettingsActivity extends Settings { /* empty */ } public static class WifiSettingsActivity extends Settings { /* empty */ } public static class InputMethodAndLanguageSettingsActivity extends Settings { /* empty */ } - public static class InputMethodConfigActivity extends Settings { /* empty */ } public static class InputMethodAndSubtypeEnablerActivity extends Settings { /* empty */ } public static class LocalePickerActivity extends Settings { /* empty */ } public static class UserDictionarySettingsActivity extends Settings { /* empty */ } diff --git a/src/com/android/settings/TrustedCredentialsSettings.java b/src/com/android/settings/TrustedCredentialsSettings.java index 340e93d..687663a 100644 --- a/src/com/android/settings/TrustedCredentialsSettings.java +++ b/src/com/android/settings/TrustedCredentialsSettings.java @@ -36,6 +36,7 @@ import android.widget.Button; import android.widget.CheckBox; import android.widget.FrameLayout; import android.widget.ListView; +import android.widget.ProgressBar; import android.widget.TabHost; import android.widget.TextView; import java.security.cert.CertificateEncodingException; @@ -191,14 +192,18 @@ public class TrustedCredentialsSettings extends Fragment { LayoutInflater inflater = LayoutInflater.from(getActivity()); view = inflater.inflate(R.layout.trusted_credential, parent, false); holder = new ViewHolder(); - holder.mSubjectView = (TextView)view.findViewById(R.id.trusted_credential_subject); + holder.mSubjectPrimaryView = (TextView) + view.findViewById(R.id.trusted_credential_subject_primary); + holder.mSubjectSecondaryView = (TextView) + view.findViewById(R.id.trusted_credential_subject_secondary); holder.mCheckBox = (CheckBox) view.findViewById(R.id.trusted_credential_status); view.setTag(holder); } else { holder = (ViewHolder) view.getTag(); } CertHolder certHolder = mCertHolders.get(position); - holder.mSubjectView.setText(certHolder.mSubject); + holder.mSubjectPrimaryView.setText(certHolder.mSubjectPrimary); + holder.mSubjectSecondaryView.setText(certHolder.mSubjectSecondary); if (mTab.mCheckbox) { holder.mCheckBox.setChecked(!certHolder.mDeleted); holder.mCheckBox.setVisibility(View.VISIBLE); @@ -206,15 +211,21 @@ public class TrustedCredentialsSettings extends Fragment { return view; }; - private class AliasLoader extends AsyncTask<Void, Void, List<CertHolder>> { + private class AliasLoader extends AsyncTask<Void, Integer, List<CertHolder>> { + ProgressBar mProgressBar; + View mList; @Override protected void onPreExecute() { View content = mTabHost.getTabContentView(); - content.findViewById(mTab.mProgress).setVisibility(View.VISIBLE); - content.findViewById(mTab.mList).setVisibility(View.GONE); + mProgressBar = (ProgressBar) content.findViewById(mTab.mProgress); + mList = content.findViewById(mTab.mList); + mProgressBar.setVisibility(View.VISIBLE); + mList.setVisibility(View.GONE); } @Override protected List<CertHolder> doInBackground(Void... params) { Set<String> aliases = mTab.getAliases(mStore); - List<CertHolder> certHolders = new ArrayList<CertHolder>(aliases.size()); + int max = aliases.size(); + int progress = 0; + List<CertHolder> certHolders = new ArrayList<CertHolder>(max); for (String alias : aliases) { X509Certificate cert = (X509Certificate) mStore.getCertificate(alias, true); certHolders.add(new CertHolder(mStore, @@ -222,17 +233,27 @@ public class TrustedCredentialsSettings extends Fragment { mTab, alias, cert)); + publishProgress(++progress, max); } Collections.sort(certHolders); return certHolders; } + @Override protected void onProgressUpdate(Integer... progressAndMax) { + int progress = progressAndMax[0]; + int max = progressAndMax[1]; + if (max != mProgressBar.getMax()) { + mProgressBar.setMax(max); + } + mProgressBar.setProgress(progress); + } @Override protected void onPostExecute(List<CertHolder> certHolders) { mCertHolders.clear(); mCertHolders.addAll(certHolders); notifyDataSetChanged(); View content = mTabHost.getTabContentView(); - content.findViewById(mTab.mProgress).setVisibility(View.GONE); - content.findViewById(mTab.mList).setVisibility(View.VISIBLE); + mProgressBar.setVisibility(View.GONE); + mList.setVisibility(View.VISIBLE); + mProgressBar.setProgress(0); } } } @@ -245,7 +266,8 @@ public class TrustedCredentialsSettings extends Fragment { private final X509Certificate mX509Cert; private final SslCertificate mSslCert; - private final String mSubject; + private final String mSubjectPrimary; + private final String mSubjectSecondary; private boolean mDeleted; private CertHolder(TrustedCertificateStore store, @@ -264,32 +286,34 @@ public class TrustedCredentialsSettings extends Fragment { String cn = mSslCert.getIssuedTo().getCName(); String o = mSslCert.getIssuedTo().getOName(); String ou = mSslCert.getIssuedTo().getUName(); - StringBuilder sb = new StringBuilder(); - if (!cn.isEmpty()) { - sb.append("CN=" + cn); - } + // if we have a O, use O as primary subject, secondary prefer CN over OU + // if we don't have an O, use CN as primary, empty secondary + // if we don't have O or CN, use DName as primary, empty secondary if (!o.isEmpty()) { - if (sb.length() != 0) { - sb.append(", "); - } - sb.append("O=" + o); - } - if (!ou.isEmpty()) { - if (sb.length() != 0) { - sb.append(", "); + if (!cn.isEmpty()) { + mSubjectPrimary = o; + mSubjectSecondary = cn; + } else { + mSubjectPrimary = o; + mSubjectSecondary = ou; } - sb.append("OU=" + ou); - } - if (sb.length() != 0) { - mSubject = sb.toString(); } else { - mSubject = mSslCert.getIssuedTo().getDName(); + if (!cn.isEmpty()) { + mSubjectPrimary = cn; + mSubjectSecondary = ""; + } else { + mSubjectPrimary = mSslCert.getIssuedTo().getDName(); + mSubjectSecondary = ""; + } } - mDeleted = mTab.deleted(mStore, mAlias); } @Override public int compareTo(CertHolder o) { - return this.mSubject.compareTo(o.mSubject); + int primary = this.mSubjectPrimary.compareToIgnoreCase(o.mSubjectPrimary); + if (primary != 0) { + return primary; + } + return this.mSubjectSecondary.compareToIgnoreCase(o.mSubjectSecondary); } @Override public boolean equals(Object o) { if (!(o instanceof CertHolder)) { @@ -304,12 +328,13 @@ public class TrustedCredentialsSettings extends Fragment { } private static class ViewHolder { - private TextView mSubjectView; + private TextView mSubjectPrimaryView; + private TextView mSubjectSecondaryView; private CheckBox mCheckBox; } private void showCertDialog(final CertHolder certHolder) { - View view = View.inflate(getActivity(), R.layout.trusted_credential_details, null); + View view = certHolder.mSslCert.inflateCertificateView(getActivity()); AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); builder.setTitle(com.android.internal.R.string.ssl_certificate); builder.setView(view); @@ -320,10 +345,12 @@ public class TrustedCredentialsSettings extends Fragment { }); final Dialog certDialog = builder.create(); - FrameLayout details = (FrameLayout) view.findViewById(R.id.cert_details); - details.addView(certHolder.mSslCert.inflateCertificateView(getActivity())); - - Button removeButton = (Button) view.findViewById(R.id.cert_remove_button); + ViewGroup body = (ViewGroup) view.findViewById(com.android.internal.R.id.body); + LayoutInflater inflater = LayoutInflater.from(getActivity()); + Button removeButton = (Button) inflater.inflate(R.layout.trusted_credential_details, + body, + false); + body.addView(removeButton); removeButton.setText(certHolder.mTab.getButtonLabel(certHolder)); removeButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { diff --git a/src/com/android/settings/accounts/ManageAccountsSettings.java b/src/com/android/settings/accounts/ManageAccountsSettings.java index 06c5ff0..7d935f0 100644 --- a/src/com/android/settings/accounts/ManageAccountsSettings.java +++ b/src/com/android/settings/accounts/ManageAccountsSettings.java @@ -19,7 +19,6 @@ package com.android.settings.accounts; import com.android.settings.AccountPreference; import com.android.settings.DialogCreatable; import com.android.settings.R; -import com.android.settings.vpn.VpnTypeSelection; import com.google.android.collect.Maps; import android.accounts.Account; diff --git a/src/com/android/settings/bluetooth/BluetoothSettings.java b/src/com/android/settings/bluetooth/BluetoothSettings.java index 76bf623..cd37309 100644 --- a/src/com/android/settings/bluetooth/BluetoothSettings.java +++ b/src/com/android/settings/bluetooth/BluetoothSettings.java @@ -101,9 +101,12 @@ public final class BluetoothSettings extends DeviceListPreferenceFragment { @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { boolean bluetoothIsEnabled = mLocalAdapter.getBluetoothState() == BluetoothAdapter.STATE_ON; - menu.add(Menu.NONE, MENU_ID_SCAN, 0, R.string.bluetooth_scan_nearby_devices) + boolean isDiscovering = mLocalAdapter.isDiscovering(); + int textId = isDiscovering ? R.string.bluetooth_searching_for_devices : + R.string.bluetooth_search_for_devices; + menu.add(Menu.NONE, MENU_ID_SCAN, 0, textId) //.setIcon(R.drawable.ic_menu_scan_network) - .setEnabled(bluetoothIsEnabled && !mLocalAdapter.isDiscovering()) + .setEnabled(bluetoothIsEnabled && !isDiscovering) .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); menu.add(Menu.NONE, MENU_ID_ADVANCED, 0, R.string.bluetooth_menu_advanced) //.setIcon(android.R.drawable.ic_menu_manage) @@ -218,7 +221,7 @@ public final class BluetoothSettings extends DeviceListPreferenceFragment { @Override public void onScanningStateChanged(boolean started) { super.onScanningStateChanged(started); - // Update 'Scan' option enabled state + // Update options' enabled state getActivity().invalidateOptionsMenu(); } diff --git a/src/com/android/settings/deviceinfo/Memory.java b/src/com/android/settings/deviceinfo/Memory.java index 236bdee..3205a3e 100644 --- a/src/com/android/settings/deviceinfo/Memory.java +++ b/src/com/android/settings/deviceinfo/Memory.java @@ -34,8 +34,12 @@ import android.os.storage.StorageEventListener; import android.os.storage.StorageManager; import android.os.storage.StorageVolume; import android.preference.Preference; +import android.preference.PreferenceActivity; import android.preference.PreferenceScreen; import android.util.Log; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; import android.widget.Toast; import com.android.settings.R; @@ -47,6 +51,8 @@ public class Memory extends SettingsPreferenceFragment { private static final int DLG_CONFIRM_UNMOUNT = 1; private static final int DLG_ERROR_UNMOUNT = 2; + private static final int MENU_ID_USB = Menu.FIRST; + private Resources mResources; // The mountToggle Preference that has last been clicked. @@ -88,6 +94,9 @@ public class Memory extends SettingsPreferenceFragment { } StorageVolume[] storageVolumes = mStorageManager.getVolumeList(); + // mass storage is enabled if primary volume supports it + boolean massStorageEnabled = (storageVolumes.length > 0 + && storageVolumes[0].allowMassStorage()); int length = storageVolumes.length; mStorageVolumePreferenceCategories = new StorageVolumePreferenceCategory[length]; for (int i = 0; i < length; i++) { @@ -99,6 +108,9 @@ public class Memory extends SettingsPreferenceFragment { getPreferenceScreen().addPreference(storagePreferenceCategory); storagePreferenceCategory.init(); } + + // only show options menu if we are not using the legacy USB mass storage support + setHasOptionsMenu(!massStorageEnabled); } @Override @@ -109,7 +121,9 @@ public class Memory extends SettingsPreferenceFragment { intentFilter.addDataScheme("file"); getActivity().registerReceiver(mMediaScannerReceiver, intentFilter); - mInternalStorageVolumePreferenceCategory.onResume(); + if (mInternalStorageVolumePreferenceCategory != null) { + mInternalStorageVolumePreferenceCategory.onResume(); + } for (int i = 0; i < mStorageVolumePreferenceCategories.length; i++) { mStorageVolumePreferenceCategories[i].onResume(); } @@ -135,7 +149,9 @@ public class Memory extends SettingsPreferenceFragment { public void onPause() { super.onPause(); getActivity().unregisterReceiver(mMediaScannerReceiver); - mInternalStorageVolumePreferenceCategory.onPause(); + if (mInternalStorageVolumePreferenceCategory != null) { + mInternalStorageVolumePreferenceCategory.onPause(); + } for (int i = 0; i < mStorageVolumePreferenceCategories.length; i++) { mStorageVolumePreferenceCategories[i].onPause(); } @@ -149,6 +165,31 @@ public class Memory extends SettingsPreferenceFragment { super.onDestroy(); } + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + menu.add(Menu.NONE, MENU_ID_USB, 0, R.string.storage_menu_usb) + //.setIcon(com.android.internal.R.drawable.stat_sys_data_usb) + .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case MENU_ID_USB: + if (getActivity() instanceof PreferenceActivity) { + ((PreferenceActivity) getActivity()).startPreferencePanel( + UsbSettings.class.getCanonicalName(), + null, + R.string.storage_title_usb, null, + this, 0); + } else { + startFragment(this, UsbSettings.class.getCanonicalName(), -1, null); + } + return true; + } + return super.onOptionsItemSelected(item); + } + private synchronized IMountService getMountService() { if (mMountService == null) { IBinder service = ServiceManager.getService("mount"); diff --git a/src/com/android/settings/deviceinfo/UsbSettings.java b/src/com/android/settings/deviceinfo/UsbSettings.java new file mode 100644 index 0000000..5da13af --- /dev/null +++ b/src/com/android/settings/deviceinfo/UsbSettings.java @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2011 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.ContentQueryMap; +import android.content.ContentResolver; +import android.content.Context; +import android.hardware.usb.UsbManager; +import android.os.Bundle; +import android.os.storage.StorageManager; +import android.os.storage.StorageVolume; +import android.preference.CheckBoxPreference; +import android.preference.Preference; +import android.preference.PreferenceScreen; +import android.provider.Settings; +import android.util.Log; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; + +import com.android.settings.R; +import com.android.settings.SettingsPreferenceFragment; + +import java.io.File; + +/** + * USB storage settings. + */ +public class UsbSettings extends SettingsPreferenceFragment { + + private static final String TAG = "UsbSettings"; + + private static final String KEY_MTP = "usb_mtp"; + private static final String KEY_PTP = "usb_ptp"; + private static final String KEY_INSTALLER_CD = "usb_installer_cd"; + private static final int MENU_ID_INSTALLER_CD = Menu.FIRST; + + private UsbManager mUsbManager; + private String mInstallerImagePath; + private CheckBoxPreference mMtp; + private CheckBoxPreference mPtp; + + private PreferenceScreen createPreferenceHierarchy() { + PreferenceScreen root = getPreferenceScreen(); + if (root != null) { + root.removeAll(); + } + addPreferencesFromResource(R.xml.usb_settings); + root = getPreferenceScreen(); + + mMtp = (CheckBoxPreference)root.findPreference(KEY_MTP); + mPtp = (CheckBoxPreference)root.findPreference(KEY_PTP); + + return root; + } + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + mUsbManager = (UsbManager)getSystemService(Context.USB_SERVICE); + mInstallerImagePath = getString(com.android.internal.R.string.config_isoImagePath); + if (!(new File(mInstallerImagePath)).exists()) { + mInstallerImagePath = null; + } + setHasOptionsMenu(mInstallerImagePath != null); + } + + @Override + public void onResume() { + super.onResume(); + + // Make sure we reload the preference hierarchy since some of these settings + // depend on others... + createPreferenceHierarchy(); + updateToggles(); + } + + private void updateToggles() { + if (mUsbManager.isFunctionEnabled(UsbManager.USB_FUNCTION_MTP)) { + mMtp.setChecked(true); + mPtp.setChecked(false); + } else if (mUsbManager.isFunctionEnabled(UsbManager.USB_FUNCTION_PTP)) { + mMtp.setChecked(false); + mPtp.setChecked(true); + } else { + mMtp.setChecked(false); + mPtp.setChecked(false); + } + } + + @Override + public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { + Log.d(TAG, "onPreferenceTreeClick " + preference); + + // temporary hack - using check boxes as radio buttons + // don't allow unchecking them + if (preference instanceof CheckBoxPreference) { + CheckBoxPreference checkBox = (CheckBoxPreference)preference; + if (!checkBox.isChecked()) { + checkBox.setChecked(true); + return true; + } + } + + if (preference == mMtp) { + mUsbManager.setCurrentFunction(UsbManager.USB_FUNCTION_MTP, true); + mPtp.setChecked(false); + } else if (preference == mPtp) { + mUsbManager.setCurrentFunction(UsbManager.USB_FUNCTION_PTP, true); + mMtp.setChecked(false); + } + + return true; + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + menu.add(Menu.NONE, MENU_ID_INSTALLER_CD, 0, R.string.usb_label_installer_cd) + //.setIcon(com.android.internal.R.drawable.stat_sys_data_usb) + .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case MENU_ID_INSTALLER_CD: + // installer CD is never default + mUsbManager.setCurrentFunction(UsbManager.USB_FUNCTION_MASS_STORAGE, false); + mUsbManager.setMassStorageBackingFile(mInstallerImagePath); + mMtp.setChecked(false); + mPtp.setChecked(false); + return true; + } + return super.onOptionsItemSelected(item); + } +} diff --git a/src/com/android/settings/inputmethod/InputMethodAndLanguageSettings.java b/src/com/android/settings/inputmethod/InputMethodAndLanguageSettings.java index c72f0ba..4ccebf0 100644 --- a/src/com/android/settings/inputmethod/InputMethodAndLanguageSettings.java +++ b/src/com/android/settings/inputmethod/InputMethodAndLanguageSettings.java @@ -24,14 +24,25 @@ import com.android.settings.VoiceInputOutputSettings; import android.app.Activity; import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; import android.content.res.Configuration; import android.os.Bundle; +import android.preference.CheckBoxPreference; import android.preference.ListPreference; import android.preference.Preference; +import android.preference.PreferenceCategory; +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.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.Set; public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment @@ -40,13 +51,28 @@ public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment private static final String KEY_PHONE_LANGUAGE = "phone_language"; private static final String KEY_CURRENT_INPUT_METHOD = "current_input_method"; private static final String KEY_INPUT_METHOD_SELECTOR = "input_method_selector"; - private static final String KEY_LANGUAGE_SETTINGS_CATEGORY = "language_settings_category"; private static final String KEY_USER_DICTIONARY_SETTINGS = "key_user_dictionary_settings"; + // false: on ICS or later + private static final boolean SHOW_INPUT_METHOD_SWITCHER_SETTINGS = false; + private static final String[] sSystemSettingNames = { + System.TEXT_AUTO_REPLACE, System.TEXT_AUTO_CAPS, System.TEXT_AUTO_PUNCTUATE, + }; + + private static final String[] sHardKeyboardKeys = { + "auto_replace", "auto_caps", "auto_punctuate", + }; private int mDefaultInputMethodSelectorVisibility = 0; private ListPreference mShowInputMethodSelectorPref; private Preference mLanguagePref; + private ArrayList<InputMethodPreference> mInputMethodPreferenceList = + new ArrayList<InputMethodPreference>(); + private boolean mHaveHardKeyboard; + private PreferenceCategory mHardKeyboardCategory; + private InputMethodManager mImm; + private List<InputMethodInfo> mImis; + private boolean mIsOnlyImeSettings; @Override public void onCreate(Bundle icicle) { @@ -66,13 +92,26 @@ public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment } else { mLanguagePref = findPreference(KEY_PHONE_LANGUAGE); } - mShowInputMethodSelectorPref = (ListPreference)findPreference( - KEY_INPUT_METHOD_SELECTOR); - mShowInputMethodSelectorPref.setOnPreferenceChangeListener(this); - // TODO: Update current input method name on summary - updateInputMethodSelectorSummary(loadInputMethodSelectorVisibility()); + if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) { + mShowInputMethodSelectorPref = (ListPreference)findPreference( + KEY_INPUT_METHOD_SELECTOR); + mShowInputMethodSelectorPref.setOnPreferenceChangeListener(this); + // TODO: Update current input method name on summary + updateInputMethodSelectorSummary(loadInputMethodSelectorVisibility()); + } new VoiceInputOutputSettings(this).onCreate(); + + // Hard keyboard + final Configuration config = getResources().getConfiguration(); + mHaveHardKeyboard = (config.keyboard == Configuration.KEYBOARD_QWERTY); + + // IME + mIsOnlyImeSettings = Settings.ACTION_INPUT_METHOD_SETTINGS.equals( + getActivity().getIntent().getAction()); + mImm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + mImis = mImm.getInputMethodList(); + createImePreferenceHierarchy((PreferenceGroup)findPreference("keyboard_settings_category")); } private void updateInputMethodSelectorSummary(int value) { @@ -109,23 +148,46 @@ public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment @Override public void onResume() { super.onResume(); - if (mLanguagePref != null) { - Configuration conf = getResources().getConfiguration(); - String locale = conf.locale.getDisplayName(conf.locale); - if (locale != null && locale.length() > 1) { - locale = Character.toUpperCase(locale.charAt(0)) + locale.substring(1); - mLanguagePref.setSummary(locale); + if (!mIsOnlyImeSettings) { + if (mLanguagePref != null) { + Configuration conf = getResources().getConfiguration(); + String locale = conf.locale.getDisplayName(conf.locale); + if (locale != null && locale.length() > 1) { + locale = Character.toUpperCase(locale.charAt(0)) + locale.substring(1); + mLanguagePref.setSummary(locale); + } + } + + updateUserDictionaryPreference(findPreference(KEY_USER_DICTIONARY_SETTINGS)); + if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) { + mShowInputMethodSelectorPref.setOnPreferenceChangeListener(this); + } + } + + // Hard keyboard + if (mHaveHardKeyboard) { + for (int i = 0; i < sHardKeyboardKeys.length; ++i) { + InputMethodPreference chkPref = (InputMethodPreference) + mHardKeyboardCategory.findPreference(sHardKeyboardKeys[i]); + chkPref.setChecked( + System.getInt(getContentResolver(), sSystemSettingNames[i], 1) > 0); } } - updateUserDictionaryPreference(findPreference(KEY_USER_DICTIONARY_SETTINGS)); - mShowInputMethodSelectorPref.setOnPreferenceChangeListener(this); + // IME + InputMethodAndSubtypeUtil.loadInputMethodSubtypeList( + this, getContentResolver(), mImis, null); + updateActiveInputMethodsSummary(); } @Override public void onPause() { super.onPause(); - mShowInputMethodSelectorPref.setOnPreferenceChangeListener(null); + if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) { + mShowInputMethodSelectorPref.setOnPreferenceChangeListener(null); + } + InputMethodAndSubtypeUtil.saveInputMethodSubtypeList( + this, getContentResolver(), mImis, mHaveHardKeyboard); } @Override @@ -142,6 +204,17 @@ public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment getSystemService(Context.INPUT_METHOD_SERVICE); imm.showInputMethodPicker(); } + } else if (preference instanceof CheckBoxPreference) { + final CheckBoxPreference chkPref = (CheckBoxPreference) preference; + if (mHaveHardKeyboard) { + for (int i = 0; i < sHardKeyboardKeys.length; ++i) { + if (chkPref == mHardKeyboardCategory.findPreference(sHardKeyboardKeys[i])) { + System.putInt(getContentResolver(), sSystemSettingNames[i], + chkPref.isChecked() ? 1 : 0); + return true; + } + } + } } return super.onPreferenceTreeClick(preferenceScreen, preference); } @@ -164,12 +237,84 @@ public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment @Override public boolean onPreferenceChange(Preference preference, Object value) { - if (preference == mShowInputMethodSelectorPref) { - if (value instanceof String) { - saveInputMethodSelectorVisibility((String)value); + if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) { + if (preference == mShowInputMethodSelectorPref) { + if (value instanceof String) { + saveInputMethodSelectorVisibility((String)value); + } } } return false; } + private void updateActiveInputMethodsSummary() { + for (Preference pref : mInputMethodPreferenceList) { + if (pref instanceof InputMethodPreference) { + ((InputMethodPreference)pref).updateSummary(); + } + } + } + + private InputMethodPreference getInputMethodPreference(InputMethodInfo imi, int imiSize) { + final PackageManager pm = getPackageManager(); + final CharSequence label = imi.loadLabel(pm); + // IME settings + final Intent intent; + final String settingsActivity = imi.getSettingsActivity(); + if (!TextUtils.isEmpty(settingsActivity)) { + intent = new Intent(Intent.ACTION_MAIN); + intent.setClassName(imi.getPackageName(), settingsActivity); + } else { + intent = null; + } + + // Add a check box for enabling/disabling IME + InputMethodPreference pref = new InputMethodPreference(this, intent, mImm, imi, imiSize); + pref.setKey(imi.getId()); + pref.setTitle(label); + return pref; + } + + private void createImePreferenceHierarchy(PreferenceGroup root) { + final Preference hardKeyPref = findPreference("hard_keyboard"); + if (mIsOnlyImeSettings) { + getPreferenceScreen().removeAll(); + if (hardKeyPref != null && mHaveHardKeyboard) { + getPreferenceScreen().addPreference(hardKeyPref); + } + if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) { + getPreferenceScreen().addPreference(mShowInputMethodSelectorPref); + } + getPreferenceScreen().addPreference(root); + } + if (hardKeyPref != null) { + if (mHaveHardKeyboard) { + mHardKeyboardCategory = (PreferenceCategory) hardKeyPref; + } else { + getPreferenceScreen().removePreference(hardKeyPref); + } + } + root.removeAll(); + mInputMethodPreferenceList.clear(); + + if (!mIsOnlyImeSettings) { + // Current IME selection + final PreferenceScreen currentIme = new PreferenceScreen(getActivity(), null); + currentIme.setKey(KEY_CURRENT_INPUT_METHOD); + currentIme.setTitle(getResources().getString(R.string.current_input_method)); + root.addPreference(currentIme); + } + + final int N = (mImis == null ? 0 : mImis.size()); + for (int i = 0; i < N; ++i) { + final InputMethodInfo imi = mImis.get(i); + final InputMethodPreference pref = getInputMethodPreference(imi, N); + mInputMethodPreferenceList.add(pref); + } + + Collections.sort(mInputMethodPreferenceList); + for (int i = 0; i < N; ++i) { + root.addPreference(mInputMethodPreferenceList.get(i)); + } + } } diff --git a/src/com/android/settings/inputmethod/InputMethodAndSubtypeUtil.java b/src/com/android/settings/inputmethod/InputMethodAndSubtypeUtil.java index 362fbb5..f62edc4 100644 --- a/src/com/android/settings/inputmethod/InputMethodAndSubtypeUtil.java +++ b/src/com/android/settings/inputmethod/InputMethodAndSubtypeUtil.java @@ -169,9 +169,10 @@ public class InputMethodAndSubtypeUtil { final boolean isImeChecked = (pref instanceof CheckBoxPreference) ? ((CheckBoxPreference) pref).isChecked() : enabledIMEAndSubtypesMap.containsKey(imiId); - boolean isCurrentInputMethod = imiId.equals(currentInputMethodId); - boolean systemIme = isSystemIme(imi); - if (((onlyOneIME || systemIme) && !hasHardKeyboard) || isImeChecked) { + final boolean isCurrentInputMethod = imiId.equals(currentInputMethodId); + final boolean auxIme = isAuxiliaryIme(imi); + final boolean systemIme = isSystemIme(imi); + if (((onlyOneIME || (systemIme && !auxIme)) && !hasHardKeyboard) || isImeChecked) { if (!enabledIMEAndSubtypesMap.containsKey(imiId)) { // imiId has just been enabled enabledIMEAndSubtypesMap.put(imiId, new HashSet<String>()); @@ -276,7 +277,7 @@ public class InputMethodAndSubtypeUtil { List<InputMethodInfo> inputMethodInfos, final Map<String, List<Preference>> inputMethodPrefsMap) { HashMap<String, HashSet<String>> enabledSubtypes = - getEnabledInputMethodsAndSubtypeList(resolver); + getEnabledInputMethodsAndSubtypeList(resolver); for (InputMethodInfo imi : inputMethodInfos) { final String imiId = imi.getId(); @@ -342,4 +343,19 @@ public class InputMethodAndSubtypeUtil { return (property.getServiceInfo().applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; } + + public static boolean isAuxiliaryIme(InputMethodInfo imi) { + final int subtypeCount = imi.getSubtypeCount(); + if (subtypeCount == 0) { + return false; + } else { + for (int i = 0; i < subtypeCount; ++i) { + final InputMethodSubtype subtype = imi.getSubtypeAt(i); + if (!subtype.isAuxiliary()) { + return false; + } + } + } + return true; + } } diff --git a/src/com/android/settings/inputmethod/InputMethodConfig.java b/src/com/android/settings/inputmethod/InputMethodConfig.java deleted file mode 100644 index 393292e..0000000 --- a/src/com/android/settings/inputmethod/InputMethodConfig.java +++ /dev/null @@ -1,286 +0,0 @@ -/* - * 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.inputmethod; - -import com.android.settings.R; -import com.android.settings.SettingsPreferenceFragment; - -import android.app.AlertDialog; -import android.content.ContentResolver; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.res.Configuration; -import android.os.Bundle; -import android.preference.CheckBoxPreference; -import android.preference.Preference; -import android.preference.Preference.OnPreferenceClickListener; -import android.preference.PreferenceCategory; -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 android.view.inputmethod.InputMethodSubtype; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; - -public class InputMethodConfig extends SettingsPreferenceFragment { - - private static final String[] sSystemSettingNames = { - System.TEXT_AUTO_REPLACE, System.TEXT_AUTO_CAPS, System.TEXT_AUTO_PUNCTUATE, - }; - - private static final String[] sHardKeyboardKeys = { - "auto_replace", "auto_caps", "auto_punctuate", - }; - - private AlertDialog mDialog = null; - private boolean mHaveHardKeyboard; - private PreferenceCategory mHardKeyboardCategory; - // Map of imi and its preferences - final private HashMap<String, List<Preference>> mInputMethodPrefsMap = - new HashMap<String, List<Preference>>(); - final private HashMap<InputMethodInfo, Preference> mActiveInputMethodsPrefMap = - new HashMap<InputMethodInfo, Preference>(); - private List<InputMethodInfo> mInputMethodProperties; - - - @Override - public void onCreate(Bundle icicle) { - super.onCreate(icicle); - Configuration config = getResources().getConfiguration(); - mHaveHardKeyboard = (config.keyboard == Configuration.KEYBOARD_QWERTY); - InputMethodManager imm = (InputMethodManager) getSystemService( - Context.INPUT_METHOD_SERVICE); - - // TODO: Change mInputMethodProperties to Map - mInputMethodProperties = imm.getInputMethodList(); - setPreferenceScreen(createPreferenceHierarchy()); - } - - @Override - public void onResume() { - super.onResume(); - - ContentResolver resolver = getContentResolver(); - if (mHaveHardKeyboard) { - for (int i = 0; i < sHardKeyboardKeys.length; ++i) { - CheckBoxPreference chkPref = (CheckBoxPreference) - mHardKeyboardCategory.findPreference(sHardKeyboardKeys[i]); - chkPref.setChecked(System.getInt(resolver, sSystemSettingNames[i], 1) > 0); - } - } - - InputMethodAndSubtypeUtil.loadInputMethodSubtypeList( - this, resolver, mInputMethodProperties, mInputMethodPrefsMap); - updateActiveInputMethodsSummary(); - } - - @Override - public void onPause() { - super.onPause(); - InputMethodAndSubtypeUtil.saveInputMethodSubtypeList(this, getContentResolver(), - mInputMethodProperties, mHaveHardKeyboard); - } - - private void showSecurityWarnDialog(InputMethodInfo imi, final CheckBoxPreference chkPref, - final String imiId) { - if (mDialog != null && mDialog.isShowing()) { - mDialog.dismiss(); - } - mDialog = (new AlertDialog.Builder(getActivity())) - .setTitle(android.R.string.dialog_alert_title) - .setIcon(android.R.drawable.ic_dialog_alert) - .setCancelable(true) - .setPositiveButton(android.R.string.ok, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - chkPref.setChecked(true); - for (Preference pref: mInputMethodPrefsMap.get(imiId)) { - pref.setEnabled(true); - } - } - }) - .setNegativeButton(android.R.string.cancel, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - } - }) - .create(); - mDialog.setMessage(getResources().getString(R.string.ime_security_warning, - imi.getServiceInfo().applicationInfo.loadLabel(getPackageManager()))); - mDialog.show(); - } - - private InputMethodInfo getInputMethodInfoFromImiId(String imiId) { - final int N = mInputMethodProperties.size(); - for (int i = 0; i < N; ++i) { - InputMethodInfo imi = mInputMethodProperties.get(i); - if (imiId.equals(imi.getId())) { - return imi; - } - } - return null; - } - - @Override - public boolean onPreferenceTreeClick( - PreferenceScreen preferenceScreen, Preference preference) { - - if (preference instanceof CheckBoxPreference) { - final CheckBoxPreference chkPref = (CheckBoxPreference) preference; - - if (mHaveHardKeyboard) { - for (int i = 0; i < sHardKeyboardKeys.length; ++i) { - if (chkPref == mHardKeyboardCategory.findPreference(sHardKeyboardKeys[i])) { - System.putInt(getContentResolver(), sSystemSettingNames[i], - chkPref.isChecked() ? 1 : 0); - return true; - } - } - } - - final String imiId = chkPref.getKey(); - if (chkPref.isChecked()) { - InputMethodInfo selImi = getInputMethodInfoFromImiId(imiId); - if (selImi != null) { - if (InputMethodAndSubtypeUtil.isSystemIme(selImi)) { - // This is a built-in IME, so no need to warn. - return super.onPreferenceTreeClick(preferenceScreen, preference); - } - } else { - return super.onPreferenceTreeClick(preferenceScreen, preference); - } - chkPref.setChecked(false); - showSecurityWarnDialog(selImi, chkPref, imiId); - } else { - for (Preference pref: mInputMethodPrefsMap.get(imiId)) { - pref.setEnabled(false); - } - } - } - return super.onPreferenceTreeClick(preferenceScreen, preference); - } - - @Override - public void onDestroy() { - super.onDestroy(); - if (mDialog != null) { - mDialog.dismiss(); - mDialog = null; - } - } - - private void addInputMethodPreference(PreferenceScreen root, InputMethodInfo imi, - final int imiSize) { - PreferenceCategory keyboardSettingsCategory = new PreferenceCategory(getActivity()); - root.addPreference(keyboardSettingsCategory); - final String imiId = imi.getId(); - mInputMethodPrefsMap.put(imiId, new ArrayList<Preference>()); - - PackageManager pm = getPackageManager(); - CharSequence label = imi.loadLabel(pm); - keyboardSettingsCategory.setTitle(label); - - final boolean isSystemIME = InputMethodAndSubtypeUtil.isSystemIme(imi); - // Add a check box for enabling/disabling IME - CheckBoxPreference chkbxPref = new CheckBoxPreference(getActivity()); - chkbxPref.setKey(imiId); - chkbxPref.setTitle(label); - keyboardSettingsCategory.addPreference(chkbxPref); - // Disable the toggle if it's the only keyboard in the system, or it's a system IME. - if (imiSize <= 1 || isSystemIME) { - chkbxPref.setEnabled(false); - } - - Intent intent; - // Add subtype settings when this IME has two or more subtypes. - PreferenceScreen prefScreen = new PreferenceScreen(getActivity(), null); - prefScreen.setTitle(R.string.active_input_method_subtypes); - if (imi.getSubtypeCount() > 1) { - prefScreen.setOnPreferenceClickListener(new OnPreferenceClickListener() { - @Override - public boolean onPreferenceClick(Preference preference){ - final Bundle bundle = new Bundle(); - bundle.putString(Settings.EXTRA_INPUT_METHOD_ID, imiId); - startFragment(InputMethodConfig.this, - InputMethodAndSubtypeEnabler.class.getName(), - 0, bundle); - return true; - } - }); - keyboardSettingsCategory.addPreference(prefScreen); - mActiveInputMethodsPrefMap.put(imi, prefScreen); - mInputMethodPrefsMap.get(imiId).add(prefScreen); - } - - // Add IME settings - String settingsActivity = imi.getSettingsActivity(); - if (!TextUtils.isEmpty(settingsActivity)) { - prefScreen = new PreferenceScreen(getActivity(), null); - prefScreen.setTitle(R.string.input_method_settings); - intent = new Intent(Intent.ACTION_MAIN); - intent.setClassName(imi.getPackageName(), settingsActivity); - prefScreen.setIntent(intent); - keyboardSettingsCategory.addPreference(prefScreen); - mInputMethodPrefsMap.get(imiId).add(prefScreen); - } - } - - private PreferenceScreen createPreferenceHierarchy() { - addPreferencesFromResource(R.xml.hard_keyboard_settings); - PreferenceScreen root = getPreferenceScreen(); - - if (mHaveHardKeyboard) { - mHardKeyboardCategory = (PreferenceCategory) findPreference("hard_keyboard"); - } else { - root.removeAll(); - } - - final int N = (mInputMethodProperties == null ? 0 : mInputMethodProperties.size()); - for (int i = 0; i < N; ++i) { - addInputMethodPreference(root, mInputMethodProperties.get(i), N); - } - return root; - } - - private void updateActiveInputMethodsSummary() { - final InputMethodManager imm = - (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); - for (InputMethodInfo imi: mActiveInputMethodsPrefMap.keySet()) { - Preference pref = mActiveInputMethodsPrefMap.get(imi); - List<InputMethodSubtype> subtypes = imm.getEnabledInputMethodSubtypeList(imi, true); - StringBuilder summary = new StringBuilder(); - boolean subtypeAdded = false; - for (InputMethodSubtype subtype: subtypes) { - if (subtypeAdded) { - summary.append(", "); - } - final CharSequence subtypeLabel = subtype.getDisplayName(getActivity(), - imi.getPackageName(), imi.getServiceInfo().applicationInfo); - summary.append(subtypeLabel); - subtypeAdded = true; - } - pref.setSummary(summary.toString()); - } - } -} diff --git a/src/com/android/settings/inputmethod/InputMethodPreference.java b/src/com/android/settings/inputmethod/InputMethodPreference.java new file mode 100644 index 0000000..21057a6 --- /dev/null +++ b/src/com/android/settings/inputmethod/InputMethodPreference.java @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2011 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.inputmethod; + +import com.android.settings.R; +import com.android.settings.SettingsPreferenceFragment; + +import android.app.AlertDialog; +import android.app.Fragment; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.res.Configuration; +import android.os.Bundle; +import android.preference.CheckBoxPreference; +import android.preference.PreferenceActivity; +import android.provider.Settings; +import android.text.TextUtils; +import android.util.Log; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.View.OnLongClickListener; +import android.view.inputmethod.InputMethodInfo; +import android.view.inputmethod.InputMethodManager; +import android.view.inputmethod.InputMethodSubtype; +import android.widget.ImageView; +import android.widget.TextView; + +import java.util.Comparator; +import java.util.List; + +public class InputMethodPreference extends CheckBoxPreference + implements Comparator<InputMethodPreference> { + private static final String TAG = InputMethodPreference.class.getSimpleName(); + private static final float DISABLED_ALPHA = 0.4f; + private final SettingsPreferenceFragment mFragment; + private final InputMethodInfo mImi; + private final InputMethodManager mImm; + private final Intent mSettingsIntent; + private final boolean mIsSystemIme; + + private AlertDialog mDialog = null; + private ImageView mInputMethodSettingsButton; + private TextView mTitleText; + private TextView mSummaryText; + private View mInputMethodPref; + + public InputMethodPreference(SettingsPreferenceFragment fragment, Intent settingsIntent, + InputMethodManager imm, InputMethodInfo imi, int imiCount) { + super(fragment.getActivity(), null, R.style.InputMethodPreferenceStyle); + setLayoutResource(R.layout.preference_inputmethod); + setWidgetLayoutResource(R.layout.preference_inputmethod_widget); + mFragment = fragment; + mSettingsIntent = settingsIntent; + mImm = imm; + mImi = imi; + updateSummary(); + mIsSystemIme = InputMethodAndSubtypeUtil.isSystemIme(imi); + final boolean isAuxIme = InputMethodAndSubtypeUtil.isAuxiliaryIme(imi); + if (imiCount <= 1 || (mIsSystemIme && !isAuxIme)) { + setEnabled(false); + } + } + + @Override + protected void onBindView(View view) { + super.onBindView(view); + mInputMethodPref = view.findViewById(R.id.inputmethod_pref); + mInputMethodPref.setOnClickListener( + new OnClickListener() { + @Override + public void onClick(View arg0) { + if (isChecked()) { + setChecked(false); + } else { + if (mIsSystemIme) { + setChecked(true); + } else { + showSecurityWarnDialog(mImi, InputMethodPreference.this); + } + } + } + }); + mInputMethodSettingsButton = (ImageView)view.findViewById(R.id.inputmethod_settings); + mTitleText = (TextView)view.findViewById(android.R.id.title); + mSummaryText = (TextView)view.findViewById(android.R.id.summary); + if (mSettingsIntent != null) { + mInputMethodSettingsButton.setOnClickListener( + new OnClickListener() { + @Override + public void onClick(View arg0) { + mFragment.startActivity(mSettingsIntent); + } + }); + } + final boolean hasSubtypes = mImi.getSubtypeCount() > 1; + final String imiId = mImi.getId(); + if (hasSubtypes) { + final OnLongClickListener listener = new OnLongClickListener() { + @Override + public boolean onLongClick(View arg0) { + final Bundle bundle = new Bundle(); + bundle.putString(Settings.EXTRA_INPUT_METHOD_ID, imiId); + startFragment(mFragment, InputMethodAndSubtypeEnabler.class.getName(), + 0, bundle); + return true; + } + }; + mInputMethodSettingsButton.setOnLongClickListener(listener); + } + if (mSettingsIntent == null) { + mInputMethodSettingsButton.setVisibility(View.GONE); + } else { + enableSettingsButton(); + } + } + + @Override + public void setEnabled(boolean enabled) { + super.setEnabled(enabled); + enableSettingsButton(); + } + + private void enableSettingsButton() { + if (mInputMethodSettingsButton != null) { + final boolean checked = isChecked(); + mInputMethodSettingsButton.setEnabled(checked); + mInputMethodSettingsButton.setClickable(checked); + mInputMethodSettingsButton.setFocusable(checked); + if (!checked) { + mInputMethodSettingsButton.setAlpha(DISABLED_ALPHA); + } + } + if (mTitleText != null) { + mTitleText.setEnabled(true); + } + if (mSummaryText != null) { + mSummaryText.setEnabled(true); + } + } + + public static boolean startFragment( + Fragment fragment, String fragmentClass, int requestCode, Bundle extras) { + if (fragment.getActivity() instanceof PreferenceActivity) { + PreferenceActivity preferenceActivity = (PreferenceActivity)fragment.getActivity(); + preferenceActivity.startPreferencePanel(fragmentClass, extras, 0, null, fragment, + requestCode); + return true; + } else { + Log.w(TAG, "Parent isn't PreferenceActivity, thus there's no way to launch the " + + "given Fragment (name: " + fragmentClass + ", requestCode: " + requestCode + + ")"); + return false; + } + } + + public String getSummaryString() { + final StringBuilder builder = new StringBuilder(); + final List<InputMethodSubtype> subtypes = mImm.getEnabledInputMethodSubtypeList(mImi, true); + for (InputMethodSubtype subtype : subtypes) { + if (builder.length() > 0) { + builder.append(", "); + } + final CharSequence subtypeLabel = subtype.getDisplayName(mFragment.getActivity(), + mImi.getPackageName(), mImi.getServiceInfo().applicationInfo); + builder.append(subtypeLabel); + } + return builder.toString(); + } + + public void updateSummary() { + final String summary = getSummaryString(); + if (TextUtils.isEmpty(summary)) { + return; + } + setSummary(summary); + } + + @Override + public void setChecked(boolean checked) { + super.setChecked(checked); + saveImeSettings(); + } + + private void showSecurityWarnDialog(InputMethodInfo imi, final CheckBoxPreference chkPref) { + if (mDialog != null && mDialog.isShowing()) { + mDialog.dismiss(); + } + mDialog = (new AlertDialog.Builder(mFragment.getActivity())) + .setTitle(android.R.string.dialog_alert_title) + .setIcon(android.R.drawable.ic_dialog_alert) + .setCancelable(true) + .setPositiveButton(android.R.string.ok, + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + chkPref.setChecked(true); + } + }) + .setNegativeButton(android.R.string.cancel, + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + } + }) + .create(); + mDialog.setMessage(mFragment.getResources().getString(R.string.ime_security_warning, + imi.getServiceInfo().applicationInfo.loadLabel( + mFragment.getActivity().getPackageManager()))); + mDialog.show(); + } + + @Override + public int compare(InputMethodPreference arg0, InputMethodPreference arg1) { + if (arg0.isEnabled() == arg0.isEnabled()) { + return arg0.mImi.getId().compareTo(arg1.mImi.getId()); + } else { + // Prefer system IMEs + return arg0.isEnabled() ? 1 : -1; + } + } + + private void saveImeSettings() { + InputMethodAndSubtypeUtil.saveInputMethodSubtypeList( + mFragment, mFragment.getActivity().getContentResolver(), mImm.getInputMethodList(), + mFragment.getResources().getConfiguration().keyboard + == Configuration.KEYBOARD_QWERTY); + } +} diff --git a/src/com/android/settings/vpn/AuthenticationActor.java b/src/com/android/settings/vpn/AuthenticationActor.java deleted file mode 100644 index f8401d6..0000000 --- a/src/com/android/settings/vpn/AuthenticationActor.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (C) 2009 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.vpn; - -import com.android.settings.R; - -import android.app.Dialog; -import android.content.Context; -import android.net.vpn.VpnManager; -import android.net.vpn.VpnProfile; -import android.net.vpn.VpnState; -import android.text.TextUtils; -import android.util.Log; -import android.view.View; -import android.widget.CheckBox; -import android.widget.TextView; - -import java.io.IOException; - -/** - * A {@link VpnProfileActor} that provides an authentication view for users to - * input username and password before connecting to the VPN server. - */ -public class AuthenticationActor implements VpnProfileActor { - private static final String TAG = AuthenticationActor.class.getName(); - - private Context mContext; - private VpnProfile mProfile; - private VpnManager mVpnManager; - - public AuthenticationActor(Context context, VpnProfile p) { - mContext = context; - mProfile = p; - mVpnManager = new VpnManager(context); - } - - //@Override - public VpnProfile getProfile() { - return mProfile; - } - - //@Override - public boolean isConnectDialogNeeded() { - return true; - } - - //@Override - public String validateInputs(Dialog d) { - TextView usernameView = (TextView) d.findViewById(R.id.username_value); - TextView passwordView = (TextView) d.findViewById(R.id.password_value); - Context c = mContext; - if (TextUtils.isEmpty(usernameView.getText().toString())) { - return c.getString(R.string.vpn_a_username); - } else if (TextUtils.isEmpty(passwordView.getText().toString())) { - return c.getString(R.string.vpn_a_password); - } else { - return null; - } - } - - //@Override - public void connect(Dialog d) { - TextView usernameView = (TextView) d.findViewById(R.id.username_value); - TextView passwordView = (TextView) d.findViewById(R.id.password_value); - CheckBox saveUsername = (CheckBox) d.findViewById(R.id.save_username); - - try { - setSavedUsername(saveUsername.isChecked() - ? usernameView.getText().toString() - : ""); - } catch (IOException e) { - Log.e(TAG, "setSavedUsername()", e); - } - - connect(usernameView.getText().toString(), - passwordView.getText().toString()); - passwordView.setText(""); - } - - //@Override - public View createConnectView() { - View v = View.inflate(mContext, R.layout.vpn_connect_dialog_view, null); - TextView usernameView = (TextView) v.findViewById(R.id.username_value); - TextView passwordView = (TextView) v.findViewById(R.id.password_value); - CheckBox saveUsername = (CheckBox) v.findViewById(R.id.save_username); - - String username = mProfile.getSavedUsername(); - if (!TextUtils.isEmpty(username)) { - usernameView.setText(username); - saveUsername.setChecked(true); - passwordView.requestFocus(); - } - return v; - } - - protected Context getContext() { - return mContext; - } - - private void connect(String username, String password) { - mVpnManager.connect(mProfile, username, password); - } - - //@Override - public void disconnect() { - mVpnManager.disconnect(); - } - - private void setSavedUsername(String name) throws IOException { - if (!name.equals(mProfile.getSavedUsername())) { - mProfile.setSavedUsername(name); - VpnSettings.saveProfileToStorage(mProfile); - } - } -} diff --git a/src/com/android/settings/vpn/L2tpEditor.java b/src/com/android/settings/vpn/L2tpEditor.java deleted file mode 100644 index 05d51d6..0000000 --- a/src/com/android/settings/vpn/L2tpEditor.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (C) 2009 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.vpn; - -import com.android.settings.R; - -import android.content.Context; -import android.net.vpn.L2tpProfile; -import android.preference.CheckBoxPreference; -import android.preference.EditTextPreference; -import android.preference.Preference; -import android.preference.PreferenceGroup; - -/** - * The class for editing {@link L2tpProfile}. - */ -class L2tpEditor extends VpnProfileEditor { - private CheckBoxPreference mSecret; - private SecretHandler mSecretHandler; - - public L2tpEditor(L2tpProfile p) { - super(p); - } - - @Override - protected void loadExtraPreferencesTo(PreferenceGroup subpanel) { - Context c = subpanel.getContext(); - subpanel.addPreference(createSecretPreference(c)); - subpanel.addPreference(createSecretStringPreference(c)); - - L2tpProfile profile = (L2tpProfile) getProfile(); - } - - @Override - public String validate() { - String result = super.validate(); - if (!mSecret.isChecked()) return result; - - return ((result != null) ? result : mSecretHandler.validate()); - } - - private Preference createSecretPreference(Context c) { - final L2tpProfile profile = (L2tpProfile) getProfile(); - CheckBoxPreference secret = mSecret = new CheckBoxPreference(c); - boolean enabled = profile.isSecretEnabled(); - setCheckBoxTitle(secret, R.string.vpn_l2tp_secret); - secret.setChecked(enabled); - setSecretSummary(secret, enabled); - secret.setOnPreferenceChangeListener( - new Preference.OnPreferenceChangeListener() { - public boolean onPreferenceChange( - Preference pref, Object newValue) { - boolean enabled = (Boolean) newValue; - profile.setSecretEnabled(enabled); - mSecretHandler.getPreference().setEnabled(enabled); - setSecretSummary(mSecret, enabled); - return true; - } - }); - return secret; - } - - private Preference createSecretStringPreference(Context c) { - SecretHandler sHandler = mSecretHandler = new SecretHandler(c, - R.string.vpn_l2tp_secret_string_title, - R.string.vpn_l2tp_secret) { - @Override - protected String getSecretFromProfile() { - return ((L2tpProfile) getProfile()).getSecretString(); - } - - @Override - protected void saveSecretToProfile(String secret) { - ((L2tpProfile) getProfile()).setSecretString(secret); - } - }; - Preference pref = sHandler.getPreference(); - pref.setEnabled(mSecret.isChecked()); - return pref; - } - - private void setSecretSummary(CheckBoxPreference secret, boolean enabled) { - Context c = secret.getContext(); - String formatString = c.getString(enabled - ? R.string.vpn_is_enabled - : R.string.vpn_is_disabled); - secret.setSummary(String.format( - formatString, c.getString(R.string.vpn_l2tp_secret))); - } -} diff --git a/src/com/android/settings/vpn/L2tpIpsecEditor.java b/src/com/android/settings/vpn/L2tpIpsecEditor.java deleted file mode 100644 index 276ee2f..0000000 --- a/src/com/android/settings/vpn/L2tpIpsecEditor.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (C) 2009 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.vpn; - -import com.android.settings.R; - -import android.content.Context; -import android.net.vpn.L2tpIpsecProfile; -import android.preference.EditTextPreference; -import android.preference.ListPreference; -import android.preference.Preference; -import android.preference.PreferenceGroup; -import android.security.Credentials; -import android.security.KeyStore; -import android.text.TextUtils; - -/** - * The class for editing {@link L2tpIpsecProfile}. - */ -class L2tpIpsecEditor extends L2tpEditor { - private static final String TAG = L2tpIpsecEditor.class.getSimpleName(); - - private KeyStore mKeyStore = KeyStore.getInstance(); - - private ListPreference mUserCertificate; - private ListPreference mCaCertificate; - - private L2tpIpsecProfile mProfile; - - public L2tpIpsecEditor(L2tpIpsecProfile p) { - super(p); - mProfile = p; - } - - @Override - protected void loadExtraPreferencesTo(PreferenceGroup subpanel) { - super.loadExtraPreferencesTo(subpanel); - Context c = subpanel.getContext(); - subpanel.addPreference(createUserCertificatePreference(c)); - subpanel.addPreference(createCaCertificatePreference(c)); - } - - @Override - public String validate() { - String result = super.validate(); - if (result == null) { - result = validate(mUserCertificate, R.string.vpn_a_user_certificate); - } - if (result == null) { - result = validate(mCaCertificate, R.string.vpn_a_ca_certificate); - } - return result; - } - - private Preference createUserCertificatePreference(Context c) { - mUserCertificate = createListPreference(c, - R.string.vpn_user_certificate_title, - mProfile.getUserCertificate(), - mKeyStore.saw(Credentials.USER_CERTIFICATE), - new Preference.OnPreferenceChangeListener() { - public boolean onPreferenceChange( - Preference pref, Object newValue) { - mProfile.setUserCertificate((String) newValue); - setSummary(pref, R.string.vpn_user_certificate, - (String) newValue); - return true; - } - }); - setSummary(mUserCertificate, R.string.vpn_user_certificate, - mProfile.getUserCertificate()); - return mUserCertificate; - } - - private Preference createCaCertificatePreference(Context c) { - mCaCertificate = createListPreference(c, - R.string.vpn_ca_certificate_title, - mProfile.getCaCertificate(), - mKeyStore.saw(Credentials.CA_CERTIFICATE), - new Preference.OnPreferenceChangeListener() { - public boolean onPreferenceChange( - Preference pref, Object newValue) { - mProfile.setCaCertificate((String) newValue); - setSummary(pref, R.string.vpn_ca_certificate, - (String) newValue); - return true; - } - }); - setSummary(mCaCertificate, R.string.vpn_ca_certificate, - mProfile.getCaCertificate()); - return mCaCertificate; - } - - private ListPreference createListPreference(Context c, int titleResId, - String text, String[] keys, - Preference.OnPreferenceChangeListener listener) { - ListPreference pref = new ListPreference(c); - pref.setTitle(titleResId); - pref.setDialogTitle(titleResId); - pref.setPersistent(true); - pref.setEntries(keys); - pref.setEntryValues(keys); - pref.setValue(text); - pref.setOnPreferenceChangeListener(listener); - return pref; - } -} diff --git a/src/com/android/settings/vpn/L2tpIpsecPskEditor.java b/src/com/android/settings/vpn/L2tpIpsecPskEditor.java deleted file mode 100644 index 1277c28..0000000 --- a/src/com/android/settings/vpn/L2tpIpsecPskEditor.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2009 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.vpn; - -import com.android.settings.R; - -import android.content.Context; -import android.net.vpn.L2tpIpsecPskProfile; -import android.preference.EditTextPreference; -import android.preference.Preference; -import android.preference.PreferenceGroup; - -/** - * The class for editing {@link L2tpIpsecPskProfile}. - */ -class L2tpIpsecPskEditor extends L2tpEditor { - private EditTextPreference mPresharedKey; - private SecretHandler mPskHandler; - - public L2tpIpsecPskEditor(L2tpIpsecPskProfile p) { - super(p); - } - - @Override - protected void loadExtraPreferencesTo(PreferenceGroup subpanel) { - Context c = subpanel.getContext(); - subpanel.addPreference(createPresharedKeyPreference(c)); - super.loadExtraPreferencesTo(subpanel); - } - - @Override - public String validate() { - String result = super.validate(); - - return ((result != null) ? result : mPskHandler.validate()); - } - - private Preference createPresharedKeyPreference(Context c) { - SecretHandler pskHandler = mPskHandler = new SecretHandler(c, - R.string.vpn_ipsec_presharedkey_title, - R.string.vpn_ipsec_presharedkey) { - @Override - protected String getSecretFromProfile() { - return ((L2tpIpsecPskProfile) getProfile()).getPresharedKey(); - } - - @Override - protected void saveSecretToProfile(String secret) { - ((L2tpIpsecPskProfile) getProfile()).setPresharedKey(secret); - } - }; - return pskHandler.getPreference(); - } -} diff --git a/src/com/android/settings/vpn/PptpEditor.java b/src/com/android/settings/vpn/PptpEditor.java deleted file mode 100644 index cfb3fa3..0000000 --- a/src/com/android/settings/vpn/PptpEditor.java +++ /dev/null @@ -1,73 +0,0 @@ -/* * Copyright (C) 2009 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.vpn; - -import com.android.settings.R; - -import android.content.Context; -import android.net.vpn.PptpProfile; -import android.preference.CheckBoxPreference; -import android.preference.Preference; -import android.preference.PreferenceGroup; - -/** - * The class for editing {@link PptpProfile}. - */ -class PptpEditor extends VpnProfileEditor { - private CheckBoxPreference mEncryption; - - public PptpEditor(PptpProfile p) { - super(p); - } - - @Override - protected void loadExtraPreferencesTo(PreferenceGroup subpanel) { - Context c = subpanel.getContext(); - subpanel.addPreference(createEncryptionPreference(c)); - - PptpProfile profile = (PptpProfile) getProfile(); - } - - private Preference createEncryptionPreference(Context c) { - final PptpProfile profile = (PptpProfile) getProfile(); - CheckBoxPreference encryption = mEncryption = new CheckBoxPreference(c); - boolean enabled = profile.isEncryptionEnabled(); - setCheckBoxTitle(encryption, R.string.vpn_pptp_encryption_title); - encryption.setChecked(enabled); - setEncryptionSummary(encryption, enabled); - encryption.setOnPreferenceChangeListener( - new Preference.OnPreferenceChangeListener() { - public boolean onPreferenceChange( - Preference pref, Object newValue) { - boolean enabled = (Boolean) newValue; - profile.setEncryptionEnabled(enabled); - setEncryptionSummary(mEncryption, enabled); - return true; - } - }); - return encryption; - } - - private void setEncryptionSummary(CheckBoxPreference encryption, - boolean enabled) { - Context c = encryption.getContext(); - String formatString = c.getString(enabled - ? R.string.vpn_is_enabled - : R.string.vpn_is_disabled); - encryption.setSummary(String.format( - formatString, c.getString(R.string.vpn_pptp_encryption))); - } -} diff --git a/src/com/android/settings/vpn/Util.java b/src/com/android/settings/vpn/Util.java deleted file mode 100644 index a37049d..0000000 --- a/src/com/android/settings/vpn/Util.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (C) 2009 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.vpn; - -import com.android.settings.R; - -import android.app.AlertDialog; -import android.content.Context; -import android.content.DialogInterface; -import android.widget.Toast; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -class Util { - - static void showShortToastMessage(Context context, String message) { - Toast.makeText(context, message, Toast.LENGTH_SHORT).show(); - } - - static void showShortToastMessage(Context context, int messageId) { - Toast.makeText(context, messageId, Toast.LENGTH_SHORT).show(); - } - - static void showLongToastMessage(Context context, String message) { - Toast.makeText(context, message, Toast.LENGTH_LONG).show(); - } - - static void showLongToastMessage(Context context, int messageId) { - Toast.makeText(context, messageId, Toast.LENGTH_LONG).show(); - } - - static void showErrorMessage(Context c, String message) { - createErrorDialog(c, message, null).show(); - } - - static void showErrorMessage(Context c, String message, - DialogInterface.OnClickListener listener) { - createErrorDialog(c, message, listener).show(); - } - - static void deleteFile(String path) { - deleteFile(new File(path)); - } - - static void deleteFile(String path, boolean toDeleteSelf) { - deleteFile(new File(path), toDeleteSelf); - } - - static void deleteFile(File f) { - deleteFile(f, true); - } - - static void deleteFile(File f, boolean toDeleteSelf) { - if (f.isDirectory()) { - for (File child : f.listFiles()) deleteFile(child, true); - } - if (toDeleteSelf) f.delete(); - } - - static boolean isFileOrEmptyDirectory(String path) { - File f = new File(path); - if (!f.isDirectory()) return true; - - String[] list = f.list(); - return ((list == null) || (list.length == 0)); - } - - static boolean copyFiles(String sourcePath , String targetPath) - throws IOException { - return copyFiles(new File(sourcePath), new File(targetPath)); - } - - // returns false if sourceLocation is the same as the targetLocation - static boolean copyFiles(File sourceLocation , File targetLocation) - throws IOException { - if (sourceLocation.equals(targetLocation)) return false; - - if (sourceLocation.isDirectory()) { - if (!targetLocation.exists()) { - targetLocation.mkdir(); - } - String[] children = sourceLocation.list(); - for (int i=0; i<children.length; i++) { - copyFiles(new File(sourceLocation, children[i]), - new File(targetLocation, children[i])); - } - } else if (sourceLocation.exists()) { - InputStream in = new FileInputStream(sourceLocation); - OutputStream out = new FileOutputStream(targetLocation); - - // Copy the bits from instream to outstream - byte[] buf = new byte[1024]; - int len; - while ((len = in.read(buf)) > 0) { - out.write(buf, 0, len); - } - in.close(); - out.close(); - } - return true; - } - - private static AlertDialog createErrorDialog(Context c, String message, - DialogInterface.OnClickListener okListener) { - AlertDialog.Builder b = new AlertDialog.Builder(c) - .setTitle(android.R.string.dialog_alert_title) - .setIcon(android.R.drawable.ic_dialog_alert) - .setMessage(message); - if (okListener != null) { - b.setPositiveButton(R.string.vpn_back_button, okListener); - } else { - b.setPositiveButton(android.R.string.ok, null); - } - return b.create(); - } - - private Util() { - } -} diff --git a/src/com/android/settings/vpn/VpnEditor.java b/src/com/android/settings/vpn/VpnEditor.java deleted file mode 100644 index d362793..0000000 --- a/src/com/android/settings/vpn/VpnEditor.java +++ /dev/null @@ -1,261 +0,0 @@ -/* - * Copyright (C) 2009 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.vpn; - -import com.android.settings.R; -import com.android.settings.SettingsPreferenceFragment; - -import android.app.Activity; -import android.app.AlertDialog; -import android.app.Dialog; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.net.vpn.L2tpIpsecProfile; -import android.net.vpn.L2tpIpsecPskProfile; -import android.net.vpn.L2tpProfile; -import android.net.vpn.PptpProfile; -import android.net.vpn.VpnManager; -import android.net.vpn.VpnProfile; -import android.os.Bundle; -import android.os.Parcel; -import android.os.Parcelable; -import android.preference.PreferenceActivity; -import android.text.TextUtils; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.widget.Toast; - -/** - * The activity class for editing a new or existing VPN profile. - */ -public class VpnEditor extends SettingsPreferenceFragment { - private static final int MENU_SAVE = Menu.FIRST; - private static final int MENU_CANCEL = Menu.FIRST + 1; - private static final int CONFIRM_DIALOG_ID = 0; - private static final String KEY_PROFILE = "profile"; - private static final String KEY_ORIGINAL_PROFILE_NAME = "orig_profile_name"; - - private VpnManager mVpnManager; - private VpnProfileEditor mProfileEditor; - private boolean mAddingProfile; - private byte[] mOriginalProfileData; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - // Loads the XML preferences file - addPreferencesFromResource(R.xml.vpn_edit); - } - - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - mVpnManager = new VpnManager(getActivity()); - - VpnProfile p; - if (savedInstanceState != null) { - p = (VpnProfile)savedInstanceState.getParcelable(KEY_PROFILE); - } else { - p = (VpnProfile)getArguments().getParcelable(VpnSettings.KEY_VPN_PROFILE); - if (p == null) { - p = getActivity().getIntent().getParcelableExtra(VpnSettings.KEY_VPN_PROFILE); - } - } - - Parcel parcel = Parcel.obtain(); - p.writeToParcel(parcel, 0); - mOriginalProfileData = parcel.marshall(); - parcel.setDataPosition(0); - VpnProfile profile = (VpnProfile) VpnProfile.CREATOR.createFromParcel(parcel); - - mProfileEditor = getEditor(profile); - mAddingProfile = TextUtils.isEmpty(profile.getName()); - - initViewFor(profile); - - registerForContextMenu(getListView()); - setHasOptionsMenu(true); - } - - @Override - public synchronized void onSaveInstanceState(Bundle outState) { - if (mProfileEditor == null) return; - - outState.putParcelable(KEY_PROFILE, getProfile()); - } - - @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - super.onCreateOptionsMenu(menu, inflater); - menu.add(0, MENU_SAVE, 0, R.string.vpn_menu_done) - .setIcon(android.R.drawable.ic_menu_save); - menu.add(0, MENU_CANCEL, 0, - mAddingProfile ? R.string.vpn_menu_cancel - : R.string.vpn_menu_revert) - .setIcon(android.R.drawable.ic_menu_close_clear_cancel); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case MENU_SAVE: - Intent resultIntent = validateAndGetResult(); - if (resultIntent != null) { - PreferenceActivity activity = - (PreferenceActivity) getActivity(); - if (!mVpnManager.isIdle()) { - Toast.makeText(activity, R.string.service_busy, - Toast.LENGTH_SHORT).show(); - } else { - activity.finishPreferencePanel(this, - Activity.RESULT_OK, resultIntent); - } - } - return true; - - case MENU_CANCEL: - if (profileChanged()) { - showDialog(CONFIRM_DIALOG_ID); - } else { - finishFragment(); - } - return true; - } - return super.onOptionsItemSelected(item); - } - - /* - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - switch (keyCode) { - case KeyEvent.KEYCODE_BACK: - if (validateAndSetResult()) finish(); - return true; - } - return super.onKeyDown(keyCode, event); - }*/ - - private void initViewFor(VpnProfile profile) { - mProfileEditor.loadPreferencesTo(getPreferenceScreen()); - } - - /* package */static String getTitle(Context context, VpnProfile profile, boolean adding) { - String formatString = adding - ? context.getString(R.string.vpn_edit_title_add) - : context.getString(R.string.vpn_edit_title_edit); - return String.format(formatString, - profile.getType().getDisplayName()); - } - - /** - * Checks the validity of the inputs and set the profile as result if valid. - * @return true if the result is successfully set - */ - private Intent validateAndGetResult() { - String errorMsg = mProfileEditor.validate(); - - if (errorMsg != null) { - Util.showErrorMessage(getActivity(), errorMsg); - return null; - } - - if (profileChanged()) { - return getResult(getProfile()); - } - return null; - } - - private Intent getResult(VpnProfile p) { - Intent intent = new Intent(getActivity(), VpnSettings.class); - intent.putExtra(VpnSettings.KEY_VPN_PROFILE, (Parcelable) p); - return intent; - } - - private VpnProfileEditor getEditor(VpnProfile p) { - switch (p.getType()) { - case L2TP_IPSEC: - return new L2tpIpsecEditor((L2tpIpsecProfile) p); - - case L2TP_IPSEC_PSK: - return new L2tpIpsecPskEditor((L2tpIpsecPskProfile) p); - - case L2TP: - return new L2tpEditor((L2tpProfile) p); - - case PPTP: - return new PptpEditor((PptpProfile) p); - - default: - return new VpnProfileEditor(p); - } - } - - - @Override - public Dialog onCreateDialog(int id) { - if (id == CONFIRM_DIALOG_ID) { - return new AlertDialog.Builder(getActivity()) - .setTitle(android.R.string.dialog_alert_title) - .setIcon(android.R.drawable.ic_dialog_alert) - .setMessage(mAddingProfile - ? R.string.vpn_confirm_add_profile_cancellation - : R.string.vpn_confirm_edit_profile_cancellation) - .setPositiveButton(R.string.vpn_yes_button, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int w) { - finishFragment(); - } - }) - .setNegativeButton(R.string.vpn_mistake_button, null) - .create(); - } - - return super.onCreateDialog(id); - } - - /* - @Override - public void onPrepareDialog(int id, Dialog dialog) { - super.onPrepareDialog(id, dialog); - - if (id == CONFIRM_DIALOG_ID) { - ((AlertDialog)dialog).setMessage(mAddingProfile - ? getString(R.string.vpn_confirm_add_profile_cancellation) - : getString(R.string.vpn_confirm_edit_profile_cancellation)); - } - }*/ - - private VpnProfile getProfile() { - return mProfileEditor.getProfile(); - } - - private boolean profileChanged() { - Parcel newParcel = Parcel.obtain(); - getProfile().writeToParcel(newParcel, 0); - byte[] newData = newParcel.marshall(); - if (mOriginalProfileData.length == newData.length) { - for (int i = 0, n = mOriginalProfileData.length; i < n; i++) { - if (mOriginalProfileData[i] != newData[i]) return true; - } - return false; - } - return true; - } -} diff --git a/src/com/android/settings/vpn/VpnProfileActor.java b/src/com/android/settings/vpn/VpnProfileActor.java deleted file mode 100644 index 4c7b014..0000000 --- a/src/com/android/settings/vpn/VpnProfileActor.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2009 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.vpn; - -import android.app.Dialog; -import android.net.vpn.VpnProfile; -import android.view.View; - -/** - * The interface to act on a {@link VpnProfile}. - */ -public interface VpnProfileActor { - VpnProfile getProfile(); - - /** - * Returns true if a connect dialog is needed before establishing a - * connection. - */ - boolean isConnectDialogNeeded(); - - /** - * Creates the view in the connect dialog. - */ - View createConnectView(); - - /** - * Validates the inputs in the dialog. - * @param dialog the connect dialog - * @return an error message if the inputs are not valid - */ - String validateInputs(Dialog dialog); - - /** - * Establishes a VPN connection. - * @param dialog the connect dialog - */ - void connect(Dialog dialog); - - /** - * Tears down the connection. - */ - void disconnect(); -} diff --git a/src/com/android/settings/vpn/VpnProfileEditor.java b/src/com/android/settings/vpn/VpnProfileEditor.java deleted file mode 100644 index 100b78e..0000000 --- a/src/com/android/settings/vpn/VpnProfileEditor.java +++ /dev/null @@ -1,254 +0,0 @@ -/* - * Copyright (C) 2009 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.vpn; - -import com.android.settings.R; - -import android.content.Context; -import android.net.vpn.VpnProfile; -import android.preference.CheckBoxPreference; -import android.preference.EditTextPreference; -import android.preference.ListPreference; -import android.preference.Preference; -import android.preference.PreferenceGroup; -import android.text.InputType; -import android.text.TextUtils; -import android.text.method.PasswordTransformationMethod; - -/** - * The common class for editing {@link VpnProfile}. - */ -class VpnProfileEditor { - private static final String KEY_VPN_NAME = "vpn_name"; - - private EditTextPreference mName; - private EditTextPreference mServerName; - private EditTextPreference mDomainSuffices; - private VpnProfile mProfile; - - public VpnProfileEditor(VpnProfile p) { - mProfile = p; - } - - //@Override - public VpnProfile getProfile() { - return mProfile; - } - - /** - * Adds the preferences to the panel. Subclasses should override - * {@link #loadExtraPreferencesTo(PreferenceGroup)} instead of this method. - */ - public void loadPreferencesTo(PreferenceGroup subpanel) { - Context c = subpanel.getContext(); - - mName = (EditTextPreference) subpanel.findPreference(KEY_VPN_NAME); - mName.setOnPreferenceChangeListener( - new Preference.OnPreferenceChangeListener() { - public boolean onPreferenceChange( - Preference pref, Object newValue) { - setName((String) newValue); - return true; - } - }); - setName(getProfile().getName()); - mName.getEditText().setInputType(InputType.TYPE_CLASS_TEXT - | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES); - - subpanel.addPreference(createServerNamePreference(c)); - loadExtraPreferencesTo(subpanel); - subpanel.addPreference(createDomainSufficesPreference(c)); - } - - /** - * Adds the extra preferences to the panel. Subclasses should add - * additional preferences in this method. - */ - protected void loadExtraPreferencesTo(PreferenceGroup subpanel) { - } - - /** - * Validates the inputs in the preferences. - * - * @return an error message that is ready to be displayed in a dialog; or - * null if all the inputs are valid - */ - public String validate() { - String result = validate(mName, R.string.vpn_a_name); - return ((result != null) - ? result - : validate(mServerName, R.string.vpn_a_vpn_server)); - } - - /** - * Creates a preference for users to input domain suffices. - */ - protected EditTextPreference createDomainSufficesPreference(Context c) { - EditTextPreference pref = mDomainSuffices = createEditTextPreference(c, - R.string.vpn_dns_search_list_title, - R.string.vpn_dns_search_list, - mProfile.getDomainSuffices(), - new Preference.OnPreferenceChangeListener() { - public boolean onPreferenceChange( - Preference pref, Object newValue) { - String v = ((String) newValue).trim(); - mProfile.setDomainSuffices(v); - setSummary(pref, R.string.vpn_dns_search_list, v, false); - return true; - } - }); - pref.getEditText().setInputType(InputType.TYPE_TEXT_VARIATION_URI); - return pref; - } - - private Preference createServerNamePreference(Context c) { - EditTextPreference pref = mServerName = createEditTextPreference(c, - R.string.vpn_vpn_server_title, - R.string.vpn_vpn_server, - mProfile.getServerName(), - new Preference.OnPreferenceChangeListener() { - public boolean onPreferenceChange( - Preference pref, Object newValue) { - String v = ((String) newValue).trim(); - mProfile.setServerName(v); - setSummary(pref, R.string.vpn_vpn_server, v); - return true; - } - }); - pref.getEditText().setInputType(InputType.TYPE_TEXT_VARIATION_URI); - return pref; - } - - protected EditTextPreference createEditTextPreference(Context c, int titleId, - int prefNameId, String value, - Preference.OnPreferenceChangeListener listener) { - EditTextPreference pref = new EditTextPreference(c); - pref.setTitle(titleId); - pref.setDialogTitle(titleId); - setSummary(pref, prefNameId, value); - pref.setText(value); - pref.setPersistent(true); - pref.setOnPreferenceChangeListener(listener); - return pref; - } - - protected String validate(Preference pref, int fieldNameId) { - Context c = pref.getContext(); - String value = (pref instanceof EditTextPreference) - ? ((EditTextPreference) pref).getText() - : ((ListPreference) pref).getValue(); - String formatString = (pref instanceof EditTextPreference) - ? c.getString(R.string.vpn_error_miss_entering) - : c.getString(R.string.vpn_error_miss_selecting); - return (TextUtils.isEmpty(value) - ? String.format(formatString, c.getString(fieldNameId)) - : null); - } - - protected void setSummary(Preference pref, int fieldNameId, String v) { - setSummary(pref, fieldNameId, v, true); - } - - protected void setSummary(Preference pref, int fieldNameId, String v, - boolean required) { - Context c = pref.getContext(); - String formatString = required - ? c.getString(R.string.vpn_field_not_set) - : c.getString(R.string.vpn_field_not_set_optional); - pref.setSummary(TextUtils.isEmpty(v) - ? String.format(formatString, c.getString(fieldNameId)) - : v); - } - - protected void setCheckBoxTitle(CheckBoxPreference pref, int fieldNameId) { - Context c = pref.getContext(); - String formatString = c.getString(R.string.vpn_enable_field); - pref.setTitle(String.format(formatString, c.getString(fieldNameId))); - } - - private void setName(String newName) { - newName = (newName == null) ? "" : newName.trim(); - mName.setText(newName); - getProfile().setName(newName); - setSummary(mName, R.string.vpn_name, newName); - } - - // Secret is tricky to handle because empty field may mean "not set" or - // "unchanged". This class hides that logic from callers. - protected static abstract class SecretHandler { - private EditTextPreference mPref; - private int mFieldNameId; - private boolean mHadSecret; - - protected SecretHandler(Context c, int titleId, int fieldNameId) { - String value = getSecretFromProfile(); - mHadSecret = !TextUtils.isEmpty(value); - mFieldNameId = fieldNameId; - - EditTextPreference pref = mPref = new EditTextPreference(c); - pref.setTitle(titleId); - pref.setDialogTitle(titleId); - pref.getEditText().setInputType( - InputType.TYPE_TEXT_VARIATION_PASSWORD); - pref.getEditText().setTransformationMethod( - new PasswordTransformationMethod()); - pref.setText(""); - pref.getEditText().setHint(mHadSecret - ? R.string.vpn_secret_unchanged - : R.string.vpn_secret_not_set); - setSecretSummary(value); - pref.setPersistent(true); - saveSecretToProfile(""); - pref.setOnPreferenceChangeListener( - new Preference.OnPreferenceChangeListener() { - public boolean onPreferenceChange( - Preference pref, Object newValue) { - saveSecretToProfile((String) newValue); - setSecretSummary((String) newValue); - return true; - } - }); - } - - protected EditTextPreference getPreference() { - return mPref; - } - - protected String validate() { - Context c = mPref.getContext(); - String value = mPref.getText(); - return ((TextUtils.isEmpty(value) && !mHadSecret) - ? String.format( - c.getString(R.string.vpn_error_miss_entering), - c.getString(mFieldNameId)) - : null); - } - - private void setSecretSummary(String value) { - EditTextPreference pref = mPref; - Context c = pref.getContext(); - String formatString = (TextUtils.isEmpty(value) && !mHadSecret) - ? c.getString(R.string.vpn_field_not_set) - : c.getString(R.string.vpn_field_is_set); - pref.setSummary( - String.format(formatString, c.getString(mFieldNameId))); - } - - protected abstract String getSecretFromProfile(); - protected abstract void saveSecretToProfile(String secret); - } -} diff --git a/src/com/android/settings/vpn/VpnSettings.java b/src/com/android/settings/vpn/VpnSettings.java deleted file mode 100644 index 9b96761..0000000 --- a/src/com/android/settings/vpn/VpnSettings.java +++ /dev/null @@ -1,1109 +0,0 @@ -/* - * Copyright (C) 2009 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.vpn; - -import com.android.settings.R; -import com.android.settings.SettingsPreferenceFragment; - -import android.app.Activity; -import android.app.AlertDialog; -import android.app.Dialog; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.net.vpn.L2tpIpsecProfile; -import android.net.vpn.L2tpIpsecPskProfile; -import android.net.vpn.L2tpProfile; -import android.net.vpn.VpnManager; -import android.net.vpn.VpnProfile; -import android.net.vpn.VpnState; -import android.net.vpn.VpnType; -import android.os.Bundle; -import android.preference.Preference; -import android.preference.PreferenceActivity; -import android.preference.PreferenceCategory; -import android.preference.PreferenceScreen; -import android.preference.Preference.OnPreferenceClickListener; -import android.security.Credentials; -import android.security.KeyStore; -import android.text.TextUtils; -import android.util.Log; -import android.view.ContextMenu; -import android.view.MenuItem; -import android.view.View; -import android.view.ContextMenu.ContextMenuInfo; -import android.widget.AdapterView.AdapterContextMenuInfo; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.nio.charset.Charsets; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -/** - * The preference activity for configuring VPN settings. - */ -public class VpnSettings extends SettingsPreferenceFragment - implements DialogInterface.OnClickListener { - - private static final boolean DEBUG = false; - - // Key to the field exchanged for profile editing. - static final String KEY_VPN_PROFILE = "vpn_profile"; - - // Key to the field exchanged for VPN type selection. - static final String KEY_VPN_TYPE = "vpn_type"; - - private static final String TAG = VpnSettings.class.getSimpleName(); - - private static final String PREF_ADD_VPN = "add_new_vpn"; - private static final String PREF_VPN_LIST = "vpn_list"; - - private static final String PROFILES_ROOT = VpnManager.getProfilePath() + "/"; - private static final String PROFILE_OBJ_FILE = ".pobj"; - - private static final String KEY_ACTIVE_PROFILE = "ActiveProfile"; - private static final String KEY_PROFILE_CONNECTING = "ProfileConnecting"; - private static final String KEY_CONNECT_DIALOG_SHOWING = "ConnectDialogShowing"; - - private static final int REQUEST_ADD_OR_EDIT_PROFILE = 1; - static final int REQUEST_SELECT_VPN_TYPE = 2; - - private static final int CONTEXT_MENU_CONNECT_ID = ContextMenu.FIRST + 0; - private static final int CONTEXT_MENU_DISCONNECT_ID = ContextMenu.FIRST + 1; - private static final int CONTEXT_MENU_EDIT_ID = ContextMenu.FIRST + 2; - private static final int CONTEXT_MENU_DELETE_ID = ContextMenu.FIRST + 3; - - private static final int CONNECT_BUTTON = DialogInterface.BUTTON_POSITIVE; - private static final int OK_BUTTON = DialogInterface.BUTTON_POSITIVE; - - private static final int DIALOG_CONNECT = VpnManager.VPN_ERROR_LARGEST + 1; - private static final int DIALOG_SECRET_NOT_SET = DIALOG_CONNECT + 1; - - private static final int NO_ERROR = VpnManager.VPN_ERROR_NO_ERROR; - - private static final String KEY_PREFIX_IPSEC_PSK = Credentials.VPN + 'i'; - private static final String KEY_PREFIX_L2TP_SECRET = Credentials.VPN + 'l'; - - private static List<VpnProfile> sVpnProfileList = new ArrayList<VpnProfile>(); - - private PreferenceScreen mAddVpn; - private PreferenceCategory mVpnListContainer; - - // profile name --> VpnPreference - private Map<String, VpnPreference> mVpnPreferenceMap; - - // profile engaged in a connection - private VpnProfile mActiveProfile; - - // actor engaged in connecting - private VpnProfileActor mConnectingActor; - - // states saved for unlocking keystore - private Runnable mUnlockAction; - - private KeyStore mKeyStore = KeyStore.getInstance(); - - private VpnManager mVpnManager; - - private ConnectivityReceiver mConnectivityReceiver = - new ConnectivityReceiver(); - - private int mConnectingErrorCode = NO_ERROR; - - private Dialog mShowingDialog; - - private boolean mConnectDialogShowing = false; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - addPreferencesFromResource(R.xml.vpn_settings); - - mVpnManager = new VpnManager(getActivity()); - // restore VpnProfile list and construct VpnPreference map - mVpnListContainer = (PreferenceCategory) findPreference(PREF_VPN_LIST); - - // set up the "add vpn" preference - mAddVpn = (PreferenceScreen) findPreference(PREF_ADD_VPN); - mAddVpn.setOnPreferenceClickListener( - new OnPreferenceClickListener() { - public boolean onPreferenceClick(Preference preference) { - startVpnTypeSelection(); - return true; - } - }); - - retrieveVpnListFromStorage(); - restoreInstanceState(savedInstanceState); - } - - @Override - public void onSaveInstanceState(Bundle savedInstanceState) { - if (mActiveProfile != null) { - savedInstanceState.putString(KEY_ACTIVE_PROFILE, - mActiveProfile.getId()); - savedInstanceState.putBoolean(KEY_PROFILE_CONNECTING, - (mConnectingActor != null)); - savedInstanceState.putBoolean(KEY_CONNECT_DIALOG_SHOWING, - mConnectDialogShowing); - } - super.onSaveInstanceState(savedInstanceState); - } - - private void restoreInstanceState(Bundle savedInstanceState) { - if (savedInstanceState == null) return; - String profileId = savedInstanceState.getString(KEY_ACTIVE_PROFILE); - if (profileId != null) { - mActiveProfile = getProfile(getProfileIndexFromId(profileId)); - if (savedInstanceState.getBoolean(KEY_PROFILE_CONNECTING)) { - mConnectingActor = getActor(mActiveProfile); - } - mConnectDialogShowing = savedInstanceState.getBoolean(KEY_CONNECT_DIALOG_SHOWING); - } - } - - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - // for long-press gesture on a profile preference - registerForContextMenu(getListView()); - } - - @Override - public void onPause() { - // ignore vpn connectivity event - mVpnManager.unregisterConnectivityReceiver(mConnectivityReceiver); - if ((mShowingDialog != null) && mShowingDialog.isShowing()) { - mShowingDialog.dismiss(); - mShowingDialog = null; - } - super.onPause(); - } - - @Override - public void onResume() { - super.onResume(); - updatePreferenceMap(); - - if (DEBUG) Log.d(TAG, "onResume"); - - // listen to vpn connectivity event - mVpnManager.registerConnectivityReceiver(mConnectivityReceiver); - - if ((mUnlockAction != null) && isKeyStoreUnlocked()) { - Runnable action = mUnlockAction; - mUnlockAction = null; - getActivity().runOnUiThread(action); - } - - if (!mConnectDialogShowing) { - // If mActiveProfile is not null but it's in IDLE state, then a - // retry dialog must be showing now as the previous connection - // attempt failed. In this case, don't call checkVpnConnectionStatus() - // as it will clean up mActiveProfile due to the IDLE state. - if ((mActiveProfile == null) - || (mActiveProfile.getState() != VpnState.IDLE)) { - checkVpnConnectionStatus(); - } - } else { - // Dismiss the connect dialog in case there is another instance - // trying to operate a vpn connection. - if (!mVpnManager.isIdle() || (mActiveProfile == null)) { - removeDialog(DIALOG_CONNECT); - checkVpnConnectionStatus(); - } - } - } - - @Override - public void onDestroyView() { - unregisterForContextMenu(getListView()); - // This should be called after the procedure above as ListView inside this Fragment - // will be deleted here. - super.onDestroyView(); - } - - @Override - public void onDestroy() { - super.onDestroy(); - // Remove any onClick listeners - if (mVpnListContainer != null) { - for (int i = 0; i < mVpnListContainer.getPreferenceCount(); i++) { - mVpnListContainer.getPreference(i).setOnPreferenceClickListener(null); - } - } - } - - @Override - public Dialog onCreateDialog (int id) { - setOnCancelListener(new DialogInterface.OnCancelListener() { - public void onCancel(DialogInterface dialog) { - if (mActiveProfile != null) { - changeState(mActiveProfile, VpnState.IDLE); - } - // Make sure onIdle() is called as the above changeState() - // may not be effective if the state is already IDLE. - // XXX: VpnService should broadcast non-IDLE state, say UNUSABLE, - // when an error occurs. - onIdle(); - } - }); - - switch (id) { - case DIALOG_CONNECT: - mConnectDialogShowing = true; - setOnDismissListener(new DialogInterface.OnDismissListener() { - public void onDismiss(DialogInterface dialog) { - mConnectDialogShowing = false; - } - }); - return createConnectDialog(); - - case DIALOG_SECRET_NOT_SET: - return createSecretNotSetDialog(); - - case VpnManager.VPN_ERROR_CHALLENGE: - case VpnManager.VPN_ERROR_UNKNOWN_SERVER: - case VpnManager.VPN_ERROR_PPP_NEGOTIATION_FAILED: - return createEditDialog(id); - - default: - Log.d(TAG, "create reconnect dialog for event " + id); - return createReconnectDialog(id); - } - } - - private Dialog createConnectDialog() { - final Activity activity = getActivity(); - return new AlertDialog.Builder(activity) - .setView(mConnectingActor.createConnectView()) - .setTitle(String.format(activity.getString(R.string.vpn_connect_to), - mConnectingActor.getProfile().getName())) - .setPositiveButton(activity.getString(R.string.vpn_connect_button), - this) - .setNegativeButton(activity.getString(android.R.string.cancel), - this) - .create(); - } - - private Dialog createReconnectDialog(int id) { - int msgId; - switch (id) { - case VpnManager.VPN_ERROR_AUTH: - msgId = R.string.vpn_auth_error_dialog_msg; - break; - - case VpnManager.VPN_ERROR_REMOTE_HUNG_UP: - msgId = R.string.vpn_remote_hung_up_error_dialog_msg; - break; - - case VpnManager.VPN_ERROR_CONNECTION_LOST: - msgId = R.string.vpn_reconnect_from_lost; - break; - - case VpnManager.VPN_ERROR_REMOTE_PPP_HUNG_UP: - msgId = R.string.vpn_remote_ppp_hung_up_error_dialog_msg; - break; - - default: - msgId = R.string.vpn_confirm_reconnect; - } - return createCommonDialogBuilder().setMessage(msgId).create(); - } - - private Dialog createEditDialog(int id) { - int msgId; - switch (id) { - case VpnManager.VPN_ERROR_CHALLENGE: - msgId = R.string.vpn_challenge_error_dialog_msg; - break; - - case VpnManager.VPN_ERROR_UNKNOWN_SERVER: - msgId = R.string.vpn_unknown_server_dialog_msg; - break; - - case VpnManager.VPN_ERROR_PPP_NEGOTIATION_FAILED: - msgId = R.string.vpn_ppp_negotiation_failed_dialog_msg; - break; - - default: - return null; - } - return createCommonEditDialogBuilder().setMessage(msgId).create(); - } - - private Dialog createSecretNotSetDialog() { - return createCommonDialogBuilder() - .setMessage(R.string.vpn_secret_not_set_dialog_msg) - .setPositiveButton(R.string.vpn_yes_button, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int w) { - startVpnEditor(mActiveProfile, false); - } - }) - .create(); - } - - private AlertDialog.Builder createCommonEditDialogBuilder() { - return createCommonDialogBuilder() - .setPositiveButton(R.string.vpn_yes_button, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int w) { - VpnProfile p = mActiveProfile; - onIdle(); - startVpnEditor(p, false); - } - }); - } - - private AlertDialog.Builder createCommonDialogBuilder() { - return new AlertDialog.Builder(getActivity()) - .setTitle(android.R.string.dialog_alert_title) - .setIcon(android.R.drawable.ic_dialog_alert) - .setPositiveButton(R.string.vpn_yes_button, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int w) { - connectOrDisconnect(mActiveProfile); - } - }) - .setNegativeButton(R.string.vpn_no_button, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int w) { - onIdle(); - } - }); - } - - @Override - public void onCreateContextMenu(ContextMenu menu, View v, - ContextMenuInfo menuInfo) { - super.onCreateContextMenu(menu, v, menuInfo); - - VpnProfile p = getProfile(getProfilePositionFrom( - (AdapterContextMenuInfo) menuInfo)); - if (p != null) { - VpnState state = p.getState(); - menu.setHeaderTitle(p.getName()); - - boolean isIdle = (state == VpnState.IDLE); - boolean isNotConnect = (isIdle || (state == VpnState.DISCONNECTING) - || (state == VpnState.CANCELLED)); - menu.add(0, CONTEXT_MENU_CONNECT_ID, 0, R.string.vpn_menu_connect) - .setEnabled(isIdle && (mActiveProfile == null)); - menu.add(0, CONTEXT_MENU_DISCONNECT_ID, 0, - R.string.vpn_menu_disconnect) - .setEnabled(state == VpnState.CONNECTED); - menu.add(0, CONTEXT_MENU_EDIT_ID, 0, R.string.vpn_menu_edit) - .setEnabled(isNotConnect); - menu.add(0, CONTEXT_MENU_DELETE_ID, 0, R.string.vpn_menu_delete) - .setEnabled(isNotConnect); - } - } - - @Override - public boolean onContextItemSelected(MenuItem item) { - int position = getProfilePositionFrom( - (AdapterContextMenuInfo) item.getMenuInfo()); - VpnProfile p = getProfile(position); - - switch(item.getItemId()) { - case CONTEXT_MENU_CONNECT_ID: - case CONTEXT_MENU_DISCONNECT_ID: - connectOrDisconnect(p); - return true; - - case CONTEXT_MENU_EDIT_ID: - startVpnEditor(p, false); - return true; - - case CONTEXT_MENU_DELETE_ID: - deleteProfile(position); - return true; - } - - return super.onContextItemSelected(item); - } - - @Override - public void onActivityResult(final int requestCode, final int resultCode, - final Intent data) { - - if (DEBUG) Log.d(TAG, "onActivityResult , result = " + resultCode + ", data = " + data); - if ((resultCode == Activity.RESULT_CANCELED) || (data == null)) { - Log.d(TAG, "no result returned by editor"); - return; - } - - if (requestCode == REQUEST_SELECT_VPN_TYPE) { - final String typeName = data.getStringExtra(KEY_VPN_TYPE); - startVpnEditor(createVpnProfile(typeName), true); - } else if (requestCode == REQUEST_ADD_OR_EDIT_PROFILE) { - VpnProfile p = data.getParcelableExtra(KEY_VPN_PROFILE); - if (p == null) { - Log.e(TAG, "null object returned by editor"); - return; - } - - final Activity activity = getActivity(); - int index = getProfileIndexFromId(p.getId()); - if (checkDuplicateName(p, index)) { - final VpnProfile profile = p; - Util.showErrorMessage(activity, String.format( - activity.getString(R.string.vpn_error_duplicate_name), - p.getName()), - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int w) { - startVpnEditor(profile, false); - } - }); - return; - } - - if (needKeyStoreToSave(p)) { - Runnable action = new Runnable() { - public void run() { - onActivityResult(requestCode, resultCode, data); - } - }; - if (!unlockKeyStore(p, action)) return; - } - - try { - if (index < 0) { - addProfile(p); - Util.showShortToastMessage(activity, String.format( - activity.getString(R.string.vpn_profile_added), p.getName())); - } else { - replaceProfile(index, p); - Util.showShortToastMessage(activity, String.format( - activity.getString(R.string.vpn_profile_replaced), - p.getName())); - } - } catch (IOException e) { - final VpnProfile profile = p; - Util.showErrorMessage(activity, e + ": " + e.getMessage(), - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int w) { - startVpnEditor(profile, false); - } - }); - } - - // Remove cached VpnEditor as it is needless anymore. - } else { - throw new RuntimeException("unknown request code: " + requestCode); - } - } - - // Called when the buttons on the connect dialog are clicked. - @Override - public synchronized void onClick(DialogInterface dialog, int which) { - if (which == CONNECT_BUTTON) { - Dialog d = (Dialog) dialog; - String error = mConnectingActor.validateInputs(d); - if (error == null) { - mConnectingActor.connect(d); - } else { - // show error dialog - final Activity activity = getActivity(); - mShowingDialog = new AlertDialog.Builder(activity) - .setTitle(android.R.string.dialog_alert_title) - .setIcon(android.R.drawable.ic_dialog_alert) - .setMessage(String.format(activity.getString( - R.string.vpn_error_miss_entering), error)) - .setPositiveButton(R.string.vpn_back_button, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, - int which) { - showDialog(DIALOG_CONNECT); - } - }) - .create(); - // The profile state is "connecting". If we allow the dialog to - // be cancelable, then we need to clear the state in the - // onCancel handler. - mShowingDialog.setCancelable(false); - mShowingDialog.show(); - } - } else { - changeState(mActiveProfile, VpnState.IDLE); - } - } - - private int getProfileIndexFromId(String id) { - int index = 0; - for (VpnProfile p : sVpnProfileList) { - if (p.getId().equals(id)) { - return index; - } else { - index++; - } - } - return -1; - } - - // Replaces the profile at index in sVpnProfileList with p. - // Returns true if p's name is a duplicate. - private boolean checkDuplicateName(VpnProfile p, int index) { - List<VpnProfile> list = sVpnProfileList; - VpnPreference pref = mVpnPreferenceMap.get(p.getName()); - if ((pref != null) && (index >= 0) && (index < list.size())) { - // not a duplicate if p is to replace the profile at index - if (pref.mProfile == list.get(index)) pref = null; - } - return (pref != null); - } - - private int getProfilePositionFrom(AdapterContextMenuInfo menuInfo) { - // excludes mVpnListContainer and the preferences above it - return menuInfo.position - mVpnListContainer.getOrder() - 1; - } - - // position: position in sVpnProfileList - private VpnProfile getProfile(int position) { - return ((position >= 0) ? sVpnProfileList.get(position) : null); - } - - // position: position in sVpnProfileList - private void deleteProfile(final int position) { - if ((position < 0) || (position >= sVpnProfileList.size())) return; - final VpnProfile target = sVpnProfileList.get(position); - DialogInterface.OnClickListener onClickListener = - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - // Double check if the target is still the one we want - // to remove. - VpnProfile p = sVpnProfileList.get(position); - if (p != target) return; - if (which == OK_BUTTON) { - sVpnProfileList.remove(position); - VpnPreference pref = - mVpnPreferenceMap.remove(p.getName()); - mVpnListContainer.removePreference(pref); - removeProfileFromStorage(p); - } - } - }; - mShowingDialog = new AlertDialog.Builder(getActivity()) - .setTitle(android.R.string.dialog_alert_title) - .setIcon(android.R.drawable.ic_dialog_alert) - .setMessage(R.string.vpn_confirm_profile_deletion) - .setPositiveButton(android.R.string.ok, onClickListener) - .setNegativeButton(R.string.vpn_no_button, onClickListener) - .create(); - mShowingDialog.show(); - } - - // Randomly generates an ID for the profile. - // The ID is unique and only set once when the profile is created. - private void setProfileId(VpnProfile profile) { - String id; - - while (true) { - id = String.valueOf(Math.abs( - Double.doubleToLongBits(Math.random()))); - if (id.length() >= 8) break; - } - for (VpnProfile p : sVpnProfileList) { - if (p.getId().equals(id)) { - setProfileId(profile); - return; - } - } - profile.setId(id); - } - - private void addProfile(VpnProfile p) throws IOException { - setProfileId(p); - processSecrets(p); - saveProfileToStorage(p); - - sVpnProfileList.add(p); - addPreferenceFor(p, true); - disableProfilePreferencesIfOneActive(); - } - - // Adds a preference in mVpnListContainer - private VpnPreference addPreferenceFor( - VpnProfile p, boolean addToContainer) { - VpnPreference pref = new VpnPreference(getActivity(), p); - mVpnPreferenceMap.put(p.getName(), pref); - if (addToContainer) mVpnListContainer.addPreference(pref); - - pref.setOnPreferenceClickListener( - new Preference.OnPreferenceClickListener() { - public boolean onPreferenceClick(Preference pref) { - connectOrDisconnect(((VpnPreference) pref).mProfile); - return true; - } - }); - return pref; - } - - // index: index to sVpnProfileList - private void replaceProfile(int index, VpnProfile p) throws IOException { - Map<String, VpnPreference> map = mVpnPreferenceMap; - VpnProfile oldProfile = sVpnProfileList.set(index, p); - VpnPreference pref = map.remove(oldProfile.getName()); - if (pref.mProfile != oldProfile) { - throw new RuntimeException("inconsistent state!"); - } - - p.setId(oldProfile.getId()); - - processSecrets(p); - - // TODO: remove copyFiles once the setId() code propagates. - // Copy config files and remove the old ones if they are in different - // directories. - if (Util.copyFiles(getProfileDir(oldProfile), getProfileDir(p))) { - removeProfileFromStorage(oldProfile); - } - saveProfileToStorage(p); - - pref.setProfile(p); - map.put(p.getName(), pref); - } - - private void startVpnTypeSelection() { - if ((getActivity() == null) || isRemoving()) return; - - ((PreferenceActivity) getActivity()).startPreferencePanel( - VpnTypeSelection.class.getCanonicalName(), null, R.string.vpn_type_title, null, - this, REQUEST_SELECT_VPN_TYPE); - } - - private boolean isKeyStoreUnlocked() { - return mKeyStore.state() == KeyStore.State.UNLOCKED; - } - - // Returns true if the profile needs to access keystore - private boolean needKeyStoreToSave(VpnProfile p) { - switch (p.getType()) { - case L2TP_IPSEC_PSK: - L2tpIpsecPskProfile pskProfile = (L2tpIpsecPskProfile) p; - String presharedKey = pskProfile.getPresharedKey(); - if (!TextUtils.isEmpty(presharedKey)) return true; - // $FALL-THROUGH$ - case L2TP: - L2tpProfile l2tpProfile = (L2tpProfile) p; - if (l2tpProfile.isSecretEnabled() && - !TextUtils.isEmpty(l2tpProfile.getSecretString())) { - return true; - } - // $FALL-THROUGH$ - default: - return false; - } - } - - // Returns true if the profile needs to access keystore - private boolean needKeyStoreToConnect(VpnProfile p) { - switch (p.getType()) { - case L2TP_IPSEC: - case L2TP_IPSEC_PSK: - return true; - - case L2TP: - return ((L2tpProfile) p).isSecretEnabled(); - - default: - return false; - } - } - - // Returns true if keystore is unlocked or keystore is not a concern - private boolean unlockKeyStore(VpnProfile p, Runnable action) { - if (isKeyStoreUnlocked()) return true; - mUnlockAction = action; - Credentials.getInstance().unlock(getActivity()); - return false; - } - - private void startVpnEditor(final VpnProfile profile, boolean add) { - if ((getActivity() == null) || isRemoving()) return; - - Bundle args = new Bundle(); - args.putParcelable(KEY_VPN_PROFILE, profile); - // TODO: Show different titles for add and edit. - ((PreferenceActivity)getActivity()).startPreferencePanel( - VpnEditor.class.getCanonicalName(), args, - 0, VpnEditor.getTitle(getActivity(), profile, add), - this, REQUEST_ADD_OR_EDIT_PROFILE); - } - - private synchronized void connect(final VpnProfile p) { - if (needKeyStoreToConnect(p)) { - Runnable action = new Runnable() { - public void run() { - connect(p); - } - }; - if (!unlockKeyStore(p, action)) return; - } - - if (!checkSecrets(p)) return; - changeState(p, VpnState.CONNECTING); - if (mConnectingActor.isConnectDialogNeeded()) { - showDialog(DIALOG_CONNECT); - } else { - mConnectingActor.connect(null); - } - } - - // Do connect or disconnect based on the current state. - private synchronized void connectOrDisconnect(VpnProfile p) { - VpnPreference pref = mVpnPreferenceMap.get(p.getName()); - switch (p.getState()) { - case IDLE: - connect(p); - break; - - case CONNECTING: - // do nothing - break; - - case CONNECTED: - case DISCONNECTING: - changeState(p, VpnState.DISCONNECTING); - getActor(p).disconnect(); - break; - } - } - - private void changeState(VpnProfile p, VpnState state) { - VpnState oldState = p.getState(); - p.setState(state); - mVpnPreferenceMap.get(p.getName()).setSummary( - getProfileSummaryString(p)); - - switch (state) { - case CONNECTED: - mConnectingActor = null; - mActiveProfile = p; - disableProfilePreferencesIfOneActive(); - break; - - case CONNECTING: - if (mConnectingActor == null) { - mConnectingActor = getActor(p); - } - // $FALL-THROUGH$ - case DISCONNECTING: - mActiveProfile = p; - disableProfilePreferencesIfOneActive(); - break; - - case CANCELLED: - changeState(p, VpnState.IDLE); - break; - - case IDLE: - assert(mActiveProfile == p); - - if (mConnectingErrorCode == NO_ERROR) { - onIdle(); - } else { - showDialog(mConnectingErrorCode); - mConnectingErrorCode = NO_ERROR; - } - break; - } - } - - private void onIdle() { - if (DEBUG) Log.d(TAG, " onIdle()"); - mActiveProfile = null; - mConnectingActor = null; - enableProfilePreferences(); - } - - private void disableProfilePreferencesIfOneActive() { - if (mActiveProfile == null) return; - - for (VpnProfile p : sVpnProfileList) { - switch (p.getState()) { - case CONNECTING: - case DISCONNECTING: - case IDLE: - mVpnPreferenceMap.get(p.getName()).setEnabled(false); - break; - - default: - mVpnPreferenceMap.get(p.getName()).setEnabled(true); - } - } - } - - private void enableProfilePreferences() { - for (VpnProfile p : sVpnProfileList) { - mVpnPreferenceMap.get(p.getName()).setEnabled(true); - } - } - - static String getProfileDir(VpnProfile p) { - return PROFILES_ROOT + p.getId(); - } - - static void saveProfileToStorage(VpnProfile p) throws IOException { - File f = new File(getProfileDir(p)); - if (!f.exists()) f.mkdirs(); - ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream( - new File(f, PROFILE_OBJ_FILE))); - oos.writeObject(p); - oos.close(); - } - - private void removeProfileFromStorage(VpnProfile p) { - Util.deleteFile(getProfileDir(p)); - } - - private void updatePreferenceMap() { - mVpnPreferenceMap = new LinkedHashMap<String, VpnPreference>(); - mVpnListContainer.removeAll(); - for (VpnProfile p : sVpnProfileList) { - addPreferenceFor(p, true); - } - // reset the mActiveProfile if the profile has been removed from the - // other instance. - if ((mActiveProfile != null) - && !mVpnPreferenceMap.containsKey(mActiveProfile.getName())) { - onIdle(); - } - } - - private void retrieveVpnListFromStorage() { - // skip the loop if the profile is loaded already. - if (sVpnProfileList.size() > 0) return; - File root = new File(PROFILES_ROOT); - String[] dirs = root.list(); - if (dirs == null) return; - for (String dir : dirs) { - File f = new File(new File(root, dir), PROFILE_OBJ_FILE); - if (!f.exists()) continue; - try { - VpnProfile p = deserialize(f); - if (p == null) continue; - if (!checkIdConsistency(dir, p)) continue; - - sVpnProfileList.add(p); - } catch (IOException e) { - Log.e(TAG, "retrieveVpnListFromStorage()", e); - } - } - Collections.sort(sVpnProfileList, new Comparator<VpnProfile>() { - public int compare(VpnProfile p1, VpnProfile p2) { - return p1.getName().compareTo(p2.getName()); - } - }); - disableProfilePreferencesIfOneActive(); - } - - private void checkVpnConnectionStatus() { - for (VpnProfile p : sVpnProfileList) { - changeState(p, mVpnManager.getState(p)); - } - } - - // A sanity check. Returns true if the profile directory name and profile ID - // are consistent. - private boolean checkIdConsistency(String dirName, VpnProfile p) { - if (!dirName.equals(p.getId())) { - Log.d(TAG, "ID inconsistent: " + dirName + " vs " + p.getId()); - return false; - } else { - return true; - } - } - - private VpnProfile deserialize(File profileObjectFile) throws IOException { - try { - ObjectInputStream ois = new ObjectInputStream(new FileInputStream( - profileObjectFile)); - VpnProfile p = (VpnProfile) ois.readObject(); - ois.close(); - return p; - } catch (ClassNotFoundException e) { - Log.d(TAG, "deserialize a profile", e); - return null; - } - } - - private String getProfileSummaryString(VpnProfile p) { - final Activity activity = getActivity(); - switch (p.getState()) { - case CONNECTING: - return activity.getString(R.string.vpn_connecting); - case DISCONNECTING: - return activity.getString(R.string.vpn_disconnecting); - case CONNECTED: - return activity.getString(R.string.vpn_connected); - default: - return activity.getString(R.string.vpn_connect_hint); - } - } - - private VpnProfileActor getActor(VpnProfile p) { - return new AuthenticationActor(getActivity(), p); - } - - private VpnProfile createVpnProfile(String type) { - return mVpnManager.createVpnProfile(Enum.valueOf(VpnType.class, type)); - } - - private boolean checkSecrets(VpnProfile p) { - boolean secretMissing = false; - - if (p instanceof L2tpIpsecProfile) { - L2tpIpsecProfile certProfile = (L2tpIpsecProfile) p; - - String cert = certProfile.getCaCertificate(); - if (TextUtils.isEmpty(cert) || - !mKeyStore.contains(Credentials.CA_CERTIFICATE + cert)) { - certProfile.setCaCertificate(null); - secretMissing = true; - } - - cert = certProfile.getUserCertificate(); - if (TextUtils.isEmpty(cert) || - !mKeyStore.contains(Credentials.USER_CERTIFICATE + cert)) { - certProfile.setUserCertificate(null); - secretMissing = true; - } - } - - if (p instanceof L2tpIpsecPskProfile) { - L2tpIpsecPskProfile pskProfile = (L2tpIpsecPskProfile) p; - String presharedKey = pskProfile.getPresharedKey(); - String key = KEY_PREFIX_IPSEC_PSK + p.getId(); - if (TextUtils.isEmpty(presharedKey) || !mKeyStore.contains(key)) { - pskProfile.setPresharedKey(null); - secretMissing = true; - } - } - - if (p instanceof L2tpProfile) { - L2tpProfile l2tpProfile = (L2tpProfile) p; - if (l2tpProfile.isSecretEnabled()) { - String secret = l2tpProfile.getSecretString(); - String key = KEY_PREFIX_L2TP_SECRET + p.getId(); - if (TextUtils.isEmpty(secret) || !mKeyStore.contains(key)) { - l2tpProfile.setSecretString(null); - secretMissing = true; - } - } - } - - if (secretMissing) { - mActiveProfile = p; - showDialog(DIALOG_SECRET_NOT_SET); - return false; - } else { - return true; - } - } - - private void processSecrets(VpnProfile p) { - switch (p.getType()) { - case L2TP_IPSEC_PSK: - L2tpIpsecPskProfile pskProfile = (L2tpIpsecPskProfile) p; - String presharedKey = pskProfile.getPresharedKey(); - String key = KEY_PREFIX_IPSEC_PSK + p.getId(); - if (!TextUtils.isEmpty(presharedKey) && - !mKeyStore.put(key, presharedKey.getBytes(Charsets.UTF_8))) { - Log.e(TAG, "keystore write failed: key=" + key); - } - pskProfile.setPresharedKey(key); - // $FALL-THROUGH$ - case L2TP_IPSEC: - case L2TP: - L2tpProfile l2tpProfile = (L2tpProfile) p; - key = KEY_PREFIX_L2TP_SECRET + p.getId(); - if (l2tpProfile.isSecretEnabled()) { - String secret = l2tpProfile.getSecretString(); - if (!TextUtils.isEmpty(secret) && - !mKeyStore.put(key, secret.getBytes(Charsets.UTF_8))) { - Log.e(TAG, "keystore write failed: key=" + key); - } - l2tpProfile.setSecretString(key); - } else { - mKeyStore.delete(key); - } - break; - } - } - - private class VpnPreference extends Preference { - VpnProfile mProfile; - VpnPreference(Context c, VpnProfile p) { - super(c); - setProfile(p); - } - - void setProfile(VpnProfile p) { - mProfile = p; - setTitle(p.getName()); - setSummary(getProfileSummaryString(p)); - } - } - - // to receive vpn connectivity events broadcast by VpnService - private class ConnectivityReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - String profileName = intent.getStringExtra( - VpnManager.BROADCAST_PROFILE_NAME); - if (profileName == null) return; - - VpnState s = (VpnState) intent.getSerializableExtra( - VpnManager.BROADCAST_CONNECTION_STATE); - - if (s == null) { - Log.e(TAG, "received null connectivity state"); - return; - } - - mConnectingErrorCode = intent.getIntExtra( - VpnManager.BROADCAST_ERROR_CODE, NO_ERROR); - - VpnPreference pref = mVpnPreferenceMap.get(profileName); - if (pref != null) { - Log.d(TAG, "received connectivity: " + profileName - + ": connected? " + s - + " err=" + mConnectingErrorCode); - // XXX: VpnService should broadcast non-IDLE state, say UNUSABLE, - // when an error occurs. - changeState(pref.mProfile, s); - } else { - Log.e(TAG, "received connectivity: " + profileName - + ": connected? " + s + ", but profile does not exist;" - + " just ignore it"); - } - } - } -} diff --git a/src/com/android/settings/vpn/VpnTypeSelection.java b/src/com/android/settings/vpn/VpnTypeSelection.java deleted file mode 100644 index 45e33b9..0000000 --- a/src/com/android/settings/vpn/VpnTypeSelection.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2009 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.vpn; - -import com.android.settings.R; -import com.android.settings.SettingsPreferenceFragment; - -import android.app.Activity; -import android.content.Intent; -import android.net.vpn.VpnManager; -import android.net.vpn.VpnType; -import android.os.Bundle; -import android.preference.Preference; -import android.preference.PreferenceActivity; -import android.preference.PreferenceScreen; - -import java.util.HashMap; -import java.util.Map; - -/** - * The activity to select a VPN type. - */ -public class VpnTypeSelection extends SettingsPreferenceFragment { - private Map<String, VpnType> mTypeMap = new HashMap<String, VpnType>(); - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - addPreferencesFromResource(R.xml.vpn_type); - initTypeList(); - } - - @Override - public boolean onPreferenceTreeClick(PreferenceScreen ps, Preference pref) { - ((PreferenceActivity)getActivity()) - .finishPreferencePanel(this, Activity.RESULT_OK, - getResultIntent(mTypeMap.get(pref.getTitle().toString()))); - return true; - } - - private void initTypeList() { - PreferenceScreen root = getPreferenceScreen(); - final Activity activity = getActivity(); - for (VpnType t : VpnManager.getSupportedVpnTypes()) { - String displayName = t.getDisplayName(); - String message = String.format( - activity.getString(R.string.vpn_edit_title_add), displayName); - mTypeMap.put(message, t); - - Preference pref = new Preference(activity); - pref.setTitle(message); - pref.setSummary(t.getDescriptionId()); - root.addPreference(pref); - } - } - - private Intent getResultIntent(VpnType type) { - Intent intent = new Intent(getActivity(), VpnSettings.class); - intent.putExtra(VpnSettings.KEY_VPN_TYPE, type.toString()); - return intent; - } -} diff --git a/src/com/android/settings/vpn2/VpnDialog.java b/src/com/android/settings/vpn2/VpnDialog.java index 92ad362..b3609a6 100644 --- a/src/com/android/settings/vpn2/VpnDialog.java +++ b/src/com/android/settings/vpn2/VpnDialog.java @@ -60,7 +60,7 @@ class VpnDialog extends AlertDialog implements TextWatcher, OnItemSelectedListen private TextView mServer; private TextView mUsername; private TextView mPassword; - private TextView mDomains; + private TextView mSearchDomains; private TextView mRoutes; private CheckBox mMppe; private TextView mL2tpSecret; @@ -92,7 +92,7 @@ class VpnDialog extends AlertDialog implements TextWatcher, OnItemSelectedListen mServer = (TextView) mView.findViewById(R.id.server); mUsername = (TextView) mView.findViewById(R.id.username); mPassword = (TextView) mView.findViewById(R.id.password); - mDomains = (TextView) mView.findViewById(R.id.domains); + mSearchDomains = (TextView) mView.findViewById(R.id.search_domains); mRoutes = (TextView) mView.findViewById(R.id.routes); mMppe = (CheckBox) mView.findViewById(R.id.mppe); mL2tpSecret = (TextView) mView.findViewById(R.id.l2tp_secret); @@ -108,7 +108,7 @@ class VpnDialog extends AlertDialog implements TextWatcher, OnItemSelectedListen mServer.setText(mProfile.server); mUsername.setText(mProfile.username); mPassword.setText(getDummy(mProfile.password)); - mDomains.setText(mProfile.domains); + mSearchDomains.setText(mProfile.searchDomains); mRoutes.setText(mProfile.routes); mMppe.setChecked(mProfile.mppe); mL2tpSecret.setText(getDummy(mProfile.l2tpSecret)); @@ -218,10 +218,10 @@ class VpnDialog extends AlertDialog implements TextWatcher, OnItemSelectedListen mView.findViewById(R.id.l2tp).setVisibility(View.VISIBLE); // fall through case VpnProfile.TYPE_IPSEC_XAUTH_RSA: - mView.findViewById(R.id.ipsec_ca).setVisibility(View.VISIBLE); + mView.findViewById(R.id.ipsec_user).setVisibility(View.VISIBLE); // fall through case VpnProfile.TYPE_IPSEC_HYBRID_RSA: - mView.findViewById(R.id.ipsec_user).setVisibility(View.VISIBLE); + mView.findViewById(R.id.ipsec_ca).setVisibility(View.VISIBLE); break; } } @@ -243,7 +243,6 @@ class VpnDialog extends AlertDialog implements TextWatcher, OnItemSelectedListen case VpnProfile.TYPE_L2TP_IPSEC_RSA: case VpnProfile.TYPE_IPSEC_XAUTH_RSA: - case VpnProfile.TYPE_IPSEC_HYBRID_RSA: return mIpsecUserCert.getSelectedItemPosition() != 0; } return false; @@ -288,7 +287,7 @@ class VpnDialog extends AlertDialog implements TextWatcher, OnItemSelectedListen profile.server = mServer.getText().toString().trim(); profile.username = mUsername.getText().toString(); profile.password = getSecret(mProfile.password, mPassword); - profile.domains = mDomains.getText().toString().trim(); + profile.searchDomains = mSearchDomains.getText().toString().trim(); profile.routes = mRoutes.getText().toString().trim(); // Then, save type-specific fields. @@ -308,13 +307,13 @@ class VpnDialog extends AlertDialog implements TextWatcher, OnItemSelectedListen profile.l2tpSecret = getSecret(mProfile.l2tpSecret, mL2tpSecret); // fall through case VpnProfile.TYPE_IPSEC_XAUTH_RSA: - if (mIpsecCaCert.getSelectedItemPosition() != 0) { - profile.ipsecCaCert = (String) mIpsecCaCert.getSelectedItem(); + if (mIpsecUserCert.getSelectedItemPosition() != 0) { + profile.ipsecUserCert = (String) mIpsecUserCert.getSelectedItem(); } // fall through case VpnProfile.TYPE_IPSEC_HYBRID_RSA: - if (mIpsecUserCert.getSelectedItemPosition() != 0) { - profile.ipsecUserCert = (String) mIpsecUserCert.getSelectedItem(); + if (mIpsecCaCert.getSelectedItemPosition() != 0) { + profile.ipsecCaCert = (String) mIpsecCaCert.getSelectedItem(); } break; } diff --git a/src/com/android/settings/vpn2/VpnProfile.java b/src/com/android/settings/vpn2/VpnProfile.java index 9e4c528..24c2f5f 100644 --- a/src/com/android/settings/vpn2/VpnProfile.java +++ b/src/com/android/settings/vpn2/VpnProfile.java @@ -42,14 +42,15 @@ class VpnProfile implements Cloneable { String server = ""; // 2 String username = ""; // 3 String password = ""; // 4 - String domains = ""; // 5 - String routes = ""; // 6 - boolean mppe = false; // 7 - String l2tpSecret = ""; // 8 - String ipsecIdentifier = "";// 9 - String ipsecSecret = ""; // 10 - String ipsecUserCert = ""; // 11 - String ipsecCaCert = ""; // 12 + String dnsServers = ""; // 5 + String searchDomains = ""; // 6 + String routes = ""; // 7 + boolean mppe = false; // 8 + String l2tpSecret = ""; // 9 + String ipsecIdentifier = "";// 10 + String ipsecSecret = ""; // 11 + String ipsecUserCert = ""; // 12 + String ipsecCaCert = ""; // 13 // Helper fields. boolean saveLogin = false; @@ -65,8 +66,8 @@ class VpnProfile implements Cloneable { } String[] values = new String(value, Charsets.UTF_8).split("\0", -1); - // Currently it always has 13 fields. - if (values.length < 13) { + // Currently it always has 14 fields. + if (values.length < 14) { return null; } @@ -79,14 +80,15 @@ class VpnProfile implements Cloneable { profile.server = values[2]; profile.username = values[3]; profile.password = values[4]; - profile.domains = values[5]; - profile.routes = values[6]; - profile.mppe = Boolean.valueOf(values[7]); - profile.l2tpSecret = values[8]; - profile.ipsecIdentifier = values[9]; - profile.ipsecSecret = values[10]; - profile.ipsecUserCert = values[11]; - profile.ipsecCaCert = values[12]; + profile.dnsServers = values[5]; + profile.searchDomains = values[6]; + profile.routes = values[7]; + profile.mppe = Boolean.valueOf(values[8]); + profile.l2tpSecret = values[9]; + profile.ipsecIdentifier = values[10]; + profile.ipsecSecret = values[11]; + profile.ipsecUserCert = values[12]; + profile.ipsecCaCert = values[13]; profile.saveLogin = !profile.username.isEmpty() || !profile.password.isEmpty(); return profile; @@ -102,7 +104,8 @@ class VpnProfile implements Cloneable { builder.append('\0').append(server); builder.append('\0').append(saveLogin ? username : ""); builder.append('\0').append(saveLogin ? password : ""); - builder.append('\0').append(domains); + builder.append('\0').append(dnsServers); + builder.append('\0').append(searchDomains); builder.append('\0').append(routes); builder.append('\0').append(mppe); builder.append('\0').append(l2tpSecret); diff --git a/src/com/android/settings/vpn2/VpnSettings.java b/src/com/android/settings/vpn2/VpnSettings.java index 56fb983..7f6c9f4 100644 --- a/src/com/android/settings/vpn2/VpnSettings.java +++ b/src/com/android/settings/vpn2/VpnSettings.java @@ -37,8 +37,11 @@ import android.view.MenuItem; import android.view.View; import android.widget.AdapterView.AdapterContextMenuInfo; +import com.android.internal.net.LegacyVpnInfo; +import com.android.internal.net.VpnConfig; import com.android.settings.SettingsPreferenceFragment; +import java.util.Arrays; import java.util.HashMap; public class VpnSettings extends SettingsPreferenceFragment implements @@ -47,13 +50,8 @@ public class VpnSettings extends SettingsPreferenceFragment implements private static final String TAG = "VpnSettings"; - // Match these constants with R.array.vpn_states. - private static final int STATE_NONE = -1; - private static final int STATE_CONNECTING = 0; - private static final int STATE_CONNECTED = 1; - private static final int STATE_DISCONNECTED = 2; - private static final int STATE_FAILED = 3; - + private final IConnectivityManager mService = IConnectivityManager.Stub + .asInterface(ServiceManager.getService(Context.CONNECTIVITY_SERVICE)); private final KeyStore mKeyStore = KeyStore.getInstance(); private boolean mUnlocking = false; @@ -61,6 +59,7 @@ public class VpnSettings extends SettingsPreferenceFragment implements private VpnDialog mDialog; private Handler mUpdater; + private LegacyVpnInfo mInfo; // The key of the profile for the current ContextMenu. private String mSelectedKey; @@ -261,8 +260,17 @@ public class VpnSettings extends SettingsPreferenceFragment implements } if (preference instanceof VpnPreference) { - mDialog = new VpnDialog(getActivity(), this, - ((VpnPreference) preference).getProfile(), false); + VpnProfile profile = ((VpnPreference) preference).getProfile(); + if (mInfo != null && profile.key.equals(mInfo.key) && + mInfo.state == LegacyVpnInfo.STATE_CONNECTED) { + try { + mInfo.intent.send(); + return true; + } catch (Exception e) { + // ignore + } + } + mDialog = new VpnDialog(getActivity(), this, profile, false); } else { // Generate a new key. Here we just use the current time. long millis = System.currentTimeMillis(); @@ -282,20 +290,30 @@ public class VpnSettings extends SettingsPreferenceFragment implements mUpdater.removeMessages(0); if (isResumed()) { - - - - + try { + LegacyVpnInfo info = mService.getLegacyVpnInfo(); + if (mInfo != null) { + VpnPreference preference = mPreferences.get(mInfo.key); + if (preference != null) { + preference.update(-1); + } + mInfo = null; + } + if (info != null) { + VpnPreference preference = mPreferences.get(info.key); + if (preference != null) { + preference.update(info.state); + mInfo = info; + } + } + } catch (Exception e) { + // ignore + } mUpdater.sendEmptyMessageDelayed(0, 1000); } return true; } - private static IConnectivityManager getService() { - return IConnectivityManager.Stub.asInterface( - ServiceManager.getService(Context.CONNECTIVITY_SERVICE)); - } - private void connect(VpnProfile profile) { String[] racoon = null; switch (profile.type) { @@ -328,7 +346,7 @@ public class VpnSettings extends SettingsPreferenceFragment implements "name", profile.username, "password", profile.password, "linkname", "vpn", "refuse-eap", "nodefaultroute", "usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400", - (profile.mppe ? "+mppe" : "nomppe"), + "ipparam", profile.routes, (profile.mppe ? "+mppe" : "nomppe"), }; break; case VpnProfile.TYPE_L2TP_IPSEC_PSK: @@ -338,28 +356,44 @@ public class VpnSettings extends SettingsPreferenceFragment implements "name", profile.username, "password", profile.password, "linkname", "vpn", "refuse-eap", "nodefaultroute", "usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400", + "ipparam", profile.routes, }; break; } + VpnConfig config = new VpnConfig(); + config.packagz = profile.key; + config.session = profile.name; + config.routes = profile.routes; + if (!profile.searchDomains.isEmpty()) { + config.searchDomains = Arrays.asList(profile.searchDomains.split(" ")); + } + try { -// getService().doLegacyVpn(racoon, mtpd); + mService.startLegacyVpn(config, racoon, mtpd); } catch (Exception e) { Log.e(TAG, "connect", e); } } private void disconnect(String key) { + if (mInfo != null && key.equals(mInfo.key)) { + try { + mService.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN); + } catch (Exception e) { + // ignore + } + } } - private class VpnPreference extends Preference { private VpnProfile mProfile; - private int mState = STATE_NONE; + private int mState = -1; VpnPreference(Context context, VpnProfile profile) { super(context); setPersistent(false); + setOrder(0); setOnPreferenceClickListener(VpnSettings.this); mProfile = profile; @@ -375,18 +409,23 @@ public class VpnSettings extends SettingsPreferenceFragment implements update(); } + void update(int state) { + mState = state; + update(); + } + void update() { - if (mState != STATE_NONE) { - String[] states = getContext().getResources() - .getStringArray(R.array.vpn_states); - setSummary(states[mState]); - } else { + if (mState < 0) { String[] types = getContext().getResources() .getStringArray(R.array.vpn_types_long); setSummary(types[mProfile.type]); + } else { + String[] states = getContext().getResources() + .getStringArray(R.array.vpn_states); + setSummary(states[mState]); } setTitle(mProfile.name); - notifyChanged(); + notifyHierarchyChanged(); } @Override @@ -394,7 +433,6 @@ public class VpnSettings extends SettingsPreferenceFragment implements int result = -1; if (preference instanceof VpnPreference) { VpnPreference another = (VpnPreference) preference; - if ((result = another.mState - mState) == 0 && (result = mProfile.name.compareTo(another.mProfile.name)) == 0 && (result = mProfile.type - another.mProfile.type) == 0) { diff --git a/src/com/android/settings/wifi/AdvancedSettings.java b/src/com/android/settings/wifi/AdvancedSettings.java deleted file mode 100644 index cd7b8a3..0000000 --- a/src/com/android/settings/wifi/AdvancedSettings.java +++ /dev/null @@ -1,174 +0,0 @@ -/* - * 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 android.content.Context; -import android.net.wifi.WifiInfo; -import android.net.wifi.WifiManager; -import android.os.Bundle; -import android.preference.CheckBoxPreference; -import android.preference.ListPreference; -import android.preference.Preference; -import android.preference.PreferenceScreen; -import android.provider.Settings; -import android.provider.Settings.Secure; -import android.text.TextUtils; -import android.util.Log; -import android.widget.Toast; - -import com.android.settings.R; -import com.android.settings.SettingsPreferenceFragment; -import com.android.settings.Utils; - -public class AdvancedSettings extends SettingsPreferenceFragment - implements Preference.OnPreferenceChangeListener { - - private static final String TAG = "AdvancedSettings"; - private static final String KEY_MAC_ADDRESS = "mac_address"; - private static final String KEY_CURRENT_IP_ADDRESS = "current_ip_address"; - private static final String KEY_FREQUENCY_BAND = "frequency_band"; - private static final String KEY_NOTIFY_OPEN_NETWORKS = "notify_open_networks"; - private static final String KEY_SLEEP_POLICY = "sleep_policy"; - private static final String KEY_ENABLE_WIFI_WATCHDOG = "wifi_enable_watchdog_service"; - - private WifiManager mWifiManager; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - addPreferencesFromResource(R.xml.wifi_advanced_settings); - } - - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE); - } - - @Override - public void onResume() { - super.onResume(); - initPreferences(); - refreshWifiInfo(); - } - - private void initPreferences() { - CheckBoxPreference notifyOpenNetworks = - (CheckBoxPreference) findPreference(KEY_NOTIFY_OPEN_NETWORKS); - notifyOpenNetworks.setChecked(Secure.getInt(getContentResolver(), - Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 0) == 1); - notifyOpenNetworks.setEnabled(mWifiManager.isWifiEnabled()); - - CheckBoxPreference watchdogEnabled = - (CheckBoxPreference) findPreference(KEY_ENABLE_WIFI_WATCHDOG); - watchdogEnabled.setChecked(Secure.getInt(getContentResolver(), - Secure.WIFI_WATCHDOG_ON, 1) == 1); - - watchdogEnabled.setEnabled(mWifiManager.isWifiEnabled()); - - ListPreference frequencyPref = (ListPreference) findPreference(KEY_FREQUENCY_BAND); - - if (mWifiManager.isDualBandSupported()) { - frequencyPref.setOnPreferenceChangeListener(this); - int value = mWifiManager.getFrequencyBand(); - if (value != -1) { - frequencyPref.setValue(String.valueOf(value)); - } else { - Log.e(TAG, "Failed to fetch frequency band"); - } - } else { - if (frequencyPref != null) { - // null if it has already been removed before resume - getPreferenceScreen().removePreference(frequencyPref); - } - } - - ListPreference sleepPolicyPref = (ListPreference) findPreference(KEY_SLEEP_POLICY); - if (sleepPolicyPref != null) { - if (Utils.isWifiOnly()) { - sleepPolicyPref.setEntries(R.array.wifi_sleep_policy_entries_wifi_only); - sleepPolicyPref.setSummary(R.string.wifi_setting_sleep_policy_summary_wifi_only); - } - sleepPolicyPref.setOnPreferenceChangeListener(this); - int value = Settings.System.getInt(getContentResolver(), - Settings.System.WIFI_SLEEP_POLICY, - Settings.System.WIFI_SLEEP_POLICY_NEVER); - sleepPolicyPref.setValue(String.valueOf(value)); - } - } - - @Override - public boolean onPreferenceTreeClick(PreferenceScreen screen, Preference preference) { - String key = preference.getKey(); - - if (KEY_NOTIFY_OPEN_NETWORKS.equals(key)) { - Secure.putInt(getContentResolver(), - Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, - ((CheckBoxPreference) preference).isChecked() ? 1 : 0); - } else if (KEY_ENABLE_WIFI_WATCHDOG.equals(key)) { - Secure.putInt(getContentResolver(), - Secure.WIFI_WATCHDOG_ON, - ((CheckBoxPreference) preference).isChecked() ? 1 : 0); - } else { - return super.onPreferenceTreeClick(screen, preference); - } - return true; - } - - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - String key = preference.getKey(); - - if (KEY_FREQUENCY_BAND.equals(key)) { - try { - mWifiManager.setFrequencyBand(Integer.parseInt(((String) newValue)), true); - } catch (NumberFormatException e) { - Toast.makeText(getActivity(), R.string.wifi_setting_frequency_band_error, - Toast.LENGTH_SHORT).show(); - return false; - } - } - - if (KEY_SLEEP_POLICY.equals(key)) { - try { - Settings.System.putInt(getContentResolver(), - Settings.System.WIFI_SLEEP_POLICY, Integer.parseInt(((String) newValue))); - } catch (NumberFormatException e) { - Toast.makeText(getActivity(), R.string.wifi_setting_sleep_policy_error, - Toast.LENGTH_SHORT).show(); - return false; - } - } - - return true; - } - - private void refreshWifiInfo() { - WifiInfo wifiInfo = mWifiManager.getConnectionInfo(); - - Preference wifiMacAddressPref = findPreference(KEY_MAC_ADDRESS); - String macAddress = wifiInfo == null ? null : wifiInfo.getMacAddress(); - wifiMacAddressPref.setSummary(!TextUtils.isEmpty(macAddress) ? macAddress - : getActivity().getString(R.string.status_unavailable)); - - Preference wifiIpAddressPref = findPreference(KEY_CURRENT_IP_ADDRESS); - String ipAddress = Utils.getWifiIpAddresses(getActivity()); - wifiIpAddressPref.setSummary(ipAddress == null ? - getActivity().getString(R.string.status_unavailable) : ipAddress); - } - -} diff --git a/src/com/android/settings/wifi/AdvancedWifiSettings.java b/src/com/android/settings/wifi/AdvancedWifiSettings.java index bc92b3a..6c983fd 100644 --- a/src/com/android/settings/wifi/AdvancedWifiSettings.java +++ b/src/com/android/settings/wifi/AdvancedWifiSettings.java @@ -20,8 +20,12 @@ import android.content.Context; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.os.Bundle; +import android.preference.CheckBoxPreference; import android.preference.ListPreference; import android.preference.Preference; +import android.preference.PreferenceScreen; +import android.provider.Settings; +import android.provider.Settings.Secure; import android.text.TextUtils; import android.util.Log; import android.widget.Toast; @@ -37,6 +41,9 @@ public class AdvancedWifiSettings extends SettingsPreferenceFragment private static final String KEY_MAC_ADDRESS = "mac_address"; private static final String KEY_CURRENT_IP_ADDRESS = "current_ip_address"; private static final String KEY_FREQUENCY_BAND = "frequency_band"; + private static final String KEY_NOTIFY_OPEN_NETWORKS = "notify_open_networks"; + private static final String KEY_SLEEP_POLICY = "sleep_policy"; + private static final String KEY_ENABLE_WIFI_WATCHDOG = "wifi_enable_watchdog_service"; private WifiManager mWifiManager; @@ -60,28 +67,73 @@ public class AdvancedWifiSettings extends SettingsPreferenceFragment } private void initPreferences() { + CheckBoxPreference notifyOpenNetworks = + (CheckBoxPreference) findPreference(KEY_NOTIFY_OPEN_NETWORKS); + notifyOpenNetworks.setChecked(Secure.getInt(getContentResolver(), + Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 0) == 1); + notifyOpenNetworks.setEnabled(mWifiManager.isWifiEnabled()); - ListPreference pref = (ListPreference) findPreference(KEY_FREQUENCY_BAND); + CheckBoxPreference watchdogEnabled = + (CheckBoxPreference) findPreference(KEY_ENABLE_WIFI_WATCHDOG); + watchdogEnabled.setChecked(Secure.getInt(getContentResolver(), + Secure.WIFI_WATCHDOG_ON, 1) == 1); + + watchdogEnabled.setEnabled(mWifiManager.isWifiEnabled()); + + ListPreference frequencyPref = (ListPreference) findPreference(KEY_FREQUENCY_BAND); if (mWifiManager.isDualBandSupported()) { - pref.setOnPreferenceChangeListener(this); + frequencyPref.setOnPreferenceChangeListener(this); int value = mWifiManager.getFrequencyBand(); if (value != -1) { - pref.setValue(String.valueOf(value)); + frequencyPref.setValue(String.valueOf(value)); } else { Log.e(TAG, "Failed to fetch frequency band"); } } else { - getPreferenceScreen().removePreference(pref); + if (frequencyPref != null) { + // null if it has already been removed before resume + getPreferenceScreen().removePreference(frequencyPref); + } + } + + ListPreference sleepPolicyPref = (ListPreference) findPreference(KEY_SLEEP_POLICY); + if (sleepPolicyPref != null) { + if (Utils.isWifiOnly()) { + sleepPolicyPref.setEntries(R.array.wifi_sleep_policy_entries_wifi_only); + sleepPolicyPref.setSummary(R.string.wifi_setting_sleep_policy_summary_wifi_only); + } + sleepPolicyPref.setOnPreferenceChangeListener(this); + int value = Settings.System.getInt(getContentResolver(), + Settings.System.WIFI_SLEEP_POLICY, + Settings.System.WIFI_SLEEP_POLICY_NEVER); + sleepPolicyPref.setValue(String.valueOf(value)); } } @Override + public boolean onPreferenceTreeClick(PreferenceScreen screen, Preference preference) { + String key = preference.getKey(); + + if (KEY_NOTIFY_OPEN_NETWORKS.equals(key)) { + Secure.putInt(getContentResolver(), + Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, + ((CheckBoxPreference) preference).isChecked() ? 1 : 0); + } else if (KEY_ENABLE_WIFI_WATCHDOG.equals(key)) { + Secure.putInt(getContentResolver(), + Secure.WIFI_WATCHDOG_ON, + ((CheckBoxPreference) preference).isChecked() ? 1 : 0); + } else { + return super.onPreferenceTreeClick(screen, preference); + } + return true; + } + + @Override public boolean onPreferenceChange(Preference preference, Object newValue) { String key = preference.getKey(); - if (key == null) return true; - if (key.equals(KEY_FREQUENCY_BAND)) { + if (KEY_FREQUENCY_BAND.equals(key)) { try { mWifiManager.setFrequencyBand(Integer.parseInt(((String) newValue)), true); } catch (NumberFormatException e) { @@ -91,6 +143,17 @@ public class AdvancedWifiSettings extends SettingsPreferenceFragment } } + if (KEY_SLEEP_POLICY.equals(key)) { + try { + Settings.System.putInt(getContentResolver(), + Settings.System.WIFI_SLEEP_POLICY, Integer.parseInt(((String) newValue))); + } catch (NumberFormatException e) { + Toast.makeText(getActivity(), R.string.wifi_setting_sleep_policy_error, + Toast.LENGTH_SHORT).show(); + return false; + } + } + return true; } diff --git a/src/com/android/settings/wifi/WifiSettings.java b/src/com/android/settings/wifi/WifiSettings.java index 549e695..3fd1bef 100644 --- a/src/com/android/settings/wifi/WifiSettings.java +++ b/src/com/android/settings/wifi/WifiSettings.java @@ -283,12 +283,12 @@ public class WifiSettings extends SettingsPreferenceFragment case MENU_ID_ADVANCED: if (getActivity() instanceof PreferenceActivity) { ((PreferenceActivity) getActivity()).startPreferencePanel( - AdvancedSettings.class.getCanonicalName(), + AdvancedWifiSettings.class.getCanonicalName(), null, R.string.wifi_advanced_titlebar, null, this, 0); } else { - startFragment(this, AdvancedSettings.class.getCanonicalName(), -1, null); + startFragment(this, AdvancedWifiSettings.class.getCanonicalName(), -1, null); } return true; } |