/*
* Copyright 2012 - 2013 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 com.yixia.zi.widget.crouton;
import android.annotation.SuppressLint;
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.View.OnClickListener;
import android.view.ViewGroup;
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;
/*
* 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 android.widget.Toast}, but better.
* <p/>
* <b>Important: </b>
* Call {@link Crouton#clearCroutonsForActivity(Activity)} within
* {@link android.app.Activity#onDestroy()} 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 OnClickListener onClickListener;
private Activity activity;
private ViewGroup viewGroup;
private FrameLayout croutonView;
private Animation inAnimation;
private Animation outAnimation;
private LifecycleCallback lifecycleCallback = null;
/**
* 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.viewGroup = null;
this.text = text;
this.style = style;
this.customView = null;
}
/**
* Creates the {@link Crouton}.
*
* @param activity
* The {@link Activity} that represents the context in which the Crouton should exist.
* @param text
* The text you want to display.
* @param style
* The style that this {@link Crouton} should be created with.
* @param viewGroup
* The {@link ViewGroup} that this {@link Crouton} should be added to.
*/
private Crouton(Activity activity, CharSequence text, Style style, ViewGroup viewGroup) {
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.viewGroup = viewGroup;
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.viewGroup = null;
this.customView = customView;
this.style = new Style.Builder().build();
this.text = null;
}
/**
* Creates the {@link Crouton}.
*
* @param activity
* The {@link Activity} that represents the context in which the Crouton should exist.
* @param customView
* The custom {@link View} to display
* @param viewGroup
* The {@link ViewGroup} that this {@link Crouton} should be added to.
*/
private Crouton(Activity activity, View customView, ViewGroup viewGroup) {
if ((activity == null) || (customView == null)) {
throw new IllegalArgumentException("Null parameters are not accepted");
}
this.activity = activity;
this.customView = customView;
this.viewGroup = viewGroup;
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 and style for a given
* activity.
*
* @param activity
* The {@link Activity} that represents the context in which the Crouton should exist.
* @param text
* The text you want to display.
* @param style
* The style that this {@link Crouton} should be created with.
* @param viewGroup
* The {@link ViewGroup} that this {@link Crouton} should be added to.
*
* @return The created {@link Crouton}.
*/
public static Crouton makeText(Activity activity, CharSequence text, Style style, ViewGroup viewGroup) {
return new Crouton(activity, text, style, viewGroup);
}
/**
* Creates a {@link Crouton} with provided text and style for a given
* activity.
*
* @param activity
* The {@link Activity} that represents the context in which the Crouton should exist.
* @param text
* The text you want to display.
* @param style
* The style that this {@link Crouton} should be created with.
* @param viewGroupResId
* The resource id of the {@link ViewGroup} that this {@link Crouton} should be added to.
*
* @return The created {@link Crouton}.
*/
public static Crouton makeText(Activity activity, CharSequence text, Style style, int viewGroupResId) {
return new Crouton(activity, text, style, (ViewGroup) activity.findViewById(viewGroupResId));
}
/**
* 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 represents the context in which the Crouton should exist.
* @param textResourceId
* The resource id of the text you want to display.
* @param style
* The style that this {@link Crouton} should be created with.
* @param viewGroup
* The {@link ViewGroup} that this {@link Crouton} should be added to.
*
* @return The created {@link Crouton}.
*/
public static Crouton makeText(Activity activity, int textResourceId, Style style, ViewGroup viewGroup) {
return makeText(activity, activity.getString(textResourceId), style, viewGroup);
}
/**
* Creates a {@link Crouton} with provided text-resource and style for a given
* activity.
*
* @param activity
* The {@link Activity} that represents the context in which the Crouton should exist.
* @param textResourceId
* The resource id of the text you want to display.
* @param style
* The style that this {@link Crouton} should be created with.
* @param viewGroupResId
* The resource id of the {@link ViewGroup} that this {@link Crouton} should be added to.
*
* @return The created {@link Crouton}.
*/
public static Crouton makeText(Activity activity, int textResourceId, Style style, int viewGroupResId) {
return makeText(activity, activity.getString(textResourceId), style,
(ViewGroup) activity.findViewById(viewGroupResId));
}
/**
* 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-resource and style for a given
* activity.
*
* @param activity
* The {@link Activity} that represents the context in which the Crouton should exist.
* @param customView
* The custom {@link View} to display
* @param viewGroup
* The {@link ViewGroup} that this {@link Crouton} should be added to.
*
* @return The created {@link Crouton}.
*/
public static Crouton make(Activity activity, View customView, ViewGroup viewGroup) {
return new Crouton(activity, customView, viewGroup);
}
/**
* Creates a {@link Crouton} with provided text-resource and style for a given
* activity.
*
* @param activity
* The {@link Activity} that represents the context in which the Crouton should exist.
* @param customView
* The custom {@link View} to display
* @param viewGroupResId
* The resource id of the {@link ViewGroup} that this {@link Crouton} should be added to.
*
* @return The created {@link Crouton}.
*/
public static Crouton make(Activity activity, View customView, int viewGroupResId) {
return new Crouton(activity, customView, (ViewGroup) activity.findViewById(viewGroupResId));
}
/**
* 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 Activity} that represents the context in which the Crouton should exist.
* @param text
* The text you want to display.
* @param style
* The style that this {@link Crouton} should be created with.
* @param viewGroup
* The {@link ViewGroup} that this {@link Crouton} should be added to.
*/
public static void showText(Activity activity, CharSequence text, Style style, ViewGroup viewGroup) {
makeText(activity, text, style, viewGroup).show();
}
/**
* Creates a {@link Crouton} with provided text and style for a given activity
* and displays it directly.
*
* @param activity
* The {@link Activity} that represents the context in which the Crouton should exist.
* @param text
* The text you want to display.
* @param style
* The style that this {@link Crouton} should be created with.
* @param viewGroupResId
* The resource id of the {@link ViewGroup} that this {@link Crouton} should be added to.
*/
public static void showText(Activity activity, CharSequence text, Style style, int viewGroupResId) {
makeText(activity, text, style, (ViewGroup) activity.findViewById(viewGroupResId)).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 and style for a given activity
* and displays it directly.
*
* @param activity
* The {@link Activity} that represents the context in which the Crouton should exist.
* @param customView
* The custom {@link View} to display
* @param viewGroup
* The {@link ViewGroup} that this {@link Crouton} should be added to.
*/
public static void show(Activity activity, View customView, ViewGroup viewGroup) {
make(activity, customView, viewGroup).show();
}
/**
* Creates a {@link Crouton} with provided text and style for a given activity
* and displays it directly.
*
* @param activity
* The {@link Activity} that represents the context in which the Crouton should exist.
* @param customView
* The custom {@link View} to display
* @param viewGroupResId
* The resource id of the {@link ViewGroup} that this {@link Crouton} should be added to.
*/
public static void show(Activity activity, View customView, int viewGroupResId) {
make(activity, customView, viewGroupResId).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);
}
/**
* Creates a {@link Crouton} with provided text-resource and style for a given
* activity and displays it directly.
*
* @param activity
* The {@link Activity} that represents the context in which the Crouton should exist.
* @param textResourceId
* The resource id of the text you want to display.
* @param style
* The style that this {@link Crouton} should be created with.
* @param viewGroup
* The {@link ViewGroup} that this {@link Crouton} should be added to.
*/
public static void showText(Activity activity, int textResourceId, Style style, ViewGroup viewGroup) {
showText(activity, activity.getString(textResourceId), style, viewGroup);
}
/**
* Creates a {@link Crouton} with provided text-resource and style for a given
* activity and displays it directly.
*
* @param activity
* The {@link Activity} that represents the context in which the Crouton should exist.
* @param textResourceId
* The resource id of the text you want to display.
* @param style
* The style that this {@link Crouton} should be created with.
* @param viewGroupResId
* The resource id of the {@link ViewGroup} that this {@link Crouton} should be added to.
*/
public static void showText(Activity activity, int textResourceId, Style style, int viewGroupResId) {
showText(activity, activity.getString(textResourceId), style, viewGroupResId);
}
/**
* Allows hiding of a previously displayed {@link Crouton}.
* @param crouton The {@link Crouton} you want to hide.
*/
public static void hide(Crouton crouton) {
Manager.getInstance().removeCrouton(crouton);
}
/**
* 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);
}
public Animation getInAnimation() {
if ((null == this.inAnimation) && (null != this.activity)) {
if (getStyle().inAnimationResId > 0) {
this.inAnimation = AnimationUtils.loadAnimation(getActivity(), getStyle().inAnimationResId);
} else {
this.inAnimation = DefaultAnimationsBuilder.buildDefaultSlideInDownAnimation();
}
}
return inAnimation;
}
public Animation getOutAnimation() {
if ((null == this.outAnimation) && (null != this.activity)) {
if (getStyle().outAnimationResId > 0) {
this.outAnimation = AnimationUtils.loadAnimation(getActivity(), getStyle().outAnimationResId);
} else {
this.outAnimation = DefaultAnimationsBuilder.buildDefaultSlideOutUpAnimation();
}
}
return outAnimation;
}
/**
* @param lifecycleCallback
* Callback object for notable events in the life of a Crouton.
*/
public void setLifecycleCallback(LifecycleCallback lifecycleCallback) {
this.lifecycleCallback = lifecycleCallback;
}
/**
* Convenience method to get the license text for embedding within your application.
* @return
* The license text.
*/
public String getLicenseText() {
return "This application uses the Crouton library.\n\n" +
"Copyright 2012 - 2013 Benjamin Weiss \n" +
"Copyright 2012 Neofonie Mobile GmbH\n" +
"\n" +
"Licensed under the Apache License, Version 2.0 (the \"License\");\n" +
"you may not use this file except in compliance with the License.\n" +
"You may obtain a copy of the License at\n" +
"\n" +
" http://www.apache.org/licenses/LICENSE-2.0\n" +
"\n" +
"Unless required by applicable law or agreed to in writing, software\n" +
"distributed under the License is distributed on an \"AS IS\" BASIS,\n" +
"WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" +
"See the License for the specific language governing permissions and\n" +
"limitations under the License.";
}
/**
* Allows setting of an {@link OnClickListener} directly to a {@link Crouton} without having to use a custom view.
* @param onClickListener The {@link OnClickListener} to set.
* @return this {@link Crouton}.
*/
public Crouton setOnClickListener(OnClickListener onClickListener){
this.onClickListener = onClickListener;
return this;
}
/**
* @return <code>true</code> if the {@link Crouton} is being displayed, else
* <code>false</code>.
*/
boolean isShowing() {
return (null != activity) && (null != croutonView) && (null != croutonView.getParent());
}
/**
* Removes the activity reference this {@link Crouton} is holding
*/
void detachActivity() {
activity = null;
}
/**
* Removes the viewGroup reference this {@link Crouton} is holding
*/
void detachViewGroup() {
viewGroup = null;
}
/**
* Removes the lifecycleCallback reference this {@link Crouton} is holding
*/
void detachLifecycleCallback() {
lifecycleCallback = null;
}
/**
* @return the lifecycleCallback
*/
LifecycleCallback getLifecycleCallback() {
return lifecycleCallback;
}
/**
* @return the style
*/
Style getStyle() {
return style;
}
/**
* @return the activity
*/
Activity getActivity() {
return activity;
}
/**
* @return the viewGroup
*/
ViewGroup getViewGroup() {
return viewGroup;
}
/**
* @return the text
*/
CharSequence getText() {
return text;
}
/**
* @return the view
*/
View getView() {
// return the custom view if one exists
if (null != this.customView) {
return this.customView;
}
// if already setup return the view
if (null == this.croutonView) {
initializeCroutonView();
}
return croutonView;
}
private void initializeCroutonView() {
Resources resources = this.activity.getResources();
this.croutonView = initializeCroutonViewGroup(resources);
// create content view
RelativeLayout contentView = initializeContentView(resources);
this.croutonView.addView(contentView);
}
@SuppressWarnings("deprecation")
@SuppressLint("NewApi")
private FrameLayout initializeCroutonViewGroup(Resources resources) {
FrameLayout croutonView = new FrameLayout(this.activity);
if(null != onClickListener)
croutonView.setOnClickListener(onClickListener);
final int height;
if (this.style.heightDimensionResId > 0) {
height = resources.getDimensionPixelSize(this.style.heightDimensionResId);
} else {
height = this.style.heightInPixels;
}
final int width;
if (this.style.widthDimensionResId > 0) {
width = resources.getDimensionPixelSize(this.style.widthDimensionResId);
} else {
width = this.style.widthInPixels;
}
croutonView.setLayoutParams(
new FrameLayout.LayoutParams(width != 0 ? width : FrameLayout.LayoutParams.MATCH_PARENT, height));
// set background
if (this.style.backgroundColorValue != -1) {
croutonView.setBackgroundColor(this.style.backgroundColorValue);
} else {
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);
}
int sdk = android.os.Build.VERSION.SDK_INT;
if(sdk < android.os.Build.VERSION_CODES.JELLY_BEAN) {
croutonView.setBackgroundDrawable(drawable);
} else {
croutonView.setBackground(drawable);
}
}
return croutonView;
}
private RelativeLayout initializeContentView(final Resources resources) {
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 ((null != this.style.imageDrawable) || (0 != this.style.imageResId)) {
image = initializeImageView();
contentView.addView(image, image.getLayoutParams());
}
TextView text = initializeTextView(resources);
RelativeLayout.LayoutParams textParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT,
RelativeLayout.LayoutParams.WRAP_CONTENT);
if (null != image) {
textParams.addRule(RelativeLayout.RIGHT_OF, image.getId());
}
contentView.addView(text, textParams);
return contentView;
}
private TextView initializeTextView(final Resources resources) {
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) {
initializeTextViewShadow(resources, text);
}
// Set the text appearance
if (this.style.textAppearanceResId != 0) {
text.setTextAppearance(this.activity, this.style.textAppearanceResId);
}
return text;
}
private void initializeTextViewShadow(final Resources resources, final TextView text) {
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);
}
private ImageView initializeImageView() {
ImageView image;
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 (null != this.style.imageDrawable) {
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);
image.setLayoutParams(imageParams);
return image;
}
@Override
public String toString() {
return "Crouton{" +
"text=" + text +
", style=" + style +
", customView=" + customView +
", activity=" + activity +
", viewGroup=" + viewGroup +
", croutonView=" + croutonView +
", inAnimation=" + inAnimation +
", outAnimation=" + outAnimation +
", lifecycleCallback=" + lifecycleCallback +
'}';
}
}