diff options
author | Sai Cheemalapati <saicheems@google.com> | 2014-06-04 18:02:24 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2014-06-04 18:02:25 +0000 |
commit | ee76114046b4bc0beda0fdd2ea2e08d6566719e3 (patch) | |
tree | 92d97800a19ce44d3c98157ad33a64dd3e3ecbb8 | |
parent | 157e688347f550a9978196b46839314b4080064b (diff) | |
parent | 414606561a0ae18d47a263eaa2b2d9622c317ae0 (diff) |
Merge "Modified floating action button."
-rw-r--r-- | res/values/dimens.xml | 2 | ||||
-rw-r--r-- | src/com/android/dialer/DialtactsActivity.java | 112 | ||||
-rw-r--r-- | src/com/android/dialer/widget/FloatingActionButtonController.java | 212 |
3 files changed, 245 insertions, 81 deletions
diff --git a/res/values/dimens.xml b/res/values/dimens.xml index b8c5588fe..45a48c2f5 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -38,7 +38,7 @@ <!-- Match call_button_height to Phone's dimens/in_call_end_button_height --> <dimen name="call_button_height">74dp</dimen> - <dimen name="floating_action_button_dialpad_margin_bottom">12dp</dimen> + <dimen name="floating_action_button_dialpad_margin_bottom_offset">4dp</dimen> <!-- Dimensions for speed dial tiles --> <dimen name="contact_tile_divider_width">1dp</dimen> diff --git a/src/com/android/dialer/DialtactsActivity.java b/src/com/android/dialer/DialtactsActivity.java index 4ea9651d5..922b38351 100644 --- a/src/com/android/dialer/DialtactsActivity.java +++ b/src/com/android/dialer/DialtactsActivity.java @@ -48,6 +48,7 @@ import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; +import android.view.ViewTreeObserver; import android.view.View.OnDragListener; import android.view.View.OnTouchListener; import android.view.animation.AccelerateInterpolator; @@ -88,6 +89,7 @@ import com.android.dialer.list.RemoveView; import com.android.dialer.list.SearchFragment; import com.android.dialer.list.SmartDialSearchFragment; import com.android.dialer.widget.ActionBarController; +import com.android.dialer.widget.FloatingActionButtonController; import com.android.dialer.widget.SearchEditTextLayout; import com.android.dialer.widget.SearchEditTextLayout.OnBackButtonClickedListener; import com.android.dialerbind.DatabaseHelperManager; @@ -180,18 +182,10 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O }; /** - * Set to true if the device is in landscape orientation. - */ - private boolean mIsLandscape; - - /** * Fragment containing the speed dial list, recents list, and all contacts list. */ private ListsFragment mListsFragment; - private View mFloatingActionButtonContainer; - private ImageButton mFloatingActionButton; - private boolean mInDialpadSearch; private boolean mInRegularSearch; private boolean mClearSearchOnPause; @@ -199,6 +193,11 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O private boolean mShowDialpadOnResume; /** + * Whether or not the device is in landscape orientation. + */ + private boolean mIsLandscape; + + /** * The position of the currently selected tab in the attached {@link ListsFragment}. */ private int mCurrentTabPosition = 0; @@ -235,10 +234,9 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O private DialerDatabaseHelper mDialerDatabaseHelper; private DragDropController mDragDropController; private ActionBarController mActionBarController; + private FloatingActionButtonController mFloatingActionButtonController; private int mActionBarHeight; - private int mFloatingActionButtonMarginBottom; - private int mFloatingActionButtonDialpadMarginBottom; private class OptionsPopupMenu extends PopupMenu { public OptionsPopupMenu(Context context, View anchor) { @@ -352,10 +350,6 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O final Resources resources = getResources(); mActionBarHeight = resources.getDimensionPixelSize(R.dimen.action_bar_height); - mFloatingActionButtonMarginBottom = resources.getDimensionPixelOffset( - R.dimen.floating_action_button_margin_bottom); - mFloatingActionButtonDialpadMarginBottom = resources.getDimensionPixelOffset( - R.dimen.floating_action_button_dialpad_margin_bottom); setContentView(R.layout.dialtacts_activity); getWindow().setBackgroundDrawable(null); @@ -383,6 +377,14 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O } }); + boolean mIsLandscape = getResources().getConfiguration().orientation + == Configuration.ORIENTATION_LANDSCAPE; + View floatingActionButtonContainer = findViewById(R.id.floating_action_button_container); + ImageButton floatingActionButton = (ImageButton) findViewById(R.id.floating_action_button); + floatingActionButton.setOnClickListener(this); + mFloatingActionButtonController = new FloatingActionButtonController(this, mIsLandscape, + floatingActionButtonContainer); + ImageButton optionsMenuButton = (ImageButton) mSearchEditTextLayout.findViewById( R.id.dialtacts_options_menu_button); optionsMenuButton.setOnClickListener(this); @@ -403,9 +405,8 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O mFirstLaunch = savedInstanceState.getBoolean(KEY_FIRST_LAUNCH); mShowDialpadOnResume = savedInstanceState.getBoolean(KEY_IS_DIALPAD_SHOWN); mActionBarController.restoreInstanceState(savedInstanceState); + mFloatingActionButtonController.restoreInstanceState(savedInstanceState); } - mIsLandscape = getResources().getConfiguration().orientation == - Configuration.ORIENTATION_LANDSCAPE; mSlideIn = AnimationUtils.loadAnimation(this, mIsLandscape ? R.anim.slide_in_right : R.anim.slide_in); @@ -417,15 +418,18 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O parentLayout = (RelativeLayout) findViewById(R.id.dialtacts_mainlayout); parentLayout.getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING); parentLayout.setOnDragListener(new LayoutOnDragListener()); + parentLayout.getViewTreeObserver().addOnGlobalLayoutListener( + new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + int screenWidth = parentLayout.getWidth(); + mFloatingActionButtonController.setScreenWidth(screenWidth); + parentLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this); + } + }); setupActivityOverlay(); - mFloatingActionButtonContainer = findViewById(R.id.floating_action_button_container); - ViewUtil.setupFloatingActionButton(mFloatingActionButtonContainer, getResources()); - - mFloatingActionButton = (ImageButton) findViewById(R.id.floating_action_button); - mFloatingActionButton.setOnClickListener(this); - mRemoveViewContainer = findViewById(R.id.remove_view_container); mDialerDatabaseHelper = DatabaseHelperManager.getDatabaseHelper(this); @@ -480,6 +484,7 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O outState.putBoolean(KEY_FIRST_LAUNCH, mFirstLaunch); outState.putBoolean(KEY_IS_DIALPAD_SHOWN, mIsDialpadShown); mActionBarController.saveInstanceState(outState); + mFloatingActionButtonController.saveInstanceState(outState); } @Override @@ -630,7 +635,7 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O * Callback from child DialpadFragment when the dialpad is shown. */ public void onDialpadShown() { - updateFloatingActionButton(); + mFloatingActionButtonController.updateByDialpadVisibility(true); if (mDialpadFragment.getAnimate()) { mDialpadFragment.getView().startAnimation(mSlideIn); } else { @@ -659,7 +664,7 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O mDialpadFragment.setAnimate(animate); updateSearchFragmentPosition(); - updateFloatingActionButton(); + mFloatingActionButtonController.updateByDialpadVisibility(false); if (animate) { mDialpadFragment.getView().startAnimation(mSlideOut); } else { @@ -993,7 +998,7 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O @Override public void setFloatingActionButtonVisible(boolean visible) { - mFloatingActionButtonContainer.setVisibility(visible ? View.VISIBLE : View.GONE); + mFloatingActionButtonController.setVisible(visible); } private boolean phoneIsInUse() { @@ -1091,72 +1096,19 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { - + mFloatingActionButtonController.onPageScrolled(position, positionOffset); } @Override public void onPageSelected(int position) { mCurrentTabPosition = position; - // If the dialpad is showing, the floating action button should always be middle aligned. - if (!mIsDialpadShown) { - alignFloatingActionButtonByTab(mCurrentTabPosition); - } + mFloatingActionButtonController.updateByTab(position); } @Override public void onPageScrollStateChanged(int state) { } - private void updateFloatingActionButton() { - if (mIsDialpadShown) { - mFloatingActionButton.setImageResource(R.drawable.fab_ic_call); - mFloatingActionButton.setContentDescription( - getResources().getString(R.string.description_dial_button)); - alignFloatingActionButtonMiddle(); - } else { - mFloatingActionButton.setImageResource(R.drawable.fab_ic_dial); - mFloatingActionButton.setContentDescription( - getResources().getString(R.string.action_menu_dialpad_button)); - alignFloatingActionButtonByTab(mCurrentTabPosition); - } - } - - private void alignFloatingActionButtonByTab(int position) { - if (position == ListsFragment.TAB_INDEX_SPEED_DIAL) { - alignFloatingActionButtonMiddle(); - } else { - alignFloatingActionButtonRight(); - } - } - - private void alignFloatingActionButtonRight() { - final RelativeLayout.LayoutParams params = - (RelativeLayout.LayoutParams) mFloatingActionButtonContainer.getLayoutParams(); - params.removeRule(RelativeLayout.CENTER_HORIZONTAL); - params.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); - updateFloatingActionButtonMargin(params); - mFloatingActionButtonContainer.setLayoutParams(params); - } - - private void alignFloatingActionButtonMiddle() { - final RelativeLayout.LayoutParams params = - (RelativeLayout.LayoutParams) mFloatingActionButtonContainer.getLayoutParams(); - params.removeRule(RelativeLayout.ALIGN_PARENT_RIGHT); - params.addRule(RelativeLayout.CENTER_HORIZONTAL); - updateFloatingActionButtonMargin(params); - mFloatingActionButtonContainer.setLayoutParams(params); - } - - private void updateFloatingActionButtonMargin(RelativeLayout.LayoutParams params) { - params.setMarginsRelative( - params.getMarginStart(), - params.topMargin, - params.getMarginEnd(), - mIsDialpadShown ? - mFloatingActionButtonDialpadMarginBottom : - mFloatingActionButtonMarginBottom); - } - private TelephonyManager getTelephonyManager() { return (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); } diff --git a/src/com/android/dialer/widget/FloatingActionButtonController.java b/src/com/android/dialer/widget/FloatingActionButtonController.java new file mode 100644 index 000000000..3f59153ef --- /dev/null +++ b/src/com/android/dialer/widget/FloatingActionButtonController.java @@ -0,0 +1,212 @@ +/* + * 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.dialer.widget; + +import android.app.Activity; +import android.content.res.Resources; +import android.os.Bundle; +import android.view.animation.AnimationUtils; +import android.view.animation.Interpolator; +import android.view.View; +import android.widget.ImageButton; + +import com.android.contacts.common.util.ViewUtil; +import com.android.dialer.R; +import com.android.dialer.list.ListsFragment; + +/** + * Controls the movement and appearance of the FAB. + */ +public class FloatingActionButtonController { + private static final String KEY_IS_DIALPAD_VISIBLE = "key_is_dialpad_visible"; + private static final String KEY_CURRENT_TAB_POSITION = "key_current_tab_position"; + private static final int ANIMATION_DURATION = 250; + + private int mScreenWidth; + + private int mCurrentTabPosition; + + private ImageButton mFloatingActionButton; + private View mFloatingActionButtonContainer; + + private boolean mIsLandscape; + private boolean mIsDialpadVisible; + private boolean mAnimateFloatingActionButton; + + private String mDescriptionDialButtonStr; + private String mActionMenuDialpadButtonStr; + + /** + * Interpolator for FAB animations. + */ + private Interpolator mFabInterpolator; + + /** + * Additional offset for FAB to be lowered when dialpad is open. + */ + private int mFloatingActionButtonDialpadMarginBottomOffset; + + public FloatingActionButtonController(Activity activity, boolean isLandscape, + View container) { + Resources resources = activity.getResources(); + mIsLandscape = isLandscape; + mFabInterpolator = AnimationUtils.loadInterpolator(activity, + android.R.interpolator.fast_out_slow_in); + mFloatingActionButtonDialpadMarginBottomOffset = resources.getDimensionPixelOffset( + R.dimen.floating_action_button_dialpad_margin_bottom_offset); + mFloatingActionButton = (ImageButton) activity. + findViewById(R.id.floating_action_button); + mDescriptionDialButtonStr = resources.getString(R.string.description_dial_button); + mActionMenuDialpadButtonStr = resources.getString(R.string.action_menu_dialpad_button); + mFloatingActionButtonContainer = container; + ViewUtil.setupFloatingActionButton(mFloatingActionButtonContainer, resources); + } + + /** + * Passes the screen width into the class. Necessary for translation calculations. + * + * @param screenWidth the width of the screen + */ + public void setScreenWidth(int screenWidth) { + mScreenWidth = screenWidth; + updateByDialpadVisibility(mIsDialpadVisible); + } + + public void setVisible(boolean visible) { + mFloatingActionButtonContainer.setVisibility(visible ? View.VISIBLE : View.GONE); + } + + /** + * Updates the FAB location (middle to right position) as the PageView scrolls. + * + * @param position tab position to align for + * @param positionOffset a fraction used to calculate position of the FAB during page scroll + */ + public void onPageScrolled(int position, float positionOffset) { + // As the page is scrolling, if we're on the first tab, update the FAB position so it + // moves along with it. + if (position == ListsFragment.TAB_INDEX_SPEED_DIAL) { + mFloatingActionButtonContainer.setTranslationX( + (int) (positionOffset * (mScreenWidth / 2f + - mFloatingActionButton.getWidth()))); + mFloatingActionButtonContainer.setTranslationY(0); + } + } + + /** + * Updates the FAB location given a tab position. + * + * @param position tab position to align for + */ + public void updateByTab(int position) { + // If the screen width hasn't been set yet, don't do anything. + if (mScreenWidth == 0 || mIsDialpadVisible) return; + alignFloatingActionButtonByTab(position, false); + mAnimateFloatingActionButton = true; + } + + /** + * Updates the FAB location to the proper location given whether or not the dialer is open. + * + * @param dialpadVisible whether or not the dialpad is currently open + */ + public void updateByDialpadVisibility(boolean dialpadVisible) { + // If the screen width hasn't been set yet, don't do anything. + if (mScreenWidth == 0) return; + mIsDialpadVisible = dialpadVisible; + + moveFloatingActionButton(mAnimateFloatingActionButton); + mAnimateFloatingActionButton = true; + } + + /** + * Moves the FAB to the best known location given what the class currently knows. + * + * @param animate whether or not to smoothly animate the button + */ + private void moveFloatingActionButton(boolean animate) { + if (mIsDialpadVisible) { + mFloatingActionButton.setImageResource(R.drawable.fab_ic_call); + mFloatingActionButton.setContentDescription(mDescriptionDialButtonStr); + alignFloatingActionButton(animate); + } else { + mFloatingActionButton.setImageResource(R.drawable.fab_ic_dial); + mFloatingActionButton.setContentDescription(mActionMenuDialpadButtonStr); + alignFloatingActionButtonByTab(mCurrentTabPosition, mAnimateFloatingActionButton); + } + } + + /** + * Aligns the FAB to the position for the indicated tab. + * + * @param position tab position to align for + * @param animate whether or not to smoothly animate the button + */ + private void alignFloatingActionButtonByTab(int position, boolean animate) { + mCurrentTabPosition = position; + alignFloatingActionButton(animate); + } + + /** + * Aligns the FAB to the correct position. + * + * @param animate whether or not to smoothly animate the button + */ + private void alignFloatingActionButton(boolean animate) { + int translationX = calculateTranslationX(); + int translationY = mIsDialpadVisible ? mFloatingActionButtonDialpadMarginBottomOffset : 0; + if (animate) { + mFloatingActionButtonContainer.animate() + .translationX(translationX) + .translationY(translationY) + .setInterpolator(mFabInterpolator) + .setDuration(ANIMATION_DURATION).start(); + } else { + mFloatingActionButtonContainer.setTranslationX(translationX); + mFloatingActionButtonContainer.setTranslationY(translationY); + } + } + + /** + * Calculates the translationX distance for the FAB. + */ + private int calculateTranslationX() { + if (mIsDialpadVisible) { + return mIsLandscape ? mScreenWidth / 4 : 0; + } + if (mCurrentTabPosition == ListsFragment.TAB_INDEX_SPEED_DIAL) { + return 0; + } + return mScreenWidth / 2 - mFloatingActionButton.getWidth(); + } + + /** + * Saves the current state of the floating action button into a provided {@link Bundle} + */ + public void saveInstanceState(Bundle outState) { + outState.putBoolean(KEY_IS_DIALPAD_VISIBLE, mIsDialpadVisible); + outState.putInt(KEY_CURRENT_TAB_POSITION, mCurrentTabPosition); + } + + /** + * Restores the floating action button state from a provided {@link Bundle} + */ + public void restoreInstanceState(Bundle inState) { + mIsDialpadVisible = inState.getBoolean(KEY_IS_DIALPAD_VISIBLE); + mCurrentTabPosition = inState.getInt(KEY_CURRENT_TAB_POSITION); + } +} |