summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYvonne Wong <ywong@cyngn.com>2016-05-11 14:02:12 -0700
committerAdrian DC <radian.dc@gmail.com>2016-12-04 00:34:21 +0100
commitc5b5d42ac53e7777c4d179420bdae7d93dcb1ece (patch)
tree381c5a92a0f438e1ec69ab5726cd7cd1bebb5bdb
parent138768c954a12cd42a426d9cb20b6c2d5fd0dafc (diff)
downloadandroid_packages_apps_Messaging-c5b5d42ac53e7777c4d179420bdae7d93dcb1ece.tar.gz
android_packages_apps_Messaging-c5b5d42ac53e7777c4d179420bdae7d93dcb1ece.tar.bz2
android_packages_apps_Messaging-c5b5d42ac53e7777c4d179420bdae7d93dcb1ece.zip
Add maps preview for addresses and integrate ridesharing request into Messaging
- Uses the first map link found in a message to get a static map view - The static map view is shown as a preview and can be clicked to go into the default maps app - Clicking on request ride will lead the user to RideCoreUI with pickup location prefilled based on the address in the map link - Clicking on the directions button will lead the user to google maps's navigation - Display placeholder background when map is loading or offline with no cached image - Use RoundedCornerTransformation to transform the map into a bitmap with rounded corners - Show brand bitmap from ridesharing services if an active provider is chosen, otherwise display the generic ridesharing icon RM-290 Change-Id: I8e74c6d9beeaa58f01f0a51e5a831c6697c890ea Issue-Id: RIDE-75, RIDE-76, RIDE-77
-rw-r--r--Android.mk8
-rw-r--r--res/drawable-hdpi/ic_map_placeholder.pngbin0 -> 9727 bytes
-rw-r--r--res/drawable-mdpi/ic_map_placeholder.pngbin0 -> 5747 bytes
-rw-r--r--res/drawable-xhdpi/ic_map_placeholder.pngbin0 -> 15806 bytes
-rw-r--r--res/drawable-xxhdpi/ic_map_placeholder.pngbin0 -> 31660 bytes
-rw-r--r--res/drawable/directions_button_background.xml6
-rw-r--r--res/drawable/ic_generic_transport_icon.xml32
-rwxr-xr-xres/drawable/ic_get_directions.xml7
-rw-r--r--res/drawable/maps_button_container_background.xml6
-rw-r--r--res/drawable/request_ride_button_background.xml6
-rw-r--r--res/layout/attachment_maps.xml81
-rw-r--r--res/layout/conversation_message_view.xml4
-rwxr-xr-xres/values/cm_colors.xml5
-rw-r--r--res/values/cm_dimens.xml19
-rw-r--r--res/values/cm_strings.xml3
-rw-r--r--res/values/google_maps_api.xml3
-rw-r--r--src/com/android/messaging/ui/AsyncImageView.java2
-rw-r--r--src/com/android/messaging/ui/conversation/ConversationMessageAdapter.java4
-rw-r--r--src/com/android/messaging/ui/conversation/ConversationMessageView.java196
-rw-r--r--src/com/cyanogenmod/messaging/util/GoogleStaticMapsUtil.java58
-rw-r--r--src/com/cyanogenmod/messaging/util/RidesharingUtil.java112
-rw-r--r--src/com/cyanogenmod/messaging/util/RoundedCornerTransformation.java46
22 files changed, 574 insertions, 24 deletions
diff --git a/Android.mk b/Android.mk
index 532752e..88b3239 100644
--- a/Android.mk
+++ b/Android.mk
@@ -51,6 +51,7 @@ LOCAL_STATIC_JAVA_LIBRARIES += guava
LOCAL_STATIC_JAVA_LIBRARIES += libchips
LOCAL_STATIC_JAVA_LIBRARIES += libphotoviewer
LOCAL_STATIC_JAVA_LIBRARIES += libphonenumber
+LOCAL_STATIC_JAVA_LIBRARIES += play
LOCAL_STATIC_JAVA_LIBRARIES += colorpicker
LOCAL_STATIC_JAVA_LIBRARIES += contacts-picaso
@@ -67,6 +68,7 @@ LOCAL_AAPT_FLAGS += --extra-packages com.android.ex.photo
LOCAL_AAPT_FLAGS += --extra-packages com.android.colorpicker
LOCAL_AAPT_FLAGS += --extra-packages com.android.contacts.common
LOCAL_AAPT_FLAGS += --extra-packages com.android.phone.common
+LOCAT_AAPT_FLAGS += --extra-packages com.google.android.gms
ifdef TARGET_BUILD_APPS
LOCAL_JNI_SHARED_LIBRARIES := libframesequence libgiftranscode
@@ -75,11 +77,7 @@ else
endif
# utilize ContactsCommon's phone-number-based contact-info lookup
-ifeq ($(contacts_common_dir),)
- contacts_common_dir := ../ContactsCommon
-endif
-CONTACTS_COMMON_LOOKUP_PROVIDER ?= $(LOCAL_PATH)/$(contacts_common_dir)/info_lookup
-include $(CONTACTS_COMMON_LOOKUP_PROVIDER)/phonenumber_lookup_provider.mk
+include $(LOCAL_PATH)/../ContactsCommon/info_lookup/phonenumber_lookup_provider.mk
LOCAL_PROGUARD_FLAGS := -ignorewarnings -include build/core/proguard_basic_keeps.flags
diff --git a/res/drawable-hdpi/ic_map_placeholder.png b/res/drawable-hdpi/ic_map_placeholder.png
new file mode 100644
index 0000000..4b40b16
--- /dev/null
+++ b/res/drawable-hdpi/ic_map_placeholder.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_map_placeholder.png b/res/drawable-mdpi/ic_map_placeholder.png
new file mode 100644
index 0000000..bd551a5
--- /dev/null
+++ b/res/drawable-mdpi/ic_map_placeholder.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_map_placeholder.png b/res/drawable-xhdpi/ic_map_placeholder.png
new file mode 100644
index 0000000..b6d5bf9
--- /dev/null
+++ b/res/drawable-xhdpi/ic_map_placeholder.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_map_placeholder.png b/res/drawable-xxhdpi/ic_map_placeholder.png
new file mode 100644
index 0000000..8bba6c2
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_map_placeholder.png
Binary files differ
diff --git a/res/drawable/directions_button_background.xml b/res/drawable/directions_button_background.xml
new file mode 100644
index 0000000..a40b30f
--- /dev/null
+++ b/res/drawable/directions_button_background.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <corners android:bottomRightRadius="2dp" android:topRightRadius="2dp" />
+ <solid android:color="@color/white_54" />
+</shape> \ No newline at end of file
diff --git a/res/drawable/ic_generic_transport_icon.xml b/res/drawable/ic_generic_transport_icon.xml
new file mode 100644
index 0000000..7bd5e2e
--- /dev/null
+++ b/res/drawable/ic_generic_transport_icon.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (c) 2016 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:fillColor="#000000"
+ android:pathData="M18.9,6c-0.2-0.6-0.8-1-1.4-1h-11C5.8,5,5.3,5.4,5.1,6L3,12v8c0,0.5,0.5,1,1,1h1c0.6,0,1-0.5,1-1v-1h12v1c0,0.5,0.5,1,1,1h1
+c0.5,0,1-0.5,1-1v-8L18.9,6z
+M6.5,16C5.7,16,5,15.3,5,14.5S5.7,13,6.5,13S8,13.7,8,14.5S7.3,16,6.5,16z M17.5,16
+c-0.8,0-1.5-0.7-1.5-1.5s0.7-1.5,1.5-1.5s1.5,0.7,1.5,1.5S18.3,16,17.5,16z
+M5,11l1.5-4.5h11L19,11H5z M11,9.5
+c0,0.8-0.7,1.5-1.5,1.5S8,10.3,8,9.5S8.7,8,9.5,8S11,8.7,11,9.5z
+M16,9.5c0,0.8-0.7,1.5-1.5,1.5S13,10.3,13,9.5S13.7,8,14.5,8 S16,8.7,16,9.5z" />
+</vector> \ No newline at end of file
diff --git a/res/drawable/ic_get_directions.xml b/res/drawable/ic_get_directions.xml
new file mode 100755
index 0000000..46c6fb6
--- /dev/null
+++ b/res/drawable/ic_get_directions.xml
@@ -0,0 +1,7 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:width="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path android:fillColor="#000000" android:fillAlpha=".54" android:pathData="M14,14.5V12H10V15H8V11A1,1 0 0,1 9,10H14V7.5L17.5,11M21.71,11.29L12.71,2.29H12.7C12.31,1.9 11.68,1.9 11.29,2.29L2.29,11.29C1.9,11.68 1.9,12.32 2.29,12.71L11.29,21.71C11.68,22.09 12.31,22.1 12.71,21.71L21.71,12.71C22.1,12.32 22.1,11.68 21.71,11.29Z" />
+</vector> \ No newline at end of file
diff --git a/res/drawable/maps_button_container_background.xml b/res/drawable/maps_button_container_background.xml
new file mode 100644
index 0000000..6e3a3a1
--- /dev/null
+++ b/res/drawable/maps_button_container_background.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <corners android:radius="2dp" />
+ <solid android:color="@android:color/transparent" />
+</shape> \ No newline at end of file
diff --git a/res/drawable/request_ride_button_background.xml b/res/drawable/request_ride_button_background.xml
new file mode 100644
index 0000000..ff3ba2e
--- /dev/null
+++ b/res/drawable/request_ride_button_background.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <corners android:bottomLeftRadius="2dp" android:topLeftRadius="2dp" />
+ <solid android:color="@color/white_54" />
+</shape> \ No newline at end of file
diff --git a/res/layout/attachment_maps.xml b/res/layout/attachment_maps.xml
new file mode 100644
index 0000000..4cb691f
--- /dev/null
+++ b/res/layout/attachment_maps.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The CyanogenMod Project (DvTonder)
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:visibility="gone"
+ android:layout_marginBottom="@dimen/conversation_maps_margin"
+ android:layout_width="@dimen/conversation_maps_width"
+ android:layout_height="@dimen/conversation_maps_height">
+ <ImageView
+ android:id="@+id/maps_image"
+ android:layout_width="@dimen/conversation_maps_width"
+ android:layout_height="@dimen/conversation_maps_height"
+ android:minWidth="@dimen/conversation_maps_width"
+ android:minHeight="@dimen/conversation_maps_height"
+ android:adjustViewBounds="true"
+ android:scaleType="centerInside"
+ android:layout_gravity="center_vertical"/>
+ <RelativeLayout
+ android:background="@drawable/maps_button_container_background"
+ android:elevation="@dimen/maps_button_elevation"
+ android:layout_width="@dimen/maps_button_width"
+ android:layout_height="@dimen/maps_button_height"
+ android:layout_alignParentBottom="true"
+ android:layout_marginLeft="@dimen/conversation_maps_margin"
+ android:layout_marginBottom="@dimen/conversation_maps_margin"
+ android:layout_marginRight="@dimen/conversation_maps_margin">
+ <Button
+ style="?android:attr/borderlessButtonStyle"
+ android:id="@+id/request_ride_button"
+ android:background="@drawable/request_ride_button_background"
+ android:layout_alignParentLeft="true"
+ android:layout_width="@dimen/request_ride_button_width"
+ android:layout_height="match_parent"
+ android:paddingLeft="28dp"
+ android:paddingRight="50dp"
+ android:textColor="@color/black_54"
+ android:textSize="@dimen/request_ride_text_size"
+ android:fontFamily="sans-serif-medium"
+ android:text="@string/request_ride"/>
+ <ImageView
+ android:id="@+id/brand_image"
+ android:layout_width="@dimen/brand_image_width"
+ android:layout_height="@dimen/brand_image_height"
+ android:layout_alignParentLeft="true"
+ android:layout_marginLeft="@dimen/brand_image_margin"
+ android:layout_marginRight="@dimen/brand_image_margin"
+ android:layout_centerVertical="true"
+ android:focusable="false"
+ android:clickable="false"
+ android:src="@drawable/ic_generic_transport_icon"/>
+ <ImageView
+ android:id="@+id/directions_button"
+ android:layout_width="@dimen/directions_button_width"
+ android:layout_height="match_parent"
+ android:background="@drawable/directions_button_background"
+ android:layout_alignParentRight="true"
+ android:padding="@dimen/directions_button_padding"
+ android:layout_centerVertical="true"
+ android:src="@drawable/ic_get_directions"/>
+ <View
+ android:id="@+id/button_divider"
+ android:layout_toRightOf="@id/request_ride_button"
+ android:layout_toLeftOf="@id/directions_button"
+ android:layout_width="@dimen/maps_button_divider_width"
+ android:layout_height="match_parent"
+ android:background="@color/transparent_gray" />
+ </RelativeLayout>
+</RelativeLayout> \ No newline at end of file
diff --git a/res/layout/conversation_message_view.xml b/res/layout/conversation_message_view.xml
index 8d10c84..004028d 100644
--- a/res/layout/conversation_message_view.xml
+++ b/res/layout/conversation_message_view.xml
@@ -66,6 +66,10 @@
app:cornerRadius="@dimen/attachment_rounded_corner_radius"
android:contentDescription="@string/message_image_content_description" />
+ <include
+ android:id="@+id/message_maps"
+ layout="@layout/attachment_maps" />
+
</LinearLayout>
<com.android.messaging.ui.conversation.MessageBubbleBackground
diff --git a/res/values/cm_colors.xml b/res/values/cm_colors.xml
index 0342f85..3af3a08 100755
--- a/res/values/cm_colors.xml
+++ b/res/values/cm_colors.xml
@@ -56,4 +56,9 @@
<color name="snack_bar_action_text_color">#EEFF41</color>
<color name="mic_recording_color">#ffffff</color>
<color name="mic_background_color">#ffffff</color>
+
+ <!-- Ridesharing -->
+ <color name="transparent_gray">#1F000000</color>
+ <color name="white_54">#8BFFFFFF</color>
+ <color name="black_54">#8B000000</color>
</resources>
diff --git a/res/values/cm_dimens.xml b/res/values/cm_dimens.xml
index 2dc13c2..f367e0c 100644
--- a/res/values/cm_dimens.xml
+++ b/res/values/cm_dimens.xml
@@ -19,4 +19,23 @@
<dimen name="convolist_attribution_logo_marginRight">13dp</dimen>
<dimen name="convo_attribution_logo_marginRight">5dp</dimen>
<dimen name="convo_attribution_logo_marginBottom">5dp</dimen>
+
+ <!-- Ride sharing -->
+ <dimen name="conversation_maps_width">221dp</dimen>
+ <dimen name="conversation_maps_height">144dp</dimen>
+ <dimen name="conversation_maps_margin">8dp</dimen>
+ <dimen name="maps_button_elevation">2dp</dimen>
+ <dimen name="maps_button_width">205dp</dimen>
+ <dimen name="maps_button_height">32dp</dimen>
+ <dimen name="request_ride_button_width">172dp</dimen>
+ <dimen name="request_ride_text_size">12sp</dimen>
+ <dimen name="brand_image_width">18dp</dimen>
+ <dimen name="brand_image_height">18dp</dimen>
+ <dimen name="brand_image_margin">7dp</dimen>
+ <dimen name="directions_button_width">32dp</dimen>
+ <dimen name="directions_button_padding">4dp</dimen>
+ <dimen name="maps_button_divider_width">1dp</dimen>
+ <dimen name="maps_error_icon_width">1dp</dimen>
+ <dimen name="maps_error_icon_height">1dp</dimen>
+ <dimen name="maps_corner_radius">3dp</dimen>
</resources>
diff --git a/res/values/cm_strings.xml b/res/values/cm_strings.xml
index 4d6d424..e48c45c 100644
--- a/res/values/cm_strings.xml
+++ b/res/values/cm_strings.xml
@@ -155,4 +155,7 @@
<string name="mediapicker_audio_list_item_selected_content_description">The audio is selected.</string>
<string name="mediapicker_audio_list_item_unselected_content_description">The audio is unselected.</string>
<string name="mediapicker_audio_list_title_selection"><xliff:g id="count">%d</xliff:g> selected</string>
+
+ <!-- Ride sharing -->
+ <string name="request_ride">Request Ride</string>
</resources>
diff --git a/res/values/google_maps_api.xml b/res/values/google_maps_api.xml
new file mode 100644
index 0000000..f165a09
--- /dev/null
+++ b/res/values/google_maps_api.xml
@@ -0,0 +1,3 @@
+<resources>
+ <string name="google_maps_key" templateMergeStrategy="preserve" translatable="false"></string>
+</resources>
diff --git a/src/com/android/messaging/ui/AsyncImageView.java b/src/com/android/messaging/ui/AsyncImageView.java
index e18d677..f2394d0 100644
--- a/src/com/android/messaging/ui/AsyncImageView.java
+++ b/src/com/android/messaging/ui/AsyncImageView.java
@@ -113,8 +113,6 @@ public class AsyncImageView extends ImageView implements MediaResourceLoadListen
attr.recycle();
}
-
-
/**
* The main entrypoint for AsyncImageView to load image resource given an ImageRequestDescriptor
* @param descriptor the request descriptor, or null if no image should be displayed
diff --git a/src/com/android/messaging/ui/conversation/ConversationMessageAdapter.java b/src/com/android/messaging/ui/conversation/ConversationMessageAdapter.java
index 3d50252..f740f39 100644
--- a/src/com/android/messaging/ui/conversation/ConversationMessageAdapter.java
+++ b/src/com/android/messaging/ui/conversation/ConversationMessageAdapter.java
@@ -28,6 +28,7 @@ import com.android.messaging.ui.CursorRecyclerAdapter;
import com.android.messaging.ui.AsyncImageView.AsyncImageViewDelayLoader;
import com.android.messaging.ui.conversation.ConversationMessageView.ConversationMessageViewHost;
import com.android.messaging.util.Assert;
+import com.cyanogenmod.messaging.util.RidesharingUtil;
import java.util.HashSet;
import java.util.List;
@@ -40,6 +41,7 @@ public class ConversationMessageAdapter extends
CursorRecyclerAdapter<ConversationMessageAdapter.ConversationMessageViewHolder> {
private final ConversationMessageViewHost mHost;
+ private final RidesharingUtil mRidesharingUtil;
private final AsyncImageViewDelayLoader mImageViewDelayLoader;
private final View.OnClickListener mViewClickListener;
private final View.OnLongClickListener mViewLongClickListener;
@@ -54,6 +56,7 @@ public class ConversationMessageAdapter extends
final View.OnLongClickListener longClickListener) {
super(context, cursor, 0);
mHost = host;
+ mRidesharingUtil = new RidesharingUtil(context);
mViewClickListener = viewClickListener;
mViewLongClickListener = longClickListener;
mImageViewDelayLoader = imageViewDelayLoader;
@@ -91,6 +94,7 @@ public class ConversationMessageAdapter extends
final ConversationMessageView conversationMessageView = (ConversationMessageView)
layoutInflater.inflate(R.layout.conversation_message_view, null);
conversationMessageView.setHost(mHost);
+ conversationMessageView.setRidesharingUtil(mRidesharingUtil);
conversationMessageView.setImageViewDelayLoader(mImageViewDelayLoader);
return new ConversationMessageViewHolder(conversationMessageView,
mViewClickListener, mViewLongClickListener);
diff --git a/src/com/android/messaging/ui/conversation/ConversationMessageView.java b/src/com/android/messaging/ui/conversation/ConversationMessageView.java
index 22128bc..fae36c3 100644
--- a/src/com/android/messaging/ui/conversation/ConversationMessageView.java
+++ b/src/com/android/messaging/ui/conversation/ConversationMessageView.java
@@ -17,6 +17,7 @@
package com.android.messaging.ui.conversation;
import android.content.Context;
+import android.content.Intent;
import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Rect;
@@ -31,15 +32,19 @@ import android.text.style.URLSpan;
import android.text.util.Linkify;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
+import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
+import android.widget.Button;
import android.widget.FrameLayout;
+import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
import android.widget.TextView;
import com.android.messaging.BugleApplication;
@@ -73,11 +78,19 @@ import com.android.messaging.util.OsUtil;
import com.android.messaging.util.PhoneUtils;
import com.android.messaging.util.UiUtils;
import com.android.messaging.util.YouTubeUtil;
+import com.cyanogen.ambient.ridesharing.core.RidesharingContract;
import com.cyanogen.lookup.phonenumber.response.LookupResponse;
import com.cyanogenmod.messaging.lookup.LookupProviderManager.LookupProviderListener;
import com.cyanogenmod.messaging.ui.AttributionContactIconView;
+import com.cyanogenmod.messaging.util.GoogleStaticMapsUtil;
+import com.cyanogenmod.messaging.util.RidesharingUtil;
+import com.cyanogenmod.messaging.util.RoundedCornerTransformation;
import com.google.common.base.Predicate;
+import com.squareup.picasso.*;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
@@ -95,13 +108,22 @@ public class ConversationMessageView extends FrameLayout implements View.OnClick
boolean excludeDefault);
}
+ private static final String TAG = ConversationMessageView.class.getSimpleName();
+
private final ConversationMessageData mData;
private LinearLayout mMessageAttachmentsView;
private MultiAttachmentLayout mMultiAttachmentView;
private AsyncImageView mMessageImageView;
+ private RelativeLayout mMessageMapsView;
+ private ImageView mMessageMapsImageView;
+ private Button mRequestRideButton;
+ private ImageView mBrandImageView;
+ private ImageView mDirectionsButton;
+ private View mButtonDivider;
private TextView mMessageTextView;
private boolean mMessageTextHasLinks;
+ private boolean mMessageHasMapsLink;
private boolean mMessageHasYouTubeLink;
private TextView mStatusTextView;
private TextView mTitleTextView;
@@ -120,11 +142,13 @@ public class ConversationMessageView extends FrameLayout implements View.OnClick
private boolean mOneOnOne;
private ConversationMessageViewHost mHost;
+ private RidesharingUtil mRidesharingUtil;
public ConversationMessageView(final Context context, final AttributeSet attrs) {
super(context, attrs);
// TODO: we should switch to using Binding and DataModel factory methods.
mData = new ConversationMessageData();
+ mRidesharingUtil = new RidesharingUtil(context);
}
@Override
@@ -146,6 +170,13 @@ public class ConversationMessageView extends FrameLayout implements View.OnClick
mMessageImageView.setOnClickListener(this);
mMessageImageView.setOnLongClickListener(this);
+ mMessageMapsView = (RelativeLayout) findViewById(R.id.message_maps);
+ mMessageMapsImageView = (ImageView) findViewById(R.id.maps_image);
+ mRequestRideButton = (Button) findViewById(R.id.request_ride_button);
+ mBrandImageView = (ImageView) findViewById(R.id.brand_image);
+ mDirectionsButton= (ImageView) findViewById(R.id.directions_button);
+ mButtonDivider = findViewById(R.id.button_divider);
+
mMessageTextView = (TextView) findViewById(R.id.message_text);
mMessageTextView.setOnClickListener(this);
IgnoreLinkLongClickHelper.ignoreLinkLongClick(mMessageTextView, this);
@@ -288,6 +319,13 @@ public class ConversationMessageView extends FrameLayout implements View.OnClick
}
/**
+ * Sets a ridesharing util to get data from ridesharing services
+ */
+ public void setRidesharingUtil(final RidesharingUtil ridesharingUtil) {
+ mRidesharingUtil = ridesharingUtil;
+ }
+
+ /**
* Sets a delay loader instance to manage loading / resuming of image attachments.
*/
public void setImageViewDelayLoader(final AsyncImageViewDelayLoader delayLoader) {
@@ -314,7 +352,7 @@ public class ConversationMessageView extends FrameLayout implements View.OnClick
*/
private boolean shouldShowMessageBubbleArrow() {
return !shouldShowSimplifiedVisualStyle()
- && !(mData.hasAttachments() || mMessageHasYouTubeLink);
+ && !(mData.hasAttachments() || mMessageHasMapsLink || mMessageHasYouTubeLink);
}
/**
@@ -543,6 +581,11 @@ public class ConversationMessageView extends FrameLayout implements View.OnClick
mMultiAttachmentView.setVisibility(View.GONE);
}
+ // In the case that we have no image attachments and a maps url then we will show a map
+ // preview
+ String mapsUrlString = null;
+ final String mapsUrlPrefix = "geo:0,0?q=";
+
// In the case that we have no image attachments and exactly one youtube link in a message
// then we will show a preview.
String youtubeThumbnailUrl = null;
@@ -553,28 +596,35 @@ public class ConversationMessageView extends FrameLayout implements View.OnClick
messageTextWithSpans.length(), URLSpan.class);
for (URLSpan span : spans) {
String url = span.getURL();
- String youtubeLinkForUrl = YouTubeUtil.getYoutubePreviewImageLink(url);
- if (!TextUtils.isEmpty(youtubeLinkForUrl)) {
- if (TextUtils.isEmpty(youtubeThumbnailUrl)) {
- // Save the youtube link if we don't already have one
- youtubeThumbnailUrl = youtubeLinkForUrl;
- originalYoutubeLink = url;
- } else {
- // We already have a youtube link. This means we have two youtube links so
- // we shall show none.
- youtubeThumbnailUrl = null;
- originalYoutubeLink = null;
- break;
+ if (url.startsWith(mapsUrlPrefix)) {
+ mapsUrlString = url;
+ break;
+ } else {
+ String youtubeLinkForUrl = YouTubeUtil.getYoutubePreviewImageLink(url);
+ if (!TextUtils.isEmpty(youtubeLinkForUrl)) {
+ if (TextUtils.isEmpty(youtubeThumbnailUrl)) {
+ // Save the youtube link if we don't already have one
+ youtubeThumbnailUrl = youtubeLinkForUrl;
+ originalYoutubeLink = url;
+ } else {
+ // We already have a youtube link. This means we have two youtube links so
+ // we shall show none.
+ youtubeThumbnailUrl = null;
+ originalYoutubeLink = null;
+ break;
+ }
}
}
}
}
// We need to keep track if we have a youtube link in the message so that we will not show
// the arrow
+ mMessageHasMapsLink = !TextUtils.isEmpty(mapsUrlString);
mMessageHasYouTubeLink = !TextUtils.isEmpty(youtubeThumbnailUrl);
- // We will show the message image view if there is one attachment or one youtube link
- if (imageParts.size() == 1 || mMessageHasYouTubeLink) {
+ // We will show the message image view if there is one attachment or one maps link or
+ // one youtube link
+ if (imageParts.size() == 1 || mMessageHasMapsLink || mMessageHasYouTubeLink) {
// Get the display metrics for a hint for how large to pull the image data into
final WindowManager windowManager = (WindowManager) getContext().
getSystemService(Context.WINDOW_SERVICE);
@@ -597,6 +647,10 @@ public class ConversationMessageView extends FrameLayout implements View.OnClick
adjustImageViewBounds(imagePart);
mMessageImageView.setImageResourceId(imageRequest);
mMessageImageView.setTag(imagePart);
+ mMessageImageView.setVisibility(View.VISIBLE);
+ } else if (mMessageHasMapsLink) {
+ // Maps image and buttons
+ showMapsPreview(mapsUrlString, mapsUrlPrefix, desiredWidth);
} else {
// Youtube Thumbnail image
final ImageRequestDescriptor imageRequest =
@@ -607,9 +661,11 @@ public class ConversationMessageView extends FrameLayout implements View.OnClick
ImageUtils.DEFAULT_CIRCLE_STROKE_COLOR /* circleStrokeColor */);
mMessageImageView.setImageResourceId(imageRequest);
mMessageImageView.setTag(originalYoutubeLink);
+ mMessageImageView.setVisibility(View.VISIBLE);
}
- mMessageImageView.setVisibility(View.VISIBLE);
} else {
+ mMessageMapsView.setVisibility(View.GONE);
+
mMessageImageView.setImageResourceId(null);
mMessageImageView.setVisibility(View.GONE);
}
@@ -626,6 +682,103 @@ public class ConversationMessageView extends FrameLayout implements View.OnClick
mMessageAttachmentsView.setVisibility(attachmentsVisible ? View.VISIBLE : View.GONE);
}
+ /**
+ * Shows a maps preview and provides buttons for requesting a ride to the location and
+ * directions to the location
+ * @param mapsUrlString Encoded map url string
+ * @param mapsUrlPrefix Prefix for the map url string (such as geo:0,0?q=)
+ * @param desiredWidth Desired width of the maps preview
+ */
+ private void showMapsPreview(String mapsUrlString, String mapsUrlPrefix, int desiredWidth) {
+ final int unspecifiedMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+ final int mapsMeasureSpec = MeasureSpec.makeMeasureSpec(desiredWidth,
+ MeasureSpec.EXACTLY);
+ mMessageMapsView.measure(mapsMeasureSpec, unspecifiedMeasureSpec);
+
+ // Reset view visibility to gone
+ mRequestRideButton.setVisibility(View.GONE);
+ mBrandImageView.setVisibility(View.GONE);
+ mDirectionsButton.setVisibility(View.GONE);
+ mButtonDivider.setVisibility(View.GONE);
+
+ final String encodedAddress = mapsUrlString.substring(mapsUrlPrefix.length());
+ final int height = getResources()
+ .getDimensionPixelSize(R.dimen.conversation_maps_height);
+ String staticMapsUrl = GoogleStaticMapsUtil.getStaticMapsUrl(mContext, desiredWidth,
+ height, encodedAddress);
+ RoundedCornerTransformation transformation =
+ new RoundedCornerTransformation(mContext.getApplicationContext(), staticMapsUrl);
+ Picasso.with(mContext.getApplicationContext())
+ .load(staticMapsUrl)
+ .placeholder(R.drawable.ic_map_placeholder)
+ .transform(transformation)
+ .into(mMessageMapsImageView, new ImageLoadedCallback());
+ final Uri mapsUri = Uri.parse(mapsUrlString);
+ mMessageMapsImageView.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(Intent.ACTION_VIEW, mapsUri);
+ mContext.startActivity(intent);
+ }
+ });
+
+ final String directionsPrefix = "google.navigation:q=";
+ final Uri directionsUri = Uri.parse(directionsPrefix + encodedAddress);
+ mDirectionsButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(Intent.ACTION_VIEW, directionsUri);
+ mContext.startActivity(intent);
+ }
+ });
+
+ final String encodingFormat = "UTF-8";
+ mRequestRideButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ String decodedAddress = null;
+ try {
+ decodedAddress = URLDecoder.decode(encodedAddress, encodingFormat);
+ } catch (UnsupportedEncodingException e) {
+ Log.e(TAG, "Unable to decode address: " + encodedAddress, e);
+ }
+
+ if (!TextUtils.isEmpty(decodedAddress)) {
+ RidesharingContract.RideRequest.Builder builder =
+ new RidesharingContract.RideRequest.Builder();
+ builder.addDropoffLocation(decodedAddress);
+ Intent intent = builder.build();
+ mContext.startActivity(intent);
+ }
+ }
+ });
+
+ mRidesharingUtil.setBrandBitmap(mBrandImageView);
+
+ final int dividerWidth = getResources()
+ .getDimensionPixelSize(R.dimen.maps_button_divider_width);
+ final int dividerWidthMeasureSpec = MeasureSpec.makeMeasureSpec(dividerWidth,
+ MeasureSpec.EXACTLY);
+ mButtonDivider.measure(dividerWidthMeasureSpec, unspecifiedMeasureSpec);
+
+ mMessageMapsView.setVisibility(View.VISIBLE);
+ }
+
+ private class ImageLoadedCallback implements com.squareup.picasso.Callback {
+ @Override
+ public void onSuccess() {
+ mRequestRideButton.setVisibility(View.VISIBLE);
+ mBrandImageView.setVisibility(View.VISIBLE);
+ mDirectionsButton.setVisibility(View.VISIBLE);
+ mButtonDivider.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ public void onError() {
+ Log.e(TAG, "Unable to load static map");
+ }
+ }
+
private void bindAttachmentsOfSameType(final Predicate<MessagePartData> attachmentTypeFilter,
final int attachmentViewLayoutRes, final AttachmentViewBinder viewBinder,
final Class<?> attachmentViewClass) {
@@ -885,6 +1038,16 @@ public class ConversationMessageView extends FrameLayout implements View.OnClick
// Tint image/video attachments when selected
final int selectedImageTint = getResources().getColor(R.color.message_image_selected_tint);
+ if (mMessageMapsImageView.getVisibility() == View.VISIBLE &&
+ mDirectionsButton.getVisibility() == View.VISIBLE) {
+ if (isSelected()) {
+ mMessageMapsImageView.setColorFilter(selectedImageTint);
+ mDirectionsButton.setColorFilter(selectedImageTint);
+ } else {
+ mMessageMapsImageView.clearColorFilter();
+ mDirectionsButton.clearColorFilter();
+ }
+ }
if (mMessageImageView.getVisibility() == View.VISIBLE) {
if (isSelected()) {
mMessageImageView.setColorFilter(selectedImageTint);
@@ -1264,5 +1427,4 @@ public class ConversationMessageView extends FrameLayout implements View.OnClick
}
}
}
-
}
diff --git a/src/com/cyanogenmod/messaging/util/GoogleStaticMapsUtil.java b/src/com/cyanogenmod/messaging/util/GoogleStaticMapsUtil.java
new file mode 100644
index 0000000..a88dfb0
--- /dev/null
+++ b/src/com/cyanogenmod/messaging/util/GoogleStaticMapsUtil.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2016 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.cyanogenmod.messaging.util;
+
+import android.content.Context;
+import com.android.messaging.R;
+
+public class GoogleStaticMapsUtil {
+ private static final String STATIC_MAPS_URL = "https://maps.googleapis.com/maps/api/staticmap?";
+
+ private static final String AND = "&";
+ private static final String PIPE = "|";
+ private static final String X = "x";
+
+ private static final String SIZE = "size=";
+ private static final String ZOOM = "zoom=18";
+ private static final String SCALE = "scale=4";
+ private static final String API_KEY = "key=";
+
+ private static final String PICK_UP_MARKER =
+ "markers=icon:http://cdn.cyngn.com/ridesharing/pickup_marker.png";
+
+ public static String getStaticMapsUrl(Context context, int width, int height, String encodedAddress) {
+ final StringBuilder sb = new StringBuilder(STATIC_MAPS_URL);
+ sb.append(AND);
+ sb.append(SIZE);
+ sb.append(width);
+ sb.append(X);
+ sb.append(height);
+ sb.append(AND);
+ sb.append(ZOOM);
+ sb.append(AND);
+ sb.append(SCALE);
+ sb.append(AND);
+ sb.append(API_KEY);
+ sb.append(context.getString(R.string.google_maps_key));
+ sb.append(AND);
+ sb.append(PICK_UP_MARKER);
+ sb.append(PIPE);
+ sb.append(encodedAddress);
+
+ return sb.toString();
+ }
+}
diff --git a/src/com/cyanogenmod/messaging/util/RidesharingUtil.java b/src/com/cyanogenmod/messaging/util/RidesharingUtil.java
new file mode 100644
index 0000000..5c2ef05
--- /dev/null
+++ b/src/com/cyanogenmod/messaging/util/RidesharingUtil.java
@@ -0,0 +1,112 @@
+package com.cyanogenmod.messaging.util;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.ImageView;
+import com.cyanogen.ambient.common.ConnectionResult;
+import com.cyanogen.ambient.common.api.AmbientApiClient;
+import com.cyanogen.ambient.common.api.ComponentNameResult;
+import com.cyanogen.ambient.common.api.PendingResult;
+import com.cyanogen.ambient.common.api.ResultCallback;
+import com.cyanogen.ambient.ridesharing.RideSharingApi;
+import com.cyanogen.ambient.ridesharing.RideSharingServices;
+import com.cyanogen.ambient.ridesharing.results.ProviderInfoResult;
+
+/**
+ * Ridesharing services util
+ */
+public class RidesharingUtil {
+ private static final String TAG = RidesharingUtil.class.getSimpleName();
+
+ private Context mContext;
+ private AmbientApiClient mAmbientApiClient;
+ private RideSharingApi mRideSharingApi;
+
+ private Bitmap mBrandBitmap;
+
+ public RidesharingUtil(Context context) {
+ mContext = context;
+ connectAmbientApiClientIfNeeded();
+ }
+
+ /**
+ * Sets brand bitmap from the active ridesharing provider to the specified {@link ImageView}
+ * @param imageView ImageView to set the brand bitmap on
+ */
+ public void setBrandBitmap(final ImageView imageView) {
+ if (mBrandBitmap == null) {
+ connectAmbientApiClientIfNeeded();
+
+ PendingResult<ComponentNameResult> pendingComponentResult =
+ mRideSharingApi.getActivePlugin(mAmbientApiClient);
+ pendingComponentResult.setResultCallback(new ResultCallback<ComponentNameResult>() {
+ @Override
+ public void onResult(final ComponentNameResult componentResult) {
+ if (componentResult != null && componentResult.component != null) {
+ PendingResult<ProviderInfoResult> pendingInfoResult =
+ mRideSharingApi.getPluginInfo(mAmbientApiClient,
+ componentResult.component);
+ pendingInfoResult.setResultCallback(new ResultCallback<ProviderInfoResult>() {
+ @Override
+ public void onResult(ProviderInfoResult infoResult) {
+ if (infoResult != null && infoResult.providerInfo != null) {
+ mBrandBitmap = infoResult.providerInfo.getBrandIcon();
+ imageView.setImageBitmap(mBrandBitmap);
+ } else {
+ Log.e(TAG, "Unable to get provider info for active plugin: "
+ + componentResult.component);
+ }
+ }
+ });
+ } else {
+ Log.e(TAG, "Unable to get active plugin");
+ }
+ }
+ });
+ } else {
+ imageView.setImageBitmap(mBrandBitmap);
+ }
+ }
+
+ /**
+ * Helper method to initialize AmbientApiClient if it is null and connect AmbientApiClient if
+ * it is not connected and not currently trying to connect
+ */
+ private void connectAmbientApiClientIfNeeded() {
+ if (mAmbientApiClient == null) {
+ Log.d(TAG, "mAmbientApiClient is null, initializing");
+ mAmbientApiClient = new AmbientApiClient.Builder(mContext)
+ .addApi(RideSharingServices.API)
+ .build();
+ if (mAmbientApiClient == null) {
+ Log.e(TAG, "AmbientApiClient couldn't initialize, returning");
+ return;
+ }
+ mAmbientApiClient.registerConnectionFailedListener(new AmbientApiClient.OnConnectionFailedListener() {
+ @Override
+ public void onConnectionFailed(ConnectionResult connectionResult) {
+ Log.e(TAG, "Unable to connect with Ambient. ConnectionResult.ErrorCode : " + connectionResult.getErrorCode());
+ }
+ });
+ mAmbientApiClient.registerConnectionCallbacks(new AmbientApiClient.ConnectionCallbacks() {
+ @Override
+ public void onConnected(Bundle bundle) {
+ Log.d(TAG, "Connected with Ambient.");
+ }
+
+ @Override
+ public void onConnectionSuspended(int i) {
+ Log.d(TAG, "Ambient client disconnected.");
+ }
+ });
+ }
+
+ if (!mAmbientApiClient.isConnected() && !mAmbientApiClient.isConnecting()) {
+ Log.d(TAG, "ambientApiClient is not connect or connecting, attempting to connect now");
+ mAmbientApiClient.connect();
+ mRideSharingApi = RideSharingServices.getInstance();
+ }
+ }
+}
diff --git a/src/com/cyanogenmod/messaging/util/RoundedCornerTransformation.java b/src/com/cyanogenmod/messaging/util/RoundedCornerTransformation.java
new file mode 100644
index 0000000..197a774
--- /dev/null
+++ b/src/com/cyanogenmod/messaging/util/RoundedCornerTransformation.java
@@ -0,0 +1,46 @@
+package com.cyanogenmod.messaging.util;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.RectF;
+import android.graphics.Shader;
+import android.support.v7.appcompat.R;
+
+public class RoundedCornerTransformation implements com.squareup.picasso.Transformation {
+ private Context mContext;
+ private String mTag;
+
+ public RoundedCornerTransformation(Context context, String tag) {
+ mContext = context;
+ mTag = tag;
+ }
+
+ @Override
+ public Bitmap transform(Bitmap source) {
+ int width = source.getWidth();
+ int height = source.getHeight();
+
+ Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ final RectF rect = new RectF(0, 0, width, height);
+
+ final int radius =
+ mContext.getResources().getDimensionPixelSize(R.dimen.maps_corner_radius);
+ Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ BitmapShader shader = new BitmapShader(source, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
+ paint.setShader(shader);
+ canvas.drawRoundRect(rect, radius, radius, paint);
+
+ source.recycle();
+
+ return bitmap;
+ }
+
+ @Override
+ public String key() {
+ return mTag;
+ }
+}