Merge "Fix NPE in decodedBitmapDrawable" into lmp-mr1-dev
diff --git a/res/layout/quickcontact_header.xml b/res/layout/quickcontact_header.xml
index 010146b..88a80f7 100644
--- a/res/layout/quickcontact_header.xml
+++ b/res/layout/quickcontact_header.xml
@@ -34,13 +34,11 @@
android:id="@+id/title_gradient"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:visibility="gone"
android:layout_gravity="bottom" />
<View
android:id="@+id/action_bar_gradient"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:visibility="gone"
android:layout_gravity="top" />
<!-- Need to set a non null background on Toolbar in order for MenuItem ripples to be drawn on
diff --git a/src/com/android/contacts/activities/ActionBarAdapter.java b/src/com/android/contacts/activities/ActionBarAdapter.java
index 6a5c1cb..634f9ad 100644
--- a/src/com/android/contacts/activities/ActionBarAdapter.java
+++ b/src/com/android/contacts/activities/ActionBarAdapter.java
@@ -143,7 +143,7 @@
new OnClickListener() {
@Override
public void onClick(View v) {
- mSearchView.setText(null);
+ setQueryString(null);
}
});
mSearchContainer.findViewById(R.id.search_back_button).setOnClickListener(
@@ -254,9 +254,8 @@
}
if (mSearchMode) {
setFocusOnSearchView();
- } else {
- mSearchView.setText(null);
}
+ setQueryString(null);
} else if (flag) {
// Everything is already set up. Still make sure the keyboard is up
if (mSearchView != null) setFocusOnSearchView();
@@ -271,6 +270,10 @@
mQueryString = query;
if (mSearchView != null) {
mSearchView.setText(query);
+ // When programmatically entering text into the search view, the most reasonable
+ // place for the cursor is after all the text.
+ mSearchView.setSelection(mSearchView.getText() == null ?
+ 0 : mSearchView.getText().length());
}
}
diff --git a/src/com/android/contacts/activities/PeopleActivity.java b/src/com/android/contacts/activities/PeopleActivity.java
index ba77438..f7c2de2 100644
--- a/src/com/android/contacts/activities/PeopleActivity.java
+++ b/src/com/android/contacts/activities/PeopleActivity.java
@@ -1188,8 +1188,8 @@
&& !Character.isWhitespace(unicodeChar)) {
String query = new String(new int[]{ unicodeChar }, 0, 1);
if (!mActionBarAdapter.isSearchMode()) {
- mActionBarAdapter.setQueryString(query);
mActionBarAdapter.setSearchMode(true);
+ mActionBarAdapter.setQueryString(query);
return true;
}
}
diff --git a/src/com/android/contacts/quickcontact/QuickContactActivity.java b/src/com/android/contacts/quickcontact/QuickContactActivity.java
index 70d3281..1da010c 100644
--- a/src/com/android/contacts/quickcontact/QuickContactActivity.java
+++ b/src/com/android/contacts/quickcontact/QuickContactActivity.java
@@ -874,7 +874,6 @@
mPhotoView.setIsBusiness(mContactData.isDisplayNameFromOrganization());
mPhotoSetter.setupContactPhoto(data, mPhotoView);
extractAndApplyTintFromPhotoViewAsynchronously();
- analyzeWhitenessOfPhotoAsynchronously();
setHeaderNameText(ContactDisplayUtils.getDisplayName(this, data).toString());
Trace.endSection();
@@ -1824,30 +1823,6 @@
}.execute();
}
- /**
- * Examine how many white pixels are in the bitmap in order to determine whether or not
- * we need gradient overlays on top of the image.
- */
- private void analyzeWhitenessOfPhotoAsynchronously() {
- final Drawable imageViewDrawable = mPhotoView.getDrawable();
- new AsyncTask<Void, Void, Boolean>() {
- @Override
- protected Boolean doInBackground(Void... params) {
- if (imageViewDrawable instanceof BitmapDrawable) {
- final Bitmap bitmap = ((BitmapDrawable) imageViewDrawable).getBitmap();
- return WhitenessUtils.isBitmapWhiteAtTopOrBottom(bitmap);
- }
- return !(imageViewDrawable instanceof LetterTileDrawable);
- }
-
- @Override
- protected void onPostExecute(Boolean isWhite) {
- super.onPostExecute(isWhite);
- mScroller.setUseGradient(isWhite);
- }
- }.execute();
- }
-
private void setThemeColor(MaterialPalette palette) {
// If the color is invalid, use the predefined default
final int primaryColor = palette.mPrimaryColor;
diff --git a/src/com/android/contacts/quickcontact/WhitenessUtils.java b/src/com/android/contacts/quickcontact/WhitenessUtils.java
deleted file mode 100644
index 0d2f2e2..0000000
--- a/src/com/android/contacts/quickcontact/WhitenessUtils.java
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright (C) 2014 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.quickcontact;
-
-import android.graphics.Bitmap;
-import android.graphics.Color;
-import android.os.Trace;
-
-/**
- * Utility class for determining whether Bitmaps contain a lot of white pixels in locations
- * where QuickContactActivity will want to place white text or buttons.
- *
- * This class liberally considers bitmaps white. All constants are chosen with a small amount of
- * experimentation. Despite a lack of rigour, this class successfully allows QuickContactsActivity
- * to detect when Bitmap are obviously *not* white. Therefore, it is better than nothing.
- */
-public class WhitenessUtils {
-
- /**
- * Analyze this amount of the top and bottom of the bitmap.
- */
- private static final float HEIGHT_PERCENT_ANALYZED = 0.2f;
-
- /**
- * An image with more than this amount white, is considered to be a whitish image.
- */
- private static final float PROPORTION_WHITE_CUTOFF = 0.1f;
-
- private static final float THIRD = 0.33f;
-
- /**
- * Colors with luma greater than this are considered close to white. This value is lower than
- * the value used in Palette's ColorUtils, since we want to liberally declare images white.
- */
- private static final float LUMINANCE_OF_WHITE = 0.90f;
-
- /**
- * Returns true if 20% of the image's top right corner is white, or 20% of the bottom
- * of the image is white.
- */
- public static boolean isBitmapWhiteAtTopOrBottom(Bitmap largeBitmap) {
- Trace.beginSection("isBitmapWhiteAtTopOrBottom");
- try {
- final Bitmap smallBitmap = scaleBitmapDown(largeBitmap);
-
- final int[] rgbPixels = new int[smallBitmap.getWidth() * smallBitmap.getHeight()];
- smallBitmap.getPixels(rgbPixels, 0, smallBitmap.getWidth(), 0, 0,
- smallBitmap.getWidth(), smallBitmap.getHeight());
-
- // look at top right corner of the bitmap
- int whiteCount = 0;
- for (int y = 0; y < smallBitmap.getHeight() * HEIGHT_PERCENT_ANALYZED; y++) {
- for (int x = (int) (smallBitmap.getWidth() * (1 - THIRD));
- x < smallBitmap.getWidth(); x++) {
- final int rgb = rgbPixels[y * smallBitmap.getWidth() + x];
- if (isWhite(rgb)) {
- whiteCount ++;
- }
- }
- }
- int totalPixels = (int) (smallBitmap.getHeight() * smallBitmap.getWidth()
- * THIRD * HEIGHT_PERCENT_ANALYZED);
- if (whiteCount / (float) totalPixels > PROPORTION_WHITE_CUTOFF) {
- return true;
- }
-
- // look at bottom portion of bitmap
- whiteCount = 0;
- for (int y = (int) (smallBitmap.getHeight() * (1 - HEIGHT_PERCENT_ANALYZED));
- y < smallBitmap.getHeight(); y++) {
- for (int x = 0; x < smallBitmap.getWidth(); x++) {
- final int rgb = rgbPixels[y * smallBitmap.getWidth() + x];
- if (isWhite(rgb)) {
- whiteCount ++;
- }
- }
- }
-
- totalPixels = (int) (smallBitmap.getHeight()
- * smallBitmap.getWidth() * HEIGHT_PERCENT_ANALYZED);
-
- return whiteCount / (float) totalPixels > PROPORTION_WHITE_CUTOFF;
- } finally {
- Trace.endSection();
- }
- }
-
- private static boolean isWhite(int rgb) {
- return calculateXyzLuma(rgb) > LUMINANCE_OF_WHITE;
- }
-
- private static float calculateXyzLuma(int rgb) {
- return (0.2126f * Color.red(rgb) +
- 0.7152f * Color.green(rgb) +
- 0.0722f * Color.blue(rgb)) / 255f;
- }
-
- /**
- * Scale down the bitmap in order to make color analysis faster. Taken from Palette.
- */
- private static Bitmap scaleBitmapDown(Bitmap bitmap) {
- final int CALCULATE_BITMAP_MIN_DIMENSION = 100;
- final int minDimension = Math.min(bitmap.getWidth(), bitmap.getHeight());
-
- if (minDimension <= CALCULATE_BITMAP_MIN_DIMENSION) {
- // If the bitmap is small enough already, just return it
- return bitmap;
- }
-
- final float scaleRatio = CALCULATE_BITMAP_MIN_DIMENSION / (float) minDimension;
- return Bitmap.createScaledBitmap(bitmap,
- Math.round(bitmap.getWidth() * scaleRatio),
- Math.round(bitmap.getHeight() * scaleRatio),
- false);
- }
-}
diff --git a/src/com/android/contacts/widget/MultiShrinkScroller.java b/src/com/android/contacts/widget/MultiShrinkScroller.java
index 5106978..f7f0c7b 100644
--- a/src/com/android/contacts/widget/MultiShrinkScroller.java
+++ b/src/com/android/contacts/widget/MultiShrinkScroller.java
@@ -76,14 +76,21 @@
private static final int EXIT_FLING_ANIMATION_DURATION_MS = 300;
/**
- * Length of the entrance animation.
- */
- private static final int ENTRANCE_ANIMATION_SLIDE_OPEN_DURATION_MS = 250;
-
- /**
* In portrait mode, the height:width ratio of the photo's starting height.
*/
- private static final float INTERMEDIATE_HEADER_HEIGHT_RATIO = 0.5f;
+ private static final float INTERMEDIATE_HEADER_HEIGHT_RATIO = 0.6f;
+
+ /**
+ * Color blending will only be performed on the contact photo once the toolbar is compressed
+ * to this ratio of its full height.
+ */
+ private static final float COLOR_BLENDING_START_RATIO = 0.5f;
+
+ /**
+ * When displaying a letter tile drawable, this alpha value should be used at the intermediate
+ * toolbar height.
+ */
+ private static final float DESIRED_INTERMEDIATE_LETTER_TILE_ALPHA = 0.8f;
/**
* Maximum velocity for flings in dips per second. Picked via non-rigorous experimentation.
@@ -166,14 +173,8 @@
private final PathInterpolator mTextSizePathInterpolator
= new PathInterpolator(0.16f, 0.4f, 0.2f, 1);
- /**
- * Interpolator that starts and ends with nearly straight segments. At x=0 it has a y of
- * approximately 0.25. We only want the contact photo 25% faded when half collapsed.
- */
- private final PathInterpolator mWhiteBlendingPathInterpolator
- = new PathInterpolator(1.0f, 0.4f, 0.9f, 0.8f);
- private final int[] mGradientColors = new int[] {0,0xAA000000};
+ private final int[] mGradientColors = new int[] {0,0x88000000};
private GradientDrawable mTitleGradientDrawable = new GradientDrawable(
GradientDrawable.Orientation.TOP_BOTTOM, mGradientColors);
private GradientDrawable mActionBarGradientDrawable = new GradientDrawable(
@@ -363,18 +364,17 @@
}
private void configureGradientViewHeights() {
- final float GRADIENT_SIZE_COEFFICIENT = 1.25f;
final FrameLayout.LayoutParams actionBarGradientLayoutParams
= (FrameLayout.LayoutParams) mActionBarGradientView.getLayoutParams();
- actionBarGradientLayoutParams.height
- = (int) (mActionBarSize * GRADIENT_SIZE_COEFFICIENT);
+ actionBarGradientLayoutParams.height = mActionBarSize;
mActionBarGradientView.setLayoutParams(actionBarGradientLayoutParams);
final FrameLayout.LayoutParams titleGradientLayoutParams
= (FrameLayout.LayoutParams) mTitleGradientView.getLayoutParams();
+ final float TITLE_GRADIENT_SIZE_COEFFICIENT = 1.25f;
final FrameLayout.LayoutParams largeTextLayoutParms
= (FrameLayout.LayoutParams) mLargeTextView.getLayoutParams();
titleGradientLayoutParams.height = (int) ((mLargeTextView.getHeight()
- + largeTextLayoutParms.bottomMargin) * GRADIENT_SIZE_COEFFICIENT);
+ + largeTextLayoutParms.bottomMargin) * TITLE_GRADIENT_SIZE_COEFFICIENT);
mTitleGradientView.setLayoutParams(titleGradientLayoutParams);
}
@@ -383,13 +383,6 @@
mPhotoTouchInterceptOverlay.setContentDescription(title);
}
- public void setUseGradient(boolean useGradient) {
- if (mTitleGradientView != null) {
- mTitleGradientView.setVisibility(useGradient ? View.VISIBLE : View.GONE);
- mActionBarGradientView.setVisibility(useGradient ? View.VISIBLE : View.GONE);
- }
- }
-
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
if (mVelocityTracker == null) {
@@ -1033,9 +1026,7 @@
}
private void updatePhotoTintAndDropShadow() {
- // Let's keep an eye on how long this method takes to complete. Right now, it takes ~0.2ms
- // on a Nexus 5. If it starts to get much slower, there are a number of easy optimizations
- // available.
+ // Let's keep an eye on how long this method takes to complete.
Trace.beginSection("updatePhotoTintAndDropShadow");
if (mIsTwoPanel && !mPhotoView.isBasedOffLetterTile()) {
@@ -1058,68 +1049,75 @@
// Reuse an existing mColorFilter (to avoid GC pauses) to change the photo's tint.
mPhotoView.clearColorFilter();
-
- // Ratio of current size to maximum size of the header.
- final float ratio;
- // The value that "ratio" will have when the header is at its starting/intermediate size.
- final float intermediateRatio = calculateHeightRatio((int)
- (mMaximumPortraitHeaderHeight * INTERMEDIATE_HEADER_HEIGHT_RATIO));
- if (!mIsTwoPanel) {
- ratio = calculateHeightRatio(toolbarHeight);
- } else {
- // We want the ratio and intermediateRatio to have the *approximate* values
- // they would have in portrait mode when at the intermediate position.
- ratio = intermediateRatio;
- }
-
- final float linearBeforeMiddle = Math.max(1 - (1 - ratio) / intermediateRatio, 0);
-
- // Want a function with a derivative of 0 at x=0. I don't want it to grow too
- // slowly before x=0.5. x^1.1 satisfies both requirements.
- final float EXPONENT_ALMOST_ONE = 1.1f;
- final float semiLinearBeforeMiddle = (float) Math.pow(linearBeforeMiddle,
- EXPONENT_ALMOST_ONE);
mColorMatrix.reset();
- mColorMatrix.setSaturation(semiLinearBeforeMiddle);
- mColorMatrix.postConcat(alphaMatrix(
- 1 - mWhiteBlendingPathInterpolator.getInterpolation(1 - ratio), Color.WHITE));
- final float colorAlpha;
- if (mPhotoView.isBasedOffLetterTile()) {
- // Since the letter tile only has white and grey, tint it more slowly. Otherwise
- // it will be completely invisible before we reach the intermediate point. The values
- // for TILE_EXPONENT and slowingFactor are chosen to achieve DESIRED_INTERMEDIATE_ALPHA
- // at the intermediate/starting position.
- final float DESIRED_INTERMEDIATE_ALPHA = 0.9f;
- final float TILE_EXPONENT = 1.5f;
- final float slowingFactor = (float) ((1 - intermediateRatio) / intermediateRatio
- / (1 - Math.pow(1 - DESIRED_INTERMEDIATE_ALPHA, 1/TILE_EXPONENT)));
- float linearBeforeMiddleish = Math.max(1 - (1 - ratio) / intermediateRatio
- / slowingFactor, 0);
- colorAlpha = 1 - (float) Math.pow(linearBeforeMiddleish, TILE_EXPONENT);
- mColorMatrix.postConcat(alphaMatrix(colorAlpha, mHeaderTintColor));
+ final int gradientAlpha;
+ if (!mPhotoView.isBasedOffLetterTile()) {
+ // Constants and equations were arbitrarily picked to choose values for saturation,
+ // whiteness, tint and gradient alpha. There were four main objectives:
+ // 1) The transition period between the unmodified image and fully colored image should
+ // be very short.
+ // 2) The tinting should be fully applied even before the background image is fully
+ // faded out and desaturated. Why? A half tinted photo looks bad and results in
+ // unappealing colors.
+ // 3) The function should have a derivative of 0 at ratio = 1 to avoid discontinuities.
+ // 4) The entire process should look awesome.
+ final float ratio = calculateHeightRatioToBlendingStartHeight(toolbarHeight);
+ final float alpha = 1.0f - (float) Math.min(Math.pow(ratio, 1.5f) * 2f, 1f);
+ final float tint = (float) Math.min(Math.pow(ratio, 1.5f) * 3f, 1f);
+ mColorMatrix.setSaturation(alpha);
+ mColorMatrix.postConcat(alphaMatrix(alpha, Color.WHITE));
+ mColorMatrix.postConcat(multiplyBlendMatrix(mHeaderTintColor, tint));
+ gradientAlpha = (int) (255 * alpha);
+ } else if (mIsTwoPanel) {
+ mColorMatrix.reset();
+ mColorMatrix.postConcat(alphaMatrix(DESIRED_INTERMEDIATE_LETTER_TILE_ALPHA,
+ mHeaderTintColor));
+ gradientAlpha = 0;
} else {
- colorAlpha = 1 - semiLinearBeforeMiddle;
- mColorMatrix.postConcat(multiplyBlendMatrix(mHeaderTintColor, colorAlpha));
+ // We want a function that has DESIRED_INTERMEDIATE_LETTER_TILE_ALPHA value
+ // at the intermediate position and uses TILE_EXPONENT. Finding an equation
+ // that satisfies this condition requires the following arithmetic.
+ final float ratio = calculateHeightRatioToFullyOpen(toolbarHeight);
+ final float intermediateRatio = calculateHeightRatioToFullyOpen((int)
+ (mMaximumPortraitHeaderHeight * INTERMEDIATE_HEADER_HEIGHT_RATIO));
+ final float TILE_EXPONENT = 3f;
+ final float slowingFactor = (float) ((1 - intermediateRatio) / intermediateRatio
+ / (1 - Math.pow(1 - DESIRED_INTERMEDIATE_LETTER_TILE_ALPHA, 1/TILE_EXPONENT)));
+ float linearBeforeIntermediate = Math.max(1 - (1 - ratio) / intermediateRatio
+ / slowingFactor, 0);
+ float colorAlpha = 1 - (float) Math.pow(linearBeforeIntermediate, TILE_EXPONENT);
+ mColorMatrix.postConcat(alphaMatrix(colorAlpha, mHeaderTintColor));
+ gradientAlpha = 0;
}
+ // TODO: remove re-allocation of ColorMatrixColorFilter objects (b/17627000)
mPhotoView.setColorFilter(new ColorMatrixColorFilter(mColorMatrix));
+
// Tell the photo view what tint we are trying to achieve. Depending on the type of
// drawable used, the photo view may or may not use this tint.
mPhotoView.setTint(mHeaderTintColor);
-
- final int gradientAlpha = (int) (255 * linearBeforeMiddle);
mTitleGradientDrawable.setAlpha(gradientAlpha);
mActionBarGradientDrawable.setAlpha(gradientAlpha);
Trace.endSection();
}
- private float calculateHeightRatio(int height) {
+ private float calculateHeightRatioToFullyOpen(int height) {
return (height - mMinimumPortraitHeaderHeight)
/ (float) (mMaximumPortraitHeaderHeight - mMinimumPortraitHeaderHeight);
}
+ private float calculateHeightRatioToBlendingStartHeight(int height) {
+ final float intermediateHeight = mMaximumPortraitHeaderHeight
+ * COLOR_BLENDING_START_RATIO;
+ final float interpolatingHeightRange = intermediateHeight - mMinimumPortraitHeaderHeight;
+ if (height > intermediateHeight) {
+ return 0;
+ }
+ return (intermediateHeight - height) / interpolatingHeightRange;
+ }
+
/**
* Simulates alpha blending an image with {@param color}.
*/