From c7b4e82f69bea8cec54030e7216a3bdfe71de1b6 Mon Sep 17 00:00:00 2001 From: Dmitry Kunin Date: Tue, 25 Jun 2013 09:34:44 +0300 Subject: [PATCH] [android] code review fixes. new place page look. (in portrait). --- android/jni/com/mapswithme/maps/Framework.cpp | 23 ++-- .../res/layout-land/activity_map_object.xml | 12 +- .../fragment_simple_navigation.xml | 20 ++-- android/res/layout/activity_map_object.xml | 14 +-- android/res/layout/fragment_map_object.xml | 40 +++---- .../res/layout/fragment_simple_navigation.xml | 72 ++++++----- android/res/values/colors.xml | 2 + android/res/values/dimens.xml | 13 +- android/res/values/styles.xml | 8 ++ .../src/com/mapswithme/maps/ArrowImage.java | 113 ++++++++++-------- .../mapswithme/maps/MapObjectFragment.java | 42 ++++--- .../maps/SimpleNavigationFragment.java | 33 ++--- android/src/com/mapswithme/util/UiUtils.java | 38 +++++- map/measurement_utils.cpp | 7 +- map/measurement_utils.hpp | 3 - 15 files changed, 257 insertions(+), 183 deletions(-) diff --git a/android/jni/com/mapswithme/maps/Framework.cpp b/android/jni/com/mapswithme/maps/Framework.cpp index cea03defa2..2067ac32df 100644 --- a/android/jni/com/mapswithme/maps/Framework.cpp +++ b/android/jni/com/mapswithme/maps/Framework.cpp @@ -709,10 +709,8 @@ namespace android if (m_bmBaloon == NULL) return; - int width = m_work.GetNavigator().Screen().GetWidth(); - int height = m_work.GetNavigator().Screen().GetHeight(); - - m_bmBaloon->onScreenSize(width, height); + ScreenBase screen = m_work.GetNavigator().Screen(); + m_bmBaloon->onScreenSize(screen.GetWidth(), screen.GetHeight()); } BookmarkBalloon * Framework::GetBookmarkBalloon() @@ -1014,17 +1012,15 @@ extern "C" { string distance; double azimut = -1.0; - g_framework->NativeFramework()->GetDistanceAndAzimut( - m2::PointD(merX, merY), cLat, cLon, north, distance, azimut); + g_framework->NativeFramework()->GetDistanceAndAzimut(m2::PointD(merX, merY), cLat, cLon, north, distance, azimut); - jclass klass = env->FindClass("com/mapswithme/maps/bookmarks/data/DistanceAndAzimut"); - ASSERT ( klass, () ); - jmethodID methodID = env->GetMethodID( - klass, "", - "(Ljava/lang/String;D)V"); + jclass daClazz = env->FindClass("com/mapswithme/maps/bookmarks/data/DistanceAndAzimut"); + ASSERT ( daClazz, () ); + + jmethodID methodID = env->GetMethodID(daClazz, "", "(Ljava/lang/String;D)V"); ASSERT ( methodID, () ); - return env->NewObject(klass, methodID, + return env->NewObject(daClazz, methodID, jni::ToJavaString(env, distance.c_str()), static_cast(azimut)); } @@ -1041,7 +1037,6 @@ extern "C" JNIEXPORT jobject JNICALL Java_com_mapswithme_maps_Framework_nativeLatLon2DMS(JNIEnv * env, jclass clazz, jdouble lat, jdouble lon) { - const string dms = MeasurementUtils::FormatLatLonAsDMS(lat, lon, false); - return jni::ToJavaString(env, dms); + return jni::ToJavaString(env, MeasurementUtils::FormatLatLonAsDMS(lat, lon, false)); } } diff --git a/android/res/layout-land/activity_map_object.xml b/android/res/layout-land/activity_map_object.xml index 5f9ca0c32b..2197cb90ee 100644 --- a/android/res/layout-land/activity_map_object.xml +++ b/android/res/layout-land/activity_map_object.xml @@ -14,20 +14,22 @@ android:id="@+id/simpleNavigationFragment" android:name="com.mapswithme.maps.SimpleNavigationFragment" android:layout_width="0dp" - android:layout_height="match_parent" + android:layout_height="wrap_content" android:layout_weight="1" tools:layout="@layout/fragment_simple_navigation" /> - + android:layout_width="@dimen/margin_large" + android:layout_weight="0"/> diff --git a/android/res/layout-land/fragment_simple_navigation.xml b/android/res/layout-land/fragment_simple_navigation.xml index 19313a1f64..4a07159cc2 100644 --- a/android/res/layout-land/fragment_simple_navigation.xml +++ b/android/res/layout-land/fragment_simple_navigation.xml @@ -11,26 +11,19 @@ android:id="@+id/staticData" android:layout_width="match_parent" android:layout_height="0dp" - android:padding="@dimen/margin_small" + android:layout_marginTop="@dimen/margin_medium" android:layout_weight="1" - android:background="@drawable/abs__item_background_holo_light" + android:background="@drawable/spinner_background_holo_light" android:gravity="center" - android:orientation="vertical" > + android:orientation="vertical" + android:padding="@dimen/margin_small" > - - + android:gravity="center|left" /> + android:gravity="center" + android:textStyle="bold" /> @@ -14,18 +11,19 @@ android:id="@+id/simpleNavigationFragment" android:name="com.mapswithme.maps.SimpleNavigationFragment" android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_weight="0" + android:layout_height="0dp" + android:layout_weight="1" tools:layout="@layout/fragment_simple_navigation" /> - \ No newline at end of file diff --git a/android/res/layout/fragment_map_object.xml b/android/res/layout/fragment_map_object.xml index 40af4e47e4..64d7e8fd9c 100644 --- a/android/res/layout/fragment_map_object.xml +++ b/android/res/layout/fragment_map_object.xml @@ -2,8 +2,7 @@ + android:orientation="vertical" > - - - - - + + + + android:layout_marginTop="@dimen/margin_small" /> - + - + android:gravity="top" + android:orientation="vertical" > - - - - - - - - + android:background="@drawable/mz"> + android:layout_alignParentLeft="true" + android:layout_alignParentTop="true" + android:gravity="center"/> + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_centerInParent="true" + android:layout_margin="@dimen/dp_x_2"/> + + + + + \ No newline at end of file diff --git a/android/res/values/colors.xml b/android/res/values/colors.xml index 795d521b52..252b50c52f 100644 --- a/android/res/values/colors.xml +++ b/android/res/values/colors.xml @@ -1,4 +1,6 @@ #FFEEEEDD + #ECECEC + #58C627 \ No newline at end of file diff --git a/android/res/values/dimens.xml b/android/res/values/dimens.xml index 85309ebe89..f81c6e0ece 100644 --- a/android/res/values/dimens.xml +++ b/android/res/values/dimens.xml @@ -5,12 +5,23 @@ 16dp 16dp + 4dp 8dp 16dp 32dp + + 4dp + 8dp + 12dp + 16dp + 24dp + 32dp + 40dp + 48dp + 320dp - 64dp + 120dp diff --git a/android/res/values/styles.xml b/android/res/values/styles.xml index 495f6dad31..4bc608914b 100644 --- a/android/res/values/styles.xml +++ b/android/res/values/styles.xml @@ -12,4 +12,12 @@ @null 0dp + diff --git a/android/src/com/mapswithme/maps/ArrowImage.java b/android/src/com/mapswithme/maps/ArrowImage.java index d0e0b798f5..7184a3300b 100644 --- a/android/src/com/mapswithme/maps/ArrowImage.java +++ b/android/src/com/mapswithme/maps/ArrowImage.java @@ -15,35 +15,38 @@ public class ArrowImage extends ImageView { static private String TAG = "ArrowImage"; - // Drawing params - private int mWidth; - private int mHeight; - private int mOffset; - private final static int BASE_OFFSET = 4; + private float mWidth; + private float mHeight; + + private final static float RAD_MULT = .45f; + private final float SQ2 = (float) Math.sqrt(2); + private float mRad; - private Paint mPaint; - private Path mPath; private boolean mDrawArrow; + private Paint mArrowPaint; + private Path mArrowPath; - private float mAngle; + private boolean mDrawCircle = false; + private Paint mCirclePaint = new Paint(); - //Animation params + // Animation params private boolean mJustStarted = true; private float mCurrentAngle; - private final static long UPDATE_RATE = 60; - private final static long UPDATE_DELAY = 1000/UPDATE_RATE; - private final static double ROTATION_SPEED = 120; // degrees per second - private final static double ROTATION_STEP = ROTATION_SPEED/UPDATE_RATE; + private final static long UPDATE_RATE = 60; + private final static long UPDATE_DELAY = 1000 / UPDATE_RATE; + private final static double ROTATION_SPEED = 120; + private final static double ROTATION_STEP = ROTATION_SPEED / UPDATE_RATE; private final static double ERR = ROTATION_STEP; + private float mAngle; private Runnable mAnimateTask = new Runnable() { @Override public void run() { - if (step()); - postDelayed(this, UPDATE_DELAY); + if (step()) + postDelayed(this, UPDATE_DELAY); } }; @@ -51,13 +54,14 @@ public class ArrowImage extends ImageView { super(context, attrs); - mPaint = new Paint(); - mPaint.setFlags(mPaint.getFlags() | Paint.ANTI_ALIAS_FLAG); - mPaint.setStyle(Style.FILL); - //TODO set from resources - mPaint.setColor(Color.BLACK); + mArrowPaint = new Paint(); + mArrowPaint.setFlags(mArrowPaint.getFlags() | Paint.ANTI_ALIAS_FLAG); + mArrowPaint.setStyle(Style.FILL); + mArrowPaint.setColor(Color.BLACK); + mArrowPath = new Path(); - mPath = new Path(); + mCirclePaint.setColor(0xafFFFFFF); + mCirclePaint.setAntiAlias(true); } public void setFlag(Resources res, String packageName, String flag) @@ -85,21 +89,29 @@ public class ArrowImage extends ImageView invalidate(); } + public void setDrawCircle(boolean draw) + { + mDrawCircle = draw; + } + + public void setCircleColor(int color) + { + mCirclePaint.setColor(color); + } + public void setAzimut(double azimut) { - // TODO add filter setVisibility(VISIBLE); setImageDrawable(null); mDrawArrow = true; - - mAngle = (float)(azimut / Math.PI * 180.0); + mAngle = (float) (azimut / Math.PI * 180.0); if (!mJustStarted) animateRotation(); else { mCurrentAngle = mAngle; // to skip rotation from 0 - mJustStarted = false; + mJustStarted = false; } invalidate(); @@ -118,7 +130,6 @@ public class ArrowImage extends ImageView mDrawArrow = false; } - @Override protected void onDetachedFromWindow() { @@ -126,7 +137,6 @@ public class ArrowImage extends ImageView removeCallbacks(mAnimateTask); } - private boolean step() { final double diff = mAngle - mCurrentAngle; @@ -136,8 +146,10 @@ public class ArrowImage extends ImageView // at [0, 360] looped segment final double signum = -1 * Math.signum(diff) * Math.signum(Math.abs(diff) - 180); mCurrentAngle += signum * ROTATION_STEP; - if (mCurrentAngle < 0) mCurrentAngle = 360; - else if (mCurrentAngle > 360) mCurrentAngle = 0; + if (mCurrentAngle < 0) + mCurrentAngle = 360; + else if (mCurrentAngle > 360) + mCurrentAngle = 0; invalidate(); return true; } @@ -149,16 +161,27 @@ public class ArrowImage extends ImageView { super.onSizeChanged(w, h, oldw, oldh); - mOffset = (int) (BASE_OFFSET * getResources().getDisplayMetrics().density); - mWidth = w - getPaddingLeft() - getPaddingRight() - 2*mOffset; - mHeight = h - getPaddingBottom() - getPaddingTop() - 2*mOffset; + mWidth = w - getPaddingLeft() - getPaddingRight(); + mHeight = h - getPaddingBottom() - getPaddingTop(); + + mRad = RAD_MULT * Math.min(w, h); + final float c0 = Math.min(h, w) / 2; + // calculate path + mArrowPath.reset(); + mArrowPath.moveTo(c0 + mRad, c0); + mArrowPath.lineTo((float) (c0 - mRad / SQ2), (float) (c0 + mRad / SQ2)); + mArrowPath.lineTo((float) (c0 - mRad * SQ2 / 4), c0); + mArrowPath.lineTo((float) (c0 - mRad / SQ2), (float) (c0 - mRad / SQ2)); + mArrowPath.lineTo(c0 + mRad, c0); + mArrowPath.close(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); - if (mWidth <= 0 || mHeight <= 0) return; + if (mWidth <= 0 || mHeight <= 0) + return; if (mDrawArrow) { @@ -166,26 +189,18 @@ public class ArrowImage extends ImageView final float w = mWidth; final float h = mHeight; + final float sSide = Math.min(w, h); + canvas.translate((w - sSide) / 2, (h - sSide) / 2); + canvas.rotate(-mCurrentAngle, sSide / 2, sSide / 2); - if (mAngle < 0.0) - canvas.drawCircle(w/2, h/2, Math.min(w/2, h/2) - 5, mPaint); - else + if (mDrawCircle) { - mPath.reset(); - mPath.moveTo(w/3, h/2); - mPath.lineTo(0, h/2 - h/3); - mPath.lineTo(w, h/2); - mPath.lineTo(0, h/2 + h/3); - mPath.lineTo(w/3, h/2); - mPath.close(); - - canvas.translate(getPaddingLeft() + mOffset, getPaddingTop() + mOffset); - canvas.rotate(-mCurrentAngle, w/2, h/2); - canvas.drawPath(mPath, mPaint); - - canvas.restore(); + final float rad = Math.min(w, h) / 2; + canvas.drawCircle(rad, rad, rad, mCirclePaint); } + canvas.drawPath(mArrowPath, mArrowPaint); + canvas.restore(); } } } diff --git a/android/src/com/mapswithme/maps/MapObjectFragment.java b/android/src/com/mapswithme/maps/MapObjectFragment.java index 456f6c80f0..9d6e3fafe9 100644 --- a/android/src/com/mapswithme/maps/MapObjectFragment.java +++ b/android/src/com/mapswithme/maps/MapObjectFragment.java @@ -49,7 +49,8 @@ public class MapObjectFragment extends Fragment private static final int MENU_SHARE = 0x100; private TextView mNameTV; - private TextView mGroupTypeTV; + private TextView mGroupTV; + private TextView mTypeTV; private TextView mDescrTV; private Button mAddToBookmarks; @@ -77,12 +78,13 @@ public class MapObjectFragment extends Fragment UiUtils.show(mEditBmk); UiUtils.hide(mOpenWith); - setTexts(bookmark.getName(), bookmark.getCategoryName(), bookmark.getBookmarkDescription(), bookmark.getLat(), bookmark.getLon()); + setTexts(bookmark.getName(), null , bookmark.getCategoryName(), bookmark.getBookmarkDescription(), bookmark.getLat(), bookmark.getLon()); - @SuppressWarnings("deprecation") - Drawable icon = new BitmapDrawable(bookmark.getIcon().getIcon()); - mNameTV.setCompoundDrawables(UiUtils - .setCompoundDrawableBounds(icon, R.dimen.icon_size, getResources()), null, null, null); + final int circleSize = (int) (getResources().getDimension(R.dimen.margin_medium) + .5); + Drawable icon = UiUtils.drawCircleForPin(bookmark.getIcon().getType(), circleSize, getResources()); + + mGroupTV.setCompoundDrawables(UiUtils + .setCompoundDrawableBounds(icon, R.dimen.margin_medium, getResources()), null, null, null); mEditBmk.setCompoundDrawables(UiUtils .setCompoundDrawableBounds(R.drawable.edit_bookmark, R.dimen.icon_size, getResources()), null, null, null); @@ -109,7 +111,7 @@ public class MapObjectFragment extends Fragment else UiUtils.hide(mOpenWith); - setTexts(name, null, null, lat, lon); + setTexts(name, null, null, null, lat, lon); mType = MapObjectType.API_POINT; } @@ -120,7 +122,7 @@ public class MapObjectFragment extends Fragment UiUtils.hide(mEditBmk); UiUtils.hide(mOpenWith); - setTexts(name, type, null, lat, lon); + setTexts(name, type, null, null, lat, lon); mType = MapObjectType.POI; } @@ -132,12 +134,12 @@ public class MapObjectFragment extends Fragment UiUtils.hide(mOpenWith); final String name = getString(R.string.my_position); - setTexts(name, "", null, lat, lon); + setTexts(name, null, null, null, lat, lon); mType = MapObjectType.MY_POSITION; } - private void setTexts(String name, String type, String descr, double lat, double lon) + private void setTexts(String name, String type, String group,String descr, double lat, double lon) { if (!TextUtils.isEmpty(name)) mName = name; @@ -146,14 +148,25 @@ public class MapObjectFragment extends Fragment mNameTV.setText(mName); + // Type of POI if (TextUtils.isEmpty(type)) - UiUtils.hide(mGroupTypeTV); + UiUtils.hide(mTypeTV); else { - mGroupTypeTV.setText(type); - UiUtils.show(mGroupTypeTV); + mTypeTV.setText(type); + UiUtils.show(mTypeTV); } + // Group of BMK + if (TextUtils.isEmpty(group)) + UiUtils.hide(mGroupTV); + else + { + mGroupTV.setText(group); + UiUtils.show(mGroupTV); + } + + // Description of BMK if (TextUtils.isEmpty(descr)) UiUtils.hide(mDescrTV); else @@ -172,8 +185,9 @@ public class MapObjectFragment extends Fragment final View view = inflater.inflate(R.layout.fragment_map_object, container, false); // find views mNameTV = (TextView) view.findViewById(R.id.name); - mGroupTypeTV = (TextView) view.findViewById(R.id.groupType); mDescrTV = (TextView) view.findViewById(R.id.descr); + mGroupTV = (TextView) view.findViewById(R.id.group); + mTypeTV = (TextView) view.findViewById(R.id.type); mAddToBookmarks = (Button) view.findViewById(R.id.addToBookmarks); diff --git a/android/src/com/mapswithme/maps/SimpleNavigationFragment.java b/android/src/com/mapswithme/maps/SimpleNavigationFragment.java index 6c10d082c3..c5c8fed200 100644 --- a/android/src/com/mapswithme/maps/SimpleNavigationFragment.java +++ b/android/src/com/mapswithme/maps/SimpleNavigationFragment.java @@ -34,7 +34,6 @@ public class SimpleNavigationFragment extends Fragment implements LocationServic private TextView mDistance; // Left private boolean mShowStatic = true; - private TextView mDegrees; private TextView mDMS; // Containers private View mRoot; @@ -52,8 +51,8 @@ public class SimpleNavigationFragment extends Fragment implements LocationServic mDynamicData = mRoot.findViewById(R.id.dynamicData); //Set up views mArrow = (ArrowImage) mRoot.findViewById(R.id.arrow); + mArrow.setDrawCircle(true); mDistance = (TextView) mRoot.findViewById(R.id.distance); - mDegrees = (TextView) mRoot.findViewById(R.id.degrees); mDMS = (TextView) mRoot.findViewById(R.id.dms); setClickers(); @@ -84,7 +83,6 @@ public class SimpleNavigationFragment extends Fragment implements LocationServic if (mPoint != null && mShowStatic) { UiUtils.show(mStaticData); - mDegrees.setText(UiUtils.formatLatLon(mPoint.y, mPoint.x)); mDMS.setText(UiUtils.formatLatLonToDMS(mPoint.y, mPoint.x)); } else @@ -167,8 +165,8 @@ public class SimpleNavigationFragment extends Fragment implements LocationServic } - private static final int MENU_COPY_DEGR = 1; - private static final int MENU_COPY_DMS = 2; + private static final int MENU_COPY_DMS = 1; + private static final int MENU_COPY_DEGR = 2; private static final int MENU_CANCEL = 999; @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) @@ -176,8 +174,8 @@ public class SimpleNavigationFragment extends Fragment implements LocationServic if (v == mStaticData) { // TODO add localizations - menu.add(Menu.NONE, MENU_COPY_DEGR, MENU_COPY_DEGR, String.format("Copy %s", mDegrees.getText().toString())); - menu.add(Menu.NONE, MENU_COPY_DMS, MENU_COPY_DEGR, String.format("Copy %s", mDMS.getText().toString())); + menu.add(Menu.NONE, MENU_COPY_DEGR, MENU_COPY_DEGR, String.format("Copy %s", UiUtils.formatLatLon(mPoint.y, mPoint.x))); + menu.add(Menu.NONE, MENU_COPY_DMS, MENU_COPY_DMS, String.format("Copy %s", mDMS.getText().toString())); menu.add(Menu.NONE, MENU_CANCEL, MENU_CANCEL, android.R.string.cancel); } @@ -188,26 +186,29 @@ public class SimpleNavigationFragment extends Fragment implements LocationServic public boolean onContextItemSelected(MenuItem item) { final int ID = item.getItemId(); + String text = null; if (ID == MENU_COPY_DEGR) { - final String text = mDegrees.getText().toString(); - Utils.copyTextToClipboard(getActivity(), text); - // TODO add localization - Utils.toastShortcut(getActivity(), "Copied to Clipboard: " + text); - return true; + text = UiUtils.formatLatLon(mPoint.y, mPoint.x); } else if (ID == MENU_COPY_DMS) { - final String text = mDMS.getText().toString(); + text = mDMS.getText().toString(); + } + else if (ID == MENU_CANCEL) + return true; + else + super.onContextItemSelected(item); + + if (text != null) + { Utils.copyTextToClipboard(getActivity(), text); // TODO add localization Utils.toastShortcut(getActivity(), "Copied to Clipboard: " + text); return true; } - else if (ID == MENU_CANCEL) - return true; - return super.onContextItemSelected(item); + return false; } private void showShareCoordinatesMenu() diff --git a/android/src/com/mapswithme/util/UiUtils.java b/android/src/com/mapswithme/util/UiUtils.java index ab3be1163f..72ea2e5737 100644 --- a/android/src/com/mapswithme/util/UiUtils.java +++ b/android/src/com/mapswithme/util/UiUtils.java @@ -1,11 +1,16 @@ package com.mapswithme.util; +import java.util.Locale; + import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.view.View; -import java.util.Locale; - import com.mapswithme.maps.Framework; public final class UiUtils @@ -44,6 +49,35 @@ public final class UiUtils return setCompoundDrawableBounds(res.getDrawable(drawableId), dimenId, res); } + public static Drawable drawCircleForPin(String type, int size, Resources res) + { + final Bitmap bmp = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); + final Paint paint = new Paint(); + + //detect color by pin name + int color = Color.RED; + try + { + final String[] parts = type.split("-"); + final String colorString = parts[parts.length - 1]; + color = Color.parseColor(colorString.trim()); + } + catch (Exception e) + { + e.printStackTrace(); + // We do nothing in this case + // Just use RED + } + paint.setColor(color); + paint.setAntiAlias(true); + + final Canvas c = new Canvas(bmp); + final float dim = size/2.0f; + c.drawCircle(dim, dim, dim, paint); + + return new BitmapDrawable(res, bmp); + } + // utility class private UiUtils() {}; } diff --git a/map/measurement_utils.cpp b/map/measurement_utils.cpp index 8b06792b72..0015db0140 100644 --- a/map/measurement_utils.cpp +++ b/map/measurement_utils.cpp @@ -1,9 +1,12 @@ #include "measurement_utils.hpp" #include "../platform/settings.hpp" - #include "../base/string_utils.hpp" +#include "../std/cmath.hpp" +#include "../std/iomanip.hpp" +#include "../std/sstream.hpp" + namespace MeasurementUtils { @@ -53,6 +56,8 @@ bool FormatDistance(double m, string & res) } } + +/// @todo change ostreamstring to something faster string FormatLatLonAsDMSImpl(string const & posPost, string const & negPost, double value,bool roundSec) { diff --git a/map/measurement_utils.hpp b/map/measurement_utils.hpp index d6d7f763c6..0be63efddf 100644 --- a/map/measurement_utils.hpp +++ b/map/measurement_utils.hpp @@ -1,9 +1,6 @@ #pragma once #include "../std/string.hpp" -#include "../std/cmath.hpp" -#include "../std/iomanip.hpp" -#include "../std/sstream.hpp" namespace MeasurementUtils {