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 android.content.Context;
  import android.graphics.Rect;
  import android.support.animation.FloatValueHolder;
  import android.support.animation.SpringAnimation;
  import android.support.animation.SpringForce;
  import android.support.annotation.NonNull;
  import android.support.annotation.Nullable;
  import android.text.Selection;
  import android.text.Spannable;
  import android.text.SpannableString;
  import android.text.SpannableStringBuilder;
  import android.text.TextUtils;
  import android.text.method.TextKeyListener;
  import android.util.AttributeSet;
  import android.view.KeyEvent;
  import android.view.View;
  import android.widget.FrameLayout;
  import com.android.launcher3.DeviceProfile;
  import com.android.launcher3.ExtendedEditText;
  import com.android.launcher3.Launcher;
  import com.android.launcher3.R;
  import com.android.launcher3.allapps.AllAppsGridAdapter;
  import com.android.launcher3.allapps.AllAppsRecyclerView;
  import com.android.launcher3.allapps.AlphabeticalAppsList;
  import com.android.launcher3.allapps.SearchUiManager;
  import com.android.launcher3.config.FeatureFlags;
  import com.android.launcher3.discovery.AppDiscoveryItem;
  import com.android.launcher3.discovery.AppDiscoveryUpdateState;
  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 FrameLayout
          implements SearchUiManager, AllAppsSearchBarController.Callbacks {
  
      private final Launcher mLauncher;
      private final int mMinHeight;
      private final int mSearchBoxHeight;
      private final AllAppsSearchBarController mSearchBarController;
      private final SpannableStringBuilder mSearchQueryBuilder;
  
      private ExtendedEditText mSearchInput;
      private AlphabeticalAppsList mApps;
      private AllAppsRecyclerView mAppsRecyclerView;
      private AllAppsGridAdapter mAdapter;
      private View mDivider;
      private HeaderElevationController mElevationController;
  
      private SpringAnimation mSpring;
  
      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);
          mMinHeight = getResources().getDimensionPixelSize(R.dimen.all_apps_search_bar_height);
          mSearchBoxHeight = getResources()
                  .getDimensionPixelSize(R.dimen.all_apps_search_bar_field_height);
          mSearchBarController = new AllAppsSearchBarController();
  
          mSearchQueryBuilder = new SpannableStringBuilder();
          Selection.setSelection(mSearchQueryBuilder, 0);
  
          // Note: This spring does nothing.
          mSpring = new SpringAnimation(new FloatValueHolder()).setSpring(new SpringForce(0));
      }
  
      @Override
      protected void onFinishInflate() {
          super.onFinishInflate();
          mSearchInput = findViewById(R.id.search_box_input);
          mDivider = findViewById(R.id.search_divider);
          mElevationController = new HeaderElevationController(mDivider);
  
          // Update the hint to contain the icon.
          // Prefix the original hint with two spaces. The first space gets replaced by the icon
          // using span. The second space is used for a singe space character between the hint
          // and the icon.
          SpannableString spanned = new SpannableString("  " + mSearchInput.getHint());
          spanned.setSpan(new TintedDrawableSpan(getContext(), R.drawable.ic_allapps_search),
                  0, 1, Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
          mSearchInput.setHint(spanned);
  
          DeviceProfile dp = mLauncher.getDeviceProfile();
          if (!dp.isVerticalBarLayout()) {
              LayoutParams lp = (LayoutParams) mDivider.getLayoutParams();
              lp.leftMargin = lp.rightMargin = dp.edgeMarginPx;
          }
      }
  
      @Override
      protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
          if (!mLauncher.getDeviceProfile().isVerticalBarLayout()) {
              getLayoutParams().height = mLauncher.getDragLayer().getInsets().top + mMinHeight;
          }
          super.onMeasure(widthMeasureSpec, heightMeasureSpec);
      }
  
  
      @Override
      public void initialize(
              AlphabeticalAppsList appsList, AllAppsRecyclerView recyclerView) {
          mApps = appsList;
          mAppsRecyclerView = recyclerView;
          mAppsRecyclerView.addOnScrollListener(mElevationController);
          mAdapter = (AllAppsGridAdapter) mAppsRecyclerView.getAdapter();
          mSearchBarController.initialize(
                  new DefaultAppSearchAlgorithm(getContext(), appsList.getApps()), mSearchInput, mLauncher, this);
      }
  
      @Override
      public @NonNull SpringAnimation getSpringForFling() {
          return mSpring;
      }
  
      @Override
      public void refreshSearchResult() {
          mSearchBarController.refreshSearchResult();
      }
  
      @Override
      public void reset() {
          mElevationController.reset();
          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 startAppsSearch() {
          if (mApps != null) {
              mSearchBarController.focusSearchField();
          }
      }
  
      @Override
      public void onSearchResult(String query, ArrayList<ComponentKey> apps) {
          if (apps != null) {
              mApps.setOrderedFilter(apps);
              notifyResultChanged();
              mAdapter.setLastSearchQuery(query);
          }
      }
  
      @Override
      public void clearSearchResult() {
          if (mApps.setOrderedFilter(null)) {
              notifyResultChanged();
          }
  
          // Clear the search query
          mSearchQueryBuilder.clear();
          mSearchQueryBuilder.clearSpans();
          Selection.setSelection(mSearchQueryBuilder, 0);
      }
  
      @Override
      public void onAppDiscoverySearchUpdate(
              @Nullable AppDiscoveryItem app, @NonNull AppDiscoveryUpdateState state) {
          if (!mLauncher.isDestroyed()) {
              mApps.onAppDiscoverySearchUpdate(app, state);
              notifyResultChanged();
          }
      }
  
      private void notifyResultChanged() {
          mElevationController.reset();
          mAppsRecyclerView.onSearchResultsChanged();
      }
  
      @Override
      public void addOnScrollRangeChangeListener(final OnScrollRangeChangeListener listener) {
          mLauncher.getHotseat().addOnLayoutChangeListener(new OnLayoutChangeListener() {
              @Override
              public void onLayoutChange(View v, int left, int top, int right, int bottom,
                      int oldLeft, int oldTop, int oldRight, int oldBottom) {
                  DeviceProfile dp = mLauncher.getDeviceProfile();
                  if (!dp.isVerticalBarLayout()) {
                      Rect insets = mLauncher.getDragLayer().getInsets();
                      int hotseatBottom = bottom - dp.hotseatBarBottomPaddingPx - insets.bottom;
                      int searchTopMargin = insets.top + (mMinHeight - mSearchBoxHeight)
                              + ((MarginLayoutParams) getLayoutParams()).bottomMargin;
                      listener.onScrollRangeChanged(hotseatBottom - searchTopMargin);
                  } else {
                      listener.onScrollRangeChanged(bottom);
                  }
              }
          });
      }
  }
  