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