| /* |
| * Copyright (C) 2020 The Android Open Source Project |
| * |
| * 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.android.car.ui.core; |
| |
| import static com.android.car.ui.core.CarUi.MIN_TARGET_API; |
| import static com.android.car.ui.utils.CarUiUtils.getThemeBoolean; |
| import static com.android.car.ui.utils.CarUiUtils.requireViewByRefId; |
| |
| import android.annotation.TargetApi; |
| import android.app.Activity; |
| import android.view.View; |
| |
| import androidx.annotation.NonNull; |
| import androidx.annotation.Nullable; |
| import androidx.fragment.app.Fragment; |
| import androidx.fragment.app.FragmentActivity; |
| |
| import com.android.car.ui.R; |
| import com.android.car.ui.baselayout.Insets; |
| import com.android.car.ui.baselayout.InsetsChangedListener; |
| import com.android.car.ui.pluginsupport.PluginFactorySingleton; |
| import com.android.car.ui.toolbar.ToolbarController; |
| |
| import java.util.Map; |
| import java.util.WeakHashMap; |
| |
| /** |
| * BaseLayoutController accepts an {@link Activity} and sets up the base layout inside of it. |
| * It also exposes a {@link ToolbarController} to access the toolbar. This may be null if |
| * used with a base layout without a Toolbar. |
| */ |
| @TargetApi(MIN_TARGET_API) |
| public final class BaseLayoutController { |
| |
| private static final Map<Activity, BaseLayoutController> sBaseLayoutMap = new WeakHashMap<>(); |
| |
| private InsetsUpdater mInsetsUpdater; |
| |
| /** |
| * Gets a BaseLayoutController for the given {@link Activity}. Must have called |
| * {@link #build(Activity)} with the same activity earlier, otherwise will return null. |
| */ |
| @Nullable |
| /* package */ static BaseLayoutController getBaseLayoutController(@Nullable Activity activity) { |
| return sBaseLayoutMap.get(activity); |
| } |
| |
| @Nullable |
| private ToolbarController mToolbarController; |
| |
| private BaseLayoutController(Activity activity) { |
| installBaseLayout(activity); |
| } |
| |
| /** |
| * Create a new BaseLayoutController for the given {@link Activity}. |
| * |
| * <p>You can get a reference to it by calling {@link #getBaseLayoutController(Activity)}. |
| */ |
| /* package */ |
| static void build(Activity activity) { |
| if (getThemeBoolean(activity, R.attr.carUiBaseLayout)) { |
| sBaseLayoutMap.put(activity, new BaseLayoutController(activity)); |
| } |
| } |
| |
| /** |
| * Destroy the BaseLayoutController for the given {@link Activity}. |
| */ |
| /* package */ |
| static void destroy(Activity activity) { |
| sBaseLayoutMap.remove(activity); |
| } |
| |
| /** |
| * Gets the {@link ToolbarController} for activities created with carUiBaseLayout and |
| * carUiToolbar set to true. |
| */ |
| @Nullable |
| /* package */ ToolbarController getToolbarController() { |
| return mToolbarController; |
| } |
| |
| /* package */ Insets getInsets() { |
| return mInsetsUpdater.getInsets(); |
| } |
| |
| /* package */ void dispatchNewInsets(Insets insets) { |
| mInsetsUpdater.onCarUiInsetsChanged(insets); |
| } |
| |
| /* package */ void replaceInsetsChangedListenerWith(InsetsChangedListener listener) { |
| mInsetsUpdater.replaceInsetsChangedListenerWith(listener); |
| } |
| |
| /** |
| * Installs the base layout into an activity, moving its content view under the base layout. |
| * |
| * <p>This function must be called during the onCreate() of the {@link Activity}. |
| * |
| * @param activity The {@link Activity} to install a base layout in. |
| */ |
| private void installBaseLayout(Activity activity) { |
| boolean toolbarEnabled = getThemeBoolean(activity, R.attr.carUiToolbar); |
| |
| View contentView = |
| requireViewByRefId(activity.getWindow().getDecorView(), android.R.id.content); |
| |
| mInsetsUpdater = new InsetsUpdater(activity, contentView); |
| mToolbarController = PluginFactorySingleton.get(activity) |
| .installBaseLayoutAround( |
| contentView, |
| mInsetsUpdater, |
| toolbarEnabled, |
| true); |
| } |
| |
| /** |
| * InsetsUpdater waits for layout changes, and when there is one, calculates the appropriate |
| * insets into the content view. |
| * |
| * <p>It then calls {@link InsetsChangedListener#onCarUiInsetsChanged(Insets)} on the |
| * {@link Activity} and any {@link Fragment Fragments} the Activity might have. If |
| * none of the Activity/Fragments implement {@link InsetsChangedListener}, it will set |
| * padding on the content view equal to the insets. |
| */ |
| public static final class InsetsUpdater implements InsetsChangedListener { |
| private InsetsChangedListener mInsetsChangedListenerDelegate; |
| |
| @Nullable |
| private Activity mActivity; |
| private View mContentView; |
| |
| @NonNull |
| private Insets mInsets = new Insets(); |
| |
| /** |
| * Constructs an InsetsUpdater that calculates and dispatches insets to an {@link Activity}. |
| * |
| * @param activity The activity that is using base layouts. Used to dispatch insets to if |
| * it implements {@link InsetsChangedListener} |
| * @param contentView The android.R.id.content View |
| */ |
| InsetsUpdater( |
| @Nullable Activity activity, |
| @NonNull View contentView) { |
| mActivity = activity; |
| mContentView = contentView; |
| } |
| |
| @NonNull |
| Insets getInsets() { |
| return mInsets; |
| } |
| |
| public void replaceInsetsChangedListenerWith(InsetsChangedListener listener) { |
| mInsetsChangedListenerDelegate = listener; |
| } |
| |
| @Override |
| public void onCarUiInsetsChanged(@NonNull Insets insets) { |
| if (mInsets.equals(insets)) { |
| return; |
| } |
| |
| mInsets = insets; |
| |
| boolean handled = false; |
| if (mInsetsChangedListenerDelegate != null) { |
| mInsetsChangedListenerDelegate.onCarUiInsetsChanged(insets); |
| handled = true; |
| } else { |
| // If an explicit InsetsChangedListener is not provided, |
| // pass the insets to activities and fragments |
| if (mActivity instanceof InsetsChangedListener) { |
| ((InsetsChangedListener) mActivity).onCarUiInsetsChanged(insets); |
| handled = true; |
| } |
| |
| if (mActivity instanceof FragmentActivity) { |
| for (Fragment fragment : ((FragmentActivity) mActivity) |
| .getSupportFragmentManager().getFragments()) { |
| if (fragment instanceof InsetsChangedListener) { |
| ((InsetsChangedListener) fragment).onCarUiInsetsChanged(insets); |
| handled = true; |
| } |
| } |
| } |
| } |
| |
| if (!handled) { |
| mContentView.setPadding(insets.getLeft(), insets.getTop(), |
| insets.getRight(), insets.getBottom()); |
| } |
| } |
| } |
| } |