blob: 8ac02042668182fbd32a5d9bd1ea141851513707 [file] [log] [blame]
// 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.app.Fragment;
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.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 {
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());
}
}