diff --git a/res/drawable/contact_photo_background.xml b/res/drawable/contact_photo_background.xml new file mode 100644 index 0000000000..ff1153bf2a --- /dev/null +++ b/res/drawable/contact_photo_background.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/res/layout/conversation_item_received.xml b/res/layout/conversation_item_received.xml index 16545c6f15..9b38941e6c 100644 --- a/res/layout/conversation_item_received.xml +++ b/res/layout/conversation_item_received.xml @@ -26,7 +26,9 @@ android:layout_marginRight="0dp" android:orientation="horizontal"> - - + + + + + + + \ No newline at end of file diff --git a/src/org/thoughtcrime/securesms/components/ForegroundImageView.java b/src/org/thoughtcrime/securesms/components/ForegroundImageView.java new file mode 100644 index 0000000000..123d52edb7 --- /dev/null +++ b/src/org/thoughtcrime/securesms/components/ForegroundImageView.java @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2006 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 org.thoughtcrime.securesms.components; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.view.Gravity; +import android.widget.ImageView; + +import org.thoughtcrime.securesms.R; + +/** + * https://gist.github.com/chrisbanes/9091754 + */ +public class ForegroundImageView extends ImageView { + + private Drawable mForeground; + + private final Rect mSelfBounds = new Rect(); + private final Rect mOverlayBounds = new Rect(); + + private int mForegroundGravity = Gravity.FILL; + + protected boolean mForegroundInPadding = true; + + boolean mForegroundBoundsChanged = false; + + public ForegroundImageView(Context context) { + super(context); + } + + public ForegroundImageView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public ForegroundImageView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ForegroundImageView, + defStyle, 0); + + mForegroundGravity = a.getInt( + R.styleable.ForegroundImageView_android_foregroundGravity, mForegroundGravity); + + final Drawable d = a.getDrawable(R.styleable.ForegroundImageView_android_foreground); + if (d != null) { + setForeground(d); + } + + mForegroundInPadding = a.getBoolean( + R.styleable.ForegroundImageView_android_foregroundInsidePadding, true); + + a.recycle(); + } + + /** + * Describes how the foreground is positioned. + * + * @return foreground gravity. + * + * @see #setForegroundGravity(int) + */ + public int getForegroundGravity() { + return mForegroundGravity; + } + + /** + * Describes how the foreground is positioned. Defaults to START and TOP. + * + * @param foregroundGravity See {@link android.view.Gravity} + * + * @see #getForegroundGravity() + */ + public void setForegroundGravity(int foregroundGravity) { + if (mForegroundGravity != foregroundGravity) { + if ((foregroundGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) { + foregroundGravity |= Gravity.START; + } + + if ((foregroundGravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) { + foregroundGravity |= Gravity.TOP; + } + + mForegroundGravity = foregroundGravity; + + + if (mForegroundGravity == Gravity.FILL && mForeground != null) { + Rect padding = new Rect(); + mForeground.getPadding(padding); + } + + requestLayout(); + } + } + + @Override + protected boolean verifyDrawable(Drawable who) { + return super.verifyDrawable(who) || (who == mForeground); + } + + @Override + public void jumpDrawablesToCurrentState() { + super.jumpDrawablesToCurrentState(); + if (mForeground != null) mForeground.jumpToCurrentState(); + } + + @Override + protected void drawableStateChanged() { + super.drawableStateChanged(); + if (mForeground != null && mForeground.isStateful()) { + mForeground.setState(getDrawableState()); + } + } + + /** + * Supply a Drawable that is to be rendered on top of all of the child + * views in the frame layout. Any padding in the Drawable will be taken + * into account by ensuring that the children are inset to be placed + * inside of the padding area. + * + * @param drawable The Drawable to be drawn on top of the children. + */ + public void setForeground(Drawable drawable) { + if (mForeground != drawable) { + if (mForeground != null) { + mForeground.setCallback(null); + unscheduleDrawable(mForeground); + } + + mForeground = drawable; + + if (drawable != null) { + setWillNotDraw(false); + drawable.setCallback(this); + if (drawable.isStateful()) { + drawable.setState(getDrawableState()); + } + if (mForegroundGravity == Gravity.FILL) { + Rect padding = new Rect(); + drawable.getPadding(padding); + } + } else { + setWillNotDraw(true); + } + requestLayout(); + invalidate(); + } + } + + /** + * Returns the drawable used as the foreground of this FrameLayout. The + * foreground drawable, if non-null, is always drawn on top of the children. + * + * @return A Drawable or null if no foreground was set. + */ + public Drawable getForeground() { + return mForeground; + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + mForegroundBoundsChanged = changed; + } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + mForegroundBoundsChanged = true; + } + + @Override + public void draw(Canvas canvas) { + super.draw(canvas); + + if (mForeground != null) { + final Drawable foreground = mForeground; + + if (mForegroundBoundsChanged) { + mForegroundBoundsChanged = false; + final Rect selfBounds = mSelfBounds; + final Rect overlayBounds = mOverlayBounds; + + final int w = getRight() - getLeft(); + final int h = getBottom() - getTop(); + + if (mForegroundInPadding) { + selfBounds.set(0, 0, w, h); + } else { + selfBounds.set(getPaddingLeft(), getPaddingTop(), + w - getPaddingRight(), h - getPaddingBottom()); + } + + Gravity.apply(mForegroundGravity, foreground.getIntrinsicWidth(), + foreground.getIntrinsicHeight(), selfBounds, overlayBounds); + foreground.setBounds(overlayBounds); + } + + foreground.draw(canvas); + } + } + +} \ No newline at end of file