summaryrefslogtreecommitdiff
path: root/mobile/android/thirdparty/com/squareup/picasso/Dispatcher.java
diff options
context:
space:
mode:
Diffstat (limited to 'mobile/android/thirdparty/com/squareup/picasso/Dispatcher.java')
-rw-r--r--mobile/android/thirdparty/com/squareup/picasso/Dispatcher.java315
1 files changed, 315 insertions, 0 deletions
diff --git a/mobile/android/thirdparty/com/squareup/picasso/Dispatcher.java b/mobile/android/thirdparty/com/squareup/picasso/Dispatcher.java
new file mode 100644
index 0000000000..6401431fb2
--- /dev/null
+++ b/mobile/android/thirdparty/com/squareup/picasso/Dispatcher.java
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2013 Square, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.squareup.picasso;
+
+import android.Manifest;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+
+import static android.content.Context.CONNECTIVITY_SERVICE;
+import static android.content.Intent.ACTION_AIRPLANE_MODE_CHANGED;
+import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
+import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
+import static com.squareup.picasso.BitmapHunter.forRequest;
+
+class Dispatcher {
+ private static final int RETRY_DELAY = 500;
+ private static final int AIRPLANE_MODE_ON = 1;
+ private static final int AIRPLANE_MODE_OFF = 0;
+
+ static final int REQUEST_SUBMIT = 1;
+ static final int REQUEST_CANCEL = 2;
+ static final int REQUEST_GCED = 3;
+ static final int HUNTER_COMPLETE = 4;
+ static final int HUNTER_RETRY = 5;
+ static final int HUNTER_DECODE_FAILED = 6;
+ static final int HUNTER_DELAY_NEXT_BATCH = 7;
+ static final int HUNTER_BATCH_COMPLETE = 8;
+ static final int NETWORK_STATE_CHANGE = 9;
+ static final int AIRPLANE_MODE_CHANGE = 10;
+
+ private static final String DISPATCHER_THREAD_NAME = "Dispatcher";
+ private static final int BATCH_DELAY = 200; // ms
+
+ final DispatcherThread dispatcherThread;
+ final Context context;
+ final ExecutorService service;
+ final Downloader downloader;
+ final Map<String, BitmapHunter> hunterMap;
+ final Handler handler;
+ final Handler mainThreadHandler;
+ final Cache cache;
+ final Stats stats;
+ final List<BitmapHunter> batch;
+ final NetworkBroadcastReceiver receiver;
+
+ NetworkInfo networkInfo;
+ boolean airplaneMode;
+
+ Dispatcher(Context context, ExecutorService service, Handler mainThreadHandler,
+ Downloader downloader, Cache cache, Stats stats) {
+ this.dispatcherThread = new DispatcherThread();
+ this.dispatcherThread.start();
+ this.context = context;
+ this.service = service;
+ this.hunterMap = new LinkedHashMap<String, BitmapHunter>();
+ this.handler = new DispatcherHandler(dispatcherThread.getLooper(), this);
+ this.downloader = downloader;
+ this.mainThreadHandler = mainThreadHandler;
+ this.cache = cache;
+ this.stats = stats;
+ this.batch = new ArrayList<BitmapHunter>(4);
+ this.airplaneMode = Utils.isAirplaneModeOn(this.context);
+ this.receiver = new NetworkBroadcastReceiver(this.context);
+ receiver.register();
+ }
+
+ void shutdown() {
+ service.shutdown();
+ dispatcherThread.quit();
+ receiver.unregister();
+ }
+
+ void dispatchSubmit(Action action) {
+ handler.sendMessage(handler.obtainMessage(REQUEST_SUBMIT, action));
+ }
+
+ void dispatchCancel(Action action) {
+ handler.sendMessage(handler.obtainMessage(REQUEST_CANCEL, action));
+ }
+
+ void dispatchComplete(BitmapHunter hunter) {
+ handler.sendMessage(handler.obtainMessage(HUNTER_COMPLETE, hunter));
+ }
+
+ void dispatchRetry(BitmapHunter hunter) {
+ handler.sendMessageDelayed(handler.obtainMessage(HUNTER_RETRY, hunter), RETRY_DELAY);
+ }
+
+ void dispatchFailed(BitmapHunter hunter) {
+ handler.sendMessage(handler.obtainMessage(HUNTER_DECODE_FAILED, hunter));
+ }
+
+ void dispatchNetworkStateChange(NetworkInfo info) {
+ handler.sendMessage(handler.obtainMessage(NETWORK_STATE_CHANGE, info));
+ }
+
+ void dispatchAirplaneModeChange(boolean airplaneMode) {
+ handler.sendMessage(handler.obtainMessage(AIRPLANE_MODE_CHANGE,
+ airplaneMode ? AIRPLANE_MODE_ON : AIRPLANE_MODE_OFF, 0));
+ }
+
+ void performSubmit(Action action) {
+ BitmapHunter hunter = hunterMap.get(action.getKey());
+ if (hunter != null) {
+ hunter.attach(action);
+ return;
+ }
+
+ if (service.isShutdown()) {
+ return;
+ }
+
+ hunter = forRequest(context, action.getPicasso(), this, cache, stats, action, downloader);
+ hunter.future = service.submit(hunter);
+ hunterMap.put(action.getKey(), hunter);
+ }
+
+ void performCancel(Action action) {
+ String key = action.getKey();
+ BitmapHunter hunter = hunterMap.get(key);
+ if (hunter != null) {
+ hunter.detach(action);
+ if (hunter.cancel()) {
+ hunterMap.remove(key);
+ }
+ }
+ }
+
+ void performRetry(BitmapHunter hunter) {
+ if (hunter.isCancelled()) return;
+
+ if (service.isShutdown()) {
+ performError(hunter);
+ return;
+ }
+
+ if (hunter.shouldRetry(airplaneMode, networkInfo)) {
+ hunter.future = service.submit(hunter);
+ } else {
+ performError(hunter);
+ }
+ }
+
+ void performComplete(BitmapHunter hunter) {
+ if (!hunter.shouldSkipMemoryCache()) {
+ cache.set(hunter.getKey(), hunter.getResult());
+ }
+ hunterMap.remove(hunter.getKey());
+ batch(hunter);
+ }
+
+ void performBatchComplete() {
+ List<BitmapHunter> copy = new ArrayList<BitmapHunter>(batch);
+ batch.clear();
+ mainThreadHandler.sendMessage(mainThreadHandler.obtainMessage(HUNTER_BATCH_COMPLETE, copy));
+ }
+
+ void performError(BitmapHunter hunter) {
+ hunterMap.remove(hunter.getKey());
+ batch(hunter);
+ }
+
+ void performAirplaneModeChange(boolean airplaneMode) {
+ this.airplaneMode = airplaneMode;
+ }
+
+ void performNetworkStateChange(NetworkInfo info) {
+ networkInfo = info;
+ if (service instanceof PicassoExecutorService) {
+ ((PicassoExecutorService) service).adjustThreadCount(info);
+ }
+ }
+
+ private void batch(BitmapHunter hunter) {
+ if (hunter.isCancelled()) {
+ return;
+ }
+ batch.add(hunter);
+ if (!handler.hasMessages(HUNTER_DELAY_NEXT_BATCH)) {
+ handler.sendEmptyMessageDelayed(HUNTER_DELAY_NEXT_BATCH, BATCH_DELAY);
+ }
+ }
+
+ private static class DispatcherHandler extends Handler {
+ private final Dispatcher dispatcher;
+
+ public DispatcherHandler(Looper looper, Dispatcher dispatcher) {
+ super(looper);
+ this.dispatcher = dispatcher;
+ }
+
+ @Override public void handleMessage(final Message msg) {
+ switch (msg.what) {
+ case REQUEST_SUBMIT: {
+ Action action = (Action) msg.obj;
+ dispatcher.performSubmit(action);
+ break;
+ }
+ case REQUEST_CANCEL: {
+ Action action = (Action) msg.obj;
+ dispatcher.performCancel(action);
+ break;
+ }
+ case HUNTER_COMPLETE: {
+ BitmapHunter hunter = (BitmapHunter) msg.obj;
+ dispatcher.performComplete(hunter);
+ break;
+ }
+ case HUNTER_RETRY: {
+ BitmapHunter hunter = (BitmapHunter) msg.obj;
+ dispatcher.performRetry(hunter);
+ break;
+ }
+ case HUNTER_DECODE_FAILED: {
+ BitmapHunter hunter = (BitmapHunter) msg.obj;
+ dispatcher.performError(hunter);
+ break;
+ }
+ case HUNTER_DELAY_NEXT_BATCH: {
+ dispatcher.performBatchComplete();
+ break;
+ }
+ case NETWORK_STATE_CHANGE: {
+ NetworkInfo info = (NetworkInfo) msg.obj;
+ dispatcher.performNetworkStateChange(info);
+ break;
+ }
+ case AIRPLANE_MODE_CHANGE: {
+ dispatcher.performAirplaneModeChange(msg.arg1 == AIRPLANE_MODE_ON);
+ break;
+ }
+ default:
+ Picasso.HANDLER.post(new Runnable() {
+ @Override public void run() {
+ throw new AssertionError("Unknown handler message received: " + msg.what);
+ }
+ });
+ }
+ }
+ }
+
+ static class DispatcherThread extends HandlerThread {
+ DispatcherThread() {
+ super(Utils.THREAD_PREFIX + DISPATCHER_THREAD_NAME, THREAD_PRIORITY_BACKGROUND);
+ }
+ }
+
+ private class NetworkBroadcastReceiver extends BroadcastReceiver {
+ private static final String EXTRA_AIRPLANE_STATE = "state";
+
+ private final ConnectivityManager connectivityManager;
+
+ NetworkBroadcastReceiver(Context context) {
+ connectivityManager = (ConnectivityManager) context.getSystemService(CONNECTIVITY_SERVICE);
+ }
+
+ void register() {
+ boolean shouldScanState = service instanceof PicassoExecutorService && //
+ Utils.hasPermission(context, Manifest.permission.ACCESS_NETWORK_STATE);
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(ACTION_AIRPLANE_MODE_CHANGED);
+ if (shouldScanState) {
+ filter.addAction(CONNECTIVITY_ACTION);
+ }
+ context.registerReceiver(this, filter);
+ }
+
+ void unregister() {
+ context.unregisterReceiver(this);
+ }
+
+ @Override public void onReceive(Context context, Intent intent) {
+ // On some versions of Android this may be called with a null Intent
+ if (null == intent) {
+ return;
+ }
+
+ String action = intent.getAction();
+ Bundle extras = intent.getExtras();
+
+ if (ACTION_AIRPLANE_MODE_CHANGED.equals(action)) {
+ dispatchAirplaneModeChange(extras.getBoolean(EXTRA_AIRPLANE_STATE, false));
+ } else if (CONNECTIVITY_ACTION.equals(action)) {
+ dispatchNetworkStateChange(connectivityManager.getActiveNetworkInfo());
+ }
+ }
+ }
+}