Update the call details UI.
New call details UI:
- Big picture at the top with a black overlay
- Small contact picture on the side
- Details of the call next to the picture
- Action button for calling back
- Action button (disabled) for deleting: waiting for asset
- Details at the bottom
This commit still preserves the list view with action to keep the app
fully usable. These will be replaced once we use a quick contact badge
instead of a plain contact picture.
Change-Id: I8e8218c3ef863abe9b33d4b677afa3c6650b9f77
diff --git a/res/layout/call_detail.xml b/res/layout/call_detail.xml
index 3b230b5..76552ab 100644
--- a/res/layout/call_detail.xml
+++ b/res/layout/call_detail.xml
@@ -4,9 +4,9 @@
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.
@@ -14,95 +14,158 @@
limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:orientation="vertical"
+ android:orientation="horizontal"
+ android:gravity="top"
+ android:paddingTop="?attr/call_detail_top_gap"
+ android:background="?attr/call_detail_transparent_background"
>
-
- <LinearLayout
+ <ImageView
+ android:id="@+id/contact_background"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="@android:drawable/title_bar_tall"
- android:orientation="horizontal"
- android:gravity="center_vertical"
- android:paddingLeft="9dip"
- android:paddingRight="5dip"
+ android:layout_height="?attr/call_detail_contact_background_height"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:adjustViewBounds="true"
+ android:scaleX="2.5"
+ android:scaleY="2.5"
+
+ android:background="@drawable/ic_contact_picture"
+ />
+ <View
+ android:id="@+id/contact_background_overlay"
+ android:layout_width="match_parent"
+ android:layout_height="?attr/call_detail_contact_background_height"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:background="?attr/call_detail_primary_background_color"
+ android:alpha="?attr/call_detail_contact_background_overlay_alpha"
+ />
+
+ <RelativeLayout
+ android:id="@+id/photo_panel"
+ android:layout_width="match_parent"
+ android:layout_height="?attr/call_detail_contact_background_height"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:paddingBottom="10dp"
>
-
- <ImageView android:id="@+id/icon"
- android:layout_width="32dip"
- android:layout_height="32dip"
- android:layout_marginRight="5dip"
+ <ImageView
+ android:id="@+id/contact_photo"
+ android:layout_width="?attr/call_detail_contact_photo_size"
+ android:layout_height="?attr/call_detail_contact_photo_size"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentBottom="true"
+
+ android:background="@drawable/ic_contact_picture"
/>
-
- <LinearLayout
+ <RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:gravity="center_vertical"
- android:orientation="vertical"
- android:layout_marginLeft="5dip"
+ android:layout_alignParentBottom="true"
+ android:layout_toRightOf="@id/contact_photo"
+ android:layout_marginLeft="10dp"
>
-
- <TextView android:id="@+id/type"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceLarge"
- />
-
- <TextView android:id="@+id/time"
+ <TextView
+ android:id="@+id/number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="?android:attr/textColorPrimaryInverse"
+ android:textColor="?attr/call_detail_primary_text_color"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentBottom="true"
/>
-
- <TextView android:id="@+id/duration"
+ <TextView
+ android:id="@+id/call_type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="?android:attr/textColorPrimaryInverse"
+ android:textColor="?attr/call_detail_primary_text_color"
+ android:layout_alignParentLeft="true"
+ android:layout_above="@id/number"
/>
-
- </LinearLayout>
-
- </LinearLayout>
-
- <FrameLayout
- android:layout_height="0dip"
+ <TextView
+ android:id="@+id/name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="?attr/call_detail_primary_text_color"
+ android:layout_alignParentLeft="true"
+ android:layout_above="@id/call_type"
+ android:paddingBottom="2dp"
+ />
+ </RelativeLayout>
+ <RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="?attr/call_detail_action_icon_size"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentBottom="true"
+ android:layout_marginBottom="10dp"
+ >
+ <ImageView
+ android:id="@+id/call"
+ android:layout_width="?attr/call_detail_action_icon_size"
+ android:layout_height="?attr/call_detail_action_icon_size"
+ android:layout_alignParentRight="true"
+ android:gravity="center_vertical"
+ android:src="@android:drawable/sym_action_call"
+ android:scaleType="center"
+ />
+ <View
+ android:id="@+id/divider"
+ android:layout_width="1px"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="5dip"
+ android:layout_marginBottom="5dip"
+ android:layout_toLeftOf="@id/call"
+ android:layout_marginLeft="11dip"
+ android:background="#FFFFFF"
+ />
+ </RelativeLayout>
+ </RelativeLayout>
+ <RelativeLayout
+ android:id="@+id/call_panel"
android:layout_width="match_parent"
- android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_below="@id/photo_panel"
+ android:paddingBottom="10dp"
+ android:paddingTop="10dp"
+ android:paddingLeft="80dp"
+ android:background="#FFFFFF"
>
- <ListView android:id="@android:id/list"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:scrollbarStyle="outsideOverlay"
- />
-
- <ScrollView android:id="@android:id/empty"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:fillViewport="true">
-
- <TextView android:id="@+id/emptyText"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/unknown"
- android:textSize="20sp"
- android:textColor="?android:attr/textColorSecondary"
- android:paddingLeft="10dip"
- android:paddingRight="10dip"
- android:paddingTop="10dip"
- android:gravity="center"
- android:lineSpacingMultiplier="0.92"/>
-
- </ScrollView>
-
- <View
- android:layout_width="match_parent"
+ <TextView
+ android:id="@+id/time"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:background="@drawable/title_bar_shadow"
+ android:layout_alignParentLeft="true"
/>
-
- </FrameLayout>
-</LinearLayout>
+ <TextView
+ android:id="@+id/duration"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_below="@id/time"
+ android:layout_alignLeft="@id/time"
+ />
+ <ImageView
+ android:id="@+id/delete"
+ android:layout_width="?attr/call_detail_action_icon_size"
+ android:layout_height="?attr/call_detail_action_icon_size"
+ android:layout_alignParentRight="true"
+ android:scaleType="center"
+ android:gravity="center_vertical"
+ android:src="@android:drawable/sym_action_call"
+ android:visibility="gone"
+ />
+ </RelativeLayout>
+ <ListView
+ android:id="@android:id/list"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/call_panel"
+ android:background="?attr/call_detail_secondary_background_color"
+ />
+</RelativeLayout>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 7e7e8b5..77863d5 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1523,4 +1523,9 @@
<!-- Button in the alert dialog when the user hits the Cancel button in the editor [CHAR LIMIT=20] -->
<string name="discard">Discard</string>
+
+ <!-- Description of a call log entry, made of a call type and a date -->
+ <string name="call_type_and_date">
+ <xliff:g id="call_type" example="Friends">%1$s</xliff:g> <xliff:g id="call_short_date" example="Friends">%2$s</xliff:g>
+ </string>
</resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 3f9986c..aefb719 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -40,6 +40,19 @@
<style name="CallDetailActivityTheme" parent="android:Theme.Holo.Light">
<item name="android:windowContentOverlay">@null</item>
+ <item name="android:windowIsTranslucent">true</item>
+ <item name="android:windowNoTitle">true</item>
+ <item name="android:windowBackground">@android:color/transparent</item>
+ <item name="android:gravity">top</item>
+ <item name="call_detail_top_gap">49dip</item>
+ <item name="call_detail_transparent_background">#CC000000</item>
+ <item name="call_detail_contact_background_height">150dip</item>
+ <item name="call_detail_contact_background_overlay_alpha">0.25</item>
+ <item name="call_detail_primary_background_color">#000000</item>
+ <item name="call_detail_contact_photo_size">60dip</item>
+ <item name="call_detail_primary_text_color">#FFFFFF</item>
+ <item name="call_detail_action_icon_size">50dip</item>
+ <item name="call_detail_secondary_background_color">#FFFFFF</item>
</style>
<style name="ContactDetailActivityTheme" parent="android:Theme.Holo.Light">
<item name="android:windowContentOverlay">@null</item>
@@ -112,6 +125,18 @@
<attr name="show_home_icon" format="boolean"/>
</declare-styleable>
+ <declare-styleable name="CallDetailActivity">
+ <attr name="call_detail_top_gap" format="dimension" />
+ <attr name="call_detail_transparent_background" format="color" />
+ <attr name="call_detail_contact_background_height" format="dimension" />
+ <attr name="call_detail_contact_background_overlay_alpha" format="float" />
+ <attr name="call_detail_contact_photo_size" format="dimension" />
+ <attr name="call_detail_action_icon_size" format="dimension" />
+ <attr name="call_detail_primary_text_color" format="color" />
+ <attr name="call_detail_primary_background_color" format="color" />
+ <attr name="call_detail_secondary_background_color" format="color" />
+ </declare-styleable>
+
<style name="PeopleTheme" parent="android:Theme.Holo.Light">
<item name="list_item_height">?android:attr/listPreferredItemHeight</item>
<item name="activated_background">@drawable/list_item_activated_background</item>
@@ -212,7 +237,7 @@
<attr name="layout_widePaddingLeft" format="dimension"/>
<attr name="layout_widePaddingRight" format="dimension"/>
</declare-styleable>
-
+
<declare-styleable name="TransitionAnimationView">
<attr name="clipMarginLeft" format="dimension"/>
<attr name="clipMarginRight" format="dimension"/>
@@ -222,7 +247,7 @@
<attr name="exitAnimation" format="reference"/>
<attr name="animationDuration" format="integer"/>
</declare-styleable>
-
+
<style name="DirectoryHeader" parent="PeopleTheme">
<item name="android:background">@drawable/directory_bg</item>
</style>
diff --git a/src/com/android/contacts/CallDetailActivity.java b/src/com/android/contacts/CallDetailActivity.java
index b3c68ef..0931e3d 100644
--- a/src/com/android/contacts/CallDetailActivity.java
+++ b/src/com/android/contacts/CallDetailActivity.java
@@ -16,6 +16,7 @@
package com.android.contacts;
+import com.android.contacts.format.FormatUtils;
import com.android.internal.telephony.CallerInfo;
import android.app.ListActivity;
@@ -25,16 +26,18 @@
import android.content.Intent;
import android.content.res.Resources;
import android.database.Cursor;
+import android.graphics.Typeface;
import android.net.Uri;
import android.os.Bundle;
import android.provider.CallLog;
import android.provider.CallLog.Calls;
+import android.provider.Contacts.Intents.Insert;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.PhoneLookup;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.Contacts.Intents.Insert;
import android.telephony.PhoneNumberUtils;
import android.telephony.TelephonyManager;
+import android.text.Spanned;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.view.KeyEvent;
@@ -57,16 +60,23 @@
AdapterView.OnItemClickListener {
private static final String TAG = "CallDetail";
- private TextView mCallType;
- private ImageView mCallTypeIcon;
- private TextView mCallTime;
- private TextView mCallDuration;
+ private TextView mNameView;
+ private TextView mCallTypeView;
+ private TextView mNumberView;
+ private TextView mCallTimeView;
+ private TextView mCallDurationView;
+ private View mCallActionView;
+ private ImageView mContactPhotoView;
+ private ImageView mContactBackgroundView;
private String mNumber = null;
private String mDefaultCountryIso;
/* package */ LayoutInflater mInflater;
/* package */ Resources mResources;
+ /** Helper to load contact photos. */
+ private ContactPhotoManager mContactPhotoManager;
+ /** Attached to the call action button in the UI. */
static final String[] CALL_LOG_PROJECTION = new String[] {
CallLog.Calls.DATE,
@@ -89,6 +99,7 @@
PhoneLookup.LABEL,
PhoneLookup.NUMBER,
PhoneLookup.NORMALIZED_NUMBER,
+ PhoneLookup.PHOTO_ID,
};
static final int COLUMN_INDEX_ID = 0;
static final int COLUMN_INDEX_NAME = 1;
@@ -96,6 +107,7 @@
static final int COLUMN_INDEX_LABEL = 3;
static final int COLUMN_INDEX_NUMBER = 4;
static final int COLUMN_INDEX_NORMALIZED_NUMBER = 5;
+ static final int COLUMN_INDEX_PHOTO_ID = 6;
@Override
protected void onCreate(Bundle icicle) {
@@ -106,12 +118,16 @@
mInflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
mResources = getResources();
- mCallType = (TextView) findViewById(R.id.type);
- mCallTypeIcon = (ImageView) findViewById(R.id.icon);
- mCallTime = (TextView) findViewById(R.id.time);
- mCallDuration = (TextView) findViewById(R.id.duration);
+ mNameView = (TextView) findViewById(R.id.name);
+ mCallTypeView = (TextView) findViewById(R.id.call_type);
+ mNumberView = (TextView) findViewById(R.id.number);
+ mCallActionView = findViewById(R.id.call);
+ mContactPhotoView = (ImageView) findViewById(R.id.contact_photo);
+ mContactBackgroundView = (ImageView) findViewById(R.id.contact_background);
+ mCallTimeView = (TextView) findViewById(R.id.time);
+ mCallDurationView = (TextView) findViewById(R.id.duration);
mDefaultCountryIso = ContactsUtils.getCurrentCountryIso(this);
-
+ mContactPhotoManager = ContactPhotoManager.getInstance(this);
getListView().setOnItemClickListener(this);
}
@@ -163,85 +179,104 @@
CharSequence dateClause = DateUtils.formatDateRange(this, date, date,
DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE |
DateUtils.FORMAT_SHOW_WEEKDAY | DateUtils.FORMAT_SHOW_YEAR);
- mCallTime.setText(dateClause);
+ mCallTimeView.setText(dateClause);
// Set the duration
if (callType == Calls.MISSED_TYPE) {
- mCallDuration.setVisibility(View.GONE);
+ mCallDurationView.setVisibility(View.GONE);
} else {
- mCallDuration.setVisibility(View.VISIBLE);
- mCallDuration.setText(formatDuration(duration));
+ mCallDurationView.setVisibility(View.VISIBLE);
+ mCallDurationView.setText(formatDuration(duration));
}
- // Set the call type icon and caption
- String callText = null;
+ CharSequence shortDateText =
+ DateUtils.getRelativeTimeSpanString(date,
+ System.currentTimeMillis(),
+ DateUtils.MINUTE_IN_MILLIS,
+ DateUtils.FORMAT_ABBREV_RELATIVE);
+
+ CharSequence callTypeText = "";
switch (callType) {
case Calls.INCOMING_TYPE:
- mCallTypeIcon.setImageResource(R.drawable.ic_call_log_header_incoming_call);
- mCallType.setText(R.string.type_incoming);
- callText = getString(R.string.callBack);
+ callTypeText = getString(R.string.type_incoming);
break;
case Calls.OUTGOING_TYPE:
- mCallTypeIcon.setImageResource(R.drawable.ic_call_log_header_outgoing_call);
- mCallType.setText(R.string.type_outgoing);
- callText = getString(R.string.callAgain);
+ callTypeText = getString(R.string.type_outgoing);
break;
case Calls.MISSED_TYPE:
- mCallTypeIcon.setImageResource(R.drawable.ic_call_log_header_missed_call);
- mCallType.setText(R.string.type_missed);
- callText = getString(R.string.returnCall);
+ callTypeText = getString(R.string.type_missed);
break;
}
+ mCallTypeView.setText(
+ getString(R.string.call_type_and_date,
+ FormatUtils.applyStyleToSpan(Typeface.BOLD,
+ callTypeText, 0, callTypeText.length(),
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE),
+ shortDateText));
+
+ long photoId = 0L;
+ CharSequence nameText = "";
+ CharSequence numberText = "";
if (mNumber.equals(CallerInfo.UNKNOWN_NUMBER) ||
mNumber.equals(CallerInfo.PRIVATE_NUMBER)) {
- // List is empty, let the empty view show instead.
- TextView emptyText = (TextView) findViewById(R.id.emptyText);
- if (emptyText != null) {
- emptyText.setText(mNumber.equals(CallerInfo.PRIVATE_NUMBER)
- ? R.string.private_num : R.string.unknown);
- }
+ nameText = getString(mNumber.equals(CallerInfo.PRIVATE_NUMBER)
+ ? R.string.private_num : R.string.unknown);
+ numberText = "";
+ mCallActionView.setVisibility(View.GONE);
} else {
// Perform a reverse-phonebook lookup to find the PERSON_ID
- String callLabel = null;
+ CharSequence callLabel = null;
Uri personUri = null;
Uri phoneUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI,
Uri.encode(mNumber));
- Cursor phonesCursor = resolver.query(phoneUri, PHONES_PROJECTION, null, null, null);
+ Cursor phonesCursor = resolver.query(
+ phoneUri, PHONES_PROJECTION, null, null, null);
try {
if (phonesCursor != null && phonesCursor.moveToFirst()) {
long personId = phonesCursor.getLong(COLUMN_INDEX_ID);
personUri = ContentUris.withAppendedId(
Contacts.CONTENT_URI, personId);
- callText = getString(R.string.recentCalls_callNumber,
- phonesCursor.getString(COLUMN_INDEX_NAME));
+ nameText = phonesCursor.getString(COLUMN_INDEX_NAME);
+ photoId = phonesCursor.getLong(COLUMN_INDEX_PHOTO_ID);
mNumber = PhoneNumberUtils.formatNumber(
phonesCursor.getString(COLUMN_INDEX_NUMBER),
phonesCursor.getString(COLUMN_INDEX_NORMALIZED_NUMBER),
countryIso);
- callLabel = Phone.getDisplayLabel(this,
+ callLabel = Phone.getTypeLabel(getResources(),
phonesCursor.getInt(COLUMN_INDEX_TYPE),
- phonesCursor.getString(COLUMN_INDEX_LABEL)).toString();
+ phonesCursor.getString(COLUMN_INDEX_LABEL));
} else {
mNumber = PhoneNumberUtils.formatNumber(mNumber, countryIso);
}
} finally {
- if (phonesCursor != null) phonesCursor.close();
+ if (phonesCursor != null) phonesCursor.close();
+ }
+
+ mCallActionView.setVisibility(View.VISIBLE);
+ mCallActionView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent callIntent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
+ Uri.fromParts("tel", mNumber, null));
+ startActivity(callIntent);
+ }
+ });
+
+ if (callLabel != null) {
+ numberText = FormatUtils.applyStyleToSpan(Typeface.BOLD,
+ callLabel + " " + mNumber, 0,
+ callLabel.length(),
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ } else {
+ numberText = mNumber;
}
// Build list of various available actions
List<ViewEntry> actions = new ArrayList<ViewEntry>();
- Intent callIntent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
- Uri.fromParts("tel", mNumber, null));
- ViewEntry entry = new ViewEntry(android.R.drawable.sym_action_call, callText,
- callIntent);
- entry.number = mNumber;
- entry.label = callLabel;
- actions.add(entry);
-
Intent smsIntent = new Intent(Intent.ACTION_SENDTO,
Uri.fromParts("sms", mNumber, null));
actions.add(new ViewEntry(R.drawable.sym_action_sms,
@@ -264,6 +299,10 @@
ViewAdapter adapter = new ViewAdapter(this, actions);
setListAdapter(adapter);
}
+ mNameView.setText(nameText);
+ mNumberView.setText(numberText);
+
+ loadContactPhotos(photoId);
} else {
// Something went wrong reading in our primary data, so we're going to
// bail out and show error to users.
@@ -278,6 +317,25 @@
}
}
+ /** Load the contact photos and places them in the corresponding views. */
+ private void loadContactPhotos(final long photoId) {
+ // There seem to be a limitation in the ContactPhotoManager that does not allow requesting
+ // two photos at once.
+ // TODO: Figure out the problem with ContactPhotoManager and remove this nonsense.
+ mContactPhotoView.post(new Runnable() {
+ @Override
+ public void run() {
+ mContactPhotoManager.loadPhoto(mContactPhotoView, photoId);
+ mContactPhotoView.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ mContactPhotoManager.loadPhoto(mContactBackgroundView, photoId);
+ }
+ }, 100);
+ }
+ });
+ }
+
private String formatDuration(long elapsedSeconds) {
long minutes = 0;
long seconds = 0;
@@ -316,18 +374,22 @@
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
+ @Override
public int getCount() {
return mActions.size();
}
+ @Override
public Object getItem(int position) {
return mActions.get(position);
}
+ @Override
public long getItemId(int position) {
return position;
}
+ @Override
public View getView(int position, View convertView, ViewGroup parent) {
// Make sure we have a valid convertView to start with
if (convertView == null) {
@@ -368,7 +430,8 @@
}
}
- public void onItemClick(AdapterView parent, View view, int position, long id) {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// Handle passing action off to correct handler.
if (view.getTag() instanceof ViewEntry) {
ViewEntry entry = (ViewEntry) view.getTag();