1package eu.siacs.conversations.ui;
2
3import eu.siacs.conversations.R;
4import eu.siacs.conversations.entities.Account;
5import eu.siacs.conversations.entities.Contact;
6import eu.siacs.conversations.entities.Conversation;
7import eu.siacs.conversations.entities.Message;
8import eu.siacs.conversations.entities.Presences;
9import eu.siacs.conversations.services.XmppConnectionService;
10import eu.siacs.conversations.services.XmppConnectionService.XmppConnectionBinder;
11import eu.siacs.conversations.utils.ExceptionHelper;
12import android.app.Activity;
13import android.app.AlertDialog;
14import android.app.PendingIntent;
15import android.app.AlertDialog.Builder;
16import android.content.ComponentName;
17import android.content.Context;
18import android.content.DialogInterface;
19import android.content.DialogInterface.OnClickListener;
20import android.content.IntentSender.SendIntentException;
21import android.content.Intent;
22import android.content.ServiceConnection;
23import android.net.Uri;
24import android.os.Bundle;
25import android.os.IBinder;
26import android.util.Log;
27import android.view.MenuItem;
28import android.view.View;
29import android.view.inputmethod.InputMethodManager;
30import android.widget.EditText;
31
32public abstract class XmppActivity extends Activity {
33
34 public static final int REQUEST_ANNOUNCE_PGP = 0x73731;
35 protected static final int REQUEST_INVITE_TO_CONVERSATION = 0x341830;
36
37 protected final static String LOGTAG = "xmppService";
38
39 public XmppConnectionService xmppConnectionService;
40 public boolean xmppConnectionServiceBound = false;
41 protected boolean handledViewIntent = false;
42
43 protected int mPrimaryTextColor;
44 protected int mSecondaryTextColor;
45
46 protected interface OnValueEdited {
47 public void onValueEdited(String value);
48 }
49
50 public interface OnPresenceSelected {
51 public void onPresenceSelected();
52 }
53
54 protected ServiceConnection mConnection = new ServiceConnection() {
55
56 @Override
57 public void onServiceConnected(ComponentName className, IBinder service) {
58 XmppConnectionBinder binder = (XmppConnectionBinder) service;
59 xmppConnectionService = binder.getService();
60 xmppConnectionServiceBound = true;
61 onBackendConnected();
62 }
63
64 @Override
65 public void onServiceDisconnected(ComponentName arg0) {
66 xmppConnectionServiceBound = false;
67 }
68 };
69
70 @Override
71 protected void onStart() {
72 super.onStart();
73 if (!xmppConnectionServiceBound) {
74 connectToBackend();
75 }
76 }
77
78 public void connectToBackend() {
79 Intent intent = new Intent(this, XmppConnectionService.class);
80 intent.setAction("ui");
81 startService(intent);
82 bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
83 }
84
85 @Override
86 protected void onStop() {
87 super.onStop();
88 if (xmppConnectionServiceBound) {
89 unbindService(mConnection);
90 xmppConnectionServiceBound = false;
91 }
92 }
93
94 protected void hideKeyboard() {
95 InputMethodManager inputManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
96
97 View focus = getCurrentFocus();
98
99 if (focus != null) {
100
101 inputManager.hideSoftInputFromWindow(focus.getWindowToken(),
102 InputMethodManager.HIDE_NOT_ALWAYS);
103 }
104 }
105
106 public boolean hasPgp() {
107 return xmppConnectionService.getPgpEngine() != null;
108 }
109
110 public void showInstallPgpDialog() {
111 Builder builder = new AlertDialog.Builder(this);
112 builder.setTitle(getString(R.string.openkeychain_required));
113 builder.setIconAttribute(android.R.attr.alertDialogIcon);
114 builder.setMessage(getText(R.string.openkeychain_required_long));
115 builder.setNegativeButton(getString(R.string.cancel), null);
116 builder.setNeutralButton(getString(R.string.restart),
117 new OnClickListener() {
118
119 @Override
120 public void onClick(DialogInterface dialog, int which) {
121 if (xmppConnectionServiceBound) {
122 unbindService(mConnection);
123 xmppConnectionServiceBound = false;
124 }
125 stopService(new Intent(XmppActivity.this,
126 XmppConnectionService.class));
127 finish();
128 }
129 });
130 builder.setPositiveButton(getString(R.string.install),
131 new OnClickListener() {
132
133 @Override
134 public void onClick(DialogInterface dialog, int which) {
135 Uri uri = Uri
136 .parse("market://details?id=org.sufficientlysecure.keychain");
137 Intent intent = new Intent(Intent.ACTION_VIEW, uri);
138 startActivity(intent);
139 finish();
140 }
141 });
142 builder.create().show();
143 }
144
145 abstract void onBackendConnected();
146
147 public boolean onOptionsItemSelected(MenuItem item) {
148 switch (item.getItemId()) {
149 case R.id.action_settings:
150 startActivity(new Intent(this, SettingsActivity.class));
151 break;
152 case R.id.action_accounts:
153 startActivity(new Intent(this, ManageAccountActivity.class));
154 break;
155 }
156 return super.onOptionsItemSelected(item);
157 }
158
159 @Override
160 protected void onCreate(Bundle savedInstanceState) {
161 super.onCreate(savedInstanceState);
162 ExceptionHelper.init(getApplicationContext());
163 mPrimaryTextColor = getResources().getColor(R.color.primarytext);
164 mSecondaryTextColor = getResources().getColor(R.color.secondarytext);
165 }
166
167 public void switchToConversation(Conversation conversation) {
168 switchToConversation(conversation, null, false);
169 }
170
171 public void switchToConversation(Conversation conversation, String text,
172 boolean newTask) {
173 Intent viewConversationIntent = new Intent(this,
174 ConversationActivity.class);
175 viewConversationIntent.setAction(Intent.ACTION_VIEW);
176 viewConversationIntent.putExtra(ConversationActivity.CONVERSATION,
177 conversation.getUuid());
178 if (text != null) {
179 viewConversationIntent.putExtra(ConversationActivity.TEXT, text);
180 }
181 viewConversationIntent.setType(ConversationActivity.VIEW_CONVERSATION);
182 if (newTask) {
183 viewConversationIntent.setFlags(viewConversationIntent.getFlags()
184 | Intent.FLAG_ACTIVITY_NEW_TASK
185 | Intent.FLAG_ACTIVITY_SINGLE_TOP);
186 } else {
187 viewConversationIntent.setFlags(viewConversationIntent.getFlags()
188 | Intent.FLAG_ACTIVITY_CLEAR_TOP);
189 }
190 startActivity(viewConversationIntent);
191 }
192
193 public void switchToContactDetails(Contact contact) {
194 Intent intent = new Intent(this, ContactDetailsActivity.class);
195 intent.setAction(ContactDetailsActivity.ACTION_VIEW_CONTACT);
196 intent.putExtra("account", contact.getAccount().getJid());
197 intent.putExtra("contact", contact.getJid());
198 startActivity(intent);
199 }
200
201 protected void inviteToConversation(Conversation conversation) {
202 Intent intent = new Intent(getApplicationContext(), ChooseContactActivity.class);
203 intent.putExtra("conversation",conversation.getUuid());
204 startActivityForResult(intent, REQUEST_INVITE_TO_CONVERSATION);
205 }
206
207 protected void announcePgp(Account account, final Conversation conversation) {
208 xmppConnectionService.getPgpEngine().generateSignature(account,
209 "online", new UiCallback<Account>() {
210
211 @Override
212 public void userInputRequried(PendingIntent pi,
213 Account account) {
214 try {
215 startIntentSenderForResult(pi.getIntentSender(),
216 REQUEST_ANNOUNCE_PGP, null, 0, 0, 0);
217 } catch (SendIntentException e) {}
218 }
219
220 @Override
221 public void success(Account account) {
222 xmppConnectionService.databaseBackend
223 .updateAccount(account);
224 xmppConnectionService.sendPresencePacket(account,
225 xmppConnectionService.getPresenceGenerator()
226 .sendPresence(account));
227 if (conversation != null) {
228 conversation
229 .setNextEncryption(Message.ENCRYPTION_PGP);
230 }
231 }
232
233 @Override
234 public void error(int error, Account account) {
235 displayErrorDialog(error);
236 }
237 });
238 }
239
240 protected void displayErrorDialog(final int errorCode) {
241 runOnUiThread(new Runnable() {
242
243 @Override
244 public void run() {
245 AlertDialog.Builder builder = new AlertDialog.Builder(
246 XmppActivity.this);
247 builder.setIconAttribute(android.R.attr.alertDialogIcon);
248 builder.setTitle(getString(R.string.error));
249 builder.setMessage(errorCode);
250 builder.setNeutralButton(R.string.accept, null);
251 builder.create().show();
252 }
253 });
254
255 }
256
257 protected void showAddToRosterDialog(final Conversation conversation) {
258 String jid = conversation.getContactJid();
259 AlertDialog.Builder builder = new AlertDialog.Builder(this);
260 builder.setTitle(jid);
261 builder.setMessage(getString(R.string.not_in_roster));
262 builder.setNegativeButton(getString(R.string.cancel), null);
263 builder.setPositiveButton(getString(R.string.add_contact),
264 new DialogInterface.OnClickListener() {
265
266 @Override
267 public void onClick(DialogInterface dialog, int which) {
268 String jid = conversation.getContactJid();
269 Account account = conversation.getAccount();
270 Contact contact = account.getRoster().getContact(jid);
271 xmppConnectionService.createContact(contact);
272 switchToContactDetails(contact);
273 }
274 });
275 builder.create().show();
276 }
277
278 protected void quickEdit(final String previousValue, final OnValueEdited callback) {
279 AlertDialog.Builder builder = new AlertDialog.Builder(this);
280 View view = (View) getLayoutInflater().inflate(R.layout.quickedit, null);
281 final EditText editor = (EditText) view.findViewById(R.id.editor);
282 editor.setText(previousValue);
283 builder.setView(view);
284 builder.setNegativeButton(R.string.cancel, null);
285 builder.setPositiveButton(R.string.edit, new OnClickListener() {
286
287 @Override
288 public void onClick(DialogInterface dialog, int which) {
289 String value = editor.getText().toString();
290 if (!previousValue.equals(value) && value.trim().length() > 0) {
291 callback.onValueEdited(value);
292 }
293 }
294 });
295 builder.create().show();
296 }
297
298 public void selectPresence(final Conversation conversation,
299 final OnPresenceSelected listener) {
300 Contact contact = conversation.getContact();
301 if (contact == null) {
302 showAddToRosterDialog(conversation);
303 } else {
304 Presences presences = contact.getPresences();
305 if (presences.size() == 0) {
306 conversation.setNextPresence(null);
307 listener.onPresenceSelected();
308 } else if (presences.size() == 1) {
309 String presence = (String) presences.asStringArray()[0];
310 conversation.setNextPresence(presence);
311 listener.onPresenceSelected();
312 } else {
313 final StringBuilder presence = new StringBuilder();
314 AlertDialog.Builder builder = new AlertDialog.Builder(this);
315 builder.setTitle(getString(R.string.choose_presence));
316 final String[] presencesArray = presences.asStringArray();
317 int preselectedPresence = 0;
318 for (int i = 0; i < presencesArray.length; ++i) {
319 if (presencesArray[i].equals(contact.lastseen.presence)) {
320 preselectedPresence = i;
321 break;
322 }
323 }
324 presence.append(presencesArray[preselectedPresence]);
325 builder.setSingleChoiceItems(presencesArray,
326 preselectedPresence,
327 new DialogInterface.OnClickListener() {
328
329 @Override
330 public void onClick(DialogInterface dialog,
331 int which) {
332 presence.delete(0, presence.length());
333 presence.append(presencesArray[which]);
334 }
335 });
336 builder.setNegativeButton(R.string.cancel, null);
337 builder.setPositiveButton(R.string.ok, new OnClickListener() {
338
339 @Override
340 public void onClick(DialogInterface dialog, int which) {
341 conversation.setNextPresence(presence.toString());
342 listener.onPresenceSelected();
343 }
344 });
345 builder.create().show();
346 }
347 }
348 }
349
350 protected void onActivityResult(int requestCode, int resultCode,
351 final Intent data) {
352 super.onActivityResult(requestCode, resultCode, data);
353 if (requestCode == REQUEST_INVITE_TO_CONVERSATION && resultCode == RESULT_OK) {
354 String contactJid = data.getStringExtra("contact");
355 String conversationUuid = data.getStringExtra("conversation");
356 Conversation conversation = xmppConnectionService.findConversationByUuid(conversationUuid);
357 if (conversation.getMode() == Conversation.MODE_MULTI) {
358 xmppConnectionService.invite(conversation, contactJid);
359 }
360 Log.d("xmppService","inviting "+contactJid+" to "+conversation.getName(true));
361 }
362 }
363
364 public int getSecondaryTextColor() {
365 return this.mSecondaryTextColor;
366 }
367
368 public int getPrimaryTextColor() {
369 return this.mPrimaryTextColor;
370 }
371}