| // Copyright (C) 2016 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.google.reviewit; |
| |
| import android.app.AlertDialog; |
| import android.content.ClipData; |
| import android.content.ClipDescription; |
| import android.content.ClipboardManager; |
| import android.content.Context; |
| import android.content.DialogInterface; |
| import android.content.Intent; |
| import android.os.AsyncTask; |
| import android.os.Bundle; |
| import android.support.annotation.LayoutRes; |
| import android.support.v4.app.Fragment; |
| import android.text.Editable; |
| import android.text.Html; |
| import android.text.TextWatcher; |
| import android.text.method.LinkMovementMethod; |
| import android.util.Log; |
| import android.view.View; |
| import android.view.inputmethod.InputMethodManager; |
| import android.widget.ArrayAdapter; |
| import android.widget.AutoCompleteTextView; |
| import android.widget.EditText; |
| import android.widget.TextView; |
| |
| import com.google.common.base.Strings; |
| import com.google.gerrit.extensions.api.GerritApi; |
| import com.google.gerrit.extensions.restapi.RestApiException; |
| import com.google.reviewit.app.ConfigManager; |
| import com.google.reviewit.app.Preferences; |
| import com.google.reviewit.app.QueryConfig; |
| import com.google.reviewit.app.ServerConfig; |
| import com.google.reviewit.util.FormatUtil; |
| import com.google.reviewit.util.WidgetUtil; |
| import com.urswolfer.gerrit.client.rest.GerritAuthData; |
| import com.urswolfer.gerrit.client.rest.GerritRestApiFactory; |
| import com.urswolfer.gerrit.client.rest.http.HttpStatusException; |
| |
| import java.net.MalformedURLException; |
| import java.net.URL; |
| |
| import static com.google.reviewit.util.WidgetUtil.setGone; |
| import static com.google.reviewit.util.WidgetUtil.setInvisible; |
| import static com.google.reviewit.util.WidgetUtil.setVisible; |
| |
| public class ServerSettingsFragment extends BaseFragment { |
| private static final String TAG = ServerSettingsFragment.class.getName(); |
| private static final String ORIGIN = |
| "com.google.reviewit.ServerSettingsFragment.ORIGIN"; |
| private static final String SERVER_ID = |
| "com.google.reviewit.ServerSettingsFragment.SERVER_ID"; |
| |
| public static ServerSettingsFragment create( |
| Class<? extends Fragment> origin) { |
| ServerSettingsFragment fragment = new ServerSettingsFragment(); |
| Bundle bundle = new Bundle(); |
| bundle.putSerializable(ORIGIN, origin); |
| fragment.setArguments(bundle); |
| return fragment; |
| } |
| |
| public static ServerSettingsFragment create( |
| Class<? extends Fragment> origin, String serverId) { |
| ServerSettingsFragment fragment = new ServerSettingsFragment(); |
| Bundle bundle = new Bundle(); |
| bundle.putSerializable(ORIGIN, origin); |
| bundle.putString(SERVER_ID, serverId); |
| fragment.setArguments(bundle); |
| return fragment; |
| } |
| |
| @Override |
| protected @LayoutRes int getLayout() { |
| return R.layout.content_server_settings; |
| } |
| |
| @Override |
| public void onActivityCreated(Bundle savedInstanceState) { |
| super.onActivityCreated(savedInstanceState); |
| |
| init(); |
| String serverId = getArguments() != null |
| ? getArguments().getString(SERVER_ID) |
| : null; |
| displayServerConfig(serverId != null ? getApp().getConfigManager() |
| .getServerConfig(serverId) : null); |
| } |
| |
| private void displayServerConfig(ServerConfig cfg) { |
| if (cfg != null) { |
| WidgetUtil.setText(v(R.id.idInput), cfg.id); |
| WidgetUtil.setText(v(R.id.nameInput), cfg.name); |
| WidgetUtil.setText(v(R.id.urlInput), cfg.url); |
| WidgetUtil.setText(v(R.id.userInput), cfg.user); |
| WidgetUtil.setText(v(R.id.passwordInput), cfg.password); |
| |
| displayCredentialsInfo(cfg.url); |
| } else { |
| WidgetUtil.setText(v(R.id.idInput), ""); |
| WidgetUtil.setText(v(R.id.nameInput), ""); |
| WidgetUtil.setText(v(R.id.urlInput), ""); |
| WidgetUtil.setText(v(R.id.userInput), ""); |
| WidgetUtil.setText(v(R.id.passwordInput), ""); |
| |
| displayCredentialsInfo(null); |
| } |
| } |
| |
| private void init() { |
| final AutoCompleteTextView urlInput = |
| (AutoCompleteTextView) v(R.id.urlInput); |
| ArrayAdapter<String> adapter = |
| new ArrayAdapter<>(getContext(), android.R.layout.simple_list_item_1, |
| getResources().getStringArray(R.array.urls)); |
| urlInput.setAdapter(adapter); |
| |
| v(R.id.pasteCredentialsButton).setOnClickListener( |
| new View.OnClickListener() { |
| @Override |
| public void onClick(View v) { |
| ClipboardManager clipboard = (ClipboardManager) getActivity() |
| .getSystemService(Context.CLIPBOARD_SERVICE); |
| if (clipboard.hasPrimaryClip() |
| && clipboard.getPrimaryClipDescription().hasMimeType( |
| ClipDescription.MIMETYPE_TEXT_PLAIN)) { |
| ClipData.Item item = clipboard.getPrimaryClip().getItemAt(0); |
| String pasteData = item.getText().toString(); |
| if (!pasteData.contains("/.gitcookies")) { |
| return; |
| } |
| |
| pasteData = pasteData.substring(pasteData.indexOf("/.gitcookies")); |
| pasteData = pasteData.substring(pasteData.lastIndexOf(",") + 1); |
| int pos = pasteData.indexOf("="); |
| String user = pasteData.substring(0, pos); |
| pasteData = pasteData.substring(pos + 1); |
| String password = pasteData.substring(0, pasteData.indexOf("\n")); |
| WidgetUtil.setText(v(R.id.userInput), user); |
| WidgetUtil.setText(v(R.id.passwordInput), password); |
| |
| // hide keyboard if it is open |
| View view = getActivity().getCurrentFocus(); |
| if (view != null) { |
| ((InputMethodManager) getActivity().getSystemService( |
| Context.INPUT_METHOD_SERVICE)) |
| .hideSoftInputFromWindow(view.getWindowToken(), 0); |
| } |
| } |
| } |
| }); |
| |
| ((EditText) v(R.id.urlInput)).addTextChangedListener(new TextWatcher() { |
| @Override |
| public void beforeTextChanged( |
| CharSequence s, int start, int count, int after) { |
| } |
| |
| @Override |
| public void onTextChanged( |
| CharSequence s, int start, int before, int count) { |
| } |
| |
| @Override |
| public void afterTextChanged(Editable s) { |
| if (Strings.isNullOrEmpty(textOf(R.id.nameInput))) { |
| try { |
| String host = new URL(s.toString()).getHost(); |
| int pos = host.indexOf("."); |
| WidgetUtil.setText(v(R.id.nameInput), |
| pos > 0 |
| ? host.substring(0, pos) |
| : host); |
| } catch (MalformedURLException e) { |
| // ignore |
| } |
| } |
| displayCredentialsInfo(s.toString()); |
| } |
| }); |
| |
| v(R.id.saveServerSettings).setOnClickListener(new View.OnClickListener() { |
| @Override |
| public void onClick(View v) { |
| enabledForm(false); |
| |
| if (!isServerInputComplete()) { |
| widgetUtil.showError(R.string.incompleteInput); |
| enabledForm(true); |
| return; |
| } |
| |
| if (!isUrlValid()) { |
| widgetUtil.showError(R.string.invalidUrl); |
| enabledForm(true); |
| return; |
| } |
| |
| if (!hasUniqueName()) { |
| widgetUtil.showError( |
| getString(R.string.duplicate_server_name, textOf(R.id.nameInput))); |
| enabledForm(true); |
| return; |
| } |
| |
| setVisible( |
| v(R.id.statusTestConnection, R.id.statusTestConnectionProgress)); |
| WidgetUtil.setText(v(R.id.statusTestConnectionText), null); |
| new AsyncTask<Void, Void, String>() { |
| private TextView status; |
| private View statusTestConnectionProgress; |
| private View statusTestConnection; |
| |
| @Override |
| protected void onPreExecute() { |
| super.onPreExecute(); |
| status = tv(R.id.statusTestConnectionText); |
| statusTestConnectionProgress = v(R.id.statusTestConnectionProgress); |
| statusTestConnection = v(R.id.statusTestConnection); |
| } |
| |
| @Override |
| protected String doInBackground(Void... v) { |
| return testConnection(); |
| } |
| |
| protected void onPostExecute(String errorMsg) { |
| if (errorMsg != null) { |
| enabledForm(true); |
| status.setTextColor(widgetUtil.color(R.color.statusFailed)); |
| status.setText(getString(R.string.test_server_connection_failed)); |
| setInvisible(statusTestConnectionProgress); |
| new AlertDialog.Builder(getContext()) |
| .setTitle(getString(R.string.error_title)) |
| .setMessage(getString(R.string.connection_failed, errorMsg)) |
| .setPositiveButton(android.R.string.ok, |
| new DialogInterface.OnClickListener() { |
| public void onClick(DialogInterface dialog, int which) { |
| // do nothing |
| } |
| }).setNegativeButton(getString(R.string.save_anyway), |
| new DialogInterface.OnClickListener() { |
| public void onClick(DialogInterface dialog, int which) { |
| setGone(statusTestConnection); |
| onServerSave(saveServerSettings()); |
| } |
| }).setIcon(android.R.drawable.ic_dialog_alert).show(); |
| } else { |
| status.setTextColor(widgetUtil.color(R.color.statusOk)); |
| status.setText(getString(R.string.test_server_connection_ok)); |
| setGone(statusTestConnection); |
| onServerSave(saveServerSettings()); |
| } |
| } |
| }.execute(); |
| } |
| }); |
| |
| enabledForm(true); |
| setGone(v(R.id.statusTestConnection)); |
| } |
| |
| private void enabledForm(boolean enable) { |
| v(R.id.urlInput).setEnabled(enable); |
| v(R.id.nameInput).setEnabled(enable); |
| v(R.id.pasteCredentialsButton).setEnabled(enable); |
| v(R.id.userInput).setEnabled(enable); |
| v(R.id.passwordInput).setEnabled(enable); |
| v(R.id.saveServerSettings).setEnabled(enable); |
| } |
| |
| private boolean isServerInputComplete() { |
| return !Strings.isNullOrEmpty(textOf(R.id.urlInput)) |
| && !Strings.isNullOrEmpty(textOf(R.id.nameInput)) |
| && !Strings.isNullOrEmpty(textOf(R.id.userInput)) |
| && !Strings.isNullOrEmpty(textOf(R.id.passwordInput)); |
| } |
| |
| private boolean isUrlValid() { |
| try { |
| new URL(textOf(R.id.urlInput)); |
| return true; |
| } catch (MalformedURLException e) { |
| return false; |
| } |
| } |
| |
| private boolean hasUniqueName() { |
| if (!Strings.isNullOrEmpty(textOf(R.id.idInput))) { |
| // update of existing entry |
| return true; |
| } |
| |
| ServerConfig otherCfg = |
| getApp().getConfigManager().getServerByName(textOf(R.id.nameInput)); |
| return otherCfg == null; |
| } |
| |
| private String testConnection() { |
| GerritApi api = new GerritRestApiFactory().create( |
| new GerritAuthData.Basic( |
| textOf(R.id.urlInput), |
| textOf(R.id.userInput), |
| textOf(R.id.passwordInput))); |
| try { |
| api.changes().query().withLimit(1).get(); |
| |
| String version = api.config().server().getVersion(); |
| Log.i(TAG, "version of " + textOf(R.id.nameInput) + ": " + version); |
| // TODO check server version (server must support labeled stars and |
| // run notedb) |
| return null; |
| } catch (HttpStatusException e) { |
| return e.getStatusCode() + " " + e.getStatusText(); |
| } catch (RestApiException e) { |
| // server not reachable |
| Log.e(TAG, "Request failed", e); |
| if (e.getCause() != null) { |
| return getString(R.string.error_with_cause, e.getMessage(), |
| e.getCause().getMessage()); |
| } else { |
| return e.getMessage(); |
| } |
| } |
| } |
| |
| private void onServerSave(String serverId) { |
| if (getArguments().get(ORIGIN).equals(IntroFragment2.class)) { |
| ConfigManager cfgManager = getApp().getConfigManager(); |
| cfgManager.setQueryConfig( |
| new QueryConfig.Builder(cfgManager.getQueryConfig()) |
| .setServerId(serverId) |
| .build()); |
| getApp().getPrefManager().setPreferences( |
| new Preferences.Builder(getApp().getPrefManager().getPreferences()) |
| .setShowIntro(false) |
| .build()); |
| startActivity(new Intent(getContext(), MainActivity.class)); |
| } else { |
| widgetUtil.toast(R.string.server_settings_saved); |
| display(ServerListFragment.class); |
| } |
| } |
| |
| private void displayCredentialsInfo(String url) { |
| if (Strings.isNullOrEmpty(url)) { |
| setGone(v(R.id.crendentialsInfo, R.id.credentialsInfoText, |
| R.id.pasteCredentialsButton)); |
| return; |
| } |
| |
| TextView credentialsInfo = (TextView) v(R.id.credentialsInfoText); |
| credentialsInfo.setMovementMethod(LinkMovementMethod.getInstance()); |
| url = FormatUtil.ensureSlash(url); |
| |
| String host; |
| try { |
| host = new URL(url).getHost(); |
| } catch (MalformedURLException e) { |
| setGone(v(R.id.crendentialsInfo, R.id.credentialsInfoText, |
| R.id.pasteCredentialsButton)); |
| return; |
| } |
| |
| if (host.endsWith(".googlesource.com")) { |
| url += "new-password"; |
| credentialsInfo.setText(Html.fromHtml( |
| getString(R.string.credentials_info_googlesource, |
| createLink(url, getString(R.string.googlesource_obtain_password))))); |
| setVisible(v(R.id.crendentialsInfo, R.id.credentialsInfoText, |
| R.id.pasteCredentialsButton)); |
| } else { |
| url += "#/settings/http-password"; |
| credentialsInfo.setText(Html.fromHtml( |
| getString(R.string.credentials_info, |
| createLink(url, getString(R.string.http_password))))); |
| setGone(v(R.id.pasteCredentialsButton)); |
| setVisible(v(R.id.crendentialsInfo, R.id.credentialsInfoText)); |
| } |
| } |
| |
| private String createLink(String url, String text) { |
| return "<a href=\"" + url + "\">" + text + "</a>"; |
| } |
| |
| private String saveServerSettings() { |
| return getApp().getConfigManager().setServerConfig( |
| new ServerConfig.Builder() |
| .setId(textOf(R.id.idInput)) |
| .setName(textOf(R.id.nameInput)) |
| .setUrl(textOf(R.id.urlInput)) |
| .setUser(textOf(R.id.userInput)) |
| .setPassword(textOf(R.id.passwordInput)) |
| .build()); |
| } |
| } |