Skip to content

Commit

Permalink
Support Fragments which are not hosted by Activities.
Browse files Browse the repository at this point in the history
A Fragment host can be of any type; if it is not an Activity, then calling
#getActivity will always return null. Use #getContext instead, which works
similarly but is non-null while the Fragment is attached.

PiperOrigin-RevId: 259059132
  • Loading branch information
daniel-google authored and glide-copybara-robot committed Jul 19, 2019
1 parent 31b501d commit 71359c7
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 16 deletions.
2 changes: 1 addition & 1 deletion library/src/main/java/com/bumptech/glide/Glide.java
Original file line number Diff line number Diff line change
Expand Up @@ -769,7 +769,7 @@ public static RequestManager with(@NonNull FragmentActivity activity) {
*/
@NonNull
public static RequestManager with(@NonNull Fragment fragment) {
return getRetriever(fragment.getActivity()).get(fragment);
return getRetriever(fragment.getContext()).get(fragment);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,13 +134,13 @@ public RequestManager get(@NonNull FragmentActivity activity) {
@NonNull
public RequestManager get(@NonNull Fragment fragment) {
Preconditions.checkNotNull(
fragment.getActivity(),
fragment.getContext(),
"You cannot start a load on a fragment before it is attached or after it is destroyed");
if (Util.isOnBackgroundThread()) {
return get(fragment.getActivity().getApplicationContext());
return get(fragment.getContext().getApplicationContext());
} else {
FragmentManager fm = fragment.getChildFragmentManager();
return supportFragmentGet(fragment.getActivity(), fm, fragment, fragment.isVisible());
return supportFragmentGet(fragment.getContext(), fm, fragment, fragment.isVisible());
}
}

Expand Down Expand Up @@ -301,7 +301,7 @@ private void findAllFragmentsWithViewsPreO(
}

@Nullable
private Activity findActivity(@NonNull Context context) {
private static Activity findActivity(@NonNull Context context) {
if (context instanceof Activity) {
return (Activity) context;
} else if (context instanceof ContextWrapper) {
Expand Down Expand Up @@ -388,15 +388,17 @@ private RequestManager fragmentGet(
}

@NonNull
SupportRequestManagerFragment getSupportRequestManagerFragment(FragmentActivity activity) {
SupportRequestManagerFragment getSupportRequestManagerFragment(
Context context, FragmentManager fragmentManager) {
return getSupportRequestManagerFragment(
activity.getSupportFragmentManager(), /*parentHint=*/ null, isActivityVisible(activity));
fragmentManager, /*parentHint=*/ null, isActivityVisible(context));
}

private static boolean isActivityVisible(Activity activity) {
private static boolean isActivityVisible(Context context) {
// This is a poor heuristic, but it's about all we have. We'd rather err on the side of visible
// and start requests than on the side of invisible and ignore valid requests.
return !activity.isFinishing();
Activity activity = findActivity(context);
return activity == null || !activity.isFinishing();
}

@NonNull
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import com.bumptech.glide.Glide;
import com.bumptech.glide.RequestManager;
import com.bumptech.glide.util.Synthetic;
Expand Down Expand Up @@ -111,9 +111,22 @@ Set<SupportRequestManagerFragment> getDescendantRequestManagerFragments() {
*/
void setParentFragmentHint(@Nullable Fragment parentFragmentHint) {
this.parentFragmentHint = parentFragmentHint;
if (parentFragmentHint != null && parentFragmentHint.getActivity() != null) {
registerFragmentWithRoot(parentFragmentHint.getActivity());
if (parentFragmentHint == null || parentFragmentHint.getContext() == null) {
return;
}
FragmentManager rootFragmentManager = getRootFragmentManager(parentFragmentHint);
if (rootFragmentManager == null) {
return;
}
registerFragmentWithRoot(parentFragmentHint.getContext(), rootFragmentManager);
}

@Nullable
private static FragmentManager getRootFragmentManager(@NonNull Fragment fragment) {
while (fragment.getParentFragment() != null) {
fragment = fragment.getParentFragment();
}
return fragment.getFragmentManager();
}

@Nullable
Expand All @@ -135,10 +148,13 @@ private boolean isDescendant(@NonNull Fragment fragment) {
return false;
}

private void registerFragmentWithRoot(@NonNull FragmentActivity activity) {
private void registerFragmentWithRoot(
@NonNull Context context, @NonNull FragmentManager fragmentManager) {
unregisterFragmentWithRoot();
rootRequestManagerFragment =
Glide.get(activity).getRequestManagerRetriever().getSupportRequestManagerFragment(activity);
Glide.get(context)
.getRequestManagerRetriever()
.getSupportRequestManagerFragment(context, fragmentManager);
if (!equals(rootRequestManagerFragment)) {
rootRequestManagerFragment.addChildRequestManagerFragment(this);
}
Expand All @@ -154,8 +170,18 @@ private void unregisterFragmentWithRoot() {
@Override
public void onAttach(Context context) {
super.onAttach(context);

FragmentManager rootFragmentManager = getRootFragmentManager(this);
if (rootFragmentManager == null) {
if (Log.isLoggable(TAG, Log.WARN)) {
// Not expected to occur; ancestor fragments should be attached before descendants.
Log.w(TAG, "Unable to register fragment with root, ancestor detached");
}
return;
}

try {
registerFragmentWithRoot(getActivity());
registerFragmentWithRoot(getContext(), rootFragmentManager);
} catch (IllegalStateException e) {
// OnAttach can be called after the activity is destroyed, see #497.
if (Log.isLoggable(TAG, Log.WARN)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,15 @@
import android.content.Context;
import android.content.ContextWrapper;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.view.LayoutInflater;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentController;
import androidx.fragment.app.FragmentHostCallback;
import androidx.test.core.app.ApplicationProvider;
import com.bumptech.glide.RequestManager;
import com.bumptech.glide.tests.BackgroundUtil.BackgroundTester;
Expand Down Expand Up @@ -50,7 +55,7 @@ public class RequestManagerRetrieverTest {
public void setUp() {
appContext = ApplicationProvider.getApplicationContext();

retriever = new RequestManagerRetriever(null /*factory*/);
retriever = new RequestManagerRetriever(/*factory=*/ null);

harnesses =
new RetrieverHarness[] {new DefaultRetrieverHarness(), new SupportRetrieverHarness()};
Expand Down Expand Up @@ -161,6 +166,23 @@ public void testSupportCanGetRequestManagerFromFragment() {
assertEquals(manager, retriever.get(fragment));
}

@Test
public void testSupportCanGetRequestManagerFromFragment_nonActivityController() {
FragmentController controller =
FragmentController.createController(new NonActivityHostCallback(appContext));
controller.attachHost(/*fragment=*/ null);
controller.dispatchCreate();
controller.dispatchStart();
controller.dispatchResume();

Fragment fragment = new Fragment();
controller.getSupportFragmentManager().beginTransaction().add(fragment, PARENT_TAG).commit();
controller.getSupportFragmentManager().executePendingTransactions();

RequestManager manager = retriever.get(fragment);
assertEquals(manager, retriever.get(fragment));
}

@Test
public void testCanGetRequestManagerFromDetachedFragment() {
helpTestCanGetRequestManagerFromDetachedFragment();
Expand Down Expand Up @@ -488,4 +510,27 @@ public void addFragmentWithTag(String tag, RequestManager manager) {
controller.get().getSupportFragmentManager().executePendingTransactions();
}
}

/** Simple callback for creating an Activity-less Fragment host. */
private final class NonActivityHostCallback
extends FragmentHostCallback<RequestManagerRetrieverTest> {

private final Context context;

NonActivityHostCallback(Context context) {
super(context, new Handler(Looper.getMainLooper()), /*windowAnimations=*/ 0);
this.context = context;
}

@Override
public LayoutInflater onGetLayoutInflater() {
return LayoutInflater.from(context).cloneInContext(context);
}

@Nullable
@Override
public RequestManagerRetrieverTest onGetHost() {
return RequestManagerRetrieverTest.this;
}
}
}

0 comments on commit 71359c7

Please sign in to comment.