Skip to content

Commit

Permalink
Add support for AndroidX fragments (#957)
Browse files Browse the repository at this point in the history
Summary:
Fix #931

This is not how I would *like* to fix this, but it should do the job.
When the switch over to AndroidX was made, the overall abstraction
started to leak and we really need to remodel this in its entirety.
There's also the question of whether we want to support both support
fragments and AndroidX fragments or not. Right now it's kinda-sorta
supported but only under some circumstances, which is not great.

I also added some more defensive try/catches as there's some unsafe casting
involved and future changes may break this causing the entire layout to disappear.

Change Log: Fix support for AndroidX fragments in Layout Inspector.

Pull Request resolved: #957

Test Plan:
Changed the sample app to include some AndroidX fragments and they
now show up (again) in the view hierarchy:

![Screenshot 2020-04-01 13 40 53](https://user-images.githubusercontent.com/9906/78138910-915fbc00-741f-11ea-8386-4eeca9b7f932.png)

Tested internally that FB4A fragments show up again, too:

{F233098198}

Reviewed By: mweststrate

Differential Revision: D20792503

Pulled By: passy

fbshipit-source-id: 7030b897ab547d1e8803b7f0d7aaa34263cfaed2
  • Loading branch information
passy authored and facebook-github-bot committed Apr 3, 2020
1 parent 492b107 commit 4be1b4d
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 16 deletions.
Expand Up @@ -8,6 +8,7 @@
package com.facebook.flipper.plugins.inspector.descriptors;

import android.app.Activity;
import android.util.Log;
import android.view.Window;
import com.facebook.flipper.core.FlipperDynamic;
import com.facebook.flipper.core.FlipperObject;
Expand All @@ -25,6 +26,8 @@

public class ActivityDescriptor extends NodeDescriptor<Activity> {

private static final String TAG = "ActivityDescriptor";

@Override
public void init(Activity node) {}

Expand Down Expand Up @@ -112,9 +115,14 @@ private static List<Object> getDialogFragments(FragmentCompat compat, Activity a
}

FragmentManagerAccessor fragmentManagerAccessor = compat.forFragmentManager();
List<Object> addedFragments = fragmentManagerAccessor.getAddedFragments(fragmentManager);
List<Object> addedFragments = null;
try {
addedFragments = fragmentManagerAccessor.getAddedFragments(fragmentManager);
} catch (Exception e) {
Log.e(TAG, "Failed to obtain list of fragments.", e);
}
if (addedFragments == null) {
return Collections.EMPTY_LIST;
return Collections.emptyList();
}

final List<Object> dialogFragments = new ArrayList<>();
Expand Down
Expand Up @@ -9,7 +9,9 @@

import android.app.Activity;
import android.os.Build;
import androidx.fragment.app.FragmentManager;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.List;
import javax.annotation.Nullable;
import javax.annotation.concurrent.NotThreadSafe;
Expand Down Expand Up @@ -37,7 +39,7 @@ public abstract class FragmentCompat<

static {
sHasSupportFragment =
ReflectionUtil.tryGetClassForName("android.support.v4.app.Fragment") != null;
ReflectionUtil.tryGetClassForName("androidx.fragment.app.Fragment") != null;
}

@Nullable
Expand Down Expand Up @@ -82,20 +84,22 @@ static class FragmentManagerAccessorViaReflection<FRAGMENT_MANAGER, FRAGMENT>
@Nullable
@Override
public List<FRAGMENT> getAddedFragments(FRAGMENT_MANAGER fragmentManager) {
// This field is actually sitting on FragmentManagerImpl, which derives from FragmentManager.
if (mFieldMAdded == null) {
Field fieldMAdded =
ReflectionUtil.tryGetDeclaredField(fragmentManager.getClass(), "mAdded");

if (fieldMAdded != null) {
fieldMAdded.setAccessible(true);
mFieldMAdded = fieldMAdded;
if (fragmentManager instanceof android.app.FragmentManager) {
// This field is actually sitting on FragmentManagerImpl, which derives from
// FragmentManager.
if (mFieldMAdded == null) {
Field fieldMAdded =
ReflectionUtil.tryGetDeclaredField(fragmentManager.getClass(), "mAdded");

if (fieldMAdded != null) {
fieldMAdded.setAccessible(true);
mFieldMAdded = fieldMAdded;
}
}
} else if (fragmentManager instanceof androidx.fragment.app.FragmentManager) {
return (List<FRAGMENT>) ((FragmentManager) fragmentManager).getFragments();
}

return (mFieldMAdded != null)
? (List<FRAGMENT>) ReflectionUtil.getFieldValue(mFieldMAdded, fragmentManager)
: null;
return Collections.emptyList();
}
}
}
Expand Up @@ -8,11 +8,15 @@
package com.facebook.flipper.plugins.inspector.descriptors.utils.stethocopies;

import android.app.Activity;
import android.util.Log;
import android.view.View;
import java.util.Collections;
import java.util.List;
import javax.annotation.Nullable;

public final class FragmentCompatUtil {
private static final String TAG = "FragmentCompatUtil";

private FragmentCompatUtil() {}

public static boolean isDialogFragment(Object fragment) {
Expand Down Expand Up @@ -79,7 +83,13 @@ private static Object findFragmentForViewInActivity(
@Nullable
private static Object findFragmentForViewInFragmentManager(
FragmentCompat compat, Object fragmentManager, View view) {
List<?> fragments = compat.forFragmentManager().getAddedFragments(fragmentManager);
List<?> fragments;
try {
fragments = compat.forFragmentManager().getAddedFragments(fragmentManager);
} catch (Exception e) {
fragments = Collections.emptyList();
Log.e(TAG, "Failed to obtain list of fragments.", e);
}

if (fragments != null) {
for (int i = 0, N = fragments.size(); i < N; ++i) {
Expand Down

0 comments on commit 4be1b4d

Please sign in to comment.