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