session-android/src/org/thoughtcrime/securesms/components/BubbleContainer.java
Jake McGinty 761ccf4b3f Fix for inconsistent thumbnail aspect ratio
Problems arose from any app that targets below API 19 and uses
views that rely on RelativeLayouts giving correct measurement specs to
their onMeasure().

Resolves #2676
Closes #2712
// FREEBIE
2015-03-19 11:21:40 -07:00

171 lines
7.0 KiB
Java

/**
* Copyright (C) 2015 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms.components;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build.VERSION_CODES;
import android.support.annotation.DrawableRes;
import android.support.annotation.IntDef;
import android.util.AttributeSet;
import android.view.View;
import android.widget.RelativeLayout;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.util.ResUtil;
import org.thoughtcrime.securesms.util.ViewUtil;
public abstract class BubbleContainer extends RelativeLayout {
@SuppressWarnings("unused")
private static final String TAG = BubbleContainer.class.getSimpleName();
public static final int TRANSPORT_STATE_PUSH_SENT = 0;
public static final int TRANSPORT_STATE_SMS_SENT = 1;
public static final int TRANSPORT_STATE_SMS_PENDING = 2;
public static final int TRANSPORT_STATE_PUSH_PENDING = 3;
public static final int MEDIA_STATE_NO_MEDIA = 0;
public static final int MEDIA_STATE_CAPTIONLESS = 1;
public static final int MEDIA_STATE_CAPTIONED = 2;
@IntDef({TRANSPORT_STATE_PUSH_SENT, TRANSPORT_STATE_PUSH_PENDING, TRANSPORT_STATE_SMS_SENT, TRANSPORT_STATE_SMS_PENDING})
public @interface TransportState {}
@IntDef({MEDIA_STATE_NO_MEDIA, MEDIA_STATE_CAPTIONLESS, MEDIA_STATE_CAPTIONED})
public @interface MediaState {}
private View bodyBubble;
private View triangleTick;
private ForegroundImageView media;
private int shadowColor;
private int mmsPendingOverlayColor;
public BubbleContainer(Context context) {
super(context);
initialize();
}
public BubbleContainer(Context context, AttributeSet attrs) {
super(context, attrs);
initialize();
}
public BubbleContainer(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initialize();
}
@TargetApi(VERSION_CODES.LOLLIPOP)
public BubbleContainer(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initialize();
}
protected abstract void onCreateView();
protected abstract int getForegroundColor(@TransportState int transportState);
protected abstract boolean[] getMessageCorners(@MediaState int mediaState);
protected abstract boolean[] getMediaCorners(@MediaState int mediaState);
protected abstract int getTriangleTickRes(@TransportState int transportState);
protected void initialize() {
onCreateView();
this.bodyBubble = findViewById(R.id.body_bubble );
this.triangleTick = findViewById(R.id.triangle_tick);
this.media = (ForegroundImageView) findViewById(R.id.image_view);
this.shadowColor = ResUtil.getColor(getContext(), R.attr.conversation_item_shadow);
this.mmsPendingOverlayColor = ResUtil.getColor(getContext(), R.attr.conversation_item_mms_pending_mask);
}
public void setState(@TransportState int transportState, @MediaState int mediaState) {
updateBodyBubble(transportState, mediaState);
if (isMediaPresent(mediaState)) {
updateMediaBubble(transportState, mediaState);
}
setMediaVisibility(mediaState);
setAlignment(mediaState);
setMediaPendingMask(transportState);
}
private void updateBodyBubble(@TransportState int transportState, @MediaState int mediaState) {
final boolean hasShadow = mediaState == MEDIA_STATE_CAPTIONED || mediaState == MEDIA_STATE_NO_MEDIA;
final BubbleDrawableBuilder builder = new BubbleDrawableBuilder();
final int color = getForegroundColor(transportState);
final Drawable bodyDrawable = builder.setColor(color)
.setShadowColor(shadowColor)
.setCorners(getMessageCorners(mediaState))
.setHasShadow(hasShadow)
.create(getContext());
ViewUtil.setBackgroundSavingPadding(triangleTick, getTriangleTickRes(transportState));
ViewUtil.setBackgroundSavingPadding(bodyBubble, bodyDrawable);
}
private void updateMediaBubble(@TransportState int transportState, @MediaState int mediaState) {
final int foregroundColor = getForegroundColor(transportState);
final BubbleDrawableBuilder builder = new BubbleDrawableBuilder();
final Drawable mediaDrawable = builder.setColor(foregroundColor)
.setShadowColor(shadowColor)
.setCorners(getMediaCorners(mediaState))
.setHasShadow(false)
.create(getContext());
ViewUtil.setBackgroundSavingPadding(media, mediaDrawable);
media.setBorderColor(foregroundColor);
}
private void setMediaVisibility(@MediaState int mediaState) {
media.reset();
if (!isMediaPresent(mediaState)) {
media.hide();
}
}
private void setMediaPendingMask(@TransportState int transportState) {
if (isPending(transportState)) {
media.setForeground(new ColorDrawable(mmsPendingOverlayColor));
} else {
media.setForeground(new ColorDrawable(Color.TRANSPARENT));
}
}
private void setAlignment(@MediaState int mediaState) {
RelativeLayout.LayoutParams parentParams = (RelativeLayout.LayoutParams) bodyBubble.getLayoutParams();
if (mediaState == MEDIA_STATE_CAPTIONLESS) {
parentParams.addRule(RelativeLayout.BELOW, 0);
parentParams.addRule(RelativeLayout.ALIGN_BOTTOM, R.id.thumbnail_container);
} else if (mediaState == MEDIA_STATE_CAPTIONED) {
parentParams.addRule(RelativeLayout.BELOW, R.id.thumbnail_container);
parentParams.addRule(RelativeLayout.ALIGN_BOTTOM, 0);
} else {
parentParams.addRule(RelativeLayout.BELOW, 0);
parentParams.addRule(RelativeLayout.ALIGN_BOTTOM, 0);
}
bodyBubble.setLayoutParams(parentParams);
}
private boolean isMediaPresent(@MediaState int mediaState) {
return mediaState != MEDIA_STATE_NO_MEDIA;
}
private boolean isPending(@TransportState int transportState) {
return transportState == TRANSPORT_STATE_PUSH_PENDING || transportState == TRANSPORT_STATE_SMS_PENDING;
}
}