Merge branch 'release/1.3.0'

pull/30/head v1.3.0
Alinson S. Xavier 10 years ago
commit 53c9dca3e2

@ -1,5 +1,14 @@
# Changelog # Changelog
### 1.3.0 (March 12, 2016)
* New frequency plot: view total repetitions per day of week
* New history editor: put checkmarks in the past
* German, French and Japanese translations
* Add about screen, with credits to all contributors
* Fix small bug that prevented habit from being reordered
* Fix small bug caused by rotating the device
### 1.2.0 (March 4, 2016) ### 1.2.0 (March 4, 2016)
* Ability to export habit data as CSV * Ability to export habit data as CSV

@ -0,0 +1,91 @@
# Copyright Notices
### ActiveAndroid
<https://github.com/pardom/ActiveAndroid>
Copyright (C) 2010 Michael Pardo
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
### Android Open Source Project
<https://source.android.com/>
Copyright (C) 2013 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
### FontAwesome
<http://fontawesome.io>
Font Awesome is a full suite of 605 pictographic icons for easy scalable
vector graphics on websites, created and maintained by Dave Gandy. Licensed
under the SIL OFL 1.1.
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
### DragSortListView
<https://github.com/bauerca/drag-sort-listview>
A subclass of the Android ListView component that enables drag
and drop re-ordering of list items.
Copyright 2012 Carl Bauer
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
### Material Design Icons
<https://github.com/google/material-design-icons>
Material design icons are the official icon set from Google that are designed
under the material design guidelines. Available under the Creative Common
Attribution 4.0 International License (CC-BY 4.0).

@ -1,31 +1,43 @@
# Loop Habit Tracker # Loop Habit Tracker
Loop is a simple Android app that helps you create and maintain good habits, allowing you to achieve your long-term goals. Detailed graphs and statistics show you how your habits improved over time. It is completely ad-free and open source. Loop is a simple Android app that helps you create and maintain good habits,
allowing you to achieve your long-term goals. Detailed graphs and statistics
show you how your habits improved over time. It is completely ad-free and open
source.
<a href="https://play.google.com/store/apps/details?id=org.isoron.uhabits&utm_source=global_co&utm_medium=prtnr&utm_content=Mar2515&utm_campaign=PartBadge&pcampaignid=MKT-AC-global-none-all-co-pr-py-PartBadges-Oct1515-1"><img alt="Get it on Google Play" src="https://play.google.com/intl/en_us/badges/images/apps/en-play-badge-border.png" width="200px"/></a> <a href="https://play.google.com/store/apps/details?id=org.isoron.uhabits&utm_source=global_co&utm_medium=prtnr&utm_content=Mar2515&utm_campaign=PartBadge&pcampaignid=MKT-AC-global-none-all-co-pr-py-PartBadges-Oct1515-1"><img alt="Get it on Google Play" src="https://play.google.com/intl/en_us/badges/images/apps/en-play-badge-border.png" height="75px"/></a>
<a href="http://f-droid.org/app/org.isoron.uhabits"><img alt="Git if on F-Droid" src="http://i.imgur.com/baSPE7X.png" height="75px"/></a>
## Features ## Features
<b>Simple, beautiful and modern interface</b> * **Simple, beautiful and modern interface.** Loop has a minimalistic interface
Loop has a minimalistic interface that is easy to use and follows the material design guidelines. that is easy to use and follows the material design guidelines.
<b>Habit score</b> * **Habit score.** In addition to showing your current streak, Loop has an
In addition to showing your current streak, Loop has an advanced algorithm for calculating the strength of your habits. Every repetition makes your habit stronger, and every missed day makes it weaker. A few missed days after a long streak, however, will not completely destroy your entire progress. advanced algorithm for calculating the strength of your habits. Every
repetition makes your habit stronger, and every missed day makes it weaker. A
few missed days after a long streak, however, will not completely destroy
your entire progress.
<b>Detailed graphs and statistics</b> * **Detailed graphs and statistics.** Clearly see how your habits improved over
Clearly see how your habits improved over time with beautiful and detailed graphs. Scroll back to see the complete history of your habits. time with beautiful and detailed graphs. Scroll back to see the complete
history of your habits.
<b>Flexible schedules</b> * **Flexible schedules.** Supports both daily habits and habits with more
Supports both daily habits and habits with more complex schedules, such as 3 times every week; one time every other week; or every other day. complex schedules, such as 3 times every week; one time every other week; or
every other day.
<b>Reminders</b> * **Reminders.** Create an individual reminder for each habit, at a chosen hour
Create an individual reminder for each habit, at a chosen hour of the day. Easily check, dismiss or snooze your habit directly from the notification, without opening the app. of the day. Easily check, dismiss or snooze your habit directly from the
notification, without opening the app.
<b>Optimized for smartwatches</b> * **Optimized for smartwatches.** Reminders can be checked, snoozed or
Reminders can be checked, snoozed or dismissed directly from your Android Wear watch. dismissed directly from your Android Wear watch.
<b>Completely ad-free and open source</b> * **Completely ad-free and open source.** There are absolutely no
There are absolutely no advertisements, annoying notifications or intrusive permissions in this app, and there will never be. The complete source code is available under the GPLv3. advertisements, annoying notifications or intrusive permissions in this app,
and there will never be. The complete source code is available under the
GPLv3.
## Screenshots ## Screenshots
@ -35,6 +47,46 @@ There are absolutely no advertisements, annoying notifications or intrusive perm
[![Habit history and streaks][screen4th]][screen4] [![Habit history and streaks][screen4th]][screen4]
[![Widgets][screen5th]][screen5] [![Widgets][screen5th]][screen5]
## Installing
The easiest way to install Loop is through the [Google Play Store][playstore] or [F-Droid][fdroid].
You may also download and install the APK from the [releases page][releases];
note, however, that the app will not be updated automatically. To build this
app from the source code, see [building instructions][build].
## Contributing
Loop is an open source project developed entirely by volunteers. If you would
like to contribute to the project, you are very welcome. There are many ways to
contribute, even if you are not a software developer.
* **Report bugs, suggest features.** The easiest way to contribute is to simply
use the app and let us know if you find any problems or have any suggestions
to improve it. You can either use the link inside the app, or open an issue
at GitHub.
* **Translate the app into your own language.** If you are not a native English
speaker, and would like to see the app translated into your own language,
please join our [open translation project at POEditor][poedit].
* **Write some code.** If you are an Android developer, you are very welcome to
contribute with code. Please, see the [developer guidelines][dev-guide] for more details.
## License
This program 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.
This program 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/>.
[screen1]: screenshots/original/uhabits1.png [screen1]: screenshots/original/uhabits1.png
[screen2]: screenshots/original/uhabits2.png [screen2]: screenshots/original/uhabits2.png
[screen3]: screenshots/original/uhabits3.png [screen3]: screenshots/original/uhabits3.png
@ -45,3 +97,9 @@ There are absolutely no advertisements, annoying notifications or intrusive perm
[screen3th]: screenshots/thumbs/uhabits3.png [screen3th]: screenshots/thumbs/uhabits3.png
[screen4th]: screenshots/thumbs/uhabits4.png [screen4th]: screenshots/thumbs/uhabits4.png
[screen5th]: screenshots/thumbs/uhabits5.png [screen5th]: screenshots/thumbs/uhabits5.png
[poedit]: https://poeditor.com/join/project/8DWX5pfjS0
[playstore]: https://play.google.com/store/apps/details?id=org.isoron.uhabits
[releases]: https://github.com/iSoron/uhabits/releases
[fdroid]: http://f-droid.org/app/org.isoron.uhabits
[dev-guide]: https://github.com/iSoron/uhabits/wiki/Developer-guidelines
[build]: https://github.com/iSoron/uhabits/wiki/Developer-guidelines#building

@ -8,6 +8,8 @@ android {
applicationId "org.isoron.uhabits" applicationId "org.isoron.uhabits"
minSdkVersion 15 minSdkVersion 15
targetSdkVersion 23 targetSdkVersion 23
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
} }
buildTypes { buildTypes {
@ -15,6 +17,9 @@ android {
minifyEnabled false minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
} }
debug {
testCoverageEnabled = true
}
} }
lintOptions { lintOptions {
@ -27,5 +32,11 @@ dependencies {
compile 'com.github.paolorotolo:appintro:3.4.0' compile 'com.github.paolorotolo:appintro:3.4.0'
compile project(':libs:drag-sort-listview:library') compile project(':libs:drag-sort-listview:library')
compile files('libs/ActiveAndroid.jar') compile files('libs/ActiveAndroid.jar')
androidTestCompile 'com.android.support:support-annotations:23.1.1'
androidTestCompile 'com.android.support.test:runner:0.4.1'
androidTestCompile 'com.android.support.test:rules:0.4.1'
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.1'
androidTestCompile 'com.android.support.test.espresso:espresso-intents:2.2.1'
} }

@ -0,0 +1,79 @@
/*
* 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;
import android.view.View;
import android.widget.Adapter;
import android.widget.AdapterView;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
import org.isoron.uhabits.models.Habit;
public class HabitMatchers
{
public static Matcher<Habit> withName(final String name)
{
return new TypeSafeMatcher<Habit>()
{
@Override
public boolean matchesSafely(Habit habit)
{
return habit.name.equals(name);
}
@Override
public void describeTo(Description description)
{
description.appendText("name should be ").appendText(name);
}
@Override
public void describeMismatchSafely(Habit habit, Description description)
{
description.appendText("was ").appendText(habit.name);
}
};
}
public static Matcher<View> containsHabit(final Matcher<Habit> matcher)
{
return new TypeSafeMatcher<View>()
{
@Override
protected boolean matchesSafely(View view)
{
Adapter adapter = ((AdapterView) view).getAdapter();
for (int i = 0; i < adapter.getCount(); i++)
if (matcher.matches(adapter.getItem(i))) return true;
return false;
}
@Override
public void describeTo(Description description)
{
description.appendText("with class name: ");
matcher.describeTo(description);
}
};
}
}

@ -0,0 +1,130 @@
/*
* 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;
import android.support.test.espresso.UiController;
import android.support.test.espresso.ViewAction;
import android.support.test.espresso.action.CoordinatesProvider;
import android.support.test.espresso.action.GeneralClickAction;
import android.support.test.espresso.action.GeneralLocation;
import android.support.test.espresso.action.Press;
import android.support.test.espresso.action.Tap;
import android.support.test.espresso.matcher.ViewMatchers;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
import org.hamcrest.Matcher;
import java.security.InvalidParameterException;
import java.util.Random;
public class HabitViewActions
{
public static ViewAction toggleAllCheckmarks()
{
final GeneralClickAction clickAction =
new GeneralClickAction(Tap.LONG, GeneralLocation.CENTER, Press.FINGER);
return new ViewAction()
{
@Override
public Matcher<View> getConstraints()
{
return ViewMatchers.isDisplayed();
}
@Override
public String getDescription()
{
return "toggleAllCheckmarks";
}
@Override
public void perform(UiController uiController, View view)
{
if (view.getId() != R.id.llButtons)
throw new InvalidParameterException("View must have id llButtons");
LinearLayout llButtons = (LinearLayout) view;
int count = llButtons.getChildCount();
for (int i = 0; i < count; i++)
{
TextView tvButton = (TextView) llButtons.getChildAt(i);
clickAction.perform(uiController, tvButton);
}
}
};
}
public static ViewAction clickAt(final int x, final int y)
{
return new GeneralClickAction(Tap.SINGLE, new CoordinatesProvider()
{
@Override
public float[] calculateCoordinates(View view)
{
int[] locations = new int[2];
view.getLocationOnScreen(locations);
final float locationX = locations[0] + x;
final float locationY = locations[1] + y;
return new float[]{locationX, locationY};
}
}, Press.FINGER);
}
public static ViewAction clickAtRandomLocations(final int count)
{
return new ViewAction()
{
@Override
public Matcher<View> getConstraints()
{
return ViewMatchers.isDisplayed();
}
@Override
public String getDescription()
{
return "clickAtRandomLocations";
}
@Override
public void perform(UiController uiController, View view)
{
int width = view.getWidth();
int height = view.getHeight();
Random random = new Random();
for(int i = 0; i < count; i++)
{
int x = random.nextInt(width);
int y = random.nextInt(height);
ViewAction action = clickAt(x, y);
action.perform(uiController, view);
}
}
};
}
}

@ -0,0 +1,178 @@
/*
* 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;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import org.isoron.uhabits.models.Habit;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import static android.support.test.espresso.Espresso.onData;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.Espresso.openContextualActionModeOverflowMenu;
import static android.support.test.espresso.Espresso.pressBack;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.action.ViewActions.longClick;
import static android.support.test.espresso.action.ViewActions.replaceText;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.withContentDescription;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.isoron.uhabits.HabitMatchers.containsHabit;
import static org.isoron.uhabits.HabitMatchers.withName;
public class MainActivityActions
{
public static String addHabit()
{
return addHabit(false);
}
public static String addHabit(boolean openDialogs)
{
String name = "New Habit " + new Random().nextInt(1000000);
String description = "Did you perform your new habit today?";
String num = "4";
String den = "8";
onView(withId(R.id.action_add))
.perform(click());
typeHabitData(name, description, num, den);
if(openDialogs)
{
onView(withId(R.id.buttonPickColor))
.perform(click());
pressBack();
onView(withId(R.id.inputReminderTime))
.perform(click());
onView(withText("Done"))
.perform(click());
onView(withId(R.id.inputReminderDays))
.perform(click());
onView(withText("OK"))
.perform(click());
}
onView(withId(R.id.buttonSave))
.perform(click());
onData(allOf(is(instanceOf(Habit.class)), withName(name)))
.onChildView(withId(R.id.label));
return name;
}
public static void typeHabitData(String name, String description, String num, String den)
{
onView(withId(R.id.input_name))
.perform(replaceText(name));
onView(withId(R.id.input_description))
.perform(replaceText(description));
onView(withId(R.id.input_freq_num))
.perform(replaceText(num));
onView(withId(R.id.input_freq_den))
.perform(replaceText(den));
}
public static void selectHabit(String name)
{
selectHabits(Collections.singletonList(name));
}
public static void selectHabits(List<String> names)
{
boolean first = true;
for(String name : names)
{
onData(allOf(is(instanceOf(Habit.class)), withName(name)))
.onChildView(withId(R.id.label))
.perform(first ? longClick() : click());
first = false;
}
}
public static void assertHabitsDontExist(List<String> names)
{
for(String name : names)
onView(withId(R.id.listView))
.check(matches(not(containsHabit(withName(name)))));
}
public static void assertHabitExists(String name)
{
List<String> names = new LinkedList<>();
names.add(name);
assertHabitsExist(names);
}
public static void assertHabitsExist(List<String> names)
{
for(String name : names)
onData(allOf(is(instanceOf(Habit.class)), withName(name)))
.check(matches(isDisplayed()));
}
public static void deleteHabit(String name)
{
deleteHabits(Collections.singletonList(name));
}
public static void deleteHabits(List<String> names)
{
selectHabits(names);
clickActionModeMenuItem(R.string.delete);
onView(withText("OK"))
.perform(click());
assertHabitsDontExist(names);
}
public static void clickActionModeMenuItem(int stringId)
{
try
{
onView(withText(stringId)).perform(click());
}
catch (Exception e1)
{
try
{
onView(withContentDescription(stringId)).perform(click());
}
catch(Exception e2)
{
openContextualActionModeOverflowMenu();
onView(withText(stringId)).perform(click());
}
}
}
}

@ -0,0 +1,200 @@
package org.isoron.uhabits;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.espresso.NoMatchingViewException;
import android.support.test.espresso.intent.rule.IntentsTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.LargeTest;
import org.isoron.uhabits.models.Habit;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import static android.support.test.espresso.Espresso.onData;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu;
import static android.support.test.espresso.Espresso.pressBack;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.action.ViewActions.longClick;
import static android.support.test.espresso.action.ViewActions.scrollTo;
import static android.support.test.espresso.action.ViewActions.swipeLeft;
import static android.support.test.espresso.action.ViewActions.swipeRight;
import static android.support.test.espresso.action.ViewActions.swipeUp;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.isRoot;
import static android.support.test.espresso.matcher.ViewMatchers.withClassName;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.endsWith;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.isoron.uhabits.HabitMatchers.withName;
import static org.isoron.uhabits.HabitViewActions.clickAtRandomLocations;
import static org.isoron.uhabits.HabitViewActions.toggleAllCheckmarks;
import static org.isoron.uhabits.MainActivityActions.addHabit;
import static org.isoron.uhabits.MainActivityActions.assertHabitExists;
import static org.isoron.uhabits.MainActivityActions.assertHabitsDontExist;
import static org.isoron.uhabits.MainActivityActions.assertHabitsExist;
import static org.isoron.uhabits.MainActivityActions.clickActionModeMenuItem;
import static org.isoron.uhabits.MainActivityActions.deleteHabit;
import static org.isoron.uhabits.MainActivityActions.deleteHabits;
import static org.isoron.uhabits.MainActivityActions.selectHabit;
import static org.isoron.uhabits.MainActivityActions.selectHabits;
import static org.isoron.uhabits.MainActivityActions.typeHabitData;
import static org.isoron.uhabits.ShowHabitActivityActions.openHistoryEditor;
@RunWith(AndroidJUnit4.class)
@LargeTest
public class MainTest
{
@Rule
public IntentsTestRule<MainActivity> activityRule = new IntentsTestRule<>(
MainActivity.class);
@Before
public void skipTutorial()
{
try
{
for (int i = 0; i < 10; i++)
onView(allOf(withClassName(endsWith("AppCompatImageButton")),
isDisplayed())).perform(click());
}
catch (NoMatchingViewException e)
{
// ignored
}
}
@Test
public void testArchiveHabits()
{
List<String> names = new LinkedList<>();
Context context = InstrumentationRegistry.getTargetContext();
for(int i = 0; i < 3; i++)
names.add(addHabit());
selectHabits(names);
clickActionModeMenuItem(R.string.archive);
assertHabitsDontExist(names);
openActionBarOverflowOrOptionsMenu(context);
onView(withText(R.string.show_archived))
.perform(click());
assertHabitsExist(names);
selectHabits(names);
clickActionModeMenuItem(R.string.unarchive);
openActionBarOverflowOrOptionsMenu(context);
onView(withText(R.string.show_archived))
.perform(click());
assertHabitsExist(names);
deleteHabits(names);
}
@Test
public void testAddInvalidHabit()
{
onView(withId(R.id.action_add))
.perform(click());
typeHabitData("", "", "15", "7");
onView(withId(R.id.buttonSave)).perform(click());
onView(withId(R.id.input_name)).check(matches(isDisplayed()));
}
@Test
public void testAddHabitAndViewStats()
{
String name = addHabit(true);
onData(allOf(is(instanceOf(Habit.class)), withName(name)))
.onChildView(withId(R.id.llButtons))
.perform(toggleAllCheckmarks());
onData(allOf(is(instanceOf(Habit.class)), withName(name)))
.onChildView(withId(R.id.label))
.perform(click());
onView(withId(R.id.scoreView))
.perform(swipeRight());
onView(withId(R.id.punchcardView))
.perform(scrollTo());
}
@Test
public void testEditHabit()
{
String name = addHabit();
onData(allOf(is(instanceOf(Habit.class)), withName(name)))
.onChildView(withId(R.id.label))
.perform(longClick());
clickActionModeMenuItem(R.string.edit);
String modifiedName = "Modified " + new Random().nextInt(10000);
typeHabitData(modifiedName, "", "1", "1");
onView(withId(R.id.buttonSave))
.perform(click());
assertHabitExists(modifiedName);
selectHabit(modifiedName);
clickActionModeMenuItem(R.string.color_picker_default_title);
pressBack();
deleteHabit(modifiedName);
}
@Test
public void testEditHistory()
{
String name = addHabit();
onData(allOf(is(instanceOf(Habit.class)), withName(name)))
.onChildView(withId(R.id.label))
.perform(click());
openHistoryEditor();
onView(withClassName(endsWith("HabitHistoryView")))
.perform(clickAtRandomLocations(20));
pressBack();
onView(withId(R.id.historyView))
.perform(scrollTo(), swipeRight(), swipeLeft());
}
@Test
public void testSettings()
{
Context context = InstrumentationRegistry.getContext();
openActionBarOverflowOrOptionsMenu(context);
onView(withText(R.string.settings)).perform(click());
}
@Test
public void testAbout()
{
Context context = InstrumentationRegistry.getContext();
openActionBarOverflowOrOptionsMenu(context);
onView(withText(R.string.about)).perform(click());
onView(isRoot()).perform(swipeUp());
}
}

@ -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/>.
*/
package org.isoron.uhabits;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.action.ViewActions.scrollTo;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
public class ShowHabitActivityActions
{
public static void openHistoryEditor()
{
onView(withId(R.id.btEditHistory))
.perform(scrollTo(), click());
}
}

@ -1,20 +1,38 @@
<?xml version="1.0" encoding="utf-8"?> <?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/>.
-->
<manifest <manifest
package="org.isoron.uhabits" package="org.isoron.uhabits"
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="9" android:versionCode="10"
android:versionName="1.2.0"> android:versionName="1.3.0">
<uses-permission <uses-permission android:name="android.permission.VIBRATE"/>
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"/>
<application <application
android:name="com.activeandroid.app.Application" android:name="com.activeandroid.app.Application"
@ -27,11 +45,9 @@
<meta-data <meta-data
android:name="AA_DB_NAME" android:name="AA_DB_NAME"
android:value="uhabits.db"/> android:value="uhabits.db"/>
<meta-data <meta-data
android:name="AA_DB_VERSION" android:name="AA_DB_VERSION"
android:value="12"/> android:value="12"/>
<meta-data <meta-data
android:name="com.google.android.backup.api_key" android:name="com.google.android.backup.api_key"
android:value="AEdPqrEAAAAI6aeWncbnMNo8E5GWeZ44dlc5cQ7tCROwFhOtiw"/> android:value="AEdPqrEAAAAI6aeWncbnMNo8E5GWeZ44dlc5cQ7tCROwFhOtiw"/>
@ -39,16 +55,13 @@
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:label="@string/main_activity_title"> android:label="@string/main_activity_title">
<intent-filter <intent-filter android:label="@string/app_name">
android:label="@string/app_name">
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/> <category android:name="android.intent.category.LAUNCHER"/>
</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"
@ -72,12 +85,27 @@
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>
<meta-data <meta-data
android:name="android.appwidget.provider" android:name="android.appwidget.provider"
android:resource="@xml/widget_checkmark_info"/> android:resource="@xml/widget_checkmark_info"/>
@ -85,10 +113,11 @@
<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>
<meta-data <meta-data
android:name="android.appwidget.provider" android:name="android.appwidget.provider"
android:resource="@xml/widget_history_info"/> android:resource="@xml/widget_history_info"/>
@ -96,10 +125,11 @@
<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>
<meta-data <meta-data
android:name="android.appwidget.provider" android:name="android.appwidget.provider"
android:resource="@xml/widget_score_info"/> android:resource="@xml/widget_score_info"/>
@ -107,24 +137,29 @@
<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>
<meta-data <meta-data
android:name="android.appwidget.provider" android:name="android.appwidget.provider"
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> <meta-data
android:name="android.appwidget.provider"
android:resource="@xml/widget_frequency_info"/>
</receiver>
<receiver android:name=".HabitBroadcastReceiver"/>
</application> </application>

@ -1,17 +1,20 @@
/* Copyright (C) 2016 Alinson Santos Xavier /*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* *
* This program is free software: you can redistribute it and/or modify it * This file is part of Loop Habit Tracker.
* 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.
* *
* This program is distributed in the hope that it will be useful, but WITHOUT * Loop Habit Tracker is free software: you can redistribute it and/or modify
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * it under the terms of the GNU General Public License as published by the
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * 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. * more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License along
* along with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.isoron.helpers; package org.isoron.helpers;

@ -1,17 +1,20 @@
/* Copyright (C) 2016 Alinson Santos Xavier /*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* *
* This program is free software: you can redistribute it and/or modify it * This file is part of Loop Habit Tracker.
* 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.
* *
* This program is distributed in the hope that it will be useful, but WITHOUT * Loop Habit Tracker is free software: you can redistribute it and/or modify
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * it under the terms of the GNU General Public License as published by the
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * 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. * more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License along
* along with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.isoron.helpers; package org.isoron.helpers;

@ -1,17 +1,20 @@
/* Copyright (C) 2016 Alinson Santos Xavier /*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* *
* This program is free software: you can redistribute it and/or modify it * This file is part of Loop Habit Tracker.
* 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.
* *
* This program is distributed in the hope that it will be useful, but WITHOUT * Loop Habit Tracker is free software: you can redistribute it and/or modify
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * it under the terms of the GNU General Public License as published by the
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * 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. * more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License along
* along with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.isoron.helpers; package org.isoron.helpers;

@ -1,17 +1,20 @@
/* Copyright (C) 2016 Alinson Santos Xavier /*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* *
* This program is free software: you can redistribute it and/or modify it * This file is part of Loop Habit Tracker.
* 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.
* *
* This program is distributed in the hope that it will be useful, but WITHOUT * Loop Habit Tracker is free software: you can redistribute it and/or modify
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * it under the terms of the GNU General Public License as published by the
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * 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. * more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License along
* along with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.isoron.helpers; package org.isoron.helpers;

@ -1,17 +1,20 @@
/* Copyright (C) 2016 Alinson Santos Xavier /*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* *
* This program is free software: you can redistribute it and/or modify it * This file is part of Loop Habit Tracker.
* 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.
* *
* This program is distributed in the hope that it will be useful, but WITHOUT * Loop Habit Tracker is free software: you can redistribute it and/or modify
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * it under the terms of the GNU General Public License as published by the
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * 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. * more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License along
* along with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.isoron.helpers; package org.isoron.helpers;

@ -0,0 +1,96 @@
/*
* 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;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import org.isoron.helpers.ColorHelper;
public class AboutActivity extends Activity implements View.OnClickListener
{
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.about);
if (android.os.Build.VERSION.SDK_INT >= 21)
{
int color = getResources().getColor(R.color.blue_700);
int darkerColor = ColorHelper.mixColors(color, Color.BLACK, 0.75f);
getActionBar().setBackgroundDrawable(new ColorDrawable(color));
getWindow().setStatusBarColor(darkerColor);
}
TextView tvVersion = (TextView) findViewById(R.id.tvVersion);
TextView tvRate = (TextView) findViewById(R.id.tvRate);
TextView tvFeedback = (TextView) findViewById(R.id.tvFeedback);
TextView tvSource = (TextView) findViewById(R.id.tvSource);
tvVersion.setText(String.format(getResources().getString(R.string.version_n),
BuildConfig.VERSION_NAME));
tvRate.setOnClickListener(this);
tvFeedback.setOnClickListener(this);
tvSource.setOnClickListener(this);
}
@Override
public void onClick(View v)
{
switch (v.getId())
{
case R.id.tvRate:
{
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse("market://details?id=org.isoron.uhabits"));
startActivity(intent);
break;
}
case R.id.tvFeedback:
{
Intent intent = new Intent();
intent.setAction(Intent.ACTION_SENDTO);
intent.setData(Uri.parse("mailto:isoron+habits@gmail.com?" +
"subject=Feedback%20about%20Loop%20Habit%20Tracker"));
startActivity(intent);
break;
}
case R.id.tvSource:
{
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse("https://github.com/iSoron/uhabits"));
startActivity(intent);
break;
}
}
}
}

@ -1,17 +1,20 @@
/* Copyright (C) 2016 Alinson Santos Xavier /*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* *
* This program is free software: you can redistribute it and/or modify it * This file is part of Loop Habit Tracker.
* 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.
* *
* This program is distributed in the hope that it will be useful, but WITHOUT * Loop Habit Tracker is free software: you can redistribute it and/or modify
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * it under the terms of the GNU General Public License as published by the
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * 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. * more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License along
* along with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.isoron.uhabits; package org.isoron.uhabits;
@ -104,6 +107,11 @@ public class HabitBroadcastReceiver extends BroadcastReceiver
habit.save(); habit.save();
dismissNotification(context, habit); dismissNotification(context, habit);
sendRefreshBroadcast(context);
}
public static void sendRefreshBroadcast(Context context)
{
LocalBroadcastManager manager = LocalBroadcastManager.getInstance(context); LocalBroadcastManager manager = LocalBroadcastManager.getInstance(context);
Intent refreshIntent = new Intent(MainActivity.ACTION_REFRESH); Intent refreshIntent = new Intent(MainActivity.ACTION_REFRESH);
manager.sendBroadcast(refreshIntent); manager.sendBroadcast(refreshIntent);

@ -1,17 +1,20 @@
/* Copyright (C) 2016 Alinson Santos Xavier /*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* *
* This program is free software: you can redistribute it and/or modify it * This file is part of Loop Habit Tracker.
* 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.
* *
* This program is distributed in the hope that it will be useful, but WITHOUT * Loop Habit Tracker is free software: you can redistribute it and/or modify
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * it under the terms of the GNU General Public License as published by the
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * 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. * more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License along
* along with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.isoron.uhabits; package org.isoron.uhabits;

@ -1,17 +1,20 @@
/* Copyright (C) 2016 Alinson Santos Xavier /*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* *
* This program is free software: you can redistribute it and/or modify it * This file is part of Loop Habit Tracker.
* 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.
* *
* This program is distributed in the hope that it will be useful, but WITHOUT * Loop Habit Tracker is free software: you can redistribute it and/or modify
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * it under the terms of the GNU General Public License as published by the
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * 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. * more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License along
* along with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.isoron.uhabits; package org.isoron.uhabits;

@ -1,17 +1,20 @@
/* Copyright (C) 2016 Alinson Santos Xavier /*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* *
* This program is free software: you can redistribute it and/or modify it * This file is part of Loop Habit Tracker.
* 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.
* *
* This program is distributed in the hope that it will be useful, but WITHOUT * Loop Habit Tracker is free software: you can redistribute it and/or modify
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * it under the terms of the GNU General Public License as published by the
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * 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. * more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License along
* along with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.isoron.uhabits; package org.isoron.uhabits;
@ -38,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;
@ -117,9 +121,18 @@ public class MainActivity extends ReplayableActivity
switch (item.getItemId()) switch (item.getItemId())
{ {
case R.id.action_settings: case R.id.action_settings:
{
Intent intent = new Intent(this, SettingsActivity.class); Intent intent = new Intent(this, SettingsActivity.class);
startActivity(intent); startActivity(intent);
return true; return true;
}
case R.id.action_about:
{
Intent intent = new Intent(this, AboutActivity.class);
startActivity(intent);
return true;
}
default: default:
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
@ -138,7 +151,16 @@ public class MainActivity extends ReplayableActivity
public void onPostExecuteCommand(Long refreshKey) public void onPostExecuteCommand(Long refreshKey)
{ {
listHabitsFragment.onPostExecuteCommand(refreshKey); listHabitsFragment.onPostExecuteCommand(refreshKey);
updateWidgets(this);
new AsyncTask<Void, Void, Void>()
{
@Override
protected Void doInBackground(Void... params)
{
updateWidgets(MainActivity.this);
return null;
}
};
} }
public static void updateWidgets(Context context) public static void updateWidgets(Context context)
@ -147,6 +169,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)

@ -1,17 +1,20 @@
/* Copyright (C) 2016 Alinson Santos Xavier /*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* *
* This program is free software: you can redistribute it and/or modify it * This file is part of Loop Habit Tracker.
* 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.
* *
* This program is distributed in the hope that it will be useful, but WITHOUT * Loop Habit Tracker is free software: you can redistribute it and/or modify
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * it under the terms of the GNU General Public License as published by the
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * 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. * more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License along
* along with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.isoron.uhabits; package org.isoron.uhabits;

@ -1,36 +1,47 @@
/* Copyright (C) 2016 Alinson Santos Xavier /*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* *
* This program is free software: you can redistribute it and/or modify it * This file is part of Loop Habit Tracker.
* 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.
* *
* This program is distributed in the hope that it will be useful, but WITHOUT * Loop Habit Tracker is free software: you can redistribute it and/or modify
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * it under the terms of the GNU General Public License as published by the
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * 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. * more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License along
* along with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.isoron.uhabits; package org.isoron.uhabits;
import android.app.ActionBar;
import android.content.BroadcastReceiver;
import android.content.ContentUris; import android.content.ContentUris;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.ColorDrawable;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.view.Menu; import android.support.v4.content.LocalBroadcastManager;
import android.view.MenuItem;
import org.isoron.helpers.ReplayableActivity; import org.isoron.helpers.ReplayableActivity;
import org.isoron.uhabits.fragments.ShowHabitFragment;
import org.isoron.uhabits.models.Habit; import org.isoron.uhabits.models.Habit;
public class ShowHabitActivity extends ReplayableActivity public class ShowHabitActivity extends ReplayableActivity
{ {
public Habit habit; public Habit habit;
private Receiver receiver;
private LocalBroadcastManager localBroadcastManager;
private ShowHabitFragment fragment;
@Override @Override
protected void onCreate(Bundle savedInstanceState) protected void onCreate(Bundle savedInstanceState)
@ -39,34 +50,39 @@ public class ShowHabitActivity extends ReplayableActivity
Uri data = getIntent().getData(); Uri data = getIntent().getData();
habit = Habit.get(ContentUris.parseId(data)); habit = Habit.get(ContentUris.parseId(data));
getActionBar().setTitle(habit.name); ActionBar actionBar = getActionBar();
if (android.os.Build.VERSION.SDK_INT >= 21) if(actionBar != null)
{ {
getActionBar().setBackgroundDrawable(new ColorDrawable(habit.color)); actionBar.setTitle(habit.name);
if (android.os.Build.VERSION.SDK_INT >= 21)
actionBar.setBackgroundDrawable(new ColorDrawable(habit.color));
} }
setContentView(R.layout.show_habit_activity); setContentView(R.layout.show_habit_activity);
}
@Override fragment = (ShowHabitFragment) getFragmentManager().findFragmentById(R.id.fragment2);
public boolean onCreateOptionsMenu(Menu menu)
{ receiver = new Receiver();
getMenuInflater().inflate(R.menu.show_habit_activity_menu, menu); localBroadcastManager = LocalBroadcastManager.getInstance(this);
return true; localBroadcastManager.registerReceiver(receiver,
new IntentFilter(MainActivity.ACTION_REFRESH));
} }
@Override class Receiver extends BroadcastReceiver
public boolean onOptionsItemSelected(MenuItem item)
{ {
switch (item.getItemId()) @Override
public void onReceive(Context context, Intent intent)
{ {
case R.id.action_settings: fragment.refreshData();
Intent intent = new Intent(this, SettingsActivity.class);
startActivity(intent);
return true;
} }
}
return super.onOptionsItemSelected(item); @Override
protected void onDestroy()
{
localBroadcastManager.unregisterReceiver(receiver);
super.onDestroy();
} }
} }

@ -1,17 +1,20 @@
/* Copyright (C) 2016 Alinson Santos Xavier /*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* *
* This program is free software: you can redistribute it and/or modify it * This file is part of Loop Habit Tracker.
* 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.
* *
* This program is distributed in the hope that it will be useful, but WITHOUT * Loop Habit Tracker is free software: you can redistribute it and/or modify
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * it under the terms of the GNU General Public License as published by the
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * 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. * more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License along
* along with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.isoron.uhabits.commands; package org.isoron.uhabits.commands;

@ -1,17 +1,20 @@
/* Copyright (C) 2016 Alinson Santos Xavier /*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* *
* This program is free software: you can redistribute it and/or modify it * This file is part of Loop Habit Tracker.
* 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.
* *
* This program is distributed in the hope that it will be useful, but WITHOUT * Loop Habit Tracker is free software: you can redistribute it and/or modify
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * it under the terms of the GNU General Public License as published by the
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * 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. * more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License along
* along with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.isoron.uhabits.commands; package org.isoron.uhabits.commands;

@ -1,17 +1,20 @@
/* Copyright (C) 2016 Alinson Santos Xavier /*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* *
* This program is free software: you can redistribute it and/or modify it * This file is part of Loop Habit Tracker.
* 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.
* *
* This program is distributed in the hope that it will be useful, but WITHOUT * Loop Habit Tracker is free software: you can redistribute it and/or modify
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * it under the terms of the GNU General Public License as published by the
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * 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. * more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License along
* along with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.isoron.uhabits.commands; package org.isoron.uhabits.commands;

@ -1,17 +1,20 @@
/* Copyright (C) 2016 Alinson Santos Xavier /*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* *
* This program is free software: you can redistribute it and/or modify it * This file is part of Loop Habit Tracker.
* 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.
* *
* This program is distributed in the hope that it will be useful, but WITHOUT * Loop Habit Tracker is free software: you can redistribute it and/or modify
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * it under the terms of the GNU General Public License as published by the
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * 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. * more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License along
* along with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.isoron.uhabits.commands; package org.isoron.uhabits.commands;
@ -36,12 +39,14 @@ public class DeleteHabitsCommand extends Command
{ {
for(Habit h : habits) for(Habit h : habits)
h.cascadeDelete(); h.cascadeDelete();
Habit.rebuildOrder();
} }
@Override @Override
public void undo() public void undo()
{ {
throw new UnsupportedOperationException();
} }
public Integer getExecuteStringId() public Integer getExecuteStringId()

@ -1,17 +1,20 @@
/* Copyright (C) 2016 Alinson Santos Xavier /*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* *
* This program is free software: you can redistribute it and/or modify it * This file is part of Loop Habit Tracker.
* 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.
* *
* This program is distributed in the hope that it will be useful, but WITHOUT * Loop Habit Tracker is free software: you can redistribute it and/or modify
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * it under the terms of the GNU General Public License as published by the
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * 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. * more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License along
* along with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.isoron.uhabits.commands; package org.isoron.uhabits.commands;
@ -33,8 +36,8 @@ public class EditHabitCommand extends Command
this.modified = new Habit(modified); this.modified = new Habit(modified);
this.original = new Habit(original); this.original = new Habit(original);
hasIntervalChanged = (this.original.freqDen != this.modified.freqDen || hasIntervalChanged = (!this.original.freqDen.equals(this.modified.freqDen) ||
this.original.freqNum != this.modified.freqNum); !this.original.freqNum.equals(this.modified.freqNum));
} }
public void execute() public void execute()

@ -1,17 +1,20 @@
/* Copyright (C) 2016 Alinson Santos Xavier /*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* *
* This program is free software: you can redistribute it and/or modify it * This file is part of Loop Habit Tracker.
* 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.
* *
* This program is distributed in the hope that it will be useful, but WITHOUT * Loop Habit Tracker is free software: you can redistribute it and/or modify
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * it under the terms of the GNU General Public License as published by the
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * 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. * more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License along
* along with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.isoron.uhabits.commands; package org.isoron.uhabits.commands;

@ -1,17 +1,20 @@
/* Copyright (C) 2016 Alinson Santos Xavier /*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* *
* This program is free software: you can redistribute it and/or modify it * This file is part of Loop Habit Tracker.
* 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.
* *
* This program is distributed in the hope that it will be useful, but WITHOUT * Loop Habit Tracker is free software: you can redistribute it and/or modify
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * it under the terms of the GNU General Public License as published by the
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * 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. * more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License along
* along with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.isoron.uhabits.commands; package org.isoron.uhabits.commands;

@ -0,0 +1,267 @@
/*
* 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.dialogs;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.view.ActionMode;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ProgressBar;
import com.android.colorpicker.ColorPickerDialog;
import com.android.colorpicker.ColorPickerSwatch;
import org.isoron.helpers.ColorHelper;
import org.isoron.helpers.DialogHelper;
import org.isoron.helpers.ReplayableActivity;
import org.isoron.uhabits.R;
import org.isoron.uhabits.commands.ArchiveHabitsCommand;
import org.isoron.uhabits.commands.ChangeHabitColorCommand;
import org.isoron.uhabits.commands.DeleteHabitsCommand;
import org.isoron.uhabits.commands.UnarchiveHabitsCommand;
import org.isoron.uhabits.fragments.EditHabitFragment;
import org.isoron.uhabits.io.CSVExporter;
import org.isoron.uhabits.loaders.HabitListLoader;
import org.isoron.uhabits.models.Habit;
import java.io.File;
import java.util.LinkedList;
import java.util.List;
public class HabitSelectionCallback implements ActionMode.Callback
{
private HabitListLoader loader;
private List<Integer> selectedPositions;
private ReplayableActivity activity;
private Listener listener;
private DialogHelper.OnSavedListener onSavedListener;
private ProgressBar progressBar;
public interface Listener
{
void onActionModeDestroyed(ActionMode mode);
}
public HabitSelectionCallback(ReplayableActivity activity, HabitListLoader loader)
{
this.activity = activity;
this.loader = loader;
selectedPositions = new LinkedList<>();
}
public void setListener(Listener listener)
{
this.listener = listener;
}
public void setProgressBar(ProgressBar progressBar)
{
this.progressBar = progressBar;
}
public void setOnSavedListener(DialogHelper.OnSavedListener onSavedListener)
{
this.onSavedListener = onSavedListener;
}
public void setSelectedPositions(List<Integer> selectedPositions)
{
this.selectedPositions = selectedPositions;
}
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu)
{
activity.getMenuInflater().inflate(R.menu.list_habits_context, menu);
updateTitle(mode);
updateActions(menu);
return true;
}
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu)
{
updateTitle(mode);
updateActions(menu);
return true;
}
private void updateActions(Menu menu)
{
boolean showEdit = (selectedPositions.size() == 1);
boolean showArchive = true;
boolean showUnarchive = true;
for (int i : selectedPositions)
{
Habit h = loader.habitsList.get(i);
if (h.isArchived())
{
showArchive = false;
}
else showUnarchive = false;
}
MenuItem itemEdit = menu.findItem(R.id.action_edit_habit);
MenuItem itemColor = menu.findItem(R.id.action_color);
MenuItem itemArchive = menu.findItem(R.id.action_archive_habit);
MenuItem itemUnarchive = menu.findItem(R.id.action_unarchive_habit);
itemColor.setVisible(true);
itemEdit.setVisible(showEdit);
itemArchive.setVisible(showArchive);
itemUnarchive.setVisible(showUnarchive);
}
private void updateTitle(ActionMode mode)
{
mode.setTitle("" + selectedPositions.size());
}
@Override
public boolean onActionItemClicked(final ActionMode mode, MenuItem item)
{
final LinkedList<Habit> selectedHabits = new LinkedList<>();
for (int i : selectedPositions)
selectedHabits.add(loader.habitsList.get(i));
Habit firstHabit = selectedHabits.getFirst();
switch (item.getItemId())
{
case R.id.action_archive_habit:
activity.executeCommand(new ArchiveHabitsCommand(selectedHabits), null);
mode.finish();
return true;
case R.id.action_unarchive_habit:
activity.executeCommand(new UnarchiveHabitsCommand(selectedHabits), null);
mode.finish();
return true;
case R.id.action_edit_habit:
{
EditHabitFragment frag = EditHabitFragment.editSingleHabitFragment(firstHabit.getId());
frag.setOnSavedListener(onSavedListener);
frag.show(activity.getFragmentManager(), "editHabit");
return true;
}
case R.id.action_color:
{
ColorPickerDialog picker = ColorPickerDialog.newInstance(R.string.color_picker_default_title,
ColorHelper.palette, firstHabit.color, 4, ColorPickerDialog.SIZE_SMALL);
picker.setOnColorSelectedListener(new ColorPickerSwatch.OnColorSelectedListener()
{
public void onColorSelected(int color)
{
activity.executeCommand(
new ChangeHabitColorCommand(selectedHabits, color), null);
mode.finish();
}
});
picker.show(activity.getFragmentManager(), "picker");
return true;
}
case R.id.action_delete:
{
new AlertDialog.Builder(activity).setTitle(R.string.delete_habits)
.setMessage(R.string.delete_habits_message)
.setPositiveButton(android.R.string.yes,
new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
{
activity.executeCommand(
new DeleteHabitsCommand(selectedHabits), null);
mode.finish();
}
}).setNegativeButton(android.R.string.no, null)
.show();
return true;
}
case R.id.action_export_csv:
{
onExportHabitsClick(selectedHabits);
return true;
}
}
return false;
}
@Override
public void onDestroyActionMode(ActionMode mode)
{
if(listener != null) listener.onActionModeDestroyed(mode);
}
private void onExportHabitsClick(final LinkedList<Habit> selectedHabits)
{
new AsyncTask<Void, Void, Void>()
{
String filename;
@Override
protected void onPreExecute()
{
if(progressBar != null)
{
progressBar.setIndeterminate(true);
progressBar.setVisibility(View.VISIBLE);
}
}
@Override
protected void onPostExecute(Void aVoid)
{
if(filename != null)
{
Intent intent = new Intent();
intent.setAction(Intent.ACTION_SEND);
intent.setType("application/zip");
intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(new File(filename)));
activity.startActivity(intent);
}
if(progressBar != null)
progressBar.setVisibility(View.GONE);
}
@Override
protected Void doInBackground(Void... params)
{
CSVExporter exporter = new CSVExporter(activity, selectedHabits);
filename = exporter.writeArchive();
return null;
}
}.execute();
}
}

@ -0,0 +1,81 @@
/*
* 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.dialogs;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.view.View;
import android.widget.TextView;
import org.isoron.helpers.DateHelper;
import org.isoron.uhabits.R;
public class HintManager
{
private Context context;
private SharedPreferences prefs;
private View hintView;
public HintManager(Context context, View hintView)
{
this.context = context;
this.hintView = hintView;
prefs = PreferenceManager.getDefaultSharedPreferences(context);
}
public void dismissHint()
{
hintView.animate().alpha(0f).setDuration(500).setListener(new AnimatorListenerAdapter()
{
@Override
public void onAnimationEnd(Animator animation)
{
hintView.setVisibility(View.GONE);
}
});
}
public void showHintIfAppropriate()
{
Integer lastHintNumber = prefs.getInt("last_hint_number", -1);
Long lastHintTimestamp = prefs.getLong("last_hint_timestamp", -1);
if (DateHelper.getStartOfToday() > lastHintTimestamp) showHint(lastHintNumber + 1);
}
private void showHint(int hintNumber)
{
String[] hints = context.getResources().getStringArray(R.array.hints);
if (hintNumber >= hints.length) return;
prefs.edit().putInt("last_hint_number", hintNumber).apply();
prefs.edit().putLong("last_hint_timestamp", DateHelper.getStartOfToday()).apply();
TextView tvContent = (TextView) hintView.findViewById(R.id.hintContent);
tvContent.setText(hints[hintNumber]);
hintView.setAlpha(0.0f);
hintView.setVisibility(View.VISIBLE);
hintView.animate().alpha(1f).setDuration(500);
}
}

@ -0,0 +1,115 @@
/*
* 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.dialogs;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.Log;
import org.isoron.uhabits.R;
import org.isoron.uhabits.models.Habit;
import org.isoron.uhabits.views.HabitHistoryView;
public class HistoryEditorDialog extends DialogFragment
implements DialogInterface.OnClickListener
{
private Habit habit;
private Listener listener;
HabitHistoryView historyView;
@Override
public Dialog onCreateDialog(Bundle savedInstanceState)
{
Context context = getActivity();
historyView = new HabitHistoryView(context, null);
int p = (int) getResources().getDimension(R.dimen.history_editor_padding);
if(savedInstanceState != null)
{
long id = savedInstanceState.getLong("habit", -1);
if(id > 0) this.habit = Habit.get(id);
}
historyView.setPadding(p, 0, p, 0);
historyView.setHabit(habit);
historyView.setIsEditable(true);
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(R.string.history)
.setView(historyView)
.setPositiveButton(android.R.string.ok, this);
return builder.create();
}
@Override
public void onResume()
{
super.onResume();
DisplayMetrics metrics = getResources().getDisplayMetrics();
int maxHeight = getResources().getDimensionPixelSize(R.dimen.history_editor_max_height);
int width = metrics.widthPixels;
int height = Math.min(metrics.heightPixels, maxHeight);
Log.d("HistoryEditorDialog", String.format("h=%d max_h=%d", height, maxHeight));
getDialog().getWindow().setLayout(width, height);
}
@Override
public void onClick(DialogInterface dialog, int which)
{
dismiss();
}
public void setHabit(Habit habit)
{
this.habit = habit;
if(historyView != null) historyView.setHabit(habit);
}
@Override
public void onPause()
{
super.onPause();
if(listener != null) listener.onHistoryEditorClosed();
}
@Override
public void onSaveInstanceState(Bundle outState)
{
outState.putLong("habit", habit.getId());
}
public void setListener(Listener listener)
{
this.listener = listener;
}
public interface Listener {
void onHistoryEditorClosed();
}
}

@ -1,3 +1,22 @@
/*
* 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.dialogs; package org.isoron.uhabits.dialogs;
import android.app.AlertDialog; import android.app.AlertDialog;

@ -1,17 +1,20 @@
/* Copyright (C) 2016 Alinson Santos Xavier /*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* *
* This program is free software: you can redistribute it and/or modify it * This file is part of Loop Habit Tracker.
* 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.
* *
* This program is distributed in the hope that it will be useful, but WITHOUT * Loop Habit Tracker is free software: you can redistribute it and/or modify
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * it under the terms of the GNU General Public License as published by the
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * 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. * more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License along
* along with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.isoron.uhabits.fragments; package org.isoron.uhabits.fragments;
@ -57,13 +60,20 @@ public class EditHabitFragment extends DialogFragment
private OnSavedListener onSavedListener; private OnSavedListener onSavedListener;
private Habit originalHabit, modifiedHabit; private Habit originalHabit;
private TextView tvName, tvDescription, tvFreqNum, tvFreqDen, tvReminderTime, tvReminderDays; private Habit modifiedHabit;
private TextView tvName;
private TextView tvDescription;
private TextView tvFreqNum;
private TextView tvFreqDen;
private TextView tvReminderTime;
private TextView tvReminderDays;
private SharedPreferences prefs; private SharedPreferences prefs;
private boolean is24HourMode; private boolean is24HourMode;
static EditHabitFragment editSingleHabitFragment(long id) public static EditHabitFragment editSingleHabitFragment(long id)
{ {
EditHabitFragment frag = new EditHabitFragment(); EditHabitFragment frag = new EditHabitFragment();
Bundle args = new Bundle(); Bundle args = new Bundle();
@ -73,7 +83,7 @@ public class EditHabitFragment extends DialogFragment
return frag; return frag;
} }
static EditHabitFragment createHabitFragment() public static EditHabitFragment createHabitFragment()
{ {
EditHabitFragment frag = new EditHabitFragment(); EditHabitFragment frag = new EditHabitFragment();
Bundle args = new Bundle(); Bundle args = new Bundle();
@ -96,13 +106,13 @@ public class EditHabitFragment extends DialogFragment
Button buttonSave = (Button) view.findViewById(R.id.buttonSave); Button buttonSave = (Button) view.findViewById(R.id.buttonSave);
Button buttonDiscard = (Button) view.findViewById(R.id.buttonDiscard); Button buttonDiscard = (Button) view.findViewById(R.id.buttonDiscard);
ImageButton buttonPickColor = (ImageButton) view.findViewById(R.id.buttonPickColor);
buttonSave.setOnClickListener(this); buttonSave.setOnClickListener(this);
buttonDiscard.setOnClickListener(this); buttonDiscard.setOnClickListener(this);
tvReminderTime.setOnClickListener(this); tvReminderTime.setOnClickListener(this);
tvReminderDays.setOnClickListener(this); tvReminderDays.setOnClickListener(this);
buttonPickColor.setOnClickListener(this);
ImageButton buttonPickColor = (ImageButton) view.findViewById(R.id.buttonPickColor);
prefs = PreferenceManager.getDefaultSharedPreferences(getActivity()); prefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
@ -134,14 +144,27 @@ public class EditHabitFragment extends DialogFragment
tvDescription.append(modifiedHabit.description); tvDescription.append(modifiedHabit.description);
} }
if(savedInstanceState != null)
{
modifiedHabit.color = savedInstanceState.getInt("color", modifiedHabit.color);
modifiedHabit.reminderMin = savedInstanceState.getInt("reminderMin", -1);
modifiedHabit.reminderHour = savedInstanceState.getInt("reminderHour", -1);
modifiedHabit.reminderDays = savedInstanceState.getInt("reminderDays", -1);
if(modifiedHabit.reminderMin < 0)
{
modifiedHabit.reminderMin = null;
modifiedHabit.reminderHour = null;
modifiedHabit.reminderDays = 127;
}
}
tvFreqNum.append(modifiedHabit.freqNum.toString()); tvFreqNum.append(modifiedHabit.freqNum.toString());
tvFreqDen.append(modifiedHabit.freqDen.toString()); tvFreqDen.append(modifiedHabit.freqDen.toString());
changeColor(modifiedHabit.color); changeColor(modifiedHabit.color);
updateReminder(); updateReminder();
buttonPickColor.setOnClickListener(this);
return view; return view;
} }
@ -225,8 +248,6 @@ public class EditHabitFragment extends DialogFragment
private void onSaveButtonClick() private void onSaveButtonClick()
{ {
Command command = null;
modifiedHabit.name = tvName.getText().toString().trim(); modifiedHabit.name = tvName.getText().toString().trim();
modifiedHabit.description = tvDescription.getText().toString().trim(); modifiedHabit.description = tvDescription.getText().toString().trim();
modifiedHabit.freqNum = Integer.parseInt(tvFreqNum.getText().toString()); modifiedHabit.freqNum = Integer.parseInt(tvFreqNum.getText().toString());
@ -239,6 +260,7 @@ public class EditHabitFragment extends DialogFragment
editor.putInt("pref_default_habit_freq_den", modifiedHabit.freqDen); editor.putInt("pref_default_habit_freq_den", modifiedHabit.freqDen);
editor.apply(); editor.apply();
Command command = null;
Habit savedHabit = null; Habit savedHabit = null;
if (mode == EDIT_MODE) if (mode == EDIT_MODE)
@ -246,8 +268,10 @@ public class EditHabitFragment extends DialogFragment
command = new EditHabitCommand(originalHabit, modifiedHabit); command = new EditHabitCommand(originalHabit, modifiedHabit);
savedHabit = originalHabit; savedHabit = originalHabit;
} }
else if (mode == CREATE_MODE)
if (mode == CREATE_MODE) command = new CreateHabitCommand(modifiedHabit); {
command = new CreateHabitCommand(modifiedHabit);
}
if (onSavedListener != null) onSavedListener.onSaved(command, savedHabit); if (onSavedListener != null) onSavedListener.onSaved(command, savedHabit);
@ -325,9 +349,24 @@ public class EditHabitFragment extends DialogFragment
int count = 0; int count = 0;
for(int i = 0; i < 7; i++) for(int i = 0; i < 7; i++)
if(selectedDays[i]) count++; if(selectedDays[i]) count++;
if(count == 0) Arrays.fill(selectedDays, true); if(count == 0) Arrays.fill(selectedDays, true);
modifiedHabit.reminderDays = DateHelper.packWeekdayList(selectedDays); modifiedHabit.reminderDays = DateHelper.packWeekdayList(selectedDays);
updateReminder(); updateReminder();
} }
@Override
public void onSaveInstanceState(Bundle outState)
{
super.onSaveInstanceState(outState);
outState.putInt("color", modifiedHabit.color);
if(modifiedHabit.reminderHour != null)
{
outState.putInt("reminderMin", modifiedHabit.reminderMin);
outState.putInt("reminderHour", modifiedHabit.reminderHour);
outState.putInt("reminderDays", modifiedHabit.reminderDays);
}
}
} }

@ -0,0 +1,116 @@
/*
* 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.fragments;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.LinearLayout;
import android.widget.TextView;
import org.isoron.helpers.DateHelper;
import org.isoron.uhabits.R;
import org.isoron.uhabits.helpers.ListHabitsHelper;
import org.isoron.uhabits.loaders.HabitListLoader;
import org.isoron.uhabits.models.Habit;
import java.util.List;
class HabitListAdapter extends BaseAdapter
{
private LayoutInflater inflater;
private HabitListLoader loader;
private ListHabitsHelper helper;
private List selectedPositions;
private View.OnLongClickListener onCheckmarkLongClickListener;
private View.OnClickListener onCheckmarkClickListener;
public HabitListAdapter(Context context, HabitListLoader loader)
{
this.loader = loader;
inflater = LayoutInflater.from(context);
helper = new ListHabitsHelper(context, loader);
}
@Override
public int getCount()
{
return loader.habits.size();
}
@Override
public Habit getItem(int position)
{
return loader.habitsList.get(position);
}
@Override
public long getItemId(int position)
{
return (getItem(position)).getId();
}
@Override
public View getView(int position, View view, ViewGroup parent)
{
final Habit habit = loader.habitsList.get(position);
if (view == null || (Long) view.getTag(R.id.timestamp_key) != DateHelper.getStartOfToday())
{
view = inflater.inflate(R.layout.list_habits_item, null);
helper.initializeLabelAndIcon(view);
helper.inflateCheckmarkButtons(view, onCheckmarkLongClickListener,
onCheckmarkClickListener, inflater);
}
TextView tvStar = ((TextView) view.findViewById(R.id.tvStar));
TextView tvName = (TextView) view.findViewById(R.id.label);
LinearLayout llInner = (LinearLayout) view.findViewById(R.id.llInner);
LinearLayout llButtons = (LinearLayout) view.findViewById(R.id.llButtons);
llInner.setTag(R.string.habit_key, habit.getId());
helper.updateNameAndIcon(habit, tvStar, tvName);
helper.updateCheckmarkButtons(habit, llButtons);
boolean selected = selectedPositions.contains(position);
helper.updateHabitBackground(llInner, selected);
return view;
}
public void setSelectedPositions(List selectedPositions)
{
this.selectedPositions = selectedPositions;
}
public void setOnCheckmarkLongClickListener(View.OnLongClickListener listener)
{
this.onCheckmarkLongClickListener = listener;
}
public void setOnCheckmarkClickListener(View.OnClickListener listener)
{
this.onCheckmarkClickListener = listener;
}
}

@ -1,37 +1,29 @@
/* Copyright (C) 2016 Alinson Santos Xavier /*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* *
* This program is free software: you can redistribute it and/or modify it * This file is part of Loop Habit Tracker.
* 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.
* *
* This program is distributed in the hope that it will be useful, but WITHOUT * Loop Habit Tracker is free software: you can redistribute it and/or modify
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * it under the terms of the GNU General Public License as published by the
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * 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. * more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License along
* along with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.isoron.uhabits.fragments; package org.isoron.uhabits.fragments;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog;
import android.app.Fragment; import android.app.Fragment;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.graphics.Color;
import android.graphics.Typeface;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.util.DisplayMetrics;
import android.view.ActionMode; import android.view.ActionMode;
import android.view.ContextMenu; import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo; import android.view.ContextMenu.ContextMenuInfo;
@ -46,293 +38,107 @@ import android.view.ViewGroup;
import android.widget.AdapterView; import android.widget.AdapterView;
import android.widget.AdapterView.AdapterContextMenuInfo; import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.AdapterView.OnItemClickListener; import android.widget.AdapterView.OnItemClickListener;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
import android.widget.ProgressBar; import android.widget.ProgressBar;
import android.widget.TextView; import android.widget.TextView;
import com.android.colorpicker.ColorPickerDialog;
import com.android.colorpicker.ColorPickerSwatch;
import com.mobeta.android.dslv.DragSortController; import com.mobeta.android.dslv.DragSortController;
import com.mobeta.android.dslv.DragSortListView; import com.mobeta.android.dslv.DragSortListView;
import com.mobeta.android.dslv.DragSortListView.DropListener; import com.mobeta.android.dslv.DragSortListView.DropListener;
import org.isoron.helpers.ColorHelper;
import org.isoron.helpers.Command; import org.isoron.helpers.Command;
import org.isoron.helpers.DateHelper; import org.isoron.helpers.DateHelper;
import org.isoron.helpers.DialogHelper; import org.isoron.helpers.DialogHelper;
import org.isoron.helpers.DialogHelper.OnSavedListener; import org.isoron.helpers.DialogHelper.OnSavedListener;
import org.isoron.helpers.ReplayableActivity; import org.isoron.helpers.ReplayableActivity;
import org.isoron.uhabits.R; import org.isoron.uhabits.R;
import org.isoron.uhabits.commands.ArchiveHabitsCommand;
import org.isoron.uhabits.commands.ChangeHabitColorCommand;
import org.isoron.uhabits.commands.DeleteHabitsCommand;
import org.isoron.uhabits.commands.ToggleRepetitionCommand; import org.isoron.uhabits.commands.ToggleRepetitionCommand;
import org.isoron.uhabits.commands.UnarchiveHabitsCommand; import org.isoron.uhabits.dialogs.HabitSelectionCallback;
import org.isoron.uhabits.dialogs.HintManager;
import org.isoron.uhabits.helpers.ListHabitsHelper;
import org.isoron.uhabits.helpers.ReminderHelper; import org.isoron.uhabits.helpers.ReminderHelper;
import org.isoron.uhabits.io.CSVExporter;
import org.isoron.uhabits.loaders.HabitListLoader; import org.isoron.uhabits.loaders.HabitListLoader;
import org.isoron.uhabits.models.Habit; import org.isoron.uhabits.models.Habit;
import org.isoron.uhabits.models.Score;
import java.io.File;
import java.util.Date; import java.util.Date;
import java.util.GregorianCalendar;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
public class ListHabitsFragment extends Fragment public class ListHabitsFragment extends Fragment
implements OnSavedListener, OnItemClickListener, OnLongClickListener, DropListener, implements OnSavedListener, OnItemClickListener, OnLongClickListener, DropListener,
OnClickListener, HabitListLoader.Listener, AdapterView.OnItemLongClickListener OnClickListener, HabitListLoader.Listener, AdapterView.OnItemLongClickListener,
HabitSelectionCallback.Listener
{ {
private class ListHabitsActionBarCallback implements ActionMode.Callback
{
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu)
{
getActivity().getMenuInflater().inflate(R.menu.list_habits_context, menu);
updateTitle(mode);
updateActions(menu);
return true;
}
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu)
{
updateTitle(mode);
updateActions(menu);
return true;
}
private void updateActions(Menu menu)
{
boolean showEdit = (selectedPositions.size() == 1);
boolean showColor = true;
boolean showArchive = true;
boolean showUnarchive = true;
if(showEdit) showColor = false;
for(int i : selectedPositions)
{
Habit h = loader.habitsList.get(i);
if(h.isArchived())
{
showColor = false;
showArchive = false;
}
else showUnarchive = false;
}
MenuItem itemEdit = menu.findItem(R.id.action_edit_habit);
MenuItem itemColor = menu.findItem(R.id.action_color);
MenuItem itemArchive = menu.findItem(R.id.action_archive_habit);
MenuItem itemUnarchive = menu.findItem(R.id.action_unarchive_habit);
itemEdit.setVisible(showEdit);
itemColor.setVisible(showColor);
itemArchive.setVisible(showArchive);
itemUnarchive.setVisible(showUnarchive);
}
private void updateTitle(ActionMode mode)
{
mode.setTitle("" + selectedPositions.size());
}
@Override
public boolean onActionItemClicked(final ActionMode mode, MenuItem item)
{
final LinkedList<Habit> selectedHabits = new LinkedList<>();
for(int i : selectedPositions)
selectedHabits.add(loader.habitsList.get(i));
Habit firstHabit = selectedHabits.getFirst();
switch(item.getItemId())
{
case R.id.action_archive_habit:
executeCommand(new ArchiveHabitsCommand(selectedHabits), null);
mode.finish();
return true;
case R.id.action_unarchive_habit:
executeCommand(new UnarchiveHabitsCommand(selectedHabits), null);
mode.finish();
return true;
case R.id.action_edit_habit:
{
EditHabitFragment frag = EditHabitFragment.editSingleHabitFragment(firstHabit.getId());
frag.setOnSavedListener(ListHabitsFragment.this);
frag.show(getFragmentManager(), "dialog");
return true;
}
case R.id.action_color:
{
ColorPickerDialog picker = ColorPickerDialog.newInstance(
R.string.color_picker_default_title, ColorHelper.palette,
firstHabit.color, 4, ColorPickerDialog.SIZE_SMALL);
picker.setOnColorSelectedListener(new ColorPickerSwatch.OnColorSelectedListener()
{
public void onColorSelected(int color)
{
executeCommand(new ChangeHabitColorCommand(selectedHabits, color), null);
mode.finish();
}
});
picker.show(getFragmentManager(), "picker");
return true;
}
case R.id.action_delete:
{
new AlertDialog.Builder(activity)
.setTitle(R.string.delete_habits)
.setMessage(R.string.delete_habits_message)
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
{
executeCommand(new DeleteHabitsCommand(selectedHabits), null);
mode.finish();
}
}).setNegativeButton(android.R.string.no, null)
.show();
return true;
}
case R.id.action_export_csv:
{
onExportHabitsClick(selectedHabits);
return true;
}
}
return false;
}
@Override
public void onDestroyActionMode(ActionMode mode)
{
actionMode = null;
selectedPositions.clear();
adapter.notifyDataSetChanged();
listView.setDragEnabled(true);
}
}
public static final int INACTIVE_COLOR = Color.rgb(200, 200, 200);
public static final int INACTIVE_CHECKMARK_COLOR = Color.rgb(230, 230, 230);
public static final int HINT_INTERVAL = 5;
public static final int HINT_INTERVAL_OFFSET = 2;
public interface OnHabitClickListener
{
void onHabitClicked(Habit habit);
}
ListHabitsAdapter adapter;
DragSortListView listView;
ReplayableActivity activity;
TextView tvNameHeader;
long lastLongClick = 0; long lastLongClick = 0;
private int tvNameWidth;
private int buttonCount;
private View llEmpty;
private View llHint;
private OnHabitClickListener habitClickListener;
private boolean isShortToggleEnabled; private boolean isShortToggleEnabled;
private HabitListLoader loader;
private boolean showArchived; private boolean showArchived;
private SharedPreferences prefs;
private ActionMode actionMode; private ActionMode actionMode;
private HabitListAdapter adapter;
private HabitListLoader loader;
private HintManager hintManager;
private ListHabitsHelper helper;
private List<Integer> selectedPositions; private List<Integer> selectedPositions;
private DragSortController dragSortController; private OnHabitClickListener habitClickListener;
private ReplayableActivity activity;
private SharedPreferences prefs;
private DragSortListView listView;
private LinearLayout llButtonsHeader;
private ProgressBar progressBar; private ProgressBar progressBar;
private View llEmpty;
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) Bundle savedInstanceState)
{ {
DisplayMetrics dm = getResources().getDisplayMetrics(); View view = inflater.inflate(R.layout.list_habits_fragment, container, false);
int width = (int) (dm.widthPixels / dm.density); View llHint = view.findViewById(R.id.llHint);
buttonCount = Math.max(0, (int) ((width - 160) / 42.0)); TextView tvStarEmpty = (TextView) view.findViewById(R.id.tvStarEmpty);
tvNameWidth = (int) ((width - 30 - buttonCount * 42) * dm.density); listView = (DragSortListView) view.findViewById(R.id.listView);
llButtonsHeader = (LinearLayout) view.findViewById(R.id.llButtonsHeader);
llEmpty = view.findViewById(R.id.llEmpty);
progressBar = (ProgressBar) view.findViewById(R.id.progressBar);
selectedPositions = new LinkedList<>();
loader = new HabitListLoader(); loader = new HabitListLoader();
helper = new ListHabitsHelper(activity, loader);
hintManager = new HintManager(activity, llHint);
loader.setListener(this); loader.setListener(this);
loader.setCheckmarkCount(buttonCount); loader.setCheckmarkCount(helper.getButtonCount());
loader.setProgressBar(progressBar);
View view = inflater.inflate(R.layout.list_habits_fragment, container, false); llHint.setOnClickListener(this);
tvNameHeader = (TextView) view.findViewById(R.id.tvNameHeader); tvStarEmpty.setTypeface(helper.getFontawesome());
progressBar = (ProgressBar) view.findViewById(R.id.progressBar); adapter = new HabitListAdapter(getActivity(), loader);
loader.setProgressBar(progressBar); adapter.setSelectedPositions(selectedPositions);
adapter.setOnCheckmarkClickListener(this);
adapter.setOnCheckmarkLongClickListener(this);
DragSortListView.DragListener dragListener = new HabitsDragListener();
DragSortController dragSortController = new HabitsDragSortController();
adapter = new ListHabitsAdapter(getActivity());
listView = (DragSortListView) view.findViewById(R.id.listView);
listView.setAdapter(adapter); listView.setAdapter(adapter);
listView.setOnItemClickListener(this); listView.setOnItemClickListener(this);
listView.setOnItemLongClickListener(this); listView.setOnItemLongClickListener(this);
listView.setDropListener(this); listView.setDropListener(this);
listView.setDragListener(new DragSortListView.DragListener() listView.setDragListener(dragListener);
{
@Override
public void drag(int from, int to)
{
}
@Override
public void startDrag(int position)
{
selectItem(position);
}
});
dragSortController = new DragSortController(listView) {
@Override
public View onCreateFloatView(int position)
{
return adapter.getView(position, null, null);
}
@Override
public void onDestroyFloatView(View floatView)
{
}
};
dragSortController.setRemoveEnabled(false);
listView.setFloatViewManager(dragSortController); listView.setFloatViewManager(dragSortController);
listView.setDragEnabled(true); listView.setDragEnabled(true);
listView.setLongClickable(true); listView.setLongClickable(true);
llHint = view.findViewById(R.id.llHint); if(savedInstanceState != null)
llHint.setOnClickListener(this); {
EditHabitFragment frag = (EditHabitFragment) getFragmentManager()
Typeface fontawesome = Typeface.createFromAsset(getActivity().getAssets(), .findFragmentByTag("editHabit");
"fontawesome-webfont.ttf"); if(frag != null) frag.setOnSavedListener(this);
((TextView) view.findViewById(R.id.tvStarEmpty)).setTypeface(fontawesome); }
llEmpty = view.findViewById(R.id.llEmpty);
loader.updateAllHabits(true); loader.updateAllHabits(true);
setHasOptionsMenu(true);
selectedPositions = new LinkedList<>();
setHasOptionsMenu(true);
return view; return view;
} }
@ -356,42 +162,19 @@ public class ListHabitsFragment extends Fragment
if (timestamp != null && timestamp != DateHelper.getStartOfToday()) if (timestamp != null && timestamp != DateHelper.getStartOfToday())
loader.updateAllHabits(true); loader.updateAllHabits(true);
updateEmptyMessage(); helper.updateEmptyMessage(llEmpty);
updateHeader(); helper.updateHeader(llButtonsHeader);
showNextHint(); hintManager.showHintIfAppropriate();
adapter.notifyDataSetChanged(); adapter.notifyDataSetChanged();
isShortToggleEnabled = prefs.getBoolean("pref_short_toggle", false); isShortToggleEnabled = prefs.getBoolean("pref_short_toggle", false);
} }
private void updateHeader()
{
LayoutInflater inflater = activity.getLayoutInflater();
View view = getView();
if (view == null) return;
GregorianCalendar day = DateHelper.getStartOfTodayCalendar();
LinearLayout llButtonsHeader = (LinearLayout) view.findViewById(R.id.llButtonsHeader);
llButtonsHeader.removeAllViews();
for (int i = 0; i < buttonCount; i++)
{
View tvDay = inflater.inflate(R.layout.list_habits_header_check, null);
Button btCheck = (Button) tvDay.findViewById(R.id.tvCheck);
btCheck.setText(DateHelper.formatHeaderDate(day));
llButtonsHeader.addView(tvDay);
day.add(GregorianCalendar.DAY_OF_MONTH, -1);
}
}
@Override @Override
public void onLoadFinished() public void onLoadFinished()
{ {
adapter.notifyDataSetChanged(); adapter.notifyDataSetChanged();
updateEmptyMessage(); helper.updateEmptyMessage(llEmpty);
} }
@Override @Override
@ -426,7 +209,7 @@ public class ListHabitsFragment extends Fragment
{ {
EditHabitFragment frag = EditHabitFragment.createHabitFragment(); EditHabitFragment frag = EditHabitFragment.createHabitFragment();
frag.setOnSavedListener(this); frag.setOnSavedListener(this);
frag.show(getFragmentManager(), "dialog"); frag.show(getFragmentManager(), "editHabit");
return true; return true;
} }
@ -485,8 +268,13 @@ public class ListHabitsFragment extends Fragment
if(actionMode == null) if(actionMode == null)
{ {
actionMode = getActivity().startActionMode(new ListHabitsActionBarCallback()); HabitSelectionCallback callback = new HabitSelectionCallback(activity, loader);
// listView.setDragEnabled(false); callback.setSelectedPositions(selectedPositions);
callback.setProgressBar(progressBar);
callback.setOnSavedListener(this);
callback.setListener(this);
actionMode = getActivity().startActionMode(callback);
} }
if(actionMode != null) actionMode.invalidate(); if(actionMode != null) actionMode.invalidate();
@ -506,12 +294,6 @@ public class ListHabitsFragment extends Fragment
if(actionMode != null) actionMode.finish(); if(actionMode != null) actionMode.finish();
} }
private void updateEmptyMessage()
{
if (loader.getLastLoadTimestamp() == null) llEmpty.setVisibility(View.GONE);
else llEmpty.setVisibility(loader.habits.size() > 0 ? View.GONE : View.VISIBLE);
}
@Override @Override
public boolean onLongClick(View v) public boolean onLongClick(View v)
{ {
@ -538,15 +320,14 @@ public class ListHabitsFragment extends Fragment
private void toggleCheck(View v) private void toggleCheck(View v)
{ {
Long tag = (Long) v.getTag(R.string.habit_key); Long tag = (Long) v.getTag(R.string.habit_key);
Habit habit = loader.habits.get(tag); Integer offset = (Integer) v.getTag(R.string.offset_key);
int offset = (Integer) v.getTag(R.string.offset_key);
long timestamp = DateHelper.getStartOfDay( long timestamp = DateHelper.getStartOfDay(
DateHelper.getLocalTime() - offset * DateHelper.millisecondsInOneDay); DateHelper.getLocalTime() - offset * DateHelper.millisecondsInOneDay);
if (v.getTag(R.string.toggle_key).equals(2)) updateCheckmark(habit.color, (TextView) v, 0); Habit habit = loader.habits.get(tag);
else updateCheckmark(habit.color, (TextView) v, 2); if(habit == null) return;
helper.toggleCheckmarkView(v, habit);
executeCommand(new ToggleRepetitionCommand(habit, timestamp), habit.getId()); executeCommand(new ToggleRepetitionCommand(habit, timestamp), habit.getId());
} }
@ -555,43 +336,6 @@ public class ListHabitsFragment extends Fragment
activity.executeCommand(c, refreshKey); activity.executeCommand(c, refreshKey);
} }
private void hideHint()
{
llHint.animate().alpha(0f).setDuration(500).setListener(new AnimatorListenerAdapter()
{
@Override
public void onAnimationEnd(Animator animation)
{
llHint.setVisibility(View.GONE);
}
});
}
private void showNextHint()
{
Integer lastHintNumber = prefs.getInt("last_hint_number", -1);
Long lastHintTimestamp = prefs.getLong("last_hint_timestamp", -1);
if(DateHelper.getStartOfToday() > lastHintTimestamp)
showHint(lastHintNumber + 1);
}
private void showHint(int hintNumber)
{
String[] hints = activity.getResources().getStringArray(R.array.hints);
if(hintNumber >= hints.length) return;
prefs.edit().putInt("last_hint_number", hintNumber).apply();
prefs.edit().putLong("last_hint_timestamp", DateHelper.getStartOfToday()).apply();
TextView tvContent = (TextView) llHint.findViewById(R.id.hintContent);
tvContent.setText(hints[hintNumber]);
llHint.setAlpha(0.0f);
llHint.setVisibility(View.VISIBLE);
llHint.animate().alpha(1f).setDuration(500);
}
@Override @Override
public void drop(int from, int to) public void drop(int from, int to)
{ {
@ -614,226 +358,61 @@ public class ListHabitsFragment extends Fragment
break; break;
case R.id.llHint: case R.id.llHint:
hideHint(); hintManager.dismissHint();
break; break;
} }
} }
class ListHabitsAdapter extends BaseAdapter public void onPostExecuteCommand(Long refreshKey)
{ {
private LayoutInflater inflater; if (refreshKey == null) loader.updateAllHabits(true);
private Typeface fontawesome; else loader.updateHabit(refreshKey);
}
public ListHabitsAdapter(Context context)
{
inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); public void onActionModeDestroyed(ActionMode mode)
fontawesome = Typeface.createFromAsset(context.getAssets(), "fontawesome-webfont.ttf"); {
} actionMode = null;
selectedPositions.clear();
adapter.notifyDataSetChanged();
listView.setDragEnabled(true);
}
@Override public interface OnHabitClickListener
public int getCount() {
{ void onHabitClicked(Habit habit);
return loader.habits.size(); }
}
@Override private class HabitsDragSortController extends DragSortController
public Object getItem(int position) {
public HabitsDragSortController()
{ {
return loader.habitsList.get(position); super(ListHabitsFragment.this.listView);
setRemoveEnabled(false);
} }
@Override @Override
public long getItemId(int position) public View onCreateFloatView(int position)
{ {
return ((Habit) getItem(position)).getId(); return adapter.getView(position, null, null);
} }
@Override @Override
public View getView(int position, View view, ViewGroup parent) public void onDestroyFloatView(View floatView)
{
final Habit habit = loader.habitsList.get(position);
if (view == null ||
(Long) view.getTag(R.id.timestamp_key) != DateHelper.getStartOfToday())
{
view = inflater.inflate(R.layout.list_habits_item, null);
((TextView) view.findViewById(R.id.tvStar)).setTypeface(fontawesome);
LinearLayout.LayoutParams params =
new LinearLayout.LayoutParams(tvNameWidth, LayoutParams.WRAP_CONTENT, 1);
view.findViewById(R.id.label).setLayoutParams(params);
inflateCheckmarkButtons(view);
view.setTag(R.id.timestamp_key, DateHelper.getStartOfToday());
}
TextView tvStar = ((TextView) view.findViewById(R.id.tvStar));
TextView tvName = (TextView) view.findViewById(R.id.label);
LinearLayout llInner = (LinearLayout) view.findViewById(R.id.llInner);
LinearLayout llButtons = (LinearLayout) view.findViewById(R.id.llButtons);
llInner.setTag(R.string.habit_key, habit.getId());
updateNameAndIcon(habit, tvStar, tvName);
updateCheckmarkButtons(habit, llButtons);
boolean selected = selectedPositions.contains(position);
if(selected)
llInner.setBackgroundResource(R.drawable.selected_box);
else
{
if (android.os.Build.VERSION.SDK_INT >= 21)
llInner.setBackgroundResource(R.drawable.ripple_white);
else
llInner.setBackgroundResource(R.drawable.card_background);
}
return view;
}
private void inflateCheckmarkButtons(View view)
{
for (int i = 0; i < buttonCount; i++)
{
View check = inflater.inflate(R.layout.list_habits_item_check, null);
TextView btCheck = (TextView) check.findViewById(R.id.tvCheck);
btCheck.setTypeface(fontawesome);
btCheck.setOnLongClickListener(ListHabitsFragment.this);
btCheck.setOnClickListener(ListHabitsFragment.this);
((LinearLayout) view.findViewById(R.id.llButtons)).addView(check);
}
}
}
private void updateCheckmarkButtons(Habit habit, LinearLayout llButtons)
{
int activeColor = getActiveColor(habit);
int m = llButtons.getChildCount();
Long habitId = habit.getId();
int isChecked[] = loader.checkmarks.get(habitId);
for (int i = 0; i < m; i++)
{ {
TextView tvCheck = (TextView) llButtons.getChildAt(i);
tvCheck.setTag(R.string.habit_key, habitId);
tvCheck.setTag(R.string.offset_key, i);
if(isChecked.length > i)
updateCheckmark(activeColor, tvCheck, isChecked[i]);
} }
} }
private void updateNameAndIcon(Habit habit, TextView tvStar, TextView tvName) private class HabitsDragListener implements DragSortListView.DragListener
{ {
int activeColor = getActiveColor(habit); @Override
public void drag(int from, int to)
tvName.setText(habit.name);
tvName.setTextColor(activeColor);
if (habit.isArchived())
{ {
tvStar.setText(getString(R.string.fa_archive));
tvStar.setTextColor(activeColor);
} }
else
{
int score = loader.scores.get(habit.getId());
if (score < Score.HALF_STAR_CUTOFF) @Override
{ public void startDrag(int position)
tvStar.setText(getString(R.string.fa_star_o));
tvStar.setTextColor(INACTIVE_COLOR);
}
else if (score < Score.FULL_STAR_CUTOFF)
{
tvStar.setText(getString(R.string.fa_star_half_o));
tvStar.setTextColor(INACTIVE_COLOR);
}
else
{
tvStar.setText(getString(R.string.fa_star));
tvStar.setTextColor(activeColor);
}
}
}
private int getActiveColor(Habit habit)
{
int activeColor = habit.color;
if(habit.isArchived()) activeColor = INACTIVE_COLOR;
return activeColor;
}
private void updateCheckmark(int activeColor, TextView tvCheck, int check)
{
switch (check)
{ {
case 2: selectItem(position);
tvCheck.setText(R.string.fa_check);
tvCheck.setTextColor(activeColor);
tvCheck.setTag(R.string.toggle_key, 2);
break;
case 1:
tvCheck.setText(R.string.fa_check);
tvCheck.setTextColor(INACTIVE_CHECKMARK_COLOR);
tvCheck.setTag(R.string.toggle_key, 1);
break;
case 0:
tvCheck.setText(R.string.fa_times);
tvCheck.setTextColor(INACTIVE_CHECKMARK_COLOR);
tvCheck.setTag(R.string.toggle_key, 0);
break;
} }
} }
public void onPostExecuteCommand(Long refreshKey)
{
if (refreshKey == null) loader.updateAllHabits(true);
else loader.updateHabit(refreshKey);
}
private void onExportHabitsClick(final LinkedList<Habit> selectedHabits)
{
new AsyncTask<Void, Void, Void>()
{
String filename;
@Override
protected void onPreExecute()
{
progressBar.setIndeterminate(true);
progressBar.setVisibility(View.VISIBLE);
}
@Override
protected void onPostExecute(Void aVoid)
{
if(filename != null)
{
Intent intent = new Intent();
intent.setAction(Intent.ACTION_SEND);
intent.setType("application/zip");
intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(new File(filename)));
startActivity(intent);
}
progressBar.setVisibility(View.GONE);
}
@Override
protected Void doInBackground(Void... params)
{
CSVExporter exporter = new CSVExporter(activity, selectedHabits);
filename = exporter.writeArchive();
return null;
}
}.execute();
}
} }

@ -1,17 +1,20 @@
/* Copyright (C) 2016 Alinson Santos Xavier /*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* *
* This program is free software: you can redistribute it and/or modify it * This file is part of Loop Habit Tracker.
* 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.
* *
* This program is distributed in the hope that it will be useful, but WITHOUT * Loop Habit Tracker is free software: you can redistribute it and/or modify
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * it under the terms of the GNU General Public License as published by the
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * 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. * more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License along
* along with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.isoron.uhabits.fragments; package org.isoron.uhabits.fragments;

@ -1,17 +1,20 @@
/* Copyright (C) 2016 Alinson Santos Xavier /*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* *
* This program is free software: you can redistribute it and/or modify it * This file is part of Loop Habit Tracker.
* 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.
* *
* This program is distributed in the hope that it will be useful, but WITHOUT * Loop Habit Tracker is free software: you can redistribute it and/or modify
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * it under the terms of the GNU General Public License as published by the
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * 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. * more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License along
* along with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.isoron.uhabits.fragments; package org.isoron.uhabits.fragments;
@ -25,25 +28,34 @@ import android.view.MenuInflater;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView; import android.widget.TextView;
import org.isoron.helpers.ColorHelper; import org.isoron.helpers.ColorHelper;
import org.isoron.helpers.Command; import org.isoron.helpers.Command;
import org.isoron.helpers.DialogHelper; import org.isoron.helpers.DialogHelper;
import org.isoron.uhabits.HabitBroadcastReceiver;
import org.isoron.uhabits.R; import org.isoron.uhabits.R;
import org.isoron.uhabits.ShowHabitActivity; import org.isoron.uhabits.ShowHabitActivity;
import org.isoron.uhabits.dialogs.HistoryEditorDialog;
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.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;
public class ShowHabitFragment extends Fragment implements DialogHelper.OnSavedListener public class ShowHabitFragment extends Fragment
implements DialogHelper.OnSavedListener, HistoryEditorDialog.Listener
{ {
protected ShowHabitActivity activity; protected ShowHabitActivity activity;
private Habit habit; private Habit habit;
private HabitStreakView streakView;
private HabitScoreView scoreView;
private HabitHistoryView historyView;
private HabitFrequencyView punchcardView;
@Override @Override
public void onStart() public void onStart()
@ -61,36 +73,75 @@ public class ShowHabitFragment extends Fragment implements DialogHelper.OnSavedL
habit.checkmarks.rebuild(); habit.checkmarks.rebuild();
if (android.os.Build.VERSION.SDK_INT >= 21) Button btEditHistory = (Button) view.findViewById(R.id.btEditHistory);
{ streakView = (HabitStreakView) view.findViewById(R.id.streakView);
int darkerHabitColor = ColorHelper.mixColors(habit.color, Color.BLACK, 0.75f); scoreView = (HabitScoreView) view.findViewById(R.id.scoreView);
activity.getWindow().setStatusBarColor(darkerHabitColor); historyView = (HabitHistoryView) view.findViewById(R.id.historyView);
} punchcardView = (HabitFrequencyView) view.findViewById(R.id.punchcardView);
TextView tvHistory = (TextView) view.findViewById(R.id.tvHistory); updateHeaders(view);
TextView tvOverview = (TextView) view.findViewById(R.id.tvOverview); updateScoreRing(view);
TextView tvStrength = (TextView) view.findViewById(R.id.tvStrength);
TextView tvStreaks = (TextView) view.findViewById(R.id.tvStreaks);
RingView scoreRing = (RingView) view.findViewById(R.id.scoreRing);
HabitStreakView streakView = (HabitStreakView) view.findViewById(R.id.streakView);
HabitScoreView scoreView = (HabitScoreView) view.findViewById(R.id.scoreView);
HabitHistoryView historyView = (HabitHistoryView) view.findViewById(R.id.historyView);
tvHistory.setTextColor(habit.color);
tvOverview.setTextColor(habit.color);
tvStrength.setTextColor(habit.color);
tvStreaks.setTextColor(habit.color);
scoreRing.setColor(habit.color);
scoreRing.setPercentage((float) habit.scores.getNewestValue() / Score.MAX_SCORE);
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()
{
@Override
public void onClick(View v)
{
HistoryEditorDialog frag = new HistoryEditorDialog();
frag.setHabit(habit);
frag.setListener(ShowHabitFragment.this);
frag.show(getFragmentManager(), "historyEditor");
}
});
if(savedInstanceState != null)
{
EditHabitFragment fragEdit = (EditHabitFragment) getFragmentManager()
.findFragmentByTag("editHabit");
HistoryEditorDialog fragEditor = (HistoryEditorDialog) getFragmentManager()
.findFragmentByTag("historyEditor");
if(fragEdit != null) fragEdit.setOnSavedListener(this);
if(fragEditor != null) fragEditor.setListener(this);
}
setHasOptionsMenu(true); setHasOptionsMenu(true);
return view; return view;
} }
private void updateScoreRing(View view)
{
RingView scoreRing = (RingView) view.findViewById(R.id.scoreRing);
scoreRing.setColor(habit.color);
scoreRing.setPercentage((float) habit.scores.getNewestValue() / Score.MAX_SCORE);
}
private void updateHeaders(View view)
{
if (android.os.Build.VERSION.SDK_INT >= 21)
{
int darkerHabitColor = ColorHelper.mixColors(habit.color, Color.BLACK, 0.75f);
activity.getWindow().setStatusBarColor(darkerHabitColor);
}
updateColor(view, R.id.tvHistory);
updateColor(view, R.id.tvOverview);
updateColor(view, R.id.tvStrength);
updateColor(view, R.id.tvStreaks);
updateColor(view, R.id.tvWeekdayFreq);
}
private void updateColor(View view, int viewId)
{
TextView textView = (TextView) view.findViewById(viewId);
textView.setTextColor(habit.color);
}
@Override @Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) public void onCreateOptionsMenu(Menu menu, MenuInflater inflater)
{ {
@ -106,7 +157,7 @@ public class ShowHabitFragment extends Fragment implements DialogHelper.OnSavedL
{ {
EditHabitFragment frag = EditHabitFragment.editSingleHabitFragment(habit.getId()); EditHabitFragment frag = EditHabitFragment.editSingleHabitFragment(habit.getId());
frag.setOnSavedListener(this); frag.setOnSavedListener(this);
frag.show(getFragmentManager(), "dialog"); frag.show(getFragmentManager(), "editHabit");
return true; return true;
} }
} }
@ -125,4 +176,20 @@ public class ShowHabitFragment extends Fragment implements DialogHelper.OnSavedL
ReminderHelper.createReminderAlarms(activity); ReminderHelper.createReminderAlarms(activity);
activity.recreate(); activity.recreate();
} }
@Override
public void onHistoryEditorClosed()
{
refreshData();
HabitBroadcastReceiver.sendRefreshBroadcast(getActivity());
}
public void refreshData()
{
streakView.refreshData();
historyView.refreshData();
scoreView.refreshData();
punchcardView.refreshData();
updateScoreRing(getView());
}
} }

@ -0,0 +1,230 @@
/*
* 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.helpers;
import android.content.Context;
import android.graphics.Color;
import android.graphics.Typeface;
import android.util.DisplayMetrics;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
import org.isoron.helpers.DateHelper;
import org.isoron.uhabits.R;
import org.isoron.uhabits.loaders.HabitListLoader;
import org.isoron.uhabits.models.Habit;
import org.isoron.uhabits.models.Score;
import java.util.GregorianCalendar;
public class ListHabitsHelper
{
public static final int INACTIVE_COLOR = Color.rgb(200, 200, 200);
public static final int INACTIVE_CHECKMARK_COLOR = Color.rgb(230, 230, 230);
private final Context context;
private final HabitListLoader loader;
private Typeface fontawesome;
public ListHabitsHelper(Context context, HabitListLoader loader)
{
this.context = context;
this.loader = loader;
fontawesome = Typeface.createFromAsset(context.getAssets(), "fontawesome-webfont.ttf");
}
public Typeface getFontawesome()
{
return fontawesome;
}
public int getButtonCount()
{
DisplayMetrics dm = context.getResources().getDisplayMetrics();
int width = (int) (dm.widthPixels / dm.density);
return Math.max(0, (int) ((width - 160) / 42.0));
}
public int getHabitNameWidth()
{
DisplayMetrics dm = context.getResources().getDisplayMetrics();
int width = (int) (dm.widthPixels / dm.density);
return (int) ((width - 30 - getButtonCount() * 42) * dm.density);
}
public void updateCheckmarkButtons(Habit habit, LinearLayout llButtons)
{
int activeColor = getActiveColor(habit);
int m = llButtons.getChildCount();
Long habitId = habit.getId();
int isChecked[] = loader.checkmarks.get(habitId);
for (int i = 0; i < m; i++)
{
TextView tvCheck = (TextView) llButtons.getChildAt(i);
tvCheck.setTag(R.string.habit_key, habitId);
tvCheck.setTag(R.string.offset_key, i);
if(isChecked.length > i)
updateCheckmark(activeColor, tvCheck, isChecked[i]);
}
}
public int getActiveColor(Habit habit)
{
int activeColor = habit.color;
if(habit.isArchived()) activeColor = INACTIVE_COLOR;
return activeColor;
}
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)
{
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());
if (score < Score.HALF_STAR_CUTOFF)
{
tvStar.setText(context.getString(R.string.fa_star_o));
tvStar.setTextColor(INACTIVE_COLOR);
}
else if (score < Score.FULL_STAR_CUTOFF)
{
tvStar.setText(context.getString(R.string.fa_star_half_o));
tvStar.setTextColor(INACTIVE_COLOR);
}
else
{
tvStar.setText(context.getString(R.string.fa_star));
tvStar.setTextColor(activeColor);
}
}
}
public void updateCheckmark(int activeColor, TextView tvCheck, int check)
{
switch (check)
{
case 2:
tvCheck.setText(R.string.fa_check);
tvCheck.setTextColor(activeColor);
tvCheck.setTag(R.string.toggle_key, 2);
break;
case 1:
tvCheck.setText(R.string.fa_check);
tvCheck.setTextColor(INACTIVE_CHECKMARK_COLOR);
tvCheck.setTag(R.string.toggle_key, 1);
break;
case 0:
tvCheck.setText(R.string.fa_times);
tvCheck.setTextColor(INACTIVE_CHECKMARK_COLOR);
tvCheck.setTag(R.string.toggle_key, 0);
break;
}
}
public void updateHabitBackground(View view, boolean isSelected)
{
if (isSelected)
view.setBackgroundResource(R.drawable.selected_box);
else
{
if (android.os.Build.VERSION.SDK_INT >= 21)
view.setBackgroundResource(R.drawable.ripple_white);
else view.setBackgroundResource(R.drawable.card_background);
}
}
public void inflateCheckmarkButtons(View view, View.OnLongClickListener onLongClickListener,
View.OnClickListener onClickListener, LayoutInflater inflater)
{
for (int i = 0; i < getButtonCount(); i++)
{
View check = inflater.inflate(R.layout.list_habits_item_check, null);
TextView btCheck = (TextView) check.findViewById(R.id.tvCheck);
btCheck.setTypeface(fontawesome);
btCheck.setOnLongClickListener(onLongClickListener);
btCheck.setOnClickListener(onClickListener);
((LinearLayout) view.findViewById(R.id.llButtons)).addView(check);
}
view.setTag(R.id.timestamp_key, DateHelper.getStartOfToday());
}
public void updateHeader(ViewGroup header)
{
LayoutInflater inflater = LayoutInflater.from(context);
GregorianCalendar day = DateHelper.getStartOfTodayCalendar();
header.removeAllViews();
for (int i = 0; i < getButtonCount(); i++)
{
View tvDay = inflater.inflate(R.layout.list_habits_header_check, null);
Button btCheck = (Button) tvDay.findViewById(R.id.tvCheck);
btCheck.setText(DateHelper.formatHeaderDate(day));
header.addView(tvDay);
day.add(GregorianCalendar.DAY_OF_MONTH, -1);
}
}
public void updateEmptyMessage(View view)
{
if (loader.getLastLoadTimestamp() == null) view.setVisibility(View.GONE);
else view.setVisibility(loader.habits.size() > 0 ? View.GONE : View.VISIBLE);
}
public void toggleCheckmarkView(View v, Habit habit)
{
if (v.getTag(R.string.toggle_key).equals(2))
updateCheckmark(habit.color, (TextView) v, 0);
else
updateCheckmark(habit.color, (TextView) v, 2);
}
}

@ -1,17 +1,20 @@
/* Copyright (C) 2016 Alinson Santos Xavier /*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* *
* This program is free software: you can redistribute it and/or modify it * This file is part of Loop Habit Tracker.
* 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.
* *
* This program is distributed in the hope that it will be useful, but WITHOUT * Loop Habit Tracker is free software: you can redistribute it and/or modify
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * it under the terms of the GNU General Public License as published by the
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * 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. * more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License along
* along with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.isoron.uhabits.helpers; package org.isoron.uhabits.helpers;

@ -1,3 +1,22 @@
/*
* 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.io; package org.isoron.uhabits.io;
import android.content.Context; import android.content.Context;

@ -1,20 +1,20 @@
/* /*
* Copyright (C) 2016 Alinson Santos Xavier * Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* *
* This program is free software: you can redistribute it and/or modify it * This file is part of Loop Habit Tracker.
* 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.
* *
* This program is distributed in the hope that it will be useful, but WITHOUT * Loop Habit Tracker is free software: you can redistribute it and/or modify
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * it under the terms of the GNU General Public License as published by the
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * Free Software Foundation, either version 3 of the License, or (at your
* more details. * option) any later version.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* *
* 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.loaders; package org.isoron.uhabits.loaders;

@ -1,17 +1,20 @@
/* Copyright (C) 2016 Alinson Santos Xavier /*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* *
* This program is free software: you can redistribute it and/or modify it * This file is part of Loop Habit Tracker.
* 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.
* *
* This program is distributed in the hope that it will be useful, but WITHOUT * Loop Habit Tracker is free software: you can redistribute it and/or modify
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * it under the terms of the GNU General Public License as published by the
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * 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. * more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License along
* along with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.isoron.uhabits.models; package org.isoron.uhabits.models;

@ -1,17 +1,20 @@
/* Copyright (C) 2016 Alinson Santos Xavier /*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* *
* This program is free software: you can redistribute it and/or modify it * This file is part of Loop Habit Tracker.
* 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.
* *
* This program is distributed in the hope that it will be useful, but WITHOUT * Loop Habit Tracker is free software: you can redistribute it and/or modify
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * it under the terms of the GNU General Public License as published by the
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * 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. * more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License along
* along with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.isoron.uhabits.models; package org.isoron.uhabits.models;

@ -1,17 +1,20 @@
/* Copyright (C) 2016 Alinson Santos Xavier /*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* *
* This program is free software: you can redistribute it and/or modify it * This file is part of Loop Habit Tracker.
* 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.
* *
* This program is distributed in the hope that it will be useful, but WITHOUT * Loop Habit Tracker is free software: you can redistribute it and/or modify
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * it under the terms of the GNU General Public License as published by the
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * 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. * more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License along
* along with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.isoron.uhabits.models; package org.isoron.uhabits.models;

@ -1,17 +1,20 @@
/* Copyright (C) 2016 Alinson Santos Xavier /*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* *
* This program is free software: you can redistribute it and/or modify it * This file is part of Loop Habit Tracker.
* 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.
* *
* This program is distributed in the hope that it will be useful, but WITHOUT * Loop Habit Tracker is free software: you can redistribute it and/or modify
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * it under the terms of the GNU General Public License as published by the
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * 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. * more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License along
* along with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.isoron.uhabits.models; package org.isoron.uhabits.models;

@ -1,27 +1,39 @@
/* Copyright (C) 2016 Alinson Santos Xavier /*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* *
* This program is free software: you can redistribute it and/or modify it * This file is part of Loop Habit Tracker.
* 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.
* *
* This program is distributed in the hope that it will be useful, but WITHOUT * Loop Habit Tracker is free software: you can redistribute it and/or modify
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * it under the terms of the GNU General Public License as published by the
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * 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. * more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License along
* along with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
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
{ {
@ -65,6 +77,8 @@ public class RepetitionList
public void toggle(long timestamp) public void toggle(long timestamp)
{ {
timestamp = DateHelper.getStartOfDay(timestamp);
if (contains(timestamp)) if (contains(timestamp))
{ {
delete(timestamp); delete(timestamp);
@ -93,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;
}
} }

@ -1,17 +1,20 @@
/* Copyright (C) 2016 Alinson Santos Xavier /*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* *
* This program is free software: you can redistribute it and/or modify it * This file is part of Loop Habit Tracker.
* 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.
* *
* This program is distributed in the hope that it will be useful, but WITHOUT * Loop Habit Tracker is free software: you can redistribute it and/or modify
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * it under the terms of the GNU General Public License as published by the
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * 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. * more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License along
* along with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.isoron.uhabits.models; package org.isoron.uhabits.models;

@ -1,17 +1,20 @@
/* Copyright (C) 2016 Alinson Santos Xavier /*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* *
* This program is free software: you can redistribute it and/or modify it * This file is part of Loop Habit Tracker.
* 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.
* *
* This program is distributed in the hope that it will be useful, but WITHOUT * Loop Habit Tracker is free software: you can redistribute it and/or modify
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * it under the terms of the GNU General Public License as published by the
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * 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. * more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License along
* along with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.isoron.uhabits.models; package org.isoron.uhabits.models;
@ -123,6 +126,9 @@ public class ScoreList
public int[] getAllValues(Long fromTimestamp, Long toTimestamp, Integer divisor) public int[] getAllValues(Long fromTimestamp, Long toTimestamp, Integer divisor)
{ {
// Force rebuild of the score table
getNewestValue();
Long offset = toTimestamp - (divisor - 1) * DateHelper.millisecondsInOneDay; Long offset = toTimestamp - (divisor - 1) * DateHelper.millisecondsInOneDay;
String query = "select ((timestamp - ?) / ?) as time, avg(score) from Score " + String query = "select ((timestamp - ?) / ?) as time, avg(score) from Score " +

@ -1,17 +1,20 @@
/* Copyright (C) 2016 Alinson Santos Xavier /*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* *
* This program is free software: you can redistribute it and/or modify it * This file is part of Loop Habit Tracker.
* 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.
* *
* This program is distributed in the hope that it will be useful, but WITHOUT * Loop Habit Tracker is free software: you can redistribute it and/or modify
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * it under the terms of the GNU General Public License as published by the
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * 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. * more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License along
* along with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.isoron.uhabits.models; package org.isoron.uhabits.models;

@ -1,17 +1,20 @@
/* Copyright (C) 2016 Alinson Santos Xavier /*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* *
* This program is free software: you can redistribute it and/or modify it * This file is part of Loop Habit Tracker.
* 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.
* *
* This program is distributed in the hope that it will be useful, but WITHOUT * Loop Habit Tracker is free software: you can redistribute it and/or modify
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * it under the terms of the GNU General Public License as published by the
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * 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. * more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License along
* along with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.isoron.uhabits.models; package org.isoron.uhabits.models;

@ -1,17 +1,20 @@
/* Copyright (C) 2016 Alinson Santos Xavier /*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* *
* This program is free software: you can redistribute it and/or modify it * This file is part of Loop Habit Tracker.
* 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.
* *
* This program is distributed in the hope that it will be useful, but WITHOUT * Loop Habit Tracker is free software: you can redistribute it and/or modify
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * it under the terms of the GNU General Public License as published by the
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * 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. * more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License along
* along with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.isoron.uhabits.views; package org.isoron.uhabits.views;

@ -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();
}
}

@ -1,17 +1,20 @@
/* Copyright (C) 2016 Alinson Santos Xavier /*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* *
* This program is free software: you can redistribute it and/or modify it * This file is part of Loop Habit Tracker.
* 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.
* *
* This program is distributed in the hope that it will be useful, but WITHOUT * Loop Habit Tracker is free software: you can redistribute it and/or modify
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * it under the terms of the GNU General Public License as published by the
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * 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. * more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License along
* along with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.isoron.uhabits.views; package org.isoron.uhabits.views;
@ -22,10 +25,13 @@ import android.graphics.Color;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.Paint.Align; import android.graphics.Paint.Align;
import android.graphics.Rect; import android.graphics.Rect;
import android.os.AsyncTask;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.MotionEvent;
import org.isoron.helpers.ColorHelper; import org.isoron.helpers.ColorHelper;
import org.isoron.helpers.DateHelper; import org.isoron.helpers.DateHelper;
import org.isoron.uhabits.R;
import org.isoron.uhabits.models.Habit; import org.isoron.uhabits.models.Habit;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
@ -47,7 +53,6 @@ public class HabitHistoryView extends ScrollableDataView
private int columnWidth; private int columnWidth;
private int columnHeight; private int columnHeight;
private int nColumns; private int nColumns;
private int baseSize;
private String wdays[]; private String wdays[];
private SimpleDateFormat dfMonth; private SimpleDateFormat dfMonth;
@ -62,11 +67,14 @@ public class HabitHistoryView extends ScrollableDataView
private boolean isBackgroundTransparent; private boolean isBackgroundTransparent;
private int textColor; private int textColor;
private boolean isEditable;
public HabitHistoryView(Context context, AttributeSet attrs) public HabitHistoryView(Context context, AttributeSet attrs)
{ {
super(context, attrs); super(context, attrs);
this.primaryColor = ColorHelper.palette[7]; this.primaryColor = ColorHelper.palette[7];
this.checkmarks = new int[0];
this.isEditable = false;
init(); init();
} }
@ -74,12 +82,13 @@ public class HabitHistoryView extends ScrollableDataView
{ {
this.habit = habit; this.habit = habit;
createColors(); createColors();
fetchData(); refreshData();
postInvalidate(); postInvalidate();
} }
private void init() private void init()
{ {
refreshData();
createPaints(); createPaints();
createColors(); createColors();
@ -93,6 +102,7 @@ public class HabitHistoryView extends ScrollableDataView
private void updateDate() private void updateDate()
{ {
baseDate = new GregorianCalendar(); baseDate = new GregorianCalendar();
baseDate.setTimeInMillis(DateHelper.getLocalTime());
baseDate.add(Calendar.DAY_OF_YEAR, -(getDataOffset() - 1) * 7); baseDate.add(Calendar.DAY_OF_YEAR, -(getDataOffset() - 1) * 7);
nDays = (nColumns - 1) * 7; nDays = (nColumns - 1) * 7;
@ -114,23 +124,42 @@ public class HabitHistoryView extends ScrollableDataView
protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight)
{ {
if(height < 8) height = 200; if(height < 8) height = 200;
int baseSize = height / 8;
baseSize = height / 8;
setScrollerBucketSize(baseSize); setScrollerBucketSize(baseSize);
columnWidth = baseSize;
columnHeight = 8 * baseSize;
nColumns = width / baseSize;
squareSpacing = (int) Math.floor(baseSize / 15.0); squareSpacing = (int) Math.floor(baseSize / 15.0);
pSquareFg.setTextSize(baseSize * 0.5f); int maxTextSize = getResources().getDimensionPixelSize(R.dimen.history_max_font_size);
pTextHeader.setTextSize(baseSize * 0.5f); float textSize = Math.min(baseSize * 0.5f, maxTextSize);
pSquareFg.setTextSize(textSize);
pTextHeader.setTextSize(textSize);
squareTextOffset = pSquareFg.getFontSpacing() * 0.4f; squareTextOffset = pSquareFg.getFontSpacing() * 0.4f;
headerTextOffset = pTextHeader.getFontSpacing() * 0.3f; headerTextOffset = pTextHeader.getFontSpacing() * 0.3f;
int rightLabelWidth = getWeekdayLabelWidth();
int horizontalPadding = getPaddingRight() + getPaddingLeft();
columnWidth = baseSize;
columnHeight = 8 * baseSize;
nColumns = (width - rightLabelWidth - horizontalPadding) / baseSize + 1;
updateDate(); updateDate();
} }
private int getWeekdayLabelWidth()
{
int width = 0;
Rect bounds = new Rect();
for(String w : wdays)
{
pSquareFg.getTextBounds(w, 0, w.length(), bounds);
width = Math.max(width, bounds.right);
}
return width;
}
private void createColors() private void createColors()
{ {
if(habit != null) if(habit != null)
@ -176,22 +205,18 @@ public class HabitHistoryView extends ScrollableDataView
pSquareFg.setTextAlign(Align.CENTER); pSquareFg.setTextAlign(Align.CENTER);
} }
protected void fetchData() public void refreshData()
{ {
if(isInEditMode()) if(isInEditMode())
generateRandomData(); generateRandomData();
else else
{ {
if(habit == null) if(habit == null) return;
{
checkmarks = new int[0];
return;
}
checkmarks = habit.checkmarks.getAllValues(); checkmarks = habit.checkmarks.getAllValues();
} }
updateDate(); updateDate();
invalidate();
} }
private void generateRandomData() private void generateRandomData()
@ -223,6 +248,7 @@ public class HabitHistoryView extends ScrollableDataView
super.onDraw(canvas); super.onDraw(canvas);
baseLocation.set(0, 0, columnWidth - squareSpacing, columnWidth - squareSpacing); baseLocation.set(0, 0, columnWidth - squareSpacing, columnWidth - squareSpacing);
baseLocation.offset(getPaddingLeft(), getPaddingTop());
previousMonth = ""; previousMonth = "";
previousYear = ""; previousYear = "";
@ -326,4 +352,59 @@ public class HabitHistoryView extends ScrollableDataView
this.isBackgroundTransparent = isBackgroundTransparent; this.isBackgroundTransparent = isBackgroundTransparent;
createColors(); createColors();
} }
@Override
public boolean onSingleTapUp(MotionEvent e)
{
if(!isEditable) return false;
int pointerId = e.getPointerId(0);
float x = e.getX(pointerId);
float y = e.getY(pointerId);
final Long timestamp = positionToTimestamp(x, y);
if(timestamp == null) return false;
new AsyncTask<Void, Void, Void>()
{
@Override
protected Void doInBackground(Void... params)
{
habit.repetitions.toggle(timestamp);
return null;
}
@Override
protected void onPostExecute(Void aVoid)
{
refreshData();
invalidate();
}
}.execute();
return true;
}
private Long positionToTimestamp(float x, float y)
{
int col = (int) (x / columnWidth);
int row = (int) (y / columnWidth);
if(row == 0) return null;
if(col == nColumns - 1) return null;
int offset = col * 7 + (row - 1);
Calendar date = (Calendar) baseDate.clone();
date.add(Calendar.DAY_OF_YEAR, offset);
if(DateHelper.getStartOfDay(date.getTimeInMillis()) > DateHelper.getStartOfToday())
return null;
return date.getTimeInMillis();
}
public void setIsEditable(boolean isEditable)
{
this.isEditable = isEditable;
}
} }

@ -1,17 +1,20 @@
/* Copyright (C) 2016 Alinson Santos Xavier /*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* *
* This program is free software: you can redistribute it and/or modify it * This file is part of Loop Habit Tracker.
* 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.
* *
* This program is distributed in the hope that it will be useful, but WITHOUT * Loop Habit Tracker is free software: you can redistribute it and/or modify
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * it under the terms of the GNU General Public License as published by the
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * 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. * more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License along
* along with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.isoron.uhabits.views; package org.isoron.uhabits.views;
@ -68,6 +71,7 @@ public class HabitScoreView extends ScrollableDataView
{ {
super(context, attrs); super(context, attrs);
this.primaryColor = ColorHelper.palette[7]; this.primaryColor = ColorHelper.palette[7];
this.scores = new int[0];
init(); init();
} }
@ -75,12 +79,13 @@ public class HabitScoreView extends ScrollableDataView
{ {
this.habit = habit; this.habit = habit;
createColors(); createColors();
fetchData(); refreshData();
postInvalidate(); postInvalidate();
} }
private void init() private void init()
{ {
refreshData();
createPaints(); createPaints();
createColors(); createColors();
@ -159,21 +164,17 @@ public class HabitScoreView extends ScrollableDataView
em = pText.getFontSpacing(); em = pText.getFontSpacing();
} }
protected void fetchData() public void refreshData()
{ {
if(isInEditMode()) if(isInEditMode())
generateRandomData(); generateRandomData();
else else
{ {
if (habit == null) if (habit == null) return;
{
scores = new int[0];
return;
}
scores = habit.scores.getAllValues(BUCKET_SIZE * DateHelper.millisecondsInOneDay); scores = habit.scores.getAllValues(BUCKET_SIZE * DateHelper.millisecondsInOneDay);
} }
invalidate();
} }
private void generateRandomData() private void generateRandomData()

@ -1,17 +1,20 @@
/* Copyright (C) 2016 Alinson Santos Xavier /*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* *
* This program is free software: you can redistribute it and/or modify it * This file is part of Loop Habit Tracker.
* 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.
* *
* This program is distributed in the hope that it will be useful, but WITHOUT * Loop Habit Tracker is free software: you can redistribute it and/or modify
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * it under the terms of the GNU General Public License as published by the
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * 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. * more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License along
* along with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.isoron.uhabits.views; package org.isoron.uhabits.views;
@ -62,6 +65,7 @@ public class HabitStreakView extends ScrollableDataView
{ {
super(context, attrs); super(context, attrs);
this.primaryColor = ColorHelper.palette[7]; this.primaryColor = ColorHelper.palette[7];
startTimes = endTimes = lengths = new long[0];
init(); init();
} }
@ -70,12 +74,13 @@ public class HabitStreakView extends ScrollableDataView
this.habit = habit; this.habit = habit;
createColors(); createColors();
fetchData(); refreshData();
postInvalidate(); postInvalidate();
} }
private void init() private void init()
{ {
refreshData();
createPaints(); createPaints();
createColors(); createColors();
@ -154,17 +159,13 @@ public class HabitStreakView extends ScrollableDataView
pBar.setAntiAlias(true); pBar.setAntiAlias(true);
} }
protected void fetchData() public void refreshData()
{ {
if(isInEditMode()) if(isInEditMode())
generateRandomData(); generateRandomData();
else else
{ {
if(habit == null) if(habit == null) return;
{
startTimes = endTimes = lengths = new long[0];
return;
}
List<Streak> streaks = habit.streaks.getAll(); List<Streak> streaks = habit.streaks.getAll();
int size = streaks.size(); int size = streaks.size();
@ -184,6 +185,8 @@ public class HabitStreakView extends ScrollableDataView
maxStreakLength = Math.max(maxStreakLength, s.length); maxStreakLength = Math.max(maxStreakLength, s.length);
} }
} }
invalidate();
} }
private void generateRandomData() private void generateRandomData()

@ -1,17 +1,20 @@
/* Copyright (C) 2016 Alinson Santos Xavier /*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* *
* This program is free software: you can redistribute it and/or modify it * This file is part of Loop Habit Tracker.
* 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.
* *
* This program is distributed in the hope that it will be useful, but WITHOUT * Loop Habit Tracker is free software: you can redistribute it and/or modify
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * it under the terms of the GNU General Public License as published by the
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * 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. * more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License along
* along with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.isoron.uhabits.views; package org.isoron.uhabits.views;

@ -1,20 +1,20 @@
/* /*
* Copyright (C) 2016 Alinson Santos Xavier * Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* *
* This program is free software: you can redistribute it and/or modify it * This file is part of Loop Habit Tracker.
* 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.
* *
* This program is distributed in the hope that it will be useful, but WITHOUT * Loop Habit Tracker is free software: you can redistribute it and/or modify
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * it under the terms of the GNU General Public License as published by the
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * Free Software Foundation, either version 3 of the License, or (at your
* more details. * option) any later version.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* *
* 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; package org.isoron.uhabits.views;
@ -58,8 +58,6 @@ public abstract class ScrollableDataView extends View implements GestureDetector
scrollAnimator.addUpdateListener(this); scrollAnimator.addUpdateListener(this);
} }
protected abstract void fetchData();
@Override @Override
public boolean onTouchEvent(MotionEvent event) public boolean onTouchEvent(MotionEvent event)
{ {

@ -1,17 +1,20 @@
/* Copyright (C) 2016 Alinson Santos Xavier /*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* *
* This program is free software: you can redistribute it and/or modify it * This file is part of Loop Habit Tracker.
* 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.
* *
* This program is distributed in the hope that it will be useful, but WITHOUT * Loop Habit Tracker is free software: you can redistribute it and/or modify
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * it under the terms of the GNU General Public License as published by the
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * 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. * more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License along
* along with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.isoron.uhabits.widgets; package org.isoron.uhabits.widgets;

@ -1,17 +1,20 @@
/* Copyright (C) 2016 Alinson Santos Xavier /*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* *
* This program is free software: you can redistribute it and/or modify it * This file is part of Loop Habit Tracker.
* 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.
* *
* This program is distributed in the hope that it will be useful, but WITHOUT * Loop Habit Tracker is free software: you can redistribute it and/or modify
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * it under the terms of the GNU General Public License as published by the
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * 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. * more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License along
* along with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.isoron.uhabits.widgets; package org.isoron.uhabits.widgets;

@ -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;
}
}

@ -1,17 +1,20 @@
/* Copyright (C) 2016 Alinson Santos Xavier /*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* *
* This program is free software: you can redistribute it and/or modify it * This file is part of Loop Habit Tracker.
* 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.
* *
* This program is distributed in the hope that it will be useful, but WITHOUT * Loop Habit Tracker is free software: you can redistribute it and/or modify
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * it under the terms of the GNU General Public License as published by the
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * 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. * more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License along
* along with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.isoron.uhabits.widgets; package org.isoron.uhabits.widgets;

@ -1,17 +1,20 @@
/* Copyright (C) 2016 Alinson Santos Xavier /*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* *
* This program is free software: you can redistribute it and/or modify it * This file is part of Loop Habit Tracker.
* 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.
* *
* This program is distributed in the hope that it will be useful, but WITHOUT * Loop Habit Tracker is free software: you can redistribute it and/or modify
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * it under the terms of the GNU General Public License as published by the
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * 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. * more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License along
* along with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.isoron.uhabits.widgets; package org.isoron.uhabits.widgets;

@ -1,17 +1,20 @@
/* Copyright (C) 2016 Alinson Santos Xavier /*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* *
* This program is free software: you can redistribute it and/or modify it * This file is part of Loop Habit Tracker.
* 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.
* *
* This program is distributed in the hope that it will be useful, but WITHOUT * Loop Habit Tracker is free software: you can redistribute it and/or modify
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * it under the terms of the GNU General Public License as published by the
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * 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. * more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License along
* along with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.isoron.uhabits.widgets; package org.isoron.uhabits.widgets;

@ -1,17 +1,20 @@
/* Copyright (C) 2016 Alinson Santos Xavier /*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* *
* This program is free software: you can redistribute it and/or modify it * This file is part of Loop Habit Tracker.
* 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.
* *
* This program is distributed in the hope that it will be useful, but WITHOUT * Loop Habit Tracker is free software: you can redistribute it and/or modify
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * it under the terms of the GNU General Public License as published by the
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * 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. * more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License along
* along with this program. If not, see <http://www.gnu.org/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.isoron.uhabits.widgets; package org.isoron.uhabits.widgets;

@ -1,4 +1,23 @@
<?xml version="1.0" encoding="utf-8"?> <?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/>.
-->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" > <layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item> <item>
<shape android:shape="oval" > <shape android:shape="oval" >

@ -1,4 +1,23 @@
<?xml version="1.0" encoding="utf-8"?> <?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/>.
-->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" > <layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item> <item>
<shape android:shape="oval" > <shape android:shape="oval" >

@ -1,3 +1,22 @@
<!--
~ 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/>.
-->
<ripple xmlns:android="http://schemas.android.com/apk/res/android" <ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="?android:colorControlHighlight"> android:color="?android:colorControlHighlight">

@ -1,3 +1,22 @@
<!--
~ 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/>.
-->
<ripple xmlns:android="http://schemas.android.com/apk/res/android" <ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="?android:colorControlHighlight"> android:color="?android:colorControlHighlight">

@ -1,4 +1,23 @@
<?xml version="1.0" encoding="utf-8"?> <?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/>.
-->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item <item
android:top="0dp" android:top="0dp"

@ -1,4 +1,23 @@
<?xml version="1.0" encoding="utf-8"?> <?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/>.
-->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" > <layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item> <item>
<shape android:shape="oval" > <shape android:shape="oval" >

@ -1,4 +1,23 @@
<?xml version="1.0" encoding="utf-8"?> <?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/>.
-->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" > <layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item> <item>
<shape android:shape="oval" > <shape android:shape="oval" >

@ -1,4 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?> <?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/>.
-->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" > <layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:top="40dp"> <item android:top="40dp">

@ -1,4 +1,23 @@
<?xml version="1.0" encoding="utf-8"?> <?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/>.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" > <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" >
<solid android:color="@color/grey_100" /> <solid android:color="@color/grey_100" />
<stroke android:width="2dip" android:color="@color/grey_500"/> <stroke android:width="2dip" android:color="@color/grey_500"/>

@ -1,4 +1,23 @@
<?xml version="1.0" encoding="utf-8"?> <?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/>.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android" <shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle"> android:shape="rectangle">

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

@ -0,0 +1,129 @@
<?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/>.
-->
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@color/windowBackground"
android:fillViewport="true">
<LinearLayout
style="@style/cardsListStyle">
<LinearLayout
style="@style/cardStyle"
android:gravity="center">
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_margin="6dp"
android:src="@drawable/intro_icon_1"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:textStyle="bold"
android:textSize="16sp"
android:layout_margin="6dp"
android:textColor="@color/blue_700"
android:text="@string/app_name"/>
<TextView
android:id="@+id/tvVersion"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text=""/>
</LinearLayout>
<LinearLayout
style="@style/cardStyle"
android:gravity="center">
<TextView
style="@style/cardHeaderStyle"
android:text="@string/links"
android:textColor="@color/blue_700"/>
<TextView
android:id="@+id/tvRate"
style="@style/aboutClickableItemStyle"
android:text="@string/pref_rate_this_app"/>
<TextView
android:id="@+id/tvFeedback"
style="@style/aboutClickableItemStyle"
android:text="@string/pref_send_feedback"/>
<TextView
android:id="@+id/tvSource"
style="@style/aboutClickableItemStyle"
android:text="@string/pref_view_source_code"/>
</LinearLayout>
<LinearLayout
style="@style/cardStyle"
android:gravity="center">
<TextView
style="@style/cardHeaderStyle"
android:text="@string/developers"
android:textColor="@color/blue_700"/>
<TextView
style="@style/aboutItemStyle"
android:text="Álinson Xavier"/>
</LinearLayout>
<LinearLayout
style="@style/cardStyle"
android:gravity="center">
<TextView
style="@style/cardHeaderStyle"
android:text="@string/translators"
android:textColor="@color/blue_700"/>
<TextView
style="@style/aboutItemStyle"
android:text="Limin Lu (中文)"/>
<TextView
style="@style/aboutItemStyle"
android:text="François Mahé (Français)"/>
<TextView
style="@style/aboutItemStyle"
android:text="naofumi (日本語)"/>
<TextView
style="@style/aboutItemStyle"
android:text="Matthias (Deutsch)"/>
<TextView
style="@style/aboutItemStyle"
android:text="Álinson Xavier (Português)"/>
</LinearLayout>
</LinearLayout>
</ScrollView>

@ -1,3 +1,22 @@
<!--
~ 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/>.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container" android:id="@+id/container"

@ -1,3 +1,22 @@
<!--
~ 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/>.
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container" android:id="@+id/container"

@ -1,4 +1,23 @@
<?xml version="1.0" encoding="utf-8"?> <?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/>.
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:dslv="http://schemas.android.com/apk/res-auto" xmlns:dslv="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"

@ -1,4 +1,23 @@
<?xml version="1.0" encoding="utf-8"?> <?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/>.
-->
<Button xmlns:android="http://schemas.android.com/apk/res/android" <Button xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/tvCheck" android:id="@+id/tvCheck"
style="@style/habitsListHeaderCheckStyle" /> style="@style/habitsListHeaderCheckStyle" />

@ -1,4 +1,23 @@
<?xml version="1.0" encoding="utf-8"?> <?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/>.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/llOuter" android:id="@+id/llOuter"
style="@style/habitsListItemStyle"> style="@style/habitsListItemStyle">

@ -1,4 +1,23 @@
<?xml version="1.0" encoding="utf-8"?> <?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/>.
-->
<TextView xmlns:android="http://schemas.android.com/apk/res/android" <TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/tvCheck" android:id="@+id/tvCheck"
style="@style/habitsListCheckStyle" /> style="@style/habitsListCheckStyle" />

@ -1,6 +1,25 @@
<!--
~ 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/>.
-->
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://isoron.org/android" xmlns:app="http://isoron.org/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@color/windowBackground" android:background="@color/windowBackground"
@ -41,43 +60,63 @@
</LinearLayout> </LinearLayout>
<LinearLayout style="@style/cardStyle"> <LinearLayout
style="@style/cardStyle"
android:clipToPadding="false"
android:orientation="vertical"
android:paddingBottom="0dp">
<TextView <TextView
android:id="@+id/tvHistory" android:id="@+id/tvHistory"
style="@style/cardHeaderStyle" style="@style/cardHeaderStyle"
android:text="@string/history"/> android:text="@string/history"/>
<LinearLayout
android:id="@+id/llHistory"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"/>
<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
android:id="@+id/btEditHistory"
style="?android:borderlessButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@drawable/ic_action_edit_light"
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>

@ -1,3 +1,22 @@
<!--
~ 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/>.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container" android:id="@+id/container"

@ -1,4 +1,23 @@
<?xml version="1.0" encoding="utf-8"?> <?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/>.
-->
<LinearLayout <LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"

@ -1,4 +1,23 @@
<?xml version="1.0" encoding="utf-8"?> <?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/>.
-->
<LinearLayout <LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"

@ -1,4 +1,23 @@
<?xml version="1.0" encoding="utf-8"?> <?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/>.
-->
<ListView android:id="@+id/listView" <ListView android:id="@+id/listView"
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"

@ -1,4 +1,23 @@
<?xml version="1.0" encoding="utf-8"?> <?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/>.
-->
<LinearLayout <LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"

@ -1,4 +1,23 @@
<?xml version="1.0" encoding="utf-8"?> <?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/>.
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android"> <menu xmlns:android="http://schemas.android.com/apk/res/android">
<item <item

@ -1,3 +1,22 @@
<!--
~ 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/>.
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android" <menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
tools:context="org.isoron.uhabits.MainActivity" > tools:context="org.isoron.uhabits.MainActivity" >

@ -1,4 +1,23 @@
<?xml version="1.0" encoding="utf-8"?> <?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/>.
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android"> <menu xmlns:android="http://schemas.android.com/apk/res/android">
<item <item

@ -1,4 +1,23 @@
<?xml version="1.0" encoding="utf-8"?> <?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/>.
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android"> <menu xmlns:android="http://schemas.android.com/apk/res/android">
<item <item

@ -1,3 +1,22 @@
<!--
~ 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/>.
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android" <menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
@ -15,4 +34,10 @@
android:title="@string/action_settings" android:title="@string/action_settings"
app:showAsAction="never"/> app:showAsAction="never"/>
<item
android:id="@+id/action_about"
android:orderInCategory="100"
android:title="@string/about"
app:showAsAction="never"/>
</menu> </menu>

@ -1,3 +1,22 @@
<!--
~ 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/>.
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android" <menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
tools:context="org.isoron.uhabits.MainActivity"> tools:context="org.isoron.uhabits.MainActivity">

@ -1,12 +0,0 @@
<menu 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"
tools:context="org.isoron.uhabits.ShowHabitActivity">
<item
android:id="@+id/action_settings"
android:orderInCategory="100"
android:title="@string/action_settings"
app:showAsAction="never"/>
</menu>

@ -1,4 +1,23 @@
<?xml version="1.0" encoding="utf-8"?> <?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/>.
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android"> <menu xmlns:android="http://schemas.android.com/apk/res/android">
<item <item

@ -0,0 +1,132 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2016 Matthias
~
~ 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/>.
-->
<resources>
<string name="app_name">"Loop Habit Tracker"</string>
<string name="main_activity_title">"Habits"</string>
<string name="action_settings">"Einstellung"</string>
<string name="edit">"Bearbeiten"</string>
<string name="delete">"Löschen"</string>
<string name="archive">"Archivieren"</string>
<string name="unarchive">"Dearchivieren"</string>
<string name="add_habit">"Habit hinzufügen"</string>
<string name="color_picker_default_title">"Farbe ändern"</string>
<string name="toast_habit_created">"Habit erstellt."</string>
<string name="toast_habit_deleted">"Habits gelöscht."</string>
<string name="toast_habit_restored">"Habits wiederhergestellt."</string>
<string name="toast_nothing_to_undo">"Nichts zum rückgängig machen."</string>
<string name="toast_nothing_to_redo">"Nichts zum wiederherstellen"</string>
<string name="toast_habit_changed">"Habit geändert"</string>
<!-- This appears when the user edits a habit, and then undoes the action. The habit is "changed back" to what is was before. Alternatively, "Habit restored". -->
<string name="toast_habit_changed_back">"Habit zurückgeändert"</string>
<string name="toast_habit_archived">"Habits archiviert."</string>
<string name="toast_habit_unarchived">"Habits dearchiviert."</string>
<string name="overview">"Übersicht"</string>
<string name="habit_strength">"Habit Stärke"</string>
<string name="history">"Verlauf"</string>
<string name="clear">"Löschen"</string>
<string name="description_hint">"Frage (Hast du heute ...?)"</string>
<!-- This and the next two terms form the sentence "Repeat 3 times in 7 days" that you see when you create a habit. Let me know if you have trouble adapting this into your language. -->
<string name="repeat">"Wiederhole"</string>
<string name="times_every">"mal in"</string>
<string name="days">"Tagen"</string>
<string name="reminder">"Erinnerung"</string>
<string name="discard">"Verwerfen"</string>
<string name="save">"Speichern"</string>
<string name="no_habits_found">"Du hast keine aktiven habits"</string>
<string name="long_press_to_toggle">"Berühre und halte um zu (de)markieren"</string>
<string name="reminder_off">"Aus"</string>
<string name="validation_name_should_not_be_blank">"Name darf nicht leer sein."</string>
<string name="validation_number_should_be_positive">"Zahl muss positiv sein."</string>
<string name="validation_at_most_one_rep_per_day">"Du musst wenigstens eine Wiederholung pro Tag haben"</string>
<string name="create_habit">"Habit erstellen"</string>
<string name="edit_habit">"Habit bearbeiten"</string>
<string name="snooze">"Später"</string>
<!-- App introduction -->
<string name="intro_title_1">"Willkommen"</string>
<string name="intro_description_1">"Loop Habit Tracker hilft dir gute Gewohnheiten anzunehmen."</string>
<string name="intro_title_2">"Erstelle einige neue habits"</string>
<string name="intro_description_2">"Jeden tag nachdem du das habit absolviert hast, hake es ab."</string>
<string name="intro_title_3">"Bleib dabei"</string>
<string name="intro_description_3">"Habits die du über längere Zeit durchgehalten hast, bekommen einen ganzen Stern."</string>
<string name="intro_title_4">"Verfolge deinen Fortschritt"</string>
<string name="intro_description_4">"Detailierte Diagramme zeigen dir, wie sich deine Angewohnheiten entwickelt haben."</string>
<string name="interval_15_minutes">"15 Minuten"</string>
<string name="interval_30_minutes">"30 Minuten"</string>
<string name="interval_1_hour">"1 Stunde"</string>
<string name="interval_2_hour">"2 Stunden"</string>
<string name="interval_4_hour">"4 Stunden"</string>
<string name="interval_8_hour">"8 Stunden"</string>
<string name="pref_toggle_title">"Wähle Wiederholungen durch kurzes Drücken"</string>
<string name="pref_toggle_description">"Bequemer, verursacht evtl. falsche Auswahl"</string>
<string name="pref_snooze_interval_title">"Pause interval bei Erinerungen"</string>
<string name="pref_rate_this_app">"Bewerte diese App bei Google Play"</string>
<string name="pref_send_feedback">"Send dem Entwickler Feedback"</string>
<string name="pref_view_source_code">"Zeige Quellcode auf GitHub"</string>
<string name="pref_view_app_introduction">"Zeige Einleitung"</string>
<string name="links">"Links"</string>
<string name="behavior">"Verhalten"</string>
<string name="name">"Name"</string>
<string name="show_archived">"Zeige Archivierte"</string>
<string name="settings">"Einstellungen"</string>
<string name="snooze_interval">"Pause interval"</string>
<string name="hint_title">"Wusstest du?"</string>
<string name="hint_drag">"Um Einträge umzusortieren, ziehe sie an die richtige Stelle."</string>
<string name="hint_landscape">"Du kannst mehr Tage sehen, wenn du dein Phone quer hälst."</string>
<string name="delete_habits">"habit löschen"</string>
<string name="delete_habits_message">"Dies habit wird permanent gelöscht. Dies kann nicht rückgängig gemacht werden."</string>
<string name="weekends">"Wochenende"</string>
<string name="any_weekday">"Arbeitstage"</string>
<string name="any_day">"Jeden Tag"</string>
<string name="select_weekdays">"Wähle die Tage"</string>
<string name="export_to_csv">"Daten exportieren"</string>
<string name="done_label">"Ok"</string>
<string name="clear_label">"Löschen"</string>
<string name="select_hours">"Wähle Stunden"</string>
<string name="select_minutes">"Wähle Minuten"</string>
<!-- Short description used on the Google Play store. There is an 80-character limit. -->
<string name="store_short_description">"Nimm gute Gewohnheiten an und verfolge deinen Fortschritt (ohne Werbung)"</string>
<string name="store_description_1">"Loop hilft dir gute Gewohnheiten anzunehmen und deine langfristigen Ziele zu erreichen. Detailierte Statistiken zeigen dir, wie du dich entwickelt hast. Es ist ohne Werbung und Open Source."</string>
<string name="store_feature_interface">"&lt;b&gt;Einfaches, schönes und modernes Oberfläche&lt;/b&gt;
Loop hat eine minimale Oberfläche und ist deshalb einfach zu nutzen. Es folgt dem material Design."</string>
<string name="store_feature_score">"&lt;b&gt;Habit Punkte&lt;/b&gt;
Um dir deine Schwächen zu zeigen, hat Loop einen Algorithmus, um deine starken Angewohnheiten zu erkennen. Jede Wiederholung verstärkt diese und jedes Aussetzen schwächt sie. Aber ein paar Verfehlungen nach langem Durchhalten machen natürlich nicht gleich alles zu nichte."</string>
<string name="store_feature_statistics">"&lt;b&gt;Statistiken&lt;/b&gt;
Schau dir an, wie sich deine Angewohnheiten im Laufe der Zeit gemacht haben. Schau auf die schönen Diagramme und gehe zurück im gesamten Verlauf."</string>
<string name="store_feature_schedules">"&lt;b&gt;Flexible Zeiten&lt;/b&gt;
Unterstützt sowohl tägliche Vorgaben, als auch komplexere Pläne, woe etwa 3 mal pro Woche; ein mal in jeder anderen Woche; oder jeden anderen Tag."</string>
<string name="store_feature_reminders">"&lt;b&gt;Erinnerungen&lt;/b&gt;
Erstelle individuelle Erinnerungen und wann diese dich benachrichtigen sollen. Kontrolliere deine Vorhaben ganz einfach und lehne sie bei Bedarf direkt ab, ohne in die App zu wechseln."</string>
<string name="store_feature_opensource">"&lt;b&gt;Komplett werbefrei und Open Source&lt;/b&gt;
Es gibt absolut keine Werbung, nervende Einblendungen oder merkwürdige Berechtigungen in dieser App und das wird auch so bleiben. Der komplette Quellcode steht unter der GPLv3."</string>
<string name="store_feature_wear">"&lt;b&gt;Optimiert für Smartwatches&lt;/b&gt;
Erinnerungen können direkt von der Android Wear watch kontrolliert, pausiert, oder verschoben werden."</string>
<string name="about">"Über"</string>
<string name="translators">"Übersetzer"</string>
<string name="developers">"Entwickler"</string>
<!-- %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="frequency">Frequenz</string>
</resources>

@ -0,0 +1,117 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2016 François Mahé
~
~ 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/>.
-->
<resources>
<string name="app_name">"Loop - Suivi d'habitudes"</string>
<string name="main_activity_title">"Habitude"</string>
<string name="action_settings">"Paramètres"</string>
<string name="edit">"Modifier"</string>
<string name="delete">"Supprimer"</string>
<string name="archive">"Archiver"</string>
<string name="unarchive">"Désarchiver"</string>
<string name="add_habit">"Ajouter une habitude"</string>
<string name="color_picker_default_title">"Changer la couleur"</string>
<string name="toast_habit_created">"Habitude crée."</string>
<string name="toast_habit_deleted">"Habitude supprimée."</string>
<string name="toast_habit_restored">"Habitude rétablie."</string>
<string name="toast_nothing_to_undo">"Rien à annuler."</string>
<string name="toast_nothing_to_redo">"Rien à refaire."</string>
<string name="toast_habit_changed">"Habitude changée."</string>
<!-- Fuzzy -->
<string name="toast_habit_changed_back">"Habitude encore changée. "</string>
<string name="toast_habit_archived">"Habitudes archivées."</string>
<string name="toast_habit_unarchived">"Habitudes désarchivées."</string>
<string name="overview">"Vue d'ensemble"</string>
<string name="habit_strength">"Force de l'habitude"</string>
<string name="history">"Histoire"</string>
<string name="clear">"Supprimer"</string>
<string name="description_hint">"Question (As-tu … aujourd'hui?)"</string>
<!-- This and the next two terms form the sentence "Repeat 3 times in 7 days" that you see when you create a habit. Let me know if you have trouble adapting this into your language. -->
<string name="repeat">"Répéter"</string>
<string name="times_every">"fois en"</string>
<string name="days">"jours"</string>
<string name="reminder">"Rappel"</string>
<string name="discard">"Annuler"</string>
<string name="save">"Sauvegarder"</string>
<string name="streaks">"Séries"</string>
<string name="no_habits_found">"Vous n'avez pas d'habitude active"</string>
<string name="long_press_to_toggle">"Appuyez longtemps pour cocher ou décocher"</string>
<string name="reminder_off">"Off"</string>
<string name="validation_name_should_not_be_blank">"Le nom ne peut être vide."</string>
<string name="validation_number_should_be_positive">"Le nombre doit être positif."</string>
<string name="validation_at_most_one_rep_per_day">"Vous pouvez avoir au plus une répétition par jour"</string>
<string name="create_habit">"Créer une habitude"</string>
<string name="edit_habit">"Modifier l'habitude"</string>
<string name="check">"Cocher"</string>
<string name="snooze">"Plus tard"</string>
<!-- App introduction -->
<string name="intro_title_1">"Bienvenue"</string>
<string name="intro_description_1">"Loop - Suivi d'habitudes vous aide à créer et maintenir de bonnes habitudes"</string>
<string name="intro_title_2">"Créer des nouvelles habitudes"</string>
<string name="intro_description_2">"Chaque jour, après avoir fait votre habitude, mettez une croix sur l'application"</string>
<string name="intro_title_3">"Continuez à le faire"</string>
<string name="intro_description_3">"Les habitudes faites de manière consistente pendant une longue période gagneront une étoile complète"</string>
<string name="intro_title_4">"Suivre votre progrès"</string>
<string name="intro_description_4">"Graphiques détaillés montrant comment vos habitudes évoluent au fil du temps"</string>
<string name="interval_15_minutes">"15 minutes"</string>
<string name="interval_30_minutes">"30 minutes"</string>
<string name="interval_1_hour">"1 heure"</string>
<string name="interval_2_hour">"2 heures"</string>
<string name="interval_4_hour">"4 heures"</string>
<string name="interval_8_hour">"8 heures"</string>
<string name="pref_toggle_title">"Activer les répétitions avec un appui court"</string>
<string name="pref_toggle_description">"Plus pratique, mais peut causer des activations accidentelles"</string>
<string name="pref_snooze_interval_title">"Éteindre l'intervalle sur les rappels"</string>
<string name="pref_rate_this_app">"Noter cette app sur le Google Play Store"</string>
<string name="pref_send_feedback">"Envoyez un avis au développeur"</string>
<string name="pref_view_source_code">"Voir le code source sur GitHub"</string>
<string name="pref_view_app_introduction">"Voir l'intro de l'app"</string>
<string name="links">"Liens"</string>
<string name="behavior">"Comportement"</string>
<string name="name">"Nom"</string>
<string name="show_archived">"Montrer les archivées"</string>
<string name="settings">"Paramètres"</string>
<string name="snooze_interval">"Éteindre l'intervalle"</string>
<string name="hint_title">"Le saviez-vous ? "</string>
<string name="hint_drag">"Pour réarranger les habitudes, faites un appui long sur le nom de l'habitude et placez là à la bonne place."</string>
<string name="hint_landscape">"Vous pouvez voir plus en jours en mettant votre téléphone en mode paysage."</string>
<string name="delete_habits">"Supprimer des habitudes"</string>
<string name="delete_habits_message">"Les habitudes seront supprimées pour toujours. Cette action ne peut être refaite."</string>
<string name="weekends">"Weekends"</string>
<string name="any_weekday">"Jours de la semaine"</string>
<string name="any_day">"N'importe quel jour"</string>
<string name="select_weekdays">"Sélectionner des jours"</string>
<string name="export_to_csv">"Exporter les données"</string>
<string name="done_label">"Fait"</string>
<string name="clear_label">"Supprimer"</string>
<string name="select_hours">"Sélectionner les heures"</string>
<string name="select_minutes">"Sélectionner les minutes"</string>
<string name="about">"A propos"</string>
<string name="translators">"Traducteurs"</string>
<string name="developers">"Développeurs"</string>
<!-- %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="frequency">Fréquence</string>
</resources>

@ -1,3 +1,22 @@
<!--
~ 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/>.
-->
<resources> <resources>
<item name="radius" format="float" type="dimen">30</item> <item name="radius" format="float" type="dimen">30</item>
</resources> </resources>

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save