();
- l.addAll(stackViews);
- l.addAll(collapsedViews);
- return l;
- }
-
- public boolean isCollapsed() {
- return isCollapsed;
- }
-
- public boolean isCollapsible() {
- return isCollapsible;
- }
-
-
- private Drawable getStackDrawable(int i){
- while(i >= cacheStackDrawables.size()) {
- Drawable d = getResources().getDrawable(stackDrawable);
- if(Build.VERSION_CODES.FROYO <= Build.VERSION.SDK_INT) {
- d = d.mutate();
- }
- cacheStackDrawables.add(d);
- }
- return cacheStackDrawables.get(i);
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int w = 0;
- int h = 0;
- int prevBot = 0;
- boolean first = true;
- int cacheStack = 0;
- if (stackViews != null) {
- for (BaseMapWidget c : stackViews) {
- cacheStack++;
- if (c.getVisibility() != View.GONE) {
- c.setBackgroundDrawable(first ? topDrawable : getStackDrawable(cacheStack));
- first = false;
- c.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
- w = Math.max(w, c.getMeasuredWidth());
- if (h > 0) {
- h -= prevBot;
- } else {
- h += c.getPaddingTop();
- }
- h += c.getMeasuredHeight();
- prevBot = c.getPaddingBottom();
- }
- }
- isCollapsible = false;
- for (BaseMapWidget c : collapsedViews) {
- cacheStack++;
- if (c.getVisibility() != View.GONE) {
- isCollapsible = true;
- if (!isCollapsed) {
- c.setBackgroundDrawable(first ? topDrawable : getStackDrawable(cacheStack));
- first = false;
- c.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
- w = Math.max(w, c.getMeasuredWidth());
- h -= c.getPaddingBottom();
- if (h > 0) {
- h -= prevBot;
- }
- h += c.getMeasuredHeight();
- prevBot = c.getPaddingBottom();
- } else {
- if (h == 0) {
- // measure one of the figure if it is collapsed and no top elements
- c.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
- h += c.getPaddingTop();
- }
- }
-
- }
- }
- if (isCollapsible) {
- h += expandView.getDrawable().getMinimumHeight();
- w = Math.max(w, expandView.getDrawable().getMinimumWidth());
- }
- }
- setMeasuredDimension(w, h);
- }
-
- // magic constant (should be removed when image will be recropped)
- private final static int MAGIC_CONSTANT_STACK = 8;
- private int shadowColor;
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- int y = 0;
- int cw = right - left;
- for (View c : stackViews) {
- if (c.getVisibility() != View.GONE) {
- if (y == 0) {
- y += c.getPaddingTop();
- }
- y -= MAGIC_CONSTANT_STACK;
- c.layout(0, y, cw, y + c.getMeasuredHeight());
- y += c.getMeasuredHeight();
- y -= c.getPaddingBottom();
- }
- }
-
- for (View c : collapsedViews) {
- if (!isCollapsed) {
- if (c.getVisibility() != View.GONE) {
- if (y == 0) {
- y += c.getPaddingTop();
- }
- y -= MAGIC_CONSTANT_STACK;
- c.layout(0, y, cw, y + c.getMeasuredHeight());
- y += c.getMeasuredHeight();
- y -= c.getPaddingBottom();
- }
- } else {
- c.layout(0, 0, 0, 0);
- if(y == 0){
- y += c.getPaddingTop();
- }
- }
- }
-
- if (isCollapsible) {
- y -= MAGIC_CONSTANT_STACK;
- expandView.setVisibility(VISIBLE);
- int w = expandView.getDrawable().getMinimumWidth();
- int h = expandView.getDrawable().getMinimumHeight();
- expandView.layout((cw - w) / 2, y, (cw + w) / 2, y + h);
- } else {
- expandView.setVisibility(GONE);
- }
- }
-
- public void setShadowColor(int shadowColor) {
- this.shadowColor = shadowColor;
- for(BaseMapWidget c : stackViews) {
- c.setShadowColor(shadowColor);
- }
- for(BaseMapWidget c : collapsedViews) {
- c.setShadowColor(shadowColor);
- }
-
- }
-
-}
\ No newline at end of file
diff --git a/OsmAnd/src/net/osmand/plus/views/mapwidgets/TextInfoWidget.java b/OsmAnd/src/net/osmand/plus/views/mapwidgets/TextInfoWidget.java
index d323b52f57..0b9fb5d085 100644
--- a/OsmAnd/src/net/osmand/plus/views/mapwidgets/TextInfoWidget.java
+++ b/OsmAnd/src/net/osmand/plus/views/mapwidgets/TextInfoWidget.java
@@ -1,115 +1,159 @@
package net.osmand.plus.views.mapwidgets;
-import net.osmand.plus.views.MapInfoLayer;
+import net.osmand.plus.R;
import net.osmand.plus.views.OsmandMapLayer.DrawSettings;
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Paint;
+import android.app.Activity;
+import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.ImageView;
+import android.widget.TextView;
-public class TextInfoWidget extends BaseMapWidget {
+public class TextInfoWidget {
- String text;
- Paint textPaint;
- String subtext;
- Paint subtextPaint;
- int leftMargin = 0;
- private Drawable imageDrawable;
- private float scaleCoefficient;
+ private String contentTitle;
+
+ private View view;
+ private ImageView imageView;
+ private TextView textView;
+ private TextView smallTextView;
+ private ImageView topImageView;
- public TextInfoWidget(Context ctx, int leftMargin, Paint textPaint, Paint subtextPaint) {
- super(ctx);
- scaleCoefficient = MapInfoLayer.scaleCoefficient;
- this.leftMargin = leftMargin;
- this.textPaint = textPaint;
- this.subtextPaint = subtextPaint;
+ private boolean explicitlyVisible;
+
+
+ public TextInfoWidget(Activity activity) {
+ view = activity.getLayoutInflater().inflate(R.layout.map_hud_widget, null);
+ topImageView = (ImageView) view.findViewById(R.id.widget_top_icon);
+ imageView = (ImageView) view.findViewById(R.id.widget_icon);
+ textView = (TextView) view.findViewById(R.id.widget_text);
+ smallTextView = (TextView) view.findViewById(R.id.widget_text_small);
+ }
+
+ public View getView() {
+ return view;
}
public void setImageDrawable(Drawable imageDrawable) {
- this.imageDrawable = imageDrawable;
+ setImageDrawable(imageDrawable, false);
}
- public Drawable getImageDrawable() {
- return imageDrawable;
+
+ public void setImageDrawable(Drawable imageDrawable, boolean gone) {
+ if(imageDrawable != null) {
+ imageView.setImageDrawable(imageDrawable);
+ imageView.setVisibility(View.VISIBLE);
+ } else {
+ imageView.setVisibility(gone ? View.GONE : View.INVISIBLE);
+ }
+ imageView.invalidate();
}
-
- @Override
- protected void onWLayout(int w, int h) {
- if (imageDrawable != null) {
- // Unknown reason to add 3*scaleCoefficient
- imageDrawable.setBounds(0, (int) (3 * scaleCoefficient), imageDrawable.getMinimumWidth(), imageDrawable.getMinimumHeight()
- + (int) (3 * scaleCoefficient));
+
+ public void setTopImageDrawable(Drawable imageDrawable, boolean gone) {
+ if(imageDrawable != null) {
+ topImageView.setImageDrawable(imageDrawable);
+ topImageView.setVisibility(View.VISIBLE);
+ } else {
+ topImageView.setVisibility(gone ? View.GONE : View.INVISIBLE);
+ }
+ topImageView.invalidate();
+ }
+
+
+
+
+ public void setContentDescription(CharSequence text) {
+ if (contentTitle != null) {
+ view.setContentDescription(contentTitle + " " + text); //$NON-NLS-1$
+ } else {
+ view.setContentDescription(text);
}
}
+ public void setContentTitle(int messageId) {
+ setContentTitle(view.getContext().getString(messageId));
+ }
+ public void setContentTitle(String text) {
+ contentTitle = text;
+ view.setContentDescription(text);
+ }
+
public void setText(String text, String subtext) {
- this.text = text;
- this.subtext = subtext;
+ setTextNoUpdateVisibility(text, subtext);
+ updateVisibility(text != null);
+ }
+
+ protected void setTextNoUpdateVisibility(String text, String subtext) {
if (text != null) {
- if (subtext != null)
+ if (subtext != null) {
setContentDescription(text + " " + subtext); //$NON-NLS-1$
- else setContentDescription(text);
- } else {
+ } else {
+ setContentDescription(text);
+ }
+ } else if(subtext != null){
setContentDescription(subtext);
}
- if(this.text != null && this.text.length() > 7) {
- this.text = this.text.substring(0, 6) +"..";
+// if(this.text != null && this.text.length() > 7) {
+// this.text = this.text.substring(0, 6) +"..";
+// }
+ if(text == null) {
+ textView.setText("");
+ } else {
+ textView.setText(text);
}
- updateVisibility(text != null);
- requestLayout();
- invalidate();
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- // ignore attributes
- int w = 0;
- int h = 0;
- if (text != null) {
- if (imageDrawable != null) {
- w += imageDrawable.getMinimumWidth() + 2 * scaleCoefficient;
- }
- w += leftMargin;
- w += textPaint.measureText(text);
- if (subtext != null) {
- w += subtextPaint.measureText(subtext) + 2 * scaleCoefficient;
- }
-
- h = (int) (5 * scaleCoefficient + Math.max(textPaint.getTextSize(), subtextPaint.getTextSize()));
- if (imageDrawable != null) {
- h = Math.max(h, (int) (imageDrawable.getMinimumHeight()));
- }
- }
- setWDimensions(w, h);
- }
-
- @Override
- protected void onDraw(Canvas cv) {
- super.onDraw(cv);
- if (isVisible()) {
- int margin = 0;
- if (imageDrawable != null) {
- imageDrawable.draw(cv);
- margin = (int) (imageDrawable.getBounds().width() + 2 * scaleCoefficient);
- }
- margin += leftMargin;
- drawShadowText(cv, text, margin, getWHeight() - 3 * scaleCoefficient, textPaint);
- if (subtext != null) {
- drawShadowText(cv, subtext, margin + 2 * scaleCoefficient + textPaint.measureText(text), getWHeight() - 3
- * scaleCoefficient, subtextPaint);
- }
+ if(subtext == null) {
+ smallTextView.setText("");
+ } else {
+ smallTextView.setText(subtext);
}
}
-
- public boolean isVisible() {
- return text != null && (text.length() > 0 || subtext != null);
- }
-
- @Override
- public boolean updateInfo(DrawSettings drawSettings) {
+ protected boolean updateVisibility(boolean visible) {
+ if (visible != (view.getVisibility() == View.VISIBLE)) {
+ if (visible) {
+ view.setVisibility(View.VISIBLE);
+ } else {
+ view.setVisibility(View.GONE);
+ }
+ view.invalidate();
+ return true;
+ }
return false;
}
+ public boolean isVisible() {
+ return view.getVisibility() == View.VISIBLE && view.getParent() != null;
+ }
+
+ public boolean updateInfo(DrawSettings drawSettings) {
+ return false;
+ }
+
+ public void setOnClickListener(OnClickListener onClickListener) {
+ view.setOnClickListener(onClickListener);
+ }
+
+ public void setExplicitlyVisible(boolean explicitlyVisible) {
+ this.explicitlyVisible = explicitlyVisible;
+ }
+
+ public boolean isExplicitlyVisible() {
+ return explicitlyVisible;
+ }
+
+ public void updateTextColor(int textColor, int textShadowColor, boolean bold, int rad) {
+ updateTextColor(smallTextView, textColor, textShadowColor, bold, rad);
+ updateTextColor(textView, textColor, textShadowColor, bold, rad);
+ }
+
+ private void updateTextColor(TextView tv, int textColor, int textShadowColor, boolean textBold, int rad) {
+ tv.setTextColor(textColor);
+ tv.setShadowLayer(rad, 0, 0, textShadowColor);
+ tv.setTypeface(Typeface.DEFAULT, textBold ? Typeface.BOLD : Typeface.NORMAL);
+ }
+
+
+
}
\ No newline at end of file
diff --git a/OsmAnd/src/net/osmand/plus/views/mapwidgets/UpdateableWidget.java b/OsmAnd/src/net/osmand/plus/views/mapwidgets/UpdateableWidget.java
deleted file mode 100644
index 1b099c90e4..0000000000
--- a/OsmAnd/src/net/osmand/plus/views/mapwidgets/UpdateableWidget.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package net.osmand.plus.views.mapwidgets;
-
-import net.osmand.plus.views.OsmandMapLayer.DrawSettings;
-
-
-public interface UpdateableWidget {
-
- public boolean updateInfo(DrawSettings drawSettings);
-
-
-}
\ No newline at end of file
diff --git a/eclipse-compile/fab/.classpath b/eclipse-compile/fab/.classpath
new file mode 100644
index 0000000000..c6e9dc9f1d
--- /dev/null
+++ b/eclipse-compile/fab/.classpath
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/eclipse-compile/fab/.gitignore b/eclipse-compile/fab/.gitignore
new file mode 100644
index 0000000000..e614fbbef9
--- /dev/null
+++ b/eclipse-compile/fab/.gitignore
@@ -0,0 +1,2 @@
+bin
+gen
diff --git a/eclipse-compile/fab/.project b/eclipse-compile/fab/.project
new file mode 100644
index 0000000000..b53f0578e5
--- /dev/null
+++ b/eclipse-compile/fab/.project
@@ -0,0 +1,39 @@
+
+
+ ShellFab
+
+
+
+
+
+ com.android.ide.eclipse.adt.ResourceManagerBuilder
+
+
+
+
+ com.android.ide.eclipse.adt.PreCompilerBuilder
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ com.android.ide.eclipse.adt.ApkBuilder
+
+
+
+
+ org.eclipse.xtext.ui.shared.xtextBuilder
+
+
+
+
+
+ com.android.ide.eclipse.adt.AndroidNature
+ org.eclipse.jdt.core.javanature
+ org.eclipse.xtext.ui.shared.xtextNature
+
+
diff --git a/eclipse-compile/fab/AndroidManifest.xml b/eclipse-compile/fab/AndroidManifest.xml
new file mode 100644
index 0000000000..7e0d38def5
--- /dev/null
+++ b/eclipse-compile/fab/AndroidManifest.xml
@@ -0,0 +1,21 @@
+
+
+
diff --git a/eclipse-compile/fab/java/com/software/shell/fab/ActionButton.java b/eclipse-compile/fab/java/com/software/shell/fab/ActionButton.java
new file mode 100644
index 0000000000..0da0da5d0c
--- /dev/null
+++ b/eclipse-compile/fab/java/com/software/shell/fab/ActionButton.java
@@ -0,0 +1,1432 @@
+/*
+ * Copyright 2015 Shell Software Inc.
+ *
+ * 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.
+ *
+ * File created: 2015-01-17 10:39:13
+ */
+
+package com.software.shell.fab;
+
+import android.annotation.SuppressLint;
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.*;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewOutlineProvider;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+
+/**
+ * This class represents a Action Button, which is used in
+ * Material Design
+ *
+ * @author Vladislav
+ * @version 1.0.3
+ * @since 1.0.0
+ */
+public class ActionButton extends View {
+
+ /**
+ * Logging tag
+ */
+ private static final String LOG_TAG = "FAB";
+
+ /**
+ * Action Button type
+ */
+ private Type type = Type.DEFAULT;
+
+ /**
+ * Action Button state
+ */
+ private State state = State.NORMAL;
+
+ /**
+ * Action Button color in {@link State#NORMAL} state
+ */
+ private int buttonColor = Color.LTGRAY;
+
+ /**
+ * Action Button color in {@link State#PRESSED} state
+ */
+ private int buttonColorPressed = Color.DKGRAY;
+
+ /**
+ * Shadow radius expressed in actual pixels
+ */
+ private float shadowRadius = MetricsConverter.dpToPx(getContext(), 2.0f);
+
+ /**
+ * Shadow X-axis offset expressed in actual pixels
+ */
+ private float shadowXOffset = MetricsConverter.dpToPx(getContext(), 1.0f);
+
+ /**
+ * Shadow Y-axis offset expressed in actual pixels
+ */
+ private float shadowYOffset = MetricsConverter.dpToPx(getContext(), 1.5f);
+
+ /**
+ * Shadow color
+ */
+ private int shadowColor = Color.parseColor("#757575");
+
+ /**
+ * Stroke width
+ */
+ private float strokeWidth = 0.0f;
+
+ /**
+ * Stroke color
+ */
+ private int strokeColor = Color.BLACK;
+
+ /**
+ * Action Button image drawable centered inside the view
+ */
+ private Drawable image;
+
+ /**
+ * Size of the Action Button image inside the view
+ */
+ private float imageSize = MetricsConverter.dpToPx(getContext(), 24.0f);
+
+ /**
+ * Animation, which is used while showing Action Button
+ */
+ private Animation showAnimation;
+
+ /**
+ * Animation, which is used while hiding or dismissing Action Button
+ */
+ private Animation hideAnimation;
+
+ /**
+ * {@link android.graphics.Paint}, which is used for drawing the elements of
+ * Action Button
+ */
+ protected final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
+
+ /**
+ * Creates an instance of the Action Button
+ *
+ * Used when instantiating Action Button programmatically
+ *
+ * @param context context the view is running in
+ */
+ public ActionButton(Context context) {
+ super(context);
+ initActionButton();
+ }
+
+ /**
+ * Creates an instance of the Action Button
+ *
+ * Used when inflating the declared Action Button
+ * within XML resource
+ *
+ * @param context context the view is running in
+ * @param attrs attributes of the XML tag that is inflating the view
+ */
+ public ActionButton(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ initActionButton(context, attrs, 0, 0);
+ }
+
+ /**
+ * Creates an instance of the Action Button
+ *
+ * Used when inflating the declared Action Button
+ * within XML resource
+ *
+ * @param context context the view is running in
+ * @param attrs attributes of the XML tag that is inflating the view
+ * @param defStyleAttr attribute in the current theme that contains a
+ * reference to a style resource that supplies default values for
+ * the view. Can be 0 to not look for defaults
+ */
+ public ActionButton(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ initActionButton(context, attrs, defStyleAttr, 0);
+ }
+
+ /**
+ * Creates an instance of the Action Button
+ *
+ * Used when inflating the declared Action Button
+ * within XML resource
+ *
+ * Might be called if target API is LOLLIPOP (21) and higher
+ *
+ * @param context context the view is running in
+ * @param attrs attributes of the XML tag that is inflating the view
+ * @param defStyleAttr attribute in the current theme that contains a
+ * reference to a style resource that supplies default values for
+ * the view. Can be 0 to not look for defaults
+ * @param defStyleRes resource identifier of a style resource that
+ * supplies default values for the view, used only if
+ * defStyleAttr is 0 or can not be found in the theme. Can be 0
+ * to not look for defaults
+ */
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+ public ActionButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ initActionButton(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ /**
+ * Initializes the Action Button, which is created programmatically
+ */
+ private void initActionButton() {
+ initLayerType();
+ Log.v(LOG_TAG, "Action Button initialized");
+ }
+
+ /**
+ * Initializes the Action Button, which is declared within XML resource
+ *
+ * Makes calls to different initialization methods for parameters initialization.
+ * For those parameters, which are not declared in the XML resource,
+ * the default value will be used
+ *
+ * @param context context the view is running in
+ * @param attrs attributes of the XML tag that is inflating the view
+ * @param defStyleAttr attribute in the current theme that contains a
+ * reference to a style resource that supplies default values for
+ * the view. Can be 0 to not look for defaults
+ * @param defStyleRes resource identifier of a style resource that
+ * supplies default values for the view, used only if
+ * defStyleAttr is 0 or can not be found in the theme. Can be 0
+ * to not look for defaults
+ */
+ private void initActionButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ initLayerType();
+ TypedArray attributes = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ActionButton,
+ defStyleAttr, defStyleRes);
+ try {
+ initType(attributes);
+ initButtonColor(attributes);
+ initButtonColorPressed(attributes);
+ initShadowRadius(attributes);
+ initShadowXOffset(attributes);
+ initShadowYOffset(attributes);
+ initShadowColor(attributes);
+ initStrokeWidth(attributes);
+ initStrokeColor(attributes);
+ initImage(attributes);
+ initImageSize(attributes);
+ initShowAnimation(attributes);
+ initHideAnimation(attributes);
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Unable to read attr", e);
+ } finally {
+ attributes.recycle();
+ }
+ Log.v(LOG_TAG, "Action Button initialized");
+ }
+
+ /**
+ * Initializes the layer type needed for shadows drawing
+ *
+ * Might be called if target API is HONEYCOMB (11) and higher
+ */
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ private void initLayerType() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+ setLayerType(LAYER_TYPE_SOFTWARE, paint);
+ Log.v(LOG_TAG, "Layer type initialized");
+ }
+ }
+
+ /**
+ * Initializes the {@link Type} of Action Button
+ *
+ * @param attrs attributes of the XML tag that is inflating the view
+ */
+ private void initType(TypedArray attrs) {
+ if (attrs.hasValue(R.styleable.ActionButton_type)) {
+ final int id = attrs.getInteger(R.styleable.ActionButton_type, type.getId());
+ type = Type.forId(id);
+ Log.v(LOG_TAG, "Initialized type: " + getType());
+ }
+ }
+
+ /**
+ * Initializes the Action Button color for
+ * {@link #state} set to {@link State#NORMAL}
+ *
+ * @param attrs attributes of the XML tag that is inflating the view
+ */
+ private void initButtonColor(TypedArray attrs) {
+ if (attrs.hasValue(R.styleable.ActionButton_button_color)) {
+ buttonColor = attrs.getColor(R.styleable.ActionButton_button_color, buttonColor);
+ Log.v(LOG_TAG, "Initialized button color: " + getButtonColor());
+ }
+ }
+
+ /**
+ * Initializes the Action Button color for
+ * {@link #state} set to {@link State#PRESSED}
+ *
+ * @param attrs attributes of the XML tag that is inflating the view
+ */
+ private void initButtonColorPressed(TypedArray attrs) {
+ if (attrs.hasValue(R.styleable.ActionButton_button_colorPressed)) {
+ buttonColorPressed = attrs.getColor(R.styleable.ActionButton_button_colorPressed,
+ buttonColorPressed);
+ Log.v(LOG_TAG, "Initialized button color pressed: " + getButtonColorPressed());
+ }
+ }
+
+ /**
+ * Initializes the shadow radius
+ *
+ * @param attrs attributes of the XML tag that is inflating the view
+ */
+ private void initShadowRadius(TypedArray attrs) {
+ if (attrs.hasValue(R.styleable.ActionButton_shadow_radius)) {
+ shadowRadius = attrs.getDimension(R.styleable.ActionButton_shadow_radius, shadowRadius);
+ Log.v(LOG_TAG, "Initialized shadow radius: " + getShadowRadius());
+ }
+ }
+
+ /**
+ * Initializes the shadow X-axis offset
+ *
+ * @param attrs attributes of the XML tag that is inflating the view
+ */
+ private void initShadowXOffset(TypedArray attrs) {
+ if (attrs.hasValue(R.styleable.ActionButton_shadow_xOffset)) {
+ shadowXOffset = attrs.getDimension(R.styleable.ActionButton_shadow_xOffset, shadowXOffset);
+ Log.v(LOG_TAG, "Initialized shadow X-axis offset: " + getShadowXOffset());
+ }
+ }
+
+ /**
+ * Initializes the shadow Y-axis offset
+ *
+ * @param attrs attributes of the XML tag that is inflating the view
+ */
+ private void initShadowYOffset(TypedArray attrs) {
+ if (attrs.hasValue(R.styleable.ActionButton_shadow_yOffset)) {
+ shadowYOffset = attrs.getDimension(R.styleable.ActionButton_shadow_yOffset, shadowYOffset);
+ Log.v(LOG_TAG, "Initialized shadow Y-axis offset: " + getShadowYOffset());
+ }
+ }
+
+ /**
+ * Initializes the shadow color
+ *
+ * @param attrs attributes of the XML tag that is inflating the view
+ */
+ private void initShadowColor(TypedArray attrs) {
+ if (attrs.hasValue(R.styleable.ActionButton_shadow_color)) {
+ shadowColor = attrs.getColor(R.styleable.ActionButton_shadow_color, shadowColor);
+ Log.v(LOG_TAG, "Initialized shadow color: " + getShadowColor());
+ }
+ }
+
+ /**
+ * Initializes the stroke width
+ *
+ * @param attrs attributes of the XML tag that is inflating the view
+ */
+ private void initStrokeWidth(TypedArray attrs) {
+ if (attrs.hasValue(R.styleable.ActionButton_stroke_width)) {
+ strokeWidth = attrs.getDimension(R.styleable.ActionButton_stroke_width, strokeWidth);
+ Log.v(LOG_TAG, "Initialized stroke width: " + getStrokeWidth());
+ }
+ }
+
+ /**
+ * Initializes the stroke color
+ *
+ * @param attrs attributes of the XML tag that is inflating the view
+ */
+ private void initStrokeColor(TypedArray attrs) {
+ if (attrs.hasValue(R.styleable.ActionButton_stroke_color)) {
+ strokeColor = attrs.getColor(R.styleable.ActionButton_stroke_color, strokeColor);
+ Log.v(LOG_TAG, "Initialized stroke color: " + getStrokeColor());
+ }
+ }
+
+ /**
+ * Initializes the animation, which is used while showing
+ * Action Button
+ *
+ * @param attrs attributes of the XML tag that is inflating the view
+ */
+ private void initShowAnimation(TypedArray attrs) {
+ if (attrs.hasValue(R.styleable.ActionButton_show_animation)) {
+ final int animResId = attrs.getResourceId(R.styleable.ActionButton_show_animation,
+ Animations.NONE.animResId);
+ showAnimation = Animations.load(getContext(), animResId);
+ Log.v(LOG_TAG, "Initialized animation on show");
+ }
+ }
+
+ /**
+ * Initializes the animation, which is used while hiding or dismissing
+ * Action Button
+ *
+ * @param attrs attributes of the XML tag that is inflating the view
+ */
+ private void initHideAnimation(TypedArray attrs) {
+ if (attrs.hasValue(R.styleable.ActionButton_hide_animation)) {
+ final int animResId = attrs.getResourceId(R.styleable.ActionButton_hide_animation,
+ Animations.NONE.animResId);
+ hideAnimation = Animations.load(getContext(), animResId);
+ Log.v(LOG_TAG, "Initialized animation on hide");
+ }
+ }
+
+ /**
+ * Initializes the image inside Action Button
+ *
+ * @param attrs attributes of the XML tag that is inflating the view
+ */
+ private void initImage(TypedArray attrs) {
+ if (attrs.hasValue(R.styleable.ActionButton_image)) {
+ image = attrs.getDrawable(R.styleable.ActionButton_image);
+ Log.v(LOG_TAG, "Initialized image");
+ }
+ }
+
+ /**
+ * Initializes the image size inside Action Button
+ *
+ * Changing the default size of the image breaks the rules of
+ * Material Design
+ *
+ * @param attrs attributes of the XML tag that is inflating the view
+ */
+ private void initImageSize(TypedArray attrs) {
+ if (attrs.hasValue(R.styleable.ActionButton_image_size)) {
+ imageSize = attrs.getDimension(R.styleable.ActionButton_image_size, imageSize);
+ Log.v(LOG_TAG, "Initialized image size: " + getImageSize());
+ }
+ }
+
+ /**
+ * Plays the {@link #showAnimation} if set
+ */
+ public void playShowAnimation() {
+ startAnimation(getShowAnimation());
+ }
+
+ /**
+ * Plays the {@link #hideAnimation} if set
+ */
+ public void playHideAnimation() {
+ startAnimation(getHideAnimation());
+ }
+
+ /**
+ * Makes the Action Button to appear and
+ * sets its visibility to {@link #VISIBLE}
+ *
+ * {@link #showAnimation} is played if set
+ */
+ public void show() {
+ if (isHidden()) {
+ playShowAnimation();
+ setVisibility(VISIBLE);
+ Log.v(LOG_TAG, "Action Button shown");
+ }
+ }
+
+ /**
+ * Makes the Action Button to disappear and
+ * sets its visibility to {@link #INVISIBLE}
+ *
+ * {@link #hideAnimation} is played if set
+ */
+ public void hide() {
+ if (!isHidden() && !isDismissed()) {
+ playHideAnimation();
+ setVisibility(INVISIBLE);
+ Log.v(LOG_TAG, "Action Button hidden");
+ }
+ }
+
+ /**
+ * Completely dismisses the Action Button,
+ * sets its visibility to {@link #GONE} and removes it from the parent view
+ *
+ * After calling this method any calls to {@link #show()} won't result in showing
+ * the Action Button so far as it is removed from the parent View
+ *
+ * {@link #hideAnimation} is played if set
+ */
+ public void dismiss() {
+ if (!isDismissed()) {
+ if (!isHidden()) {
+ playHideAnimation();
+ }
+ setVisibility(GONE);
+ ViewGroup parent = (ViewGroup) getParent();
+ parent.removeView(this);
+ Log.v(LOG_TAG, "Action Button dismissed");
+ }
+ }
+
+ /**
+ * Checks whether Action Button is hidden
+ *
+ * @return true if Action Button is hidden, otherwise false
+ */
+ public boolean isHidden() {
+ return getVisibility() == INVISIBLE;
+ }
+
+ /**
+ * Checks whether Action Button is dismissed
+ *
+ * @return true if Action Button is dismissed, otherwise false
+ */
+ public boolean isDismissed() {
+ ViewGroup parent = (ViewGroup) getParent();
+ return parent == null;
+ }
+
+ /**
+ * Returns the size of the Action Button in actual pixels (px).
+ * Size of the Action Button is the diameter of the main circle
+ *
+ * @return size of the Action Button in actual pixels (px)
+ */
+ public int getButtonSize() {
+ final int buttonSize = (int) type.getSize(getContext());
+ Log.v(LOG_TAG, "Button size is: " + buttonSize);
+ return buttonSize;
+ }
+
+ /**
+ * Returns the type of the Action Button
+ *
+ * @return type of the Action Button
+ */
+ public Type getType() {
+ return type;
+ }
+
+ /**
+ * Sets the type of the Action Button and
+ * invalidates the layout of the view
+ *
+ * @param type type of the Action Button
+ */
+ public void setType(Type type) {
+ this.type = type;
+ requestLayout();
+ Log.v(LOG_TAG, "Type changed to: " + getType());
+ }
+
+ /**
+ * Returns the current state of the Action Button
+ *
+ * @return current state of the Action Button
+ */
+ public State getState() {
+ return state;
+ }
+
+ /**
+ * Sets the current state of the Action Button and
+ * invalidates the view
+ *
+ * @param state new state of the Action Button
+ */
+ public void setState(State state) {
+ this.state = state;
+ invalidate();
+ Log.v(LOG_TAG, "State changed to: " + getState());
+ }
+
+ /**
+ * Returns the Action Button color when in
+ * {@link State#NORMAL} state
+ *
+ * @return Action Button color when in
+ * {@link State#NORMAL} state
+ */
+ public int getButtonColor() {
+ return buttonColor;
+ }
+
+ /**
+ * Sets the Action Button color when in
+ * {@link State#NORMAL} state and invalidates the view
+ *
+ * @param buttonColor Action Button color
+ * when in {@link State#NORMAL} state
+ */
+ public void setButtonColor(int buttonColor) {
+ this.buttonColor = buttonColor;
+ invalidate();
+ Log.v(LOG_TAG, "Color changed to: " + getButtonColor());
+ }
+
+ /**
+ * Sets the Action Button color when in
+ * {@link State#PRESSED} state
+ *
+ * @return Action Button color when in
+ * {@link State#PRESSED} state
+ */
+ public int getButtonColorPressed() {
+ return buttonColorPressed;
+ }
+
+ /**
+ * Sets the Action Button color when in
+ * {@link State#PRESSED} state and invalidates the view
+ *
+ * @param buttonColorPressed Action Button color
+ * when in {@link State#PRESSED} state
+ */
+ public void setButtonColorPressed(int buttonColorPressed) {
+ this.buttonColorPressed = buttonColorPressed;
+ invalidate();
+ Log.v(LOG_TAG, "Pressed color changed to: " + getButtonColorPressed());
+ }
+
+ /**
+ * Checks whether Action Button has shadow by determining shadow radius
+ *
+ * Shadow is disabled if elevation is set API level is {@code 21 Lollipop} and higher
+ *
+ * @return true if Action Button has radius, otherwise false
+ */
+ public boolean hasShadow() {
+ return !hasElevation() && getShadowRadius() > 0.0f;
+ }
+
+ /**
+ * Returns the Action Button shadow radius in actual
+ * pixels (px)
+ *
+ * @return Action Button shadow radius in actual pixels (px)
+ */
+ public float getShadowRadius() {
+ return shadowRadius;
+ }
+
+ /**
+ * Sets the Action Button shadow radius and
+ * invalidates the layout of the view
+ *
+ * Must be specified in density-independent (dp) pixels, which are
+ * then converted into actual pixels (px). If shadow radius is set to 0,
+ * shadow is removed
+ *
+ * @param shadowRadius shadow radius specified in density-independent
+ * (dp) pixels
+ */
+ public void setShadowRadius(float shadowRadius) {
+ this.shadowRadius = MetricsConverter.dpToPx(getContext(), shadowRadius);
+ requestLayout();
+ Log.v(LOG_TAG, "Shadow radius changed to:" + getShadowRadius());
+ }
+
+ /**
+ * Removes the Action Button shadow by setting its radius to 0
+ */
+ public void removeShadow() {
+ if (hasShadow()) {
+ setShadowRadius(0.0f);
+ }
+ }
+
+ /**
+ * Returns the Action Button shadow X-axis offset
+ * in actual pixels (px)
+ *
+ * If X-axis offset is greater than 0 shadow is shifted right.
+ * If X-axis offset is lesser than 0 shadow is shifted left.
+ * 0 X-axis offset means that shadow is not X-axis shifted at all
+ *
+ * @return Action Button shadow X-axis offset
+ * in actual pixels (px)
+ */
+ public float getShadowXOffset() {
+ return shadowXOffset;
+ }
+
+ /**
+ * Sets the Action Button shadow X-axis offset and
+ * invalidates the layout of the view
+ *
+ * If X-axis offset is greater than 0 shadow is shifted right.
+ * If X-axis offset is lesser than 0 shadow is shifted left.
+ * 0 X-axis offset means that shadow is not shifted at all
+ *
+ * Must be specified in density-independent (dp) pixels, which are
+ * then converted into actual pixels (px)
+ *
+ * @param shadowXOffset shadow X-axis offset specified in density-independent
+ * (dp) pixels
+ */
+ public void setShadowXOffset(float shadowXOffset) {
+ this.shadowXOffset = MetricsConverter.dpToPx(getContext(), shadowXOffset);
+ requestLayout();
+ Log.v(LOG_TAG, "Shadow X offset changed to: " + getShadowXOffset());
+ }
+
+ /**
+ * Returns the Action Button shadow Y-axis offset
+ * in actual pixels (px)
+ *
+ * If Y-axis offset is greater than 0 shadow is shifted down.
+ * If Y-axis offset is lesser than 0 shadow is shifted up.
+ * 0 Y-axis offset means that shadow is not Y-axis shifted at all
+ *
+ * @return Action Button shadow Y-axis offset
+ * in actual pixels (px)
+ */
+ public float getShadowYOffset() {
+ return shadowYOffset;
+ }
+
+ /**
+ * Sets the Action Button shadow Y-axis offset and
+ * invalidates the layout of the view
+ *
+ * If Y-axis offset is greater than 0 shadow is shifted down.
+ * If Y-axis offset is lesser than 0 shadow is shifted up.
+ * 0 Y-axis offset means that shadow is not Y-axis shifted at all
+ *
+ * Must be specified in density-independent (dp) pixels, which are
+ * then converted into actual pixels (px)
+ *
+ * @param shadowYOffset shadow Y-axis offset specified in density-independent
+ * (dp) pixels
+ */
+ public void setShadowYOffset(float shadowYOffset) {
+ this.shadowYOffset = MetricsConverter.dpToPx(getContext(), shadowYOffset);
+ requestLayout();
+ Log.v(LOG_TAG, "Shadow Y offset changed to:" + getShadowYOffset());
+ }
+
+ /**
+ * Returns Action Button shadow color
+ *
+ * @return Action Button shadow color
+ */
+ public int getShadowColor() {
+ return shadowColor;
+ }
+
+ /**
+ * Sets the Action Button shadow color and
+ * invalidates the view
+ *
+ * @param shadowColor Action Button color
+ */
+ public void setShadowColor(int shadowColor) {
+ this.shadowColor = shadowColor;
+ invalidate();
+ Log.v(LOG_TAG, "Shadow color changed to: " + getShadowColor());
+ }
+
+ /**
+ * Returns the Action Button stroke width in actual
+ * pixels (px)
+ *
+ * @return Action Button stroke width in actual
+ * pixels (px)
+ */
+ public float getStrokeWidth() {
+ return strokeWidth;
+ }
+
+ /**
+ * Checks whether Action Button has stroke by checking
+ * stroke width
+ *
+ * @return true if Action Button has stroke, otherwise false
+ */
+ public boolean hasStroke() {
+ return getStrokeWidth() > 0.0f;
+ }
+
+ /**
+ * Sets the Action Button stroke width and
+ * invalidates the layout of the view
+ *
+ * Stroke width value must be greater than 0. If stroke width is
+ * set to 0 stroke is removed
+ *
+ * Must be specified in density-independent (dp) pixels, which are
+ * then converted into actual pixels (px)
+ *
+ * @param strokeWidth stroke width specified in density-independent
+ * (dp) pixels
+ */
+ public void setStrokeWidth(float strokeWidth) {
+ this.strokeWidth = MetricsConverter.dpToPx(getContext(), strokeWidth);
+ requestLayout();
+ Log.v(LOG_TAG, "Stroke width changed to: " + getStrokeWidth());
+ }
+
+ /**
+ * Removes the Action Button stroke by setting its width to 0
+ */
+ public void removeStroke() {
+ if (hasStroke()) {
+ setStrokeWidth(0.0f);
+ }
+ }
+
+ /**
+ * Returns the Action Button stroke color
+ *
+ * @return Action Button stroke color
+ */
+ public int getStrokeColor() {
+ return strokeColor;
+ }
+
+ /**
+ * Sets the Action Button stroke color and
+ * invalidates the view
+ *
+ * @param strokeColor Action Button stroke color
+ */
+ public void setStrokeColor(int strokeColor) {
+ this.strokeColor = strokeColor;
+ invalidate();
+ Log.v(LOG_TAG, "Stroke color changed to: " + getStrokeColor());
+ }
+
+ /**
+ * Returns the Action Button image drawable centered
+ * inside the view
+ *
+ * @return Action Button image drawable centered
+ * inside the view
+ */
+ public Drawable getImage() {
+ return image;
+ }
+
+ /**
+ * Checks whether Action Button has an image centered
+ * inside the view
+ *
+ * @return true if Action Button has an image centered
+ * inside the view, otherwise false
+ */
+ public boolean hasImage() {
+ return getImage() != null;
+ }
+
+ /**
+ * Places the image drawable centered inside the view and
+ * invalidates the view
+ *
+ * Size of the image while drawing is fit to {@link #imageSize}
+ *
+ * @param image image drawable, which will be placed centered
+ * inside the view
+ */
+ public void setImageDrawable(Drawable image) {
+ this.image = image;
+ invalidate();
+ Log.v(LOG_TAG, "Image drawable set");
+ }
+
+ /**
+ * Resolves the drawable resource id and places the resolved image drawable
+ * centered inside the view
+ *
+ * @param resId drawable resource id, which is to be resolved to
+ * image drawable and used as parameter when calling
+ * {@link #setImageDrawable(android.graphics.drawable.Drawable)}
+ */
+ public void setImageResource(int resId) {
+ setImageDrawable(getResources().getDrawable(resId));
+ }
+
+ /**
+ * Creates the {@link android.graphics.drawable.BitmapDrawable} from the given
+ * {@link android.graphics.Bitmap} and places it centered inside the view
+ *
+ * @param bitmap bitmap, from which {@link android.graphics.drawable.BitmapDrawable}
+ * is created and used as parameter when calling
+ * {@link #setImageDrawable(android.graphics.drawable.Drawable)}
+ */
+ public void setImageBitmap(Bitmap bitmap) {
+ setImageDrawable(new BitmapDrawable(getResources(), bitmap));
+ }
+
+ /**
+ * Removes the Action Button image by setting its value to null
+ */
+ public void removeImage() {
+ if (hasImage()) {
+ setImageDrawable(null);
+ }
+ }
+
+ /**
+ * Returns the Action Button image size in actual pixels (px).
+ * If Action Button image is not set returns 0
+ *
+ * @return Action Button image size in actual pixels (px),
+ * 0 if image is not set
+ */
+ public float getImageSize() {
+ return getImage() != null ? imageSize : 0.0f;
+ }
+
+ /**
+ * Sets the size of the Action Button image
+ *
+ * Changing the default size of the image breaks the rules of
+ * Material Design
+ *
+ * Must be specified in density-independent (dp) pixels, which are
+ * then converted into actual pixels (px)
+ *
+ * @param size size of the Action Button image
+ * specified in density-independent (dp) pixels
+ */
+ public void setImageSize(float size) {
+ this.imageSize = MetricsConverter.dpToPx(getContext(), size);
+ Log.v(LOG_TAG, "Image size changed to: " + getImageSize());
+ }
+
+ /**
+ * Returns an animation, which is used while showing Action Button
+ *
+ * @return animation, which is used while showing Action Button
+ */
+ public Animation getShowAnimation() {
+ return showAnimation;
+ }
+
+ /**
+ * Sets the animation, which is used while showing Action Button
+ *
+ * @param animation animation, which is to be used while showing
+ * Action Button
+ */
+ public void setShowAnimation(Animation animation) {
+ this.showAnimation = animation;
+ Log.v(LOG_TAG, "Show animation set");
+ }
+
+ /**
+ * Sets one of the {@link Animations} as animation, which is used while showing
+ * Action Button
+ *
+ * @param animation one of the {@link Animations}, which is to be used while
+ * showing Action Button
+ */
+ public void setShowAnimation(Animations animation) {
+ setShowAnimation(Animations.load(getContext(), animation.animResId));
+ }
+
+ /**
+ * Removes the animation, which is used while showing Action Button
+ */
+ public void removeShowAnimation() {
+ setShowAnimation(Animations.NONE);
+ Log.v(LOG_TAG, "Show animation removed");
+ }
+
+ /**
+ * Returns an animation, which is used while hiding Action Button
+ *
+ * @return animation, which is used while hiding Action Button
+ */
+ public Animation getHideAnimation() {
+ return hideAnimation;
+ }
+
+ /**
+ * Sets the animation, which is used while hiding Action Button
+ *
+ * @param animation animation, which is to be used while hiding
+ * Action Button
+ */
+ public void setHideAnimation(Animation animation) {
+ this.hideAnimation = animation;
+ Log.v(LOG_TAG, "Hide animation set");
+ }
+
+ /**
+ * Sets one of the {@link Animations} as animation, which is used while hiding
+ * Action Button
+ *
+ * @param animation one of the {@link Animations}, which is to be used while
+ * hiding Action Button
+ */
+ public void setHideAnimation(Animations animation) {
+ setHideAnimation(Animations.load(getContext(), animation.animResId));
+ }
+
+ public void removeHideAnimation() {
+ setHideAnimation(Animations.NONE);
+ Log.v(LOG_TAG, "Hide animation removed");
+ }
+
+ /**
+ * Adds additional actions on motion events:
+ * 1. Changes the Action Button {@link #state} to {@link State#PRESSED}
+ * on {@link android.view.MotionEvent#ACTION_DOWN}
+ * 2. Changes the Action Button {@link #state} to {@link State#NORMAL}
+ * on {@link android.view.MotionEvent#ACTION_UP}
+ *
+ * @param event motion event
+ * @return true if event was handled, otherwise false
+ */
+ @SuppressWarnings("all")
+ @SuppressLint("ClickableViewAccessibility")
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ super.onTouchEvent(event);
+ final int action = event.getAction();
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ Log.v(LOG_TAG, "Motion event action down detected");
+ setState(State.PRESSED);
+ return true;
+ case MotionEvent.ACTION_UP:
+ Log.v(LOG_TAG, "Motion event action up detected");
+ setState(State.NORMAL);
+ return true;
+ default:
+ Log.v(LOG_TAG, "Unrecognized motion event detected");
+ return false;
+ }
+ }
+
+ /**
+ * Adds additional checking whether animation is null before starting to play it
+ *
+ * @param animation animation to play
+ */
+ @SuppressWarnings("all")
+ @Override
+ public void startAnimation(Animation animation) {
+ if (animation != null) {
+ super.startAnimation(animation);
+ }
+ }
+
+ /**
+ * Resets the paint to its default values and sets initial flags to it
+ *
+ * Use this method before drawing the new element of the view
+ */
+ protected final void resetPaint() {
+ paint.reset();
+ paint.setFlags(Paint.ANTI_ALIAS_FLAG);
+ Log.v(LOG_TAG, "Paint reset");
+ }
+
+ /**
+ * Draws the elements of the Action Button
+ *
+ * @param canvas canvas, on which the drawing is to be performed
+ */
+ @SuppressWarnings("all")
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ Log.v(LOG_TAG, "Action Button onDraw called");
+ drawCircle(canvas);
+ if (hasElevation()) {
+ drawElevation();
+ }
+ if (hasStroke()) {
+ drawStroke(canvas);
+ }
+ if (hasImage()) {
+ drawImage(canvas);
+ }
+ }
+
+ /**
+ * Draws the main circle of the Action Button and calls
+ * {@link #drawShadow()} to draw the shadow if present
+ *
+ * @param canvas canvas, on which circle is to be drawn
+ */
+ protected void drawCircle(Canvas canvas) {
+ resetPaint();
+ if (hasShadow()) {
+ drawShadow();
+ }
+ paint.setStyle(Paint.Style.FILL);
+ paint.setColor(getState() == State.PRESSED ? getButtonColorPressed() : getButtonColor());
+ canvas.drawCircle(calculateCenterX(), calculateCenterY(), calculateCircleRadius(), paint);
+ Log.v(LOG_TAG, "Circle drawn");
+ }
+
+ /**
+ * Calculates the X-axis center coordinate of the entire view
+ *
+ * @return X-axis center coordinate of the entire view
+ */
+ protected float calculateCenterX() {
+ final float centerX = getMeasuredWidth() / 2;
+ Log.v(LOG_TAG, "Calculated center X = " + centerX);
+ return centerX;
+ }
+
+ /**
+ * Calculates the Y-axis center coordinate of the entire view
+ *
+ * @return Y-axis center coordinate of the entire view
+ */
+ protected float calculateCenterY() {
+ final float centerY = getMeasuredHeight() / 2;
+ Log.v(LOG_TAG, "Calculated center Y = " + centerY);
+ return centerY;
+ }
+
+ /**
+ * Calculates the radius of the main circle
+ *
+ * @return radius of the main circle
+ */
+ protected final float calculateCircleRadius() {
+ final float circleRadius = getButtonSize() / 2;
+ Log.v(LOG_TAG, "Calculated circle circleRadius = " + circleRadius);
+ return circleRadius;
+ }
+
+ /**
+ * Draws the shadow if view elevation is not enabled
+ */
+ protected void drawShadow() {
+ paint.setShadowLayer(getShadowRadius(), getShadowXOffset(), getShadowYOffset(), getShadowColor());
+ Log.v(LOG_TAG, "Shadow drawn");
+ }
+
+ /**
+ * Draws the elevation around the main circle
+ *
+ * Uses the stroke corrective, which helps to avoid the elevation overlapping issue
+ */
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+ protected void drawElevation() {
+ final int strokeWeightCorrective = (int) (getStrokeWidth() / 1.5f);
+ final int width = getWidth() - strokeWeightCorrective;
+ final int height = getHeight() - strokeWeightCorrective;
+ final ViewOutlineProvider outlineProvider = new ActionButtonOutlineProvider(width, height);
+ setOutlineProvider(outlineProvider);
+ Log.v(LOG_TAG, "Elevation drawn");
+ }
+
+ /**
+ * Checks whether view elevation is enabled
+ *
+ * @return true if view elevation enabled, otherwise false
+ */
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+ private boolean hasElevation() {
+ return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && getElevation() > 0.0f;
+ }
+
+ /**
+ * Draws stroke around the main circle
+ *
+ * @param canvas canvas, on which circle is to be drawn
+ */
+ protected void drawStroke(Canvas canvas) {
+ resetPaint();
+ paint.setStyle(Paint.Style.STROKE);
+ paint.setStrokeWidth(getStrokeWidth());
+ paint.setColor(getStrokeColor());
+ canvas.drawCircle(calculateCenterX(), calculateCenterY(), calculateCircleRadius(), paint);
+ Log.v(LOG_TAG, "Stroke drawn");
+ }
+
+ /**
+ * Draws the image centered inside the view
+ *
+ * @param canvas canvas, on which circle is to be drawn
+ */
+ protected void drawImage(Canvas canvas) {
+ final int startPointX = (int) (calculateCenterX() - getImageSize() / 2);
+ final int startPointY = (int) (calculateCenterY() - getImageSize() / 2);
+ final int endPointX = (int) (startPointX + getImageSize());
+ final int endPointY = (int) (startPointY + getImageSize());
+ getImage().setBounds(startPointX, startPointY, endPointX, endPointY);
+ getImage().draw(canvas);
+ Log.v(LOG_TAG, String.format("Image drawn on canvas with coordinates: startPointX = %s, startPointY = %s, " +
+ "endPointX = %s, endPointY = %s", startPointX, startPointY, endPointX, endPointY));
+ }
+
+ /**
+ * Sets the measured dimension for the entire view
+ *
+ * @param widthMeasureSpec horizontal space requirements as imposed by the parent.
+ * The requirements are encoded with
+ * {@link android.view.View.MeasureSpec}
+ * @param heightMeasureSpec vertical space requirements as imposed by the parent.
+ * The requirements are encoded with
+ * {@link android.view.View.MeasureSpec}
+ */
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ Log.v(LOG_TAG, "Action Button onMeasure called");
+ setMeasuredDimension(calculateMeasuredWidth(), calculateMeasuredHeight());
+ Log.v(LOG_TAG, String.format("View size measured with: height = %s, width = %s", getHeight(), getWidth()));
+ }
+
+ /**
+ * Calculates the measured width in actual pixels for the entire view
+ *
+ * @return measured width in actual pixels for the entire view
+ */
+ private int calculateMeasuredWidth() {
+ final int measuredWidth = getButtonSize() + calculateShadowWidth() + calculateStrokeWeight();
+ Log.v(LOG_TAG, "Calculated measured width = " + measuredWidth);
+ return measuredWidth;
+ }
+
+ /**
+ * Calculates the measured height in actual pixels for the entire view
+ *
+ * @return measured width in actual pixels for the entire view
+ */
+ private int calculateMeasuredHeight() {
+ final int measuredHeight = getButtonSize() + calculateShadowHeight() + calculateStrokeWeight();
+ Log.v(LOG_TAG, "Calculated measured height = " + measuredHeight);
+ return measuredHeight;
+ }
+
+ /**
+ * Calculates shadow width in actual pixels
+ *
+ * @return shadow width in actual pixels
+ */
+ private int calculateShadowWidth() {
+ final int shadowWidth = hasShadow() ? (int) ((getShadowRadius() + Math.abs(getShadowXOffset())) * 2) : 0;
+ Log.v(LOG_TAG, "Calculated shadow width = " + shadowWidth);
+ return shadowWidth;
+ }
+
+ /**
+ * Calculates shadow height in actual pixels
+ *
+ * @return shadow height in actual pixels
+ */
+ private int calculateShadowHeight() {
+ final int shadowHeight = hasShadow() ? (int) ((getShadowRadius() + Math.abs(getShadowYOffset())) * 2) : 0;
+ Log.v(LOG_TAG, "Calculated shadow height = " + shadowHeight);
+ return shadowHeight;
+ }
+
+ /**
+ * Calculates the stroke weight in actual pixels
+ * *
+ * @return stroke weight in actual pixels
+ */
+ private int calculateStrokeWeight() {
+ final int strokeWeight = (int) (getStrokeWidth() * 2.0f);
+ Log.v(LOG_TAG, "Calculated stroke weight is: " + strokeWeight);
+ return strokeWeight;
+ }
+
+ /**
+ * Determines the Action Button types
+ */
+ public enum Type {
+
+ /**
+ * Action Button default (56dp) type
+ */
+ DEFAULT {
+ @Override
+ int getId() {
+ return 0;
+ }
+
+ @Override
+ float getSize(Context context) {
+ return MetricsConverter.dpToPx(context, 56.0f);
+ }
+ },
+
+ /**
+ * Action Button mini (40dp) type
+ */
+ MINI {
+ @Override
+ int getId() {
+ return 1;
+ }
+
+ @Override
+ float getSize(Context context) {
+ return MetricsConverter.dpToPx(context, 40.0f);
+ }
+ };
+
+ /**
+ * Returns an {@code id} for specific Action Button
+ * type, which is defined in attributes
+ *
+ * @return {@code id} for particular Action Button type,
+ * which is defined in attributes
+ */
+ abstract int getId();
+
+ /**
+ * Returns the size of the specific type of the Action Button
+ *
+ * @param context context the view is running in
+ * @return size of the particular type of the Action Button
+ */
+ abstract float getSize(Context context);
+
+ /**
+ * Returns the Action Button type for a specific {@code id}
+ *
+ * @param id an {@code id}, for which Action Button type required
+ * @return Action Button type
+ */
+ static Type forId(int id) {
+ for (Type type : values()) {
+ if (type.getId() == id) {
+ return type;
+ }
+ }
+ return DEFAULT;
+ }
+
+ }
+
+ /**
+ * Determines the Action Button states
+ */
+ public enum State {
+
+ /**
+ * Action Button normal state
+ */
+ NORMAL,
+
+ /**
+ * Action Button pressed state
+ */
+ PRESSED
+
+ }
+
+ public enum Animations {
+
+ /**
+ * None. Animation absent
+ */
+ NONE (0),
+
+ /**
+ * Fade in animation
+ */
+ FADE_IN (R.anim.fab_fade_in),
+
+ /**
+ * Fade out animation
+ */
+ FADE_OUT (R.anim.fab_fade_out),
+
+ /**
+ * Scale up animation
+ */
+ SCALE_UP (R.anim.fab_scale_up),
+
+ /**
+ * Scale down animation
+ */
+ SCALE_DOWN (R.anim.fab_scale_down),
+
+ /**
+ * Roll from down animation
+ */
+ ROLL_FROM_DOWN (R.anim.fab_roll_from_down),
+
+ /**
+ * Roll to down animation
+ */
+ ROLL_TO_DOWN (R.anim.fab_roll_to_down),
+
+ /**
+ * Roll from right animation
+ */
+ ROLL_FROM_RIGHT (R.anim.fab_roll_from_right),
+
+ /**
+ * Roll to right animation
+ */
+ ROLL_TO_RIGHT (R.anim.fab_roll_to_right),
+
+ /**
+ * Jump from down animation
+ */
+ JUMP_FROM_DOWN (R.anim.fab_jump_from_down),
+
+ /**
+ * Jump to down animation
+ */
+ JUMP_TO_DOWN (R.anim.fab_jump_to_down),
+
+ /**
+ * Jump from right animation
+ */
+ JUMP_FROM_RIGHT (R.anim.fab_jump_from_right),
+
+ /**
+ * Jump to right animation
+ */
+ JUMP_TO_RIGHT (R.anim.fab_jump_to_right);
+
+ /**
+ * Correspondent animation resource id
+ */
+ final int animResId;
+
+ private Animations(int animResId) {
+ this.animResId = animResId;
+ }
+
+ /**
+ * Loads an animation from animation resource id
+ *
+ * @param context context the view is running in
+ * @param animResId resource id of the animation, which is to be loaded
+ * @return loaded animation
+ */
+ protected static Animation load(Context context, int animResId) {
+ return animResId == NONE.animResId ? null : AnimationUtils.loadAnimation(context, animResId);
+ }
+
+ }
+
+}
diff --git a/eclipse-compile/fab/java/com/software/shell/fab/ActionButtonOutlineProvider.java b/eclipse-compile/fab/java/com/software/shell/fab/ActionButtonOutlineProvider.java
new file mode 100644
index 0000000000..d154c3998f
--- /dev/null
+++ b/eclipse-compile/fab/java/com/software/shell/fab/ActionButtonOutlineProvider.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2015 Shell Software Inc.
+ *
+ * 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.
+ *
+ * File created: 2015-02-25 19:54:28
+ */
+
+package com.software.shell.fab;
+
+import android.annotation.TargetApi;
+import android.graphics.Outline;
+import android.os.Build;
+import android.view.View;
+import android.view.ViewOutlineProvider;
+
+/**
+ * An implementation of the {@link android.view.ViewOutlineProvider}
+ * for Action Button
+ *
+ * Used for drawing the elevation shadow for {@code API 21 Lollipop} and higher
+ *
+ * @author Vladislav
+ * @version 1.0.0
+ * @since 1.0.0
+ */
+@TargetApi(Build.VERSION_CODES.LOLLIPOP)
+class ActionButtonOutlineProvider extends ViewOutlineProvider {
+
+ /**
+ * Outline provider width
+ */
+ private int width;
+
+ /**
+ * Outline provider height
+ */
+ private int height;
+
+ /**
+ * Creates an instance of the {@link com.software.shell.fab.ActionButtonOutlineProvider}
+ *
+ * @param width initial outline provider width
+ * @param height initial outline provider height
+ */
+ ActionButtonOutlineProvider(int width, int height) {
+ this.width = width;
+ this.height = height;
+ }
+
+ /**
+ * Called to get the provider to populate the Outline. This method will be called by a View
+ * when its owned Drawables are invalidated, when the View's size changes, or if invalidateOutline()
+ * is called explicitly. The input outline is empty and has an alpha of 1.0f
+ *
+ * @param view a view, which builds the outline
+ * @param outline an empty outline, which is to be populated
+ */
+ @Override
+ public void getOutline(View view, Outline outline) {
+ outline.setOval(0, 0, width, height);
+ }
+
+}
diff --git a/eclipse-compile/fab/java/com/software/shell/fab/FloatingActionButton.java b/eclipse-compile/fab/java/com/software/shell/fab/FloatingActionButton.java
new file mode 100644
index 0000000000..196c8e7ad5
--- /dev/null
+++ b/eclipse-compile/fab/java/com/software/shell/fab/FloatingActionButton.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright 2015 Shell Software Inc.
+ *
+ * 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.
+ *
+ * File created: 2015-02-16 10:56:57
+ */
+
+package com.software.shell.fab;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.animation.Animation;
+
+/**
+ * Deprecated since version 1.0.2. Use {@link com.software.shell.fab.ActionButton}
+ * class instead.
+ * The reason is the rename of base class name from FloatingActionButton to
+ * ActionButton and some of the methods, which are present in this class.
+ *
+ * Will be removed in version 2.0.0. Please use {@link com.software.shell.fab.ActionButton} and
+ * methods declared there instead
+ *
+ * @author Vladislav
+ * @version 1.0.0
+ * @since 1.0.0
+ */
+@Deprecated
+public class FloatingActionButton extends ActionButton{
+
+ private static final String LOG_TAG = "FAB";
+
+ public FloatingActionButton(Context context) {
+ super(context);
+ }
+
+ public FloatingActionButton(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ initActionButton(context, attrs, 0, 0);
+ }
+
+ public FloatingActionButton(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ initActionButton(context, attrs, defStyleAttr, 0);
+ }
+
+ public FloatingActionButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ initActionButton(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ /**
+ * Returns an animation, which is used while showing Floating Action Button
+ * @deprecated since version 1.0.2. Please use {@link #getShowAnimation()} instead
+ *
+ * @return animation, which is used while showing Floating Action Button
+ */
+ @Deprecated
+ public Animation getAnimationOnShow() {
+ return getShowAnimation();
+ }
+
+ /**
+ * Sets the animation, which is used while showing Floating Action Button
+ * @deprecated since version 1.0.2. Please use
+ * {@link #setShowAnimation(android.view.animation.Animation)} instead
+ *
+ * @param animation animation, which is to be used while showing
+ * Floating Action Button
+ */
+ @Deprecated
+ public void setAnimationOnShow(Animation animation) {
+ setShowAnimation(animation);
+ }
+
+ /**
+ * Sets one of the {@link Animations} as animation, which is used while showing
+ * Floating Action Button
+ * @deprecated since version 1.0.2. Please use
+ * {@link #setShowAnimation(com.software.shell.fab.ActionButton.Animations)} instead
+ *
+ * @param animation one of the {@link Animations}, which is to be used while
+ * showing Floating Action Button
+ */
+ @Deprecated
+ public void setAnimationOnShow(Animations animation) {
+ setShowAnimation(animation);
+ }
+
+ /**
+ * Returns an animation, which is used while hiding Floating Action Button
+ * @deprecated since version 1.0.2. Please use {@link #getHideAnimation()}
+ * instead
+ *
+ * @return animation, which is used while hiding Floating Action Button
+ */
+ @Deprecated
+ public Animation getAnimationOnHide() {
+ return getHideAnimation();
+ }
+
+ /**
+ * Sets the animation, which is used while hiding Floating Action Button
+ * @deprecated since version 1.0.2. Please use
+ * {@link #setHideAnimation(android.view.animation.Animation)} instead
+ *
+ * @param animation animation, which is to be used while hiding
+ * Floating Action Button
+ */
+ @Deprecated
+ public void setAnimationOnHide(Animation animation) {
+ setHideAnimation(animation);
+ }
+
+ /**
+ * Sets one of the {@link Animations} as animation, which is used while hiding
+ * Floating Action Button
+ * @deprecated since version 1.0.2. Please use
+ * {@link #setHideAnimation(com.software.shell.fab.ActionButton.Animations)} )} instead
+ *
+ * @param animation one of the {@link Animations}, which is to be used while
+ * hiding Floating Action Button
+ */
+ @Deprecated
+ public void setAnimationOnHide(Animations animation) {
+ setHideAnimation(animation);
+ }
+
+ private void initActionButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ TypedArray attributes = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ActionButton,
+ defStyleAttr, defStyleRes);
+ try {
+ initType(attributes);
+ initShowAnimation(attributes);
+ initHideAnimation(attributes);
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Unable to read attr", e);
+ } finally {
+ attributes.recycle();
+ }
+ Log.v(LOG_TAG, "Floating Action Button initialized");
+ }
+
+ private void initType(TypedArray attrs) {
+ if (attrs.hasValue(R.styleable.ActionButton_type)) {
+ final int id = attrs.getInteger(R.styleable.ActionButton_type, 0);
+ setType(Type.forId(id));
+ Log.v(LOG_TAG, "Initialized type: " + getType());
+ }
+ }
+
+ /**
+ * Initializes the animation, which is used while showing
+ * Action Button
+ * @deprecated since 1.0.2 and will be removed in version 2.0.0.
+ * Use show_animation and hide_animation in XML instead
+ *
+ * @param attrs attributes of the XML tag that is inflating the view
+ */
+ @Deprecated
+ private void initShowAnimation(TypedArray attrs) {
+ if (attrs.hasValue(R.styleable.ActionButton_animation_onShow)) {
+ final int animResId = attrs.getResourceId(R.styleable.ActionButton_animation_onShow,
+ Animations.NONE.animResId);
+ setShowAnimation(Animations.load(getContext(), animResId));
+ }
+ }
+
+ /**
+ * Initializes the animation, which is used while hiding or dismissing
+ * Action Button
+ * @deprecated since 1.0.2 and will be removed in version 2.0.0
+ * Use show_animation and hide_animation in XML instead
+ *
+ * @param attrs attributes of the XML tag that is inflating the view
+ */
+ @Deprecated
+ private void initHideAnimation(TypedArray attrs) {
+ if (attrs.hasValue(R.styleable.ActionButton_animation_onHide)) {
+ final int animResId = attrs.getResourceId(R.styleable.ActionButton_animation_onHide,
+ Animations.NONE.animResId);
+ setHideAnimation(Animations.load(getContext(), animResId));
+ }
+ }
+
+}
diff --git a/eclipse-compile/fab/java/com/software/shell/fab/MetricsConverter.java b/eclipse-compile/fab/java/com/software/shell/fab/MetricsConverter.java
new file mode 100644
index 0000000000..c4770449c4
--- /dev/null
+++ b/eclipse-compile/fab/java/com/software/shell/fab/MetricsConverter.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2015 Shell Software Inc.
+ *
+ * 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.
+ *
+ * File created: 2015-01-30 22:55:44
+ */
+
+package com.software.shell.fab;
+
+import android.content.Context;
+
+/**
+ * Contains utility methods for metrics conversion
+ *
+ * @author Vladislav
+ * @version 1.0.0
+ * @since 1.0.0
+ */
+public final class MetricsConverter {
+
+ /**
+ * Prevents from creating {@link com.software.shell.fab.MetricsConverter} instances
+ */
+ private MetricsConverter() {
+ }
+
+ /**
+ * Converts the density-independent value into real pixel value based on display metrics
+ *
+ * @param context application context
+ * @param dp density-independent value
+ * @return converted real pixel value
+ */
+ public static float dpToPx(Context context, float dp) {
+ final float scale = context.getResources().getDisplayMetrics().density;
+ return dp * scale;
+ }
+
+}
diff --git a/eclipse-compile/fab/project.properties b/eclipse-compile/fab/project.properties
new file mode 100644
index 0000000000..17b8e6b92f
--- /dev/null
+++ b/eclipse-compile/fab/project.properties
@@ -0,0 +1,15 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system use,
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+
+# Indicates whether an apk should be generated for each density.
+split.density=false
+# Project target.
+target=android-21
+dex.force.jumbo=true
+android.library=true
diff --git a/eclipse-compile/fab/res/anim-v11/fab_jump_from_down.xml b/eclipse-compile/fab/res/anim-v11/fab_jump_from_down.xml
new file mode 100644
index 0000000000..87095c9bc7
--- /dev/null
+++ b/eclipse-compile/fab/res/anim-v11/fab_jump_from_down.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
diff --git a/eclipse-compile/fab/res/anim-v11/fab_jump_from_right.xml b/eclipse-compile/fab/res/anim-v11/fab_jump_from_right.xml
new file mode 100644
index 0000000000..9db4bf984f
--- /dev/null
+++ b/eclipse-compile/fab/res/anim-v11/fab_jump_from_right.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
diff --git a/eclipse-compile/fab/res/anim-v11/fab_jump_to_down.xml b/eclipse-compile/fab/res/anim-v11/fab_jump_to_down.xml
new file mode 100644
index 0000000000..ca59b72de1
--- /dev/null
+++ b/eclipse-compile/fab/res/anim-v11/fab_jump_to_down.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
diff --git a/eclipse-compile/fab/res/anim-v11/fab_jump_to_right.xml b/eclipse-compile/fab/res/anim-v11/fab_jump_to_right.xml
new file mode 100644
index 0000000000..d4287e6264
--- /dev/null
+++ b/eclipse-compile/fab/res/anim-v11/fab_jump_to_right.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
diff --git a/eclipse-compile/fab/res/anim-v11/fab_roll_from_down.xml b/eclipse-compile/fab/res/anim-v11/fab_roll_from_down.xml
new file mode 100644
index 0000000000..438160f361
--- /dev/null
+++ b/eclipse-compile/fab/res/anim-v11/fab_roll_from_down.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
diff --git a/eclipse-compile/fab/res/anim-v11/fab_roll_from_right.xml b/eclipse-compile/fab/res/anim-v11/fab_roll_from_right.xml
new file mode 100644
index 0000000000..0ef84e389b
--- /dev/null
+++ b/eclipse-compile/fab/res/anim-v11/fab_roll_from_right.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
diff --git a/eclipse-compile/fab/res/anim-v11/fab_roll_to_down.xml b/eclipse-compile/fab/res/anim-v11/fab_roll_to_down.xml
new file mode 100644
index 0000000000..8e8140cf93
--- /dev/null
+++ b/eclipse-compile/fab/res/anim-v11/fab_roll_to_down.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
diff --git a/eclipse-compile/fab/res/anim-v11/fab_roll_to_right.xml b/eclipse-compile/fab/res/anim-v11/fab_roll_to_right.xml
new file mode 100644
index 0000000000..22915225a9
--- /dev/null
+++ b/eclipse-compile/fab/res/anim-v11/fab_roll_to_right.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
diff --git a/eclipse-compile/fab/res/anim-v11/fab_scale_down.xml b/eclipse-compile/fab/res/anim-v11/fab_scale_down.xml
new file mode 100644
index 0000000000..ce63f0fd5f
--- /dev/null
+++ b/eclipse-compile/fab/res/anim-v11/fab_scale_down.xml
@@ -0,0 +1,30 @@
+
+
+
+
diff --git a/eclipse-compile/fab/res/anim-v11/fab_scale_up.xml b/eclipse-compile/fab/res/anim-v11/fab_scale_up.xml
new file mode 100644
index 0000000000..1d18f40544
--- /dev/null
+++ b/eclipse-compile/fab/res/anim-v11/fab_scale_up.xml
@@ -0,0 +1,29 @@
+
+
+
diff --git a/eclipse-compile/fab/res/anim/fab_fade_in.xml b/eclipse-compile/fab/res/anim/fab_fade_in.xml
new file mode 100644
index 0000000000..2b0ebedb7f
--- /dev/null
+++ b/eclipse-compile/fab/res/anim/fab_fade_in.xml
@@ -0,0 +1,25 @@
+
+
+
+
diff --git a/eclipse-compile/fab/res/anim/fab_fade_out.xml b/eclipse-compile/fab/res/anim/fab_fade_out.xml
new file mode 100644
index 0000000000..064ff9e45b
--- /dev/null
+++ b/eclipse-compile/fab/res/anim/fab_fade_out.xml
@@ -0,0 +1,25 @@
+
+
+
+
diff --git a/eclipse-compile/fab/res/anim/fab_jump_from_down.xml b/eclipse-compile/fab/res/anim/fab_jump_from_down.xml
new file mode 100644
index 0000000000..f41e38317f
--- /dev/null
+++ b/eclipse-compile/fab/res/anim/fab_jump_from_down.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
diff --git a/eclipse-compile/fab/res/anim/fab_jump_from_right.xml b/eclipse-compile/fab/res/anim/fab_jump_from_right.xml
new file mode 100644
index 0000000000..c9bdc0da7a
--- /dev/null
+++ b/eclipse-compile/fab/res/anim/fab_jump_from_right.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
diff --git a/eclipse-compile/fab/res/anim/fab_jump_to_down.xml b/eclipse-compile/fab/res/anim/fab_jump_to_down.xml
new file mode 100644
index 0000000000..0649c71a04
--- /dev/null
+++ b/eclipse-compile/fab/res/anim/fab_jump_to_down.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
diff --git a/eclipse-compile/fab/res/anim/fab_jump_to_right.xml b/eclipse-compile/fab/res/anim/fab_jump_to_right.xml
new file mode 100644
index 0000000000..8ea8b69a3a
--- /dev/null
+++ b/eclipse-compile/fab/res/anim/fab_jump_to_right.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
diff --git a/eclipse-compile/fab/res/anim/fab_roll_from_down.xml b/eclipse-compile/fab/res/anim/fab_roll_from_down.xml
new file mode 100644
index 0000000000..66a284f25d
--- /dev/null
+++ b/eclipse-compile/fab/res/anim/fab_roll_from_down.xml
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+
diff --git a/eclipse-compile/fab/res/anim/fab_roll_from_right.xml b/eclipse-compile/fab/res/anim/fab_roll_from_right.xml
new file mode 100644
index 0000000000..3c38343fcd
--- /dev/null
+++ b/eclipse-compile/fab/res/anim/fab_roll_from_right.xml
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+
diff --git a/eclipse-compile/fab/res/anim/fab_roll_to_down.xml b/eclipse-compile/fab/res/anim/fab_roll_to_down.xml
new file mode 100644
index 0000000000..16a6946ea9
--- /dev/null
+++ b/eclipse-compile/fab/res/anim/fab_roll_to_down.xml
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+
diff --git a/eclipse-compile/fab/res/anim/fab_roll_to_right.xml b/eclipse-compile/fab/res/anim/fab_roll_to_right.xml
new file mode 100644
index 0000000000..420734f2b8
--- /dev/null
+++ b/eclipse-compile/fab/res/anim/fab_roll_to_right.xml
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+
diff --git a/eclipse-compile/fab/res/anim/fab_scale_down.xml b/eclipse-compile/fab/res/anim/fab_scale_down.xml
new file mode 100644
index 0000000000..e0dd6bea64
--- /dev/null
+++ b/eclipse-compile/fab/res/anim/fab_scale_down.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
diff --git a/eclipse-compile/fab/res/anim/fab_scale_up.xml b/eclipse-compile/fab/res/anim/fab_scale_up.xml
new file mode 100644
index 0000000000..956f6ed79d
--- /dev/null
+++ b/eclipse-compile/fab/res/anim/fab_scale_up.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/eclipse-compile/fab/res/drawable-hdpi/fab_plus_icon.png b/eclipse-compile/fab/res/drawable-hdpi/fab_plus_icon.png
new file mode 100644
index 0000000000..b4358ce201
Binary files /dev/null and b/eclipse-compile/fab/res/drawable-hdpi/fab_plus_icon.png differ
diff --git a/eclipse-compile/fab/res/drawable-mdpi/fab_plus_icon.png b/eclipse-compile/fab/res/drawable-mdpi/fab_plus_icon.png
new file mode 100644
index 0000000000..6a32089858
Binary files /dev/null and b/eclipse-compile/fab/res/drawable-mdpi/fab_plus_icon.png differ
diff --git a/eclipse-compile/fab/res/drawable-xhdpi/fab_plus_icon.png b/eclipse-compile/fab/res/drawable-xhdpi/fab_plus_icon.png
new file mode 100644
index 0000000000..a92d0a8f34
Binary files /dev/null and b/eclipse-compile/fab/res/drawable-xhdpi/fab_plus_icon.png differ
diff --git a/eclipse-compile/fab/res/drawable-xxhdpi/fab_plus_icon.png b/eclipse-compile/fab/res/drawable-xxhdpi/fab_plus_icon.png
new file mode 100644
index 0000000000..569b089f47
Binary files /dev/null and b/eclipse-compile/fab/res/drawable-xxhdpi/fab_plus_icon.png differ
diff --git a/eclipse-compile/fab/res/drawable-xxxhdpi/fab_plus_icon.png b/eclipse-compile/fab/res/drawable-xxxhdpi/fab_plus_icon.png
new file mode 100644
index 0000000000..90a1d16dcf
Binary files /dev/null and b/eclipse-compile/fab/res/drawable-xxxhdpi/fab_plus_icon.png differ
diff --git a/eclipse-compile/fab/res/values-sw600dp/dimens.xml b/eclipse-compile/fab/res/values-sw600dp/dimens.xml
new file mode 100644
index 0000000000..3ca9bdfce2
--- /dev/null
+++ b/eclipse-compile/fab/res/values-sw600dp/dimens.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+ 24dp
+
diff --git a/eclipse-compile/fab/res/values/attrs.xml b/eclipse-compile/fab/res/values/attrs.xml
new file mode 100644
index 0000000000..bdd08225a7
--- /dev/null
+++ b/eclipse-compile/fab/res/values/attrs.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/eclipse-compile/fab/res/values/colors.xml b/eclipse-compile/fab/res/values/colors.xml
new file mode 100644
index 0000000000..60ca1a0bbb
--- /dev/null
+++ b/eclipse-compile/fab/res/values/colors.xml
@@ -0,0 +1,86 @@
+
+
+
+
+
+
+ #f44336
+ #b71c1c
+
+ #e91e63
+ #880e4f
+
+ #9c27b0
+ #4a148c
+
+ #673ab7
+ #311b92
+
+ #3f51b5
+ #1a237e
+
+ #2196f3
+ #0d47a1
+
+ #03a9f4
+ #01579b
+
+ #00bcd4
+ #006064
+
+ #009688
+ #004d40
+
+ #4caf50
+ #1b5e20
+
+ #8bc34a
+ #33691e
+
+ #cddc39
+ #827717
+
+ #ffeb3b
+ #f57f17
+
+ #ffc107
+ #ff6f00
+
+ #ff9800
+ #e65100
+
+ #ff5722
+ #bf360c
+
+ #795548
+ #3e2723
+
+ #9e9e9e
+ #212121
+
+ #607d8b
+ #263238
+
+ #000000
+
+ #ffffff
+
diff --git a/eclipse-compile/fab/res/values/dimens.xml b/eclipse-compile/fab/res/values/dimens.xml
new file mode 100644
index 0000000000..06d5dccfcd
--- /dev/null
+++ b/eclipse-compile/fab/res/values/dimens.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+ 16dp
+
+