Merge pull request #4452 from goblinr/MAPSME-18-external-place-page-states

[android] New Place Page opening and scrolling behavior
This commit is contained in:
alexzatsepin 2016-10-20 13:42:26 +03:00 committed by GitHub
commit 1379a7f2dc
16 changed files with 573 additions and 291 deletions

View file

@ -0,0 +1,80 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.mapswithme.maps.widget.ObservableScrollView
android:id="@+id/pp__details"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?ppBackground"
android:overScrollMode="never">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<include
layout="@layout/divider_horizontal"/>
<include
layout="@layout/place_page_bookmark_details"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/margin_half"
tools:visibility="gone"/>
<include layout="@layout/place_page_hotel_gallery"/>
<include layout="@layout/place_page_hotel_facilities"/>
<include layout="@layout/place_page_hotel_description"/>
<include layout="@layout/place_page_placename"/>
<include layout="@layout/place_page_opening_hours"/>
<include layout="@layout/place_page_entrance"/>
<include layout="@layout/place_page_phone"/>
<include layout="@layout/place_page_website"/>
<include layout="@layout/place_page_wiki"/>
<include layout="@layout/place_page_email"/>
<include layout="@layout/place_page_latlon"/>
<include layout="@layout/place_page_wifi"/>
<include layout="@layout/place_page_operator"/>
<include layout="@layout/place_page_cuisine"/>
<include layout="@layout/place_page_hotel_nearby"/>
<include layout="@layout/place_page_hotel_rating"/>
<!--TODO: remove this after booking_api.cpp will be done-->
<include layout="@layout/place_page_more"/>
<include layout="@layout/divider_horizontal"/>
<include layout="@layout/place_page_editor"/>
<include layout="@layout/place_page_add_business"/>
<include layout="@layout/place_page_add"/>
</LinearLayout>
</com.mapswithme.maps.widget.ObservableScrollView>
<include layout="@layout/shadow_top"/>
<include layout="@layout/shadow_bottom"/>
</FrameLayout>

View file

@ -1,13 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/black">
<ProgressBar
android:id="@+id/pb__loading_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"/>
<ImageView
android:id="@+id/iv__image"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
</FrameLayout>

View file

@ -5,7 +5,7 @@
android:layout_width="match_parent"
android:layout_height="@dimen/height_block_base"
android:background="?clickableBackground"
android:fontFamily="@string/robotoLight"
android:fontFamily="@string/robotoMedium"
android:gravity="center_vertical"
android:paddingLeft="@dimen/margin_base"
android:paddingRight="@dimen/margin_base"

View file

@ -1,20 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<include
android:id="@+id/pp__preview"
layout="@layout/place_page_preview"
<com.mapswithme.maps.widget.ObservableScrollView
android:id="@+id/pp__details"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@+id/pp__buttons"/>
android:layout_height="match_parent"
android:overScrollMode="never"
android:layout_above="@+id/pp__buttons">
<include
android:id="@+id/pp__details_frame"
layout="@layout/place_page_details"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@+id/pp__buttons"
android:background="?panel"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<include
android:id="@+id/pp__preview"
layout="@layout/place_page_preview"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<include
android:id="@+id/pp__details_frame"
layout="@layout/place_page_details"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?panel"/>
</LinearLayout>
</com.mapswithme.maps.widget.ObservableScrollView>
<include
android:id="@+id/pp__buttons"

View file

@ -5,74 +5,66 @@
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.mapswithme.maps.widget.ObservableScrollView
android:id="@+id/pp__details"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_height="wrap_content"
android:background="?ppBackground"
android:overScrollMode="never">
android:orientation="vertical">
<LinearLayout
<include
layout="@layout/divider_horizontal"/>
<include
layout="@layout/place_page_bookmark_details"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
android:layout_marginBottom="@dimen/margin_half"
tools:visibility="gone"/>
<include
layout="@layout/divider_horizontal"/>
<include layout="@layout/place_page_hotel_gallery"/>
<include
layout="@layout/place_page_bookmark_details"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/margin_half"
tools:visibility="gone"/>
<include layout="@layout/place_page_hotel_facilities"/>
<include layout="@layout/place_page_hotel_gallery"/>
<include layout="@layout/place_page_hotel_description"/>
<include layout="@layout/place_page_hotel_facilities"/>
<include layout="@layout/place_page_placename"/>
<include layout="@layout/place_page_hotel_description"/>
<include layout="@layout/place_page_opening_hours"/>
<include layout="@layout/place_page_placename"/>
<include layout="@layout/place_page_entrance"/>
<include layout="@layout/place_page_opening_hours"/>
<include layout="@layout/place_page_phone"/>
<include layout="@layout/place_page_entrance"/>
<include layout="@layout/place_page_website"/>
<include layout="@layout/place_page_phone"/>
<include layout="@layout/place_page_wiki"/>
<include layout="@layout/place_page_website"/>
<include layout="@layout/place_page_email"/>
<include layout="@layout/place_page_wiki"/>
<include layout="@layout/place_page_latlon"/>
<include layout="@layout/place_page_email"/>
<include layout="@layout/place_page_wifi"/>
<include layout="@layout/place_page_latlon"/>
<include layout="@layout/place_page_operator"/>
<include layout="@layout/place_page_wifi"/>
<include layout="@layout/place_page_cuisine"/>
<include layout="@layout/place_page_operator"/>
<include layout="@layout/place_page_hotel_nearby"/>
<include layout="@layout/place_page_cuisine"/>
<include layout="@layout/place_page_hotel_rating"/>
<include layout="@layout/place_page_hotel_nearby"/>
<!--TODO: remove this after booking_api.cpp will be done-->
<include layout="@layout/place_page_more"/>
<include layout="@layout/place_page_hotel_rating"/>
<include layout="@layout/divider_horizontal"/>
<!--TODO: remove this after booking_api.cpp will be done-->
<include layout="@layout/place_page_more"/>
<include layout="@layout/place_page_editor"/>
<include layout="@layout/divider_horizontal"/>
<include layout="@layout/place_page_add_business"/>
<include layout="@layout/place_page_editor"/>
<include layout="@layout/place_page_add"/>
<include layout="@layout/place_page_add_business"/>
<include layout="@layout/place_page_add"/>
</LinearLayout>
</com.mapswithme.maps.widget.ObservableScrollView>
</LinearLayout>
<include layout="@layout/shadow_top"/>

View file

@ -8,7 +8,7 @@
style="@style/MwmWidget.ToolbarStyle"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:theme="@style/MwmWidget.ToolbarTheme"/>
android:theme="@style/MwmWidget.ToolbarTheme.DownButton"/>
<FrameLayout
style="@style/MwmWidget.FrameLayout.Elevation"

View file

@ -102,6 +102,14 @@
<item name="windowActionBarOverlay">true</item>
</style>
<style
name="MwmWidget.ToolbarTheme.DownButton"
parent="ThemeOverlay.AppCompat.Dark.ActionBar">
<item name="android:gravity">center_vertical</item>
<item name="colorAccent">@android:color/white</item>
<item name="android:homeAsUpIndicator">@drawable/ic_down</item>
</style>
<style name="MwmWidget.ListView" parent="Widget.AppCompat.ListView">
<item name="android:fadingEdge">none</item>
<item name="android:divider">@color/divider</item>

View file

@ -108,7 +108,7 @@ public class MwmActivity extends BaseMwmFragmentActivity
EditorHostFragment.class.getName(),
ReportFragment.class.getName() };
// Instance state
private static final String STATE_PP_OPENED = "PpOpened";
private static final String STATE_PP = "PpState";
private static final String STATE_MAP_OBJECT = "MapObject";
// Map tasks that we run AFTER rendering initialized
@ -143,6 +143,7 @@ public class MwmActivity extends BaseMwmFragmentActivity
// The first launch of application ever - onboarding screen will be shown.
private boolean mFirstStart;
private boolean mPlacePageRestored;
public interface LeftAnimationTrackListener
{
@ -522,7 +523,7 @@ public class MwmActivity extends BaseMwmFragmentActivity
mNavMyPosition = new MyPositionButton(frame.findViewById(R.id.my_position));
}
private boolean closePlacePage()
public boolean closePlacePage()
{
if (mPlacePage.isHidden())
return false;
@ -735,7 +736,7 @@ public class MwmActivity extends BaseMwmFragmentActivity
{
if (!mPlacePage.isHidden())
{
outState.putBoolean(STATE_PP_OPENED, true);
outState.putInt(STATE_PP, mPlacePage.getState().ordinal());
outState.putParcelable(STATE_MAP_OBJECT, mPlacePage.getMapObject());
}
@ -761,8 +762,13 @@ public class MwmActivity extends BaseMwmFragmentActivity
{
super.onRestoreInstanceState(savedInstanceState);
if (savedInstanceState.getBoolean(STATE_PP_OPENED))
mPlacePage.setState(State.PREVIEW);
State state = State.values()[savedInstanceState.getInt(STATE_PP, 0)];
if (state != State.HIDDEN)
{
mPlacePageRestored = true;
mPlacePage.setMapObject((MapObject) savedInstanceState.getParcelable(STATE_MAP_OBJECT), true);
mPlacePage.setState(state);
}
if (!mIsFragmentContainer && RoutingController.get().isPlanning())
mRoutingPlanInplaceController.restoreState(savedInstanceState);
@ -820,6 +826,7 @@ public class MwmActivity extends BaseMwmFragmentActivity
{
super.onResume();
mPlacePageRestored = mPlacePage.getState() != State.HIDDEN;
mSearchController.refreshToolbar();
mMainMenu.onResume(new Runnable()
{
@ -1025,7 +1032,9 @@ public class MwmActivity extends BaseMwmFragmentActivity
setFullscreen(false);
mPlacePage.setMapObject(object, true);
mPlacePage.setState(State.PREVIEW);
if (!mPlacePageRestored)
mPlacePage.setState(State.PREVIEW);
mPlacePageRestored = false;
if (UiUtils.isVisible(mFadeView))
mFadeView.fadeOut();

View file

@ -120,7 +120,7 @@ class DownloaderAdapter extends RecyclerView.Adapter<DownloaderAdapter.ViewHolde
}
@Override
void invoke(final CountryItem item, DownloaderAdapter adapter)
void invoke(final CountryItem item, final DownloaderAdapter adapter)
{
if (RoutingController.get().isNavigating())
{
@ -134,7 +134,7 @@ class DownloaderAdapter extends RecyclerView.Adapter<DownloaderAdapter.ViewHolde
if (!MapManager.nativeHasUnsavedEditorChanges(item.id))
{
deleteNode(item);
deleteNode(item, adapter);
return;
}
@ -147,10 +147,19 @@ class DownloaderAdapter extends RecyclerView.Adapter<DownloaderAdapter.ViewHolde
@Override
public void onClick(DialogInterface dialog, int which)
{
deleteNode(item);
deleteNode(item, adapter);
}
}).show();
}
private void deleteNode(CountryItem item, DownloaderAdapter adapter)
{
if (adapter.mActivity instanceof MwmActivity)
{
((MwmActivity) adapter.mActivity).closePlacePage();
}
deleteNode(item);
}
},
CANCEL(R.drawable.ic_cancel, R.string.cancel)

View file

@ -6,8 +6,12 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.Toast;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.resource.drawable.GlideDrawable;
import com.bumptech.glide.request.RequestListener;
import com.bumptech.glide.request.target.Target;
import com.mapswithme.maps.R;
import com.mapswithme.maps.base.BaseMwmFragment;
@ -35,8 +39,34 @@ public class FullScreenGalleryFragment extends BaseMwmFragment
if (mImage != null)
{
ImageView imageView = (ImageView) view.findViewById(R.id.iv__image);
final View progress = view.findViewById(R.id.pb__loading_image);
Glide.with(view.getContext())
.load(mImage.getUrl())
.listener(new RequestListener<String, GlideDrawable>()
{
@Override
public boolean onException(Exception e, String model, Target<GlideDrawable> target,
boolean isFirstResource)
{
if (isVisible())
{
progress.setVisibility(View.GONE);
Toast.makeText(getContext(), getString(R.string.download_failed),
Toast.LENGTH_LONG).show();
}
return false;
}
@Override
public boolean onResourceReady(GlideDrawable resource, String model,
Target<GlideDrawable> target, boolean isFromMemoryCache,
boolean isFirstResource)
{
if (isVisible())
progress.setVisibility(View.GONE);
return false;
}
})
.into(imageView);
}
}

View file

@ -2,6 +2,8 @@ package com.mapswithme.maps.widget;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.view.GestureDetectorCompat;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.ScrollView;
@ -48,6 +50,8 @@ public class ObservableScrollView extends ScrollView
private ScrollListener mScrollListener;
private int mPrevScroll;
private boolean mTouched;
@Nullable
private GestureDetectorCompat mGestureDetector;
public ObservableScrollView(Context context)
@ -65,6 +69,17 @@ public class ObservableScrollView extends ScrollView
super(context, attrs, defStyle);
}
/**
* Translate all {@link MotionEvent}s to specified {@link GestureDetectorCompat}
* all consuming flags from GestureDetectorCompat.onTouchEvent are ignored.
*
* @param gestureDetector {@link GestureDetectorCompat} to use.
*/
public void setGestureDetector(@Nullable GestureDetectorCompat gestureDetector)
{
mGestureDetector = gestureDetector;
}
private boolean shouldSkipEvent(MotionEvent ev)
{
return (mTouched &&
@ -83,6 +98,9 @@ public class ObservableScrollView extends ScrollView
@Override
public boolean onTouchEvent(@NonNull MotionEvent ev)
{
if (mGestureDetector != null)
mGestureDetector.onTouchEvent(ev);
if (!super.onTouchEvent(ev))
return false;

View file

@ -10,58 +10,65 @@ import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Interpolator;
import android.widget.ScrollView;
import com.mapswithme.maps.MwmApplication;
import com.mapswithme.maps.R;
import com.mapswithme.maps.bookmarks.data.MapObject;
import com.mapswithme.maps.widget.ObservableScrollView;
import com.mapswithme.maps.widget.placepage.PlacePageView.State;
import com.mapswithme.util.UiUtils;
/**
* Class is responsible for animations of PP(place page) and PPP(place page preview).
*/
public abstract class BasePlacePageAnimationController
public abstract class BasePlacePageAnimationController implements ObservableScrollView.ScrollListener
{
protected static final int DURATION = MwmApplication.get().getResources().getInteger(R.integer.anim_placepage);
protected static final TimeInterpolator INTERPOLATOR = new AccelerateInterpolator();
protected State mState = State.HIDDEN;
private static final int DURATION = MwmApplication.get()
.getResources()
.getInteger(R.integer.anim_placepage);
private static final TimeInterpolator INTERPOLATOR = new AccelerateInterpolator();
State mState = State.HIDDEN;
protected PlacePageView mPlacePage;
protected ViewGroup mPreview;
protected ViewGroup mDetailsFrame;
protected ScrollView mDetailsScroll;
protected View mDetailsContent;
protected ViewGroup mBookmarkDetails;
protected ViewGroup mButtons;
PlacePageView mPlacePage;
ViewGroup mPreview;
ViewGroup mDetailsFrame;
ObservableScrollView mDetailsScroll;
View mDetailsContent;
ViewGroup mBookmarkDetails;
ViewGroup mButtons;
// Gestures
protected GestureDetectorCompat mGestureDetector;
protected boolean mIsDragging;
protected float mDownCoord;
protected float mTouchSlop;
GestureDetectorCompat mGestureDetector;
boolean mIsDragging;
float mDownCoord;
float mTouchSlop;
int mCurrentScrollY;
protected OnVisibilityChangedListener mVisibilityListener;
private OnVisibilityChangedListener mVisibilityListener;
public interface OnVisibilityChangedListener
{
void onPreviewVisibilityChanged(boolean isVisible);
void onPlacePageVisibilityChanged(boolean isVisible);
}
protected OnAnimationListener mProgressListener;
public interface OnAnimationListener
private OnAnimationListener mProgressListener;
interface OnAnimationListener
{
void onProgress(float translationX, float translationY);
}
protected abstract void initGestureDetector();
public BasePlacePageAnimationController(@NonNull PlacePageView placePage)
BasePlacePageAnimationController(@NonNull PlacePageView placePage)
{
mPlacePage = placePage;
mPreview = (ViewGroup) placePage.findViewById(R.id.pp__preview);
mDetailsFrame = (ViewGroup) placePage.findViewById(R.id.pp__details_frame);
mDetailsScroll = (ScrollView) placePage.findViewById(R.id.pp__details);
mDetailsScroll = (ObservableScrollView) placePage.findViewById(R.id.pp__details);
mDetailsScroll.setScrollListener(this);
mDetailsContent = mDetailsScroll.getChildAt(0);
mBookmarkDetails = (ViewGroup) mDetailsFrame.findViewById(R.id.bookmark_frame);
mButtons = (ViewGroup) placePage.findViewById(R.id.pp__buttons);
@ -70,7 +77,8 @@ public abstract class BasePlacePageAnimationController
mTouchSlop = ViewConfiguration.get(mPlacePage.getContext()).getScaledTouchSlop();
if (mPlacePage.isFloating() || mPlacePage.isDocked())
mDetailsFrame.setPadding(mDetailsFrame.getPaddingLeft(), mDetailsFrame.getPaddingTop(), mDetailsFrame.getPaddingRight(),
mDetailsFrame.setPadding(mDetailsFrame.getPaddingLeft(), mDetailsFrame.getPaddingTop(), mDetailsFrame
.getPaddingRight(),
UiUtils.dimen(R.dimen.place_page_buttons_height));
if (mPlacePage.isDocked())
@ -83,6 +91,17 @@ public abstract class BasePlacePageAnimationController
initialVisibility();
}
@Override
public void onScroll(int left, int top)
{
mCurrentScrollY = top;
}
@Override
public void onScrollEnd()
{
}
protected void initialVisibility()
{
UiUtils.invisible(mPlacePage, mPreview, mDetailsFrame, mBookmarkDetails);
@ -93,9 +112,10 @@ public abstract class BasePlacePageAnimationController
return mState;
}
public void setState(final State state, @MapObject.MapObjectType final int type)
void setState(final State state, @MapObject.MapObjectType final int type)
{
mPlacePage.post(new Runnable() {
mPlacePage.post(new Runnable()
{
@Override
public void run()
{
@ -107,7 +127,7 @@ public abstract class BasePlacePageAnimationController
protected abstract void onStateChanged(State currentState, State newState, int type);
public void setOnVisibilityChangedListener(OnVisibilityChangedListener listener)
void setOnVisibilityChangedListener(OnVisibilityChangedListener listener)
{
mVisibilityListener = listener;
}
@ -124,7 +144,7 @@ public abstract class BasePlacePageAnimationController
return mGestureDetector.onTouchEvent(event);
}
protected void notifyVisibilityListener(boolean previewShown, boolean ppShown)
void notifyVisibilityListener(boolean previewShown, boolean ppShown)
{
if (mVisibilityListener != null)
{
@ -133,7 +153,7 @@ public abstract class BasePlacePageAnimationController
}
}
protected void notifyProgress(float translationX, float translationY)
void notifyProgress(float translationX, float translationY)
{
if (mProgressListener == null)
return;
@ -141,12 +161,12 @@ public abstract class BasePlacePageAnimationController
mProgressListener.onProgress(translationX, translationY);
}
protected void startDefaultAnimator(ValueAnimator animator)
void startDefaultAnimator(ValueAnimator animator)
{
startDefaultAnimator(animator, new AccelerateInterpolator());
}
protected void startDefaultAnimator(ValueAnimator animator, Interpolator interpolator)
void startDefaultAnimator(ValueAnimator animator, Interpolator interpolator)
{
animator.setDuration(DURATION);
animator.setInterpolator(interpolator == null ? INTERPOLATOR : interpolator);

View file

@ -6,13 +6,13 @@ import android.annotation.SuppressLint;
import android.support.annotation.NonNull;
import android.support.v4.view.GestureDetectorCompat;
import android.support.v7.widget.Toolbar;
import android.util.DisplayMetrics;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Interpolator;
import android.view.animation.OvershootInterpolator;
import android.widget.LinearLayout;
import com.mapswithme.maps.R;
@ -23,32 +23,21 @@ import com.mapswithme.util.concurrency.UiThread;
class BottomPlacePageAnimationController extends BasePlacePageAnimationController
{
@SuppressWarnings("unused")
private static final String TAG = BottomPlacePageAnimationController.class.getSimpleName();
private static final float DETAIL_RATIO = 0.7f;
private static final float SCROLL_DELTA = 50.0f;
private final ViewGroup mLayoutToolbar;
private final AnimationHelper mAnimationHelper = new AnimationHelper();
private ValueAnimator mCurrentAnimator;
private boolean mIsGestureStartedInsideView;
private boolean mIsGestureFinished;
private class AnimationHelper
{
final View.OnLayoutChangeListener mListener = 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)
{
if (mState == State.DETAILS && v.getId() == mDetailsFrame.getId() && top != oldTop)
{
mPreview.setTranslationY(-mDetailsContent.getHeight());
refreshToolbarVisibility();
}
}
};
}
private float mDetailMaxHeight;
private float mScrollDelta;
public BottomPlacePageAnimationController(@NonNull PlacePageView placePage)
BottomPlacePageAnimationController(@NonNull PlacePageView placePage)
{
super(placePage);
mLayoutToolbar = (LinearLayout) mPlacePage.findViewById(R.id.toolbar_layout);
@ -68,6 +57,11 @@ class BottomPlacePageAnimationController extends BasePlacePageAnimationControlle
}
});
}
DisplayMetrics dm = placePage.getResources().getDisplayMetrics();
float screenHeight = dm.heightPixels;
mDetailMaxHeight = screenHeight * DETAIL_RATIO;
mScrollDelta = SCROLL_DELTA * dm.density;
}
@Override
@ -75,60 +69,32 @@ class BottomPlacePageAnimationController extends BasePlacePageAnimationControlle
{
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
if (!isInsideView(event.getY()))
{
mIsGestureStartedInsideView = false;
case MotionEvent.ACTION_DOWN:
if (!UiUtils.isViewTouched(event, mDetailsScroll))
{
mIsGestureStartedInsideView = false;
break;
}
mIsGestureStartedInsideView = true;
mIsDragging = false;
mIsGestureFinished = false;
mDownCoord = event.getY();
break;
}
case MotionEvent.ACTION_MOVE:
if (!mIsGestureStartedInsideView)
break;
final float delta = mDownCoord - event.getY();
if (Math.abs(delta) > mTouchSlop && !isDetailsScroll(delta))
return true;
mIsGestureStartedInsideView = true;
mIsDragging = false;
mIsGestureFinished = false;
mDownCoord = event.getY();
break;
case MotionEvent.ACTION_MOVE:
if (!mIsGestureStartedInsideView)
break;
final float delta = mDownCoord - event.getY();
if (Math.abs(delta) > mTouchSlop && !isDetailsScroll(mDownCoord, delta))
return true;
break;
}
return false;
}
private boolean isInsideView(float y)
{
return y > mPreview.getY() && y < mButtons.getY();
}
/**
* @return whether gesture is scrolling of details content(and not dragging PP itself).
*/
private boolean isDetailsScroll(float y, float delta)
{
return isOnDetails(y) && isDetailsScrollable() && canScroll(delta);
}
private boolean isOnDetails(float y)
{
return y > mDetailsFrame.getY() && y < mButtons.getY();
}
private boolean isDetailsScrollable()
{
return mDetailsFrame.getHeight() < mDetailsContent.getHeight();
}
private boolean canScroll(float delta)
{
return mDetailsScroll.getScrollY() != 0 || delta > 0;
}
@Override
protected boolean onTouchEvent(@NonNull MotionEvent event)
{
@ -136,14 +102,13 @@ class BottomPlacePageAnimationController extends BasePlacePageAnimationControlle
return false;
final boolean finishedDrag = (mIsDragging &&
(event.getAction() == MotionEvent.ACTION_UP ||
event.getAction() == MotionEvent.ACTION_CANCEL));
if (!mIsGestureStartedInsideView ||
!isInsideView(event.getY()) ||
finishedDrag)
(event.getAction() == MotionEvent.ACTION_UP ||
event.getAction() == MotionEvent.ACTION_CANCEL));
if (!mIsGestureStartedInsideView || !UiUtils.isViewTouched(event, mDetailsScroll)
|| finishedDrag)
{
mIsGestureFinished = true;
finishDrag();
finishDrag(mDownCoord - event.getY());
return false;
}
@ -151,6 +116,41 @@ class BottomPlacePageAnimationController extends BasePlacePageAnimationControlle
return true;
}
@Override
public void onScroll(int left, int top)
{
super.onScroll(left, top);
if (mCurrentScrollY > 0 && mDetailsScroll.getTranslationY() > 0)
{
mPlacePage.setState(State.HIDDEN);
}
refreshToolbarVisibility();
}
/**
* @return whether gesture is scrolling of details content(and not dragging PP itself).
*/
private boolean isDetailsScroll(float delta)
{
return isDetailsScrollable() && canScroll(delta);
}
private boolean isDetailsScrollable()
{
return mPlacePage.getState() == State.FULLSCREEN && isDetailContentScrollable();
}
private boolean isDetailContentScrollable()
{
return mDetailsScroll.getHeight() < mDetailsContent.getHeight();
}
private boolean canScroll(float delta)
{
return mDetailsScroll.getScrollY() != 0 || delta > 0;
}
@Override
protected void initGestureDetector()
{
@ -166,7 +166,18 @@ class BottomPlacePageAnimationController extends BasePlacePageAnimationControlle
if (isVertical)
{
mIsDragging = true;
translateBy(-distanceY);
if (!translateBy(-distanceY))
{
if (mDetailsScroll.getTranslationY() == 0)
{
mDetailsScroll.scrollBy((int) distanceX, (int) distanceY);
mState = State.FULLSCREEN;
}
else
{
mPlacePage.setState(State.HIDDEN);
}
}
}
return true;
@ -175,67 +186,136 @@ class BottomPlacePageAnimationController extends BasePlacePageAnimationControlle
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY)
{
mPlacePage.setState(velocityY > 0 ? State.HIDDEN : State.DETAILS);
return super.onFling(e1, e2, velocityX, velocityY);
finishDrag(-velocityY);
return true;
}
@Override
public boolean onSingleTapConfirmed(MotionEvent e)
{
if (!isInPreview(e.getY()))
return false;
mPlacePage.setState(mPlacePage.getState() == State.PREVIEW ? State.DETAILS
: State.PREVIEW);
MotionEvent evt = MotionEvent.obtain(e.getDownTime(),
e.getEventTime(),
e.getAction(),
e.getX(),
mDownCoord,
e.getMetaState());
if (UiUtils.isViewTouched(evt, mPreview))
{
if (mPlacePage.getState() == State.PREVIEW)
{
if (isDetailContentScrollable())
{
mPlacePage.setState(State.DETAILS);
}
else
{
mPlacePage.setState(State.FULLSCREEN);
}
}
else
{
mPlacePage.setState(State.PREVIEW);
}
}
return true;
}
});
mDetailsScroll.setGestureDetector(mGestureDetector);
}
private boolean isInPreview(float y)
private void finishDrag(float distance)
{
return y > (mPreview.getTop() + mPreview.getTranslationY())
&& y < (mPreview.getBottom() + mPreview.getTranslationY());
}
private void finishDrag()
{
final float currentTranslation = mDetailsFrame.getTranslationY();
final float currentTranslation = mDetailsScroll.getTranslationY();
if (currentTranslation > mDetailsScroll.getHeight())
{
mPlacePage.setState(State.HIDDEN);
return;
}
@SuppressWarnings("UnnecessaryLocalVariable")
final float deltaTop = currentTranslation;
final float deltaBottom = mDetailsContent.getHeight() - currentTranslation;
mPlacePage.setState(deltaBottom > deltaTop ? State.DETAILS
: State.PREVIEW);
if (distance >= 0.0f) // drag up
{
if (mPlacePage.getState() == State.PREVIEW)
{
if (isDetailContentScrollable())
{
mPlacePage.setState(State.DETAILS);
}
else
{
mPlacePage.setState(State.FULLSCREEN);
}
}
else if (mPlacePage.getState() != State.FULLSCREEN)
{
mPlacePage.setState(State.FULLSCREEN);
}
else if (mCurrentScrollY == 0 || mDetailsScroll.getTranslationY() > 0)
{
mPlacePage.setState(State.DETAILS);
}
}
else // drag down
{
if (mPlacePage.getState() == State.FULLSCREEN)
{
if (isDetailContentScrollable())
{
mPlacePage.setState(State.DETAILS);
}
else
{
mPlacePage.setState(State.PREVIEW);
}
}
else if (mPlacePage.getState() == State.DETAILS)
{
mPlacePage.setState(State.PREVIEW);
}
else
{
mPlacePage.setState(State.HIDDEN);
}
}
}
private void translateBy(float distanceY)
private boolean translateBy(float distanceY)
{
final float detailsHeight = mDetailsScroll.getHeight();
float detailsTranslation = mDetailsFrame.getTranslationY() + distanceY;
float previewTranslation = mPreview.getTranslationY() + distanceY;
if (detailsTranslation < 0)
if (mCurrentScrollY > 0)
return false;
if (Math.abs(distanceY) > mScrollDelta)
distanceY = 0.0f;
float detailsTranslation = mDetailsScroll.getTranslationY() + distanceY;
final boolean isScrollable = isDetailContentScrollable();
boolean consumeEvent = true;
final float maxTranslationY = mDetailsScroll.getHeight() - mDetailsContent.getHeight();
if ((isScrollable && detailsTranslation < 0.0f) || detailsTranslation < maxTranslationY)
{
detailsTranslation = 0;
previewTranslation = -detailsHeight;
if (isScrollable)
{
detailsTranslation = 0.0f;
mDetailsScroll.setGestureDetector(null);
}
else
{
detailsTranslation = maxTranslationY;
}
mState = State.FULLSCREEN;
consumeEvent = false;
}
mPreview.setTranslationY(previewTranslation);
mDetailsFrame.setTranslationY(detailsTranslation);
mDetailsScroll.setTranslationY(detailsTranslation);
refreshToolbarVisibility();
return consumeEvent;
}
@Override
protected void onStateChanged(final State currentState, final State newState, @MapObject.MapObjectType int type)
{
prepareYTranslations(currentState, newState, type);
prepareYTranslations(newState, type);
mDetailsScroll.setGestureDetector(mGestureDetector);
mPlacePage.post(new Runnable()
{
@Override
@ -244,79 +324,72 @@ class BottomPlacePageAnimationController extends BasePlacePageAnimationControlle
endRunningAnimation();
switch (newState)
{
case HIDDEN:
hidePlacePage();
break;
case PREVIEW:
showPreview(currentState);
break;
case DETAILS:
showDetails(currentState);
break;
case HIDDEN:
hidePlacePage();
break;
case PREVIEW:
showPreview(currentState);
break;
case DETAILS:
showDetails();
break;
case FULLSCREEN:
if (isDetailContentScrollable())
mDetailsScroll.setGestureDetector(null);
showFullscreen();
break;
}
}
});
}
/**
* Prepares widgets for animating, places them vertically accordingly to their supposed positions.
* Prepares widgets for animating, places them vertically accordingly to their supposed
* positions.
*/
private void prepareYTranslations(State currentState, State newState, @MapObject.MapObjectType int type)
private void prepareYTranslations(State newState, @MapObject.MapObjectType int type)
{
switch (newState)
{
case PREVIEW:
if (mState == State.HIDDEN)
{
UiUtils.invisible(mPlacePage, mPreview, mDetailsFrame, mButtons);
UiUtils.showIf(type == MapObject.BOOKMARK, mBookmarkDetails);
mPlacePage.post(new Runnable()
case PREVIEW:
if (mState == State.HIDDEN)
{
@Override
public void run()
UiUtils.invisible(mPlacePage, mPreview, mDetailsFrame, mButtons);
UiUtils.showIf(type == MapObject.BOOKMARK, mBookmarkDetails);
mPlacePage.post(new Runnable()
{
final float previewTranslation = mPreview.getHeight() + mButtons.getHeight();
mPreview.setTranslationY(previewTranslation);
mDetailsFrame.setTranslationY(mDetailsFrame.getHeight());
mButtons.setTranslationY(previewTranslation);
@Override
public void run()
{
mDetailsScroll.setTranslationY(mDetailsScroll.getHeight());
mButtons.setTranslationY(mPreview.getHeight() + mButtons.getHeight());
UiUtils.show(mPlacePage, mPreview, mButtons, mDetailsFrame);
}
});
}
break;
case DETAILS:
UiUtils.show(mPlacePage, mPreview, mDetailsFrame);
UiUtils.showIf(type == MapObject.BOOKMARK, mBookmarkDetails);
break;
UiUtils.show(mPlacePage, mPreview, mButtons, mDetailsFrame);
}
});
}
break;
case FULLSCREEN:
case DETAILS:
UiUtils.show(mPlacePage, mPreview, mButtons, mDetailsFrame);
UiUtils.showIf(type == MapObject.BOOKMARK, mBookmarkDetails);
break;
}
}
protected void showPreview(final State currentState)
private void showPreview(final State currentState)
{
if (mLayoutToolbar != null)
UiUtils.hide(mLayoutToolbar);
mDetailsFrame.addOnLayoutChangeListener(mAnimationHelper.mListener);
mCurrentAnimator = ValueAnimator.ofFloat(mPreview.getTranslationY(), 0f);
final float detailsHeight = mDetailsFrame.getHeight();
mCurrentAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()
{
@Override
public void onAnimationUpdate(ValueAnimator animation)
{
float translationY = (Float) animation.getAnimatedValue();
mPreview.setTranslationY(translationY);
mDetailsFrame.setTranslationY(translationY + detailsHeight);
notifyProgress();
}
});
Interpolator interpolator;
final float translation = mDetailsScroll.getHeight() - mPreview.getHeight();
mCurrentAnimator = ValueAnimator.ofFloat(mDetailsScroll.getTranslationY(), translation);
mCurrentAnimator.addUpdateListener(new UpdateListener());
Interpolator interpolator = new AccelerateInterpolator();
if (currentState == State.HIDDEN)
{
interpolator = new OvershootInterpolator();
mCurrentAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()
ValueAnimator buttonAnimator = ValueAnimator.ofFloat(mButtons.getTranslationY(), 0);
buttonAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()
{
@Override
public void onAnimationUpdate(ValueAnimator animation)
@ -324,10 +397,7 @@ class BottomPlacePageAnimationController extends BasePlacePageAnimationControlle
mButtons.setTranslationY((Float) animation.getAnimatedValue());
}
});
}
else
{
interpolator = new AccelerateInterpolator();
startDefaultAnimator(buttonAnimator, interpolator);
}
mCurrentAnimator.addListener(new UiUtils.SimpleAnimatorListener()
@ -342,45 +412,48 @@ class BottomPlacePageAnimationController extends BasePlacePageAnimationControlle
startDefaultAnimator(mCurrentAnimator, interpolator);
}
protected void showDetails(final State currentState)
private void showDetails()
{
final float detailsScreenHeight = mDetailsScroll.getHeight();
if (isDetailContentScrollable())
{
mCurrentAnimator = ValueAnimator.ofFloat(mDetailsScroll.getTranslationY(),
mDetailsScroll.getHeight() - mDetailMaxHeight + mButtons
.getHeight());
}
else
{
mCurrentAnimator = ValueAnimator.ofFloat(mDetailsScroll.getTranslationY(),
mDetailsScroll.getHeight() - mDetailsContent.getHeight());
}
mCurrentAnimator.addUpdateListener(new UpdateListener());
mCurrentAnimator.addListener(new AnimationListener());
mCurrentAnimator = ValueAnimator.ofFloat(mPreview.getTranslationY(), -detailsScreenHeight);
mCurrentAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()
startDefaultAnimator();
}
private void showFullscreen()
{
if (isDetailContentScrollable())
{
@Override
public void onAnimationUpdate(ValueAnimator animation)
{
float translationY = (Float) animation.getAnimatedValue();
mPreview.setTranslationY(translationY);
mDetailsFrame.setTranslationY(translationY + detailsScreenHeight);
notifyProgress();
}
});
mCurrentAnimator.addListener(new UiUtils.SimpleAnimatorListener()
mCurrentAnimator = ValueAnimator.ofFloat(mDetailsScroll.getTranslationY(), 0);
}
else
{
@Override
public void onAnimationEnd(Animator animation)
{
refreshToolbarVisibility();
notifyVisibilityListener(true, true);
mDetailsScroll.scrollTo(0, 0);
notifyProgress();
}
});
mCurrentAnimator = ValueAnimator.ofFloat(mDetailsScroll.getTranslationY(),
mDetailsScroll.getHeight() - mDetailsContent.getHeight());
}
mCurrentAnimator.addUpdateListener(new UpdateListener());
mCurrentAnimator.addListener(new AnimationListener());
startDefaultAnimator();
}
@SuppressLint("NewApi")
protected void hidePlacePage()
private void hidePlacePage()
{
if (mLayoutToolbar != null)
UiUtils.hide(mLayoutToolbar);
mDetailsFrame.removeOnLayoutChangeListener(mAnimationHelper.mListener);
final float animHeight = mPlacePage.getHeight() - mPreview.getY();
mCurrentAnimator = ValueAnimator.ofFloat(0f, animHeight);
mCurrentAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()
@ -423,7 +496,7 @@ class BottomPlacePageAnimationController extends BasePlacePageAnimationControlle
}
}
protected void refreshToolbarVisibility()
private void refreshToolbarVisibility()
{
if (mLayoutToolbar != null)
UiThread.runLater(new Runnable()
@ -431,7 +504,7 @@ class BottomPlacePageAnimationController extends BasePlacePageAnimationControlle
@Override
public void run()
{
UiUtils.showIf(mPreview.getY() < 0, mLayoutToolbar);
UiUtils.showIf(mCurrentScrollY > 0, mLayoutToolbar);
}
});
}
@ -440,4 +513,27 @@ class BottomPlacePageAnimationController extends BasePlacePageAnimationControlle
{
notifyProgress(0, mPreview.getTranslationY());
}
private class UpdateListener implements ValueAnimator.AnimatorUpdateListener
{
@Override
public void onAnimationUpdate(ValueAnimator animation)
{
float translationY = (Float) animation.getAnimatedValue();
mDetailsScroll.setTranslationY(translationY);
notifyProgress();
}
}
private class AnimationListener extends UiUtils.SimpleAnimatorListener
{
@Override
public void onAnimationEnd(Animator animation)
{
refreshToolbarVisibility();
notifyVisibilityListener(true, true);
mDetailsScroll.scrollTo(0, 0);
notifyProgress();
}
}
}

View file

@ -22,7 +22,6 @@ import com.mapswithme.maps.bookmarks.ChooseBookmarkCategoryFragment.Listener;
import com.mapswithme.maps.bookmarks.data.Bookmark;
import com.mapswithme.maps.bookmarks.data.BookmarkManager;
import com.mapswithme.maps.bookmarks.data.Icon;
import com.mapswithme.util.InputUtils;
import com.mapswithme.util.UiUtils;
import com.mapswithme.util.statistics.Statistics;
@ -171,11 +170,11 @@ public class EditBookmarkFragment extends BaseMwmDialogFragment implements View.
private void refreshBookmark()
{
mEtName.setText(mBookmark.getTitle());
mEtName.selectAll();
InputUtils.showKeyboard(mEtName);
if (TextUtils.isEmpty(mEtName.getText()))
mEtName.setText(mBookmark.getTitle());
mEtDescription.setText(mBookmark.getBookmarkDescription());
if (TextUtils.isEmpty(mEtDescription.getText()))
mEtDescription.setText(mBookmark.getBookmarkDescription());
mTvBookmarkGroup.setText(mBookmark.getCategoryName());
refreshColorMarker();
}

View file

@ -96,6 +96,7 @@ class LeftPlacePageAnimationController extends BasePlacePageAnimationController
case HIDDEN:
hidePlacePage();
break;
case FULLSCREEN:
case DETAILS:
case PREVIEW:
showPlacePage(currentState);

View file

@ -20,6 +20,7 @@ import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.ForegroundColorSpan;
import android.text.util.Linkify;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.Menu;
@ -96,7 +97,7 @@ public class PlacePageView extends RelativeLayout
private static final String PREF_USE_DMS = "use_dms";
//TODO: remove this after booking_api.cpp will be done
private static final boolean USE_OLD_BOOKING = true;
private static final boolean USE_OLD_BOOKING = false;
private boolean mIsDocked;
private boolean mIsFloating;
@ -218,7 +219,8 @@ public class PlacePageView extends RelativeLayout
{
HIDDEN,
PREVIEW,
DETAILS
DETAILS,
FULLSCREEN
}
public PlacePageView(Context context)
@ -976,6 +978,7 @@ public class PlacePageView extends RelativeLayout
mBookmarkSet = true;
UiUtils.show(mBookmarkFrame);
UiUtils.setTextAndHideIfEmpty(mBookmarkNote, ((Bookmark) mMapObject).getBookmarkDescription());
Linkify.addLinks(mBookmarkNote, Linkify.ALL);
updateButtons();
}
@ -1209,7 +1212,7 @@ public class PlacePageView extends RelativeLayout
@Override
public void run()
{
setState(State.DETAILS);
setState(mBookmarkSet ? State.DETAILS : State.PREVIEW);
}
});
}
@ -1316,7 +1319,7 @@ public class PlacePageView extends RelativeLayout
if (mIsDocked || mIsFloating)
return false;
if (getState() == State.DETAILS)
if (getState() == State.DETAILS || getState() == State.FULLSCREEN)
{
hide();
return true;