/* * Copyright (C) 2013 The CyanogenMod 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; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.content.Context; import android.graphics.Color; import android.graphics.PointF; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.Message; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.LinearSmoothScroller; import android.support.v7.widget.RecyclerView; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; import android.widget.SeekBar; import android.widget.TextView; import java.lang.IllegalArgumentException; import java.util.ArrayList; /** * AppDrawerScrubber *
* This is the scrubber at the bottom of the app drawer layout for navigating the application * list ** * @see {@link android.widget.LinearLayout} */ public class AppDrawerScrubber extends LinearLayout { private AppDrawerListAdapter mAdapter; private RecyclerView mListView; private TextView mScrubberIndicator; private SeekBar mSeekBar; private AutoExpandTextView mScrubberText; private SectionContainer mSectionContainer; private LinearLayoutManager mLayoutManager; private ScrubberAnimationState mScrubberAnimationState; private Drawable mTransparentDrawable; private AppDrawerSmoothScroller mLinearSmoothScroller; private static final int MSG_SET_TARGET = 1000; private static final int MSG_SMOOTH_SCROLL = MSG_SET_TARGET + 1; private static final int MSG_ANIMATE_PICK = MSG_SMOOTH_SCROLL + 1; /** * UiHandler *
* Using a handler for sending signals to perform certain actions. The reason for * using this is to be able to remove and replace a signal if signals are being * sent too fast (e.g. user scrubbing like crazy). This allows the touch loop to * complete then later run the animations in their own loops. **/ private class UiHandler extends Handler { @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_SET_TARGET: int adapterIndex = msg.arg1; performSetTarget(adapterIndex); break; case MSG_ANIMATE_PICK: int index = msg.arg1; int width = msg.arg2; int lastIndex = (Integer)msg.obj; performAnimatePickMessage(index, width, lastIndex); break; case MSG_SMOOTH_SCROLL: int itemDiff = msg.arg1; int itemIndex = msg.arg2; performSmoothScroll(itemDiff, itemIndex); break; default: super.handleMessage(msg); } } /** * Overidden to remove identical calls if they are called subsequently fast enough. * * This is the final point that is public in the call chain. Other calls to sendMessageXXX * will eventually call this function which calls "enqueueMessage" which is private. * * @param msg {@link android.os.Message} * @param uptimeMillis {@link java.lang.Long} * * @throws IllegalArgumentException {@link java.lang.IllegalArgumentException} */ @Override public boolean sendMessageAtTime(Message msg, long uptimeMillis) throws IllegalArgumentException { if (msg == null) { throw new IllegalArgumentException("'msg' cannot be null!"); } if (hasMessages(msg.what)) { removeMessages(msg.what); } return super.sendMessageAtTime(msg, uptimeMillis); } } private Handler mUiHandler = new UiHandler(); private void sendSetTargetMessage(int adapterIndex) { Message msg = mUiHandler.obtainMessage(MSG_SET_TARGET); msg.what = MSG_SET_TARGET; msg.arg1 = adapterIndex; mUiHandler.sendMessage(msg); } private void performSetTarget(int adapterIndex) { if (mAdapter != null) { mAdapter.setSectionTarget(adapterIndex); } } private void sendAnimatePickMessage(int index, int width, int lastIndex) { Message msg = mUiHandler.obtainMessage(MSG_ANIMATE_PICK); msg.what = MSG_ANIMATE_PICK; msg.arg1 = index; msg.arg2 = width; msg.obj = lastIndex; mUiHandler.sendMessage(msg); } private void performAnimatePickMessage(int index, int width, int lastIndex) { if (mScrubberIndicator != null) { // get the index based on the direction the user is scrolling int directionalIndex = mSectionContainer.getDirectionalIndex(lastIndex, index); String sectionText = mSectionContainer.getHeader(directionalIndex); float translateX = (index * width) / (float) mSectionContainer.size(); // if we are showing letters, grab the position based on the text view if (mSectionContainer.showLetters()) { translateX = mScrubberText.getPositionOfSection(index); } // center the x position translateX -= mScrubberIndicator.getMeasuredWidth() / 2; mScrubberIndicator.setTranslationX(translateX); mScrubberIndicator.setText(sectionText); } } private void sendSmoothScrollMessage(int itemDiff, int itemIndex) { Message msg = mUiHandler.obtainMessage(MSG_SMOOTH_SCROLL); msg.what = MSG_SMOOTH_SCROLL; msg.arg1 = itemDiff; msg.arg2 = itemIndex; mUiHandler.sendMessage(msg); } private void performSmoothScroll(int itemDiff, int itemIndex) { if (mLinearSmoothScroller == null) { mLinearSmoothScroller = new AppDrawerSmoothScroller(mContext); } mLinearSmoothScroller.setItemDiff(itemDiff); mLinearSmoothScroller.setTargetPosition(itemIndex); mLayoutManager.startSmoothScroll(mLinearSmoothScroller); } /** * Constructor * * @param context {@link android.content.Context} * @param attrs {@link android.util.AttributeSet} */ public AppDrawerScrubber(Context context, AttributeSet attrs) { super(context, attrs); init(context); } /** * Constructor * * @param context {@link android.content.Context} */ public AppDrawerScrubber(Context context) { super(context); init(context); } /** * AppDrawerSmoothScroller *
* This is a smooth scroller with the ability to set an item diff ** * @see {@link android.support.v7.widget.LinearSmoothScroller} */ private class AppDrawerSmoothScroller extends LinearSmoothScroller { // Members private int mItemDiff = 0; public AppDrawerSmoothScroller(Context context) { super(context); } @Override protected int getVerticalSnapPreference() { // position the item against the end of the list view return SNAP_TO_END; } @Override public PointF computeScrollVectorForPosition(int targetPosition) { return mLayoutManager.computeScrollVectorForPosition(targetPosition); } @Override public int calculateDyToMakeVisible(View view, int snapPreference) { int dy = super.calculateDyToMakeVisible(view, snapPreference); return dy - mItemDiff; } /** * Set the item difference * * @param itemDiff */ public void setItemDiff(int itemDiff) { mItemDiff = itemDiff; } /** * Get the item difference * * @return {@link java.lang.Integer} */ public int getItemDiff() { return mItemDiff; } } /** * Simple container class that tries to abstract out the knowledge of complex sections vs * simple string sections */ private static class SectionContainer { private ArrayList