content: /*
   * Copyright (C) 2017 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.launcher3.allapps.search;
  
  import static android.view.View.MeasureSpec.EXACTLY;
  import static android.view.View.MeasureSpec.getSize;
  import static android.view.View.MeasureSpec.makeMeasureSpec;
  
  import static com.android.launcher3.LauncherState.ALL_APPS_HEADER;
  import static com.android.launcher3.Utilities.prefixTextWithIcon;
  import static com.android.launcher3.icons.IconNormalizer.ICON_VISIBLE_AREA_FACTOR;
  
  import android.content.Context;
  import android.graphics.Rect;
  import android.text.Selection;
  import android.text.Spannable;
  import android.text.SpannableString;
  import android.text.SpannableStringBuilder;
  import android.text.method.TextKeyListener;
  import android.util.AttributeSet;
  import android.view.KeyEvent;
  import android.view.View;
  import android.view.ViewGroup.MarginLayoutParams;
  import android.view.animation.Interpolator;
  
  import com.android.launcher3.DeviceProfile;
  import com.android.launcher3.ExtendedEditText;
  import com.android.launcher3.Insettable;
  import com.android.launcher3.Launcher;
  import com.android.launcher3.R;
  import com.android.launcher3.Utilities;
  import com.android.launcher3.allapps.AllAppsContainerView;
  import com.android.launcher3.allapps.AllAppsStore;
  import com.android.launcher3.allapps.AlphabeticalAppsList;
  import com.android.launcher3.allapps.SearchUiManager;
  import com.android.launcher3.anim.PropertySetter;
  import com.android.launcher3.graphics.TintedDrawableSpan;
  import com.android.launcher3.util.ComponentKey;
  
  import java.util.ArrayList;
  
  /**
   * Layout to contain the All-apps search UI.
   */
  public class AppsSearchContainerLayout extends ExtendedEditText
          implements SearchUiManager, AllAppsSearchBarController.Callbacks,
          AllAppsStore.OnUpdateListener, Insettable {
  
  
      private final Launcher mLauncher;
      private final AllAppsSearchBarController mSearchBarController;
      private final SpannableStringBuilder mSearchQueryBuilder;
  
      private AlphabeticalAppsList mApps;
      private AllAppsContainerView mAppsView;
  
      // This value was used to position the QSB. We store it here for translationY animations.
      private final float mFixedTranslationY;
      private final float mMarginTopAdjusting;
  
      public AppsSearchContainerLayout(Context context) {
          this(context, null);
      }
  
      public AppsSearchContainerLayout(Context context, AttributeSet attrs) {
          this(context, attrs, 0);
      }
  
      public AppsSearchContainerLayout(Context context, AttributeSet attrs, int defStyleAttr) {
          super(context, attrs, defStyleAttr);
  
          mLauncher = Launcher.getLauncher(context);
          mSearchBarController = new AllAppsSearchBarController();
  
          mSearchQueryBuilder = new SpannableStringBuilder();
          Selection.setSelection(mSearchQueryBuilder, 0);
  
          mFixedTranslationY = getTranslationY();
          mMarginTopAdjusting = mFixedTranslationY - getPaddingTop();
  
          setHint(prefixTextWithIcon(getContext(), R.drawable.ic_allapps_search, getHint()));
      }
  
      @Override
      protected void onAttachedToWindow() {
          super.onAttachedToWindow();
          mLauncher.getAppsView().getAppsStore().addUpdateListener(this);
      }
  
      @Override
      protected void onDetachedFromWindow() {
          super.onDetachedFromWindow();
          mLauncher.getAppsView().getAppsStore().removeUpdateListener(this);
      }
  
      @Override
      protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
          // Update the width to match the grid padding
          DeviceProfile dp = mLauncher.getDeviceProfile();
          int myRequestedWidth = getSize(widthMeasureSpec);
          int rowWidth = myRequestedWidth - mAppsView.getActiveRecyclerView().getPaddingLeft()
                  - mAppsView.getActiveRecyclerView().getPaddingRight();
  
          int cellWidth = DeviceProfile.calculateCellWidth(rowWidth, dp.inv.numHotseatIcons);
          int iconVisibleSize = Math.round(ICON_VISIBLE_AREA_FACTOR * dp.iconSizePx);
          int iconPadding = cellWidth - iconVisibleSize;
  
          int myWidth = rowWidth - iconPadding + getPaddingLeft() + getPaddingRight();
          super.onMeasure(makeMeasureSpec(myWidth, EXACTLY), heightMeasureSpec);
      }
  
      @Override
      protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
          super.onLayout(changed, left, top, right, bottom);
  
          // Shift the widget horizontally so that its centered in the parent (b/63428078)
          View parent = (View) getParent();
          int availableWidth = parent.getWidth() - parent.getPaddingLeft() - parent.getPaddingRight();
          int myWidth = right - left;
          int expectedLeft = parent.getPaddingLeft() + (availableWidth - myWidth) / 2;
          int shift = expectedLeft - left;
          setTranslationX(shift);
      }
  
      @Override
      public void initialize(AllAppsContainerView appsView) {
          mApps = appsView.getApps();
          mAppsView = appsView;
          mSearchBarController.initialize(
                  new DefaultAppSearchAlgorithm(mApps.getApps()), this, mLauncher, this);
      }
  
      @Override
      public void onAppsUpdated() {
          mSearchBarController.refreshSearchResult();
      }
  
      @Override
      public void resetSearch() {
          mSearchBarController.reset();
      }
  
      @Override
      public void preDispatchKeyEvent(KeyEvent event) {
          // Determine if the key event was actual text, if so, focus the search bar and then dispatch
          // the key normally so that it can process this key event
          if (!mSearchBarController.isSearchFieldFocused() &&
                  event.getAction() == KeyEvent.ACTION_DOWN) {
              final int unicodeChar = event.getUnicodeChar();
              final boolean isKeyNotWhitespace = unicodeChar > 0 &&
                      !Character.isWhitespace(unicodeChar) && !Character.isSpaceChar(unicodeChar);
              if (isKeyNotWhitespace) {
                  boolean gotKey = TextKeyListener.getInstance().onKeyDown(this, mSearchQueryBuilder,
                          event.getKeyCode(), event);
                  if (gotKey && mSearchQueryBuilder.length() > 0) {
                      mSearchBarController.focusSearchField();
                  }
              }
          }
      }
  
      @Override
      public void onSearchResult(String query, ArrayList<ComponentKey> apps) {
          if (apps != null) {
              mApps.setOrderedFilter(apps);
              notifyResultChanged();
              mAppsView.setLastSearchQuery(query);
          }
      }
  
      @Override
      public void clearSearchResult() {
          if (mApps.setOrderedFilter(null)) {
              notifyResultChanged();
          }
  
          // Clear the search query
          mSearchQueryBuilder.clear();
          mSearchQueryBuilder.clearSpans();
          Selection.setSelection(mSearchQueryBuilder, 0);
          mAppsView.onClearSearchResult();
      }
  
      private void notifyResultChanged() {
          mAppsView.onSearchResultsChanged();
      }
  
      @Override
      public void setInsets(Rect insets) {
          MarginLayoutParams mlp = (MarginLayoutParams) getLayoutParams();
          mlp.topMargin = Math.round(Math.max(-mFixedTranslationY, insets.top - mMarginTopAdjusting));
          requestLayout();
      }
  
      @Override
      public float getScrollRangeDelta(Rect insets) {
          if (mLauncher.getDeviceProfile().isVerticalBarLayout()) {
              return 0;
          } else {
              int topMargin = Math.round(Math.max(
                      -mFixedTranslationY, insets.top - mMarginTopAdjusting));
             return insets.bottom + topMargin + mFixedTranslationY;
          }
      }
  
      @Override
      public void setContentVisibility(int visibleElements, PropertySetter setter,
              Interpolator interpolator) {
          setter.setViewAlpha(this, (visibleElements & ALL_APPS_HEADER) != 0 ? 1 : 0, interpolator);
      }
  }
  