Merge pull request #4810 from goblinr/MAPSME-86-ext-screen-transition-animations

[android] Transition animations
This commit is contained in:
mpimenov 2016-12-29 17:08:07 +03:00 committed by GitHub
commit a2c5ebe00a
15 changed files with 639 additions and 270 deletions

View file

@ -22,21 +22,25 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.design.widget.TabLayout
android:id="@+id/tabs"
android:theme="@style/ThemeOverlay.AppCompat"
<LinearLayout
android:id="@+id/tabs_layout"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="@dimen/tabs_height"
android:elevation="@dimen/appbar_elevation"
app:tabIndicatorColor="?colorAccent"
app:tabMode="fixed"
app:tabGravity="fill"/>
android:layout_height="wrap_content">
<android.support.design.widget.TabLayout
android:id="@+id/tabs"
android:theme="@style/ThemeOverlay.AppCompat"
android:layout_width="match_parent"
android:layout_height="@dimen/tabs_height"
app:tabIndicatorColor="?colorAccent"
app:tabMode="fixed"
app:tabGravity="fill"/>
<View
android:id="@+id/tabs_divider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?dividerHorizontal"/>
<FrameLayout
style="@style/MwmWidget.FrameLayout.Elevation"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
<android.support.v4.view.ViewPager
android:id="@+id/pages"

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/MwmWidget.FrameLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include
layout="@layout/recycler_default"/>
</FrameLayout>

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/MwmWidget.FrameLayout.Elevation"
style="@style/MwmWidget.FrameLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="@layout/recycler_default"/>

View file

@ -20,6 +20,7 @@ import android.support.v7.widget.Toolbar;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.ImageButton;
@ -105,6 +106,7 @@ public class MwmActivity extends BaseMwmFragmentActivity
RoutingController.Container,
LocationHelper.UiCallback,
RoutingPlanController.OnToggleListener,
RoutingPlanController.SearchPoiTransitionListener,
FloatingSearchToolbarController.VisibilityListener
{
public static final String EXTRA_TASK = "map_task";
@ -148,6 +150,8 @@ public class MwmActivity extends BaseMwmFragmentActivity
private View mPositionChooser;
private ViewGroup mRootView;
private boolean mIsFragmentContainer;
private boolean mIsFullscreen;
private boolean mIsFullscreenAnimating;
@ -452,6 +456,7 @@ public class MwmActivity extends BaseMwmFragmentActivity
{
mRoutingPlanInplaceController = new RoutingPlanInplaceController(this);
mRoutingPlanInplaceController.setOnToggleListener(this);
mRoutingPlanInplaceController.setPoiTransitionListener(this);
removeCurrentFragment(false);
}
@ -529,7 +534,9 @@ public class MwmActivity extends BaseMwmFragmentActivity
.commit();
}
findViewById(R.id.map_fragment_container).setOnTouchListener(this);
View container = findViewById(R.id.map_fragment_container);
container.setOnTouchListener(this);
mRootView = (ViewGroup) container.getParent();
}
private void initNavigationButtons()
@ -544,7 +551,8 @@ public class MwmActivity extends BaseMwmFragmentActivity
ImageButton traffic = (ImageButton) frame.findViewById(R.id.traffic);
mTraffic = new TrafficButton(this, traffic);
mTrafficButtonController = new TrafficButtonController(mTraffic, this);
mNavAnimationController = new NavigationButtonsAnimationController(zoomIn, zoomOut, myPosition);
mNavAnimationController = new NavigationButtonsAnimationController(
zoomIn, zoomOut, myPosition, getWindow().getDecorView().getRootView());
}
public boolean closePlacePage()
@ -1169,9 +1177,6 @@ public class MwmActivity extends BaseMwmFragmentActivity
Framework.nativeDeactivatePopup();
mPlacePage.setMapObject(null, false);
}
if (mNavAnimationController != null)
mNavAnimationController.onPlacePageVisibilityChanged(isVisible);
}
@Override
@ -1355,48 +1360,83 @@ public class MwmActivity extends BaseMwmFragmentActivity
@Override
public void updateMenu()
{
adjustMenuLineFrameVisibility();
if (RoutingController.get().isNavigating())
adjustMenuLineFrameVisibility(new Runnable()
{
if (mNavigationController != null)
mNavigationController.show(true);
mSearchController.hide();
mMainMenu.setState(MainMenu.State.NAVIGATION, false, mIsFullscreen);
return;
}
@Override
public void run()
{
if (RoutingController.get().isNavigating())
{
if (mNavigationController != null)
mNavigationController.show(true);
mSearchController.hide();
mMainMenu.setState(MainMenu.State.NAVIGATION, false, mIsFullscreen);
return;
}
if (mIsFragmentContainer)
{
mMainMenu.setEnabled(MainMenu.Item.P2P, !RoutingController.get().isPlanning());
mMainMenu.setEnabled(MainMenu.Item.SEARCH, !RoutingController.get().isWaitingPoiPick());
}
else if (RoutingController.get().isPlanning())
{
mMainMenu.setState(MainMenu.State.ROUTE_PREPARE, false, mIsFullscreen);
return;
}
if (mIsFragmentContainer)
{
mMainMenu.setEnabled(MainMenu.Item.P2P, !RoutingController.get().isPlanning());
mMainMenu.setEnabled(MainMenu.Item.SEARCH, !RoutingController.get().isWaitingPoiPick());
}
else if (RoutingController.get().isPlanning())
{
mMainMenu.setState(MainMenu.State.ROUTE_PREPARE, false, mIsFullscreen);
return;
}
mMainMenu.setState(MainMenu.State.MENU, false, mIsFullscreen);
mMainMenu.setState(MainMenu.State.MENU, false, mIsFullscreen);
}
});
}
private void adjustMenuLineFrameVisibility()
private void adjustMenuLineFrameVisibility(@Nullable final Runnable completion)
{
final RoutingController controller = RoutingController.get();
if (controller.isBuilt() || controller.isUberRequestHandled())
{
mMainMenu.showLineFrame(true);
mMainMenu.showLineFrame(true, new Runnable()
{
@Override
public void run()
{
adjustRuler(0, 0);
}
});
if (completion != null)
completion.run();
return;
}
if (controller.isPlanning() || controller.isBuilding() || controller.isErrorEncountered())
{
mMainMenu.showLineFrame(false);
mMainMenu.showLineFrame(false, new Runnable()
{
@Override
public void run()
{
final int menuHeight = getCurrentMenu().getFrame().getHeight();
adjustRuler(0, menuHeight);
if (completion != null)
completion.run();
}
});
return;
}
mMainMenu.showLineFrame(true);
mMainMenu.showLineFrame(true, new Runnable()
{
@Override
public void run()
{
adjustRuler(0, 0);
}
});
if (completion != null)
completion.run();
}
private void setNavButtonsTopLimit(int limit)
@ -1410,6 +1450,11 @@ public class MwmActivity extends BaseMwmFragmentActivity
@Override
public void showRoutePlan(boolean show, @Nullable Runnable completionListener)
{
if (mNavAnimationController != null && !mIsFragmentContainer)
{
mNavAnimationController.setBottomLimit(show ? 0 : getCurrentMenu().getFrame().getHeight());
mNavAnimationController.slide(show, getCurrentMenu().getFrame().getHeight());
}
if (show)
{
mSearchController.hide();
@ -1529,6 +1574,13 @@ public class MwmActivity extends BaseMwmFragmentActivity
}
}
@Override
public void animateSearchPoiTransition(@NonNull final Rect startRect,
@Nullable final Runnable runnable)
{
Animations.riseTransition(mRootView, startRect, runnable);
}
@Override
public void onUberInfoReceived(@NonNull UberInfo info)
{

View file

@ -1,5 +1,8 @@
package com.mapswithme.maps;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.res.Resources;
import android.support.annotation.NonNull;
import android.view.View;
@ -7,12 +10,11 @@ import com.mapswithme.maps.routing.RoutingController;
import com.mapswithme.util.Animations;
import com.mapswithme.util.Config;
import com.mapswithme.util.UiUtils;
import com.mapswithme.util.log.DebugLogger;
class NavigationButtonsAnimationController
{
private static final DebugLogger LOGGER =
new DebugLogger(NavigationButtonsAnimationController.class.getSimpleName());
private static final int ANIM_TOGGLE = MwmApplication.get().getResources().getInteger(R.integer.anim_slots_toggle);
@NonNull
private final View mZoomIn;
@NonNull
@ -20,19 +22,40 @@ class NavigationButtonsAnimationController
@NonNull
private final View mMyPosition;
private float mBottomLimit;
private float mTopLimit;
private final float mMargin;
private float mContentHeight;
private float mMyPositionBottom;
private boolean mMyPosAnimate;
private boolean mZoomAnimating;
private boolean mMyPosAnimating;
private boolean mSlidingDown;
private float mTopLimit;
private float mBottomLimit;
private float mCurrentOffset;
NavigationButtonsAnimationController(@NonNull View zoomIn, @NonNull View zoomOut,
@NonNull View myPosition)
@NonNull View myPosition, @NonNull final View contentView)
{
mZoomIn = zoomIn;
mZoomOut = zoomOut;
checkZoomButtonsVisibility();
mMyPosition = myPosition;
calculateBottomLimit();
Resources res = mZoomIn.getResources();
mMargin = res.getDimension(R.dimen.margin_base_plus);
mBottomLimit = res.getDimension(R.dimen.menu_line_height);
calculateLimitTranslations();
contentView.addOnLayoutChangeListener(new View.OnLayoutChangeListener()
{
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
int oldTop, int oldRight, int oldBottom)
{
mContentHeight = bottom - top;
contentView.removeOnLayoutChangeListener(this);
}
});
}
private void checkZoomButtonsVisibility()
@ -40,6 +63,193 @@ class NavigationButtonsAnimationController
UiUtils.showIf(showZoomButtons(), mZoomIn, mZoomOut);
}
private void calculateLimitTranslations()
{
mTopLimit = mMargin;
mMyPosition.addOnLayoutChangeListener(new View.OnLayoutChangeListener()
{
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom)
{
mMyPositionBottom = bottom;
mMyPosition.removeOnLayoutChangeListener(this);
}
});
}
void setTopLimit(float limit)
{
mTopLimit = limit + mMargin;
update();
}
void setBottomLimit(float limit)
{
mBottomLimit = limit;
update();
}
private void fadeOutZoom()
{
if (mSlidingDown)
return;
mZoomAnimating = true;
Animations.fadeOutView(mZoomIn, new Runnable()
{
@Override
public void run()
{
mZoomIn.setVisibility(View.INVISIBLE);
mZoomAnimating = false;
}
});
Animations.fadeOutView(mZoomOut, new Runnable()
{
@Override
public void run()
{
mZoomOut.setVisibility(View.INVISIBLE);
}
});
}
private void fadeInZoom()
{
mZoomAnimating = true;
mZoomIn.setVisibility(View.VISIBLE);
mZoomOut.setVisibility(View.VISIBLE);
Animations.fadeInView(mZoomIn, new Runnable()
{
@Override
public void run()
{
mZoomAnimating = false;
}
});
Animations.fadeInView(mZoomOut, null);
}
private void fadeOutMyPosition()
{
if (mSlidingDown)
return;
mMyPosAnimating = true;
Animations.fadeOutView(mMyPosition, new Runnable()
{
@Override
public void run()
{
UiUtils.invisible(mMyPosition);
mMyPosAnimating = false;
}
});
}
private void fadeInMyPosition()
{
mMyPosAnimating = true;
mMyPosition.setVisibility(View.VISIBLE);
Animations.fadeInView(mMyPosition, new Runnable()
{
@Override
public void run()
{
mMyPosAnimating = false;
}
});
}
void onPlacePageMoved(float translationY)
{
if (UiUtils.isLandscape(mMyPosition.getContext()) || mMyPositionBottom == 0 || mContentHeight == 0)
return;
final float amount = mCurrentOffset > 0 ? mMargin : -mMargin;
final float translation = translationY - (mMyPositionBottom - mCurrentOffset + amount);
update(translation <= mCurrentOffset ? translation : mCurrentOffset);
}
private void update()
{
update(mZoomIn.getTranslationY());
}
private void update(final float translation)
{
mMyPosition.setTranslationY(translation);
mZoomOut.setTranslationY(translation);
mZoomIn.setTranslationY(translation);
if (!mZoomAnimating && mZoomIn.getVisibility() == View.VISIBLE
&& !isViewInsideLimits(mZoomIn))
{
fadeOutZoom();
}
else if (!mZoomAnimating && mZoomIn.getVisibility() == View.INVISIBLE
&& isViewInsideLimits(mZoomIn))
{
fadeInZoom();
}
if (!shouldBeHidden() && !mMyPosAnimating
&& mMyPosition.getVisibility() == View.VISIBLE && !isViewInsideLimits(mMyPosition))
{
fadeOutMyPosition();
}
else if (!shouldBeHidden() && !mMyPosAnimating
&& mMyPosition.getVisibility() == View.INVISIBLE && isViewInsideLimits(mMyPosition))
{
fadeInMyPosition();
}
}
private boolean isViewInsideLimits(@NonNull View view)
{
return view.getY() >= mTopLimit &&
view.getBottom() + view.getTranslationY() <= mContentHeight - mBottomLimit;
}
private boolean shouldBeHidden()
{
return LocationState.getMode() == LocationState.FOLLOW_AND_ROTATE
&& (RoutingController.get().isPlanning() || RoutingController.get().isNavigating());
}
void slide(boolean isDown, float distance)
{
if (UiUtils.isLandscape(mMyPosition.getContext())
|| (!isDown && mZoomIn.getTranslationY() <= 0))
return;
mSlidingDown = isDown;
mCurrentOffset = isDown ? distance : 0;
ValueAnimator animator = ValueAnimator.ofFloat(isDown ? 0 : distance, isDown ? distance : 0);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()
{
@Override
public void onAnimationUpdate(ValueAnimator animation)
{
float value = (float) animation.getAnimatedValue();
update(value);
}
});
animator.addListener(new UiUtils.SimpleAnimatorListener()
{
@Override
public void onAnimationEnd(Animator animation)
{
mSlidingDown = false;
update();
}
});
animator.setDuration(ANIM_TOGGLE);
animator.start();
}
void disappearZoomButtons()
{
if (!showZoomButtons())
@ -54,204 +264,8 @@ class NavigationButtonsAnimationController
if (!showZoomButtons())
return;
if (!canZoomButtonsFitInScreen())
return;
Animations.appearSliding(mZoomIn, Animations.RIGHT, null);
Animations.appearSliding(mZoomOut, Animations.RIGHT, null);
updateZoomButtonsPosition();
}
void setTopLimit(float limit)
{
mTopLimit = limit;
updateZoomButtonsPosition();
}
void onPlacePageVisibilityChanged(boolean isVisible)
{
if (isVisible)
fadeOutZooms();
else
fadeInZooms();
}
void onPlacePageMoved(float translationY)
{
if (mBottomLimit == 0)
return;
float translation = translationY - mBottomLimit;
animateMyPosition(translation <= 0 ? translation : 0);
}
private void calculateBottomLimit()
{
mMyPosition.addOnLayoutChangeListener(new View.OnLayoutChangeListener()
{
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom)
{
mBottomLimit = bottom;
mMyPosition.removeOnLayoutChangeListener(this);
}
});
}
private void updateZoomButtonsPosition()
{
// It means that the zoom buttons fit in screen perfectly,
// any updates of position are no needed.
if (mZoomIn.getTop() >= mTopLimit && UiUtils.isVisible(mZoomIn))
return;
// If the top limit is decreased we try to return zoom buttons at initial position.
if (mTopLimit < mZoomIn.getTop() && tryPlaceZoomButtonsAtInitialPosition())
{
LOGGER.d("Zoom buttons were come back to initial position");
return;
}
// If top view overlaps the zoomIn button.
if (mTopLimit > mZoomIn.getTop())
{
// We should try to pull out the zoomIn button from under the top view
// if available space allows us doing that.
if (tryPlaceZoomButtonsUnderTopLimit())
return;
// Otherwise, we just fade out the zoom buttons
// since there is not enough space on the screen for them.
fadeOutZooms();
}
}
private boolean tryPlaceZoomButtonsAtInitialPosition()
{
float availableSpace = mBottomLimit - mTopLimit;
float requiredSpace = mBottomLimit - mZoomIn.getTop() - mZoomIn.getTranslationY();
if (requiredSpace > availableSpace)
return false;
mZoomIn.setTranslationY(0);
mZoomOut.setTranslationY(0);
return true;
}
private boolean tryPlaceZoomButtonsUnderTopLimit()
{
if (!canZoomButtonsFitInScreen())
return false;
float requiredTranslate = mTopLimit - mZoomIn.getTop();
mZoomIn.setTranslationY(requiredTranslate);
mZoomOut.setTranslationY(requiredTranslate);
return true;
}
private boolean canZoomButtonsFitInScreen()
{
float availableSpace = mBottomLimit - mTopLimit;
return getMinimumRequiredHeightForButtons() <= availableSpace;
}
private float getMinimumRequiredHeightForButtons()
{
return mZoomIn.getHeight() + mZoomOut.getHeight() + mMyPosition.getHeight();
}
private void fadeOutZooms()
{
Animations.fadeOutView(mZoomIn, new Runnable()
{
@Override
public void run()
{
mZoomIn.setVisibility(View.INVISIBLE);
}
});
Animations.fadeOutView(mZoomOut, new Runnable()
{
@Override
public void run()
{
mZoomOut.setVisibility(View.INVISIBLE);
}
});
}
private void fadeInZooms()
{
if (!showZoomButtons())
return;
if (!canZoomButtonsFitInScreen())
return;
mZoomIn.setVisibility(View.VISIBLE);
mZoomOut.setVisibility(View.VISIBLE);
Animations.fadeInView(mZoomIn, null);
Animations.fadeInView(mZoomOut, null);
}
private void fadeOutMyPosition()
{
mMyPosAnimate = true;
Animations.fadeOutView(mMyPosition, new Runnable()
{
@Override
public void run()
{
UiUtils.invisible(mMyPosition);
mMyPosAnimate = false;
}
});
}
private void fadeInMyPosition()
{
mMyPosAnimate = true;
mMyPosition.setVisibility(View.VISIBLE);
Animations.fadeInView(mMyPosition, new Runnable()
{
@Override
public void run()
{
mMyPosAnimate = false;
}
});
}
private void animateMyPosition(float translation)
{
mMyPosition.setTranslationY(translation);
if (!shouldMyPositionBeHidden() && !mMyPosAnimate
&& isOverTopLimit(mMyPosition))
{
fadeOutMyPosition();
}
else if (!shouldMyPositionBeHidden() && !mMyPosAnimate
&& satisfyTopLimit(mMyPosition))
{
fadeInMyPosition();
}
}
private boolean isOverTopLimit(@NonNull View view)
{
return view.getVisibility() == View.VISIBLE && view.getY() <= mTopLimit;
}
private boolean satisfyTopLimit(@NonNull View view)
{
return view.getVisibility() == View.INVISIBLE && view.getY() >= mTopLimit;
}
private boolean shouldMyPositionBeHidden()
{
return LocationState.getMode() == LocationState.FOLLOW_AND_ROTATE
&& (RoutingController.get().isPlanning());
Animations.appearSliding(mZoomIn, Animations.LEFT, null);
Animations.appearSliding(mZoomOut, Animations.LEFT, null);
}
private static boolean showZoomButtons()

View file

@ -3,6 +3,7 @@ package com.mapswithme.maps.routing;
import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Rect;
import android.support.annotation.DimenRes;
import android.support.annotation.IntRange;
import android.support.annotation.MainThread;

View file

@ -4,6 +4,7 @@ import android.animation.Animator;
import android.animation.ValueAnimator;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.location.Location;
import android.os.Bundle;
import android.support.annotation.DrawableRes;
@ -42,7 +43,7 @@ import com.mapswithme.util.Utils;
import com.mapswithme.util.statistics.AlohaHelper;
import com.mapswithme.util.statistics.Statistics;
public class RoutingPlanController extends ToolbarController
public class RoutingPlanController extends ToolbarController implements SlotFrame.SlotClickListener
{
static final int ANIM_TOGGLE = MwmApplication.get().getResources().getInteger(R.integer.anim_slots_toggle);
private static final String STATE_ALTITUDE_CHART_SHOWN = "altitude_chart_shown";
@ -62,7 +63,7 @@ public class RoutingPlanController extends ToolbarController
private final View mUberFrame;
private final RotateDrawable mToggleImage = new RotateDrawable(R.drawable.ic_down);
private int mFrameHeight;
int mFrameHeight;
private int mToolbarHeight;
private boolean mOpen;
@Nullable
@ -74,11 +75,20 @@ public class RoutingPlanController extends ToolbarController
@Nullable
private OnToggleListener mToggleListener;
@Nullable
private SearchPoiTransitionListener mPoiTransitionListener;
public interface OnToggleListener
{
void onToggle(boolean state);
}
public interface SearchPoiTransitionListener
{
void animateSearchPoiTransition(@NonNull final Rect startRect,
@Nullable final Runnable runnable);
}
private RadioButton setupRouterButton(@IdRes int buttonId, final @DrawableRes int iconRes, View.OnClickListener clickListener)
{
CompoundButton.OnCheckedChangeListener listener = new CompoundButton.OnCheckedChangeListener()
@ -109,6 +119,7 @@ public class RoutingPlanController extends ToolbarController
mToggle = (ImageView) mToolbar.findViewById(R.id.toggle);
mSlotFrame = (SlotFrame) root.findViewById(R.id.slots);
mSlotFrame.setSlotClickListener(this);
mRouterTypes = (RadioGroup) mToolbar.findViewById(R.id.route_type);
setupRouterButton(R.id.vehicle, R.drawable.ic_car, new View.OnClickListener()
@ -186,7 +197,32 @@ public class RoutingPlanController extends ToolbarController
RoutingController.get().cancelPlanning();
}
private boolean checkFrameHeight()
@Override
public void onSlotClicked(final int order, @NonNull Rect rect)
{
if (mPoiTransitionListener != null)
{
mPoiTransitionListener.animateSearchPoiTransition(rect, new Runnable()
{
@Override
public void run()
{
RoutingController.get().searchPoi(order);
}
});
}
else
{
RoutingController.get().searchPoi(order);
}
}
public void setPoiTransitionListener(@Nullable SearchPoiTransitionListener poiTransitionListener)
{
mPoiTransitionListener = poiTransitionListener;
}
boolean checkFrameHeight()
{
if (mFrameHeight > 0)
return true;

View file

@ -1,7 +1,10 @@
package com.mapswithme.maps.routing;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.widget.ImageView;
import com.mapswithme.maps.MwmActivity;
@ -20,7 +23,7 @@ public class RoutingPlanInplaceController extends RoutingPlanController
super(activity.findViewById(R.id.routing_plan_frame), activity);
}
public void show(boolean show)
public void show(final boolean show)
{
if (show == UiUtils.isVisible(mFrame))
return;
@ -36,9 +39,21 @@ public class RoutingPlanInplaceController extends RoutingPlanController
mSlotsRestoredState = null;
}
UiUtils.showIf(show, mFrame);
if (show)
{
UiUtils.show(mFrame);
updatePoints();
}
animateFrame(show, new Runnable()
{
@Override
public void run()
{
if (!show)
UiUtils.hide(mFrame);
}
});
}
public void onSaveState(@NonNull Bundle outState)
@ -61,4 +76,42 @@ public class RoutingPlanInplaceController extends RoutingPlanController
ImageView altitudeChart = (ImageView) mActivity.findViewById(R.id.altitude_chart);
showRouteAltitudeChartInternal(altitudeChart);
}
private void animateFrame(final boolean show, final @Nullable Runnable completion)
{
if (!checkFrameHeight())
{
mFrame.post(new Runnable()
{
@Override
public void run()
{
animateFrame(show, completion);
}
});
return;
}
ValueAnimator animator =
ValueAnimator.ofFloat(show ? -mFrameHeight : 0, show ? 0 : -mFrameHeight);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()
{
@Override
public void onAnimationUpdate(ValueAnimator animation)
{
mFrame.setTranslationY((Float) animation.getAnimatedValue());
}
});
animator.addListener(new UiUtils.SimpleAnimatorListener()
{
@Override
public void onAnimationEnd(Animator animation)
{
if (completion != null)
completion.run();
}
});
animator.setDuration(ANIM_TOGGLE);
animator.start();
}
}

View file

@ -7,6 +7,7 @@ import android.graphics.Rect;
import android.os.Build;
import android.support.annotation.IdRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.MotionEvent;
@ -35,6 +36,14 @@ public class SlotFrame extends LinearLayout
private int mTextColor;
private int mHintColor;
@Nullable
private SlotClickListener mSlotClickListener;
interface SlotClickListener
{
void onSlotClicked(int order, @NonNull Rect rect);
}
private class Slot
{
private final View mFrame;
@ -74,7 +83,10 @@ public class SlotFrame extends LinearLayout
@Override
public void onClick(View v)
{
RoutingController.get().searchPoi(mOrder);
Rect rect = new Rect();
mFrame.getGlobalVisibleRect(rect);
if (mSlotClickListener != null)
mSlotClickListener.onSlotClicked(mOrder, rect);
}
});
@ -334,6 +346,11 @@ public class SlotFrame extends LinearLayout
super(context, attrs, defStyleAttr);
}
public void setSlotClickListener(@Nullable SlotClickListener slotClickListener)
{
mSlotClickListener = slotClickListener;
}
public void update()
{
mSlotFrom.setMapObject(RoutingController.get().getStartPoint());

View file

@ -1,5 +1,6 @@
package com.mapswithme.maps.search;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.support.annotation.NonNull;
@ -17,11 +18,12 @@ public class SearchActivity extends BaseMwmFragmentActivity implements CustomNav
{
public static final String EXTRA_QUERY = "search_query";
public static void start(Context context, String query)
public static void start(@NonNull Activity activity, String query)
{
final Intent i = new Intent(context, SearchActivity.class);
final Intent i = new Intent(activity, SearchActivity.class);
i.putExtra(EXTRA_QUERY, query);
context.startActivity(i);
activity.startActivity(i);
activity.overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
}
@Override
@ -44,6 +46,7 @@ public class SearchActivity extends BaseMwmFragmentActivity implements CustomNav
if (manager.getBackStackEntryCount() == 0)
{
NavUtils.navigateUpFromSameTask(this);
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
return;
}
@ -58,5 +61,6 @@ public class SearchActivity extends BaseMwmFragmentActivity implements CustomNav
return;
super.onBackPressed();
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
}
}

View file

@ -0,0 +1,67 @@
package com.mapswithme.maps.search;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.View;
import com.mapswithme.maps.MwmApplication;
import com.mapswithme.maps.R;
import com.mapswithme.util.UiUtils;
final class SearchAnimationController
{
private static final int DURATION = MwmApplication.get().getResources().getInteger(R.integer.anim_menu);
@NonNull
private final View mToolbar;
@NonNull
private final View mTabBar;
SearchAnimationController(@NonNull View toolbar, @NonNull View tabBar)
{
mToolbar = toolbar;
mTabBar = tabBar;
}
void animate(final boolean show, @Nullable final Runnable completion)
{
if (mToolbar.getHeight() == 0 || mTabBar.getHeight() == 0)
{
mToolbar.post(new Runnable()
{
@Override
public void run()
{
animate(show, completion);
}
});
return;
}
final float translation = -mTabBar.getHeight() - mToolbar.getHeight();
ValueAnimator animator =
ValueAnimator.ofFloat(show ? translation: 0, show ? 0 : translation);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()
{
@Override
public void onAnimationUpdate(ValueAnimator animation)
{
final float value = (float) animation.getAnimatedValue();
mToolbar.setTranslationY(value);
mTabBar.setTranslationY(value);
}
});
animator.addListener(new UiUtils.SimpleAnimatorListener()
{
@Override
public void onAnimationEnd(Animator animation)
{
if (completion != null)
completion.run();
}
});
animator.setDuration(DURATION);
animator.start();
}
}

View file

@ -3,6 +3,8 @@ package com.mapswithme.maps.search;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;
import com.mapswithme.maps.R;
import com.mapswithme.maps.base.BaseMwmRecyclerFragment;
public class SearchCategoriesFragment extends BaseMwmRecyclerFragment
@ -14,6 +16,12 @@ public class SearchCategoriesFragment extends BaseMwmRecyclerFragment
return new CategoriesAdapter(this);
}
@Override
protected int getLayoutRes()
{
return R.layout.fragment_search_categories;
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState)
{

View file

@ -224,12 +224,10 @@ public class SearchFragment extends BaseMwmFragment
mTabFrame = root.findViewById(R.id.tab_frame);
ViewPager pager = (ViewPager) mTabFrame.findViewById(R.id.pages);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
UiUtils.hide(mTabFrame.findViewById(R.id.tabs_divider));
mToolbarController = new ToolbarController(view);
final TabAdapter tabAdapter = new TabAdapter(getChildFragmentManager(), pager, (TabLayout) root.findViewById(R.id.tabs));
TabLayout tabLayout = (TabLayout) root.findViewById(R.id.tabs);
final TabAdapter tabAdapter = new TabAdapter(getChildFragmentManager(), pager, tabLayout);
mResultsFrame = root.findViewById(R.id.results_frame);
RecyclerView results = (RecyclerView) mResultsFrame.findViewById(R.id.recycler);
@ -480,10 +478,20 @@ public class SearchFragment extends BaseMwmFragment
if (mFromRoutePlan)
{
RoutingController.get().onPoiSelected(null);
return !(getActivity() instanceof SearchActivity);
final boolean isSearchActivity = getActivity() instanceof SearchActivity;
if (isSearchActivity)
closeSearch();
return true;
}
return false;
closeSearch();
return true;
}
private void closeSearch()
{
getActivity().finish();
getActivity().overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
}
public void setRecyclerScrollListener(RecyclerView recycler)

View file

@ -1,8 +1,8 @@
package com.mapswithme.maps.widget.menu;
import android.support.annotation.Nullable;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import com.mapswithme.maps.MwmActivity;
@ -11,9 +11,9 @@ import com.mapswithme.maps.R;
import com.mapswithme.maps.downloader.MapManager;
import com.mapswithme.maps.downloader.UpdateInfo;
import com.mapswithme.maps.routing.RoutingController;
import com.mapswithme.util.Animations;
import com.mapswithme.util.Graphics;
import com.mapswithme.util.UiUtils;
import ru.mail.android.mytarget.core.models.Stat;
import java.util.ArrayList;
import java.util.HashMap;
@ -263,9 +263,9 @@ public class MainMenu extends BaseMenu
{
UiUtils.show(mButtonsFrame);
expandContent = false;
} else
}
else
{
UiUtils.showIf(state == State.MENU, mButtonsFrame);
UiUtils.showIf(isRouting, mRoutePlanFrame);
if (isRouting)
@ -313,8 +313,17 @@ public class MainMenu extends BaseMenu
return mAnimationTrackListener;
}
public void showLineFrame(boolean show)
public void showLineFrame(boolean show, @Nullable Runnable completion)
{
UiUtils.showIf(show, mLineFrame);
if (show)
{
UiUtils.hide(mFrame);
Animations.appearSliding(mFrame, Animations.BOTTOM, completion);
}
else
{
UiUtils.show(mFrame);
Animations.disappearSliding(mFrame, Animations.BOTTOM, completion);
}
}
}

View file

@ -1,10 +1,17 @@
package com.mapswithme.util;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Rect;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewPropertyAnimator;
import com.mapswithme.maps.MwmApplication;
@ -26,6 +33,7 @@ public final class Animations
public static final int BOTTOM = 3;
private static final int DURATION_DEFAULT = MwmApplication.get().getResources().getInteger(R.integer.anim_default);
private static final int DURATION_MENU = MwmApplication.get().getResources().getInteger(R.integer.anim_menu);
public static void appearSliding(final View view, @AnimationDirection int appearFrom, @Nullable final Runnable completionListener)
{
@ -134,4 +142,83 @@ public final class Animations
}
});
}
public static void riseTransition(@NonNull final ViewGroup rootView, @NonNull final Rect startRect,
@Nullable final Runnable runnable)
{
final Context context = rootView.getContext();
final View view = new View(context);
setCardBackgroundColor(view);
DisplayMetrics dm = context.getResources().getDisplayMetrics();
final float screenWidth = dm.widthPixels;
final float screenHeight = dm.heightPixels;
final float width = startRect.width();
final float height = startRect.height();
ViewGroup.MarginLayoutParams lp = new ViewGroup.MarginLayoutParams((int) width,
(int) height);
lp.topMargin = startRect.top;
lp.leftMargin = startRect.left;
final float right = screenWidth - startRect.right;
lp.rightMargin = (int) right;
rootView.addView(view, lp);
ValueAnimator animator = ValueAnimator.ofFloat(0.0f, 1.0f);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()
{
@Override
public void onAnimationUpdate(ValueAnimator animation)
{
final float t = (float) animation.getAnimatedValue();
final float topMargin = startRect.top - t * startRect.top;
final float leftMargin = startRect.left - t * startRect.left;
final float rightMargin = right - t * right;
final float newWidth = width + t * (screenWidth - width);
final float newHeight = height + t * (screenHeight - height);
ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
lp.width = (int) newWidth;
lp.height = (int) newHeight;
lp.topMargin = (int) topMargin;
lp.leftMargin = (int) leftMargin;
lp.rightMargin = (int) rightMargin;
view.setLayoutParams(lp);
}
});
animator.addListener(new UiUtils.SimpleAnimatorListener()
{
@Override
public void onAnimationEnd(Animator animation)
{
if (runnable != null)
runnable.run();
rootView.postDelayed(new Runnable()
{
@Override
public void run()
{
rootView.removeView(view);
}
}, context.getResources().getInteger(android.R.integer.config_longAnimTime));
}
});
animator.setDuration(DURATION_MENU);
animator.start();
}
private static void setCardBackgroundColor(@NonNull View view)
{
Context context = view.getContext();
TypedArray a = null;
try
{
a = context.obtainStyledAttributes(new int[] { R.attr.cardBackgroundColor });
int color = a.getColor(0, ContextCompat.getColor(context, R.color.bg_cards));
view.setBackgroundColor(color);
}
finally
{
if (a != null)
a.recycle();
}
}
}