am 60e42746: (-s ours) am 615ed9c5: Check for WRITE_CONTACTS permission
* commit '60e427460a6fe2b258cdd99703322a6f7d7b8ed7':
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index d86d69b..69ab231 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -453,14 +453,6 @@
android:exported="false" />
<!-- end vCard related -->
- <!-- Pinned header list demo -->
- <activity android:name=".widget.PinnedHeaderListDemoActivity">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
- </activity>
-
<!-- Intercept Dialer Intents for devices without a phone.
This activity should have the same intent filters as the DialtactsActivity,
so that its capturing the same events. Omit android.intent.category.LAUNCHER, because
@@ -490,6 +482,14 @@
</intent-filter>
</activity>
+ <activity android:name="com.android.contacts.common.dialog.CallSubjectDialog"
+ android:theme="@style/Theme.CallSubjectDialogTheme"
+ android:windowSoftInputMode="stateVisible|adjustResize">
+ <intent-filter>
+ <action android:name="android.intent.action.VIEW"/>
+ </intent-filter>
+ </activity>
+
<!-- Service that is exclusively for the Phone application that sends out a view
notification. This service might be removed in future versions of the app.
diff --git a/src/com/android/contacts/activities/PeopleActivity.java b/src/com/android/contacts/activities/PeopleActivity.java
index 1591866..97c6ea0 100644
--- a/src/com/android/contacts/activities/PeopleActivity.java
+++ b/src/com/android/contacts/activities/PeopleActivity.java
@@ -1246,7 +1246,7 @@
final Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
final Uri lookupUri = Contacts.getLookupUri(getContentResolver(), contactUri);
List<String> pathSegments = lookupUri.getPathSegments();
- uriListBuilder.append(pathSegments.get(pathSegments.size() - 2));
+ uriListBuilder.append(Uri.encode(pathSegments.get(pathSegments.size() - 2)));
firstIteration = false;
}
final Uri uri = Uri.withAppendedPath(
diff --git a/src/com/android/contacts/quickcontact/ExpandingEntryCardView.java b/src/com/android/contacts/quickcontact/ExpandingEntryCardView.java
index 10b2898..10887cb 100644
--- a/src/com/android/contacts/quickcontact/ExpandingEntryCardView.java
+++ b/src/com/android/contacts/quickcontact/ExpandingEntryCardView.java
@@ -19,12 +19,14 @@
import android.animation.Animator.AnimatorListener;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
+import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.ColorFilter;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.os.Bundle;
import android.support.v7.widget.CardView;
import android.text.Spannable;
import android.text.TextUtils;
@@ -50,6 +52,7 @@
import android.widget.TextView;
import com.android.contacts.R;
+import com.android.contacts.common.dialog.CallSubjectDialog;
import java.util.ArrayList;
import java.util.List;
@@ -87,6 +90,12 @@
* Entry data.
*/
public static final class Entry {
+ // No action when clicking a button is specified.
+ public static final int ACTION_NONE = 1;
+ // Button action is an intent.
+ public static final int ACTION_INTENT = 2;
+ // Button action will open the call with subject dialog.
+ public static final int ACTION_CALL_WITH_SUBJECT = 3;
private final int mId;
private final Drawable mIcon;
@@ -107,6 +116,8 @@
private final Intent mThirdIntent;
private final String mThirdContentDescription;
private final int mIconResourceId;
+ private final int mThirdAction;
+ private final Bundle mThirdExtras;
public Entry(int id, Drawable mainIcon, String header, String subHeader,
Drawable subHeaderIcon, String text, Drawable textIcon,
@@ -114,7 +125,8 @@
Drawable alternateIcon, Intent alternateIntent, String alternateContentDescription,
boolean shouldApplyColor, boolean isEditable,
EntryContextMenuInfo entryContextMenuInfo, Drawable thirdIcon, Intent thirdIntent,
- String thirdContentDescription, int iconResourceId) {
+ String thirdContentDescription, int thirdAction, Bundle thirdExtras,
+ int iconResourceId) {
mId = id;
mIcon = mainIcon;
mHeader = header;
@@ -133,6 +145,8 @@
mThirdIcon = thirdIcon;
mThirdIntent = thirdIntent;
mThirdContentDescription = thirdContentDescription;
+ mThirdAction = thirdAction;
+ mThirdExtras = thirdExtras;
mIconResourceId = iconResourceId;
}
@@ -211,6 +225,14 @@
int getIconResourceId() {
return mIconResourceId;
}
+
+ public int getThirdAction() {
+ return mThirdAction;
+ }
+
+ public Bundle getThirdExtras() {
+ return mThirdExtras;
+ }
}
public interface ExpandingEntryCardViewListener {
@@ -761,10 +783,28 @@
alternateIcon.setContentDescription(entry.getAlternateContentDescription());
}
- if (entry.getThirdIcon() != null && entry.getThirdIntent() != null) {
+ if (entry.getThirdIcon() != null && entry.getThirdAction() != Entry.ACTION_NONE) {
thirdIcon.setImageDrawable(entry.getThirdIcon());
- thirdIcon.setOnClickListener(mOnClickListener);
- thirdIcon.setTag(new EntryTag(entry.getId(), entry.getThirdIntent()));
+ if (entry.getThirdAction() == Entry.ACTION_INTENT) {
+ thirdIcon.setOnClickListener(mOnClickListener);
+ thirdIcon.setTag(new EntryTag(entry.getId(), entry.getThirdIntent()));
+ } else if (entry.getThirdAction() == Entry.ACTION_CALL_WITH_SUBJECT) {
+ thirdIcon.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Object tag = v.getTag();
+ if (!(tag instanceof Bundle)) {
+ return;
+ }
+
+ Context context = getContext();
+ if (context instanceof Activity) {
+ CallSubjectDialog.start((Activity) context, entry.getThirdExtras());
+ }
+ }
+ });
+ thirdIcon.setTag(entry.getThirdExtras());
+ }
thirdIcon.setVisibility(View.VISIBLE);
thirdIcon.setContentDescription(entry.getThirdContentDescription());
}
diff --git a/src/com/android/contacts/quickcontact/QuickContactActivity.java b/src/com/android/contacts/quickcontact/QuickContactActivity.java
index 60d6a62..26f84a8 100644
--- a/src/com/android/contacts/quickcontact/QuickContactActivity.java
+++ b/src/com/android/contacts/quickcontact/QuickContactActivity.java
@@ -96,6 +96,7 @@
import com.android.contacts.common.Collapser;
import com.android.contacts.common.ContactsUtils;
import com.android.contacts.common.activity.RequestPermissionsActivity;
+import com.android.contacts.common.dialog.CallSubjectDialog;
import com.android.contacts.common.editor.SelectAccountDialogFragment;
import com.android.contacts.common.interactions.TouchPointManager;
import com.android.contacts.common.lettertiles.LetterTileDrawable;
@@ -125,6 +126,7 @@
import com.android.contacts.common.util.DateUtils;
import com.android.contacts.common.util.MaterialColorMapUtils;
import com.android.contacts.common.util.MaterialColorMapUtils.MaterialPalette;
+import com.android.contacts.common.util.UriUtils;
import com.android.contacts.common.util.ViewUtil;
import com.android.contacts.detail.ContactDisplayUtils;
import com.android.contacts.editor.ContactEditorFragment;
@@ -1135,7 +1137,9 @@
/* thirdIcon = */ null,
/* thirdIntent = */ null,
/* thirdContentDescription = */ null,
- /* iconResourceId = */ 0);
+ /* thirdAction = */ Entry.ACTION_NONE,
+ /* thirdExtras = */ null,
+ /* iconResourceId = */ 0);
List<Entry> phoneticList = new ArrayList<>();
phoneticList.add(phoneticEntry);
// Phonetic name comes after nickname. Check to see if the first entry type is nickname
@@ -1187,7 +1191,10 @@
/* alternateContentDescription = */ null, /* shouldApplyColor = */ true,
/* isEditable = */ false, /* EntryContextMenuInfo = */ null,
/* thirdIcon = */ null, /* thirdIntent = */ null,
- /* thirdContentDescription = */ null, R.drawable.ic_phone_24dp);
+ /* thirdContentDescription = */ null,
+ /* thirdAction = */ Entry.ACTION_NONE,
+ /* thirdExtras = */ null,
+ R.drawable.ic_phone_24dp);
final Drawable emailIcon = getResources().getDrawable(
R.drawable.ic_email_24dp).mutate();
@@ -1200,6 +1207,7 @@
/* shouldApplyColor = */ true, /* isEditable = */ false,
/* EntryContextMenuInfo = */ null, /* thirdIcon = */ null,
/* thirdIntent = */ null, /* thirdContentDescription = */ null,
+ /* thirdAction = */ Entry.ACTION_NONE, /* thirdExtras = */ null,
R.drawable.ic_email_24dp);
final List<List<Entry>> promptEntries = new ArrayList<>();
@@ -1366,7 +1374,9 @@
EntryContextMenuInfo entryContextMenuInfo = null;
Drawable thirdIcon = null;
Intent thirdIntent = null;
+ int thirdAction = Entry.ACTION_NONE;
String thirdContentDescription = null;
+ Bundle thirdExtras = null;
int iconResourceId = 0;
context = context.getApplicationContext();
@@ -1479,6 +1489,7 @@
}
} else if (dataItem instanceof PhoneDataItem) {
final PhoneDataItem phone = (PhoneDataItem) dataItem;
+ String phoneLabel = null;
if (!TextUtils.isEmpty(phone.getNumber())) {
primaryContentDescription.append(res.getString(R.string.call_other)).append(" ");
header = sBidiFormatter.unicodeWrap(phone.buildDataStringForDisplay(context, kind),
@@ -1489,10 +1500,12 @@
if (phone.hasKindTypeColumn(kind)) {
final int kindTypeColumn = phone.getKindTypeColumn(kind);
final String label = phone.getLabel();
+ phoneLabel = label;
if (kindTypeColumn == Phone.TYPE_CUSTOM && TextUtils.isEmpty(label)) {
text = "";
} else {
text = Phone.getTypeLabel(res, kindTypeColumn, label).toString();
+ phoneLabel= text;
primaryContentDescription.append(text).append(" ");
}
}
@@ -1508,9 +1521,33 @@
alternateIcon = res.getDrawable(R.drawable.ic_message_24dp);
alternateContentDescription.append(res.getString(R.string.sms_custom, header));
- // Add video call button if supported
- if (CallUtil.isVideoEnabled(context)) {
+ if (CallUtil.isCallWithSubjectSupported(context)) {
+ thirdIcon = res.getDrawable(R.drawable.ic_call_note_white_24dp);
+ thirdAction = Entry.ACTION_CALL_WITH_SUBJECT;
+ thirdContentDescription =
+ res.getString(R.string.call_with_a_note);
+
+ // Create a bundle containing the data the call subject dialog requires.
+ thirdExtras = new Bundle();
+ thirdExtras.putLong(CallSubjectDialog.ARG_PHOTO_ID,
+ contactData.getPhotoId());
+ thirdExtras.putParcelable(CallSubjectDialog.ARG_PHOTO_URI,
+ UriUtils.parseUriOrNull(contactData.getPhotoUri()));
+ thirdExtras.putParcelable(CallSubjectDialog.ARG_CONTACT_URI,
+ contactData.getLookupUri());
+ thirdExtras.putString(CallSubjectDialog.ARG_NAME_OR_NUMBER,
+ contactData.getDisplayName());
+ thirdExtras.putBoolean(CallSubjectDialog.ARG_IS_BUSINESS, false);
+ thirdExtras.putString(CallSubjectDialog.ARG_NUMBER,
+ phone.getNumber());
+ thirdExtras.putString(CallSubjectDialog.ARG_DISPLAY_NUMBER,
+ phone.getFormattedPhoneNumber());
+ thirdExtras.putString(CallSubjectDialog.ARG_NUMBER_LABEL,
+ phoneLabel);
+ } else if (CallUtil.isVideoEnabled(context)) {
+ // Add video call button if supported
thirdIcon = res.getDrawable(R.drawable.ic_videocam);
+ thirdAction = Entry.ACTION_INTENT;
thirdIntent = CallUtil.getVideoCallIntent(phone.getNumber(),
CALL_ORIGIN_QUICK_CONTACTS_ACTIVITY);
thirdContentDescription =
@@ -1707,8 +1744,8 @@
new SpannableString(primaryContentDescription.toString()),
intent, alternateIcon, alternateIntent,
alternateContentDescription.toString(), shouldApplyColor, isEditable,
- entryContextMenuInfo, thirdIcon, thirdIntent, thirdContentDescription,
- iconResourceId);
+ entryContextMenuInfo, thirdIcon, thirdIntent, thirdContentDescription, thirdAction,
+ thirdExtras, iconResourceId);
}
private List<Entry> dataItemsToEntries(List<DataItem> dataItems,
@@ -1979,6 +2016,8 @@
/* thirdIcon = */ null,
/* thirdIntent = */ null,
/* thirdContentDescription = */ null,
+ /* thirdAction = */ Entry.ACTION_NONE,
+ /* thirdActionExtras = */ null,
interaction.getIconResourceId()));
}
return entries;
diff --git a/src/com/android/contacts/widget/PinnedHeaderListDemoActivity.java b/src/com/android/contacts/widget/PinnedHeaderListDemoActivity.java
deleted file mode 100644
index bc9f07e..0000000
--- a/src/com/android/contacts/widget/PinnedHeaderListDemoActivity.java
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Copyright (C) 2010 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.contacts.widget;
-
-import android.app.ListActivity;
-import android.content.Context;
-import android.database.Cursor;
-import android.database.MatrixCursor;
-import android.os.Bundle;
-import android.os.Handler;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.TextView;
-
-import com.android.contacts.R;
-import com.android.contacts.common.activity.RequestPermissionsActivity;
-import com.android.contacts.common.list.PinnedHeaderListAdapter;
-
-/**
- * An activity that demonstrates various use cases for the {@link com.android.contacts.common.list.PinnedHeaderListView}.
- * If we decide to move PinnedHeaderListView to the framework, this class could go
- * to API demos.
- */
-public class PinnedHeaderListDemoActivity extends ListActivity {
-
- public final static class TestPinnedHeaderListAdapter extends PinnedHeaderListAdapter {
-
- public TestPinnedHeaderListAdapter(Context context) {
- super(context);
- setPinnedPartitionHeadersEnabled(true);
- }
-
- private String[] mHeaders;
- private int mPinnedHeaderCount;
-
- public void setHeaders(String[] headers) {
- this.mHeaders = headers;
- }
-
- @Override
- protected View newHeaderView(Context context, int partition, Cursor cursor,
- ViewGroup parent) {
- LayoutInflater inflater = LayoutInflater.from(context);
- return inflater.inflate(R.layout.list_section, null);
- }
-
- @Override
- protected void bindHeaderView(View view, int parition, Cursor cursor) {
- TextView headerText = (TextView)view.findViewById(R.id.header_text);
- headerText.setText(mHeaders[parition]);
- }
-
- @Override
- protected View newView(Context context, int partition, Cursor cursor, int position,
- ViewGroup parent) {
- LayoutInflater inflater = LayoutInflater.from(context);
- return inflater.inflate(android.R.layout.simple_list_item_1, null);
- }
-
- @Override
- protected void bindView(View v, int partition, Cursor cursor, int position) {
- TextView text = (TextView)v.findViewById(android.R.id.text1);
- text.setText(cursor.getString(1));
- }
-
- @Override
- public View getPinnedHeaderView(int viewIndex, View convertView, ViewGroup parent) {
- LayoutInflater inflater = LayoutInflater.from(getContext());
- View view = inflater.inflate(R.layout.list_section, parent, false);
- view.setFocusable(false);
- view.setEnabled(false);
- bindHeaderView(view, viewIndex, null);
- return view;
- }
-
- @Override
- public int getPinnedHeaderCount() {
- return mPinnedHeaderCount;
- }
- }
-
- private Handler mHandler = new Handler();
-
- @Override
- protected void onCreate(Bundle bundle) {
- super.onCreate(bundle);
- if (RequestPermissionsActivity.startPermissionActivity(this)) {
- return;
- }
-
- setContentView(R.layout.pinned_header_list_demo);
-
- final TestPinnedHeaderListAdapter adapter = new TestPinnedHeaderListAdapter(this);
-
- Bundle extras = getIntent().getExtras();
- int[] counts = extras.getIntArray("counts");
- String[] names = extras.getStringArray("names");
- boolean[] showIfEmpty = extras.getBooleanArray("showIfEmpty");
- boolean[] hasHeader = extras.getBooleanArray("headers");
- int[] delays = extras.getIntArray("delays");
-
- if (counts == null || names == null || showIfEmpty == null || delays == null) {
- throw new IllegalArgumentException("Missing required extras");
- }
-
- adapter.setHeaders(names);
- for (int i = 0; i < counts.length; i++) {
- adapter.addPartition(showIfEmpty[i], names[i] != null);
- adapter.mPinnedHeaderCount = names.length;
- }
- setListAdapter(adapter);
- for (int i = 0; i < counts.length; i++) {
- final int sectionId = i;
- final Cursor cursor = makeCursor(names[i], counts[i]);
- mHandler.postDelayed(new Runnable() {
-
- public void run() {
- adapter.changeCursor(sectionId, cursor);
-
- }
- }, delays[i]);
- }
- }
-
- private Cursor makeCursor(String name, int count) {
- MatrixCursor cursor = new MatrixCursor(new String[]{"_id", name});
- for (int i = 0; i < count; i++) {
- cursor.addRow(new Object[]{i, name + "[" + i + "]"});
- }
- return cursor;
- }
-}
diff --git a/tests/src/com/android/contacts/tests/widget/PinnedHeaderUseCaseActivity.java b/tests/src/com/android/contacts/tests/widget/PinnedHeaderUseCaseActivity.java
deleted file mode 100644
index 46a1f43..0000000
--- a/tests/src/com/android/contacts/tests/widget/PinnedHeaderUseCaseActivity.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2010 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.contacts.tests.widget;
-
-import android.app.ListActivity;
-import android.content.ComponentName;
-import android.content.Intent;
-import android.os.Bundle;
-import android.view.View;
-import android.widget.ArrayAdapter;
-import android.widget.ListView;
-
-import com.android.contacts.tests.R;
-import com.android.contacts.common.list.PinnedHeaderListView;
-
-/**
- * An activity that demonstrates various use cases for the {@link PinnedHeaderListView}.
- */
-public class PinnedHeaderUseCaseActivity extends ListActivity {
-
- private static final int SINGLE_SHORT_SECTION_NO_HEADERS = 0;
- private static final int TWO_SHORT_SECTIONS_WITH_HEADERS = 1;
- private static final int FIVE_SHORT_SECTIONS_WITH_HEADERS = 2;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setListAdapter(new ArrayAdapter<String>(this, R.layout.intent_list_item,
- getResources().getStringArray(R.array.pinnedHeaderUseCases)));
- }
-
- @Override
- protected void onListItemClick(ListView l, View v, int position, long id) {
- switch (position) {
- case SINGLE_SHORT_SECTION_NO_HEADERS:
- startActivity(
- new int[]{5},
- new String[]{"Line"},
- new boolean[]{false},
- new boolean[]{false},
- new int[]{0});
- break;
- case TWO_SHORT_SECTIONS_WITH_HEADERS:
- startActivity(
- new int[]{2, 30},
- new String[]{"First", "Second"},
- new boolean[]{true, true},
- new boolean[]{false, false},
- new int[]{0, 2000});
- break;
- case FIVE_SHORT_SECTIONS_WITH_HEADERS:
- startActivity(
- new int[]{1, 5, 5, 5, 5},
- new String[]{"First", "Second", "Third", "Fourth", "Fifth"},
- new boolean[]{true, true, true, true, true},
- new boolean[]{false, false, false, false, false},
- new int[]{0, 2000, 3000, 4000, 5000});
- break;
- }
- }
-
- private void startActivity(int[] counts, String[] names, boolean[] headers,
- boolean[] showIfEmpty, int[] delays) {
- Intent intent = new Intent();
- intent.setComponent(new ComponentName(
- getResources().getString(R.string.target_package_name),
- "com.android.contacts.widget.PinnedHeaderListDemoActivity"));
- intent.putExtra("counts", counts);
- intent.putExtra("names", names);
- intent.putExtra("headers", headers);
- intent.putExtra("showIfEmpty", showIfEmpty);
- intent.putExtra("delays", delays);
-
- startActivity(intent);
- }
-}