1package eu.siacs.conversations.ui;
2
3import java.util.ArrayList;
4import java.util.List;
5
6import eu.siacs.conversations.R;
7import eu.siacs.conversations.crypto.PgpEngine;
8import eu.siacs.conversations.crypto.PgpEngine.UserInputRequiredException;
9import eu.siacs.conversations.entities.Account;
10import eu.siacs.conversations.ui.EditAccount.EditAccountListener;
11import eu.siacs.conversations.xmpp.OnTLSExceptionReceived;
12import android.app.Activity;
13import android.app.AlertDialog;
14import android.content.Context;
15import android.content.DialogInterface;
16import android.content.DialogInterface.OnClickListener;
17import android.content.Intent;
18import android.content.IntentSender.SendIntentException;
19import android.os.Bundle;
20import android.util.Log;
21import android.view.ActionMode;
22import android.view.LayoutInflater;
23import android.view.Menu;
24import android.view.MenuInflater;
25import android.view.MenuItem;
26import android.view.View;
27import android.view.ViewGroup;
28import android.widget.AdapterView;
29import android.widget.AdapterView.OnItemClickListener;
30import android.widget.AdapterView.OnItemLongClickListener;
31import android.widget.ArrayAdapter;
32import android.widget.ListView;
33import android.widget.TextView;
34
35public class ManageAccountActivity extends XmppActivity {
36
37 public static final int REQUEST_ANNOUNCE_PGP = 0x73731;
38
39 protected boolean isActionMode = false;
40 protected ActionMode actionMode;
41 protected Account selectedAccountForActionMode = null;
42 protected ManageAccountActivity activity = this;
43
44 protected List<Account> accountList = new ArrayList<Account>();
45 protected ListView accountListView;
46 protected ArrayAdapter<Account> accountListViewAdapter;
47 protected OnAccountListChangedListener accountChanged = new OnAccountListChangedListener() {
48
49 @Override
50 public void onAccountListChangedListener() {
51 Log.d("xmppService", "ui on account list changed listener");
52 accountList.clear();
53 accountList.addAll(xmppConnectionService.getAccounts());
54 runOnUiThread(new Runnable() {
55
56 @Override
57 public void run() {
58 accountListViewAdapter.notifyDataSetChanged();
59 }
60 });
61 }
62 };
63
64 protected OnTLSExceptionReceived tlsExceptionReceived = new OnTLSExceptionReceived() {
65
66 @Override
67 public void onTLSExceptionReceived(final String fingerprint, final Account account) {
68 activity.runOnUiThread(new Runnable() {
69
70 @Override
71 public void run() {
72 AlertDialog.Builder builder = new AlertDialog.Builder(activity);
73 builder.setTitle("Untrusted Certificate");
74 builder.setIconAttribute(android.R.attr.alertDialogIcon);
75 View view = (View) getLayoutInflater().inflate(R.layout.cert_warning, null);
76 TextView sha = (TextView) view.findViewById(R.id.sha);
77 TextView hint = (TextView) view.findViewById(R.id.hint);
78 StringBuilder humanReadableSha = new StringBuilder();
79 humanReadableSha.append(fingerprint);
80 for(int i = 2; i < 59; i += 3) {
81 Log.d("gultsch","insert into "+i);
82 if ((i==14)||(i==29)||(i==44)) {
83 humanReadableSha.insert(i, "\n");
84 } else {
85 humanReadableSha.insert(i, ":");
86 }
87
88 }
89 hint.setText(getString(R.string.untrusted_cert_hint,account.getServer()));
90 sha.setText(humanReadableSha.toString());
91 builder.setView(view);
92 //builder.setMessage(server+" presented you with an unstrusted, possible self signed, certificate. The SHA1 fingerprint is "+fingerprint+" Do not connect unless you know exactly what you are doing");
93 builder.setNegativeButton("Don't connect", null);
94 builder.setPositiveButton("Trust certificate", new OnClickListener() {
95
96 @Override
97 public void onClick(DialogInterface dialog, int which) {
98 account.setSSLCertFingerprint(fingerprint);
99 activity.xmppConnectionService.updateAccount(account);
100 }
101 });
102 builder.create().show();
103 }
104 });
105
106 }
107 };
108
109 @Override
110 protected void onCreate(Bundle savedInstanceState) {
111
112 super.onCreate(savedInstanceState);
113
114 setContentView(R.layout.manage_accounts);
115
116 accountListView = (ListView) findViewById(R.id.account_list);
117 accountListViewAdapter = new ArrayAdapter<Account>(
118 getApplicationContext(), R.layout.account_row, this.accountList) {
119 @Override
120 public View getView(int position, View view, ViewGroup parent) {
121 Account account = getItem(position);
122 if (view == null) {
123 LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
124 view = (View) inflater.inflate(R.layout.account_row, null);
125 }
126 ((TextView) view.findViewById(R.id.account_jid))
127 .setText(account.getJid());
128 TextView statusView = (TextView) view
129 .findViewById(R.id.account_status);
130 switch (account.getStatus()) {
131 case Account.STATUS_DISABLED:
132 statusView.setText("temporarily disabled");
133 statusView.setTextColor(0xFF1da9da);
134 break;
135 case Account.STATUS_ONLINE:
136 statusView.setText("online");
137 statusView.setTextColor(0xFF83b600);
138 break;
139 case Account.STATUS_CONNECTING:
140 statusView.setText("connecting\u2026");
141 statusView.setTextColor(0xFF1da9da);
142 break;
143 case Account.STATUS_OFFLINE:
144 statusView.setText("offline");
145 statusView.setTextColor(0xFFe92727);
146 break;
147 case Account.STATUS_UNAUTHORIZED:
148 statusView.setText("unauthorized");
149 statusView.setTextColor(0xFFe92727);
150 break;
151 case Account.STATUS_SERVER_NOT_FOUND:
152 statusView.setText("server not found");
153 statusView.setTextColor(0xFFe92727);
154 break;
155 case Account.STATUS_NO_INTERNET:
156 statusView.setText("no internet");
157 statusView.setTextColor(0xFFe92727);
158 break;
159 case Account.STATUS_SERVER_REQUIRES_TLS:
160 statusView.setText("server requires TLS");
161 statusView.setTextColor(0xFFe92727);
162 break;
163 case Account.STATUS_TLS_ERROR:
164 statusView.setText("untrusted cerficate");
165 statusView.setTextColor(0xFFe92727);
166 break;
167 default:
168 break;
169 }
170
171 return view;
172 }
173 };
174 final XmppActivity activity = this;
175 accountListView.setAdapter(this.accountListViewAdapter);
176 accountListView.setOnItemClickListener(new OnItemClickListener() {
177
178 @Override
179 public void onItemClick(AdapterView<?> arg0, View view,
180 int position, long arg3) {
181 if (!isActionMode) {
182 Account account = accountList.get(position);
183 if ((account.getStatus() != Account.STATUS_ONLINE)&&(account.getStatus() != Account.STATUS_CONNECTING)&&(!account.isOptionSet(Account.OPTION_DISABLED))) {
184 activity.xmppConnectionService.reconnectAccount(accountList.get(position));
185 } else if (account.getStatus() == Account.STATUS_ONLINE) {
186 activity.startActivity(new Intent(activity.getApplicationContext(),NewConversationActivity.class));
187 }
188
189 Log.d("gultsch","clicked on account "+accountList.get(position).getJid());
190 } else {
191 selectedAccountForActionMode = accountList.get(position);
192 actionMode.invalidate();
193 }
194 }
195 });
196 accountListView.setOnItemLongClickListener(new OnItemLongClickListener() {
197
198 @Override
199 public boolean onItemLongClick(AdapterView<?> arg0, View view,
200 int position, long arg3) {
201 if (!isActionMode) {
202 accountListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
203 accountListView.setItemChecked(position,true);
204 selectedAccountForActionMode = accountList.get(position);
205 actionMode = activity.startActionMode((new ActionMode.Callback() {
206
207 @Override
208 public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
209 if (selectedAccountForActionMode.isOptionSet(Account.OPTION_DISABLED)) {
210 menu.findItem(R.id.mgmt_account_enable).setVisible(true);
211 menu.findItem(R.id.mgmt_account_disable).setVisible(false);
212 } else {
213 menu.findItem(R.id.mgmt_account_disable).setVisible(true);
214 menu.findItem(R.id.mgmt_account_enable).setVisible(false);
215 }
216 return true;
217 }
218
219 @Override
220 public void onDestroyActionMode(ActionMode mode) {
221 // TODO Auto-generated method stub
222
223 }
224
225 @Override
226 public boolean onCreateActionMode(ActionMode mode, Menu menu) {
227 MenuInflater inflater = mode.getMenuInflater();
228 inflater.inflate(R.menu.manageaccounts_context, menu);
229 return true;
230 }
231
232 @Override
233 public boolean onActionItemClicked(final ActionMode mode, MenuItem item) {
234 if (item.getItemId()==R.id.mgmt_account_edit) {
235 EditAccount dialog = new EditAccount();
236 dialog.setAccount(selectedAccountForActionMode);
237 dialog.setEditAccountListener(new EditAccountListener() {
238
239 @Override
240 public void onAccountEdited(Account account) {
241 xmppConnectionService.updateAccount(account);
242 actionMode.finish();
243 }
244 });
245 dialog.show(getFragmentManager(), "edit_account");
246 } else if (item.getItemId()==R.id.mgmt_account_disable) {
247 selectedAccountForActionMode.setOption(Account.OPTION_DISABLED, true);
248 xmppConnectionService.updateAccount(selectedAccountForActionMode);
249 mode.finish();
250 } else if (item.getItemId()==R.id.mgmt_account_enable) {
251 selectedAccountForActionMode.setOption(Account.OPTION_DISABLED, false);
252 xmppConnectionService.updateAccount(selectedAccountForActionMode);
253 mode.finish();
254 } else if (item.getItemId()==R.id.mgmt_account_delete) {
255 AlertDialog.Builder builder = new AlertDialog.Builder(activity);
256 builder.setTitle("Are you sure?");
257 builder.setIconAttribute(android.R.attr.alertDialogIcon);
258 builder.setMessage("If you delete your account your entire conversation history will be lost");
259 builder.setPositiveButton("Delete", new OnClickListener() {
260
261 @Override
262 public void onClick(DialogInterface dialog, int which) {
263 xmppConnectionService.deleteAccount(selectedAccountForActionMode);
264 selectedAccountForActionMode = null;
265 mode.finish();
266 }
267 });
268 builder.setNegativeButton("Cancel",null);
269 builder.create().show();
270 } else if (item.getItemId()==R.id.mgmt_account_announce_pgp) {
271 if (activity.hasPgp()) {
272 mode.finish();
273 try {
274 xmppConnectionService.generatePgpAnnouncement(selectedAccountForActionMode);
275 } catch (PgpEngine.UserInputRequiredException e) {
276 try {
277 startIntentSenderForResult(e.getPendingIntent().getIntentSender(), REQUEST_ANNOUNCE_PGP, null, 0, 0, 0);
278 } catch (SendIntentException e1) {
279 Log.d("gultsch","sending intent failed");
280 }
281 }
282 }
283 }
284 return true;
285 }
286 }));
287 return true;
288 } else {
289 return false;
290 }
291 }
292 });
293 }
294
295 @Override
296 protected void onStop() {
297 if (xmppConnectionServiceBound) {
298 xmppConnectionService.removeOnAccountListChangedListener();
299 xmppConnectionService.removeOnTLSExceptionReceivedListener();
300 }
301 super.onStop();
302 }
303
304 @Override
305 void onBackendConnected() {
306 xmppConnectionService.setOnAccountListChangedListener(accountChanged);
307 xmppConnectionService.setOnTLSExceptionReceivedListener(tlsExceptionReceived);
308 this.accountList.clear();
309 this.accountList.addAll(xmppConnectionService.getAccounts());
310 accountListViewAdapter.notifyDataSetChanged();
311 if (this.accountList.size() == 0) {
312 getActionBar().setDisplayHomeAsUpEnabled(false);
313 addAccount();
314 }
315 }
316
317 @Override
318 public boolean onCreateOptionsMenu(Menu menu) {
319 getMenuInflater().inflate(R.menu.manageaccounts, menu);
320 return true;
321 }
322
323 @Override
324 public boolean onOptionsItemSelected(MenuItem item) {
325 switch (item.getItemId()) {
326 case R.id.action_settings:
327 startActivity(new Intent(this, SettingsActivity.class));
328 break;
329 case R.id.action_add_account:
330 addAccount();
331 break;
332 default:
333 break;
334 }
335 return super.onOptionsItemSelected(item);
336 }
337
338 protected void addAccount() {
339 final Activity activity = this;
340 EditAccount dialog = new EditAccount();
341 dialog.setEditAccountListener(new EditAccountListener() {
342
343 @Override
344 public void onAccountEdited(Account account) {
345 xmppConnectionService.createAccount(account);
346 activity.getActionBar().setDisplayHomeAsUpEnabled(true);
347 }
348 });
349 dialog.show(getFragmentManager(), "add_account");
350 }
351
352
353 @Override
354 public void onActionModeStarted(ActionMode mode) {
355 super.onActionModeStarted(mode);
356 this.isActionMode = true;
357 }
358
359 @Override
360 public void onActionModeFinished(ActionMode mode) {
361 super.onActionModeFinished(mode);
362 this.isActionMode = false;
363 accountListView.clearChoices();
364 accountListView.requestLayout();
365 accountListView.post(new Runnable() {
366 @Override
367 public void run() {
368 accountListView.setChoiceMode(ListView.CHOICE_MODE_NONE);
369 }
370 });
371 }
372
373 @Override
374 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
375 super.onActivityResult(requestCode, resultCode, data);
376 if (resultCode == RESULT_OK) {
377 if (requestCode == REQUEST_ANNOUNCE_PGP) {
378 try {
379 xmppConnectionService.generatePgpAnnouncement(selectedAccountForActionMode);
380 } catch (UserInputRequiredException e) {
381 Log.d("gultsch","already came back. ignoring");
382 }
383 }
384 }
385 }
386}