mirror of
https://github.com/iSoron/uhabits.git
synced 2025-12-06 09:08:52 -06:00
Merge branch 'feature/frequency-view' into dev
This commit is contained in:
@@ -18,15 +18,18 @@
|
|||||||
~ with this program. If not, see <http://www.gnu.org/licenses/>.
|
~ with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<manifest package="org.isoron.uhabits"
|
<manifest
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
package="org.isoron.uhabits"
|
||||||
android:versionCode="9"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:versionName="1.2.0">
|
android:versionCode="9"
|
||||||
|
android:versionName="1.2.0">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.VIBRATE"/>
|
<uses-permission android:name="android.permission.VIBRATE"/>
|
||||||
|
|
||||||
<uses-permission
|
<uses-permission
|
||||||
android:name="android.permission.READ_EXTERNAL_STORAGE"
|
android:name="android.permission.READ_EXTERNAL_STORAGE"
|
||||||
android:maxSdkVersion="18"/>
|
android:maxSdkVersion="18"/>
|
||||||
|
|
||||||
<uses-permission
|
<uses-permission
|
||||||
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||||
android:maxSdkVersion="18"/>
|
android:maxSdkVersion="18"/>
|
||||||
@@ -38,6 +41,7 @@
|
|||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/main_activity_title"
|
android:label="@string/main_activity_title"
|
||||||
android:theme="@style/AppBaseTheme">
|
android:theme="@style/AppBaseTheme">
|
||||||
|
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="AA_DB_NAME"
|
android:name="AA_DB_NAME"
|
||||||
android:value="uhabits.db"/>
|
android:value="uhabits.db"/>
|
||||||
@@ -58,8 +62,6 @@
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<receiver android:name=".HabitBroadcastReceiver"/>
|
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".ShowHabitActivity"
|
android:name=".ShowHabitActivity"
|
||||||
android:label="@string/title_activity_show_habit"
|
android:label="@string/title_activity_show_habit"
|
||||||
@@ -68,6 +70,7 @@
|
|||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value="org.isoron.uhabits.MainActivity"/>
|
android:value="org.isoron.uhabits.MainActivity"/>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".SettingsActivity"
|
android:name=".SettingsActivity"
|
||||||
android:label="@string/settings"
|
android:label="@string/settings"
|
||||||
@@ -82,9 +85,23 @@
|
|||||||
android:label=""
|
android:label=""
|
||||||
android:theme="@style/Theme.AppCompat.Light.NoActionBar"/>
|
android:theme="@style/Theme.AppCompat.Light.NoActionBar"/>
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".widgets.HabitPickerDialog"
|
||||||
|
android:theme="@style/Theme.AppCompat.Light.Dialog">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/>
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".AboutActivity"
|
||||||
|
android:label="@string/about"
|
||||||
|
android:parentActivityName=".MainActivity">
|
||||||
|
</activity>
|
||||||
|
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".widgets.CheckmarkWidgetProvider"
|
android:name=".widgets.CheckmarkWidgetProvider"
|
||||||
android:label="Checkmark">
|
android:label="@string/checkmark">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
|
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
@@ -93,9 +110,10 @@
|
|||||||
android:name="android.appwidget.provider"
|
android:name="android.appwidget.provider"
|
||||||
android:resource="@xml/widget_checkmark_info"/>
|
android:resource="@xml/widget_checkmark_info"/>
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".widgets.HistoryWidgetProvider"
|
android:name=".widgets.HistoryWidgetProvider"
|
||||||
android:label="History">
|
android:label="@string/history">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
|
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
@@ -104,9 +122,10 @@
|
|||||||
android:name="android.appwidget.provider"
|
android:name="android.appwidget.provider"
|
||||||
android:resource="@xml/widget_history_info"/>
|
android:resource="@xml/widget_history_info"/>
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".widgets.ScoreWidgetProvider"
|
android:name=".widgets.ScoreWidgetProvider"
|
||||||
android:label="Score">
|
android:label="@string/habit_strength">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
|
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
@@ -115,9 +134,10 @@
|
|||||||
android:name="android.appwidget.provider"
|
android:name="android.appwidget.provider"
|
||||||
android:resource="@xml/widget_score_info"/>
|
android:resource="@xml/widget_score_info"/>
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".widgets.StreakWidgetProvider"
|
android:name=".widgets.StreakWidgetProvider"
|
||||||
android:label="Streaks">
|
android:label="@string/streaks">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
|
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
@@ -127,18 +147,20 @@
|
|||||||
android:resource="@xml/widget_streak_info"/>
|
android:resource="@xml/widget_streak_info"/>
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
<activity
|
<receiver
|
||||||
android:name=".widgets.HabitPickerDialog"
|
android:name=".widgets.FrequencyWidgetProvider"
|
||||||
android:theme="@style/Theme.AppCompat.Light.Dialog">
|
android:label="@string/frequency">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/>
|
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
|
||||||
<activity
|
<meta-data
|
||||||
android:name=".AboutActivity"
|
android:name="android.appwidget.provider"
|
||||||
android:label="@string/about"
|
android:resource="@xml/widget_frequency_info"/>
|
||||||
android:parentActivityName=".MainActivity">
|
</receiver>
|
||||||
</activity>
|
|
||||||
|
<receiver android:name=".HabitBroadcastReceiver"/>
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ import org.isoron.uhabits.fragments.ListHabitsFragment;
|
|||||||
import org.isoron.uhabits.helpers.ReminderHelper;
|
import org.isoron.uhabits.helpers.ReminderHelper;
|
||||||
import org.isoron.uhabits.models.Habit;
|
import org.isoron.uhabits.models.Habit;
|
||||||
import org.isoron.uhabits.widgets.CheckmarkWidgetProvider;
|
import org.isoron.uhabits.widgets.CheckmarkWidgetProvider;
|
||||||
|
import org.isoron.uhabits.widgets.FrequencyWidgetProvider;
|
||||||
import org.isoron.uhabits.widgets.HistoryWidgetProvider;
|
import org.isoron.uhabits.widgets.HistoryWidgetProvider;
|
||||||
import org.isoron.uhabits.widgets.ScoreWidgetProvider;
|
import org.isoron.uhabits.widgets.ScoreWidgetProvider;
|
||||||
import org.isoron.uhabits.widgets.StreakWidgetProvider;
|
import org.isoron.uhabits.widgets.StreakWidgetProvider;
|
||||||
@@ -159,6 +160,7 @@ public class MainActivity extends ReplayableActivity
|
|||||||
updateWidgets(context, HistoryWidgetProvider.class);
|
updateWidgets(context, HistoryWidgetProvider.class);
|
||||||
updateWidgets(context, ScoreWidgetProvider.class);
|
updateWidgets(context, ScoreWidgetProvider.class);
|
||||||
updateWidgets(context, StreakWidgetProvider.class);
|
updateWidgets(context, StreakWidgetProvider.class);
|
||||||
|
updateWidgets(context, FrequencyWidgetProvider.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void updateWidgets(Context context, Class providerClass)
|
private static void updateWidgets(Context context, Class providerClass)
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ import org.isoron.uhabits.helpers.ReminderHelper;
|
|||||||
import org.isoron.uhabits.models.Habit;
|
import org.isoron.uhabits.models.Habit;
|
||||||
import org.isoron.uhabits.models.Score;
|
import org.isoron.uhabits.models.Score;
|
||||||
import org.isoron.uhabits.views.HabitHistoryView;
|
import org.isoron.uhabits.views.HabitHistoryView;
|
||||||
|
import org.isoron.uhabits.views.HabitFrequencyView;
|
||||||
import org.isoron.uhabits.views.HabitScoreView;
|
import org.isoron.uhabits.views.HabitScoreView;
|
||||||
import org.isoron.uhabits.views.HabitStreakView;
|
import org.isoron.uhabits.views.HabitStreakView;
|
||||||
import org.isoron.uhabits.views.RingView;
|
import org.isoron.uhabits.views.RingView;
|
||||||
@@ -54,6 +55,7 @@ public class ShowHabitFragment extends Fragment
|
|||||||
private HabitStreakView streakView;
|
private HabitStreakView streakView;
|
||||||
private HabitScoreView scoreView;
|
private HabitScoreView scoreView;
|
||||||
private HabitHistoryView historyView;
|
private HabitHistoryView historyView;
|
||||||
|
private HabitFrequencyView punchcardView;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onStart()
|
public void onStart()
|
||||||
@@ -75,6 +77,7 @@ public class ShowHabitFragment extends Fragment
|
|||||||
streakView = (HabitStreakView) view.findViewById(R.id.streakView);
|
streakView = (HabitStreakView) view.findViewById(R.id.streakView);
|
||||||
scoreView = (HabitScoreView) view.findViewById(R.id.scoreView);
|
scoreView = (HabitScoreView) view.findViewById(R.id.scoreView);
|
||||||
historyView = (HabitHistoryView) view.findViewById(R.id.historyView);
|
historyView = (HabitHistoryView) view.findViewById(R.id.historyView);
|
||||||
|
punchcardView = (HabitFrequencyView) view.findViewById(R.id.punchcardView);
|
||||||
|
|
||||||
updateHeaders(view);
|
updateHeaders(view);
|
||||||
updateScoreRing(view);
|
updateScoreRing(view);
|
||||||
@@ -82,6 +85,7 @@ public class ShowHabitFragment extends Fragment
|
|||||||
streakView.setHabit(habit);
|
streakView.setHabit(habit);
|
||||||
scoreView.setHabit(habit);
|
scoreView.setHabit(habit);
|
||||||
historyView.setHabit(habit);
|
historyView.setHabit(habit);
|
||||||
|
punchcardView.setHabit(habit);
|
||||||
|
|
||||||
btEditHistory.setOnClickListener(new View.OnClickListener()
|
btEditHistory.setOnClickListener(new View.OnClickListener()
|
||||||
{
|
{
|
||||||
@@ -125,14 +129,17 @@ public class ShowHabitFragment extends Fragment
|
|||||||
activity.getWindow().setStatusBarColor(darkerHabitColor);
|
activity.getWindow().setStatusBarColor(darkerHabitColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
TextView tvHistory = (TextView) view.findViewById(R.id.tvHistory);
|
updateColor(view, R.id.tvHistory);
|
||||||
TextView tvOverview = (TextView) view.findViewById(R.id.tvOverview);
|
updateColor(view, R.id.tvOverview);
|
||||||
TextView tvStrength = (TextView) view.findViewById(R.id.tvStrength);
|
updateColor(view, R.id.tvStrength);
|
||||||
TextView tvStreaks = (TextView) view.findViewById(R.id.tvStreaks);
|
updateColor(view, R.id.tvStreaks);
|
||||||
tvHistory.setTextColor(habit.color);
|
updateColor(view, R.id.tvWeekdayFreq);
|
||||||
tvOverview.setTextColor(habit.color);
|
}
|
||||||
tvStrength.setTextColor(habit.color);
|
|
||||||
tvStreaks.setTextColor(habit.color);
|
private void updateColor(View view, int viewId)
|
||||||
|
{
|
||||||
|
TextView textView = (TextView) view.findViewById(viewId);
|
||||||
|
textView.setTextColor(habit.color);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -182,6 +189,7 @@ public class ShowHabitFragment extends Fragment
|
|||||||
streakView.refreshData();
|
streakView.refreshData();
|
||||||
historyView.refreshData();
|
historyView.refreshData();
|
||||||
scoreView.refreshData();
|
scoreView.refreshData();
|
||||||
|
punchcardView.refreshData();
|
||||||
updateScoreRing(getView());
|
updateScoreRing(getView());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,12 +19,21 @@
|
|||||||
|
|
||||||
package org.isoron.uhabits.models;
|
package org.isoron.uhabits.models;
|
||||||
|
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
|
||||||
|
import com.activeandroid.Cache;
|
||||||
import com.activeandroid.query.Delete;
|
import com.activeandroid.query.Delete;
|
||||||
import com.activeandroid.query.From;
|
import com.activeandroid.query.From;
|
||||||
import com.activeandroid.query.Select;
|
import com.activeandroid.query.Select;
|
||||||
|
|
||||||
import org.isoron.helpers.DateHelper;
|
import org.isoron.helpers.DateHelper;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.GregorianCalendar;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
public class RepetitionList
|
public class RepetitionList
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -98,4 +107,53 @@ public class RepetitionList
|
|||||||
int reps[] = habit.checkmarks.getValues(today - DateHelper.millisecondsInOneDay, today);
|
int reps[] = habit.checkmarks.getValues(today - DateHelper.millisecondsInOneDay, today);
|
||||||
return (reps[0] > 0);
|
return (reps[0] > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public HashMap<Long, Integer[]> getWeekdayFrequency()
|
||||||
|
{
|
||||||
|
Repetition oldestRep = getOldest();
|
||||||
|
if(oldestRep == null) return new HashMap<>();
|
||||||
|
|
||||||
|
String query = "select strftime('%Y', timestamp / 1000, 'unixepoch') as year," +
|
||||||
|
"strftime('%m', timestamp / 1000, 'unixepoch') as month," +
|
||||||
|
"strftime('%w', timestamp / 1000, 'unixepoch') as weekday, " +
|
||||||
|
"count(*) from repetitions " +
|
||||||
|
"where habit = ? " +
|
||||||
|
"group by year, month, weekday";
|
||||||
|
|
||||||
|
String[] params = { habit.getId().toString() };
|
||||||
|
|
||||||
|
SQLiteDatabase db = Cache.openDatabase();
|
||||||
|
Cursor cursor = db.rawQuery(query, params);
|
||||||
|
|
||||||
|
if(!cursor.moveToFirst()) return new HashMap<>();
|
||||||
|
|
||||||
|
HashMap <Long, Integer[]> map = new HashMap<>();
|
||||||
|
GregorianCalendar date = DateHelper.getStartOfTodayCalendar();
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
int year = Integer.parseInt(cursor.getString(0));
|
||||||
|
int month = Integer.parseInt(cursor.getString(1));
|
||||||
|
int weekday = (Integer.parseInt(cursor.getString(2)) + 1) % 7;
|
||||||
|
int count = cursor.getInt(3);
|
||||||
|
|
||||||
|
date.set(year, month - 1, 1);
|
||||||
|
long timestamp = date.getTimeInMillis();
|
||||||
|
|
||||||
|
Integer[] list = map.get(timestamp);
|
||||||
|
|
||||||
|
if(list == null)
|
||||||
|
{
|
||||||
|
list = new Integer[7];
|
||||||
|
Arrays.fill(list, 0);
|
||||||
|
map.put(timestamp, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
list[weekday] = count;
|
||||||
|
}
|
||||||
|
while (cursor.moveToNext());
|
||||||
|
cursor.close();
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,295 @@
|
|||||||
|
/*
|
||||||
|
* 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.RectF;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
|
||||||
|
import org.isoron.helpers.ColorHelper;
|
||||||
|
import org.isoron.helpers.DateHelper;
|
||||||
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.GregorianCalendar;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
|
||||||
|
public class HabitFrequencyView extends ScrollableDataView
|
||||||
|
{
|
||||||
|
|
||||||
|
private Paint pGrid;
|
||||||
|
private float em;
|
||||||
|
private Habit habit;
|
||||||
|
private SimpleDateFormat dfMonth;
|
||||||
|
private SimpleDateFormat dfYear;
|
||||||
|
|
||||||
|
private Paint pText, pGraph;
|
||||||
|
private RectF rect, prevRect;
|
||||||
|
private int baseSize;
|
||||||
|
private int paddingTop;
|
||||||
|
|
||||||
|
private int columnWidth;
|
||||||
|
private int columnHeight;
|
||||||
|
private int nColumns;
|
||||||
|
|
||||||
|
private int textColor;
|
||||||
|
private int dimmedTextColor;
|
||||||
|
private int[] colors;
|
||||||
|
private int primaryColor;
|
||||||
|
private boolean isBackgroundTransparent;
|
||||||
|
|
||||||
|
private HashMap<Long, Integer[]> frequency;
|
||||||
|
private String wdays[];
|
||||||
|
|
||||||
|
public HabitFrequencyView(Context context, AttributeSet attrs)
|
||||||
|
{
|
||||||
|
super(context, attrs);
|
||||||
|
this.primaryColor = ColorHelper.palette[7];
|
||||||
|
this.frequency = new HashMap<>();
|
||||||
|
wdays = DateHelper.getShortDayNames();
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHabit(Habit habit)
|
||||||
|
{
|
||||||
|
this.habit = habit;
|
||||||
|
createColors();
|
||||||
|
refreshData();
|
||||||
|
postInvalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init()
|
||||||
|
{
|
||||||
|
refreshData();
|
||||||
|
createPaints();
|
||||||
|
createColors();
|
||||||
|
|
||||||
|
dfMonth = new SimpleDateFormat("MMM", Locale.getDefault());
|
||||||
|
dfYear = new SimpleDateFormat("yyyy", Locale.getDefault());
|
||||||
|
|
||||||
|
dfMonth.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||||
|
dfYear.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||||
|
|
||||||
|
rect = new RectF();
|
||||||
|
prevRect = new RectF();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createColors()
|
||||||
|
{
|
||||||
|
if(habit != null)
|
||||||
|
this.primaryColor = habit.color;
|
||||||
|
|
||||||
|
if (isBackgroundTransparent)
|
||||||
|
{
|
||||||
|
primaryColor = ColorHelper.setSaturation(primaryColor, 0.75f);
|
||||||
|
primaryColor = ColorHelper.setValue(primaryColor, 1.0f);
|
||||||
|
|
||||||
|
textColor = Color.argb(192, 255, 255, 255);
|
||||||
|
dimmedTextColor = Color.argb(128, 255, 255, 255);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
textColor = Color.argb(64, 0, 0, 0);
|
||||||
|
dimmedTextColor = Color.argb(16, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
colors = new int[4];
|
||||||
|
|
||||||
|
colors[0] = Color.rgb(230, 230, 230);
|
||||||
|
colors[3] = primaryColor;
|
||||||
|
colors[1] = ColorHelper.mixColors(colors[0], colors[3], 0.66f);
|
||||||
|
colors[2] = ColorHelper.mixColors(colors[0], colors[3], 0.33f);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void createPaints()
|
||||||
|
{
|
||||||
|
pText = new Paint();
|
||||||
|
pText.setAntiAlias(true);
|
||||||
|
|
||||||
|
pGraph = new Paint();
|
||||||
|
pGraph.setTextAlign(Paint.Align.CENTER);
|
||||||
|
pGraph.setAntiAlias(true);
|
||||||
|
|
||||||
|
pGrid = new Paint();
|
||||||
|
pGrid.setAntiAlias(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
|
||||||
|
{
|
||||||
|
int width = MeasureSpec.getSize(widthMeasureSpec);
|
||||||
|
int height = MeasureSpec.getSize(heightMeasureSpec);
|
||||||
|
setMeasuredDimension(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight)
|
||||||
|
{
|
||||||
|
if(height < 9) height = 200;
|
||||||
|
|
||||||
|
baseSize = height / 8;
|
||||||
|
setScrollerBucketSize(baseSize);
|
||||||
|
|
||||||
|
columnWidth = baseSize;
|
||||||
|
columnHeight = 8 * baseSize;
|
||||||
|
nColumns = width / baseSize;
|
||||||
|
paddingTop = 0;
|
||||||
|
|
||||||
|
pText.setTextSize(baseSize * 0.4f);
|
||||||
|
pGraph.setTextSize(baseSize * 0.4f);
|
||||||
|
pGraph.setStrokeWidth(baseSize * 0.1f);
|
||||||
|
pGrid.setStrokeWidth(baseSize * 0.05f);
|
||||||
|
em = pText.getFontSpacing();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void refreshData()
|
||||||
|
{
|
||||||
|
if(isInEditMode())
|
||||||
|
generateRandomData();
|
||||||
|
else if(habit != null)
|
||||||
|
frequency = habit.repetitions.getWeekdayFrequency();
|
||||||
|
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void generateRandomData()
|
||||||
|
{
|
||||||
|
GregorianCalendar date = DateHelper.getStartOfTodayCalendar();
|
||||||
|
date.set(Calendar.DAY_OF_MONTH, 1);
|
||||||
|
Random rand = new Random();
|
||||||
|
frequency.clear();
|
||||||
|
|
||||||
|
for(int i = 0; i < 40; i++)
|
||||||
|
{
|
||||||
|
Integer values[] = new Integer[7];
|
||||||
|
for(int j = 0; j < 7; j++)
|
||||||
|
values[j] = rand.nextInt(5);
|
||||||
|
|
||||||
|
frequency.put(date.getTimeInMillis(), values);
|
||||||
|
date.add(Calendar.MONTH, -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDraw(Canvas canvas)
|
||||||
|
{
|
||||||
|
super.onDraw(canvas);
|
||||||
|
|
||||||
|
rect.set(0, 0, nColumns * columnWidth, columnHeight);
|
||||||
|
rect.offset(0, paddingTop);
|
||||||
|
|
||||||
|
drawGrid(canvas, rect);
|
||||||
|
|
||||||
|
pText.setTextAlign(Paint.Align.CENTER);
|
||||||
|
pText.setColor(textColor);
|
||||||
|
pGraph.setColor(primaryColor);
|
||||||
|
prevRect.setEmpty();
|
||||||
|
|
||||||
|
GregorianCalendar currentDate = DateHelper.getStartOfTodayCalendar();
|
||||||
|
|
||||||
|
currentDate.set(Calendar.DAY_OF_MONTH, 1);
|
||||||
|
currentDate.add(Calendar.MONTH, -nColumns + 2 - getDataOffset());
|
||||||
|
|
||||||
|
for(int i = 0; i < nColumns - 1; i++)
|
||||||
|
{
|
||||||
|
rect.set(0, 0, columnWidth, columnHeight);
|
||||||
|
rect.offset(i * columnWidth, 0);
|
||||||
|
|
||||||
|
drawColumn(canvas, rect, currentDate);
|
||||||
|
currentDate.add(Calendar.MONTH, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawColumn(Canvas canvas, RectF rect, GregorianCalendar date)
|
||||||
|
{
|
||||||
|
Integer values[] = frequency.get(date.getTimeInMillis());
|
||||||
|
float rowHeight = rect.height() / 8.0f;
|
||||||
|
prevRect.set(rect);
|
||||||
|
|
||||||
|
for (int i = 0; i < 7; i++)
|
||||||
|
{
|
||||||
|
rect.set(0, 0, baseSize, baseSize);
|
||||||
|
rect.offset(prevRect.left, prevRect.top + columnWidth * i);
|
||||||
|
|
||||||
|
if(values != null)
|
||||||
|
drawMarker(canvas, rect, values[i]);
|
||||||
|
|
||||||
|
rect.offset(0, rowHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
drawFooter(canvas, rect, date);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawFooter(Canvas canvas, RectF rect, GregorianCalendar date)
|
||||||
|
{
|
||||||
|
Date time = date.getTime();
|
||||||
|
|
||||||
|
canvas.drawText(dfMonth.format(time), rect.centerX(), rect.centerY() - 0.1f * em, pText);
|
||||||
|
|
||||||
|
if(date.get(Calendar.MONTH) == 1)
|
||||||
|
canvas.drawText(dfYear.format(time), rect.centerX(), rect.centerY() + 0.9f * em, pText);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawMarker(Canvas canvas, RectF rect, Integer value)
|
||||||
|
{
|
||||||
|
float padding = rect.height() * 0.2f;
|
||||||
|
float radius = (rect.height() - 2 * padding) / 2.0f / 4.0f * Math.min(value, 4);
|
||||||
|
|
||||||
|
pGraph.setColor(colors[Math.min(3, Math.max(0, value - 1))]);
|
||||||
|
canvas.drawCircle(rect.centerX(), rect.centerY(), radius, pGraph);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawGrid(Canvas canvas, RectF rGrid)
|
||||||
|
{
|
||||||
|
int nRows = 7;
|
||||||
|
float rowHeight = rGrid.height() / (nRows + 1);
|
||||||
|
|
||||||
|
pText.setTextAlign(Paint.Align.LEFT);
|
||||||
|
pText.setColor(textColor);
|
||||||
|
pGrid.setColor(dimmedTextColor);
|
||||||
|
|
||||||
|
for (int i = 0; i < nRows; i++)
|
||||||
|
{
|
||||||
|
canvas.drawText(wdays[i], rGrid.right - columnWidth,
|
||||||
|
rGrid.top + rowHeight / 2 + 0.25f * em, pText);
|
||||||
|
|
||||||
|
pGrid.setStrokeWidth(1f);
|
||||||
|
canvas.drawLine(rGrid.left, rGrid.top, rGrid.right, rGrid.top, pGrid);
|
||||||
|
|
||||||
|
rGrid.offset(0, rowHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas.drawLine(rGrid.left, rGrid.top, rGrid.right, rGrid.top, pGrid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIsBackgroundTransparent(boolean isBackgroundTransparent)
|
||||||
|
{
|
||||||
|
this.isBackgroundTransparent = isBackgroundTransparent;
|
||||||
|
createColors();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* 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.widgets;
|
||||||
|
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.R;
|
||||||
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
import org.isoron.uhabits.views.HabitFrequencyView;
|
||||||
|
|
||||||
|
public class FrequencyWidgetProvider extends BaseWidgetProvider
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
protected View buildCustomView(Context context, Habit habit)
|
||||||
|
{
|
||||||
|
HabitFrequencyView view = new HabitFrequencyView(context, null);
|
||||||
|
view.setIsBackgroundTransparent(true);
|
||||||
|
view.setHabit(habit);
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected PendingIntent getOnClickPendingIntent(Context context, Habit habit)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int getDefaultHeight()
|
||||||
|
{
|
||||||
|
return 200;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int getDefaultWidth()
|
||||||
|
{
|
||||||
|
return 200;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int getLayoutId()
|
||||||
|
{
|
||||||
|
return R.layout.widget_graph;
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
app/src/main/res/drawable/widget_preview_frequency.png
Normal file
BIN
app/src/main/res/drawable/widget_preview_frequency.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
@@ -62,9 +62,9 @@
|
|||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
style="@style/cardStyle"
|
style="@style/cardStyle"
|
||||||
android:paddingBottom="0dp"
|
|
||||||
android:clipToPadding="false"
|
android:clipToPadding="false"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical"
|
||||||
|
android:paddingBottom="0dp">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/tvHistory"
|
android:id="@+id/tvHistory"
|
||||||
@@ -74,37 +74,49 @@
|
|||||||
<org.isoron.uhabits.views.HabitHistoryView
|
<org.isoron.uhabits.views.HabitHistoryView
|
||||||
android:id="@+id/historyView"
|
android:id="@+id/historyView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="160dp" />
|
android:layout_height="160dp"/>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
style="?android:borderlessButtonStyle"
|
|
||||||
android:id="@+id/btEditHistory"
|
android:id="@+id/btEditHistory"
|
||||||
|
style="?android:borderlessButtonStyle"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:src="@drawable/ic_action_edit_light"
|
android:src="@drawable/ic_action_edit_light"
|
||||||
android:textColor="@color/grey_400"
|
android:text="@string/edit"
|
||||||
android:text="@string/edit"/>
|
android:textColor="@color/grey_400"/>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout style="@style/cardStyle">
|
<LinearLayout
|
||||||
|
style="@style/cardStyle"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/tvStreaks"
|
android:id="@+id/tvStreaks"
|
||||||
style="@style/cardHeaderStyle"
|
style="@style/cardHeaderStyle"
|
||||||
android:text="@string/streaks"/>
|
android:text="@string/streaks"/>
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/llStreaks"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="horizontal"/>
|
|
||||||
|
|
||||||
<org.isoron.uhabits.views.HabitStreakView
|
<org.isoron.uhabits.views.HabitStreakView
|
||||||
android:id="@+id/streakView"
|
android:id="@+id/streakView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="200dp"/>
|
android:layout_height="200dp"/>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
style="@style/cardStyle"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvWeekdayFreq"
|
||||||
|
style="@style/cardHeaderStyle"
|
||||||
|
android:text="@string/frequency"/>
|
||||||
|
|
||||||
|
<org.isoron.uhabits.views.HabitFrequencyView
|
||||||
|
android:id="@+id/punchcardView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="200dp"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
@@ -95,4 +95,5 @@
|
|||||||
|
|
||||||
<!-- %s will get replaced by the version number. For example, "Versão %d" will become "Versão 1.2.0". -->
|
<!-- %s will get replaced by the version number. For example, "Versão %d" will become "Versão 1.2.0". -->
|
||||||
<string name="version_n">"Version %s"</string>
|
<string name="version_n">"Version %s"</string>
|
||||||
|
<string name="frequency">Fréquence</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -106,4 +106,5 @@
|
|||||||
<string name="developers">Desenvolvedores</string>
|
<string name="developers">Desenvolvedores</string>
|
||||||
<string name="translators">Tradutores</string>
|
<string name="translators">Tradutores</string>
|
||||||
<string name="version_n">Versão %s</string>
|
<string name="version_n">Versão %s</string>
|
||||||
|
<string name="frequency">Frequência</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -116,4 +116,5 @@
|
|||||||
<string name="translators">翻译者</string>
|
<string name="translators">翻译者</string>
|
||||||
<string name="export_to_csv">导出数据</string>
|
<string name="export_to_csv">导出数据</string>
|
||||||
<string name="version_n">%s版</string>
|
<string name="version_n">%s版</string>
|
||||||
|
<string name="frequency">频率</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -122,5 +122,7 @@
|
|||||||
<string name="translators">Translators</string>
|
<string name="translators">Translators</string>
|
||||||
<string name="developers">Developers</string>
|
<string name="developers">Developers</string>
|
||||||
<string name="version_n">Version %s</string>
|
<string name="version_n">Version %s</string>
|
||||||
|
<string name="frequency">Frequency</string>
|
||||||
|
<string name="checkmark">Checkmark</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
33
app/src/main/res/xml/widget_frequency_info.xml
Normal file
33
app/src/main/res/xml/widget_frequency_info.xml
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ 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/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:minHeight="80dp"
|
||||||
|
android:minWidth="80dp"
|
||||||
|
android:minResizeWidth="40dp"
|
||||||
|
android:minResizeHeight="40dp"
|
||||||
|
android:initialLayout="@layout/widget_graph"
|
||||||
|
android:previewImage="@drawable/widget_preview_frequency"
|
||||||
|
android:resizeMode="vertical|horizontal"
|
||||||
|
android:updatePeriodMillis="3600000"
|
||||||
|
android:configure="org.isoron.uhabits.widgets.HabitPickerDialog"
|
||||||
|
android:widgetCategory="home_screen">
|
||||||
|
|
||||||
|
</appwidget-provider>
|
||||||
Reference in New Issue
Block a user