package dev.ukanth.ufirewall.widget; import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.util.DisplayMetrics; import android.view.MotionEvent; import android.view.View; import android.view.animation.AccelerateInterpolator; import android.view.animation.AnimationSet; import android.view.animation.DecelerateInterpolator; import android.view.animation.ScaleAnimation; import android.view.animation.TranslateAnimation; import java.util.ArrayList; import java.util.List; public class RadialMenuWidget extends View { //Defines the interface public interface RadialMenuEntry { public String getName(); public String getLabel(); public int getIcon(); public List<RadialMenuEntry> getChildren(); public void menuActiviated(); } private List<RadialMenuEntry> menuEntries = new ArrayList<RadialMenuEntry>(); private RadialMenuEntry centerCircle = null; private float screen_density = getContext().getResources().getDisplayMetrics().density; private int defaultColor = Color.rgb(0, 0, 0); //default color of wedge pieces private int defaultAlpha = 180; //transparency of the colors, 255=Opague, 0=Transparent private int wedge2Color = Color.rgb(85, 85, 85); //default color of wedge pieces private int wedge2Alpha = 210; private int outlineColor = Color.rgb(255, 255, 255); //color of outline private int outlineAlpha = 255; //transparency of outline private int selectedColor = Color.rgb(0, 0, 0); //color to fill when something is selected private int selectedAlpha = 210; //transparency of fill when something is selected private int disabledColor = Color.rgb(85, 85, 85); //color to fill when something is selected private int disabledAlpha = 100; //transparency of fill when something is selected private int pictureAlpha = 255; //transparency of images private int textColor = Color.rgb(255, 255, 255); //color to fill when something is selected private int textAlpha = 255; //transparency of fill when something is selected private int headerTextColor = Color.rgb(255, 255, 255); //color of header text private int headerTextAlpha = 255; //transparency of header text private int headerBackgroundColor = Color.rgb(0, 0, 0); //color of header background private int headerBackgroundAlpha = 180; //transparency of header background private int wedgeQty = 1; //Number of wedges private Wedge[] Wedges = new Wedge[wedgeQty]; private Wedge selected = null; //Keeps track of which wedge is selected private Wedge enabled = null; //Keeps track of which wedge is enabled for outer ring private Rect[] iconRect = new Rect[wedgeQty]; private int wedgeQty2 = 1; //Number of wedges private Wedge[] Wedges2 = new Wedge[wedgeQty2]; private Wedge selected2 = null; //Keeps track of which wedge is selected private Rect[] iconRect2 = new Rect[wedgeQty2]; private RadialMenuEntry wedge2Data = null; //Keeps track off which menuItem data is being used for the outer ring private int MinSize = scalePX(35); //Radius of inner ring size private int MaxSize = scalePX(90); //Radius of outer ring size private int r2MinSize = MaxSize+scalePX(5); //Radius of inner second ring size private int r2MaxSize = r2MinSize+scalePX(45); //Radius of outer second ring size private int MinIconSize = scalePX(15); //Min Size of Image in Wedge private int MaxIconSize = scalePX(35); //Max Size of Image in Wedge //private int BitmapSize = scalePX(40); //Size of Image in Wedge private int cRadius = MinSize-scalePX(7); //Inner Circle Radius private int textSize = scalePX(15); //TextSize private int animateTextSize = textSize; private int xPosition = getSizeX(); //Center X location of Radial Menu private int yPosition = getSizeY(); //Center Y location of Radial Menu private int xSource = 0; //Source X of clicked location private int ySource = 0; //Center Y of clicked location private boolean showSource = false; //Display icon where at source location private boolean inWedge = false; //Identifies touch event was in first wedge private boolean inWedge2 = false; //Identifies touch event was in second wedge private boolean inCircle = false; //Identifies touch event was in middle circle private boolean Wedge2Shown = false; //Identifies 2nd wedge is drawn private boolean HeaderBoxBounded = false; //Identifies if header box is drawn private String headerString = null; private int headerTextSize = textSize; //TextSize private int headerBuffer = scalePX(8); private Rect textRect = new Rect(); private RectF textBoxRect = new RectF(); private int headerTextLeft; private int headerTextBottom; //private RotateAnimation rotate; //private AlphaAnimation blend; private ScaleAnimation scale; private TranslateAnimation move; private AnimationSet spriteAnimation; private long animationSpeed = 400L; private static final int ANIMATE_IN = 1; private static final int ANIMATE_OUT = 2; private int animateSections = 4; private int r2VariableSize; private boolean animateOuterIn = false; private boolean animateOuterOut = false; @SuppressLint("NewApi") public RadialMenuWidget(Context context) { super(context); // Gets screen specs and defaults to center of screen /*DisplayMetrics dm = new DisplayMetrics(); WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); wm.getDefaultDisplay().getMetrics(dm); Point size = new Point(); try { wm.getDefaultDisplay().getRealSize(size); //FIXME: handle large tablets if (size.x > 1200) this.xPosition = size.x / 3; else this.xPosition = size.x / 2; this.yPosition = size.y / 2; } catch (NoSuchMethodError e) { if (wm.getDefaultDisplay().getWidth() > 1200) { this.xPosition = wm.getDefaultDisplay().getWidth() / 3; } else { this.xPosition = wm.getDefaultDisplay().getWidth() / 2; } this.yPosition = wm.getDefaultDisplay().getHeight() / 2; }*/ determineWedges(); onOpenAnimation(); } @Override public boolean onTouchEvent(MotionEvent e) { int state = e.getAction(); int eventX = (int) e.getX(); int eventY = (int) e.getY(); if (state == MotionEvent.ACTION_DOWN) { //selected = null; //selected2 = null; inWedge = false; inWedge2 = false; inCircle = false; //Checks if a pie slice is selected in first Wedge for (int i = 0; i < Wedges.length; i++) { Wedge f = Wedges[i]; double slice = (2*Math.PI) / wedgeQty; double start = (2*Math.PI)*(0.75) - (slice/2); //this is done so top slice is the centered on top of the circle inWedge = pntInWedge(eventX, eventY, xPosition, yPosition, MinSize, MaxSize, (i* slice)+start, slice); if (inWedge == true) { selected = f; break; } } //Checks if a pie slice is selected in second Wedge if (Wedge2Shown == true) { for (int i = 0; i < Wedges2.length; i++) { Wedge f = Wedges2[i]; double slice = (2*Math.PI) / wedgeQty2; double start = (2*Math.PI)*(0.75) - (slice/2); //this is done so top slice is the centered on top of the circle inWedge2 = pntInWedge(eventX, eventY, xPosition, yPosition, r2MinSize, r2MaxSize, (i* slice)+start, slice); if (inWedge2 == true) { selected2 = f; break; } } } //Checks if center circle is selected inCircle = pntInCircle(eventX, eventY, xPosition,yPosition,cRadius); } else if (state == MotionEvent.ACTION_UP) { //execute commands... //put in stuff here to "return" the button that was pressed. if (inCircle == true) { if (Wedge2Shown == true) { enabled = null; animateOuterIn = true; //sets Wedge2Shown = false; } selected = null; //Toast.makeText(getContext(), centerCircle.getName() + " pressed.", Toast.LENGTH_SHORT).show(); centerCircle.menuActiviated(); } else if (selected != null){ for (int i = 0; i < Wedges.length; i++) { Wedge f = Wedges[i]; if (f == selected) { //Checks if a inner ring is enabled if so closes the outer ring an if (enabled != null) { //Toast.makeText(getContext(), "Closing outer ring", Toast.LENGTH_SHORT).show(); enabled = null; animateOuterIn = true; //sets Wedge2Shown = false; //If outer ring is not enabled, then executes event } else { //Toast.makeText(getContext(), menuEntries.get(i).getName() + " pressed.", Toast.LENGTH_SHORT).show(); menuEntries.get(i).menuActiviated(); //Figures out how many outer rings if (menuEntries.get(i).getChildren() != null) { determineOuterWedges(menuEntries.get(i)); enabled = f; animateOuterOut = true; //sets Wedge2Shown = true;m } else { Wedge2Shown = false; } } selected = null; } } } else if (selected2 != null){ for (int i = 0; i < Wedges2.length; i++) { Wedge f = Wedges2[i]; if (f == selected2) { //Toast.makeText(getContext(), wedge2Data.getChildren().get(i).getName() + " pressed.", Toast.LENGTH_SHORT).show(); animateOuterIn = true; //sets Wedge2Shown = false; wedge2Data.getChildren().get(i).menuActiviated(); enabled = null; selected = null; } } } else { //This is when something outside the circle or any of the rings is selected //selected = null; //enabled = null; } //selected = null; selected2 = null; inCircle = false; } invalidate(); return true; } @Override protected void onDraw(Canvas c) { Paint paint = new Paint(); paint.setAntiAlias(true); paint.setStrokeWidth(3); // draws a dot at the source of the press if (showSource == true ) { paint.setColor(outlineColor); paint.setAlpha(outlineAlpha); paint.setStyle(Paint.Style.STROKE); c.drawCircle(xSource, ySource, cRadius/10, paint); paint.setColor(selectedColor); paint.setAlpha(selectedAlpha); paint.setStyle(Paint.Style.FILL); c.drawCircle(xSource, ySource, cRadius/10, paint); } for (int i = 0; i < Wedges.length; i++) { Wedge f = Wedges[i]; paint.setColor(outlineColor); paint.setAlpha(outlineAlpha); paint.setStyle(Paint.Style.STROKE); c.drawPath(f, paint); if (f == enabled && Wedge2Shown == true) { paint.setColor(wedge2Color); paint.setAlpha(wedge2Alpha); paint.setStyle(Paint.Style.FILL); c.drawPath(f, paint); } else if (f != enabled && Wedge2Shown == true) { paint.setColor(disabledColor); paint.setAlpha(disabledAlpha); paint.setStyle(Paint.Style.FILL); c.drawPath(f, paint); } else if (f == enabled && Wedge2Shown == false) { paint.setColor(wedge2Color); paint.setAlpha(wedge2Alpha); paint.setStyle(Paint.Style.FILL); c.drawPath(f, paint); } else if (f == selected) { paint.setColor(wedge2Color); paint.setAlpha(wedge2Alpha); paint.setStyle(Paint.Style.FILL); c.drawPath(f, paint); } else { paint.setColor(defaultColor); paint.setAlpha(defaultAlpha); paint.setStyle(Paint.Style.FILL); c.drawPath(f, paint); } Rect rf = iconRect[i]; if ((menuEntries.get(i).getIcon() != 0) && (menuEntries.get(i).getLabel() != null)) { //This will look for a "new line" and split into multiple lines String menuItemName = menuEntries.get(i).getLabel(); String[] stringArray = menuItemName.split("\n"); paint.setColor(textColor); if (f != enabled && Wedge2Shown == true) { paint.setAlpha(disabledAlpha); } else { paint.setAlpha(textAlpha); } paint.setStyle(Paint.Style.FILL); paint.setTextSize(textSize); Rect rect = new Rect(); float textHeight = 0; for (int j = 0; j < stringArray.length; j++) { paint.getTextBounds(stringArray[j],0,stringArray[j].length(),rect); textHeight = textHeight+(rect.height()+3); } Rect rf2 = new Rect(); rf2.set(rf.left, rf.top-((int)textHeight/2), rf.right, rf.bottom-((int)textHeight/2)); float textBottom = rf2.bottom; for (int j = 0; j < stringArray.length; j++) { paint.getTextBounds(stringArray[j],0,stringArray[j].length(),rect); float textLeft = rf.centerX() - rect.width()/2; textBottom = textBottom + (rect.height()+3); c.drawText(stringArray[j], textLeft-rect.left, textBottom-rect.bottom, paint); } //Puts in the Icon Drawable drawable = getResources().getDrawable(menuEntries.get(i).getIcon()); drawable.setBounds(rf2); if (f != enabled && Wedge2Shown == true) { drawable.setAlpha(disabledAlpha); } else { drawable.setAlpha(pictureAlpha); } drawable.draw(c); //Icon Only } else if (menuEntries.get(i).getIcon() != 0) { //Puts in the Icon Drawable drawable = getResources().getDrawable(menuEntries.get(i).getIcon()); drawable.setBounds(rf); if (f != enabled && Wedge2Shown == true) { drawable.setAlpha(disabledAlpha); } else { drawable.setAlpha(pictureAlpha); } drawable.draw(c); //Text Only } else { //Puts in the Text if no Icon paint.setColor(textColor); if (f != enabled && Wedge2Shown == true) { paint.setAlpha(disabledAlpha); } else { paint.setAlpha(textAlpha); } paint.setStyle(Paint.Style.FILL); paint.setTextSize(textSize); //This will look for a "new line" and split into multiple lines String menuItemName = menuEntries.get(i).getLabel(); String[] stringArray = menuItemName.split("\n"); //gets total height Rect rect = new Rect(); float textHeight = 0; for (int j = 0; j < stringArray.length; j++) { paint.getTextBounds(stringArray[j],0,stringArray[j].length(),rect); textHeight = textHeight+(rect.height()+3); } float textBottom = rf.centerY()-(textHeight/2); for (int j = 0; j < stringArray.length; j++) { paint.getTextBounds(stringArray[j],0,stringArray[j].length(),rect); float textLeft = rf.centerX() - rect.width()/2; textBottom = textBottom + (rect.height()+3); c.drawText(stringArray[j], textLeft-rect.left, textBottom-rect.bottom, paint); } } } //Animate the outer ring in/out if (animateOuterIn == true) { animateOuterWedges(ANIMATE_IN); } else if (animateOuterOut == true) { animateOuterWedges(ANIMATE_OUT); } if (Wedge2Shown == true) { for (int i = 0; i < Wedges2.length; i++) { Wedge f = Wedges2[i]; paint.setColor(outlineColor); paint.setAlpha(outlineAlpha); paint.setStyle(Paint.Style.STROKE); c.drawPath(f, paint); if (f == selected2) { paint.setColor(selectedColor); paint.setAlpha(selectedAlpha); paint.setStyle(Paint.Style.FILL); c.drawPath(f, paint); } else { paint.setColor(wedge2Color); paint.setAlpha(wedge2Alpha); paint.setStyle(Paint.Style.FILL); c.drawPath(f, paint); } Rect rf = iconRect2[i]; if ((wedge2Data.getChildren().get(i).getIcon() != 0) && (wedge2Data.getChildren().get(i).getLabel() != null)) { //This will look for a "new line" and split into multiple lines String menuItemName = wedge2Data.getChildren().get(i).getLabel(); String[] stringArray = menuItemName.split("\n"); paint.setColor(textColor); paint.setAlpha(textAlpha); paint.setStyle(Paint.Style.FILL); paint.setTextSize(animateTextSize); Rect rect = new Rect(); float textHeight = 0; for (int j = 0; j < stringArray.length; j++) { paint.getTextBounds(stringArray[j],0,stringArray[j].length(),rect); textHeight = textHeight+(rect.height()+3); } Rect rf2 = new Rect(); rf2.set(rf.left, rf.top-((int)textHeight/2), rf.right, rf.bottom-((int)textHeight/2)); float textBottom = rf2.bottom; for (int j = 0; j < stringArray.length; j++) { paint.getTextBounds(stringArray[j],0,stringArray[j].length(),rect); float textLeft = rf.centerX() - rect.width()/2; textBottom = textBottom + (rect.height()+3); c.drawText(stringArray[j], textLeft-rect.left, textBottom-rect.bottom, paint); } //Puts in the Icon Drawable drawable = getResources().getDrawable(wedge2Data.getChildren().get(i).getIcon()); drawable.setBounds(rf2); drawable.setAlpha(pictureAlpha); drawable.draw(c); //Icon Only } else if (wedge2Data.getChildren().get(i).getIcon() != 0) { //Puts in the Icon Drawable drawable = getResources().getDrawable(wedge2Data.getChildren().get(i).getIcon()); drawable.setBounds(rf); drawable.setAlpha(pictureAlpha); drawable.draw(c); //Text Only } else { //Puts in the Text if no Icon paint.setColor(textColor); paint.setAlpha(textAlpha); paint.setStyle(Paint.Style.FILL); paint.setTextSize(animateTextSize); //This will look for a "new line" and split into multiple lines String menuItemName = wedge2Data.getChildren().get(i).getLabel(); String[] stringArray = menuItemName.split("\n"); //gets total height Rect rect = new Rect(); float textHeight = 0; for (int j = 0; j < stringArray.length; j++) { paint.getTextBounds(stringArray[j],0,stringArray[j].length(),rect); textHeight = textHeight+(rect.height()+3); } float textBottom = rf.centerY()-(textHeight/2); for (int j = 0; j < stringArray.length; j++) { paint.getTextBounds(stringArray[j],0,stringArray[j].length(),rect); float textLeft = rf.centerX() - rect.width()/2; textBottom = textBottom + (rect.height()+3); c.drawText(stringArray[j], textLeft-rect.left, textBottom-rect.bottom, paint); } } } } //Draws the Middle Circle paint.setColor(outlineColor); paint.setAlpha(outlineAlpha); paint.setStyle(Paint.Style.STROKE); c.drawCircle(xPosition, yPosition, cRadius, paint); if (inCircle == true) { paint.setColor(selectedColor); paint.setAlpha(selectedAlpha); paint.setStyle(Paint.Style.FILL); c.drawCircle(xPosition, yPosition, cRadius, paint); onCloseAnimation(); } else { paint.setColor(defaultColor); paint.setAlpha(defaultAlpha); paint.setStyle(Paint.Style.FILL); c.drawCircle(xPosition, yPosition, cRadius, paint); } // Draw the circle picture if ((centerCircle.getIcon() != 0) && (centerCircle.getLabel() != null)) { //This will look for a "new line" and split into multiple lines String menuItemName = centerCircle.getLabel(); String[] stringArray = menuItemName.split("\n"); paint.setColor(textColor); paint.setAlpha(textAlpha); paint.setStyle(Paint.Style.FILL); paint.setTextSize(textSize); Rect rectText = new Rect(); Rect rectIcon = new Rect(); Drawable drawable = getResources().getDrawable(centerCircle.getIcon()); int h = getIconSize(drawable.getIntrinsicHeight(),MinIconSize,MaxIconSize); int w = getIconSize(drawable.getIntrinsicWidth(),MinIconSize,MaxIconSize); rectIcon.set(xPosition-w/2, yPosition-h/2, xPosition+w/2, yPosition+h/2); float textHeight = 0; for (int j = 0; j < stringArray.length; j++) { paint.getTextBounds(stringArray[j],0,stringArray[j].length(),rectText); textHeight = textHeight+(rectText.height()+3); } rectIcon.set(rectIcon.left, rectIcon.top-((int)textHeight/2), rectIcon.right, rectIcon.bottom-((int)textHeight/2)); float textBottom = rectIcon.bottom; for (int j = 0; j < stringArray.length; j++) { paint.getTextBounds(stringArray[j],0,stringArray[j].length(),rectText); float textLeft = xPosition - rectText.width()/2; textBottom = textBottom + (rectText.height()+3); c.drawText(stringArray[j], textLeft-rectText.left, textBottom-rectText.bottom, paint); } //Puts in the Icon drawable.setBounds(rectIcon); drawable.setAlpha(pictureAlpha); drawable.draw(c); //Icon Only } else if (centerCircle.getIcon() != 0) { Rect rect = new Rect(); Drawable drawable = getResources().getDrawable(centerCircle.getIcon()); int h = getIconSize(drawable.getIntrinsicHeight(),MinIconSize,MaxIconSize); int w = getIconSize(drawable.getIntrinsicWidth(),MinIconSize,MaxIconSize); rect.set(xPosition-w/2, yPosition-h/2, xPosition+w/2, yPosition+h/2); drawable.setBounds(rect); drawable.setAlpha(pictureAlpha); drawable.draw(c); //Text Only } else { //Puts in the Text if no Icon paint.setColor(textColor); paint.setAlpha(textAlpha); paint.setStyle(Paint.Style.FILL); paint.setTextSize(textSize); //This will look for a "new line" and split into multiple lines String menuItemName = centerCircle.getLabel(); String[] stringArray = menuItemName.split("\n"); //gets total height Rect rect = new Rect(); float textHeight = 0; for (int j = 0; j < stringArray.length; j++) { paint.getTextBounds(stringArray[j],0,stringArray[j].length(),rect); textHeight = textHeight+(rect.height()+3); } float textBottom = yPosition-(textHeight/2); for (int j = 0; j < stringArray.length; j++) { paint.getTextBounds(stringArray[j],0,stringArray[j].length(),rect); float textLeft = xPosition - rect.width()/2; textBottom = textBottom + (rect.height()+3); c.drawText(stringArray[j], textLeft-rect.left, textBottom-rect.bottom, paint); } } // Draws Text in TextBox if (headerString != null) { paint.setTextSize(headerTextSize); paint.getTextBounds(headerString,0,headerString.length(),this.textRect); if (HeaderBoxBounded == false) { determineHeaderBox(); HeaderBoxBounded = true; } paint.setColor(outlineColor); paint.setAlpha(outlineAlpha); paint.setStyle(Paint.Style.STROKE); c.drawRoundRect(this.textBoxRect, scalePX(5), scalePX(5), paint); paint.setColor(headerBackgroundColor); paint.setAlpha(headerBackgroundAlpha); paint.setStyle(Paint.Style.FILL); c.drawRoundRect(this.textBoxRect, scalePX(5), scalePX(5), paint); paint.setColor(headerTextColor); paint.setAlpha(headerTextAlpha); paint.setStyle(Paint.Style.FILL); paint.setTextSize(headerTextSize); c.drawText(headerString, headerTextLeft, headerTextBottom, paint); } } private int getIconSize(int iconSize, int minSize, int maxSize) { if (iconSize > minSize) { if (iconSize > maxSize) { return maxSize; } else { //iconSize < maxSize return iconSize; } } else { //iconSize < minSize return minSize; } } private void onOpenAnimation() { //rotate = new RotateAnimation(0, 360, xPosition, yPosition); //rotate.setRepeatMode(Animation.REVERSE); //rotate.setRepeatCount(Animation.INFINITE); scale = new ScaleAnimation(0, 1, 0, 1, xPosition, yPosition); //scale.setRepeatMode(Animation.REVERSE); //scale.setRepeatCount(Animation.INFINITE); scale.setInterpolator(new DecelerateInterpolator()); move = new TranslateAnimation(xSource-xPosition, 0, ySource-yPosition, 0); spriteAnimation = new AnimationSet(true); //spriteAnimation.addAnimation(rotate); spriteAnimation.addAnimation(scale); spriteAnimation.addAnimation(move); spriteAnimation.setDuration(animationSpeed); startAnimation(spriteAnimation); } private void onCloseAnimation() { //rotate = new RotateAnimation(360, 0, xPosition, yPosition); scale = new ScaleAnimation(1, 0, 1, 0, xPosition, yPosition); scale.setInterpolator(new AccelerateInterpolator()); move = new TranslateAnimation(0, xSource-xPosition, 0, ySource-yPosition); spriteAnimation = new AnimationSet(true); //spriteAnimation.addAnimation(rotate); spriteAnimation.addAnimation(scale); spriteAnimation.addAnimation(move); spriteAnimation.setDuration(animationSpeed); startAnimation(spriteAnimation); } private boolean pntInCircle(double px, double py, double x1, double y1, double radius) { double diffX = x1 - px; double diffY = y1 - py; double dist = diffX*diffX + diffY*diffY; return dist < radius*radius; } private boolean pntInWedge(double px, double py, float xRadiusCenter, float yRadiusCenter, int innerRadius, int outerRadius, double startAngle, double sweepAngle) { double diffX = px-xRadiusCenter; double diffY = py-yRadiusCenter; double angle = Math.atan2(diffY,diffX); if (angle < 0) angle += (2*Math.PI); if (startAngle >= (2*Math.PI)) { startAngle = startAngle-(2*Math.PI); } //checks if point falls between the start and end of the wedge if ((angle >= startAngle && angle <= startAngle + sweepAngle) || (angle+(2*Math.PI) >= startAngle && (angle+(2*Math.PI)) <= startAngle + sweepAngle)) { // checks if point falls inside the radius of the wedge double dist = diffX*diffX + diffY*diffY; if (dist < outerRadius*outerRadius && dist > innerRadius*innerRadius) { return true; } } return false; } public boolean addMenuEntry( RadialMenuEntry entry ) { menuEntries.add( entry ); determineWedges(); return true; } public boolean setCenterCircle( RadialMenuEntry entry ) { centerCircle = entry; return true; } public void setInnerRingRadius( int InnerRadius, int OuterRadius ) { this.MinSize = scalePX(InnerRadius); this.MaxSize = scalePX(OuterRadius); determineWedges(); } public void setOuterRingRadius( int InnerRadius, int OuterRadius ) { this.r2MinSize = scalePX(InnerRadius); this.r2MaxSize = scalePX(OuterRadius); determineWedges(); } public void setCenterCircleRadius( int centerRadius ) { this.cRadius = scalePX(centerRadius); determineWedges(); } public void setTextSize( int TextSize ) { this.textSize = scalePX(TextSize); this.animateTextSize = this.textSize; } public void setIconSize( int minIconSize, int maxIconSize ) { this.MinIconSize = scalePX(minIconSize); this.MaxIconSize = scalePX(maxIconSize); determineWedges(); } public void setCenterLocation( int x, int y ) { this.xPosition = x; this.yPosition = y; determineWedges(); onOpenAnimation(); } public void setSourceLocation( int x, int y ) { this.xSource = x; this.ySource = y; onOpenAnimation(); } public void setShowSourceLocation( boolean showSourceLocation ) { this.showSource = showSourceLocation; onOpenAnimation(); } public void setAnimationSpeed( long millis ) { this.animationSpeed = millis; onOpenAnimation(); } public void setInnerRingColor( int color, int alpha ) { this.defaultColor = color; this.defaultAlpha = alpha; } public void setOuterRingColor( int color, int alpha ) { this.wedge2Color = color; this.wedge2Alpha = alpha; } public void setOutlineColor( int color, int alpha ) { this.outlineColor = color; this.outlineAlpha = alpha; } public void setSelectedColor( int color, int alpha ) { this.selectedColor = color; this.selectedAlpha = alpha; } public void setDisabledColor( int color, int alpha ) { this.disabledColor = color; this.disabledAlpha = alpha; } public void setTextColor( int color, int alpha ) { this.textColor = color; this.textAlpha = alpha; } public void setHeader( String header, int TextSize ) { this.headerString = header; this.headerTextSize = scalePX(TextSize); HeaderBoxBounded = false; } public void setHeaderColors( int TextColor, int TextAlpha, int BgColor, int BgAlpha ) { this.headerTextColor = TextColor; this.headerTextAlpha = TextAlpha; this.headerBackgroundColor = BgColor; this.headerBackgroundAlpha = BgAlpha; } private int scalePX( int dp_size ) { int px_size = (int) (dp_size * screen_density + 0.5f); return px_size; } private int getSizeX() { DisplayMetrics displayMetrics = getContext().getResources().getDisplayMetrics(); Float f = displayMetrics.widthPixels / displayMetrics.density; return f.intValue(); } private int getSizeY() { DisplayMetrics displayMetrics = getContext().getResources().getDisplayMetrics(); Float f = displayMetrics.heightPixels / displayMetrics.density; return f.intValue(); } private void animateOuterWedges( int animation_direction) { boolean animationComplete = false; //Wedge 2 float slice2 = 360 / wedgeQty2; float start_slice2 = 270 - (slice2/2); //calculates where to put the images double rSlice2 = (2*Math.PI) / wedgeQty2; double rStart2 = (2*Math.PI)*(0.75) - (rSlice2/2); this.Wedges2 = new Wedge[wedgeQty2]; this.iconRect2 = new Rect[wedgeQty2]; Wedge2Shown = true; int wedgeSizeChange = (r2MaxSize-r2MinSize)/animateSections; if (animation_direction==ANIMATE_OUT) { if ( r2MinSize+r2VariableSize+wedgeSizeChange < r2MaxSize) { r2VariableSize += wedgeSizeChange; } else { animateOuterOut = false; r2VariableSize = r2MaxSize - r2MinSize; animationComplete = true; } //animates text size change this.animateTextSize = (textSize/animateSections) * (r2VariableSize/wedgeSizeChange); //calculates new wedge sizes for (int i = 0; i < Wedges2.length; i++) { this.Wedges2[i] = new Wedge(xPosition, yPosition, r2MinSize, r2MinSize+r2VariableSize, (i * slice2)+start_slice2, slice2); float xCenter = (float)(Math.cos(((rSlice2*i)+(rSlice2*0.5))+rStart2) * (r2MinSize+r2VariableSize+r2MinSize)/2)+xPosition; float yCenter = (float)(Math.sin(((rSlice2*i)+(rSlice2*0.5))+rStart2) * (r2MinSize+r2VariableSize+r2MinSize)/2)+yPosition; int h = MaxIconSize; int w = MaxIconSize; if ( wedge2Data.getChildren().get(i).getIcon() != 0 ) { Drawable drawable = getResources().getDrawable(wedge2Data.getChildren().get(i).getIcon()); h = getIconSize(drawable.getIntrinsicHeight(),MinIconSize,MaxIconSize); w = getIconSize(drawable.getIntrinsicWidth(),MinIconSize,MaxIconSize); } if (r2VariableSize < h) { h = r2VariableSize; } if (r2VariableSize < w) { w = r2VariableSize; } this.iconRect2[i] = new Rect((int) xCenter-w/2, (int) yCenter-h/2, (int) xCenter+w/2, (int) yCenter+h/2); int widthOffset = MaxSize; if (widthOffset < this.textRect.width()/2) { widthOffset = this.textRect.width()/2+scalePX(3); } this.textBoxRect.set((xPosition - (widthOffset)), (int) (yPosition - (r2MinSize+r2VariableSize) - headerBuffer-this.textRect.height()-scalePX(3)), (xPosition + (widthOffset)), (yPosition - (r2MinSize+r2VariableSize) - headerBuffer+scalePX(3))); this.headerTextBottom = yPosition - (r2MinSize+r2VariableSize) - headerBuffer-this.textRect.bottom; } } else if (animation_direction==ANIMATE_IN) { if ( r2MinSize < r2MaxSize-r2VariableSize-wedgeSizeChange) { r2VariableSize += wedgeSizeChange; } else { animateOuterIn = false; r2VariableSize = r2MaxSize; animationComplete = true; } //animates text size change this.animateTextSize = textSize - ((textSize/animateSections) * (r2VariableSize/wedgeSizeChange)); for (int i = 0; i < Wedges2.length; i++) { this.Wedges2[i] = new Wedge(xPosition, yPosition, r2MinSize, r2MaxSize-r2VariableSize, (i * slice2)+start_slice2, slice2); float xCenter = (float)(Math.cos(((rSlice2*i)+(rSlice2*0.5))+rStart2) * (r2MaxSize-r2VariableSize+r2MinSize)/2)+xPosition; float yCenter = (float)(Math.sin(((rSlice2*i)+(rSlice2*0.5))+rStart2) * (r2MaxSize-r2VariableSize+r2MinSize)/2)+yPosition; int h = MaxIconSize; int w = MaxIconSize; if ( wedge2Data.getChildren().get(i).getIcon() != 0 ) { Drawable drawable = getResources().getDrawable(wedge2Data.getChildren().get(i).getIcon()); h = getIconSize(drawable.getIntrinsicHeight(),MinIconSize,MaxIconSize); w = getIconSize(drawable.getIntrinsicWidth(),MinIconSize,MaxIconSize); } if (r2MaxSize-r2MinSize-r2VariableSize < h) { h = r2MaxSize-r2MinSize-r2VariableSize; } if (r2MaxSize-r2MinSize-r2VariableSize < w) { w = r2MaxSize-r2MinSize-r2VariableSize; } this.iconRect2[i] = new Rect((int) xCenter-w/2, (int) yCenter-h/2, (int) xCenter+w/2, (int) yCenter+h/2); //computes header text box int heightOffset = r2MaxSize-r2VariableSize; int widthOffset = MaxSize; if (MaxSize > r2MaxSize-r2VariableSize) {heightOffset = MaxSize;} if (widthOffset < this.textRect.width()/2) { widthOffset = this.textRect.width()/2+scalePX(3); } this.textBoxRect.set((xPosition - (widthOffset)), (int) (yPosition - (heightOffset) - headerBuffer-this.textRect.height()-scalePX(3)), (xPosition + (widthOffset)), (yPosition - (heightOffset) - headerBuffer+scalePX(3))); this.headerTextBottom = yPosition - (heightOffset) - headerBuffer-this.textRect.bottom; } } if (animationComplete == true) { r2VariableSize = 0; this.animateTextSize = textSize; if (animation_direction==ANIMATE_IN) { Wedge2Shown = false; } } invalidate(); //re-draws the picture } private void determineWedges() { int entriesQty = menuEntries.size(); if ( entriesQty > 0) { wedgeQty = entriesQty; float degSlice = 360 / wedgeQty; float start_degSlice = 270 - (degSlice/2); //calculates where to put the images double rSlice = (2*Math.PI) / wedgeQty; double rStart = (2*Math.PI)*(0.75) - (rSlice/2); this.Wedges = new Wedge[wedgeQty]; this.iconRect = new Rect[wedgeQty]; for (int i = 0; i < Wedges.length; i++) { this.Wedges[i] = new Wedge(xPosition, yPosition, MinSize, MaxSize, (i * degSlice)+start_degSlice, degSlice); float xCenter = (float)(Math.cos(((rSlice*i)+(rSlice*0.5))+rStart) * (MaxSize+MinSize)/2)+xPosition; float yCenter = (float)(Math.sin(((rSlice*i)+(rSlice*0.5))+rStart) * (MaxSize+MinSize)/2)+yPosition; int h = MaxIconSize; int w = MaxIconSize; if ( menuEntries.get(i).getIcon() != 0 ) { Drawable drawable = getResources().getDrawable(menuEntries.get(i).getIcon()); h = getIconSize(drawable.getIntrinsicHeight(),MinIconSize,MaxIconSize); w = getIconSize(drawable.getIntrinsicWidth(),MinIconSize,MaxIconSize); } this.iconRect[i] = new Rect( (int) xCenter-w/2, (int) yCenter-h/2, (int) xCenter+w/2, (int) yCenter+h/2); } invalidate(); //re-draws the picture } } private void determineOuterWedges(RadialMenuEntry entry) { int entriesQty = entry.getChildren().size(); wedgeQty2 = entriesQty; //Wedge 2 float degSlice2 = 360 / wedgeQty2; float start_degSlice2 = 270 - (degSlice2/2); //calculates where to put the images double rSlice2 = (2*Math.PI) / wedgeQty2; double rStart2 = (2*Math.PI)*(0.75) - (rSlice2/2); this.Wedges2 = new Wedge[wedgeQty2]; this.iconRect2 = new Rect[wedgeQty2]; for (int i = 0; i < Wedges2.length; i++) { this.Wedges2[i] = new Wedge(xPosition, yPosition, r2MinSize, r2MaxSize, (i * degSlice2)+start_degSlice2, degSlice2); float xCenter = (float)(Math.cos(((rSlice2*i)+(rSlice2*0.5))+rStart2) * (r2MaxSize+r2MinSize)/2)+xPosition; float yCenter = (float)(Math.sin(((rSlice2*i)+(rSlice2*0.5))+rStart2) * (r2MaxSize+r2MinSize)/2)+yPosition; int h = MaxIconSize; int w = MaxIconSize; if ( entry.getChildren().get(i).getIcon() != 0 ) { Drawable drawable = getResources().getDrawable(entry.getChildren().get(i).getIcon()); h = getIconSize(drawable.getIntrinsicHeight(),MinIconSize,MaxIconSize); w = getIconSize(drawable.getIntrinsicWidth(),MinIconSize,MaxIconSize); } this.iconRect2[i] = new Rect((int) xCenter-w/2, (int) yCenter-h/2, (int) xCenter+w/2, (int) yCenter+h/2); } this.wedge2Data = entry; invalidate(); //re-draws the picture } private void determineHeaderBox() { this.headerTextLeft = xPosition - this.textRect.width()/2; this.headerTextBottom = yPosition - (MaxSize) - headerBuffer-this.textRect.bottom; int offset = MaxSize; if (offset < this.textRect.width()/2) { offset = this.textRect.width()/2+scalePX(3); } this.textBoxRect.set((xPosition - (offset)), (int) (yPosition - (MaxSize) - headerBuffer-this.textRect.height()-scalePX(3)), (xPosition + (offset)), (yPosition - (MaxSize) - headerBuffer+scalePX(3))); } public class Wedge extends Path { private int x, y; private int InnerSize, OuterSize; private float StartArc; private float ArcWidth; private Wedge(int x, int y, int InnerSize, int OuterSize, float StartArc, float ArcWidth) { super(); if (StartArc >= 360) { StartArc = StartArc-360; } this.x = x; this.y = y; this.InnerSize = InnerSize; this.OuterSize = OuterSize; this.StartArc = StartArc; this.ArcWidth = ArcWidth; this.buildPath(); } private void buildPath() { final RectF rect = new RectF(); final RectF rect2 = new RectF(); //Rectangles values rect.set(this.x-this.InnerSize, this.y-this.InnerSize, this.x+this.InnerSize, this.y+this.InnerSize); rect2.set(this.x-this.OuterSize, this.y-this.OuterSize, this.x+this.OuterSize, this.y+this.OuterSize); this.reset(); //this.moveTo(100, 100); this.arcTo(rect2, StartArc, ArcWidth); this.arcTo(rect, StartArc+ArcWidth, -ArcWidth); this.close(); } } }