Home | History | Annotate | Download | only in settings
      1 /*
      2  * Copyright (C) 2006 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.settings;
     18 
     19 import com.android.settings.SettingsPreferenceFragment.SettingsDialogFragment;
     20 
     21 import android.app.Activity;
     22 import android.app.AlertDialog;
     23 import android.app.Dialog;
     24 import android.app.Fragment;
     25 import android.app.admin.DevicePolicyManager;
     26 import android.content.ContentResolver;
     27 import android.content.Context;
     28 import android.content.Intent;
     29 import android.net.ConnectivityManager;
     30 import android.net.Proxy;
     31 import android.net.ProxyProperties;
     32 import android.os.Bundle;
     33 import android.provider.Settings;
     34 import android.text.Selection;
     35 import android.text.Spannable;
     36 import android.text.TextUtils;
     37 import android.util.Log;
     38 import android.view.LayoutInflater;
     39 import android.view.View;
     40 import android.view.View.OnClickListener;
     41 import android.view.View.OnFocusChangeListener;
     42 import android.view.ViewGroup;
     43 import android.widget.Button;
     44 import android.widget.EditText;
     45 import android.widget.TextView;
     46 
     47 import java.net.InetSocketAddress;
     48 import java.util.regex.Matcher;
     49 import java.util.regex.Pattern;
     50 
     51 public class ProxySelector extends Fragment implements DialogCreatable {
     52     private static final String TAG = "ProxySelector";
     53 
     54     EditText    mHostnameField;
     55     EditText    mPortField;
     56     EditText    mExclusionListField;
     57     Button      mOKButton;
     58     Button      mClearButton;
     59     Button      mDefaultButton;
     60 
     61     // Allows underscore char to supports proxies that do not
     62     // follow the spec
     63     private static final String HC = "a-zA-Z0-9\\_";
     64 
     65     // Matches blank input, ips, and domain names
     66     private static final String HOSTNAME_REGEXP =
     67             "^$|^[" + HC + "]+(\\-[" + HC + "]+)*(\\.[" + HC + "]+(\\-[" + HC + "]+)*)*$";
     68     private static final Pattern HOSTNAME_PATTERN;
     69     private static final String EXCLUSION_REGEXP =
     70             "$|^(\\*)?\\.?[" + HC + "]+(\\-[" + HC + "]+)*(\\.[" + HC + "]+(\\-[" + HC + "]+)*)*$";
     71     private static final Pattern EXCLUSION_PATTERN;
     72     static {
     73         HOSTNAME_PATTERN = Pattern.compile(HOSTNAME_REGEXP);
     74         EXCLUSION_PATTERN = Pattern.compile(EXCLUSION_REGEXP);
     75     }
     76 
     77     private static final int ERROR_DIALOG_ID = 0;
     78 
     79     private SettingsDialogFragment mDialogFragment;
     80     private View mView;
     81 
     82     @Override
     83     public void onCreate(Bundle icicle) {
     84         super.onCreate(icicle);
     85     }
     86 
     87     @Override
     88     public View onCreateView(LayoutInflater inflater, ViewGroup container,
     89             Bundle savedInstanceState) {
     90         mView = inflater.inflate(R.layout.proxy, container, false);
     91         initView(mView);
     92         // TODO: Populate based on connection status
     93         populateFields();
     94         return mView;
     95     }
     96 
     97     @Override
     98     public void onActivityCreated(Bundle savedInstanceState) {
     99         super.onActivityCreated(savedInstanceState);
    100         final DevicePolicyManager dpm =
    101                 (DevicePolicyManager)getActivity().getSystemService(Context.DEVICE_POLICY_SERVICE);
    102 
    103         final boolean userSetGlobalProxy = (dpm.getGlobalProxyAdmin() == null);
    104         // Disable UI if the Global Proxy is being controlled by a Device Admin
    105         mHostnameField.setEnabled(userSetGlobalProxy);
    106         mPortField.setEnabled(userSetGlobalProxy);
    107         mExclusionListField.setEnabled(userSetGlobalProxy);
    108         mOKButton.setEnabled(userSetGlobalProxy);
    109         mClearButton.setEnabled(userSetGlobalProxy);
    110         mDefaultButton.setEnabled(userSetGlobalProxy);
    111     }
    112 
    113     // Dialog management
    114 
    115     @Override
    116     public Dialog onCreateDialog(int id) {
    117         if (id == ERROR_DIALOG_ID) {
    118             String hostname = mHostnameField.getText().toString().trim();
    119             String portStr = mPortField.getText().toString().trim();
    120             String exclList = mExclusionListField.getText().toString().trim();
    121             String msg = getActivity().getString(validate(hostname, portStr, exclList));
    122 
    123             return new AlertDialog.Builder(getActivity())
    124                     .setTitle(R.string.proxy_error)
    125                     .setPositiveButton(R.string.proxy_error_dismiss, null)
    126                     .setMessage(msg)
    127                     .create();
    128         }
    129         return null;
    130     }
    131 
    132     private void showDialog(int dialogId) {
    133         if (mDialogFragment != null) {
    134             Log.e(TAG, "Old dialog fragment not null!");
    135         }
    136         mDialogFragment = new SettingsDialogFragment(this, dialogId);
    137         mDialogFragment.show(getActivity().getFragmentManager(), Integer.toString(dialogId));
    138     }
    139 
    140     private void initView(View view) {
    141         mHostnameField = (EditText)view.findViewById(R.id.hostname);
    142         mHostnameField.setOnFocusChangeListener(mOnFocusChangeHandler);
    143 
    144         mPortField = (EditText)view.findViewById(R.id.port);
    145         mPortField.setOnClickListener(mOKHandler);
    146         mPortField.setOnFocusChangeListener(mOnFocusChangeHandler);
    147 
    148         mExclusionListField = (EditText)view.findViewById(R.id.exclusionlist);
    149         mExclusionListField.setOnFocusChangeListener(mOnFocusChangeHandler);
    150 
    151         mOKButton = (Button)view.findViewById(R.id.action);
    152         mOKButton.setOnClickListener(mOKHandler);
    153 
    154         mClearButton = (Button)view.findViewById(R.id.clear);
    155         mClearButton.setOnClickListener(mClearHandler);
    156 
    157         mDefaultButton = (Button)view.findViewById(R.id.defaultView);
    158         mDefaultButton.setOnClickListener(mDefaultHandler);
    159     }
    160 
    161     void populateFields() {
    162         final Activity activity = getActivity();
    163         String hostname = "";
    164         int port = -1;
    165         String exclList = "";
    166         // Use the last setting given by the user
    167         ConnectivityManager cm =
    168                 (ConnectivityManager)getActivity().getSystemService(Context.CONNECTIVITY_SERVICE);
    169 
    170         ProxyProperties proxy = cm.getGlobalProxy();
    171         if (proxy != null) {
    172             hostname = proxy.getHost();
    173             port = proxy.getPort();
    174             exclList = proxy.getExclusionList();
    175         }
    176 
    177         if (hostname == null) {
    178             hostname = "";
    179         }
    180 
    181         mHostnameField.setText(hostname);
    182 
    183         String portStr = port == -1 ? "" : Integer.toString(port);
    184         mPortField.setText(portStr);
    185 
    186         mExclusionListField.setText(exclList);
    187 
    188         final Intent intent = activity.getIntent();
    189 
    190         String buttonLabel = intent.getStringExtra("button-label");
    191         if (!TextUtils.isEmpty(buttonLabel)) {
    192             mOKButton.setText(buttonLabel);
    193         }
    194 
    195         String title = intent.getStringExtra("title");
    196         if (!TextUtils.isEmpty(title)) {
    197             activity.setTitle(title);
    198         }
    199     }
    200 
    201     /**
    202      * validate syntax of hostname and port entries
    203      * @return 0 on success, string resource ID on failure
    204      */
    205     public static int validate(String hostname, String port, String exclList) {
    206         Matcher match = HOSTNAME_PATTERN.matcher(hostname);
    207         String exclListArray[] = exclList.split(",");
    208 
    209         if (!match.matches()) return R.string.proxy_error_invalid_host;
    210 
    211         for (String excl : exclListArray) {
    212             Matcher m = EXCLUSION_PATTERN.matcher(excl);
    213             if (!m.matches()) return R.string.proxy_error_invalid_exclusion_list;
    214         }
    215 
    216         if (hostname.length() > 0 && port.length() == 0) {
    217             return R.string.proxy_error_empty_port;
    218         }
    219 
    220         if (port.length() > 0) {
    221             if (hostname.length() == 0) {
    222                 return R.string.proxy_error_empty_host_set_port;
    223             }
    224             int portVal = -1;
    225             try {
    226                 portVal = Integer.parseInt(port);
    227             } catch (NumberFormatException ex) {
    228                 return R.string.proxy_error_invalid_port;
    229             }
    230             if (portVal <= 0 || portVal > 0xFFFF) {
    231                 return R.string.proxy_error_invalid_port;
    232             }
    233         }
    234         return 0;
    235     }
    236 
    237     /**
    238      * returns true on success, false if the user must correct something
    239      */
    240     boolean saveToDb() {
    241 
    242         String hostname = mHostnameField.getText().toString().trim();
    243         String portStr = mPortField.getText().toString().trim();
    244         String exclList = mExclusionListField.getText().toString().trim();
    245         int port = 0;
    246 
    247         int result = validate(hostname, portStr, exclList);
    248         if (result > 0) {
    249             showDialog(ERROR_DIALOG_ID);
    250             return false;
    251         }
    252 
    253         if (portStr.length() > 0) {
    254             try {
    255                 port = Integer.parseInt(portStr);
    256             } catch (NumberFormatException ex) {
    257                 // should never happen - caught by validate above
    258                 return false;
    259             }
    260         }
    261         ProxyProperties p = new ProxyProperties(hostname, port, exclList);
    262         // FIXME: The best solution would be to make a better UI that would
    263         // disable editing of the text boxes if the user chooses to use the
    264         // default settings. i.e. checking a box to always use the default
    265         // carrier. http:/b/issue?id=756480
    266         // FIXME: If the user types in a proxy that matches the default, should
    267         // we keep that setting? Can be fixed with a new UI.
    268         ConnectivityManager cm =
    269                 (ConnectivityManager)getActivity().getSystemService(Context.CONNECTIVITY_SERVICE);
    270 
    271         cm.setGlobalProxy(p);
    272         return true;
    273     }
    274 
    275     OnClickListener mOKHandler = new OnClickListener() {
    276             public void onClick(View v) {
    277                 if (saveToDb()) {
    278                     getActivity().onBackPressed();
    279                 }
    280             }
    281         };
    282 
    283     OnClickListener mClearHandler = new OnClickListener() {
    284             public void onClick(View v) {
    285                 mHostnameField.setText("");
    286                 mPortField.setText("");
    287                 mExclusionListField.setText("");
    288             }
    289         };
    290 
    291     OnClickListener mDefaultHandler = new OnClickListener() {
    292             public void onClick(View v) {
    293                 // TODO: populate based on connection status
    294                 populateFields();
    295             }
    296         };
    297 
    298     OnFocusChangeListener mOnFocusChangeHandler = new OnFocusChangeListener() {
    299             public void onFocusChange(View v, boolean hasFocus) {
    300                 if (hasFocus) {
    301                     TextView textView = (TextView) v;
    302                     Selection.selectAll((Spannable) textView.getText());
    303                 }
    304             }
    305         };
    306 }
    307