diff options
Diffstat (limited to 'InCallUI')
-rw-r--r-- | InCallUI/res/drawable-hdpi/ic_schedule_white_24dp.png | bin | 0 -> 575 bytes | |||
-rw-r--r-- | InCallUI/res/drawable-mdpi/ic_schedule_white_24dp.png | bin | 0 -> 377 bytes | |||
-rw-r--r-- | InCallUI/res/drawable-xhdpi/ic_schedule_white_24dp.png | bin | 0 -> 737 bytes | |||
-rw-r--r-- | InCallUI/res/drawable-xxhdpi/ic_schedule_white_24dp.png | bin | 0 -> 1107 bytes | |||
-rw-r--r-- | InCallUI/res/drawable-xxxhdpi/ic_schedule_white_24dp.png | bin | 0 -> 1478 bytes | |||
-rw-r--r-- | InCallUI/res/values/strings.xml | 6 | ||||
-rw-r--r-- | InCallUI/src/com/android/incallui/CallCardPresenter.java | 7 | ||||
-rw-r--r-- | InCallUI/src/com/android/incallui/ContactInfoCache.java | 15 | ||||
-rw-r--r-- | InCallUI/src/com/android/incallui/ContactUtils.java | 6 | ||||
-rw-r--r-- | InCallUI/src/com/android/incallui/InCallContactInteractions.java | 127 | ||||
-rw-r--r-- | InCallUI/tests/src/com/android/incallui/InCallContactInteractionsTest.java | 73 |
11 files changed, 208 insertions, 26 deletions
diff --git a/InCallUI/res/drawable-hdpi/ic_schedule_white_24dp.png b/InCallUI/res/drawable-hdpi/ic_schedule_white_24dp.png Binary files differnew file mode 100644 index 000000000..f3581d104 --- /dev/null +++ b/InCallUI/res/drawable-hdpi/ic_schedule_white_24dp.png diff --git a/InCallUI/res/drawable-mdpi/ic_schedule_white_24dp.png b/InCallUI/res/drawable-mdpi/ic_schedule_white_24dp.png Binary files differnew file mode 100644 index 000000000..501ee842e --- /dev/null +++ b/InCallUI/res/drawable-mdpi/ic_schedule_white_24dp.png diff --git a/InCallUI/res/drawable-xhdpi/ic_schedule_white_24dp.png b/InCallUI/res/drawable-xhdpi/ic_schedule_white_24dp.png Binary files differnew file mode 100644 index 000000000..2e27936a4 --- /dev/null +++ b/InCallUI/res/drawable-xhdpi/ic_schedule_white_24dp.png diff --git a/InCallUI/res/drawable-xxhdpi/ic_schedule_white_24dp.png b/InCallUI/res/drawable-xxhdpi/ic_schedule_white_24dp.png Binary files differnew file mode 100644 index 000000000..bfc72736a --- /dev/null +++ b/InCallUI/res/drawable-xxhdpi/ic_schedule_white_24dp.png diff --git a/InCallUI/res/drawable-xxxhdpi/ic_schedule_white_24dp.png b/InCallUI/res/drawable-xxxhdpi/ic_schedule_white_24dp.png Binary files differnew file mode 100644 index 000000000..b94f4dfa1 --- /dev/null +++ b/InCallUI/res/drawable-xxxhdpi/ic_schedule_white_24dp.png diff --git a/InCallUI/res/values/strings.xml b/InCallUI/res/values/strings.xml index 7a90953f4..20a724cd6 100644 --- a/InCallUI/res/values/strings.xml +++ b/InCallUI/res/values/strings.xml @@ -477,4 +477,10 @@ <string name="distance_imperial_away"><xliff:g id="distance">%.1f</xliff:g> mi away</string> <!-- Used to inform the user how far away a location is in kilometers. [CHAR LIMIT=NONE] --> <string name="distance_metric_away"><xliff:g id="distance">%.1f</xliff:g> km away</string> + <!-- Used to indicate the opening hours for a location as a time span. [CHAR LIMIT=NONE] --> + <string name="opening_hours"><xliff:g id="open_time">%s</xliff:g> - <xliff:g id="close_time">%s</xliff:g></string> + <!-- Displayed when a place is open. --> + <string name="open_now">Open now</string> + <!-- Displayed when a place is closed. --> + <string name="closed_now">Closed now</string> </resources> diff --git a/InCallUI/src/com/android/incallui/CallCardPresenter.java b/InCallUI/src/com/android/incallui/CallCardPresenter.java index aa022f448..e7d6f0c3e 100644 --- a/InCallUI/src/com/android/incallui/CallCardPresenter.java +++ b/InCallUI/src/com/android/incallui/CallCardPresenter.java @@ -595,8 +595,8 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi> private void updateContactInteractions() { if (mPrimary != null && mPrimaryContactInfo != null - && mPrimaryContactInfo.locationAddress != null) { - + && (mPrimaryContactInfo.locationAddress != null + || mPrimaryContactInfo.openingHours != null)) { // TODO: This is hardcoded to "isBusiness" because functionality to differentiate // between business and personal has not yet been added. if (setInCallContactInteractionsType(true /* isBusiness */)) { @@ -606,7 +606,8 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi> mInCallContactInteractions.setBusinessInfo( mPrimaryContactInfo.locationAddress, - mDistanceHelper.calculateDistance(mPrimaryContactInfo.locationAddress)); + mDistanceHelper.calculateDistance(mPrimaryContactInfo.locationAddress), + mPrimaryContactInfo.openingHours); getUi().setContactContextContent(mInCallContactInteractions.getListAdapter()); getUi().showContactContext(mPrimary.getState() != State.INCOMING); } diff --git a/InCallUI/src/com/android/incallui/ContactInfoCache.java b/InCallUI/src/com/android/incallui/ContactInfoCache.java index 0e6a3d4e0..e3457d5cb 100644 --- a/InCallUI/src/com/android/incallui/ContactInfoCache.java +++ b/InCallUI/src/com/android/incallui/ContactInfoCache.java @@ -30,6 +30,7 @@ import android.provider.ContactsContract.DisplayNameSources; import android.provider.ContactsContract.CommonDataKinds.Phone; import android.telecom.TelecomManager; import android.text.TextUtils; +import android.util.Pair; import com.android.contacts.common.util.PhoneNumberHelper; import com.android.dialer.calllog.ContactInfo; @@ -299,10 +300,11 @@ public class ContactInfoCache implements ContactsAsyncHelper.OnImageLoadComplete entry.photo = mContext.getResources().getDrawable(R.drawable.img_business); } - String address = null; + boolean hasContactInteractions = false; if (mContactUtils != null) { - // This method will callback "onAddressDetailsFound". - address = mContactUtils.getAddressFromLookupKey(info.getLookupKey(), this); + // This method will callback "onContactInteractionsFound". + hasContactInteractions = mContactUtils.retrieveContactInteractionsFromLookupKey( + info.getLookupKey(), this); } // Add the contact info to the cache. @@ -310,7 +312,7 @@ public class ContactInfoCache implements ContactsAsyncHelper.OnImageLoadComplete sendInfoNotifications(mCallId, entry); // If there is no image then we should not expect another callback. - if (info.getImageUrl() == null && address == null) { + if (info.getImageUrl() == null && !hasContactInteractions) { // We're done, so clear callbacks clearCallbacks(mCallId); } @@ -322,9 +324,10 @@ public class ContactInfoCache implements ContactsAsyncHelper.OnImageLoadComplete } @Override - public void onAddressDetailsFound(Address address) { + public void onContactInteractionsFound(Address address, Pair<String, String> openingHours) { final ContactCacheEntry entry = mInfoMap.get(mCallId); entry.locationAddress = address; + entry.openingHours = openingHours; sendContactInteractionsNotifications(mCallId, entry); clearCallbacks(mCallId); } @@ -614,6 +617,7 @@ public class ContactInfoCache implements ContactsAsyncHelper.OnImageLoadComplete public Uri lookupUri; // Sent to NotificationMananger public String lookupKey; public Address locationAddress; + public Pair<String, String> openingHours; public int contactLookupResult = LogState.LOOKUP_NOT_FOUND; @Override @@ -628,6 +632,7 @@ public class ContactInfoCache implements ContactsAsyncHelper.OnImageLoadComplete .add("contactUri", contactUri) .add("displayPhotoUri", displayPhotoUri) .add("locationAddress", locationAddress) + .add("openingHours", openingHours) .add("contactLookupResult", contactLookupResult) .toString(); } diff --git a/InCallUI/src/com/android/incallui/ContactUtils.java b/InCallUI/src/com/android/incallui/ContactUtils.java index eac748494..dfacade8a 100644 --- a/InCallUI/src/com/android/incallui/ContactUtils.java +++ b/InCallUI/src/com/android/incallui/ContactUtils.java @@ -17,6 +17,7 @@ package com.android.incallui; import android.content.Context; import android.location.Address; +import android.util.Pair; import com.android.incalluibind.ObjectFactory; @@ -35,8 +36,9 @@ public abstract class ContactUtils { } public interface Listener { - public void onAddressDetailsFound(Address address); + public void onContactInteractionsFound(Address address, Pair<String, String> openingHours); } - public abstract String getAddressFromLookupKey(String lookupKey, Listener listener); + public abstract boolean retrieveContactInteractionsFromLookupKey(String lookupKey, + Listener listener); } diff --git a/InCallUI/src/com/android/incallui/InCallContactInteractions.java b/InCallUI/src/com/android/incallui/InCallContactInteractions.java index 04caecc13..6d1c9fc8f 100644 --- a/InCallUI/src/com/android/incallui/InCallContactInteractions.java +++ b/InCallUI/src/com/android/incallui/InCallContactInteractions.java @@ -16,9 +16,13 @@ package com.android.incallui; +import com.google.common.annotations.VisibleForTesting; + import android.content.Context; import android.location.Address; import android.text.TextUtils; +import android.text.format.DateFormat; +import android.util.Pair; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -29,7 +33,11 @@ import android.widget.RelativeLayout; import android.widget.RelativeLayout.LayoutParams; import android.widget.TextView; +import java.text.ParseException; +import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; import java.util.List; import java.util.Locale; @@ -41,6 +49,7 @@ import java.util.Locale; * is a business contact or not and logic for the manipulation of data for the call context. */ public class InCallContactInteractions { + private static final String TAG = InCallContactInteractions.class.getSimpleName(); private Context mContext; private InCallContactInteractionsListAdapter mListAdapter; private boolean mIsBusiness; @@ -77,11 +86,6 @@ public class InCallContactInteractions { return false; } - public void setBusinessInfo(Address address, float distance) { - mListAdapter.clear(); - mListAdapter.addAll(constructBusinessContextInfo(address, distance)); - } - public View getBusinessListHeaderView() { if (mBusinessHeaderView == null) { mBusinessHeaderView = mInflater.inflate( @@ -90,30 +94,121 @@ public class InCallContactInteractions { return mBusinessHeaderView; } - private List<ContactContextInfo> constructBusinessContextInfo(Address address, float distance) { + public void setBusinessInfo(Address address, float distance, + Pair<String, String> openingHours) { + mListAdapter.clear(); List<ContactContextInfo> info = new ArrayList<ContactContextInfo>(); - //TODO: hours of operation information + // Hours of operation + if (openingHours != null) { + BusinessContextInfo hoursInfo = constructHoursInfo(openingHours); + if (hoursInfo != null) { + info.add(hoursInfo); + } + } // Location information - BusinessContextInfo distanceInfo = new BusinessContextInfo(); - distanceInfo.iconId = R.drawable.ic_location_on_white_24dp; + if (address != null) { + BusinessContextInfo locationInfo = constructLocationInfo(address, distance); + info.add(locationInfo); + } + + mListAdapter.addAll(info); + } + + /** + * Construct a BusinessContextInfo object containing hours of operation information. + * The format is: + * [Open now/Closed now] + * [Hours] + * + * @param openingHours + * @return BusinessContextInfo object with the schedule icon, the heading set to whether the + * business is open or not and the details set to the hours of operation. + */ + private BusinessContextInfo constructHoursInfo(Pair<String, String> openingHours) { + return constructHoursInfoByTime(Calendar.getInstance(), openingHours); + } + + /** + * Pass in arbitrary current calendar time. + */ + @VisibleForTesting + BusinessContextInfo constructHoursInfoByTime( + Calendar currentTime, Pair<String, String> openingHours) { + BusinessContextInfo hoursInfo = new BusinessContextInfo(); + hoursInfo.iconId = R.drawable.ic_schedule_white_24dp; + + Calendar openTime = getCalendarFromTime(currentTime, openingHours.first); + Calendar closeTime = getCalendarFromTime(currentTime, openingHours.second); + + if (openTime == null || closeTime == null) { + return null; + } + + if (currentTime.after(openTime) && currentTime.before(closeTime)) { + hoursInfo.heading = mContext.getString(R.string.open_now); + } else { + hoursInfo.heading = mContext.getString(R.string.closed_now); + } + + hoursInfo.detail = mContext.getString( + R.string.opening_hours, + DateFormat.getTimeFormat(mContext).format(openTime.getTime()), + DateFormat.getTimeFormat(mContext).format(closeTime.getTime())); + return hoursInfo; + } + + /** + * Construct a BusinessContextInfo object with the location information of the business. + * The format is: + * [Straight line distance in miles or kilometers] + * [Address without state/country/etc.] + * + * @param address An Address object containing address details of the business + * @param distance The distance to the location in meters + * @return A BusinessContextInfo object with the location icon, the heading as the distance to + * the business and the details containing the address. + */ + @VisibleForTesting + BusinessContextInfo constructLocationInfo(Address address, float distance) { + if (address == null) { + return null; + } + + BusinessContextInfo locationInfo = new BusinessContextInfo(); + locationInfo.iconId = R.drawable.ic_location_on_white_24dp; if (distance != DistanceHelper.DISTANCE_NOT_FOUND) { //TODO: add a setting to allow the user to select "KM" or "MI" as their distance units. if (Locale.US.equals(Locale.getDefault())) { - distanceInfo.heading = mContext.getString(R.string.distance_imperial_away, + locationInfo.heading = mContext.getString(R.string.distance_imperial_away, distance * DistanceHelper.MILES_PER_METER); } else { - distanceInfo.heading = mContext.getString(R.string.distance_metric_away, + locationInfo.heading = mContext.getString(R.string.distance_metric_away, distance * DistanceHelper.KILOMETERS_PER_METER); } } - if (address != null) { - distanceInfo.detail = address.getAddressLine(0); - } - info.add(distanceInfo); + locationInfo.detail = address.getAddressLine(0); + return locationInfo; + } - return info; + /** + * Get a calendar object set to the current calendar date and the time set to the "hhmm" string + * passed in. + */ + private Calendar getCalendarFromTime(Calendar currentTime, String time) { + try { + Calendar newCalendar = Calendar.getInstance(); + newCalendar.setTime(new SimpleDateFormat("hhmm").parse(time)); + newCalendar.set( + currentTime.get(Calendar.YEAR), + currentTime.get(Calendar.MONTH), + currentTime.get(Calendar.DATE)); + return newCalendar; + } catch (ParseException e) { + Log.w(TAG, "Could not parse time string" + time); + } + return null; } /** diff --git a/InCallUI/tests/src/com/android/incallui/InCallContactInteractionsTest.java b/InCallUI/tests/src/com/android/incallui/InCallContactInteractionsTest.java new file mode 100644 index 000000000..c3ec08d69 --- /dev/null +++ b/InCallUI/tests/src/com/android/incallui/InCallContactInteractionsTest.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2015 The Android Open Source 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.android.incallui; + +import android.test.AndroidTestCase; +import android.util.Pair; + +import com.android.incallui.InCallContactInteractions.BusinessContextInfo; + +import java.util.Calendar; + +public class InCallContactInteractionsTest extends AndroidTestCase { + private InCallContactInteractions mInCallContactInteractions; + + @Override + protected void setUp() { + mInCallContactInteractions = new InCallContactInteractions(mContext, true /* isBusiness */); + } + + public void testIsOpenNow() { + Calendar currentTimeForTest = Calendar.getInstance(); + currentTimeForTest.set(Calendar.HOUR_OF_DAY, 10); + BusinessContextInfo info = + mInCallContactInteractions.constructHoursInfoByTime( + currentTimeForTest, + Pair.create("0800", "2000")); + assertEquals(mContext.getString(R.string.open_now), info.heading); + } + + public void testIsClosedNow_BeforeOpen() { + Calendar currentTimeForTest = Calendar.getInstance(); + currentTimeForTest.set(Calendar.HOUR_OF_DAY, 6); + BusinessContextInfo info = + mInCallContactInteractions.constructHoursInfoByTime( + currentTimeForTest, + Pair.create("0800", "2000")); + assertEquals(mContext.getString(R.string.closed_now), info.heading); + } + + public void testIsClosedNow_AfterClosed() { + Calendar currentTimeForTest = Calendar.getInstance(); + currentTimeForTest.set(Calendar.HOUR_OF_DAY, 21); + BusinessContextInfo info = + mInCallContactInteractions.constructHoursInfoByTime( + currentTimeForTest, + Pair.create("0800", "2000")); + assertEquals(mContext.getString(R.string.closed_now), info.heading); + } + + public void testInvalidOpeningHours() { + Calendar currentTimeForTest = Calendar.getInstance(); + currentTimeForTest.set(Calendar.HOUR_OF_DAY, 21); + BusinessContextInfo info = + mInCallContactInteractions.constructHoursInfoByTime( + currentTimeForTest, + Pair.create("", "2000")); + assertEquals(null, info); + } +} |