1package eu.siacs.conversations.ui;
2
3import android.app.AlertDialog;
4import android.app.PendingIntent;
5import android.content.Intent;
6import android.graphics.Bitmap;
7import android.graphics.Color;
8import android.graphics.Point;
9import android.os.Bundle;
10import android.text.Editable;
11import android.text.TextWatcher;
12import android.util.Log;
13import android.view.Menu;
14import android.view.MenuItem;
15import android.view.View;
16import android.view.View.OnClickListener;
17import android.widget.AutoCompleteTextView;
18import android.widget.Button;
19import android.widget.CheckBox;
20import android.widget.CompoundButton;
21import android.widget.CompoundButton.OnCheckedChangeListener;
22import android.widget.EditText;
23import android.widget.ImageButton;
24import android.widget.ImageView;
25import android.widget.LinearLayout;
26import android.widget.RelativeLayout;
27import android.widget.TextView;
28import android.widget.Toast;
29
30import com.google.zxing.BarcodeFormat;
31import com.google.zxing.EncodeHintType;
32import com.google.zxing.WriterException;
33import com.google.zxing.common.BitMatrix;
34import com.google.zxing.integration.android.IntentIntegrator;
35import com.google.zxing.integration.android.IntentResult;
36import com.google.zxing.qrcode.QRCodeWriter;
37import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
38
39import java.util.Hashtable;
40
41import eu.siacs.conversations.Config;
42import eu.siacs.conversations.R;
43import eu.siacs.conversations.entities.Account;
44import eu.siacs.conversations.entities.Conversation;
45import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate;
46import eu.siacs.conversations.ui.adapter.KnownHostsAdapter;
47import eu.siacs.conversations.utils.UIHelper;
48import eu.siacs.conversations.utils.Validator;
49import eu.siacs.conversations.xmpp.XmppConnection.Features;
50import eu.siacs.conversations.xmpp.pep.Avatar;
51
52public class EditAccountActivity extends XmppActivity {
53
54 private AutoCompleteTextView mAccountJid;
55 private EditText mPassword;
56 private EditText mPasswordConfirm;
57 private CheckBox mRegisterNew;
58 private Button mCancelButton;
59 private Button mSaveButton;
60
61 private LinearLayout mStats;
62 private TextView mServerInfoSm;
63 private TextView mServerInfoCarbons;
64 private TextView mServerInfoPep;
65 private TextView mSessionEst;
66 private TextView mOtrFingerprint;
67 private RelativeLayout mOtrFingerprintBox;
68 private ImageButton mOtrFingerprintToClipboardButton;
69
70 private String jidToEdit;
71 private Account mAccount;
72
73 private boolean mFetchingAvatar = false;
74
75 private OnClickListener mSaveButtonClickListener = new OnClickListener() {
76
77 @Override
78 public void onClick(View v) {
79 if (mAccount != null
80 && mAccount.getStatus() == Account.STATUS_DISABLED) {
81 mAccount.setOption(Account.OPTION_DISABLED, false);
82 xmppConnectionService.updateAccount(mAccount);
83 return;
84 }
85 if (!Validator.isValidJid(mAccountJid.getText().toString())) {
86 mAccountJid.setError(getString(R.string.invalid_jid));
87 mAccountJid.requestFocus();
88 return;
89 }
90 boolean registerNewAccount = mRegisterNew.isChecked();
91 String[] jidParts = mAccountJid.getText().toString().split("@");
92 String username = jidParts[0];
93 String server;
94 if (jidParts.length >= 2) {
95 server = jidParts[1];
96 } else {
97 server = "";
98 }
99 String password = mPassword.getText().toString();
100 String passwordConfirm = mPasswordConfirm.getText().toString();
101 if (registerNewAccount) {
102 if (!password.equals(passwordConfirm)) {
103 mPasswordConfirm
104 .setError(getString(R.string.passwords_do_not_match));
105 mPasswordConfirm.requestFocus();
106 return;
107 }
108 }
109 if (mAccount != null) {
110 mAccount.setPassword(password);
111 mAccount.setUsername(username);
112 mAccount.setServer(server);
113 mAccount.setOption(Account.OPTION_REGISTER, registerNewAccount);
114 xmppConnectionService.updateAccount(mAccount);
115 } else {
116 if (xmppConnectionService.findAccountByJid(mAccountJid
117 .getText().toString()) != null) {
118 mAccountJid
119 .setError(getString(R.string.account_already_exists));
120 mAccountJid.requestFocus();
121 return;
122 }
123 mAccount = new Account(username, server, password);
124 mAccount.setOption(Account.OPTION_USETLS, true);
125 mAccount.setOption(Account.OPTION_USECOMPRESSION, true);
126 mAccount.setOption(Account.OPTION_REGISTER, registerNewAccount);
127 xmppConnectionService.createAccount(mAccount);
128 }
129 if (jidToEdit != null) {
130 finish();
131 } else {
132 updateSaveButton();
133 updateAccountInformation();
134 }
135
136 }
137 };
138 private OnClickListener mCancelButtonClickListener = new OnClickListener() {
139
140 @Override
141 public void onClick(View v) {
142 finish();
143 }
144 };
145 private OnAccountUpdate mOnAccountUpdateListener = new OnAccountUpdate() {
146
147 @Override
148 public void onAccountUpdate() {
149 runOnUiThread(new Runnable() {
150
151 @Override
152 public void run() {
153 if (mAccount != null
154 && mAccount.getStatus() != Account.STATUS_ONLINE
155 && mFetchingAvatar) {
156 startActivity(new Intent(getApplicationContext(),
157 ManageAccountActivity.class));
158 finish();
159 } else if (jidToEdit == null && mAccount != null
160 && mAccount.getStatus() == Account.STATUS_ONLINE) {
161 if (!mFetchingAvatar) {
162 mFetchingAvatar = true;
163 xmppConnectionService.checkForAvatar(mAccount,
164 mAvatarFetchCallback);
165 }
166 } else {
167 updateSaveButton();
168 }
169 if (mAccount != null) {
170 updateAccountInformation();
171 }
172 }
173 });
174 }
175 };
176 private UiCallback<Avatar> mAvatarFetchCallback = new UiCallback<Avatar>() {
177
178 @Override
179 public void userInputRequried(PendingIntent pi, Avatar avatar) {
180 finishInitialSetup(avatar);
181 }
182
183 @Override
184 public void success(Avatar avatar) {
185 finishInitialSetup(avatar);
186 }
187
188 @Override
189 public void error(int errorCode, Avatar avatar) {
190 finishInitialSetup(avatar);
191 }
192 };
193 private KnownHostsAdapter mKnownHostsAdapter;
194 private TextWatcher mTextWatcher = new TextWatcher() {
195
196 @Override
197 public void onTextChanged(CharSequence s, int start, int before,
198 int count) {
199 updateSaveButton();
200 }
201
202 @Override
203 public void beforeTextChanged(CharSequence s, int start, int count,
204 int after) {
205
206 }
207
208 @Override
209 public void afterTextChanged(Editable s) {
210
211 }
212 };
213
214 protected void finishInitialSetup(final Avatar avatar) {
215 runOnUiThread(new Runnable() {
216
217 @Override
218 public void run() {
219 Intent intent;
220 if (avatar != null) {
221 intent = new Intent(getApplicationContext(),
222 StartConversationActivity.class);
223 } else {
224 intent = new Intent(getApplicationContext(),
225 PublishProfilePictureActivity.class);
226 intent.putExtra("account", mAccount.getJid());
227 intent.putExtra("setup", true);
228 }
229 startActivity(intent);
230 finish();
231 }
232 });
233 }
234
235 protected boolean inputDataDiffersFromAccount() {
236 if (mAccount == null) {
237 return true;
238 } else {
239 return (!mAccount.getJid().equals(mAccountJid.getText().toString()))
240 || (!mAccount.getPassword().equals(
241 mPassword.getText().toString()) || mAccount
242 .isOptionSet(Account.OPTION_REGISTER) != mRegisterNew
243 .isChecked());
244 }
245 }
246
247 protected void updateSaveButton() {
248 if (mAccount != null
249 && mAccount.getStatus() == Account.STATUS_CONNECTING) {
250 this.mSaveButton.setEnabled(false);
251 this.mSaveButton.setTextColor(getSecondaryTextColor());
252 this.mSaveButton.setText(R.string.account_status_connecting);
253 } else if (mAccount != null
254 && mAccount.getStatus() == Account.STATUS_DISABLED) {
255 this.mSaveButton.setEnabled(true);
256 this.mSaveButton.setTextColor(getPrimaryTextColor());
257 this.mSaveButton.setText(R.string.enable);
258 } else {
259 this.mSaveButton.setEnabled(true);
260 this.mSaveButton.setTextColor(getPrimaryTextColor());
261 if (jidToEdit != null) {
262 if (mAccount != null
263 && mAccount.getStatus() == Account.STATUS_ONLINE) {
264 this.mSaveButton.setText(R.string.save);
265 if (!accountInfoEdited()) {
266 this.mSaveButton.setEnabled(false);
267 this.mSaveButton.setTextColor(getSecondaryTextColor());
268 }
269 } else {
270 this.mSaveButton.setText(R.string.connect);
271 }
272 } else {
273 this.mSaveButton.setText(R.string.next);
274 }
275 }
276 }
277
278 protected boolean accountInfoEdited() {
279 return (!this.mAccount.getJid().equals(
280 this.mAccountJid.getText().toString()))
281 || (!this.mAccount.getPassword().equals(
282 this.mPassword.getText().toString()));
283 }
284
285 @Override
286 protected String getShareableUri() {
287 if (mAccount!=null) {
288 return "xmpp:"+mAccount.getJid();
289 } else {
290 return "";
291 }
292 }
293
294 @Override
295 protected void onCreate(Bundle savedInstanceState) {
296 super.onCreate(savedInstanceState);
297 setContentView(R.layout.activity_edit_account);
298 this.mAccountJid = (AutoCompleteTextView) findViewById(R.id.account_jid);
299 this.mAccountJid.addTextChangedListener(this.mTextWatcher);
300 this.mPassword = (EditText) findViewById(R.id.account_password);
301 this.mPassword.addTextChangedListener(this.mTextWatcher);
302 this.mPasswordConfirm = (EditText) findViewById(R.id.account_password_confirm);
303 this.mRegisterNew = (CheckBox) findViewById(R.id.account_register_new);
304 this.mStats = (LinearLayout) findViewById(R.id.stats);
305 this.mSessionEst = (TextView) findViewById(R.id.session_est);
306 this.mServerInfoCarbons = (TextView) findViewById(R.id.server_info_carbons);
307 this.mServerInfoSm = (TextView) findViewById(R.id.server_info_sm);
308 this.mServerInfoPep = (TextView) findViewById(R.id.server_info_pep);
309 this.mOtrFingerprint = (TextView) findViewById(R.id.otr_fingerprint);
310 this.mOtrFingerprintBox = (RelativeLayout) findViewById(R.id.otr_fingerprint_box);
311 this.mOtrFingerprintToClipboardButton = (ImageButton) findViewById(R.id.action_copy_to_clipboard);
312 this.mSaveButton = (Button) findViewById(R.id.save_button);
313 this.mCancelButton = (Button) findViewById(R.id.cancel_button);
314 this.mSaveButton.setOnClickListener(this.mSaveButtonClickListener);
315 this.mCancelButton.setOnClickListener(this.mCancelButtonClickListener);
316 this.mRegisterNew
317 .setOnCheckedChangeListener(new OnCheckedChangeListener() {
318
319 @Override
320 public void onCheckedChanged(CompoundButton buttonView,
321 boolean isChecked) {
322 if (isChecked) {
323 mPasswordConfirm.setVisibility(View.VISIBLE);
324 } else {
325 mPasswordConfirm.setVisibility(View.GONE);
326 }
327 updateSaveButton();
328 }
329 });
330 }
331
332 @Override
333 public boolean onCreateOptionsMenu(Menu menu) {
334 super.onCreateOptionsMenu(menu);
335 getMenuInflater().inflate(R.menu.editaccount, menu);
336 MenuItem showQrCode = menu.findItem(R.id.action_show_qr_code);
337 if (mAccount == null) {
338 showQrCode.setVisible(false);
339 }
340 return true;
341 }
342
343 @Override
344 protected void onStart() {
345 super.onStart();
346 if (getIntent() != null) {
347 this.jidToEdit = getIntent().getStringExtra("jid");
348 if (this.jidToEdit != null) {
349 this.mRegisterNew.setVisibility(View.GONE);
350 getActionBar().setTitle(jidToEdit);
351 } else {
352 getActionBar().setTitle(R.string.action_add_account);
353 }
354 }
355 }
356
357 @Override
358 protected void onStop() {
359 if (xmppConnectionServiceBound) {
360 xmppConnectionService.removeOnAccountListChangedListener();
361 }
362 super.onStop();
363 }
364
365 @Override
366 protected void onBackendConnected() {
367 this.mKnownHostsAdapter = new KnownHostsAdapter(this,
368 android.R.layout.simple_list_item_1,
369 xmppConnectionService.getKnownHosts());
370 this.xmppConnectionService
371 .setOnAccountListChangedListener(this.mOnAccountUpdateListener);
372 if (this.jidToEdit != null) {
373 this.mAccount = xmppConnectionService.findAccountByJid(jidToEdit);
374 updateAccountInformation();
375 } else if (this.xmppConnectionService.getAccounts().size() == 0) {
376 getActionBar().setDisplayHomeAsUpEnabled(false);
377 getActionBar().setDisplayShowHomeEnabled(false);
378 this.mCancelButton.setEnabled(false);
379 this.mCancelButton.setTextColor(getSecondaryTextColor());
380 }
381 this.mAccountJid.setAdapter(this.mKnownHostsAdapter);
382 updateSaveButton();
383 }
384
385 private void updateAccountInformation() {
386 this.mAccountJid.setText(this.mAccount.getJid());
387 this.mPassword.setText(this.mAccount.getPassword());
388 if (this.mAccount.isOptionSet(Account.OPTION_REGISTER)) {
389 this.mRegisterNew.setVisibility(View.VISIBLE);
390 this.mRegisterNew.setChecked(true);
391 this.mPasswordConfirm.setText(this.mAccount.getPassword());
392 } else {
393 this.mRegisterNew.setVisibility(View.GONE);
394 this.mRegisterNew.setChecked(false);
395 }
396 if (this.mAccount.getStatus() == Account.STATUS_ONLINE
397 && !this.mFetchingAvatar) {
398 this.mStats.setVisibility(View.VISIBLE);
399 this.mSessionEst.setText(UIHelper.readableTimeDifference(
400 getApplicationContext(), this.mAccount.getXmppConnection()
401 .getLastSessionEstablished()));
402 Features features = this.mAccount.getXmppConnection().getFeatures();
403 if (features.carbons()) {
404 this.mServerInfoCarbons.setText(R.string.server_info_available);
405 } else {
406 this.mServerInfoCarbons
407 .setText(R.string.server_info_unavailable);
408 }
409 if (features.sm()) {
410 this.mServerInfoSm.setText(R.string.server_info_available);
411 } else {
412 this.mServerInfoSm.setText(R.string.server_info_unavailable);
413 }
414 if (features.pubsub()) {
415 this.mServerInfoPep.setText(R.string.server_info_available);
416 } else {
417 this.mServerInfoPep.setText(R.string.server_info_unavailable);
418 }
419 final String fingerprint = this.mAccount
420 .getOtrFingerprint(xmppConnectionService);
421 if (fingerprint != null) {
422 this.mOtrFingerprintBox.setVisibility(View.VISIBLE);
423 this.mOtrFingerprint.setText(fingerprint);
424 this.mOtrFingerprintToClipboardButton
425 .setVisibility(View.VISIBLE);
426 this.mOtrFingerprintToClipboardButton
427 .setOnClickListener(new View.OnClickListener() {
428
429 @Override
430 public void onClick(View v) {
431
432 if (copyTextToClipboard(fingerprint, R.string.otr_fingerprint)) {
433 Toast.makeText(
434 EditAccountActivity.this,
435 R.string.toast_message_otr_fingerprint,
436 Toast.LENGTH_SHORT).show();
437 }
438 }
439 });
440 } else {
441 this.mOtrFingerprintBox.setVisibility(View.GONE);
442 }
443 } else {
444 if (this.mAccount.errorStatus()) {
445 this.mAccountJid.setError(getString(this.mAccount
446 .getReadableStatusId()));
447 this.mAccountJid.requestFocus();
448 }
449 this.mStats.setVisibility(View.GONE);
450 }
451 }
452}