/*
* Copyright 2012 Benjamin Weiss
* Copyright 2012 Neofonie Mobile GmbH
*
* 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 de.neofonie.mobile.app.android.widget.crouton;
import android.app.Activity;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Shader;
import android.graphics.Typeface;
import android.graphics.drawable.BitmapDrawable;
import android.util.TypedValue;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
/*
* Based on an article by Cyril Mottier (http://android.cyrilmottier.com/?p=773) <br>
*/
/**
* Displays information in a non-invasive context related manner. Like
* {@link Toast}, but better.
* <p/>
* Call {@link Manager#clearCroutonsForActivity(Activity)} within your
* Activitie's onDestroy method to avoid {@link Context} leaks.
*
*/
public final class Crouton {
private static final int IMAGE_ID = 0x100;
private static final int TEXT_ID = 0x101;
private final CharSequence text;
private final Style style;
private final View customView;
private Activity activity;
private FrameLayout croutonView;
/**
* Creates the {@link Crouton}.
*
* @param activity
* The {@link Activity} that the {@link Crouton} should be attached
* to.
* @param text
* The text you want to display.
* @param style
* The style that this {@link Crouton} should be created with.
*/
private Crouton(Activity activity, CharSequence text, Style style) {
if ((activity == null) || (text == null) || (style == null)) {
throw new IllegalArgumentException("Null parameters are not accepted");
}
this.activity = activity;
this.text = text;
this.style = style;
this.customView = null;
}
/**
* Creates the {@link Crouton}.
*
* @param activity
* The {@link Activity} that the {@link Crouton} should be attached
* to.
* @param customView
* The custom {@link View} to display
*/
private Crouton(Activity activity, View customView) {
if ((activity == null) || (customView == null)) {
throw new IllegalArgumentException("Null parameters are not accepted");
}
this.activity = activity;
this.customView = customView;
this.style = new Style.Builder().build();
this.text = null;
}
/**
* Creates a {@link Crouton} with provided text and style for a given
* activity.
*
* @param activity
* The {@link Activity} that the {@link Crouton} should be attached
* to.
* @param text
* The text you want to display.
* @param style
* The style that this {@link Crouton} should be created with.
* @return The created {@link Crouton}.
*/
public static Crouton makeText(Activity activity, CharSequence text, Style style) {
return new Crouton(activity, text, style);
}
/**
* Creates a {@link Crouton} with provided text-resource and style for a given
* activity.
*
* @param activity
* The {@link Activity} that the {@link Crouton} should be attached
* to.
* @param textResourceId
* The resource id of the text you want to display.
* @param style
* The style that this {@link Crouton} should be created with.
* @return The created {@link Crouton}.
*/
public static Crouton makeText(Activity activity, int textResourceId, Style style) {
return makeText(activity, activity.getString(textResourceId), style);
}
/**
* Creates a {@link Crouton} with provided text-resource and style for a given
* activity.
*
* @param activity
* The {@link Activity} that the {@link Crouton} should be attached
* to.
* @param customView
* The custom {@link View} to display
* @return The created {@link Crouton}.
*/
public static Crouton make(Activity activity, View customView) {
return new Crouton(activity, customView);
}
/**
* Creates a {@link Crouton} with provided text and style for a given activity
* and displays it directly.
*
* @param activity
* The {@link android.app.Activity} that the {@link Crouton} should
* be attached to.
* @param text
* The text you want to display.
* @param style
* The style that this {@link Crouton} should be created with.
*
*/
public static void showText(Activity activity, CharSequence text, Style style) {
makeText(activity, text, style).show();
}
/**
* Creates a {@link Crouton} with provided text and style for a given activity
* and displays it directly.
*
* @param activity
* The {@link android.app.Activity} that the {@link Crouton} should
* be attached to.
* @param customView
* The custom {@link View} to display
*
*/
public static void show(Activity activity, View customView) {
make(activity, customView).show();
}
/**
* Creates a {@link Crouton} with provided text-resource and style for a given
* activity and displays it directly.
*
* @param activity
* The {@link Activity} that the {@link Crouton} should be attached
* to.
* @param textResourceId
* The resource id of the text you want to display.
* @param style
* The style that this {@link Crouton} should be created with.
*
*/
public static void showText(Activity activity, int textResourceId, Style style) {
showText(activity, activity.getString(textResourceId), style);
}
/**
* Cancels all queued {@link Crouton}s. If there is a {@link Crouton}
* displayed currently, it will be the last one displayed.
*/
public static void cancelAllCroutons() {
Manager.getInstance().clearCroutonQueue();
}
/**
* Clears (and removes from {@link Activity}'s content view, if necessary) all
* croutons for the provided activity
*
* @param activity
* - The {@link} Activity to clear the croutons for
*/
public static void clearCroutonsForActivity(Activity activity) {
Manager.getInstance().clearCroutonsForActivity(activity);
}
/**
* Cancels a {@link Crouton} immediately.
*/
public void cancel() {
Manager manager = Manager.getInstance();
manager.removeCroutonImmediately(this);
}
/**
* Displays the {@link Crouton}. If there's another {@link Crouton} visible at
* the time, this {@link Crouton} will be displayed afterwards.
*/
public void show() {
Manager.getInstance().add(this);
}
/**
* @return <code>true</code> if the {@link Crouton} is being displayed, else
* <code>false</code>.
*/
boolean isShowing() {
return (activity != null) && (croutonView != null) && (croutonView.getParent() != null);
}
/**
* Removes the activity reference this {@link Crouton} is holding
*/
void detachActivity() {
activity = null;
}
/**
* @return the style
*/
Style getStyle() {
return style;
}
/**
* @return the activity
*/
Activity getActivity() {
return activity;
}
/**
* @return the text
*/
CharSequence getText() {
return text;
}
/**
* @return the view
*/
View getView() {
// return the custom view if one exists
if (this.customView != null) {
return this.customView;
}
// if already setup return the view
if (this.croutonView == null) {
initializeCroutonView();
}
return croutonView;
}
private void initializeCroutonView() {
Resources resources = this.activity.getResources();
// create outer frame
this.croutonView = new FrameLayout(this.activity);
int height = this.style.heightInPixels;
// if a height dimension has been set, this will overwrite any height in
// pixels
if (this.style.heightDimensionResId > 0) {
height = resources.getDimensionPixelSize(this.style.heightDimensionResId);
}
this.croutonView.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, height));
// set background
if (this.style.backgroundColorValue != -1) {
this.croutonView.setBackgroundColor(this.style.backgroundColorValue);
} else {
this.croutonView.setBackgroundColor(resources.getColor(this.style.backgroundColorResourceId));
}
// set the background drawable if set. This will override the background
// color.
if (this.style.backgroundDrawableResourceId != 0) {
Bitmap background = BitmapFactory.decodeResource(resources, this.style.backgroundDrawableResourceId);
BitmapDrawable drawable = new BitmapDrawable(resources, background);
if (this.style.isTileEnabled) {
drawable.setTileModeXY(Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
}
this.croutonView.setBackgroundDrawable(drawable);
}
// create content view
RelativeLayout contentView = new RelativeLayout(this.activity);
contentView.setLayoutParams(new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT));
// set padding
int padding = this.style.paddingInPixels;
// if a padding dimension has been set, this will overwrite any padding
// in pixels
if (this.style.paddingDimensionResId > 0) {
padding = resources.getDimensionPixelSize(this.style.paddingDimensionResId);
}
contentView.setPadding(padding, padding, padding, padding);
// only setup image if one is requested
ImageView image = null;
if ((this.style.imageDrawable != null) || (this.style.imageResId != 0)) {
image = new ImageView(this.activity);
image.setId(IMAGE_ID);
image.setAdjustViewBounds(true);
image.setScaleType(this.style.imageScaleType);
// set the image drawable if not null
if (this.style.imageDrawable != null) {
image.setImageDrawable(this.style.imageDrawable);
}
// set the image resource if not 0. This will overwrite the drawable
// if both are set
if (this.style.imageResId != 0) {
image.setImageResource(this.style.imageResId);
}
RelativeLayout.LayoutParams imageParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT,
RelativeLayout.LayoutParams.WRAP_CONTENT);
imageParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT, RelativeLayout.TRUE);
imageParams.addRule(RelativeLayout.CENTER_VERTICAL, RelativeLayout.TRUE);
contentView.addView(image, imageParams);
}
TextView text = new TextView(this.activity);
text.setId(TEXT_ID);
text.setText(this.text);
text.setTypeface(Typeface.DEFAULT_BOLD);
text.setGravity(this.style.gravity);
// set the text color if set
if (this.style.textColorResourceId != 0) {
text.setTextColor(resources.getColor(this.style.textColorResourceId));
}
// Set the text size. If the user has set a text size and text
// appearance, the text size in the text appearance
// will override this.
if (this.style.textSize != 0) {
text.setTextSize(TypedValue.COMPLEX_UNIT_SP, this.style.textSize);
}
// Setup the shadow if requested
if (this.style.textShadowColorResId != 0) {
int textShadowColor = resources.getColor(this.style.textShadowColorResId);
float textShadowRadius = this.style.textShadowRadius;
float textShadowDx = this.style.textShadowDx;
float textShadowDy = this.style.textShadowDy;
text.setShadowLayer(textShadowRadius, textShadowDx, textShadowDy, textShadowColor);
}
// Set the text appearance
if (this.style.textAppearanceResId != 0) {
text.setTextAppearance(this.activity, this.style.textAppearanceResId);
}
RelativeLayout.LayoutParams textParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
if (image != null) {
textParams.addRule(RelativeLayout.RIGHT_OF, image.getId());
}
contentView.addView(text, textParams);
this.croutonView.addView(contentView);
}
private Animation inAnimation;
private Animation outAnimation;
public Animation getInAnimation() {
if ((this.inAnimation == null) && (this.activity != null)) {
if (getStyle().inAnimationResId > 0) {
this.inAnimation = AnimationUtils.loadAnimation(getActivity(), getStyle().inAnimationResId);
} else {
this.inAnimation = AnimationsBuilder.buildSlideInDownAnimation();
}
}
return inAnimation;
}
public Animation getOutAnimation() {
if ((this.outAnimation == null) && (this.activity != null)) {
if (getStyle().outAnimationResId > 0) {
this.outAnimation = AnimationUtils.loadAnimation(getActivity(), getStyle().outAnimationResId);
} else {
this.outAnimation = AnimationsBuilder.buildSlideOutUpAnimation();
}
}
return outAnimation;
}
}