diff options
author | alexzatsepin <alexander.zatzepin@gmail.com> | 2016-10-07 17:28:28 +0300 |
---|---|---|
committer | alexzatsepin <alexander.zatzepin@gmail.com> | 2016-10-12 11:17:02 +0300 |
commit | c1c839a308b37e149a9b72a2f9b6d2ce2fdfccd6 (patch) | |
tree | ae9d13b7aaf2132b2ca09e29d95b3425571a58a0 | |
parent | b624f9f227a25e0720f2a453b29edc0b80f5c259 (diff) |
[android] Implemented Uber feature
1. Made a Uber panel layout
2. Implemented the 'Order' button allowing to order the Uber by launching the Uber app or directing to the Google Play
32 files changed, 715 insertions, 108 deletions
diff --git a/.gitignore b/.gitignore index 02e6ae4ce7..38baa9e9eb 100644 --- a/.gitignore +++ b/.gitignore @@ -133,3 +133,7 @@ android/fabric.properties android/pushwoosh.properties android/res/values/google-service.xml server + +*.li + +*.autosave diff --git a/android/jni/com/mapswithme/maps/Framework.cpp b/android/jni/com/mapswithme/maps/Framework.cpp index 361dba42b5..7557f82d8a 100644 --- a/android/jni/com/mapswithme/maps/Framework.cpp +++ b/android/jni/com/mapswithme/maps/Framework.cpp @@ -513,6 +513,17 @@ void Framework::EnableDownloadOn3g() m_work.GetDownloadingPolicy().EnableCellularDownload(true); } +void Framework::RequestUberProducts(ms::LatLon const & from, ms::LatLon const & to, + uber::ProductsCallback const & callback) +{ + m_work.GetUberApi().GetAvailableProducts(from, to, callback); +} + +uber::RideRequestLinks Framework::GetUberLinks(string const & productId, ms::LatLon const & from, ms::LatLon const & to) +{ + return uber::Api::GetRideRequestLinks(productId, from, to); +} + } // namespace android @@ -1183,4 +1194,64 @@ Java_com_mapswithme_maps_Framework_nativeSetVisibleRect(JNIEnv * env, jclass, ji frm()->SetVisibleViewport(m2::RectD(left, top, right, bottom)); } -} // extern "C" +JNIEXPORT void JNICALL +Java_com_mapswithme_maps_Framework_nativeRequestUberProducts(JNIEnv * env, jclass, jdouble srcLat, jdouble srcLon, + jdouble dstLat, jdouble dstLon) +{ + ms::LatLon const from(srcLat, srcLon); + ms::LatLon const to(dstLat, dstLon); + + g_framework->RequestUberProducts(from, to, [](vector<uber::Product> const & products, size_t const requestId) + { + GetPlatform().RunOnGuiThread([=]() + { + JNIEnv * env = jni::GetEnv(); + jclass const productClass = env->FindClass("com/mapswithme/maps/api/uber/UberInfo$Product"); + jmethodID const productConstructor = jni::GetConstructorID(env, productClass, + "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); + + auto uberProducts = jni::ToJavaArray(env, productClass, products, + [productClass, productConstructor](JNIEnv * env, uber::Product const & item) + { + return env->NewObject(productClass, productConstructor, + jni::ToJavaString(env, item.m_productId), + jni::ToJavaString(env, item.m_name), + jni::ToJavaString(env, item.m_time), + jni::ToJavaString(env, item.m_price)); + }); + + jclass const routingControllerClass = env->FindClass("com/mapswithme/maps/routing/RoutingController"); + jmethodID const routingControllerGetMethod = jni::GetStaticMethodID(env, routingControllerClass, "get", + "()Lcom/mapswithme/maps/routing/RoutingController;"); + jobject const routingControllerInstance = env->CallStaticObjectMethod(routingControllerClass, routingControllerGetMethod); + + jmethodID const uberInfoCallbackMethod = jni::GetMethodID(env, routingControllerInstance, "onUberInfoReceived", + "(Lcom/mapswithme/maps/api/uber/UberInfo;)V"); + + jclass const uberInfoClass = env->FindClass("com/mapswithme/maps/api/uber/UberInfo"); + jmethodID const uberInfoConstructor = jni::GetConstructorID(env, uberInfoClass, + "([Lcom/mapswithme/maps/api/uber/UberInfo$Product;)V"); + + env->CallVoidMethod(routingControllerInstance, uberInfoCallbackMethod, env->NewObject(uberInfoClass, + uberInfoConstructor, uberProducts)); + }); + }); +} + +JNIEXPORT jobject JNICALL +Java_com_mapswithme_maps_Framework_nativeGetUberLinks(JNIEnv * env, jclass, jstring productId, jdouble srcLat, + jdouble srcLon, jdouble dstLat, jdouble dstLon) +{ + ms::LatLon const from(srcLat, srcLon); + ms::LatLon const to(dstLat, dstLon); + + uber::RideRequestLinks const links = android::Framework::GetUberLinks(jni::ToNativeString(env, productId), from, to); + + jclass const uberLinksClass = env->FindClass("com/mapswithme/maps/api/uber/UberLinks"); + jmethodID const uberLinksConstructor = jni::GetConstructorID(env, uberLinksClass, + "(Ljava/lang/String;Ljava/lang/String;)V"); + return env->NewObject(uberLinksClass, uberLinksConstructor, jni::ToJavaString(env, links.m_deepLink), + jni::ToJavaString(env, links.m_universalLink)); +} +}// extern "C" + diff --git a/android/jni/com/mapswithme/maps/Framework.hpp b/android/jni/com/mapswithme/maps/Framework.hpp index 12fcdcd85d..09f7d9ba8b 100644 --- a/android/jni/com/mapswithme/maps/Framework.hpp +++ b/android/jni/com/mapswithme/maps/Framework.hpp @@ -166,6 +166,9 @@ namespace android bool IsAutoRetryDownloadFailed(); bool IsDownloadOn3gEnabled(); void EnableDownloadOn3g(); + + void RequestUberProducts(ms::LatLon const & from, ms::LatLon const & to, uber::ProductsCallback const & callback); + static uber::RideRequestLinks GetUberLinks(string const & productId, ms::LatLon const & from, ms::LatLon const & to); }; } diff --git a/android/jni/com/mapswithme/maps/SponsoredHotel.cpp b/android/jni/com/mapswithme/maps/SponsoredHotel.cpp index c420bc4320..986709c2ec 100644 --- a/android/jni/com/mapswithme/maps/SponsoredHotel.cpp +++ b/android/jni/com/mapswithme/maps/SponsoredHotel.cpp @@ -2,9 +2,7 @@ #include "../core/jni_helper.hpp" #include "../platform/Platform.hpp" - #include "map/place_page_info.hpp" - #include "partners_api/booking_api.hpp" #include "std/bind.hpp" diff --git a/android/res/drawable-hdpi/ic_logo_uber.png b/android/res/drawable-hdpi/ic_logo_uber.png Binary files differnew file mode 100644 index 0000000000..157cd1659f --- /dev/null +++ b/android/res/drawable-hdpi/ic_logo_uber.png diff --git a/android/res/drawable-hdpi/ic_taxi.png b/android/res/drawable-hdpi/ic_taxi.png Binary files differnew file mode 100644 index 0000000000..3c5996a596 --- /dev/null +++ b/android/res/drawable-hdpi/ic_taxi.png diff --git a/android/res/drawable-mdpi/ic_logo_uber.png b/android/res/drawable-mdpi/ic_logo_uber.png Binary files differnew file mode 100644 index 0000000000..5d0eee863f --- /dev/null +++ b/android/res/drawable-mdpi/ic_logo_uber.png diff --git a/android/res/drawable-mdpi/ic_taxi.png b/android/res/drawable-mdpi/ic_taxi.png Binary files differnew file mode 100644 index 0000000000..aa8d167fba --- /dev/null +++ b/android/res/drawable-mdpi/ic_taxi.png diff --git a/android/res/drawable-xhdpi/ic_logo_uber.png b/android/res/drawable-xhdpi/ic_logo_uber.png Binary files differnew file mode 100644 index 0000000000..9f0ddb7ccd --- /dev/null +++ b/android/res/drawable-xhdpi/ic_logo_uber.png diff --git a/android/res/drawable-xhdpi/ic_taxi.png b/android/res/drawable-xhdpi/ic_taxi.png Binary files differnew file mode 100644 index 0000000000..0f7f9e0d66 --- /dev/null +++ b/android/res/drawable-xhdpi/ic_taxi.png diff --git a/android/res/drawable-xxhdpi/ic_logo_uber.png b/android/res/drawable-xxhdpi/ic_logo_uber.png Binary files differnew file mode 100644 index 0000000000..32e0674db7 --- /dev/null +++ b/android/res/drawable-xxhdpi/ic_logo_uber.png diff --git a/android/res/drawable-xxhdpi/ic_taxi.png b/android/res/drawable-xxhdpi/ic_taxi.png Binary files differnew file mode 100644 index 0000000000..af67db7f08 --- /dev/null +++ b/android/res/drawable-xxhdpi/ic_taxi.png diff --git a/android/res/drawable-xxxhdpi/ic_logo_uber.png b/android/res/drawable-xxxhdpi/ic_logo_uber.png Binary files differnew file mode 100644 index 0000000000..5570496517 --- /dev/null +++ b/android/res/drawable-xxxhdpi/ic_logo_uber.png diff --git a/android/res/drawable-xxxhdpi/ic_taxi.png b/android/res/drawable-xxxhdpi/ic_taxi.png Binary files differnew file mode 100644 index 0000000000..a809111fef --- /dev/null +++ b/android/res/drawable-xxxhdpi/ic_taxi.png diff --git a/android/res/layout/menu_route_plan_line.xml b/android/res/layout/menu_route_plan_line.xml index a778073571..8e632d8a79 100644 --- a/android/res/layout/menu_route_plan_line.xml +++ b/android/res/layout/menu_route_plan_line.xml @@ -6,7 +6,10 @@ android:layout_width="match_parent" android:layout_height="wrap_content"> - <include layout="@layout/altitude_chart_panel"/> + <include layout="@layout/altitude_chart_panel" + tools:visibility="gone"/> + + <include layout="@layout/uber_panel"/> <include layout="@layout/start_button"/> </LinearLayout>
\ No newline at end of file diff --git a/android/res/layout/routing_plan.xml b/android/res/layout/routing_plan.xml index 118b4e9d96..3b9fc31da4 100644 --- a/android/res/layout/routing_plan.xml +++ b/android/res/layout/routing_plan.xml @@ -73,9 +73,17 @@ style="@style/MwmWidget.ProgressWheel.RoutingPlan" android:layout_marginTop="@dimen/routing_selector_wheel_margin" android:layout_marginBottom="@dimen/routing_selector_wheel_margin" - android:layout_marginRight="@dimen/routing_selector_wheel_margin" + android:layout_marginRight="12dp" tools:visibility="visible" /> + <com.mapswithme.maps.widget.WheelProgressView + android:id="@+id/progress_taxi" + style="@style/MwmWidget.ProgressWheel.RoutingPlan" + android:layout_marginTop="@dimen/routing_selector_wheel_margin" + android:layout_marginBottom="@dimen/routing_selector_wheel_margin" + android:layout_marginRight="@dimen/routing_selector_wheel_margin" + tools:visibility="visible" /> + </LinearLayout> <RadioGroup @@ -106,8 +114,16 @@ android:id="@+id/pedestrian" android:layout_width="@dimen/routing_selector_size" android:layout_height="@dimen/routing_selector_size" + android:layout_marginRight="@dimen/routing_selector_wheel_margin" tools:button="@drawable/ic_walk" tools:buttonTint="?iconTintLight" /> + + <RadioButton + android:id="@+id/taxi" + android:layout_width="@dimen/routing_selector_size" + android:layout_height="@dimen/routing_selector_size" + tools:button="@drawable/ic_route_type_taxi" + tools:buttonTint="?iconTintLight" /> </RadioGroup> <ImageView diff --git a/android/res/layout/uber_pager_item.xml b/android/res/layout/uber_pager_item.xml new file mode 100644 index 0000000000..a7b3c99676 --- /dev/null +++ b/android/res/layout/uber_pager_item.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <TextView + android:id="@+id/product_name" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + style="@style/MwmTextAppearance.RoutingDetail" + tools:text="UberX"/> + + <TextView + android:id="@+id/arrival_time_price" + android:layout_marginTop="@dimen/margin_quarter" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + style="@style/MwmTextAppearance.Body3" + android:maxLines="2" + tools:text="Ожидание 10 мин • 350-400 \u20BD"/> +</LinearLayout> diff --git a/android/res/layout/uber_panel.xml b/android/res/layout/uber_panel.xml new file mode 100644 index 0000000000..05b9b6a6ea --- /dev/null +++ b/android/res/layout/uber_panel.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="utf-8"?> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/uber_panel" + android:paddingLeft="@dimen/margin_base" + android:paddingStart="@dimen/margin_base" + android:paddingEnd="@dimen/margin_base" + android:paddingRight="@dimen/margin_base" + android:paddingTop="@dimen/margin_base" + android:paddingBottom="@dimen/margin_half" + android:layout_width="0dp" + android:layout_weight="1" + android:layout_height="wrap_content"> + <ImageView + android:id="@+id/icon" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:background="@drawable/ic_logo_uber" + android:layout_alignParentLeft="true" + android:layout_alignParentStart="true" + android:layout_alignParentTop="true"/> + + <android.support.v4.view.ViewPager + android:id="@+id/pager" + android:layout_marginStart="@dimen/margin_double" + android:layout_marginLeft="@dimen/margin_double" + android:layout_toRightOf="@id/icon" + android:layout_toEndOf="@id/icon" + android:layout_width="wrap_content" + android:layout_height="64dp"/> + + <LinearLayout android:id="@+id/indicator" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:layout_centerHorizontal="true" + android:layout_below="@id/pager" + android:layout_marginTop="@dimen/margin_half" + tools:background="?secondary" + tools:layout_height="4dp" + tools:layout_width="16dp"/> +</RelativeLayout> diff --git a/android/src/com/mapswithme/maps/Framework.java b/android/src/com/mapswithme/maps/Framework.java index 016dcd5bd5..438b52ac02 100644 --- a/android/src/com/mapswithme/maps/Framework.java +++ b/android/src/com/mapswithme/maps/Framework.java @@ -12,6 +12,7 @@ import java.lang.annotation.RetentionPolicy; import com.mapswithme.maps.api.ParsedRoutingData; import com.mapswithme.maps.api.ParsedUrlMwmRequest; +import com.mapswithme.maps.api.uber.UberLinks; import com.mapswithme.maps.bookmarks.data.DistanceAndAzimut; import com.mapswithme.maps.bookmarks.data.MapObject; import com.mapswithme.maps.routing.RoutingInfo; @@ -28,12 +29,14 @@ public class Framework public static final int MAP_STYLE_CLEAR = 2; @Retention(RetentionPolicy.SOURCE) - @IntDef({ROUTER_TYPE_VEHICLE, ROUTER_TYPE_PEDESTRIAN, ROUTER_TYPE_BICYCLE}) + @IntDef({ROUTER_TYPE_VEHICLE, ROUTER_TYPE_PEDESTRIAN, ROUTER_TYPE_BICYCLE, ROUTER_TYPE_TAXI}) + public @interface RouterType {} public static final int ROUTER_TYPE_VEHICLE = 0; public static final int ROUTER_TYPE_PEDESTRIAN = 1; public static final int ROUTER_TYPE_BICYCLE = 2; + public static final int ROUTER_TYPE_TAXI = 3; @SuppressWarnings("unused") public interface MapObjectListener @@ -258,4 +261,10 @@ public class Framework public static native String nativeGetActiveObjectFormattedCuisine(); public static native void nativeSetVisibleRect(int left, int top, int right, int bottom); + + public static native void nativeRequestUberProducts(double srcLat, double srcLon, double dstLat, double dstLon); + + @NonNull + public static native UberLinks nativeGetUberLinks(@NonNull String productId, double srcLon, double srcLat, + double dstLat, double dstLon); } diff --git a/android/src/com/mapswithme/maps/MwmActivity.java b/android/src/com/mapswithme/maps/MwmActivity.java index 9313971013..ca87b27ed3 100644 --- a/android/src/com/mapswithme/maps/MwmActivity.java +++ b/android/src/com/mapswithme/maps/MwmActivity.java @@ -28,6 +28,7 @@ import com.mapswithme.maps.api.ParsedMwmRequest; import com.mapswithme.maps.api.ParsedRoutingData; import com.mapswithme.maps.api.ParsedUrlMwmRequest; import com.mapswithme.maps.api.RoutePoint; +import com.mapswithme.maps.api.uber.UberInfo; import com.mapswithme.maps.base.BaseMwmFragmentActivity; import com.mapswithme.maps.base.OnBackPressListener; import com.mapswithme.maps.bookmarks.BookmarkCategoriesActivity; @@ -904,8 +905,6 @@ public class MwmActivity extends BaseMwmFragmentActivity { super.onStart(); RoutingController.get().attach(this); - if (!mIsFragmentContainer) - mRoutingPlanInplaceController.setStartButton(); if (MapFragment.nativeIsEngineCreated()) LocationHelper.INSTANCE.attach(this); @@ -1406,17 +1405,21 @@ public class MwmActivity extends BaseMwmFragmentActivity } @Override - public void onRouteBuilt(@Framework.RouterType int router) + public void onRouteBuilt() { if (mIsFragmentContainer) { RoutingPlanFragment fragment = (RoutingPlanFragment) getFragment(RoutingPlanFragment.class); if (fragment != null) - fragment.showRouteAltitudeChart(router != Framework.ROUTER_TYPE_VEHICLE); + { + fragment.showRouteAltitudeChart(); + fragment.setStartButton(); + } } else { - mRoutingPlanInplaceController.showRouteAltitudeChart(router != Framework.ROUTER_TYPE_VEHICLE); + mRoutingPlanInplaceController.showRouteAltitudeChart(); + mRoutingPlanInplaceController.setStartButton(); } } @@ -1435,6 +1438,12 @@ public class MwmActivity extends BaseMwmFragmentActivity } } + @Override + public void onUberInfoReceived(@NonNull UberInfo info) + { + mRoutingPlanInplaceController.showUberInfo(info); + } + boolean isFirstStart() { boolean res = mFirstStart; diff --git a/android/src/com/mapswithme/maps/api/uber/UberInfo.java b/android/src/com/mapswithme/maps/api/uber/UberInfo.java new file mode 100644 index 0000000000..e5ed494740 --- /dev/null +++ b/android/src/com/mapswithme/maps/api/uber/UberInfo.java @@ -0,0 +1,87 @@ +package com.mapswithme.maps.api.uber; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import java.util.Arrays; + +public class UberInfo +{ + @Nullable + private final Product[] mProducts; + + public UberInfo(@Nullable Product[] products) + { + mProducts = products; + } + + @Nullable + public Product[] getProducts() + { + return mProducts; + } + + @Override + public String toString() + { + return "UberInfo{" + + "mProducts=" + Arrays.toString(mProducts) + + '}'; + } + + public static class Product + { + @NonNull + private final String mProductId; + @NonNull + private final String mName; + @NonNull + private final String mTime; + @NonNull + private final String mPrice; + + public Product(@NonNull String productId, @NonNull String name, @NonNull String time, @NonNull String price) + { + mProductId = productId; + mName = name; + mTime = time; + mPrice = price; + } + + @NonNull + public String getProductId() + { + return mProductId; + } + + @NonNull + public String getName() + { + return mName; + } + + @NonNull + public String getTime() + { + return mTime; + } + + @NonNull + public String getPrice() + { + return mPrice; + } + + @Override + public String toString() + { + return "Product{" + + "mProductId='" + mProductId + '\'' + + ", mName='" + mName + '\'' + + ", mTime='" + mTime + '\'' + + ", mPrice='" + mPrice + '\'' + + '}'; + } + } +} + diff --git a/android/src/com/mapswithme/maps/api/uber/UberLinks.java b/android/src/com/mapswithme/maps/api/uber/UberLinks.java new file mode 100644 index 0000000000..3b83b5ed84 --- /dev/null +++ b/android/src/com/mapswithme/maps/api/uber/UberLinks.java @@ -0,0 +1,29 @@ +package com.mapswithme.maps.api.uber; + +import android.support.annotation.NonNull; + +public class UberLinks +{ + @NonNull + private final String mDeepLink; + @NonNull + private final String mUniversalLink; + + public UberLinks(@NonNull String deepLink, @NonNull String universalLink) + { + mDeepLink = deepLink; + mUniversalLink = universalLink; + } + + @NonNull + public String getDeepLink() + { + return mDeepLink; + } + + @NonNull + public String getUniversalLink() + { + return mUniversalLink; + } +} diff --git a/android/src/com/mapswithme/maps/routing/RoutingController.java b/android/src/com/mapswithme/maps/routing/RoutingController.java index 4dc809cfd4..c4406dedaa 100644 --- a/android/src/com/mapswithme/maps/routing/RoutingController.java +++ b/android/src/com/mapswithme/maps/routing/RoutingController.java @@ -5,25 +5,26 @@ import android.content.Context; import android.content.DialogInterface; import android.support.annotation.DimenRes; import android.support.annotation.IntRange; +import android.support.annotation.MainThread; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.app.FragmentActivity; import android.support.v7.app.AlertDialog; import android.text.TextUtils; import android.view.View; -import android.widget.Button; import android.widget.TextView; import com.mapswithme.maps.Framework; import com.mapswithme.maps.MwmApplication; import com.mapswithme.maps.R; +import com.mapswithme.maps.api.uber.UberInfo; +import com.mapswithme.maps.api.uber.UberLinks; import com.mapswithme.maps.bookmarks.data.MapObject; import com.mapswithme.maps.downloader.MapManager; import com.mapswithme.maps.location.LocationHelper; import com.mapswithme.util.Config; import com.mapswithme.util.StringUtils; import com.mapswithme.util.ThemeSwitcher; -import com.mapswithme.util.UiUtils; import com.mapswithme.util.Utils; import com.mapswithme.util.concurrency.UiThread; import com.mapswithme.util.log.DebugLogger; @@ -63,7 +64,8 @@ public class RoutingController void showDownloader(boolean openDownloaded); void updateMenu(); void updatePoints(); - void onRouteBuilt(@Framework.RouterType int router); + void onRouteBuilt(); + void onUberInfoReceived(@NonNull UberInfo info); /** * @param progress progress to be displayed. @@ -72,10 +74,9 @@ public class RoutingController } private static final RoutingController sInstance = new RoutingController(); - private final Logger mLogger = new DebugLogger("RCSTATE"); - + private static final Logger mLogger = new DebugLogger("RCSTATE"); + @Nullable private Container mContainer; - private Button mStartButton; private BuildState mBuildState = BuildState.NONE; private State mState = State.NONE; @@ -118,7 +119,7 @@ public class RoutingController setBuildState(BuildState.BUILT); mLastBuildProgress = 100; if (mContainer != null) - mContainer.onRouteBuilt(mLastRouterType); + mContainer.onRouteBuilt(); } processRoutingEvent(); @@ -227,7 +228,6 @@ public class RoutingController public void detach() { mContainer = null; - mStartButton = null; } public void restore() @@ -400,25 +400,6 @@ public class RoutingController private void updatePlan() { updateProgress(); - updateStartButton(); - } - - private void updateStartButton() - { - mLogger.d("updateStartButton" + (mStartButton == null ? ": SKIP" : "")); - - if (mStartButton == null) - return; - - mStartButton.setEnabled(mState == State.PREPARE && mBuildState == BuildState.BUILT); - UiUtils.updateAccentButton(mStartButton); - } - - void setStartButton(@Nullable Button button) - { - mLogger.d("setStartButton"); - mStartButton = button; - updateStartButton(); } private void cancelInternal() @@ -767,4 +748,31 @@ public class RoutingController return true; } + + void requestUberInfo() + { + MapObject start = RoutingController.get().getStartPoint(); + MapObject end = RoutingController.get().getEndPoint(); + Framework.nativeRequestUberProducts(start.getLat(), start.getLon(), end.getLat(), end.getLon()); + } + + @NonNull + UberLinks getUberLink(@NonNull String productId) + { + MapObject start = RoutingController.get().getStartPoint(); + MapObject end = RoutingController.get().getEndPoint(); + return Framework.nativeGetUberLinks(productId, start.getLat(), start.getLon(), end.getLat(), end.getLon()); + } + + /** + * Called from the native code + * @param info this object contains information about Uber products + */ + @MainThread + private void onUberInfoReceived(@NonNull UberInfo info) + { + mLogger.d("onUberInfoReceived uberInfo = " + info); + if (mContainer != null) + mContainer.onUberInfoReceived(info); + } } diff --git a/android/src/com/mapswithme/maps/routing/RoutingPlanController.java b/android/src/com/mapswithme/maps/routing/RoutingPlanController.java index 46b354457a..79667a63aa 100644 --- a/android/src/com/mapswithme/maps/routing/RoutingPlanController.java +++ b/android/src/com/mapswithme/maps/routing/RoutingPlanController.java @@ -7,22 +7,32 @@ import android.os.Bundle; import android.support.annotation.DrawableRes; import android.support.annotation.IdRes; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.view.PagerAdapter; +import android.support.v4.view.ViewPager; import android.view.View; import android.view.ViewGroup; +import android.widget.Button; import android.widget.CompoundButton; import android.widget.ImageView; import android.widget.RadioButton; import android.widget.RadioGroup; import android.widget.TextView; +import android.widget.Toast; import com.mapswithme.maps.Framework; import com.mapswithme.maps.MwmApplication; import com.mapswithme.maps.R; +import com.mapswithme.maps.api.uber.UberInfo; +import com.mapswithme.maps.api.uber.UberLinks; +import com.mapswithme.maps.uber.UberAdapter; +import com.mapswithme.maps.widget.DotPager; import com.mapswithme.maps.widget.RotateDrawable; import com.mapswithme.maps.widget.ToolbarController; import com.mapswithme.maps.widget.WheelProgressView; import com.mapswithme.util.Graphics; import com.mapswithme.util.UiUtils; +import com.mapswithme.util.Utils; import com.mapswithme.util.statistics.AlohaHelper; import com.mapswithme.util.statistics.Statistics; @@ -38,7 +48,9 @@ public class RoutingPlanController extends ToolbarController private final WheelProgressView mProgressVehicle; private final WheelProgressView mProgressPedestrian; private final WheelProgressView mProgressBicycle; + private final WheelProgressView mProgressTaxi; private final View mAltitudeChartFrame; + private final View mUberFrame; private final RotateDrawable mToggleImage = new RotateDrawable(R.drawable.ic_down); private int mFrameHeight; @@ -46,6 +58,9 @@ public class RoutingPlanController extends ToolbarController private boolean mOpen; private boolean mAltitudeChartShown; + @Nullable + private UberInfo.Product mUberProduct; + private RadioButton setupRouterButton(@IdRes int buttonId, final @DrawableRes int iconRes, View.OnClickListener clickListener) { CompoundButton.OnCheckedChangeListener listener = new CompoundButton.OnCheckedChangeListener() @@ -66,7 +81,7 @@ public class RoutingPlanController extends ToolbarController return rb; } - public RoutingPlanController(View root, Activity activity) + RoutingPlanController(View root, Activity activity) { super(root, activity); mFrame = root; @@ -108,10 +123,23 @@ public class RoutingPlanController extends ToolbarController } }); + setupRouterButton(R.id.taxi, R.drawable.ic_taxi, new View.OnClickListener() + { + @Override + public void onClick(View v) + { + RoutingController.get().requestUberInfo(); + AlohaHelper.logClick(AlohaHelper.ROUTING_TAXI_SET); + Statistics.INSTANCE.trackEvent(Statistics.EventName.ROUTING_TAXI_SET); + RoutingController.get().setRouterType(Framework.ROUTER_TYPE_TAXI); + } + }); + View progressFrame = mToolbar.findViewById(R.id.progress_frame); mProgressVehicle = (WheelProgressView) progressFrame.findViewById(R.id.progress_vehicle); mProgressPedestrian = (WheelProgressView) progressFrame.findViewById(R.id.progress_pedestrian); mProgressBicycle = (WheelProgressView) progressFrame.findViewById(R.id.progress_bicycle); + mProgressTaxi = (WheelProgressView) progressFrame.findViewById(R.id.progress_taxi); View altitudeChartFrame = mFrame.findViewById(R.id.altitude_chart_panel); if (altitudeChartFrame == null) @@ -120,6 +148,13 @@ public class RoutingPlanController extends ToolbarController mAltitudeChartFrame = altitudeChartFrame; UiUtils.hide(mAltitudeChartFrame); + View uberFrame = mFrame.findViewById(R.id.uber_panel); + if (uberFrame == null) + uberFrame = mActivity.findViewById(R.id.uber_panel); + + mUberFrame = uberFrame; + UiUtils.hide(mUberFrame); + mToggle.setImageDrawable(mToggleImage); mToggle.setOnClickListener(new View.OnClickListener() { @@ -178,6 +213,10 @@ public class RoutingPlanController extends ToolbarController private void showAltitudeChartAndRoutingDetails() { + if (isTaxiRouteChecked()) + return; + + UiUtils.hide(mUberFrame); UiUtils.show(mAltitudeChartFrame); mAltitudeChartShown = true; showRoutingDetails(); @@ -219,7 +258,7 @@ public class RoutingPlanController extends ToolbarController public void updateBuildProgress(int progress, @Framework.RouterType int router) { updateProgressLabels(); - UiUtils.invisible(mProgressVehicle, mProgressPedestrian, mProgressBicycle); + UiUtils.invisible(mProgressVehicle, mProgressPedestrian, mProgressBicycle, mProgressTaxi); WheelProgressView progressView; if (router == Framework.ROUTER_TYPE_VEHICLE) { @@ -231,6 +270,11 @@ public class RoutingPlanController extends ToolbarController mRouterTypes.check(R.id.pedestrian); progressView = mProgressPedestrian; } + else if (router == Framework.ROUTER_TYPE_TAXI) + { + mRouterTypes.check(R.id.taxi); + progressView = mProgressTaxi; + } else { mRouterTypes.check(R.id.bicycle); @@ -253,7 +297,7 @@ public class RoutingPlanController extends ToolbarController showSlots(!mOpen, true); } - protected void showSlots(final boolean show, final boolean animate) + void showSlots(final boolean show, final boolean animate) { if (!checkFrameHeight()) { @@ -296,12 +340,17 @@ public class RoutingPlanController extends ToolbarController } } - protected boolean isVehicleRouteChecked() + private boolean isVehicleRouteChecked() { return mRouterTypes.getCheckedRadioButtonId() == R.id.vehicle; } - public void disableToggle() + private boolean isTaxiRouteChecked() + { + return mRouterTypes.getCheckedRadioButtonId() == R.id.taxi; + } + + void disableToggle() { UiUtils.hide(mToggle); showSlots(true, false); @@ -312,15 +361,15 @@ public class RoutingPlanController extends ToolbarController return mOpen; } - public void showRouteAltitudeChart(boolean show) + public void showRouteAltitudeChart() { ImageView altitudeChart = (ImageView) mFrame.findViewById(R.id.altitude_chart); - showRouteAltitudeChartInternal(show, altitudeChart); + showRouteAltitudeChartInternal(altitudeChart); } - protected void showRouteAltitudeChartInternal(boolean show, ImageView altitudeChart) + void showRouteAltitudeChartInternal(@NonNull ImageView altitudeChart) { - if (!show) + if (isVehicleRouteChecked()) { UiUtils.hide(altitudeChart); return; @@ -336,14 +385,80 @@ public class RoutingPlanController extends ToolbarController } } - public void saveAltitudeChartState(@NonNull Bundle outState) + public void showUberInfo(@NonNull UberInfo info) + { + final UberInfo.Product[] products = info.getProducts(); + if (products == null || info.getProducts().length == 0) + { + //TOOD: show the panel "There is no taxi here" + return; + } + + mUberProduct = products[0]; + final PagerAdapter adapter = new UberAdapter(mActivity, products); + DotPager pager = new DotPager.Builder(mActivity, (ViewPager) mUberFrame.findViewById(R.id.pager), adapter) + .setIndicatorContainer((ViewGroup) mUberFrame.findViewById(R.id.indicator)) + .setPageChangedListener(new DotPager.OnPageChangedListener() + { + @Override + public void onPageChanged(int position) + { + mUberProduct = products[position]; + } + }).build(); + pager.show(); + UiUtils.hide(mAltitudeChartFrame); + setStartButton(); + UiUtils.show(mUberFrame); + } + + void saveAltitudeChartState(@NonNull Bundle outState) { outState.putBoolean(STATE_ALTITUDE_CHART_SHOWN, mAltitudeChartShown); } - public void restoreAltitudeChartState(@NonNull Bundle state) + void restoreAltitudeChartState(@NonNull Bundle state) { if (state.getBoolean(STATE_ALTITUDE_CHART_SHOWN)) - showRouteAltitudeChart(!isVehicleRouteChecked()); + showRouteAltitudeChart(); + } + + public void setStartButton() + { + Button start = (Button) mFrame.findViewById(R.id.start); + if (start == null) + start = (Button) mActivity.findViewById(R.id.start); + + if (isTaxiRouteChecked()) + { + //TODO: use localized string!!! + start.setText("Заказать"); + start.setOnClickListener(new View.OnClickListener() + { + @Override + public void onClick(View v) + { + if (mUberProduct != null) + { + UberLinks links = RoutingController.get().getUberLink(mUberProduct.getProductId()); + Utils.launchUber(mActivity, links); + } + } + }); + } else + { + start.setText(mActivity.getText(R.string.p2p_start)); + start.setOnClickListener(new View.OnClickListener() + { + @Override + public void onClick(View v) + { + RoutingController.get().start(); + } + }); + } + + UiUtils.updateAccentButton(start); } + } diff --git a/android/src/com/mapswithme/maps/routing/RoutingPlanFragment.java b/android/src/com/mapswithme/maps/routing/RoutingPlanFragment.java index 3903cf0f80..5693156563 100644 --- a/android/src/com/mapswithme/maps/routing/RoutingPlanFragment.java +++ b/android/src/com/mapswithme/maps/routing/RoutingPlanFragment.java @@ -16,6 +16,7 @@ import com.mapswithme.maps.base.OnBackPressListener; public class RoutingPlanFragment extends BaseMwmFragment implements OnBackPressListener { + private RoutingPlanController mPlanController; @Override @@ -26,17 +27,6 @@ public class RoutingPlanFragment extends BaseMwmFragment mPlanController = new RoutingPlanController(res, getActivity()); updatePoints(); - Button start = (Button) res.findViewById(R.id.start); - RoutingController.get().setStartButton(start); - start.setOnClickListener(new View.OnClickListener() - { - @Override - public void onClick(View v) - { - RoutingController.get().start(); - } - }); - Bundle activityState = getMwmActivity().getSavedInstanceState(); if (activityState != null) restoreAltitudeChartState(activityState); @@ -50,13 +40,6 @@ public class RoutingPlanFragment extends BaseMwmFragment mPlanController.disableToggle(); } - @Override - public void onDestroyView() - { - super.onDestroyView(); - RoutingController.get().setStartButton(null); - } - public void updatePoints() { mPlanController.updatePoints(); @@ -73,9 +56,14 @@ public class RoutingPlanFragment extends BaseMwmFragment return RoutingController.get().cancelPlanning(); } - public void showRouteAltitudeChart(boolean show) + public void showRouteAltitudeChart() + { + mPlanController.showRouteAltitudeChart(); + } + + public void setStartButton() { - mPlanController.showRouteAltitudeChart(show); + mPlanController.setStartButton(); } public void restoreAltitudeChartState(@NonNull Bundle state) diff --git a/android/src/com/mapswithme/maps/routing/RoutingPlanInplaceController.java b/android/src/com/mapswithme/maps/routing/RoutingPlanInplaceController.java index 9027f85698..a6320fc85c 100644 --- a/android/src/com/mapswithme/maps/routing/RoutingPlanInplaceController.java +++ b/android/src/com/mapswithme/maps/routing/RoutingPlanInplaceController.java @@ -3,8 +3,6 @@ package com.mapswithme.maps.routing; import android.os.Build; import android.os.Bundle; import android.support.annotation.NonNull; -import android.view.View; -import android.widget.Button; import android.widget.ImageView; import com.mapswithme.maps.MwmActivity; @@ -12,8 +10,6 @@ import com.mapswithme.maps.R; import com.mapswithme.maps.bookmarks.data.MapObject; import com.mapswithme.util.ThemeUtils; import com.mapswithme.util.UiUtils; -import com.mapswithme.util.statistics.AlohaHelper; -import com.mapswithme.util.statistics.Statistics; public class RoutingPlanInplaceController extends RoutingPlanController { @@ -56,29 +52,6 @@ public class RoutingPlanInplaceController extends RoutingPlanController updatePoints(); } - public void setStartButton() - { - final MwmActivity activity = (MwmActivity) mActivity; - - Button start = activity.getMainMenu().getRouteStartButton(); - RoutingController.get().setStartButton(start); - start.setOnClickListener(new View.OnClickListener() - { - @Override - public void onClick(View v) - { - activity.closeMenu(Statistics.EventName.ROUTING_START, AlohaHelper.ROUTING_START, new Runnable() - { - @Override - public void run() - { - RoutingController.get().start(); - } - }); - } - }); - } - public void onSaveState(@NonNull Bundle outState) { outState.putBoolean(STATE_OPEN, isOpen()); @@ -94,9 +67,9 @@ public class RoutingPlanInplaceController extends RoutingPlanController } @Override - public void showRouteAltitudeChart(boolean show) + public void showRouteAltitudeChart() { ImageView altitudeChart = (ImageView) mActivity.findViewById(R.id.altitude_chart); - showRouteAltitudeChartInternal(show, altitudeChart); + showRouteAltitudeChartInternal(altitudeChart); } } diff --git a/android/src/com/mapswithme/maps/uber/UberAdapter.java b/android/src/com/mapswithme/maps/uber/UberAdapter.java new file mode 100644 index 0000000000..b44dd03976 --- /dev/null +++ b/android/src/com/mapswithme/maps/uber/UberAdapter.java @@ -0,0 +1,59 @@ +package com.mapswithme.maps.uber; + +import android.content.Context; +import android.support.annotation.NonNull; +import android.support.v4.view.PagerAdapter; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import com.mapswithme.maps.R; +import com.mapswithme.maps.api.uber.UberInfo; + +public class UberAdapter extends PagerAdapter +{ + + @NonNull + private final Context mContext; + @NonNull + private final UberInfo.Product[] mProducts; + + public UberAdapter(@NonNull Context context, @NonNull UberInfo.Product[] products) + { + mContext = context; + mProducts = products; + } + + @Override + public int getCount() + { + return mProducts.length; + } + + @Override + public boolean isViewFromObject(View view, Object object) + { + return view == object; + } + + @Override + public Object instantiateItem(ViewGroup container, int position) + { + UberInfo.Product product = mProducts[position]; + + View v = LayoutInflater.from(mContext).inflate(R.layout.uber_pager_item, null); + TextView name = (TextView) v.findViewById(R.id.product_name); + name.setText(product.getName()); + TextView timeAndPrice = (TextView) v.findViewById(R.id.arrival_time_price); + timeAndPrice.setText(product.getTime() + " • " + product.getPrice()); + container.addView(v, 0); + return v; + } + + @Override + public void destroyItem(ViewGroup container, int position, Object object) + { + container.removeView((View) object); + } +} diff --git a/android/src/com/mapswithme/maps/widget/DotPager.java b/android/src/com/mapswithme/maps/widget/DotPager.java new file mode 100644 index 0000000000..959e57a88d --- /dev/null +++ b/android/src/com/mapswithme/maps/widget/DotPager.java @@ -0,0 +1,159 @@ +package com.mapswithme.maps.widget; + +import android.content.Context; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.view.PagerAdapter; +import android.support.v4.view.ViewPager; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.LinearLayout; + +import com.mapswithme.maps.R; +import com.mapswithme.util.ThemeUtils; +import com.mapswithme.util.UiUtils; + +public class DotPager implements ViewPager.OnPageChangeListener +{ + @NonNull + private final ViewPager mPager; + @NonNull + private final PagerAdapter mAdapter; + @Nullable + private final ViewGroup mIndicator; + @NonNull + private final ImageView[] mDots; + @NonNull + private final Context mContext; + @Nullable + private final OnPageChangedListener mListener; + + private DotPager(@NonNull Builder builder) + { + mContext = builder.mContext; + mPager = builder.mPager; + mAdapter = builder.mAdapter; + mIndicator = builder.mIndicatorContainer; + mListener = builder.mListener; + mDots = new ImageView[mAdapter.getCount()]; + } + + public void show() + { + configure(); + updateIndicator(); + } + + + private void configure() + { + configurePager(); + configureIndicator(); + } + + private void configurePager() + { + mPager.setAdapter(mAdapter); + mPager.addOnPageChangeListener(this); + } + + private void configureIndicator() + { + if (mIndicator == null) + return; + + mIndicator.removeAllViews(); + + if (mAdapter.getCount() == 1) + return; + + for (int i = 0; i < mDots.length; i++) + { + mDots[i] = new ImageView(mContext); + LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + layoutParams.setMargins(0, 0, UiUtils.dimen(mContext, R.dimen.margin_half), 0); + mIndicator.addView(mDots[i], i, layoutParams); + } + } + + @Override + public void onPageSelected(int position) + { + if (mIndicator != null) + updateIndicator(); + + if (mListener != null) + mListener.onPageChanged(position); + } + + private void updateIndicator() + { + int currentPage = mPager.getCurrentItem(); + for (int i = 0; i < mAdapter.getCount(); i++) + { + mDots[i].setImageResource(ThemeUtils.isNightTheme() ? i == currentPage ? R.drawable.news_marker_active_night + : R.drawable.news_marker_inactive_night + : i == currentPage ? R.drawable.news_marker_active + : R.drawable.news_marker_inactive); + } + } + + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) + { + //no op + } + + @Override + public void onPageScrollStateChanged(int state) + { + //no op + } + + public static class Builder + { + @NonNull + private final ViewPager mPager; + @NonNull + private final PagerAdapter mAdapter; + @Nullable + private ViewGroup mIndicatorContainer; + @NonNull + private final ImageView[] mDots; + @NonNull + private final Context mContext; + @Nullable + private OnPageChangedListener mListener; + + public Builder(@NonNull Context context, @NonNull ViewPager pager, @NonNull PagerAdapter adapter) + { + mContext = context; + mPager = pager; + mAdapter = adapter; + mDots = new ImageView[mAdapter.getCount()]; + } + + public Builder setIndicatorContainer(@NonNull ViewGroup indicatorContainer) + { + mIndicatorContainer = indicatorContainer; + return this; + } + + public Builder setPageChangedListener(@Nullable OnPageChangedListener listener) + { + mListener = listener; + return this; + } + + public DotPager build() + { + return new DotPager(this); + } + } + + public interface OnPageChangedListener + { + void onPageChanged(int position); + } +} diff --git a/android/src/com/mapswithme/maps/widget/menu/MainMenu.java b/android/src/com/mapswithme/maps/widget/menu/MainMenu.java index 1c62360eca..fff888657f 100644 --- a/android/src/com/mapswithme/maps/widget/menu/MainMenu.java +++ b/android/src/com/mapswithme/maps/widget/menu/MainMenu.java @@ -57,7 +57,6 @@ public class MainMenu extends BaseMenu private final List<View> mCollapseViews = new ArrayList<>(); private final MenuToggle mToggle; - private Button mRouteStartButton; // Maps Item into button view placed on mContentFrame private final Map<Item, View> mItemViews = new HashMap<>(); @@ -234,9 +233,6 @@ public class MainMenu extends BaseMenu mNewsMarker = mButtonsFrame.findViewById(R.id.marker); mNewsCounter = (TextView) mContentFrame.findViewById(R.id.counter); - if (mRoutePlanFrame != null) - mRouteStartButton = (Button) mRoutePlanFrame.findViewById(R.id.start); - init(); } @@ -306,11 +302,6 @@ public class MainMenu extends BaseMenu return mAnimationTrackListener; } - public Button getRouteStartButton() - { - return mRouteStartButton; - } - public void showLineFrame(boolean show) { UiUtils.showIf(show, mLineFrame); diff --git a/android/src/com/mapswithme/util/Utils.java b/android/src/com/mapswithme/util/Utils.java index 120c675fcf..c5df86b759 100644 --- a/android/src/com/mapswithme/util/Utils.java +++ b/android/src/com/mapswithme/util/Utils.java @@ -31,6 +31,7 @@ import com.mapswithme.maps.BuildConfig; import com.mapswithme.maps.MwmApplication; import com.mapswithme.maps.R; import com.mapswithme.maps.activity.CustomNavigateUpListener; +import com.mapswithme.maps.api.uber.UberLinks; import com.mapswithme.util.statistics.AlohaHelper; import java.io.Closeable; @@ -399,4 +400,22 @@ public class Utils return installationId; } + + public static void launchUber(@NonNull Activity context, @NonNull UberLinks links) + { + try + { + PackageManager pm = context.getPackageManager(); + pm.getPackageInfo("com.ubercab", PackageManager.GET_ACTIVITIES); + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setData(Uri.parse(links.getDeepLink())); + context.startActivity(intent); + } catch (PackageManager.NameNotFoundException e) + { + // No Uber app! Open mobile website. + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse(links.getUniversalLink())); + context.startActivity(i); + } + } } diff --git a/android/src/com/mapswithme/util/statistics/AlohaHelper.java b/android/src/com/mapswithme/util/statistics/AlohaHelper.java index 09efd5bd8f..30d914d366 100644 --- a/android/src/com/mapswithme/util/statistics/AlohaHelper.java +++ b/android/src/com/mapswithme/util/statistics/AlohaHelper.java @@ -74,6 +74,7 @@ public class AlohaHelper public static final String ROUTING_VEHICLE_SET = "routerSetVehicle"; public static final String ROUTING_PEDESTRIAN_SET = "routerSetPedestrian"; public static final String ROUTING_BICYCLE_SET = "routerSetBicycle"; + public static final String ROUTING_TAXI_SET = "routerSetTaxi"; public static final String ROUTING_SWAP_POINTS = "routeSwapPoints"; public static final String ROUTING_TOGGLE = "routeToggle"; public static final String ROUTING_SEARCH_POINT = "routSearchPoint"; diff --git a/android/src/com/mapswithme/util/statistics/Statistics.java b/android/src/com/mapswithme/util/statistics/Statistics.java index 4be6ba2f42..0b77c7be9d 100644 --- a/android/src/com/mapswithme/util/statistics/Statistics.java +++ b/android/src/com/mapswithme/util/statistics/Statistics.java @@ -110,6 +110,7 @@ public enum Statistics public static final String ROUTING_VEHICLE_SET = "Routing. Set vehicle"; public static final String ROUTING_PEDESTRIAN_SET = "Routing. Set pedestrian"; public static final String ROUTING_BICYCLE_SET = "Routing. Set bicycle"; + public static final String ROUTING_TAXI_SET = "Routing. Set taxi"; public static final String ROUTING_SWAP_POINTS = "Routing. Swap points"; public static final String ROUTING_TOGGLE = "Routing. Toggle"; public static final String ROUTING_SEARCH_POINT = "Routing. Search point"; |