• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.bumptech.glide.manager;
2 
3 import android.annotation.TargetApi;
4 import android.app.Activity;
5 import android.app.Application;
6 import android.content.Context;
7 import android.content.ContextWrapper;
8 import android.os.Build;
9 import android.os.Handler;
10 import android.os.Looper;
11 import android.os.Message;
12 import android.support.v4.app.Fragment;
13 import android.support.v4.app.FragmentActivity;
14 import android.support.v4.app.FragmentManager;
15 import android.util.Log;
16 
17 import com.bumptech.glide.RequestManager;
18 import com.bumptech.glide.util.Util;
19 
20 import java.util.HashMap;
21 import java.util.Map;
22 
23 /**
24  * A collection of static methods for creating new {@link com.bumptech.glide.RequestManager}s or retrieving existing
25  * ones from activities and fragment.
26  */
27 public class RequestManagerRetriever implements Handler.Callback {
28     static final String TAG = "com.bumptech.glide.manager";
29 
30     /** The singleton instance of RequestManagerRetriever. */
31     private static final RequestManagerRetriever INSTANCE = new RequestManagerRetriever();
32 
33     private static final int ID_REMOVE_FRAGMENT_MANAGER = 1;
34     private static final int ID_REMOVE_SUPPORT_FRAGMENT_MANAGER = 2;
35 
36     /** The top application level RequestManager. */
37     private volatile RequestManager applicationManager;
38 
39     // Visible for testing.
40     /** Pending adds for RequestManagerFragments. */
41     final Map<android.app.FragmentManager, RequestManagerFragment> pendingRequestManagerFragments =
42             new HashMap<android.app.FragmentManager, RequestManagerFragment>();
43 
44     // Visible for testing.
45     /** Pending adds for SupportRequestManagerFragments. */
46     final Map<FragmentManager, SupportRequestManagerFragment> pendingSupportRequestManagerFragments =
47             new HashMap<FragmentManager, SupportRequestManagerFragment>();
48 
49     /** Main thread handler to handle cleaning up pending fragment maps. */
50     private final Handler handler;
51 
52     /**
53      * Retrieves and returns the RequestManagerRetriever singleton.
54      */
get()55     public static RequestManagerRetriever get() {
56         return INSTANCE;
57     }
58 
59     // Visible for testing.
RequestManagerRetriever()60     RequestManagerRetriever() {
61         handler = new Handler(Looper.getMainLooper(), this /* Callback */);
62     }
63 
getApplicationManager(Context context)64     private RequestManager getApplicationManager(Context context) {
65         // Either an application context or we're on a background thread.
66         if (applicationManager == null) {
67             synchronized (this) {
68                 if (applicationManager == null) {
69                     // Normally pause/resume is taken care of by the fragment we add to the fragment or activity.
70                     // However, in this case since the manager attached to the application will not receive lifecycle
71                     // events, we must force the manager to start resumed using ApplicationLifecycle.
72                     applicationManager = new RequestManager(context.getApplicationContext(),
73                             new ApplicationLifecycle());
74                 }
75             }
76         }
77 
78         return applicationManager;
79     }
80 
get(Context context)81     public RequestManager get(Context context) {
82         if (context == null) {
83             throw new IllegalArgumentException("You cannot start a load on a null Context");
84         } else if (Util.isOnMainThread() && !(context instanceof Application)) {
85             if (context instanceof FragmentActivity) {
86                 return get((FragmentActivity) context);
87             } else if (context instanceof Activity) {
88                 return get((Activity) context);
89             } else if (context instanceof ContextWrapper) {
90                 return get(((ContextWrapper) context).getBaseContext());
91             }
92         }
93 
94         return getApplicationManager(context);
95     }
96 
get(FragmentActivity activity)97     public RequestManager get(FragmentActivity activity) {
98         if (Util.isOnBackgroundThread()) {
99             return get(activity.getApplicationContext());
100         } else {
101             assertNotDestroyed(activity);
102             FragmentManager fm = activity.getSupportFragmentManager();
103             return supportFragmentGet(activity, fm);
104         }
105     }
106 
get(Fragment fragment)107     public RequestManager get(Fragment fragment) {
108         if (fragment.getActivity() == null) {
109             throw new IllegalArgumentException("You cannot start a load on a fragment before it is attached");
110         }
111         if (Util.isOnBackgroundThread()) {
112             return get(fragment.getActivity().getApplicationContext());
113         } else {
114             if (fragment.isDetached()) {
115                 throw new IllegalArgumentException("You cannot start a load on a detached fragment");
116             }
117             FragmentManager fm = fragment.getChildFragmentManager();
118             return supportFragmentGet(fragment.getActivity(), fm);
119         }
120     }
121 
122     @TargetApi(Build.VERSION_CODES.HONEYCOMB)
get(Activity activity)123     public RequestManager get(Activity activity) {
124         if (Util.isOnBackgroundThread() || Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
125             return get(activity.getApplicationContext());
126         } else {
127             assertNotDestroyed(activity);
128             android.app.FragmentManager fm = activity.getFragmentManager();
129             return fragmentGet(activity, fm);
130         }
131     }
132 
133     @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
assertNotDestroyed(Activity activity)134     private static void assertNotDestroyed(Activity activity) {
135         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && activity.isDestroyed()) {
136             throw new IllegalArgumentException("You cannot start a load for a destroyed activity");
137         }
138     }
139 
140     @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
get(android.app.Fragment fragment)141     public RequestManager get(android.app.Fragment fragment) {
142         if (fragment.getActivity() == null) {
143             throw new IllegalArgumentException("You cannot start a load on a fragment before it is attached");
144         }
145         if (Util.isOnBackgroundThread() || Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
146             return get(fragment.getActivity().getApplicationContext());
147         } else {
148             assertNotDetached(fragment);
149             android.app.FragmentManager fm = fragment.getChildFragmentManager();
150             return fragmentGet(fragment.getActivity(), fm);
151         }
152     }
153 
154     @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
assertNotDetached(android.app.Fragment fragment)155     private static void assertNotDetached(android.app.Fragment fragment) {
156         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2 && fragment.isDetached()) {
157             throw new IllegalArgumentException("You cannot start a load on a detached fragment");
158         }
159     }
160 
161     @TargetApi(Build.VERSION_CODES.HONEYCOMB)
fragmentGet(Context context, final android.app.FragmentManager fm)162     RequestManager fragmentGet(Context context, final android.app.FragmentManager fm) {
163         RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(TAG);
164         if (current == null) {
165             current = pendingRequestManagerFragments.get(fm);
166             if (current == null) {
167                 current = new RequestManagerFragment();
168                 pendingRequestManagerFragments.put(fm, current);
169                 fm.beginTransaction().add(current, TAG).commitAllowingStateLoss();
170                 handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
171             }
172         }
173         RequestManager requestManager = current.getRequestManager();
174         if (requestManager == null) {
175             requestManager = new RequestManager(context, current.getGlideLifecycle());
176             current.setRequestManager(requestManager);
177         }
178         return requestManager;
179 
180     }
181 
supportFragmentGet(Context context, final FragmentManager fm)182     RequestManager supportFragmentGet(Context context, final FragmentManager fm) {
183         SupportRequestManagerFragment current = (SupportRequestManagerFragment) fm.findFragmentByTag(TAG);
184         if (current == null) {
185             current = pendingSupportRequestManagerFragments.get(fm);
186             if (current == null) {
187                 current = new SupportRequestManagerFragment();
188                 pendingSupportRequestManagerFragments.put(fm, current);
189                 fm.beginTransaction().add(current, TAG).commitAllowingStateLoss();
190                 handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();
191             }
192         }
193         RequestManager requestManager = current.getRequestManager();
194         if (requestManager == null) {
195             requestManager = new RequestManager(context, current.getGlideLifecycle());
196             current.setRequestManager(requestManager);
197         }
198         return requestManager;
199     }
200 
201     @Override
handleMessage(Message message)202     public boolean handleMessage(Message message) {
203         boolean handled = true;
204         Object removed = null;
205         Object key = null;
206         switch (message.what) {
207             case ID_REMOVE_FRAGMENT_MANAGER:
208                 android.app.FragmentManager fm = (android.app.FragmentManager) message.obj;
209                 key = fm;
210                 removed = pendingRequestManagerFragments.remove(fm);
211                 break;
212             case ID_REMOVE_SUPPORT_FRAGMENT_MANAGER:
213                 FragmentManager supportFm = (FragmentManager) message.obj;
214                 key = supportFm;
215                 removed = pendingSupportRequestManagerFragments.remove(supportFm);
216                 break;
217             default:
218                 handled = false;
219         }
220         if (handled && removed == null && Log.isLoggable(TAG, Log.WARN)) {
221             Log.w(TAG, "Failed to remove expected request manager fragment, manager: " + key);
222         }
223         return handled;
224     }
225 }
226