Commit 5c26c215 authored by Robbie McElrath's avatar Robbie McElrath Committed by Commit Bot

Revert "[WebLayer] Create FragmentHostingRemoteFragmentImpl"

This reverts commit d5f58580.

Reason for revert: A CL this depends on got reverted

Original change's description:
> [WebLayer] Create FragmentHostingRemoteFragmentImpl
>
> This CL removes the fake FragmentActivity from SiteSettingsFragmentImpl
> and MediaRouteDialogFragmentImpl, replacing them with a Context that
> hosts the fragments instead. This is possible now that we've landed
> the bytecode rewriting that changes the return type of
> Fragment.getActivity() from FragmentActivity to Activity.
>
> Bug: 1123216
> Change-Id: I98c2c07eb348d6833ca2e5d976110f7f29b5359e
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2523751
> Commit-Queue: Robbie McElrath <rmcelrath@chromium.org>
> Reviewed-by: Evan Stade <estade@chromium.org>
> Reviewed-by: Scott Violet <sky@chromium.org>
> Reviewed-by: Finnur Thorarinsson <finnur@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#828649}

TBR=sky@chromium.org,finnur@chromium.org,estade@chromium.org,rmcelrath@chromium.org

Change-Id: I2b83becfef2a92bd878bf32ae3aa5b750853a752
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: 1123216
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2547783Reviewed-by: default avatarRobbie McElrath <rmcelrath@chromium.org>
Commit-Queue: Robbie McElrath <rmcelrath@chromium.org>
Cr-Commit-Position: refs/heads/master@{#828839}
parent b613ea78
...@@ -165,7 +165,7 @@ public class AllSiteSettings extends SiteSettingsPreferenceFragment ...@@ -165,7 +165,7 @@ public class AllSiteSettings extends SiteSettingsPreferenceFragment
/** OnClickListener for the clear button. We show an alert dialog to confirm the action */ /** OnClickListener for the clear button. We show an alert dialog to confirm the action */
@Override @Override
public void onClick(View v) { public void onClick(View v) {
if (getContext() == null || v != mClearButton) return; if (getActivity() == null || v != mClearButton) return;
long totalUsage = 0; long totalUsage = 0;
boolean includesApps = false; boolean includesApps = false;
...@@ -181,10 +181,9 @@ public class AllSiteSettings extends SiteSettingsPreferenceFragment ...@@ -181,10 +181,9 @@ public class AllSiteSettings extends SiteSettingsPreferenceFragment
} }
} }
AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
LayoutInflater inflater = View dialogView =
(LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); getActivity().getLayoutInflater().inflate(R.layout.clear_data_dialog, null);
View dialogView = inflater.inflate(R.layout.clear_data_dialog, null);
TextView message = dialogView.findViewById(android.R.id.message); TextView message = dialogView.findViewById(android.R.id.message);
TextView signedOutText = dialogView.findViewById(R.id.signed_out_text); TextView signedOutText = dialogView.findViewById(R.id.signed_out_text);
TextView offlineText = dialogView.findViewById(R.id.offline_text); TextView offlineText = dialogView.findViewById(R.id.offline_text);
...@@ -193,7 +192,7 @@ public class AllSiteSettings extends SiteSettingsPreferenceFragment ...@@ -193,7 +192,7 @@ public class AllSiteSettings extends SiteSettingsPreferenceFragment
String dialogFormattedText = String dialogFormattedText =
getString(includesApps ? R.string.webstorage_clear_data_dialog_message_with_app getString(includesApps ? R.string.webstorage_clear_data_dialog_message_with_app
: R.string.webstorage_clear_data_dialog_message, : R.string.webstorage_clear_data_dialog_message,
Formatter.formatShortFileSize(getContext(), totalUsage)); Formatter.formatShortFileSize(getActivity(), totalUsage));
message.setText(dialogFormattedText); message.setText(dialogFormattedText);
builder.setView(dialogView); builder.setView(dialogView);
builder.setPositiveButton(R.string.storage_clear_dialog_clear_storage_option, builder.setPositiveButton(R.string.storage_clear_dialog_clear_storage_option,
...@@ -247,7 +246,7 @@ public class AllSiteSettings extends SiteSettingsPreferenceFragment ...@@ -247,7 +246,7 @@ public class AllSiteSettings extends SiteSettingsPreferenceFragment
MenuItem help = menu.add( MenuItem help = menu.add(
Menu.NONE, R.id.menu_id_site_settings_help, Menu.NONE, R.string.menu_help); Menu.NONE, R.id.menu_id_site_settings_help, Menu.NONE, R.string.menu_help);
help.setIcon(VectorDrawableCompat.create( help.setIcon(VectorDrawableCompat.create(
getResources(), R.drawable.ic_help_and_feedback, getContext().getTheme())); getResources(), R.drawable.ic_help_and_feedback, getActivity().getTheme()));
} }
} }
......
...@@ -122,7 +122,7 @@ public class ChosenObjectSettings extends SiteSettingsPreferenceFragment { ...@@ -122,7 +122,7 @@ public class ChosenObjectSettings extends SiteSettingsPreferenceFragment {
MenuItem help = menu.add( MenuItem help = menu.add(
Menu.NONE, R.id.menu_id_site_settings_help, Menu.NONE, R.string.menu_help); Menu.NONE, R.id.menu_id_site_settings_help, Menu.NONE, R.string.menu_help);
help.setIcon(VectorDrawableCompat.create( help.setIcon(VectorDrawableCompat.create(
getResources(), R.drawable.ic_help_and_feedback, getContext().getTheme())); getResources(), R.drawable.ic_help_and_feedback, getActivity().getTheme()));
} }
} }
...@@ -166,7 +166,7 @@ public class ChosenObjectSettings extends SiteSettingsPreferenceFragment { ...@@ -166,7 +166,7 @@ public class ChosenObjectSettings extends SiteSettingsPreferenceFragment {
// Managed objects cannot be revoked, so finish the activity only if the list did not // Managed objects cannot be revoked, so finish the activity only if the list did not
// contain managed objects. // contain managed objects.
if (hasManagedObject) { if (hasManagedObject) {
ManagedPreferencesUtils.showManagedSettingsCannotBeResetToast(getContext()); ManagedPreferencesUtils.showManagedSettingsCannotBeResetToast(getActivity());
} else { } else {
getActivity().finish(); getActivity().finish();
} }
...@@ -240,7 +240,7 @@ public class ChosenObjectSettings extends SiteSettingsPreferenceFragment { ...@@ -240,7 +240,7 @@ public class ChosenObjectSettings extends SiteSettingsPreferenceFragment {
header.setTitle(titleText); header.setTitle(titleText);
header.setImageView(R.drawable.ic_delete_white_24dp, header.setImageView(R.drawable.ic_delete_white_24dp,
R.string.website_settings_revoke_all_permissions_for_device, (View view) -> { R.string.website_settings_revoke_all_permissions_for_device, (View view) -> {
new AlertDialog.Builder(getContext(), R.style.Theme_Chromium_AlertDialog) new AlertDialog.Builder(getActivity(), R.style.Theme_Chromium_AlertDialog)
.setTitle(R.string.reset) .setTitle(R.string.reset)
.setMessage(dialogMsg) .setMessage(dialogMsg)
.setPositiveButton(R.string.reset, .setPositiveButton(R.string.reset,
......
...@@ -363,7 +363,7 @@ public class SingleCategorySettings extends SiteSettingsPreferenceFragment ...@@ -363,7 +363,7 @@ public class SingleCategorySettings extends SiteSettingsPreferenceFragment
MenuItem help = menu.add( MenuItem help = menu.add(
Menu.NONE, R.id.menu_id_site_settings_help, Menu.NONE, R.string.menu_help); Menu.NONE, R.id.menu_id_site_settings_help, Menu.NONE, R.string.menu_help);
help.setIcon(VectorDrawableCompat.create( help.setIcon(VectorDrawableCompat.create(
getResources(), R.drawable.ic_help_and_feedback, getContext().getTheme())); getResources(), R.drawable.ic_help_and_feedback, getActivity().getTheme()));
} }
} }
...@@ -588,7 +588,8 @@ public class SingleCategorySettings extends SiteSettingsPreferenceFragment ...@@ -588,7 +588,8 @@ public class SingleCategorySettings extends SiteSettingsPreferenceFragment
String hostname = primaryPattern.equals(SITE_WILDCARD) ? secondaryPattern : primaryPattern; String hostname = primaryPattern.equals(SITE_WILDCARD) ? secondaryPattern : primaryPattern;
Toast.makeText(getActivity(), Toast.makeText(getActivity(),
String.format(getContext().getString(R.string.website_settings_add_site_toast), String.format(
getActivity().getString(R.string.website_settings_add_site_toast),
hostname), hostname),
Toast.LENGTH_SHORT) Toast.LENGTH_SHORT)
.show(); .show();
...@@ -790,7 +791,7 @@ public class SingleCategorySettings extends SiteSettingsPreferenceFragment ...@@ -790,7 +791,7 @@ public class SingleCategorySettings extends SiteSettingsPreferenceFragment
extras.putString(EXTRA_TITLE, getActivity().getTitle().toString()); extras.putString(EXTRA_TITLE, getActivity().getTitle().toString());
extras.putSerializable(ChosenObjectSettings.EXTRA_OBJECT_INFOS, entry.first); extras.putSerializable(ChosenObjectSettings.EXTRA_OBJECT_INFOS, entry.first);
extras.putSerializable(ChosenObjectSettings.EXTRA_SITES, entry.second); extras.putSerializable(ChosenObjectSettings.EXTRA_SITES, entry.second);
preference.setIcon(SettingsUtils.getTintedIcon(getContext(), preference.setIcon(SettingsUtils.getTintedIcon(getActivity(),
ContentSettingsResources.getIcon(mCategory.getContentSettingsType()))); ContentSettingsResources.getIcon(mCategory.getContentSettingsType())));
preference.setTitle(entry.first.get(0).getName()); preference.setTitle(entry.first.get(0).getName());
preference.setFragment(ChosenObjectSettings.class.getCanonicalName()); preference.setFragment(ChosenObjectSettings.class.getCanonicalName());
...@@ -1060,7 +1061,7 @@ public class SingleCategorySettings extends SiteSettingsPreferenceFragment ...@@ -1060,7 +1061,7 @@ public class SingleCategorySettings extends SiteSettingsPreferenceFragment
descriptions[1] = descriptions[1] =
getString(ContentSettingsResources.getSiteSummary(ContentSettingValues.BLOCK)); getString(ContentSettingsResources.getSiteSummary(ContentSettingValues.BLOCK));
return new AlertDialog.Builder(getContext(), R.style.Theme_Chromium_AlertDialog) return new AlertDialog.Builder(getActivity(), R.style.Theme_Chromium_AlertDialog)
.setPositiveButton(R.string.cancel, null) .setPositiveButton(R.string.cancel, null)
.setNegativeButton(R.string.remove, .setNegativeButton(R.string.remove,
(dialog, which) -> { (dialog, which) -> {
......
...@@ -227,7 +227,7 @@ public class SingleWebsiteSettings extends SiteSettingsPreferenceFragment ...@@ -227,7 +227,7 @@ public class SingleWebsiteSettings extends SiteSettingsPreferenceFragment
@Override @Override
public void onActivityCreated(Bundle savedInstanceState) { public void onActivityCreated(Bundle savedInstanceState) {
getActivity().setTitle(getContext().getString(R.string.prefs_site_settings)); getActivity().setTitle(R.string.prefs_site_settings);
init(); init();
super.onActivityCreated(savedInstanceState); super.onActivityCreated(savedInstanceState);
} }
...@@ -358,7 +358,7 @@ public class SingleWebsiteSettings extends SiteSettingsPreferenceFragment ...@@ -358,7 +358,7 @@ public class SingleWebsiteSettings extends SiteSettingsPreferenceFragment
@ContentSettingValues @Nullable Integer value, boolean enabled) { @ContentSettingValues @Nullable Integer value, boolean enabled) {
Drawable icon = enabled Drawable icon = enabled
? SettingsUtils.getTintedIcon( ? SettingsUtils.getTintedIcon(
getContext(), ContentSettingsResources.getIcon(contentSettingsType)) getActivity(), ContentSettingsResources.getIcon(contentSettingsType))
: ContentSettingsResources.getDisabledIcon(contentSettingsType, getResources()); : ContentSettingsResources.getDisabledIcon(contentSettingsType, getResources());
if (getSiteSettingsClient().isPageInfoV2Enabled() && value != null if (getSiteSettingsClient().isPageInfoV2Enabled() && value != null
&& value == ContentSettingValues.BLOCK) { && value == ContentSettingValues.BLOCK) {
...@@ -691,8 +691,8 @@ public class SingleWebsiteSettings extends SiteSettingsPreferenceFragment ...@@ -691,8 +691,8 @@ public class SingleWebsiteSettings extends SiteSettingsPreferenceFragment
new ChromeImageViewPreference(getStyledContext()); new ChromeImageViewPreference(getStyledContext());
preference.setKey(CHOOSER_PERMISSION_PREFERENCE_KEY); preference.setKey(CHOOSER_PERMISSION_PREFERENCE_KEY);
preference.setIcon(SettingsUtils.getTintedIcon( preference.setIcon(SettingsUtils.getTintedIcon(getActivity(),
getContext(), ContentSettingsResources.getIcon(info.getContentSettingsType()))); ContentSettingsResources.getIcon(info.getContentSettingsType())));
preference.setOrder(maxPermissionOrder); preference.setOrder(maxPermissionOrder);
preference.setTitle(info.getName()); preference.setTitle(info.getName());
preference.setImageView(R.drawable.ic_delete_white_24dp, preference.setImageView(R.drawable.ic_delete_white_24dp,
...@@ -1076,7 +1076,7 @@ public class SingleWebsiteSettings extends SiteSettingsPreferenceFragment ...@@ -1076,7 +1076,7 @@ public class SingleWebsiteSettings extends SiteSettingsPreferenceFragment
: R.string.website_reset_confirmation; : R.string.website_reset_confirmation;
int buttonResId = mHideNonPermissionPreferences ? R.string.reset : titleResId; int buttonResId = mHideNonPermissionPreferences ? R.string.reset : titleResId;
// Handle the Clear & Reset preference click by showing a confirmation. // Handle the Clear & Reset preference click by showing a confirmation.
new AlertDialog.Builder(getContext(), R.style.Theme_Chromium_AlertDialog) new AlertDialog.Builder(getActivity(), R.style.Theme_Chromium_AlertDialog)
.setTitle(titleResId) .setTitle(titleResId)
.setMessage(confirmationResId) .setMessage(confirmationResId)
.setPositiveButton(buttonResId, .setPositiveButton(buttonResId,
...@@ -1154,7 +1154,7 @@ public class SingleWebsiteSettings extends SiteSettingsPreferenceFragment ...@@ -1154,7 +1154,7 @@ public class SingleWebsiteSettings extends SiteSettingsPreferenceFragment
mObjectUserPermissionCount = 0; mObjectUserPermissionCount = 0;
if (mObjectPolicyPermissionCount > 0) { if (mObjectPolicyPermissionCount > 0) {
ManagedPreferencesUtils.showManagedSettingsCannotBeResetToast(getContext()); ManagedPreferencesUtils.showManagedSettingsCannotBeResetToast(getActivity());
} }
} }
} }
...@@ -31,7 +31,7 @@ public class SiteSettings ...@@ -31,7 +31,7 @@ public class SiteSettings
@Override @Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
SettingsUtils.addPreferencesFromResource(this, R.xml.site_settings_preferences); SettingsUtils.addPreferencesFromResource(this, R.xml.site_settings_preferences);
getActivity().setTitle(getContext().getString(R.string.prefs_site_settings)); getActivity().setTitle(R.string.prefs_site_settings);
configurePreferences(); configurePreferences();
updatePreferenceStates(); updatePreferenceStates();
...@@ -120,7 +120,7 @@ public class SiteSettings ...@@ -120,7 +120,7 @@ public class SiteSettings
if (p.isEnabled()) { if (p.isEnabled()) {
p.setIcon(SettingsUtils.getTintedIcon( p.setIcon(SettingsUtils.getTintedIcon(
getContext(), ContentSettingsResources.getIcon(contentType))); getActivity(), ContentSettingsResources.getIcon(contentType)));
} else { } else {
p.setIcon(ContentSettingsResources.getDisabledIcon(contentType, getResources())); p.setIcon(ContentSettingsResources.getDisabledIcon(contentType, getResources()));
} }
......
...@@ -123,7 +123,6 @@ android_library("java") { ...@@ -123,7 +123,6 @@ android_library("java") {
"org/chromium/weblayer_private/ExternalNavigationDelegateImpl.java", "org/chromium/weblayer_private/ExternalNavigationDelegateImpl.java",
"org/chromium/weblayer_private/FaviconCallbackProxy.java", "org/chromium/weblayer_private/FaviconCallbackProxy.java",
"org/chromium/weblayer_private/FragmentAndroidPermissionDelegate.java", "org/chromium/weblayer_private/FragmentAndroidPermissionDelegate.java",
"org/chromium/weblayer_private/FragmentHostingRemoteFragmentImpl.java",
"org/chromium/weblayer_private/FragmentWindowAndroid.java", "org/chromium/weblayer_private/FragmentWindowAndroid.java",
"org/chromium/weblayer_private/FullscreenCallbackProxy.java", "org/chromium/weblayer_private/FullscreenCallbackProxy.java",
"org/chromium/weblayer_private/FullscreenToast.java", "org/chromium/weblayer_private/FullscreenToast.java",
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.weblayer_private;
import android.content.Context;
import android.content.ContextWrapper;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.InflateException;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewStub;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.fragment.app.FragmentController;
import androidx.fragment.app.FragmentHostCallback;
import androidx.fragment.app.FragmentManager;
import org.chromium.weblayer_private.interfaces.IRemoteFragmentClient;
import org.chromium.weblayer_private.interfaces.StrictModeWorkaround;
import java.lang.reflect.Constructor;
/**
* A base class for RemoteFragmentImpls that need to host child Fragments.
*
* Because Fragments created in WebLayer use the AndroidX library from WebLayer's ClassLoader, we
* can't attach Fragments created here directly to the embedder's Fragment tree, and have to create
* a local FragmentController to manage them. This class handles creating the FragmentController,
* and forwards all Fragment lifecycle events from the RemoteFragment in the embedder's Fragment
* tree to child Fragments of this class.
*/
public abstract class FragmentHostingRemoteFragmentImpl extends RemoteFragmentImpl {
// The WebLayer-wrapped context object. This context gets assets and resources from WebLayer,
// not from the embedder. Use this for the most part, especially to resolve WebLayer-specific
// resource IDs.
private Context mContext;
private boolean mStarted;
private FragmentController mFragmentController;
protected static class RemoteFragmentContext
extends ContextWrapper implements LayoutInflater.Factory2 {
private static final Class<?>[] VIEW_CONSTRUCTOR_ARGS =
new Class[] {Context.class, AttributeSet.class};
public RemoteFragmentContext(Context webLayerContext) {
super(webLayerContext);
// Register ourselves as a the LayoutInflater factory so we can handle loading Views.
// See onCreateView for information about why this is needed.
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) {
getLayoutInflater().setFactory2(this);
}
}
// This method is needed to work around a LayoutInflater bug in Android <N. Before
// LayoutInflater creates an instance of a View, it needs to look up the class by name to
// get a reference to its Constructor. As an optimization, it caches this name to
// Constructor mapping. This cache causes issues if a class gets loaded multiple times with
// different ClassLoaders. In some UIs, some AndroidX Views get loaded early on with the
// embedding app's ClassLoader, so the Constructor from that ClassLoader's version of the
// class gets cached. When the WebLayer implementation later tries to inflate the same
// class, it instantiates a version from the wrong ClassLoader, which leads to a
// ClassCastException when casting that View to its original class. This was fixed in
// Android N, but to work around it on L & M, we inflate the Views manually here, which
// bypasses LayoutInflater's cache.
@Override
public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
// If the class doesn't have a '.' in its name, it's probably a built-in Android View,
// which are often referenced by just their class names with no package prefix. For
// these classes we can return null to fall back to LayoutInflater's default behavior.
if (name.indexOf('.') == -1) {
return null;
}
Class<? extends View> clazz = null;
try {
clazz = context.getClassLoader().loadClass(name).asSubclass(View.class);
LayoutInflater inflater = getLayoutInflater();
if (inflater.getFilter() != null && !inflater.getFilter().onLoadClass(clazz)) {
throw new InflateException(attrs.getPositionDescription()
+ ": Class not allowed to be inflated " + name);
}
Constructor<? extends View> constructor =
clazz.getConstructor(VIEW_CONSTRUCTOR_ARGS);
constructor.setAccessible(true);
View view = constructor.newInstance(new Object[] {context, attrs});
if (view instanceof ViewStub) {
// Use the same Context when inflating ViewStub later.
ViewStub viewStub = (ViewStub) view;
viewStub.setLayoutInflater(inflater.cloneInContext(context));
}
return view;
} catch (Exception e) {
InflateException ie = new InflateException(attrs.getPositionDescription()
+ ": Error inflating class "
+ (clazz == null ? "<unknown>" : clazz.getName()));
ie.initCause(e);
throw ie;
}
}
@Override
public View onCreateView(String name, Context context, AttributeSet attrs) {
return null;
}
private LayoutInflater getLayoutInflater() {
return (LayoutInflater) getBaseContext().getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
}
}
private static class RemoteFragmentHostCallback extends FragmentHostCallback<Context> {
private final FragmentHostingRemoteFragmentImpl mFragmentImpl;
private RemoteFragmentHostCallback(FragmentHostingRemoteFragmentImpl fragmentImpl) {
super(fragmentImpl.getWebLayerContext(), new Handler(), 0);
mFragmentImpl = fragmentImpl;
}
@Override
public Context onGetHost() {
return mFragmentImpl.getWebLayerContext();
}
@Override
public LayoutInflater onGetLayoutInflater() {
Context context = mFragmentImpl.getWebLayerContext();
return ((LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE))
.cloneInContext(context);
}
@Override
public boolean onHasView() {
return mFragmentImpl.getView() != null;
}
@Override
public View onFindViewById(int id) {
return onHasView() ? mFragmentImpl.getView().findViewById(id) : null;
}
}
protected FragmentHostingRemoteFragmentImpl(IRemoteFragmentClient remoteFragmentClient) {
super(remoteFragmentClient);
}
@Override
public void onAttach(Context embedderContext) {
StrictModeWorkaround.apply();
super.onAttach(embedderContext);
mContext = createRemoteFragmentContext(embedderContext);
mFragmentController =
FragmentController.createController(new RemoteFragmentHostCallback(this));
// Some appcompat functionality depends on Fragments being hosted from within an
// AppCompatActivity, which performs some static initialization. Even if we're running
// within an AppCompatActivity, it will be from the embedder's ClassLoader, so in WebLayer's
// ClassLoader the initialization hasn't occurred. Creating an AppCompatDelegate manually
// here will perform the necessary initialization.
AppCompatDelegate.create(getActivity(), null);
}
@Override
public void onCreate(Bundle savedInstanceState) {
StrictModeWorkaround.apply();
mFragmentController.attachHost(null);
super.onCreate(savedInstanceState);
mFragmentController.dispatchCreate();
}
@Override
public void onDestroyView() {
StrictModeWorkaround.apply();
super.onDestroyView();
mFragmentController.dispatchDestroyView();
}
@Override
public void onDestroy() {
StrictModeWorkaround.apply();
super.onDestroy();
mFragmentController.dispatchDestroy();
}
@Override
public void onDetach() {
StrictModeWorkaround.apply();
super.onDetach();
mContext = null;
}
@Override
public void onStart() {
super.onStart();
if (!mStarted) {
mStarted = true;
mFragmentController.dispatchActivityCreated();
}
mFragmentController.noteStateNotSaved();
mFragmentController.execPendingActions();
mFragmentController.dispatchStart();
}
@Override
public void onStop() {
super.onStop();
mFragmentController.dispatchStop();
}
@Override
public void onResume() {
super.onResume();
mFragmentController.dispatchResume();
}
@Override
public void onPause() {
super.onPause();
mFragmentController.dispatchPause();
}
public FragmentManager getSupportFragmentManager() {
return mFragmentController.getSupportFragmentManager();
}
/**
* Returns the RemoteFragmentContext that should be used in the child Fragment tree.
*
* Implementations will typically wrap embedderContext with ClassLoaderContextWrapperFactory,
* and possibly set a Theme.
*/
protected abstract RemoteFragmentContext createRemoteFragmentContext(Context embedderContext);
protected Context getWebLayerContext() {
return mContext;
}
}
...@@ -4,15 +4,26 @@ ...@@ -4,15 +4,26 @@
package org.chromium.weblayer_private; package org.chromium.weblayer_private;
import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.ContextThemeWrapper; import android.view.ContextThemeWrapper;
import android.view.InflateException;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.View.OnAttachStateChangeListener; import android.view.View.OnAttachStateChangeListener;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.ViewStub;
import android.view.Window;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentController;
import androidx.fragment.app.FragmentHostCallback;
import androidx.preference.Preference; import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat; import androidx.preference.PreferenceFragmentCompat;
...@@ -32,29 +43,153 @@ import org.chromium.weblayer_private.interfaces.SiteSettingsFragmentArgs; ...@@ -32,29 +43,153 @@ import org.chromium.weblayer_private.interfaces.SiteSettingsFragmentArgs;
import org.chromium.weblayer_private.interfaces.SiteSettingsIntentHelper; import org.chromium.weblayer_private.interfaces.SiteSettingsIntentHelper;
import org.chromium.weblayer_private.interfaces.StrictModeWorkaround; import org.chromium.weblayer_private.interfaces.StrictModeWorkaround;
import java.lang.reflect.Constructor;
/** /**
* WebLayer's implementation of the client library's SiteSettingsFragment. * WebLayer's implementation of the client library's SiteSettingsFragment.
* *
* This class creates an instance of the Fragment given in its FRAGMENT_NAME argument. * This class creates an instance of the Fragment given in its FRAGMENT_NAME argument, and forwards
* all incoming lifecycle events from SiteSettingsFragment to it. Because Fragments created in
* WebLayer use the AndroidX library from WebLayer's ClassLoader, we can't attach the Fragment
* created here directly to the embedder's Fragment tree, and have to create a local
* FragmentController to manage it.
*/ */
public class SiteSettingsFragmentImpl extends FragmentHostingRemoteFragmentImpl { public class SiteSettingsFragmentImpl extends RemoteFragmentImpl {
private static final String FRAGMENT_TAG = "site_settings_fragment"; private static final String FRAGMENT_TAG = "site_settings_fragment";
private final ProfileImpl mProfile; private final ProfileImpl mProfile;
private final Class<? extends SiteSettingsPreferenceFragment> mFragmentClass; private final Class<? extends SiteSettingsPreferenceFragment> mFragmentClass;
private final Bundle mFragmentArguments; private final Bundle mFragmentArguments;
private static class SiteSettingsContext // The embedder's original context object.
extends FragmentHostingRemoteFragmentImpl.RemoteFragmentContext private Context mEmbedderContext;
// The WebLayer-wrapped context object. This context gets assets and resources from WebLayer,
// not from the embedder. Use this for the most part, especially to resolve WebLayer-specific
// resource IDs.
private Context mContext;
private boolean mStarted;
private FragmentController mFragmentController;
/**
* A fake FragmentActivity needed to make the Fragment system happy.
*
* PreferenceFragmentCompat calls Fragment.getActivity, which casts the Activity given to the
* FragmentHostCallback to a FragmentActivity. Because of the AndroidX ClassLoader issue
* mentioned aove, this cast will fail if we use the embedder's Activity because the
* FragmentActivity it derives from lives in another ClassLoader. This class exists to provide
* a FragmentActivity for the Site Settings Fragments to run in, and forwards necessary methods
* to the remote Activity.
*/
private static class PassthroughFragmentActivity extends FragmentActivity
implements PreferenceFragmentCompat.OnPreferenceStartFragmentCallback { implements PreferenceFragmentCompat.OnPreferenceStartFragmentCallback {
private final Context mEmbedderContext; private static final Class<?>[] VIEW_CONSTRUCTOR_ARGS =
new Class[] {Context.class, AttributeSet.class};
private final SiteSettingsFragmentImpl mFragmentImpl; private final SiteSettingsFragmentImpl mFragmentImpl;
public SiteSettingsContext(SiteSettingsFragmentImpl fragmentImpl, Context embedderContext) { private PassthroughFragmentActivity(SiteSettingsFragmentImpl fragmentImpl) {
super(new ContextThemeWrapper(ClassLoaderContextWrapperFactory.get(embedderContext),
R.style.Theme_WebLayer_SiteSettings));
mEmbedderContext = embedderContext;
mFragmentImpl = fragmentImpl; mFragmentImpl = fragmentImpl;
attachBaseContext(mFragmentImpl.getWebLayerContext());
// Register ourselves as a the LayoutInflater factory so we can handle loading Views.
// See onCreateView for information about why this is needed.
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) {
getLayoutInflater().setFactory2(this);
}
// This class doesn't extend AppCompatActivity, so some appcompat functionality doesn't
// get initialized, which leads to some appcompat widgets (like switches) rendering
// incorrectly. There are some resource issues with having this class extend
// AppCompatActivity, but until we sort those out, creating an AppCompatDelegate will
// perform the necessary initialization.
AppCompatDelegate.create(this, null);
}
@Override
public Object getSystemService(String name) {
if (Context.LAYOUT_INFLATER_SERVICE.equals(name)) {
return getLayoutInflater();
}
return getEmbedderActivity().getSystemService(name);
}
@Override
public LayoutInflater getLayoutInflater() {
return (LayoutInflater) getBaseContext().getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
}
// This method is needed to work around a LayoutInflater bug in Android <N. Before
// LayoutInflater creates an instance of a View, it needs to look up the class by name to
// get a reference to its Constructor. As an optimization, it caches this name to
// Constructor mapping. This cache causes issues if a class gets loaded multiple times with
// different ClassLoaders. In Site Settings, some AndroidX Views get loaded early on with
// the embedding app's ClassLoader, so the Constructor from that ClassLoader's version of
// the class gets cached. When the WebLayer implementation later tries to inflate the same
// class, it instantiates a version from the wrong ClassLoader, which leads to a
// ClassCastException when casting that View to its original class. This was fixed in
// Android N, but to work around it on L & M, we inflate the Views manually here, which
// bypasses LayoutInflater's cache.
@Override
public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
// If the class doesn't have a '.' in its name, it's probably a built-in Android View,
// which are often referenced by just their class names with no package prefix. For
// these classes we can return null to fall back to LayoutInflater's default behavior.
if (name.indexOf('.') == -1) {
return null;
}
Class<? extends View> clazz = null;
try {
clazz = context.getClassLoader().loadClass(name).asSubclass(View.class);
LayoutInflater inflater = getLayoutInflater();
if (inflater.getFilter() != null && !inflater.getFilter().onLoadClass(clazz)) {
throw new InflateException(attrs.getPositionDescription()
+ ": Class not allowed to be inflated " + name);
}
Constructor<? extends View> constructor =
clazz.getConstructor(VIEW_CONSTRUCTOR_ARGS);
constructor.setAccessible(true);
View view = constructor.newInstance(new Object[] {context, attrs});
if (view instanceof ViewStub) {
// Use the same Context when inflating ViewStub later.
ViewStub viewStub = (ViewStub) view;
viewStub.setLayoutInflater(inflater.cloneInContext(context));
}
return view;
} catch (Exception e) {
InflateException ie = new InflateException(attrs.getPositionDescription()
+ ": Error inflating class "
+ (clazz == null ? "<unknown>" : clazz.getName()));
ie.initCause(e);
throw ie;
}
}
@Override
public Window getWindow() {
return getEmbedderActivity().getWindow();
}
@Override
public Context getApplicationContext() {
return getEmbedderActivity().getApplicationContext();
}
@Override
public void startActivity(Intent intent) {
getEmbedderActivity().startActivity(intent);
}
@Override
public void setTitle(int titleId) {
getEmbedderActivity().setTitle(mFragmentImpl.getWebLayerContext().getString(titleId));
}
@Override
public void setTitle(CharSequence title) {
getEmbedderActivity().setTitle(title);
} }
@Override @Override
...@@ -73,15 +208,18 @@ public class SiteSettingsFragmentImpl extends FragmentHostingRemoteFragmentImpl ...@@ -73,15 +208,18 @@ public class SiteSettingsFragmentImpl extends FragmentHostingRemoteFragmentImpl
ProfileImpl profile = mFragmentImpl.getProfile(); ProfileImpl profile = mFragmentImpl.getProfile();
if (newFragmentClassName.equals(SiteSettings.class.getName())) { if (newFragmentClassName.equals(SiteSettings.class.getName())) {
intent = SiteSettingsIntentHelper.createIntentForCategoryList( intent = SiteSettingsIntentHelper.createIntentForCategoryList(
mEmbedderContext, profile.getName(), profile.isIncognito()); mFragmentImpl.getEmbedderContext(), profile.getName(),
profile.isIncognito());
} else if (newFragmentClassName.equals(SingleCategorySettings.class.getName())) { } else if (newFragmentClassName.equals(SingleCategorySettings.class.getName())) {
intent = SiteSettingsIntentHelper.createIntentForSingleCategory(mEmbedderContext, intent = SiteSettingsIntentHelper.createIntentForSingleCategory(
profile.getName(), profile.isIncognito(), mFragmentImpl.getEmbedderContext(), profile.getName(),
profile.isIncognito(),
newFragmentArgs.getString(SingleCategorySettings.EXTRA_CATEGORY), newFragmentArgs.getString(SingleCategorySettings.EXTRA_CATEGORY),
newFragmentArgs.getString(SingleCategorySettings.EXTRA_TITLE)); newFragmentArgs.getString(SingleCategorySettings.EXTRA_TITLE));
} else if (newFragmentClassName.equals(AllSiteSettings.class.getName())) { } else if (newFragmentClassName.equals(AllSiteSettings.class.getName())) {
intent = SiteSettingsIntentHelper.createIntentForAllSites(mEmbedderContext, intent = SiteSettingsIntentHelper.createIntentForAllSites(
profile.getName(), profile.isIncognito(), mFragmentImpl.getEmbedderContext(), profile.getName(),
profile.isIncognito(),
newFragmentArgs.getString(AllSiteSettings.EXTRA_CATEGORY), newFragmentArgs.getString(AllSiteSettings.EXTRA_CATEGORY),
newFragmentArgs.getString(AllSiteSettings.EXTRA_TITLE)); newFragmentArgs.getString(AllSiteSettings.EXTRA_TITLE));
} else if (newFragmentClassName.equals(SingleWebsiteSettings.class.getName())) { } else if (newFragmentClassName.equals(SingleWebsiteSettings.class.getName())) {
...@@ -96,14 +234,50 @@ public class SiteSettingsFragmentImpl extends FragmentHostingRemoteFragmentImpl ...@@ -96,14 +234,50 @@ public class SiteSettingsFragmentImpl extends FragmentHostingRemoteFragmentImpl
} else { } else {
throw new IllegalArgumentException("No website provided"); throw new IllegalArgumentException("No website provided");
} }
intent = SiteSettingsIntentHelper.createIntentForSingleWebsite(mEmbedderContext, intent = SiteSettingsIntentHelper.createIntentForSingleWebsite(
profile.getName(), profile.isIncognito(), address.getOrigin()); mFragmentImpl.getEmbedderContext(), profile.getName(),
profile.isIncognito(), address.getOrigin());
} else { } else {
throw new IllegalArgumentException("Unsupported Fragment: " + newFragmentClassName); throw new IllegalArgumentException("Unsupported Fragment: " + newFragmentClassName);
} }
mFragmentImpl.getActivity().startActivity(intent); getEmbedderActivity().startActivity(intent);
return true; return true;
} }
private Activity getEmbedderActivity() {
return mFragmentImpl.getActivity();
}
}
private static class SiteSettingsFragmentHostCallback extends FragmentHostCallback<Context> {
private final SiteSettingsFragmentImpl mFragmentImpl;
private SiteSettingsFragmentHostCallback(SiteSettingsFragmentImpl fragmentImpl) {
super(new PassthroughFragmentActivity(fragmentImpl), new Handler(), 0);
mFragmentImpl = fragmentImpl;
}
@Override
public Context onGetHost() {
return mFragmentImpl.getWebLayerContext();
}
@Override
public LayoutInflater onGetLayoutInflater() {
Context context = mFragmentImpl.getWebLayerContext();
return ((LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE))
.cloneInContext(context);
}
@Override
public boolean onHasView() {
return mFragmentImpl.getView() != null;
}
@Override
public View onFindViewById(int id) {
return onHasView() ? mFragmentImpl.getView().findViewById(id) : null;
}
} }
public SiteSettingsFragmentImpl(ProfileManager profileManager, public SiteSettingsFragmentImpl(ProfileManager profileManager,
...@@ -118,7 +292,6 @@ public class SiteSettingsFragmentImpl extends FragmentHostingRemoteFragmentImpl ...@@ -118,7 +292,6 @@ public class SiteSettingsFragmentImpl extends FragmentHostingRemoteFragmentImpl
isIncognito = "".equals(profileName); isIncognito = "".equals(profileName);
} }
mProfile = profileManager.getProfile(profileName, isIncognito); mProfile = profileManager.getProfile(profileName, isIncognito);
// Convert the WebLayer ABI's Site Settings arguments into the format the Site Settings // Convert the WebLayer ABI's Site Settings arguments into the format the Site Settings
// implementation fragments expect. // implementation fragments expect.
Bundle fragmentArgs = intentExtras.getBundle(SiteSettingsFragmentArgs.FRAGMENT_ARGUMENTS); Bundle fragmentArgs = intentExtras.getBundle(SiteSettingsFragmentArgs.FRAGMENT_ARGUMENTS);
...@@ -154,9 +327,25 @@ public class SiteSettingsFragmentImpl extends FragmentHostingRemoteFragmentImpl ...@@ -154,9 +327,25 @@ public class SiteSettingsFragmentImpl extends FragmentHostingRemoteFragmentImpl
} }
@Override @Override
protected FragmentHostingRemoteFragmentImpl.RemoteFragmentContext createRemoteFragmentContext( public void onAttach(Context context) {
Context embedderContext) { StrictModeWorkaround.apply();
return new SiteSettingsContext(this, embedderContext); super.onAttach(context);
mEmbedderContext = context;
mContext = new ContextThemeWrapper(
ClassLoaderContextWrapperFactory.get(context), R.style.Theme_WebLayer_SiteSettings);
mFragmentController =
FragmentController.createController(new SiteSettingsFragmentHostCallback(this));
}
@Override
public void onCreate(Bundle savedInstanceState) {
StrictModeWorkaround.apply();
mFragmentController.attachHost(null);
super.onCreate(savedInstanceState);
mFragmentController.dispatchCreate();
} }
@Override @Override
...@@ -165,13 +354,14 @@ public class SiteSettingsFragmentImpl extends FragmentHostingRemoteFragmentImpl ...@@ -165,13 +354,14 @@ public class SiteSettingsFragmentImpl extends FragmentHostingRemoteFragmentImpl
Context.LAYOUT_INFLATER_SERVICE); Context.LAYOUT_INFLATER_SERVICE);
View root = View root =
inflater.inflate(R.layout.site_settings_layout, container, /*attachToRoot=*/false); inflater.inflate(R.layout.site_settings_layout, container, /*attachToRoot=*/false);
if (getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG) == null) { if (mFragmentController.getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG)
== null) {
try { try {
SiteSettingsPreferenceFragment siteSettingsFragment = mFragmentClass.newInstance(); SiteSettingsPreferenceFragment siteSettingsFragment = mFragmentClass.newInstance();
siteSettingsFragment.setArguments(mFragmentArguments); siteSettingsFragment.setArguments(mFragmentArguments);
siteSettingsFragment.setSiteSettingsClient( siteSettingsFragment.setSiteSettingsClient(
new WebLayerSiteSettingsClient(mProfile)); new WebLayerSiteSettingsClient(mProfile));
getSupportFragmentManager() mFragmentController.getSupportFragmentManager()
.beginTransaction() .beginTransaction()
.add(R.id.site_settings_container, siteSettingsFragment, FRAGMENT_TAG) .add(R.id.site_settings_container, siteSettingsFragment, FRAGMENT_TAG)
.commitNow(); .commitNow();
...@@ -185,7 +375,8 @@ public class SiteSettingsFragmentImpl extends FragmentHostingRemoteFragmentImpl ...@@ -185,7 +375,8 @@ public class SiteSettingsFragmentImpl extends FragmentHostingRemoteFragmentImpl
public void onViewAttachedToWindow(View view) { public void onViewAttachedToWindow(View view) {
// Add the shadow scroll listener here once the View is attached to the Window. // Add the shadow scroll listener here once the View is attached to the Window.
SiteSettingsPreferenceFragment preferenceFragment = SiteSettingsPreferenceFragment preferenceFragment =
(SiteSettingsPreferenceFragment) getSupportFragmentManager() (SiteSettingsPreferenceFragment) mFragmentController
.getSupportFragmentManager()
.findFragmentByTag(FRAGMENT_TAG); .findFragmentByTag(FRAGMENT_TAG);
ViewGroup listView = preferenceFragment.getListView(); ViewGroup listView = preferenceFragment.getListView();
listView.getViewTreeObserver().addOnScrollChangedListener( listView.getViewTreeObserver().addOnScrollChangedListener(
...@@ -199,6 +390,58 @@ public class SiteSettingsFragmentImpl extends FragmentHostingRemoteFragmentImpl ...@@ -199,6 +390,58 @@ public class SiteSettingsFragmentImpl extends FragmentHostingRemoteFragmentImpl
return root; return root;
} }
@Override
public void onDestroyView() {
StrictModeWorkaround.apply();
super.onDestroyView();
mFragmentController.dispatchDestroyView();
}
@Override
public void onDestroy() {
StrictModeWorkaround.apply();
super.onDestroy();
mFragmentController.dispatchDestroy();
}
@Override
public void onDetach() {
StrictModeWorkaround.apply();
super.onDetach();
mContext = null;
}
@Override
public void onStart() {
super.onStart();
if (!mStarted) {
mStarted = true;
mFragmentController.dispatchActivityCreated();
}
mFragmentController.noteStateNotSaved();
mFragmentController.execPendingActions();
mFragmentController.dispatchStart();
}
@Override
public void onStop() {
super.onStop();
mFragmentController.dispatchStop();
}
@Override
public void onResume() {
super.onResume();
mFragmentController.dispatchResume();
}
@Override
public void onPause() {
super.onPause();
mFragmentController.dispatchPause();
}
public ISiteSettingsFragment asISiteSettingsFragment() { public ISiteSettingsFragment asISiteSettingsFragment() {
return new ISiteSettingsFragment.Stub() { return new ISiteSettingsFragment.Stub() {
@Override @Override
...@@ -209,6 +452,14 @@ public class SiteSettingsFragmentImpl extends FragmentHostingRemoteFragmentImpl ...@@ -209,6 +452,14 @@ public class SiteSettingsFragmentImpl extends FragmentHostingRemoteFragmentImpl
}; };
} }
private Context getWebLayerContext() {
return mContext;
}
private Context getEmbedderContext() {
return mEmbedderContext;
}
private ProfileImpl getProfile() { private ProfileImpl getProfile() {
return mProfile; return mProfile;
} }
......
...@@ -4,20 +4,37 @@ ...@@ -4,20 +4,37 @@
package org.chromium.weblayer_private.media; package org.chromium.weblayer_private.media;
import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.InflateException;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewStub;
import android.view.Window;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentController;
import androidx.fragment.app.FragmentHostCallback;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import org.chromium.components.embedder_support.application.ClassLoaderContextWrapperFactory; import org.chromium.components.embedder_support.application.ClassLoaderContextWrapperFactory;
import org.chromium.weblayer_private.FragmentHostingRemoteFragmentImpl;
import org.chromium.weblayer_private.R; import org.chromium.weblayer_private.R;
import org.chromium.weblayer_private.RemoteFragmentImpl;
import org.chromium.weblayer_private.interfaces.IMediaRouteDialogFragment; import org.chromium.weblayer_private.interfaces.IMediaRouteDialogFragment;
import org.chromium.weblayer_private.interfaces.IRemoteFragment; import org.chromium.weblayer_private.interfaces.IRemoteFragment;
import org.chromium.weblayer_private.interfaces.IRemoteFragmentClient; import org.chromium.weblayer_private.interfaces.IRemoteFragmentClient;
import org.chromium.weblayer_private.interfaces.StrictModeWorkaround; import org.chromium.weblayer_private.interfaces.StrictModeWorkaround;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.lang.reflect.Constructor;
/** /**
* WebLayer's implementation of the client library's MediaRouteDialogFragment. * WebLayer's implementation of the client library's MediaRouteDialogFragment.
...@@ -25,21 +42,160 @@ import java.lang.ref.WeakReference; ...@@ -25,21 +42,160 @@ import java.lang.ref.WeakReference;
* This class is the impl-side representation of a client fragment which is added to the browser * This class is the impl-side representation of a client fragment which is added to the browser
* fragment, and is parent to MediaRouter-related {@link DialogFragment} instances. This class will * fragment, and is parent to MediaRouter-related {@link DialogFragment} instances. This class will
* automatically clean up the client-side fragment when the child fragment is detached. * automatically clean up the client-side fragment when the child fragment is detached.
*
* This class is modeled after {@link SiteSettingsFragmentImpl}, see it for details.
*/ */
public class MediaRouteDialogFragmentImpl extends FragmentHostingRemoteFragmentImpl { public class MediaRouteDialogFragmentImpl extends RemoteFragmentImpl {
// The WebLayer-wrapped context object. This context gets assets and resources from WebLayer,
// not from the embedder. Use this for the most part, especially to resolve WebLayer-specific
// resource IDs.
private Context mContext;
private boolean mStarted;
private FragmentController mFragmentController;
// The instance for the currently active dialog, if any. This is a WeakReference to get around // The instance for the currently active dialog, if any. This is a WeakReference to get around
// StaticFieldLeak warnings. // StaticFieldLeak warnings.
private static WeakReference<MediaRouteDialogFragmentImpl> sInstanceForTest; private static WeakReference<MediaRouteDialogFragmentImpl> sInstanceForTest;
private static class MediaRouteDialogContext /**
extends FragmentHostingRemoteFragmentImpl.RemoteFragmentContext { * A fake FragmentActivity needed to make the Fragment system happy.
public MediaRouteDialogContext(Context embedderContext) { *
super(ClassLoaderContextWrapperFactory.get(embedderContext)); * See {@link SiteSettingsFragmentImpl#PassthroughFragmentActivity}.
* TODO(crbug.com/1123216): remove this class.
*/
private static class PassthroughFragmentActivity extends FragmentActivity {
private static final Class<?>[] VIEW_CONSTRUCTOR_ARGS =
new Class[] {Context.class, AttributeSet.class};
private final MediaRouteDialogFragmentImpl mFragmentImpl;
int getThemeResource(int attr) {
TypedValue value = new TypedValue();
return getTheme().resolveAttribute(attr, value, true) ? value.resourceId : 0;
}
private PassthroughFragmentActivity(MediaRouteDialogFragmentImpl fragmentImpl) {
mFragmentImpl = fragmentImpl;
attachBaseContext(mFragmentImpl.getWebLayerContext());
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) {
getLayoutInflater().setFactory2(this);
}
AppCompatDelegate.create(this, null);
// TODO(estade): this is necessary because MediaRouter dialogs crash if the theme has an // TODO(estade): this is necessary because MediaRouter dialogs crash if the theme has an
// action bar. It's unclear why this is necessary when it's not in Chrome, and why // action bar. It's unclear why this is necessary when it's not in Chrome, and why
// ContextThemeWrapper doesn't work. // ContextThemeWrapper doesn't work.
getTheme().applyStyle(R.style.Theme_BrowserUI, true); getTheme().applyStyle(R.style.Theme_BrowserUI, true);
} }
@Override
public Object getSystemService(String name) {
if (Context.LAYOUT_INFLATER_SERVICE.equals(name)) {
return getLayoutInflater();
}
return getEmbedderActivity().getSystemService(name);
}
@Override
public LayoutInflater getLayoutInflater() {
return (LayoutInflater) getBaseContext().getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
}
@Override
public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
if (name.indexOf('.') == -1) {
return null;
}
Class<? extends View> clazz = null;
try {
clazz = context.getClassLoader().loadClass(name).asSubclass(View.class);
LayoutInflater inflater = getLayoutInflater();
if (inflater.getFilter() != null && !inflater.getFilter().onLoadClass(clazz)) {
throw new InflateException(attrs.getPositionDescription()
+ ": Class not allowed to be inflated " + name);
}
Constructor<? extends View> constructor =
clazz.getConstructor(VIEW_CONSTRUCTOR_ARGS);
constructor.setAccessible(true);
View view = constructor.newInstance(new Object[] {context, attrs});
if (view instanceof ViewStub) {
// Use the same Context when inflating ViewStub later.
ViewStub viewStub = (ViewStub) view;
viewStub.setLayoutInflater(inflater.cloneInContext(context));
}
return view;
} catch (Exception e) {
InflateException ie = new InflateException(attrs.getPositionDescription()
+ ": Error inflating class "
+ (clazz == null ? "<unknown>" : clazz.getName()));
ie.initCause(e);
throw ie;
}
}
@Override
public Window getWindow() {
return getEmbedderActivity().getWindow();
}
@Override
public Context getApplicationContext() {
return getEmbedderActivity().getApplicationContext();
}
@Override
public void startActivity(Intent intent) {
getEmbedderActivity().startActivity(intent);
}
@Override
public void setTitle(int titleId) {
getEmbedderActivity().setTitle(mFragmentImpl.getWebLayerContext().getString(titleId));
}
@Override
public void setTitle(CharSequence title) {
getEmbedderActivity().setTitle(title);
}
private Activity getEmbedderActivity() {
return mFragmentImpl.getActivity();
}
}
private static class MediaRouteDialogFragmentHostCallback
extends FragmentHostCallback<Context> {
private final MediaRouteDialogFragmentImpl mFragmentImpl;
private MediaRouteDialogFragmentHostCallback(MediaRouteDialogFragmentImpl fragmentImpl) {
super(new PassthroughFragmentActivity(fragmentImpl), new Handler(), 0);
mFragmentImpl = fragmentImpl;
}
@Override
public Context onGetHost() {
return mFragmentImpl.getWebLayerContext();
}
@Override
public LayoutInflater onGetLayoutInflater() {
Context context = mFragmentImpl.getWebLayerContext();
return ((LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE))
.cloneInContext(context);
}
@Override
public boolean onHasView() {
return mFragmentImpl.getView() != null;
}
@Override
public View onFindViewById(int id) {
return onHasView() ? mFragmentImpl.getView().findViewById(id) : null;
}
} }
public MediaRouteDialogFragmentImpl(IRemoteFragmentClient remoteFragmentClient) { public MediaRouteDialogFragmentImpl(IRemoteFragmentClient remoteFragmentClient) {
...@@ -52,8 +208,12 @@ public class MediaRouteDialogFragmentImpl extends FragmentHostingRemoteFragmentI ...@@ -52,8 +208,12 @@ public class MediaRouteDialogFragmentImpl extends FragmentHostingRemoteFragmentI
StrictModeWorkaround.apply(); StrictModeWorkaround.apply();
super.onAttach(context); super.onAttach(context);
mContext = ClassLoaderContextWrapperFactory.get(context);
mFragmentController =
FragmentController.createController(new MediaRouteDialogFragmentHostCallback(this));
// Remove the host fragment as soon as the media router dialog fragment is detached. // Remove the host fragment as soon as the media router dialog fragment is detached.
getSupportFragmentManager().registerFragmentLifecycleCallbacks( mFragmentController.getSupportFragmentManager().registerFragmentLifecycleCallbacks(
new FragmentManager.FragmentLifecycleCallbacks() { new FragmentManager.FragmentLifecycleCallbacks() {
@Override @Override
public void onFragmentDetached(FragmentManager fm, Fragment f) { public void onFragmentDetached(FragmentManager fm, Fragment f) {
...@@ -64,9 +224,66 @@ public class MediaRouteDialogFragmentImpl extends FragmentHostingRemoteFragmentI ...@@ -64,9 +224,66 @@ public class MediaRouteDialogFragmentImpl extends FragmentHostingRemoteFragmentI
} }
@Override @Override
protected FragmentHostingRemoteFragmentImpl.RemoteFragmentContext createRemoteFragmentContext( public void onCreate(Bundle savedInstanceState) {
Context embedderContext) { StrictModeWorkaround.apply();
return new MediaRouteDialogContext(embedderContext); mFragmentController.attachHost(null);
super.onCreate(savedInstanceState);
mFragmentController.dispatchCreate();
}
@Override
public void onDestroyView() {
StrictModeWorkaround.apply();
super.onDestroyView();
mFragmentController.dispatchDestroyView();
}
@Override
public void onDestroy() {
StrictModeWorkaround.apply();
super.onDestroy();
mFragmentController.dispatchDestroy();
sInstanceForTest = null;
}
@Override
public void onDetach() {
StrictModeWorkaround.apply();
super.onDetach();
mContext = null;
}
@Override
public void onStart() {
super.onStart();
if (!mStarted) {
mStarted = true;
mFragmentController.dispatchActivityCreated();
}
mFragmentController.noteStateNotSaved();
mFragmentController.execPendingActions();
mFragmentController.dispatchStart();
}
@Override
public void onStop() {
super.onStop();
mFragmentController.dispatchStop();
}
@Override
public void onResume() {
super.onResume();
mFragmentController.dispatchResume();
}
@Override
public void onPause() {
super.onPause();
mFragmentController.dispatchPause();
} }
public IMediaRouteDialogFragment asIMediaRouteDialogFragment() { public IMediaRouteDialogFragment asIMediaRouteDialogFragment() {
...@@ -83,6 +300,14 @@ public class MediaRouteDialogFragmentImpl extends FragmentHostingRemoteFragmentI ...@@ -83,6 +300,14 @@ public class MediaRouteDialogFragmentImpl extends FragmentHostingRemoteFragmentI
return (MediaRouteDialogFragmentImpl) remoteFragment; return (MediaRouteDialogFragmentImpl) remoteFragment;
} }
public FragmentManager getSupportFragmentManager() {
return mFragmentController.getSupportFragmentManager();
}
private Context getWebLayerContext() {
return mContext;
}
public static MediaRouteDialogFragmentImpl getInstanceForTest() { public static MediaRouteDialogFragmentImpl getInstanceForTest() {
return sInstanceForTest.get(); return sInstanceForTest.get();
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment