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
17package com.android.settings;
18
19import android.app.Activity;
20import android.app.AlertDialog;
21import android.app.Dialog;
22import android.app.admin.DevicePolicyManager;
23import android.content.Context;
24import android.content.Intent;
25import android.net.ConnectivityManager;
26import android.net.Proxy;
27import android.net.ProxyInfo;
28import android.os.Bundle;
29import android.text.Selection;
30import android.text.Spannable;
31import android.text.TextUtils;
32import android.util.Log;
33import android.view.LayoutInflater;
34import android.view.View;
35import android.view.View.OnClickListener;
36import android.view.View.OnFocusChangeListener;
37import android.view.ViewGroup;
38import android.widget.Button;
39import android.widget.EditText;
40import android.widget.TextView;
41
42import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
43import com.android.settings.SettingsPreferenceFragment.SettingsDialogFragment;
44import com.android.settings.core.InstrumentedPreferenceFragment;
45
46public class ProxySelector extends InstrumentedPreferenceFragment implements DialogCreatable {
47    private static final String TAG = "ProxySelector";
48
49    EditText    mHostnameField;
50    EditText    mPortField;
51    EditText    mExclusionListField;
52    Button      mOKButton;
53    Button      mClearButton;
54    Button      mDefaultButton;
55
56    private static final int ERROR_DIALOG_ID = 0;
57
58    private SettingsDialogFragment mDialogFragment;
59    private View mView;
60
61    @Override
62    public void onCreate(Bundle icicle) {
63        super.onCreate(icicle);
64    }
65
66    @Override
67    public View onCreateView(LayoutInflater inflater, ViewGroup container,
68            Bundle savedInstanceState) {
69        mView = inflater.inflate(R.layout.proxy, container, false);
70        initView(mView);
71        // TODO: Populate based on connection status
72        populateFields();
73        return mView;
74    }
75
76    @Override
77    public void onActivityCreated(Bundle savedInstanceState) {
78        super.onActivityCreated(savedInstanceState);
79        final DevicePolicyManager dpm =
80                (DevicePolicyManager)getActivity().getSystemService(Context.DEVICE_POLICY_SERVICE);
81
82        final boolean userSetGlobalProxy = (dpm.getGlobalProxyAdmin() == null);
83        // Disable UI if the Global Proxy is being controlled by a Device Admin
84        mHostnameField.setEnabled(userSetGlobalProxy);
85        mPortField.setEnabled(userSetGlobalProxy);
86        mExclusionListField.setEnabled(userSetGlobalProxy);
87        mOKButton.setEnabled(userSetGlobalProxy);
88        mClearButton.setEnabled(userSetGlobalProxy);
89        mDefaultButton.setEnabled(userSetGlobalProxy);
90    }
91
92    // Dialog management
93
94    @Override
95    public Dialog onCreateDialog(int id) {
96        if (id == ERROR_DIALOG_ID) {
97            String hostname = mHostnameField.getText().toString().trim();
98            String portStr = mPortField.getText().toString().trim();
99            String exclList = mExclusionListField.getText().toString().trim();
100            String msg = getActivity().getString(validate(hostname, portStr, exclList));
101
102            return new AlertDialog.Builder(getActivity())
103                    .setTitle(R.string.proxy_error)
104                    .setPositiveButton(R.string.proxy_error_dismiss, null)
105                    .setMessage(msg)
106                    .create();
107        }
108        return null;
109    }
110
111    @Override
112    public int getDialogMetricsCategory(int dialogId) {
113        return MetricsEvent.DIALOG_PROXY_SELECTOR_ERROR;
114    }
115
116    private void showDialog(int dialogId) {
117        if (mDialogFragment != null) {
118            Log.e(TAG, "Old dialog fragment not null!");
119        }
120        mDialogFragment = new SettingsDialogFragment(this, dialogId);
121        mDialogFragment.show(getActivity().getFragmentManager(), Integer.toString(dialogId));
122    }
123
124    private void initView(View view) {
125        mHostnameField = (EditText)view.findViewById(R.id.hostname);
126        mHostnameField.setOnFocusChangeListener(mOnFocusChangeHandler);
127
128        mPortField = (EditText)view.findViewById(R.id.port);
129        mPortField.setOnClickListener(mOKHandler);
130        mPortField.setOnFocusChangeListener(mOnFocusChangeHandler);
131
132        mExclusionListField = (EditText)view.findViewById(R.id.exclusionlist);
133        mExclusionListField.setOnFocusChangeListener(mOnFocusChangeHandler);
134
135        mOKButton = (Button)view.findViewById(R.id.action);
136        mOKButton.setOnClickListener(mOKHandler);
137
138        mClearButton = (Button)view.findViewById(R.id.clear);
139        mClearButton.setOnClickListener(mClearHandler);
140
141        mDefaultButton = (Button)view.findViewById(R.id.defaultView);
142        mDefaultButton.setOnClickListener(mDefaultHandler);
143    }
144
145    void populateFields() {
146        final Activity activity = getActivity();
147        String hostname = "";
148        int port = -1;
149        String exclList = "";
150        // Use the last setting given by the user
151        ConnectivityManager cm =
152                (ConnectivityManager)getActivity().getSystemService(Context.CONNECTIVITY_SERVICE);
153
154        ProxyInfo proxy = cm.getGlobalProxy();
155        if (proxy != null) {
156            hostname = proxy.getHost();
157            port = proxy.getPort();
158            exclList = proxy.getExclusionListAsString();
159        }
160
161        if (hostname == null) {
162            hostname = "";
163        }
164
165        mHostnameField.setText(hostname);
166
167        String portStr = port == -1 ? "" : Integer.toString(port);
168        mPortField.setText(portStr);
169
170        mExclusionListField.setText(exclList);
171
172        final Intent intent = activity.getIntent();
173
174        String buttonLabel = intent.getStringExtra("button-label");
175        if (!TextUtils.isEmpty(buttonLabel)) {
176            mOKButton.setText(buttonLabel);
177        }
178
179        String title = intent.getStringExtra("title");
180        if (!TextUtils.isEmpty(title)) {
181            activity.setTitle(title);
182        }
183    }
184
185    /**
186     * validate syntax of hostname and port entries
187     * @return 0 on success, string resource ID on failure
188     */
189    public static int validate(String hostname, String port, String exclList) {
190        switch (Proxy.validate(hostname, port, exclList)) {
191            case Proxy.PROXY_VALID:
192                return 0;
193            case Proxy.PROXY_HOSTNAME_EMPTY:
194                return R.string.proxy_error_empty_host_set_port;
195            case Proxy.PROXY_HOSTNAME_INVALID:
196                return R.string.proxy_error_invalid_host;
197            case Proxy.PROXY_PORT_EMPTY:
198                return R.string.proxy_error_empty_port;
199            case Proxy.PROXY_PORT_INVALID:
200                return R.string.proxy_error_invalid_port;
201            case Proxy.PROXY_EXCLLIST_INVALID:
202                return R.string.proxy_error_invalid_exclusion_list;
203            default:
204                // should neven happen
205                Log.e(TAG, "Unknown proxy settings error");
206                return -1;
207        }
208    }
209
210    /**
211     * returns true on success, false if the user must correct something
212     */
213    boolean saveToDb() {
214
215        String hostname = mHostnameField.getText().toString().trim();
216        String portStr = mPortField.getText().toString().trim();
217        String exclList = mExclusionListField.getText().toString().trim();
218        int port = 0;
219
220        int result = validate(hostname, portStr, exclList);
221        if (result != 0) {
222            showDialog(ERROR_DIALOG_ID);
223            return false;
224        }
225
226        if (portStr.length() > 0) {
227            try {
228                port = Integer.parseInt(portStr);
229            } catch (NumberFormatException ex) {
230                // should never happen - caught by validate above
231                return false;
232            }
233        }
234        ProxyInfo p = new ProxyInfo(hostname, port, exclList);
235        // FIXME: The best solution would be to make a better UI that would
236        // disable editing of the text boxes if the user chooses to use the
237        // default settings. i.e. checking a box to always use the default
238        // carrier. http:/b/issue?id=756480
239        // FIXME: If the user types in a proxy that matches the default, should
240        // we keep that setting? Can be fixed with a new UI.
241        ConnectivityManager cm =
242                (ConnectivityManager)getActivity().getSystemService(Context.CONNECTIVITY_SERVICE);
243
244        cm.setGlobalProxy(p);
245        return true;
246    }
247
248    OnClickListener mOKHandler = new OnClickListener() {
249            public void onClick(View v) {
250                if (saveToDb()) {
251                    getActivity().onBackPressed();
252                }
253            }
254        };
255
256    OnClickListener mClearHandler = new OnClickListener() {
257            public void onClick(View v) {
258                mHostnameField.setText("");
259                mPortField.setText("");
260                mExclusionListField.setText("");
261            }
262        };
263
264    OnClickListener mDefaultHandler = new OnClickListener() {
265            public void onClick(View v) {
266                // TODO: populate based on connection status
267                populateFields();
268            }
269        };
270
271    OnFocusChangeListener mOnFocusChangeHandler = new OnFocusChangeListener() {
272            public void onFocusChange(View v, boolean hasFocus) {
273                if (hasFocus) {
274                    TextView textView = (TextView) v;
275                    Selection.selectAll((Spannable) textView.getText());
276                }
277            }
278        };
279
280    @Override
281    public int getMetricsCategory() {
282        return MetricsEvent.PROXY_SELECTOR;
283    }
284}
285