1package eu.siacs.conversations.ui;
2
3import android.annotation.SuppressLint;
4import android.app.Activity;
5import android.app.AlertDialog;
6import android.app.AlertDialog.Builder;
7import android.app.PendingIntent;
8import android.content.ClipData;
9import android.content.ClipboardManager;
10import android.content.ComponentName;
11import android.content.Context;
12import android.content.DialogInterface;
13import android.content.DialogInterface.OnClickListener;
14import android.content.Intent;
15import android.content.IntentSender.SendIntentException;
16import android.content.ServiceConnection;
17import android.content.SharedPreferences;
18import android.content.pm.PackageManager;
19import android.content.pm.ResolveInfo;
20import android.content.res.Resources;
21import android.graphics.Bitmap;
22import android.graphics.Color;
23import android.graphics.Point;
24import android.graphics.drawable.BitmapDrawable;
25import android.graphics.drawable.Drawable;
26import android.net.Uri;
27import android.nfc.NdefMessage;
28import android.nfc.NdefRecord;
29import android.nfc.NfcAdapter;
30import android.nfc.NfcEvent;
31import android.os.AsyncTask;
32import android.os.Bundle;
33import android.os.IBinder;
34import android.preference.PreferenceManager;
35import android.text.InputType;
36import android.util.DisplayMetrics;
37import android.util.Log;
38import android.view.MenuItem;
39import android.view.View;
40import android.view.inputmethod.InputMethodManager;
41import android.widget.EditText;
42import android.widget.ImageView;
43
44import com.google.zxing.BarcodeFormat;
45import com.google.zxing.EncodeHintType;
46import com.google.zxing.WriterException;
47import com.google.zxing.common.BitMatrix;
48import com.google.zxing.qrcode.QRCodeWriter;
49import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
50
51import java.io.FileNotFoundException;
52import java.lang.ref.WeakReference;
53import java.util.Hashtable;
54import java.util.List;
55import java.util.concurrent.RejectedExecutionException;
56
57import eu.siacs.conversations.Config;
58import eu.siacs.conversations.R;
59import eu.siacs.conversations.entities.Account;
60import eu.siacs.conversations.entities.Contact;
61import eu.siacs.conversations.entities.Conversation;
62import eu.siacs.conversations.entities.Message;
63import eu.siacs.conversations.entities.Presences;
64import eu.siacs.conversations.services.AvatarService;
65import eu.siacs.conversations.services.XmppConnectionService;
66import eu.siacs.conversations.services.XmppConnectionService.XmppConnectionBinder;
67import eu.siacs.conversations.utils.ExceptionHelper;
68
69public abstract class XmppActivity extends Activity {
70
71 protected static final int REQUEST_ANNOUNCE_PGP = 0x0101;
72 protected static final int REQUEST_INVITE_TO_CONVERSATION = 0x0102;
73
74 public XmppConnectionService xmppConnectionService;
75 public boolean xmppConnectionServiceBound = false;
76
77 protected int mPrimaryTextColor;
78 protected int mSecondaryTextColor;
79 protected int mSecondaryBackgroundColor;
80 protected int mColorRed;
81 protected int mColorOrange;
82 protected int mColorGreen;
83 protected int mPrimaryColor;
84
85 protected boolean mUseSubject = true;
86
87 private DisplayMetrics metrics;
88
89 protected interface OnValueEdited {
90 public void onValueEdited(String value);
91 }
92
93 public interface OnPresenceSelected {
94 public void onPresenceSelected();
95 }
96
97 protected ServiceConnection mConnection = new ServiceConnection() {
98
99 @Override
100 public void onServiceConnected(ComponentName className, IBinder service) {
101 XmppConnectionBinder binder = (XmppConnectionBinder) service;
102 xmppConnectionService = binder.getService();
103 xmppConnectionServiceBound = true;
104 onBackendConnected();
105 }
106
107 @Override
108 public void onServiceDisconnected(ComponentName arg0) {
109 xmppConnectionServiceBound = false;
110 }
111 };
112
113 @Override
114 protected void onStart() {
115 super.onStart();
116 if (!xmppConnectionServiceBound) {
117 connectToBackend();
118 }
119 }
120
121 public void connectToBackend() {
122 Intent intent = new Intent(this, XmppConnectionService.class);
123 intent.setAction("ui");
124 startService(intent);
125 bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
126 }
127
128 @Override
129 protected void onStop() {
130 super.onStop();
131 if (xmppConnectionServiceBound) {
132 unbindService(mConnection);
133 xmppConnectionServiceBound = false;
134 }
135 }
136
137 protected void hideKeyboard() {
138 InputMethodManager inputManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
139
140 View focus = getCurrentFocus();
141
142 if (focus != null) {
143
144 inputManager.hideSoftInputFromWindow(focus.getWindowToken(),
145 InputMethodManager.HIDE_NOT_ALWAYS);
146 }
147 }
148
149 public boolean hasPgp() {
150 return xmppConnectionService.getPgpEngine() != null;
151 }
152
153 public void showInstallPgpDialog() {
154 Builder builder = new AlertDialog.Builder(this);
155 builder.setTitle(getString(R.string.openkeychain_required));
156 builder.setIconAttribute(android.R.attr.alertDialogIcon);
157 builder.setMessage(getText(R.string.openkeychain_required_long));
158 builder.setNegativeButton(getString(R.string.cancel), null);
159 builder.setNeutralButton(getString(R.string.restart),
160 new OnClickListener() {
161
162 @Override
163 public void onClick(DialogInterface dialog, int which) {
164 if (xmppConnectionServiceBound) {
165 unbindService(mConnection);
166 xmppConnectionServiceBound = false;
167 }
168 stopService(new Intent(XmppActivity.this,
169 XmppConnectionService.class));
170 finish();
171 }
172 });
173 builder.setPositiveButton(getString(R.string.install),
174 new OnClickListener() {
175
176 @Override
177 public void onClick(DialogInterface dialog, int which) {
178 Uri uri = Uri
179 .parse("market://details?id=org.sufficientlysecure.keychain");
180 Intent marketIntent = new Intent(Intent.ACTION_VIEW,
181 uri);
182 PackageManager manager = getApplicationContext()
183 .getPackageManager();
184 List<ResolveInfo> infos = manager
185 .queryIntentActivities(marketIntent, 0);
186 if (infos.size() > 0) {
187 startActivity(marketIntent);
188 } else {
189 uri = Uri.parse("http://www.openkeychain.org/");
190 Intent browserIntent = new Intent(
191 Intent.ACTION_VIEW, uri);
192 startActivity(browserIntent);
193 }
194 finish();
195 }
196 });
197 builder.create().show();
198 }
199
200 abstract void onBackendConnected();
201
202 public boolean onOptionsItemSelected(MenuItem item) {
203 switch (item.getItemId()) {
204 case R.id.action_settings:
205 startActivity(new Intent(this, SettingsActivity.class));
206 break;
207 case R.id.action_accounts:
208 startActivity(new Intent(this, ManageAccountActivity.class));
209 break;
210 case android.R.id.home:
211 finish();
212 break;
213 case R.id.action_show_qr_code:
214 showQrCode();
215 break;
216 }
217 return super.onOptionsItemSelected(item);
218 }
219
220 @Override
221 protected void onCreate(Bundle savedInstanceState) {
222 super.onCreate(savedInstanceState);
223 metrics = getResources().getDisplayMetrics();
224 ExceptionHelper.init(getApplicationContext());
225 mPrimaryTextColor = getResources().getColor(R.color.primarytext);
226 mSecondaryTextColor = getResources().getColor(R.color.secondarytext);
227 mColorRed = getResources().getColor(R.color.red);
228 mColorOrange = getResources().getColor(R.color.orange);
229 mColorGreen = getResources().getColor(R.color.green);
230 mPrimaryColor = getResources().getColor(R.color.primary);
231 mSecondaryBackgroundColor = getResources().getColor(
232 R.color.secondarybackground);
233 if (getPreferences().getBoolean("use_larger_font", false)) {
234 setTheme(R.style.ConversationsTheme_LargerText);
235 }
236 mUseSubject = getPreferences().getBoolean("use_subject", true);
237 }
238
239 protected SharedPreferences getPreferences() {
240 return PreferenceManager
241 .getDefaultSharedPreferences(getApplicationContext());
242 }
243
244 public boolean useSubjectToIdentifyConference() {
245 return mUseSubject;
246 }
247
248 public void switchToConversation(Conversation conversation) {
249 switchToConversation(conversation, null, false);
250 }
251
252 public void switchToConversation(Conversation conversation, String text,
253 boolean newTask) {
254 Intent viewConversationIntent = new Intent(this,
255 ConversationActivity.class);
256 viewConversationIntent.setAction(Intent.ACTION_VIEW);
257 viewConversationIntent.putExtra(ConversationActivity.CONVERSATION,
258 conversation.getUuid());
259 if (text != null) {
260 viewConversationIntent.putExtra(ConversationActivity.TEXT, text);
261 }
262 viewConversationIntent.setType(ConversationActivity.VIEW_CONVERSATION);
263 if (newTask) {
264 viewConversationIntent.setFlags(viewConversationIntent.getFlags()
265 | Intent.FLAG_ACTIVITY_NEW_TASK
266 | Intent.FLAG_ACTIVITY_SINGLE_TOP);
267 } else {
268 viewConversationIntent.setFlags(viewConversationIntent.getFlags()
269 | Intent.FLAG_ACTIVITY_CLEAR_TOP);
270 }
271 startActivity(viewConversationIntent);
272 finish();
273 }
274
275 public void switchToContactDetails(Contact contact) {
276 Intent intent = new Intent(this, ContactDetailsActivity.class);
277 intent.setAction(ContactDetailsActivity.ACTION_VIEW_CONTACT);
278 intent.putExtra("account", contact.getAccount().getJid());
279 intent.putExtra("contact", contact.getJid());
280 startActivity(intent);
281 }
282
283 public void switchToAccount(Account account) {
284 Intent intent = new Intent(this, EditAccountActivity.class);
285 intent.putExtra("jid", account.getJid());
286 startActivity(intent);
287 }
288
289 protected void inviteToConversation(Conversation conversation) {
290 Intent intent = new Intent(getApplicationContext(),
291 ChooseContactActivity.class);
292 intent.putExtra("conversation", conversation.getUuid());
293 startActivityForResult(intent, REQUEST_INVITE_TO_CONVERSATION);
294 }
295
296 protected void announcePgp(Account account, final Conversation conversation) {
297 xmppConnectionService.getPgpEngine().generateSignature(account,
298 "online", new UiCallback<Account>() {
299
300 @Override
301 public void userInputRequried(PendingIntent pi,
302 Account account) {
303 try {
304 startIntentSenderForResult(pi.getIntentSender(),
305 REQUEST_ANNOUNCE_PGP, null, 0, 0, 0);
306 } catch (SendIntentException e) {
307 }
308 }
309
310 @Override
311 public void success(Account account) {
312 xmppConnectionService.databaseBackend
313 .updateAccount(account);
314 xmppConnectionService.sendPresencePacket(account,
315 xmppConnectionService.getPresenceGenerator()
316 .sendPresence(account));
317 if (conversation != null) {
318 conversation
319 .setNextEncryption(Message.ENCRYPTION_PGP);
320 xmppConnectionService.databaseBackend
321 .updateConversation(conversation);
322 }
323 }
324
325 @Override
326 public void error(int error, Account account) {
327 displayErrorDialog(error);
328 }
329 });
330 }
331
332 protected void displayErrorDialog(final int errorCode) {
333 runOnUiThread(new Runnable() {
334
335 @Override
336 public void run() {
337 AlertDialog.Builder builder = new AlertDialog.Builder(
338 XmppActivity.this);
339 builder.setIconAttribute(android.R.attr.alertDialogIcon);
340 builder.setTitle(getString(R.string.error));
341 builder.setMessage(errorCode);
342 builder.setNeutralButton(R.string.accept, null);
343 builder.create().show();
344 }
345 });
346
347 }
348
349 protected void showAddToRosterDialog(final Conversation conversation) {
350 String jid = conversation.getContactJid();
351 AlertDialog.Builder builder = new AlertDialog.Builder(this);
352 builder.setTitle(jid);
353 builder.setMessage(getString(R.string.not_in_roster));
354 builder.setNegativeButton(getString(R.string.cancel), null);
355 builder.setPositiveButton(getString(R.string.add_contact),
356 new DialogInterface.OnClickListener() {
357
358 @Override
359 public void onClick(DialogInterface dialog, int which) {
360 String jid = conversation.getContactJid();
361 Account account = conversation.getAccount();
362 Contact contact = account.getRoster().getContact(jid);
363 xmppConnectionService.createContact(contact);
364 switchToContactDetails(contact);
365 }
366 });
367 builder.create().show();
368 }
369
370 private void showAskForPresenceDialog(final Contact contact) {
371 AlertDialog.Builder builder = new AlertDialog.Builder(this);
372 builder.setTitle(contact.getJid());
373 builder.setMessage(R.string.request_presence_updates);
374 builder.setNegativeButton(R.string.cancel, null);
375 builder.setPositiveButton(R.string.request_now,
376 new DialogInterface.OnClickListener() {
377
378 @Override
379 public void onClick(DialogInterface dialog, int which) {
380 if (xmppConnectionServiceBound) {
381 xmppConnectionService.sendPresencePacket(contact
382 .getAccount(), xmppConnectionService
383 .getPresenceGenerator()
384 .requestPresenceUpdatesFrom(contact));
385 }
386 }
387 });
388 builder.create().show();
389 }
390
391 private void warnMutalPresenceSubscription(final Conversation conversation,
392 final OnPresenceSelected listener) {
393 AlertDialog.Builder builder = new AlertDialog.Builder(this);
394 builder.setTitle(conversation.getContact().getJid());
395 builder.setMessage(R.string.without_mutual_presence_updates);
396 builder.setNegativeButton(R.string.cancel, null);
397 builder.setPositiveButton(R.string.ignore, new OnClickListener() {
398
399 @Override
400 public void onClick(DialogInterface dialog, int which) {
401 conversation.setNextPresence(null);
402 if (listener != null) {
403 listener.onPresenceSelected();
404 }
405 }
406 });
407 builder.create().show();
408 }
409
410 protected void quickEdit(String previousValue, OnValueEdited callback) {
411 quickEdit(previousValue, callback, false);
412 }
413
414 protected void quickPasswordEdit(String previousValue,
415 OnValueEdited callback) {
416 quickEdit(previousValue, callback, true);
417 }
418
419 @SuppressLint("InflateParams")
420 private void quickEdit(final String previousValue,
421 final OnValueEdited callback, boolean password) {
422 AlertDialog.Builder builder = new AlertDialog.Builder(this);
423 View view = getLayoutInflater().inflate(R.layout.quickedit, null);
424 final EditText editor = (EditText) view.findViewById(R.id.editor);
425 OnClickListener mClickListener = new OnClickListener() {
426
427 @Override
428 public void onClick(DialogInterface dialog, int which) {
429 String value = editor.getText().toString();
430 if (!previousValue.equals(value) && value.trim().length() > 0) {
431 callback.onValueEdited(value);
432 }
433 }
434 };
435 if (password) {
436 editor.setInputType(InputType.TYPE_CLASS_TEXT
437 | InputType.TYPE_TEXT_VARIATION_PASSWORD);
438 editor.setHint(R.string.password);
439 builder.setPositiveButton(R.string.accept, mClickListener);
440 } else {
441 builder.setPositiveButton(R.string.edit, mClickListener);
442 }
443 editor.requestFocus();
444 editor.setText(previousValue);
445 builder.setView(view);
446 builder.setNegativeButton(R.string.cancel, null);
447 builder.create().show();
448 }
449
450 public void selectPresence(final Conversation conversation,
451 final OnPresenceSelected listener) {
452 Contact contact = conversation.getContact();
453 if (!contact.showInRoster()) {
454 showAddToRosterDialog(conversation);
455 } else {
456 Presences presences = contact.getPresences();
457 if (presences.size() == 0) {
458 if (!contact.getOption(Contact.Options.TO)
459 && !contact.getOption(Contact.Options.ASKING)
460 && contact.getAccount().getStatus() == Account.STATUS_ONLINE) {
461 showAskForPresenceDialog(contact);
462 } else if (!contact.getOption(Contact.Options.TO)
463 || !contact.getOption(Contact.Options.FROM)) {
464 warnMutalPresenceSubscription(conversation, listener);
465 } else {
466 conversation.setNextPresence(null);
467 listener.onPresenceSelected();
468 }
469 } else if (presences.size() == 1) {
470 String presence = presences.asStringArray()[0];
471 conversation.setNextPresence(presence);
472 listener.onPresenceSelected();
473 } else {
474 final StringBuilder presence = new StringBuilder();
475 AlertDialog.Builder builder = new AlertDialog.Builder(this);
476 builder.setTitle(getString(R.string.choose_presence));
477 final String[] presencesArray = presences.asStringArray();
478 int preselectedPresence = 0;
479 for (int i = 0; i < presencesArray.length; ++i) {
480 if (presencesArray[i].equals(contact.lastseen.presence)) {
481 preselectedPresence = i;
482 break;
483 }
484 }
485 presence.append(presencesArray[preselectedPresence]);
486 builder.setSingleChoiceItems(presencesArray,
487 preselectedPresence,
488 new DialogInterface.OnClickListener() {
489
490 @Override
491 public void onClick(DialogInterface dialog,
492 int which) {
493 presence.delete(0, presence.length());
494 presence.append(presencesArray[which]);
495 }
496 });
497 builder.setNegativeButton(R.string.cancel, null);
498 builder.setPositiveButton(R.string.ok, new OnClickListener() {
499
500 @Override
501 public void onClick(DialogInterface dialog, int which) {
502 conversation.setNextPresence(presence.toString());
503 listener.onPresenceSelected();
504 }
505 });
506 builder.create().show();
507 }
508 }
509 }
510
511 protected void onActivityResult(int requestCode, int resultCode,
512 final Intent data) {
513 super.onActivityResult(requestCode, resultCode, data);
514 if (requestCode == REQUEST_INVITE_TO_CONVERSATION
515 && resultCode == RESULT_OK) {
516 String contactJid = data.getStringExtra("contact");
517 String conversationUuid = data.getStringExtra("conversation");
518 Conversation conversation = xmppConnectionService
519 .findConversationByUuid(conversationUuid);
520 if (conversation.getMode() == Conversation.MODE_MULTI) {
521 xmppConnectionService.invite(conversation, contactJid);
522 }
523 Log.d(Config.LOGTAG, "inviting " + contactJid + " to "
524 + conversation.getName());
525 }
526 }
527
528 public int getSecondaryTextColor() {
529 return this.mSecondaryTextColor;
530 }
531
532 public int getPrimaryTextColor() {
533 return this.mPrimaryTextColor;
534 }
535
536 public int getWarningTextColor() {
537 return this.mColorRed;
538 }
539
540 public int getPrimaryColor() {
541 return this.mPrimaryColor;
542 }
543
544 public int getSecondaryBackgroundColor() {
545 return this.mSecondaryBackgroundColor;
546 }
547
548 public int getPixel(int dp) {
549 DisplayMetrics metrics = getResources().getDisplayMetrics();
550 return ((int) (dp * metrics.density));
551 }
552
553 public boolean copyTextToClipboard(String text, int labelResId) {
554 ClipboardManager mClipBoardManager = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
555 String label = getResources().getString(labelResId);
556 if (mClipBoardManager != null) {
557 ClipData mClipData = ClipData.newPlainText(label, text);
558 mClipBoardManager.setPrimaryClip(mClipData);
559 return true;
560 }
561 return false;
562 }
563
564 protected void registerNdefPushMessageCallback() {
565 NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
566 if (nfcAdapter != null && nfcAdapter.isEnabled()) {
567 nfcAdapter.setNdefPushMessageCallback(new NfcAdapter.CreateNdefMessageCallback() {
568 @Override
569 public NdefMessage createNdefMessage(NfcEvent nfcEvent) {
570 NdefMessage msg = new NdefMessage(new NdefRecord[]{
571 NdefRecord.createUri(getShareableUri()),
572 NdefRecord.createApplicationRecord("eu.siacs.conversations")
573 });
574 return msg;
575 }
576 }, this);
577 }
578 }
579
580 protected void unregisterNdefPushMessageCallback() {
581 NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
582 if (nfcAdapter != null && nfcAdapter.isEnabled()) {
583 nfcAdapter.setNdefPushMessageCallback(null,this);
584 }
585 }
586
587 protected String getShareableUri() {
588 return null;
589 }
590
591 @Override
592 public void onResume() {
593 super.onResume();
594 if (this.getShareableUri()!=null) {
595 this.registerNdefPushMessageCallback();
596 }
597 }
598
599 @Override
600 public void onPause() {
601 super.onPause();
602 this.unregisterNdefPushMessageCallback();
603 }
604
605 protected void showQrCode() {
606 String uri = getShareableUri();
607 if (uri!=null) {
608 Point size = new Point();
609 getWindowManager().getDefaultDisplay().getSize(size);
610 final int width = (size.x < size.y ? size.x : size.y);
611 Bitmap bitmap = createQrCodeBitmap(uri, width);
612 ImageView view = new ImageView(this);
613 view.setImageBitmap(bitmap);
614 AlertDialog.Builder builder = new AlertDialog.Builder(this);
615 builder.setView(view);
616 builder.create().show();
617 }
618 }
619
620 protected Bitmap createQrCodeBitmap(String input, int size) {
621 try {
622 final QRCodeWriter QR_CODE_WRITER = new QRCodeWriter();
623 final Hashtable<EncodeHintType, Object> hints = new Hashtable<EncodeHintType, Object>();
624 hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M);
625 final BitMatrix result = QR_CODE_WRITER.encode(input, BarcodeFormat.QR_CODE, size, size, hints);
626 final int width = result.getWidth();
627 final int height = result.getHeight();
628 final int[] pixels = new int[width * height];
629 for (int y = 0; y < height; y++) {
630 final int offset = y * width;
631 for (int x = 0; x < width; x++) {
632 pixels[offset + x] = result.get(x, y) ? Color.BLACK : Color.TRANSPARENT;
633 }
634 }
635 final Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
636 bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
637 return bitmap;
638 } catch (final WriterException e) {
639 return null;
640 }
641 }
642
643 public AvatarService avatarService() {
644 return xmppConnectionService.getAvatarService();
645 }
646
647 class BitmapWorkerTask extends AsyncTask<Message, Void, Bitmap> {
648 private final WeakReference<ImageView> imageViewReference;
649 private Message message = null;
650
651 public BitmapWorkerTask(ImageView imageView) {
652 imageViewReference = new WeakReference<ImageView>(imageView);
653 }
654
655 @Override
656 protected Bitmap doInBackground(Message... params) {
657 message = params[0];
658 try {
659 return xmppConnectionService.getFileBackend().getThumbnail(
660 message, (int) (metrics.density * 288), false);
661 } catch (FileNotFoundException e) {
662 return null;
663 }
664 }
665
666 @Override
667 protected void onPostExecute(Bitmap bitmap) {
668 if (imageViewReference != null && bitmap != null) {
669 final ImageView imageView = imageViewReference.get();
670 if (imageView != null) {
671 imageView.setImageBitmap(bitmap);
672 imageView.setBackgroundColor(0x00000000);
673 }
674 }
675 }
676 }
677
678 public void loadBitmap(Message message, ImageView imageView) {
679 Bitmap bm;
680 try {
681 bm = xmppConnectionService.getFileBackend().getThumbnail(message,
682 (int) (metrics.density * 288), true);
683 } catch (FileNotFoundException e) {
684 bm = null;
685 }
686 if (bm != null) {
687 imageView.setImageBitmap(bm);
688 imageView.setBackgroundColor(0x00000000);
689 } else {
690 if (cancelPotentialWork(message, imageView)) {
691 imageView.setBackgroundColor(0xff333333);
692 final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
693 final AsyncDrawable asyncDrawable = new AsyncDrawable(
694 getResources(), null, task);
695 imageView.setImageDrawable(asyncDrawable);
696 try {
697 task.execute(message);
698 } catch (RejectedExecutionException e) {
699 return;
700 }
701 }
702 }
703 }
704
705 public static boolean cancelPotentialWork(Message message,
706 ImageView imageView) {
707 final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
708
709 if (bitmapWorkerTask != null) {
710 final Message oldMessage = bitmapWorkerTask.message;
711 if (oldMessage == null || message != oldMessage) {
712 bitmapWorkerTask.cancel(true);
713 } else {
714 return false;
715 }
716 }
717 return true;
718 }
719
720 private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
721 if (imageView != null) {
722 final Drawable drawable = imageView.getDrawable();
723 if (drawable instanceof AsyncDrawable) {
724 final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
725 return asyncDrawable.getBitmapWorkerTask();
726 }
727 }
728 return null;
729 }
730
731 static class AsyncDrawable extends BitmapDrawable {
732 private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
733
734 public AsyncDrawable(Resources res, Bitmap bitmap,
735 BitmapWorkerTask bitmapWorkerTask) {
736 super(res, bitmap);
737 bitmapWorkerTaskReference = new WeakReference<BitmapWorkerTask>(
738 bitmapWorkerTask);
739 }
740
741 public BitmapWorkerTask getBitmapWorkerTask() {
742 return bitmapWorkerTaskReference.get();
743 }
744 }
745}