diff --git a/CHANGELOG.md b/CHANGELOG.md
index 37344041d..b2b072e4a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,19 @@
# Changelog
+### 1.5.0 (May 15, 2016)
+
+* Add night mode, with AMOLED support
+* Backport material design to older devices
+* Display more information on statistics screen
+* Display score on main screen and checkmark widget
+* Make widgets react immediately to touch
+* Reschedule reminders after reboot
+* Pick first day of the week according to country
+* Add option to reverse order of days on main screen
+* Add option to change notification sounds
+* Add Catalan, Indonesian, Turkish, Ukrainian translations
+* Switch between Simplified/Traditional Chinese according to country
+
### 1.4.1 (April 9, 2016)
* Show error message on widgets, instead of crashing
diff --git a/README.md b/README.md
index 46a9cc93b..f108eca4e 100644
--- a/README.md
+++ b/README.md
@@ -54,6 +54,7 @@ source.
[![Habit strength][screen3th]][screen3]
[![Habit history and streaks][screen4th]][screen4]
[![Widgets][screen5th]][screen5]
+[![Night mode][screen6th]][screen6]
## Installing
@@ -71,7 +72,8 @@ 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.
+ at GitHub. If you would like to receive the newest versions of the app
+ earlier than everyone else, [join our open beta on Google Play][beta].
* **Spread the word.** If you like the app, share it with your family, friends
and colleagues. You can also rate and review the app on Google Play Store, to help
@@ -105,14 +107,17 @@ contribute, even if you are not a software developer.
[screen3]: screenshots/original/uhabits3.png
[screen4]: screenshots/original/uhabits4.png
[screen5]: screenshots/original/uhabits5.png
+[screen6]: screenshots/original/uhabits6.png
[screen1th]: screenshots/thumbs/uhabits1.png
[screen2th]: screenshots/thumbs/uhabits2.png
[screen3th]: screenshots/thumbs/uhabits3.png
[screen4th]: screenshots/thumbs/uhabits4.png
[screen5th]: screenshots/thumbs/uhabits5.png
+[screen6th]: screenshots/thumbs/uhabits6.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
+[beta]: https://play.google.com/apps/testing/org.isoron.uhabits
diff --git a/app/build.gradle b/app/build.gradle
index d98191ed1..35eaaa0dc 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -2,14 +2,14 @@ apply plugin: 'com.android.application'
android {
compileSdkVersion 23
- buildToolsVersion "23.0.1"
+ buildToolsVersion "23.0.3"
defaultConfig {
applicationId "org.isoron.uhabits"
minSdkVersion 15
targetSdkVersion 23
- buildConfigField "Integer", "databaseVersion", "13"
+ buildConfigField "Integer", "databaseVersion", "14"
buildConfigField "String", "databaseFilename", "\"uhabits.db\""
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
@@ -32,18 +32,31 @@ android {
}
dependencies {
- compile 'com.android.support:support-v4:23.1.1'
+ compile 'com.android.support:support-v4:23.3.0'
+ compile 'com.android.support:appcompat-v7:23.3.0'
+ compile 'com.android.support:design:23.3.0'
+ compile 'com.android.support:preference-v14:23.3.0'
compile 'com.github.paolorotolo:appintro:3.4.0'
compile 'org.apmem.tools:layouts:1.10@aar'
compile 'com.opencsv:opencsv:3.7'
compile project(':libs:drag-sort-listview:library')
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'
+ androidTestCompile 'com.android.support:support-annotations:23.3.0'
+ androidTestCompile 'com.android.support.test:runner:0.5'
+ androidTestCompile 'com.android.support.test:rules:0.5'
+
+ androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.1') {
+ exclude group: 'com.android.support'
+ }
+
+ androidTestCompile('com.android.support.test.espresso:espresso-intents:2.2.1') {
+ exclude group: 'com.android.support'
+ }
+
+ androidTestCompile('com.android.support.test.espresso:espresso-contrib:2.2.1') {
+ exclude group: 'com.android.support'
+ }
}
diff --git a/app/src/androidTest/assets/views-v21/CheckmarkView/checked.png b/app/src/androidTest/assets/views-v21/CheckmarkView/checked.png
deleted file mode 100644
index b54e5707d..000000000
Binary files a/app/src/androidTest/assets/views-v21/CheckmarkView/checked.png and /dev/null differ
diff --git a/app/src/androidTest/assets/views-v21/CheckmarkView/implicitly_checked.png b/app/src/androidTest/assets/views-v21/CheckmarkView/implicitly_checked.png
deleted file mode 100644
index 39d8e87f3..000000000
Binary files a/app/src/androidTest/assets/views-v21/CheckmarkView/implicitly_checked.png and /dev/null differ
diff --git a/app/src/androidTest/assets/views-v21/CheckmarkView/large_size.png b/app/src/androidTest/assets/views-v21/CheckmarkView/large_size.png
deleted file mode 100644
index 99e439e72..000000000
Binary files a/app/src/androidTest/assets/views-v21/CheckmarkView/large_size.png and /dev/null differ
diff --git a/app/src/androidTest/assets/views-v21/CheckmarkView/unchecked.png b/app/src/androidTest/assets/views-v21/CheckmarkView/unchecked.png
deleted file mode 100644
index 2dc0f531a..000000000
Binary files a/app/src/androidTest/assets/views-v21/CheckmarkView/unchecked.png and /dev/null differ
diff --git a/app/src/androidTest/assets/views/CheckmarkView/checked.png b/app/src/androidTest/assets/views/CheckmarkView/checked.png
index 7884c804f..69f650a0b 100644
Binary files a/app/src/androidTest/assets/views/CheckmarkView/checked.png and b/app/src/androidTest/assets/views/CheckmarkView/checked.png differ
diff --git a/app/src/androidTest/assets/views/CheckmarkView/implicitly_checked.png b/app/src/androidTest/assets/views/CheckmarkView/implicitly_checked.png
index 3096be180..992a9e5b2 100644
Binary files a/app/src/androidTest/assets/views/CheckmarkView/implicitly_checked.png and b/app/src/androidTest/assets/views/CheckmarkView/implicitly_checked.png differ
diff --git a/app/src/androidTest/assets/views/CheckmarkView/large_size.png b/app/src/androidTest/assets/views/CheckmarkView/large_size.png
index 79152fb18..8aa13d5e2 100644
Binary files a/app/src/androidTest/assets/views/CheckmarkView/large_size.png and b/app/src/androidTest/assets/views/CheckmarkView/large_size.png differ
diff --git a/app/src/androidTest/assets/views/CheckmarkView/unchecked.png b/app/src/androidTest/assets/views/CheckmarkView/unchecked.png
index b0d90c5c0..576d369ec 100644
Binary files a/app/src/androidTest/assets/views/CheckmarkView/unchecked.png and b/app/src/androidTest/assets/views/CheckmarkView/unchecked.png differ
diff --git a/app/src/androidTest/assets/views/HabitFrequencyView/render.png b/app/src/androidTest/assets/views/HabitFrequencyView/render.png
index 28c46f5a2..70ec73c84 100644
Binary files a/app/src/androidTest/assets/views/HabitFrequencyView/render.png and b/app/src/androidTest/assets/views/HabitFrequencyView/render.png differ
diff --git a/app/src/androidTest/assets/views/HabitFrequencyView/renderDataOffset.png b/app/src/androidTest/assets/views/HabitFrequencyView/renderDataOffset.png
index 1125e478a..66ac9f4a3 100644
Binary files a/app/src/androidTest/assets/views/HabitFrequencyView/renderDataOffset.png and b/app/src/androidTest/assets/views/HabitFrequencyView/renderDataOffset.png differ
diff --git a/app/src/androidTest/assets/views/HabitFrequencyView/renderDifferentSize.png b/app/src/androidTest/assets/views/HabitFrequencyView/renderDifferentSize.png
index cb5347b1c..7dd7a684c 100644
Binary files a/app/src/androidTest/assets/views/HabitFrequencyView/renderDifferentSize.png and b/app/src/androidTest/assets/views/HabitFrequencyView/renderDifferentSize.png differ
diff --git a/app/src/androidTest/assets/views/HabitFrequencyView/renderTransparent.png b/app/src/androidTest/assets/views/HabitFrequencyView/renderTransparent.png
index a0437b31f..70ec73c84 100644
Binary files a/app/src/androidTest/assets/views/HabitFrequencyView/renderTransparent.png and b/app/src/androidTest/assets/views/HabitFrequencyView/renderTransparent.png differ
diff --git a/app/src/androidTest/assets/views/HabitHistoryView/render.png b/app/src/androidTest/assets/views/HabitHistoryView/render.png
index fce176813..fabd5891e 100644
Binary files a/app/src/androidTest/assets/views/HabitHistoryView/render.png and b/app/src/androidTest/assets/views/HabitHistoryView/render.png differ
diff --git a/app/src/androidTest/assets/views/HabitHistoryView/renderDataOffset.png b/app/src/androidTest/assets/views/HabitHistoryView/renderDataOffset.png
index 679ad00d4..0fee4e308 100644
Binary files a/app/src/androidTest/assets/views/HabitHistoryView/renderDataOffset.png and b/app/src/androidTest/assets/views/HabitHistoryView/renderDataOffset.png differ
diff --git a/app/src/androidTest/assets/views/HabitHistoryView/renderDifferentSize.png b/app/src/androidTest/assets/views/HabitHistoryView/renderDifferentSize.png
index be64fb851..e2a08b0d7 100644
Binary files a/app/src/androidTest/assets/views/HabitHistoryView/renderDifferentSize.png and b/app/src/androidTest/assets/views/HabitHistoryView/renderDifferentSize.png differ
diff --git a/app/src/androidTest/assets/views/HabitHistoryView/renderTransparent.png b/app/src/androidTest/assets/views/HabitHistoryView/renderTransparent.png
index d52288da7..b7be74220 100644
Binary files a/app/src/androidTest/assets/views/HabitHistoryView/renderTransparent.png and b/app/src/androidTest/assets/views/HabitHistoryView/renderTransparent.png differ
diff --git a/app/src/androidTest/assets/views/HabitScoreView/render.png b/app/src/androidTest/assets/views/HabitScoreView/render.png
index f5c98549c..4f606e5e9 100644
Binary files a/app/src/androidTest/assets/views/HabitScoreView/render.png and b/app/src/androidTest/assets/views/HabitScoreView/render.png differ
diff --git a/app/src/androidTest/assets/views/HabitScoreView/renderDataOffset.png b/app/src/androidTest/assets/views/HabitScoreView/renderDataOffset.png
index c84c230a1..a6eab96e6 100644
Binary files a/app/src/androidTest/assets/views/HabitScoreView/renderDataOffset.png and b/app/src/androidTest/assets/views/HabitScoreView/renderDataOffset.png differ
diff --git a/app/src/androidTest/assets/views/HabitScoreView/renderDifferentSize.png b/app/src/androidTest/assets/views/HabitScoreView/renderDifferentSize.png
index 133068dc4..8eb052d73 100644
Binary files a/app/src/androidTest/assets/views/HabitScoreView/renderDifferentSize.png and b/app/src/androidTest/assets/views/HabitScoreView/renderDifferentSize.png differ
diff --git a/app/src/androidTest/assets/views/HabitScoreView/renderMonthly.png b/app/src/androidTest/assets/views/HabitScoreView/renderMonthly.png
index 83c31bcfe..75f74d515 100644
Binary files a/app/src/androidTest/assets/views/HabitScoreView/renderMonthly.png and b/app/src/androidTest/assets/views/HabitScoreView/renderMonthly.png differ
diff --git a/app/src/androidTest/assets/views/HabitScoreView/renderTransparent.png b/app/src/androidTest/assets/views/HabitScoreView/renderTransparent.png
index b6fd78857..0b7f70a7e 100644
Binary files a/app/src/androidTest/assets/views/HabitScoreView/renderTransparent.png and b/app/src/androidTest/assets/views/HabitScoreView/renderTransparent.png differ
diff --git a/app/src/androidTest/assets/views/HabitScoreView/renderYearly.png b/app/src/androidTest/assets/views/HabitScoreView/renderYearly.png
index 1e45b47d1..d186ceefc 100644
Binary files a/app/src/androidTest/assets/views/HabitScoreView/renderYearly.png and b/app/src/androidTest/assets/views/HabitScoreView/renderYearly.png differ
diff --git a/app/src/androidTest/assets/views/HabitStreakView/render.png b/app/src/androidTest/assets/views/HabitStreakView/render.png
index 365231bff..4bab05b52 100644
Binary files a/app/src/androidTest/assets/views/HabitStreakView/render.png and b/app/src/androidTest/assets/views/HabitStreakView/render.png differ
diff --git a/app/src/androidTest/assets/views/HabitStreakView/renderSmallSize.png b/app/src/androidTest/assets/views/HabitStreakView/renderSmallSize.png
index eaa0ca53e..00eb8daa6 100644
Binary files a/app/src/androidTest/assets/views/HabitStreakView/renderSmallSize.png and b/app/src/androidTest/assets/views/HabitStreakView/renderSmallSize.png differ
diff --git a/app/src/androidTest/assets/views/HabitStreakView/renderTransparent.png b/app/src/androidTest/assets/views/HabitStreakView/renderTransparent.png
index 408a215d3..4bab05b52 100644
Binary files a/app/src/androidTest/assets/views/HabitStreakView/renderTransparent.png and b/app/src/androidTest/assets/views/HabitStreakView/renderTransparent.png differ
diff --git a/app/src/androidTest/assets/views/RingView/render.png b/app/src/androidTest/assets/views/RingView/render.png
index c77355c7c..0bf125778 100644
Binary files a/app/src/androidTest/assets/views/RingView/render.png and b/app/src/androidTest/assets/views/RingView/render.png differ
diff --git a/app/src/androidTest/assets/views/RingView/renderDifferentParams.png b/app/src/androidTest/assets/views/RingView/renderDifferentParams.png
index 02dfdf803..87874ba92 100644
Binary files a/app/src/androidTest/assets/views/RingView/renderDifferentParams.png and b/app/src/androidTest/assets/views/RingView/renderDifferentParams.png differ
diff --git a/app/src/androidTest/assets/views/RingView/renderLongLabel.png b/app/src/androidTest/assets/views/RingView/renderLongLabel.png
deleted file mode 100644
index 48b9998ab..000000000
Binary files a/app/src/androidTest/assets/views/RingView/renderLongLabel.png and /dev/null differ
diff --git a/app/src/androidTest/java/org/isoron/uhabits/BaseTest.java b/app/src/androidTest/java/org/isoron/uhabits/BaseTest.java
index a73f5cd9d..5515b5121 100644
--- a/app/src/androidTest/java/org/isoron/uhabits/BaseTest.java
+++ b/app/src/androidTest/java/org/isoron/uhabits/BaseTest.java
@@ -25,6 +25,7 @@ import android.os.Looper;
import android.support.test.InstrumentationRegistry;
import org.isoron.uhabits.helpers.DateHelper;
+import org.isoron.uhabits.helpers.UIHelper;
import org.isoron.uhabits.tasks.BaseTask;
import org.junit.Before;
@@ -50,6 +51,7 @@ public class BaseTest
targetContext = InstrumentationRegistry.getTargetContext();
testContext = InstrumentationRegistry.getContext();
+ UIHelper.setFixedTheme(R.style.AppBaseTheme);
DateHelper.setFixedLocalTime(FIXED_LOCAL_TIME);
}
diff --git a/app/src/androidTest/java/org/isoron/uhabits/ui/MainActivityActions.java b/app/src/androidTest/java/org/isoron/uhabits/ui/MainActivityActions.java
index 40699aa35..a933cd3a9 100644
--- a/app/src/androidTest/java/org/isoron/uhabits/ui/MainActivityActions.java
+++ b/app/src/androidTest/java/org/isoron/uhabits/ui/MainActivityActions.java
@@ -20,6 +20,7 @@
package org.isoron.uhabits.ui;
import android.support.test.espresso.NoMatchingViewException;
+import android.support.test.espresso.contrib.RecyclerViewActions;
import org.isoron.uhabits.R;
import org.isoron.uhabits.models.Habit;
@@ -31,7 +32,6 @@ 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;
@@ -39,12 +39,16 @@ 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.RootMatchers.isPlatformPopup;
import static android.support.test.espresso.matcher.ViewMatchers.Visibility.VISIBLE;
+import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.withClassName;
import static android.support.test.espresso.matcher.ViewMatchers.withContentDescription;
import static android.support.test.espresso.matcher.ViewMatchers.withEffectiveVisibility;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withParent;
import static android.support.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.Matchers.allOf;
+import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
@@ -168,13 +172,13 @@ public class MainActivityActions
public static void deleteHabits(List names)
{
selectHabits(names);
- clickActionModeMenuItem(R.string.delete);
+ clickMenuItem(R.string.delete);
onView(withText("OK"))
.perform(click());
assertHabitsDontExist(names);
}
- public static void clickActionModeMenuItem(int stringId)
+ public static void clickMenuItem(int stringId)
{
try
{
@@ -188,9 +192,34 @@ public class MainActivityActions
}
catch(Exception e2)
{
- openContextualActionModeOverflowMenu();
- onView(withText(stringId)).perform(click());
+ clickHiddenMenuItem(stringId);
}
}
}
+
+ private static void clickHiddenMenuItem(int stringId)
+ {
+ try
+ {
+ // Try the ActionMode overflow menu first
+ onView(allOf(withContentDescription("More options"), withParent(withParent(
+ withClassName(containsString("Action")))))).perform(click());
+ }
+ catch (Exception e1)
+ {
+ // Try the toolbar overflow menu
+ onView(allOf(withContentDescription("More options"), withParent(withParent(
+ withClassName(containsString("Toolbar")))))).perform(click());
+ }
+
+ onView(withText(stringId)).perform(click());
+ }
+
+ public static void clickSettingsItem(String text)
+ {
+ onView(withClassName(containsString("RecyclerView")))
+ .perform(RecyclerViewActions.actionOnItem(
+ hasDescendant(withText(containsString(text))),
+ click()));
+ }
}
diff --git a/app/src/androidTest/java/org/isoron/uhabits/ui/MainTest.java b/app/src/androidTest/java/org/isoron/uhabits/ui/MainTest.java
index 0ea767757..89751372f 100644
--- a/app/src/androidTest/java/org/isoron/uhabits/ui/MainTest.java
+++ b/app/src/androidTest/java/org/isoron/uhabits/ui/MainTest.java
@@ -45,7 +45,6 @@ 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;
@@ -61,7 +60,6 @@ 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.containsString;
import static org.hamcrest.Matchers.endsWith;
@@ -69,7 +67,6 @@ import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.startsWith;
-import static org.isoron.uhabits.ui.HabitMatchers.isPreferenceWithText;
import static org.isoron.uhabits.ui.HabitMatchers.withName;
import static org.isoron.uhabits.ui.HabitViewActions.clickAtRandomLocations;
import static org.isoron.uhabits.ui.HabitViewActions.toggleAllCheckmarks;
@@ -77,7 +74,8 @@ import static org.isoron.uhabits.ui.MainActivityActions.addHabit;
import static org.isoron.uhabits.ui.MainActivityActions.assertHabitExists;
import static org.isoron.uhabits.ui.MainActivityActions.assertHabitsDontExist;
import static org.isoron.uhabits.ui.MainActivityActions.assertHabitsExist;
-import static org.isoron.uhabits.ui.MainActivityActions.clickActionModeMenuItem;
+import static org.isoron.uhabits.ui.MainActivityActions.clickMenuItem;
+import static org.isoron.uhabits.ui.MainActivityActions.clickSettingsItem;
import static org.isoron.uhabits.ui.MainActivityActions.deleteHabit;
import static org.isoron.uhabits.ui.MainActivityActions.deleteHabits;
import static org.isoron.uhabits.ui.MainActivityActions.selectHabit;
@@ -152,20 +150,16 @@ public class MainTest
selectHabits(names);
- clickActionModeMenuItem(R.string.archive);
+ clickMenuItem(R.string.archive);
assertHabitsDontExist(names);
- openActionBarOverflowOrOptionsMenu(targetContext);
- onView(withText(R.string.show_archived))
- .perform(click());
+ clickMenuItem(R.string.show_archived);
assertHabitsExist(names);
selectHabits(names);
- clickActionModeMenuItem(R.string.unarchive);
+ clickMenuItem(R.string.unarchive);
- openActionBarOverflowOrOptionsMenu(targetContext);
- onView(withText(R.string.show_archived))
- .perform(click());
+ clickMenuItem(R.string.show_archived);
assertHabitsExist(names);
deleteHabits(names);
@@ -227,7 +221,7 @@ public class MainTest
.onChildView(withId(R.id.label))
.perform(longClick());
- clickActionModeMenuItem(R.string.edit);
+ clickMenuItem(R.string.edit);
String modifiedName = "Modified " + new Random().nextInt(10000);
typeHabitData(modifiedName, "", "1", "1");
@@ -238,7 +232,7 @@ public class MainTest
assertHabitExists(modifiedName);
selectHabit(modifiedName);
- clickActionModeMenuItem(R.string.color_picker_default_title);
+ clickMenuItem(R.string.color_picker_default_title);
pressBack();
deleteHabit(modifiedName);
@@ -272,8 +266,7 @@ public class MainTest
@Test
public void testSettings()
{
- openActionBarOverflowOrOptionsMenu(targetContext);
- onView(withText(R.string.settings)).perform(click());
+ clickMenuItem(R.string.settings);
}
/**
@@ -282,8 +275,7 @@ public class MainTest
@Test
public void testAbout()
{
- openActionBarOverflowOrOptionsMenu(targetContext);
- onView(withText(R.string.about)).perform(click());
+ clickMenuItem(R.string.about);
onView(isRoot()).perform(swipeUp());
}
@@ -293,8 +285,7 @@ public class MainTest
@Test
public void testHelp()
{
- openActionBarOverflowOrOptionsMenu(targetContext);
- onView(withText(R.string.help)).perform(click());
+ clickMenuItem(R.string.help);
intended(hasAction(Intent.ACTION_VIEW));
}
@@ -307,20 +298,18 @@ public class MainTest
{
String name = addHabit();
- openActionBarOverflowOrOptionsMenu(targetContext);
- onView(withText(R.string.settings)).perform(click());
+ clickMenuItem(R.string.settings);
String date = DateHelper.getBackupDateFormat().format(DateHelper.getLocalTime());
date = date.substring(0, date.length() - 2);
- onData(isPreferenceWithText("Export full backup")).perform(click());
+ clickSettingsItem("Export full backup");
intended(hasAction(Intent.ACTION_SEND));
deleteHabit(name);
- openActionBarOverflowOrOptionsMenu(targetContext);
- onView(withText(R.string.settings)).perform(click());
- onData(isPreferenceWithText("Import data")).perform(click());
+ clickMenuItem(R.string.settings);
+ clickSettingsItem("Import data");
onData(allOf(is(instanceOf(String.class)), startsWith("Backups")))
.perform(click());
@@ -339,9 +328,8 @@ public class MainTest
public void testExportCSV()
{
addHabit();
- openActionBarOverflowOrOptionsMenu(targetContext);
- onView(withText(R.string.settings)).perform(click());
- onData(isPreferenceWithText("Export as CSV")).perform(click());
+ clickMenuItem(R.string.settings);
+ clickSettingsItem("Export as CSV");
intended(hasAction(Intent.ACTION_SEND));
}
@@ -351,9 +339,8 @@ public class MainTest
@Test
public void testGenerateBugReport()
{
- openActionBarOverflowOrOptionsMenu(targetContext);
- onView(withText(R.string.settings)).perform(click());
- onData(isPreferenceWithText("Generate bug report")).perform(click());
+ clickMenuItem(R.string.settings);
+ clickSettingsItem("Generate bug report");
intended(hasAction(Intent.ACTION_SENDTO));
}
}
diff --git a/app/src/androidTest/java/org/isoron/uhabits/unit/HabitFixtures.java b/app/src/androidTest/java/org/isoron/uhabits/unit/HabitFixtures.java
index 22a425266..09298bb08 100644
--- a/app/src/androidTest/java/org/isoron/uhabits/unit/HabitFixtures.java
+++ b/app/src/androidTest/java/org/isoron/uhabits/unit/HabitFixtures.java
@@ -23,7 +23,6 @@ import android.content.Context;
import android.support.annotation.Nullable;
import android.util.Log;
-import org.isoron.uhabits.helpers.ColorHelper;
import org.isoron.uhabits.helpers.DatabaseHelper;
import org.isoron.uhabits.helpers.DateHelper;
import org.isoron.uhabits.models.Habit;
@@ -66,7 +65,7 @@ public class HabitFixtures
Habit habit = new Habit();
habit.name = "Meditate";
habit.description = "Did you meditate this morning?";
- habit.color = ColorHelper.palette[3];
+ habit.color = 3;
habit.freqNum = 1;
habit.freqDen = 1;
habit.save();
@@ -78,7 +77,7 @@ public class HabitFixtures
Habit habit = createEmptyHabit();
habit.freqNum = 3;
habit.freqDen = 7;
- habit.color = ColorHelper.palette[4];
+ habit.color = 4;
habit.save();
long day = DateHelper.millisecondsInOneDay;
diff --git a/app/src/androidTest/java/org/isoron/uhabits/unit/commands/ChangeHabitColorCommandTest.java b/app/src/androidTest/java/org/isoron/uhabits/unit/commands/ChangeHabitColorCommandTest.java
index 54fd200fa..cc6c886d1 100644
--- a/app/src/androidTest/java/org/isoron/uhabits/unit/commands/ChangeHabitColorCommandTest.java
+++ b/app/src/androidTest/java/org/isoron/uhabits/unit/commands/ChangeHabitColorCommandTest.java
@@ -24,7 +24,6 @@ import android.test.suitebuilder.annotation.SmallTest;
import org.isoron.uhabits.BaseTest;
import org.isoron.uhabits.commands.ChangeHabitColorCommand;
-import org.isoron.uhabits.helpers.ColorHelper;
import org.isoron.uhabits.models.Habit;
import org.isoron.uhabits.unit.HabitFixtures;
import org.junit.Before;
@@ -53,12 +52,12 @@ public class ChangeHabitColorCommandTest extends BaseTest
for(int i = 0; i < 3; i ++)
{
Habit habit = HabitFixtures.createShortHabit();
- habit.color = ColorHelper.palette[i+1];
+ habit.color = i+1;
habit.save();
habits.add(habit);
}
- command = new ChangeHabitColorCommand(habits, ColorHelper.palette[0]);
+ command = new ChangeHabitColorCommand(habits, 0);
}
@Test
@@ -80,12 +79,12 @@ public class ChangeHabitColorCommandTest extends BaseTest
{
int k = 0;
for(Habit h : habits)
- assertThat(h.color, equalTo(ColorHelper.palette[++k]));
+ assertThat(h.color, equalTo(++k));
}
private void checkNewColors()
{
for(Habit h : habits)
- assertThat(h.color, equalTo(ColorHelper.palette[0]));
+ assertThat(h.color, equalTo(0));
}
}
diff --git a/app/src/androidTest/java/org/isoron/uhabits/unit/models/HabitTest.java b/app/src/androidTest/java/org/isoron/uhabits/unit/models/HabitTest.java
index a71a0bac3..f09664031 100644
--- a/app/src/androidTest/java/org/isoron/uhabits/unit/models/HabitTest.java
+++ b/app/src/androidTest/java/org/isoron/uhabits/unit/models/HabitTest.java
@@ -19,7 +19,6 @@
package org.isoron.uhabits.unit.models;
-import android.graphics.Color;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
@@ -78,7 +77,7 @@ public class HabitTest extends BaseTest
Habit model = new Habit();
model.archived = 1;
model.highlight = 1;
- model.color = Color.BLACK;
+ model.color = 0;
model.freqNum = 10;
model.freqDen = 20;
model.reminderDays = 1;
diff --git a/app/src/androidTest/java/org/isoron/uhabits/unit/views/CheckmarkViewTest.java b/app/src/androidTest/java/org/isoron/uhabits/unit/views/CheckmarkWidgetViewTest.java
similarity index 88%
rename from app/src/androidTest/java/org/isoron/uhabits/unit/views/CheckmarkViewTest.java
rename to app/src/androidTest/java/org/isoron/uhabits/unit/views/CheckmarkWidgetViewTest.java
index 637edb5ec..874c8666f 100644
--- a/app/src/androidTest/java/org/isoron/uhabits/unit/views/CheckmarkViewTest.java
+++ b/app/src/androidTest/java/org/isoron/uhabits/unit/views/CheckmarkWidgetViewTest.java
@@ -22,10 +22,12 @@ package org.isoron.uhabits.unit.views;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
+import org.isoron.uhabits.R;
import org.isoron.uhabits.helpers.DateHelper;
+import org.isoron.uhabits.helpers.UIHelper;
import org.isoron.uhabits.models.Habit;
import org.isoron.uhabits.unit.HabitFixtures;
-import org.isoron.uhabits.views.CheckmarkView;
+import org.isoron.uhabits.views.CheckmarkWidgetView;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -34,18 +36,19 @@ import java.io.IOException;
@RunWith(AndroidJUnit4.class)
@SmallTest
-public class CheckmarkViewTest extends ViewTest
+public class CheckmarkWidgetViewTest extends ViewTest
{
- private CheckmarkView view;
+ private CheckmarkWidgetView view;
private Habit habit;
@Before
public void setup()
{
super.setup();
+ UIHelper.setFixedTheme(R.style.TransparentWidgetTheme);
habit = HabitFixtures.createShortHabit();
- view = new CheckmarkView(targetContext);
+ view = new CheckmarkWidgetView(targetContext);
view.setHabit(habit);
refreshData(view);
measureView(dpToPixels(100), dpToPixels(200), view);
diff --git a/app/src/androidTest/java/org/isoron/uhabits/unit/views/HabitHistoryViewTest.java b/app/src/androidTest/java/org/isoron/uhabits/unit/views/HabitHistoryViewTest.java
index 6748dc895..ea8f0f54f 100644
--- a/app/src/androidTest/java/org/isoron/uhabits/unit/views/HabitHistoryViewTest.java
+++ b/app/src/androidTest/java/org/isoron/uhabits/unit/views/HabitHistoryViewTest.java
@@ -52,7 +52,7 @@ public class HabitHistoryViewTest extends ViewTest
view = new HabitHistoryView(targetContext);
view.setHabit(habit);
- measureView(dpToPixels(300), dpToPixels(100), view);
+ measureView(dpToPixels(400), dpToPixels(200), view);
refreshData(view);
}
@@ -89,7 +89,7 @@ public class HabitHistoryViewTest extends ViewTest
public void tapDate_withEditableView() throws Throwable
{
view.setIsEditable(true);
- tap(view, 270, 30);
+ tap(view, 340, 40); // today's square
waitForAsyncTasks();
long today = DateHelper.getStartOfToday();
@@ -102,9 +102,9 @@ public class HabitHistoryViewTest extends ViewTest
int expectedCheckmarkValues[] = habit.checkmarks.getAllValues();
view.setIsEditable(true);
- tap(view, 45, 5); // header
- tap(view, 270, 43); // tomorrow's square
- tap(view, 280, 30); // right axis
+ tap(view, 118, 13); // header
+ tap(view, 336, 60); // tomorrow's square
+ tap(view, 370, 60); // right axis
waitForAsyncTasks();
int actualCheckmarkValues[] = habit.checkmarks.getAllValues();
@@ -115,7 +115,7 @@ public class HabitHistoryViewTest extends ViewTest
public void tapDate_withReadOnlyView() throws Throwable
{
view.setIsEditable(false);
- tap(view, 270, 30);
+ tap(view, 340, 40); // today's square
waitForAsyncTasks();
long today = DateHelper.getStartOfToday();
diff --git a/app/src/androidTest/java/org/isoron/uhabits/unit/views/HabitScoreViewTest.java b/app/src/androidTest/java/org/isoron/uhabits/unit/views/HabitScoreViewTest.java
index 89ccdcdb6..a7f1228b9 100644
--- a/app/src/androidTest/java/org/isoron/uhabits/unit/views/HabitScoreViewTest.java
+++ b/app/src/androidTest/java/org/isoron/uhabits/unit/views/HabitScoreViewTest.java
@@ -49,7 +49,7 @@ public class HabitScoreViewTest extends ViewTest
view.setHabit(habit);
view.setBucketSize(7);
refreshData(view);
- measureView(dpToPixels(300), dpToPixels(100), view);
+ measureView(dpToPixels(300), dpToPixels(200), view);
}
@Test
@@ -62,7 +62,7 @@ public class HabitScoreViewTest extends ViewTest
@Test
public void testRender_withTransparentBackground() throws Throwable
{
- view.setIsBackgroundTransparent(true);
+ view.setIsTransparencyEnabled(true);
assertRenders(view, "HabitScoreView/renderTransparent.png");
}
diff --git a/app/src/androidTest/java/org/isoron/uhabits/unit/views/NumberViewTest.java b/app/src/androidTest/java/org/isoron/uhabits/unit/views/NumberViewTest.java
index 93269befa..a8f371af6 100644
--- a/app/src/androidTest/java/org/isoron/uhabits/unit/views/NumberViewTest.java
+++ b/app/src/androidTest/java/org/isoron/uhabits/unit/views/NumberViewTest.java
@@ -45,7 +45,7 @@ public class NumberViewTest extends ViewTest
view = new NumberView(targetContext);
view.setLabel("Hello world");
view.setNumber(31);
- view.setColor(ColorHelper.palette[0]);
+ view.setColor(ColorHelper.CSV_PALETTE[0]);
measureView(dpToPixels(100), dpToPixels(100), view);
}
@@ -68,10 +68,10 @@ public class NumberViewTest extends ViewTest
public void testRender_withDifferentParams() throws IOException
{
view.setNumber(500);
- view.setColor(ColorHelper.palette[5]);
+ view.setColor(ColorHelper.CSV_PALETTE[5]);
view.setTextSize(targetContext.getResources().getDimension(R.dimen.tinyTextSize));
measureView(dpToPixels(200), dpToPixels(200), view);
assertRenders(view, "NumberView/renderDifferentParams.png");
}
-}
+}
\ No newline at end of file
diff --git a/app/src/androidTest/java/org/isoron/uhabits/unit/views/RingViewTest.java b/app/src/androidTest/java/org/isoron/uhabits/unit/views/RingViewTest.java
index d7fcb48d6..9f831ba49 100644
--- a/app/src/androidTest/java/org/isoron/uhabits/unit/views/RingViewTest.java
+++ b/app/src/androidTest/java/org/isoron/uhabits/unit/views/RingViewTest.java
@@ -19,6 +19,7 @@
package org.isoron.uhabits.unit.views;
+import android.graphics.Color;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
@@ -42,10 +43,11 @@ public class RingViewTest extends ViewTest
super.setup();
view = new RingView(targetContext);
- view.setLabel("Hello world");
view.setPercentage(0.6f);
- view.setColor(ColorHelper.palette[0]);
- view.setMaxDiameter(dpToPixels(100));
+ view.setText("60%");
+ view.setColor(ColorHelper.CSV_PALETTE[0]);
+ view.setBackgroundColor(Color.WHITE);
+ view.setThickness(dpToPixels(3));
}
@Test
@@ -55,22 +57,11 @@ public class RingViewTest extends ViewTest
assertRenders(view, "RingView/render.png");
}
- @Test
- public void testRender_withLongLabel() throws IOException
- {
- view.setLabel("The quick brown fox jumps over the lazy fox");
-
- measureView(dpToPixels(100), dpToPixels(100), view);
- assertRenders(view, "RingView/renderLongLabel.png");
- }
-
@Test
public void testRender_withDifferentParams() throws IOException
{
- view.setLabel("Habit Strength");
view.setPercentage(0.25f);
- view.setMaxDiameter(dpToPixels(50));
- view.setColor(ColorHelper.palette[5]);
+ view.setColor(ColorHelper.CSV_PALETTE[5]);
measureView(dpToPixels(200), dpToPixels(200), view);
assertRenders(view, "RingView/renderDifferentParams.png");
diff --git a/app/src/androidTest/java/org/isoron/uhabits/unit/views/ViewTest.java b/app/src/androidTest/java/org/isoron/uhabits/unit/views/ViewTest.java
index d5e69fa60..d87c3b9fc 100644
--- a/app/src/androidTest/java/org/isoron/uhabits/unit/views/ViewTest.java
+++ b/app/src/androidTest/java/org/isoron/uhabits/unit/views/ViewTest.java
@@ -80,8 +80,8 @@ public class ViewTest extends BaseTest
if(!similarEnough)
{
- saveBitmap(expectedImagePath, ".scaledExpected", scaledExpected);
- String path = saveBitmap(expectedImagePath, ".actual", actual);
+ saveBitmap(expectedImagePath, ".expected", scaledExpected);
+ String path = saveBitmap(expectedImagePath, "", actual);
errorMessage.append(String.format("Actual rendered image " + "saved to %s", path));
fail(errorMessage.toString());
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 23c17a53a..3bac419f5 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -21,8 +21,8 @@
+ android:versionCode="16"
+ android:versionName="1.5.0">
@@ -34,6 +34,8 @@
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="18" />
+
+
+ android:label="@string/title_activity_show_habit">
@@ -68,8 +69,7 @@
+ android:label="@string/settings">
@@ -90,8 +90,10 @@
+ android:label="@string/about">
+
-
+
+
+
+
+
diff --git a/app/src/main/assets/migrations/14.sql b/app/src/main/assets/migrations/14.sql
new file mode 100644
index 000000000..e267afc2c
--- /dev/null
+++ b/app/src/main/assets/migrations/14.sql
@@ -0,0 +1,14 @@
+update habits set color=0 where color=-2937041;
+update habits set color=1 where color=-1684967;
+update habits set color=2 where color=-415707;
+update habits set color=3 where color=-5262293;
+update habits set color=4 where color=-13070788;
+update habits set color=5 where color=-16742021;
+update habits set color=6 where color=-16732991;
+update habits set color=7 where color=-16540699;
+update habits set color=8 where color=-10603087;
+update habits set color=9 where color=-7461718;
+update habits set color=10 where color=-2614432;
+update habits set color=11 where color=-13619152;
+update habits set color=12 where color=-5592406;
+update habits set color=0 where color<0 or color>12;
\ No newline at end of file
diff --git a/app/src/main/java/com/android/colorpicker/ColorPickerDialog.java b/app/src/main/java/com/android/colorpicker/ColorPickerDialog.java
index a47e39d43..114c5fed5 100644
--- a/app/src/main/java/com/android/colorpicker/ColorPickerDialog.java
+++ b/app/src/main/java/com/android/colorpicker/ColorPickerDialog.java
@@ -16,24 +16,24 @@
package com.android.colorpicker;
-import org.isoron.uhabits.R;
-
import android.app.Activity;
-import android.app.AlertDialog;
import android.app.Dialog;
-import android.app.DialogFragment;
import android.os.Bundle;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.app.AppCompatDialogFragment;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ProgressBar;
import com.android.colorpicker.ColorPickerSwatch.OnColorSelectedListener;
+import org.isoron.uhabits.R;
+
/**
* A dialog which takes in as input an array of palette and creates a palette allowing the user to
* select a specific color swatch, which invokes a listener.
*/
-public class ColorPickerDialog extends DialogFragment implements OnColorSelectedListener {
+public class ColorPickerDialog extends AppCompatDialogFragment implements OnColorSelectedListener {
public static final int SIZE_LARGE = 1;
public static final int SIZE_SMALL = 2;
diff --git a/app/src/main/java/com/android/datetimepicker/time/TimePickerDialog.java b/app/src/main/java/com/android/datetimepicker/time/TimePickerDialog.java
index e365005f4..f36ad2780 100644
--- a/app/src/main/java/com/android/datetimepicker/time/TimePickerDialog.java
+++ b/app/src/main/java/com/android/datetimepicker/time/TimePickerDialog.java
@@ -30,6 +30,7 @@ import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.os.Bundle;
+import android.support.v7.app.AppCompatDialogFragment;
import android.util.Log;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
@@ -49,7 +50,7 @@ import com.android.datetimepicker.time.RadialPickerLayout.OnValueSelectedListene
/**
* Dialog to set a time.
*/
-public class TimePickerDialog extends DialogFragment implements OnValueSelectedListener{
+public class TimePickerDialog extends AppCompatDialogFragment implements OnValueSelectedListener{
private static final String TAG = "TimePickerDialog";
private static final String KEY_HOUR_OF_DAY = "hour_of_day";
diff --git a/app/src/main/java/org/isoron/uhabits/AboutActivity.java b/app/src/main/java/org/isoron/uhabits/AboutActivity.java
index 3f84528e1..279a96d45 100644
--- a/app/src/main/java/org/isoron/uhabits/AboutActivity.java
+++ b/app/src/main/java/org/isoron/uhabits/AboutActivity.java
@@ -19,33 +19,27 @@
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.uhabits.helpers.ColorHelper;
+import org.isoron.uhabits.helpers.UIHelper;
-public class AboutActivity extends Activity implements View.OnClickListener
+public class AboutActivity extends BaseActivity implements View.OnClickListener
{
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
+
setContentView(R.layout.about);
+ setupSupportActionBar(true);
- 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);
- }
+ int color = UIHelper.getStyledColor(this, R.attr.aboutScreenColor);
+ setupActionBarColor(color);
TextView tvVersion = (TextView) findViewById(R.id.tvVersion);
TextView tvRate = (TextView) findViewById(R.id.tvRate);
diff --git a/app/src/main/java/org/isoron/uhabits/BaseActivity.java b/app/src/main/java/org/isoron/uhabits/BaseActivity.java
index 4c8b2b75a..1444f228a 100644
--- a/app/src/main/java/org/isoron/uhabits/BaseActivity.java
+++ b/app/src/main/java/org/isoron/uhabits/BaseActivity.java
@@ -19,17 +19,25 @@
package org.isoron.uhabits;
-import android.app.Activity;
import android.app.backup.BackupManager;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
import android.os.AsyncTask;
+import android.os.Build;
import android.os.Bundle;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.View;
import android.widget.Toast;
import org.isoron.uhabits.commands.Command;
+import org.isoron.uhabits.helpers.ColorHelper;
+import org.isoron.uhabits.helpers.UIHelper;
import java.util.LinkedList;
-abstract public class BaseActivity extends Activity implements Thread.UncaughtExceptionHandler
+abstract public class BaseActivity extends AppCompatActivity implements Thread.UncaughtExceptionHandler
{
private static int MAX_UNDO_LEVEL = 15;
@@ -44,6 +52,8 @@ abstract public class BaseActivity extends Activity implements Thread.UncaughtEx
{
super.onCreate(savedInstanceState);
+ UIHelper.applyCurrentTheme(this);
+
androidExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(this);
@@ -117,6 +127,23 @@ abstract public class BaseActivity extends Activity implements Thread.UncaughtEx
showToast(command.getExecuteStringId());
}
+ protected void setupSupportActionBar(boolean homeButtonEnabled)
+ {
+ Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ if(toolbar == null) return;
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
+ toolbar.setElevation(UIHelper.dpToPixels(this, 2));
+
+ setSupportActionBar(toolbar);
+
+ ActionBar actionBar = getSupportActionBar();
+ if(actionBar == null) return;
+
+ if(homeButtonEnabled)
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ }
+
public void onPostExecuteCommand(Long refreshKey)
{
}
@@ -139,4 +166,39 @@ abstract public class BaseActivity extends Activity implements Thread.UncaughtEx
else
System.exit(1);
}
+
+ protected void setupActionBarColor(int color)
+ {
+ ActionBar actionBar = getSupportActionBar();
+ if(actionBar == null) return;
+
+ if (!UIHelper.getStyledBoolean(this, R.attr.useHabitColorAsPrimary)) return;
+
+ ColorDrawable drawable = new ColorDrawable(color);
+ actionBar.setBackgroundDrawable(drawable);
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
+ {
+ int darkerColor = ColorHelper.mixColors(color, Color.BLACK, 0.75f);
+ getWindow().setStatusBarColor(darkerColor);
+ }
+ }
+
+ @Override
+ protected void onPostResume()
+ {
+ super.onPostResume();
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
+ hideFakeToolbarShadow();
+ }
+
+ protected void hideFakeToolbarShadow()
+ {
+ View view = findViewById(R.id.toolbarShadow);
+ if(view != null) view.setVisibility(View.GONE);
+
+ view = findViewById(R.id.headerShadow);
+ if(view != null) view.setVisibility(View.GONE);
+ }
}
diff --git a/app/src/main/java/org/isoron/uhabits/HabitBroadcastReceiver.java b/app/src/main/java/org/isoron/uhabits/HabitBroadcastReceiver.java
index 057aedfba..e1a45aa5e 100644
--- a/app/src/main/java/org/isoron/uhabits/HabitBroadcastReceiver.java
+++ b/app/src/main/java/org/isoron/uhabits/HabitBroadcastReceiver.java
@@ -29,11 +29,11 @@ import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.BitmapFactory;
-import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.support.v4.app.NotificationCompat;
+import android.support.v4.app.TaskStackBuilder;
import android.support.v4.content.LocalBroadcastManager;
import org.isoron.uhabits.helpers.DateHelper;
@@ -58,7 +58,7 @@ public class HabitBroadcastReceiver extends BroadcastReceiver
{
case ACTION_SHOW_REMINDER:
createNotification(context, intent);
- createReminderAlarms(context);
+ createReminderAlarmsDelayed(context);
break;
case ACTION_DISMISS:
@@ -72,10 +72,14 @@ public class HabitBroadcastReceiver extends BroadcastReceiver
case ACTION_SNOOZE:
snoozeHabit(context, intent);
break;
+
+ case Intent.ACTION_BOOT_COMPLETED:
+ ReminderHelper.createReminderAlarms(context);
+ break;
}
}
- private void createReminderAlarms(final Context context)
+ private void createReminderAlarmsDelayed(final Context context)
{
new Handler().postDelayed(new Runnable()
{
@@ -163,9 +167,7 @@ public class HabitBroadcastReceiver extends BroadcastReceiver
{
if (todayValue != Checkmark.UNCHECKED) return;
if (!checkWeekday(intent, habit)) return;
-
- // Check if reminder has been turned off after alarm was scheduled
- if (habit.reminderHour == null) return;
+ if (!habit.hasReminder()) return;
Intent contentIntent = new Intent(context, MainActivity.class);
contentIntent.setData(data);
@@ -176,14 +178,16 @@ public class HabitBroadcastReceiver extends BroadcastReceiver
PendingIntent checkIntentPending = buildCheckIntent(context, habit, timestamp);
PendingIntent snoozeIntentPending = buildSnoozeIntent(context, habit);
- Uri soundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
+ Uri ringtoneUri = ReminderHelper.getRingtoneUri(context);
NotificationCompat.WearableExtender wearableExtender =
new NotificationCompat.WearableExtender().setBackground(
- BitmapFactory.decodeResource(context.getResources(), R.drawable.stripe));
+ BitmapFactory.decodeResource(context.getResources(),
+ R.drawable.stripe));
Notification notification =
- new NotificationCompat.Builder(context).setSmallIcon(R.drawable.ic_notification)
+ new NotificationCompat.Builder(context)
+ .setSmallIcon(R.drawable.ic_notification)
.setContentTitle(habit.name)
.setContentText(habit.description)
.setContentIntent(contentPendingIntent)
@@ -192,7 +196,7 @@ public class HabitBroadcastReceiver extends BroadcastReceiver
context.getString(R.string.check), checkIntentPending)
.addAction(R.drawable.ic_action_snooze,
context.getString(R.string.snooze), snoozeIntentPending)
- .setSound(soundUri)
+ .setSound(ringtoneUri)
.extend(wearableExtender)
.setWhen(reminderTime)
.setShowWhen(true)
@@ -201,7 +205,8 @@ public class HabitBroadcastReceiver extends BroadcastReceiver
notification.flags |= Notification.FLAG_AUTO_CANCEL;
NotificationManager notificationManager =
- (NotificationManager) context.getSystemService(Activity.NOTIFICATION_SERVICE);
+ (NotificationManager) context.getSystemService(
+ Activity.NOTIFICATION_SERVICE);
int notificationId = (int) (habit.getId() % Integer.MAX_VALUE);
notificationManager.notify(notificationId, notification);
@@ -241,7 +246,10 @@ public class HabitBroadcastReceiver extends BroadcastReceiver
{
Intent intent = new Intent(context, ShowHabitActivity.class);
intent.setData(Uri.parse("content://org.isoron.uhabits/habit/" + habit.getId()));
- return PendingIntent.getActivity(context, 0, intent, 0);
+
+ return TaskStackBuilder.create(context.getApplicationContext())
+ .addNextIntentWithParentStack(intent)
+ .getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
}
private boolean checkWeekday(Intent intent, Habit habit)
@@ -254,4 +262,13 @@ public class HabitBroadcastReceiver extends BroadcastReceiver
return reminderDays[weekday];
}
+ public static void dismissNotification(Context context, Habit habit)
+ {
+ NotificationManager notificationManager =
+ (NotificationManager) context.getSystemService(
+ Activity.NOTIFICATION_SERVICE);
+
+ int notificationId = (int) (habit.getId() % Integer.MAX_VALUE);
+ notificationManager.cancel(notificationId);
+ }
}
diff --git a/app/src/main/java/org/isoron/uhabits/IntroActivity.java b/app/src/main/java/org/isoron/uhabits/IntroActivity.java
index e7685aa87..e298d1c63 100644
--- a/app/src/main/java/org/isoron/uhabits/IntroActivity.java
+++ b/app/src/main/java/org/isoron/uhabits/IntroActivity.java
@@ -40,10 +40,6 @@ public class IntroActivity extends AppIntro2
getString(R.string.intro_description_2), R.drawable.intro_icon_2,
Color.parseColor("#ffa726")));
- addSlide(AppIntroFragment.newInstance(getString(R.string.intro_title_3),
- getString(R.string.intro_description_3), R.drawable.intro_icon_3,
- Color.parseColor("#7cb342")));
-
addSlide(AppIntroFragment.newInstance(getString(R.string.intro_title_4),
getString(R.string.intro_description_4), R.drawable.intro_icon_4,
Color.parseColor("#9575cd")));
diff --git a/app/src/main/java/org/isoron/uhabits/MainActivity.java b/app/src/main/java/org/isoron/uhabits/MainActivity.java
index 760ad134e..a8dad23b8 100644
--- a/app/src/main/java/org/isoron/uhabits/MainActivity.java
+++ b/app/src/main/java/org/isoron/uhabits/MainActivity.java
@@ -27,19 +27,24 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
+import android.graphics.drawable.ColorDrawable;
import android.net.Uri;
import android.os.AsyncTask;
+import android.os.Build;
import android.os.Bundle;
+import android.os.Handler;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.v4.content.LocalBroadcastManager;
+import android.support.v7.app.ActionBar;
import android.view.Menu;
import android.view.MenuItem;
-import org.isoron.uhabits.helpers.DateHelper;
-import org.isoron.uhabits.helpers.UIHelper;
import org.isoron.uhabits.fragments.ListHabitsFragment;
+import org.isoron.uhabits.helpers.DateHelper;
import org.isoron.uhabits.helpers.ReminderHelper;
+import org.isoron.uhabits.helpers.UIHelper;
+import org.isoron.uhabits.models.Checkmark;
import org.isoron.uhabits.models.Habit;
import org.isoron.uhabits.tasks.BaseTask;
import org.isoron.uhabits.widgets.CheckmarkWidgetProvider;
@@ -70,19 +75,35 @@ public class MainActivity extends BaseActivity
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
+
setContentView(R.layout.list_habits_activity);
+ setupSupportActionBar(false);
+
prefs = PreferenceManager.getDefaultSharedPreferences(this);
listHabitsFragment =
- (ListHabitsFragment) getFragmentManager().findFragmentById(R.id.fragment1);
+ (ListHabitsFragment) getSupportFragmentManager().findFragmentById(R.id.fragment1);
receiver = new Receiver();
localBroadcastManager = LocalBroadcastManager.getInstance(this);
localBroadcastManager.registerReceiver(receiver, new IntentFilter(ACTION_REFRESH));
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
+ onPreLollipopStartup();
+
onStartup();
}
+ private void onPreLollipopStartup()
+ {
+ ActionBar actionBar = getSupportActionBar();
+ if(actionBar == null) return;
+ if(UIHelper.isNightMode()) return;
+
+ int color = getResources().getColor(R.color.grey_900);
+ actionBar.setBackgroundDrawable(new ColorDrawable(color));
+ }
+
private void onStartup()
{
PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
@@ -121,7 +142,12 @@ public class MainActivity extends BaseActivity
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
+ menu.clear();
getMenuInflater().inflate(R.menu.list_habits_menu, menu);
+
+ MenuItem nightModeItem = menu.findItem(R.id.action_night_mode);
+ nightModeItem.setChecked(UIHelper.isNightMode());
+
return true;
}
@@ -130,6 +156,17 @@ public class MainActivity extends BaseActivity
{
switch (item.getItemId())
{
+ case R.id.action_night_mode:
+ {
+ if(UIHelper.isNightMode())
+ UIHelper.setCurrentTheme(UIHelper.THEME_LIGHT);
+ else
+ UIHelper.setCurrentTheme(UIHelper.THEME_DARK);
+
+ refreshTheme();
+ return true;
+ }
+
case R.id.action_settings:
{
Intent intent = new Intent(this, SettingsActivity.class);
@@ -158,6 +195,23 @@ public class MainActivity extends BaseActivity
}
}
+ private void refreshTheme()
+ {
+ new Handler().postDelayed(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ Intent intent = new Intent(MainActivity.this, MainActivity.class);
+
+ MainActivity.this.finish();
+ overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
+ startActivity(intent);
+
+ }
+ }, 500); // Let the menu disappear first
+ }
+
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
@@ -218,11 +272,21 @@ public class MainActivity extends BaseActivity
@Override
protected void doInBackground()
{
+ dismissNotifications(MainActivity.this);
updateWidgets(MainActivity.this);
}
}.execute();
}
+ private void dismissNotifications(Context context)
+ {
+ for(Habit h : Habit.getHabitsWithReminder())
+ {
+ if(h.checkmarks.getTodayValue() != Checkmark.UNCHECKED)
+ HabitBroadcastReceiver.dismissNotification(context, h);
+ }
+ }
+
public static void updateWidgets(Context context)
{
updateWidgets(context, CheckmarkWidgetProvider.class);
diff --git a/app/src/main/java/org/isoron/uhabits/SettingsActivity.java b/app/src/main/java/org/isoron/uhabits/SettingsActivity.java
index 9e43b1bba..579e7a7a6 100644
--- a/app/src/main/java/org/isoron/uhabits/SettingsActivity.java
+++ b/app/src/main/java/org/isoron/uhabits/SettingsActivity.java
@@ -19,19 +19,20 @@
package org.isoron.uhabits;
-import android.app.Activity;
import android.os.Bundle;
-import org.isoron.uhabits.fragments.SettingsFragment;
+import org.isoron.uhabits.helpers.UIHelper;
-public class SettingsActivity extends Activity
+public class SettingsActivity extends BaseActivity
{
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
- getFragmentManager().beginTransaction()
- .replace(android.R.id.content, new SettingsFragment())
- .commit();
+ setContentView(R.layout.settings_activity);
+ setupSupportActionBar(true);
+
+ int color = UIHelper.getStyledColor(this, R.attr.aboutScreenColor);
+ setupActionBarColor(color);
}
}
diff --git a/app/src/main/java/org/isoron/uhabits/ShowHabitActivity.java b/app/src/main/java/org/isoron/uhabits/ShowHabitActivity.java
index 4fa707bd9..8be54b586 100644
--- a/app/src/main/java/org/isoron/uhabits/ShowHabitActivity.java
+++ b/app/src/main/java/org/isoron/uhabits/ShowHabitActivity.java
@@ -19,12 +19,12 @@
package org.isoron.uhabits;
-import android.app.ActionBar;
import android.content.ContentUris;
-import android.graphics.drawable.ColorDrawable;
import android.net.Uri;
import android.os.Bundle;
+import android.support.v7.app.ActionBar;
+import org.isoron.uhabits.helpers.ColorHelper;
import org.isoron.uhabits.models.Habit;
public class ShowHabitActivity extends BaseActivity
@@ -38,16 +38,23 @@ public class ShowHabitActivity extends BaseActivity
Uri data = getIntent().getData();
habit = Habit.get(ContentUris.parseId(data));
- ActionBar actionBar = getActionBar();
-
- if(actionBar != null && getHabit() != null)
- {
- actionBar.setTitle(getHabit().name);
- if (android.os.Build.VERSION.SDK_INT >= 21)
- actionBar.setBackgroundDrawable(new ColorDrawable(getHabit().color));
- }
setContentView(R.layout.show_habit_activity);
+
+ setupSupportActionBar(true);
+ setupHabitActionBar();
+ }
+
+ private void setupHabitActionBar()
+ {
+ if(habit == null) return;
+
+ ActionBar actionBar = getSupportActionBar();
+ if(actionBar == null) return;
+
+ actionBar.setTitle(habit.name);
+
+ setupActionBarColor(ColorHelper.getColor(this, habit.color));
}
public Habit getHabit()
diff --git a/app/src/main/java/org/isoron/uhabits/fragments/EditHabitFragment.java b/app/src/main/java/org/isoron/uhabits/dialogs/EditHabitDialogFragment.java
similarity index 92%
rename from app/src/main/java/org/isoron/uhabits/fragments/EditHabitFragment.java
rename to app/src/main/java/org/isoron/uhabits/dialogs/EditHabitDialogFragment.java
index 3f1a117c9..1baa97cbe 100644
--- a/app/src/main/java/org/isoron/uhabits/fragments/EditHabitFragment.java
+++ b/app/src/main/java/org/isoron/uhabits/dialogs/EditHabitDialogFragment.java
@@ -17,13 +17,13 @@
* with this program. If not, see .
*/
-package org.isoron.uhabits.fragments;
+package org.isoron.uhabits.dialogs;
import android.annotation.SuppressLint;
-import android.app.DialogFragment;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
+import android.support.v7.app.AppCompatDialogFragment;
import android.text.format.DateFormat;
import android.view.LayoutInflater;
import android.view.View;
@@ -40,19 +40,18 @@ import com.android.colorpicker.ColorPickerSwatch;
import com.android.datetimepicker.time.RadialPickerLayout;
import com.android.datetimepicker.time.TimePickerDialog;
-import org.isoron.uhabits.helpers.ColorHelper;
-import org.isoron.uhabits.helpers.DateHelper;
-import org.isoron.uhabits.helpers.UIHelper.OnSavedListener;
import org.isoron.uhabits.R;
import org.isoron.uhabits.commands.Command;
import org.isoron.uhabits.commands.CreateHabitCommand;
import org.isoron.uhabits.commands.EditHabitCommand;
-import org.isoron.uhabits.dialogs.WeekdayPickerDialog;
+import org.isoron.uhabits.helpers.ColorHelper;
+import org.isoron.uhabits.helpers.DateHelper;
+import org.isoron.uhabits.helpers.UIHelper.OnSavedListener;
import org.isoron.uhabits.models.Habit;
import java.util.Arrays;
-public class EditHabitFragment extends DialogFragment
+public class EditHabitDialogFragment extends AppCompatDialogFragment
implements OnClickListener, WeekdayPickerDialog.OnWeekdaysPickedListener,
TimePickerDialog.OnTimeSetListener, Spinner.OnItemSelectedListener
{
@@ -79,9 +78,9 @@ public class EditHabitFragment extends DialogFragment
private SharedPreferences prefs;
private boolean is24HourMode;
- public static EditHabitFragment editSingleHabitFragment(long id)
+ public static EditHabitDialogFragment editSingleHabitFragment(long id)
{
- EditHabitFragment frag = new EditHabitFragment();
+ EditHabitDialogFragment frag = new EditHabitDialogFragment();
Bundle args = new Bundle();
args.putLong("habitId", id);
args.putInt("editMode", EDIT_MODE);
@@ -89,9 +88,9 @@ public class EditHabitFragment extends DialogFragment
return frag;
}
- public static EditHabitFragment createHabitFragment()
+ public static EditHabitDialogFragment createHabitFragment()
{
- EditHabitFragment frag = new EditHabitFragment();
+ EditHabitDialogFragment frag = new EditHabitDialogFragment();
Bundle args = new Bundle();
args.putInt("editMode", CREATE_MODE);
frag.setArguments(args);
@@ -138,7 +137,7 @@ public class EditHabitFragment extends DialogFragment
modifiedHabit = new Habit();
modifiedHabit.freqNum = 1;
modifiedHabit.freqDen = 1;
- modifiedHabit.color = prefs.getInt("pref_default_habit_color", modifiedHabit.color);
+ modifiedHabit.color = prefs.getInt("pref_default_habit_palette_color", modifiedHabit.color);
}
else if (mode == EDIT_MODE)
{
@@ -174,13 +173,13 @@ public class EditHabitFragment extends DialogFragment
return view;
}
- private void changeColor(Integer color)
+ private void changeColor(int paletteColor)
{
- modifiedHabit.color = color;
- tvName.setTextColor(color);
+ modifiedHabit.color = paletteColor;
+ tvName.setTextColor(ColorHelper.getColor(getActivity(), paletteColor));
SharedPreferences.Editor editor = prefs.edit();
- editor.putInt("pref_default_habit_color", color);
+ editor.putInt("pref_default_habit_palette_color", paletteColor);
editor.apply();
}
@@ -237,15 +236,18 @@ public class EditHabitFragment extends DialogFragment
private void onColorButtonClick()
{
+ int originalAndroidColor = ColorHelper.getColor(getActivity(), modifiedHabit.color);
+
ColorPickerDialog picker = ColorPickerDialog.newInstance(
- R.string.color_picker_default_title, ColorHelper.palette, modifiedHabit.color, 4,
- ColorPickerDialog.SIZE_SMALL);
+ R.string.color_picker_default_title, ColorHelper.getPalette(getActivity()),
+ originalAndroidColor, 4, ColorPickerDialog.SIZE_SMALL);
picker.setOnColorSelectedListener(new ColorPickerSwatch.OnColorSelectedListener()
{
- public void onColorSelected(int color)
+ public void onColorSelected(int androidColor)
{
- changeColor(color);
+ int paletteColor = ColorHelper.colorToPaletteIndex(getActivity(), androidColor);
+ changeColor(paletteColor);
}
});
picker.show(getFragmentManager(), "picker");
diff --git a/app/src/main/java/org/isoron/uhabits/dialogs/HistoryEditorDialog.java b/app/src/main/java/org/isoron/uhabits/dialogs/HistoryEditorDialog.java
index 7654fb0ed..cd95e9212 100644
--- a/app/src/main/java/org/isoron/uhabits/dialogs/HistoryEditorDialog.java
+++ b/app/src/main/java/org/isoron/uhabits/dialogs/HistoryEditorDialog.java
@@ -19,21 +19,20 @@
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.support.v7.app.AlertDialog;
+import android.support.v7.app.AppCompatDialogFragment;
import android.util.DisplayMetrics;
-import android.util.Log;
import org.isoron.uhabits.R;
import org.isoron.uhabits.models.Habit;
import org.isoron.uhabits.tasks.BaseTask;
import org.isoron.uhabits.views.HabitHistoryView;
-public class HistoryEditorDialog extends DialogFragment
+public class HistoryEditorDialog extends AppCompatDialogFragment
implements DialogInterface.OnClickListener
{
private Habit habit;
@@ -89,8 +88,6 @@ public class HistoryEditorDialog extends DialogFragment
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);
}
diff --git a/app/src/main/java/org/isoron/uhabits/dialogs/WeekdayPickerDialog.java b/app/src/main/java/org/isoron/uhabits/dialogs/WeekdayPickerDialog.java
index f2d54f5a2..5297b80eb 100644
--- a/app/src/main/java/org/isoron/uhabits/dialogs/WeekdayPickerDialog.java
+++ b/app/src/main/java/org/isoron/uhabits/dialogs/WeekdayPickerDialog.java
@@ -19,16 +19,16 @@
package org.isoron.uhabits.dialogs;
-import android.app.AlertDialog;
import android.app.Dialog;
-import android.app.DialogFragment;
import android.content.DialogInterface;
import android.os.Bundle;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.app.AppCompatDialogFragment;
-import org.isoron.uhabits.helpers.DateHelper;
import org.isoron.uhabits.R;
+import org.isoron.uhabits.helpers.DateHelper;
-public class WeekdayPickerDialog extends DialogFragment
+public class WeekdayPickerDialog extends AppCompatDialogFragment
implements DialogInterface.OnMultiChoiceClickListener, DialogInterface.OnClickListener
{
diff --git a/app/src/main/java/org/isoron/uhabits/fragments/HabitListAdapter.java b/app/src/main/java/org/isoron/uhabits/fragments/HabitListAdapter.java
index dfbd959f2..e01ceef84 100644
--- a/app/src/main/java/org/isoron/uhabits/fragments/HabitListAdapter.java
+++ b/app/src/main/java/org/isoron/uhabits/fragments/HabitListAdapter.java
@@ -24,11 +24,9 @@ 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.uhabits.helpers.DateHelper;
import org.isoron.uhabits.R;
+import org.isoron.uhabits.helpers.DateHelper;
import org.isoron.uhabits.helpers.ListHabitsHelper;
import org.isoron.uhabits.loaders.HabitListLoader;
import org.isoron.uhabits.models.Habit;
@@ -74,28 +72,15 @@ class HabitListAdapter extends BaseAdapter
public View getView(int position, View view, ViewGroup parent)
{
final Habit habit = loader.habitsList.get(position);
+ boolean selected = selectedPositions.contains(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);
+ view = helper.inflateHabitCard(inflater, onCheckmarkLongClickListener,
+ onCheckmarkClickListener);
}
- 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);
-
+ helper.updateHabitCard(view, habit, selected);
return view;
}
diff --git a/app/src/main/java/org/isoron/uhabits/fragments/HabitSelectionCallback.java b/app/src/main/java/org/isoron/uhabits/fragments/HabitSelectionCallback.java
index 9d124a691..bf0973aaf 100644
--- a/app/src/main/java/org/isoron/uhabits/fragments/HabitSelectionCallback.java
+++ b/app/src/main/java/org/isoron/uhabits/fragments/HabitSelectionCallback.java
@@ -19,9 +19,9 @@
package org.isoron.uhabits.fragments;
-import android.app.AlertDialog;
import android.content.DialogInterface;
-import android.view.ActionMode;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.view.ActionMode;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.ProgressBar;
@@ -29,12 +29,13 @@ import android.widget.ProgressBar;
import com.android.colorpicker.ColorPickerDialog;
import com.android.colorpicker.ColorPickerSwatch;
-import org.isoron.uhabits.R;
import org.isoron.uhabits.BaseActivity;
+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.dialogs.EditHabitDialogFragment;
import org.isoron.uhabits.helpers.ColorHelper;
import org.isoron.uhabits.helpers.UIHelper;
import org.isoron.uhabits.loaders.HabitListLoader;
@@ -155,27 +156,33 @@ public class HabitSelectionCallback implements ActionMode.Callback
case R.id.action_edit_habit:
{
- EditHabitFragment frag = EditHabitFragment.editSingleHabitFragment(firstHabit.getId());
+ EditHabitDialogFragment
+ frag = EditHabitDialogFragment.editSingleHabitFragment(firstHabit.getId());
frag.setOnSavedListener(onSavedListener);
- frag.show(activity.getFragmentManager(), "editHabit");
+ frag.show(activity.getSupportFragmentManager(), "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);
+ int originalAndroidColor = ColorHelper.getColor(activity, firstHabit.color);
+
+ ColorPickerDialog picker = ColorPickerDialog.newInstance(
+ R.string.color_picker_default_title, ColorHelper.getPalette(activity),
+ originalAndroidColor, 4, ColorPickerDialog.SIZE_SMALL);
picker.setOnColorSelectedListener(new ColorPickerSwatch.OnColorSelectedListener()
{
- public void onColorSelected(int color)
+ public void onColorSelected(int androidColor)
{
- activity.executeCommand(
- new ChangeHabitColorCommand(selectedHabits, color), null);
+ int paletteColor = ColorHelper.colorToPaletteIndex(activity,
+ androidColor);
+ activity.executeCommand(new ChangeHabitColorCommand(selectedHabits,
+ paletteColor), null);
mode.finish();
}
});
- picker.show(activity.getFragmentManager(), "picker");
+ picker.show(activity.getSupportFragmentManager(), "picker");
return true;
}
diff --git a/app/src/main/java/org/isoron/uhabits/fragments/ListHabitsFragment.java b/app/src/main/java/org/isoron/uhabits/fragments/ListHabitsFragment.java
index 1e9983833..1319a69b1 100644
--- a/app/src/main/java/org/isoron/uhabits/fragments/ListHabitsFragment.java
+++ b/app/src/main/java/org/isoron/uhabits/fragments/ListHabitsFragment.java
@@ -20,14 +20,14 @@
package org.isoron.uhabits.fragments;
import android.app.Activity;
-import android.app.Fragment;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.annotation.Nullable;
-import android.view.ActionMode;
+import android.support.v4.app.Fragment;
+import android.support.v7.view.ActionMode;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.HapticFeedbackConstants;
@@ -50,17 +50,19 @@ import com.mobeta.android.dslv.DragSortController;
import com.mobeta.android.dslv.DragSortListView;
import com.mobeta.android.dslv.DragSortListView.DropListener;
-import org.isoron.uhabits.R;
import org.isoron.uhabits.BaseActivity;
+import org.isoron.uhabits.R;
import org.isoron.uhabits.commands.Command;
import org.isoron.uhabits.commands.ToggleRepetitionCommand;
+import org.isoron.uhabits.dialogs.EditHabitDialogFragment;
import org.isoron.uhabits.dialogs.FilePickerDialog;
import org.isoron.uhabits.helpers.DatabaseHelper;
import org.isoron.uhabits.helpers.DateHelper;
-import org.isoron.uhabits.helpers.UIHelper.OnSavedListener;
import org.isoron.uhabits.helpers.HintManager;
import org.isoron.uhabits.helpers.ListHabitsHelper;
import org.isoron.uhabits.helpers.ReminderHelper;
+import org.isoron.uhabits.helpers.UIHelper;
+import org.isoron.uhabits.helpers.UIHelper.OnSavedListener;
import org.isoron.uhabits.loaders.HabitListLoader;
import org.isoron.uhabits.models.Habit;
import org.isoron.uhabits.tasks.ExportCSVTask;
@@ -120,7 +122,7 @@ public class ListHabitsFragment extends Fragment
loader.setCheckmarkCount(helper.getButtonCount());
llHint.setOnClickListener(this);
- tvStarEmpty.setTypeface(helper.getFontawesome());
+ tvStarEmpty.setTypeface(UIHelper.getFontAwesome(activity));
adapter = new HabitListAdapter(getActivity(), loader);
adapter.setSelectedPositions(selectedPositions);
@@ -141,7 +143,7 @@ public class ListHabitsFragment extends Fragment
if(savedInstanceState != null)
{
- EditHabitFragment frag = (EditHabitFragment) getFragmentManager()
+ EditHabitDialogFragment frag = (EditHabitDialogFragment) getFragmentManager()
.findFragmentByTag("editHabit");
if(frag != null) frag.setOnSavedListener(this);
}
@@ -217,7 +219,7 @@ public class ListHabitsFragment extends Fragment
{
case R.id.action_add:
{
- EditHabitFragment frag = EditHabitFragment.createHabitFragment();
+ EditHabitDialogFragment frag = EditHabitDialogFragment.createHabitFragment();
frag.setOnSavedListener(this);
frag.show(getFragmentManager(), "editHabit");
return true;
@@ -284,7 +286,7 @@ public class ListHabitsFragment extends Fragment
callback.setOnSavedListener(this);
callback.setListener(this);
- actionMode = getActivity().startActionMode(callback);
+ actionMode = activity.startSupportActionMode(callback);
}
if(actionMode != null) actionMode.invalidate();
@@ -328,17 +330,18 @@ public class ListHabitsFragment extends Fragment
private void toggleCheck(View v)
{
- Long tag = (Long) v.getTag(R.string.habit_key);
- Integer offset = (Integer) v.getTag(R.string.offset_key);
- long timestamp = DateHelper.getStartOfDay(
- DateHelper.getLocalTime() - offset * DateHelper.millisecondsInOneDay);
-
- Habit habit = loader.habits.get(tag);
+ Long id = helper.getHabitIdFromCheckmarkView(v);
+ Habit habit = loader.habits.get(id);
if(habit == null) return;
- listView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+ float x = v.getX() + v.getWidth() / 2.0f + ((View) v.getParent()).getX();
+ float y = v.getY() + v.getHeight() / 2.0f + ((View) v.getParent()).getY();
+ helper.triggerRipple((View) v.getParent().getParent(), x, y);
+ listView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
helper.toggleCheckmarkView(v, habit);
+
+ long timestamp = helper.getTimestampFromCheckmarkView(v);
executeCommand(new ToggleRepetitionCommand(habit, timestamp), habit.getId());
}
@@ -380,6 +383,7 @@ public class ListHabitsFragment extends Fragment
else loader.updateHabit(refreshKey);
}
+ @Override
public void onActionModeDestroyed(ActionMode mode)
{
actionMode = null;
diff --git a/app/src/main/java/org/isoron/uhabits/fragments/SettingsFragment.java b/app/src/main/java/org/isoron/uhabits/fragments/SettingsFragment.java
index 0c4491b34..838527589 100644
--- a/app/src/main/java/org/isoron/uhabits/fragments/SettingsFragment.java
+++ b/app/src/main/java/org/isoron/uhabits/fragments/SettingsFragment.java
@@ -20,19 +20,23 @@
package org.isoron.uhabits.fragments;
import android.app.backup.BackupManager;
+import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
-import android.preference.Preference;
-import android.preference.PreferenceCategory;
-import android.preference.PreferenceFragment;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceCategory;
+import android.support.v7.preference.PreferenceFragmentCompat;
import org.isoron.uhabits.MainActivity;
import org.isoron.uhabits.R;
+import org.isoron.uhabits.helpers.ReminderHelper;
import org.isoron.uhabits.helpers.UIHelper;
-public class SettingsFragment extends PreferenceFragment
+public class SettingsFragment extends PreferenceFragmentCompat
implements SharedPreferences.OnSharedPreferenceChangeListener
{
+ private static int RINGTONE_REQUEST_CODE = 1;
+
@Override
public void onCreate(Bundle savedInstanceState)
{
@@ -44,10 +48,18 @@ public class SettingsFragment extends PreferenceFragment
setResultOnPreferenceClick("exportDB", MainActivity.RESULT_EXPORT_DB);
setResultOnPreferenceClick("bugReport", MainActivity.RESULT_BUG_REPORT);
+ updateRingtoneDescription();
+
if(UIHelper.isLocaleFullyTranslated())
removePreference("translate", "linksCategory");
}
+ @Override
+ public void onCreatePreferences(Bundle bundle, String s)
+ {
+
+ }
+
private void removePreference(String preferenceKey, String categoryKey)
{
PreferenceCategory cat = (PreferenceCategory) findPreference(categoryKey);
@@ -91,4 +103,38 @@ public class SettingsFragment extends PreferenceFragment
{
BackupManager.dataChanged("org.isoron.uhabits");
}
+
+ @Override
+ public boolean onPreferenceTreeClick(Preference preference)
+ {
+ if(preference.getKey() == null) return false;
+
+ if (preference.getKey().equals("reminderSound"))
+ {
+ ReminderHelper.startRingtonePickerActivity(this, RINGTONE_REQUEST_CODE);
+ return true;
+ }
+
+ return super.onPreferenceTreeClick(preference);
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data)
+ {
+ if(requestCode == RINGTONE_REQUEST_CODE)
+ {
+ ReminderHelper.parseRingtoneData(getContext(), data);
+ updateRingtoneDescription();
+ return;
+ }
+
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+
+ private void updateRingtoneDescription()
+ {
+ String ringtoneName = ReminderHelper.getRingtoneName(getContext());
+ Preference ringtonePreference = findPreference("reminderSound");
+ ringtonePreference.setSummary(ringtoneName);
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/org/isoron/uhabits/fragments/ShowHabitFragment.java b/app/src/main/java/org/isoron/uhabits/fragments/ShowHabitFragment.java
index 1afb5dabd..f95c83881 100644
--- a/app/src/main/java/org/isoron/uhabits/fragments/ShowHabitFragment.java
+++ b/app/src/main/java/org/isoron/uhabits/fragments/ShowHabitFragment.java
@@ -19,12 +19,9 @@
package org.isoron.uhabits.fragments;
-import android.app.Fragment;
-import android.content.SharedPreferences;
-import android.graphics.Color;
import android.os.Bundle;
-import android.preference.PreferenceManager;
import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@@ -40,8 +37,10 @@ import org.isoron.uhabits.HabitBroadcastReceiver;
import org.isoron.uhabits.R;
import org.isoron.uhabits.ShowHabitActivity;
import org.isoron.uhabits.commands.Command;
+import org.isoron.uhabits.dialogs.EditHabitDialogFragment;
import org.isoron.uhabits.dialogs.HistoryEditorDialog;
import org.isoron.uhabits.helpers.ColorHelper;
+import org.isoron.uhabits.helpers.DateHelper;
import org.isoron.uhabits.helpers.ReminderHelper;
import org.isoron.uhabits.helpers.UIHelper;
import org.isoron.uhabits.models.Habit;
@@ -73,11 +72,15 @@ public class ShowHabitFragment extends Fragment
@Nullable
private HabitScoreView scoreView;
- @Nullable
- private SharedPreferences prefs;
-
private int previousScoreInterval;
+ private float todayScore;
+ private float lastMonthScore;
+ private float lastYearScore;
+
+ private int activeColor;
+ private int inactiveColor;
+
@Override
public void onStart()
{
@@ -90,7 +93,12 @@ public class ShowHabitFragment extends Fragment
{
View view = inflater.inflate(R.layout.show_habit, container, false);
activity = (ShowHabitActivity) getActivity();
+
habit = activity.getHabit();
+ activeColor = ColorHelper.getColor(getContext(), habit.color);
+ inactiveColor = UIHelper.getStyledColor(getContext(), R.attr.mediumContrastTextColor);
+
+ updateHeader(view);
dataViews = new LinkedList<>();
@@ -99,13 +107,10 @@ public class ShowHabitFragment extends Fragment
scoreView = (HabitScoreView) view.findViewById(R.id.scoreView);
- prefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
- int defaultScoreInterval = prefs.getInt("pref_score_view_interval", 1);
- if(defaultScoreInterval > 5 || defaultScoreInterval < 0) defaultScoreInterval = 1;
+ int defaultScoreInterval = UIHelper.getDefaultScoreInterval(getContext());
previousScoreInterval = defaultScoreInterval;
setScoreBucketSize(defaultScoreInterval);
-
sStrengthInterval.setSelection(defaultScoreInterval);
sStrengthInterval.setOnItemSelectedListener(this);
@@ -133,7 +138,7 @@ public class ShowHabitFragment extends Fragment
if(savedInstanceState != null)
{
- EditHabitFragment fragEdit = (EditHabitFragment) getFragmentManager()
+ EditHabitDialogFragment fragEdit = (EditHabitDialogFragment) getFragmentManager()
.findFragmentByTag("editHabit");
HistoryEditorDialog fragEditor = (HistoryEditorDialog) getFragmentManager()
.findFragmentByTag("historyEditor");
@@ -147,6 +152,56 @@ public class ShowHabitFragment extends Fragment
return view;
}
+ private void updateHeader(View view)
+ {
+ if(habit == null) return;
+
+ TextView questionLabel = (TextView) view.findViewById(R.id.questionLabel);
+ questionLabel.setTextColor(activeColor);
+ questionLabel.setText(habit.description);
+
+ TextView reminderLabel = (TextView) view.findViewById(R.id.reminderLabel);
+ if(habit.hasReminder())
+ reminderLabel.setText(DateHelper.formatTime(getActivity(), habit.reminderHour,
+ habit.reminderMin));
+ else
+ reminderLabel.setText(getResources().getString(R.string.reminder_off));
+
+ TextView frequencyLabel = (TextView) view.findViewById(R.id.frequencyLabel);
+ frequencyLabel.setText(getFreqText());
+
+ if(habit.description.isEmpty())
+ questionLabel.setVisibility(View.GONE);
+ }
+
+ private String getFreqText()
+ {
+ if(habit == null)
+ return "";
+
+ if(habit.freqNum.equals(habit.freqDen))
+ return getResources().getString(R.string.every_day);
+
+ if(habit.freqNum == 1)
+ {
+ if (habit.freqDen == 7)
+ return getResources().getString(R.string.every_week);
+
+ if (habit.freqDen % 7 == 0)
+ return getResources().getString(R.string.every_x_weeks, habit.freqDen / 7);
+
+ return getResources().getString(R.string.every_x_days, habit.freqDen);
+ }
+
+ String times_every = getResources().getString(R.string.times_every);
+
+ if(habit.freqNum == 1)
+ times_every = getResources().getString(R.string.time_every);
+
+ return String.format("%d %s %d %s", habit.freqNum, times_every, habit.freqDen,
+ getResources().getString(R.string.days));
+ }
+
@Override
public void onResume()
{
@@ -154,42 +209,53 @@ public class ShowHabitFragment extends Fragment
refreshData();
}
- private void updateScoreRing(View view)
+ private void updateScore(View view)
{
if(habit == null) return;
if(view == null) return;
- float todayValue = (float) habit.scores.getTodayValue();
- float percentage = todayValue / Score.MAX_VALUE;
+ float todayPercentage = todayScore / Score.MAX_VALUE;
+ float monthDiff = todayPercentage - (lastMonthScore / Score.MAX_VALUE);
+ float yearDiff = todayPercentage - (lastYearScore / Score.MAX_VALUE);
RingView scoreRing = (RingView) view.findViewById(R.id.scoreRing);
- scoreRing.setColor(habit.color);
- scoreRing.setPercentage(percentage);
+ int androidColor = ColorHelper.getColor(getActivity(), habit.color);
+ scoreRing.setColor(androidColor);
+ scoreRing.setPercentage(todayPercentage);
+
+ TextView scoreLabel = (TextView) view.findViewById(R.id.scoreLabel);
+ TextView monthDiffLabel = (TextView) view.findViewById(R.id.monthDiffLabel);
+ TextView yearDiffLabel = (TextView) view.findViewById(R.id.yearDiffLabel);
+
+ scoreLabel.setText(String.format("%.0f%%", todayPercentage * 100));
+
+ String minus = "\u2212";
+ monthDiffLabel.setText(String.format("%s%.0f%%", (monthDiff >= 0 ? "+" : minus),
+ Math.abs(monthDiff) * 100));
+ yearDiffLabel.setText(
+ String.format("%s%.0f%%", (yearDiff >= 0 ? "+" : minus), Math.abs(yearDiff) * 100));
+
+ monthDiffLabel.setTextColor(monthDiff >= 0 ? activeColor : inactiveColor);
+ yearDiffLabel.setTextColor(yearDiff >= 0 ? activeColor : inactiveColor);
}
private void updateHeaders(View view)
{
- if(habit == null | activity == null) return;
-
- 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);
+ updateColor(view, R.id.scoreLabel);
}
private void updateColor(View view, int viewId)
{
- if(habit == null) return;
+ if(habit == null || activity == null) return;
TextView textView = (TextView) view.findViewById(viewId);
- textView.setTextColor(habit.color);
+ int androidColor = ColorHelper.getColor(activity, habit.color);
+ textView.setTextColor(androidColor);
}
@Override
@@ -207,7 +273,8 @@ public class ShowHabitFragment extends Fragment
{
case R.id.action_edit_habit:
{
- EditHabitFragment frag = EditHabitFragment.editSingleHabitFragment(habit.getId());
+ EditHabitDialogFragment
+ frag = EditHabitDialogFragment.editSingleHabitFragment(habit.getId());
frag.setOnSavedListener(this);
frag.show(getFragmentManager(), "editHabit");
return true;
@@ -227,6 +294,8 @@ public class ShowHabitFragment extends Fragment
else activity.executeCommand(command, h.getId());
ReminderHelper.createReminderAlarms(activity);
+ HabitBroadcastReceiver.sendRefreshBroadcast(getActivity());
+
activity.recreate();
}
@@ -241,25 +310,32 @@ public class ShowHabitFragment extends Fragment
{
new BaseTask()
{
- float percentage;
-
@Override
protected void doInBackground()
{
+ if(habit == null) return;
if(dataViews == null) return;
+ long today = DateHelper.getStartOfToday();
+ long lastMonth = today - 30 * DateHelper.millisecondsInOneDay;
+ long lastYear = today - 365 * DateHelper.millisecondsInOneDay;
+
+ todayScore = (float) habit.scores.getTodayValue();
+ lastMonthScore = (float) habit.scores.getValue(lastMonth);
+ lastYearScore = (float) habit.scores.getValue(lastYear);
+
int count = 0;
for(HabitDataView view : dataViews)
{
view.refreshData();
- onProgressUpdate(count++);
+ publishProgress(count++);
}
}
@Override
protected void onProgressUpdate(Integer... values)
{
- updateScoreRing(getView());
+ updateScore(getView());
if(dataViews == null) return;
dataViews.get(values[0]).postInvalidate();
}
@@ -278,15 +354,15 @@ public class ShowHabitFragment extends Fragment
{
if(scoreView == null) return;
- int sizes[] = { 1, 7, 31, 92, 365 };
- int size = sizes[position];
-
- scoreView.setBucketSize(size);
- if(position != previousScoreInterval) refreshData();
+ scoreView.setBucketSize(HabitScoreView.DEFAULT_BUCKET_SIZES[position]);
- if(prefs != null)
- prefs.edit().putInt("pref_score_view_interval", position).apply();
+ if(position != previousScoreInterval)
+ {
+ refreshData();
+ HabitBroadcastReceiver.sendRefreshBroadcast(getActivity());
+ }
+ UIHelper.setDefaultScoreInterval(getContext(), position);
previousScoreInterval = position;
}
diff --git a/app/src/main/java/org/isoron/uhabits/helpers/ColorHelper.java b/app/src/main/java/org/isoron/uhabits/helpers/ColorHelper.java
index e58a73745..d64f4ac3d 100644
--- a/app/src/main/java/org/isoron/uhabits/helpers/ColorHelper.java
+++ b/app/src/main/java/org/isoron/uhabits/helpers/ColorHelper.java
@@ -19,27 +19,63 @@
package org.isoron.uhabits.helpers;
+import android.content.Context;
import android.graphics.Color;
+import android.util.Log;
+
+import org.isoron.uhabits.R;
public class ColorHelper
{
- public static final int[] palette =
+ public static int CSV_PALETTE[] =
{
- Color.parseColor("#D32F2F"), // red
- Color.parseColor("#E64A19"), // orange
- Color.parseColor("#F9A825"), // yellow
- Color.parseColor("#AFB42B"), // light green
- Color.parseColor("#388E3C"), // dark green
- Color.parseColor("#00897B"), // teal
- Color.parseColor("#00ACC1"), // cyan
- Color.parseColor("#039BE5"), // blue
- Color.parseColor("#5E35B1"), // deep purple
- Color.parseColor("#8E24AA"), // purple
- Color.parseColor("#D81B60"), // pink
- Color.parseColor("#303030"), // dark grey
- Color.parseColor("#aaaaaa") // light grey
+ Color.parseColor("#D32F2F"), // 0 red
+ Color.parseColor("#E64A19"), // 1 orange
+ Color.parseColor("#F9A825"), // 2 yellow
+ Color.parseColor("#AFB42B"), // 3 light green
+ Color.parseColor("#388E3C"), // 4 dark green
+ Color.parseColor("#00897B"), // 5 teal
+ Color.parseColor("#00ACC1"), // 6 cyan
+ Color.parseColor("#039BE5"), // 7 blue
+ Color.parseColor("#5E35B1"), // 8 deep purple
+ Color.parseColor("#8E24AA"), // 9 purple
+ Color.parseColor("#D81B60"), // 10 pink
+ Color.parseColor("#303030"), // 11 dark grey
+ Color.parseColor("#aaaaaa") // 12 light grey
};
+ public static int colorToPaletteIndex(Context context, int color)
+ {
+ int[] palette = getPalette(context);
+
+ for(int k = 0; k < palette.length; k++)
+ if(palette[k] == color) return k;
+
+ return -1;
+ }
+
+ public static int[] getPalette(Context context)
+ {
+ int resourceId = UIHelper.getStyleResource(context, R.attr.palette);
+ if(resourceId < 0) return CSV_PALETTE;
+
+ return context.getResources().getIntArray(resourceId);
+ }
+
+ public static int getColor(Context context, int paletteColor)
+ {
+ if(context == null) throw new IllegalArgumentException("Context is null");
+
+ int palette[] = getPalette(context);
+ if(paletteColor < 0 || paletteColor >= palette.length)
+ {
+ Log.w("ColorHelper", String.format("Invalid color: %d. Returning default.", paletteColor));
+ paletteColor = 0;
+ }
+
+ return palette[paletteColor];
+ }
+
public static int mixColors(int color1, int color2, float amount)
{
final byte ALPHA_CHANNEL = 24;
@@ -76,6 +112,12 @@ public class ColorHelper
return setHSVParameter(color, newValue, 2);
}
+ public static int setAlpha(int color, float newAlpha)
+ {
+ int intAlpha = (int) (newAlpha * 255);
+ return Color.argb(intAlpha, Color.red(color), Color.green(color), Color.blue(color));
+ }
+
public static int setMinValue(int color, float newValue)
{
float hsv[] = new float[3];
diff --git a/app/src/main/java/org/isoron/uhabits/helpers/DateHelper.java b/app/src/main/java/org/isoron/uhabits/helpers/DateHelper.java
index b2c300453..4d0c02c32 100644
--- a/app/src/main/java/org/isoron/uhabits/helpers/DateHelper.java
+++ b/app/src/main/java/org/isoron/uhabits/helpers/DateHelper.java
@@ -25,6 +25,7 @@ import android.text.format.DateFormat;
import org.isoron.uhabits.R;
import java.text.SimpleDateFormat;
+import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Locale;
@@ -155,14 +156,26 @@ public class DateHelper
}
+ /**
+ * Throughout the code, it is assumed that the weekdays are numbered from 0 (Saturday) to 6
+ * (Friday). In the Java Calendar they are numbered from 1 (Sunday) to 7 (Saturday). This
+ * function converts from Java to our internal representation.
+ *
+ * @return weekday number in the internal interpretation
+ */
+ public static int javaWeekdayToLoopWeekday(int number)
+ {
+ return number % 7;
+ }
+
public static String[] getDayNames(int format)
{
String[] wdays = new String[7];
- GregorianCalendar day = new GregorianCalendar();
- day.set(GregorianCalendar.DAY_OF_WEEK, 0);
+ Calendar day = new GregorianCalendar();
+ day.set(GregorianCalendar.DAY_OF_WEEK, Calendar.SATURDAY);
- for (int i = 0; i < 7; i++)
+ for (int i = 0; i < wdays.length; i++)
{
wdays[i] = day.getDisplayName(GregorianCalendar.DAY_OF_WEEK, format,
Locale.getDefault());
@@ -172,6 +185,43 @@ public class DateHelper
return wdays;
}
+ /**
+ * @return array with weekday names starting according to locale settings,
+ * e.g. [Mo,Di,Mi,Do,Fr,Sa,So] in Europe
+ */
+ public static String[] getLocaleDayNames(int format)
+ {
+ String[] days = new String[7];
+
+ Calendar calendar = new GregorianCalendar();
+ calendar.set(GregorianCalendar.DAY_OF_WEEK, calendar.getFirstDayOfWeek());
+ for (int i = 0; i < days.length; i++)
+ {
+ days[i] = calendar.getDisplayName(GregorianCalendar.DAY_OF_WEEK, format,
+ Locale.getDefault());
+ calendar.add(GregorianCalendar.DAY_OF_MONTH, 1);
+ }
+
+ return days;
+ }
+
+ /**
+ * @return array with week days numbers starting according to locale settings,
+ * e.g. [2,3,4,5,6,7,1] in Europe
+ */
+ public static Integer[] getLocaleWeekdayList()
+ {
+ Integer[] dayNumbers = new Integer[7];
+ Calendar calendar = new GregorianCalendar();
+ calendar.set(GregorianCalendar.DAY_OF_WEEK, calendar.getFirstDayOfWeek());
+ for (int i = 0; i < dayNumbers.length; i++)
+ {
+ dayNumbers[i] = calendar.get(GregorianCalendar.DAY_OF_WEEK);
+ calendar.add(GregorianCalendar.DAY_OF_MONTH, 1);
+ }
+ return dayNumbers;
+ }
+
public static String formatWeekdayList(Context context, boolean weekday[])
{
String shortDayNames[] = getShortDayNames();
diff --git a/app/src/main/java/org/isoron/uhabits/helpers/ListHabitsHelper.java b/app/src/main/java/org/isoron/uhabits/helpers/ListHabitsHelper.java
index 0cd6567fd..91a3a4aff 100644
--- a/app/src/main/java/org/isoron/uhabits/helpers/ListHabitsHelper.java
+++ b/app/src/main/java/org/isoron/uhabits/helpers/ListHabitsHelper.java
@@ -20,13 +20,14 @@
package org.isoron.uhabits.helpers;
import android.content.Context;
-import android.graphics.Color;
-import android.graphics.Typeface;
-import android.util.DisplayMetrics;
+import android.content.SharedPreferences;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.preference.PreferenceManager;
import android.view.LayoutInflater;
+import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -34,43 +35,44 @@ import org.isoron.uhabits.R;
import org.isoron.uhabits.loaders.HabitListLoader;
import org.isoron.uhabits.models.Habit;
import org.isoron.uhabits.models.Score;
+import org.isoron.uhabits.views.RingView;
import java.util.GregorianCalendar;
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 static final int CHECKMARK_LEFT_TO_RIGHT = 0;
+ private static final int CHECKMARK_RIGHT_TO_LEFT = 1;
+
+ private final int lowContrastColor;
+ private final int mediumContrastColor;
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;
+ lowContrastColor = UIHelper.getStyledColor(context, R.attr.lowContrastTextColor);
+ mediumContrastColor = UIHelper.getStyledColor(context, R.attr.mediumContrastTextColor);
}
public int getButtonCount()
{
- DisplayMetrics dm = context.getResources().getDisplayMetrics();
- int width = (int) (dm.widthPixels / dm.density);
- return Math.max(0, (int) ((width - 160) / 42.0));
+ float screenWidth = UIHelper.getScreenWidth(context);
+ float labelWidth = context.getResources().getDimension(R.dimen.habitNameWidth);
+ float buttonWidth = context.getResources().getDimension(R.dimen.checkmarkWidth);
+ return Math.max(0, (int) ((screenWidth - labelWidth) / buttonWidth));
}
public int getHabitNameWidth()
{
- DisplayMetrics dm = context.getResources().getDisplayMetrics();
- int width = (int) (dm.widthPixels / dm.density);
- return (int) ((width - 30 - getButtonCount() * 42) * dm.density);
+ float screenWidth = UIHelper.getScreenWidth(context);
+ float buttonWidth = context.getResources().getDimension(R.dimen.checkmarkWidth);
+ float padding = UIHelper.dpToPixels(context, 15);
+ return (int) (screenWidth - padding - getButtonCount() * buttonWidth);
}
public void updateCheckmarkButtons(Habit habit, LinearLayout llButtons)
@@ -83,8 +85,12 @@ public class ListHabitsHelper
for (int i = 0; i < m; i++)
{
+ int position = i;
- TextView tvCheck = (TextView) llButtons.getChildAt(i);
+ if(getCheckmarkOrder() == CHECKMARK_RIGHT_TO_LEFT)
+ position = m - i - 1;
+
+ TextView tvCheck = (TextView) llButtons.getChildAt(position);
tvCheck.setTag(R.string.habit_key, habitId);
tvCheck.setTag(R.string.offset_key, i);
if(isChecked.length > i)
@@ -94,54 +100,32 @@ public class ListHabitsHelper
public int getActiveColor(Habit habit)
{
- int activeColor = habit.color;
- if(habit.isArchived()) activeColor = INACTIVE_COLOR;
+ int activeColor = ColorHelper.getColor(context, habit.color);
+ if(habit.isArchived()) activeColor = mediumContrastColor;
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)
+ public void updateNameAndIcon(Habit habit, RingView ring, TextView tvName)
{
int activeColor = getActiveColor(habit);
tvName.setText(habit.name);
tvName.setTextColor(activeColor);
- if (habit.isArchived())
- {
- tvStar.setText(context.getString(R.string.fa_archive));
- tvStar.setTextColor(activeColor);
- }
- else
- {
- int score = loader.scores.get(habit.getId());
+ int score = loader.scores.get(habit.getId());
+ float percentage = (float) score / Score.MAX_VALUE;
- if (score < Score.HALF_STAR_CUTOFF)
- {
- tvStar.setText(context.getString(R.string.fa_star_o));
- tvStar.setTextColor(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);
- }
- }
+ ring.setColor(activeColor);
+ ring.setPercentage(percentage);
+ ring.setPrecision(1.0f / 16);
}
public void updateCheckmark(int activeColor, TextView tvCheck, int check)
@@ -156,27 +140,64 @@ public class ListHabitsHelper
case 1:
tvCheck.setText(R.string.fa_check);
- tvCheck.setTextColor(INACTIVE_CHECKMARK_COLOR);
+ tvCheck.setTextColor(lowContrastColor);
tvCheck.setTag(R.string.toggle_key, 1);
break;
case 0:
tvCheck.setText(R.string.fa_times);
- tvCheck.setTextColor(INACTIVE_CHECKMARK_COLOR);
+ tvCheck.setTextColor(lowContrastColor);
tvCheck.setTag(R.string.toggle_key, 0);
break;
}
}
- public void updateHabitBackground(View view, boolean isSelected)
+ public View inflateHabitCard(LayoutInflater inflater,
+ View.OnLongClickListener onCheckmarkLongClickListener,
+ View.OnClickListener onCheckmarkClickListener)
+ {
+ View view = inflater.inflate(R.layout.list_habits_item, null);
+ initializeLabelAndIcon(view);
+ inflateCheckmarkButtons(view, onCheckmarkLongClickListener, onCheckmarkClickListener,
+ inflater);
+ return view;
+ }
+
+ public void updateHabitCard(View view, Habit habit, boolean selected)
{
- if (isSelected)
- view.setBackgroundResource(R.drawable.selected_box);
+ RingView scoreRing = ((RingView) view.findViewById(R.id.scoreRing));
+ TextView tvName = (TextView) view.findViewById(R.id.label);
+ LinearLayout llInner = (LinearLayout) view.findViewById(R.id.llInner);
+ LinearLayout llButtons = (LinearLayout) view.findViewById(R.id.llButtons);
+
+ llInner.setTag(R.string.habit_key, habit.getId());
+ llInner.setOnTouchListener(new HotspotTouchListener());
+
+ updateNameAndIcon(habit, scoreRing, tvName);
+ updateCheckmarkButtons(habit, llButtons);
+ updateHabitCardBackground(llInner, selected);
+ }
+
+
+ public void updateHabitCardBackground(View view, boolean isSelected)
+ {
+ if (android.os.Build.VERSION.SDK_INT >= 21)
+ {
+ if (isSelected)
+ view.setBackgroundResource(R.drawable.selected_box);
+ else
+ view.setBackgroundResource(R.drawable.ripple);
+ }
else
{
- if (android.os.Build.VERSION.SDK_INT >= 21)
- view.setBackgroundResource(R.drawable.ripple_white);
- else view.setBackgroundResource(R.drawable.card_background);
+ Drawable background;
+
+ if (isSelected)
+ background = UIHelper.getStyledDrawable(context, R.attr.selectedBackground);
+ else
+ background = UIHelper.getStyledDrawable(context, R.attr.cardBackground);
+
+ view.setBackgroundDrawable(background);
}
}
@@ -187,7 +208,7 @@ public class ListHabitsHelper
{
View check = inflater.inflate(R.layout.list_habits_item_check, null);
TextView btCheck = (TextView) check.findViewById(R.id.tvCheck);
- btCheck.setTypeface(fontawesome);
+ btCheck.setTypeface(UIHelper.getFontAwesome(context));
btCheck.setOnLongClickListener(onLongClickListener);
btCheck.setOnClickListener(onClickListener);
btCheck.setHapticFeedbackEnabled(false);
@@ -205,11 +226,15 @@ public class ListHabitsHelper
for (int i = 0; i < getButtonCount(); i++)
{
+ int position = 0;
+
+ if(getCheckmarkOrder() == CHECKMARK_LEFT_TO_RIGHT)
+ position = i;
+
View tvDay = inflater.inflate(R.layout.list_habits_header_check, null);
- Button btCheck = (Button) tvDay.findViewById(R.id.tvCheck);
+ TextView btCheck = (TextView) tvDay.findViewById(R.id.tvCheck);
btCheck.setText(DateHelper.formatHeaderDate(day));
- header.addView(tvDay);
-
+ header.addView(tvDay, position);
day.add(GregorianCalendar.DAY_OF_MONTH, -1);
}
}
@@ -222,9 +247,59 @@ public class ListHabitsHelper
public void toggleCheckmarkView(View v, Habit habit)
{
+ int androidColor = ColorHelper.getColor(context, habit.color);
+
if (v.getTag(R.string.toggle_key).equals(2))
- updateCheckmark(habit.color, (TextView) v, 0);
+ updateCheckmark(androidColor, (TextView) v, 0);
else
- updateCheckmark(habit.color, (TextView) v, 2);
+ updateCheckmark(androidColor, (TextView) v, 2);
+ }
+
+ public Long getHabitIdFromCheckmarkView(View v)
+ {
+ return (Long) v.getTag(R.string.habit_key);
+ }
+
+ public long getTimestampFromCheckmarkView(View v)
+ {
+ Integer offset = (Integer) v.getTag(R.string.offset_key);
+ return DateHelper.getStartOfDay(DateHelper.getLocalTime() -
+ offset * DateHelper.millisecondsInOneDay);
+ }
+
+ public void triggerRipple(View v, final float x, final float y)
+ {
+ final Drawable background = v.getBackground();
+ if (android.os.Build.VERSION.SDK_INT >= 21)
+ background.setHotspot(x, y);
+
+ background.setState(new int[]{android.R.attr.state_pressed, android.R.attr.state_enabled});
+
+ new Handler().postDelayed(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ background.setState(new int[]{});
+ }
+ }, 25);
+ }
+
+ private static class HotspotTouchListener implements View.OnTouchListener
+ {
+ @Override
+ public boolean onTouch(View v, MotionEvent event)
+ {
+ if (android.os.Build.VERSION.SDK_INT >= 21)
+ v.getBackground().setHotspot(event.getX(), event.getY());
+ return false;
+ }
+ }
+
+ public int getCheckmarkOrder()
+ {
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+ boolean reverse = prefs.getBoolean("pref_checkmark_reverse_order", false);
+ return reverse ? CHECKMARK_RIGHT_TO_LEFT : CHECKMARK_LEFT_TO_RIGHT;
}
}
diff --git a/app/src/main/java/org/isoron/uhabits/helpers/ReminderHelper.java b/app/src/main/java/org/isoron/uhabits/helpers/ReminderHelper.java
index 876c229fb..c575ffcf4 100644
--- a/app/src/main/java/org/isoron/uhabits/helpers/ReminderHelper.java
+++ b/app/src/main/java/org/isoron/uhabits/helpers/ReminderHelper.java
@@ -23,12 +23,19 @@ import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
+import android.content.SharedPreferences;
+import android.media.Ringtone;
+import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Build;
+import android.preference.PreferenceManager;
+import android.provider.Settings;
import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
import android.util.Log;
import org.isoron.uhabits.HabitBroadcastReceiver;
+import org.isoron.uhabits.R;
import org.isoron.uhabits.models.Habit;
import java.text.DateFormat;
@@ -89,4 +96,68 @@ public class ReminderHelper
Log.d("ReminderHelper", String.format("Setting alarm (%s): %s",
DateFormat.getDateTimeInstance().format(new Date(reminderTime)), habit.name));
}
+
+ @Nullable
+ public static Uri getRingtoneUri(Context context)
+ {
+ Uri ringtoneUri = null;
+ Uri defaultRingtoneUri = Settings.System.DEFAULT_NOTIFICATION_URI;
+
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+ String prefRingtoneUri = prefs.getString("pref_ringtone_uri", defaultRingtoneUri.toString());
+ if (prefRingtoneUri.length() > 0) ringtoneUri = Uri.parse(prefRingtoneUri);
+
+ return ringtoneUri;
+ }
+
+ public static void parseRingtoneData(Context context, @Nullable Intent data)
+ {
+ if(data == null) return;
+
+ Uri ringtoneUri = data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI);
+
+ if (ringtoneUri != null)
+ {
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+ prefs.edit().putString("pref_ringtone_uri", ringtoneUri.toString()).apply();
+ }
+ else
+ {
+ String off = context.getResources().getString(R.string.none);
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+ prefs.edit().putString("pref_ringtone_uri", "").apply();
+ }
+ }
+
+ public static void startRingtonePickerActivity(Fragment fragment, int requestCode)
+ {
+ Uri existingRingtoneUri = ReminderHelper.getRingtoneUri(fragment.getContext());
+ Uri defaultRingtoneUri = Settings.System.DEFAULT_NOTIFICATION_URI;
+
+ Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
+ intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, RingtoneManager.TYPE_NOTIFICATION);
+ intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true);
+ intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, true);
+ intent.putExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI, defaultRingtoneUri);
+ intent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, existingRingtoneUri);
+ fragment.startActivityForResult(intent, requestCode);
+ }
+
+ public static String getRingtoneName(Context context)
+ {
+ Uri ringtoneUri = getRingtoneUri(context);
+ String ringtoneName = context.getResources().getString(R.string.none);
+
+ if(ringtoneUri != null)
+ {
+ Ringtone ringtone = RingtoneManager.getRingtone(context, ringtoneUri);
+ if(ringtone != null)
+ {
+ ringtoneName = ringtone.getTitle(context);
+ ringtone.stop();
+ }
+ }
+
+ return ringtoneName;
+ }
}
diff --git a/app/src/main/java/org/isoron/uhabits/helpers/UIHelper.java b/app/src/main/java/org/isoron/uhabits/helpers/UIHelper.java
index d545e8a51..7e6c8c968 100644
--- a/app/src/main/java/org/isoron/uhabits/helpers/UIHelper.java
+++ b/app/src/main/java/org/isoron/uhabits/helpers/UIHelper.java
@@ -19,37 +19,58 @@
package org.isoron.uhabits.helpers;
+import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Resources;
+import android.content.res.TypedArray;
import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Debug;
import android.os.Looper;
import android.preference.PreferenceManager;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
-import android.util.Log;
import android.util.TypedValue;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import org.isoron.uhabits.BuildConfig;
+import org.isoron.uhabits.HabitsApplication;
+import org.isoron.uhabits.R;
import org.isoron.uhabits.commands.Command;
import java.util.Locale;
public abstract class UIHelper
{
-
public static final String ISORON_NAMESPACE = "http://isoron.org/android";
- private static Typeface fontawesome;
+
+ public static final int THEME_LIGHT = 0;
+ public static final int THEME_DARK = 1;
+
+ private static Typeface fontAwesome;
+ private static Integer fixedTheme;
+
+ public static void setFixedTheme(Integer fixedTheme)
+ {
+ UIHelper.fixedTheme = fixedTheme;
+ }
public interface OnSavedListener
{
void onSaved(Command command, Object savedObject);
}
+ public static Typeface getFontAwesome(Context context)
+ {
+ if(fontAwesome == null)
+ fontAwesome = Typeface.createFromAsset(context.getAssets(), "fontawesome-webfont.ttf");
+
+ return fontAwesome;
+ }
+
public static void showSoftKeyboard(View view)
{
InputMethodManager imm = (InputMethodManager) view.getContext()
@@ -87,6 +108,14 @@ public abstract class UIHelper
else return defaultValue;
}
+ public static Integer getColorAttribute(Context context, AttributeSet attrs, String name,
+ Integer defaultValue)
+ {
+ int resId = attrs.getAttributeResourceValue(ISORON_NAMESPACE, name, 0);
+ if (resId != 0) return context.getResources().getColor(resId);
+ else return defaultValue;
+ }
+
public static int getIntAttribute(Context context, AttributeSet attrs, String name,
int defaultValue)
{
@@ -95,6 +124,14 @@ public abstract class UIHelper
else return defaultValue;
}
+ public static boolean getBooleanAttribute(Context context, AttributeSet attrs, String name,
+ boolean defaultValue)
+ {
+ String boolText = getAttribute(context, attrs, name, null);
+ if(boolText != null) return Boolean.parseBoolean(boolText);
+ else return defaultValue;
+ }
+
public static float getFloatAttribute(Context context, AttributeSet attrs, String name,
float defaultValue)
{
@@ -153,16 +190,132 @@ public abstract class UIHelper
public static boolean isLocaleFullyTranslated()
{
// TODO: Move this to another place, or detect automatically
- String fullyTranslatedLanguages[] = { "en", "ar", "cs", "de", "it", "ja", "ko", "po", "pl",
- "pt", "ru", "sv", "zh", "es", "fr" };
+ String fullyTranslatedLanguages[] = { "ca", "zh", "en", "de", "in", "it", "ko", "pl", "pt",
+ "es", "tk", "uk"};
final String currentLanguage = Locale.getDefault().getLanguage();
- Log.d("UIHelper", String.format("lang=%s", currentLanguage));
-
for(String lang : fullyTranslatedLanguages)
if(currentLanguage.equals(lang)) return true;
return false;
}
+
+ public static float getScreenWidth(Context context)
+ {
+ return context.getResources().getDisplayMetrics().widthPixels;
+ }
+
+ public static int getStyledColor(Context context, int attrId)
+ {
+ TypedArray ta = getTypedArray(context, attrId);
+ int color = ta.getColor(0, 0);
+ ta.recycle();
+
+ return color;
+ }
+
+ private static TypedArray getTypedArray(Context context, int attrId)
+ {
+ int[] attrs = new int[]{ attrId };
+ if(fixedTheme != null)
+ return context.getTheme().obtainStyledAttributes(fixedTheme, attrs);
+ else
+ return context.obtainStyledAttributes(attrs);
+ }
+
+ public static Drawable getStyledDrawable(Context context, int attrId)
+ {
+ TypedArray ta = getTypedArray(context, attrId);
+ Drawable drawable = ta.getDrawable(0);
+ ta.recycle();
+
+ return drawable;
+ }
+
+ public static boolean getStyledBoolean(Context context, int attrId)
+ {
+ TypedArray ta = getTypedArray(context, attrId);
+ boolean bool = ta.getBoolean(0, false);
+ ta.recycle();
+
+ return bool;
+ }
+
+ public static float getStyledFloat(Context context, int attrId)
+ {
+ TypedArray ta = getTypedArray(context, attrId);
+ float f = ta.getFloat(0, 0);
+ ta.recycle();
+
+ return f;
+ }
+
+ static int getStyleResource(Context context, int attrId)
+ {
+ TypedArray ta = getTypedArray(context, attrId);
+ int resourceId = ta.getResourceId(0, -1);
+ ta.recycle();
+
+ return resourceId;
+ }
+
+ public static void applyCurrentTheme(Activity activity)
+ {
+ switch(getCurrentTheme())
+ {
+ case THEME_DARK:
+ {
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity);
+ boolean pureBlackEnabled = prefs.getBoolean("pref_pure_black", false);
+
+ if(pureBlackEnabled)
+ activity.setTheme(R.style.AppBaseThemeDark_PureBlack);
+ else
+ activity.setTheme(R.style.AppBaseThemeDark);
+
+ break;
+ }
+
+ case THEME_LIGHT:
+ default:
+ activity.setTheme(R.style.AppBaseTheme);
+ break;
+ }
+ }
+
+ private static int getCurrentTheme()
+ {
+ Context appContext = HabitsApplication.getContext();
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(appContext);
+ return prefs.getInt("pref_theme", THEME_LIGHT);
+ }
+
+ public static void setCurrentTheme(int theme)
+ {
+ Context appContext = HabitsApplication.getContext();
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(appContext);
+ prefs.edit().putInt("pref_theme", theme).apply();
+ }
+
+ public static boolean isNightMode()
+ {
+ return getCurrentTheme() == THEME_DARK;
+ }
+
+
+ public static void setDefaultScoreInterval(Context context, int position)
+ {
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+ prefs.edit().putInt("pref_score_view_interval", position).apply();
+ }
+
+ public static int getDefaultScoreInterval(Context context)
+ {
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+ int defaultScoreInterval = prefs.getInt("pref_score_view_interval", 1);
+ if(defaultScoreInterval > 5 || defaultScoreInterval < 0) defaultScoreInterval = 1;
+
+ return defaultScoreInterval;
+ }
}
diff --git a/app/src/main/java/org/isoron/uhabits/models/Habit.java b/app/src/main/java/org/isoron/uhabits/models/Habit.java
index a3c51e3fe..1fdb982ad 100644
--- a/app/src/main/java/org/isoron/uhabits/models/Habit.java
+++ b/app/src/main/java/org/isoron/uhabits/models/Habit.java
@@ -71,7 +71,11 @@ public class Habit extends Model
public Integer freqDen;
/**
- * Color of the habit. The format is the same as android.graphics.Color.
+ * Color of the habit.
+ *
+ * This number is not an android.graphics.Color, but an index to the activity color palette,
+ * which changes according to the theme. To convert this color into an android.graphics.Color,
+ * use ColorHelper.getColor(context, habit.color).
*/
@Column(name = "color")
public Integer color;
@@ -166,7 +170,7 @@ public class Habit extends Model
*/
public Habit()
{
- this.color = ColorHelper.palette[5];
+ this.color = 5;
this.position = Habit.countWithArchived();
this.highlight = 0;
this.archived = 0;
@@ -492,8 +496,15 @@ public class Habit extends Model
for(Habit habit : habits)
{
- String[] cols = { habit.name, habit.description, Integer.toString(habit.freqNum),
- Integer.toString(habit.freqDen), ColorHelper.toHTML(habit.color) };
+ String[] cols =
+ {
+ habit.name,
+ habit.description,
+ Integer.toString(habit.freqNum),
+ Integer.toString(habit.freqDen),
+ ColorHelper.toHTML(ColorHelper.CSV_PALETTE[habit.color])
+ };
+
csv.writeNext(cols, false);
}
diff --git a/app/src/main/java/org/isoron/uhabits/views/CheckmarkView.java b/app/src/main/java/org/isoron/uhabits/views/CheckmarkView.java
deleted file mode 100644
index 8819e8e37..000000000
--- a/app/src/main/java/org/isoron/uhabits/views/CheckmarkView.java
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * Copyright (C) 2016 Álinson Santos Xavier
- *
- * 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 .
- */
-
-package org.isoron.uhabits.views;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.graphics.Typeface;
-import android.os.Build;
-import android.text.Layout;
-import android.text.StaticLayout;
-import android.text.TextPaint;
-import android.util.AttributeSet;
-import android.view.View;
-
-import org.isoron.uhabits.R;
-import org.isoron.uhabits.helpers.ColorHelper;
-import org.isoron.uhabits.models.Habit;
-
-public class CheckmarkView extends View implements HabitDataView
-{
- private Paint pCard;
- private Paint pIcon;
-
- private int primaryColor;
- private int timesColor;
- private int darkGrey;
-
- private int width;
- private int height;
- private float leftMargin;
- private float topMargin;
- private float padding;
- private String label;
-
- private String fa_check;
- private String fa_times;
-
- private int check_status;
-
- private Rect rect;
- private TextPaint textPaint;
- private StaticLayout labelLayout;
- private Habit habit;
-
- public CheckmarkView(Context context)
- {
- super(context);
- init(context);
- }
-
- public CheckmarkView(Context context, AttributeSet attrs)
- {
- super(context, attrs);
- init(context);
- }
-
- private void init(Context context)
- {
- Typeface fontawesome =
- Typeface.createFromAsset(context.getAssets(), "fontawesome-webfont.ttf");
-
- pCard = new Paint();
- pCard.setAntiAlias(true);
-
- pIcon = new Paint();
- pIcon.setAntiAlias(true);
- pIcon.setTypeface(fontawesome);
- pIcon.setTextAlign(Paint.Align.CENTER);
-
- textPaint = new TextPaint();
- textPaint.setColor(Color.WHITE);
- textPaint.setAntiAlias(true);
-
- fa_check = context.getString(R.string.fa_check);
- fa_times = context.getString(R.string.fa_times);
-
- primaryColor = ColorHelper.palette[10];
- timesColor = Color.argb(128, 255, 255, 255);
- darkGrey = Color.argb(64, 0, 0, 0);
-
- rect = new Rect();
- check_status = 0;
- label = "Habit";
- }
-
- public void setHabit(Habit habit)
- {
- this.habit = habit;
- }
-
- @Override
- protected void onDraw(Canvas canvas)
- {
- super.onDraw(canvas);
-
- drawBackground(canvas);
- drawCheckmark(canvas);
- drawLabel(canvas);
- }
-
- private void drawBackground(Canvas canvas)
- {
- int color = (check_status == 2 ? primaryColor : darkGrey);
-
- pCard.setColor(color);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
- canvas.drawRoundRect(leftMargin, topMargin, width - leftMargin, height - topMargin, padding,
- padding, pCard);
- else
- canvas.drawRect(leftMargin, topMargin, width - leftMargin, height - topMargin, pCard);
- }
-
- private void drawCheckmark(Canvas canvas)
- {
- String text = (check_status == 0 ? fa_times : fa_check);
- int color = (check_status == 2 ? Color.WHITE : timesColor);
-
- pIcon.setColor(color);
- pIcon.setTextSize(width * 0.5f);
- pIcon.getTextBounds(text, 0, 1, rect);
-
- int y = (int) ((0.67f * height - rect.bottom - rect.top) / 2);
- canvas.drawText(text, width / 2, y, pIcon);
- }
-
- private void drawLabel(Canvas canvas)
- {
- canvas.save();
- float y;
- int nLines = labelLayout.getLineCount();
-
- if(nLines == 1)
- y = height * 0.8f - padding;
- else
- y = height * 0.7f - padding;
-
- canvas.translate(leftMargin + padding, y);
-
- labelLayout.draw(canvas);
- canvas.restore();
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
- {
- int width = MeasureSpec.getSize(widthMeasureSpec);
- setMeasuredDimension(width, (int) (width * 1.25));
- }
-
- @Override
- protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight)
- {
- this.width = getMeasuredWidth();
- this.height = getMeasuredHeight();
-
- leftMargin = (width * 0.015f);
- topMargin = (height * 0.015f);
- padding = 8 * leftMargin;
- textPaint.setTextSize(0.15f * width);
-
- updateLabel();
- }
-
- public void refreshData()
- {
- this.check_status = habit.checkmarks.getTodayValue();
- this.primaryColor = Color.argb(230, Color.red(habit.color), Color.green(habit.color),
- Color.blue(habit.color));
- this.label = habit.name;
-
- updateLabel();
- postInvalidate();
- }
-
- private void updateLabel()
- {
- textPaint.setColor(Color.WHITE);
- labelLayout = new StaticLayout(label, textPaint,
- (int) (width - 2 * leftMargin - 2 * padding),
- Layout.Alignment.ALIGN_CENTER, 1.0f, 0.0f, false);
- }
-}
diff --git a/app/src/main/java/org/isoron/uhabits/views/CheckmarkWidgetView.java b/app/src/main/java/org/isoron/uhabits/views/CheckmarkWidgetView.java
new file mode 100644
index 000000000..01cf98a19
--- /dev/null
+++ b/app/src/main/java/org/isoron/uhabits/views/CheckmarkWidgetView.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2016 Álinson Santos Xavier
+ *
+ * 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 .
+ */
+
+package org.isoron.uhabits.views;
+
+import android.content.Context;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.util.AttributeSet;
+import android.widget.TextView;
+
+import org.isoron.uhabits.R;
+import org.isoron.uhabits.helpers.ColorHelper;
+import org.isoron.uhabits.helpers.UIHelper;
+import org.isoron.uhabits.models.Checkmark;
+import org.isoron.uhabits.models.Habit;
+import org.isoron.uhabits.models.Score;
+
+public class CheckmarkWidgetView extends HabitWidgetView implements HabitDataView
+{
+ private int activeColor;
+ private float percentage;
+
+ @Nullable
+ private String name;
+
+ @Nullable
+ private RingView ring;
+ private TextView label;
+ private int checkmarkValue;
+
+ public CheckmarkWidgetView(Context context)
+ {
+ super(context);
+ init();
+ }
+
+ public CheckmarkWidgetView(Context context, AttributeSet attrs)
+ {
+ super(context, attrs);
+ init();
+ }
+
+ private void init()
+ {
+ ring = (RingView) findViewById(R.id.scoreRing);
+ label = (TextView) findViewById(R.id.label);
+
+ if(ring != null) ring.setIsTransparencyEnabled(true);
+
+ if(isInEditMode())
+ {
+ percentage = 0.75f;
+ name = "Wake up early";
+ activeColor = ColorHelper.CSV_PALETTE[6];
+ checkmarkValue = Checkmark.CHECKED_EXPLICITLY;
+ refresh();
+ }
+ }
+
+ @Override
+ public void setHabit(@NonNull Habit habit)
+ {
+ super.setHabit(habit);
+ this.name = habit.name;
+ this.activeColor = ColorHelper.getColor(getContext(), habit.color);
+ refresh();
+ }
+
+ public void refresh()
+ {
+ if (backgroundPaint == null || frame == null || ring == null) return;
+
+ Context context = getContext();
+
+ String text;
+ int backgroundColor;
+ int foregroundColor;
+
+ switch (checkmarkValue)
+ {
+ case Checkmark.CHECKED_EXPLICITLY:
+ text = getResources().getString(R.string.fa_check);
+ backgroundColor = activeColor;
+ foregroundColor =
+ UIHelper.getStyledColor(context, R.attr.highContrastReverseTextColor);
+
+ setShadowAlpha(0x4f);
+ rebuildBackground();
+
+ backgroundPaint.setColor(backgroundColor);
+ frame.setBackgroundDrawable(background);
+ break;
+
+ case Checkmark.CHECKED_IMPLICITLY:
+ text = getResources().getString(R.string.fa_check);
+ backgroundColor = UIHelper.getStyledColor(context, R.attr.cardBackgroundColor);
+ foregroundColor = UIHelper.getStyledColor(context, R.attr.mediumContrastTextColor);
+ break;
+
+ case Checkmark.UNCHECKED:
+ default:
+ text = getResources().getString(R.string.fa_times);
+ backgroundColor = UIHelper.getStyledColor(context, R.attr.cardBackgroundColor);
+ foregroundColor = UIHelper.getStyledColor(context, R.attr.mediumContrastTextColor);
+ break;
+ }
+
+ ring.setPercentage(percentage);
+ ring.setColor(foregroundColor);
+ ring.setBackgroundColor(backgroundColor);
+ ring.setText(text);
+
+ label.setText(name);
+ label.setTextColor(foregroundColor);
+
+ requestLayout();
+ postInvalidate();
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
+ {
+ int width = MeasureSpec.getSize(widthMeasureSpec);
+ int height = MeasureSpec.getSize(heightMeasureSpec);
+
+ float w = width;
+ float h = width * 1.25f;
+ float scale = Math.min(width / w, height / h);
+
+ w *= scale;
+ h *= scale;
+
+ widthMeasureSpec = MeasureSpec.makeMeasureSpec((int) w, MeasureSpec.EXACTLY);
+ heightMeasureSpec = MeasureSpec.makeMeasureSpec((int) h, MeasureSpec.EXACTLY);
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ @Override
+ public void refreshData()
+ {
+ if(habit == null) return;
+ this.percentage = (float) habit.scores.getTodayValue() / Score.MAX_VALUE;
+ this.checkmarkValue = habit.checkmarks.getTodayValue();
+ refresh();
+ }
+
+ @NonNull
+ protected Integer getInnerLayoutId()
+ {
+ return R.layout.widget_checkmark;
+ }
+}
diff --git a/app/src/main/java/org/isoron/uhabits/views/GraphWidgetView.java b/app/src/main/java/org/isoron/uhabits/views/GraphWidgetView.java
new file mode 100644
index 000000000..bb142ca7d
--- /dev/null
+++ b/app/src/main/java/org/isoron/uhabits/views/GraphWidgetView.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2016 Álinson Santos Xavier
+ *
+ * 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 .
+ */
+
+package org.isoron.uhabits.views;
+
+import android.content.Context;
+import android.support.annotation.NonNull;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import org.isoron.uhabits.R;
+import org.isoron.uhabits.models.Habit;
+
+public class GraphWidgetView extends HabitWidgetView implements HabitDataView
+{
+
+ private final HabitDataView dataView;
+ private TextView title;
+
+ public GraphWidgetView(Context context, HabitDataView dataView)
+ {
+ super(context);
+ this.dataView = dataView;
+ init();
+ }
+
+ private void init()
+ {
+ ViewGroup.LayoutParams params =
+ new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT);
+ ((View) dataView).setLayoutParams(params);
+
+ ViewGroup innerFrame = (ViewGroup) findViewById(R.id.innerFrame);
+ innerFrame.addView(((View) dataView));
+
+ title = (TextView) findViewById(R.id.title);
+ title.setVisibility(VISIBLE);
+ }
+
+ @Override
+ public void setHabit(@NonNull Habit habit)
+ {
+ super.setHabit(habit);
+ dataView.setHabit(habit);
+ title.setText(habit.name);
+ }
+
+ @Override
+ public void refreshData()
+ {
+ if(habit == null) return;
+ dataView.refreshData();
+ }
+
+ @NonNull
+ protected Integer getInnerLayoutId()
+ {
+ return R.layout.widget_graph;
+ }
+}
diff --git a/app/src/main/java/org/isoron/uhabits/views/HabitFrequencyView.java b/app/src/main/java/org/isoron/uhabits/views/HabitFrequencyView.java
index 55abb3959..7890757dd 100644
--- a/app/src/main/java/org/isoron/uhabits/views/HabitFrequencyView.java
+++ b/app/src/main/java/org/isoron/uhabits/views/HabitFrequencyView.java
@@ -21,13 +21,14 @@ 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.uhabits.R;
import org.isoron.uhabits.helpers.ColorHelper;
import org.isoron.uhabits.helpers.DateHelper;
+import org.isoron.uhabits.helpers.UIHelper;
import org.isoron.uhabits.models.Habit;
import java.text.SimpleDateFormat;
@@ -56,13 +57,12 @@ public class HabitFrequencyView extends ScrollableDataView implements HabitDataV
private int nColumns;
private int textColor;
- private int dimmedTextColor;
+ private int gridColor;
private int[] colors;
private int primaryColor;
private boolean isBackgroundTransparent;
private HashMap frequency;
- private String wdays[];
public HabitFrequencyView(Context context)
{
@@ -73,7 +73,7 @@ public class HabitFrequencyView extends ScrollableDataView implements HabitDataV
public HabitFrequencyView(Context context, AttributeSet attrs)
{
super(context, attrs);
- this.primaryColor = ColorHelper.palette[7];
+ this.primaryColor = ColorHelper.getColor(getContext(), 7);
this.frequency = new HashMap<>();
init();
}
@@ -89,8 +89,6 @@ public class HabitFrequencyView extends ScrollableDataView implements HabitDataV
createPaints();
createColors();
- wdays = DateHelper.getShortDayNames();
-
dfMonth = DateHelper.getDateFormat("MMM");
dfYear = DateHelper.getDateFormat("yyyy");
@@ -101,25 +99,15 @@ public class HabitFrequencyView extends ScrollableDataView implements HabitDataV
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);
+ this.primaryColor = ColorHelper.getColor(getContext(), habit.color);
}
- colors = new int[4];
+ textColor = UIHelper.getStyledColor(getContext(), R.attr.mediumContrastTextColor);
+ gridColor = UIHelper.getStyledColor(getContext(), R.attr.lowContrastTextColor);
- colors[0] = Color.rgb(230, 230, 230);
+ colors = new int[4];
+ colors[0] = gridColor;
colors[3] = primaryColor;
colors[1] = ColorHelper.mixColors(colors[0], colors[3], 0.66f);
colors[2] = ColorHelper.mixColors(colors[0], colors[3], 0.33f);
@@ -247,11 +235,13 @@ public class HabitFrequencyView extends ScrollableDataView implements HabitDataV
float rowHeight = rect.height() / 8.0f;
prevRect.set(rect);
- for (int i = 0; i < 7; i++)
+ Integer[] localeWeekdayList = DateHelper.getLocaleWeekdayList();
+ for (int j = 0; j < localeWeekdayList.length; j++)
{
rect.set(0, 0, baseSize, baseSize);
- rect.offset(prevRect.left, prevRect.top + baseSize * i);
+ rect.offset(prevRect.left, prevRect.top + baseSize * j);
+ int i = DateHelper.javaWeekdayToLoopWeekday(localeWeekdayList[j]);
if(values != null)
drawMarker(canvas, rect, values[i]);
@@ -287,11 +277,10 @@ public class HabitFrequencyView extends ScrollableDataView implements HabitDataV
pText.setTextAlign(Paint.Align.LEFT);
pText.setColor(textColor);
- pGrid.setColor(dimmedTextColor);
+ pGrid.setColor(gridColor);
- for (int i = 0; i < nRows; i++)
- {
- canvas.drawText(wdays[i], rGrid.right - columnWidth,
+ for (String day : DateHelper.getLocaleDayNames(Calendar.SHORT)) {
+ canvas.drawText(day, rGrid.right - columnWidth,
rGrid.top + rowHeight / 2 + 0.25f * em, pText);
pGrid.setStrokeWidth(1f);
diff --git a/app/src/main/java/org/isoron/uhabits/views/HabitHistoryView.java b/app/src/main/java/org/isoron/uhabits/views/HabitHistoryView.java
index 0958bf275..96899585e 100644
--- a/app/src/main/java/org/isoron/uhabits/views/HabitHistoryView.java
+++ b/app/src/main/java/org/isoron/uhabits/views/HabitHistoryView.java
@@ -57,19 +57,20 @@ public class HabitHistoryView extends ScrollableDataView implements HabitDataVie
private float columnHeight;
private int nColumns;
- private String wdays[];
private SimpleDateFormat dfMonth;
private SimpleDateFormat dfYear;
private Calendar baseDate;
private int nDays;
- private int todayWeekday;
+ /** 0-based-position of today in the column */
+ private int todayPositionInColumn;
private int colors[];
private RectF baseLocation;
private int primaryColor;
private boolean isBackgroundTransparent;
private int textColor;
+ private int reverseTextColor;
private boolean isEditable;
public HabitHistoryView(Context context)
@@ -92,13 +93,12 @@ public class HabitHistoryView extends ScrollableDataView implements HabitDataVie
private void init()
{
- createPaints();
createColors();
+ createPaints();
isEditable = false;
checkmarks = new int[0];
- primaryColor = ColorHelper.palette[7];
- wdays = DateHelper.getShortDayNames();
+ primaryColor = ColorHelper.getColor(getContext(), 7);
dfMonth = DateHelper.getDateFormat("MMM");
dfYear = DateHelper.getDateFormat("yyyy");
@@ -111,10 +111,11 @@ public class HabitHistoryView extends ScrollableDataView implements HabitDataVie
baseDate.add(Calendar.DAY_OF_YEAR, -(getDataOffset() - 1) * 7);
nDays = (nColumns - 1) * 7;
- todayWeekday = DateHelper.getStartOfTodayCalendar().get(Calendar.DAY_OF_WEEK) % 7;
+ int realWeekday = DateHelper.getStartOfTodayCalendar().get(Calendar.DAY_OF_WEEK);
+ todayPositionInColumn = (7 + realWeekday - baseDate.getFirstDayOfWeek()) % 7;
baseDate.add(Calendar.DAY_OF_YEAR, -nDays);
- baseDate.add(Calendar.DAY_OF_YEAR, -todayWeekday);
+ baseDate.add(Calendar.DAY_OF_YEAR, -todayPositionInColumn);
}
@Override
@@ -133,8 +134,9 @@ public class HabitHistoryView extends ScrollableDataView implements HabitDataVie
setScrollerBucketSize((int) baseSize);
squareSpacing = UIHelper.dpToPixels(getContext(), 1.0f);
- float maxTextSize = getResources().getDimensionPixelSize(R.dimen.regularTextSize);
- float textSize = Math.min(baseSize * 0.5f, maxTextSize);
+ float maxTextSize = getResources().getDimension(R.dimen.regularTextSize);
+ float textSize = height * 0.06f;
+ textSize = Math.min(textSize, maxTextSize);
pSquareFg.setTextSize(textSize);
pTextHeader.setTextSize(textSize);
@@ -155,7 +157,7 @@ public class HabitHistoryView extends ScrollableDataView implements HabitDataVie
{
float width = 0;
- for(String w : wdays)
+ for(String w : DateHelper.getLocaleDayNames(Calendar.SHORT))
width = Math.max(width, pSquareFg.measureText(w));
return width;
@@ -164,7 +166,7 @@ public class HabitHistoryView extends ScrollableDataView implements HabitDataVie
private void createColors()
{
if(habit != null)
- this.primaryColor = habit.color;
+ this.primaryColor = ColorHelper.getColor(getContext(), habit.color);
if(isBackgroundTransparent)
primaryColor = ColorHelper.setMinValue(primaryColor, 0.75f);
@@ -179,15 +181,17 @@ public class HabitHistoryView extends ScrollableDataView implements HabitDataVie
colors[0] = Color.argb(16, 255, 255, 255);
colors[1] = Color.argb(128, red, green, blue);
colors[2] = primaryColor;
- textColor = Color.rgb(255, 255, 255);
+ textColor = Color.WHITE;
+ reverseTextColor = Color.WHITE;
}
else
{
colors = new int[3];
- colors[0] = Color.argb(25, 0, 0, 0);
+ colors[0] = UIHelper.getStyledColor(getContext(), R.attr.lowContrastTextColor);
colors[1] = Color.argb(127, red, green, blue);
colors[2] = primaryColor;
- textColor = Color.argb(64, 0, 0, 0);
+ textColor = UIHelper.getStyledColor(getContext(), R.attr.mediumContrastTextColor);
+ reverseTextColor = UIHelper.getStyledColor(getContext(), R.attr.highContrastReverseTextColor);
}
}
@@ -198,10 +202,8 @@ public class HabitHistoryView extends ScrollableDataView implements HabitDataVie
pTextHeader.setAntiAlias(true);
pSquareBg = new Paint();
- pSquareBg.setColor(primaryColor);
pSquareFg = new Paint();
- pSquareFg.setColor(Color.WHITE);
pSquareFg.setAntiAlias(true);
pSquareFg.setTextAlign(Align.CENTER);
}
@@ -274,9 +276,10 @@ public class HabitHistoryView extends ScrollableDataView implements HabitDataVie
for (int j = 0; j < 7; j++)
{
- if (!(column == nColumns - 2 && getDataOffset() == 0 && j > todayWeekday))
+ if (!(column == nColumns - 2 && getDataOffset() == 0 && j > todayPositionInColumn))
{
- int checkmarkOffset = getDataOffset() * 7 + nDays - 7 * (column + 1) + todayWeekday - j;
+ int checkmarkOffset = getDataOffset() * 7 + nDays - 7 * (column + 1) +
+ todayPositionInColumn - j;
drawSquare(canvas, location, date, checkmarkOffset);
}
@@ -291,6 +294,7 @@ public class HabitHistoryView extends ScrollableDataView implements HabitDataVie
if (checkmarkOffset >= checkmarks.length) pSquareBg.setColor(colors[0]);
else pSquareBg.setColor(colors[checkmarks[checkmarkOffset]]);
+ pSquareFg.setColor(reverseTextColor);
canvas.drawRect(location, pSquareBg);
String text = Integer.toString(date.get(Calendar.DAY_OF_MONTH));
canvas.drawText(text, location.centerX(), location.centerY() + squareTextOffset, pSquareFg);
@@ -298,11 +302,13 @@ public class HabitHistoryView extends ScrollableDataView implements HabitDataVie
private void drawAxis(Canvas canvas, RectF location)
{
- for (int i = 0; i < 7; i++)
+ float verticalOffset = pTextHeader.getFontSpacing() * 0.4f;
+
+ for (String day : DateHelper.getLocaleDayNames(Calendar.SHORT))
{
location.offset(0, columnWidth);
- canvas.drawText(wdays[i], location.left + headerTextOffset,
- location.bottom - headerTextOffset, pTextHeader);
+ canvas.drawText(day, location.left + headerTextOffset,
+ location.centerY() + verticalOffset, pTextHeader);
}
}
diff --git a/app/src/main/java/org/isoron/uhabits/views/HabitScoreView.java b/app/src/main/java/org/isoron/uhabits/views/HabitScoreView.java
index 4e1ae4c7c..6258e7b13 100644
--- a/app/src/main/java/org/isoron/uhabits/views/HabitScoreView.java
+++ b/app/src/main/java/org/isoron/uhabits/views/HabitScoreView.java
@@ -20,6 +20,7 @@
package org.isoron.uhabits.views;
import android.content.Context;
+import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
@@ -32,6 +33,7 @@ import android.util.AttributeSet;
import org.isoron.uhabits.R;
import org.isoron.uhabits.helpers.ColorHelper;
import org.isoron.uhabits.helpers.DateHelper;
+import org.isoron.uhabits.helpers.UIHelper;
import org.isoron.uhabits.models.Habit;
import org.isoron.uhabits.models.Score;
@@ -47,6 +49,8 @@ public class HabitScoreView extends ScrollableDataView implements HabitDataView
public static final PorterDuffXfermode XFERMODE_SRC =
new PorterDuffXfermode(PorterDuff.Mode.SRC);
+ public static int DEFAULT_BUCKET_SIZES[] = { 1, 7, 31, 92, 365 };
+
private Paint pGrid;
private float em;
private Habit habit;
@@ -65,15 +69,19 @@ public class HabitScoreView extends ScrollableDataView implements HabitDataView
private int nColumns;
private int textColor;
- private int dimmedTextColor;
+ private int gridColor;
@Nullable
private int[] scores;
private int primaryColor;
- private boolean isBackgroundTransparent;
private int bucketSize = 7;
private int footerHeight;
+ private int backgroundColor;
+
+ private Bitmap drawingCache;
+ private Canvas cacheCanvas;
+ private boolean isTransparencyEnabled;
public HabitScoreView(Context context)
{
@@ -84,7 +92,7 @@ public class HabitScoreView extends ScrollableDataView implements HabitDataView
public HabitScoreView(Context context, AttributeSet attrs)
{
super(context, attrs);
- this.primaryColor = ColorHelper.palette[7];
+ this.primaryColor = ColorHelper.getColor(getContext(), 7);
init();
}
@@ -110,21 +118,11 @@ public class HabitScoreView extends ScrollableDataView implements HabitDataView
private void createColors()
{
if(habit != null)
- this.primaryColor = habit.color;
-
- if (isBackgroundTransparent)
- {
- primaryColor = ColorHelper.setSaturation(primaryColor, 0.75f);
- primaryColor = ColorHelper.setValue(primaryColor, 1.0f);
+ this.primaryColor = ColorHelper.getColor(getContext(), habit.color);
- 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);
- }
+ textColor = UIHelper.getStyledColor(getContext(), R.attr.mediumContrastTextColor);
+ gridColor = UIHelper.getStyledColor(getContext(), R.attr.lowContrastTextColor);
+ backgroundColor = UIHelper.getStyledColor(getContext(), R.attr.cardBackgroundColor);
}
protected void createPaints()
@@ -153,8 +151,9 @@ public class HabitScoreView extends ScrollableDataView implements HabitDataView
{
if(height < 9) height = 200;
- int maxTextSize = getResources().getDimensionPixelSize(R.dimen.regularTextSize);
- pText.setTextSize(Math.min(height * 0.047f, maxTextSize));
+ float maxTextSize = getResources().getDimension(R.dimen.tinyTextSize);
+ float textSize = height * 0.06f;
+ pText.setTextSize(Math.min(textSize, maxTextSize));
em = pText.getFontSpacing();
footerHeight = (int)(3 * em);
@@ -167,12 +166,25 @@ public class HabitScoreView extends ScrollableDataView implements HabitDataView
columnWidth = Math.max(columnWidth, getMaxDayWidth() * 1.5f);
columnWidth = Math.max(columnWidth, getMaxMonthWidth() * 1.2f);
- columnHeight = 8 * baseSize;
nColumns = (int) (width / columnWidth);
+ columnWidth = (float) width / nColumns;
+ columnHeight = 8 * baseSize;
+
+ float minStrokeWidth = UIHelper.dpToPixels(getContext(), 1);
pGraph.setTextSize(baseSize * 0.5f);
pGraph.setStrokeWidth(baseSize * 0.1f);
- pGrid.setStrokeWidth(baseSize * 0.05f);
+ pGrid.setStrokeWidth(Math.min(minStrokeWidth, baseSize * 0.05f));
+
+ if(isTransparencyEnabled)
+ initCache(width, height);
+ }
+
+ private void initCache(int width, int height)
+ {
+ if (drawingCache != null) drawingCache.recycle();
+ drawingCache = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ cacheCanvas = new Canvas(drawingCache);
}
public void refreshData()
@@ -211,12 +223,26 @@ public class HabitScoreView extends ScrollableDataView implements HabitDataView
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
+ Canvas activeCanvas;
+
+ if(isTransparencyEnabled)
+ {
+ if(drawingCache == null) initCache(getWidth(), getHeight());
+
+ activeCanvas = cacheCanvas;
+ drawingCache.eraseColor(Color.TRANSPARENT);
+ }
+ else
+ {
+ activeCanvas = canvas;
+ }
+
if (habit == null || scores == null) return;
rect.set(0, 0, nColumns * columnWidth, columnHeight);
rect.offset(0, paddingTop);
- drawGrid(canvas, rect);
+ drawGrid(activeCanvas, rect);
pText.setColor(textColor);
pGraph.setColor(primaryColor);
@@ -241,24 +267,28 @@ public class HabitScoreView extends ScrollableDataView implements HabitDataView
int height = (int) (columnHeight * relativeScore);
rect.set(0, 0, baseSize, baseSize);
- rect.offset(k * columnWidth, paddingTop + columnHeight - height - columnWidth / 2);
+ rect.offset(k * columnWidth + (columnWidth - baseSize) / 2,
+ paddingTop + columnHeight - height - baseSize / 2);
if (!prevRect.isEmpty())
{
- drawLine(canvas, prevRect, rect);
- drawMarker(canvas, prevRect);
+ drawLine(activeCanvas, prevRect, rect);
+ drawMarker(activeCanvas, prevRect);
}
- if (k == nColumns - 1) drawMarker(canvas, rect);
+ if (k == nColumns - 1) drawMarker(activeCanvas, rect);
prevRect.set(rect);
rect.set(0, 0, columnWidth, columnHeight);
rect.offset(k * columnWidth, paddingTop);
- drawFooter(canvas, rect, currentDate);
+ drawFooter(activeCanvas, rect, currentDate);
currentDate += bucketSize * DateHelper.millisecondsInOneDay;
}
+
+ if(activeCanvas != canvas)
+ canvas.drawBitmap(drawingCache, 0, 0, null);
}
private int skipYear = 0;
@@ -322,7 +352,7 @@ public class HabitScoreView extends ScrollableDataView implements HabitDataView
pText.setTextAlign(Paint.Align.LEFT);
pText.setColor(textColor);
- pGrid.setColor(dimmedTextColor);
+ pGrid.setColor(gridColor);
for (int i = 0; i < nRows; i++)
{
@@ -345,7 +375,7 @@ public class HabitScoreView extends ScrollableDataView implements HabitDataView
private void drawMarker(Canvas canvas, RectF rect)
{
rect.inset(baseSize * 0.15f, baseSize * 0.15f);
- setModeOrColor(pGraph, XFERMODE_CLEAR, Color.WHITE);
+ setModeOrColor(pGraph, XFERMODE_CLEAR, backgroundColor);
canvas.drawOval(rect, pGraph);
rect.inset(baseSize * 0.1f, baseSize * 0.1f);
@@ -353,22 +383,23 @@ public class HabitScoreView extends ScrollableDataView implements HabitDataView
canvas.drawOval(rect, pGraph);
rect.inset(baseSize * 0.1f, baseSize * 0.1f);
- setModeOrColor(pGraph, XFERMODE_CLEAR, Color.WHITE);
+ setModeOrColor(pGraph, XFERMODE_CLEAR, backgroundColor);
canvas.drawOval(rect, pGraph);
- if(isBackgroundTransparent)
+ if(isTransparencyEnabled)
pGraph.setXfermode(XFERMODE_SRC);
}
- public void setIsBackgroundTransparent(boolean isBackgroundTransparent)
+ public void setIsTransparencyEnabled(boolean enabled)
{
- this.isBackgroundTransparent = isBackgroundTransparent;
+ this.isTransparencyEnabled = enabled;
createColors();
+ requestLayout();
}
private void setModeOrColor(Paint p, PorterDuffXfermode mode, int color)
{
- if(isBackgroundTransparent)
+ if(isTransparencyEnabled)
p.setXfermode(mode);
else
p.setColor(color);
diff --git a/app/src/main/java/org/isoron/uhabits/views/HabitStreakView.java b/app/src/main/java/org/isoron/uhabits/views/HabitStreakView.java
index 37d2e16c9..74fc6c518 100644
--- a/app/src/main/java/org/isoron/uhabits/views/HabitStreakView.java
+++ b/app/src/main/java/org/isoron/uhabits/views/HabitStreakView.java
@@ -29,6 +29,7 @@ import android.view.View;
import org.isoron.uhabits.R;
import org.isoron.uhabits.helpers.ColorHelper;
+import org.isoron.uhabits.helpers.UIHelper;
import org.isoron.uhabits.models.Habit;
import org.isoron.uhabits.models.Streak;
@@ -53,7 +54,6 @@ public class HabitStreakView extends View implements HabitDataView
private List streaks;
private boolean isBackgroundTransparent;
- private int textColor;
private DateFormat dateFormat;
private int width;
private float em;
@@ -61,6 +61,8 @@ public class HabitStreakView extends View implements HabitDataView
private float textMargin;
private boolean shouldShowLabels;
private int maxStreakCount;
+ private int textColor;
+ private int reverseTextColor;
public HabitStreakView(Context context)
{
@@ -71,7 +73,7 @@ public class HabitStreakView extends View implements HabitDataView
public HabitStreakView(Context context, AttributeSet attrs)
{
super(context, attrs);
- this.primaryColor = ColorHelper.palette[7];
+ this.primaryColor = ColorHelper.getColor(getContext(), 7);
init();
}
@@ -109,10 +111,11 @@ public class HabitStreakView extends View implements HabitDataView
maxStreakCount = height / baseSize;
this.width = width;
- int maxTextSize = getResources().getDimensionPixelSize(R.dimen.regularTextSize);
- float regularTextSize = Math.min(baseSize * 0.56f, maxTextSize);
+ float minTextSize = getResources().getDimension(R.dimen.tinyTextSize);
+ float maxTextSize = getResources().getDimension(R.dimen.regularTextSize);
+ float textSize = baseSize * 0.5f;
- paint.setTextSize(regularTextSize);
+ paint.setTextSize(Math.max(Math.min(textSize, maxTextSize), minTextSize));
em = paint.getFontSpacing();
textMargin = 0.5f * em;
@@ -122,36 +125,19 @@ public class HabitStreakView extends View implements HabitDataView
private void createColors()
{
if(habit != null)
- this.primaryColor = habit.color;
-
- if(isBackgroundTransparent)
- {
- primaryColor = ColorHelper.setSaturation(primaryColor, 0.75f);
- primaryColor = ColorHelper.setValue(primaryColor, 1.0f);
- }
+ this.primaryColor = ColorHelper.getColor(getContext(), habit.color);
int red = Color.red(primaryColor);
int green = Color.green(primaryColor);
int blue = Color.blue(primaryColor);
- if(isBackgroundTransparent)
- {
- colors = new int[4];
- colors[3] = primaryColor;
- colors[2] = Color.argb(213, red, green, blue);
- colors[1] = Color.argb(170, red, green, blue);
- colors[0] = Color.argb(128, red, green, blue);
- textColor = Color.rgb(255, 255, 255);
- }
- else
- {
- colors = new int[4];
- colors[3] = primaryColor;
- colors[2] = Color.argb(192, red, green, blue);
- colors[1] = Color.argb(96, red, green, blue);
- colors[0] = Color.argb(32, 0, 0, 0);
- textColor = Color.argb(64, 0, 0, 0);
- }
+ colors = new int[4];
+ colors[3] = primaryColor;
+ colors[2] = Color.argb(192, red, green, blue);
+ colors[1] = Color.argb(96, red, green, blue);
+ colors[0] = UIHelper.getStyledColor(getContext(), R.attr.lowContrastTextColor);
+ textColor = UIHelper.getStyledColor(getContext(), R.attr.mediumContrastTextColor);
+ reverseTextColor = UIHelper.getStyledColor(getContext(), R.attr.highContrastReverseTextColor);
}
protected void createPaints()
@@ -216,7 +202,7 @@ public class HabitStreakView extends View implements HabitDataView
if(shouldShowLabels) availableWidth -= 2 * textMargin;
float barWidth = percentage * availableWidth;
- float minBarWidth = paint.measureText(streak.length.toString());
+ float minBarWidth = paint.measureText(streak.length.toString()) + em;
barWidth = Math.max(barWidth, minBarWidth);
float gap = (width - barWidth) / 2;
@@ -229,7 +215,7 @@ public class HabitStreakView extends View implements HabitDataView
float yOffset = rect.centerY() + 0.3f * em;
- paint.setColor(Color.WHITE);
+ paint.setColor(reverseTextColor);
paint.setTextAlign(Paint.Align.CENTER);
canvas.drawText(streak.length.toString(), rect.centerX(), yOffset, paint);
diff --git a/app/src/main/java/org/isoron/uhabits/views/HabitWidgetView.java b/app/src/main/java/org/isoron/uhabits/views/HabitWidgetView.java
new file mode 100644
index 000000000..bab859aaa
--- /dev/null
+++ b/app/src/main/java/org/isoron/uhabits/views/HabitWidgetView.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2016 Álinson Santos Xavier
+ *
+ * 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 .
+ */
+
+package org.isoron.uhabits.views;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.drawable.InsetDrawable;
+import android.graphics.drawable.ShapeDrawable;
+import android.graphics.drawable.shapes.RoundRectShape;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.util.AttributeSet;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+import org.isoron.uhabits.R;
+import org.isoron.uhabits.helpers.UIHelper;
+import org.isoron.uhabits.models.Habit;
+
+import java.util.Arrays;
+
+public abstract class HabitWidgetView extends FrameLayout implements HabitDataView
+{
+ @Nullable
+ protected InsetDrawable background;
+
+ @Nullable
+ protected Paint backgroundPaint;
+
+ @Nullable
+ protected Habit habit;
+ protected ViewGroup frame;
+
+ public void setShadowAlpha(int shadowAlpha)
+ {
+ this.shadowAlpha = shadowAlpha;
+ }
+
+ private int shadowAlpha;
+
+ public HabitWidgetView(Context context)
+ {
+ super(context);
+ init();
+ }
+
+ public HabitWidgetView(Context context, AttributeSet attrs)
+ {
+ super(context, attrs);
+ init();
+ }
+
+ private void init()
+ {
+ inflate(getContext(), getInnerLayoutId(), this);
+ shadowAlpha = (int) (255 * UIHelper.getStyledFloat(getContext(), R.attr.widgetShadowAlpha));
+ rebuildBackground();
+ }
+
+ protected abstract @NonNull Integer getInnerLayoutId();
+
+ protected void rebuildBackground()
+ {
+ Context context = getContext();
+
+ int backgroundAlpha =
+ (int) (255 * UIHelper.getStyledFloat(context, R.attr.widgetBackgroundAlpha));
+
+ int shadowRadius = (int) UIHelper.dpToPixels(context, 2);
+ int shadowOffset = (int) UIHelper.dpToPixels(context, 1);
+ int shadowColor = Color.argb(shadowAlpha, 0, 0, 0);
+
+ float cornerRadius = UIHelper.dpToPixels(context, 5);
+ float[] radii = new float[8];
+ Arrays.fill(radii, cornerRadius);
+
+ RoundRectShape shape = new RoundRectShape(radii, null, null);
+ ShapeDrawable innerDrawable = new ShapeDrawable(shape);
+
+ int insetLeftTop = Math.max(shadowRadius - shadowOffset, 0);
+ int insetRightBottom = shadowRadius + shadowOffset;
+
+ background = new InsetDrawable(innerDrawable, insetLeftTop, insetLeftTop, insetRightBottom,
+ insetRightBottom);
+ backgroundPaint = innerDrawable.getPaint();
+ backgroundPaint.setShadowLayer(shadowRadius, shadowOffset, shadowOffset, shadowColor);
+ backgroundPaint.setColor(UIHelper.getStyledColor(context, R.attr.cardBackgroundColor));
+ backgroundPaint.setAlpha(backgroundAlpha);
+
+ frame = (ViewGroup) findViewById(R.id.frame);
+ if(frame != null) frame.setBackgroundDrawable(background);
+ }
+
+ @Override
+ public void setHabit(@NonNull Habit habit)
+ {
+ this.habit = habit;
+ }
+}
diff --git a/app/src/main/java/org/isoron/uhabits/views/NumberView.java b/app/src/main/java/org/isoron/uhabits/views/NumberView.java
index 2d5ab4402..b5412a7a3 100644
--- a/app/src/main/java/org/isoron/uhabits/views/NumberView.java
+++ b/app/src/main/java/org/isoron/uhabits/views/NumberView.java
@@ -71,7 +71,7 @@ public class NumberView extends View
this.textSize = UIHelper.getFloatAttribute(context, attrs, "textSize",
getResources().getDimension(R.dimen.regularTextSize));
- this.color = ColorHelper.palette[7];
+ this.color = ColorHelper.getColor(getContext(), 7);
init();
}
diff --git a/app/src/main/java/org/isoron/uhabits/views/RepetitionCountView.java b/app/src/main/java/org/isoron/uhabits/views/RepetitionCountView.java
index e0f80aa90..ee850ab2f 100644
--- a/app/src/main/java/org/isoron/uhabits/views/RepetitionCountView.java
+++ b/app/src/main/java/org/isoron/uhabits/views/RepetitionCountView.java
@@ -23,6 +23,7 @@ import android.content.Context;
import android.util.AttributeSet;
import org.isoron.uhabits.R;
+import org.isoron.uhabits.helpers.ColorHelper;
import org.isoron.uhabits.helpers.DateHelper;
import org.isoron.uhabits.helpers.UIHelper;
import org.isoron.uhabits.models.Habit;
@@ -79,6 +80,6 @@ public class RepetitionCountView extends NumberView implements HabitDataView
public void setHabit(Habit habit)
{
this.habit = habit;
- setColor(habit.color);
+ setColor(ColorHelper.getColor(getContext(), habit.color));
}
}
diff --git a/app/src/main/java/org/isoron/uhabits/views/RingView.java b/app/src/main/java/org/isoron/uhabits/views/RingView.java
index 0d63ff958..de7ee345a 100644
--- a/app/src/main/java/org/isoron/uhabits/views/RingView.java
+++ b/app/src/main/java/org/isoron/uhabits/views/RingView.java
@@ -19,42 +19,59 @@
package org.isoron.uhabits.views;
-import android.annotation.SuppressLint;
import android.content.Context;
+import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
-import android.text.Layout;
-import android.text.StaticLayout;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.view.View;
import org.isoron.uhabits.R;
+import org.isoron.uhabits.helpers.ColorHelper;
import org.isoron.uhabits.helpers.UIHelper;
public class RingView extends View
{
+ public static final PorterDuffXfermode XFERMODE_CLEAR =
+ new PorterDuffXfermode(PorterDuff.Mode.CLEAR);
+
private int color;
+ private float precision;
private float percentage;
- private float labelMarginTop;
- private TextPaint pRing;
- private String label;
+ private int diameter;
+ private float thickness;
+
private RectF rect;
- private StaticLayout labelLayout;
+ private TextPaint pRing;
- private int width;
- private int height;
- private float diameter;
+ private Integer backgroundColor;
+ private Integer inactiveColor;
- private float maxDiameter;
+ private float em;
+ private String text;
private float textSize;
- private int fadedTextColor;
+ private boolean enableFontAwesome;
+
+ private Bitmap drawingCache;
+ private Canvas cacheCanvas;
+ private boolean isTransparencyEnabled;
public RingView(Context context)
{
super(context);
+
+ percentage = 0.0f;
+ precision = 0.01f;
+ color = ColorHelper.CSV_PALETTE[0];
+ thickness = UIHelper.dpToPixels(getContext(), 2);
+ text = "";
+ textSize = context.getResources().getDimension(R.dimen.smallTextSize);
+
init();
}
@@ -62,9 +79,24 @@ public class RingView extends View
{
super(context, attrs);
- this.label = UIHelper.getAttribute(context, attrs, "label", "Label");
- this.maxDiameter = UIHelper.getFloatAttribute(context, attrs, "maxDiameter", 100);
- this.maxDiameter = UIHelper.dpToPixels(context, maxDiameter);
+ percentage = UIHelper.getFloatAttribute(context, attrs, "percentage", 0);
+ precision = UIHelper.getFloatAttribute(context, attrs, "precision", 0.01f);
+
+ color = UIHelper.getColorAttribute(context, attrs, "color", 0);
+ backgroundColor = UIHelper.getColorAttribute(context, attrs, "backgroundColor", null);
+ inactiveColor = UIHelper.getColorAttribute(context, attrs, "inactiveColor", null);
+
+ thickness = UIHelper.getFloatAttribute(context, attrs, "thickness", 0);
+ thickness = UIHelper.dpToPixels(context, thickness);
+
+ float defaultTextSize = context.getResources().getDimension(R.dimen.smallTextSize);
+ textSize = UIHelper.getFloatAttribute(context, attrs, "textSize", defaultTextSize);
+ textSize = UIHelper.spToPixels(context, textSize);
+
+ text = UIHelper.getAttribute(context, attrs, "text", "");
+
+ enableFontAwesome = UIHelper.getBooleanAttribute(context, attrs, "enableFontAwesome", false);
+
init();
}
@@ -74,19 +106,34 @@ public class RingView extends View
postInvalidate();
}
- public void setMaxDiameter(float maxDiameter)
+ @Override
+ public void setBackgroundColor(int backgroundColor)
{
- this.maxDiameter = maxDiameter;
+ this.backgroundColor = backgroundColor;
+ postInvalidate();
}
- public void setLabel(String label)
+ public void setPercentage(float percentage)
{
- this.label = label;
+ this.percentage = percentage;
+ postInvalidate();
}
- public void setPercentage(float percentage)
+ public void setPrecision(float precision)
{
- this.percentage = percentage;
+ this.precision = precision;
+ postInvalidate();
+ }
+
+ public void setThickness(float thickness)
+ {
+ this.thickness = thickness;
+ postInvalidate();
+ }
+
+ public void setText(String text)
+ {
+ this.text = text;
postInvalidate();
}
@@ -97,59 +144,94 @@ public class RingView extends View
pRing.setColor(color);
pRing.setTextAlign(Paint.Align.CENTER);
- fadedTextColor = getResources().getColor(R.color.fadedTextColor);
- textSize = getResources().getDimension(R.dimen.smallTextSize);
+ if(backgroundColor == null)
+ backgroundColor = UIHelper.getStyledColor(getContext(), R.attr.cardBackgroundColor);
+
+ if(inactiveColor == null)
+ inactiveColor = UIHelper.getStyledColor(getContext(), R.attr.highContrastTextColor);
+
+ inactiveColor = ColorHelper.setAlpha(inactiveColor, 0.1f);
rect = new RectF();
}
@Override
- @SuppressLint("DrawAllocation")
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- width = MeasureSpec.getSize(widthMeasureSpec);
- height = MeasureSpec.getSize(heightMeasureSpec);
-
- diameter = Math.min(maxDiameter, width);
+ int width = MeasureSpec.getSize(widthMeasureSpec);
+ int height = MeasureSpec.getSize(heightMeasureSpec);
+ diameter = Math.min(height, width);
pRing.setTextSize(textSize);
- labelMarginTop = textSize * 0.80f;
- labelLayout = new StaticLayout(label, pRing, width, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0f,
- false);
+ em = pRing.measureText("M");
- width = Math.max(width, labelLayout.getWidth());
- height = (int) (diameter + labelLayout.getHeight() + labelMarginTop);
+ setMeasuredDimension(diameter, diameter);
+ }
- setMeasuredDimension(width, height);
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh)
+ {
+ super.onSizeChanged(w, h, oldw, oldh);
+
+ if(isTransparencyEnabled)
+ {
+ if (drawingCache != null) drawingCache.recycle();
+ drawingCache = Bitmap.createBitmap(diameter, diameter, Bitmap.Config.ARGB_8888);
+ cacheCanvas = new Canvas(drawingCache);
+ }
}
@Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
- float thickness = diameter * 0.15f;
+ Canvas activeCanvas;
+
+ if(isTransparencyEnabled)
+ {
+ activeCanvas = cacheCanvas;
+ drawingCache.eraseColor(Color.TRANSPARENT);
+ }
+ else
+ {
+ activeCanvas = canvas;
+ }
pRing.setColor(color);
rect.set(0, 0, diameter, diameter);
- rect.offset((width - diameter) / 2, 0);
- canvas.drawArc(rect, -90, 360 * percentage, true, pRing);
- pRing.setColor(Color.argb(255, 230, 230, 230));
- canvas.drawArc(rect, 360 * percentage - 90 + 2, 360 * (1 - percentage) - 4, true, pRing);
+ float angle = 360 * Math.round(percentage / precision) * precision;
- pRing.setColor(Color.WHITE);
- rect.inset(thickness, thickness);
- canvas.drawArc(rect, -90, 360, true, pRing);
+ activeCanvas.drawArc(rect, -90, angle, true, pRing);
- pRing.setColor(fadedTextColor);
- pRing.setTextSize(textSize);
- float lineHeight = pRing.getFontSpacing();
- canvas.drawText(String.format("%.0f%%", percentage * 100), rect.centerX(),
- rect.centerY() + lineHeight / 3, pRing);
- pRing.setTextSize(textSize);
- canvas.translate(width / 2, diameter + labelMarginTop);
- labelLayout.draw(canvas);
+ pRing.setColor(inactiveColor);
+ activeCanvas.drawArc(rect, angle - 90, 360 - angle, true, pRing);
+
+ if(thickness > 0)
+ {
+ if(isTransparencyEnabled)
+ pRing.setXfermode(XFERMODE_CLEAR);
+ else
+ pRing.setColor(backgroundColor);
+
+ rect.inset(thickness, thickness);
+ activeCanvas.drawArc(rect, 0, 360, true, pRing);
+ pRing.setXfermode(null);
+
+ pRing.setColor(color);
+ pRing.setTextSize(textSize);
+ if(enableFontAwesome) pRing.setTypeface(UIHelper.getFontAwesome(getContext()));
+ activeCanvas.drawText(text, rect.centerX(), rect.centerY() + 0.4f * em, pRing);
+ }
+
+ if(activeCanvas != canvas)
+ canvas.drawBitmap(drawingCache, 0, 0, null);
+ }
+
+ public void setIsTransparencyEnabled(boolean isTransparencyEnabled)
+ {
+ this.isTransparencyEnabled = isTransparencyEnabled;
}
}
diff --git a/app/src/main/java/org/isoron/uhabits/widgets/BaseWidgetProvider.java b/app/src/main/java/org/isoron/uhabits/widgets/BaseWidgetProvider.java
index 558a5da41..6490084f7 100644
--- a/app/src/main/java/org/isoron/uhabits/widgets/BaseWidgetProvider.java
+++ b/app/src/main/java/org/isoron/uhabits/widgets/BaseWidgetProvider.java
@@ -33,6 +33,7 @@ import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.RemoteViews;
+import android.widget.TextView;
import org.isoron.uhabits.R;
import org.isoron.uhabits.helpers.UIHelper;
@@ -45,7 +46,8 @@ import java.io.IOException;
public abstract class BaseWidgetProvider extends AppWidgetProvider
{
- private int width, height;
+ private int portraitWidth, portraitHeight;
+ private int landscapeWidth, landscapeHeight;
protected abstract int getDefaultHeight();
@@ -122,15 +124,19 @@ public abstract class BaseWidgetProvider extends AppWidgetProvider
protected abstract void refreshCustomViewData(View widgetView);
- private void savePreview(Context context, int widgetId, Bitmap widgetCache)
+ private void savePreview(Context context, int widgetId, Bitmap widgetCache, int width,
+ int height, String label)
{
try
{
LayoutInflater inflater = LayoutInflater.from(context);
View view = inflater.inflate(getLayoutId(), null);
+ TextView tvLabel = (TextView) view.findViewById(R.id.label);
+ if(tvLabel != null) tvLabel.setText(label);
+
ImageView iv = (ImageView) view.findViewById(R.id.imageView);
- iv.setImageBitmap(widgetCache);
+ if(iv != null) iv.setImageBitmap(widgetCache);
view.measure(width, height);
view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
@@ -138,7 +144,7 @@ public abstract class BaseWidgetProvider extends AppWidgetProvider
view.buildDrawingCache();
Bitmap previewCache = view.getDrawingCache();
- String filename = String.format("%s/%d.png", context.getExternalCacheDir(), widgetId);
+ String filename = String.format("%s/%d_%d.png", context.getExternalCacheDir(), widgetId, width);
Log.d("BaseWidgetProvider", String.format("Writing %s", filename));
FileOutputStream out = new FileOutputStream(filename);
@@ -172,8 +178,11 @@ public abstract class BaseWidgetProvider extends AppWidgetProvider
options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT));
}
- width = maxWidth;
- height = maxHeight;
+ portraitWidth = minWidth;
+ portraitHeight = maxHeight;
+
+ landscapeWidth = maxWidth;
+ landscapeHeight = minHeight;
}
private void measureCustomView(Context context, int w, int h, View customView)
@@ -203,8 +212,8 @@ public abstract class BaseWidgetProvider extends AppWidgetProvider
private final Context context;
private final Habit habit;
private final AppWidgetManager manager;
- public RemoteViews remoteViews;
- public View widgetView;
+ public RemoteViews portraitRemoteViews, landscapeRemoteViews;
+ public View portraitWidgetView, landscapeWidgetView;
public RenderWidgetTask(int widgetId, Context context, Habit habit,
AppWidgetManager manager)
@@ -219,17 +228,31 @@ public abstract class BaseWidgetProvider extends AppWidgetProvider
protected void onPreExecute()
{
super.onPreExecute();
+ context.setTheme(R.style.TransparentWidgetTheme);
+
+ portraitRemoteViews = new RemoteViews(context.getPackageName(), getLayoutId());
+ portraitWidgetView = buildCustomView(context, habit);
+ measureCustomView(context, portraitWidth, portraitHeight, portraitWidgetView);
- remoteViews = new RemoteViews(context.getPackageName(), getLayoutId());
- widgetView = buildCustomView(context, habit);
- measureCustomView(context, width, height, widgetView);
- manager.updateAppWidget(widgetId, remoteViews);
+ landscapeRemoteViews = new RemoteViews(context.getPackageName(), getLayoutId());
+ landscapeWidgetView = buildCustomView(context, habit);
+ measureCustomView(context, landscapeWidth, landscapeHeight, landscapeWidgetView);
+ }
+
+ private void updateAppWidget()
+ {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
+ manager.updateAppWidget(widgetId, new RemoteViews(landscapeRemoteViews,
+ portraitRemoteViews));
+ else
+ manager.updateAppWidget(widgetId, portraitRemoteViews);
}
@Override
protected void doInBackground()
{
- refreshCustomViewData(widgetView);
+ refreshCustomViewData(portraitWidgetView);
+ refreshCustomViewData(landscapeWidgetView);
}
@Override
@@ -237,20 +260,9 @@ public abstract class BaseWidgetProvider extends AppWidgetProvider
{
try
{
- widgetView.invalidate();
- widgetView.setDrawingCacheEnabled(true);
- widgetView.buildDrawingCache(true);
- Bitmap drawingCache = widgetView.getDrawingCache();
- remoteViews.setTextViewText(R.id.label, habit.name);
- remoteViews.setImageViewBitmap(R.id.imageView, drawingCache);
-
- //savePreview(context, widgetId, drawingCache);
-
- PendingIntent onClickIntent = getOnClickPendingIntent(context, habit);
- if (onClickIntent != null) remoteViews.setOnClickPendingIntent(R.id.imageView,
- onClickIntent);
-
- manager.updateAppWidget(widgetId, remoteViews);
+ buildRemoteViews(portraitWidgetView, portraitRemoteViews, portraitWidth, portraitHeight);
+ buildRemoteViews(landscapeWidgetView, landscapeRemoteViews, landscapeWidth, landscapeHeight);
+ updateAppWidget();
}
catch (Exception e)
{
@@ -260,5 +272,39 @@ public abstract class BaseWidgetProvider extends AppWidgetProvider
super.onPostExecute(aVoid);
}
+
+ private void buildRemoteViews(View widgetView, RemoteViews remoteViews, int width, int height)
+ {
+ widgetView.invalidate();
+ widgetView.setDrawingCacheEnabled(true);
+ widgetView.buildDrawingCache(true);
+ Bitmap drawingCache = widgetView.getDrawingCache();
+ remoteViews.setTextViewText(R.id.label, habit.name);
+ remoteViews.setImageViewBitmap(R.id.imageView, drawingCache);
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
+ {
+ int imageWidth = widgetView.getMeasuredWidth();
+ int imageHeight = widgetView.getMeasuredHeight();
+ int p[] = getPadding(width, height, imageWidth, imageHeight);
+
+ remoteViews.setViewPadding(R.id.buttonOverlay, p[0], p[1], p[2], p[3]);
+ }
+
+ //savePreview(context, widgetId, drawingCache, width, height, habit.name);
+
+ PendingIntent onClickIntent = getOnClickPendingIntent(context, habit);
+ if (onClickIntent != null) remoteViews.setOnClickPendingIntent(R.id.button,
+ onClickIntent);
+ }
+ }
+
+ private int[] getPadding(int entireWidth, int entireHeight, int imageWidth,
+ int imageHeight)
+ {
+ int w = (int) (((float) entireWidth - imageWidth) / 2);
+ int h = (int) (((float) entireHeight - imageHeight) / 2);
+
+ return new int[]{ w, h, w, h };
}
}
diff --git a/app/src/main/java/org/isoron/uhabits/widgets/CheckmarkWidgetProvider.java b/app/src/main/java/org/isoron/uhabits/widgets/CheckmarkWidgetProvider.java
index bd04e8eb0..cc392ba7e 100644
--- a/app/src/main/java/org/isoron/uhabits/widgets/CheckmarkWidgetProvider.java
+++ b/app/src/main/java/org/isoron/uhabits/widgets/CheckmarkWidgetProvider.java
@@ -25,7 +25,7 @@ import android.view.View;
import org.isoron.uhabits.HabitBroadcastReceiver;
import org.isoron.uhabits.R;
import org.isoron.uhabits.models.Habit;
-import org.isoron.uhabits.views.CheckmarkView;
+import org.isoron.uhabits.views.CheckmarkWidgetView;
import org.isoron.uhabits.views.HabitDataView;
public class CheckmarkWidgetProvider extends BaseWidgetProvider
@@ -33,7 +33,7 @@ public class CheckmarkWidgetProvider extends BaseWidgetProvider
@Override
protected View buildCustomView(Context context, Habit habit)
{
- CheckmarkView view = new CheckmarkView(context);
+ CheckmarkWidgetView view = new CheckmarkWidgetView(context);
view.setHabit(habit);
return view;
}
@@ -53,19 +53,19 @@ public class CheckmarkWidgetProvider extends BaseWidgetProvider
@Override
protected int getDefaultHeight()
{
- return 200;
+ return 125;
}
@Override
protected int getDefaultWidth()
{
- return 200;
+ return 125;
}
@Override
protected int getLayoutId()
{
- return R.layout.widget_checkmark;
+ return R.layout.widget_wrapper;
}
diff --git a/app/src/main/java/org/isoron/uhabits/widgets/FrequencyWidgetProvider.java b/app/src/main/java/org/isoron/uhabits/widgets/FrequencyWidgetProvider.java
index 2cac07c06..2fdbedb71 100644
--- a/app/src/main/java/org/isoron/uhabits/widgets/FrequencyWidgetProvider.java
+++ b/app/src/main/java/org/isoron/uhabits/widgets/FrequencyWidgetProvider.java
@@ -26,6 +26,7 @@ import android.view.View;
import org.isoron.uhabits.HabitBroadcastReceiver;
import org.isoron.uhabits.R;
import org.isoron.uhabits.models.Habit;
+import org.isoron.uhabits.views.GraphWidgetView;
import org.isoron.uhabits.views.HabitDataView;
import org.isoron.uhabits.views.HabitFrequencyView;
@@ -34,8 +35,8 @@ public class FrequencyWidgetProvider extends BaseWidgetProvider
@Override
protected View buildCustomView(Context context, Habit habit)
{
- HabitFrequencyView view = new HabitFrequencyView(context, null);
- view.setIsBackgroundTransparent(true);
+ HabitFrequencyView dataView = new HabitFrequencyView(context);
+ GraphWidgetView view = new GraphWidgetView(context, dataView);
view.setHabit(habit);
return view;
}
@@ -67,6 +68,6 @@ public class FrequencyWidgetProvider extends BaseWidgetProvider
@Override
protected int getLayoutId()
{
- return R.layout.widget_graph;
+ return R.layout.widget_wrapper;
}
}
diff --git a/app/src/main/java/org/isoron/uhabits/widgets/HistoryWidgetProvider.java b/app/src/main/java/org/isoron/uhabits/widgets/HistoryWidgetProvider.java
index e02cb608c..bb8be7e25 100644
--- a/app/src/main/java/org/isoron/uhabits/widgets/HistoryWidgetProvider.java
+++ b/app/src/main/java/org/isoron/uhabits/widgets/HistoryWidgetProvider.java
@@ -25,6 +25,7 @@ import android.view.View;
import org.isoron.uhabits.HabitBroadcastReceiver;
import org.isoron.uhabits.R;
import org.isoron.uhabits.models.Habit;
+import org.isoron.uhabits.views.GraphWidgetView;
import org.isoron.uhabits.views.HabitDataView;
import org.isoron.uhabits.views.HabitHistoryView;
@@ -33,9 +34,9 @@ public class HistoryWidgetProvider extends BaseWidgetProvider
@Override
protected View buildCustomView(Context context, Habit habit)
{
- HabitHistoryView view = new HabitHistoryView(context, null);
+ HabitHistoryView dataView = new HabitHistoryView(context);
+ GraphWidgetView view = new GraphWidgetView(context, dataView);
view.setHabit(habit);
- view.setIsBackgroundTransparent(true);
return view;
}
@@ -54,18 +55,18 @@ public class HistoryWidgetProvider extends BaseWidgetProvider
@Override
protected int getDefaultHeight()
{
- return 200;
+ return 250;
}
@Override
protected int getDefaultWidth()
{
- return 200;
+ return 250;
}
@Override
protected int getLayoutId()
{
- return R.layout.widget_graph;
+ return R.layout.widget_wrapper;
}
}
diff --git a/app/src/main/java/org/isoron/uhabits/widgets/ScoreWidgetProvider.java b/app/src/main/java/org/isoron/uhabits/widgets/ScoreWidgetProvider.java
index 456491efe..2608887b4 100644
--- a/app/src/main/java/org/isoron/uhabits/widgets/ScoreWidgetProvider.java
+++ b/app/src/main/java/org/isoron/uhabits/widgets/ScoreWidgetProvider.java
@@ -24,17 +24,25 @@ import android.view.View;
import org.isoron.uhabits.HabitBroadcastReceiver;
import org.isoron.uhabits.R;
+import org.isoron.uhabits.helpers.UIHelper;
import org.isoron.uhabits.models.Habit;
+import org.isoron.uhabits.views.GraphWidgetView;
import org.isoron.uhabits.views.HabitDataView;
import org.isoron.uhabits.views.HabitScoreView;
-public class ScoreWidgetProvider extends BaseWidgetProvider
+public class ScoreWidgetProvider extends BaseWidgetProvider
{
@Override
protected View buildCustomView(Context context, Habit habit)
{
- HabitScoreView view = new HabitScoreView(context, null);
- view.setIsBackgroundTransparent(true);
+ int defaultScoreInterval = UIHelper.getDefaultScoreInterval(context);
+ int size = HabitScoreView.DEFAULT_BUCKET_SIZES[defaultScoreInterval];
+
+ HabitScoreView dataView = new HabitScoreView(context);
+ dataView.setIsTransparencyEnabled(true);
+ dataView.setBucketSize(size);
+
+ GraphWidgetView view = new GraphWidgetView(context, dataView);
view.setHabit(habit);
return view;
}
@@ -54,18 +62,18 @@ public class ScoreWidgetProvider extends BaseWidgetProvider
@Override
protected int getDefaultHeight()
{
- return 200;
+ return 300;
}
@Override
protected int getDefaultWidth()
{
- return 200;
+ return 300;
}
@Override
protected int getLayoutId()
{
- return R.layout.widget_graph;
+ return R.layout.widget_wrapper;
}
}
diff --git a/app/src/main/java/org/isoron/uhabits/widgets/StreakWidgetProvider.java b/app/src/main/java/org/isoron/uhabits/widgets/StreakWidgetProvider.java
index f5d81d4f6..f0455d00a 100644
--- a/app/src/main/java/org/isoron/uhabits/widgets/StreakWidgetProvider.java
+++ b/app/src/main/java/org/isoron/uhabits/widgets/StreakWidgetProvider.java
@@ -25,6 +25,7 @@ import android.view.View;
import org.isoron.uhabits.HabitBroadcastReceiver;
import org.isoron.uhabits.R;
import org.isoron.uhabits.models.Habit;
+import org.isoron.uhabits.views.GraphWidgetView;
import org.isoron.uhabits.views.HabitDataView;
import org.isoron.uhabits.views.HabitStreakView;
@@ -33,8 +34,8 @@ public class StreakWidgetProvider extends BaseWidgetProvider
@Override
protected View buildCustomView(Context context, Habit habit)
{
- HabitStreakView view = new HabitStreakView(context, null);
- view.setIsBackgroundTransparent(true);
+ HabitStreakView dataView = new HabitStreakView(context);
+ GraphWidgetView view = new GraphWidgetView(context, dataView);
view.setHabit(habit);
return view;
}
@@ -66,6 +67,6 @@ public class StreakWidgetProvider extends BaseWidgetProvider
@Override
protected int getLayoutId()
{
- return R.layout.widget_graph;
+ return R.layout.widget_wrapper;
}
}
diff --git a/app/src/main/res/drawable-hdpi/apptheme_textfield_activated_holo_light.9.png b/app/src/main/res/drawable-hdpi/apptheme_textfield_activated_holo_light.9.png
deleted file mode 100644
index 56c957fbc..000000000
Binary files a/app/src/main/res/drawable-hdpi/apptheme_textfield_activated_holo_light.9.png and /dev/null differ
diff --git a/app/src/main/res/drawable-hdpi/apptheme_textfield_default_holo_light.9.png b/app/src/main/res/drawable-hdpi/apptheme_textfield_default_holo_light.9.png
deleted file mode 100644
index d5ac69c23..000000000
Binary files a/app/src/main/res/drawable-hdpi/apptheme_textfield_default_holo_light.9.png and /dev/null differ
diff --git a/app/src/main/res/drawable-hdpi/apptheme_textfield_disabled_focused_holo_light.9.png b/app/src/main/res/drawable-hdpi/apptheme_textfield_disabled_focused_holo_light.9.png
deleted file mode 100644
index b70db4e10..000000000
Binary files a/app/src/main/res/drawable-hdpi/apptheme_textfield_disabled_focused_holo_light.9.png and /dev/null differ
diff --git a/app/src/main/res/drawable-hdpi/apptheme_textfield_disabled_holo_light.9.png b/app/src/main/res/drawable-hdpi/apptheme_textfield_disabled_holo_light.9.png
deleted file mode 100644
index a77d66d99..000000000
Binary files a/app/src/main/res/drawable-hdpi/apptheme_textfield_disabled_holo_light.9.png and /dev/null differ
diff --git a/app/src/main/res/drawable-hdpi/apptheme_textfield_focused_holo_light.9.png b/app/src/main/res/drawable-hdpi/apptheme_textfield_focused_holo_light.9.png
deleted file mode 100644
index d7e90cb12..000000000
Binary files a/app/src/main/res/drawable-hdpi/apptheme_textfield_focused_holo_light.9.png and /dev/null differ
diff --git a/app/src/main/res/drawable-hdpi/ic_action_add.png b/app/src/main/res/drawable-hdpi/ic_action_add.png
deleted file mode 100644
index aa7cf4f2c..000000000
Binary files a/app/src/main/res/drawable-hdpi/ic_action_add.png and /dev/null differ
diff --git a/app/src/main/res/drawable-hdpi/ic_action_add_dark.png b/app/src/main/res/drawable-hdpi/ic_action_add_dark.png
index 65fd03cb1..fb70e738f 100644
Binary files a/app/src/main/res/drawable-hdpi/ic_action_add_dark.png and b/app/src/main/res/drawable-hdpi/ic_action_add_dark.png differ
diff --git a/app/src/main/res/drawable-hdpi/ic_action_add_light.png b/app/src/main/res/drawable-hdpi/ic_action_add_light.png
new file mode 100644
index 000000000..641e14989
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_action_add_light.png differ
diff --git a/app/src/main/res/drawable-hdpi/ic_action_edit_dark.png b/app/src/main/res/drawable-hdpi/ic_action_edit_dark.png
index 4112c9047..cd5af349d 100644
Binary files a/app/src/main/res/drawable-hdpi/ic_action_edit_dark.png and b/app/src/main/res/drawable-hdpi/ic_action_edit_dark.png differ
diff --git a/app/src/main/res/drawable-hdpi/ic_action_edit_light.png b/app/src/main/res/drawable-hdpi/ic_action_edit_light.png
index 5c8bcf881..70cb75e1f 100644
Binary files a/app/src/main/res/drawable-hdpi/ic_action_edit_light.png and b/app/src/main/res/drawable-hdpi/ic_action_edit_light.png differ
diff --git a/app/src/main/res/drawable-hdpi/ic_alarm_black.png b/app/src/main/res/drawable-hdpi/ic_alarm_black.png
new file mode 100644
index 000000000..907491104
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_alarm_black.png differ
diff --git a/app/src/main/res/drawable-hdpi/ic_alarm_white.png b/app/src/main/res/drawable-hdpi/ic_alarm_white.png
new file mode 100644
index 000000000..6dc94d2b5
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_alarm_white.png differ
diff --git a/app/src/main/res/drawable-hdpi/ic_repeat_black.png b/app/src/main/res/drawable-hdpi/ic_repeat_black.png
new file mode 100644
index 000000000..d8b42bbe4
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_repeat_black.png differ
diff --git a/app/src/main/res/drawable-hdpi/ic_repeat_white.png b/app/src/main/res/drawable-hdpi/ic_repeat_white.png
new file mode 100644
index 000000000..81c5be793
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_repeat_white.png differ
diff --git a/app/src/main/res/drawable-mdpi/apptheme_textfield_activated_holo_light.9.png b/app/src/main/res/drawable-mdpi/apptheme_textfield_activated_holo_light.9.png
deleted file mode 100644
index d7118fbab..000000000
Binary files a/app/src/main/res/drawable-mdpi/apptheme_textfield_activated_holo_light.9.png and /dev/null differ
diff --git a/app/src/main/res/drawable-mdpi/apptheme_textfield_default_holo_light.9.png b/app/src/main/res/drawable-mdpi/apptheme_textfield_default_holo_light.9.png
deleted file mode 100644
index 47302c93e..000000000
Binary files a/app/src/main/res/drawable-mdpi/apptheme_textfield_default_holo_light.9.png and /dev/null differ
diff --git a/app/src/main/res/drawable-mdpi/apptheme_textfield_disabled_focused_holo_light.9.png b/app/src/main/res/drawable-mdpi/apptheme_textfield_disabled_focused_holo_light.9.png
deleted file mode 100644
index 0d5ea839d..000000000
Binary files a/app/src/main/res/drawable-mdpi/apptheme_textfield_disabled_focused_holo_light.9.png and /dev/null differ
diff --git a/app/src/main/res/drawable-mdpi/apptheme_textfield_disabled_holo_light.9.png b/app/src/main/res/drawable-mdpi/apptheme_textfield_disabled_holo_light.9.png
deleted file mode 100644
index ea6d2f74b..000000000
Binary files a/app/src/main/res/drawable-mdpi/apptheme_textfield_disabled_holo_light.9.png and /dev/null differ
diff --git a/app/src/main/res/drawable-mdpi/apptheme_textfield_focused_holo_light.9.png b/app/src/main/res/drawable-mdpi/apptheme_textfield_focused_holo_light.9.png
deleted file mode 100644
index a3e40613c..000000000
Binary files a/app/src/main/res/drawable-mdpi/apptheme_textfield_focused_holo_light.9.png and /dev/null differ
diff --git a/app/src/main/res/drawable-mdpi/ic_action_add.png b/app/src/main/res/drawable-mdpi/ic_action_add.png
deleted file mode 100644
index 99b189a0c..000000000
Binary files a/app/src/main/res/drawable-mdpi/ic_action_add.png and /dev/null differ
diff --git a/app/src/main/res/drawable-mdpi/ic_action_add_dark.png b/app/src/main/res/drawable-mdpi/ic_action_add_dark.png
index e6b36b2b0..278f816ac 100644
Binary files a/app/src/main/res/drawable-mdpi/ic_action_add_dark.png and b/app/src/main/res/drawable-mdpi/ic_action_add_dark.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_action_add_light.png b/app/src/main/res/drawable-mdpi/ic_action_add_light.png
new file mode 100644
index 000000000..9a73578d5
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_action_add_light.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_action_edit_dark.png b/app/src/main/res/drawable-mdpi/ic_action_edit_dark.png
index a5ab91338..ee59997a9 100644
Binary files a/app/src/main/res/drawable-mdpi/ic_action_edit_dark.png and b/app/src/main/res/drawable-mdpi/ic_action_edit_dark.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_action_edit_light.png b/app/src/main/res/drawable-mdpi/ic_action_edit_light.png
index c6367decf..7807f449f 100644
Binary files a/app/src/main/res/drawable-mdpi/ic_action_edit_light.png and b/app/src/main/res/drawable-mdpi/ic_action_edit_light.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_alarm_black.png b/app/src/main/res/drawable-mdpi/ic_alarm_black.png
new file mode 100644
index 000000000..ed2f90aee
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_alarm_black.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_alarm_white.png b/app/src/main/res/drawable-mdpi/ic_alarm_white.png
new file mode 100644
index 000000000..ca4f94707
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_alarm_white.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_repeat_black.png b/app/src/main/res/drawable-mdpi/ic_repeat_black.png
new file mode 100644
index 000000000..c00a24ebf
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_repeat_black.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_repeat_white.png b/app/src/main/res/drawable-mdpi/ic_repeat_white.png
new file mode 100644
index 000000000..b1c2e04ab
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_repeat_white.png differ
diff --git a/app/src/main/res/drawable-nodpi/widget_preview_checkmark.png b/app/src/main/res/drawable-nodpi/widget_preview_checkmark.png
new file mode 100644
index 000000000..0cbac62e6
Binary files /dev/null and b/app/src/main/res/drawable-nodpi/widget_preview_checkmark.png differ
diff --git a/app/src/main/res/drawable-nodpi/widget_preview_frequency.png b/app/src/main/res/drawable-nodpi/widget_preview_frequency.png
new file mode 100644
index 000000000..561afb45c
Binary files /dev/null and b/app/src/main/res/drawable-nodpi/widget_preview_frequency.png differ
diff --git a/app/src/main/res/drawable-nodpi/widget_preview_history.png b/app/src/main/res/drawable-nodpi/widget_preview_history.png
new file mode 100644
index 000000000..95297b6ee
Binary files /dev/null and b/app/src/main/res/drawable-nodpi/widget_preview_history.png differ
diff --git a/app/src/main/res/drawable-nodpi/widget_preview_score.png b/app/src/main/res/drawable-nodpi/widget_preview_score.png
new file mode 100644
index 000000000..8d9b3baa5
Binary files /dev/null and b/app/src/main/res/drawable-nodpi/widget_preview_score.png differ
diff --git a/app/src/main/res/drawable-nodpi/widget_preview_streaks.png b/app/src/main/res/drawable-nodpi/widget_preview_streaks.png
new file mode 100644
index 000000000..b1fe031e6
Binary files /dev/null and b/app/src/main/res/drawable-nodpi/widget_preview_streaks.png differ
diff --git a/app/src/main/res/drawable-v21/ripple_white.xml b/app/src/main/res/drawable-v21/ripple.xml
similarity index 93%
rename from app/src/main/res/drawable-v21/ripple_white.xml
rename to app/src/main/res/drawable-v21/ripple.xml
index 5721b50cb..18e2c0fb9 100644
--- a/app/src/main/res/drawable-v21/ripple_white.xml
+++ b/app/src/main/res/drawable-v21/ripple.xml
@@ -20,5 +20,5 @@
-
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable-v21/selected_box.xml b/app/src/main/res/drawable-v21/selected_box.xml
new file mode 100644
index 000000000..9c11643e2
--- /dev/null
+++ b/app/src/main/res/drawable-v21/selected_box.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable-v21/widget_button_background.xml b/app/src/main/res/drawable-v21/widget_button_background.xml
new file mode 100644
index 000000000..fdeb02bcd
--- /dev/null
+++ b/app/src/main/res/drawable-v21/widget_button_background.xml
@@ -0,0 +1,39 @@
+
+
+
+ -
+
+
+
-
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable-xhdpi/apptheme_textfield_activated_holo_light.9.png b/app/src/main/res/drawable-xhdpi/apptheme_textfield_activated_holo_light.9.png
deleted file mode 100644
index cf064ec12..000000000
Binary files a/app/src/main/res/drawable-xhdpi/apptheme_textfield_activated_holo_light.9.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xhdpi/apptheme_textfield_default_holo_light.9.png b/app/src/main/res/drawable-xhdpi/apptheme_textfield_default_holo_light.9.png
deleted file mode 100644
index f0ad55a66..000000000
Binary files a/app/src/main/res/drawable-xhdpi/apptheme_textfield_default_holo_light.9.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xhdpi/apptheme_textfield_disabled_focused_holo_light.9.png b/app/src/main/res/drawable-xhdpi/apptheme_textfield_disabled_focused_holo_light.9.png
deleted file mode 100644
index 40a28cf70..000000000
Binary files a/app/src/main/res/drawable-xhdpi/apptheme_textfield_disabled_focused_holo_light.9.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xhdpi/apptheme_textfield_disabled_holo_light.9.png b/app/src/main/res/drawable-xhdpi/apptheme_textfield_disabled_holo_light.9.png
deleted file mode 100644
index 4ffdd869e..000000000
Binary files a/app/src/main/res/drawable-xhdpi/apptheme_textfield_disabled_holo_light.9.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xhdpi/apptheme_textfield_focused_holo_light.9.png b/app/src/main/res/drawable-xhdpi/apptheme_textfield_focused_holo_light.9.png
deleted file mode 100644
index 9bb556e7a..000000000
Binary files a/app/src/main/res/drawable-xhdpi/apptheme_textfield_focused_holo_light.9.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_action_add.png b/app/src/main/res/drawable-xhdpi/ic_action_add.png
deleted file mode 100644
index 9d6af0423..000000000
Binary files a/app/src/main/res/drawable-xhdpi/ic_action_add.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_action_add_dark.png b/app/src/main/res/drawable-xhdpi/ic_action_add_dark.png
index 25f4060a9..454cd4f44 100644
Binary files a/app/src/main/res/drawable-xhdpi/ic_action_add_dark.png and b/app/src/main/res/drawable-xhdpi/ic_action_add_dark.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_action_add_light.png b/app/src/main/res/drawable-xhdpi/ic_action_add_light.png
new file mode 100644
index 000000000..efce32f5d
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_action_add_light.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_action_edit_dark.png b/app/src/main/res/drawable-xhdpi/ic_action_edit_dark.png
index cb2395307..ac1f3fa8f 100644
Binary files a/app/src/main/res/drawable-xhdpi/ic_action_edit_dark.png and b/app/src/main/res/drawable-xhdpi/ic_action_edit_dark.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_action_edit_light.png b/app/src/main/res/drawable-xhdpi/ic_action_edit_light.png
index 955a22d51..472959624 100644
Binary files a/app/src/main/res/drawable-xhdpi/ic_action_edit_light.png and b/app/src/main/res/drawable-xhdpi/ic_action_edit_light.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_alarm_black.png b/app/src/main/res/drawable-xhdpi/ic_alarm_black.png
new file mode 100644
index 000000000..e37eab0e4
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_alarm_black.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_alarm_white.png b/app/src/main/res/drawable-xhdpi/ic_alarm_white.png
new file mode 100644
index 000000000..280e09df3
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_alarm_white.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_repeat_black.png b/app/src/main/res/drawable-xhdpi/ic_repeat_black.png
new file mode 100644
index 000000000..75ecb0462
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_repeat_black.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_repeat_white.png b/app/src/main/res/drawable-xhdpi/ic_repeat_white.png
new file mode 100644
index 000000000..ad8b8c0df
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_repeat_white.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/apptheme_textfield_activated_holo_light.9.png b/app/src/main/res/drawable-xxhdpi/apptheme_textfield_activated_holo_light.9.png
deleted file mode 100644
index bddef0f4f..000000000
Binary files a/app/src/main/res/drawable-xxhdpi/apptheme_textfield_activated_holo_light.9.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xxhdpi/apptheme_textfield_default_holo_light.9.png b/app/src/main/res/drawable-xxhdpi/apptheme_textfield_default_holo_light.9.png
deleted file mode 100644
index 6db1dd0aa..000000000
Binary files a/app/src/main/res/drawable-xxhdpi/apptheme_textfield_default_holo_light.9.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xxhdpi/apptheme_textfield_disabled_focused_holo_light.9.png b/app/src/main/res/drawable-xxhdpi/apptheme_textfield_disabled_focused_holo_light.9.png
deleted file mode 100644
index d157d7d60..000000000
Binary files a/app/src/main/res/drawable-xxhdpi/apptheme_textfield_disabled_focused_holo_light.9.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xxhdpi/apptheme_textfield_disabled_holo_light.9.png b/app/src/main/res/drawable-xxhdpi/apptheme_textfield_disabled_holo_light.9.png
deleted file mode 100644
index c91f7da91..000000000
Binary files a/app/src/main/res/drawable-xxhdpi/apptheme_textfield_disabled_holo_light.9.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xxhdpi/apptheme_textfield_focused_holo_light.9.png b/app/src/main/res/drawable-xxhdpi/apptheme_textfield_focused_holo_light.9.png
deleted file mode 100644
index 6c5c67aad..000000000
Binary files a/app/src/main/res/drawable-xxhdpi/apptheme_textfield_focused_holo_light.9.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_add.png b/app/src/main/res/drawable-xxhdpi/ic_action_add.png
deleted file mode 100644
index 3ec4d3ae8..000000000
Binary files a/app/src/main/res/drawable-xxhdpi/ic_action_add.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_add_dark.png b/app/src/main/res/drawable-xxhdpi/ic_action_add_dark.png
index b38238286..6c8df63b2 100644
Binary files a/app/src/main/res/drawable-xxhdpi/ic_action_add_dark.png and b/app/src/main/res/drawable-xxhdpi/ic_action_add_dark.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_add_light.png b/app/src/main/res/drawable-xxhdpi/ic_action_add_light.png
new file mode 100644
index 000000000..96c3ec67b
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_action_add_light.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_edit_dark.png b/app/src/main/res/drawable-xxhdpi/ic_action_edit_dark.png
index 1f0c3c081..a1647d879 100644
Binary files a/app/src/main/res/drawable-xxhdpi/ic_action_edit_dark.png and b/app/src/main/res/drawable-xxhdpi/ic_action_edit_dark.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_edit_light.png b/app/src/main/res/drawable-xxhdpi/ic_action_edit_light.png
index e2a84d81f..f8e12dda3 100644
Binary files a/app/src/main/res/drawable-xxhdpi/ic_action_edit_light.png and b/app/src/main/res/drawable-xxhdpi/ic_action_edit_light.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_alarm_black.png b/app/src/main/res/drawable-xxhdpi/ic_alarm_black.png
new file mode 100644
index 000000000..eb1d08d7a
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_alarm_black.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_alarm_white.png b/app/src/main/res/drawable-xxhdpi/ic_alarm_white.png
new file mode 100644
index 000000000..75e5a46ee
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_alarm_white.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_repeat_black.png b/app/src/main/res/drawable-xxhdpi/ic_repeat_black.png
new file mode 100644
index 000000000..965a319ff
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_repeat_black.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_repeat_white.png b/app/src/main/res/drawable-xxhdpi/ic_repeat_white.png
new file mode 100644
index 000000000..5de7a2951
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_repeat_white.png differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_action_add_light.png b/app/src/main/res/drawable-xxxhdpi/ic_action_add_light.png
new file mode 100644
index 000000000..3cb10924a
Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_action_add_light.png differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_alarm_black.png b/app/src/main/res/drawable-xxxhdpi/ic_alarm_black.png
new file mode 100644
index 000000000..04b6c71d7
Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_alarm_black.png differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_alarm_white.png b/app/src/main/res/drawable-xxxhdpi/ic_alarm_white.png
new file mode 100644
index 000000000..aa8102fd2
Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_alarm_white.png differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_repeat_black.png b/app/src/main/res/drawable-xxxhdpi/ic_repeat_black.png
new file mode 100644
index 000000000..fd4d0c028
Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_repeat_black.png differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_repeat_white.png b/app/src/main/res/drawable-xxxhdpi/ic_repeat_white.png
new file mode 100644
index 000000000..c7f3072ee
Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_repeat_white.png differ
diff --git a/app/src/main/res/drawable/apptheme_edit_text_holo_light.xml b/app/src/main/res/drawable/apptheme_edit_text_holo_light.xml
deleted file mode 100644
index 26e8fbe9b..000000000
--- a/app/src/main/res/drawable/apptheme_edit_text_holo_light.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/drawable/card_amoled_background.xml b/app/src/main/res/drawable/card_amoled_background.xml
new file mode 100644
index 000000000..d46bbb1e5
--- /dev/null
+++ b/app/src/main/res/drawable/card_amoled_background.xml
@@ -0,0 +1,58 @@
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/card_dark_background.xml b/app/src/main/res/drawable/card_dark_background.xml
new file mode 100644
index 000000000..7bf94a482
--- /dev/null
+++ b/app/src/main/res/drawable/card_dark_background.xml
@@ -0,0 +1,58 @@
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/card_background.xml b/app/src/main/res/drawable/card_light_background.xml
similarity index 100%
rename from app/src/main/res/drawable/card_background.xml
rename to app/src/main/res/drawable/card_light_background.xml
diff --git a/app/src/main/res/drawable/habits_list_header_amoled_background.xml b/app/src/main/res/drawable/habits_list_header_amoled_background.xml
new file mode 100644
index 000000000..b65fb29c3
--- /dev/null
+++ b/app/src/main/res/drawable/habits_list_header_amoled_background.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/habits_list_header_dark_background.xml b/app/src/main/res/drawable/habits_list_header_dark_background.xml
new file mode 100644
index 000000000..88989c254
--- /dev/null
+++ b/app/src/main/res/drawable/habits_list_header_dark_background.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/habits_list_header_background.xml b/app/src/main/res/drawable/habits_list_header_light_background.xml
similarity index 77%
rename from app/src/main/res/drawable/habits_list_header_background.xml
rename to app/src/main/res/drawable/habits_list_header_light_background.xml
index 83385eaa5..66413f277 100644
--- a/app/src/main/res/drawable/habits_list_header_background.xml
+++ b/app/src/main/res/drawable/habits_list_header_light_background.xml
@@ -20,7 +20,7 @@
- -
+
-
- -
-
-
-
-
-
-
+
diff --git a/app/src/main/res/drawable/intro_icon_2.png b/app/src/main/res/drawable/intro_icon_2.png
index 8955a7d8a..7daa79b2a 100644
Binary files a/app/src/main/res/drawable/intro_icon_2.png and b/app/src/main/res/drawable/intro_icon_2.png differ
diff --git a/app/src/main/res/drawable/intro_icon_3.png b/app/src/main/res/drawable/intro_icon_3.png
deleted file mode 100644
index 8372ea904..000000000
Binary files a/app/src/main/res/drawable/intro_icon_3.png and /dev/null differ
diff --git a/app/src/main/res/drawable/selected_box_amoled.xml b/app/src/main/res/drawable/selected_box_amoled.xml
new file mode 100644
index 000000000..a83732fd0
--- /dev/null
+++ b/app/src/main/res/drawable/selected_box_amoled.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/selected_box_dark.xml b/app/src/main/res/drawable/selected_box_dark.xml
new file mode 100644
index 000000000..ca0fb00ab
--- /dev/null
+++ b/app/src/main/res/drawable/selected_box_dark.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/selected_box.xml b/app/src/main/res/drawable/selected_box_light.xml
similarity index 100%
rename from app/src/main/res/drawable/selected_box.xml
rename to app/src/main/res/drawable/selected_box_light.xml
diff --git a/app/src/main/res/menu-v21/list_habits_options.xml b/app/src/main/res/drawable/toolbar_shadow.xml
similarity index 69%
rename from app/src/main/res/menu-v21/list_habits_options.xml
rename to app/src/main/res/drawable/toolbar_shadow.xml
index eac148ffb..d408e202f 100644
--- a/app/src/main/res/menu-v21/list_habits_options.xml
+++ b/app/src/main/res/drawable/toolbar_shadow.xml
@@ -1,3 +1,4 @@
+
-
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/widget_button_background.xml b/app/src/main/res/drawable/widget_button_background.xml
new file mode 100644
index 000000000..a50eaa160
--- /dev/null
+++ b/app/src/main/res/drawable/widget_button_background.xml
@@ -0,0 +1,34 @@
+
+
+
+ -
+
+
-
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/widget_preview_checkmark.png b/app/src/main/res/drawable/widget_preview_checkmark.png
deleted file mode 100644
index ceb7d6541..000000000
Binary files a/app/src/main/res/drawable/widget_preview_checkmark.png and /dev/null differ
diff --git a/app/src/main/res/drawable/widget_preview_frequency.png b/app/src/main/res/drawable/widget_preview_frequency.png
deleted file mode 100644
index a3fc86505..000000000
Binary files a/app/src/main/res/drawable/widget_preview_frequency.png and /dev/null differ
diff --git a/app/src/main/res/drawable/widget_preview_history.png b/app/src/main/res/drawable/widget_preview_history.png
deleted file mode 100644
index bcc829bbc..000000000
Binary files a/app/src/main/res/drawable/widget_preview_history.png and /dev/null differ
diff --git a/app/src/main/res/drawable/widget_preview_score.png b/app/src/main/res/drawable/widget_preview_score.png
deleted file mode 100644
index 101569dbc..000000000
Binary files a/app/src/main/res/drawable/widget_preview_score.png and /dev/null differ
diff --git a/app/src/main/res/drawable/widget_preview_streaks.png b/app/src/main/res/drawable/widget_preview_streaks.png
deleted file mode 100644
index b7771c2b8..000000000
Binary files a/app/src/main/res/drawable/widget_preview_streaks.png and /dev/null differ
diff --git a/app/src/main/res/layout/about.xml b/app/src/main/res/layout/about.xml
index 86a0f9f26..47fd5ea92 100644
--- a/app/src/main/res/layout/about.xml
+++ b/app/src/main/res/layout/about.xml
@@ -18,160 +18,213 @@
~ with this program. If not, see .
-->
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
+ style="@style/CardList">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
diff --git a/app/src/main/res/layout/edit_habit.xml b/app/src/main/res/layout/edit_habit.xml
index 493dc1c67..bc2f0a98c 100644
--- a/app/src/main/res/layout/edit_habit.xml
+++ b/app/src/main/res/layout/edit_habit.xml
@@ -22,7 +22,7 @@
style="@style/dialogForm"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
- tools:context="org.isoron.uhabits.fragments.EditHabitFragment"
+ tools:context=".dialogs.EditHabitDialogFragment"
tools:ignore="MergeRootFrame">
+ android:src="?dialogIconChangeColor"/>
-
diff --git a/app/src/main/res/layout/list_habits_activity.xml b/app/src/main/res/layout/list_habits_activity.xml
index 054a4d84e..0bb0106cd 100644
--- a/app/src/main/res/layout/list_habits_activity.xml
+++ b/app/src/main/res/layout/list_habits_activity.xml
@@ -17,19 +17,30 @@
~ with this program. If not, see .
-->
-
+
+
+
+ android:layout_below="@id/toolbar"
+ tools:layout="@layout/list_habits_fragment"/>
+
+
diff --git a/app/src/main/res/layout/list_habits_fragment.xml b/app/src/main/res/layout/list_habits_fragment.xml
index 8ec48be74..bf97d95be 100644
--- a/app/src/main/res/layout/list_habits_fragment.xml
+++ b/app/src/main/res/layout/list_habits_fragment.xml
@@ -26,12 +26,17 @@
+ android:layout_width="match_parent"
+ android:background="?windowBackgroundColor"
+ android:paddingTop="@dimen/checkmarkHeight"
+ android:layout_height="match_parent"
+ android:divider="?windowBackgroundColor"
+ android:listSelector="@android:color/transparent"
+ android:dividerHeight="1dp"/>
-
+
+ style="@style/ListHabits.Star"/>
+ style="@style/ListHabits.CheckmarkPanel"/>
+ style="@style/Card">
.
-->
-
+ style="@style/ListHabits.Checkmark"
+ android:layout_width="20dp"
+ android:layout_height="20dp"
+ android:background="@color/transparent"
+ android:textColor="?mediumContrastTextColor"
+ android:textAllCaps="true"
+ android:textStyle="bold"
+ android:textSize="10sp"/>
diff --git a/app/src/main/res/layout/list_habits_item.xml b/app/src/main/res/layout/list_habits_item.xml
index 5cac48249..63a766d35 100644
--- a/app/src/main/res/layout/list_habits_item.xml
+++ b/app/src/main/res/layout/list_habits_item.xml
@@ -19,24 +19,30 @@
-->
+ xmlns:habit="http://isoron.org/android"
+ android:id="@+id/llOuter"
+ style="@style/ListHabits.Item">
+ style="@style/ListHabits.HabitCard">
-
+
+ style="@style/ListHabits.Label" />
+ style="@style/ListHabits.CheckmarkPanel" />
\ No newline at end of file
diff --git a/app/src/main/res/layout/list_habits_item_check.xml b/app/src/main/res/layout/list_habits_item_check.xml
index ded038688..80372031c 100644
--- a/app/src/main/res/layout/list_habits_item_check.xml
+++ b/app/src/main/res/layout/list_habits_item_check.xml
@@ -20,4 +20,4 @@
+ style="@style/ListHabits.Checkmark" />
diff --git a/app/src/main/res/layout/preference_category_custom.xml b/app/src/main/res/layout/preference_category_custom.xml
new file mode 100644
index 000000000..5b1e58947
--- /dev/null
+++ b/app/src/main/res/layout/preference_category_custom.xml
@@ -0,0 +1,26 @@
+
+
+
+
diff --git a/app/src/main/res/layout/settings_activity.xml b/app/src/main/res/layout/settings_activity.xml
new file mode 100644
index 000000000..cf4cf8048
--- /dev/null
+++ b/app/src/main/res/layout/settings_activity.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/show_habit.xml b/app/src/main/res/layout/show_habit.xml
index fd5da21b5..fe3369181 100644
--- a/app/src/main/res/layout/show_habit.xml
+++ b/app/src/main/res/layout/show_habit.xml
@@ -17,25 +17,107 @@
~ with this program. If not, see .
-->
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+ android:layout_alignParentStart="true"
+ android:layout_alignParentTop="true"
+ android:text="@string/habit_strength"/>
-
\ No newline at end of file
+
diff --git a/app/src/main/res/layout/show_habit_activity.xml b/app/src/main/res/layout/show_habit_activity.xml
index c1b832451..2df9d424b 100644
--- a/app/src/main/res/layout/show_habit_activity.xml
+++ b/app/src/main/res/layout/show_habit_activity.xml
@@ -17,21 +17,33 @@
~ with this program. If not, see .
-->
-
+
+
+ android:layout_below="@id/toolbar"
+ android:layout_gravity="center"
+ tools:layout="@layout/show_habit"/>
+
+
-
+
diff --git a/app/src/main/res/layout/widget_checkmark.xml b/app/src/main/res/layout/widget_checkmark.xml
index e495ff16f..a6d0c01b7 100644
--- a/app/src/main/res/layout/widget_checkmark.xml
+++ b/app/src/main/res/layout/widget_checkmark.xml
@@ -19,18 +19,42 @@
-->
+ android:orientation="vertical">
-
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ habit:thickness="2"
+ habit:textSize="16"
+ habit:enableFontAwesome="true"
+ android:layout_marginTop="8dp"
+ android:layout_marginLeft="12dp"
+ android:layout_marginRight="12dp"/>
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/widget_graph.xml b/app/src/main/res/layout/widget_graph.xml
index 666b41045..60c4b3bae 100644
--- a/app/src/main/res/layout/widget_graph.xml
+++ b/app/src/main/res/layout/widget_graph.xml
@@ -18,33 +18,33 @@
~ with this program. If not, see .
-->
-
+ android:orientation="vertical">
-
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:paddingTop="4dp"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
+ android:paddingBottom="4dp"
+ tools:ignore="UselessParent">
-
+
+
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/small_widget_preview.xml b/app/src/main/res/layout/widget_wrapper.xml
similarity index 71%
rename from app/src/main/res/layout/small_widget_preview.xml
rename to app/src/main/res/layout/widget_wrapper.xml
index d77cbc468..1d739750d 100644
--- a/app/src/main/res/layout/small_widget_preview.xml
+++ b/app/src/main/res/layout/widget_wrapper.xml
@@ -18,19 +18,32 @@
~ with this program. If not, see .
-->
-
+ android:padding="0dp">
-
-
\ No newline at end of file
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu-v21/list_habits_context.xml b/app/src/main/res/menu-v21/list_habits_context.xml
deleted file mode 100644
index b632f3c99..000000000
--- a/app/src/main/res/menu-v21/list_habits_context.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/menu/list_habits_context.xml b/app/src/main/res/menu/list_habits_context.xml
index b48ffc38c..4f274e39e 100644
--- a/app/src/main/res/menu/list_habits_context.xml
+++ b/app/src/main/res/menu/list_habits_context.xml
@@ -18,30 +18,32 @@
~ with this program. If not, see .
-->
-
diff --git a/app/src/main/res/menu/show_habit_fragment_menu.xml b/app/src/main/res/menu/show_habit_fragment_menu.xml
index d19555502..ec81337c8 100644
--- a/app/src/main/res/menu/show_habit_fragment_menu.xml
+++ b/app/src/main/res/menu/show_habit_fragment_menu.xml
@@ -18,12 +18,13 @@
~ with this program. If not, see .
-->
-
+
+ app:showAsAction="ifRoom"/>
\ No newline at end of file
diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml
new file mode 100644
index 000000000..1cfd0b8b4
--- /dev/null
+++ b/app/src/main/res/values-ca/strings.xml
@@ -0,0 +1,180 @@
+
+
+"Loop Habit Tracker"
+"Hàbits"
+"Ajustaments"
+"Editar"
+"Esborrar"
+"Arxivar"
+"Treure de l'arxiu"
+"Afegir hàbit"
+"Canviar color"
+"Hàbit creat."
+"Hàbits esborrats."
+"Hàbits restaurats."
+"Res a desfer."
+"Res a refer."
+"Hàbit modificat."
+
+
+"Hàbit restaurat."
+"Hàbits arxivats."
+"Hàbits trets de l'arxiu."
+"Visió general"
+"Fortalesa de l'hàbit"
+"Història"
+"Netejar"
+"Pregunta (Avui vas...?)"
+
+
+"Repetir"
+"vegades en"
+"dies"
+"Recordatori"
+"Descartar"
+"Desar"
+
+
+"Ratxa"
+"No tens hàbits actius"
+"Prem i manté per a marcar o desmarcar"
+"Desactivat"
+"El nom no pot estar buit"
+"El número ha de ser positiu"
+"Pots tenir com a màxim una repetició per dia"
+"Crear hàbit"
+"Editar hàbit"
+"Revisar"
+"Més tard"
+
+
+"Benvingut"
+"Loop Habit Tracker t'ajuda a crear i mantenir bons hàbits"
+"Crear alguns hàbits nous"
+"Cada dia, després de complir el teu hàbit, posa una marca amb la app"
+"Continua fent-ho"
+"Els hàbits que facis consistentment durant una llarga temporada obtindran una estrella"
+"Segueix el teu progrès"
+"Els gràfics detallats et mostren com han mirollat els teus hàbits al llarg del temps"
+"15 minuts"
+"30 minuts"
+"1 hora"
+"2 hores"
+"4 hores"
+"8 hores"
+"Activar/desactivar repeticions prement curt"
+"Més adequat, però pot causar activacions accidentals"
+"Interval d'endarreriment en recordatoris"
+"Valora aquesta app a Google Play"
+"Enviar resposta al desenvolupador"
+"Veure codi font a Github"
+"Veure introducció de l'app"
+"Enllaços"
+"Comportament"
+"Nom"
+"Mostrar arxivats"
+"Ajustaments"
+"Interval d'endarreriment"
+"Ho sabies?"
+"Per a ordenar les entrades, prem i mantè sobre el nom de l'hàbit, després arrossega'l al lloc correcte."
+"Pots veure més dies posant el teu telèfon en orientació apaisada."
+"Esborrar hàbits"
+"Els hàbits seran esborrats permanentment. Aquesta acció no es pot desfer."
+"Caps de setmana"
+"Dilluns a divendres"
+"Qualsevol dia de la setmana"
+"Selecciona els dies"
+"Exportar a CSV"
+"Fet"
+"Treure"
+"Selecciona les hores"
+"Selecciona els minuts"
+
+
+"Crea bons hàbits i segueix el seu progrés al llarg del temps (sense publicitat)"
+"Loop t'ajuda a crear i mantenir bons hàbits, permetent-te aconseguir els teus objectius a llarg termini. Els gràfics i estadístiques detallades et mostren com han millorat els teus hàbits al llarg del temps. És completament de codi obert i lliure de publicitat."
+"<b>Senzilla, bonica i moderna interfície</b>
+Loop té una interfície minimalista que és fàcil d'utilitzar i segueix les guies de \"material design\"."
+"<b>Puntuació d'hàbit</b>
+A més de mostrar la teva ratxa actual, Loop té un algoritme avançat per a calcular la fortalesa dels teus hàbits. Cada repetició fa el teu hàbit més fort, i cada dia que fallis el farà més dèbil. Tot i això uns quants dies que fallis després d'una llarga ratxa no malmetrà completament el teu progrès."
+"<b>Gràfics i estadístiques detallades</b>
+Permet veure de forma clara com han millorat els teus hàbits al llarg del temps amb gràfics bonics i detallats. Pots anar enrera i veure també l'històric complet dels teus hàbits."
+"<b>Planificació flexible</b>
+Suporta tant hàbits diaris com hàbits amb planificacions més complexes, com per exemple 3 vegades per setmana; un cop setmana si setmana no; o un dia si i altre no."
+"<b>Recordatoris</b>
+Crea un recordatori individual per a cada hàbit, a l'hora escollida del dia. Revisa fàcilment, endarrereix o anul·la el teu hàbit directament des de la notificació, sense obrir l'app."
+"<b>Completament de codi obert i lliure de publicitat</b>
+No hi ha cap tipus de publicitat, notificacions molestes o permisos intrusius en aquesta app, i mai n'hi haurà. El codi font complet està disponible sota la llicència GPLv3."
+"<b>Optimitzat per a rellotges intel·ligents</b>
+Els recordatoris poden ser revisats, endarrerits o anul·lats directament des del teu rellotge Android Wear."
+"En quant a"
+"Traductors"
+"Desenvolupadors"
+
+
+"Versió %s"
+"Freqüència"
+"Marca"
+
+
+"Fortalesa"
+"Millors ratxes"
+"Ratxa actual"
+"Nombre de repeticions"
+"Últims %d dies"
+"Últimes %d setmanes"
+"Últims %d mesos"
+"Últims %d anys"
+
+
+"Tot el temps"
+"Cada dia"
+"Cada setmana"
+"2 cops per setmana"
+"5 cops per setmana"
+"Personalitzar ..."
+"Ajuda i Preguntes Freqüents"
+"Error exportant dades."
+"Error important dades."
+
+
+"Fitxer no reconegut."
+"Hàbits importats correctament."
+"Còpia de seguretat sencera exportada satisfactòriament."
+"Importar dades"
+"Exportar còpia de seguretat sencera"
+"Suporta còpies de seguretat exportades per aquesta app, també fitxers generats per Tickmate, HabitBull o Rewire. Mira les Preguntes Freqüents per a més informació."
+"Genera fitxers que poden ser oberts per programari de fulles de càlcul, com ara Microsoft Excel o OpenOffice Calc. Aquest fitxer no pot tornar-se a importar."
+"Genera un fitxer que contè totes les teves dades. Aquest fitxer pot tornar-se a importar."
+"Ha fallat la generació de l'informe d'error."
+"Generar informe d'error"
+"Resolució de problemes"
+"Ajuda a traduïr aquesta app"
+"Mode nocturn"
+"Utilitzar negre pur en el mode nocturn"
+"Reemplaça fons grisos per negre pur en el mode nocturn. Redueix consum de bateria en telèfons amb pantalla AMOLED."
+"Interfície"
+"Ordre invers de dies"
+"Mostra els dies en ordre invers en la pantalla principal"
+"Dia"
+"Setmana"
+"Mes"
+
+
+"Quatrimestre"
+"Any"
+
+
+
+"vegades en"
+"Cada %d dies"
+"Cada %d setmanes"
+"Cada %d mesos"
+
+
+"Puntuació"
+"So de recordatori"
+
+
+"Cap"
+
\ No newline at end of file
diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml
index 22246de86..c5a9e73f4 100644
--- a/app/src/main/res/values-cs/strings.xml
+++ b/app/src/main/res/values-cs/strings.xml
@@ -1,6 +1,6 @@
-"Sledování pravidelných návyků"
+"Loop Habit Tracker
"Zvyky"
"Nastavení"
"Upravit"
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index de965f419..e3fa85f7f 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -21,6 +21,8 @@
"Gewohnheiten archiviert."
"Gewohnheiten dearchiviert."
"Übersicht"
+
+
"Stärke"
"Verlauf"
"Löschen"
@@ -28,7 +30,9 @@
"Wiederhole"
-"mal in"
+
+
+"Mal in"
"Tagen"
"Erinnerung"
"Verwerfen"
@@ -36,7 +40,7 @@
-"Strähne"
+"Strähnen"
"Du hast keine aktiven Gewohnheiten"
"Berühre und halte um zu (de)markieren"
"Aus"
@@ -45,7 +49,7 @@
"Du musst wenigstens eine Wiederholung pro Tag haben"
"Gewohnheit erstellen"
"Gewohnheit bearbeiten"
-"Bestätigen"
+"Abhaken"
"Später"
@@ -63,13 +67,13 @@
"2 Stunden"
"4 Stunden"
"8 Stunden"
-"Wähle Wiederholungen durch kurzes Drücken"
+"Gewohnheiten durch kurzes Drücken abhaken"
"Bequemer, kann aber eine falsche Auswahl verursachen."
"\"Später erinnern\"-Intervall bei Erinnerungen"
"Bewerte diese App bei Google Play"
"Sende dem Entwickler Feedback"
"Zeige den Quellcode auf GitHub"
-"Zeige Einleitung"
+"Zeige Anleitung"
"Links"
"Verhalten"
"Name"
@@ -86,8 +90,7 @@
"Jeden Tag"
"Wähle die Tage"
"Daten als CSV exportieren"
-"Fertig
-"
+"Fertig"
"Löschen"
"Wähle Stunden"
"Wähle Minuten"
@@ -97,18 +100,20 @@
"Loop hilft dir gute Gewohnheiten anzunehmen und deine langfristigen Ziele zu erreichen. Detaillierte Statistiken zeigen dir, wie du dich entwickelt hast. Es ist ohne Werbung und Open Source."
"<b>Einfache, schöne und moderne Oberfläche</b>
Loop hat eine minimale Oberfläche und ist deshalb einfach zu benutzen. Es folgt den Material Design Richtlinien."
-"<b>Habit Punkte</b>
+
+
+"<b>Habit Wertung</b>
Um dir deine kleinen Schwächen zu zeigen hat Loop einen Algorithmus, um deine Gewohnheiten 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."
"<b>Statistiken</b>
Schau dir an, wie sich deine Gewohnheiten im Laufe der Zeit entwickelt haben. Schau auf die schönen Diagramme und gehe zurück im gesamten Verlauf."
"<b>Flexible Zeiten</b>
-Unterstützt sowohl tägliche Vorgaben, als auch komplexere Pläne, wie etwa 3 mal pro Woche; ein mal in jeder anderen Woche; oder jeden anderen Tag."
+Unterstützt sowohl tägliche Vorgaben, als auch komplexere Pläne, wie etwa 3 Mal pro Woche; einmal in jeder anderen Woche; oder jeden anderen Tag."
"<b>Erinnerungen</b>
Erstelle individuelle Erinnerungen und wann diese dich benachrichtigen sollen. Kontrolliere deine Vorhaben ganz einfach und lehne sie bei Bedarf direkt ab, ohne die App zu öffnen."
"<b>Komplett werbefrei und Open Source</b>
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."
"<b>Optimiert für Smartwatches</b>
-Erinnerungen können direkt von deiner Android Wear Watch kontrolliert, pausiert oder verschoben werden."
+Erinnerungen können direkt von deiner Android Wear Watch abgehakt, pausiert oder verschoben werden."
"Über"
"Übersetzer"
"Entwickler"
@@ -121,6 +126,7 @@ Erinnerungen können direkt von deiner Android Wear Watch kontrolliert, pausiert
"Häkchen"
+
"Stärke"
@@ -129,18 +135,18 @@ Erinnerungen können direkt von deiner Android Wear Watch kontrolliert, pausiert
"Derzeitige Strähne"
"Anzahl der Wiederholungen"
-"Letzte %d Tage"
-"Letzte %d Wochen"
-"Letzte %d Monate"
-"Letzte %d Jahre"
+"Letzten %d Tage"
+"Letzten %d Wochen"
+"Letzten %d Monate"
+"Letzten %d Jahre"
"Allzeit"
"Jeden Tag"
"Jede Woche"
-"2 mal pro Woche"
-"5 mal pro Woche"
+"2 Mal pro Woche"
+"5 Mal pro Woche"
"Benutzerdefiniert"
"Hilfe & FAQ"
"Der Export von Daten ist fehlgeschlagen."
@@ -155,7 +161,35 @@ Erinnerungen können direkt von deiner Android Wear Watch kontrolliert, pausiert
"Unterstützt vollständige Sicherungen dieser App, als auch erstellte Sicherungen von Tickmate, HabitBull oder Rewire. Siehe FAQ für mehr Information."
"Erzeugt Dateien, die von Tabellenkalkulationsprogrammen, wie Microsoft Excel oder LibreOffice Calc, geöffnet werden können. Diese Dateien können nicht wieder importiert werden."
"Erzeugt eine Datei, die alle deine Daten enthält. Diese Datei kann wieder importiert werden."
-"Bug-Report konnte nicht erstellt werden."
-"Erstelle einen Bug-Report."
+"Fehlermeldung konnte nicht erstellt werden."
+"Einen Fehler melden"
"Fehlerbehebung"
+"Hilf diese App zu übersetzen"
+"Nachtmodus"
+"Reines Schwarz im Nachtmodus verwenden"
+"Ersetzt das Grau im Hintergrund mit einem reinen Schwarz im Nachtmodus. Reduziert den Stromverbrauch von Smartphones mit einem AMOLED Display."
+"Oberfläche"
+"Die Reihenfolge der Tage umkehren"
+"Zeigt die Tage im Hauptfenster in umgekehrter Reihenfolge an."
+"Tag"
+"Woche"
+"Monat"
+
+
+"Quartal"
+"Jahr"
+
+
+
+"Mal in"
+"Jeden %d Tag"
+"Jede %d Woche"
+"Jeden %d Monat"
+
+
+"Wertung"
+"Benachrichtigungston"
+
+
+"Kein Ton"
\ No newline at end of file
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index efacce39c..4958c0d09 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -36,9 +36,9 @@
"Rachas"
-"No hay ningún hábito activo"
-"Mantener apretado para"
-"No"
+"No tienes hábitos activos"
+"Mantener apretado para marcar o desmarcar"
+"Apagado"
"Nombre no puede estar en blanco."
"Número debe ser positivo."
"Puedes tener como máximo una repetición por día"
@@ -167,4 +167,32 @@ Los recordatorios se pueden marcar, aplazar o descartar directamente desde tu re
"Fallo al generar el informe del bug."
"Generar informe de bug"
"Solución de problemas"
+"Ayuda a traducir esta app"
+"Modo nocturno"
+"Utilizar color negro en modo nocturno"
+"Reemplaza fondos grises por color negro en modo nocturno. Reduce el consumo de batería en teléfonos con pantalla AMOLED."
+"Interfície"
+"Orden inverso de días"
+"Mostrar días en orden inverso en la pantalla principal"
+"Día"
+"Semana"
+"Mes"
+
+
+"Cuatrimestre"
+"Año"
+
+
+
+"veces en"
+"Cada %d días"
+"Cada %d semanas"
+"Cada %d meses"
+
+
+"Puntuación"
+"Sonido de recordatorio"
+
+
+"Ninguno"
\ No newline at end of file
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index 2d407adc5..5e1c56b0a 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -141,17 +141,9 @@ Les rappels peuvent être cochés, reportés ou supprimés directement à partir
"Depuis le début"
-
-
"Tous les jours"
-
-
"Toutes les semaines"
-
-
"2 fois par semaine"
-
-
"5 fois par semaine"
@@ -167,9 +159,32 @@ Les rappels peuvent être cochés, reportés ou supprimés directement à partir
"Importer des données"
"Exporter une sauvegarde complète"
"Supporte les sauvegardes complètes générées par cette app, ainsi que les fichiers Tickmate, HabitBull et Rewire. Voir la FAQ pour plus d'informations."
-"Génère des fichiers pouvant être ouverts par des tableurs comme Microsoft Excel ou OpenOffice Calc. Ces fichiers ne peuvent être réimportés. "
+"Génère des fichiers pouvant être ouverts par des tableurs comme Microsoft Excel ou OpenOffice Calc. Ces fichiers ne peuvent être réimportés."
"Génère un fichier contenant toutes vos données. Ce fichier peut être réimporté."
-"La génération du rapport de bug à échouée."
+"La génération du rapport de bug a échouée."
"Générer un rapport de bug."
"Résolution de problèmes"
+"Aider à traduire cette application"
+"Mode Nuit"
+"Jour"
+"Semaine"
+"Mois"
+
+
+"Quarter"
+"Année"
+
+
+
+"fois tous les"
+"Tous les %d jours"
+"Toutes les %d semaines"
+"Tous les %d mois"
+
+
+"Score"
+"Son du rappel"
+
+
+"Aucun"
\ No newline at end of file
diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml
new file mode 100644
index 000000000..92602f02f
--- /dev/null
+++ b/app/src/main/res/values-in/strings.xml
@@ -0,0 +1,181 @@
+
+
+"Loop Habit Tracker"
+"Kebiasaan"
+"Pengaturan"
+"Ubah"
+"Hapus"
+"Arsip"
+"Keluarkan dari arsip"
+"Tambah Kebiasaan"
+"Ganti warna"
+"Kebiasaan ditambahkan."
+"Kebiasaan dihapus."
+"Kebiasaan dikembalikan."
+"Tidak ada aksi sebelumnya."
+"Tidak ada aksi sesudahnya."
+"Kebiasaan diubah."
+
+
+"Kebiasaan telah dikembalikan."
+"Kebiasaan diarsipkan."
+"Kebiasaan dikeluarkan dari arsip."
+"Keseluruhan"
+"Kekuatan Kebiasaan"
+"Riwayat"
+"Bersihkan"
+"Pertanyaan (Apakah kamu ... hari ini?)"
+
+
+"Ulangi"
+"kali dalam"
+"hari"
+"Pengingat"
+"Batalkan"
+"Simpan"
+
+
+"Rentetan"
+"Anda tidak memiliki Kebiasaan yang aktif"
+"Tekan dan tahan untuk menambah atau menghapus tanda cek"
+"Mati"
+"Nama tidak boleh kosong."
+"Angka harus positif."
+"Maksimal satu kali pengulangan dalam satu hari"
+"Buat Kebiasaan"
+"Ubah Kebiasaan"
+"Tanda cek"
+"Tunda"
+
+
+"Selamat datang"
+"Loop Habit Tracker membantu mencatat dan mengelola Kebiasaan baik Anda."
+"Buat beberapa Kebiasaan baru"
+"Berikan tanda cek setiap kali Anda selesai melakukannya."
+"Terus lakukan"
+"Kebiasaan yang dilakukan secara konsisten dalam jangka waktu panjang akan mendapatkan tanda bintang penuh."
+"Catat perkembangan Anda"
+"Detail grafik menampilkan perkembangan Kebiasaanmu dari waktu ke waktu."
+"15 menit"
+"30 menit"
+"1 jam"
+"2 jam"
+"4 jam"
+"8 jam"
+"Tandai dengan cepat."
+"Lebih nyaman namun memungkinkan kesalahan."
+"Durasi tunda sejenak pada pengingat"
+"Berikan rating aplikasi ini di Google Play"
+"Kirimkan umpan balik kepada Developer"
+"Lihat kode aplikasi di GitHub"
+"Perkenalan aplikasi"
+"Tautan"
+"Kebiasaan"
+"Nama"
+"Tampilkan arsip"
+"Pengaturan"
+"Durasi tunda sejenak"
+"Tahukah kamu?"
+"Untuk mengatur urutan, tekan dan tahan judul Kebiasaan lalu tempatkan pada posisi yang Anda inginkan."
+"Anda dapat melihat tampilan hari dengan merubah posisi menjadi mode landscape."
+"Hapus Kebiasaan"
+"Kebiasaan ini akan dihapus permanen. Tidak dapat dikembalikan."
+"Akhir pekan"
+"Senin - Jumat"
+"Seluruh hari dalam satu minggu"
+"Pilih hari"
+"Ekspor (CSV)"
+"Selesai"
+"Hapus"
+"Pilih jam"
+"Pilih menit"
+
+
+"Buat Kebiasaan baik dan catat perkembangannya setiap waktu (bebas iklan)"
+"Loop membantu Anda untuk membuat dan mengelola Kebiasaan baik, memungkinkan Anda mencapai gol jangka panjang. Grafik detail dan statistik menampilkan perkembangan Kebiasaan Anda dari waktu ke waktu. Aplikasi ini bebas iklan dan open-source."
+"<b>Sederhana, minimalis dengan balutan antarmuka yang modern</b>
+Loop memiliki tampilan minimalis yang mudah digunakan dan mengikuti panduan material design."
+"<b>Skor Kebiasaan</b>
+Algoritma Loop akan menghitung kekuatan Kebiasaan Anda. Setiap kali Anda melakukan pengulangan maka skor Anda akan bertambah, sebaliknya jika Anda tidak mengerjakan Kebiasaan maka nilai akan dikurangi. Beberapa hari yang terlewat tidak akan menghapus seluruh perkembangan Kebiasaan Anda."
+"<b>Detail Grafik dan Statistik</b>
+Lihat perkembangan Kebiasaan dari waktu ke waktu dengan tampilan yang menarik dan detail. Geser ke bawah untuk melihat seluruh catatan Kebiasaan Anda."
+"<b>Pengaturan jadwal fleksibel</b>
+Mendukung kebiasaan harian dan kebiasaan dengan penjadwalan yang kompleks, seperti 3 kali dalam setiap minggu; 2 minggu sekali; hingga 2 hari sekali."
+"<b>Pengingat</b>
+Anda dapat membuat pengingat untuk setiap Kebiasaan, dengan waktu yang telah Anda tentukan setiap harinya. Mudah untuk di-cek, batalkan ataupun tunda melalui panel notifikasi tanpa perlu membuka aplikasi."
+"<b>Bebas dari iklan dan open-source</b>
+Tidak ada iklan, notifikasi menyebalkan dan juga hak akses yang tidak dibutuhkan.
+Kode aplikasi tersedia dengan lisensi GPLv3"
+"<b>Mendukung Smartwatch</b>
+Anda dapat dengan mudah mengecek, menunda ataupun membatalkan pengingat melalui smartwatch Anda."
+"Tentang"
+"Penerjemah"
+"Developer"
+
+
+"Versi %s"
+"Frekuensi"
+"Cek"
+
+
+"Kekuatan"
+"Rentetan terbaik"
+"Rentetan saat ini"
+"Jumlah pengulangan"
+"%d hari terakhir"
+"%d minggu terakhir"
+"%d bulan terakhir"
+"%d tahun terakhir"
+
+
+"Seluruh waktu"
+"Setiap hari"
+"Setiap minggu"
+"2 kali setiap minggu"
+"5 kali setiap minggu"
+"Tersuai ..."
+"Bantuan & FAQ"
+"Ekspor data gagal."
+"Impor data gagal."
+
+
+"File tidak dikenali."
+"Impor data berhasil."
+"Ekspor data berhasil."
+"Impor data"
+"Ekspor data"
+"Mendukung ekspor data dan file dari aplikasi Tickmate, HabitBull atau Rewire. Lihat FAQ untuk informasi lebih lanjut."
+"Menghasilkan lembar kerja yang dapat dibuka menggunakan aplikasi seperti Microsoft Excel atau OpenOffice Calc. File ini tidak dapat di-impor kembali."
+"Menghasilkan file yang berisikan seluruh data. File ini dapat di-impor kembali."
+"Gagal membuat laporan masalah."
+"Membuat laporan masalah"
+"Troubleshoot"
+"Bantu terjemahkan"
+"Mode malam"
+"Gunakan warna hitam pada mode malam"
+"Ganti warna latar abu-abu dengan warna hitam pada mode malam. Mengurangi penggunaan baterai pada layar AMOLED."
+"Antar muka"
+"Ubah urutan hari"
+"Tampilkan hari dalam urutan terbalik pada layar utama"
+"Hari"
+"Minggu"
+"Bulan"
+
+
+"Perempat"
+"Tahun"
+
+
+
+"kali dalam"
+"Setiap %d hari"
+"Setiap %d minggu"
+"Setiap %d bulan"
+
+
+"Nilai"
+"Suara pengingat"
+
+
+"Hening"
+
\ No newline at end of file
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index e4d6514da..ad39efbdd 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -110,7 +110,7 @@ Crea un promemoria per ogni abitudine, ad una specifica ora del giorno. Completa
"<b>Completamente gratuito ed opensource</b>
Non ci sono pubblicità, notifiche invasive o permessi intrusivi e mai ce ne saranno. Il codice sorgente completo è disponibile sotto licenza GPLv3."
"<b>Ottimizzata per gli smartwatch</b> I promemoria possono essere completati, ritardati o ignorati direttamente dal tuo orologio Android Wear."
-"A proposito di"
+"Su Loop"
"Traduttori"
"Sviluppatori"
@@ -154,4 +154,35 @@ Non ci sono pubblicità, notifiche invasive o permessi intrusivi e mai ce ne sar
"Generazione del bug report fallita"
"Genera bug report"
"Risoluzione dei problemi"
+"Aiuta a tradurre questa app"
+"Modalità notte"
+"Usa nero puro nella modalità notte"
+"Sostituisce gli sfondi grigi con sfondi neri nella modalità notte. Riduce il consumo di batteria nei dispositivi con schermo AMOLED."
+"Interfaccia"
+"Inverti ordine giorni"
+"Mostra i giorni in ordine inverso nella schermata principale"
+"Giorno"
+"Settimana"
+"Mese"
+
+
+
+"Quadrimestre"
+"Anno"
+
+
+
+
+"volta in"
+"Ogni %d giorni"
+"Ogni %d settimane"
+"Ogni %d mesi"
+
+
+"Punteggio"
+"Suono notifica"
+
+
+
+"Nessuno"
\ No newline at end of file
diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml
index cf6a89845..ed2a8d872 100644
--- a/app/src/main/res/values-ko/strings.xml
+++ b/app/src/main/res/values-ko/strings.xml
@@ -158,4 +158,32 @@ Loop은 Material디자인을 따라 사용이 편합니다."
"오류보고서를 작성하지 못했습니다."
"오류보고서 작성하기"
"문제가 있나요?"
+"변역을 도와주세요"
+"어두운 배경"
+"검정색 배경"
+"야간모드의 회색 바탕을 검정 바탕색으로 바꿉니다. AMOLED를 사용하는 기기는 검정바탕으로 배터리를 절약할 수 있습니다."
+"바탕화면 설정"
+"날짜 순서 뒤집기"
+"날짜 순서를 뒤바꾸어 보여줍니다."
+"일"
+"주"
+"월"
+
+
+"4분기-1년"
+"년"
+
+
+
+"번"
+"%d 일 마다"
+"%d 주 마다"
+"%d 개월 마다"
+
+
+"습관점수"
+"알림 소리"
+
+
+"무음"
\ No newline at end of file
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index 3c3eba523..195629e7e 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -155,4 +155,32 @@ Przypomnienia można sprawdzić, uśpić czy usunąć bezpośrednio z twojego ze
"Nie udało się wygenerować raportu o błędach."
"Wygeneruj raport o błędach"
"Rozwiązywanie problemów"
+"Pomóż w tłumaczeniu tej aplikacji"
+"Tryb nocny"
+"Używaj pełnej czerni w trybie nocnym"
+"Zamienia szare tła na pełną czerń w trybie nocnym. Zmniejsza zużycie baterii w telefonach z ekranem AMOLED."
+"Interfejs"
+"Odwróć kolejność dni"
+"Pokaż dni w odwrotnej kolejności na głównym ekranie"
+"Dzień"
+"Tydzień"
+"Miesiąc"
+
+
+"Kwartał"
+"Rok"
+
+
+
+"raz w"
+"Co %d dni"
+"Co %d tygodni"
+"Co %d miesięcy"
+
+
+"Wynik"
+"Dźwięk przypomnienia"
+
+
+"Brak"
\ No newline at end of file
diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml
index a0a4d2e61..b1ae2b971 100644
--- a/app/src/main/res/values-pt/strings.xml
+++ b/app/src/main/res/values-pt/strings.xml
@@ -149,4 +149,32 @@ Lembretes podem ser marcados ou ignorados diretamente a partir do seu relógio A
"Erro ao gerar relatório de erros."
"Gerar relatório de erros."
"Solução de problemas"
+"Ajude a traduzir este app"
+"Modo noturno"
+"Usar preto em modo noturno"
+"Substitui os tons de cinza por preto puro em modo noturno. Economiza bateria em telefones com tela AMOLED."
+"Interface"
+"Inverter a ordem dos dias"
+"Mostra os dias em ordem inversa na tela principal"
+"Dia"
+"Semana"
+"Mês"
+
+
+"Trimestre"
+"Ano"
+
+
+
+"vez em"
+"A cada %d dias"
+"A cada %d semanas"
+"A cada %d meses"
+
+
+"Estabilidade"
+"Toque dos lembretes"
+
+
+"Nenhum"
\ No newline at end of file
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 665d46c40..9cf1e9677 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -106,7 +106,7 @@
"<b>Детализированные диаграммы и статистика</b>
При помощи красивых и детализированных диаграмм вы с лёгкостью можете просмотреть, как ваши привычки закрепились со временем. Просмотрите полную историю ваших привычек, пролистав её."
"<b>Гибкий график</b>
-Поддерживаются как ежедневные привычки, так и привычки с более сложным графиком. Например: 3 раза в неделю; один раз каждую неделю; через день."
+Поддерживаются как ежедневные привычки, так и привычки с более сложным графиком. Например: 3 раза в неделю; один раз в две недели; через день."
"<b>Напоминания</b>
Создавайте отдельные для каждой привычки напоминания в выбранное время дня. С лёгкостью отмечайте, пропускайте или откладывайте выполнение привычки прямо из уведомления - без открытия приложения."
"<b>Полное отсутствие рекламы и открытый исходный код</b>
@@ -155,4 +155,32 @@
"Ошибка генерации отчёта об ошибке."
"Сгенерировать отчёт об ошибке"
"Устранение неполадок"
+"Помогите перевести это приложение"
+"Ночной режим"
+"Использовать подлинный черный в ночном режиме"
+"Заменяет серый фон на подлинный черный в ночном режиме. Уменьшает использование батареи в телефонах с дисплеем AMOLED."
+"Интерфейс"
+"Обратный порядок дней"
+"Показывать дни в обратном порядке на главном экране"
+"День"
+"Неделя"
+"Месяц"
+
+
+"Квартал"
+"Год"
+
+
+
+"раз за"
+"Каждые %d дней"
+"Каждые %d недель"
+"Каждые %d месяцев"
+
+
+"Стабильность"
+"Звук напоминания"
+
+
+"Без звука"
\ No newline at end of file
diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml
new file mode 100644
index 000000000..214e2ca61
--- /dev/null
+++ b/app/src/main/res/values-tr/strings.xml
@@ -0,0 +1,173 @@
+
+
+"Loop Alışkanlık Takip"
+"Alışkanlıklar"
+"Ayarlar"
+"Düzenle"
+"Sil"
+"Arşivle"
+"Arşivden Çıkar"
+"Alışkanlık ekle"
+"Renk Değiştir"
+"Alışkanlık oluşturuldu."
+"Alışkanlık silindi."
+"Alışkanlık"
+"Geri alınacak birşey yok."
+"Tekrar yapılacak birşey yok."
+"Alışkanlık değişti."
+
+
+"Alışkanlık eski haline getirildi."
+"Alışkanlık arşivlendi."
+"Alışkanlık arşivden çıkarıldı."
+"Genel bakış"
+"Alışkanlık dayanımı"
+"Geçmiş"
+"Temizle"
+"Soru (Bugün ... yaptın mı?)"
+
+
+"Tekrar"
+"kez"
+"günde"
+"Hatırlatma"
+"İptal"
+"Kaydet"
+
+
+"Etkinlikler"
+"Etkin alışkanlığın yok"
+"Yapıldı veya yapılmadı işareti koymak için uzun basılı tut"
+"Kapalı"
+"Adı boş bırakamazsın."
+"Sayılar sıfırdan büyük olmalı."
+"Günde en fazla bir tekrar yapabilirsin."
+"Alışkanlık oluştur"
+"Alışkanlık düzenle"
+"Yaptım"
+"Sonra"
+
+
+"Hoşgeldin"
+"İyi alışkanlıklar edinmek ve devam etmene yardımcı olur."
+"Yeni alışkanlıklar oluştur"
+"Her gün, alışkanlığını gerçekleştirdikten sonra, uygulamada onay işareti koy."
+"Yapmaya devam et"
+"Uzun bir süre ile sürekli yaptığın alışkanlıkların için tam yıldız kazanacaksın."
+"Gelişimini izle"
+"Detaylı grafiklerle, zaman içinde alışkanlıklarını nasıl geliştiğini gör."
+"15 dakika"
+"30 dakika"
+"1 saat"
+"2 saat"
+"4 saat"
+"8 saat"
+"Daha kısa süre basma ile yapıldı/yapılmadı işaretleme"
+"Daha kullanışlı ama kazara istenmeyen işaretlemeler olabilir"
+"Hatırlatmalardaki erteleme süresi"
+"Google Play'de uygulamayı oyla"
+"Geliştiriciye geri bildirim gönder"
+"Github'da kaynak kodunu bak"
+"Uygulama tanıtımını göster"
+"Bağlantılar"
+"Davranış"
+"Ad"
+"Arşivlileri göster"
+"Ayarlar"
+"Erteleme süresi"
+"Biliyor musun?"
+"Girdileri yeniden düzenlemek için, alışkanlık adının üstüne bas ve doğru yere sürükle."
+"Cihazını yatay tutarak daha fazla gün görebilirsin."
+"Alışkanlıkları Sil"
+"Alışkanlıklar kalıcı olarak silinecek. Bu eylem geri alınamaz."
+"Hafta sonları"
+"Pazartesinden Cumaya"
+"Haftanın herhangi bir günü"
+"Günleri seç"
+"CSV olarak dışa aktar"
+"Tamam"
+"Temizle"
+"Saat seç"
+"Dakika seç"
+
+
+"İyi alışkanlıklar kazan ve gelişmesini takip et (reklamsız)"
+"İyi alışkanlar kazanmanı ve sürdürmene yardımcı olur, uzun dönemli hedeflerini başarmanı sağlar. Ayrıntılı çizelge ve istatistikler, zaman içinde alışkanlıklarını nasıl geliştiğini gösterir. Tamamen reklamsız ve açık kaynak."
+"<b>Basit, güzel ve çağdaş arayüz</b> Kullanımı kolay sade arayüze sahiptir ve material design kurallarını takip eder."
+"<b>Alışkanlık Puanı</b> Alışkanlıklarının dayanımını hesaplamak için gelişmiş algoritmaya sahiptir. Her tekrar alışkanlığını güçlendirir ve her iptal zayıflatır. Uzun süre etkinlikten sonra birkaç gün devamsızlık, tüm gelişmenizi tamamen etkilemeyecek."
+"<b>Ayrıntılı çizelgeler ve istatistikler</b> Güzel ve ayrıntılı çizelgeler ile alışkanlıklarının zaman içinde nasıl geliştiğini açıkça görebilirsin. Alışkanlıklarının geçmişini tamamen görmek için geriye sürükle."
+"<b>Esnek zamanlamalar</b> Günlük alışkanlanlıklar ve haftada 3 kez, diğer haftalarda bir kez veya diğer hergün gibi daha gelişmiş zamanlamalı alışkanlıkları destekler."
+"<b>Hatırlatmalar</b> Herbir alışkanlık için seçtiğin gün ve saatte ayrı hatırlatma oluştur. Kolayca uygulamayı açmadan, bildirimlerden alışkanlıklarını yapıldı, iptal et veya ertele olarak işaretle."
+"<b>Tamamen reklamsız ve açık kaynak</b> Bu uygulama içinde kesinlikle hiç reklam yok, can sıkıcı bildirimler veya zorunlu izinler yok ve asla da olmayacak. Kaynak kodun tamamı GPLv3 özgür yazılım lisansı kapsamındadır."
+"<b>Akıllı saatler için uyarlandı</b> Android Wear saatinden, hatırlatmaları yapıldı, ertele veya iptal edebilirsin."
+"Hakkında"
+"Çevirmenler"
+"Geliştiriciler"
+
+
+"Sürüm %s"
+"Sıklık"
+"Yapıldı işareti"
+
+
+"Dayanım"
+"En iyi etkinlik günü"
+"Bugünkü etkinlik"
+"Tekrar sayısı"
+"Son %d gün"
+"Son %d hafta"
+"Son %d ay"
+"Son %d yıl"
+
+
+"Tüm zamanlar"
+"Her gün"
+"Her hafta"
+"Haftada 2 kez"
+"Haftada 5 kez"
+"Özel ..."
+"Yardım & SSS"
+"Dışarı veri aktarımı başarısız."
+"İçeri veri aktarımı başarısız."
+
+
+"Dosya tanınamadı."
+"Alışkanlıklar başarılı içeri aktarıldı."
+"Tam yedek başarıyla dışarı aktarıldı."
+"Veri içeri aktar"
+"Tüm yedeği dışarı aktar"
+"Hem bu uygulama tarafından dışarı aktarılmış tam yedekleri, hem de Tickmate, HabitBull veya Rewire tarafından üretilmiş dosyaları destekler. Daha fazla bilgi için SSS bakın."
+"Üretilen dosyalar, Microsoft Excel veya OpenOffice Calc. gibi hesap taplosu uygulamaları ile açılabilir. Bu dosya yeniden içeri aktarılamaz."
+"Üretilen dosya, tüm verilerini içerir. Bu dosya yeniden içeri aktarılabilir."
+"Hata raporu oluşturulamadı."
+"Hata raporu üret"
+"Sorun Giderme"
+"Bu uygulamanın çevirisine yardım et"
+"Gece kipi"
+"Gece kipinde saf siyah kullan"
+"Gece kipinde gri arkaplanını, saf siyah ile değiştir. AMOLED ekranlı cihazlarda pil kullanımını düşür."
+"Arayüz"
+"Günleri ters sırala"
+"Ana ekranda günleri tersen göster"
+"Gün"
+"Hafta"
+"Ay"
+
+
+"3 Ay"
+"Yıl"
+
+
+
+"kez"
+"Her %d gün"
+"Her %d hafta"
+"Her %d ay"
+
+
+"Puan"
+"Hatırlatma sesi"
+
+
+"Sessiz"
+
\ No newline at end of file
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
new file mode 100644
index 000000000..fcaca320b
--- /dev/null
+++ b/app/src/main/res/values-uk/strings.xml
@@ -0,0 +1,180 @@
+
+
+"Трекер звичок Loop"
+"Звички"
+"Налаштування"
+"Редагувати"
+"Видалити"
+"Архівувати"
+"Повернути з архіву"
+"Додати звичку"
+"Змінити колір"
+"Звичка створена."
+"Звички видалені."
+"Звички відновлені."
+"Відміняти нічого."
+"Повторювати нічого."
+"Звичка змінена."
+
+
+"Зміна звички скасована."
+"Звички архівовані."
+"Звички повернуті з архіву."
+"Огляд"
+"Сила звички"
+"Історія"
+"Очистити"
+"Питання (приклад: \"Чи робили ви ранкову зарядку сьогодні?\")"
+
+
+"Повторювати"
+"разів за"
+"днів"
+"Нагадування"
+"Відмінити"
+"Зберегти"
+
+
+"Серії"
+"У вас немає активних звичок"
+"Натисніть та утримуйте, щоб встановити або зняти галочку"
+"Вимкнути"
+"Назва не може бути порожньою."
+"Число повинно бути додатнім."
+"Може бути не більше одного повторення на добу"
+"Додати звичку"
+"Редагувати звичку"
+"Відмітити"
+"Відкласти"
+
+
+"Ласкаво просимо"
+"Трекер звичок Loop допомагає вам розвивати і підтримувати корисні звички."
+"Додайте кілька звичок"
+"Кожного дня, після виконання вашої звички, поставте галочку в програмі."
+"Продовжуйте в тому ж дусі"
+"Постійно дотримувані звички будуть відмічені повною зірочкою."
+"Відстежуйте свої успіхи"
+"Деталізовані діаграми демонструють вам, як ваші звички покращилися з часом."
+"15 хвилин"
+"30 хвилин"
+"1 година"
+"2 години"
+"4 години"
+"8 годин"
+"Відмічати повторення коротким натисканням"
+"Зручніше, але може стати причиною випадкових натискань."
+"Інтервал відкладення нагадувань"
+"Оцінити цю програму в Google Play"
+"Надіслати відгук розробнику"
+"Подивитись вихідний код на GitHub"
+"Переглянути вступні інструкції"
+"Посилання"
+"Поведінка"
+"Назва"
+"Показати архівовані"
+"Налаштування"
+"Інтервал відкладення"
+"Чи знаєте ви?"
+"Щоб змінити порядок записів, натисніть і утримуйте назву запису, потім перетягніть запис на потрібне місце."
+"В альбомному режимі відображається більше днів."
+"Видалити звички"
+"Звички будуть видалені. Ця дія не може бути скасованою."
+"По вихідних"
+"По буднях"
+"Кожного дня"
+"Оберіть дні"
+"Експортувати дані (CVS)"
+"Готово"
+"Очистити"
+"Оберіть години"
+"Оберіть хвилини"
+
+
+"Розвивайте корисні звички і відстежуйте свої успіхи (без реклами)"
+"Трекер звичок Loop допоможе вам розвивати та підтримувати хороші звички, що дозволить вам досягнути ваших довгострокових цілей. Докладні графіки та статистичні дані показують вам, як ваші звички покращилися з часом. Трекер не містить реклами і є програмою з відкритим вихідним кодом."
+"<b>Простий, красивий і сучасний інтерфейс</b>
+Loop має легкий у використанні мінімалістичний інтерфейс, що відповідає принципам Material Design."
+"<b>Оцінка звичок</b>
+На додаток до відображення поточної серії повторень, програма має передовий алгоритм розрахунку \"сили\" ваших звичок. Кожне повторення робить вашу звичку \"сильніше\", а кожний пропуск - \"слабкіше\". Проте, кілька пропущених днів після тривалої серії повторень не зведуть ваші успіхи повністю нанівець."
+"<b>Детальні діаграми і статистичні дані</b>
+За допомогою красивих і деталізованих діаграм ви з легкістю можете переглянути, як ваші звички закріпилися з часом. Прокрутіть назад, щод переглянути повну історію ваших звичок."
+"<b>Гнучкий графік</b>
+Підтримуються як щоденні звички, так і звички з більш складними графіками, наприклад: 3 рази на тиждень, один раз на два тижні, через день."
+"<b>Нагадування</b>
+Створюйте окремі нагадування для кожної звички в певні години дня. З легкістю відмічайте, пропускайте або відкладайте виконання звички безпосередньо із повідомлення, не відкриваючи програму."
+"<b>Повна відсутність реклами та відкритий вихідний код</b>
+У програмі абсолютно немає реклами, дратівливих повідомлень або нав'язливих дозволів,і ніколи не буде. Весь вихідний код доступний на умовах ліцензії GPLv3."
+"<b>Оптимізовано для \"розумних\" годинників</b>
+Нагадування можуть бути відмічені, відкладені або пропущені безпосередньо з вашого годинника Android Wear."
+"Про програму"
+"Перекладачі"
+"Розробники"
+
+
+"Версія %s"
+"Частота"
+"Галочка"
+
+
+"Сила"
+"Кращі серії"
+"Поточна серія"
+"Кількість повторень"
+"Останні %d днів"
+"Останні %d тижнів"
+"Останні %d місяців"
+"Останні %d років"
+
+
+"За весь час"
+"Кожен день"
+"Кожен тиждень"
+"2 рази на тиждень"
+"5 разів на тиждень"
+"Інше ..."
+"Допомога та FAQ"
+"Помилка експорту даних."
+"Помилка імпорту даних."
+
+
+"Неможливо визначити тип файлу."
+"Звички успішно імпортовані."
+"Повна резервна копія успішно експортована."
+"Імпортувати дані"
+"Експортувати повну резервну копію"
+"Підтримує імпорт повної резервної копії, експортованої цією програмою, а також файлів, згенерованих програмами Tickmate, HabitBull та Rewire. Більше інформації дивіться в FAQ."
+"Генерує файли, які можна відкрити в програмах для роботи з таблицями (таких як Microsoft Excel або OpenOffice Calc). Цей файл не можна імпортувати назад."
+"Генерує файл, що містить всі ваші дані. Цей файл можна імпортувати назад."
+"Помилка генерації звіту про помилку."
+"Згенерувати звіт про помилку"
+"Усунення несправностей"
+"Допоможіть перекласти цю програму"
+"Нічний режим"
+"Використовувати справжній чорний в нічному режимі"
+"Замінює сірий фон на справжній чорний в нічному режимі. Зменшує використання батареї в телефонах з дисплеєм AMOLED."
+"Інтерфейс"
+"Зворотній порядок днів"
+"Відображати дні в зворотному порядку на головному екрані"
+"День"
+"Тиждень"
+"Місяць"
+
+
+"Квартал"
+"Рік"
+
+
+
+"раз за"
+"Кожні %d днів"
+"Кожні %d тижнів"
+"Кожні %d місяців"
+
+
+"Стабільність"
+"Звук нагадування"
+
+
+"Немає"
+
\ No newline at end of file
diff --git a/app/src/main/res/menu-v21/show_habit_fragment_menu.xml b/app/src/main/res/values-v21/dimens.xml
similarity index 80%
rename from app/src/main/res/menu-v21/show_habit_fragment_menu.xml
rename to app/src/main/res/values-v21/dimens.xml
index c4a051c8c..9b0bd618c 100644
--- a/app/src/main/res/menu-v21/show_habit_fragment_menu.xml
+++ b/app/src/main/res/values-v21/dimens.xml
@@ -18,11 +18,6 @@
~ with this program. If not, see .
-->
-
-
-
-
\ No newline at end of file
+
+ -6dp
+
\ No newline at end of file
diff --git a/app/src/main/res/values-v21/styles.xml b/app/src/main/res/values-v21/styles.xml
index ae57bd247..a1ebac70b 100644
--- a/app/src/main/res/values-v21/styles.xml
+++ b/app/src/main/res/values-v21/styles.xml
@@ -18,30 +18,12 @@
-->
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
similarity index 63%
rename from app/src/main/res/values-zh/strings.xml
rename to app/src/main/res/values-zh-rCN/strings.xml
index fe4e1c59a..24348045e 100644
--- a/app/src/main/res/values-zh/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -9,9 +9,9 @@
"取消存档"
"添加新习惯"
"选择颜色"
-"习惯建成."
-"习惯删除."
-"习惯恢复成功"
+"习惯已创建"
+"习惯已删除"
+"习惯已恢复"
"没有可以撤销的习惯."
"没有可以恢复的习惯."
"习惯已被修改."
@@ -24,21 +24,21 @@
"习惯强度"
"历史"
"取消"
-"提醒问题(你xxx了吗)"
+"提醒问题(你今天 xxx 了吗)"
"重复"
"次每"
"天"
"提醒"
-"丢弃"
+"放弃"
"保存"
-"记录"
+"连续完成次数"
"你还没有任何习惯"
"长按 标记/取消标记"
-"关"
+"关闭"
"习惯标题不能为空"
"数字必须大于零."
"每天最多重复一次"
@@ -55,30 +55,30 @@
"保持习惯"
"当连续完成习惯达到一定时间时,你将可以点亮该习惯的成就之星"
"记录你的进步"
-"详细的图形信息显示你的进步"
+"详细图表展示长期以来习惯养成情况"
"15分钟"
"30分钟"
"1小时"
"2小时"
"4小时"
"8小时"
-"短时间按来记录习惯"
+"轻触以启动重复"
"更加方便,但有可能造成意外记录"
"提醒延迟间隔"
"去Play商店评价这个应用"
"发送反馈给开发者"
-"看源代码在GitHub"
+"在Github上查看源代码"
"阅读应用介绍"
"链接"
"功能"
"习惯标题"
"显示已存档的习惯"
"设置"
-"延迟时间间隔"
+"推迟提醒间隔"
"你知道吗?"
"如果要重新排列习惯,按住习惯的名字拖到想要的位置"
-"横过来手机可以看到更多天数的习惯"
-"删除"
+"转至横屏查看更多日期"
+"删除该习惯"
"习惯会被永久删除,且该操作不可恢复"
"周末"
"工作日"
@@ -96,34 +96,34 @@
"建立好习惯且随着时间记录你的进步(无广告)"
-"循环帮助你建立和维持好习惯,达到长期目标。详细的图片和数据显示你的进步。完全无广告和开源软件。"
+"本应用帮助你养成并保持习惯,并能记录长期目标。详尽图表以及统计数据能展示长期以来习惯养成情况。本应用已开源且永无广告。"
"<b>简单,美观,现代的界面</b>
微型界面易于应用。"
-"<b> 习惯分数</b>
-除了显示你目前的习惯连胜,循环习惯记录有高级的算法计算你的习惯强度。每重复一次习惯能使你的习惯强度加强,每次错过会使习惯强度减弱。然而,几次错过并不会完全破坏你整个的进步。"
+"<b>习惯养成得分</b>
+本应用不仅仅展示您当前连续完成次数,更有独特算法计算当前习惯的强度。每次重复达成都将让您的当前习惯更加有力,相应地,如果你忘记了完成了当日习惯,该习惯强度将被削弱。但是,如果经历了相当长一段连续达成时间,有那么几天没有完成习惯也不会完全毁掉您的整个习惯养成进度。"
"<b> 详细的图片和数据分析</b>
详细的图片能清晰的显示随着时间你习惯的进步。往后滚动屏幕可以看完整的历史纪录。"
"<b> 灵活的时间表</b>
支持日常习惯或者更加复杂时间间隔的习惯(例如每周3次,每周1次)"
"<b>提醒</b>
-在可以选择为每个习惯设置特定的个人提醒。不需要打开应用就可以记录,忽略,或者推迟的习惯。"
-"<b>完全无广告和开源软件</b>
-完全无广告和烦人的提醒,并且永远也不会有。完整的代码在GPLv3。"
-"<b>可以用在智能手表上</b>
-提醒可以直接在安卓手表上 记录,忽略,或者推迟。"
+为每个习惯添加一个提醒,应用将在每天指定时间提醒您。您可以在通知栏轻松完成诸如将习惯标记为已完成、忽略提醒以及推迟提醒等操作,而完全不必打开应用。"
+"<b>完全无广告,开源</b>
+完全没有广告,没有过分的权限申请,并且永远也不会有。完整的代码以 GPLv3 协议发布。"
+"<b>已为智能手表优化</b>
+在 Android Wear 手表上,显示提醒,记录习惯,忽略提醒,或者推迟习惯。"
"关于应用"
"翻译者"
"开发者"
-"%s版"
+"当前版本:%s"
"频率"
"标记"
"强度"
-"最长连胜"
-"目前连胜"
+"最佳连续完成次数"
+"当前连续完成次数"
"重复次数"
"过去 %d 天"
"过去 %d 周"
@@ -137,20 +137,49 @@
"每周两次"
"每周5次"
"自定义"
-"帮助&常见问题"
+"帮助 & 常见问题"
"导出数据失败"
"导入数据失败"
-"无法识别"
+"无法识别文件"
"习惯导入成功"
"习惯备份成功"
-"导入数据 "
+"导入数据"
"导出完整备份"
-"支持此应用导出的备份文件, 也支持Tickmate , HabitBull 或 rewire 导出文件,。请参阅常见问题以获取更多信息。"
-"生成可以通过电子表格软件打开的文件,如Microsoft Excel或OpenOffice Calc。此文件无法重新导入。"
-"生成一个包含所有数据的文件。该文件可以重新导入回来。"
+"支持此应用导出的备份文件, 也支持 Tickmate , HabitBull 或 Rewire 导出文件,请参阅常见问题以获取更多信息。"
+"生成可以通过电子表格软件打开的文件,如 Microsoft Excel 或 OpenOffice Calc。该文件无法重新导入。"
+"生成一个包含所有数据的文件。该文件可以重新导入。"
"错误报告生成失败"
"生成错误报告"
"故障排除"
+"帮助翻译这个应用"
+"夜间模式"
+"在夜间模式中使用纯黑色"
+"以纯黑色背景代替夜间模式中的灰色背景。
+这将帮助减少 AMOLED 屏幕手机的电量使用。"
+"界面"
+"反向顺序的日子"
+"在主界面显示反向顺序的日子"
+"天"
+"周"
+"月"
+
+
+"季度"
+"年"
+
+
+
+"次每"
+"每 %d 天"
+"每 %d 周"
+"每 %d 月"
+
+
+"成绩"
+"提醒声音"
+
+
+"无"
\ No newline at end of file
diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml
new file mode 100644
index 000000000..926c20863
--- /dev/null
+++ b/app/src/main/res/values-zh-rTW/strings.xml
@@ -0,0 +1,180 @@
+
+
+"循環習慣記錄"
+"習慣"
+"偏好設定"
+"編輯"
+"刪除"
+"存檔"
+"取消存檔"
+"加入新習慣"
+"選擇顏色"
+"已創建的習慣"
+"刪除習慣"
+"恢復習慣"
+"沒有可以撤銷的習慣"
+"沒有可以恢復的習慣"
+"習慣已經被修改"
+
+
+"修改已取消"
+"習慣存檔成功"
+"未完成"
+"總覽"
+"強度"
+"歴史紀錄"
+"清空"
+"問題(你今天…了嗎?)"
+
+
+"重複"
+"次每"
+"日"
+"提醒"
+"放棄"
+"保存"
+
+
+"記錄"
+"你還沒有任何習慣"
+"長按 標記/取消標記"
+"關閉"
+"習慣標題不能空白"
+"數字必需大於零"
+"每天最多重複一次"
+"添加習慣"
+"編輯習慣"
+"標記"
+"稍後"
+
+
+"歡迎"
+"習以為常幫你創建並保持你的習慣。"
+"建立一些習慣吧"
+"每天,在完成你的習慣後,進入APP來打個勾吧。"
+"堅持下去!"
+"習以為常幫助你建立並維持好習慣"
+"追蹤你的進步"
+"詳細的圖示將顯示你的進步"
+"15分鐘"
+"30分鐘"
+"1小時"
+"2小時"
+"4小時"
+"8小時"
+"短按來記錄習慣"
+"但有可能造成意外記錄"
+"提醒延遲間隔"
+"評價這個APP在Google Play"
+"發送改進意見給開發者"
+"在GitHub上查看原始碼"
+"App簡介"
+"連結"
+"功能"
+"習慣標題"
+"顯示已存檔的習慣"
+"設定"
+"延遲時間間隔"
+"你知道嗎?"
+"如果要重新排列習慣,拖曳習慣到想要的位置"
+"把手機橫放可以看到更多天數"
+"刪除"
+"習慣會被永久刪除,而且該操作不可恢復"
+"周末"
+"工作日"
+"每天"
+"選擇天數"
+"導出數據 (CSV)"
+"完成"
+"取消"
+"選擇小時"
+"選擇分鐘"
+
+
+"建立好新習慣並隨著時間進步 (無廣告)"
+"習以為常幫助你建立和維持好習慣,達到長期目標。詳細的圖片和數字顯示出你的進步。完成無廣告的開源軟體。"
+"<b>簡單,美觀,時尚的界面</b>
+輕量型界面易於使用。"
+"<b> 習慣分數</b>
+除了顯示你目前的習慣連勝,循環記錄有高級的演算法計算你的習慣強度。每完成一次能使你的習慣強度增加,每次錯過則會減弱。然而,幾次錯過並不會影響你的習慣進步。"
+"<b> 詳細的圖片和資料分析</b>
+詳細的圖片能顯示出你的進步。往後滑動可以查看完整的歷史記錄。"
+"<b> 靈活的時間表</b>
+支援日常習慣或者是更複雜的時間間隔(例如每周3次,每周1次)"
+"<b>提醒</b>
+可以選擇為每個習慣加入提醒。不需要打開程式就可以 記錄、忽略或者是延遲習慣。"
+"<b>完全無廣告和開源軟體</b>
+永遠不會有廣告和煩人的提醒。完整的程式碼在GPLv3。"
+"<b>可以用在智慧型手錶上</b>
+提醒可以直接在android手錶上 記錄、忽略,或者延遲。"
+"關於本程式"
+"翻譯者"
+"開發者"
+
+
+"%s 版"
+"頻率"
+"標記"
+
+
+"強度"
+"最長連勝"
+"目前連勝"
+"重複次數"
+"過去 %d 天"
+"過去 %d 周"
+"過去 %d 月"
+"過去 %d 年"
+
+
+"全部"
+"每天"
+"每周"
+"每周兩次"
+"每周五次"
+"自訂"
+"幫助&常見問題"
+"輸出資料失數"
+"輸入資料失數"
+
+
+"無法辨識"
+"習慣輸入成功"
+"習慣備份成功"
+"輸入資料"
+"導出完整備份"
+"支援此程式輸出的資料, 也支援Tickmate , HabitBull 或 rewire 導出文件,。請參閱常見問題以獲得更多訊息。"
+"生成可以利用表格程式打開的文件,如Microsoft Excel或OpenOffice Calc。此文件無法重新導入。"
+"生成一個包含所有資料的文件。該文件可以再重新輸入回来。"
+"錯誤報告:生成失敗"
+"生成錯誤報告"
+"問題排除"
+"協助翻譯本程式"
+"夜間模式"
+"在夜間模式中使用深色設定"
+"在夜間模式中,使用黑色背景取代灰色背景。這樣可以減少AMOLED螢幕的手機電量消耗。"
+"介面"
+"反向排序日期"
+"在主畫面上顯示反向排序的結果"
+"日"
+"周"
+"月"
+
+
+"季"
+"年"
+
+
+
+"次每"
+"每 %d 天"
+"每 %d 周"
+"每 %d 月"
+
+
+"分數"
+"提醒音效"
+
+
+"無"
+
\ No newline at end of file
diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml
new file mode 100644
index 000000000..3fcf43e96
--- /dev/null
+++ b/app/src/main/res/values/attrs.xml
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index f7f6d597f..29616a09e 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -15,15 +15,54 @@
limitations under the License.
-->
-
- #37474f
- #263238
- #e6e6e6
- @color/grey_900
- @color/grey_800
- @color/grey_600
- #ffffff
+
+ - @color/red_700
+ - @color/deep_orange_700
+ - @color/yellow_800
+ - @color/lime_700
+ - @color/green_700
+ - @color/teal_600
+ - @color/cyan_600
+ - @color/light_blue_600
+ - @color/deep_purple_600
+ - @color/purple_600
+ - @color/pink_600
+ - @color/grey_800
+ - @color/grey_500
+
+
+
+ - @color/red_200
+ - @color/deep_orange_200
+ - @color/yellow_200
+ - @color/lime_200
+ - @color/green_A200
+ - @color/teal_200
+ - @color/cyan_200
+ - @color/light_blue_200
+ - @color/deep_purple_200
+ - @color/purple_200
+ - @color/pink_200
+ - @color/grey_100
+ - @color/grey_500
+
+
+
+ - @color/red_800
+ - @color/deep_orange_800
+ - @color/yellow_800
+ - @color/lime_800
+ - @color/green_700
+ - @color/teal_700
+ - @color/cyan_700
+ - @color/light_blue_700
+ - @color/deep_purple_700
+ - @color/purple_700
+ - @color/pink_700
+ - @color/black_aa
+ - @color/black_aa
+
#f2f2f2
@@ -53,267 +92,267 @@
#808080
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ #FFEBEE
+ #FFCDD2
+ #EF9A9A
+ #E57373
+ #EF5350
+ #F44336
+ #E53935
+ #D32F2F
+ #C62828
+ #B71C1C
+ #FF8A80
+ #FF5252
+ #FF1744
+ #D50000
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ #EDE7F6
+ #D1C4E9
+ #B39DDB
+ #9575CD
+ #7E57C2
+ #673AB7
+ #5E35B1
+ #512DA8
+ #4527A0
+ #311B92
+ #B388FF
+ #7C4DFF
+ #651FFF
+ #6200EA
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ #E1F5FE
+ #B3E5FC
+ #81D4FA
+ #4FC3F7
+ #29B6F6
+ #03A9F4
+ #039BE5
+ #0288D1
+ #0277BD
+ #01579B
+ #80D8FF
+ #40C4FF
+ #00B0FF
+ #0091EA
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ #E8F5E9
+ #C8E6C9
+ #A5D6A7
+ #81C784
+ #66BB6A
+ #4CAF50
+ #43A047
+ #388E3C
+ #2E7D32
+ #1B5E20
+ #B9F6CA
+ #69F0AE
+ #00E676
+ #00C853
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ #FFFDE7
+ #FFF9C4
+ #FFF59D
+ #FFF176
+ #FFEE58
+ #FFEB3B
+ #FDD835
+ #FBC02D
+ #F9A825
+ #F57F17
+ #FFFF8D
+ #FFFF00
+ #FFEA00
+ #FFD600
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ #FBE9E7
+ #FFCCBC
+ #FFAB91
+ #FF8A65
+ #FF7043
+ #FF5722
+ #F4511E
+ #E64A19
+ #D84315
+ #BF360C
+ #FF9E80
+ #FF6E40
+ #FF3D00
+ #DD2C00
-
-
-
-
-
-
-
-
-
-
+ #ECEFF1
+ #CFD8DC
+ #B0BEC5
+ #90A4AE
+ #78909C
+ #607D8B
+ #546E7A
+ #455A64
+ #37474F
+ #263238
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ #FCE4EC
+ #F8BBD0
+ #F48FB1
+ #F06292
+ #EC407A
+ #E91E63
+ #D81B60
+ #C2185B
+ #AD1457
+ #880E4F
+ #FF80AB
+ #FF4081
+ #F50057
+ #C51162
-
-
-
-
-
+ #E8EAF6
+ #C5CAE9
+ #9FA8DA
+ #7986CB
+ #5C6BC0
#3F51B5
-
-
-
-
-
-
-
-
+ #3949AB
+ #303F9F
+ #283593
+ #1A237E
+ #8C9EFF
+ #536DFE
+ #3D5AFE
+ #304FFE
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ #E0F7FA
+ #B2EBF2
+ #80DEEA
+ #4DD0E1
+ #26C6DA
+ #00BCD4
+ #00ACC1
+ #0097A7
+ #00838F
+ #006064
+ #84FFFF
+ #18FFFF
+ #00E5FF
+ #00B8D4
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ #F1F8E9
+ #DCEDC8
+ #C5E1A5
+ #AED581
+ #9CCC65
+ #8BC34A
+ #7CB342
+ #689F38
+ #558B2F
+ #33691E
+ #CCFF90
+ #B2FF59
+ #76FF03
+ #64DD17
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ #FFF8E1
+ #FFECB3
+ #FFE082
+ #FFD54F
+ #FFCA28
+ #FFC107
+ #FFB300
+ #FFA000
+ #FF8F00
+ #FF6F00
+ #FFE57F
+ #FFD740
+ #FFC400
+ #FFAB00
-
-
-
-
-
-
-
-
-
-
+ #EFEBE9
+ #D7CCC8
+ #BCAAA4
+ #A1887F
+ #8D6E63
+ #795548
+ #6D4C41
+ #5D4037
+ #4E342E
+ #3E2723
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ #F3E5F5
+ #E1BEE7
+ #CE93D8
+ #BA68C8
+ #AB47BC
+ #9C27B0
+ #8E24AA
+ #7B1FA2
+ #6A1B9A
+ #4A148C
+ #EA80FC
+ #E040FB
+ #D500F9
+ #AA00FF
-
-
-
-
-
-
-
+ #E3F2FD
+ #BBDEFB
+ #90CAF9
+ #64B5F6
+ #42A5F5
+ #2196F3
+ #1E88E5
#1976D2
-
-
-
-
-
-
+ #1565C0
+ #0D47A1
+ #82B1FF
+ #448AFF
+ #2979FF
+ #2962FF
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ #E0F2F1
+ #B2DFDB
+ #80CBC4
+ #4DB6AC
+ #26A69A
+ #009688
+ #00897B
+ #00796B
+ #00695C
+ #004D40
+ #A7FFEB
+ #64FFDA
+ #1DE9B6
+ #00BFA5
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ #F9FBE7
+ #F0F4C3
+ #E6EE9C
+ #DCE775
+ #D4E157
+ #CDDC39
+ #C0CA33
+ #AFB42B
+ #9E9D24
+ #827717
+ #F4FF81
+ #EEFF41
+ #C6FF00
+ #AEEA00
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ #FFF3E0
+ #FFE0B2
+ #FFCC80
+ #FFB74D
+ #FFA726
+ #FF9800
+ #FB8C00
+ #F57C00
+ #EF6C00
+ #E65100
+ #FFD180
+ #FFAB40
+ #FF9100
+ #FF6D00
#FAFAFA
#F5F5F5
@@ -323,6 +362,31 @@
#9E9E9E
#757575
#616161
+ #525252
#424242
+ #303030
+ #282828
#212121
+ #101010
+
+ #ffffff
+ #000000
+
+ #ef000000
+ #cf000000
+ #af000000
+ #8f000000
+ #6f000000
+ #4f000000
+ #2f000000
+ #0f000000
+
+ #efffffff
+ #cfffffff
+ #afffffff
+ #8fffffff
+ #6fffffff
+ #4fffffff
+ #2fffffff
+ #0fffffff
\ No newline at end of file
diff --git a/app/src/main/res/values/constants.xml b/app/src/main/res/values/constants.xml
index a1fdd7591..05b679f93 100644
--- a/app/src/main/res/values/constants.xml
+++ b/app/src/main/res/values/constants.xml
@@ -24,4 +24,48 @@
mailto:dev@loophabits.org?subject=Bug%20Report%20-%20Loop%20Habit%20Tracker
https://github.com/iSoron/uhabits
https://poeditor.com/join/project/8DWX5pfjS0
+
+
+ - @string/interval_15_minutes
+ - @string/interval_30_minutes
+ - @string/interval_1_hour
+ - @string/interval_2_hour
+ - @string/interval_4_hour
+ - @string/interval_8_hour
+
+
+
+ - 15
+ - 30
+ - 60
+ - 120
+ - 240
+ - 480
+
+
+
+ - @string/every_day
+ - @string/every_week
+ - @string/two_times_per_week
+ - @string/five_times_per_week
+ - @string/custom_frequency
+
+
+
+ - @string/day
+ - @string/week
+ - @string/month
+ - @string/quarter
+ - @string/year
+
+
+
+ - 1
+ - 7
+ - 31
+ - 92
+ - 365
+
+
+ 15
\ No newline at end of file
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index 33880dbc2..42c044142 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -19,55 +19,14 @@
20dp
- 42dp
+ 42dp
+ 48dp
450dp
8dp
@dimen/regularTextSize
16sp
14sp
10sp
-
-
- - @string/interval_15_minutes
- - @string/interval_30_minutes
- - @string/interval_1_hour
- - @string/interval_2_hour
- - @string/interval_4_hour
- - @string/interval_8_hour
-
-
-
- - 15
- - 30
- - 60
- - 120
- - 240
- - 480
-
-
-
- - @string/every_day
- - @string/every_week
- - @string/two_times_per_week
- - @string/five_times_per_week
- - @string/custom_frequency
-
-
-
- - Day
- - Week
- - Month
- - Quarter
- - Year
-
-
-
- - 1
- - 7
- - 31
- - 92
- - 365
-
-
- 15
+ 160dp
+ -10dp
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 858b3e5d9..23f9e3719 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -140,6 +140,7 @@
Every week
2 times per week
5 times per week
+
Custom …
Help & FAQ
Failed to export data.
@@ -155,5 +156,28 @@
Failed to generate bug report.
Generate bug report
Troubleshooting
+
Help translate this app
+ Night mode
+ Use pure black in night mode
+ Replaces gray backgrounds with pure black in night mode. Reduces battery usage in phones with AMOLED display.
+ Interface
+ Reverse order of days
+ Show days in reverse order on the main screen
+
+ Day
+ Week
+ Month
+ Quarter
+ Year
+
+
+ time in
+
+ Every %d days
+ Every %d weeks
+ Every %d months
+ Score
+ Reminder sound
+ None
\ No newline at end of file
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index dcf0588eb..d0c3dde42 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -18,10 +18,140 @@
-->
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
diff --git a/app/src/main/res/values/styles_dialog.xml b/app/src/main/res/values/styles_dialog.xml
index 3f45780de..91f07fe20 100644
--- a/app/src/main/res/values/styles_dialog.xml
+++ b/app/src/main/res/values/styles_dialog.xml
@@ -45,19 +45,19 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-