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