From 806d114d5975c89843c9ba16eadfcaf143afdebb Mon Sep 17 00:00:00 2001 From: Igor Murashkin Date: Thu, 17 Oct 2013 10:52:49 -0700 Subject: camera2: Harden AutoFocusStateMachine against null values in callbacks Bug: 11269834 Change-Id: Id3cb85eb3e9aaaf6714fd81a5d53811d4cbfefa9 --- .../ex/camera2/pos/AutoFocusStateMachine.java | 24 ++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/camera2/public/src/com/android/ex/camera2/pos/AutoFocusStateMachine.java b/camera2/public/src/com/android/ex/camera2/pos/AutoFocusStateMachine.java index cb636ea..5b11c2b 100644 --- a/camera2/public/src/com/android/ex/camera2/pos/AutoFocusStateMachine.java +++ b/camera2/public/src/com/android/ex/camera2/pos/AutoFocusStateMachine.java @@ -16,6 +16,7 @@ package com.android.ex.camera2.pos; import android.hardware.camera2.CameraDevice; +import android.hardware.camera2.CameraMetadata.Key; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.CaptureResult; import android.util.Log; @@ -90,6 +91,29 @@ public class AutoFocusStateMachine { */ public synchronized void onCaptureCompleted(CaptureResult result) { + /** + * Work-around for b/11269834 + * Although these should never-ever happen, harden for ship + */ + if (result == null) { + Log.w(TAG, "onCaptureCompleted - missing result, skipping AF update"); + return; + } + + Key keyAfState = CaptureResult.CONTROL_AF_STATE; + if (keyAfState == null) { + Log.e(TAG, "onCaptureCompleted - missing android.control.afState key, " + + "skipping AF update"); + return; + } + + Key keyAfMode = CaptureResult.CONTROL_AF_MODE; + if (keyAfMode == null) { + Log.e(TAG, "onCaptureCompleted - missing android.control.afMode key, " + + "skipping AF update"); + return; + } + Integer afState = result.get(CaptureResult.CONTROL_AF_STATE); Integer afMode = result.get(CaptureResult.CONTROL_AF_MODE); -- cgit v1.2.3 From 30662b3366c815603d3ed61dc3868e0ea40788e7 Mon Sep 17 00:00:00 2001 From: Igor Murashkin Date: Fri, 18 Oct 2013 12:15:06 -0700 Subject: gcam: Add empty SysTrace class Bug: 11071158 Change-Id: I31f8c449dc4c47c610b0ef3c24849e0402634c86 --- .../src/com/android/ex/camera2/utils/SysTrace.java | 121 +++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 camera2/public/src/com/android/ex/camera2/utils/SysTrace.java diff --git a/camera2/public/src/com/android/ex/camera2/utils/SysTrace.java b/camera2/public/src/com/android/ex/camera2/utils/SysTrace.java new file mode 100644 index 0000000..fd92216 --- /dev/null +++ b/camera2/public/src/com/android/ex/camera2/utils/SysTrace.java @@ -0,0 +1,121 @@ +/* + * Copyright 2013 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.ex.camera2.utils; + +import android.util.Log; + +/** + * Writes trace events to the system trace buffer. These trace events can be + * collected and visualized using the Systrace tool. + * + *

+ * This tracing mechanism is independent of the method tracing mechanism + * offered by {@link Debug#startMethodTracing}. In particular, it enables + * tracing of events that occur across multiple processes. + *

+ * + *

+ * All traces are written using the

APP
tag. + *

+ */ +public final class SysTrace { + + private static final String TAG = "SysTrace"; + private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE); + + private static int sNestingLevel = 0; + + /** + * Writes trace message to indicate the value of a given counter. + * + * @param counterName The counter name to appear in the trace. + * @param counterValue The counter value. + * + */ + public static void traceCounter(String counterName, int counterValue) { + if (VERBOSE) { + Log.v(TAG, "traceCounter " + counterName + " " + counterValue); + } + } + + /** + * Writes a trace message to indicate that a given section of code has begun. This call must + * be followed by a corresponding call to {@link #endSection()} on the same thread. + * + *

At this time the vertical bar character '|', newline character '\n', and + * null character '\0' are used internally by the tracing mechanism. If sectionName contains + * these characters they will be replaced with a space character in the trace. + * + * @param sectionName The name of the code section to appear in the trace. This may be at + * most 127 Unicode code units long. + */ + public static void beginSection(String sectionName) { + if (VERBOSE) { + Log.v(TAG, String.format("beginSection[%d] %s", sNestingLevel, sectionName)); + sNestingLevel++; + } + } + + /** + * Writes a trace message to indicate that a given section of code has + * ended. + *

+ * This call must be preceded by a corresponding call to + * {@link #beginSection(String)}. Calling this method will mark the end of + * the most recently begun section of code, so care must be taken to ensure + * that beginSection / endSection pairs are properly nested and called from + * the same thread. + *

+ */ + public static void endSection() { + if (VERBOSE) { + sNestingLevel--; + Log.v(TAG, String.format("endSection[%d]", sNestingLevel)); + } + } + + /** + * Writes a trace message to indicate that a given section of code has + * begun. + * + *

Must be followed by a call to {@link #endSectionAsync} using the same + * tag. Unlike {@link #beginSection} and {@link #endSection}, + * asynchronous events do not need to be nested. The name and cookie used to + * begin an event must be used to end it.

+ * + * @param methodName The method name to appear in the trace. + * @param cookie Unique identifier for distinguishing simultaneous events + */ + public static void beginSectionAsync(String methodName, int cookie) { + if (VERBOSE) { + Log.v(TAG, "beginSectionAsync " + methodName + " " + cookie); + } + } + + /** + * Writes a trace message to indicate that the current method has ended. + * Must be called exactly once for each call to {@link #beginSectionAsync} + * using the same tag, name and cookie. + * + * @param methodName The method name to appear in the trace. + * @param cookie Unique identifier for distinguishing simultaneous events + */ + public static void endSectionAsync(String methodName, int cookie) { + if (VERBOSE) { + Log.v(TAG, "endSectionAsync " + methodName + " " + cookie); + } + } +} -- cgit v1.2.3 From 2d8e55f45e52ac0b18c0e46de602e6a6d5ab1001 Mon Sep 17 00:00:00 2001 From: Igor Murashkin Date: Mon, 4 Nov 2013 16:42:51 -0800 Subject: camera2: Add tracing to AutoFocusStateMachine (lens-locking actions only) Bug: 11071158 Change-Id: I6a1d0130b6ca36c853f539ef3f1afb363a70df24 --- .../ex/camera2/pos/AutoFocusStateMachine.java | 35 ++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/camera2/public/src/com/android/ex/camera2/pos/AutoFocusStateMachine.java b/camera2/public/src/com/android/ex/camera2/pos/AutoFocusStateMachine.java index 5b11c2b..ce66285 100644 --- a/camera2/public/src/com/android/ex/camera2/pos/AutoFocusStateMachine.java +++ b/camera2/public/src/com/android/ex/camera2/pos/AutoFocusStateMachine.java @@ -21,6 +21,8 @@ import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.CaptureResult; import android.util.Log; +import com.android.ex.camera2.utils.SysTrace; + /** * Manage the auto focus state machine for CameraDevice. * @@ -72,6 +74,10 @@ public class AutoFocusStateMachine { private int mCurrentAfMode = AF_UNINITIALIZED; private int mCurrentAfTrigger = AF_UNINITIALIZED; + private int mCurrentAfCookie = AF_UNINITIALIZED; + private String mCurrentAfTrace = ""; + private int mLastAfCookie = 0; + public AutoFocusStateMachine(AutoFocusStateListener listener) { if (listener == null) { throw new IllegalArgumentException("listener should not be null"); @@ -146,9 +152,11 @@ public class AutoFocusStateMachine { switch (afState) { case CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED: mListener.onAutoFocusSuccess(result, /*locked*/true); + endTraceAsync(); break; case CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED: mListener.onAutoFocusFail(result, /*locked*/true); + endTraceAsync(); break; case CaptureResult.CONTROL_AF_STATE_PASSIVE_FOCUSED: mListener.onAutoFocusSuccess(result, /*locked*/false); @@ -195,6 +203,8 @@ public class AutoFocusStateMachine { throw new IllegalStateException("AF mode was not enabled"); } + beginTraceAsync("AFSM_lockAutoFocus"); + mCurrentAfTrigger = CaptureRequest.CONTROL_AF_TRIGGER_START; repeatingBuilder.set(CaptureRequest.CONTROL_AF_MODE, mCurrentAfMode); @@ -275,6 +285,8 @@ public class AutoFocusStateMachine { CaptureRequest.Builder requestBuilder) { if (VERBOSE_LOGGING) Log.v(TAG, "setActiveAutoFocus"); + beginTraceAsync("AFSM_setActiveAutoFocus"); + mCurrentAfMode = CaptureRequest.CONTROL_AF_MODE_AUTO; repeatingBuilder.set(CaptureRequest.CONTROL_AF_MODE, mCurrentAfMode); @@ -311,4 +323,27 @@ public class AutoFocusStateMachine { repeatingBuilder.set(CaptureRequest.CONTROL_AF_MODE, mCurrentAfMode); } + + private synchronized void beginTraceAsync(String sectionName) { + if (mCurrentAfCookie != AF_UNINITIALIZED) { + // Terminate any currently active async sections before beginning another section + SysTrace.endSectionAsync(mCurrentAfTrace, mCurrentAfCookie); + } + + mLastAfCookie++; + mCurrentAfCookie = mLastAfCookie; + mCurrentAfTrace = sectionName; + + SysTrace.beginSectionAsync(sectionName, mCurrentAfCookie); + } + + private synchronized void endTraceAsync() { + if (mCurrentAfCookie == AF_UNINITIALIZED) { + Log.w(TAG, "endTraceAsync - no current trace active"); + return; + } + + SysTrace.endSectionAsync(mCurrentAfTrace, mCurrentAfCookie); + mCurrentAfCookie = AF_UNINITIALIZED; + } } -- cgit v1.2.3 From e2fa3d7185929f5e51a851d23db2618a41f18d9d Mon Sep 17 00:00:00 2001 From: Igor Murashkin Date: Wed, 6 Nov 2013 12:09:53 -0800 Subject: camera2: Add #resetState to AutoFocusStateMachine Bug: 11071158 Change-Id: I24bc2d771703bb502dcbb676b6262954f2017630 --- .../ex/camera2/pos/AutoFocusStateMachine.java | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/camera2/public/src/com/android/ex/camera2/pos/AutoFocusStateMachine.java b/camera2/public/src/com/android/ex/camera2/pos/AutoFocusStateMachine.java index ce66285..23959e4 100644 --- a/camera2/public/src/com/android/ex/camera2/pos/AutoFocusStateMachine.java +++ b/camera2/public/src/com/android/ex/camera2/pos/AutoFocusStateMachine.java @@ -31,6 +31,10 @@ import com.android.ex.camera2.utils.SysTrace; */ public class AutoFocusStateMachine { + /** + * Observe state AF state transitions triggered by + * {@link AutoFocusStateMachine#onCaptureCompleted onCaptureCompleted}. + */ public interface AutoFocusStateListener { /** * The camera is currently focused (either active or passive). @@ -176,6 +180,22 @@ public class AutoFocusStateMachine { } } + /** + * Reset the current AF state. + * + *

+ * When dropping capture results (by not invoking {@link #onCaptureCompleted} when a new + * {@link CaptureResult} is available), call this function to reset the state. Otherwise + * the next time a new state is observed this class may incorrectly consider it as the same + * state as before, and not issue any callbacks by {@link AutoFocusStateListener}. + *

+ */ + public synchronized void resetState() { + if (VERBOSE_LOGGING) Log.v(TAG, "resetState - last state was " + mLastAfState); + + mLastAfState = AF_UNINITIALIZED; + } + /** * Lock the lens from moving. Typically used before taking a picture. * -- cgit v1.2.3 From d4429271623c05376ee1445d13fde9bcf76a0033 Mon Sep 17 00:00:00 2001 From: Zhijun He Date: Tue, 29 Oct 2013 19:07:56 -0700 Subject: camera2: Add updateCaptureRequest method It can be used to update repeating request with current auto focus mode. Bug: 10649854 Change-Id: I5e92218c61dea04990581d37f92bf25a1f8dbe91 --- .../ex/camera2/pos/AutoFocusStateMachine.java | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/camera2/public/src/com/android/ex/camera2/pos/AutoFocusStateMachine.java b/camera2/public/src/com/android/ex/camera2/pos/AutoFocusStateMachine.java index ce66285..a2bf703 100644 --- a/camera2/public/src/com/android/ex/camera2/pos/AutoFocusStateMachine.java +++ b/camera2/public/src/com/android/ex/camera2/pos/AutoFocusStateMachine.java @@ -346,4 +346,25 @@ public class AutoFocusStateMachine { SysTrace.endSectionAsync(mCurrentAfTrace, mCurrentAfCookie); mCurrentAfCookie = AF_UNINITIALIZED; } + + /** + * Update the repeating request with current focus mode. + * + *

This is typically used when a new repeating request is created to update preview with + * new metadata (i.e. crop region). The current auto focus mode needs to be carried over for + * correct auto focus behavior.

+ * + * @param repeatingBuilder Builder for a repeating request. + */ + public synchronized void updateCaptureRequest(CaptureRequest.Builder repeatingBuilder) { + if (repeatingBuilder == null) { + throw new IllegalArgumentException("repeatingBuilder shouldn't be null"); + } + + if (mCurrentAfMode == AF_UNINITIALIZED) { + throw new IllegalStateException("AF mode was not enabled"); + } + + repeatingBuilder.set(CaptureRequest.CONTROL_AF_MODE, mCurrentAfMode); + } } -- cgit v1.2.3 From 7d1e2245d443faaf833c19e7e9c837112d618c68 Mon Sep 17 00:00:00 2001 From: Igor Murashkin Date: Wed, 6 Nov 2013 12:09:53 -0800 Subject: camera2: Add #resetState to AutoFocusStateMachine Bug: 11071158 Change-Id: I24bc2d771703bb502dcbb676b6262954f2017630 --- .../ex/camera2/pos/AutoFocusStateMachine.java | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/camera2/public/src/com/android/ex/camera2/pos/AutoFocusStateMachine.java b/camera2/public/src/com/android/ex/camera2/pos/AutoFocusStateMachine.java index ce66285..23959e4 100644 --- a/camera2/public/src/com/android/ex/camera2/pos/AutoFocusStateMachine.java +++ b/camera2/public/src/com/android/ex/camera2/pos/AutoFocusStateMachine.java @@ -31,6 +31,10 @@ import com.android.ex.camera2.utils.SysTrace; */ public class AutoFocusStateMachine { + /** + * Observe state AF state transitions triggered by + * {@link AutoFocusStateMachine#onCaptureCompleted onCaptureCompleted}. + */ public interface AutoFocusStateListener { /** * The camera is currently focused (either active or passive). @@ -176,6 +180,22 @@ public class AutoFocusStateMachine { } } + /** + * Reset the current AF state. + * + *

+ * When dropping capture results (by not invoking {@link #onCaptureCompleted} when a new + * {@link CaptureResult} is available), call this function to reset the state. Otherwise + * the next time a new state is observed this class may incorrectly consider it as the same + * state as before, and not issue any callbacks by {@link AutoFocusStateListener}. + *

+ */ + public synchronized void resetState() { + if (VERBOSE_LOGGING) Log.v(TAG, "resetState - last state was " + mLastAfState); + + mLastAfState = AF_UNINITIALIZED; + } + /** * Lock the lens from moving. Typically used before taking a picture. * -- cgit v1.2.3 From 301ff06a565c55404e472433c46f506e5bef2ee7 Mon Sep 17 00:00:00 2001 From: Zhijun He Date: Tue, 29 Oct 2013 19:07:56 -0700 Subject: camera2: Add updateCaptureRequest method It can be used to update repeating request with current auto focus mode. Bug: 10649854 Change-Id: I5e92218c61dea04990581d37f92bf25a1f8dbe91 --- .../ex/camera2/pos/AutoFocusStateMachine.java | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/camera2/public/src/com/android/ex/camera2/pos/AutoFocusStateMachine.java b/camera2/public/src/com/android/ex/camera2/pos/AutoFocusStateMachine.java index 23959e4..500fb12 100644 --- a/camera2/public/src/com/android/ex/camera2/pos/AutoFocusStateMachine.java +++ b/camera2/public/src/com/android/ex/camera2/pos/AutoFocusStateMachine.java @@ -366,4 +366,25 @@ public class AutoFocusStateMachine { SysTrace.endSectionAsync(mCurrentAfTrace, mCurrentAfCookie); mCurrentAfCookie = AF_UNINITIALIZED; } + + /** + * Update the repeating request with current focus mode. + * + *

This is typically used when a new repeating request is created to update preview with + * new metadata (i.e. crop region). The current auto focus mode needs to be carried over for + * correct auto focus behavior.

+ * + * @param repeatingBuilder Builder for a repeating request. + */ + public synchronized void updateCaptureRequest(CaptureRequest.Builder repeatingBuilder) { + if (repeatingBuilder == null) { + throw new IllegalArgumentException("repeatingBuilder shouldn't be null"); + } + + if (mCurrentAfMode == AF_UNINITIALIZED) { + throw new IllegalStateException("AF mode was not enabled"); + } + + repeatingBuilder.set(CaptureRequest.CONTROL_AF_MODE, mCurrentAfMode); + } } -- cgit v1.2.3 From 3e8176df16950c067bcb1b37e62b2fc07d74065a Mon Sep 17 00:00:00 2001 From: Scott Kennedy Date: Wed, 13 Nov 2013 15:58:08 -0800 Subject: Don't show the alternates dialog for GAL contacts There's a bug in the chips code that causes this to show either no alternates, or a different person, which is scaring people. The proper future fix is to store some more information about GAL contacts so we can do a proper lookup. Bug: 11535331 Change-Id: Id8b6fc834446afe33bd6ec4e99b3d2b75b985bde --- .../com/android/ex/chips/BaseRecipientAdapter.java | 22 +++++++++++------ .../ex/chips/RecipientAlternatesAdapter.java | 6 +++-- .../android/ex/chips/RecipientEditTextView.java | 3 ++- chips/src/com/android/ex/chips/RecipientEntry.java | 28 ++++++++++++++-------- .../ex/chips/recipientchip/BaseRecipientChip.java | 7 ++++++ .../recipientchip/InvisibleRecipientChip.java | 5 ++++ .../chips/recipientchip/SimpleRecipientChip.java | 5 ++++ .../chips/recipientchip/VisibleRecipientChip.java | 5 ++++ .../ex/chips/RecipientAlternatesAdapterTest.java | 14 +++++++---- 9 files changed, 70 insertions(+), 25 deletions(-) diff --git a/chips/src/com/android/ex/chips/BaseRecipientAdapter.java b/chips/src/com/android/ex/chips/BaseRecipientAdapter.java index d9eb64e..cdab4f3 100644 --- a/chips/src/com/android/ex/chips/BaseRecipientAdapter.java +++ b/chips/src/com/android/ex/chips/BaseRecipientAdapter.java @@ -152,6 +152,7 @@ public abstract class BaseRecipientAdapter extends BaseAdapter implements Filter public final long dataId; public final String thumbnailUriString; public final int displayNameSource; + public final boolean isGalContact; public TemporaryEntry( String displayName, @@ -161,7 +162,8 @@ public abstract class BaseRecipientAdapter extends BaseAdapter implements Filter long contactId, long dataId, String thumbnailUriString, - int displayNameSource) { + int displayNameSource, + boolean isGalContact) { this.displayName = displayName; this.destination = destination; this.destinationType = destinationType; @@ -170,9 +172,10 @@ public abstract class BaseRecipientAdapter extends BaseAdapter implements Filter this.dataId = dataId; this.thumbnailUriString = thumbnailUriString; this.displayNameSource = displayNameSource; + this.isGalContact = isGalContact; } - public TemporaryEntry(Cursor cursor) { + public TemporaryEntry(Cursor cursor, boolean isGalContact) { this.displayName = cursor.getString(Queries.Query.NAME); this.destination = cursor.getString(Queries.Query.DESTINATION); this.destinationType = cursor.getInt(Queries.Query.DESTINATION_TYPE); @@ -181,6 +184,7 @@ public abstract class BaseRecipientAdapter extends BaseAdapter implements Filter this.dataId = cursor.getLong(Queries.Query.DATA_ID); this.thumbnailUriString = cursor.getString(Queries.Query.PHOTO_THUMBNAIL_URI); this.displayNameSource = cursor.getInt(Queries.Query.DISPLAY_NAME_SOURCE); + this.isGalContact = isGalContact; } } @@ -251,7 +255,8 @@ public abstract class BaseRecipientAdapter extends BaseAdapter implements Filter while (defaultDirectoryCursor.moveToNext()) { // Note: At this point each entry doesn't contain any photo // (thus getPhotoBytes() returns null). - putOneEntry(new TemporaryEntry(defaultDirectoryCursor), + putOneEntry(new TemporaryEntry(defaultDirectoryCursor, + false /* isGalContact */), true, entryMap, nonAggregatedEntries, existingDestinations); } @@ -382,7 +387,7 @@ public abstract class BaseRecipientAdapter extends BaseAdapter implements Filter if (cursor != null) { while (cursor.moveToNext()) { - tempEntries.add(new TemporaryEntry(cursor)); + tempEntries.add(new TemporaryEntry(cursor, true /* isGalContact */)); } } } finally { @@ -683,7 +688,8 @@ public abstract class BaseRecipientAdapter extends BaseAdapter implements Filter entry.displayName, entry.displayNameSource, entry.destination, entry.destinationType, entry.destinationLabel, - entry.contactId, entry.dataId, entry.thumbnailUriString, true)); + entry.contactId, entry.dataId, entry.thumbnailUriString, true, + entry.isGalContact)); } else if (entryMap.containsKey(entry.contactId)) { // We already have a section for the person. final List entryList = entryMap.get(entry.contactId); @@ -691,14 +697,16 @@ public abstract class BaseRecipientAdapter extends BaseAdapter implements Filter entry.displayName, entry.displayNameSource, entry.destination, entry.destinationType, entry.destinationLabel, - entry.contactId, entry.dataId, entry.thumbnailUriString, true)); + entry.contactId, entry.dataId, entry.thumbnailUriString, true, + entry.isGalContact)); } else { final List entryList = new ArrayList(); entryList.add(RecipientEntry.constructTopLevelEntry( entry.displayName, entry.displayNameSource, entry.destination, entry.destinationType, entry.destinationLabel, - entry.contactId, entry.dataId, entry.thumbnailUriString, true)); + entry.contactId, entry.dataId, entry.thumbnailUriString, true, + entry.isGalContact)); entryMap.put(entry.contactId, entryList); } } diff --git a/chips/src/com/android/ex/chips/RecipientAlternatesAdapter.java b/chips/src/com/android/ex/chips/RecipientAlternatesAdapter.java index f64c166..b9a7c80 100644 --- a/chips/src/com/android/ex/chips/RecipientAlternatesAdapter.java +++ b/chips/src/com/android/ex/chips/RecipientAlternatesAdapter.java @@ -229,7 +229,8 @@ public class RecipientAlternatesAdapter extends CursorAdapter { c.getLong(Queries.Query.CONTACT_ID), c.getLong(Queries.Query.DATA_ID), c.getString(Queries.Query.PHOTO_THUMBNAIL_URI), - true); + true, + false /* isGalContact TODO(skennedy) We should look these up eventually */); /* * In certain situations, we may have two results for one address, where one of the @@ -429,7 +430,8 @@ public class RecipientAlternatesAdapter extends CursorAdapter { c.getLong(Queries.Query.CONTACT_ID), c.getLong(Queries.Query.DATA_ID), c.getString(Queries.Query.PHOTO_THUMBNAIL_URI), - true); + true, + false /* isGalContact TODO(skennedy) We should look these up eventually */); } @Override diff --git a/chips/src/com/android/ex/chips/RecipientEditTextView.java b/chips/src/com/android/ex/chips/RecipientEditTextView.java index 6ee9986..0a1cdff 100644 --- a/chips/src/com/android/ex/chips/RecipientEditTextView.java +++ b/chips/src/com/android/ex/chips/RecipientEditTextView.java @@ -1987,7 +1987,8 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements return constructChipSpan( RecipientEntry.constructFakeEntry((String) text, isValid(text.toString())), true, false); - } else if (currentChip.getContactId() == RecipientEntry.GENERATED_CONTACT) { + } else if (currentChip.getContactId() == RecipientEntry.GENERATED_CONTACT + || currentChip.isGalContact()) { int start = getChipStart(currentChip); int end = getChipEnd(currentChip); getSpannable().removeSpan(currentChip); diff --git a/chips/src/com/android/ex/chips/RecipientEntry.java b/chips/src/com/android/ex/chips/RecipientEntry.java index 44bc767..30fccae 100644 --- a/chips/src/com/android/ex/chips/RecipientEntry.java +++ b/chips/src/com/android/ex/chips/RecipientEntry.java @@ -74,9 +74,11 @@ public class RecipientEntry { */ private byte[] mPhotoBytes; + private final boolean mIsGalContact; + private RecipientEntry(int entryType, String displayName, String destination, int destinationType, String destinationLabel, long contactId, long dataId, - Uri photoThumbnailUri, boolean isFirstLevel, boolean isValid) { + Uri photoThumbnailUri, boolean isFirstLevel, boolean isValid, boolean isGalContact) { mEntryType = entryType; mIsFirstLevel = isFirstLevel; mDisplayName = displayName; @@ -89,6 +91,7 @@ public class RecipientEntry { mPhotoBytes = null; mIsDivider = false; mIsValid = isValid; + mIsGalContact = isGalContact; } public boolean isValid() { @@ -114,7 +117,7 @@ public class RecipientEntry { return new RecipientEntry(ENTRY_TYPE_PERSON, tokenizedAddress, tokenizedAddress, INVALID_DESTINATION_TYPE, null, - INVALID_CONTACT, INVALID_CONTACT, null, true, isValid); + INVALID_CONTACT, INVALID_CONTACT, null, true, isValid, false /* isGalContact */); } /** @@ -124,7 +127,7 @@ public class RecipientEntry { final boolean isValid) { return new RecipientEntry(ENTRY_TYPE_PERSON, phoneNumber, phoneNumber, INVALID_DESTINATION_TYPE, null, - INVALID_CONTACT, INVALID_CONTACT, null, true, isValid); + INVALID_CONTACT, INVALID_CONTACT, null, true, isValid, false /* isGalContact */); } /** @@ -146,34 +149,35 @@ public class RecipientEntry { public static RecipientEntry constructGeneratedEntry(String display, String address, boolean isValid) { return new RecipientEntry(ENTRY_TYPE_PERSON, display, address, INVALID_DESTINATION_TYPE, - null, GENERATED_CONTACT, GENERATED_CONTACT, null, true, isValid); + null, GENERATED_CONTACT, GENERATED_CONTACT, null, true, isValid, + false /* isGalContact */); } public static RecipientEntry constructTopLevelEntry(String displayName, int displayNameSource, String destination, int destinationType, String destinationLabel, long contactId, - long dataId, Uri photoThumbnailUri, boolean isValid) { + long dataId, Uri photoThumbnailUri, boolean isValid, boolean isGalContact) { return new RecipientEntry(ENTRY_TYPE_PERSON, pickDisplayName(displayNameSource, displayName, destination), destination, destinationType, destinationLabel, - contactId, dataId, photoThumbnailUri, true, isValid); + contactId, dataId, photoThumbnailUri, true, isValid, isGalContact); } public static RecipientEntry constructTopLevelEntry(String displayName, int displayNameSource, String destination, int destinationType, String destinationLabel, long contactId, - long dataId, String thumbnailUriAsString, boolean isValid) { + long dataId, String thumbnailUriAsString, boolean isValid, boolean isGalContact) { return new RecipientEntry(ENTRY_TYPE_PERSON, pickDisplayName(displayNameSource, displayName, destination), destination, destinationType, destinationLabel, contactId, dataId, (thumbnailUriAsString != null ? Uri.parse(thumbnailUriAsString) - : null), true, isValid); + : null), true, isValid, isGalContact); } public static RecipientEntry constructSecondLevelEntry(String displayName, int displayNameSource, String destination, int destinationType, String destinationLabel, long contactId, long dataId, String thumbnailUriAsString, - boolean isValid) { + boolean isValid, boolean isGalContact) { return new RecipientEntry(ENTRY_TYPE_PERSON, pickDisplayName(displayNameSource, displayName, destination), destination, destinationType, destinationLabel, contactId, dataId, (thumbnailUriAsString != null ? Uri.parse(thumbnailUriAsString) - : null), false, isValid); + : null), false, isValid, isGalContact); } public int getEntryType() { @@ -230,6 +234,10 @@ public class RecipientEntry { return mEntryType == ENTRY_TYPE_PERSON; } + public boolean isGalContact() { + return mIsGalContact; + } + @Override public String toString() { return mDisplayName + " <" + mDestination + ">, isValid=" + mIsValid; diff --git a/chips/src/com/android/ex/chips/recipientchip/BaseRecipientChip.java b/chips/src/com/android/ex/chips/recipientchip/BaseRecipientChip.java index a080ee7..032d3b2 100644 --- a/chips/src/com/android/ex/chips/recipientchip/BaseRecipientChip.java +++ b/chips/src/com/android/ex/chips/recipientchip/BaseRecipientChip.java @@ -70,4 +70,11 @@ interface BaseRecipientChip { * before any reverse lookups. */ CharSequence getOriginalText(); + + /** + * Checks if this contact was retrieved from a GAL lookup. + * + * @return true if it came from GAL, false otherwise + */ + boolean isGalContact(); } diff --git a/chips/src/com/android/ex/chips/recipientchip/InvisibleRecipientChip.java b/chips/src/com/android/ex/chips/recipientchip/InvisibleRecipientChip.java index 0380a81..11a66da 100644 --- a/chips/src/com/android/ex/chips/recipientchip/InvisibleRecipientChip.java +++ b/chips/src/com/android/ex/chips/recipientchip/InvisibleRecipientChip.java @@ -81,6 +81,11 @@ public class InvisibleRecipientChip extends ReplacementSpan implements DrawableR return mDelegate.getOriginalText(); } + @Override + public boolean isGalContact() { + return mDelegate.isGalContact(); + } + @Override public void draw(final Canvas canvas, final CharSequence text, final int start, final int end, final float x, final int top, final int y, final int bottom, final Paint paint) { diff --git a/chips/src/com/android/ex/chips/recipientchip/SimpleRecipientChip.java b/chips/src/com/android/ex/chips/recipientchip/SimpleRecipientChip.java index c04b3be..ac8e897 100644 --- a/chips/src/com/android/ex/chips/recipientchip/SimpleRecipientChip.java +++ b/chips/src/com/android/ex/chips/recipientchip/SimpleRecipientChip.java @@ -92,6 +92,11 @@ class SimpleRecipientChip implements BaseRecipientChip { return !TextUtils.isEmpty(mOriginalText) ? mOriginalText : mEntry.getDestination(); } + @Override + public boolean isGalContact() { + return mEntry.isGalContact(); + } + @Override public String toString() { return mDisplay + " <" + mValue + ">"; diff --git a/chips/src/com/android/ex/chips/recipientchip/VisibleRecipientChip.java b/chips/src/com/android/ex/chips/recipientchip/VisibleRecipientChip.java index acade7f..4637f69 100644 --- a/chips/src/com/android/ex/chips/recipientchip/VisibleRecipientChip.java +++ b/chips/src/com/android/ex/chips/recipientchip/VisibleRecipientChip.java @@ -82,6 +82,11 @@ public class VisibleRecipientChip extends ImageSpan implements DrawableRecipient return mDelegate.getOriginalText(); } + @Override + public boolean isGalContact() { + return mDelegate.isGalContact(); + } + @Override public Rect getBounds() { return getDrawable().getBounds(); diff --git a/chips/tests/src/com/android/ex/chips/RecipientAlternatesAdapterTest.java b/chips/tests/src/com/android/ex/chips/RecipientAlternatesAdapterTest.java index a1a1c7a..d4c0460 100644 --- a/chips/tests/src/com/android/ex/chips/RecipientAlternatesAdapterTest.java +++ b/chips/tests/src/com/android/ex/chips/RecipientAlternatesAdapterTest.java @@ -120,7 +120,8 @@ public class RecipientAlternatesAdapterTest extends AndroidTestCase { { final RecipientEntry entry1 = RecipientEntry.constructTopLevelEntry("Android", DisplayNameSources.NICKNAME, - "1@android.com", 0, null, 0, 0, (Uri) null, true); + "1@android.com", 0, null, 0, 0, (Uri) null, true, + false /* isGalContact */); final RecipientEntry entry2 = RecipientEntry.constructFakeEntry("1@android.com", true); assertEquals(RecipientAlternatesAdapter.getBetterRecipient(entry1, entry2), entry1); @@ -132,10 +133,12 @@ public class RecipientAlternatesAdapterTest extends AndroidTestCase { { final RecipientEntry entry1 = RecipientEntry.constructTopLevelEntry("Android", DisplayNameSources.NICKNAME, - "1@android.com", 0, null, 0, 0, (Uri) null, true); + "1@android.com", 0, null, 0, 0, (Uri) null, true, + false /* isGalContact */); final RecipientEntry entry2 = RecipientEntry.constructTopLevelEntry("2@android.com", DisplayNameSources.EMAIL, - "2@android.com", 0, null, 0, 0, (Uri) null, true); + "2@android.com", 0, null, 0, 0, (Uri) null, true, + false /* isGalContact */); assertEquals(RecipientAlternatesAdapter.getBetterRecipient(entry1, entry2), entry1); assertEquals(RecipientAlternatesAdapter.getBetterRecipient(entry2, entry1), entry1); @@ -146,10 +149,11 @@ public class RecipientAlternatesAdapterTest extends AndroidTestCase { final RecipientEntry entry1 = RecipientEntry.constructTopLevelEntry("Android", DisplayNameSources.NICKNAME, "1@android.com", 0, null, 0, 0, Uri.parse("http://www.android.com"), - true); + true, false /* isGalContact */); final RecipientEntry entry2 = RecipientEntry.constructTopLevelEntry("Android", DisplayNameSources.EMAIL, - "2@android.com", 0, null, 0, 0, (Uri) null, true); + "2@android.com", 0, null, 0, 0, (Uri) null, true, + false /* isGalContact */); assertEquals(RecipientAlternatesAdapter.getBetterRecipient(entry1, entry2), entry1); assertEquals(RecipientAlternatesAdapter.getBetterRecipient(entry2, entry1), entry1); -- cgit v1.2.3