Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mapsme/omim.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorburivuh <burivuh@maps.me>2016-10-05 20:22:18 +0300
committerGitHub <noreply@github.com>2016-10-05 20:22:18 +0300
commitd487f11baa9d61b59bb6e41c2639fbd320fb0a8a (patch)
tree2247a7eb6727e44a607752bdea50db4847be67e7 /android
parente75d76e880fafdaf337bc90959f38c835584c6a6 (diff)
parente666c1dad90a5db45455eb19a73e33307d39d0f8 (diff)
Merge pull request #4367 from goblinr/MAPSME-1-external-place-page-booking
[android] place page booking
Diffstat (limited to 'android')
-rw-r--r--android/AndroidManifest.xml95
-rw-r--r--android/build.gradle21
-rw-r--r--android/jni/com/mapswithme/maps/Framework.cpp6
-rw-r--r--android/jni/com/mapswithme/maps/Framework.hpp2
-rw-r--r--android/jni/com/mapswithme/maps/SponsoredHotel.cpp134
-rw-r--r--android/res/drawable/bg_circle_green.xml8
-rw-r--r--android/res/drawable/bg_circle_green_night.xml8
-rw-r--r--android/res/drawable/bg_circle_red.xml8
-rw-r--r--android/res/drawable/bg_circle_red_night.xml8
-rw-r--r--android/res/drawable/divider_transparent.xml6
-rw-r--r--android/res/drawable/ic_chevron_right_white.xml9
-rw-r--r--android/res/drawable/ic_minus_red.xml8
-rw-r--r--android/res/drawable/ic_negative_review.xml15
-rw-r--r--android/res/drawable/ic_negative_review_night.xml15
-rw-r--r--android/res/drawable/ic_plus_green.xml8
-rw-r--r--android/res/drawable/ic_positive_review.xml15
-rw-r--r--android/res/drawable/ic_positive_review_night.xml15
-rw-r--r--android/res/layout-w600dp-port/place_page_details.xml12
-rw-r--r--android/res/layout/activity_full_screen_gallery.xml94
-rw-r--r--android/res/layout/fragment_fullscreen_image.xml13
-rw-r--r--android/res/layout/fragment_gallery.xml16
-rw-r--r--android/res/layout/fragment_review.xml13
-rw-r--r--android/res/layout/item_comment.xml122
-rw-r--r--android/res/layout/item_facility.xml24
-rw-r--r--android/res/layout/item_gallery.xml22
-rw-r--r--android/res/layout/item_image.xml15
-rw-r--r--android/res/layout/item_more_button.xml8
-rw-r--r--android/res/layout/item_nearby.xml66
-rw-r--r--android/res/layout/item_rating.xml29
-rw-r--r--android/res/layout/place_page_details.xml11
-rw-r--r--android/res/layout/place_page_hotel_description.xml43
-rw-r--r--android/res/layout/place_page_hotel_facilities.xml42
-rw-r--r--android/res/layout/place_page_hotel_gallery.xml18
-rw-r--r--android/res/layout/place_page_hotel_nearby.xml29
-rw-r--r--android/res/layout/place_page_hotel_rating.xml58
-rw-r--r--android/res/layout/place_page_more.xml1
-rw-r--r--android/res/layout/toolbar_transparent.xml8
-rw-r--r--android/res/values-ru/strings.xml5
-rw-r--r--android/res/values-v16/strings.xml11
-rw-r--r--android/res/values-v16/styles-text.xml7
-rw-r--r--android/res/values-w840dp/strings.xml11
-rw-r--r--android/res/values/colors.xml11
-rw-r--r--android/res/values/dimens.xml10
-rw-r--r--android/res/values/integer.xml3
-rw-r--r--android/res/values/plurals.xml11
-rw-r--r--android/res/values/strings.xml5
-rw-r--r--android/res/values/styles-place_page.xml19
-rw-r--r--android/res/values/styles-text.xml16
-rw-r--r--android/res/values/styles.xml68
-rw-r--r--android/res/values/themes-attrs.xml4
-rw-r--r--android/res/values/themes-base.xml10
-rw-r--r--android/res/values/themes.xml22
-rw-r--r--android/src/com/mapswithme/maps/base/BaseMwmExtraTitleActivity.java43
-rw-r--r--android/src/com/mapswithme/maps/gallery/FullScreenGalleryActivity.java204
-rw-r--r--android/src/com/mapswithme/maps/gallery/FullScreenGalleryFragment.java50
-rw-r--r--android/src/com/mapswithme/maps/gallery/GalleryActivity.java29
-rw-r--r--android/src/com/mapswithme/maps/gallery/GalleryFragment.java65
-rw-r--r--android/src/com/mapswithme/maps/gallery/GalleryPageAdapter.java44
-rw-r--r--android/src/com/mapswithme/maps/gallery/Image.java144
-rw-r--r--android/src/com/mapswithme/maps/gallery/ImageAdapter.java79
-rw-r--r--android/src/com/mapswithme/maps/review/Review.java121
-rw-r--r--android/src/com/mapswithme/maps/review/ReviewActivity.java37
-rw-r--r--android/src/com/mapswithme/maps/review/ReviewAdapter.java217
-rw-r--r--android/src/com/mapswithme/maps/review/ReviewFragment.java76
-rw-r--r--android/src/com/mapswithme/maps/widget/LineCountTextView.java56
-rw-r--r--android/src/com/mapswithme/maps/widget/StaticGridView.java30
-rw-r--r--android/src/com/mapswithme/maps/widget/placepage/FacilitiesAdapter.java97
-rw-r--r--android/src/com/mapswithme/maps/widget/placepage/GalleryAdapter.java205
-rw-r--r--android/src/com/mapswithme/maps/widget/placepage/LeftPlacePageAnimationController.java43
-rw-r--r--android/src/com/mapswithme/maps/widget/placepage/NearbyAdapter.java134
-rw-r--r--android/src/com/mapswithme/maps/widget/placepage/PlacePageView.java621
-rw-r--r--android/src/com/mapswithme/maps/widget/placepage/ReviewAdapter.java136
-rw-r--r--android/src/com/mapswithme/maps/widget/placepage/SponsoredHotel.java253
-rw-r--r--android/src/com/mapswithme/maps/widget/recycler/DividerItemDecoration.java131
-rw-r--r--android/src/com/mapswithme/maps/widget/recycler/GridDividerItemDecoration.java128
-rw-r--r--android/src/com/mapswithme/util/UiUtils.java20
76 files changed, 3838 insertions, 363 deletions
diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml
index bec1087871..af1a0b02e9 100644
--- a/android/AndroidManifest.xml
+++ b/android/AndroidManifest.xml
@@ -71,11 +71,11 @@
<meta-data
android:name="PW_APPID"
- android:value="${PW_APPID}" />
+ android:value="${PW_APPID}"/>
<meta-data
android:name="PW_PROJECT_ID"
- android:value="${PW_PROJECT_ID}" />
+ android:value="${PW_PROJECT_ID}"/>
<!--meta-data
android:name="PW_LOG_LEVEL"
@@ -241,52 +241,79 @@
</activity>
<activity
- android:name="com.mapswithme.maps.editor.EditorActivity"
- android:configChanges="orientation|screenLayout|screenSize"
- android:label="@string/edit_place"
- android:theme="@style/MwmTheme.EditorActivity"
- android:windowSoftInputMode="adjustResize|stateHidden"
- android:parentActivityName="com.mapswithme.maps.MwmActivity">
+ android:name="com.mapswithme.maps.editor.EditorActivity"
+ android:configChanges="orientation|screenLayout|screenSize"
+ android:label="@string/edit_place"
+ android:theme="@style/MwmTheme.EditorActivity"
+ android:windowSoftInputMode="adjustResize|stateHidden"
+ android:parentActivityName="com.mapswithme.maps.MwmActivity">
+ <!-- The meta-data element is needed for versions lower than 4.1 -->
+ <meta-data
+ android:name="android.support.PARENT_ACTIVITY"
+ android:value="com.mapswithme.maps.MwmActivity"/>
+ </activity>
+
+ <activity
+ android:name="com.mapswithme.maps.editor.ProfileActivity"
+ android:parentActivityName="com.mapswithme.maps.settings.SettingsActivity">
+ <!-- The meta-data element is needed for versions lower than 4.1 -->
+ <meta-data
+ android:name="android.support.PARENT_ACTIVITY"
+ android:value="com.mapswithme.maps.settings.SettingsActivity"/>
+ </activity>
+
+ <activity
+ android:name="com.mapswithme.maps.editor.FeatureCategoryActivity"
+ android:parentActivityName="com.mapswithme.maps.MwmActivity">
+ <!-- The meta-data element is needed for versions lower than 4.1 -->
+ <meta-data
+ android:name="android.support.PARENT_ACTIVITY"
+ android:value="com.mapswithme.maps.MwmActivity"/>
+ </activity>
+
+ <activity
+ android:name="com.mapswithme.maps.editor.ReportActivity"
+ android:parentActivityName="com.mapswithme.maps.MwmActivity">
<!-- The meta-data element is needed for versions lower than 4.1 -->
<meta-data
- android:name="android.support.PARENT_ACTIVITY"
- android:value="com.mapswithme.maps.MwmActivity"/>
+ android:name="android.support.PARENT_ACTIVITY"
+ android:value="com.mapswithme.maps.MwmActivity"/>
</activity>
<activity
- android:name="com.mapswithme.maps.editor.ProfileActivity"
- android:parentActivityName="com.mapswithme.maps.settings.SettingsActivity">
+ android:name="com.mapswithme.maps.editor.OsmAuthActivity"
+ android:parentActivityName="com.mapswithme.maps.MwmActivity">
<!-- The meta-data element is needed for versions lower than 4.1 -->
<meta-data
- android:name="android.support.PARENT_ACTIVITY"
- android:value="com.mapswithme.maps.settings.SettingsActivity"/>
+ android:name="android.support.PARENT_ACTIVITY"
+ android:value="com.mapswithme.maps.MwmActivity"/>
</activity>
<activity
- android:name="com.mapswithme.maps.editor.FeatureCategoryActivity"
- android:parentActivityName="com.mapswithme.maps.MwmActivity">
+ android:name="com.mapswithme.maps.gallery.GalleryActivity"
+ android:parentActivityName="com.mapswithme.maps.MwmActivity">
<!-- The meta-data element is needed for versions lower than 4.1 -->
<meta-data
- android:name="android.support.PARENT_ACTIVITY"
- android:value="com.mapswithme.maps.MwmActivity"/>
+ android:name="android.support.PARENT_ACTIVITY"
+ android:value="com.mapswithme.maps.MwmActivity"/>
</activity>
<activity
- android:name="com.mapswithme.maps.editor.ReportActivity"
- android:parentActivityName="com.mapswithme.maps.MwmActivity">
+ android:name="com.mapswithme.maps.gallery.FullScreenGalleryActivity"
+ android:parentActivityName="com.mapswithme.maps.MwmActivity">
<!-- The meta-data element is needed for versions lower than 4.1 -->
<meta-data
- android:name="android.support.PARENT_ACTIVITY"
- android:value="com.mapswithme.maps.MwmActivity"/>
+ android:name="android.support.PARENT_ACTIVITY"
+ android:value="com.mapswithme.maps.MwmActivity"/>
</activity>
<activity
- android:name="com.mapswithme.maps.editor.OsmAuthActivity"
- android:parentActivityName="com.mapswithme.maps.MwmActivity">
+ android:name="com.mapswithme.maps.review.ReviewActivity"
+ android:parentActivityName="com.mapswithme.maps.MwmActivity">
<!-- The meta-data element is needed for versions lower than 4.1 -->
<meta-data
- android:name="android.support.PARENT_ACTIVITY"
- android:value="com.mapswithme.maps.MwmActivity"/>
+ android:name="android.support.PARENT_ACTIVITY"
+ android:value="com.mapswithme.maps.MwmActivity"/>
</activity>
<!-- facebook -->
@@ -346,17 +373,17 @@
</receiver>
<!-- PushWoosh -->
- <activity android:name="com.pushwoosh.richpages.RichPageActivity" />
- <activity android:name="com.pushwoosh.MessageActivity" />
- <activity android:name="com.pushwoosh.PushHandlerActivity" />
+ <activity android:name="com.pushwoosh.richpages.RichPageActivity"/>
+ <activity android:name="com.pushwoosh.MessageActivity"/>
+ <activity android:name="com.pushwoosh.PushHandlerActivity"/>
<receiver
android:name="com.google.android.gms.gcm.GcmReceiver"
android:exported="true"
- android:permission="com.google.android.c2dm.permission.SEND" >
+ android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
- <action android:name="com.google.android.c2dm.intent.RECEIVE" />
- <category android:name="${applicationId}" />
+ <action android:name="com.google.android.c2dm.intent.RECEIVE"/>
+ <category android:name="${applicationId}"/>
</intent-filter>
</receiver>
@@ -378,10 +405,10 @@
<service
android:name="com.pushwoosh.GCMRegistrationService"
- android:exported="false" />
+ android:exported="false"/>
<service
- android:name="com.pushwoosh.location.GeoLocationService" />
+ android:name="com.pushwoosh.location.GeoLocationService"/>
<!-- Catches app upgraded intent -->
<receiver android:name=".background.UpgradeReceiver">
diff --git a/android/build.gradle b/android/build.gradle
index 4706ea08e1..99fdd323a2 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -57,6 +57,8 @@ dependencies {
// TODO remove this library when default LinearLayoutManager will be fixed.
compile 'org.solovyev.android.views:linear-layout-manager:0.5@aar'
compile 'com.timehop.stickyheadersrecyclerview:library:0.4.3@aar'
+ //Glide
+ compile 'com.github.bumptech.glide:glide:3.7.0'
}
def getDate() {
@@ -93,15 +95,15 @@ android {
// Crashlytics API key
Properties props = new Properties()
props.load(new FileInputStream("${projectDir}/fabric.properties"));
- manifestPlaceholders = [ 'FABRIC_API_KEY': props['apiKey'] ]
+ manifestPlaceholders = ['FABRIC_API_KEY': props['apiKey']]
buildConfigField 'String', 'FABRIC_API_KEY', /"${props['apiKey']}"/
// PushWoosh keys
Properties pwProps = new Properties()
pwProps.load(new FileInputStream("${projectDir}/pushwoosh.properties"));
- manifestPlaceholders += [ 'PW_APPID': pwProps['pwAppId'] ]
+ manifestPlaceholders += ['PW_APPID': pwProps['pwAppId']]
buildConfigField 'String', 'PW_APPID', /"${pwProps['pwAppId']}"/
- manifestPlaceholders += [ 'PW_PROJECT_ID': pwProps['pwProjectId'] ]
+ manifestPlaceholders += ['PW_PROJECT_ID': pwProps['pwProjectId']]
}
sourceSets.main {
@@ -210,13 +212,6 @@ android {
android.sourceSets.blackberry.assets.srcDirs = ['flavors/mwm-ttf-assets']
buildConfigField 'String', 'REVIEW_URL', '"https://appworld.blackberry.com/webstore/content/51013892"'
}
-
- nineStore {
- versionName = android.defaultConfig.versionName + '-NineStore'
- android.sourceSets.blackberry.assets.srcDirs = ['flavors/mwm-ttf-assets']
- buildConfigField 'String', 'SUPPORT_MAIL', '"ninestore@mapswithme.com"'
- buildConfigField 'String', 'REVIEW_URL', '"http://www.ninestore.ru/android-apps/mapswithme-maps-pro"'
- }
}
// Currently (as of 1.2.3 gradle plugin) ABI filters aren't supported inside of product flavors, so we cannot generate splitted builds only for Google build.
@@ -331,7 +326,7 @@ if (System.properties['os.name'].toLowerCase().contains('windows'))
project.ext.NDK_BUILD += ".cmd"
def archs = ['x86', 'armeabi-v7a-hard']
-def buildTypes = [[ndkType: 'release', cppType: "production", flags : propReleaseNdkFlags], [ndkType: 'debug', cppType: "debug", flags : propDebugNdkFlags]]
+def buildTypes = [[ndkType: 'release', cppType: "production", flags: propReleaseNdkFlags], [ndkType: 'debug', cppType: "debug", flags: propDebugNdkFlags]]
buildTypes.each { type ->
def suffix = type.ndkType.capitalize()
@@ -408,14 +403,14 @@ obbGenerate.dependsOn obbClean, obbMainGenerate, obbPatchGenerate, obbMainAlign,
def createObbGenerateTask(type, data, name) {
return tasks.create(name: "obb${type}Generate", type: Exec, description: 'Generate obb files') {
- commandLine ((['zip', '-0', '-j', name, data]).flatten())
+ commandLine((['zip', '-0', '-j', name, data]).flatten())
}
}
def createObbAlignTask(type, rawObb, alignedObb) {
def sdkDir = "${android.getSdkDirectory().getAbsolutePath()}"
def zipalignPath = sdkDir + File.separator + "build-tools" + File.separator +
- propBuildToolsVersion + File.separator + "zipalign";
+ propBuildToolsVersion + File.separator + "zipalign";
return tasks.create(name: "obb${type}Align", dependsOn: "obb${type}Generate", type: Exec, description: 'Align obb files') {
commandLine zipalignPath, '-v', '8', rawObb, alignedObb
diff --git a/android/jni/com/mapswithme/maps/Framework.cpp b/android/jni/com/mapswithme/maps/Framework.cpp
index bbd1d9e42c..361dba42b5 100644
--- a/android/jni/com/mapswithme/maps/Framework.cpp
+++ b/android/jni/com/mapswithme/maps/Framework.cpp
@@ -476,6 +476,12 @@ void Framework::RequestBookingMinPrice(string const & hotelId, string const & cu
return m_work.GetBookingApi().GetMinPrice(hotelId, currencyCode, callback);
}
+void Framework::RequestBookingInfo(string const & hotelId, string const & lang,
+ function<void(BookingApi::HotelInfo const &)> const & callback)
+{
+ return m_work.GetBookingApi().GetHotelInfo(hotelId, lang, callback);
+}
+
bool Framework::HasSpaceForMigration()
{
return m_work.IsEnoughSpaceForMigrate();
diff --git a/android/jni/com/mapswithme/maps/Framework.hpp b/android/jni/com/mapswithme/maps/Framework.hpp
index 2937dbb7bc..12fcdcd85d 100644
--- a/android/jni/com/mapswithme/maps/Framework.hpp
+++ b/android/jni/com/mapswithme/maps/Framework.hpp
@@ -155,6 +155,8 @@ namespace android
void SetPlacePageInfo(place_page::Info const & info);
place_page::Info & GetPlacePageInfo();
void RequestBookingMinPrice(string const & hotelId, string const & currency, function<void(string const &, string const &)> const & callback);
+ void RequestBookingInfo(string const & hotelId, string const & lang,
+ function<void(BookingApi::HotelInfo const &)> const & callback);
bool HasSpaceForMigration();
storage::TCountryId PreMigrate(ms::LatLon const & position, storage::Storage::TChangeCountryFunction const & statusChangeListener,
diff --git a/android/jni/com/mapswithme/maps/SponsoredHotel.cpp b/android/jni/com/mapswithme/maps/SponsoredHotel.cpp
index b9ece774fd..cabe3fe119 100644
--- a/android/jni/com/mapswithme/maps/SponsoredHotel.cpp
+++ b/android/jni/com/mapswithme/maps/SponsoredHotel.cpp
@@ -2,16 +2,28 @@
#include "../core/jni_helper.hpp"
#include "../platform/Platform.hpp"
+#include "map/booking_api.hpp"
#include "map/place_page_info.hpp"
#include "std/bind.hpp"
+#include "std/chrono.hpp"
namespace
{
-
jclass g_hotelClass;
-jmethodID g_hotelClassCtor;
+jclass g_facilityTypeClass;
+jclass g_nearbyObjectClass;
+jclass g_imageClass;
+jclass g_reviewClass;
+jclass g_hotelInfoClass;
+jmethodID g_facilityConstructor;
+jmethodID g_nearbyConstructor;
+jmethodID g_imageConstructor;
+jmethodID g_reviewConstructor;
+jmethodID g_hotelInfoConstructor;
+jmethodID g_hotelClassConstructor;
jmethodID g_priceCallback;
+jmethodID g_infoCallback;
void PrepareClassRefs(JNIEnv * env, jclass hotelClass)
{
@@ -19,21 +31,51 @@ void PrepareClassRefs(JNIEnv * env, jclass hotelClass)
return;
g_hotelClass = static_cast<jclass>(env->NewGlobalRef(hotelClass));
+ g_hotelInfoClass =
+ jni::GetGlobalClassRef(env, "com/mapswithme/maps/widget/placepage/SponsoredHotel$HotelInfo");
+ g_facilityTypeClass = jni::GetGlobalClassRef(
+ env, "com/mapswithme/maps/widget/placepage/SponsoredHotel$FacilityType");
+ g_nearbyObjectClass = jni::GetGlobalClassRef(
+ env, "com/mapswithme/maps/widget/placepage/SponsoredHotel$NearbyObject");
+ g_reviewClass = jni::GetGlobalClassRef(env, "com/mapswithme/maps/review/Review");
+ g_imageClass = jni::GetGlobalClassRef(env, "com/mapswithme/maps/gallery/Image");
+
+ g_facilityConstructor =
+ jni::GetConstructorID(env, g_facilityTypeClass, "(Ljava/lang/String;Ljava/lang/String;)V");
+ g_nearbyConstructor = jni::GetConstructorID(
+ env, g_nearbyObjectClass, "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;DD)V");
+ g_imageConstructor =
+ jni::GetConstructorID(env, g_imageClass, "(Ljava/lang/String;Ljava/lang/String;)V");
+ g_reviewConstructor = jni::GetConstructorID(env, g_reviewClass,
+ "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/"
+ "String;Ljava/lang/String;Ljava/lang/String;FJ)V");
+ g_hotelInfoConstructor = jni::GetConstructorID(
+ env, g_hotelInfoClass,
+ "(Ljava/lang/String;[Lcom/mapswithme/maps/gallery/Image;[Lcom/mapswithme/maps/widget/"
+ "placepage/SponsoredHotel$FacilityType;[Lcom/mapswithme/maps/review/Review;[Lcom/mapswithme/"
+ "maps/widget/placepage/SponsoredHotel$NearbyObject;)V");
// SponsoredHotel(String rating, String price, String urlBook, String urlDescription)
- g_hotelClassCtor = jni::GetConstructorID(env, g_hotelClass, "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
+ g_hotelClassConstructor = jni::GetConstructorID(
+ env, g_hotelClass,
+ "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
// static void onPriceReceived(final String id, final String price, final String currency)
- g_priceCallback = jni::GetStaticMethodID(env, g_hotelClass, "onPriceReceived", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
+ g_priceCallback =
+ jni::GetStaticMethodID(env, g_hotelClass, "onPriceReceived",
+ "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
+ // static void onDescriptionReceived(final String id, final String description)
+ g_infoCallback = jni::GetStaticMethodID(
+ env, g_hotelClass, "onInfoReceived",
+ "(Ljava/lang/String;Lcom/mapswithme/maps/widget/placepage/SponsoredHotel$HotelInfo;)V");
}
-} // namespace
+} // namespace
-extern "C"
-{
+extern "C" {
// static SponsoredHotel nativeGetCurrent();
-JNIEXPORT jobject JNICALL
-Java_com_mapswithme_maps_widget_placepage_SponsoredHotel_nativeGetCurrent(JNIEnv * env, jclass clazz)
+JNIEXPORT jobject JNICALL Java_com_mapswithme_maps_widget_placepage_SponsoredHotel_nativeGetCurrent(
+ JNIEnv * env, jclass clazz)
{
PrepareClassRefs(env, clazz);
@@ -41,31 +83,79 @@ Java_com_mapswithme_maps_widget_placepage_SponsoredHotel_nativeGetCurrent(JNIEnv
if (!ppInfo.m_isSponsoredHotel)
return nullptr;
- return env->NewObject(g_hotelClass, g_hotelClassCtor, jni::ToJavaString(env, ppInfo.GetRatingFormatted()),
- jni::ToJavaString(env, ppInfo.GetApproximatePricing()),
- jni::ToJavaString(env, ppInfo.GetSponsoredBookingUrl()),
- jni::ToJavaString(env, ppInfo.GetSponsoredDescriptionUrl()));
+ return env->NewObject(g_hotelClass, g_hotelClassConstructor,
+ jni::ToJavaString(env, ppInfo.GetRatingFormatted()),
+ jni::ToJavaString(env, ppInfo.GetApproximatePricing()),
+ jni::ToJavaString(env, ppInfo.GetSponsoredBookingUrl()),
+ jni::ToJavaString(env, ppInfo.GetSponsoredDescriptionUrl()));
}
// static void nativeRequestPrice(String id, String currencyCode);
-JNIEXPORT void JNICALL
-Java_com_mapswithme_maps_widget_placepage_SponsoredHotel_nativeRequestPrice(JNIEnv * env, jclass clazz, jstring id, jstring currencyCode)
+JNIEXPORT void JNICALL Java_com_mapswithme_maps_widget_placepage_SponsoredHotel_nativeRequestPrice(
+ JNIEnv * env, jclass clazz, jstring id, jstring currencyCode)
{
PrepareClassRefs(env, clazz);
string const hotelId = jni::ToNativeString(env, id);
string const code = jni::ToNativeString(env, currencyCode);
- g_framework->RequestBookingMinPrice(hotelId, code, [hotelId](string const & price, string const & currency)
- {
- GetPlatform().RunOnGuiThread([=]()
- {
+ g_framework->RequestBookingMinPrice(hotelId, code, [hotelId](string const & price,
+ string const & currency) {
+ GetPlatform().RunOnGuiThread([=]() {
JNIEnv * env = jni::GetEnv();
env->CallStaticVoidMethod(g_hotelClass, g_priceCallback, jni::ToJavaString(env, hotelId),
- jni::ToJavaString(env, price),
- jni::ToJavaString(env, currency));
+ jni::ToJavaString(env, price), jni::ToJavaString(env, currency));
+ });
+ });
+}
+
+// static void nativeRequestInfo(String id, String locale);
+JNIEXPORT void JNICALL Java_com_mapswithme_maps_widget_placepage_SponsoredHotel_nativeRequestInfo(
+ JNIEnv * env, jclass clazz, jstring id, jstring locale)
+{
+ PrepareClassRefs(env, clazz);
+
+ string const hotelId = jni::ToNativeString(env, id);
+ string const code = jni::ToNativeString(env, locale);
+
+ g_framework->RequestBookingInfo(hotelId, code, [hotelId](
+ BookingApi::HotelInfo const & hotelInfo) {
+ GetPlatform().RunOnGuiThread([=]() {
+ JNIEnv * env = jni::GetEnv();
+
+ auto description = jni::ToJavaString(env, hotelInfo.m_description);
+ auto photos =
+ jni::ToJavaArray(env, g_imageClass, hotelInfo.m_photos,
+ [](JNIEnv * env, BookingApi::HotelPhotoUrls const & item) {
+ return env->NewObject(g_imageClass, g_imageConstructor,
+ jni::ToJavaString(env, item.m_original),
+ jni::ToJavaString(env, item.m_small));
+ });
+ auto facilities =
+ jni::ToJavaArray(env, g_facilityTypeClass, hotelInfo.m_facilities,
+ [](JNIEnv * env, BookingApi::Facility const & item) {
+ return env->NewObject(g_facilityTypeClass, g_facilityConstructor,
+ jni::ToJavaString(env, item.m_id),
+ jni::ToJavaString(env, item.m_localizedName));
+ });
+ auto reviews = jni::ToJavaArray(
+ env, g_reviewClass, hotelInfo.m_reviews,
+ [](JNIEnv * env, BookingApi::HotelReview const & item) {
+ return env->NewObject(
+ g_reviewClass, g_reviewConstructor, jni::ToJavaString(env, item.m_reviewNeutral),
+ jni::ToJavaString(env, item.m_reviewPositive),
+ jni::ToJavaString(env, item.m_reviewNegative),
+ jni::ToJavaString(env, item.m_author), jni::ToJavaString(env, item.m_authorPictUrl),
+ item.m_rating,
+ time_point_cast<milliseconds>(item.m_date).time_since_epoch().count());
+ });
+ auto nearby = env->NewObjectArray(0, g_nearbyObjectClass, 0);
+
+ env->CallStaticVoidMethod(g_hotelClass, g_infoCallback, jni::ToJavaString(env, hotelId),
+ env->NewObject(g_hotelInfoClass, g_hotelInfoConstructor,
+ description, photos, facilities, reviews, nearby));
});
});
}
-} // extern "C"
+} // extern "C"
diff --git a/android/res/drawable/bg_circle_green.xml b/android/res/drawable/bg_circle_green.xml
new file mode 100644
index 0000000000..24f254e751
--- /dev/null
+++ b/android/res/drawable/bg_circle_green.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="oval">
+ <solid android:color="@color/bg_placepage_rating_positive"/>
+ <size
+ android:height="@dimen/placepage_margin_rating"
+ android:width="@dimen/placepage_margin_rating"/>
+</shape>
diff --git a/android/res/drawable/bg_circle_green_night.xml b/android/res/drawable/bg_circle_green_night.xml
new file mode 100644
index 0000000000..59127c39ac
--- /dev/null
+++ b/android/res/drawable/bg_circle_green_night.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="oval">
+ <solid android:color="@color/bg_placepage_rating_positive_night"/>
+ <size
+ android:height="@dimen/placepage_margin_rating"
+ android:width="@dimen/placepage_margin_rating"/>
+</shape>
diff --git a/android/res/drawable/bg_circle_red.xml b/android/res/drawable/bg_circle_red.xml
new file mode 100644
index 0000000000..c5a44a59a0
--- /dev/null
+++ b/android/res/drawable/bg_circle_red.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="oval">
+ <solid android:color="@color/bg_placepage_rating_negative"/>
+ <size
+ android:height="@dimen/placepage_margin_rating"
+ android:width="@dimen/placepage_margin_rating"/>
+</shape>
diff --git a/android/res/drawable/bg_circle_red_night.xml b/android/res/drawable/bg_circle_red_night.xml
new file mode 100644
index 0000000000..61cb40cf65
--- /dev/null
+++ b/android/res/drawable/bg_circle_red_night.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="oval">
+ <solid android:color="@color/bg_placepage_rating_negative_night"/>
+ <size
+ android:height="@dimen/placepage_margin_rating"
+ android:width="@dimen/placepage_margin_rating"/>
+</shape>
diff --git a/android/res/drawable/divider_transparent.xml b/android/res/drawable/divider_transparent.xml
new file mode 100644
index 0000000000..377b4d7e12
--- /dev/null
+++ b/android/res/drawable/divider_transparent.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <size android:width="@dimen/margin_quarter"
+ android:height="@dimen/margin_quarter"/>
+ <solid android:color="@android:color/transparent"/>
+</shape>
diff --git a/android/res/drawable/ic_chevron_right_white.xml b/android/res/drawable/ic_chevron_right_white.xml
new file mode 100644
index 0000000000..0563f22f11
--- /dev/null
+++ b/android/res/drawable/ic_chevron_right_white.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FFF"
+ android:pathData="M10,6L8.59,7.41 13.17,12l-4.58,4.59L10,18l6,-6z"/>
+</vector>
diff --git a/android/res/drawable/ic_minus_red.xml b/android/res/drawable/ic_minus_red.xml
new file mode 100644
index 0000000000..b108146e81
--- /dev/null
+++ b/android/res/drawable/ic_minus_red.xml
@@ -0,0 +1,8 @@
+<vector android:height="10dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0"
+ android:width="10dp"
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="#FFF44336"
+ android:pathData="M19,13H5v-2h14v2z"/>
+</vector>
diff --git a/android/res/drawable/ic_negative_review.xml b/android/res/drawable/ic_negative_review.xml
new file mode 100644
index 0000000000..a28a8a5646
--- /dev/null
+++ b/android/res/drawable/ic_negative_review.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:drawable="@drawable/bg_circle_red"
+ android:left="@dimen/margin_eighth"
+ android:right="@dimen/margin_eighth"
+ android:bottom="@dimen/margin_eighth"
+ android:top="@dimen/margin_eighth"/>
+ <item
+ android:drawable="@drawable/ic_minus_red"
+ android:left="@dimen/margin_eighth"
+ android:right="@dimen/margin_eighth"
+ android:bottom="@dimen/margin_eighth"
+ android:top="@dimen/margin_eighth"/>
+</layer-list>
diff --git a/android/res/drawable/ic_negative_review_night.xml b/android/res/drawable/ic_negative_review_night.xml
new file mode 100644
index 0000000000..475fd403dc
--- /dev/null
+++ b/android/res/drawable/ic_negative_review_night.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:drawable="@drawable/bg_circle_red_night"
+ android:left="@dimen/margin_eighth"
+ android:right="@dimen/margin_eighth"
+ android:bottom="@dimen/margin_eighth"
+ android:top="@dimen/margin_eighth"/>
+ <item
+ android:drawable="@drawable/ic_minus_red"
+ android:left="@dimen/margin_eighth"
+ android:right="@dimen/margin_eighth"
+ android:bottom="@dimen/margin_eighth"
+ android:top="@dimen/margin_eighth"/>
+</layer-list>
diff --git a/android/res/drawable/ic_plus_green.xml b/android/res/drawable/ic_plus_green.xml
new file mode 100644
index 0000000000..d8e49364af
--- /dev/null
+++ b/android/res/drawable/ic_plus_green.xml
@@ -0,0 +1,8 @@
+<vector android:height="10dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0"
+ android:width="10dp"
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="#FF578B2D"
+ android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
+</vector>
diff --git a/android/res/drawable/ic_positive_review.xml b/android/res/drawable/ic_positive_review.xml
new file mode 100644
index 0000000000..e3fc607c73
--- /dev/null
+++ b/android/res/drawable/ic_positive_review.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:drawable="@drawable/bg_circle_green"
+ android:left="@dimen/margin_eighth"
+ android:right="@dimen/margin_eighth"
+ android:bottom="@dimen/margin_eighth"
+ android:top="@dimen/margin_eighth"/>
+ <item
+ android:drawable="@drawable/ic_plus_green"
+ android:left="@dimen/margin_eighth"
+ android:right="@dimen/margin_eighth"
+ android:bottom="@dimen/margin_eighth"
+ android:top="@dimen/margin_eighth"/>
+</layer-list>
diff --git a/android/res/drawable/ic_positive_review_night.xml b/android/res/drawable/ic_positive_review_night.xml
new file mode 100644
index 0000000000..317ce7719c
--- /dev/null
+++ b/android/res/drawable/ic_positive_review_night.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:drawable="@drawable/bg_circle_green_night"
+ android:left="@dimen/margin_eighth"
+ android:right="@dimen/margin_eighth"
+ android:bottom="@dimen/margin_eighth"
+ android:top="@dimen/margin_eighth"/>
+ <item
+ android:drawable="@drawable/ic_plus_green"
+ android:left="@dimen/margin_eighth"
+ android:right="@dimen/margin_eighth"
+ android:bottom="@dimen/margin_eighth"
+ android:top="@dimen/margin_eighth"/>
+</layer-list>
diff --git a/android/res/layout-w600dp-port/place_page_details.xml b/android/res/layout-w600dp-port/place_page_details.xml
index 790899f741..1e03e0f8e7 100644
--- a/android/res/layout-w600dp-port/place_page_details.xml
+++ b/android/res/layout-w600dp-port/place_page_details.xml
@@ -47,6 +47,12 @@
android:layout_toLeftOf="@id/anchor_center"
android:layout_toStartOf="@id/anchor_center"
android:orientation="vertical">
+ <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_entrance"/>
@@ -82,7 +88,13 @@
<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"/>
+
</LinearLayout>
</RelativeLayout>
diff --git a/android/res/layout/activity_full_screen_gallery.xml b/android/res/layout/activity_full_screen_gallery.xml
new file mode 100644
index 0000000000..69411ca818
--- /dev/null
+++ b/android/res/layout/activity_full_screen_gallery.xml
@@ -0,0 +1,94 @@
+<?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:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@android:color/black">
+
+ <android.support.v4.view.ViewPager
+ android:id="@+id/vp__image"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+
+ <include
+ layout="@layout/toolbar_transparent"/>
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/margin_half"
+ android:layout_marginEnd="@dimen/margin_base"
+ android:layout_marginLeft="@dimen/margin_base"
+ android:layout_marginRight="@dimen/margin_base"
+ android:layout_marginStart="@dimen/margin_base"
+ android:layout_marginTop="@dimen/margin_base"
+ android:layout_gravity="bottom">
+
+ <TextView
+ android:id="@+id/tv__description"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/margin_half"
+ android:textColor="@color/white_primary"
+ android:textAppearance="@style/MwmTextAppearance.Body1"
+ android:maxLines="1"
+ tools:text="Staff, rooftop view, location, free bike…"/>
+
+ <RelativeLayout
+ android:id="@+id/rl__user_block"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+ <View
+ android:id="@+id/divider"
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="@color/divider_gallery"/>
+ <ImageView
+ android:id="@+id/iv__avatar"
+ android:layout_width="@dimen/track_circle_size"
+ android:layout_height="@dimen/track_circle_size"
+ android:layout_marginTop="@dimen/margin_half"
+ android:layout_below="@id/divider"
+ android:layout_marginRight="@dimen/margin_half"
+ android:layout_marginEnd="@dimen/margin_half"
+ tools:src="@drawable/img_editor_medal"/>
+ <TextView
+ android:id="@+id/tv__name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/margin_half"
+ android:layout_toRightOf="@id/iv__avatar"
+ android:layout_toEndOf="@id/iv__avatar"
+ android:layout_below="@id/divider"
+ android:maxLines="1"
+ android:textColor="@color/white_primary"
+ android:textAppearance="@style/MwmTextAppearance.Body1"
+ tools:text="Polina"/>
+ <TextView
+ android:id="@+id/tv__source"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_toRightOf="@id/iv__avatar"
+ android:layout_toEndOf="@id/iv__avatar"
+ android:layout_below="@id/tv__name"
+ android:maxLines="1"
+ android:textColor="@color/white_primary"
+ android:textAppearance="@style/MwmTextAppearance.Body4"
+ tools:text="via Booking"/>
+ <TextView
+ android:id="@+id/tv__date"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_toRightOf="@id/tv__source"
+ android:layout_toEndOf="@id/tv__source"
+ android:layout_alignBaseline="@id/tv__source"
+ android:gravity="end"
+ android:maxLines="1"
+ android:textColor="@color/white_primary"
+ android:textAppearance="@style/MwmTextAppearance.Body4"
+ tools:text="Jule 8, 2016"/>
+ </RelativeLayout>
+ </LinearLayout>
+</FrameLayout>
diff --git a/android/res/layout/fragment_fullscreen_image.xml b/android/res/layout/fragment_fullscreen_image.xml
new file mode 100644
index 0000000000..2894f973c2
--- /dev/null
+++ b/android/res/layout/fragment_fullscreen_image.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@android:color/black">
+
+ <ImageView
+ android:id="@+id/iv__image"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+
+</LinearLayout>
diff --git a/android/res/layout/fragment_gallery.xml b/android/res/layout/fragment_gallery.xml
new file mode 100644
index 0000000000..3e09892448
--- /dev/null
+++ b/android/res/layout/fragment_gallery.xml
@@ -0,0 +1,16 @@
+<?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">
+
+ <android.support.v7.widget.RecyclerView
+ android:id="@+id/rv__gallery"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingLeft="@dimen/margin_half"
+ android:paddingRight="@dimen/margin_half"
+ android:paddingTop="@dimen/margin_half_plus"
+ tools:listitem="@layout/item_image"/>
+</LinearLayout>
diff --git a/android/res/layout/fragment_review.xml b/android/res/layout/fragment_review.xml
new file mode 100644
index 0000000000..a8d0336aee
--- /dev/null
+++ b/android/res/layout/fragment_review.xml
@@ -0,0 +1,13 @@
+<?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"
+ android:background="?cardBackground">
+ <android.support.v7.widget.RecyclerView
+ android:id="@+id/rv__review"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:listitem="@layout/item_comment"/>
+</LinearLayout>
diff --git a/android/res/layout/item_comment.xml b/android/res/layout/item_comment.xml
new file mode 100644
index 0000000000..7497d9c945
--- /dev/null
+++ b/android/res/layout/item_comment.xml
@@ -0,0 +1,122 @@
+<?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:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <View
+ android:id="@+id/v__divider"
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:layout_marginLeft="@dimen/margin_base"
+ android:layout_marginRight="@dimen/margin_base"
+ android:background="?dividerHorizontal"/>
+
+ <TextView
+ android:id="@+id/tv__user_name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="@dimen/margin_base"
+ android:layout_marginStart="@dimen/margin_base"
+ android:layout_marginTop="@dimen/margin_base"
+ android:layout_toLeftOf="@+id/tv__user_rating"
+ android:layout_toStartOf="@+id/tv__user_rating"
+ android:layout_below="@id/v__divider"
+ android:textAppearance="@style/MwmTextAppearance.Body1"
+ tools:text="Аleksey"/>
+
+ <TextView
+ android:id="@+id/tv__comment_date"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="@dimen/margin_base"
+ android:layout_marginStart="@dimen/margin_base"
+ android:layout_marginBottom="@dimen/margin_base"
+ android:layout_below="@id/tv__user_name"
+ android:textAppearance="@style/MwmTextAppearance.Body4"
+ tools:text="March 29, 2016"/>
+
+ <TextView
+ android:id="@+id/tv__user_rating"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginRight="@dimen/margin_base"
+ android:layout_marginEnd="@dimen/margin_base"
+ android:layout_marginTop="@dimen/margin_base"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true"
+ android:layout_below="@id/v__divider"
+ android:textAppearance="@style/MwmTextAppearance.Headline"
+ tools:text="9.2"/>
+
+ <TextView
+ android:id="@+id/tv__review"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="@dimen/margin_base"
+ android:layout_marginStart="@dimen/margin_base"
+ android:layout_marginRight="@dimen/margin_base"
+ android:layout_marginEnd="@dimen/margin_base"
+ android:layout_marginBottom="@dimen/margin_base"
+ android:layout_below="@id/tv__comment_date"
+ android:textAppearance="@style/MwmTextAppearance.Body3.Primary"
+ android:visibility="gone"
+ tools:text="Interesting place among SoHo, Little Italy and China town. Modern design. Great view from roof. Near subway. Free refreshment every afternoon. The staff was very friendly."/>
+
+ <LinearLayout
+ android:id="@+id/ll__positive_review"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/tv__comment_date">
+ <ImageView
+ android:layout_width="@dimen/margin_base_plus"
+ android:layout_height="@dimen/margin_base_plus"
+ android:layout_marginLeft="@dimen/margin_base"
+ android:layout_marginStart="@dimen/margin_base"
+ android:src="?ppPositive"/>
+
+ <TextView
+ android:id="@+id/tv__positive_review"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="@dimen/margin_double"
+ android:layout_marginStart="@dimen/margin_double"
+ android:layout_marginRight="@dimen/margin_base"
+ android:layout_marginEnd="@dimen/margin_base"
+ android:layout_marginBottom="@dimen/margin_base"
+ android:paddingTop="@dimen/margin_eighth"
+ android:textAppearance="@style/MwmTextAppearance.Body3.Primary"
+ tools:text="Interesting place among SoHo, Little Italy and China town. Modern design. Great view from roof. Near subway. Free refreshment every afternoon. The staff was very friendly."/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/ll__negative_review"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/ll__positive_review">
+ <ImageView
+ android:id="@+id/iv__negative_review"
+ android:layout_width="@dimen/margin_base_plus"
+ android:layout_height="@dimen/margin_base_plus"
+ android:layout_marginBottom="@dimen/margin_base"
+ android:layout_marginLeft="@dimen/margin_base"
+ android:layout_marginStart="@dimen/margin_base"
+ android:src="?ppNegative"/>
+
+ <TextView
+ android:id="@+id/tv__negative_review"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="@dimen/margin_double"
+ android:layout_marginStart="@dimen/margin_double"
+ android:layout_marginRight="@dimen/margin_base"
+ android:layout_marginEnd="@dimen/margin_base"
+ android:layout_marginBottom="@dimen/margin_base"
+ android:paddingTop="@dimen/margin_eighth"
+ android:textAppearance="@style/MwmTextAppearance.Body3.Primary"
+ tools:text="Little bit noise from outsideLittle bit noise from outside"/>
+ </LinearLayout>
+
+</RelativeLayout>
diff --git a/android/res/layout/item_facility.xml b/android/res/layout/item_facility.xml
new file mode 100644
index 0000000000..69790a57e5
--- /dev/null
+++ b/android/res/layout/item_facility.xml
@@ -0,0 +1,24 @@
+<?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:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/height_block_base"
+ android:orientation="horizontal"
+ android:gravity="center_vertical"
+ android:background="?clickableBackground"
+ android:clickable="true"
+ tools:visibility="visible">
+
+ <ImageView
+ android:id="@+id/iv__icon"
+ style="@style/PlacePageMetadataIcon"
+ android:src="@drawable/ic_entrance"/>
+
+ <TextView
+ android:id="@+id/tv__facility"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="@style/MwmTextAppearance.Body3.Primary"
+ tools:text="Pets are allowed on request"/>
+</LinearLayout>
diff --git a/android/res/layout/item_gallery.xml b/android/res/layout/item_gallery.xml
new file mode 100644
index 0000000000..ae725d082b
--- /dev/null
+++ b/android/res/layout/item_gallery.xml
@@ -0,0 +1,22 @@
+<?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="@dimen/placepage_hotel_gallery_width"
+ android:layout_height="@dimen/placepage_hotel_gallery_height"
+ android:clickable="true"
+ android:focusable="true"
+ android:foreground="?attr/selectableItemBackground">
+
+ <ImageView
+ android:id="@+id/iv__image"
+ android:layout_width="@dimen/placepage_hotel_gallery_width"
+ android:layout_height="@dimen/placepage_hotel_gallery_height"
+ tools:src="@color/base_green"/>
+
+ <TextView
+ android:id="@+id/tv__more"
+ style="@style/PlacePageGalleryText"
+ android:text="@string/placepage_more_button"
+ android:visibility="gone"
+ tools:visibility="visible"/>
+</FrameLayout>
diff --git a/android/res/layout/item_image.xml b/android/res/layout/item_image.xml
new file mode 100644
index 0000000000..9cc9280539
--- /dev/null
+++ b/android/res/layout/item_image.xml
@@ -0,0 +1,15 @@
+<?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="@dimen/gallery_image_height"
+ android:clickable="true"
+ android:focusable="true"
+ android:foreground="?attr/selectableItemBackground">
+
+ <ImageView
+ android:id="@+id/iv__image"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/gallery_image_height"
+ tools:src="@color/base_green"/>
+</FrameLayout>
diff --git a/android/res/layout/item_more_button.xml b/android/res/layout/item_more_button.xml
new file mode 100644
index 0000000000..8854ec9750
--- /dev/null
+++ b/android/res/layout/item_more_button.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<TextView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ style="@style/PlacePageMetadataText.Button"
+ android:height="@dimen/height_block_base"
+ android:background="?clickableBackground"
+ android:gravity="center"
+ android:text="@string/placepage_more_reviews_button"/>
diff --git a/android/res/layout/item_nearby.xml b/android/res/layout/item_nearby.xml
new file mode 100644
index 0000000000..da838d14bf
--- /dev/null
+++ b/android/res/layout/item_nearby.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:paddingTop="@dimen/margin_half_plus"
+ android:paddingBottom="@dimen/margin_half_plus"
+ android:paddingRight="@dimen/margin_base"
+ android:paddingEnd="@dimen/margin_base"
+ android:paddingLeft="@dimen/margin_base"
+ android:paddingStart="@dimen/margin_base"
+ android:clickable="true"
+ android:focusable="true"
+ android:background="?attr/selectableItemBackground"
+ tools:background="#4000FFFF">
+
+ <ImageView
+ android:id="@+id/iv__icon"
+ android:layout_width="@dimen/placepage_hotel_nearby_icon_size"
+ android:layout_height="@dimen/placepage_hotel_nearby_icon_size"
+ android:layout_centerVertical="true"
+ tools:src="@color/base_green"/>
+
+ <LinearLayout
+ android:id="@+id/ll__info"
+ android:orientation="vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="@dimen/margin_double"
+ android:layout_marginStart="@dimen/margin_double"
+ android:layout_centerVertical="true"
+ android:layout_toRightOf="@id/iv__icon"
+ android:layout_toEndOf="@id/iv__icon"
+ android:gravity="center_vertical">
+
+ <TextView
+ android:id="@+id/tv__title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:maxLines="1"
+ android:textAppearance="@style/MwmTextAppearance.Body1"
+ tools:text="Bowery"/>
+ <TextView
+ android:id="@+id/tv__type"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/margin_quarter"
+ android:maxLines="1"
+ android:textAppearance="@style/MwmTextAppearance.Body3"
+ tools:text="Subway Station"/>
+ </LinearLayout>
+
+ <TextView
+ android:id="@+id/tv__distance"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true"
+ android:layout_centerVertical="true"
+ android:layout_toRightOf="@id/ll__info"
+ android:layout_toEndOf="@id/ll__info"
+ android:maxLines="1"
+ android:textAppearance="@style/MwmTextAppearance.PlacePage.Accent"
+ android:gravity="end"
+ tools:text="800 ft"/>
+</RelativeLayout>
diff --git a/android/res/layout/item_rating.xml b/android/res/layout/item_rating.xml
new file mode 100644
index 0000000000..b079ce2110
--- /dev/null
+++ b/android/res/layout/item_rating.xml
@@ -0,0 +1,29 @@
+<?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="wrap_content"
+ android:paddingTop="@dimen/placepage_margin_rating"
+ android:paddingBottom="@dimen/margin_base"
+ android:paddingLeft="@dimen/margin_base"
+ android:paddingRight="@dimen/margin_base"
+ android:background="?ppRatingBackground">
+
+ <TextView
+ android:id="@+id/tv__place_hotel_rating"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="@style/MwmTextAppearance.Body1"
+ android:textColor="?ppRatingText"
+ tools:text="Rating: 8.7 (Excellent)"/>
+
+ <TextView
+ android:id="@+id/tv__place_hotel_rating_base"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/margin_half"
+ android:textAppearance="@style/MwmTextAppearance.Body3"
+ tools:text="Based on 848 hotel reviews"/>
+</LinearLayout>
diff --git a/android/res/layout/place_page_details.xml b/android/res/layout/place_page_details.xml
index b3b07aa77c..515eac12f6 100644
--- a/android/res/layout/place_page_details.xml
+++ b/android/res/layout/place_page_details.xml
@@ -27,6 +27,12 @@
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"/>
@@ -49,6 +55,11 @@
<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"/>
diff --git a/android/res/layout/place_page_hotel_description.xml b/android/res/layout/place_page_hotel_description.xml
new file mode 100644
index 0000000000..9cd23bdf12
--- /dev/null
+++ b/android/res/layout/place_page_hotel_description.xml
@@ -0,0 +1,43 @@
+<?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:id="@+id/ll__place_hotel_description"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:minHeight="@dimen/height_block_base"
+ android:visibility="gone"
+ tools:background="#20FF0000"
+ tools:visibility="visible">
+
+ <TextView
+ android:layout_marginLeft="@dimen/margin_base"
+ android:layout_marginRight="@dimen/margin_base"
+ android:layout_marginTop="@dimen/margin_base"
+ style="@style/PlacePageTitleText"
+ android:text="@string/details"/>
+
+ <com.mapswithme.maps.widget.LineCountTextView
+ android:id="@+id/tv__place_hotel_details"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="@dimen/margin_base"
+ android:layout_marginRight="@dimen/margin_base"
+ android:layout_marginTop="@dimen/margin_half_plus"
+ android:layout_marginBottom="@dimen/margin_half_plus"
+ android:textAppearance="@style/MwmTextAppearance.Body3.Primary"
+ android:maxLines="@integer/pp_hotel_description_lines"
+ tools:text="One of our top picks in New York City. This boutique hotel in the Manhattan neighborhood of Nolita features a private rooftop and rooms with free WiFi. The Bowery subway station is 1 block from this New York hotel."/>
+
+ <TextView
+ android:id="@+id/tv__place_hotel_more"
+ style="@style/PlacePageMetadataText.Button"
+ android:layout_marginLeft="@dimen/margin_base"
+ android:layout_marginRight="@dimen/margin_base"
+ android:height="@dimen/height_block_base"
+ android:background="?clickableBackground"
+ android:gravity="center"
+ android:text="@string/placepage_more_button"/>
+
+ <include layout="@layout/divider_horizontal"/>
+</LinearLayout>
diff --git a/android/res/layout/place_page_hotel_facilities.xml b/android/res/layout/place_page_hotel_facilities.xml
new file mode 100644
index 0000000000..d126cbbe70
--- /dev/null
+++ b/android/res/layout/place_page_hotel_facilities.xml
@@ -0,0 +1,42 @@
+<?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:id="@+id/ll__place_hotel_facilities"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:minHeight="@dimen/height_block_base"
+ android:visibility="gone"
+ tools:background="#4000FFFF"
+ tools:visibility="visible">
+
+ <TextView
+ android:layout_marginLeft="@dimen/margin_base"
+ android:layout_marginRight="@dimen/margin_base"
+ android:layout_marginTop="@dimen/margin_base"
+ style="@style/PlacePageTitleText"
+ android:text="@string/placepage_hotel_facilities"/>
+
+ <com.mapswithme.maps.widget.StaticGridView
+ android:id="@+id/gv__place_hotel_facilities"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="@dimen/margin_base"
+ android:layout_marginRight="@dimen/margin_base"
+ android:layout_marginTop="@dimen/margin_base"
+ android:layout_marginBottom="@dimen/margin_base"
+ android:numColumns="2"
+ tools:listitem="@layout/item_facility"/>
+
+ <TextView
+ android:id="@+id/tv__place_hotel_facilities_more"
+ style="@style/PlacePageMetadataText.Button"
+ android:layout_marginLeft="@dimen/margin_base"
+ android:layout_marginRight="@dimen/margin_base"
+ android:height="@dimen/height_block_base"
+ android:background="?clickableBackground"
+ android:gravity="center"
+ android:text="@string/placepage_more_button"/>
+
+ <include layout="@layout/divider_horizontal"/>
+</LinearLayout>
diff --git a/android/res/layout/place_page_hotel_gallery.xml b/android/res/layout/place_page_hotel_gallery.xml
new file mode 100644
index 0000000000..15c541325c
--- /dev/null
+++ b/android/res/layout/place_page_hotel_gallery.xml
@@ -0,0 +1,18 @@
+<?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:id="@+id/ll__place_hotel_gallery"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/placepage_hotel_gallery_height"
+ android:orientation="horizontal"
+ android:minHeight="@dimen/placepage_hotel_gallery_height"
+ android:visibility="gone"
+ tools:background="#20FF0000"
+ tools:visibility="visible">
+
+ <android.support.v7.widget.RecyclerView
+ android:id="@+id/rv__place_hotel_gallery"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:listitem="@layout/item_gallery"/>
+</LinearLayout>
diff --git a/android/res/layout/place_page_hotel_nearby.xml b/android/res/layout/place_page_hotel_nearby.xml
new file mode 100644
index 0000000000..ea675113cb
--- /dev/null
+++ b/android/res/layout/place_page_hotel_nearby.xml
@@ -0,0 +1,29 @@
+<?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:id="@+id/ll__place_hotel_nearby"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:minHeight="@dimen/height_block_base"
+ android:visibility="gone"
+ tools:background="#4000FFFF"
+ tools:visibility="visible">
+
+ <include layout="@layout/divider_horizontal"/>
+
+ <TextView
+ android:layout_marginLeft="@dimen/margin_base"
+ android:layout_marginRight="@dimen/margin_base"
+ android:layout_marginTop="@dimen/margin_base"
+ style="@style/PlacePageTitleText"
+ android:text="@string/placepage_hotel_nearby"/>
+
+ <com.mapswithme.maps.widget.StaticGridView
+ android:id="@+id/gv__place_hotel_nearby"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/margin_quarter"
+ android:numColumns="1"
+ tools:listitem="@layout/item_nearby"/>
+</LinearLayout>
diff --git a/android/res/layout/place_page_hotel_rating.xml b/android/res/layout/place_page_hotel_rating.xml
new file mode 100644
index 0000000000..864e64538d
--- /dev/null
+++ b/android/res/layout/place_page_hotel_rating.xml
@@ -0,0 +1,58 @@
+<?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:id="@+id/ll__place_hotel_rating"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:visibility="gone"
+ tools:visibility="visible">
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="@dimen/placepage_margin_rating"
+ android:paddingBottom="@dimen/margin_base"
+ android:paddingLeft="@dimen/margin_base"
+ android:paddingRight="@dimen/margin_base"
+ android:background="?ppRatingBackground">
+
+ <TextView
+ android:id="@+id/tv__place_hotel_rating"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="@style/MwmTextAppearance.Body1"
+ android:textColor="?ppRatingText"
+ tools:text="Rating: 8.7 (Excellent)"/>
+
+ <TextView
+ android:id="@+id/tv__place_hotel_rating_base"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/margin_half"
+ android:textAppearance="@style/MwmTextAppearance.Body3"
+ tools:text="Based on 848 hotel reviews"/>
+ </LinearLayout>
+
+ <com.mapswithme.maps.widget.StaticGridView
+ android:id="@+id/gv__place_hotel_review"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:numColumns="1"
+ tools:listitem="@layout/item_comment"/>
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:layout_marginLeft="@dimen/margin_base"
+ android:layout_marginRight="@dimen/margin_base"
+ android:background="?dividerHorizontal"/>
+ <TextView
+ android:id="@+id/tv__place_hotel_reviews_more"
+ style="@style/PlacePageMetadataText.Button"
+ android:height="@dimen/height_block_base"
+ android:background="?clickableBackground"
+ android:gravity="center"
+ android:text="@string/placepage_more_reviews_button"/>
+</LinearLayout>
diff --git a/android/res/layout/place_page_more.xml b/android/res/layout/place_page_more.xml
index e93771213c..b597a2c134 100644
--- a/android/res/layout/place_page_more.xml
+++ b/android/res/layout/place_page_more.xml
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
+<!--TODO: remove this layout after booking_api.cpp will be done-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/ll__more"
diff --git a/android/res/layout/toolbar_transparent.xml b/android/res/layout/toolbar_transparent.xml
new file mode 100644
index 0000000000..9a18440d8d
--- /dev/null
+++ b/android/res/layout/toolbar_transparent.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.v7.widget.Toolbar
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/toolbar"
+ style="@style/MwmWidget.ToolbarStyle"
+ android:layout_width="match_parent"
+ android:layout_height="?attr/actionBarSize"
+ android:theme="@style/MwmWidget.ToolbarTheme.Transparent"/>
diff --git a/android/res/values-ru/strings.xml b/android/res/values-ru/strings.xml
index 576d2216eb..b6453fce7c 100644
--- a/android/res/values-ru/strings.xml
+++ b/android/res/values-ru/strings.xml
@@ -879,6 +879,7 @@
<string name="minute">мин</string>
<string name="placepage_place_description">Описание</string>
<string name="placepage_more_button">Ещё</string>
+ <string name="placepage_more_reviews_button">Ещё отзывы</string>
<string name="bookingcom_book_button">Забронировать</string>
<string name="placepage_call_button">Позвонить</string>
<string name="placepage_edit_bookmark_button">Редактировать метку</string>
@@ -964,4 +965,8 @@
<string name="whats_new_route_profile_message">На пеших и веломаршрутах отображается профиль рельефа.</string>
<string name="whats_new_booking_improve_title">Экономь на бронировании отеля</string>
<string name="whats_new_booking_improve_message">Результаты поиска отелей на карте показывают ценовую категорию.\nОтелей для бронирования стало на 110 000 больше.</string>
+ <!-- For place page hotel facilities block -->
+ <string name="placepage_hotel_facilities">Удобства</string>
+ <!-- For place page hotel nearby block -->
+ <string name="placepage_hotel_nearby">Рядом</string>
</resources>
diff --git a/android/res/values-v16/strings.xml b/android/res/values-v16/strings.xml
new file mode 100644
index 0000000000..b7ddcc4b0b
--- /dev/null
+++ b/android/res/values-v16/strings.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Android Strings File -->
+<!-- Generated by Twine 0.6.0 -->
+<!-- Language: v16 -->
+<resources>
+ <!-- SECTION: Strings -->
+
+ <!-- SECTION: Routing dialogs strings -->
+
+ <!-- SECTION: Strings for downloading map from search -->
+</resources>
diff --git a/android/res/values-v16/styles-text.xml b/android/res/values-v16/styles-text.xml
new file mode 100644
index 0000000000..03e6be2368
--- /dev/null
+++ b/android/res/values-v16/styles-text.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <style name="MwmTextAppearance.PlacePage.Title"
+ parent="MwmTextAppearance.Body3">
+ <item name="android:fontFamily">sans-serif-medium</item>
+ </style>
+</resources>
diff --git a/android/res/values-w840dp/strings.xml b/android/res/values-w840dp/strings.xml
new file mode 100644
index 0000000000..388be69c77
--- /dev/null
+++ b/android/res/values-w840dp/strings.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Android Strings File -->
+<!-- Generated by Twine 0.6.0 -->
+<!-- Language: w840dp -->
+<resources>
+ <!-- SECTION: Strings -->
+
+ <!-- SECTION: Routing dialogs strings -->
+
+ <!-- SECTION: Strings for downloading map from search -->
+</resources>
diff --git a/android/res/values/colors.xml b/android/res/values/colors.xml
index da1c4a3062..0e9f2bfe07 100644
--- a/android/res/values/colors.xml
+++ b/android/res/values/colors.xml
@@ -32,6 +32,10 @@
<color name="divider">#1E000000</color>
<color name="divider_night">#1EFFFFFF</color>
+ <color name="divider_gallery">#66FFFFFF</color>
+
+ <color name="text_placepage_rating">#568B2E</color>
+ <color name="text_placepage_rating_night">#46A046</color>
<!-- Backgrounds -->
<color name="bg_window">#FFEEEEEE</color>
@@ -59,6 +63,13 @@
<color name="bg_azimut_arrow">#1D414651</color>
<color name="fg_azimut_arrow">#FFFFFFFF</color>
+ <color name="bg_placepage_rating">#F1F8E9</color>
+ <color name="bg_placepage_rating_night">#1EF0FAEB</color>
+ <color name="bg_placepage_rating_positive">#DCEDC8</color>
+ <color name="bg_placepage_rating_positive_night">#1EF0FAEB</color>
+ <color name="bg_placepage_rating_negative">#FFCDD2</color>
+ <color name="bg_placepage_rating_negative_night">#1EFFEBF0</color>
+
<!-- Buttons -->
<color name="button">@color/button_normal</color>
<color name="button_night">@color/button_normal_night</color>
diff --git a/android/res/values/dimens.xml b/android/res/values/dimens.xml
index 6910b5b8ae..c5d743cc34 100644
--- a/android/res/values/dimens.xml
+++ b/android/res/values/dimens.xml
@@ -145,6 +145,16 @@
<dimen name="altitude_chart_image_height">40dp</dimen>
<dimen name="altitude_chart_image_width">232dp</dimen>
+ <!-- Gallery-->
+ <dimen name="placepage_hotel_gallery_height">100dp</dimen>
+ <dimen name="placepage_hotel_gallery_width">150dp</dimen>
+ <dimen name="gallery_image_height">84dp</dimen>
+ <!-- Nearby-->
+ <dimen name="placepage_hotel_nearby_height">64dp</dimen>
+ <dimen name="placepage_hotel_nearby_icon_size">24dp</dimen>
+
+ <!-- Rating-->
+ <dimen name="placepage_margin_rating">20dp</dimen>
</resources>
diff --git a/android/res/values/integer.xml b/android/res/values/integer.xml
index 1eb091d22b..cebfdf8b74 100644
--- a/android/res/values/integer.xml
+++ b/android/res/values/integer.xml
@@ -2,6 +2,7 @@
<resources>
<integer name="pp_title_lines">5</integer>
<integer name="pp_buttons_max">4</integer>
+ <integer name="pp_hotel_description_lines">5</integer>
<integer name="sharing_initial_rows">2</integer>
-</resources> \ No newline at end of file
+</resources>
diff --git a/android/res/values/plurals.xml b/android/res/values/plurals.xml
new file mode 100644
index 0000000000..92249cb081
--- /dev/null
+++ b/android/res/values/plurals.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <plurals name="place_page_booking_rating_base">
+ <item quantity="zero">Based on %d hotel reviews</item>
+ <item quantity="one">Based on %d hotel reviews</item>
+ <item quantity="two">Based on %d hotel reviews</item>
+ <item quantity="few">Based on %d hotel reviews</item>
+ <item quantity="many">Based on %d hotel reviews</item>
+ <item quantity="other">Based on %d hotel reviews</item>
+ </plurals>
+</resources>
diff --git a/android/res/values/strings.xml b/android/res/values/strings.xml
index 605fa4643d..c2620d0202 100644
--- a/android/res/values/strings.xml
+++ b/android/res/values/strings.xml
@@ -883,6 +883,7 @@
<string name="minute">min</string>
<string name="placepage_place_description">Description</string>
<string name="placepage_more_button">More</string>
+ <string name="placepage_more_reviews_button">More Reviews</string>
<string name="bookingcom_book_button">Book</string>
<string name="placepage_call_button">Call</string>
<string name="placepage_edit_bookmark_button">Edit Bookmark</string>
@@ -968,4 +969,8 @@
<string name="whats_new_route_profile_message">For pedestrian and bike routes we now display the elevation profile.</string>
<string name="whats_new_booking_improve_title">Save when booking hotels</string>
<string name="whats_new_booking_improve_message">Search results for hotels now contain the price category.\nWe also added more than 110,000 hotels.</string>
+ <!-- For place page hotel facilities block -->
+ <string name="placepage_hotel_facilities">Facilities</string>
+ <!-- For place page hotel nearby block -->
+ <string name="placepage_hotel_nearby">Nearby</string>
</resources>
diff --git a/android/res/values/styles-place_page.xml b/android/res/values/styles-place_page.xml
index ce734846a6..6feb7dc81a 100644
--- a/android/res/values/styles-place_page.xml
+++ b/android/res/values/styles-place_page.xml
@@ -59,6 +59,23 @@
<item name="android:textAppearance">@style/MwmTextAppearance.PlacePage.Accent</item>
</style>
+ <style name="PlacePageTitleText">
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:textAppearance">@style/MwmTextAppearance.PlacePage.Title</item>
+ </style>
+
+ <style name="PlacePageGalleryText">
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:layout_gravity">center</item>
+ <item name="android:gravity">center</item>
+ <item name="android:textColor">@color/white_primary</item>
+ <item name="android:textAllCaps">true</item>
+ <item name="android:drawableRight">@drawable/ic_chevron_right_white</item>
+ <item name="android:drawableEnd" tools:targetApi="jelly_bean_mr1">@drawable/ic_chevron_right_white</item>
+ </style>
+
<style name="PlacePageMetadataIcon">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
@@ -66,4 +83,4 @@
<item name="android:layout_marginEnd" tools:targetApi="jelly_bean_mr1">@dimen/margin_base</item>
<item name="android:layout_marginRight">@dimen/margin_base</item>
</style>
-</resources> \ No newline at end of file
+</resources>
diff --git a/android/res/values/styles-text.xml b/android/res/values/styles-text.xml
index fc7ebe23a0..dc016d2b54 100644
--- a/android/res/values/styles-text.xml
+++ b/android/res/values/styles-text.xml
@@ -53,6 +53,11 @@
<item name="android:textColor">?android:textColorSecondary</item>
</style>
+ <style name="MwmTextAppearance.Body3.Primary">
+ <item name="android:textSize">@dimen/text_size_body_3</item>
+ <item name="android:textColor">?android:textColorPrimary</item>
+ </style>
+
<style name="MwmTextAppearance.Body3.Light">
<item name="android:textColor">?android:textColorPrimaryInverse</item>
</style>
@@ -116,6 +121,10 @@
<item name="android:textStyle">bold</item>
</style>
+ <style name="MwmTextAppearance.RoutingDimension.Inline" parent="MwmTextAppearance.RoutingNumber">
+ <item name="android:textSize">@dimen/text_size_routing_dimension_inline</item>
+ </style>
+
<style name="MwmTextAppearance.RoutingDetail">
<item name="android:textSize">@dimen/text_size_routing_plan_detail</item>
<item name="android:fontFamily" tools:ignore="NewApi">@string/robotoMedium</item>
@@ -143,13 +152,14 @@
<item name="android:textSize">@dimen/text_size_nav_circle_exit</item>
</style>
- <style name="MwmTextAppearance.PlacePage"
- parent="MwmTextAppearance.Body1"/>
+ <style name="MwmTextAppearance.PlacePage" parent="MwmTextAppearance.Body1"/>
<style name="MwmTextAppearance.PlacePage.Accent">
<item name="android:textColor">?colorAccent</item>
</style>
+ <style name="MwmTextAppearance.PlacePage.Title" parent="MwmTextAppearance.Body3"/>
+
<style name="MwmTextAppearance.Editor">
</style>
@@ -161,4 +171,4 @@
<style name="MwmTextAppearance.Editor.Buttons">
<item name="android:textSize">@dimen/text_size_body_3</item>
</style>
-</resources> \ No newline at end of file
+</resources>
diff --git a/android/res/values/styles.xml b/android/res/values/styles.xml
index 843ebfb231..56c21826f4 100644
--- a/android/res/values/styles.xml
+++ b/android/res/values/styles.xml
@@ -16,8 +16,7 @@
<item name="android:layout_marginLeft">0dp</item>
</style>
- <style name="MwmWidget.MapButton"
- parent="android:Widget.ImageButton">
+ <style name="MwmWidget.MapButton" parent="android:Widget.ImageButton">
<item name="android:scaleType">center</item>
<item name="android:layout_height">64dp</item>
<item name="android:layout_width">64dp</item>
@@ -53,73 +52,56 @@
</style>
<style name="MwmWidget.Floating">
- <item name="android:elevation"
- tools:ignore="NewApi">@dimen/appbar_elevation
- </item>
+ <item name="android:elevation" tools:ignore="NewApi">@dimen/appbar_elevation</item>
</style>
<style name="MwmWidget.Floating.Panel">
<item name="android:background">?panel</item>
</style>
- <style
- name="MwmWidget.PlacePage.EditText"
- parent="Widget.AppCompat.EditText">
+ <style name="MwmWidget.PlacePage.EditText" parent="Widget.AppCompat.EditText">
<item name="android:imeOptions">actionDone</item>
<item name="android:textAppearance">@style/MwmTextAppearance.PlacePage</item>
<item name="android:textColorHint">?secondary</item>
<item name="android:textCursorDrawable">@null</item>
- <item name="android:fontFamily"
- tools:ignore="NewApi">@string/robotoRegular
- </item>
+ <item name="android:fontFamily" tools:ignore="NewApi">@string/robotoRegular</item>
</style>
- <style
- name="MwmWidget.ToolbarStyle"
- parent="ThemeOverlay.AppCompat.Dark.ActionBar">
+ <style name="MwmWidget.ToolbarStyle" parent="ThemeOverlay.AppCompat.Dark.ActionBar">
<item name="android:background">?colorPrimary</item>
- <item name="android:elevation"
- tools:ignore="NewApi">@dimen/appbar_elevation
- </item>
+ <item name="android:elevation" tools:ignore="NewApi">@dimen/appbar_elevation</item>
<item name="android:displayOptions">homeAsUp|showTitle</item>
<item name="contentInsetStart">72dp</item>
- <item name="android:titleTextAppearance"
- tools:ignore="NewApi">@style/MwmTextAppearance.Toolbar.Title
- </item>
+ <item name="android:titleTextAppearance" tools:ignore="NewApi">@style/MwmTextAppearance.Toolbar.Title</item>
<item name="titleTextAppearance">@style/MwmTextAppearance.Toolbar.Title</item>
<item name="contentInsetLeft">72dp</item>
- <item name="android:contentInsetStart"
- tools:ignore="NewApi">72dp
- </item>
- <item name="android:contentInsetLeft"
- tools:ignore="NewApi">72dp
- </item>
+ <item name="android:contentInsetStart" tools:ignore="NewApi">72dp</item>
+ <item name="android:contentInsetLeft" tools:ignore="NewApi">72dp</item>
</style>
<style name="MwmWidget.ToolbarStyle.Light">
- <item name="android:titleTextAppearance"
- tools:targetApi="lollipop">@style/MwmTextAppearance.Toolbar.Title.Light
- </item>
+ <item name="android:titleTextAppearance" tools:targetApi="lollipop">@style/MwmTextAppearance.Toolbar.Title.Light</item>
<item name="titleTextAppearance">@style/MwmTextAppearance.Toolbar.Title.Light</item>
</style>
- <style
- name="MwmWidget.ToolbarTheme"
- parent="ThemeOverlay.AppCompat.Dark.ActionBar">
+ <style name="MwmWidget.ToolbarTheme" parent="ThemeOverlay.AppCompat.Dark.ActionBar">
<item name="android:gravity">center_vertical</item>
<item name="colorAccent">@android:color/white</item>
</style>
- <style
- name="MwmWidget.ToolbarTheme.Light"
- parent="ThemeOverlay.AppCompat.ActionBar">
+ <style name="MwmWidget.ToolbarTheme.Light" parent="ThemeOverlay.AppCompat.ActionBar">
<item name="android:gravity">center_vertical</item>
<item name="colorAccent">@color/bg_window_night</item>
</style>
- <style
- name="MwmWidget.ListView"
- parent="Widget.AppCompat.ListView">
+ <style name="MwmWidget.ToolbarTheme.Transparent" parent="ThemeOverlay.AppCompat.Dark.ActionBar">
+ <item name="android:gravity">center_vertical</item>
+ <item name="colorAccent">@android:color/white</item>
+ <item name="android:windowActionBarOverlay">true</item>
+ <item name="windowActionBarOverlay">true</item>
+ </style>
+
+ <style name="MwmWidget.ListView" parent="Widget.AppCompat.ListView">
<item name="android:fadingEdge">none</item>
<item name="android:divider">@color/divider</item>
<item name="android:background">@null</item>
@@ -129,8 +111,7 @@
<item name="android:cacheColorHint">@android:color/transparent</item>
</style>
- <style name="MwmWidget.TextView"
- parent="android:Widget.TextView">
+ <style name="MwmWidget.TextView" parent="android:Widget.TextView">
<item name="android:background">@android:color/transparent</item>
</style>
@@ -203,8 +184,7 @@
<item name="android:foreground">@drawable/shadow_top</item>
</style>
- <style name="MwmWidget.RatingBar"
- parent="android:Widget.RatingBar">
+ <style name="MwmWidget.RatingBar" parent="android:Widget.RatingBar">
<item name="android:progressDrawable">@drawable/rating_bar</item>
<item name="android:indeterminateDrawable">@drawable/rating_bar</item>
</style>
@@ -240,9 +220,7 @@
<item name="android:textAppearance">@style/MwmTextAppearance.Toolbar.Title.Button</item>
</style>
- <style
- name="MwmWidget.SearchNavigationButton"
- parent="android:Widget.ImageButton">
+ <style name="MwmWidget.SearchNavigationButton" parent="android:Widget.ImageButton">
<item name="android:scaleType">center</item>
<item name="android:layout_height">44dp</item>
<item name="android:layout_width">44dp</item>
diff --git a/android/res/values/themes-attrs.xml b/android/res/values/themes-attrs.xml
index da4c68bd8e..ea638e55b7 100644
--- a/android/res/values/themes-attrs.xml
+++ b/android/res/values/themes-attrs.xml
@@ -24,6 +24,10 @@
<attr name="ppPreviewHeadOpen" format="reference"/>
<attr name="ppPreviewHeadClosed" format="reference"/>
<attr name="ppArrowDrawable" format="reference"/>
+ <attr name="ppRatingBackground" format="color"/>
+ <attr name="ppRatingText" format="color"/>
+ <attr name="ppPositive" format="reference"/>
+ <attr name="ppNegative" format="reference"/>
<attr name="routingSlot" format="reference"/>
<attr name="routingSlotPressed" format="reference"/>
diff --git a/android/res/values/themes-base.xml b/android/res/values/themes-base.xml
index be39a44486..bc57ba64b7 100644
--- a/android/res/values/themes-base.xml
+++ b/android/res/values/themes-base.xml
@@ -75,6 +75,11 @@
<item name="routingButtonHint">@color/routing_button_tint</item>
<item name="routingButtonPressedHint">@color/routing_button_pressed_tint</item>
+
+ <item name="ppRatingBackground">@color/bg_placepage_rating</item>
+ <item name="ppRatingText">@color/text_placepage_rating</item>
+ <item name="ppPositive">@drawable/ic_positive_review</item>
+ <item name="ppNegative">@drawable/ic_negative_review</item>
</style>
<!-- Night theme -->
@@ -152,5 +157,10 @@
<item name="routingButtonHint">@color/routing_button_tint</item>
<item name="routingButtonPressedHint">@color/routing_button_pressed_tint</item>
+
+ <item name="ppRatingBackground">@color/bg_placepage_rating_night</item>
+ <item name="ppRatingText">@color/text_placepage_rating_night</item>
+ <item name="ppPositive">@drawable/ic_positive_review_night</item>
+ <item name="ppNegative">@drawable/ic_negative_review_night</item>
</style>
</resources> \ No newline at end of file
diff --git a/android/res/values/themes.xml b/android/res/values/themes.xml
index fa9c2df6fd..25c60b246b 100644
--- a/android/res/values/themes.xml
+++ b/android/res/values/themes.xml
@@ -153,4 +153,24 @@
<item name="newsMarker">@drawable/news_marker_night</item>
</style>
-</resources> \ No newline at end of file
+ <style name="MwmTheme.FullScreenGalleryActivity">
+ <item name="colorPrimary">@android:color/transparent</item>
+ <item name="android:colorPrimaryDark" tools:targetApi="lollipop">@android:color/black</item>
+ <item name="android:timePickerStyle" tools:targetApi="lollipop">@style/MwmWidget.Editor.TimePicker</item>
+ <item name="android:windowBackground">@null</item>
+ <item name="windowActionBar">false</item>
+ <item name="windowActionBarOverlay">true</item>
+ <item name="android:windowActionBarOverlay">true</item>
+ </style>
+
+ <style name="MwmTheme.Night.FullScreenGalleryActivity">
+ <item name="colorPrimary">@android:color/transparent</item>
+ <item name="android:colorPrimaryDark" tools:targetApi="lollipop">@android:color/black</item>
+ <item name="android:timePickerStyle" tools:targetApi="lollipop">@style/MwmWidget.Editor.TimePicker</item>
+ <item name="android:windowBackground">@null</item>
+ <item name="windowActionBar">false</item>
+ <item name="windowActionBarOverlay">true</item>
+ <item name="android:windowActionBarOverlay">true</item>
+ </style>
+
+</resources>
diff --git a/android/src/com/mapswithme/maps/base/BaseMwmExtraTitleActivity.java b/android/src/com/mapswithme/maps/base/BaseMwmExtraTitleActivity.java
new file mode 100644
index 0000000000..13b2fbbfb1
--- /dev/null
+++ b/android/src/com/mapswithme/maps/base/BaseMwmExtraTitleActivity.java
@@ -0,0 +1,43 @@
+package com.mapswithme.maps.base;
+
+import android.os.Bundle;
+import android.support.annotation.CallSuper;
+import android.support.v7.widget.Toolbar;
+
+import com.mapswithme.maps.R;
+import com.mapswithme.util.UiUtils;
+
+public class BaseMwmExtraTitleActivity extends BaseMwmFragmentActivity
+{
+ protected static final String EXTRA_TITLE = "activity_title";
+
+ @Override
+ @CallSuper
+ protected void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+
+ String title = "";
+ Bundle bundle = getIntent().getExtras();
+ if (bundle != null)
+ {
+ title = bundle.getString(EXTRA_TITLE);
+ }
+ Toolbar toolbar = getToolbar();
+ toolbar.setTitle(title);
+ UiUtils.showHomeUpButton(toolbar);
+ displayToolbarAsActionBar();
+ }
+
+ @Override
+ protected int getContentLayoutResId()
+ {
+ return R.layout.activity_fragment_and_toolbar;
+ }
+
+ @Override
+ protected int getFragmentContentResId()
+ {
+ return R.id.fragment_container;
+ }
+}
diff --git a/android/src/com/mapswithme/maps/gallery/FullScreenGalleryActivity.java b/android/src/com/mapswithme/maps/gallery/FullScreenGalleryActivity.java
new file mode 100644
index 0000000000..96c2d13620
--- /dev/null
+++ b/android/src/com/mapswithme/maps/gallery/FullScreenGalleryActivity.java
@@ -0,0 +1,204 @@
+package com.mapswithme.maps.gallery;
+
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
+import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory;
+import android.support.v4.view.ViewPager;
+import android.support.v7.widget.Toolbar;
+import android.text.TextUtils;
+import android.text.format.DateFormat;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.request.target.BitmapImageViewTarget;
+import com.mapswithme.maps.R;
+import com.mapswithme.maps.base.BaseMwmFragmentActivity;
+import com.mapswithme.util.ThemeUtils;
+import com.mapswithme.util.UiUtils;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+public class FullScreenGalleryActivity extends BaseMwmFragmentActivity
+ implements ViewPager.OnPageChangeListener
+{
+ public static final String EXTRA_IMAGES = "gallery_images";
+ public static final String EXTRA_POSITION = "gallery_position";
+
+ private List<Image> mImages;
+ private int mPosition;
+ private View mUserBlock;
+ private TextView mDescription;
+ private TextView mUserName;
+ private TextView mSource;
+ private TextView mDate;
+ private ImageView mAvatar;
+
+ private GalleryPageAdapter mGalleryPageAdapter;
+
+ public static void start(Context context, ArrayList<Image> images, int position)
+ {
+ final Intent i = new Intent(context, FullScreenGalleryActivity.class);
+ i.putParcelableArrayListExtra(EXTRA_IMAGES, images);
+ i.putExtra(EXTRA_POSITION, position);
+ context.startActivity(i);
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState)
+ {
+ requestWindowFeature(Window.FEATURE_NO_TITLE);
+ getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
+
+ super.onCreate(savedInstanceState);
+ Toolbar toolbar = getToolbar();
+ toolbar.setTitle("");
+ UiUtils.showHomeUpButton(toolbar);
+ displayToolbarAsActionBar();
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
+
+ mUserBlock = findViewById(R.id.rl__user_block);
+ mDescription = (TextView) findViewById(R.id.tv__description);
+ mUserName = (TextView) findViewById(R.id.tv__name);
+ mSource = (TextView) findViewById(R.id.tv__source);
+ mDate = (TextView) findViewById(R.id.tv__date);
+ mAvatar = (ImageView) findViewById(R.id.iv__avatar);
+
+ readParameters();
+ if (mImages != null)
+ {
+ mGalleryPageAdapter = new GalleryPageAdapter(getSupportFragmentManager(), mImages);
+ final ViewPager viewPager = (ViewPager) findViewById(R.id.vp__image);
+ viewPager.addOnPageChangeListener(this);
+ viewPager.setAdapter(mGalleryPageAdapter);
+ viewPager.setCurrentItem(mPosition);
+ viewPager.post(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ onPageSelected(viewPager.getCurrentItem());
+ }
+ });
+ }
+ }
+
+ @Override
+ public int getThemeResourceId(String theme)
+ {
+ if (ThemeUtils.isDefaultTheme(theme))
+ return R.style.MwmTheme_FullScreenGalleryActivity;
+
+ if (ThemeUtils.isNightTheme(theme))
+ return R.style.MwmTheme_Night_FullScreenGalleryActivity;
+
+ throw new IllegalArgumentException("Attempt to apply unsupported theme: " + theme);
+ }
+
+ @Override
+ protected int getContentLayoutResId()
+ {
+ return R.layout.activity_full_screen_gallery;
+ }
+
+ @Override
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
+ {
+ }
+
+ @Override
+ public void onPageSelected(int position)
+ {
+ updateInformation(mGalleryPageAdapter.getImage(position));
+ }
+
+ @Override
+ public void onPageScrollStateChanged(int state)
+ {
+ }
+
+ private void readParameters()
+ {
+ Bundle extras = getIntent().getExtras();
+ if (extras != null)
+ {
+ mImages = extras.getParcelableArrayList(EXTRA_IMAGES);
+ mPosition = extras.getInt(EXTRA_POSITION);
+ }
+ }
+
+ private void updateInformation(@NonNull Image image)
+ {
+ UiUtils.setTextAndHideIfEmpty(mDescription, image.getDescription());
+ UiUtils.setTextAndHideIfEmpty(mUserName, image.getUserName());
+ UiUtils.setTextAndHideIfEmpty(mSource, image.getSource());
+ updateDate(image);
+ updateUserAvatar(image);
+ updateUserBlock();
+ }
+
+ private void updateDate(Image image)
+ {
+ if (image.getDate() != null)
+ {
+ Date date = new Date(image.getDate());
+ mDate.setText(DateFormat.getMediumDateFormat(this).format(date));
+ UiUtils.show(mDate);
+ }
+ else
+ {
+ UiUtils.hide(mDate);
+ }
+ }
+
+ private void updateUserAvatar(Image image)
+ {
+ if (!TextUtils.isEmpty(image.getUserAvatar()))
+ {
+ UiUtils.show(mAvatar);
+ Glide.with(this)
+ .load(image.getUserAvatar())
+ .asBitmap()
+ .centerCrop()
+ .into(new BitmapImageViewTarget(mAvatar)
+ {
+ @Override
+ protected void setResource(Bitmap resource)
+ {
+ RoundedBitmapDrawable circularBitmapDrawable =
+ RoundedBitmapDrawableFactory.create(getResources(), resource);
+ circularBitmapDrawable.setCircular(true);
+ mAvatar.setImageDrawable(circularBitmapDrawable);
+ }
+ });
+ }
+ else
+ UiUtils.hide(mAvatar);
+ }
+
+ private void updateUserBlock()
+ {
+ if (UiUtils.isHidden(mUserName)
+ && UiUtils.isHidden(mSource)
+ && UiUtils.isHidden(mDate)
+ && UiUtils.isHidden(mAvatar))
+ {
+ UiUtils.hide(mUserBlock);
+ }
+ else
+ {
+ UiUtils.show(mUserBlock);
+ }
+ }
+}
diff --git a/android/src/com/mapswithme/maps/gallery/FullScreenGalleryFragment.java b/android/src/com/mapswithme/maps/gallery/FullScreenGalleryFragment.java
new file mode 100644
index 0000000000..465ff4f515
--- /dev/null
+++ b/android/src/com/mapswithme/maps/gallery/FullScreenGalleryFragment.java
@@ -0,0 +1,50 @@
+package com.mapswithme.maps.gallery;
+
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+import com.bumptech.glide.Glide;
+import com.mapswithme.maps.R;
+import com.mapswithme.maps.base.BaseMwmFragment;
+
+public class FullScreenGalleryFragment extends BaseMwmFragment
+{
+ static final String ARGUMENT_IMAGE = "argument_image";
+
+ @Nullable
+ private Image mImage;
+
+ @Nullable
+ @Override
+ public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState)
+ {
+ return inflater.inflate(R.layout.fragment_fullscreen_image, container, false);
+ }
+
+ @Override
+ public void onViewCreated(View view, @Nullable Bundle savedInstanceState)
+ {
+ super.onViewCreated(view, savedInstanceState);
+ readArguments();
+
+ if (mImage != null)
+ {
+ ImageView imageView = (ImageView) view.findViewById(R.id.iv__image);
+ Glide.with(view.getContext())
+ .load(mImage.getUrl())
+ .into(imageView);
+ }
+ }
+
+ private void readArguments()
+ {
+ Bundle args = getArguments();
+ if (args != null)
+ mImage = args.getParcelable(ARGUMENT_IMAGE);
+ }
+}
diff --git a/android/src/com/mapswithme/maps/gallery/GalleryActivity.java b/android/src/com/mapswithme/maps/gallery/GalleryActivity.java
new file mode 100644
index 0000000000..bd9b7b6805
--- /dev/null
+++ b/android/src/com/mapswithme/maps/gallery/GalleryActivity.java
@@ -0,0 +1,29 @@
+package com.mapswithme.maps.gallery;
+
+import android.content.Context;
+import android.content.Intent;
+import android.support.annotation.NonNull;
+import android.support.v4.app.Fragment;
+
+import com.mapswithme.maps.base.BaseMwmExtraTitleActivity;
+
+import java.util.ArrayList;
+
+public class GalleryActivity extends BaseMwmExtraTitleActivity
+{
+ public static final String EXTRA_IMAGES = "gallery_images";
+
+ public static void start(Context context, @NonNull ArrayList<Image> images, @NonNull String title)
+ {
+ final Intent i = new Intent(context, GalleryActivity.class);
+ i.putParcelableArrayListExtra(EXTRA_IMAGES, images);
+ i.putExtra(EXTRA_TITLE, title);
+ context.startActivity(i);
+ }
+
+ @Override
+ protected Class<? extends Fragment> getFragmentClass()
+ {
+ return GalleryFragment.class;
+ }
+}
diff --git a/android/src/com/mapswithme/maps/gallery/GalleryFragment.java b/android/src/com/mapswithme/maps/gallery/GalleryFragment.java
new file mode 100644
index 0000000000..c1c02c8f5d
--- /dev/null
+++ b/android/src/com/mapswithme/maps/gallery/GalleryFragment.java
@@ -0,0 +1,65 @@
+package com.mapswithme.maps.gallery;
+
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v4.content.ContextCompat;
+import android.support.v7.widget.GridLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.mapswithme.maps.R;
+import com.mapswithme.maps.base.BaseMwmFragment;
+import com.mapswithme.maps.widget.recycler.GridDividerItemDecoration;
+import com.mapswithme.maps.widget.recycler.RecyclerClickListener;
+
+import java.util.ArrayList;
+
+public class GalleryFragment extends BaseMwmFragment implements RecyclerClickListener
+{
+ private static final int NUM_COLUMNS = 3;
+
+ @Nullable
+ private ArrayList<Image> mImages;
+
+ @Nullable
+ @Override
+ public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState)
+ {
+ return inflater.inflate(R.layout.fragment_gallery, container, false);
+ }
+
+ @Override
+ public void onViewCreated(View view, @Nullable Bundle savedInstanceState)
+ {
+ super.onViewCreated(view, savedInstanceState);
+ readArguments();
+
+ if (mImages != null)
+ {
+ RecyclerView rvGallery = (RecyclerView) view.findViewById(R.id.rv__gallery);
+ rvGallery.setLayoutManager(new GridLayoutManager(getContext(), NUM_COLUMNS));
+ rvGallery.setAdapter(new ImageAdapter(mImages, this));
+ Drawable divider = ContextCompat.getDrawable(getContext(), R.drawable.divider_transparent);
+ rvGallery.addItemDecoration(new GridDividerItemDecoration(divider, divider, NUM_COLUMNS));
+ }
+ }
+
+ private void readArguments()
+ {
+ final Bundle arguments = getArguments();
+ if (arguments == null)
+ return;
+
+ mImages = arguments.getParcelableArrayList(GalleryActivity.EXTRA_IMAGES);
+ }
+
+ @Override
+ public void onItemClick(View v, int position)
+ {
+ FullScreenGalleryActivity.start(getContext(), mImages, position);
+ }
+}
diff --git a/android/src/com/mapswithme/maps/gallery/GalleryPageAdapter.java b/android/src/com/mapswithme/maps/gallery/GalleryPageAdapter.java
new file mode 100644
index 0000000000..14724038d5
--- /dev/null
+++ b/android/src/com/mapswithme/maps/gallery/GalleryPageAdapter.java
@@ -0,0 +1,44 @@
+package com.mapswithme.maps.gallery;
+
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentStatePagerAdapter;
+
+import java.util.List;
+
+class GalleryPageAdapter extends FragmentStatePagerAdapter
+{
+
+ @NonNull
+ private final List<Image> mImages;
+
+ GalleryPageAdapter(@NonNull FragmentManager fm, @NonNull List<Image> images)
+ {
+ super(fm);
+ mImages = images;
+ }
+
+ @Override
+ public Fragment getItem(int position)
+ {
+ Bundle args = new Bundle();
+ args.putParcelable(FullScreenGalleryFragment.ARGUMENT_IMAGE, mImages.get(position));
+ FullScreenGalleryFragment fragment = new FullScreenGalleryFragment();
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ @Override
+ public int getCount()
+ {
+ return mImages.size();
+ }
+
+ @NonNull
+ Image getImage(int position)
+ {
+ return mImages.get(position);
+ }
+}
diff --git a/android/src/com/mapswithme/maps/gallery/Image.java b/android/src/com/mapswithme/maps/gallery/Image.java
new file mode 100644
index 0000000000..8812814779
--- /dev/null
+++ b/android/src/com/mapswithme/maps/gallery/Image.java
@@ -0,0 +1,144 @@
+package com.mapswithme.maps.gallery;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+
+public class Image implements Parcelable
+{
+ @NonNull
+ private final String mUrl;
+ @NonNull
+ private final String mSmallUrl;
+ @Nullable
+ private String mDescription;
+ @Nullable
+ private String mUserName;
+ @Nullable
+ private String mUserAvatar;
+ @Nullable
+ private String mSource;
+ @Nullable
+ private Long mDate;
+
+ @SuppressWarnings("unused")
+ public Image(@NonNull String url, @NonNull String smallUrl)
+ {
+ this.mUrl = url;
+ this.mSmallUrl = smallUrl;
+ }
+
+ protected Image(Parcel in)
+ {
+ mUrl = in.readString();
+ mSmallUrl = in.readString();
+ mDescription = in.readString();
+ mUserName = in.readString();
+ mUserAvatar = in.readString();
+ mSource = in.readString();
+ mDate = (Long) in.readValue(Long.class.getClassLoader());
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags)
+ {
+ dest.writeString(mUrl);
+ dest.writeString(mSmallUrl);
+ dest.writeString(mDescription);
+ dest.writeString(mUserName);
+ dest.writeString(mUserAvatar);
+ dest.writeString(mSource);
+ dest.writeValue(mDate);
+ }
+
+ @Override
+ public int describeContents()
+ {
+ return 0;
+ }
+
+ public static final Creator<Image> CREATOR = new Creator<Image>()
+ {
+ @Override
+ public Image createFromParcel(Parcel in)
+ {
+ return new Image(in);
+ }
+
+ @Override
+ public Image[] newArray(int size)
+ {
+ return new Image[size];
+ }
+ };
+
+ @NonNull
+ public String getUrl()
+ {
+ return mUrl;
+ }
+
+ @NonNull
+ public String getSmallUrl()
+ {
+ return mSmallUrl;
+ }
+
+ @Nullable
+ public String getDescription()
+ {
+ return mDescription;
+ }
+
+ public void setDescription(@Nullable String description)
+ {
+ this.mDescription = description;
+ }
+
+ @Nullable
+ String getUserName()
+ {
+ return mUserName;
+ }
+
+ @SuppressWarnings("unused")
+ public void setUserName(@Nullable String userName)
+ {
+ this.mUserName = userName;
+ }
+
+ @Nullable
+ String getUserAvatar()
+ {
+ return mUserAvatar;
+ }
+
+ @SuppressWarnings("unused")
+ public void setUserAvatar(@Nullable String userAvatar)
+ {
+ this.mUserAvatar = userAvatar;
+ }
+
+ @Nullable
+ public String getSource()
+ {
+ return mSource;
+ }
+
+ public void setSource(@Nullable String source)
+ {
+ this.mSource = source;
+ }
+
+ @Nullable
+ public Long getDate()
+ {
+ return mDate;
+ }
+
+ public void setDate(@Nullable Long date)
+ {
+ this.mDate = date;
+ }
+}
diff --git a/android/src/com/mapswithme/maps/gallery/ImageAdapter.java b/android/src/com/mapswithme/maps/gallery/ImageAdapter.java
new file mode 100644
index 0000000000..ff0d977ac8
--- /dev/null
+++ b/android/src/com/mapswithme/maps/gallery/ImageAdapter.java
@@ -0,0 +1,79 @@
+package com.mapswithme.maps.gallery;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+import com.bumptech.glide.Glide;
+import com.mapswithme.maps.R;
+import com.mapswithme.maps.widget.recycler.RecyclerClickListener;
+
+import java.util.ArrayList;
+
+class ImageAdapter extends RecyclerView.Adapter<ImageAdapter.ViewHolder>
+{
+ @NonNull
+ private final ArrayList<Image> mItems;
+ @Nullable
+ private final RecyclerClickListener mListener;
+
+ ImageAdapter(@NonNull ArrayList<Image> images, @Nullable RecyclerClickListener listener)
+ {
+ mItems = images;
+ mListener = listener;
+ }
+
+ @Override
+ public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
+ {
+ return new ViewHolder(LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.item_image, parent, false), mListener);
+ }
+
+ @Override
+ public void onBindViewHolder(ViewHolder holder, int position)
+ {
+ holder.bind(mItems.get(position), position);
+ }
+
+ @Override
+ public int getItemCount()
+ {
+ return mItems.size();
+ }
+
+ static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener
+ {
+ private ImageView mImage;
+ private final RecyclerClickListener mListener;
+ private int mPosition;
+
+ public ViewHolder(View itemView, RecyclerClickListener listener)
+ {
+ super(itemView);
+ mListener = listener;
+ itemView.setOnClickListener(this);
+ mImage = (ImageView) itemView.findViewById(R.id.iv__image);
+ }
+
+ @Override
+ public void onClick(View v)
+ {
+ if (mListener != null)
+ mListener.onItemClick(v, mPosition);
+ }
+
+ public void bind(Image image, int position)
+ {
+ mPosition = position;
+ Glide.with(mImage.getContext())
+ .load(image.getSmallUrl())
+ .centerCrop()
+ .into(mImage);
+ }
+ }
+}
diff --git a/android/src/com/mapswithme/maps/review/Review.java b/android/src/com/mapswithme/maps/review/Review.java
new file mode 100644
index 0000000000..bc55fc569e
--- /dev/null
+++ b/android/src/com/mapswithme/maps/review/Review.java
@@ -0,0 +1,121 @@
+package com.mapswithme.maps.review;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+
+public class Review implements Parcelable
+{
+ @Nullable
+ private final String mReview;
+ @Nullable
+ private final String mReviewPositive;
+ @Nullable
+ private final String mReviewNegative;
+ @NonNull
+ private final String mAuthor;
+ @NonNull
+ private final String mAuthorAvatar;
+ private final float mRating;
+ private final long mDate;
+
+ public Review(@Nullable String review, @Nullable String reviewPositive,
+ @Nullable String reviewNegative, @NonNull String author,
+ @NonNull String authorAvatar,
+ float rating, long date)
+ {
+ mReview = review;
+ mReviewPositive = reviewPositive;
+ mReviewNegative = reviewNegative;
+ mAuthor = author;
+ mAuthorAvatar = authorAvatar;
+ mRating = rating;
+ mDate = date;
+ }
+
+ protected Review(Parcel in)
+ {
+ mReview = in.readString();
+ mReviewPositive = in.readString();
+ mReviewNegative = in.readString();
+ mAuthor = in.readString();
+ mAuthorAvatar = in.readString();
+ mRating = in.readFloat();
+ mDate = in.readLong();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags)
+ {
+ dest.writeString(mReview);
+ dest.writeString(mReviewPositive);
+ dest.writeString(mReviewNegative);
+ dest.writeString(mAuthor);
+ dest.writeString(mAuthorAvatar);
+ dest.writeFloat(mRating);
+ dest.writeLong(mDate);
+ }
+
+ @Override
+ public int describeContents()
+ {
+ return 0;
+ }
+
+ public static final Creator<Review> CREATOR = new Creator<Review>()
+ {
+ @Override
+ public Review createFromParcel(Parcel in)
+ {
+ return new Review(in);
+ }
+
+ @Override
+ public Review[] newArray(int size)
+ {
+ return new Review[size];
+ }
+ };
+
+ @Nullable
+ public String getReview()
+ {
+ return mReview;
+ }
+
+ @Nullable
+ public String getReviewPositive()
+ {
+ return mReviewPositive;
+ }
+
+ @Nullable
+ public String getReviewNegative()
+ {
+ return mReviewNegative;
+ }
+
+ @NonNull
+ public String getAuthor()
+ {
+ return mAuthor;
+ }
+
+ @SuppressWarnings("unused")
+ @NonNull
+ public String getAuthorAvatar()
+ {
+ return mAuthorAvatar;
+ }
+
+ public float getRating()
+ {
+ return mRating;
+ }
+
+ public long getDate()
+ {
+ return mDate;
+ }
+}
diff --git a/android/src/com/mapswithme/maps/review/ReviewActivity.java b/android/src/com/mapswithme/maps/review/ReviewActivity.java
new file mode 100644
index 0000000000..588580a961
--- /dev/null
+++ b/android/src/com/mapswithme/maps/review/ReviewActivity.java
@@ -0,0 +1,37 @@
+package com.mapswithme.maps.review;
+
+import android.content.Context;
+import android.content.Intent;
+import android.support.annotation.NonNull;
+import android.support.v4.app.Fragment;
+
+import com.mapswithme.maps.base.BaseMwmExtraTitleActivity;
+
+import java.util.ArrayList;
+
+public class ReviewActivity extends BaseMwmExtraTitleActivity
+{
+ static final String EXTRA_REVIEWS = "review_items";
+ static final String EXTRA_RATING = "review_rating";
+ static final String EXTRA_RATING_BASE = "review_rating_base";
+ static final String EXTRA_RATING_URL = "review_rating_url";
+
+ public static void start(Context context, @NonNull ArrayList<Review> items,
+ @NonNull String title, @NonNull String rating, int ratingBase,
+ @NonNull String url)
+ {
+ final Intent i = new Intent(context, ReviewActivity.class);
+ i.putParcelableArrayListExtra(EXTRA_REVIEWS, items);
+ i.putExtra(EXTRA_TITLE, title);
+ i.putExtra(EXTRA_RATING, rating);
+ i.putExtra(EXTRA_RATING_BASE, ratingBase);
+ i.putExtra(EXTRA_RATING_URL, url);
+ context.startActivity(i);
+ }
+
+ @Override
+ protected Class<? extends Fragment> getFragmentClass()
+ {
+ return ReviewFragment.class;
+ }
+}
diff --git a/android/src/com/mapswithme/maps/review/ReviewAdapter.java b/android/src/com/mapswithme/maps/review/ReviewAdapter.java
new file mode 100644
index 0000000000..b4ea102f97
--- /dev/null
+++ b/android/src/com/mapswithme/maps/review/ReviewAdapter.java
@@ -0,0 +1,217 @@
+package com.mapswithme.maps.review;
+
+import android.support.annotation.CallSuper;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v7.widget.RecyclerView;
+import android.text.TextUtils;
+import android.text.format.DateFormat;
+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.widget.recycler.RecyclerClickListener;
+import com.mapswithme.util.UiUtils;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Locale;
+
+class ReviewAdapter extends RecyclerView.Adapter<ReviewAdapter.BaseViewHolder>
+{
+ private static final int MAX_COUNT = 15;
+ private static final int VIEW_TYPE_REVIEW = 0;
+ private static final int VIEW_TYPE_MORE = 1;
+ private static final int VIEW_TYPE_RATING = 2;
+
+ @NonNull
+ private final ArrayList<Review> mItems;
+ @Nullable
+ private final RecyclerClickListener mListener;
+ @NonNull
+ private final String mRating;
+ private final int mRatingBase;
+
+ ReviewAdapter(@NonNull ArrayList<Review> images, @Nullable RecyclerClickListener listener,
+ @NonNull String rating,
+ int ratingBase)
+ {
+ mItems = images;
+ mListener = listener;
+ mRating = rating;
+ mRatingBase = ratingBase;
+ }
+
+ @Override
+ public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
+ {
+ if (viewType == VIEW_TYPE_REVIEW)
+ return new ReviewHolder(LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.item_comment, parent, false), mListener);
+
+ if (viewType == VIEW_TYPE_MORE)
+ return new MoreHolder(LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.item_more_button, parent, false), mListener);
+
+ return new RatingHolder(LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.item_rating, parent, false), mListener);
+ }
+
+ @Override
+ public void onBindViewHolder(BaseViewHolder holder, int position)
+ {
+ int positionNoHeader = position - 1;
+
+ if (position == 0)
+ ((RatingHolder) holder).bind(mRating, mRatingBase);
+ else if (positionNoHeader < mItems.size())
+ holder.bind(mItems.get(positionNoHeader), positionNoHeader);
+ else
+ holder.bind(null, positionNoHeader);
+ }
+
+ @Override
+ public int getItemCount()
+ {
+ if (mItems.size() > MAX_COUNT)
+ // 1 overall rating item + MAX_COUNT user reviews + 1 "more reviews" item
+ return MAX_COUNT + 2;
+
+ // 1 overall rating item + count of user reviews + 1 "more reviews" item
+ return mItems.size() + 2;
+ }
+
+ @Override
+ public int getItemViewType(int position)
+ {
+ int positionNoHeader = position - 1;
+
+ if (position == 0)
+ return VIEW_TYPE_RATING;
+ if (positionNoHeader == mItems.size())
+ return VIEW_TYPE_MORE;
+
+ return VIEW_TYPE_REVIEW;
+ }
+
+ static abstract class BaseViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener
+ {
+ private final RecyclerClickListener mListener;
+ private int mPosition;
+
+ BaseViewHolder(View itemView, RecyclerClickListener listener)
+ {
+ super(itemView);
+ mListener = listener;
+ }
+
+ @Override
+ public void onClick(View v)
+ {
+ if (mListener != null)
+ mListener.onItemClick(v, mPosition);
+ }
+
+ @CallSuper
+ public void bind(Review item, int position)
+ {
+ mPosition = position;
+ }
+ }
+
+ private static class ReviewHolder extends BaseViewHolder
+ {
+ final View mDivider;
+ final TextView mUserName;
+ final TextView mCommentDate;
+ final TextView mRating;
+ final TextView mReview;
+ final View mPositiveReview;
+ final TextView mTvPositiveReview;
+ final View mNegativeReview;
+ final TextView mTvNegativeReview;
+
+ ReviewHolder(View itemView, RecyclerClickListener listener)
+ {
+ super(itemView, listener);
+ mDivider = itemView.findViewById(R.id.v__divider);
+ mUserName = (TextView) itemView.findViewById(R.id.tv__user_name);
+ mCommentDate = (TextView) itemView.findViewById(R.id.tv__comment_date);
+ mRating = (TextView) itemView.findViewById(R.id.tv__user_rating);
+ mReview = (TextView) itemView.findViewById(R.id.tv__review);
+ mPositiveReview = itemView.findViewById(R.id.ll__positive_review);
+ mTvPositiveReview = (TextView) itemView.findViewById(R.id.tv__positive_review);
+ mNegativeReview = itemView.findViewById(R.id.ll__negative_review);
+ mTvNegativeReview = (TextView) itemView.findViewById(R.id.tv__negative_review);
+ }
+
+ @Override
+ public void bind(Review item, int position)
+ {
+ super.bind(item, position);
+ UiUtils.showIf(position > 0, mDivider);
+ mUserName.setText(item.getAuthor());
+ Date date = new Date(item.getDate());
+ mCommentDate.setText(DateFormat.getMediumDateFormat(mCommentDate.getContext()).format(date));
+ mRating.setText(String.format(Locale.getDefault(), "%.1f", item.getRating()));
+ if (TextUtils.isEmpty(item.getReviewPositive()))
+ {
+ UiUtils.hide(mPositiveReview);
+ }
+ else
+ {
+ UiUtils.show(mPositiveReview);
+ mTvPositiveReview.setText(item.getReviewPositive());
+ }
+ if (TextUtils.isEmpty(item.getReviewNegative()))
+ {
+ UiUtils.hide(mNegativeReview);
+ }
+ else
+ {
+ UiUtils.show(mNegativeReview);
+ mTvNegativeReview.setText(item.getReviewNegative());
+ }
+ if (UiUtils.isHidden(mNegativeReview) && UiUtils.isHidden(mPositiveReview))
+ {
+ UiUtils.showIf(!TextUtils.isEmpty(item.getReview()), mReview);
+ }
+ else
+ {
+ UiUtils.hide(mReview);
+ }
+ }
+ }
+
+ private static class MoreHolder extends BaseViewHolder
+ {
+ MoreHolder(View itemView, RecyclerClickListener listener)
+ {
+ super(itemView, listener);
+ itemView.setOnClickListener(this);
+ }
+ }
+
+ private static class RatingHolder extends BaseViewHolder
+ {
+ final TextView mHotelRating;
+ final TextView mHotelRatingBase;
+
+ RatingHolder(View itemView, RecyclerClickListener listener)
+ {
+ super(itemView, listener);
+ mHotelRating = (TextView) itemView.findViewById(R.id.tv__place_hotel_rating);
+ mHotelRatingBase = (TextView) itemView.findViewById(R.id.tv__place_hotel_rating_base);
+ }
+
+ public void bind(String rating, int ratingBase)
+ {
+ mHotelRating.setText(rating);
+ mHotelRatingBase.setText(mHotelRatingBase.getContext().getResources()
+ .getQuantityString(R.plurals.place_page_booking_rating_base,
+ ratingBase, ratingBase));
+ }
+ }
+}
diff --git a/android/src/com/mapswithme/maps/review/ReviewFragment.java b/android/src/com/mapswithme/maps/review/ReviewFragment.java
new file mode 100644
index 0000000000..cfb39156e1
--- /dev/null
+++ b/android/src/com/mapswithme/maps/review/ReviewFragment.java
@@ -0,0 +1,76 @@
+package com.mapswithme.maps.review;
+
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.mapswithme.maps.R;
+import com.mapswithme.maps.base.BaseMwmFragment;
+import com.mapswithme.maps.widget.recycler.RecyclerClickListener;
+
+import java.util.ArrayList;
+
+public class ReviewFragment extends BaseMwmFragment implements RecyclerClickListener
+{
+ @Nullable
+ private ArrayList<Review> mItems;
+ @Nullable
+ private String mRating;
+ private int mRatingBase;
+ @Nullable
+ private String mUrl;
+
+ @Nullable
+ @Override
+ public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState)
+ {
+ return inflater.inflate(R.layout.fragment_review, container, false);
+ }
+
+ @Override
+ public void onViewCreated(View view, @Nullable Bundle savedInstanceState)
+ {
+ super.onViewCreated(view, savedInstanceState);
+ readArguments();
+
+ if (mItems != null && mRating != null)
+ {
+ RecyclerView rvGallery = (RecyclerView) view.findViewById(R.id.rv__review);
+ rvGallery.setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false));
+ rvGallery.setAdapter(new ReviewAdapter(mItems, this, mRating, mRatingBase));
+ }
+ }
+
+ private void readArguments()
+ {
+ final Bundle arguments = getArguments();
+ if (arguments == null)
+ return;
+
+ mItems = arguments.getParcelableArrayList(ReviewActivity.EXTRA_REVIEWS);
+ mRating = arguments.getString(ReviewActivity.EXTRA_RATING);
+ mRatingBase = arguments.getInt(ReviewActivity.EXTRA_RATING_BASE);
+ mUrl = arguments.getString(ReviewActivity.EXTRA_RATING_URL);
+ }
+
+ @Override
+ public void onItemClick(View v, int position)
+ {
+ if (mUrl == null)
+ return;
+
+ final Intent intent = new Intent(Intent.ACTION_VIEW);
+ String url = mUrl;
+ if (!url.startsWith("http://") && !url.startsWith("https://"))
+ url = "http://" + url;
+ intent.setData(Uri.parse(url));
+ getContext().startActivity(intent);
+ }
+}
diff --git a/android/src/com/mapswithme/maps/widget/LineCountTextView.java b/android/src/com/mapswithme/maps/widget/LineCountTextView.java
new file mode 100644
index 0000000000..46d3d9e644
--- /dev/null
+++ b/android/src/com/mapswithme/maps/widget/LineCountTextView.java
@@ -0,0 +1,56 @@
+package com.mapswithme.maps.widget;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.text.Layout;
+import android.util.AttributeSet;
+import android.widget.TextView;
+
+public class LineCountTextView extends TextView
+{
+ public interface OnLineCountCalculatedListener
+ {
+
+ void onLineCountCalculated(boolean grater);
+ }
+
+ private OnLineCountCalculatedListener mListener;
+
+ public LineCountTextView(Context context)
+ {
+ super(context);
+ }
+
+ public LineCountTextView(Context context, AttributeSet attrs)
+ {
+ super(context, attrs);
+ }
+
+ public LineCountTextView(Context context, AttributeSet attrs, int defStyleAttr)
+ {
+ super(context, attrs, defStyleAttr);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas)
+ {
+ super.onDraw(canvas);
+ Layout layout = getLayout();
+
+ if (layout != null)
+ {
+ int textHeight = layout.getHeight();
+ int viewHeight = getHeight();
+
+ if (mListener != null)
+ {
+ mListener.onLineCountCalculated(textHeight > viewHeight);
+ }
+ }
+ }
+
+ public void setListener(OnLineCountCalculatedListener listener)
+ {
+ mListener = listener;
+ }
+}
diff --git a/android/src/com/mapswithme/maps/widget/StaticGridView.java b/android/src/com/mapswithme/maps/widget/StaticGridView.java
new file mode 100644
index 0000000000..f2a1662c16
--- /dev/null
+++ b/android/src/com/mapswithme/maps/widget/StaticGridView.java
@@ -0,0 +1,30 @@
+package com.mapswithme.maps.widget;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.GridView;
+
+public class StaticGridView extends GridView
+{
+ public StaticGridView(Context context)
+ {
+ super(context);
+ }
+
+ public StaticGridView(Context context, AttributeSet attrs)
+ {
+ super(context, attrs);
+ }
+
+ public StaticGridView(Context context, AttributeSet attrs, int defStyleAttr)
+ {
+ super(context, attrs, defStyleAttr);
+ }
+
+ @Override
+ public void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
+ {
+ super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(MEASURED_SIZE_MASK, MeasureSpec.AT_MOST));
+ getLayoutParams().height = getMeasuredHeight();
+ }
+}
diff --git a/android/src/com/mapswithme/maps/widget/placepage/FacilitiesAdapter.java b/android/src/com/mapswithme/maps/widget/placepage/FacilitiesAdapter.java
new file mode 100644
index 0000000000..3a5aa72e7e
--- /dev/null
+++ b/android/src/com/mapswithme/maps/widget/placepage/FacilitiesAdapter.java
@@ -0,0 +1,97 @@
+package com.mapswithme.maps.widget.placepage;
+
+import android.support.annotation.NonNull;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.mapswithme.maps.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+class FacilitiesAdapter extends BaseAdapter
+{
+ static final int MAX_COUNT = 6;
+
+ @NonNull
+ private List<SponsoredHotel.FacilityType> mItems = new ArrayList<>();
+ private boolean isShowAll = false;
+
+ @Override
+ public int getCount()
+ {
+ if (mItems.size() > MAX_COUNT && !isShowAll)
+ {
+ return MAX_COUNT;
+ }
+ return mItems.size();
+ }
+
+ @Override
+ public Object getItem(int position)
+ {
+ return mItems.get(position);
+ }
+
+ @Override
+ public long getItemId(int position)
+ {
+ return position;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent)
+ {
+ ViewHolder holder;
+ if (convertView == null)
+ {
+ convertView = LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.item_facility, parent, false);
+ holder = new ViewHolder(convertView);
+ convertView.setTag(holder);
+ }
+ else
+ {
+ holder = (ViewHolder) convertView.getTag();
+ }
+
+ holder.bind(mItems.get(position));
+
+ return convertView;
+ }
+
+ public void setItems(@NonNull List<SponsoredHotel.FacilityType> items)
+ {
+ this.mItems = items;
+ notifyDataSetChanged();
+ }
+
+ void setShowAll(boolean showAll)
+ {
+ isShowAll = showAll;
+ notifyDataSetChanged();
+ }
+
+ private static class ViewHolder
+ {
+ ImageView mIcon;
+ TextView mName;
+
+ public ViewHolder(View view)
+ {
+ mIcon = (ImageView) view.findViewById(R.id.iv__icon);
+ mName = (TextView) view.findViewById(R.id.tv__facility);
+ }
+
+ public void bind(SponsoredHotel.FacilityType facility)
+ {
+// TODO map facility key to image resource id
+ mIcon.setImageResource(R.drawable.ic_entrance);
+ mName.setText(facility.getName());
+ }
+ }
+}
diff --git a/android/src/com/mapswithme/maps/widget/placepage/GalleryAdapter.java b/android/src/com/mapswithme/maps/widget/placepage/GalleryAdapter.java
new file mode 100644
index 0000000000..ed3cfba45e
--- /dev/null
+++ b/android/src/com/mapswithme/maps/widget/placepage/GalleryAdapter.java
@@ -0,0 +1,205 @@
+package com.mapswithme.maps.widget.placepage;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.request.animation.GlideAnimation;
+import com.bumptech.glide.request.target.SimpleTarget;
+import com.mapswithme.maps.R;
+import com.mapswithme.maps.gallery.Image;
+import com.mapswithme.maps.widget.recycler.RecyclerClickListener;
+import com.mapswithme.util.UiUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+class GalleryAdapter extends RecyclerView.Adapter<GalleryAdapter.ViewHolder>
+{
+ static final int MAX_COUNT = 5;
+
+ private final Context mContext;
+ @NonNull
+ private ArrayList<Image> mItems = new ArrayList<>();
+ @NonNull
+ private final List<Item> mLoadedItems = new ArrayList<>();
+ @NonNull
+ private final List<Item> mItemsToDownload = new ArrayList<>();
+ @Nullable
+ private RecyclerClickListener mListener;
+ private final int mImageWidth;
+ private final int mImageHeight;
+
+ GalleryAdapter(Context context)
+ {
+ mContext = context;
+
+ mImageWidth = (int) context.getResources().getDimension(R.dimen.placepage_hotel_gallery_width);
+ mImageHeight = (int) context.getResources()
+ .getDimension(R.dimen.placepage_hotel_gallery_height);
+ }
+
+ @Override
+ public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
+ {
+ return new ViewHolder(LayoutInflater.from(mContext)
+ .inflate(R.layout.item_gallery, parent, false), mListener);
+ }
+
+ @Override
+ public void onBindViewHolder(ViewHolder holder, int position)
+ {
+ Item item = mLoadedItems.get(position);
+ item.setShowMore(position == MAX_COUNT - 1 && mItems.size() > MAX_COUNT);
+ holder.bind(item, position);
+ }
+
+ @Override
+ public int getItemCount()
+ {
+ return mLoadedItems.size();
+ }
+
+ @NonNull
+ public ArrayList<Image> getItems()
+ {
+ return mItems;
+ }
+
+ public void setItems(@NonNull ArrayList<Image> items)
+ {
+ mItems = items;
+
+ for (Item item : mItemsToDownload)
+ {
+ item.setCanceled(true);
+ }
+ mItemsToDownload.clear();
+ mLoadedItems.clear();
+ loadImages();
+ notifyDataSetChanged();
+ }
+
+ public void setListener(@Nullable RecyclerClickListener listener)
+ {
+ mListener = listener;
+ }
+
+ private void loadImages()
+ {
+ int size = Math.min(mItems.size(), MAX_COUNT);
+
+ for (int i = 0; i < size; i++)
+ {
+ final Item item = new Item(null);
+ mItemsToDownload.add(item);
+ Image image = mItems.get(i);
+ Glide.with(mContext)
+ .load(image.getSmallUrl())
+ .asBitmap()
+ .centerCrop()
+ .into(new SimpleTarget<Bitmap>(mImageWidth, mImageHeight)
+ {
+ @Override
+ public void onResourceReady(Bitmap resource,
+ GlideAnimation<? super Bitmap> glideAnimation)
+ {
+ if (item.isCanceled())
+ return;
+
+ item.setBitmap(resource);
+ int size = mLoadedItems.size();
+ mLoadedItems.add(item);
+ notifyItemInserted(size);
+ }
+ });
+ }
+ }
+
+ static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener
+ {
+ @NonNull
+ private ImageView mImage;
+ @NonNull
+ private View mMore;
+ @Nullable
+ private final RecyclerClickListener mListener;
+ private int mPosition;
+
+ public ViewHolder(View itemView, @Nullable RecyclerClickListener listener)
+ {
+ super(itemView);
+ mListener = listener;
+ mImage = (ImageView) itemView.findViewById(R.id.iv__image);
+ mMore = itemView.findViewById(R.id.tv__more);
+ itemView.setOnClickListener(this);
+ }
+
+ @Override
+ public void onClick(View v)
+ {
+ if (mListener == null)
+ return;
+
+ mListener.onItemClick(v, mPosition);
+ }
+
+ public void bind(Item item, int position)
+ {
+ mPosition = position;
+ mImage.setImageBitmap(item.getBitmap());
+ UiUtils.showIf(item.isShowMore(), mMore);
+ }
+ }
+
+ static class Item
+ {
+ @Nullable
+ private Bitmap mBitmap;
+ private boolean isShowMore;
+ private boolean isCanceled = false;
+
+ Item(@Nullable Bitmap bitmap)
+ {
+ this.mBitmap = bitmap;
+ }
+
+ @Nullable
+ Bitmap getBitmap()
+ {
+ return mBitmap;
+ }
+
+ void setBitmap(@Nullable Bitmap bitmap)
+ {
+ mBitmap = bitmap;
+ }
+
+ void setShowMore(boolean showMore)
+ {
+ isShowMore = showMore;
+ }
+
+ boolean isShowMore()
+ {
+ return isShowMore;
+ }
+
+ boolean isCanceled()
+ {
+ return isCanceled;
+ }
+
+ void setCanceled(boolean canceled)
+ {
+ isCanceled = canceled;
+ }
+ }
+}
diff --git a/android/src/com/mapswithme/maps/widget/placepage/LeftPlacePageAnimationController.java b/android/src/com/mapswithme/maps/widget/placepage/LeftPlacePageAnimationController.java
index 40f45ec182..d981692c74 100644
--- a/android/src/com/mapswithme/maps/widget/placepage/LeftPlacePageAnimationController.java
+++ b/android/src/com/mapswithme/maps/widget/placepage/LeftPlacePageAnimationController.java
@@ -13,7 +13,7 @@ import com.mapswithme.util.UiUtils;
class LeftPlacePageAnimationController extends BasePlacePageAnimationController
{
- public LeftPlacePageAnimationController(@NonNull PlacePageView placePage)
+ LeftPlacePageAnimationController(@NonNull PlacePageView placePage)
{
super(placePage);
}
@@ -27,18 +27,21 @@ class LeftPlacePageAnimationController extends BasePlacePageAnimationController
@Override
protected boolean onInterceptTouchEvent(MotionEvent event)
{
+ if (mPlacePage.isTouchGallery(event))
+ return false;
+
switch (event.getAction())
{
- case MotionEvent.ACTION_DOWN:
- mIsDragging = false;
- mDownCoord = event.getX();
- break;
- case MotionEvent.ACTION_MOVE:
- if (mDownCoord > mPlacePage.getRight())
- return false;
- if (Math.abs(mDownCoord - event.getX()) > mTouchSlop)
- return true;
- break;
+ case MotionEvent.ACTION_DOWN:
+ mIsDragging = false;
+ mDownCoord = event.getX();
+ break;
+ case MotionEvent.ACTION_MOVE:
+ if (mDownCoord > mPlacePage.getRight())
+ return false;
+ if (Math.abs(mDownCoord - event.getX()) > mTouchSlop)
+ return true;
+ break;
}
return false;
@@ -70,7 +73,7 @@ class LeftPlacePageAnimationController extends BasePlacePageAnimationController
final boolean isInRange = Math.abs(distanceX) > X_MIN && Math.abs(distanceX) < X_MAX;
if (!isHorizontal || !isInRange)
- return false;;
+ return false;
if (!mIsDragging)
{
@@ -90,13 +93,13 @@ class LeftPlacePageAnimationController extends BasePlacePageAnimationController
{
switch (newState)
{
- case HIDDEN:
- hidePlacePage();
- break;
- case DETAILS:
- case PREVIEW:
- showPlacePage(currentState, newState);
- break;
+ case HIDDEN:
+ hidePlacePage();
+ break;
+ case DETAILS:
+ case PREVIEW:
+ showPlacePage(currentState);
+ break;
}
}
@@ -130,7 +133,7 @@ class LeftPlacePageAnimationController extends BasePlacePageAnimationController
tracker.onTrackLeftAnimation(offset + mPlacePage.getDockedWidth());
}
- private void showPlacePage(final State currentState, final State newState)
+ private void showPlacePage(final State currentState)
{
UiUtils.show(mPlacePage);
if (currentState != State.HIDDEN)
diff --git a/android/src/com/mapswithme/maps/widget/placepage/NearbyAdapter.java b/android/src/com/mapswithme/maps/widget/placepage/NearbyAdapter.java
new file mode 100644
index 0000000000..97bdca8995
--- /dev/null
+++ b/android/src/com/mapswithme/maps/widget/placepage/NearbyAdapter.java
@@ -0,0 +1,134 @@
+package com.mapswithme.maps.widget.placepage;
+
+import android.content.res.Resources;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.mapswithme.maps.R;
+import com.mapswithme.util.ThemeUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+class NearbyAdapter extends BaseAdapter
+{
+ @NonNull
+ private List<SponsoredHotel.NearbyObject> mItems = new ArrayList<>();
+ @Nullable
+ private final OnItemClickListener mListener;
+
+ NearbyAdapter(@Nullable OnItemClickListener listener)
+ {
+ mListener = listener;
+ }
+
+ interface OnItemClickListener
+ {
+ void onItemClick(@NonNull SponsoredHotel.NearbyObject item);
+ }
+
+ @Override
+ public int getCount()
+ {
+ return mItems.size();
+ }
+
+ @Override
+ public Object getItem(int position)
+ {
+ return mItems.get(position);
+ }
+
+ @Override
+ public long getItemId(int position)
+ {
+ return position;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent)
+ {
+ ViewHolder holder;
+ if (convertView == null)
+ {
+ convertView = LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.item_nearby, parent, false);
+ holder = new ViewHolder(convertView, mListener);
+ convertView.setTag(holder);
+ } else
+ {
+ holder = (ViewHolder) convertView.getTag();
+ }
+
+ holder.bind(mItems.get(position));
+
+ return convertView;
+ }
+
+ public void setItems(@NonNull List<SponsoredHotel.NearbyObject> items)
+ {
+ this.mItems = items;
+ notifyDataSetChanged();
+ }
+
+ private static class ViewHolder implements View.OnClickListener
+ {
+ @Nullable
+ final OnItemClickListener mListener;
+ @NonNull
+ ImageView mIcon;
+ @NonNull
+ TextView mTitle;
+ @NonNull
+ TextView mType;
+ @NonNull
+ TextView mDistance;
+ @Nullable
+ SponsoredHotel.NearbyObject mItem;
+
+ public ViewHolder(View view, @Nullable OnItemClickListener listener)
+ {
+ mListener = listener;
+ mIcon = (ImageView) view.findViewById(R.id.iv__icon);
+ mTitle = (TextView) view.findViewById(R.id.tv__title);
+ mType = (TextView) view.findViewById(R.id.tv__type);
+ mDistance = (TextView) view.findViewById(R.id.tv__distance);
+ view.setOnClickListener(this);
+ }
+
+ @Override
+ public void onClick(View v)
+ {
+ if (mListener != null && mItem != null)
+ mListener.onItemClick(mItem);
+ }
+
+ public void bind(@NonNull SponsoredHotel.NearbyObject item)
+ {
+ mItem = item;
+ String packageName = mType.getContext().getPackageName();
+ final boolean isNightTheme = ThemeUtils.isNightTheme();
+ Resources resources = mType.getResources();
+ int categoryRes = resources.getIdentifier(item.getCategory(), "string", packageName);
+ if (categoryRes == 0)
+ throw new IllegalStateException("Can't get string resource id for category:" + item.getCategory());
+
+ String iconId = "ic_category_" + item.getCategory();
+ if (isNightTheme)
+ iconId = iconId + "_night";
+ int iconRes = resources.getIdentifier(iconId, "drawable", packageName);
+ if (iconRes == 0)
+ throw new IllegalStateException("Can't get icon resource id:" + iconId);
+ mIcon.setImageResource(iconRes);
+ mTitle.setText(item.getTitle());
+ mType.setText(categoryRes);
+ mDistance.setText(item.getDistance());
+ }
+ }
+}
diff --git a/android/src/com/mapswithme/maps/widget/placepage/PlacePageView.java b/android/src/com/mapswithme/maps/widget/placepage/PlacePageView.java
index bd14f2a079..7a679cd25e 100644
--- a/android/src/com/mapswithme/maps/widget/placepage/PlacePageView.java
+++ b/android/src/com/mapswithme/maps/widget/placepage/PlacePageView.java
@@ -12,6 +12,9 @@ import android.os.Build;
import android.support.annotation.ColorInt;
import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
+import android.support.v4.content.ContextCompat;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
@@ -24,6 +27,7 @@ import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.GridView;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.PopupMenu;
@@ -31,15 +35,6 @@ import android.widget.RelativeLayout;
import android.widget.ScrollView;
import android.widget.TextView;
-import java.text.NumberFormat;
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.Currency;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-
import com.mapswithme.maps.Framework;
import com.mapswithme.maps.MwmActivity;
import com.mapswithme.maps.MwmApplication;
@@ -57,12 +52,18 @@ import com.mapswithme.maps.editor.Editor;
import com.mapswithme.maps.editor.OpeningHours;
import com.mapswithme.maps.editor.data.TimeFormatUtils;
import com.mapswithme.maps.editor.data.Timetable;
+import com.mapswithme.maps.gallery.FullScreenGalleryActivity;
+import com.mapswithme.maps.gallery.GalleryActivity;
import com.mapswithme.maps.location.LocationHelper;
+import com.mapswithme.maps.review.ReviewActivity;
import com.mapswithme.maps.routing.RoutingController;
import com.mapswithme.maps.widget.ArrowView;
import com.mapswithme.maps.widget.BaseShadowController;
+import com.mapswithme.maps.widget.LineCountTextView;
import com.mapswithme.maps.widget.ObservableScrollView;
import com.mapswithme.maps.widget.ScrollViewShadowController;
+import com.mapswithme.maps.widget.recycler.DividerItemDecoration;
+import com.mapswithme.maps.widget.recycler.RecyclerClickListener;
import com.mapswithme.util.Graphics;
import com.mapswithme.util.StringUtils;
import com.mapswithme.util.ThemeUtils;
@@ -73,14 +74,30 @@ import com.mapswithme.util.sharing.ShareOption;
import com.mapswithme.util.statistics.AlohaHelper;
import com.mapswithme.util.statistics.Statistics;
+import java.text.NumberFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Currency;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
public class PlacePageView extends RelativeLayout
- implements View.OnClickListener,
- View.OnLongClickListener,
- SponsoredHotel.OnPriceReceivedListener
+ implements View.OnClickListener,
+ View.OnLongClickListener,
+ SponsoredHotel.OnPriceReceivedListener,
+ SponsoredHotel.OnInfoReceivedListener,
+ LineCountTextView.OnLineCountCalculatedListener,
+ RecyclerClickListener,
+ NearbyAdapter.OnItemClickListener
{
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 boolean mIsDocked;
private boolean mIsFloating;
@@ -118,7 +135,6 @@ public class PlacePageView extends RelativeLayout
private View mEditPlace;
private View mAddOrganisation;
private View mAddPlace;
- private View mMoreInfo;
// Bookmark
private View mBookmarkFrame;
private TextView mBookmarkNote;
@@ -126,6 +142,20 @@ public class PlacePageView extends RelativeLayout
// Place page buttons
private PlacePageButtons mButtons;
private ImageView mBookmarkButtonIcon;
+ // Hotel
+ private View mHotelDescription;
+ private LineCountTextView mTvHotelDescription;
+ private View mHotelMoreDescription;
+ private View mHotelFacilities;
+ private View mHotelMoreFacilities;
+ private View mHotelGallery;
+ private RecyclerView mRvHotelGallery;
+ private View mHotelNearby;
+ private View mHotelReview;
+ private TextView mHotelRating;
+ private TextView mHotelRatingBase;
+//TODO: remove this after booking_api.cpp will be done
+ private View mHotelMore;
// Animations
private BaseShadowController mShadowController;
@@ -136,6 +166,14 @@ public class PlacePageView extends RelativeLayout
private SponsoredHotel mSponsoredHotel;
private String mSponsoredHotelPrice;
private boolean mIsLatLonDms;
+ @NonNull
+ private final FacilitiesAdapter mFacilitiesAdapter = new FacilitiesAdapter();
+ @NonNull
+ private final GalleryAdapter mGalleryAdapter;
+ @NonNull
+ private final NearbyAdapter mNearbyAdapter = new NearbyAdapter(this);
+ @NonNull
+ private final ReviewAdapter mReviewAdapter = new ReviewAdapter();
// Downloader`s stuff
private DownloaderStatusIcon mDownloaderIcon;
@@ -198,12 +236,17 @@ public class PlacePageView extends RelativeLayout
super(context, attrs);
mIsLatLonDms = MwmApplication.prefs().getBoolean(PREF_USE_DMS, false);
-
+ mGalleryAdapter = new GalleryAdapter(context);
init(attrs, defStyleAttr);
}
public ViewGroup GetPreview() { return mPreview; }
+ public boolean isTouchGallery(@NonNull MotionEvent event)
+ {
+ return UiUtils.isViewTouched(event, mHotelGallery);
+ }
+
private void initViews()
{
LayoutInflater.from(getContext()).inflate(R.layout.place_page, this);
@@ -257,8 +300,6 @@ public class PlacePageView extends RelativeLayout
mAddOrganisation.setOnClickListener(this);
mAddPlace = mDetails.findViewById(R.id.ll__place_add);
mAddPlace.setOnClickListener(this);
- mMoreInfo = mDetails.findViewById(R.id.ll__more);
- mMoreInfo.setOnClickListener(this);
latlon.setOnLongClickListener(this);
address.setOnLongClickListener(this);
mPhone.setOnLongClickListener(this);
@@ -274,6 +315,16 @@ public class PlacePageView extends RelativeLayout
ViewGroup ppButtons = (ViewGroup) findViewById(R.id.pp__buttons);
+// TODO: remove this after booking_api.cpp will be done
+ mHotelMore = findViewById(R.id.ll__more);
+ mHotelMore.setOnClickListener(this);
+
+ initHotelDescriptionView();
+ initHotelFacilitiesView();
+ initHotelGalleryView();
+ initHotelNearbyView();
+ initHotelRatingView();
+
mButtons = new PlacePageButtons(this, ppButtons, new PlacePageButtons.ItemListener()
{
@Override
@@ -283,21 +334,21 @@ public class PlacePageView extends RelativeLayout
switch (item)
{
- case BOOKING:
- frame.setBackgroundResource(R.drawable.button_booking);
- color = Color.WHITE;
- break;
-
- case BOOKMARK:
- mBookmarkButtonIcon = icon;
- updateButtons();
- color = ThemeUtils.getColor(getContext(), R.attr.iconTint);
- break;
-
- default:
- color = ThemeUtils.getColor(getContext(), R.attr.iconTint);
- icon.setColorFilter(color);
- break;
+ case BOOKING:
+ frame.setBackgroundResource(R.drawable.button_booking);
+ color = Color.WHITE;
+ break;
+
+ case BOOKMARK:
+ mBookmarkButtonIcon = icon;
+ updateButtons();
+ color = ThemeUtils.getColor(getContext(), R.attr.iconTint);
+ break;
+
+ default:
+ color = ThemeUtils.getColor(getContext(), R.attr.iconTint);
+ icon.setColorFilter(color);
+ break;
}
title.setTextColor(color);
@@ -338,62 +389,63 @@ public class PlacePageView extends RelativeLayout
hide();
break;
- case ROUTE_TO:
- if (RoutingController.get().isPlanning())
- {
- if (RoutingController.get().setEndPoint(mMapObject))
- hide();
- }
- else
- {
- getActivity().startLocationToPoint(Statistics.EventName.PP_ROUTE, AlohaHelper.PP_ROUTE, getMapObject());
- }
- break;
-
- case BOOKING:
- onBookingClick(true /* book */);
- break;
+ case ROUTE_TO:
+ if (RoutingController.get().isPlanning())
+ {
+ if (RoutingController.get().setEndPoint(mMapObject))
+ hide();
+ }
+ else
+ {
+ getActivity().startLocationToPoint(Statistics.EventName.PP_ROUTE, AlohaHelper.PP_ROUTE, getMapObject());
+ }
+ break;
+
+ case BOOKING:
+ onBookingClick(true /* book */);
+ break;
}
}
});
mDownloaderIcon = new DownloaderStatusIcon(mPreview.findViewById(R.id.downloader_status_frame))
- .setOnIconClickListener(new OnClickListener()
- {
- @Override
- public void onClick(View v)
- {
- MapManager.warn3gAndDownload(getActivity(), mCurrentCountry.id, new Runnable()
- {
- @Override
- public void run()
- {
- Statistics.INSTANCE.trackEvent(Statistics.EventName.DOWNLOADER_ACTION,
- Statistics.params()
- .add(Statistics.EventParam.ACTION, "download")
- .add(Statistics.EventParam.FROM, "placepage")
- .add("is_auto", "false")
- .add("scenario", (mCurrentCountry.isExpandable() ? "download_group"
- : "download")));
- }
- });
- }
- }).setOnCancelClickListener(new OnClickListener()
- {
- @Override
- public void onClick(View v)
- {
- MapManager.nativeCancel(mCurrentCountry.id);
- Statistics.INSTANCE.trackEvent(Statistics.EventName.DOWNLOADER_CANCEL,
- Statistics.params().add(Statistics.EventParam.FROM, "placepage"));
- }
- });
+ .setOnIconClickListener(new OnClickListener()
+ {
+ @Override
+ public void onClick(View v)
+ {
+ MapManager.warn3gAndDownload(getActivity(), mCurrentCountry.id, new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ Statistics.INSTANCE.trackEvent(Statistics.EventName.DOWNLOADER_ACTION,
+ Statistics.params()
+ .add(Statistics.EventParam.ACTION, "download")
+ .add(Statistics.EventParam.FROM, "placepage")
+ .add("is_auto", "false")
+ .add("scenario", (mCurrentCountry.isExpandable() ? "download_group"
+ : "download")));
+ }
+ });
+ }
+ }).setOnCancelClickListener(new OnClickListener()
+ {
+ @Override
+ public void onClick(View v)
+ {
+ MapManager.nativeCancel(mCurrentCountry.id);
+ Statistics.INSTANCE.trackEvent(Statistics.EventName.DOWNLOADER_CANCEL,
+ Statistics.params()
+ .add(Statistics.EventParam.FROM, "placepage"));
+ }
+ });
mDownloaderInfo = (TextView) mPreview.findViewById(R.id.tv__downloader_details);
mShadowController = new ScrollViewShadowController((ObservableScrollView) mDetails)
- .addBottomShadow()
- .attach();
+ .addBottomShadow()
+ .attach();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
setElevation(UiUtils.dimen(R.dimen.placepage_elevation));
@@ -401,11 +453,62 @@ public class PlacePageView extends RelativeLayout
if (UiUtils.isLandscape(getContext()))
mDetails.setBackgroundResource(0);
- SponsoredHotel.setListener(this);
+ SponsoredHotel.setPriceListener(this);
+ SponsoredHotel.setInfoListener(this);
+ }
+
+ private void initHotelRatingView()
+ {
+ mHotelReview = findViewById(R.id.ll__place_hotel_rating);
+ GridView gvHotelReview = (GridView) findViewById(R.id.gv__place_hotel_review);
+ gvHotelReview.setAdapter(mReviewAdapter);
+ mHotelRating = (TextView) findViewById(R.id.tv__place_hotel_rating);
+ mHotelRatingBase = (TextView) findViewById(R.id.tv__place_hotel_rating_base);
+ View hotelMoreReviews = findViewById(R.id.tv__place_hotel_reviews_more);
+ hotelMoreReviews.setOnClickListener(this);
+ }
+
+ private void initHotelNearbyView()
+ {
+ mHotelNearby = findViewById(R.id.ll__place_hotel_nearby);
+ GridView gvHotelNearby = (GridView) findViewById(R.id.gv__place_hotel_nearby);
+ gvHotelNearby.setAdapter(mNearbyAdapter);
+ }
+
+ private void initHotelGalleryView()
+ {
+ mHotelGallery = findViewById(R.id.ll__place_hotel_gallery);
+ mRvHotelGallery = (RecyclerView) findViewById(
+ R.id.rv__place_hotel_gallery);
+ mRvHotelGallery.setLayoutManager(new LinearLayoutManager(getContext(),
+ LinearLayoutManager.HORIZONTAL, false));
+ mRvHotelGallery.addItemDecoration(new DividerItemDecoration(ContextCompat.getDrawable(getContext(),
+ R.drawable.divider_transparent)));
+ mGalleryAdapter.setListener(this);
+ mRvHotelGallery.setAdapter(mGalleryAdapter);
+ }
+
+ private void initHotelFacilitiesView()
+ {
+ mHotelFacilities = findViewById(R.id.ll__place_hotel_facilities);
+ GridView gvHotelFacilities = (GridView) findViewById(R.id.gv__place_hotel_facilities);
+ mHotelMoreFacilities = findViewById(R.id.tv__place_hotel_facilities_more);
+ gvHotelFacilities.setAdapter(mFacilitiesAdapter);
+ mHotelMoreFacilities.setOnClickListener(this);
+ }
+
+ private void initHotelDescriptionView()
+ {
+ mHotelDescription = findViewById(R.id.ll__place_hotel_description);
+ mTvHotelDescription = (LineCountTextView) findViewById(R.id.tv__place_hotel_details);
+ mHotelMoreDescription = findViewById(R.id.tv__place_hotel_more);
+ mTvHotelDescription.setListener(this);
+ mHotelMoreDescription.setOnClickListener(this);
}
@Override
- public void onPriceReceived(String id, String price, String currencyCode)
+ public void onPriceReceived(@NonNull String id, @NonNull String price,
+ @NonNull String currencyCode)
{
if (mSponsoredHotel == null || !TextUtils.equals(id, mSponsoredHotel.getId()))
return;
@@ -424,6 +527,110 @@ public class PlacePageView extends RelativeLayout
refreshPreview();
}
+ @Override
+ public void onInfoReceived(@NonNull String id, @NonNull SponsoredHotel.HotelInfo info)
+ {
+ if (mSponsoredHotel == null || !TextUtils.equals(id, mSponsoredHotel.getId()))
+ return;
+
+ updateHotelDetails(info);
+ updateHotelFacilities(info);
+ updateHotelGallery(info);
+ updateHotelNearby(info);
+ updateHotelRating(info);
+ }
+
+ private void updateHotelRating(@NonNull SponsoredHotel.HotelInfo info)
+ {
+ if (info.mReviews == null || info.mReviews.length == 0)
+ {
+ UiUtils.hide(mHotelReview);
+ }
+ else
+ {
+ UiUtils.show(mHotelReview);
+ mReviewAdapter.setItems(new ArrayList<>(Arrays.asList(info.mReviews)));
+ mHotelRating.setText(mSponsoredHotel.mRating);
+ mHotelRatingBase.setText(getResources().getQuantityString(R.plurals.place_page_booking_rating_base,
+ info.mReviews.length, info.mReviews.length));
+ }
+ }
+
+ private void updateHotelNearby(@NonNull SponsoredHotel.HotelInfo info)
+ {
+ if (info.mNearby == null || info.mNearby.length == 0)
+ {
+ UiUtils.hide(mHotelNearby);
+ }
+ else
+ {
+ UiUtils.show(mHotelNearby);
+ mNearbyAdapter.setItems(Arrays.asList(info.mNearby));
+ }
+ }
+
+ private void updateHotelGallery(@NonNull SponsoredHotel.HotelInfo info)
+ {
+ if (info.mPhotos == null || info.mPhotos.length == 0)
+ {
+ UiUtils.hide(mHotelGallery);
+ }
+ else
+ {
+ UiUtils.show(mHotelGallery);
+ mGalleryAdapter.setItems(new ArrayList<>(Arrays.asList(info.mPhotos)));
+ mRvHotelGallery.scrollToPosition(0);
+ }
+ }
+
+ private void updateHotelFacilities(@NonNull SponsoredHotel.HotelInfo info)
+ {
+ if (info.mFacilities == null || info.mFacilities.length == 0)
+ {
+ UiUtils.hide(mHotelFacilities);
+ }
+ else
+ {
+ UiUtils.show(mHotelFacilities);
+ mFacilitiesAdapter.setShowAll(false);
+ mFacilitiesAdapter.setItems(Arrays.asList(info.mFacilities));
+ mHotelMoreFacilities.setVisibility(info.mFacilities.length > FacilitiesAdapter.MAX_COUNT
+ ? VISIBLE : GONE);
+ }
+ }
+
+ private void updateHotelDetails(@NonNull SponsoredHotel.HotelInfo info)
+ {
+ mTvHotelDescription.setMaxLines(getResources().getInteger(R.integer.pp_hotel_description_lines));
+ refreshMetadataOrHide(info.mDescription, mHotelDescription, mTvHotelDescription);
+ mHotelMoreDescription.setVisibility(GONE);
+ }
+
+ @Override
+ public void onLineCountCalculated(boolean grater)
+ {
+ UiUtils.showIf(grater, mHotelMoreDescription);
+ }
+
+ @Override
+ public void onItemClick(View v, int position)
+ {
+ if (position == GalleryAdapter.MAX_COUNT - 1)
+ {
+ GalleryActivity.start(getContext(), mGalleryAdapter.getItems(), mMapObject.getTitle());
+ }
+ else
+ {
+ FullScreenGalleryActivity.start(getContext(), mGalleryAdapter.getItems(), position);
+ }
+ }
+
+ @Override
+ public void onItemClick(@NonNull SponsoredHotel.NearbyObject item)
+ {
+// TODO go to selected object on map
+ }
+
private void onBookingClick(final boolean book)
{
// TODO (trashkalmar): Set correct text
@@ -453,7 +660,7 @@ public class PlacePageView extends RelativeLayout
try
{
- followUrl(book ? info.urlBook : info.urlDescription);
+ followUrl(book ? info.mUrlBook : info.mUrlDescription);
} catch (ActivityNotFoundException e)
{
AlohaHelper.logException(e);
@@ -482,7 +689,7 @@ public class PlacePageView extends RelativeLayout
public void restore()
{
// if (mMapObject != null)
- // FIXME query map object again
+ // FIXME query map object again
}
@Override
@@ -539,7 +746,7 @@ public class PlacePageView extends RelativeLayout
/**
* @param mapObject new MapObject
- * @param force if true, new object'll be set without comparison with the old one
+ * @param force if true, new object'll be set without comparison with the old one
*/
public void setMapObject(MapObject mapObject, boolean force)
{
@@ -555,10 +762,14 @@ public class PlacePageView extends RelativeLayout
if (mSponsoredHotel != null)
{
mSponsoredHotel.updateId(mMapObject);
- mSponsoredHotelPrice = mSponsoredHotel.price;
+ mSponsoredHotelPrice = mSponsoredHotel.mPrice;
- Currency currency = Currency.getInstance(Locale.getDefault());
+ Locale locale = Locale.getDefault();
+ Currency currency = Currency.getInstance(locale);
SponsoredHotel.requestPrice(mSponsoredHotel.getId(), currency.getCurrencyCode());
+// TODO: remove this after booking_api.cpp will be done
+ if (!USE_OLD_BOOKING)
+ SponsoredHotel.requestInfo(mSponsoredHotel.getId(), locale.toString());
}
String country = MapManager.nativeGetSelectedCountry();
@@ -580,27 +791,27 @@ public class PlacePageView extends RelativeLayout
switch (mMapObject.getMapObjectType())
{
- case MapObject.BOOKMARK:
- refreshDistanceToObject(loc);
- showBookmarkDetails();
- setButtons(false, true);
- break;
- case MapObject.POI:
- case MapObject.SEARCH:
- refreshDistanceToObject(loc);
- hideBookmarkDetails();
- setButtons(false, true);
- break;
- case MapObject.API_POINT:
- refreshDistanceToObject(loc);
- hideBookmarkDetails();
- setButtons(true, true);
- break;
- case MapObject.MY_POSITION:
- refreshMyPosition(loc);
- hideBookmarkDetails();
- setButtons(false, false);
- break;
+ case MapObject.BOOKMARK:
+ refreshDistanceToObject(loc);
+ showBookmarkDetails();
+ setButtons(false, true);
+ break;
+ case MapObject.POI:
+ case MapObject.SEARCH:
+ refreshDistanceToObject(loc);
+ hideBookmarkDetails();
+ setButtons(false, true);
+ break;
+ case MapObject.API_POINT:
+ refreshDistanceToObject(loc);
+ hideBookmarkDetails();
+ setButtons(true, true);
+ break;
+ case MapObject.MY_POSITION:
+ refreshMyPosition(loc);
+ hideBookmarkDetails();
+ setButtons(false, false);
+ break;
}
UiThread.runLater(new Runnable()
@@ -645,7 +856,7 @@ public class PlacePageView extends RelativeLayout
UiUtils.showIf(sponsored, mHotelInfo);
if (sponsored)
{
- mTvHotelRating.setText(mSponsoredHotel.rating);
+ mTvHotelRating.setText(mSponsoredHotel.mRating);
UiUtils.setTextAndHideIfEmpty(mTvHotelPrice, mSponsoredHotelPrice);
}
}
@@ -658,9 +869,21 @@ public class PlacePageView extends RelativeLayout
{
final String website = mMapObject.getMetadata(Metadata.MetadataType.FMD_WEBSITE);
refreshMetadataOrHide(TextUtils.isEmpty(website) ? mMapObject.getMetadata(Metadata.MetadataType.FMD_URL) : website, mWebsite, mTvWebsite);
+ UiUtils.hide(mHotelDescription);
+ UiUtils.hide(mHotelFacilities);
+ UiUtils.hide(mHotelGallery);
+ UiUtils.hide(mHotelNearby);
+ UiUtils.hide(mHotelReview);
+// TODO: remove this after booking_api.cpp will be done
+ UiUtils.hide(mHotelMore);
}
else
+ {
UiUtils.hide(mWebsite);
+// TODO: remove this after booking_api.cpp will be done
+ if (!USE_OLD_BOOKING)
+ UiUtils.hide(mHotelMore);
+ }
refreshMetadataOrHide(mMapObject.getMetadata(Metadata.MetadataType.FMD_PHONE_NUMBER), mPhone, mTvPhone);
refreshMetadataOrHide(mMapObject.getMetadata(Metadata.MetadataType.FMD_EMAIL), mEmail, mTvEmail);
@@ -683,8 +906,6 @@ public class PlacePageView extends RelativeLayout
UiUtils.showIf(Editor.nativeShouldShowAddBusiness(), mAddOrganisation);
UiUtils.showIf(Editor.nativeShouldShowAddPlace(), mAddPlace);
}
-
- UiUtils.showIf(mSponsoredHotel != null, mMoreInfo);
}
private void refreshOpeningHours()
@@ -824,8 +1045,11 @@ public class PlacePageView extends RelativeLayout
return;
mTvDistance.setVisibility(View.VISIBLE);
- DistanceAndAzimut distanceAndAzimuth = Framework.nativeGetDistanceAndAzimuthFromLatLon(mMapObject.getLat(), mMapObject.getLon(),
- l.getLatitude(), l.getLongitude(), 0.0);
+ DistanceAndAzimut distanceAndAzimuth = Framework.nativeGetDistanceAndAzimuthFromLatLon(mMapObject
+ .getLat(), mMapObject
+ .getLon(),
+ l.getLatitude(), l
+ .getLongitude(), 0.0);
mTvDistance.setText(distanceAndAzimuth.getDistance());
}
@@ -853,16 +1077,18 @@ public class PlacePageView extends RelativeLayout
public void refreshAzimuth(double northAzimuth)
{
if (isHidden() ||
- mMapObject == null ||
- MapObject.isOfType(MapObject.MY_POSITION, mMapObject))
+ mMapObject == null ||
+ MapObject.isOfType(MapObject.MY_POSITION, mMapObject))
return;
final Location location = LocationHelper.INSTANCE.getSavedLocation();
if (location == null)
return;
- final double azimuth = Framework.nativeGetDistanceAndAzimuthFromLatLon(mMapObject.getLat(), mMapObject.getLon(),
- location.getLatitude(), location.getLongitude(),
+ final double azimuth = Framework.nativeGetDistanceAndAzimuthFromLatLon(mMapObject.getLat(), mMapObject
+ .getLon(),
+ location.getLatitude(), location
+ .getLongitude(),
northAzimuth)
.getAzimuth();
if (azimuth >= 0)
@@ -880,7 +1106,8 @@ public class PlacePageView extends RelativeLayout
private void addOrganisation()
{
Statistics.INSTANCE.trackEvent(Statistics.EventName.EDITOR_ADD_CLICK,
- Statistics.params().add(Statistics.EventParam.FROM, "placepage"));
+ Statistics.params()
+ .add(Statistics.EventParam.FROM, "placepage"));
getActivity().showPositionChooser(true, false);
}
@@ -895,56 +1122,69 @@ public class PlacePageView extends RelativeLayout
{
switch (v.getId())
{
- case R.id.ll__place_editor:
- getActivity().showEditor();
- break;
- case R.id.ll__add_organisation:
- addOrganisation();
- break;
- case R.id.ll__place_add:
- addPlace();
- break;
- case R.id.ll__more:
- onBookingClick(false /* book */);
- break;
- case R.id.ll__place_latlon:
- mIsLatLonDms = !mIsLatLonDms;
- MwmApplication.prefs().edit().putBoolean(PREF_USE_DMS, mIsLatLonDms).commit();
- refreshLatLon();
- break;
- case R.id.ll__place_phone:
- Intent intent = new Intent(Intent.ACTION_DIAL);
- intent.setData(Uri.parse("tel:" + mTvPhone.getText()));
- try
- {
+ case R.id.ll__place_editor:
+ getActivity().showEditor();
+ break;
+ case R.id.ll__add_organisation:
+ addOrganisation();
+ break;
+ case R.id.ll__place_add:
+ addPlace();
+ break;
+ case R.id.ll__more:
+ onBookingClick(false /* book */);
+ break;
+ case R.id.ll__place_latlon:
+ mIsLatLonDms = !mIsLatLonDms;
+ MwmApplication.prefs().edit().putBoolean(PREF_USE_DMS, mIsLatLonDms).commit();
+ refreshLatLon();
+ break;
+ case R.id.ll__place_phone:
+ Intent intent = new Intent(Intent.ACTION_DIAL);
+ intent.setData(Uri.parse("tel:" + mTvPhone.getText()));
+ try
+ {
+ getContext().startActivity(intent);
+ } catch (ActivityNotFoundException e)
+ {
+ AlohaHelper.logException(e);
+ }
+ break;
+ case R.id.ll__place_website:
+ followUrl(mTvWebsite.getText().toString());
+ break;
+ case R.id.ll__place_wiki:
+ // TODO: Refactor and use separate getters for Wiki and all other PP meta info too.
+ followUrl(mMapObject.getMetadata(Metadata.MetadataType.FMD_WIKIPEDIA));
+ break;
+ case R.id.direction_frame:
+ Statistics.INSTANCE.trackEvent(Statistics.EventName.PP_DIRECTION_ARROW);
+ AlohaHelper.logClick(AlohaHelper.PP_DIRECTION_ARROW);
+ showBigDirection();
+ break;
+ case R.id.ll__place_email:
+ intent = new Intent(Intent.ACTION_SENDTO);
+ intent.setData(Utils.buildMailUri(mTvEmail.getText().toString(), "", ""));
getContext().startActivity(intent);
- } catch (ActivityNotFoundException e)
- {
- AlohaHelper.logException(e);
- }
- break;
- case R.id.ll__place_website:
- followUrl(mTvWebsite.getText().toString());
- break;
- case R.id.ll__place_wiki:
- // TODO: Refactor and use separate getters for Wiki and all other PP meta info too.
- followUrl(mMapObject.getMetadata(Metadata.MetadataType.FMD_WIKIPEDIA));
- break;
- case R.id.direction_frame:
- Statistics.INSTANCE.trackEvent(Statistics.EventName.PP_DIRECTION_ARROW);
- AlohaHelper.logClick(AlohaHelper.PP_DIRECTION_ARROW);
- showBigDirection();
- break;
- case R.id.ll__place_email:
- intent = new Intent(Intent.ACTION_SENDTO);
- intent.setData(Utils.buildMailUri(mTvEmail.getText().toString(), "", ""));
- getContext().startActivity(intent);
- break;
- case R.id.tv__bookmark_edit:
- Bookmark bookmark = (Bookmark) mMapObject;
- EditBookmarkFragment.editBookmark(bookmark.getCategoryId(), bookmark.getBookmarkId(),
- getActivity(), getActivity().getSupportFragmentManager());
- break;
+ break;
+ case R.id.tv__bookmark_edit:
+ Bookmark bookmark = (Bookmark) mMapObject;
+ EditBookmarkFragment.editBookmark(bookmark.getCategoryId(), bookmark.getBookmarkId(),
+ getActivity(), getActivity().getSupportFragmentManager());
+ break;
+ case R.id.tv__place_hotel_more:
+ UiUtils.hide(mHotelMoreDescription);
+ mTvHotelDescription.setMaxLines(Integer.MAX_VALUE);
+ break;
+ case R.id.tv__place_hotel_facilities_more:
+ UiUtils.hide(mHotelMoreFacilities);
+ mFacilitiesAdapter.setShowAll(true);
+ break;
+ case R.id.tv__place_hotel_reviews_more:
+ ReviewActivity.start(getContext(), mReviewAdapter.getItems(), mMapObject.getTitle(),
+ mSponsoredHotel.mRating, mReviewAdapter.getItems()
+ .size(), mSponsoredHotel.mUrlBook);
+ break;
}
}
@@ -976,7 +1216,8 @@ public class PlacePageView extends RelativeLayout
private void showBigDirection()
{
- final DirectionFragment fragment = (DirectionFragment) Fragment.instantiate(getActivity(), DirectionFragment.class.getName(), null);
+ final DirectionFragment fragment = (DirectionFragment) Fragment.instantiate(getActivity(), DirectionFragment.class
+ .getName(), null);
fragment.setMapObject(mMapObject);
fragment.show(getActivity().getSupportFragmentManager(), null);
}
@@ -993,30 +1234,30 @@ public class PlacePageView extends RelativeLayout
final List<String> items = new ArrayList<>();
switch (v.getId())
{
- case R.id.ll__place_latlon:
- final double lat = mMapObject.getLat();
- final double lon = mMapObject.getLon();
- items.add(Framework.nativeFormatLatLon(lat, lon, false));
- items.add(Framework.nativeFormatLatLon(lat, lon, true));
- break;
- case R.id.ll__place_website:
- items.add(mTvWebsite.getText().toString());
- break;
- case R.id.ll__place_email:
- items.add(mTvEmail.getText().toString());
- break;
- case R.id.ll__place_phone:
- items.add(mTvPhone.getText().toString());
- break;
- case R.id.ll__place_schedule:
- items.add(mFullOpeningHours.getText().toString());
- break;
- case R.id.ll__place_operator:
- items.add(mTvOperator.getText().toString());
- break;
- case R.id.ll__place_wiki:
- items.add(mMapObject.getMetadata(Metadata.MetadataType.FMD_WIKIPEDIA));
- break;
+ case R.id.ll__place_latlon:
+ final double lat = mMapObject.getLat();
+ final double lon = mMapObject.getLon();
+ items.add(Framework.nativeFormatLatLon(lat, lon, false));
+ items.add(Framework.nativeFormatLatLon(lat, lon, true));
+ break;
+ case R.id.ll__place_website:
+ items.add(mTvWebsite.getText().toString());
+ break;
+ case R.id.ll__place_email:
+ items.add(mTvEmail.getText().toString());
+ break;
+ case R.id.ll__place_phone:
+ items.add(mTvPhone.getText().toString());
+ break;
+ case R.id.ll__place_schedule:
+ items.add(mFullOpeningHours.getText().toString());
+ break;
+ case R.id.ll__place_operator:
+ items.add(mTvOperator.getText().toString());
+ break;
+ case R.id.ll__place_wiki:
+ items.add(mMapObject.getMetadata(Metadata.MetadataType.FMD_WIKIPEDIA));
+ break;
}
final String copyText = getResources().getString(android.R.string.copy);
diff --git a/android/src/com/mapswithme/maps/widget/placepage/ReviewAdapter.java b/android/src/com/mapswithme/maps/widget/placepage/ReviewAdapter.java
new file mode 100644
index 0000000000..901acbe98a
--- /dev/null
+++ b/android/src/com/mapswithme/maps/widget/placepage/ReviewAdapter.java
@@ -0,0 +1,136 @@
+package com.mapswithme.maps.widget.placepage;
+
+import android.support.annotation.NonNull;
+import android.text.TextUtils;
+import android.text.format.DateFormat;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.TextView;
+
+import com.mapswithme.maps.R;
+import com.mapswithme.maps.review.Review;
+import com.mapswithme.util.UiUtils;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Locale;
+
+class ReviewAdapter extends BaseAdapter
+{
+ private static final int MAX_COUNT = 3;
+
+ @NonNull
+ private ArrayList<Review> mItems = new ArrayList<>();
+
+ @Override
+ public int getCount()
+ {
+ return Math.min(mItems.size(), MAX_COUNT);
+ }
+
+ @Override
+ public Object getItem(int position)
+ {
+ return mItems.get(position);
+ }
+
+ @Override
+ public long getItemId(int position)
+ {
+ return position;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent)
+ {
+ ViewHolder holder;
+ if (convertView == null)
+ {
+ convertView = LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.item_comment, parent, false);
+ holder = new ViewHolder(convertView);
+ convertView.setTag(holder);
+ }
+ else
+ {
+ holder = (ViewHolder) convertView.getTag();
+ }
+
+ holder.bind(mItems.get(position), position > 0);
+
+ return convertView;
+ }
+
+ public void setItems(@NonNull ArrayList<Review> items)
+ {
+ this.mItems = items;
+ notifyDataSetChanged();
+ }
+
+ @NonNull
+ public ArrayList<Review> getItems()
+ {
+ return mItems;
+ }
+
+ private static class ViewHolder
+ {
+ @NonNull
+ final View mDivider;
+ @NonNull
+ final TextView mUserName;
+ @NonNull
+ final TextView mCommentDate;
+ @NonNull
+ final TextView mRating;
+ @NonNull
+ final View mPositiveReview;
+ @NonNull
+ final TextView mTvPositiveReview;
+ @NonNull
+ final View mNegativeReview;
+ @NonNull
+ final TextView mTvNegativeReview;
+
+ public ViewHolder(View view)
+ {
+ mDivider = view.findViewById(R.id.v__divider);
+ mUserName = (TextView) view.findViewById(R.id.tv__user_name);
+ mCommentDate = (TextView) view.findViewById(R.id.tv__comment_date);
+ mRating = (TextView) view.findViewById(R.id.tv__user_rating);
+ mPositiveReview = view.findViewById(R.id.ll__positive_review);
+ mTvPositiveReview = (TextView) view.findViewById(R.id.tv__positive_review);
+ mNegativeReview = view.findViewById(R.id.ll__negative_review);
+ mTvNegativeReview = (TextView) view.findViewById(R.id.tv__negative_review);
+ }
+
+ public void bind(Review item, boolean isShowDivider)
+ {
+ UiUtils.showIf(isShowDivider, mDivider);
+ mUserName.setText(item.getAuthor());
+ Date date = new Date(item.getDate());
+ mCommentDate.setText(DateFormat.getMediumDateFormat(mCommentDate.getContext()).format(date));
+ mRating.setText(String.format(Locale.getDefault(), "%.1f", item.getRating()));
+ if (TextUtils.isEmpty(item.getReviewPositive()))
+ {
+ UiUtils.hide(mPositiveReview);
+ }
+ else
+ {
+ UiUtils.show(mPositiveReview);
+ mTvPositiveReview.setText(item.getReviewPositive());
+ }
+ if (TextUtils.isEmpty(item.getReviewNegative()))
+ {
+ UiUtils.hide(mNegativeReview);
+ }
+ else
+ {
+ UiUtils.show(mNegativeReview);
+ mTvNegativeReview.setText(item.getReviewNegative());
+ }
+ }
+ }
+}
diff --git a/android/src/com/mapswithme/maps/widget/placepage/SponsoredHotel.java b/android/src/com/mapswithme/maps/widget/placepage/SponsoredHotel.java
index 2b97298c41..1ef4c46d28 100644
--- a/android/src/com/mapswithme/maps/widget/placepage/SponsoredHotel.java
+++ b/android/src/com/mapswithme/maps/widget/placepage/SponsoredHotel.java
@@ -1,53 +1,194 @@
package com.mapswithme.maps.widget.placepage;
+import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import android.text.TextUtils;
+import com.mapswithme.maps.bookmarks.data.MapObject;
+import com.mapswithme.maps.bookmarks.data.Metadata;
+import com.mapswithme.maps.gallery.Image;
+import com.mapswithme.maps.review.Review;
+
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;
-import com.mapswithme.maps.bookmarks.data.MapObject;
-import com.mapswithme.maps.bookmarks.data.Metadata;
-
@UiThread
public final class SponsoredHotel
{
private static class Price
{
- final String price;
- final String currency;
+ @NonNull
+ final String mPrice;
+ @NonNull
+ final String mCurrency;
+
+ private Price(@NonNull String price, @NonNull String currency)
+ {
+ mPrice = price;
+ mCurrency = currency;
+ }
+ }
+
+ static class FacilityType
+ {
+ @NonNull
+ private final String mKey;
+ @NonNull
+ private final String mName;
+
+ public FacilityType(@NonNull String key, @NonNull String name)
+ {
+ mKey = key;
+ mName = name;
+ }
+
+ @NonNull
+ public String getKey()
+ {
+ return mKey;
+ }
+
+ @NonNull
+ public String getName()
+ {
+ return mName;
+ }
+ }
+
+ static class NearbyObject
+ {
+ @NonNull
+ private final String mCategory;
+ @NonNull
+ private final String mTitle;
+ @NonNull
+ private final String mDistance;
+ private final double mLatitude;
+ private final double mLongitude;
+
+ public NearbyObject(@NonNull String category, @NonNull String title,
+ @NonNull String distance, double lat, double lon)
+ {
+ mCategory = category;
+ mTitle = title;
+ mDistance = distance;
+ mLatitude = lat;
+ mLongitude = lon;
+ }
+
+ @NonNull
+ public String getCategory()
+ {
+ return mCategory;
+ }
+
+ @NonNull
+ public String getTitle()
+ {
+ return mTitle;
+ }
+
+ @NonNull
+ public String getDistance()
+ {
+ return mDistance;
+ }
+
+ public double getLatitude()
+ {
+ return mLatitude;
+ }
+
+ public double getLongitude()
+ {
+ return mLongitude;
+ }
+ }
+
+ static class HotelInfo
+ {
+ @Nullable
+ final String mDescription;
+ @Nullable
+ final Image[] mPhotos;
+ @Nullable
+ final FacilityType[] mFacilities;
+ @Nullable
+ final Review[] mReviews;
+ @Nullable
+ final NearbyObject[] mNearby;
- private Price(String price, String currency)
+ public HotelInfo(@Nullable String description, @Nullable Image[] photos,
+ @Nullable FacilityType[] facilities, @Nullable Review[] reviews,
+ @Nullable NearbyObject[] nearby)
{
- this.price = price;
- this.currency = currency;
+ mDescription = description;
+ mPhotos = photos;
+ mFacilities = facilities;
+ mReviews = reviews;
+ mNearby = nearby;
}
}
interface OnPriceReceivedListener
{
- void onPriceReceived(String id, String price, String currency);
+ /**
+ * This method is called from the native core on the UI thread
+ * when the Hotel price will be obtained
+ *
+ * @param id A hotel id
+ * @param price A price
+ * @param currency A price currency
+ */
+ @UiThread
+ void onPriceReceived(@NonNull String id, @NonNull String price, @NonNull String currency);
+ }
+
+ interface OnInfoReceivedListener
+ {
+ /**
+ * This method is called from the native core on the UI thread
+ * when the Hotel information will be obtained
+ *
+ * @param id A hotel id
+ * @param info A hotel info
+ */
+ @UiThread
+ void onInfoReceived(@NonNull String id, @NonNull HotelInfo info);
}
// Hotel ID -> Price
+ @NonNull
private static final Map<String, Price> sPriceCache = new HashMap<>();
- private static WeakReference<OnPriceReceivedListener> sListener;
+ // Hotel ID -> Description
+ @NonNull
+ private static final Map<String, HotelInfo> sInfoCache = new HashMap<>();
+ @NonNull
+ private static WeakReference<OnPriceReceivedListener> sPriceListener = new WeakReference<>(null);
+ @NonNull
+ private static WeakReference<OnInfoReceivedListener> sInfoListener = new WeakReference<>(null);
+ @Nullable
private String mId;
- final String rating;
- final String price;
- final String urlBook;
- final String urlDescription;
+ @NonNull
+ final String mRating;
+ @NonNull
+ final String mPrice;
+ @NonNull
+ final String mUrlBook;
+ @NonNull
+ final String mUrlDescription;
- public SponsoredHotel(String rating, String price, String urlBook, String urlDescription)
+ public SponsoredHotel(@NonNull String rating, @NonNull String price, @NonNull String urlBook,
+ @NonNull String urlDescription)
{
- this.rating = rating;
- this.price = price;
- this.urlBook = urlBook;
- this.urlDescription = urlDescription;
+ mRating = rating;
+ mPrice = price;
+ mUrlBook = urlBook;
+ mUrlDescription = urlDescription;
}
void updateId(MapObject point)
@@ -55,61 +196,107 @@ public final class SponsoredHotel
mId = point.getMetadata(Metadata.MetadataType.FMD_SPONSORED_ID);
}
+ @Nullable
public String getId()
{
return mId;
}
+ @NonNull
public String getRating()
{
- return rating;
+ return mRating;
}
+ @NonNull
public String getPrice()
{
- return price;
+ return mPrice;
}
+ @NonNull
public String getUrlBook()
{
- return urlBook;
+ return mUrlBook;
}
+ @NonNull
public String getUrlDescription()
{
- return urlDescription;
+ return mUrlDescription;
+ }
+
+ static void setPriceListener(@NonNull OnPriceReceivedListener listener)
+ {
+ sPriceListener = new WeakReference<>(listener);
}
- public static void setListener(OnPriceReceivedListener listener)
+ static void setInfoListener(@NonNull OnInfoReceivedListener listener)
{
- sListener = new WeakReference<>(listener);
+ sInfoListener = new WeakReference<>(listener);
}
+ /**
+ * Make request to obtain hotel price information.
+ * This method also checks cache for requested hotel id
+ * and if cache exists - call {@link #onPriceReceived(String, String, String) onPriceReceived} immediately
+ *
+ * @param id A Hotel id
+ * @param currencyCode A user currency
+ */
static void requestPrice(String id, String currencyCode)
{
Price p = sPriceCache.get(id);
if (p != null)
- onPriceReceived(id, p.price, p.currency);
+ onPriceReceived(id, p.mPrice, p.mCurrency);
nativeRequestPrice(id, currencyCode);
}
- @SuppressWarnings("unused")
- private static void onPriceReceived(String id, String price, String currency)
+ /**
+ * Make request to obtain hotel information.
+ * This method also checks cache for requested hotel id
+ * and if cache exists - call {@link #onInfoReceived(String, HotelInfo) onInfoReceived} immediately
+ *
+ * @param id A Hotel id
+ * @param locale A user locale
+ */
+ static void requestInfo(String id, String locale)
+ {
+ HotelInfo info = sInfoCache.get(id);
+ if (info != null)
+ onInfoReceived(id, info);
+
+ nativeRequestInfo(id, locale);
+ }
+
+ private static void onPriceReceived(@NonNull String id, @NonNull String price,
+ @NonNull String currency)
{
if (TextUtils.isEmpty(price))
return;
sPriceCache.put(id, new Price(price, currency));
- OnPriceReceivedListener listener = sListener.get();
- if (listener == null)
- sListener = null;
- else
+
+ OnPriceReceivedListener listener = sPriceListener.get();
+ if (listener != null)
listener.onPriceReceived(id, price, currency);
}
+ private static void onInfoReceived(@NonNull String id, @NonNull HotelInfo info)
+ {
+ sInfoCache.put(id, info);
+
+ OnInfoReceivedListener listener = sInfoListener.get();
+ if (listener != null)
+ listener.onInfoReceived(id, info);
+ }
+
@Nullable
public static native SponsoredHotel nativeGetCurrent();
- private static native void nativeRequestPrice(String id, String currencyCode);
+
+ private static native void nativeRequestPrice(@NonNull String id, @NonNull String currencyCode);
+
+ private static native void nativeRequestInfo(@NonNull String id, @NonNull String locale);
}
diff --git a/android/src/com/mapswithme/maps/widget/recycler/DividerItemDecoration.java b/android/src/com/mapswithme/maps/widget/recycler/DividerItemDecoration.java
new file mode 100644
index 0000000000..ea3d6f0da1
--- /dev/null
+++ b/android/src/com/mapswithme/maps/widget/recycler/DividerItemDecoration.java
@@ -0,0 +1,131 @@
+package com.mapswithme.maps.widget.recycler;
+
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.NonNull;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+
+/**
+ * Adds interior dividers to a RecyclerView with a LinearLayoutManager or its
+ * subclass.
+ */
+public class DividerItemDecoration extends RecyclerView.ItemDecoration
+{
+
+ @NonNull
+ private final Drawable mDivider;
+ private int mOrientation;
+
+ /**
+ * Sole constructor. Takes in a {@link Drawable} to be used as the interior
+ * divider.
+ *
+ * @param divider A divider {@code Drawable} to be drawn on the RecyclerView
+ */
+ public DividerItemDecoration(@NonNull Drawable divider)
+ {
+ mDivider = divider;
+ }
+
+ /**
+ * Draws horizontal or vertical dividers onto the parent RecyclerView.
+ *
+ * @param canvas The {@link Canvas} onto which dividers will be drawn
+ * @param parent The RecyclerView onto which dividers are being added
+ * @param state The current RecyclerView.State of the RecyclerView
+ */
+ @Override
+ public void onDraw(Canvas canvas, RecyclerView parent, RecyclerView.State state)
+ {
+ if (mOrientation == LinearLayoutManager.HORIZONTAL)
+ drawHorizontalDividers(canvas, parent);
+ else if (mOrientation == LinearLayoutManager.VERTICAL)
+ drawVerticalDividers(canvas, parent);
+ }
+
+ /**
+ * Determines the size and location of offsets between items in the parent
+ * RecyclerView.
+ *
+ * @param outRect The {@link Rect} of offsets to be added around the child
+ * view
+ * @param view The child view to be decorated with an offset
+ * @param parent The RecyclerView onto which dividers are being added
+ * @param state The current RecyclerView.State of the RecyclerView
+ */
+ @Override
+ public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state)
+ {
+ super.getItemOffsets(outRect, view, parent, state);
+
+ if (parent.getChildAdapterPosition(view) == 0)
+ return;
+
+ mOrientation = ((LinearLayoutManager) parent.getLayoutManager()).getOrientation();
+ if (mOrientation == LinearLayoutManager.HORIZONTAL)
+ outRect.left = mDivider.getIntrinsicWidth();
+ else if (mOrientation == LinearLayoutManager.VERTICAL)
+ outRect.top = mDivider.getIntrinsicHeight();
+ }
+
+ /**
+ * Adds dividers to a RecyclerView with a LinearLayoutManager or its
+ * subclass oriented horizontally.
+ *
+ * @param canvas The {@link Canvas} onto which horizontal dividers will be
+ * drawn
+ * @param parent The RecyclerView onto which horizontal dividers are being
+ * added
+ */
+ private void drawHorizontalDividers(Canvas canvas, RecyclerView parent)
+ {
+ int parentTop = parent.getPaddingTop();
+ int parentBottom = parent.getHeight() - parent.getPaddingBottom();
+
+ int childCount = parent.getChildCount();
+ for (int i = 0; i < childCount - 1; i++)
+ {
+ View child = parent.getChildAt(i);
+
+ RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
+
+ int parentLeft = child.getRight() + params.rightMargin;
+ int parentRight = parentLeft + mDivider.getIntrinsicWidth();
+
+ mDivider.setBounds(parentLeft, parentTop, parentRight, parentBottom);
+ mDivider.draw(canvas);
+ }
+ }
+
+ /**
+ * Adds dividers to a RecyclerView with a LinearLayoutManager or its
+ * subclass oriented vertically.
+ *
+ * @param canvas The {@link Canvas} onto which vertical dividers will be
+ * drawn
+ * @param parent The RecyclerView onto which vertical dividers are being
+ * added
+ */
+ private void drawVerticalDividers(Canvas canvas, RecyclerView parent)
+ {
+ int parentLeft = parent.getPaddingLeft();
+ int parentRight = parent.getWidth() - parent.getPaddingRight();
+
+ int childCount = parent.getChildCount();
+ for (int i = 0; i < childCount - 1; i++)
+ {
+ View child = parent.getChildAt(i);
+
+ RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
+
+ int parentTop = child.getBottom() + params.bottomMargin;
+ int parentBottom = parentTop + mDivider.getIntrinsicHeight();
+
+ mDivider.setBounds(parentLeft, parentTop, parentRight, parentBottom);
+ mDivider.draw(canvas);
+ }
+ }
+}
diff --git a/android/src/com/mapswithme/maps/widget/recycler/GridDividerItemDecoration.java b/android/src/com/mapswithme/maps/widget/recycler/GridDividerItemDecoration.java
new file mode 100644
index 0000000000..1dc7334c46
--- /dev/null
+++ b/android/src/com/mapswithme/maps/widget/recycler/GridDividerItemDecoration.java
@@ -0,0 +1,128 @@
+package com.mapswithme.maps.widget.recycler;
+
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.NonNull;
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+
+/**
+ * Adds interior dividers to a RecyclerView with a GridLayoutManager.
+ */
+public class GridDividerItemDecoration extends RecyclerView.ItemDecoration
+{
+
+ @NonNull
+ private final Drawable mHorizontalDivider;
+ @NonNull
+ private final Drawable mVerticalDivider;
+ private final int mNumColumns;
+
+ /**
+ * Sole constructor. Takes in {@link Drawable} objects to be used as
+ * horizontal and vertical dividers.
+ *
+ * @param horizontalDivider A divider {@code Drawable} to be drawn on the
+ * rows of the grid of the RecyclerView
+ * @param verticalDivider A divider {@code Drawable} to be drawn on the
+ * columns of the grid of the RecyclerView
+ * @param numColumns The number of columns in the grid of the RecyclerView
+ */
+ public GridDividerItemDecoration(@NonNull Drawable horizontalDivider,
+ @NonNull Drawable verticalDivider, int numColumns)
+ {
+ mHorizontalDivider = horizontalDivider;
+ mVerticalDivider = verticalDivider;
+ mNumColumns = numColumns;
+ }
+
+ /**
+ * Draws horizontal and/or vertical dividers onto the parent RecyclerView.
+ *
+ * @param canvas The {@link Canvas} onto which dividers will be drawn
+ * @param parent The RecyclerView onto which dividers are being added
+ * @param state The current RecyclerView.State of the RecyclerView
+ */
+ @Override
+ public void onDraw(Canvas canvas, RecyclerView parent, RecyclerView.State state)
+ {
+ drawHorizontalDividers(canvas, parent);
+ drawVerticalDividers(canvas, parent);
+ }
+
+ /**
+ * Determines the size and location of offsets between items in the parent
+ * RecyclerView.
+ *
+ * @param outRect The {@link Rect} of offsets to be added around the child view
+ * @param view The child view to be decorated with an offset
+ * @param parent The RecyclerView onto which dividers are being added
+ * @param state The current RecyclerView.State of the RecyclerView
+ */
+ @Override
+ public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state)
+ {
+ super.getItemOffsets(outRect, view, parent, state);
+
+ boolean childIsInLeftmostColumn = (parent.getChildAdapterPosition(view) % mNumColumns) == 0;
+ if (!childIsInLeftmostColumn)
+ outRect.left = mHorizontalDivider.getIntrinsicWidth();
+
+ boolean childIsInFirstRow = (parent.getChildAdapterPosition(view)) < mNumColumns;
+ if (!childIsInFirstRow)
+ outRect.top = mVerticalDivider.getIntrinsicHeight();
+ }
+
+ /**
+ * Adds horizontal dividers to a RecyclerView with a GridLayoutManager or
+ * its subclass.
+ *
+ * @param canvas The {@link Canvas} onto which dividers will be drawn
+ * @param parent The RecyclerView onto which dividers are being added
+ */
+ private void drawHorizontalDividers(Canvas canvas, RecyclerView parent)
+ {
+ int parentTop = parent.getPaddingTop();
+ int parentBottom = parent.getHeight() - parent.getPaddingBottom();
+
+ for (int i = 0; i < mNumColumns; i++)
+ {
+ View child = parent.getChildAt(i);
+ RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
+
+ int parentLeft = child.getRight() + params.rightMargin;
+ int parentRight = parentLeft + mHorizontalDivider.getIntrinsicWidth();
+
+ mHorizontalDivider.setBounds(parentLeft, parentTop, parentRight, parentBottom);
+ mHorizontalDivider.draw(canvas);
+ }
+ }
+
+ /**
+ * Adds vertical dividers to a RecyclerView with a GridLayoutManager or its
+ * subclass.
+ *
+ * @param canvas The {@link Canvas} onto which dividers will be drawn
+ * @param parent The RecyclerView onto which dividers are being added
+ */
+ private void drawVerticalDividers(Canvas canvas, RecyclerView parent)
+ {
+ int parentLeft = parent.getPaddingLeft();
+ int parentRight = parent.getWidth() - parent.getPaddingRight();
+
+ int childCount = parent.getChildCount();
+ int numRows = (childCount + (mNumColumns - 1)) / mNumColumns;
+ for (int i = 0; i < numRows - 1; i++)
+ {
+ View child = parent.getChildAt(i * mNumColumns);
+ RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
+
+ int parentTop = child.getBottom() + params.bottomMargin;
+ int parentBottom = parentTop + mVerticalDivider.getIntrinsicHeight();
+
+ mVerticalDivider.setBounds(parentLeft, parentTop, parentRight, parentBottom);
+ mVerticalDivider.draw(canvas);
+ }
+ }
+}
diff --git a/android/src/com/mapswithme/util/UiUtils.java b/android/src/com/mapswithme/util/UiUtils.java
index 55c329dc93..1e9fdfb469 100644
--- a/android/src/com/mapswithme/util/UiUtils.java
+++ b/android/src/com/mapswithme/util/UiUtils.java
@@ -7,6 +7,7 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.graphics.Rect;
import android.net.Uri;
import android.os.Build;
import android.support.annotation.AnyRes;
@@ -19,6 +20,7 @@ import android.support.design.widget.TextInputLayout;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
+import android.view.MotionEvent;
import android.view.Surface;
import android.view.View;
import android.view.ViewTreeObserver;
@@ -339,6 +341,24 @@ public final class UiUtils
return context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
}
+ public static boolean isViewTouched(@NonNull MotionEvent event, @NonNull View view)
+ {
+ if (UiUtils.isHidden(view))
+ return false;
+
+ int x = (int) event.getX();
+ int y = (int) event.getY();
+ int[] location = new int[2];
+ view.getLocationOnScreen(location);
+ int viewX = location[0];
+ int viewY = location[1];
+ int width = view.getWidth();
+ int height = view.getHeight();
+ Rect viewRect = new Rect(viewX, viewY, viewX + width, viewY + height);
+
+ return viewRect.contains(x, y);
+ }
+
// utility class
private UiUtils() {}
}