content: /*
   * Copyright (C) 2014 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.layoutlib.bridge.bars;
  
  import com.android.ide.common.rendering.api.RenderResources;
  import com.android.ide.common.rendering.api.ResourceValue;
  import com.android.ide.common.rendering.api.SessionParams;
  import com.android.internal.R;
  import com.android.internal.view.menu.MenuBuilder;
  import com.android.internal.view.menu.MenuItemImpl;
  import com.android.layoutlib.bridge.android.BridgeContext;
  import com.android.layoutlib.bridge.impl.ResourceHelper;
  
  import android.annotation.NonNull;
  import android.annotation.Nullable;
  import android.content.Context;
  import android.content.res.TypedArray;
  import android.util.DisplayMetrics;
  import android.util.TypedValue;
  import android.view.InflateException;
  import android.view.View;
  import android.view.View.MeasureSpec;
  import android.view.ViewGroup;
  import android.view.ViewGroup.LayoutParams;
  import android.widget.ActionMenuPresenter;
  import android.widget.FrameLayout;
  import android.widget.ListAdapter;
  import android.widget.ListView;
  import android.widget.RelativeLayout;
  
  import java.util.List;
  
  /**
   * Creates the ActionBar as done by the framework.
   */
  public class FrameworkActionBar extends BridgeActionBar {
      private static final String LAYOUT_ATTR_NAME = "windowActionBarFullscreenDecorLayout";
  
      // The Action Bar
      @NonNull private FrameworkActionBarWrapper mActionBar;
  
      // A fake parent for measuring views.
      @Nullable private ViewGroup mMeasureParent;
  
      /**
       * Inflate the action bar and attach it to {@code parentView}
       */
      public FrameworkActionBar(@NonNull BridgeContext context, @NonNull SessionParams params) {
          super(context, params);
  
          View decorContent = getDecorContent();
  
          mActionBar = FrameworkActionBarWrapper.getActionBarWrapper(context, getCallBack(),
                  decorContent);
  
          FrameLayout contentRoot = (FrameLayout) decorContent.findViewById(android.R.id.content);
  
          // If something went wrong and we were not able to initialize the content root,
          // just add a frame layout inside this and return.
          if (contentRoot == null) {
              contentRoot = new FrameLayout(context);
              setMatchParent(contentRoot);
              if (mEnclosingLayout != null) {
                  mEnclosingLayout.addView(contentRoot);
              }
              setContentRoot(contentRoot);
          } else {
              setContentRoot(contentRoot);
              setupActionBar();
              mActionBar.inflateMenus();
          }
      }
  
      @Override
      protected ResourceValue getLayoutResource(BridgeContext context) {
          ResourceValue layoutName =
                  context.getRenderResources().findItemInTheme(
                          BridgeContext.createFrameworkAttrReference(LAYOUT_ATTR_NAME));
          if (layoutName != null) {
              // We may need to resolve the reference obtained.
              layoutName = context.getRenderResources().dereference(layoutName);
          }
          if (layoutName == null) {
               throw new InflateException("Unable to find action bar layout (" + LAYOUT_ATTR_NAME
                      + ") in the current theme.");
          }
          return layoutName;
      }
  
      @Override
      protected void setupActionBar() {
          super.setupActionBar();
          mActionBar.setupActionBar();
      }
  
      @Override
      protected void setHomeAsUp(boolean homeAsUp) {
          mActionBar.setHomeAsUp(homeAsUp);
      }
  
      @Override
      protected void setTitle(CharSequence title) {
          mActionBar.setTitle(title);
      }
  
      @Override
      protected void setSubtitle(CharSequence subtitle) {
          mActionBar.setSubTitle(subtitle);
      }
  
      @Override
      protected void setIcon(ResourceValue icon) {
          mActionBar.setIcon(icon);
      }
  
      /**
       * Creates a Popup and adds it to the content frame. It also adds another {@link FrameLayout} to
       * the content frame which shall serve as the new content root.
       */
      @Override
      public void createMenuPopup() {
          if (!isOverflowPopupNeeded()) {
              return;
          }
  
          DisplayMetrics metrics = mBridgeContext.getMetrics();
          MenuBuilder menu = mActionBar.getMenuBuilder();
          OverflowMenuAdapter adapter = new OverflowMenuAdapter(menu, mActionBar.getPopupContext());
  
          ListView listView = new ListView(mActionBar.getPopupContext(), null,
                  R.attr.dropDownListViewStyle);
          RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(
                  measureContentWidth(adapter), LayoutParams.WRAP_CONTENT);
          layoutParams.addRule(RelativeLayout.ALIGN_PARENT_END);
          if (mActionBar.isSplit()) {
              layoutParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
              layoutParams.bottomMargin = getActionBarHeight() + mActionBar.getMenuPopupMargin();
          } else {
              layoutParams.addRule(RelativeLayout.ALIGN_PARENT_TOP);
              layoutParams.topMargin = getActionBarHeight() + mActionBar.getMenuPopupMargin();
          }
          layoutParams.setMarginEnd(getPixelValue("5dp", metrics));
          listView.setLayoutParams(layoutParams);
          listView.setAdapter(adapter);
          final TypedArray a = mActionBar.getPopupContext().obtainStyledAttributes(null,
                  R.styleable.PopupWindow, R.attr.popupMenuStyle, 0);
          listView.setBackground(a.getDrawable(R.styleable.PopupWindow_popupBackground));
          listView.setDivider(a.getDrawable(R.attr.actionBarDivider));
          a.recycle();
          listView.setElevation(mActionBar.getMenuPopupElevation());
          assert mEnclosingLayout != null : "Unable to find view to attach ActionMenuPopup.";
          mEnclosingLayout.addView(listView);
      }
  
      private boolean isOverflowPopupNeeded() {
          boolean needed = mActionBar.isOverflowPopupNeeded();
          if (!needed) {
              return false;
          }
          // Copied from android.widget.ActionMenuPresenter.updateMenuView()
          List<MenuItemImpl> menus = mActionBar.getMenuBuilder().getNonActionItems();
          ActionMenuPresenter presenter = mActionBar.getActionMenuPresenter();
          if (presenter == null) {
              assert false : "Failed to create a Presenter for Action Bar Menus.";
              return false;
          }
          if (presenter.isOverflowReserved() &&
                  menus != null) {
              final int count = menus.size();
              if (count == 1) {
                  needed = !menus.get(0).isActionViewExpanded();
              } else {
                  needed = count > 0;
              }
          }
          return needed;
      }
  
      // Copied from com.android.internal.view.menu.MenuPopHelper.measureContentWidth()
      private int measureContentWidth(@NonNull ListAdapter adapter) {
          // Menus don't tend to be long, so this is more sane than it looks.
          int maxWidth = 0;
          View itemView = null;
          int itemType = 0;
  
          Context context = mActionBar.getPopupContext();
          final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
          final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
          final int count = adapter.getCount();
          for (int i = 0; i < count; i++) {
              final int positionType = adapter.getItemViewType(i);
              if (positionType != itemType) {
                  itemType = positionType;
                  itemView = null;
              }
  
              if (mMeasureParent == null) {
                  mMeasureParent = new FrameLayout(context);
              }
  
              itemView = adapter.getView(i, itemView, mMeasureParent);
              itemView.measure(widthMeasureSpec, heightMeasureSpec);
  
              final int itemWidth = itemView.getMeasuredWidth();
              int popupMaxWidth = Math.max(mBridgeContext.getMetrics().widthPixels / 2,
                      context.getResources().getDimensionPixelSize(R.dimen.config_prefDialogWidth));
              if (itemWidth >= popupMaxWidth) {
                  return popupMaxWidth;
              } else if (itemWidth > maxWidth) {
                  maxWidth = itemWidth;
              }
          }
  
          return maxWidth;
      }
  
      static int getPixelValue(@NonNull String value, @NonNull DisplayMetrics metrics) {
          TypedValue typedValue = ResourceHelper.getValue(null, value, false /*requireUnit*/);
          return (int) typedValue.getDimension(metrics);
      }
  
      // TODO: This is duplicated from RenderSessionImpl.
      private int getActionBarHeight() {
          RenderResources resources = mBridgeContext.getRenderResources();
          DisplayMetrics metrics = mBridgeContext.getMetrics();
          ResourceValue value = resources.findItemInTheme(
                  BridgeContext.createFrameworkAttrReference("actionBarSize"));
  
          // resolve it
          value = resources.resolveResValue(value);
  
          if (value != null) {
              // get the numerical value, if available
              TypedValue typedValue = ResourceHelper.getValue("actionBarSize", value.getValue(),
                      true);
              if (typedValue != null) {
                  // compute the pixel value based on the display metrics
                  return (int) typedValue.getDimension(metrics);
  
              }
          }
          return 0;
      }
  }
  