Merge branch 'feature/ring-view' into dev

pull/84/merge
Alinson S. Xavier 10 years ago
commit 7e4c508d7d

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

@ -25,7 +25,7 @@ import android.test.suitebuilder.annotation.SmallTest;
import org.isoron.uhabits.helpers.DateHelper;
import org.isoron.uhabits.models.Habit;
import org.isoron.uhabits.unit.HabitFixtures;
import org.isoron.uhabits.views.CheckmarkView;
import org.isoron.uhabits.views.CheckmarkWidgetView;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -34,9 +34,9 @@ import java.io.IOException;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class CheckmarkViewTest extends ViewTest
public class CheckmarkWidgetViewTest extends ViewTest
{
private CheckmarkView view;
private CheckmarkWidgetView view;
private Habit habit;
@Before
@ -45,7 +45,7 @@ public class CheckmarkViewTest extends ViewTest
super.setup();
habit = HabitFixtures.createShortHabit();
view = new CheckmarkView(targetContext);
view = new CheckmarkWidgetView(targetContext);
view.setHabit(habit);
refreshData(view);
measureView(dpToPixels(100), dpToPixels(200), view);

@ -62,7 +62,7 @@ public class HabitScoreViewTest extends ViewTest
@Test
public void testRender_withTransparentBackground() throws Throwable
{
view.setIsBackgroundTransparent(true);
view.setIsTransparencyEnabled(true);
assertRenders(view, "HabitScoreView/renderTransparent.png");
}

@ -19,6 +19,7 @@
package org.isoron.uhabits.unit.views;
import android.graphics.Color;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
@ -42,10 +43,11 @@ public class RingViewTest extends ViewTest
super.setup();
view = new RingView(targetContext);
view.setLabel("Hello world");
view.setPercentage(0.6f);
view.setText("60%");
view.setColor(ColorHelper.CSV_PALETTE[0]);
view.setMaxDiameter(dpToPixels(100));
view.setBackgroundColor(Color.WHITE);
view.setThickness(dpToPixels(3));
}
@Test
@ -55,21 +57,10 @@ public class RingViewTest extends ViewTest
assertRenders(view, "RingView/render.png");
}
@Test
public void testRender_withLongLabel() throws IOException
{
view.setLabel("The quick brown fox jumps over the lazy fox");
measureView(dpToPixels(100), dpToPixels(100), view);
assertRenders(view, "RingView/renderLongLabel.png");
}
@Test
public void testRender_withDifferentParams() throws IOException
{
view.setLabel("Habit Strength");
view.setPercentage(0.25f);
view.setMaxDiameter(dpToPixels(50));
view.setColor(ColorHelper.CSV_PALETTE[5]);
measureView(dpToPixels(200), dpToPixels(200), view);

@ -133,7 +133,7 @@ abstract public class BaseActivity extends AppCompatActivity implements Thread.U
if(toolbar == null) return;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
toolbar.setElevation(UIHelper.dpToPixels(this, 3));
toolbar.setElevation(UIHelper.dpToPixels(this, 2));
setSupportActionBar(toolbar);
@ -197,5 +197,8 @@ abstract public class BaseActivity extends AppCompatActivity implements Thread.U
{
View view = findViewById(R.id.toolbarShadow);
if(view != null) view.setVisibility(View.GONE);
view = findViewById(R.id.headerShadow);
if(view != null) view.setVisibility(View.GONE);
}
}

@ -61,6 +61,7 @@ import org.isoron.uhabits.helpers.DateHelper;
import org.isoron.uhabits.helpers.HintManager;
import org.isoron.uhabits.helpers.ListHabitsHelper;
import org.isoron.uhabits.helpers.ReminderHelper;
import org.isoron.uhabits.helpers.UIHelper;
import org.isoron.uhabits.helpers.UIHelper.OnSavedListener;
import org.isoron.uhabits.loaders.HabitListLoader;
import org.isoron.uhabits.models.Habit;
@ -121,7 +122,7 @@ public class ListHabitsFragment extends Fragment
loader.setCheckmarkCount(helper.getButtonCount());
llHint.setOnClickListener(this);
tvStarEmpty.setTypeface(helper.getFontawesome());
tvStarEmpty.setTypeface(UIHelper.getFontAwesome(activity));
adapter = new HabitListAdapter(getActivity(), loader);
adapter.setSelectedPositions(selectedPositions);

@ -42,6 +42,7 @@ import org.isoron.uhabits.commands.Command;
import org.isoron.uhabits.dialogs.EditHabitDialogFragment;
import org.isoron.uhabits.dialogs.HistoryEditorDialog;
import org.isoron.uhabits.helpers.ColorHelper;
import org.isoron.uhabits.helpers.DateHelper;
import org.isoron.uhabits.helpers.ReminderHelper;
import org.isoron.uhabits.helpers.UIHelper;
import org.isoron.uhabits.models.Habit;
@ -78,6 +79,13 @@ public class ShowHabitFragment extends Fragment
private int previousScoreInterval;
private float todayScore;
private float lastMonthScore;
private float lastYearScore;
private int activeColor;
private int inactiveColor;
@Override
public void onStart()
{
@ -90,7 +98,12 @@ public class ShowHabitFragment extends Fragment
{
View view = inflater.inflate(R.layout.show_habit, container, false);
activity = (ShowHabitActivity) getActivity();
habit = activity.getHabit();
activeColor = ColorHelper.getColor(getContext(), habit.color);
inactiveColor = UIHelper.getStyledColor(getContext(), R.attr.mediumContrastTextColor);
updateHeader(view);
dataViews = new LinkedList<>();
@ -105,7 +118,6 @@ public class ShowHabitFragment extends Fragment
previousScoreInterval = defaultScoreInterval;
setScoreBucketSize(defaultScoreInterval);
sStrengthInterval.setSelection(defaultScoreInterval);
sStrengthInterval.setOnItemSelectedListener(this);
@ -147,6 +159,46 @@ public class ShowHabitFragment extends Fragment
return view;
}
private void updateHeader(View view)
{
if(habit == null) return;
TextView questionLabel = (TextView) view.findViewById(R.id.questionLabel);
questionLabel.setTextColor(activeColor);
questionLabel.setText(habit.description);
TextView reminderLabel = (TextView) view.findViewById(R.id.reminderLabel);
if(habit.hasReminder())
reminderLabel.setText(DateHelper.formatTime(getActivity(), habit.reminderHour,
habit.reminderMin));
else
reminderLabel.setText(getResources().getString(R.string.reminder_off));
TextView frequencyLabel = (TextView) view.findViewById(R.id.frequencyLabel);
String freqText;
if(habit.freqNum.equals(habit.freqDen))
freqText = getResources().getString(R.string.every_day);
else if(habit.freqNum == 1 && habit.freqDen == 7)
freqText = getResources().getString(R.string.every_week);
else
{
String times_every;
if(habit.freqNum == 1)
times_every = getResources().getString(R.string.time_every);
else
times_every = getResources().getString(R.string.times_every);
freqText = String.format("%d %s %d %s", habit.freqNum, times_every, habit.freqDen,
getResources().getString(R.string.days));
}
frequencyLabel.setText(freqText);
if(habit.description.isEmpty())
questionLabel.setVisibility(View.GONE);
}
@Override
public void onResume()
{
@ -154,18 +206,34 @@ public class ShowHabitFragment extends Fragment
refreshData();
}
private void updateScoreRing(View view)
private void updateScore(View view)
{
if(habit == null) return;
if(view == null) return;
float todayValue = (float) habit.scores.getTodayValue();
float percentage = todayValue / Score.MAX_VALUE;
float todayPercentage = todayScore / Score.MAX_VALUE;
float monthDiff = todayPercentage - (lastMonthScore / Score.MAX_VALUE);
float yearDiff = todayPercentage - (lastYearScore / Score.MAX_VALUE);
RingView scoreRing = (RingView) view.findViewById(R.id.scoreRing);
int androidColor = ColorHelper.getColor(getActivity(), habit.color);
scoreRing.setColor(androidColor);
scoreRing.setPercentage(percentage);
scoreRing.setPercentage(todayPercentage);
TextView scoreLabel = (TextView) view.findViewById(R.id.scoreLabel);
TextView monthDiffLabel = (TextView) view.findViewById(R.id.monthDiffLabel);
TextView yearDiffLabel = (TextView) view.findViewById(R.id.yearDiffLabel);
scoreLabel.setText(String.format("%.0f%%", todayPercentage * 100));
String minus = "\u2212";
monthDiffLabel.setText(String.format("%s%.0f%%", (monthDiff >= 0 ? "+" : minus),
Math.abs(monthDiff) * 100));
yearDiffLabel.setText(
String.format("%s%.0f%%", (yearDiff >= 0 ? "+" : minus), Math.abs(yearDiff) * 100));
monthDiffLabel.setTextColor(monthDiff >= 0 ? activeColor : inactiveColor);
yearDiffLabel.setTextColor(yearDiff >= 0 ? activeColor : inactiveColor);
}
private void updateHeaders(View view)
@ -175,6 +243,7 @@ public class ShowHabitFragment extends Fragment
updateColor(view, R.id.tvStrength);
updateColor(view, R.id.tvStreaks);
updateColor(view, R.id.tvWeekdayFreq);
updateColor(view, R.id.scoreLabel);
}
private void updateColor(View view, int viewId)
@ -236,25 +305,32 @@ public class ShowHabitFragment extends Fragment
{
new BaseTask()
{
float percentage;
@Override
protected void doInBackground()
{
if(habit == null) return;
if(dataViews == null) return;
long today = DateHelper.getStartOfToday();
long lastMonth = today - 30 * DateHelper.millisecondsInOneDay;
long lastYear = today - 365 * DateHelper.millisecondsInOneDay;
todayScore = (float) habit.scores.getTodayValue();
lastMonthScore = (float) habit.scores.getValue(lastMonth);
lastYearScore = (float) habit.scores.getValue(lastYear);
int count = 0;
for(HabitDataView view : dataViews)
{
view.refreshData();
onProgressUpdate(count++);
publishProgress(count++);
}
}
@Override
protected void onProgressUpdate(Integer... values)
{
updateScoreRing(getView());
updateScore(getView());
if(dataViews == null) return;
dataViews.get(values[0]).postInvalidate();
}

@ -29,19 +29,19 @@ public class ColorHelper
{
public static int CSV_PALETTE[] =
{
Color.parseColor("#D32F2F"), // red
Color.parseColor("#E64A19"), // orange
Color.parseColor("#F9A825"), // yellow
Color.parseColor("#AFB42B"), // light green
Color.parseColor("#388E3C"), // dark green
Color.parseColor("#00897B"), // teal
Color.parseColor("#00ACC1"), // cyan
Color.parseColor("#039BE5"), // blue
Color.parseColor("#5E35B1"), // deep purple
Color.parseColor("#8E24AA"), // purple
Color.parseColor("#D81B60"), // pink
Color.parseColor("#303030"), // dark grey
Color.parseColor("#aaaaaa") // light grey
Color.parseColor("#D32F2F"), // 0 red
Color.parseColor("#E64A19"), // 1 orange
Color.parseColor("#F9A825"), // 2 yellow
Color.parseColor("#AFB42B"), // 3 light green
Color.parseColor("#388E3C"), // 4 dark green
Color.parseColor("#00897B"), // 5 teal
Color.parseColor("#00ACC1"), // 6 cyan
Color.parseColor("#039BE5"), // 7 blue
Color.parseColor("#5E35B1"), // 8 deep purple
Color.parseColor("#8E24AA"), // 9 purple
Color.parseColor("#D81B60"), // 10 pink
Color.parseColor("#303030"), // 11 dark grey
Color.parseColor("#aaaaaa") // 12 light grey
};
public static int colorToPaletteIndex(Context context, int color)
@ -112,6 +112,12 @@ public class ColorHelper
return setHSVParameter(color, newValue, 2);
}
public static int setAlpha(int color, float newAlpha)
{
int intAlpha = (int) (newAlpha * 255);
return Color.argb(intAlpha, Color.red(color), Color.green(color), Color.blue(color));
}
public static int setMinValue(int color, float newValue)
{
float hsv[] = new float[3];

@ -20,7 +20,6 @@
package org.isoron.uhabits.helpers;
import android.content.Context;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.view.LayoutInflater;
import android.view.MotionEvent;
@ -33,6 +32,7 @@ import org.isoron.uhabits.R;
import org.isoron.uhabits.loaders.HabitListLoader;
import org.isoron.uhabits.models.Habit;
import org.isoron.uhabits.models.Score;
import org.isoron.uhabits.views.RingView;
import java.util.GregorianCalendar;
@ -43,7 +43,6 @@ public class ListHabitsHelper
private final Context context;
private final HabitListLoader loader;
private Typeface fontawesome;
public ListHabitsHelper(Context context, HabitListLoader loader)
{
@ -52,12 +51,6 @@ public class ListHabitsHelper
lowContrastColor = UIHelper.getStyledColor(context, R.attr.lowContrastTextColor);
mediumContrastColor = UIHelper.getStyledColor(context, R.attr.mediumContrastTextColor);
fontawesome = Typeface.createFromAsset(context.getAssets(), "fontawesome-webfont.ttf");
}
public Typeface getFontawesome()
{
return fontawesome;
}
public int getButtonCount()
@ -105,46 +98,24 @@ public class ListHabitsHelper
public void initializeLabelAndIcon(View itemView)
{
TextView tvStar = (TextView) itemView.findViewById(R.id.tvStar);
tvStar.setTypeface(getFontawesome());
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(getHabitNameWidth(),
LinearLayout.LayoutParams.WRAP_CONTENT, 1);
itemView.findViewById(R.id.label).setLayoutParams(params);
}
public void updateNameAndIcon(Habit habit, TextView tvStar, TextView tvName)
public void updateNameAndIcon(Habit habit, RingView ring, TextView tvName)
{
int activeColor = getActiveColor(habit);
tvName.setText(habit.name);
tvName.setTextColor(activeColor);
if (habit.isArchived())
{
tvStar.setText(context.getString(R.string.fa_archive));
tvStar.setTextColor(activeColor);
}
else
{
int score = loader.scores.get(habit.getId());
float percentage = (float) score / Score.MAX_VALUE;
if (score < Score.HALF_STAR_CUTOFF)
{
tvStar.setText(context.getString(R.string.fa_star_o));
tvStar.setTextColor(lowContrastColor);
}
else if (score < Score.FULL_STAR_CUTOFF)
{
tvStar.setText(context.getString(R.string.fa_star_half_o));
tvStar.setTextColor(lowContrastColor);
}
else
{
tvStar.setText(context.getString(R.string.fa_star));
tvStar.setTextColor(activeColor);
}
}
ring.setColor(activeColor);
ring.setPercentage(percentage);
ring.setPrecision(1.0f / 16);
}
public void updateCheckmark(int activeColor, TextView tvCheck, int check)
@ -184,7 +155,7 @@ public class ListHabitsHelper
public void updateHabitCard(View view, Habit habit, boolean selected)
{
TextView tvStar = ((TextView) view.findViewById(R.id.tvStar));
RingView scoreRing = ((RingView) view.findViewById(R.id.scoreRing));
TextView tvName = (TextView) view.findViewById(R.id.label);
LinearLayout llInner = (LinearLayout) view.findViewById(R.id.llInner);
LinearLayout llButtons = (LinearLayout) view.findViewById(R.id.llButtons);
@ -192,7 +163,7 @@ public class ListHabitsHelper
llInner.setTag(R.string.habit_key, habit.getId());
llInner.setOnTouchListener(new HotspotTouchListener());
updateNameAndIcon(habit, tvStar, tvName);
updateNameAndIcon(habit, scoreRing, tvName);
updateCheckmarkButtons(habit, llButtons);
updateHabitCardBackground(llInner, selected);
}
@ -227,7 +198,7 @@ public class ListHabitsHelper
{
View check = inflater.inflate(R.layout.list_habits_item_check, null);
TextView btCheck = (TextView) check.findViewById(R.id.tvCheck);
btCheck.setTypeface(fontawesome);
btCheck.setTypeface(UIHelper.getFontAwesome(context));
btCheck.setOnLongClickListener(onLongClickListener);
btCheck.setOnClickListener(onClickListener);
btCheck.setHapticFeedbackEnabled(false);

@ -50,13 +50,21 @@ public abstract class UIHelper
public static final int THEME_LIGHT = 0;
public static final int THEME_DARK = 1;
private static Typeface fontawesome;
private static Typeface fontAwesome;
public interface OnSavedListener
{
void onSaved(Command command, Object savedObject);
}
public static Typeface getFontAwesome(Context context)
{
if(fontAwesome == null)
fontAwesome = Typeface.createFromAsset(context.getAssets(), "fontawesome-webfont.ttf");
return fontAwesome;
}
public static void showSoftKeyboard(View view)
{
InputMethodManager imm = (InputMethodManager) view.getContext()
@ -94,6 +102,14 @@ public abstract class UIHelper
else return defaultValue;
}
public static Integer getColorAttribute(Context context, AttributeSet attrs, String name,
Integer defaultValue)
{
int resId = attrs.getAttributeResourceValue(ISORON_NAMESPACE, name, 0);
if (resId != 0) return context.getResources().getColor(resId);
else return defaultValue;
}
public static int getIntAttribute(Context context, AttributeSet attrs, String name,
int defaultValue)
{
@ -102,6 +118,14 @@ public abstract class UIHelper
else return defaultValue;
}
public static boolean getBooleanAttribute(Context context, AttributeSet attrs, String name,
boolean defaultValue)
{
String boolText = getAttribute(context, attrs, name, null);
if(boolText != null) return Boolean.parseBoolean(boolText);
else return defaultValue;
}
public static float getFloatAttribute(Context context, AttributeSet attrs, String name,
float defaultValue)
{
@ -206,6 +230,16 @@ public abstract class UIHelper
return bool;
}
public static float getStyledFloat(Context context, int attrId)
{
int[] attrs = new int[]{ attrId };
TypedArray ta = context.obtainStyledAttributes(attrs);
float f = ta.getFloat(0, 0);
ta.recycle();
return f;
}
static int getStyleResource(Context context, int attrId)
{
int[] attr = new int[] { attrId };

@ -1,203 +0,0 @@
/*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
* Loop Habit Tracker is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Loop Habit Tracker is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.views;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.os.Build;
import android.text.Layout;
import android.text.StaticLayout;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.view.View;
import org.isoron.uhabits.R;
import org.isoron.uhabits.helpers.ColorHelper;
import org.isoron.uhabits.models.Habit;
public class CheckmarkView extends View implements HabitDataView
{
private Paint pCard;
private Paint pIcon;
private int primaryColor;
private int timesColor;
private int darkGrey;
private int width;
private int height;
private float leftMargin;
private float topMargin;
private float padding;
private String label;
private String fa_check;
private String fa_times;
private int check_status;
private Rect rect;
private TextPaint textPaint;
private StaticLayout labelLayout;
private Habit habit;
public CheckmarkView(Context context)
{
super(context);
init(context);
}
public CheckmarkView(Context context, AttributeSet attrs)
{
super(context, attrs);
init(context);
}
private void init(Context context)
{
Typeface fontawesome =
Typeface.createFromAsset(context.getAssets(), "fontawesome-webfont.ttf");
pCard = new Paint();
pCard.setAntiAlias(true);
pIcon = new Paint();
pIcon.setAntiAlias(true);
pIcon.setTypeface(fontawesome);
pIcon.setTextAlign(Paint.Align.CENTER);
textPaint = new TextPaint();
textPaint.setColor(Color.WHITE);
textPaint.setAntiAlias(true);
fa_check = context.getString(R.string.fa_check);
fa_times = context.getString(R.string.fa_times);
primaryColor = ColorHelper.getColor(getContext(), 10);
timesColor = Color.argb(128, 255, 255, 255);
darkGrey = Color.argb(64, 0, 0, 0);
rect = new Rect();
check_status = 0;
label = "Habit";
}
public void setHabit(Habit habit)
{
this.habit = habit;
}
@Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
drawBackground(canvas);
drawCheckmark(canvas);
drawLabel(canvas);
}
private void drawBackground(Canvas canvas)
{
int color = (check_status == 2 ? primaryColor : darkGrey);
pCard.setColor(color);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
canvas.drawRoundRect(leftMargin, topMargin, width - leftMargin, height - topMargin, padding,
padding, pCard);
else
canvas.drawRect(leftMargin, topMargin, width - leftMargin, height - topMargin, pCard);
}
private void drawCheckmark(Canvas canvas)
{
String text = (check_status == 0 ? fa_times : fa_check);
int color = (check_status == 2 ? Color.WHITE : timesColor);
pIcon.setColor(color);
pIcon.setTextSize(width * 0.5f);
pIcon.getTextBounds(text, 0, 1, rect);
int y = (int) ((0.67f * height - rect.bottom - rect.top) / 2);
canvas.drawText(text, width / 2, y, pIcon);
}
private void drawLabel(Canvas canvas)
{
canvas.save();
float y;
int nLines = labelLayout.getLineCount();
if(nLines == 1)
y = height * 0.8f - padding;
else
y = height * 0.7f - padding;
canvas.translate(leftMargin + padding, y);
labelLayout.draw(canvas);
canvas.restore();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
int width = MeasureSpec.getSize(widthMeasureSpec);
setMeasuredDimension(width, (int) (width * 1.25));
}
@Override
protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight)
{
this.width = getMeasuredWidth();
this.height = getMeasuredHeight();
leftMargin = (width * 0.015f);
topMargin = (height * 0.015f);
padding = 8 * leftMargin;
textPaint.setTextSize(0.15f * width);
updateLabel();
}
public void refreshData()
{
this.check_status = habit.checkmarks.getTodayValue();
int color = ColorHelper.getColor(getContext(), habit.color);
this.primaryColor = Color.argb(230, Color.red(color), Color.green(color),
Color.blue(color));
this.label = habit.name;
updateLabel();
postInvalidate();
}
private void updateLabel()
{
textPaint.setColor(Color.WHITE);
labelLayout = new StaticLayout(label, textPaint,
(int) (width - 2 * leftMargin - 2 * padding),
Layout.Alignment.ALIGN_CENTER, 1.0f, 0.0f, false);
}
}

@ -0,0 +1,169 @@
/*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
* Loop Habit Tracker is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Loop Habit Tracker is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.views;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.widget.TextView;
import org.isoron.uhabits.R;
import org.isoron.uhabits.helpers.ColorHelper;
import org.isoron.uhabits.helpers.UIHelper;
import org.isoron.uhabits.models.Checkmark;
import org.isoron.uhabits.models.Habit;
import org.isoron.uhabits.models.Score;
public class CheckmarkWidgetView extends HabitWidgetView implements HabitDataView
{
private int activeColor;
private float percentage;
@Nullable
private String name;
@Nullable
private RingView ring;
private TextView label;
private int checkmarkValue;
public CheckmarkWidgetView(Context context)
{
super(context);
init();
}
public CheckmarkWidgetView(Context context, AttributeSet attrs)
{
super(context, attrs);
init();
}
private void init()
{
ring = (RingView) findViewById(R.id.scoreRing);
label = (TextView) findViewById(R.id.label);
if(ring != null) ring.setIsTransparencyEnabled(true);
if(isInEditMode())
{
percentage = 0.75f;
name = "Wake up early";
activeColor = ColorHelper.CSV_PALETTE[6];
checkmarkValue = Checkmark.CHECKED_EXPLICITLY;
refresh();
}
}
@Override
public void setHabit(@NonNull Habit habit)
{
super.setHabit(habit);
this.name = habit.name;
this.activeColor = ColorHelper.getColor(getContext(), habit.color);
refresh();
}
public void refresh()
{
if (backgroundPaint == null || frame == null || ring == null) return;
Context context = getContext();
String text;
int backgroundColor;
int foregroundColor;
switch (checkmarkValue)
{
case Checkmark.CHECKED_EXPLICITLY:
text = getResources().getString(R.string.fa_check);
backgroundColor = activeColor;
foregroundColor =
UIHelper.getStyledColor(context, R.attr.highContrastReverseTextColor);
setShadowAlpha(0x4f);
rebuildBackground();
backgroundPaint.setColor(backgroundColor);
frame.setBackgroundDrawable(background);
break;
case Checkmark.CHECKED_IMPLICITLY:
text = getResources().getString(R.string.fa_check);
backgroundColor = UIHelper.getStyledColor(context, R.attr.cardBackgroundColor);
foregroundColor = UIHelper.getStyledColor(context, R.attr.mediumContrastTextColor);
break;
case Checkmark.UNCHECKED:
default:
text = getResources().getString(R.string.fa_times);
backgroundColor = UIHelper.getStyledColor(context, R.attr.cardBackgroundColor);
foregroundColor = UIHelper.getStyledColor(context, R.attr.mediumContrastTextColor);
break;
}
ring.setPercentage(percentage);
ring.setColor(foregroundColor);
ring.setBackgroundColor(backgroundColor);
ring.setText(text);
label.setText(name);
label.setTextColor(foregroundColor);
requestLayout();
postInvalidate();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
float w = width;
float h = width * 1.25f;
float scale = Math.min(width / w, height / h);
w *= scale;
h *= scale;
widthMeasureSpec = MeasureSpec.makeMeasureSpec((int) w, MeasureSpec.EXACTLY);
heightMeasureSpec = MeasureSpec.makeMeasureSpec((int) h, MeasureSpec.EXACTLY);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
public void refreshData()
{
if(habit == null) return;
this.percentage = (float) habit.scores.getTodayValue() / Score.MAX_VALUE;
this.checkmarkValue = habit.checkmarks.getTodayValue();
refresh();
}
@NonNull
protected Integer getInnerLayoutId()
{
return R.layout.widget_checkmark;
}
}

@ -0,0 +1,78 @@
/*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
* Loop Habit Tracker is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Loop Habit Tracker is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.views;
import android.content.Context;
import android.support.annotation.NonNull;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import org.isoron.uhabits.R;
import org.isoron.uhabits.models.Habit;
public class GraphWidgetView extends HabitWidgetView implements HabitDataView
{
private final HabitDataView dataView;
private TextView title;
public GraphWidgetView(Context context, HabitDataView dataView)
{
super(context);
this.dataView = dataView;
init();
}
private void init()
{
ViewGroup.LayoutParams params =
new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
((View) dataView).setLayoutParams(params);
ViewGroup innerFrame = (ViewGroup) findViewById(R.id.innerFrame);
innerFrame.addView(((View) dataView));
title = (TextView) findViewById(R.id.title);
title.setVisibility(VISIBLE);
}
@Override
public void setHabit(@NonNull Habit habit)
{
super.setHabit(habit);
dataView.setHabit(habit);
title.setText(habit.name);
}
@Override
public void refreshData()
{
if(habit == null) return;
dataView.refreshData();
}
@NonNull
protected Integer getInnerLayoutId()
{
return R.layout.widget_graph;
}
}

@ -21,7 +21,6 @@ package org.isoron.uhabits.views;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
@ -104,22 +103,10 @@ public class HabitFrequencyView extends ScrollableDataView implements HabitDataV
this.primaryColor = ColorHelper.getColor(getContext(), habit.color);
}
if (isBackgroundTransparent)
{
primaryColor = ColorHelper.setSaturation(primaryColor, 0.75f);
primaryColor = ColorHelper.setValue(primaryColor, 1.0f);
textColor = Color.argb(192, 255, 255, 255);
gridColor = Color.argb(128, 255, 255, 255);
}
else
{
textColor = UIHelper.getStyledColor(getContext(), R.attr.mediumContrastTextColor);
gridColor = UIHelper.getStyledColor(getContext(), R.attr.lowContrastTextColor);
}
colors = new int[4];
colors[0] = gridColor;
colors[3] = primaryColor;
colors[1] = ColorHelper.mixColors(colors[0], colors[3], 0.66f);

@ -20,6 +20,7 @@
package org.isoron.uhabits.views;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
@ -72,11 +73,14 @@ public class HabitScoreView extends ScrollableDataView implements HabitDataView
private int[] scores;
private int primaryColor;
private boolean isBackgroundTransparent;
private int bucketSize = 7;
private int footerHeight;
private int backgroundColor;
private Bitmap drawingCache;
private Canvas cacheCanvas;
private boolean isTransparencyEnabled;
public HabitScoreView(Context context)
{
super(context);
@ -114,21 +118,10 @@ public class HabitScoreView extends ScrollableDataView implements HabitDataView
if(habit != null)
this.primaryColor = ColorHelper.getColor(getContext(), habit.color);
if (isBackgroundTransparent)
{
primaryColor = ColorHelper.setSaturation(primaryColor, 0.75f);
primaryColor = ColorHelper.setValue(primaryColor, 1.0f);
textColor = Color.argb(192, 255, 255, 255);
gridColor = Color.argb(128, 255, 255, 255);
}
else
{
textColor = UIHelper.getStyledColor(getContext(), R.attr.mediumContrastTextColor);
gridColor = UIHelper.getStyledColor(getContext(), R.attr.lowContrastTextColor);
backgroundColor = UIHelper.getStyledColor(getContext(), R.attr.cardBackgroundColor);
}
}
protected void createPaints()
{
@ -180,6 +173,16 @@ public class HabitScoreView extends ScrollableDataView implements HabitDataView
pGraph.setTextSize(baseSize * 0.5f);
pGraph.setStrokeWidth(baseSize * 0.1f);
pGrid.setStrokeWidth(baseSize * 0.025f);
if(isTransparencyEnabled)
initCache(width, height);
}
private void initCache(int width, int height)
{
if (drawingCache != null) drawingCache.recycle();
drawingCache = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
cacheCanvas = new Canvas(drawingCache);
}
public void refreshData()
@ -218,12 +221,26 @@ public class HabitScoreView extends ScrollableDataView implements HabitDataView
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
Canvas activeCanvas;
if(isTransparencyEnabled)
{
if(drawingCache == null) initCache(getWidth(), getHeight());
activeCanvas = cacheCanvas;
drawingCache.eraseColor(Color.TRANSPARENT);
}
else
{
activeCanvas = canvas;
}
if (habit == null || scores == null) return;
rect.set(0, 0, nColumns * columnWidth, columnHeight);
rect.offset(0, paddingTop);
drawGrid(canvas, rect);
drawGrid(activeCanvas, rect);
pText.setColor(textColor);
pGraph.setColor(primaryColor);
@ -253,20 +270,23 @@ public class HabitScoreView extends ScrollableDataView implements HabitDataView
if (!prevRect.isEmpty())
{
drawLine(canvas, prevRect, rect);
drawMarker(canvas, prevRect);
drawLine(activeCanvas, prevRect, rect);
drawMarker(activeCanvas, prevRect);
}
if (k == nColumns - 1) drawMarker(canvas, rect);
if (k == nColumns - 1) drawMarker(activeCanvas, rect);
prevRect.set(rect);
rect.set(0, 0, columnWidth, columnHeight);
rect.offset(k * columnWidth, paddingTop);
drawFooter(canvas, rect, currentDate);
drawFooter(activeCanvas, rect, currentDate);
currentDate += bucketSize * DateHelper.millisecondsInOneDay;
}
if(activeCanvas != canvas)
canvas.drawBitmap(drawingCache, 0, 0, null);
}
private int skipYear = 0;
@ -364,19 +384,20 @@ public class HabitScoreView extends ScrollableDataView implements HabitDataView
setModeOrColor(pGraph, XFERMODE_CLEAR, backgroundColor);
canvas.drawOval(rect, pGraph);
if(isBackgroundTransparent)
if(isTransparencyEnabled)
pGraph.setXfermode(XFERMODE_SRC);
}
public void setIsBackgroundTransparent(boolean isBackgroundTransparent)
public void setIsTransparencyEnabled(boolean enabled)
{
this.isBackgroundTransparent = isBackgroundTransparent;
this.isTransparencyEnabled = enabled;
createColors();
requestLayout();
}
private void setModeOrColor(Paint p, PorterDuffXfermode mode, int color)
{
if(isBackgroundTransparent)
if(isTransparencyEnabled)
p.setXfermode(mode);
else
p.setColor(color);

@ -127,28 +127,10 @@ public class HabitStreakView extends View implements HabitDataView
if(habit != null)
this.primaryColor = ColorHelper.getColor(getContext(), habit.color);
if(isBackgroundTransparent)
{
primaryColor = ColorHelper.setSaturation(primaryColor, 0.75f);
primaryColor = ColorHelper.setValue(primaryColor, 1.0f);
}
int red = Color.red(primaryColor);
int green = Color.green(primaryColor);
int blue = Color.blue(primaryColor);
if(isBackgroundTransparent)
{
colors = new int[4];
colors[3] = primaryColor;
colors[2] = Color.argb(213, red, green, blue);
colors[1] = Color.argb(170, red, green, blue);
colors[0] = Color.argb(128, red, green, blue);
textColor = Color.WHITE;
reverseTextColor = Color.WHITE;
}
else
{
colors = new int[4];
colors[3] = primaryColor;
colors[2] = Color.argb(192, red, green, blue);
@ -157,7 +139,6 @@ public class HabitStreakView extends View implements HabitDataView
textColor = UIHelper.getStyledColor(getContext(), R.attr.mediumContrastTextColor);
reverseTextColor = UIHelper.getStyledColor(getContext(), R.attr.highContrastReverseTextColor);
}
}
protected void createPaints()
{

@ -0,0 +1,118 @@
/*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
* Loop Habit Tracker is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Loop Habit Tracker is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.views;
import android.content.Context;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.drawable.InsetDrawable;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.RoundRectShape;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import org.isoron.uhabits.R;
import org.isoron.uhabits.helpers.UIHelper;
import org.isoron.uhabits.models.Habit;
import java.util.Arrays;
public abstract class HabitWidgetView extends FrameLayout implements HabitDataView
{
@Nullable
protected InsetDrawable background;
@Nullable
protected Paint backgroundPaint;
@Nullable
protected Habit habit;
protected ViewGroup frame;
public void setShadowAlpha(int shadowAlpha)
{
this.shadowAlpha = shadowAlpha;
}
private int shadowAlpha;
public HabitWidgetView(Context context)
{
super(context);
init();
}
public HabitWidgetView(Context context, AttributeSet attrs)
{
super(context, attrs);
init();
}
private void init()
{
inflate(getContext(), getInnerLayoutId(), this);
shadowAlpha = (int) (255 * UIHelper.getStyledFloat(getContext(), R.attr.widgetShadowAlpha));
rebuildBackground();
}
protected abstract @NonNull Integer getInnerLayoutId();
protected void rebuildBackground()
{
Context context = getContext();
context.setTheme(R.style.TransparentWidgetTheme);
int backgroundAlpha =
(int) (255 * UIHelper.getStyledFloat(context, R.attr.widgetBackgroundAlpha));
int shadowRadius = (int) UIHelper.dpToPixels(context, 2);
int shadowOffset = (int) UIHelper.dpToPixels(context, 1);
int shadowColor = Color.argb(shadowAlpha, 0, 0, 0);
float cornerRadius = UIHelper.dpToPixels(context, 5);
float[] radii = new float[8];
Arrays.fill(radii, cornerRadius);
RoundRectShape shape = new RoundRectShape(radii, null, null);
ShapeDrawable innerDrawable = new ShapeDrawable(shape);
int insetLeftTop = Math.max(shadowRadius - shadowOffset, 0);
int insetRightBottom = shadowRadius + shadowOffset;
background = new InsetDrawable(innerDrawable, insetLeftTop, insetLeftTop, insetRightBottom,
insetRightBottom);
backgroundPaint = innerDrawable.getPaint();
backgroundPaint.setShadowLayer(shadowRadius, shadowOffset, shadowOffset, shadowColor);
backgroundPaint.setColor(UIHelper.getStyledColor(context, R.attr.cardBackgroundColor));
backgroundPaint.setAlpha(backgroundAlpha);
frame = (ViewGroup) findViewById(R.id.frame);
if(frame != null) frame.setBackgroundDrawable(background);
}
@Override
public void setHabit(@NonNull Habit habit)
{
this.habit = habit;
}
}

@ -19,42 +19,59 @@
package org.isoron.uhabits.views;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.text.Layout;
import android.text.StaticLayout;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.view.View;
import org.isoron.uhabits.R;
import org.isoron.uhabits.helpers.ColorHelper;
import org.isoron.uhabits.helpers.UIHelper;
public class RingView extends View
{
public static final PorterDuffXfermode XFERMODE_CLEAR =
new PorterDuffXfermode(PorterDuff.Mode.CLEAR);
private int color;
private float precision;
private float percentage;
private float labelMarginTop;
private TextPaint pRing;
private String label;
private int diameter;
private float thickness;
private RectF rect;
private StaticLayout labelLayout;
private TextPaint pRing;
private int width;
private int height;
private float diameter;
private Integer backgroundColor;
private Integer inactiveColor;
private float maxDiameter;
private float em;
private String text;
private float textSize;
private int textColor;
private int backgroundColor;
private boolean enableFontAwesome;
private Bitmap drawingCache;
private Canvas cacheCanvas;
private boolean isTransparencyEnabled;
public RingView(Context context)
{
super(context);
percentage = 0.0f;
precision = 0.01f;
color = ColorHelper.CSV_PALETTE[0];
thickness = UIHelper.dpToPixels(getContext(), 2);
text = "";
textSize = context.getResources().getDimension(R.dimen.smallTextSize);
init();
}
@ -62,9 +79,24 @@ public class RingView extends View
{
super(context, attrs);
this.label = UIHelper.getAttribute(context, attrs, "label", "Label");
this.maxDiameter = UIHelper.getFloatAttribute(context, attrs, "maxDiameter", 100);
this.maxDiameter = UIHelper.dpToPixels(context, maxDiameter);
percentage = UIHelper.getFloatAttribute(context, attrs, "percentage", 0);
precision = UIHelper.getFloatAttribute(context, attrs, "precision", 0.01f);
color = UIHelper.getColorAttribute(context, attrs, "color", 0);
backgroundColor = UIHelper.getColorAttribute(context, attrs, "backgroundColor", null);
inactiveColor = UIHelper.getColorAttribute(context, attrs, "inactiveColor", null);
thickness = UIHelper.getFloatAttribute(context, attrs, "thickness", 0);
thickness = UIHelper.dpToPixels(context, thickness);
float defaultTextSize = context.getResources().getDimension(R.dimen.smallTextSize);
textSize = UIHelper.getFloatAttribute(context, attrs, "textSize", defaultTextSize);
textSize = UIHelper.spToPixels(context, textSize);
text = UIHelper.getAttribute(context, attrs, "text", "");
enableFontAwesome = UIHelper.getBooleanAttribute(context, attrs, "enableFontAwesome", false);
init();
}
@ -74,19 +106,34 @@ public class RingView extends View
postInvalidate();
}
public void setMaxDiameter(float maxDiameter)
@Override
public void setBackgroundColor(int backgroundColor)
{
this.maxDiameter = maxDiameter;
this.backgroundColor = backgroundColor;
postInvalidate();
}
public void setLabel(String label)
public void setPercentage(float percentage)
{
this.label = label;
this.percentage = percentage;
postInvalidate();
}
public void setPercentage(float percentage)
public void setPrecision(float precision)
{
this.percentage = percentage;
this.precision = precision;
postInvalidate();
}
public void setThickness(float thickness)
{
this.thickness = thickness;
postInvalidate();
}
public void setText(String text)
{
this.text = text;
postInvalidate();
}
@ -97,61 +144,94 @@ public class RingView extends View
pRing.setColor(color);
pRing.setTextAlign(Paint.Align.CENTER);
if(backgroundColor == null)
backgroundColor = UIHelper.getStyledColor(getContext(), R.attr.cardBackgroundColor);
textColor = UIHelper.getStyledColor(getContext(), R.attr.mediumContrastTextColor);
textSize = getResources().getDimension(R.dimen.smallTextSize);
if(inactiveColor == null)
inactiveColor = UIHelper.getStyledColor(getContext(), R.attr.highContrastTextColor);
inactiveColor = ColorHelper.setAlpha(inactiveColor, 0.1f);
rect = new RectF();
}
@Override
@SuppressLint("DrawAllocation")
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
width = MeasureSpec.getSize(widthMeasureSpec);
height = MeasureSpec.getSize(heightMeasureSpec);
diameter = Math.min(maxDiameter, width);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
diameter = Math.min(height, width);
pRing.setTextSize(textSize);
labelMarginTop = textSize * 0.80f;
labelLayout = new StaticLayout(label, pRing, width, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0f,
false);
em = pRing.measureText("M");
width = Math.max(width, labelLayout.getWidth());
height = (int) (diameter + labelLayout.getHeight() + labelMarginTop);
setMeasuredDimension(diameter, diameter);
}
setMeasuredDimension(width, height);
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh)
{
super.onSizeChanged(w, h, oldw, oldh);
if(isTransparencyEnabled)
{
if (drawingCache != null) drawingCache.recycle();
drawingCache = Bitmap.createBitmap(diameter, diameter, Bitmap.Config.ARGB_8888);
cacheCanvas = new Canvas(drawingCache);
}
}
@Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
float thickness = diameter * 0.15f;
Canvas activeCanvas;
if(isTransparencyEnabled)
{
activeCanvas = cacheCanvas;
drawingCache.eraseColor(Color.TRANSPARENT);
}
else
{
activeCanvas = canvas;
}
pRing.setColor(color);
rect.set(0, 0, diameter, diameter);
rect.offset((width - diameter) / 2, 0);
canvas.drawArc(rect, -90, 360 * percentage, true, pRing);
int grey = UIHelper.getStyledColor(getContext(), R.attr.lowContrastTextColor);
pRing.setColor(grey);
canvas.drawArc(rect, 360 * percentage - 90 + 2, 360 * (1 - percentage) - 4, true, pRing);
float angle = 360 * Math.round(percentage / precision) * precision;
activeCanvas.drawArc(rect, -90, angle, true, pRing);
pRing.setColor(inactiveColor);
activeCanvas.drawArc(rect, angle - 90, 360 - angle, true, pRing);
if(thickness > 0)
{
if(isTransparencyEnabled)
pRing.setXfermode(XFERMODE_CLEAR);
else
pRing.setColor(backgroundColor);
rect.inset(thickness, thickness);
canvas.drawArc(rect, -90, 360, true, pRing);
activeCanvas.drawArc(rect, 0, 360, true, pRing);
pRing.setXfermode(null);
pRing.setColor(textColor);
pRing.setTextSize(textSize);
float lineHeight = pRing.getFontSpacing();
canvas.drawText(String.format("%.0f%%", percentage * 100), rect.centerX(),
rect.centerY() + lineHeight / 3, pRing);
pRing.setColor(color);
pRing.setTextSize(textSize);
canvas.translate(width / 2, diameter + labelMarginTop);
labelLayout.draw(canvas);
if(enableFontAwesome) pRing.setTypeface(UIHelper.getFontAwesome(getContext()));
activeCanvas.drawText(text, rect.centerX(), rect.centerY() + 0.4f * em, pRing);
}
if(activeCanvas != canvas)
canvas.drawBitmap(drawingCache, 0, 0, null);
}
public void setIsTransparencyEnabled(boolean isTransparencyEnabled)
{
this.isTransparencyEnabled = isTransparencyEnabled;
}
}

@ -33,6 +33,7 @@ import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.RemoteViews;
import android.widget.TextView;
import org.isoron.uhabits.R;
import org.isoron.uhabits.helpers.UIHelper;
@ -123,23 +124,27 @@ public abstract class BaseWidgetProvider extends AppWidgetProvider
protected abstract void refreshCustomViewData(View widgetView);
private void savePreview(Context context, int widgetId, Bitmap widgetCache)
private void savePreview(Context context, int widgetId, Bitmap widgetCache, int width,
int height, String label)
{
try
{
LayoutInflater inflater = LayoutInflater.from(context);
View view = inflater.inflate(getLayoutId(), null);
TextView tvLabel = (TextView) view.findViewById(R.id.label);
if(tvLabel != null) tvLabel.setText(label);
ImageView iv = (ImageView) view.findViewById(R.id.imageView);
iv.setImageBitmap(widgetCache);
if(iv != null) iv.setImageBitmap(widgetCache);
view.measure(portraitWidth, portraitHeight);
view.measure(width, height);
view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
view.setDrawingCacheEnabled(true);
view.buildDrawingCache();
Bitmap previewCache = view.getDrawingCache();
String filename = String.format("%s/%d.png", context.getExternalCacheDir(), widgetId);
String filename = String.format("%s/%d_%d.png", context.getExternalCacheDir(), widgetId, width);
Log.d("BaseWidgetProvider", String.format("Writing %s", filename));
FileOutputStream out = new FileOutputStream(filename);
@ -254,8 +259,8 @@ public abstract class BaseWidgetProvider extends AppWidgetProvider
{
try
{
buildRemoteViews(portraitWidgetView, portraitRemoteViews);
buildRemoteViews(landscapeWidgetView, landscapeRemoteViews);
buildRemoteViews(portraitWidgetView, portraitRemoteViews, portraitWidth, portraitHeight);
buildRemoteViews(landscapeWidgetView, landscapeRemoteViews, landscapeWidth, landscapeHeight);
updateAppWidget();
}
catch (Exception e)
@ -267,7 +272,7 @@ public abstract class BaseWidgetProvider extends AppWidgetProvider
super.onPostExecute(aVoid);
}
private void buildRemoteViews(View widgetView, RemoteViews remoteViews)
private void buildRemoteViews(View widgetView, RemoteViews remoteViews, int width, int height)
{
widgetView.invalidate();
widgetView.setDrawingCacheEnabled(true);
@ -276,11 +281,29 @@ public abstract class BaseWidgetProvider extends AppWidgetProvider
remoteViews.setTextViewText(R.id.label, habit.name);
remoteViews.setImageViewBitmap(R.id.imageView, drawingCache);
//savePreview(context, widgetId, drawingCache);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
{
int imageWidth = widgetView.getMeasuredWidth();
int imageHeight = widgetView.getMeasuredHeight();
int p[] = getPadding(width, height, imageWidth, imageHeight);
remoteViews.setViewPadding(R.id.buttonOverlay, p[0], p[1], p[2], p[3]);
}
//savePreview(context, widgetId, drawingCache, width, height, habit.name);
PendingIntent onClickIntent = getOnClickPendingIntent(context, habit);
if (onClickIntent != null) remoteViews.setOnClickPendingIntent(R.id.imageView,
if (onClickIntent != null) remoteViews.setOnClickPendingIntent(R.id.button,
onClickIntent);
}
}
private int[] getPadding(int entireWidth, int entireHeight, int imageWidth,
int imageHeight)
{
int w = (int) (((float) entireWidth - imageWidth) / 2);
int h = (int) (((float) entireHeight - imageHeight) / 2);
return new int[]{ w, h, w, h };
}
}

@ -25,7 +25,7 @@ import android.view.View;
import org.isoron.uhabits.HabitBroadcastReceiver;
import org.isoron.uhabits.R;
import org.isoron.uhabits.models.Habit;
import org.isoron.uhabits.views.CheckmarkView;
import org.isoron.uhabits.views.CheckmarkWidgetView;
import org.isoron.uhabits.views.HabitDataView;
public class CheckmarkWidgetProvider extends BaseWidgetProvider
@ -33,7 +33,7 @@ public class CheckmarkWidgetProvider extends BaseWidgetProvider
@Override
protected View buildCustomView(Context context, Habit habit)
{
CheckmarkView view = new CheckmarkView(context);
CheckmarkWidgetView view = new CheckmarkWidgetView(context);
view.setHabit(habit);
return view;
}
@ -65,7 +65,7 @@ public class CheckmarkWidgetProvider extends BaseWidgetProvider
@Override
protected int getLayoutId()
{
return R.layout.widget_checkmark;
return R.layout.widget_wrapper;
}

@ -26,6 +26,7 @@ import android.view.View;
import org.isoron.uhabits.HabitBroadcastReceiver;
import org.isoron.uhabits.R;
import org.isoron.uhabits.models.Habit;
import org.isoron.uhabits.views.GraphWidgetView;
import org.isoron.uhabits.views.HabitDataView;
import org.isoron.uhabits.views.HabitFrequencyView;
@ -34,8 +35,8 @@ public class FrequencyWidgetProvider extends BaseWidgetProvider
@Override
protected View buildCustomView(Context context, Habit habit)
{
HabitFrequencyView view = new HabitFrequencyView(context, null);
view.setIsBackgroundTransparent(true);
HabitFrequencyView dataView = new HabitFrequencyView(context);
GraphWidgetView view = new GraphWidgetView(context, dataView);
view.setHabit(habit);
return view;
}
@ -67,6 +68,6 @@ public class FrequencyWidgetProvider extends BaseWidgetProvider
@Override
protected int getLayoutId()
{
return R.layout.widget_graph;
return R.layout.widget_wrapper;
}
}

@ -25,6 +25,7 @@ import android.view.View;
import org.isoron.uhabits.HabitBroadcastReceiver;
import org.isoron.uhabits.R;
import org.isoron.uhabits.models.Habit;
import org.isoron.uhabits.views.GraphWidgetView;
import org.isoron.uhabits.views.HabitDataView;
import org.isoron.uhabits.views.HabitHistoryView;
@ -33,9 +34,9 @@ public class HistoryWidgetProvider extends BaseWidgetProvider
@Override
protected View buildCustomView(Context context, Habit habit)
{
HabitHistoryView view = new HabitHistoryView(context, null);
HabitHistoryView dataView = new HabitHistoryView(context);
GraphWidgetView view = new GraphWidgetView(context, dataView);
view.setHabit(habit);
view.setIsBackgroundTransparent(true);
return view;
}
@ -66,6 +67,6 @@ public class HistoryWidgetProvider extends BaseWidgetProvider
@Override
protected int getLayoutId()
{
return R.layout.widget_graph;
return R.layout.widget_wrapper;
}
}

@ -25,6 +25,7 @@ import android.view.View;
import org.isoron.uhabits.HabitBroadcastReceiver;
import org.isoron.uhabits.R;
import org.isoron.uhabits.models.Habit;
import org.isoron.uhabits.views.GraphWidgetView;
import org.isoron.uhabits.views.HabitDataView;
import org.isoron.uhabits.views.HabitScoreView;
@ -33,8 +34,9 @@ public class ScoreWidgetProvider extends BaseWidgetProvider
@Override
protected View buildCustomView(Context context, Habit habit)
{
HabitScoreView view = new HabitScoreView(context, null);
view.setIsBackgroundTransparent(true);
HabitScoreView dataView = new HabitScoreView(context);
dataView.setIsTransparencyEnabled(true);
GraphWidgetView view = new GraphWidgetView(context, dataView);
view.setHabit(habit);
return view;
}
@ -66,6 +68,6 @@ public class ScoreWidgetProvider extends BaseWidgetProvider
@Override
protected int getLayoutId()
{
return R.layout.widget_graph;
return R.layout.widget_wrapper;
}
}

@ -25,6 +25,7 @@ import android.view.View;
import org.isoron.uhabits.HabitBroadcastReceiver;
import org.isoron.uhabits.R;
import org.isoron.uhabits.models.Habit;
import org.isoron.uhabits.views.GraphWidgetView;
import org.isoron.uhabits.views.HabitDataView;
import org.isoron.uhabits.views.HabitStreakView;
@ -33,8 +34,8 @@ public class StreakWidgetProvider extends BaseWidgetProvider
@Override
protected View buildCustomView(Context context, Habit habit)
{
HabitStreakView view = new HabitStreakView(context, null);
view.setIsBackgroundTransparent(true);
HabitStreakView dataView = new HabitStreakView(context);
GraphWidgetView view = new GraphWidgetView(context, dataView);
view.setHabit(habit);
return view;
}
@ -66,6 +67,6 @@ public class StreakWidgetProvider extends BaseWidgetProvider
@Override
protected int getLayoutId()
{
return R.layout.widget_graph;
return R.layout.widget_wrapper;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 610 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 610 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 388 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 403 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

@ -0,0 +1,39 @@
<!--
~ Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
~
~ This file is part of Loop Habit Tracker.
~
~ Loop Habit Tracker is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by the
~ Free Software Foundation, either version 3 of the License, or (at your
~ option) any later version.
~
~ Loop Habit Tracker is distributed in the hope that it will be useful, but
~ WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
~ more details.
~
~ You should have received a copy of the GNU General Public License along
~ with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:top="1dp"
android:left="1dp"
android:bottom="3dp"
android:right="3dp">
<ripple
android:color="#60ffffff">
<item android:id="@android:id/mask">
<shape android:shape="rectangle">
<corners android:radius="5dp"/>
<solid android:color="?android:colorPrimary"/>
</shape>
<color android:color="@color/white"/>
</item>
</ripple>
</item>
</layer-list>

Binary file not shown.

After

Width:  |  Height:  |  Size: 798 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 821 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 243 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 257 B

@ -0,0 +1,34 @@
<!--
~ Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
~
~ This file is part of Loop Habit Tracker.
~
~ Loop Habit Tracker is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by the
~ Free Software Foundation, either version 3 of the License, or (at your
~ option) any later version.
~
~ Loop Habit Tracker is distributed in the hope that it will be useful, but
~ WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
~ more details.
~
~ You should have received a copy of the GNU General Public License along
~ with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="true" android:state_pressed="true">
<layer-list>
<item android:bottom="3dp"
android:left="1dp"
android:right="3dp"
android:top="1dp">
<shape android:shape="rectangle">
<corners android:radius="5dp"/>
<solid android:color="#30ffffff"/>
</shape>
</item>
</layer-list>
</item>
</selector>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

@ -19,6 +19,7 @@
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:habit="http://isoron.org/android"
android:id="@+id/llOuter"
style="@style/ListHabits.Item">
@ -26,9 +27,14 @@
android:id="@+id/llInner"
style="@style/ListHabits.HabitCard">
<TextView
android:id="@+id/tvStar"
style="@style/ListHabits.Star" />
<org.isoron.uhabits.views.RingView
android:layout_height="15dp"
android:layout_width="15dp"
android:id="@+id/scoreRing"
habit:thickness="3"
android:layout_marginTop="0dp"
android:layout_marginRight="10dp"
android:layout_marginLeft="8dp"/>
<TextView
android:id="@+id/label"

@ -17,20 +17,102 @@
~ with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://isoron.org/android"
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:habit="http://isoron.org/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="?windowBackgroundColor"
android:clipToPadding="false"
android:fillViewport="true">
<LinearLayout
style="@style/CardList"
android:clipToPadding="false"
tools:context="org.isoron.uhabits.ShowHabitActivity">
<LinearLayout
android:id="@+id/subtitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?headerBackgroundColor"
android:elevation="2dp"
android:orientation="vertical"
android:paddingBottom="10dp"
android:paddingLeft="60dp"
android:paddingStart="60dp"
android:paddingRight="10dp"
android:paddingEnd="10dp"
android:paddingTop="15dp">
<TextView
android:id="@+id/questionLabel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:textColor="?mediumContrastTextColor"
android:textSize="@dimen/regularTextSize"
/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="5dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<ImageView
android:id="@+id/frequencyIcon"
android:layout_width="18dp"
android:layout_height="18dp"
android:layout_marginRight="5dp"
android:layout_marginEnd="5dp"
android:alpha="0.3"
android:src="?iconFrequency"
/>
<TextView
android:id="@+id/frequencyLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/every_day"
android:textColor="?mediumContrastTextColor"
android:textSize="@dimen/smallTextSize"
/>
<ImageView
android:id="@+id/reminderIcon"
android:layout_width="18dp"
android:layout_height="18dp"
android:layout_marginLeft="10dp"
android:layout_marginStart="10dp"
android:layout_marginRight="5dp"
android:layout_marginEnd="5dp"
android:alpha="0.3"
android:src="?iconReminder"
/>
<TextView
android:id="@+id/reminderLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="1dp"
android:textColor="?mediumContrastTextColor"
android:textSize="@dimen/smallTextSize"
/>
</LinearLayout>
</LinearLayout>
<View
android:id="@+id/headerShadow"
style="@style/ToolbarShadow"/>
<LinearLayout
style="@style/Card"
android:paddingTop="12dp"
android:gravity="start">
<TextView
@ -45,16 +127,80 @@
android:gravity="center"
android:orientation="horizontal">
<FrameLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="5">
<org.isoron.uhabits.views.RingView
android:id="@+id/scoreRing"
style="@style/SmallDataView"
android:layout_width="100dp"
app:label="@string/strength"
app:maxDiameter="80"
app:textSize="@dimen/smallTextSize"/>
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_gravity="center"
habit:percentage="0"
habit:textSize="12"
habit:thickness="5"/>
</FrameLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="4"
android:orientation="vertical">
<TextView
android:id="@+id/scoreLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/score"
android:textColor="?mediumContrastTextColor"/>
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="4"
android:orientation="vertical">
<TextView
android:id="@+id/monthDiffLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/month"
android:textColor="?mediumContrastTextColor"/>
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="4"
android:orientation="vertical">
<TextView
android:id="@+id/yearDiffLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/year"
android:textColor="?mediumContrastTextColor"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
<RelativeLayout
@ -65,20 +211,20 @@
android:id="@+id/sStrengthInterval"
android:layout_width="wrap_content"
android:layout_height="22dp"
android:entries="@array/strengthIntervalNames"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:entries="@array/strengthIntervalNames"
android:theme="@style/SmallSpinner"
/>
<TextView
android:id="@+id/tvStrength"
style="@style/CardHeader"
android:text="@string/habit_strength"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"/>
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:text="@string/habit_strength"/>
<org.isoron.uhabits.views.HabitScoreView
android:id="@+id/scoreView"

@ -17,10 +17,11 @@
~ with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
<RelativeLayout
android:id="@+id/container"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="org.isoron.uhabits.ShowHabitActivity"
@ -29,18 +30,17 @@
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
app:popupTheme="?toolbarPopupTheme"
style="@style/Toolbar"/>
style="@style/Toolbar"
app:popupTheme="?toolbarPopupTheme"/>
<fragment
android:id="@+id/fragment2"
android:name="org.isoron.uhabits.fragments.ShowHabitFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:layout="@layout/show_habit"
android:layout_below="@id/toolbar"
android:layout_gravity="center"
android:layout_below="@id/toolbar"/>
tools:layout="@layout/show_habit"/>
<View
android:id="@+id/toolbarShadow"

@ -19,18 +19,42 @@
-->
<LinearLayout
android:id="@+id/frame"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:habit="http://isoron.org/android"
android:gravity="center"
android:orientation="vertical"
android:padding="0dp">
android:orientation="vertical">
<ImageView
android:id="@+id/imageView"
<org.isoron.uhabits.views.RingView
android:id="@+id/scoreRing"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adjustViewBounds="true"
/>
android:layout_height="0dp"
android:layout_weight="1"
habit:thickness="2"
habit:textSize="16"
habit:enableFontAwesome="true"
android:layout_marginTop="8dp"
android:layout_marginLeft="12dp"
android:layout_marginRight="12dp"/>
<TextView
android:id="@+id/label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="0"
android:textSize="12sp"
android:textColor="@color/white"
android:layout_marginLeft="6dp"
android:layout_marginRight="6dp"
android:gravity="center"
android:scrollHorizontally="true"
android:ellipsize="end"
android:maxLines="2"
android:layout_marginTop="4dp"
android:layout_marginBottom="4dp"
android:fontFamily="sans-serif-condensed"
android:breakStrategy="balanced" />
</LinearLayout>

@ -18,29 +18,33 @@
~ with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<LinearLayout
<FrameLayout
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/frame"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/widget_background"
android:gravity="center"
android:orientation="vertical">
<LinearLayout
android:id="@+id/innerFrame"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="4dp">
android:paddingTop="4dp"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:paddingBottom="4dp"
tools:ignore="UselessParent">
<TextView
android:id="@+id/label"
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textColor="#ffffff"/>
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:adjustViewBounds="true"
/>
android:textColor="@color/white"/>
</LinearLayout>
</LinearLayout>
</FrameLayout>

@ -18,19 +18,32 @@
~ with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<LinearLayout
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:padding="4dp">
android:padding="0dp">
<org.isoron.uhabits.views.CheckmarkView
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adjustViewBounds="true"
/>
</LinearLayout>
<FrameLayout
android:id="@+id/buttonOverlay"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/widget_button_background"
/>
</FrameLayout>
</RelativeLayout>

@ -41,9 +41,14 @@
<attr name="iconEdit" format="reference"/>
<attr name="iconUnarchive" format="reference"/>
<attr name="dialogIconChangeColor" format="reference"/>
<attr name="iconReminder" format="reference"/>
<attr name="iconFrequency" format="reference"/>
<attr name="toolbarPopupTheme" format="reference"/>
<attr name="widgetShadowAlpha" format="float"/>
<attr name="widgetBackgroundAlpha" format="float"/>
<!-- Pre-Lollipop -->
<attr name="cardBackground" format="reference"/>
<attr name="headerBackground" format="reference"/>

@ -48,6 +48,22 @@
<item>@color/grey_500</item>
</array>
<array name="transparentWidgetPalette">
<item>@color/red_800</item>
<item>@color/deep_orange_800</item>
<item>@color/yellow_800</item>
<item>@color/lime_800</item>
<item>@color/green_700</item>
<item>@color/teal_700</item>
<item>@color/cyan_700</item>
<item>@color/light_blue_700</item>
<item>@color/deep_purple_700</item>
<item>@color/purple_700</item>
<item>@color/pink_700</item>
<item>@color/black_aa</item>
<item>@color/black_aa</item>
</array>
<!-- Time and Date picker -->
<color name="circle_background">#f2f2f2</color>
<color name="line_background">#cccccc</color>
@ -355,4 +371,22 @@
<color name="white">#ffffff</color>
<color name="black">#000000</color>
<color name="black_ae">#ef000000</color>
<color name="black_ac">#cf000000</color>
<color name="black_aa">#af000000</color>
<color name="black_a8">#8f000000</color>
<color name="black_a6">#6f000000</color>
<color name="black_a4">#4f000000</color>
<color name="black_a2">#2f000000</color>
<color name="black_a0">#0f000000</color>
<color name="white_ae">#efffffff</color>
<color name="white_ac">#cfffffff</color>
<color name="white_aa">#afffffff</color>
<color name="white_a8">#8fffffff</color>
<color name="white_a6">#6fffffff</color>
<color name="white_a4">#4fffffff</color>
<color name="white_a2">#2fffffff</color>
<color name="white_a0">#0fffffff</color>
</resources>

@ -156,6 +156,7 @@
<string name="bug_report_failed">Failed to generate bug report.</string>
<string name="generate_bug_report">Generate bug report</string>
<string name="troubleshooting">Troubleshooting</string>
<string name="help_translate">Help translate this app</string>
<string name="night_mode">Night mode</string>
<string name="use_pure_black">Pure black for night mode</string>
@ -167,4 +168,12 @@
<string name="month">Month</string>
<string name="quarter">Quarter</string>
<string name="year">Year</string>
<!-- Middle part of the sentence '1 time in xx days' -->
<string name="time_every">time in</string>
<string name="every_x_days">Every %d days</string>
<string name="every_x_weeks">Every %d weeks</string>
<string name="every_x_months">Every %d months</string>
<string name="score">Score</string>
</resources>

@ -56,10 +56,14 @@
<item name="iconEdit">@drawable/ic_action_edit_dark</item>
<item name="iconUnarchive">@drawable/ic_action_unarchive_dark</item>
<item name="iconChangeColor">@drawable/ic_action_color_dark</item>
<item name="iconFrequency">@drawable/ic_repeat_black</item>
<item name="iconReminder">@drawable/ic_alarm_black</item>
<item name="dialogIconChangeColor">@drawable/ic_action_color_light</item>
<item name="toolbarPopupTheme">@style/ThemeOverlay.AppCompat.Light</item>
<item name="aboutScreenColor">@color/blue_800</item>
<item name="widgetShadowAlpha">0.25</item>
<item name="widgetBackgroundAlpha">1</item>
</style>
<style name="SmallSpinner">
@ -95,7 +99,7 @@
<item name="lowContrastTextColor">@color/grey_800</item>
<item name="highContrastReverseTextColor">@color/grey_900</item>
<item name="mediumContrastReverseTextColor">@color/grey_700</item>
<item name="mediumContrastReverseTextColor">@color/grey_750</item>
<item name="lowContrastReverseTextColor">@color/grey_300</item>
<item name="iconAdd">@drawable/ic_action_add_dark</item>
@ -104,11 +108,17 @@
<item name="iconUnarchive">@drawable/ic_action_unarchive_dark</item>
<item name="iconChangeColor">@drawable/ic_action_color_dark</item>
<item name="dialogIconChangeColor">@drawable/ic_action_color_dark</item>
<item name="iconFrequency">@drawable/ic_repeat_white</item>
<item name="iconReminder">@drawable/ic_alarm_white</item>
<item name="toolbarPopupTheme">@style/ThemeOverlay.AppCompat</item>
<item name="palette">@array/darkPalette</item>
<item name="aboutScreenColor">@color/blue_300</item>
<item name="widgetShadowAlpha">0.25</item>
<item name="widgetBackgroundAlpha">1</item>
</style>
<style name="AppBaseThemeDark.PureBlack">
@ -127,6 +137,23 @@
<item name="highContrastReverseTextColor">@color/black</item>
</style>
<style name="TransparentWidgetTheme" parent="AppBaseThemeDark">
<item name="cardBackgroundColor">@color/black</item>
<item name="highContrastTextColor">@color/white</item>
<item name="mediumContrastTextColor">@color/white_ac</item>
<item name="lowContrastTextColor">@color/white_a0</item>
<item name="highContrastReverseTextColor">@color/white</item>
<item name="mediumContrastReverseTextColor">@color/grey_500</item>
<item name="lowContrastReverseTextColor">@color/grey_800</item>
<item name="palette">@array/transparentWidgetPalette</item>
<item name="widgetShadowAlpha">0</item>
<item name="widgetBackgroundAlpha">0.25</item>
</style>
<style name="time_label">
<item name="android:textSize">@dimen/time_label_size</item>
<item name="android:textColor">@color/numbers_text_color</item>
@ -222,9 +249,9 @@
<style name="ToolbarShadow">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">3dp</item>
<item name="android:layout_height">2dp</item>
<item name="android:background">@drawable/toolbar_shadow</item>
<item name="android:alpha">0.25</item>
<item name="android:alpha">0.2</item>
<item name="android:layout_below">@id/toolbar</item>
</style>
</resources>

@ -45,6 +45,10 @@
<item name="android:layout_width">0dp</item>
<item name="android:layout_weight">1</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:breakStrategy">balanced</item>
<item name="android:maxLines">2</item>
<item name="android:ellipsize">end</item>
<item name="android:scrollHorizontally">true</item>
</style>
<style name="ListHabits.CheckmarkPanel">

@ -21,7 +21,7 @@
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minHeight="40dp"
android:minWidth="40dp"
android:initialLayout="@layout/widget_checkmark"
android:initialLayout="@layout/widget_wrapper"
android:previewImage="@drawable/widget_preview_checkmark"
android:resizeMode="none"
android:updatePeriodMillis="3600000"

@ -40,6 +40,7 @@ grep -q "FAILURES\!\!\!" ${OUTPUT_DIR}/runner.txt && failed=1
info "Fetching failed generated files..."
mkdir -p ${OUTPUT_DIR}/failed
adb pull /mnt/sdcard/test-screenshots/ ${OUTPUT_DIR}/failed >> $LOG 2>> $LOG
adb pull /sdcard/Android/data/${PACKAGE_NAME}/files/test-screenshots/ \
${OUTPUT_DIR}/failed >> $LOG 2>> $LOG
adb shell rm -r /sdcard/Android/data/${PACKAGE_NAME}/files/test-screenshots/ >> $LOG 2>> $LOG

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 144 KiB

After

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Loading…
Cancel
Save