Merge branch 'release/1.5.0'
14
CHANGELOG.md
@@ -1,5 +1,19 @@
|
|||||||
# Changelog
|
# 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)
|
### 1.4.1 (April 9, 2016)
|
||||||
|
|
||||||
* Show error message on widgets, instead of crashing
|
* Show error message on widgets, instead of crashing
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ source.
|
|||||||
[![Habit strength][screen3th]][screen3]
|
[![Habit strength][screen3th]][screen3]
|
||||||
[![Habit history and streaks][screen4th]][screen4]
|
[![Habit history and streaks][screen4th]][screen4]
|
||||||
[![Widgets][screen5th]][screen5]
|
[![Widgets][screen5th]][screen5]
|
||||||
|
[![Night mode][screen6th]][screen6]
|
||||||
|
|
||||||
## Installing
|
## 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
|
* **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
|
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
|
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
|
* **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
|
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
|
[screen3]: screenshots/original/uhabits3.png
|
||||||
[screen4]: screenshots/original/uhabits4.png
|
[screen4]: screenshots/original/uhabits4.png
|
||||||
[screen5]: screenshots/original/uhabits5.png
|
[screen5]: screenshots/original/uhabits5.png
|
||||||
|
[screen6]: screenshots/original/uhabits6.png
|
||||||
[screen1th]: screenshots/thumbs/uhabits1.png
|
[screen1th]: screenshots/thumbs/uhabits1.png
|
||||||
[screen2th]: screenshots/thumbs/uhabits2.png
|
[screen2th]: screenshots/thumbs/uhabits2.png
|
||||||
[screen3th]: screenshots/thumbs/uhabits3.png
|
[screen3th]: screenshots/thumbs/uhabits3.png
|
||||||
[screen4th]: screenshots/thumbs/uhabits4.png
|
[screen4th]: screenshots/thumbs/uhabits4.png
|
||||||
[screen5th]: screenshots/thumbs/uhabits5.png
|
[screen5th]: screenshots/thumbs/uhabits5.png
|
||||||
|
[screen6th]: screenshots/thumbs/uhabits6.png
|
||||||
[poedit]: https://poeditor.com/join/project/8DWX5pfjS0
|
[poedit]: https://poeditor.com/join/project/8DWX5pfjS0
|
||||||
[playstore]: https://play.google.com/store/apps/details?id=org.isoron.uhabits
|
[playstore]: https://play.google.com/store/apps/details?id=org.isoron.uhabits
|
||||||
[releases]: https://github.com/iSoron/uhabits/releases
|
[releases]: https://github.com/iSoron/uhabits/releases
|
||||||
[fdroid]: http://f-droid.org/app/org.isoron.uhabits
|
[fdroid]: http://f-droid.org/app/org.isoron.uhabits
|
||||||
[dev-guide]: https://github.com/iSoron/uhabits/wiki/Developer-guidelines
|
[dev-guide]: https://github.com/iSoron/uhabits/wiki/Developer-guidelines
|
||||||
[build]: https://github.com/iSoron/uhabits/wiki/Developer-guidelines#building
|
[build]: https://github.com/iSoron/uhabits/wiki/Developer-guidelines#building
|
||||||
|
[beta]: https://play.google.com/apps/testing/org.isoron.uhabits
|
||||||
|
|||||||
@@ -2,14 +2,14 @@ apply plugin: 'com.android.application'
|
|||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 23
|
compileSdkVersion 23
|
||||||
buildToolsVersion "23.0.1"
|
buildToolsVersion "23.0.3"
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "org.isoron.uhabits"
|
applicationId "org.isoron.uhabits"
|
||||||
minSdkVersion 15
|
minSdkVersion 15
|
||||||
targetSdkVersion 23
|
targetSdkVersion 23
|
||||||
|
|
||||||
buildConfigField "Integer", "databaseVersion", "13"
|
buildConfigField "Integer", "databaseVersion", "14"
|
||||||
buildConfigField "String", "databaseFilename", "\"uhabits.db\""
|
buildConfigField "String", "databaseFilename", "\"uhabits.db\""
|
||||||
|
|
||||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||||
@@ -32,18 +32,31 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
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 'com.github.paolorotolo:appintro:3.4.0'
|
||||||
compile 'org.apmem.tools:layouts:1.10@aar'
|
compile 'org.apmem.tools:layouts:1.10@aar'
|
||||||
compile 'com.opencsv:opencsv:3.7'
|
compile 'com.opencsv:opencsv:3.7'
|
||||||
compile project(':libs:drag-sort-listview:library')
|
compile project(':libs:drag-sort-listview:library')
|
||||||
compile files('libs/ActiveAndroid.jar')
|
compile files('libs/ActiveAndroid.jar')
|
||||||
|
|
||||||
androidTestCompile 'com.android.support:support-annotations:23.1.1'
|
androidTestCompile 'com.android.support:support-annotations:23.3.0'
|
||||||
androidTestCompile 'com.android.support.test:runner:0.4.1'
|
androidTestCompile 'com.android.support.test:runner:0.5'
|
||||||
androidTestCompile 'com.android.support.test:rules:0.4.1'
|
androidTestCompile 'com.android.support.test:rules:0.5'
|
||||||
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.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'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 7.8 KiB |
|
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 8.6 KiB |
|
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 8.5 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 8.6 KiB |
|
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 8.3 KiB |
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 6.3 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 6.7 KiB After Width: | Height: | Size: 8.3 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 9.9 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 9.8 KiB After Width: | Height: | Size: 8.2 KiB |
|
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 17 KiB |
@@ -25,6 +25,7 @@ import android.os.Looper;
|
|||||||
import android.support.test.InstrumentationRegistry;
|
import android.support.test.InstrumentationRegistry;
|
||||||
|
|
||||||
import org.isoron.uhabits.helpers.DateHelper;
|
import org.isoron.uhabits.helpers.DateHelper;
|
||||||
|
import org.isoron.uhabits.helpers.UIHelper;
|
||||||
import org.isoron.uhabits.tasks.BaseTask;
|
import org.isoron.uhabits.tasks.BaseTask;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
|
||||||
@@ -50,6 +51,7 @@ public class BaseTest
|
|||||||
targetContext = InstrumentationRegistry.getTargetContext();
|
targetContext = InstrumentationRegistry.getTargetContext();
|
||||||
testContext = InstrumentationRegistry.getContext();
|
testContext = InstrumentationRegistry.getContext();
|
||||||
|
|
||||||
|
UIHelper.setFixedTheme(R.style.AppBaseTheme);
|
||||||
DateHelper.setFixedLocalTime(FIXED_LOCAL_TIME);
|
DateHelper.setFixedLocalTime(FIXED_LOCAL_TIME);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
package org.isoron.uhabits.ui;
|
package org.isoron.uhabits.ui;
|
||||||
|
|
||||||
import android.support.test.espresso.NoMatchingViewException;
|
import android.support.test.espresso.NoMatchingViewException;
|
||||||
|
import android.support.test.espresso.contrib.RecyclerViewActions;
|
||||||
|
|
||||||
import org.isoron.uhabits.R;
|
import org.isoron.uhabits.R;
|
||||||
import org.isoron.uhabits.models.Habit;
|
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.onData;
|
||||||
import static android.support.test.espresso.Espresso.onView;
|
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.Espresso.pressBack;
|
||||||
import static android.support.test.espresso.action.ViewActions.click;
|
import static android.support.test.espresso.action.ViewActions.click;
|
||||||
import static android.support.test.espresso.action.ViewActions.longClick;
|
import static android.support.test.espresso.action.ViewActions.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.assertion.ViewAssertions.matches;
|
||||||
import static android.support.test.espresso.matcher.RootMatchers.isPlatformPopup;
|
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.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.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.withContentDescription;
|
||||||
import static android.support.test.espresso.matcher.ViewMatchers.withEffectiveVisibility;
|
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.withId;
|
||||||
|
import static android.support.test.espresso.matcher.ViewMatchers.withParent;
|
||||||
import static android.support.test.espresso.matcher.ViewMatchers.withText;
|
import static android.support.test.espresso.matcher.ViewMatchers.withText;
|
||||||
import static org.hamcrest.Matchers.allOf;
|
import static org.hamcrest.Matchers.allOf;
|
||||||
|
import static org.hamcrest.Matchers.containsString;
|
||||||
import static org.hamcrest.Matchers.instanceOf;
|
import static org.hamcrest.Matchers.instanceOf;
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
import static org.hamcrest.Matchers.not;
|
import static org.hamcrest.Matchers.not;
|
||||||
@@ -168,13 +172,13 @@ public class MainActivityActions
|
|||||||
public static void deleteHabits(List<String> names)
|
public static void deleteHabits(List<String> names)
|
||||||
{
|
{
|
||||||
selectHabits(names);
|
selectHabits(names);
|
||||||
clickActionModeMenuItem(R.string.delete);
|
clickMenuItem(R.string.delete);
|
||||||
onView(withText("OK"))
|
onView(withText("OK"))
|
||||||
.perform(click());
|
.perform(click());
|
||||||
assertHabitsDontExist(names);
|
assertHabitsDontExist(names);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void clickActionModeMenuItem(int stringId)
|
public static void clickMenuItem(int stringId)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -188,9 +192,34 @@ public class MainActivityActions
|
|||||||
}
|
}
|
||||||
catch(Exception e2)
|
catch(Exception e2)
|
||||||
{
|
{
|
||||||
openContextualActionModeOverflowMenu();
|
clickHiddenMenuItem(stringId);
|
||||||
onView(withText(stringId)).perform(click());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,7 +45,6 @@ import java.util.Random;
|
|||||||
|
|
||||||
import static android.support.test.espresso.Espresso.onData;
|
import static android.support.test.espresso.Espresso.onData;
|
||||||
import static android.support.test.espresso.Espresso.onView;
|
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.Espresso.pressBack;
|
||||||
import static android.support.test.espresso.action.ViewActions.click;
|
import static android.support.test.espresso.action.ViewActions.click;
|
||||||
import static android.support.test.espresso.action.ViewActions.longClick;
|
import static android.support.test.espresso.action.ViewActions.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.isRoot;
|
||||||
import static android.support.test.espresso.matcher.ViewMatchers.withClassName;
|
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.withId;
|
||||||
import static android.support.test.espresso.matcher.ViewMatchers.withText;
|
|
||||||
import static org.hamcrest.Matchers.allOf;
|
import static org.hamcrest.Matchers.allOf;
|
||||||
import static org.hamcrest.Matchers.containsString;
|
import static org.hamcrest.Matchers.containsString;
|
||||||
import static org.hamcrest.Matchers.endsWith;
|
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.instanceOf;
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
import static org.hamcrest.Matchers.startsWith;
|
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.HabitMatchers.withName;
|
||||||
import static org.isoron.uhabits.ui.HabitViewActions.clickAtRandomLocations;
|
import static org.isoron.uhabits.ui.HabitViewActions.clickAtRandomLocations;
|
||||||
import static org.isoron.uhabits.ui.HabitViewActions.toggleAllCheckmarks;
|
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.assertHabitExists;
|
||||||
import static org.isoron.uhabits.ui.MainActivityActions.assertHabitsDontExist;
|
import static org.isoron.uhabits.ui.MainActivityActions.assertHabitsDontExist;
|
||||||
import static org.isoron.uhabits.ui.MainActivityActions.assertHabitsExist;
|
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.deleteHabit;
|
||||||
import static org.isoron.uhabits.ui.MainActivityActions.deleteHabits;
|
import static org.isoron.uhabits.ui.MainActivityActions.deleteHabits;
|
||||||
import static org.isoron.uhabits.ui.MainActivityActions.selectHabit;
|
import static org.isoron.uhabits.ui.MainActivityActions.selectHabit;
|
||||||
@@ -152,20 +150,16 @@ public class MainTest
|
|||||||
|
|
||||||
selectHabits(names);
|
selectHabits(names);
|
||||||
|
|
||||||
clickActionModeMenuItem(R.string.archive);
|
clickMenuItem(R.string.archive);
|
||||||
assertHabitsDontExist(names);
|
assertHabitsDontExist(names);
|
||||||
|
|
||||||
openActionBarOverflowOrOptionsMenu(targetContext);
|
clickMenuItem(R.string.show_archived);
|
||||||
onView(withText(R.string.show_archived))
|
|
||||||
.perform(click());
|
|
||||||
|
|
||||||
assertHabitsExist(names);
|
assertHabitsExist(names);
|
||||||
selectHabits(names);
|
selectHabits(names);
|
||||||
clickActionModeMenuItem(R.string.unarchive);
|
clickMenuItem(R.string.unarchive);
|
||||||
|
|
||||||
openActionBarOverflowOrOptionsMenu(targetContext);
|
clickMenuItem(R.string.show_archived);
|
||||||
onView(withText(R.string.show_archived))
|
|
||||||
.perform(click());
|
|
||||||
|
|
||||||
assertHabitsExist(names);
|
assertHabitsExist(names);
|
||||||
deleteHabits(names);
|
deleteHabits(names);
|
||||||
@@ -227,7 +221,7 @@ public class MainTest
|
|||||||
.onChildView(withId(R.id.label))
|
.onChildView(withId(R.id.label))
|
||||||
.perform(longClick());
|
.perform(longClick());
|
||||||
|
|
||||||
clickActionModeMenuItem(R.string.edit);
|
clickMenuItem(R.string.edit);
|
||||||
|
|
||||||
String modifiedName = "Modified " + new Random().nextInt(10000);
|
String modifiedName = "Modified " + new Random().nextInt(10000);
|
||||||
typeHabitData(modifiedName, "", "1", "1");
|
typeHabitData(modifiedName, "", "1", "1");
|
||||||
@@ -238,7 +232,7 @@ public class MainTest
|
|||||||
assertHabitExists(modifiedName);
|
assertHabitExists(modifiedName);
|
||||||
|
|
||||||
selectHabit(modifiedName);
|
selectHabit(modifiedName);
|
||||||
clickActionModeMenuItem(R.string.color_picker_default_title);
|
clickMenuItem(R.string.color_picker_default_title);
|
||||||
pressBack();
|
pressBack();
|
||||||
|
|
||||||
deleteHabit(modifiedName);
|
deleteHabit(modifiedName);
|
||||||
@@ -272,8 +266,7 @@ public class MainTest
|
|||||||
@Test
|
@Test
|
||||||
public void testSettings()
|
public void testSettings()
|
||||||
{
|
{
|
||||||
openActionBarOverflowOrOptionsMenu(targetContext);
|
clickMenuItem(R.string.settings);
|
||||||
onView(withText(R.string.settings)).perform(click());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -282,8 +275,7 @@ public class MainTest
|
|||||||
@Test
|
@Test
|
||||||
public void testAbout()
|
public void testAbout()
|
||||||
{
|
{
|
||||||
openActionBarOverflowOrOptionsMenu(targetContext);
|
clickMenuItem(R.string.about);
|
||||||
onView(withText(R.string.about)).perform(click());
|
|
||||||
onView(isRoot()).perform(swipeUp());
|
onView(isRoot()).perform(swipeUp());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,8 +285,7 @@ public class MainTest
|
|||||||
@Test
|
@Test
|
||||||
public void testHelp()
|
public void testHelp()
|
||||||
{
|
{
|
||||||
openActionBarOverflowOrOptionsMenu(targetContext);
|
clickMenuItem(R.string.help);
|
||||||
onView(withText(R.string.help)).perform(click());
|
|
||||||
intended(hasAction(Intent.ACTION_VIEW));
|
intended(hasAction(Intent.ACTION_VIEW));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,20 +298,18 @@ public class MainTest
|
|||||||
{
|
{
|
||||||
String name = addHabit();
|
String name = addHabit();
|
||||||
|
|
||||||
openActionBarOverflowOrOptionsMenu(targetContext);
|
clickMenuItem(R.string.settings);
|
||||||
onView(withText(R.string.settings)).perform(click());
|
|
||||||
|
|
||||||
String date = DateHelper.getBackupDateFormat().format(DateHelper.getLocalTime());
|
String date = DateHelper.getBackupDateFormat().format(DateHelper.getLocalTime());
|
||||||
date = date.substring(0, date.length() - 2);
|
date = date.substring(0, date.length() - 2);
|
||||||
|
|
||||||
onData(isPreferenceWithText("Export full backup")).perform(click());
|
clickSettingsItem("Export full backup");
|
||||||
intended(hasAction(Intent.ACTION_SEND));
|
intended(hasAction(Intent.ACTION_SEND));
|
||||||
|
|
||||||
deleteHabit(name);
|
deleteHabit(name);
|
||||||
|
|
||||||
openActionBarOverflowOrOptionsMenu(targetContext);
|
clickMenuItem(R.string.settings);
|
||||||
onView(withText(R.string.settings)).perform(click());
|
clickSettingsItem("Import data");
|
||||||
onData(isPreferenceWithText("Import data")).perform(click());
|
|
||||||
|
|
||||||
onData(allOf(is(instanceOf(String.class)), startsWith("Backups")))
|
onData(allOf(is(instanceOf(String.class)), startsWith("Backups")))
|
||||||
.perform(click());
|
.perform(click());
|
||||||
@@ -339,9 +328,8 @@ public class MainTest
|
|||||||
public void testExportCSV()
|
public void testExportCSV()
|
||||||
{
|
{
|
||||||
addHabit();
|
addHabit();
|
||||||
openActionBarOverflowOrOptionsMenu(targetContext);
|
clickMenuItem(R.string.settings);
|
||||||
onView(withText(R.string.settings)).perform(click());
|
clickSettingsItem("Export as CSV");
|
||||||
onData(isPreferenceWithText("Export as CSV")).perform(click());
|
|
||||||
intended(hasAction(Intent.ACTION_SEND));
|
intended(hasAction(Intent.ACTION_SEND));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -351,9 +339,8 @@ public class MainTest
|
|||||||
@Test
|
@Test
|
||||||
public void testGenerateBugReport()
|
public void testGenerateBugReport()
|
||||||
{
|
{
|
||||||
openActionBarOverflowOrOptionsMenu(targetContext);
|
clickMenuItem(R.string.settings);
|
||||||
onView(withText(R.string.settings)).perform(click());
|
clickSettingsItem("Generate bug report");
|
||||||
onData(isPreferenceWithText("Generate bug report")).perform(click());
|
|
||||||
intended(hasAction(Intent.ACTION_SENDTO));
|
intended(hasAction(Intent.ACTION_SENDTO));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ import android.content.Context;
|
|||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import org.isoron.uhabits.helpers.ColorHelper;
|
|
||||||
import org.isoron.uhabits.helpers.DatabaseHelper;
|
import org.isoron.uhabits.helpers.DatabaseHelper;
|
||||||
import org.isoron.uhabits.helpers.DateHelper;
|
import org.isoron.uhabits.helpers.DateHelper;
|
||||||
import org.isoron.uhabits.models.Habit;
|
import org.isoron.uhabits.models.Habit;
|
||||||
@@ -66,7 +65,7 @@ public class HabitFixtures
|
|||||||
Habit habit = new Habit();
|
Habit habit = new Habit();
|
||||||
habit.name = "Meditate";
|
habit.name = "Meditate";
|
||||||
habit.description = "Did you meditate this morning?";
|
habit.description = "Did you meditate this morning?";
|
||||||
habit.color = ColorHelper.palette[3];
|
habit.color = 3;
|
||||||
habit.freqNum = 1;
|
habit.freqNum = 1;
|
||||||
habit.freqDen = 1;
|
habit.freqDen = 1;
|
||||||
habit.save();
|
habit.save();
|
||||||
@@ -78,7 +77,7 @@ public class HabitFixtures
|
|||||||
Habit habit = createEmptyHabit();
|
Habit habit = createEmptyHabit();
|
||||||
habit.freqNum = 3;
|
habit.freqNum = 3;
|
||||||
habit.freqDen = 7;
|
habit.freqDen = 7;
|
||||||
habit.color = ColorHelper.palette[4];
|
habit.color = 4;
|
||||||
habit.save();
|
habit.save();
|
||||||
|
|
||||||
long day = DateHelper.millisecondsInOneDay;
|
long day = DateHelper.millisecondsInOneDay;
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ import android.test.suitebuilder.annotation.SmallTest;
|
|||||||
|
|
||||||
import org.isoron.uhabits.BaseTest;
|
import org.isoron.uhabits.BaseTest;
|
||||||
import org.isoron.uhabits.commands.ChangeHabitColorCommand;
|
import org.isoron.uhabits.commands.ChangeHabitColorCommand;
|
||||||
import org.isoron.uhabits.helpers.ColorHelper;
|
|
||||||
import org.isoron.uhabits.models.Habit;
|
import org.isoron.uhabits.models.Habit;
|
||||||
import org.isoron.uhabits.unit.HabitFixtures;
|
import org.isoron.uhabits.unit.HabitFixtures;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
@@ -53,12 +52,12 @@ public class ChangeHabitColorCommandTest extends BaseTest
|
|||||||
for(int i = 0; i < 3; i ++)
|
for(int i = 0; i < 3; i ++)
|
||||||
{
|
{
|
||||||
Habit habit = HabitFixtures.createShortHabit();
|
Habit habit = HabitFixtures.createShortHabit();
|
||||||
habit.color = ColorHelper.palette[i+1];
|
habit.color = i+1;
|
||||||
habit.save();
|
habit.save();
|
||||||
habits.add(habit);
|
habits.add(habit);
|
||||||
}
|
}
|
||||||
|
|
||||||
command = new ChangeHabitColorCommand(habits, ColorHelper.palette[0]);
|
command = new ChangeHabitColorCommand(habits, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -80,12 +79,12 @@ public class ChangeHabitColorCommandTest extends BaseTest
|
|||||||
{
|
{
|
||||||
int k = 0;
|
int k = 0;
|
||||||
for(Habit h : habits)
|
for(Habit h : habits)
|
||||||
assertThat(h.color, equalTo(ColorHelper.palette[++k]));
|
assertThat(h.color, equalTo(++k));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkNewColors()
|
private void checkNewColors()
|
||||||
{
|
{
|
||||||
for(Habit h : habits)
|
for(Habit h : habits)
|
||||||
assertThat(h.color, equalTo(ColorHelper.palette[0]));
|
assertThat(h.color, equalTo(0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
|
|
||||||
package org.isoron.uhabits.unit.models;
|
package org.isoron.uhabits.unit.models;
|
||||||
|
|
||||||
import android.graphics.Color;
|
|
||||||
import android.support.test.runner.AndroidJUnit4;
|
import android.support.test.runner.AndroidJUnit4;
|
||||||
import android.test.suitebuilder.annotation.SmallTest;
|
import android.test.suitebuilder.annotation.SmallTest;
|
||||||
|
|
||||||
@@ -78,7 +77,7 @@ public class HabitTest extends BaseTest
|
|||||||
Habit model = new Habit();
|
Habit model = new Habit();
|
||||||
model.archived = 1;
|
model.archived = 1;
|
||||||
model.highlight = 1;
|
model.highlight = 1;
|
||||||
model.color = Color.BLACK;
|
model.color = 0;
|
||||||
model.freqNum = 10;
|
model.freqNum = 10;
|
||||||
model.freqDen = 20;
|
model.freqDen = 20;
|
||||||
model.reminderDays = 1;
|
model.reminderDays = 1;
|
||||||
|
|||||||
@@ -22,10 +22,12 @@ package org.isoron.uhabits.unit.views;
|
|||||||
import android.support.test.runner.AndroidJUnit4;
|
import android.support.test.runner.AndroidJUnit4;
|
||||||
import android.test.suitebuilder.annotation.SmallTest;
|
import android.test.suitebuilder.annotation.SmallTest;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.R;
|
||||||
import org.isoron.uhabits.helpers.DateHelper;
|
import org.isoron.uhabits.helpers.DateHelper;
|
||||||
|
import org.isoron.uhabits.helpers.UIHelper;
|
||||||
import org.isoron.uhabits.models.Habit;
|
import org.isoron.uhabits.models.Habit;
|
||||||
import org.isoron.uhabits.unit.HabitFixtures;
|
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.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
@@ -34,18 +36,19 @@ import java.io.IOException;
|
|||||||
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
@SmallTest
|
@SmallTest
|
||||||
public class CheckmarkViewTest extends ViewTest
|
public class CheckmarkWidgetViewTest extends ViewTest
|
||||||
{
|
{
|
||||||
private CheckmarkView view;
|
private CheckmarkWidgetView view;
|
||||||
private Habit habit;
|
private Habit habit;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setup()
|
public void setup()
|
||||||
{
|
{
|
||||||
super.setup();
|
super.setup();
|
||||||
|
UIHelper.setFixedTheme(R.style.TransparentWidgetTheme);
|
||||||
|
|
||||||
habit = HabitFixtures.createShortHabit();
|
habit = HabitFixtures.createShortHabit();
|
||||||
view = new CheckmarkView(targetContext);
|
view = new CheckmarkWidgetView(targetContext);
|
||||||
view.setHabit(habit);
|
view.setHabit(habit);
|
||||||
refreshData(view);
|
refreshData(view);
|
||||||
measureView(dpToPixels(100), dpToPixels(200), view);
|
measureView(dpToPixels(100), dpToPixels(200), view);
|
||||||
@@ -52,7 +52,7 @@ public class HabitHistoryViewTest extends ViewTest
|
|||||||
|
|
||||||
view = new HabitHistoryView(targetContext);
|
view = new HabitHistoryView(targetContext);
|
||||||
view.setHabit(habit);
|
view.setHabit(habit);
|
||||||
measureView(dpToPixels(300), dpToPixels(100), view);
|
measureView(dpToPixels(400), dpToPixels(200), view);
|
||||||
refreshData(view);
|
refreshData(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,7 +89,7 @@ public class HabitHistoryViewTest extends ViewTest
|
|||||||
public void tapDate_withEditableView() throws Throwable
|
public void tapDate_withEditableView() throws Throwable
|
||||||
{
|
{
|
||||||
view.setIsEditable(true);
|
view.setIsEditable(true);
|
||||||
tap(view, 270, 30);
|
tap(view, 340, 40); // today's square
|
||||||
waitForAsyncTasks();
|
waitForAsyncTasks();
|
||||||
|
|
||||||
long today = DateHelper.getStartOfToday();
|
long today = DateHelper.getStartOfToday();
|
||||||
@@ -102,9 +102,9 @@ public class HabitHistoryViewTest extends ViewTest
|
|||||||
int expectedCheckmarkValues[] = habit.checkmarks.getAllValues();
|
int expectedCheckmarkValues[] = habit.checkmarks.getAllValues();
|
||||||
|
|
||||||
view.setIsEditable(true);
|
view.setIsEditable(true);
|
||||||
tap(view, 45, 5); // header
|
tap(view, 118, 13); // header
|
||||||
tap(view, 270, 43); // tomorrow's square
|
tap(view, 336, 60); // tomorrow's square
|
||||||
tap(view, 280, 30); // right axis
|
tap(view, 370, 60); // right axis
|
||||||
waitForAsyncTasks();
|
waitForAsyncTasks();
|
||||||
|
|
||||||
int actualCheckmarkValues[] = habit.checkmarks.getAllValues();
|
int actualCheckmarkValues[] = habit.checkmarks.getAllValues();
|
||||||
@@ -115,7 +115,7 @@ public class HabitHistoryViewTest extends ViewTest
|
|||||||
public void tapDate_withReadOnlyView() throws Throwable
|
public void tapDate_withReadOnlyView() throws Throwable
|
||||||
{
|
{
|
||||||
view.setIsEditable(false);
|
view.setIsEditable(false);
|
||||||
tap(view, 270, 30);
|
tap(view, 340, 40); // today's square
|
||||||
waitForAsyncTasks();
|
waitForAsyncTasks();
|
||||||
|
|
||||||
long today = DateHelper.getStartOfToday();
|
long today = DateHelper.getStartOfToday();
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ public class HabitScoreViewTest extends ViewTest
|
|||||||
view.setHabit(habit);
|
view.setHabit(habit);
|
||||||
view.setBucketSize(7);
|
view.setBucketSize(7);
|
||||||
refreshData(view);
|
refreshData(view);
|
||||||
measureView(dpToPixels(300), dpToPixels(100), view);
|
measureView(dpToPixels(300), dpToPixels(200), view);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -62,7 +62,7 @@ public class HabitScoreViewTest extends ViewTest
|
|||||||
@Test
|
@Test
|
||||||
public void testRender_withTransparentBackground() throws Throwable
|
public void testRender_withTransparentBackground() throws Throwable
|
||||||
{
|
{
|
||||||
view.setIsBackgroundTransparent(true);
|
view.setIsTransparencyEnabled(true);
|
||||||
assertRenders(view, "HabitScoreView/renderTransparent.png");
|
assertRenders(view, "HabitScoreView/renderTransparent.png");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ public class NumberViewTest extends ViewTest
|
|||||||
view = new NumberView(targetContext);
|
view = new NumberView(targetContext);
|
||||||
view.setLabel("Hello world");
|
view.setLabel("Hello world");
|
||||||
view.setNumber(31);
|
view.setNumber(31);
|
||||||
view.setColor(ColorHelper.palette[0]);
|
view.setColor(ColorHelper.CSV_PALETTE[0]);
|
||||||
measureView(dpToPixels(100), dpToPixels(100), view);
|
measureView(dpToPixels(100), dpToPixels(100), view);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,10 +68,10 @@ public class NumberViewTest extends ViewTest
|
|||||||
public void testRender_withDifferentParams() throws IOException
|
public void testRender_withDifferentParams() throws IOException
|
||||||
{
|
{
|
||||||
view.setNumber(500);
|
view.setNumber(500);
|
||||||
view.setColor(ColorHelper.palette[5]);
|
view.setColor(ColorHelper.CSV_PALETTE[5]);
|
||||||
view.setTextSize(targetContext.getResources().getDimension(R.dimen.tinyTextSize));
|
view.setTextSize(targetContext.getResources().getDimension(R.dimen.tinyTextSize));
|
||||||
|
|
||||||
measureView(dpToPixels(200), dpToPixels(200), view);
|
measureView(dpToPixels(200), dpToPixels(200), view);
|
||||||
assertRenders(view, "NumberView/renderDifferentParams.png");
|
assertRenders(view, "NumberView/renderDifferentParams.png");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
package org.isoron.uhabits.unit.views;
|
package org.isoron.uhabits.unit.views;
|
||||||
|
|
||||||
|
import android.graphics.Color;
|
||||||
import android.support.test.runner.AndroidJUnit4;
|
import android.support.test.runner.AndroidJUnit4;
|
||||||
import android.test.suitebuilder.annotation.SmallTest;
|
import android.test.suitebuilder.annotation.SmallTest;
|
||||||
|
|
||||||
@@ -42,10 +43,11 @@ public class RingViewTest extends ViewTest
|
|||||||
super.setup();
|
super.setup();
|
||||||
|
|
||||||
view = new RingView(targetContext);
|
view = new RingView(targetContext);
|
||||||
view.setLabel("Hello world");
|
|
||||||
view.setPercentage(0.6f);
|
view.setPercentage(0.6f);
|
||||||
view.setColor(ColorHelper.palette[0]);
|
view.setText("60%");
|
||||||
view.setMaxDiameter(dpToPixels(100));
|
view.setColor(ColorHelper.CSV_PALETTE[0]);
|
||||||
|
view.setBackgroundColor(Color.WHITE);
|
||||||
|
view.setThickness(dpToPixels(3));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -55,22 +57,11 @@ public class RingViewTest extends ViewTest
|
|||||||
assertRenders(view, "RingView/render.png");
|
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
|
@Test
|
||||||
public void testRender_withDifferentParams() throws IOException
|
public void testRender_withDifferentParams() throws IOException
|
||||||
{
|
{
|
||||||
view.setLabel("Habit Strength");
|
|
||||||
view.setPercentage(0.25f);
|
view.setPercentage(0.25f);
|
||||||
view.setMaxDiameter(dpToPixels(50));
|
view.setColor(ColorHelper.CSV_PALETTE[5]);
|
||||||
view.setColor(ColorHelper.palette[5]);
|
|
||||||
|
|
||||||
measureView(dpToPixels(200), dpToPixels(200), view);
|
measureView(dpToPixels(200), dpToPixels(200), view);
|
||||||
assertRenders(view, "RingView/renderDifferentParams.png");
|
assertRenders(view, "RingView/renderDifferentParams.png");
|
||||||
|
|||||||
@@ -80,8 +80,8 @@ public class ViewTest extends BaseTest
|
|||||||
|
|
||||||
if(!similarEnough)
|
if(!similarEnough)
|
||||||
{
|
{
|
||||||
saveBitmap(expectedImagePath, ".scaledExpected", scaledExpected);
|
saveBitmap(expectedImagePath, ".expected", scaledExpected);
|
||||||
String path = saveBitmap(expectedImagePath, ".actual", actual);
|
String path = saveBitmap(expectedImagePath, "", actual);
|
||||||
errorMessage.append(String.format("Actual rendered image " + "saved to %s", path));
|
errorMessage.append(String.format("Actual rendered image " + "saved to %s", path));
|
||||||
fail(errorMessage.toString());
|
fail(errorMessage.toString());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,8 +21,8 @@
|
|||||||
<manifest
|
<manifest
|
||||||
package="org.isoron.uhabits"
|
package="org.isoron.uhabits"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:versionCode="15"
|
android:versionCode="16"
|
||||||
android:versionName="1.4.1">
|
android:versionName="1.5.0">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.VIBRATE"/>
|
<uses-permission android:name="android.permission.VIBRATE"/>
|
||||||
|
|
||||||
@@ -34,6 +34,8 @@
|
|||||||
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||||
android:maxSdkVersion="18" />
|
android:maxSdkVersion="18" />
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name="HabitsApplication"
|
android:name="HabitsApplication"
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
@@ -59,8 +61,7 @@
|
|||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".ShowHabitActivity"
|
android:name=".ShowHabitActivity"
|
||||||
android:label="@string/title_activity_show_habit"
|
android:label="@string/title_activity_show_habit">
|
||||||
android:parentActivityName=".MainActivity">
|
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value="org.isoron.uhabits.MainActivity"/>
|
android:value="org.isoron.uhabits.MainActivity"/>
|
||||||
@@ -68,8 +69,7 @@
|
|||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".SettingsActivity"
|
android:name=".SettingsActivity"
|
||||||
android:label="@string/settings"
|
android:label="@string/settings">
|
||||||
android:parentActivityName=".MainActivity">
|
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value="org.isoron.uhabits.MainActivity"/>
|
android:value="org.isoron.uhabits.MainActivity"/>
|
||||||
@@ -90,8 +90,10 @@
|
|||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".AboutActivity"
|
android:name=".AboutActivity"
|
||||||
android:label="@string/about"
|
android:label="@string/about">
|
||||||
android:parentActivityName=".MainActivity">
|
<meta-data
|
||||||
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
|
android:value=".MainActivity" />
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<receiver
|
<receiver
|
||||||
@@ -154,7 +156,11 @@
|
|||||||
android:resource="@xml/widget_frequency_info"/>
|
android:resource="@xml/widget_frequency_info"/>
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
<receiver android:name=".HabitBroadcastReceiver"/>
|
<receiver android:name=".HabitBroadcastReceiver">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||||
|
</intent-filter>
|
||||||
|
</receiver>
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
|
|||||||
14
app/src/main/assets/migrations/14.sql
Normal file
@@ -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;
|
||||||
@@ -16,24 +16,24 @@
|
|||||||
|
|
||||||
package com.android.colorpicker;
|
package com.android.colorpicker;
|
||||||
|
|
||||||
import org.isoron.uhabits.R;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.AlertDialog;
|
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
import android.app.DialogFragment;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.support.v7.app.AlertDialog;
|
||||||
|
import android.support.v7.app.AppCompatDialogFragment;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.ProgressBar;
|
import android.widget.ProgressBar;
|
||||||
|
|
||||||
import com.android.colorpicker.ColorPickerSwatch.OnColorSelectedListener;
|
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
|
* 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.
|
* 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_LARGE = 1;
|
||||||
public static final int SIZE_SMALL = 2;
|
public static final int SIZE_SMALL = 2;
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import android.content.Context;
|
|||||||
import android.content.res.ColorStateList;
|
import android.content.res.ColorStateList;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.support.v7.app.AppCompatDialogFragment;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.KeyCharacterMap;
|
import android.view.KeyCharacterMap;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
@@ -49,7 +50,7 @@ import com.android.datetimepicker.time.RadialPickerLayout.OnValueSelectedListene
|
|||||||
/**
|
/**
|
||||||
* Dialog to set a time.
|
* 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 TAG = "TimePickerDialog";
|
||||||
|
|
||||||
private static final String KEY_HOUR_OF_DAY = "hour_of_day";
|
private static final String KEY_HOUR_OF_DAY = "hour_of_day";
|
||||||
|
|||||||
@@ -19,33 +19,27 @@
|
|||||||
|
|
||||||
package org.isoron.uhabits;
|
package org.isoron.uhabits;
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.graphics.Color;
|
|
||||||
import android.graphics.drawable.ColorDrawable;
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.TextView;
|
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
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState)
|
protected void onCreate(Bundle savedInstanceState)
|
||||||
{
|
{
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.about);
|
|
||||||
|
|
||||||
if (android.os.Build.VERSION.SDK_INT >= 21)
|
setContentView(R.layout.about);
|
||||||
{
|
setupSupportActionBar(true);
|
||||||
int color = getResources().getColor(R.color.blue_700);
|
|
||||||
int darkerColor = ColorHelper.mixColors(color, Color.BLACK, 0.75f);
|
int color = UIHelper.getStyledColor(this, R.attr.aboutScreenColor);
|
||||||
getActionBar().setBackgroundDrawable(new ColorDrawable(color));
|
setupActionBarColor(color);
|
||||||
getWindow().setStatusBarColor(darkerColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
TextView tvVersion = (TextView) findViewById(R.id.tvVersion);
|
TextView tvVersion = (TextView) findViewById(R.id.tvVersion);
|
||||||
TextView tvRate = (TextView) findViewById(R.id.tvRate);
|
TextView tvRate = (TextView) findViewById(R.id.tvRate);
|
||||||
|
|||||||
@@ -19,17 +19,25 @@
|
|||||||
|
|
||||||
package org.isoron.uhabits;
|
package org.isoron.uhabits;
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.app.backup.BackupManager;
|
import android.app.backup.BackupManager;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.drawable.ColorDrawable;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
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 android.widget.Toast;
|
||||||
|
|
||||||
import org.isoron.uhabits.commands.Command;
|
import org.isoron.uhabits.commands.Command;
|
||||||
|
import org.isoron.uhabits.helpers.ColorHelper;
|
||||||
|
import org.isoron.uhabits.helpers.UIHelper;
|
||||||
|
|
||||||
import java.util.LinkedList;
|
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;
|
private static int MAX_UNDO_LEVEL = 15;
|
||||||
|
|
||||||
@@ -44,6 +52,8 @@ abstract public class BaseActivity extends Activity implements Thread.UncaughtEx
|
|||||||
{
|
{
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
UIHelper.applyCurrentTheme(this);
|
||||||
|
|
||||||
androidExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
|
androidExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
|
||||||
Thread.setDefaultUncaughtExceptionHandler(this);
|
Thread.setDefaultUncaughtExceptionHandler(this);
|
||||||
|
|
||||||
@@ -117,6 +127,23 @@ abstract public class BaseActivity extends Activity implements Thread.UncaughtEx
|
|||||||
showToast(command.getExecuteStringId());
|
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)
|
public void onPostExecuteCommand(Long refreshKey)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -139,4 +166,39 @@ abstract public class BaseActivity extends Activity implements Thread.UncaughtEx
|
|||||||
else
|
else
|
||||||
System.exit(1);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,11 +29,11 @@ import android.content.Context;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.graphics.BitmapFactory;
|
import android.graphics.BitmapFactory;
|
||||||
import android.media.RingtoneManager;
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.support.v4.app.NotificationCompat;
|
import android.support.v4.app.NotificationCompat;
|
||||||
|
import android.support.v4.app.TaskStackBuilder;
|
||||||
import android.support.v4.content.LocalBroadcastManager;
|
import android.support.v4.content.LocalBroadcastManager;
|
||||||
|
|
||||||
import org.isoron.uhabits.helpers.DateHelper;
|
import org.isoron.uhabits.helpers.DateHelper;
|
||||||
@@ -58,7 +58,7 @@ public class HabitBroadcastReceiver extends BroadcastReceiver
|
|||||||
{
|
{
|
||||||
case ACTION_SHOW_REMINDER:
|
case ACTION_SHOW_REMINDER:
|
||||||
createNotification(context, intent);
|
createNotification(context, intent);
|
||||||
createReminderAlarms(context);
|
createReminderAlarmsDelayed(context);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ACTION_DISMISS:
|
case ACTION_DISMISS:
|
||||||
@@ -72,10 +72,14 @@ public class HabitBroadcastReceiver extends BroadcastReceiver
|
|||||||
case ACTION_SNOOZE:
|
case ACTION_SNOOZE:
|
||||||
snoozeHabit(context, intent);
|
snoozeHabit(context, intent);
|
||||||
break;
|
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()
|
new Handler().postDelayed(new Runnable()
|
||||||
{
|
{
|
||||||
@@ -163,9 +167,7 @@ public class HabitBroadcastReceiver extends BroadcastReceiver
|
|||||||
{
|
{
|
||||||
if (todayValue != Checkmark.UNCHECKED) return;
|
if (todayValue != Checkmark.UNCHECKED) return;
|
||||||
if (!checkWeekday(intent, habit)) return;
|
if (!checkWeekday(intent, habit)) return;
|
||||||
|
if (!habit.hasReminder()) return;
|
||||||
// Check if reminder has been turned off after alarm was scheduled
|
|
||||||
if (habit.reminderHour == null) return;
|
|
||||||
|
|
||||||
Intent contentIntent = new Intent(context, MainActivity.class);
|
Intent contentIntent = new Intent(context, MainActivity.class);
|
||||||
contentIntent.setData(data);
|
contentIntent.setData(data);
|
||||||
@@ -176,14 +178,16 @@ public class HabitBroadcastReceiver extends BroadcastReceiver
|
|||||||
PendingIntent checkIntentPending = buildCheckIntent(context, habit, timestamp);
|
PendingIntent checkIntentPending = buildCheckIntent(context, habit, timestamp);
|
||||||
PendingIntent snoozeIntentPending = buildSnoozeIntent(context, habit);
|
PendingIntent snoozeIntentPending = buildSnoozeIntent(context, habit);
|
||||||
|
|
||||||
Uri soundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
|
Uri ringtoneUri = ReminderHelper.getRingtoneUri(context);
|
||||||
|
|
||||||
NotificationCompat.WearableExtender wearableExtender =
|
NotificationCompat.WearableExtender wearableExtender =
|
||||||
new NotificationCompat.WearableExtender().setBackground(
|
new NotificationCompat.WearableExtender().setBackground(
|
||||||
BitmapFactory.decodeResource(context.getResources(), R.drawable.stripe));
|
BitmapFactory.decodeResource(context.getResources(),
|
||||||
|
R.drawable.stripe));
|
||||||
|
|
||||||
Notification notification =
|
Notification notification =
|
||||||
new NotificationCompat.Builder(context).setSmallIcon(R.drawable.ic_notification)
|
new NotificationCompat.Builder(context)
|
||||||
|
.setSmallIcon(R.drawable.ic_notification)
|
||||||
.setContentTitle(habit.name)
|
.setContentTitle(habit.name)
|
||||||
.setContentText(habit.description)
|
.setContentText(habit.description)
|
||||||
.setContentIntent(contentPendingIntent)
|
.setContentIntent(contentPendingIntent)
|
||||||
@@ -192,7 +196,7 @@ public class HabitBroadcastReceiver extends BroadcastReceiver
|
|||||||
context.getString(R.string.check), checkIntentPending)
|
context.getString(R.string.check), checkIntentPending)
|
||||||
.addAction(R.drawable.ic_action_snooze,
|
.addAction(R.drawable.ic_action_snooze,
|
||||||
context.getString(R.string.snooze), snoozeIntentPending)
|
context.getString(R.string.snooze), snoozeIntentPending)
|
||||||
.setSound(soundUri)
|
.setSound(ringtoneUri)
|
||||||
.extend(wearableExtender)
|
.extend(wearableExtender)
|
||||||
.setWhen(reminderTime)
|
.setWhen(reminderTime)
|
||||||
.setShowWhen(true)
|
.setShowWhen(true)
|
||||||
@@ -201,7 +205,8 @@ public class HabitBroadcastReceiver extends BroadcastReceiver
|
|||||||
notification.flags |= Notification.FLAG_AUTO_CANCEL;
|
notification.flags |= Notification.FLAG_AUTO_CANCEL;
|
||||||
|
|
||||||
NotificationManager notificationManager =
|
NotificationManager notificationManager =
|
||||||
(NotificationManager) context.getSystemService(Activity.NOTIFICATION_SERVICE);
|
(NotificationManager) context.getSystemService(
|
||||||
|
Activity.NOTIFICATION_SERVICE);
|
||||||
|
|
||||||
int notificationId = (int) (habit.getId() % Integer.MAX_VALUE);
|
int notificationId = (int) (habit.getId() % Integer.MAX_VALUE);
|
||||||
notificationManager.notify(notificationId, notification);
|
notificationManager.notify(notificationId, notification);
|
||||||
@@ -241,7 +246,10 @@ public class HabitBroadcastReceiver extends BroadcastReceiver
|
|||||||
{
|
{
|
||||||
Intent intent = new Intent(context, ShowHabitActivity.class);
|
Intent intent = new Intent(context, ShowHabitActivity.class);
|
||||||
intent.setData(Uri.parse("content://org.isoron.uhabits/habit/" + habit.getId()));
|
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)
|
private boolean checkWeekday(Intent intent, Habit habit)
|
||||||
@@ -254,4 +262,13 @@ public class HabitBroadcastReceiver extends BroadcastReceiver
|
|||||||
return reminderDays[weekday];
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,10 +40,6 @@ public class IntroActivity extends AppIntro2
|
|||||||
getString(R.string.intro_description_2), R.drawable.intro_icon_2,
|
getString(R.string.intro_description_2), R.drawable.intro_icon_2,
|
||||||
Color.parseColor("#ffa726")));
|
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),
|
addSlide(AppIntroFragment.newInstance(getString(R.string.intro_title_4),
|
||||||
getString(R.string.intro_description_4), R.drawable.intro_icon_4,
|
getString(R.string.intro_description_4), R.drawable.intro_icon_4,
|
||||||
Color.parseColor("#9575cd")));
|
Color.parseColor("#9575cd")));
|
||||||
|
|||||||
@@ -27,19 +27,24 @@ import android.content.Intent;
|
|||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
|
import android.graphics.drawable.ColorDrawable;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.v4.content.LocalBroadcastManager;
|
import android.support.v4.content.LocalBroadcastManager;
|
||||||
|
import android.support.v7.app.ActionBar;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
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.fragments.ListHabitsFragment;
|
||||||
|
import org.isoron.uhabits.helpers.DateHelper;
|
||||||
import org.isoron.uhabits.helpers.ReminderHelper;
|
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.models.Habit;
|
||||||
import org.isoron.uhabits.tasks.BaseTask;
|
import org.isoron.uhabits.tasks.BaseTask;
|
||||||
import org.isoron.uhabits.widgets.CheckmarkWidgetProvider;
|
import org.isoron.uhabits.widgets.CheckmarkWidgetProvider;
|
||||||
@@ -70,19 +75,35 @@ public class MainActivity extends BaseActivity
|
|||||||
protected void onCreate(Bundle savedInstanceState)
|
protected void onCreate(Bundle savedInstanceState)
|
||||||
{
|
{
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
setContentView(R.layout.list_habits_activity);
|
setContentView(R.layout.list_habits_activity);
|
||||||
|
|
||||||
|
setupSupportActionBar(false);
|
||||||
|
|
||||||
prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||||
listHabitsFragment =
|
listHabitsFragment =
|
||||||
(ListHabitsFragment) getFragmentManager().findFragmentById(R.id.fragment1);
|
(ListHabitsFragment) getSupportFragmentManager().findFragmentById(R.id.fragment1);
|
||||||
|
|
||||||
receiver = new Receiver();
|
receiver = new Receiver();
|
||||||
localBroadcastManager = LocalBroadcastManager.getInstance(this);
|
localBroadcastManager = LocalBroadcastManager.getInstance(this);
|
||||||
localBroadcastManager.registerReceiver(receiver, new IntentFilter(ACTION_REFRESH));
|
localBroadcastManager.registerReceiver(receiver, new IntentFilter(ACTION_REFRESH));
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
|
||||||
|
onPreLollipopStartup();
|
||||||
|
|
||||||
onStartup();
|
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()
|
private void onStartup()
|
||||||
{
|
{
|
||||||
PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
|
PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
|
||||||
@@ -121,7 +142,12 @@ public class MainActivity extends BaseActivity
|
|||||||
@Override
|
@Override
|
||||||
public boolean onCreateOptionsMenu(Menu menu)
|
public boolean onCreateOptionsMenu(Menu menu)
|
||||||
{
|
{
|
||||||
|
menu.clear();
|
||||||
getMenuInflater().inflate(R.menu.list_habits_menu, menu);
|
getMenuInflater().inflate(R.menu.list_habits_menu, menu);
|
||||||
|
|
||||||
|
MenuItem nightModeItem = menu.findItem(R.id.action_night_mode);
|
||||||
|
nightModeItem.setChecked(UIHelper.isNightMode());
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,6 +156,17 @@ public class MainActivity extends BaseActivity
|
|||||||
{
|
{
|
||||||
switch (item.getItemId())
|
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:
|
case R.id.action_settings:
|
||||||
{
|
{
|
||||||
Intent intent = new Intent(this, SettingsActivity.class);
|
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
|
@Override
|
||||||
protected void onActivityResult(int requestCode, int resultCode, Intent data)
|
protected void onActivityResult(int requestCode, int resultCode, Intent data)
|
||||||
{
|
{
|
||||||
@@ -218,11 +272,21 @@ public class MainActivity extends BaseActivity
|
|||||||
@Override
|
@Override
|
||||||
protected void doInBackground()
|
protected void doInBackground()
|
||||||
{
|
{
|
||||||
|
dismissNotifications(MainActivity.this);
|
||||||
updateWidgets(MainActivity.this);
|
updateWidgets(MainActivity.this);
|
||||||
}
|
}
|
||||||
}.execute();
|
}.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)
|
public static void updateWidgets(Context context)
|
||||||
{
|
{
|
||||||
updateWidgets(context, CheckmarkWidgetProvider.class);
|
updateWidgets(context, CheckmarkWidgetProvider.class);
|
||||||
|
|||||||
@@ -19,19 +19,20 @@
|
|||||||
|
|
||||||
package org.isoron.uhabits;
|
package org.isoron.uhabits;
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.os.Bundle;
|
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
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState)
|
protected void onCreate(Bundle savedInstanceState)
|
||||||
{
|
{
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
getFragmentManager().beginTransaction()
|
setContentView(R.layout.settings_activity);
|
||||||
.replace(android.R.id.content, new SettingsFragment())
|
setupSupportActionBar(true);
|
||||||
.commit();
|
|
||||||
|
int color = UIHelper.getStyledColor(this, R.attr.aboutScreenColor);
|
||||||
|
setupActionBarColor(color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,12 +19,12 @@
|
|||||||
|
|
||||||
package org.isoron.uhabits;
|
package org.isoron.uhabits;
|
||||||
|
|
||||||
import android.app.ActionBar;
|
|
||||||
import android.content.ContentUris;
|
import android.content.ContentUris;
|
||||||
import android.graphics.drawable.ColorDrawable;
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.support.v7.app.ActionBar;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.helpers.ColorHelper;
|
||||||
import org.isoron.uhabits.models.Habit;
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
|
||||||
public class ShowHabitActivity extends BaseActivity
|
public class ShowHabitActivity extends BaseActivity
|
||||||
@@ -38,16 +38,23 @@ public class ShowHabitActivity extends BaseActivity
|
|||||||
|
|
||||||
Uri data = getIntent().getData();
|
Uri data = getIntent().getData();
|
||||||
habit = Habit.get(ContentUris.parseId(data));
|
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);
|
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()
|
public Habit getHabit()
|
||||||
|
|||||||
@@ -17,13 +17,13 @@
|
|||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.isoron.uhabits.fragments;
|
package org.isoron.uhabits.dialogs;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.app.DialogFragment;
|
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
|
import android.support.v7.app.AppCompatDialogFragment;
|
||||||
import android.text.format.DateFormat;
|
import android.text.format.DateFormat;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@@ -40,19 +40,18 @@ import com.android.colorpicker.ColorPickerSwatch;
|
|||||||
import com.android.datetimepicker.time.RadialPickerLayout;
|
import com.android.datetimepicker.time.RadialPickerLayout;
|
||||||
import com.android.datetimepicker.time.TimePickerDialog;
|
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.R;
|
||||||
import org.isoron.uhabits.commands.Command;
|
import org.isoron.uhabits.commands.Command;
|
||||||
import org.isoron.uhabits.commands.CreateHabitCommand;
|
import org.isoron.uhabits.commands.CreateHabitCommand;
|
||||||
import org.isoron.uhabits.commands.EditHabitCommand;
|
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 org.isoron.uhabits.models.Habit;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
public class EditHabitFragment extends DialogFragment
|
public class EditHabitDialogFragment extends AppCompatDialogFragment
|
||||||
implements OnClickListener, WeekdayPickerDialog.OnWeekdaysPickedListener,
|
implements OnClickListener, WeekdayPickerDialog.OnWeekdaysPickedListener,
|
||||||
TimePickerDialog.OnTimeSetListener, Spinner.OnItemSelectedListener
|
TimePickerDialog.OnTimeSetListener, Spinner.OnItemSelectedListener
|
||||||
{
|
{
|
||||||
@@ -79,9 +78,9 @@ public class EditHabitFragment extends DialogFragment
|
|||||||
private SharedPreferences prefs;
|
private SharedPreferences prefs;
|
||||||
private boolean is24HourMode;
|
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();
|
Bundle args = new Bundle();
|
||||||
args.putLong("habitId", id);
|
args.putLong("habitId", id);
|
||||||
args.putInt("editMode", EDIT_MODE);
|
args.putInt("editMode", EDIT_MODE);
|
||||||
@@ -89,9 +88,9 @@ public class EditHabitFragment extends DialogFragment
|
|||||||
return frag;
|
return frag;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static EditHabitFragment createHabitFragment()
|
public static EditHabitDialogFragment createHabitFragment()
|
||||||
{
|
{
|
||||||
EditHabitFragment frag = new EditHabitFragment();
|
EditHabitDialogFragment frag = new EditHabitDialogFragment();
|
||||||
Bundle args = new Bundle();
|
Bundle args = new Bundle();
|
||||||
args.putInt("editMode", CREATE_MODE);
|
args.putInt("editMode", CREATE_MODE);
|
||||||
frag.setArguments(args);
|
frag.setArguments(args);
|
||||||
@@ -138,7 +137,7 @@ public class EditHabitFragment extends DialogFragment
|
|||||||
modifiedHabit = new Habit();
|
modifiedHabit = new Habit();
|
||||||
modifiedHabit.freqNum = 1;
|
modifiedHabit.freqNum = 1;
|
||||||
modifiedHabit.freqDen = 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)
|
else if (mode == EDIT_MODE)
|
||||||
{
|
{
|
||||||
@@ -174,13 +173,13 @@ public class EditHabitFragment extends DialogFragment
|
|||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void changeColor(Integer color)
|
private void changeColor(int paletteColor)
|
||||||
{
|
{
|
||||||
modifiedHabit.color = color;
|
modifiedHabit.color = paletteColor;
|
||||||
tvName.setTextColor(color);
|
tvName.setTextColor(ColorHelper.getColor(getActivity(), paletteColor));
|
||||||
|
|
||||||
SharedPreferences.Editor editor = prefs.edit();
|
SharedPreferences.Editor editor = prefs.edit();
|
||||||
editor.putInt("pref_default_habit_color", color);
|
editor.putInt("pref_default_habit_palette_color", paletteColor);
|
||||||
editor.apply();
|
editor.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,15 +236,18 @@ public class EditHabitFragment extends DialogFragment
|
|||||||
|
|
||||||
private void onColorButtonClick()
|
private void onColorButtonClick()
|
||||||
{
|
{
|
||||||
|
int originalAndroidColor = ColorHelper.getColor(getActivity(), modifiedHabit.color);
|
||||||
|
|
||||||
ColorPickerDialog picker = ColorPickerDialog.newInstance(
|
ColorPickerDialog picker = ColorPickerDialog.newInstance(
|
||||||
R.string.color_picker_default_title, ColorHelper.palette, modifiedHabit.color, 4,
|
R.string.color_picker_default_title, ColorHelper.getPalette(getActivity()),
|
||||||
ColorPickerDialog.SIZE_SMALL);
|
originalAndroidColor, 4, ColorPickerDialog.SIZE_SMALL);
|
||||||
|
|
||||||
picker.setOnColorSelectedListener(new ColorPickerSwatch.OnColorSelectedListener()
|
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");
|
picker.show(getFragmentManager(), "picker");
|
||||||
@@ -19,21 +19,20 @@
|
|||||||
|
|
||||||
package org.isoron.uhabits.dialogs;
|
package org.isoron.uhabits.dialogs;
|
||||||
|
|
||||||
import android.app.AlertDialog;
|
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
import android.app.DialogFragment;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.support.v7.app.AlertDialog;
|
||||||
|
import android.support.v7.app.AppCompatDialogFragment;
|
||||||
import android.util.DisplayMetrics;
|
import android.util.DisplayMetrics;
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import org.isoron.uhabits.R;
|
import org.isoron.uhabits.R;
|
||||||
import org.isoron.uhabits.models.Habit;
|
import org.isoron.uhabits.models.Habit;
|
||||||
import org.isoron.uhabits.tasks.BaseTask;
|
import org.isoron.uhabits.tasks.BaseTask;
|
||||||
import org.isoron.uhabits.views.HabitHistoryView;
|
import org.isoron.uhabits.views.HabitHistoryView;
|
||||||
|
|
||||||
public class HistoryEditorDialog extends DialogFragment
|
public class HistoryEditorDialog extends AppCompatDialogFragment
|
||||||
implements DialogInterface.OnClickListener
|
implements DialogInterface.OnClickListener
|
||||||
{
|
{
|
||||||
private Habit habit;
|
private Habit habit;
|
||||||
@@ -89,8 +88,6 @@ public class HistoryEditorDialog extends DialogFragment
|
|||||||
int width = metrics.widthPixels;
|
int width = metrics.widthPixels;
|
||||||
int height = Math.min(metrics.heightPixels, maxHeight);
|
int height = Math.min(metrics.heightPixels, maxHeight);
|
||||||
|
|
||||||
Log.d("HistoryEditorDialog", String.format("h=%d max_h=%d", height, maxHeight));
|
|
||||||
|
|
||||||
getDialog().getWindow().setLayout(width, height);
|
getDialog().getWindow().setLayout(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,16 +19,16 @@
|
|||||||
|
|
||||||
package org.isoron.uhabits.dialogs;
|
package org.isoron.uhabits.dialogs;
|
||||||
|
|
||||||
import android.app.AlertDialog;
|
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
import android.app.DialogFragment;
|
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.os.Bundle;
|
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.R;
|
||||||
|
import org.isoron.uhabits.helpers.DateHelper;
|
||||||
|
|
||||||
public class WeekdayPickerDialog extends DialogFragment
|
public class WeekdayPickerDialog extends AppCompatDialogFragment
|
||||||
implements DialogInterface.OnMultiChoiceClickListener, DialogInterface.OnClickListener
|
implements DialogInterface.OnMultiChoiceClickListener, DialogInterface.OnClickListener
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|||||||
@@ -24,11 +24,9 @@ import android.view.LayoutInflater;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.BaseAdapter;
|
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.R;
|
||||||
|
import org.isoron.uhabits.helpers.DateHelper;
|
||||||
import org.isoron.uhabits.helpers.ListHabitsHelper;
|
import org.isoron.uhabits.helpers.ListHabitsHelper;
|
||||||
import org.isoron.uhabits.loaders.HabitListLoader;
|
import org.isoron.uhabits.loaders.HabitListLoader;
|
||||||
import org.isoron.uhabits.models.Habit;
|
import org.isoron.uhabits.models.Habit;
|
||||||
@@ -74,28 +72,15 @@ class HabitListAdapter extends BaseAdapter
|
|||||||
public View getView(int position, View view, ViewGroup parent)
|
public View getView(int position, View view, ViewGroup parent)
|
||||||
{
|
{
|
||||||
final Habit habit = loader.habitsList.get(position);
|
final Habit habit = loader.habitsList.get(position);
|
||||||
|
boolean selected = selectedPositions.contains(position);
|
||||||
|
|
||||||
if (view == null || (Long) view.getTag(R.id.timestamp_key) != DateHelper.getStartOfToday())
|
if (view == null || (Long) view.getTag(R.id.timestamp_key) != DateHelper.getStartOfToday())
|
||||||
{
|
{
|
||||||
view = inflater.inflate(R.layout.list_habits_item, null);
|
view = helper.inflateHabitCard(inflater, onCheckmarkLongClickListener,
|
||||||
helper.initializeLabelAndIcon(view);
|
onCheckmarkClickListener);
|
||||||
helper.inflateCheckmarkButtons(view, onCheckmarkLongClickListener,
|
|
||||||
onCheckmarkClickListener, inflater);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TextView tvStar = ((TextView) view.findViewById(R.id.tvStar));
|
helper.updateHabitCard(view, habit, selected);
|
||||||
TextView tvName = (TextView) view.findViewById(R.id.label);
|
|
||||||
LinearLayout llInner = (LinearLayout) view.findViewById(R.id.llInner);
|
|
||||||
LinearLayout llButtons = (LinearLayout) view.findViewById(R.id.llButtons);
|
|
||||||
|
|
||||||
llInner.setTag(R.string.habit_key, habit.getId());
|
|
||||||
|
|
||||||
helper.updateNameAndIcon(habit, tvStar, tvName);
|
|
||||||
helper.updateCheckmarkButtons(habit, llButtons);
|
|
||||||
|
|
||||||
boolean selected = selectedPositions.contains(position);
|
|
||||||
helper.updateHabitBackground(llInner, selected);
|
|
||||||
|
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,9 +19,9 @@
|
|||||||
|
|
||||||
package org.isoron.uhabits.fragments;
|
package org.isoron.uhabits.fragments;
|
||||||
|
|
||||||
import android.app.AlertDialog;
|
|
||||||
import android.content.DialogInterface;
|
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.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.widget.ProgressBar;
|
import android.widget.ProgressBar;
|
||||||
@@ -29,12 +29,13 @@ import android.widget.ProgressBar;
|
|||||||
import com.android.colorpicker.ColorPickerDialog;
|
import com.android.colorpicker.ColorPickerDialog;
|
||||||
import com.android.colorpicker.ColorPickerSwatch;
|
import com.android.colorpicker.ColorPickerSwatch;
|
||||||
|
|
||||||
import org.isoron.uhabits.R;
|
|
||||||
import org.isoron.uhabits.BaseActivity;
|
import org.isoron.uhabits.BaseActivity;
|
||||||
|
import org.isoron.uhabits.R;
|
||||||
import org.isoron.uhabits.commands.ArchiveHabitsCommand;
|
import org.isoron.uhabits.commands.ArchiveHabitsCommand;
|
||||||
import org.isoron.uhabits.commands.ChangeHabitColorCommand;
|
import org.isoron.uhabits.commands.ChangeHabitColorCommand;
|
||||||
import org.isoron.uhabits.commands.DeleteHabitsCommand;
|
import org.isoron.uhabits.commands.DeleteHabitsCommand;
|
||||||
import org.isoron.uhabits.commands.UnarchiveHabitsCommand;
|
import org.isoron.uhabits.commands.UnarchiveHabitsCommand;
|
||||||
|
import org.isoron.uhabits.dialogs.EditHabitDialogFragment;
|
||||||
import org.isoron.uhabits.helpers.ColorHelper;
|
import org.isoron.uhabits.helpers.ColorHelper;
|
||||||
import org.isoron.uhabits.helpers.UIHelper;
|
import org.isoron.uhabits.helpers.UIHelper;
|
||||||
import org.isoron.uhabits.loaders.HabitListLoader;
|
import org.isoron.uhabits.loaders.HabitListLoader;
|
||||||
@@ -155,27 +156,33 @@ public class HabitSelectionCallback implements ActionMode.Callback
|
|||||||
|
|
||||||
case R.id.action_edit_habit:
|
case R.id.action_edit_habit:
|
||||||
{
|
{
|
||||||
EditHabitFragment frag = EditHabitFragment.editSingleHabitFragment(firstHabit.getId());
|
EditHabitDialogFragment
|
||||||
|
frag = EditHabitDialogFragment.editSingleHabitFragment(firstHabit.getId());
|
||||||
frag.setOnSavedListener(onSavedListener);
|
frag.setOnSavedListener(onSavedListener);
|
||||||
frag.show(activity.getFragmentManager(), "editHabit");
|
frag.show(activity.getSupportFragmentManager(), "editHabit");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case R.id.action_color:
|
case R.id.action_color:
|
||||||
{
|
{
|
||||||
ColorPickerDialog picker = ColorPickerDialog.newInstance(R.string.color_picker_default_title,
|
int originalAndroidColor = ColorHelper.getColor(activity, firstHabit.color);
|
||||||
ColorHelper.palette, firstHabit.color, 4, ColorPickerDialog.SIZE_SMALL);
|
|
||||||
|
ColorPickerDialog picker = ColorPickerDialog.newInstance(
|
||||||
|
R.string.color_picker_default_title, ColorHelper.getPalette(activity),
|
||||||
|
originalAndroidColor, 4, ColorPickerDialog.SIZE_SMALL);
|
||||||
|
|
||||||
picker.setOnColorSelectedListener(new ColorPickerSwatch.OnColorSelectedListener()
|
picker.setOnColorSelectedListener(new ColorPickerSwatch.OnColorSelectedListener()
|
||||||
{
|
{
|
||||||
public void onColorSelected(int color)
|
public void onColorSelected(int androidColor)
|
||||||
{
|
{
|
||||||
activity.executeCommand(
|
int paletteColor = ColorHelper.colorToPaletteIndex(activity,
|
||||||
new ChangeHabitColorCommand(selectedHabits, color), null);
|
androidColor);
|
||||||
|
activity.executeCommand(new ChangeHabitColorCommand(selectedHabits,
|
||||||
|
paletteColor), null);
|
||||||
mode.finish();
|
mode.finish();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
picker.show(activity.getFragmentManager(), "picker");
|
picker.show(activity.getSupportFragmentManager(), "picker");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,14 +20,14 @@
|
|||||||
package org.isoron.uhabits.fragments;
|
package org.isoron.uhabits.fragments;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.Fragment;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.support.annotation.Nullable;
|
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;
|
||||||
import android.view.ContextMenu.ContextMenuInfo;
|
import android.view.ContextMenu.ContextMenuInfo;
|
||||||
import android.view.HapticFeedbackConstants;
|
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;
|
||||||
import com.mobeta.android.dslv.DragSortListView.DropListener;
|
import com.mobeta.android.dslv.DragSortListView.DropListener;
|
||||||
|
|
||||||
import org.isoron.uhabits.R;
|
|
||||||
import org.isoron.uhabits.BaseActivity;
|
import org.isoron.uhabits.BaseActivity;
|
||||||
|
import org.isoron.uhabits.R;
|
||||||
import org.isoron.uhabits.commands.Command;
|
import org.isoron.uhabits.commands.Command;
|
||||||
import org.isoron.uhabits.commands.ToggleRepetitionCommand;
|
import org.isoron.uhabits.commands.ToggleRepetitionCommand;
|
||||||
|
import org.isoron.uhabits.dialogs.EditHabitDialogFragment;
|
||||||
import org.isoron.uhabits.dialogs.FilePickerDialog;
|
import org.isoron.uhabits.dialogs.FilePickerDialog;
|
||||||
import org.isoron.uhabits.helpers.DatabaseHelper;
|
import org.isoron.uhabits.helpers.DatabaseHelper;
|
||||||
import org.isoron.uhabits.helpers.DateHelper;
|
import org.isoron.uhabits.helpers.DateHelper;
|
||||||
import org.isoron.uhabits.helpers.UIHelper.OnSavedListener;
|
|
||||||
import org.isoron.uhabits.helpers.HintManager;
|
import org.isoron.uhabits.helpers.HintManager;
|
||||||
import org.isoron.uhabits.helpers.ListHabitsHelper;
|
import org.isoron.uhabits.helpers.ListHabitsHelper;
|
||||||
import org.isoron.uhabits.helpers.ReminderHelper;
|
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.loaders.HabitListLoader;
|
||||||
import org.isoron.uhabits.models.Habit;
|
import org.isoron.uhabits.models.Habit;
|
||||||
import org.isoron.uhabits.tasks.ExportCSVTask;
|
import org.isoron.uhabits.tasks.ExportCSVTask;
|
||||||
@@ -120,7 +122,7 @@ public class ListHabitsFragment extends Fragment
|
|||||||
loader.setCheckmarkCount(helper.getButtonCount());
|
loader.setCheckmarkCount(helper.getButtonCount());
|
||||||
|
|
||||||
llHint.setOnClickListener(this);
|
llHint.setOnClickListener(this);
|
||||||
tvStarEmpty.setTypeface(helper.getFontawesome());
|
tvStarEmpty.setTypeface(UIHelper.getFontAwesome(activity));
|
||||||
|
|
||||||
adapter = new HabitListAdapter(getActivity(), loader);
|
adapter = new HabitListAdapter(getActivity(), loader);
|
||||||
adapter.setSelectedPositions(selectedPositions);
|
adapter.setSelectedPositions(selectedPositions);
|
||||||
@@ -141,7 +143,7 @@ public class ListHabitsFragment extends Fragment
|
|||||||
|
|
||||||
if(savedInstanceState != null)
|
if(savedInstanceState != null)
|
||||||
{
|
{
|
||||||
EditHabitFragment frag = (EditHabitFragment) getFragmentManager()
|
EditHabitDialogFragment frag = (EditHabitDialogFragment) getFragmentManager()
|
||||||
.findFragmentByTag("editHabit");
|
.findFragmentByTag("editHabit");
|
||||||
if(frag != null) frag.setOnSavedListener(this);
|
if(frag != null) frag.setOnSavedListener(this);
|
||||||
}
|
}
|
||||||
@@ -217,7 +219,7 @@ public class ListHabitsFragment extends Fragment
|
|||||||
{
|
{
|
||||||
case R.id.action_add:
|
case R.id.action_add:
|
||||||
{
|
{
|
||||||
EditHabitFragment frag = EditHabitFragment.createHabitFragment();
|
EditHabitDialogFragment frag = EditHabitDialogFragment.createHabitFragment();
|
||||||
frag.setOnSavedListener(this);
|
frag.setOnSavedListener(this);
|
||||||
frag.show(getFragmentManager(), "editHabit");
|
frag.show(getFragmentManager(), "editHabit");
|
||||||
return true;
|
return true;
|
||||||
@@ -284,7 +286,7 @@ public class ListHabitsFragment extends Fragment
|
|||||||
callback.setOnSavedListener(this);
|
callback.setOnSavedListener(this);
|
||||||
callback.setListener(this);
|
callback.setListener(this);
|
||||||
|
|
||||||
actionMode = getActivity().startActionMode(callback);
|
actionMode = activity.startSupportActionMode(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(actionMode != null) actionMode.invalidate();
|
if(actionMode != null) actionMode.invalidate();
|
||||||
@@ -328,17 +330,18 @@ public class ListHabitsFragment extends Fragment
|
|||||||
|
|
||||||
private void toggleCheck(View v)
|
private void toggleCheck(View v)
|
||||||
{
|
{
|
||||||
Long tag = (Long) v.getTag(R.string.habit_key);
|
Long id = helper.getHabitIdFromCheckmarkView(v);
|
||||||
Integer offset = (Integer) v.getTag(R.string.offset_key);
|
Habit habit = loader.habits.get(id);
|
||||||
long timestamp = DateHelper.getStartOfDay(
|
|
||||||
DateHelper.getLocalTime() - offset * DateHelper.millisecondsInOneDay);
|
|
||||||
|
|
||||||
Habit habit = loader.habits.get(tag);
|
|
||||||
if(habit == null) return;
|
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);
|
helper.toggleCheckmarkView(v, habit);
|
||||||
|
|
||||||
|
long timestamp = helper.getTimestampFromCheckmarkView(v);
|
||||||
executeCommand(new ToggleRepetitionCommand(habit, timestamp), habit.getId());
|
executeCommand(new ToggleRepetitionCommand(habit, timestamp), habit.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -380,6 +383,7 @@ public class ListHabitsFragment extends Fragment
|
|||||||
else loader.updateHabit(refreshKey);
|
else loader.updateHabit(refreshKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void onActionModeDestroyed(ActionMode mode)
|
public void onActionModeDestroyed(ActionMode mode)
|
||||||
{
|
{
|
||||||
actionMode = null;
|
actionMode = null;
|
||||||
|
|||||||
@@ -20,19 +20,23 @@
|
|||||||
package org.isoron.uhabits.fragments;
|
package org.isoron.uhabits.fragments;
|
||||||
|
|
||||||
import android.app.backup.BackupManager;
|
import android.app.backup.BackupManager;
|
||||||
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.Preference;
|
import android.support.v7.preference.Preference;
|
||||||
import android.preference.PreferenceCategory;
|
import android.support.v7.preference.PreferenceCategory;
|
||||||
import android.preference.PreferenceFragment;
|
import android.support.v7.preference.PreferenceFragmentCompat;
|
||||||
|
|
||||||
import org.isoron.uhabits.MainActivity;
|
import org.isoron.uhabits.MainActivity;
|
||||||
import org.isoron.uhabits.R;
|
import org.isoron.uhabits.R;
|
||||||
|
import org.isoron.uhabits.helpers.ReminderHelper;
|
||||||
import org.isoron.uhabits.helpers.UIHelper;
|
import org.isoron.uhabits.helpers.UIHelper;
|
||||||
|
|
||||||
public class SettingsFragment extends PreferenceFragment
|
public class SettingsFragment extends PreferenceFragmentCompat
|
||||||
implements SharedPreferences.OnSharedPreferenceChangeListener
|
implements SharedPreferences.OnSharedPreferenceChangeListener
|
||||||
{
|
{
|
||||||
|
private static int RINGTONE_REQUEST_CODE = 1;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState)
|
public void onCreate(Bundle savedInstanceState)
|
||||||
{
|
{
|
||||||
@@ -44,10 +48,18 @@ public class SettingsFragment extends PreferenceFragment
|
|||||||
setResultOnPreferenceClick("exportDB", MainActivity.RESULT_EXPORT_DB);
|
setResultOnPreferenceClick("exportDB", MainActivity.RESULT_EXPORT_DB);
|
||||||
setResultOnPreferenceClick("bugReport", MainActivity.RESULT_BUG_REPORT);
|
setResultOnPreferenceClick("bugReport", MainActivity.RESULT_BUG_REPORT);
|
||||||
|
|
||||||
|
updateRingtoneDescription();
|
||||||
|
|
||||||
if(UIHelper.isLocaleFullyTranslated())
|
if(UIHelper.isLocaleFullyTranslated())
|
||||||
removePreference("translate", "linksCategory");
|
removePreference("translate", "linksCategory");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreatePreferences(Bundle bundle, String s)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private void removePreference(String preferenceKey, String categoryKey)
|
private void removePreference(String preferenceKey, String categoryKey)
|
||||||
{
|
{
|
||||||
PreferenceCategory cat = (PreferenceCategory) findPreference(categoryKey);
|
PreferenceCategory cat = (PreferenceCategory) findPreference(categoryKey);
|
||||||
@@ -91,4 +103,38 @@ public class SettingsFragment extends PreferenceFragment
|
|||||||
{
|
{
|
||||||
BackupManager.dataChanged("org.isoron.uhabits");
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -19,12 +19,9 @@
|
|||||||
|
|
||||||
package org.isoron.uhabits.fragments;
|
package org.isoron.uhabits.fragments;
|
||||||
|
|
||||||
import android.app.Fragment;
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.graphics.Color;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.PreferenceManager;
|
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.v4.app.Fragment;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
@@ -40,8 +37,10 @@ import org.isoron.uhabits.HabitBroadcastReceiver;
|
|||||||
import org.isoron.uhabits.R;
|
import org.isoron.uhabits.R;
|
||||||
import org.isoron.uhabits.ShowHabitActivity;
|
import org.isoron.uhabits.ShowHabitActivity;
|
||||||
import org.isoron.uhabits.commands.Command;
|
import org.isoron.uhabits.commands.Command;
|
||||||
|
import org.isoron.uhabits.dialogs.EditHabitDialogFragment;
|
||||||
import org.isoron.uhabits.dialogs.HistoryEditorDialog;
|
import org.isoron.uhabits.dialogs.HistoryEditorDialog;
|
||||||
import org.isoron.uhabits.helpers.ColorHelper;
|
import org.isoron.uhabits.helpers.ColorHelper;
|
||||||
|
import org.isoron.uhabits.helpers.DateHelper;
|
||||||
import org.isoron.uhabits.helpers.ReminderHelper;
|
import org.isoron.uhabits.helpers.ReminderHelper;
|
||||||
import org.isoron.uhabits.helpers.UIHelper;
|
import org.isoron.uhabits.helpers.UIHelper;
|
||||||
import org.isoron.uhabits.models.Habit;
|
import org.isoron.uhabits.models.Habit;
|
||||||
@@ -73,11 +72,15 @@ public class ShowHabitFragment extends Fragment
|
|||||||
@Nullable
|
@Nullable
|
||||||
private HabitScoreView scoreView;
|
private HabitScoreView scoreView;
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private SharedPreferences prefs;
|
|
||||||
|
|
||||||
private int previousScoreInterval;
|
private int previousScoreInterval;
|
||||||
|
|
||||||
|
private float todayScore;
|
||||||
|
private float lastMonthScore;
|
||||||
|
private float lastYearScore;
|
||||||
|
|
||||||
|
private int activeColor;
|
||||||
|
private int inactiveColor;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onStart()
|
public void onStart()
|
||||||
{
|
{
|
||||||
@@ -90,7 +93,12 @@ public class ShowHabitFragment extends Fragment
|
|||||||
{
|
{
|
||||||
View view = inflater.inflate(R.layout.show_habit, container, false);
|
View view = inflater.inflate(R.layout.show_habit, container, false);
|
||||||
activity = (ShowHabitActivity) getActivity();
|
activity = (ShowHabitActivity) getActivity();
|
||||||
|
|
||||||
habit = activity.getHabit();
|
habit = activity.getHabit();
|
||||||
|
activeColor = ColorHelper.getColor(getContext(), habit.color);
|
||||||
|
inactiveColor = UIHelper.getStyledColor(getContext(), R.attr.mediumContrastTextColor);
|
||||||
|
|
||||||
|
updateHeader(view);
|
||||||
|
|
||||||
dataViews = new LinkedList<>();
|
dataViews = new LinkedList<>();
|
||||||
|
|
||||||
@@ -99,13 +107,10 @@ public class ShowHabitFragment extends Fragment
|
|||||||
|
|
||||||
scoreView = (HabitScoreView) view.findViewById(R.id.scoreView);
|
scoreView = (HabitScoreView) view.findViewById(R.id.scoreView);
|
||||||
|
|
||||||
prefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
|
int defaultScoreInterval = UIHelper.getDefaultScoreInterval(getContext());
|
||||||
int defaultScoreInterval = prefs.getInt("pref_score_view_interval", 1);
|
|
||||||
if(defaultScoreInterval > 5 || defaultScoreInterval < 0) defaultScoreInterval = 1;
|
|
||||||
previousScoreInterval = defaultScoreInterval;
|
previousScoreInterval = defaultScoreInterval;
|
||||||
setScoreBucketSize(defaultScoreInterval);
|
setScoreBucketSize(defaultScoreInterval);
|
||||||
|
|
||||||
|
|
||||||
sStrengthInterval.setSelection(defaultScoreInterval);
|
sStrengthInterval.setSelection(defaultScoreInterval);
|
||||||
sStrengthInterval.setOnItemSelectedListener(this);
|
sStrengthInterval.setOnItemSelectedListener(this);
|
||||||
|
|
||||||
@@ -133,7 +138,7 @@ public class ShowHabitFragment extends Fragment
|
|||||||
|
|
||||||
if(savedInstanceState != null)
|
if(savedInstanceState != null)
|
||||||
{
|
{
|
||||||
EditHabitFragment fragEdit = (EditHabitFragment) getFragmentManager()
|
EditHabitDialogFragment fragEdit = (EditHabitDialogFragment) getFragmentManager()
|
||||||
.findFragmentByTag("editHabit");
|
.findFragmentByTag("editHabit");
|
||||||
HistoryEditorDialog fragEditor = (HistoryEditorDialog) getFragmentManager()
|
HistoryEditorDialog fragEditor = (HistoryEditorDialog) getFragmentManager()
|
||||||
.findFragmentByTag("historyEditor");
|
.findFragmentByTag("historyEditor");
|
||||||
@@ -147,6 +152,56 @@ public class ShowHabitFragment extends Fragment
|
|||||||
return view;
|
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
|
@Override
|
||||||
public void onResume()
|
public void onResume()
|
||||||
{
|
{
|
||||||
@@ -154,42 +209,53 @@ public class ShowHabitFragment extends Fragment
|
|||||||
refreshData();
|
refreshData();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateScoreRing(View view)
|
private void updateScore(View view)
|
||||||
{
|
{
|
||||||
if(habit == null) return;
|
if(habit == null) return;
|
||||||
if(view == null) return;
|
if(view == null) return;
|
||||||
|
|
||||||
float todayValue = (float) habit.scores.getTodayValue();
|
float todayPercentage = todayScore / Score.MAX_VALUE;
|
||||||
float percentage = todayValue / 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);
|
RingView scoreRing = (RingView) view.findViewById(R.id.scoreRing);
|
||||||
scoreRing.setColor(habit.color);
|
int androidColor = ColorHelper.getColor(getActivity(), habit.color);
|
||||||
scoreRing.setPercentage(percentage);
|
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)
|
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.tvHistory);
|
||||||
updateColor(view, R.id.tvOverview);
|
updateColor(view, R.id.tvOverview);
|
||||||
updateColor(view, R.id.tvStrength);
|
updateColor(view, R.id.tvStrength);
|
||||||
updateColor(view, R.id.tvStreaks);
|
updateColor(view, R.id.tvStreaks);
|
||||||
updateColor(view, R.id.tvWeekdayFreq);
|
updateColor(view, R.id.tvWeekdayFreq);
|
||||||
|
updateColor(view, R.id.scoreLabel);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateColor(View view, int viewId)
|
private void updateColor(View view, int viewId)
|
||||||
{
|
{
|
||||||
if(habit == null) return;
|
if(habit == null || activity == null) return;
|
||||||
|
|
||||||
TextView textView = (TextView) view.findViewById(viewId);
|
TextView textView = (TextView) view.findViewById(viewId);
|
||||||
textView.setTextColor(habit.color);
|
int androidColor = ColorHelper.getColor(activity, habit.color);
|
||||||
|
textView.setTextColor(androidColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -207,7 +273,8 @@ public class ShowHabitFragment extends Fragment
|
|||||||
{
|
{
|
||||||
case R.id.action_edit_habit:
|
case R.id.action_edit_habit:
|
||||||
{
|
{
|
||||||
EditHabitFragment frag = EditHabitFragment.editSingleHabitFragment(habit.getId());
|
EditHabitDialogFragment
|
||||||
|
frag = EditHabitDialogFragment.editSingleHabitFragment(habit.getId());
|
||||||
frag.setOnSavedListener(this);
|
frag.setOnSavedListener(this);
|
||||||
frag.show(getFragmentManager(), "editHabit");
|
frag.show(getFragmentManager(), "editHabit");
|
||||||
return true;
|
return true;
|
||||||
@@ -227,6 +294,8 @@ public class ShowHabitFragment extends Fragment
|
|||||||
else activity.executeCommand(command, h.getId());
|
else activity.executeCommand(command, h.getId());
|
||||||
|
|
||||||
ReminderHelper.createReminderAlarms(activity);
|
ReminderHelper.createReminderAlarms(activity);
|
||||||
|
HabitBroadcastReceiver.sendRefreshBroadcast(getActivity());
|
||||||
|
|
||||||
activity.recreate();
|
activity.recreate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -241,25 +310,32 @@ public class ShowHabitFragment extends Fragment
|
|||||||
{
|
{
|
||||||
new BaseTask()
|
new BaseTask()
|
||||||
{
|
{
|
||||||
float percentage;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doInBackground()
|
protected void doInBackground()
|
||||||
{
|
{
|
||||||
|
if(habit == null) return;
|
||||||
if(dataViews == 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;
|
int count = 0;
|
||||||
for(HabitDataView view : dataViews)
|
for(HabitDataView view : dataViews)
|
||||||
{
|
{
|
||||||
view.refreshData();
|
view.refreshData();
|
||||||
onProgressUpdate(count++);
|
publishProgress(count++);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onProgressUpdate(Integer... values)
|
protected void onProgressUpdate(Integer... values)
|
||||||
{
|
{
|
||||||
updateScoreRing(getView());
|
updateScore(getView());
|
||||||
if(dataViews == null) return;
|
if(dataViews == null) return;
|
||||||
dataViews.get(values[0]).postInvalidate();
|
dataViews.get(values[0]).postInvalidate();
|
||||||
}
|
}
|
||||||
@@ -278,15 +354,15 @@ public class ShowHabitFragment extends Fragment
|
|||||||
{
|
{
|
||||||
if(scoreView == null) return;
|
if(scoreView == null) return;
|
||||||
|
|
||||||
int sizes[] = { 1, 7, 31, 92, 365 };
|
scoreView.setBucketSize(HabitScoreView.DEFAULT_BUCKET_SIZES[position]);
|
||||||
int size = sizes[position];
|
|
||||||
|
|
||||||
scoreView.setBucketSize(size);
|
if(position != previousScoreInterval)
|
||||||
if(position != previousScoreInterval) refreshData();
|
{
|
||||||
|
refreshData();
|
||||||
if(prefs != null)
|
HabitBroadcastReceiver.sendRefreshBroadcast(getActivity());
|
||||||
prefs.edit().putInt("pref_score_view_interval", position).apply();
|
}
|
||||||
|
|
||||||
|
UIHelper.setDefaultScoreInterval(getContext(), position);
|
||||||
previousScoreInterval = position;
|
previousScoreInterval = position;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,27 +19,63 @@
|
|||||||
|
|
||||||
package org.isoron.uhabits.helpers;
|
package org.isoron.uhabits.helpers;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.R;
|
||||||
|
|
||||||
public class ColorHelper
|
public class ColorHelper
|
||||||
{
|
{
|
||||||
public static final int[] palette =
|
public static int CSV_PALETTE[] =
|
||||||
{
|
{
|
||||||
Color.parseColor("#D32F2F"), // red
|
Color.parseColor("#D32F2F"), // 0 red
|
||||||
Color.parseColor("#E64A19"), // orange
|
Color.parseColor("#E64A19"), // 1 orange
|
||||||
Color.parseColor("#F9A825"), // yellow
|
Color.parseColor("#F9A825"), // 2 yellow
|
||||||
Color.parseColor("#AFB42B"), // light green
|
Color.parseColor("#AFB42B"), // 3 light green
|
||||||
Color.parseColor("#388E3C"), // dark green
|
Color.parseColor("#388E3C"), // 4 dark green
|
||||||
Color.parseColor("#00897B"), // teal
|
Color.parseColor("#00897B"), // 5 teal
|
||||||
Color.parseColor("#00ACC1"), // cyan
|
Color.parseColor("#00ACC1"), // 6 cyan
|
||||||
Color.parseColor("#039BE5"), // blue
|
Color.parseColor("#039BE5"), // 7 blue
|
||||||
Color.parseColor("#5E35B1"), // deep purple
|
Color.parseColor("#5E35B1"), // 8 deep purple
|
||||||
Color.parseColor("#8E24AA"), // purple
|
Color.parseColor("#8E24AA"), // 9 purple
|
||||||
Color.parseColor("#D81B60"), // pink
|
Color.parseColor("#D81B60"), // 10 pink
|
||||||
Color.parseColor("#303030"), // dark grey
|
Color.parseColor("#303030"), // 11 dark grey
|
||||||
Color.parseColor("#aaaaaa") // light 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)
|
public static int mixColors(int color1, int color2, float amount)
|
||||||
{
|
{
|
||||||
final byte ALPHA_CHANNEL = 24;
|
final byte ALPHA_CHANNEL = 24;
|
||||||
@@ -76,6 +112,12 @@ public class ColorHelper
|
|||||||
return setHSVParameter(color, newValue, 2);
|
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)
|
public static int setMinValue(int color, float newValue)
|
||||||
{
|
{
|
||||||
float hsv[] = new float[3];
|
float hsv[] = new float[3];
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import android.text.format.DateFormat;
|
|||||||
import org.isoron.uhabits.R;
|
import org.isoron.uhabits.R;
|
||||||
|
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Calendar;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.GregorianCalendar;
|
import java.util.GregorianCalendar;
|
||||||
import java.util.Locale;
|
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)
|
public static String[] getDayNames(int format)
|
||||||
{
|
{
|
||||||
String[] wdays = new String[7];
|
String[] wdays = new String[7];
|
||||||
|
|
||||||
GregorianCalendar day = new GregorianCalendar();
|
Calendar day = new GregorianCalendar();
|
||||||
day.set(GregorianCalendar.DAY_OF_WEEK, 0);
|
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,
|
wdays[i] = day.getDisplayName(GregorianCalendar.DAY_OF_WEEK, format,
|
||||||
Locale.getDefault());
|
Locale.getDefault());
|
||||||
@@ -172,6 +185,43 @@ public class DateHelper
|
|||||||
return wdays;
|
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[])
|
public static String formatWeekdayList(Context context, boolean weekday[])
|
||||||
{
|
{
|
||||||
String shortDayNames[] = getShortDayNames();
|
String shortDayNames[] = getShortDayNames();
|
||||||
|
|||||||
@@ -20,13 +20,14 @@
|
|||||||
package org.isoron.uhabits.helpers;
|
package org.isoron.uhabits.helpers;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.Color;
|
import android.content.SharedPreferences;
|
||||||
import android.graphics.Typeface;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.util.DisplayMetrics;
|
import android.os.Handler;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.Button;
|
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
@@ -34,43 +35,44 @@ import org.isoron.uhabits.R;
|
|||||||
import org.isoron.uhabits.loaders.HabitListLoader;
|
import org.isoron.uhabits.loaders.HabitListLoader;
|
||||||
import org.isoron.uhabits.models.Habit;
|
import org.isoron.uhabits.models.Habit;
|
||||||
import org.isoron.uhabits.models.Score;
|
import org.isoron.uhabits.models.Score;
|
||||||
|
import org.isoron.uhabits.views.RingView;
|
||||||
|
|
||||||
import java.util.GregorianCalendar;
|
import java.util.GregorianCalendar;
|
||||||
|
|
||||||
public class ListHabitsHelper
|
public class ListHabitsHelper
|
||||||
{
|
{
|
||||||
public static final int INACTIVE_COLOR = Color.rgb(200, 200, 200);
|
private static final int CHECKMARK_LEFT_TO_RIGHT = 0;
|
||||||
public static final int INACTIVE_CHECKMARK_COLOR = Color.rgb(230, 230, 230);
|
private static final int CHECKMARK_RIGHT_TO_LEFT = 1;
|
||||||
|
|
||||||
|
private final int lowContrastColor;
|
||||||
|
private final int mediumContrastColor;
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
private final HabitListLoader loader;
|
private final HabitListLoader loader;
|
||||||
private Typeface fontawesome;
|
|
||||||
|
|
||||||
public ListHabitsHelper(Context context, HabitListLoader loader)
|
public ListHabitsHelper(Context context, HabitListLoader loader)
|
||||||
{
|
{
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.loader = loader;
|
this.loader = loader;
|
||||||
|
|
||||||
fontawesome = Typeface.createFromAsset(context.getAssets(), "fontawesome-webfont.ttf");
|
lowContrastColor = UIHelper.getStyledColor(context, R.attr.lowContrastTextColor);
|
||||||
}
|
mediumContrastColor = UIHelper.getStyledColor(context, R.attr.mediumContrastTextColor);
|
||||||
|
|
||||||
public Typeface getFontawesome()
|
|
||||||
{
|
|
||||||
return fontawesome;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getButtonCount()
|
public int getButtonCount()
|
||||||
{
|
{
|
||||||
DisplayMetrics dm = context.getResources().getDisplayMetrics();
|
float screenWidth = UIHelper.getScreenWidth(context);
|
||||||
int width = (int) (dm.widthPixels / dm.density);
|
float labelWidth = context.getResources().getDimension(R.dimen.habitNameWidth);
|
||||||
return Math.max(0, (int) ((width - 160) / 42.0));
|
float buttonWidth = context.getResources().getDimension(R.dimen.checkmarkWidth);
|
||||||
|
return Math.max(0, (int) ((screenWidth - labelWidth) / buttonWidth));
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getHabitNameWidth()
|
public int getHabitNameWidth()
|
||||||
{
|
{
|
||||||
DisplayMetrics dm = context.getResources().getDisplayMetrics();
|
float screenWidth = UIHelper.getScreenWidth(context);
|
||||||
int width = (int) (dm.widthPixels / dm.density);
|
float buttonWidth = context.getResources().getDimension(R.dimen.checkmarkWidth);
|
||||||
return (int) ((width - 30 - getButtonCount() * 42) * dm.density);
|
float padding = UIHelper.dpToPixels(context, 15);
|
||||||
|
return (int) (screenWidth - padding - getButtonCount() * buttonWidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateCheckmarkButtons(Habit habit, LinearLayout llButtons)
|
public void updateCheckmarkButtons(Habit habit, LinearLayout llButtons)
|
||||||
@@ -83,8 +85,12 @@ public class ListHabitsHelper
|
|||||||
|
|
||||||
for (int i = 0; i < m; i++)
|
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.habit_key, habitId);
|
||||||
tvCheck.setTag(R.string.offset_key, i);
|
tvCheck.setTag(R.string.offset_key, i);
|
||||||
if(isChecked.length > i)
|
if(isChecked.length > i)
|
||||||
@@ -94,54 +100,32 @@ public class ListHabitsHelper
|
|||||||
|
|
||||||
public int getActiveColor(Habit habit)
|
public int getActiveColor(Habit habit)
|
||||||
{
|
{
|
||||||
int activeColor = habit.color;
|
int activeColor = ColorHelper.getColor(context, habit.color);
|
||||||
if(habit.isArchived()) activeColor = INACTIVE_COLOR;
|
if(habit.isArchived()) activeColor = mediumContrastColor;
|
||||||
|
|
||||||
return activeColor;
|
return activeColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void initializeLabelAndIcon(View itemView)
|
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 params = new LinearLayout.LayoutParams(getHabitNameWidth(),
|
||||||
LinearLayout.LayoutParams.WRAP_CONTENT, 1);
|
LinearLayout.LayoutParams.WRAP_CONTENT, 1);
|
||||||
itemView.findViewById(R.id.label).setLayoutParams(params);
|
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);
|
int activeColor = getActiveColor(habit);
|
||||||
|
|
||||||
tvName.setText(habit.name);
|
tvName.setText(habit.name);
|
||||||
tvName.setTextColor(activeColor);
|
tvName.setTextColor(activeColor);
|
||||||
|
|
||||||
if (habit.isArchived())
|
int score = loader.scores.get(habit.getId());
|
||||||
{
|
float percentage = (float) score / Score.MAX_VALUE;
|
||||||
tvStar.setText(context.getString(R.string.fa_archive));
|
|
||||||
tvStar.setTextColor(activeColor);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int score = loader.scores.get(habit.getId());
|
|
||||||
|
|
||||||
if (score < Score.HALF_STAR_CUTOFF)
|
ring.setColor(activeColor);
|
||||||
{
|
ring.setPercentage(percentage);
|
||||||
tvStar.setText(context.getString(R.string.fa_star_o));
|
ring.setPrecision(1.0f / 16);
|
||||||
tvStar.setTextColor(INACTIVE_COLOR);
|
|
||||||
}
|
|
||||||
else if (score < Score.FULL_STAR_CUTOFF)
|
|
||||||
{
|
|
||||||
tvStar.setText(context.getString(R.string.fa_star_half_o));
|
|
||||||
tvStar.setTextColor(INACTIVE_COLOR);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
tvStar.setText(context.getString(R.string.fa_star));
|
|
||||||
tvStar.setTextColor(activeColor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateCheckmark(int activeColor, TextView tvCheck, int check)
|
public void updateCheckmark(int activeColor, TextView tvCheck, int check)
|
||||||
@@ -156,27 +140,64 @@ public class ListHabitsHelper
|
|||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
tvCheck.setText(R.string.fa_check);
|
tvCheck.setText(R.string.fa_check);
|
||||||
tvCheck.setTextColor(INACTIVE_CHECKMARK_COLOR);
|
tvCheck.setTextColor(lowContrastColor);
|
||||||
tvCheck.setTag(R.string.toggle_key, 1);
|
tvCheck.setTag(R.string.toggle_key, 1);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0:
|
case 0:
|
||||||
tvCheck.setText(R.string.fa_times);
|
tvCheck.setText(R.string.fa_times);
|
||||||
tvCheck.setTextColor(INACTIVE_CHECKMARK_COLOR);
|
tvCheck.setTextColor(lowContrastColor);
|
||||||
tvCheck.setTag(R.string.toggle_key, 0);
|
tvCheck.setTag(R.string.toggle_key, 0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateHabitBackground(View view, boolean isSelected)
|
public View inflateHabitCard(LayoutInflater inflater,
|
||||||
|
View.OnLongClickListener onCheckmarkLongClickListener,
|
||||||
|
View.OnClickListener onCheckmarkClickListener)
|
||||||
{
|
{
|
||||||
if (isSelected)
|
View view = inflater.inflate(R.layout.list_habits_item, null);
|
||||||
view.setBackgroundResource(R.drawable.selected_box);
|
initializeLabelAndIcon(view);
|
||||||
|
inflateCheckmarkButtons(view, onCheckmarkLongClickListener, onCheckmarkClickListener,
|
||||||
|
inflater);
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateHabitCard(View view, Habit habit, boolean selected)
|
||||||
|
{
|
||||||
|
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
|
else
|
||||||
{
|
{
|
||||||
if (android.os.Build.VERSION.SDK_INT >= 21)
|
Drawable background;
|
||||||
view.setBackgroundResource(R.drawable.ripple_white);
|
|
||||||
else view.setBackgroundResource(R.drawable.card_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);
|
View check = inflater.inflate(R.layout.list_habits_item_check, null);
|
||||||
TextView btCheck = (TextView) check.findViewById(R.id.tvCheck);
|
TextView btCheck = (TextView) check.findViewById(R.id.tvCheck);
|
||||||
btCheck.setTypeface(fontawesome);
|
btCheck.setTypeface(UIHelper.getFontAwesome(context));
|
||||||
btCheck.setOnLongClickListener(onLongClickListener);
|
btCheck.setOnLongClickListener(onLongClickListener);
|
||||||
btCheck.setOnClickListener(onClickListener);
|
btCheck.setOnClickListener(onClickListener);
|
||||||
btCheck.setHapticFeedbackEnabled(false);
|
btCheck.setHapticFeedbackEnabled(false);
|
||||||
@@ -205,11 +226,15 @@ public class ListHabitsHelper
|
|||||||
|
|
||||||
for (int i = 0; i < getButtonCount(); i++)
|
for (int i = 0; i < getButtonCount(); i++)
|
||||||
{
|
{
|
||||||
View tvDay = inflater.inflate(R.layout.list_habits_header_check, null);
|
int position = 0;
|
||||||
Button btCheck = (Button) tvDay.findViewById(R.id.tvCheck);
|
|
||||||
btCheck.setText(DateHelper.formatHeaderDate(day));
|
|
||||||
header.addView(tvDay);
|
|
||||||
|
|
||||||
|
if(getCheckmarkOrder() == CHECKMARK_LEFT_TO_RIGHT)
|
||||||
|
position = i;
|
||||||
|
|
||||||
|
View tvDay = inflater.inflate(R.layout.list_habits_header_check, null);
|
||||||
|
TextView btCheck = (TextView) tvDay.findViewById(R.id.tvCheck);
|
||||||
|
btCheck.setText(DateHelper.formatHeaderDate(day));
|
||||||
|
header.addView(tvDay, position);
|
||||||
day.add(GregorianCalendar.DAY_OF_MONTH, -1);
|
day.add(GregorianCalendar.DAY_OF_MONTH, -1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -222,9 +247,59 @@ public class ListHabitsHelper
|
|||||||
|
|
||||||
public void toggleCheckmarkView(View v, Habit habit)
|
public void toggleCheckmarkView(View v, Habit habit)
|
||||||
{
|
{
|
||||||
|
int androidColor = ColorHelper.getColor(context, habit.color);
|
||||||
|
|
||||||
if (v.getTag(R.string.toggle_key).equals(2))
|
if (v.getTag(R.string.toggle_key).equals(2))
|
||||||
updateCheckmark(habit.color, (TextView) v, 0);
|
updateCheckmark(androidColor, (TextView) v, 0);
|
||||||
else
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,12 +23,19 @@ import android.app.AlarmManager;
|
|||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.media.Ringtone;
|
||||||
|
import android.media.RingtoneManager;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
import android.provider.Settings;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.v4.app.Fragment;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import org.isoron.uhabits.HabitBroadcastReceiver;
|
import org.isoron.uhabits.HabitBroadcastReceiver;
|
||||||
|
import org.isoron.uhabits.R;
|
||||||
import org.isoron.uhabits.models.Habit;
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
|
||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
@@ -89,4 +96,68 @@ public class ReminderHelper
|
|||||||
Log.d("ReminderHelper", String.format("Setting alarm (%s): %s",
|
Log.d("ReminderHelper", String.format("Setting alarm (%s): %s",
|
||||||
DateFormat.getDateTimeInstance().format(new Date(reminderTime)), habit.name));
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,37 +19,58 @@
|
|||||||
|
|
||||||
package org.isoron.uhabits.helpers;
|
package org.isoron.uhabits.helpers;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
|
import android.content.res.TypedArray;
|
||||||
import android.graphics.Typeface;
|
import android.graphics.Typeface;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Debug;
|
import android.os.Debug;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.util.DisplayMetrics;
|
import android.util.DisplayMetrics;
|
||||||
import android.util.Log;
|
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.inputmethod.InputMethodManager;
|
import android.view.inputmethod.InputMethodManager;
|
||||||
|
|
||||||
import org.isoron.uhabits.BuildConfig;
|
import org.isoron.uhabits.BuildConfig;
|
||||||
|
import org.isoron.uhabits.HabitsApplication;
|
||||||
|
import org.isoron.uhabits.R;
|
||||||
import org.isoron.uhabits.commands.Command;
|
import org.isoron.uhabits.commands.Command;
|
||||||
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
public abstract class UIHelper
|
public abstract class UIHelper
|
||||||
{
|
{
|
||||||
|
|
||||||
public static final String ISORON_NAMESPACE = "http://isoron.org/android";
|
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
|
public interface OnSavedListener
|
||||||
{
|
{
|
||||||
void onSaved(Command command, Object savedObject);
|
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)
|
public static void showSoftKeyboard(View view)
|
||||||
{
|
{
|
||||||
InputMethodManager imm = (InputMethodManager) view.getContext()
|
InputMethodManager imm = (InputMethodManager) view.getContext()
|
||||||
@@ -87,6 +108,14 @@ public abstract class UIHelper
|
|||||||
else return defaultValue;
|
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,
|
public static int getIntAttribute(Context context, AttributeSet attrs, String name,
|
||||||
int defaultValue)
|
int defaultValue)
|
||||||
{
|
{
|
||||||
@@ -95,6 +124,14 @@ public abstract class UIHelper
|
|||||||
else return defaultValue;
|
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,
|
public static float getFloatAttribute(Context context, AttributeSet attrs, String name,
|
||||||
float defaultValue)
|
float defaultValue)
|
||||||
{
|
{
|
||||||
@@ -153,16 +190,132 @@ public abstract class UIHelper
|
|||||||
public static boolean isLocaleFullyTranslated()
|
public static boolean isLocaleFullyTranslated()
|
||||||
{
|
{
|
||||||
// TODO: Move this to another place, or detect automatically
|
// TODO: Move this to another place, or detect automatically
|
||||||
String fullyTranslatedLanguages[] = { "en", "ar", "cs", "de", "it", "ja", "ko", "po", "pl",
|
String fullyTranslatedLanguages[] = { "ca", "zh", "en", "de", "in", "it", "ko", "pl", "pt",
|
||||||
"pt", "ru", "sv", "zh", "es", "fr" };
|
"es", "tk", "uk"};
|
||||||
|
|
||||||
final String currentLanguage = Locale.getDefault().getLanguage();
|
final String currentLanguage = Locale.getDefault().getLanguage();
|
||||||
|
|
||||||
Log.d("UIHelper", String.format("lang=%s", currentLanguage));
|
|
||||||
|
|
||||||
for(String lang : fullyTranslatedLanguages)
|
for(String lang : fullyTranslatedLanguages)
|
||||||
if(currentLanguage.equals(lang)) return true;
|
if(currentLanguage.equals(lang)) return true;
|
||||||
|
|
||||||
return false;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,7 +71,11 @@ public class Habit extends Model
|
|||||||
public Integer freqDen;
|
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")
|
@Column(name = "color")
|
||||||
public Integer color;
|
public Integer color;
|
||||||
@@ -166,7 +170,7 @@ public class Habit extends Model
|
|||||||
*/
|
*/
|
||||||
public Habit()
|
public Habit()
|
||||||
{
|
{
|
||||||
this.color = ColorHelper.palette[5];
|
this.color = 5;
|
||||||
this.position = Habit.countWithArchived();
|
this.position = Habit.countWithArchived();
|
||||||
this.highlight = 0;
|
this.highlight = 0;
|
||||||
this.archived = 0;
|
this.archived = 0;
|
||||||
@@ -492,8 +496,15 @@ public class Habit extends Model
|
|||||||
|
|
||||||
for(Habit habit : habits)
|
for(Habit habit : habits)
|
||||||
{
|
{
|
||||||
String[] cols = { habit.name, habit.description, Integer.toString(habit.freqNum),
|
String[] cols =
|
||||||
Integer.toString(habit.freqDen), ColorHelper.toHTML(habit.color) };
|
{
|
||||||
|
habit.name,
|
||||||
|
habit.description,
|
||||||
|
Integer.toString(habit.freqNum),
|
||||||
|
Integer.toString(habit.freqDen),
|
||||||
|
ColorHelper.toHTML(ColorHelper.CSV_PALETTE[habit.color])
|
||||||
|
};
|
||||||
|
|
||||||
csv.writeNext(cols, false);
|
csv.writeNext(cols, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,202 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
|
||||||
*
|
|
||||||
* This file is part of Loop Habit Tracker.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by the
|
|
||||||
* Free Software Foundation, either version 3 of the License, or (at your
|
|
||||||
* option) any later version.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.isoron.uhabits.views;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.graphics.Canvas;
|
|
||||||
import android.graphics.Color;
|
|
||||||
import android.graphics.Paint;
|
|
||||||
import android.graphics.Rect;
|
|
||||||
import android.graphics.Typeface;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.text.Layout;
|
|
||||||
import android.text.StaticLayout;
|
|
||||||
import android.text.TextPaint;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
import android.view.View;
|
|
||||||
|
|
||||||
import org.isoron.uhabits.R;
|
|
||||||
import org.isoron.uhabits.helpers.ColorHelper;
|
|
||||||
import org.isoron.uhabits.models.Habit;
|
|
||||||
|
|
||||||
public class CheckmarkView extends View implements HabitDataView
|
|
||||||
{
|
|
||||||
private Paint pCard;
|
|
||||||
private Paint pIcon;
|
|
||||||
|
|
||||||
private int primaryColor;
|
|
||||||
private int timesColor;
|
|
||||||
private int darkGrey;
|
|
||||||
|
|
||||||
private int width;
|
|
||||||
private int height;
|
|
||||||
private float leftMargin;
|
|
||||||
private float topMargin;
|
|
||||||
private float padding;
|
|
||||||
private String label;
|
|
||||||
|
|
||||||
private String fa_check;
|
|
||||||
private String fa_times;
|
|
||||||
|
|
||||||
private int check_status;
|
|
||||||
|
|
||||||
private Rect rect;
|
|
||||||
private TextPaint textPaint;
|
|
||||||
private StaticLayout labelLayout;
|
|
||||||
private Habit habit;
|
|
||||||
|
|
||||||
public CheckmarkView(Context context)
|
|
||||||
{
|
|
||||||
super(context);
|
|
||||||
init(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public CheckmarkView(Context context, AttributeSet attrs)
|
|
||||||
{
|
|
||||||
super(context, attrs);
|
|
||||||
init(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void init(Context context)
|
|
||||||
{
|
|
||||||
Typeface fontawesome =
|
|
||||||
Typeface.createFromAsset(context.getAssets(), "fontawesome-webfont.ttf");
|
|
||||||
|
|
||||||
pCard = new Paint();
|
|
||||||
pCard.setAntiAlias(true);
|
|
||||||
|
|
||||||
pIcon = new Paint();
|
|
||||||
pIcon.setAntiAlias(true);
|
|
||||||
pIcon.setTypeface(fontawesome);
|
|
||||||
pIcon.setTextAlign(Paint.Align.CENTER);
|
|
||||||
|
|
||||||
textPaint = new TextPaint();
|
|
||||||
textPaint.setColor(Color.WHITE);
|
|
||||||
textPaint.setAntiAlias(true);
|
|
||||||
|
|
||||||
fa_check = context.getString(R.string.fa_check);
|
|
||||||
fa_times = context.getString(R.string.fa_times);
|
|
||||||
|
|
||||||
primaryColor = ColorHelper.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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,169 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.views;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.R;
|
||||||
|
import org.isoron.uhabits.helpers.ColorHelper;
|
||||||
|
import org.isoron.uhabits.helpers.UIHelper;
|
||||||
|
import org.isoron.uhabits.models.Checkmark;
|
||||||
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
import org.isoron.uhabits.models.Score;
|
||||||
|
|
||||||
|
public class CheckmarkWidgetView extends HabitWidgetView implements HabitDataView
|
||||||
|
{
|
||||||
|
private int activeColor;
|
||||||
|
private float percentage;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private RingView ring;
|
||||||
|
private TextView label;
|
||||||
|
private int checkmarkValue;
|
||||||
|
|
||||||
|
public CheckmarkWidgetView(Context context)
|
||||||
|
{
|
||||||
|
super(context);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CheckmarkWidgetView(Context context, AttributeSet attrs)
|
||||||
|
{
|
||||||
|
super(context, attrs);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init()
|
||||||
|
{
|
||||||
|
ring = (RingView) findViewById(R.id.scoreRing);
|
||||||
|
label = (TextView) findViewById(R.id.label);
|
||||||
|
|
||||||
|
if(ring != null) ring.setIsTransparencyEnabled(true);
|
||||||
|
|
||||||
|
if(isInEditMode())
|
||||||
|
{
|
||||||
|
percentage = 0.75f;
|
||||||
|
name = "Wake up early";
|
||||||
|
activeColor = ColorHelper.CSV_PALETTE[6];
|
||||||
|
checkmarkValue = Checkmark.CHECKED_EXPLICITLY;
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setHabit(@NonNull Habit habit)
|
||||||
|
{
|
||||||
|
super.setHabit(habit);
|
||||||
|
this.name = habit.name;
|
||||||
|
this.activeColor = ColorHelper.getColor(getContext(), habit.color);
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void refresh()
|
||||||
|
{
|
||||||
|
if (backgroundPaint == null || frame == null || ring == null) return;
|
||||||
|
|
||||||
|
Context context = getContext();
|
||||||
|
|
||||||
|
String text;
|
||||||
|
int backgroundColor;
|
||||||
|
int foregroundColor;
|
||||||
|
|
||||||
|
switch (checkmarkValue)
|
||||||
|
{
|
||||||
|
case Checkmark.CHECKED_EXPLICITLY:
|
||||||
|
text = getResources().getString(R.string.fa_check);
|
||||||
|
backgroundColor = activeColor;
|
||||||
|
foregroundColor =
|
||||||
|
UIHelper.getStyledColor(context, R.attr.highContrastReverseTextColor);
|
||||||
|
|
||||||
|
setShadowAlpha(0x4f);
|
||||||
|
rebuildBackground();
|
||||||
|
|
||||||
|
backgroundPaint.setColor(backgroundColor);
|
||||||
|
frame.setBackgroundDrawable(background);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Checkmark.CHECKED_IMPLICITLY:
|
||||||
|
text = getResources().getString(R.string.fa_check);
|
||||||
|
backgroundColor = UIHelper.getStyledColor(context, R.attr.cardBackgroundColor);
|
||||||
|
foregroundColor = UIHelper.getStyledColor(context, R.attr.mediumContrastTextColor);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Checkmark.UNCHECKED:
|
||||||
|
default:
|
||||||
|
text = getResources().getString(R.string.fa_times);
|
||||||
|
backgroundColor = UIHelper.getStyledColor(context, R.attr.cardBackgroundColor);
|
||||||
|
foregroundColor = UIHelper.getStyledColor(context, R.attr.mediumContrastTextColor);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ring.setPercentage(percentage);
|
||||||
|
ring.setColor(foregroundColor);
|
||||||
|
ring.setBackgroundColor(backgroundColor);
|
||||||
|
ring.setText(text);
|
||||||
|
|
||||||
|
label.setText(name);
|
||||||
|
label.setTextColor(foregroundColor);
|
||||||
|
|
||||||
|
requestLayout();
|
||||||
|
postInvalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
|
||||||
|
{
|
||||||
|
int width = MeasureSpec.getSize(widthMeasureSpec);
|
||||||
|
int height = MeasureSpec.getSize(heightMeasureSpec);
|
||||||
|
|
||||||
|
float w = width;
|
||||||
|
float h = width * 1.25f;
|
||||||
|
float scale = Math.min(width / w, height / h);
|
||||||
|
|
||||||
|
w *= scale;
|
||||||
|
h *= scale;
|
||||||
|
|
||||||
|
widthMeasureSpec = MeasureSpec.makeMeasureSpec((int) w, MeasureSpec.EXACTLY);
|
||||||
|
heightMeasureSpec = MeasureSpec.makeMeasureSpec((int) h, MeasureSpec.EXACTLY);
|
||||||
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void refreshData()
|
||||||
|
{
|
||||||
|
if(habit == null) return;
|
||||||
|
this.percentage = (float) habit.scores.getTodayValue() / Score.MAX_VALUE;
|
||||||
|
this.checkmarkValue = habit.checkmarks.getTodayValue();
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
protected Integer getInnerLayoutId()
|
||||||
|
{
|
||||||
|
return R.layout.widget_checkmark;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.views;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.R;
|
||||||
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
|
||||||
|
public class GraphWidgetView extends HabitWidgetView implements HabitDataView
|
||||||
|
{
|
||||||
|
|
||||||
|
private final HabitDataView dataView;
|
||||||
|
private TextView title;
|
||||||
|
|
||||||
|
public GraphWidgetView(Context context, HabitDataView dataView)
|
||||||
|
{
|
||||||
|
super(context);
|
||||||
|
this.dataView = dataView;
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init()
|
||||||
|
{
|
||||||
|
ViewGroup.LayoutParams params =
|
||||||
|
new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT);
|
||||||
|
((View) dataView).setLayoutParams(params);
|
||||||
|
|
||||||
|
ViewGroup innerFrame = (ViewGroup) findViewById(R.id.innerFrame);
|
||||||
|
innerFrame.addView(((View) dataView));
|
||||||
|
|
||||||
|
title = (TextView) findViewById(R.id.title);
|
||||||
|
title.setVisibility(VISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setHabit(@NonNull Habit habit)
|
||||||
|
{
|
||||||
|
super.setHabit(habit);
|
||||||
|
dataView.setHabit(habit);
|
||||||
|
title.setText(habit.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void refreshData()
|
||||||
|
{
|
||||||
|
if(habit == null) return;
|
||||||
|
dataView.refreshData();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
protected Integer getInnerLayoutId()
|
||||||
|
{
|
||||||
|
return R.layout.widget_graph;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,13 +21,14 @@ package org.isoron.uhabits.views;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.Color;
|
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
import android.graphics.RectF;
|
import android.graphics.RectF;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.R;
|
||||||
import org.isoron.uhabits.helpers.ColorHelper;
|
import org.isoron.uhabits.helpers.ColorHelper;
|
||||||
import org.isoron.uhabits.helpers.DateHelper;
|
import org.isoron.uhabits.helpers.DateHelper;
|
||||||
|
import org.isoron.uhabits.helpers.UIHelper;
|
||||||
import org.isoron.uhabits.models.Habit;
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
@@ -56,13 +57,12 @@ public class HabitFrequencyView extends ScrollableDataView implements HabitDataV
|
|||||||
private int nColumns;
|
private int nColumns;
|
||||||
|
|
||||||
private int textColor;
|
private int textColor;
|
||||||
private int dimmedTextColor;
|
private int gridColor;
|
||||||
private int[] colors;
|
private int[] colors;
|
||||||
private int primaryColor;
|
private int primaryColor;
|
||||||
private boolean isBackgroundTransparent;
|
private boolean isBackgroundTransparent;
|
||||||
|
|
||||||
private HashMap<Long, Integer[]> frequency;
|
private HashMap<Long, Integer[]> frequency;
|
||||||
private String wdays[];
|
|
||||||
|
|
||||||
public HabitFrequencyView(Context context)
|
public HabitFrequencyView(Context context)
|
||||||
{
|
{
|
||||||
@@ -73,7 +73,7 @@ public class HabitFrequencyView extends ScrollableDataView implements HabitDataV
|
|||||||
public HabitFrequencyView(Context context, AttributeSet attrs)
|
public HabitFrequencyView(Context context, AttributeSet attrs)
|
||||||
{
|
{
|
||||||
super(context, attrs);
|
super(context, attrs);
|
||||||
this.primaryColor = ColorHelper.palette[7];
|
this.primaryColor = ColorHelper.getColor(getContext(), 7);
|
||||||
this.frequency = new HashMap<>();
|
this.frequency = new HashMap<>();
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
@@ -89,8 +89,6 @@ public class HabitFrequencyView extends ScrollableDataView implements HabitDataV
|
|||||||
createPaints();
|
createPaints();
|
||||||
createColors();
|
createColors();
|
||||||
|
|
||||||
wdays = DateHelper.getShortDayNames();
|
|
||||||
|
|
||||||
dfMonth = DateHelper.getDateFormat("MMM");
|
dfMonth = DateHelper.getDateFormat("MMM");
|
||||||
dfYear = DateHelper.getDateFormat("yyyy");
|
dfYear = DateHelper.getDateFormat("yyyy");
|
||||||
|
|
||||||
@@ -101,25 +99,15 @@ public class HabitFrequencyView extends ScrollableDataView implements HabitDataV
|
|||||||
private void createColors()
|
private void createColors()
|
||||||
{
|
{
|
||||||
if(habit != null)
|
if(habit != null)
|
||||||
this.primaryColor = habit.color;
|
|
||||||
|
|
||||||
if (isBackgroundTransparent)
|
|
||||||
{
|
{
|
||||||
primaryColor = ColorHelper.setSaturation(primaryColor, 0.75f);
|
this.primaryColor = ColorHelper.getColor(getContext(), habit.color);
|
||||||
primaryColor = ColorHelper.setValue(primaryColor, 1.0f);
|
}
|
||||||
|
|
||||||
textColor = Color.argb(192, 255, 255, 255);
|
textColor = UIHelper.getStyledColor(getContext(), R.attr.mediumContrastTextColor);
|
||||||
dimmedTextColor = Color.argb(128, 255, 255, 255);
|
gridColor = UIHelper.getStyledColor(getContext(), R.attr.lowContrastTextColor);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
textColor = Color.argb(64, 0, 0, 0);
|
|
||||||
dimmedTextColor = Color.argb(16, 0, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
colors = new int[4];
|
colors = new int[4];
|
||||||
|
colors[0] = gridColor;
|
||||||
colors[0] = Color.rgb(230, 230, 230);
|
|
||||||
colors[3] = primaryColor;
|
colors[3] = primaryColor;
|
||||||
colors[1] = ColorHelper.mixColors(colors[0], colors[3], 0.66f);
|
colors[1] = ColorHelper.mixColors(colors[0], colors[3], 0.66f);
|
||||||
colors[2] = ColorHelper.mixColors(colors[0], colors[3], 0.33f);
|
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;
|
float rowHeight = rect.height() / 8.0f;
|
||||||
prevRect.set(rect);
|
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.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)
|
if(values != null)
|
||||||
drawMarker(canvas, rect, values[i]);
|
drawMarker(canvas, rect, values[i]);
|
||||||
|
|
||||||
@@ -287,11 +277,10 @@ public class HabitFrequencyView extends ScrollableDataView implements HabitDataV
|
|||||||
|
|
||||||
pText.setTextAlign(Paint.Align.LEFT);
|
pText.setTextAlign(Paint.Align.LEFT);
|
||||||
pText.setColor(textColor);
|
pText.setColor(textColor);
|
||||||
pGrid.setColor(dimmedTextColor);
|
pGrid.setColor(gridColor);
|
||||||
|
|
||||||
for (int i = 0; i < nRows; i++)
|
for (String day : DateHelper.getLocaleDayNames(Calendar.SHORT)) {
|
||||||
{
|
canvas.drawText(day, rGrid.right - columnWidth,
|
||||||
canvas.drawText(wdays[i], rGrid.right - columnWidth,
|
|
||||||
rGrid.top + rowHeight / 2 + 0.25f * em, pText);
|
rGrid.top + rowHeight / 2 + 0.25f * em, pText);
|
||||||
|
|
||||||
pGrid.setStrokeWidth(1f);
|
pGrid.setStrokeWidth(1f);
|
||||||
|
|||||||
@@ -57,19 +57,20 @@ public class HabitHistoryView extends ScrollableDataView implements HabitDataVie
|
|||||||
private float columnHeight;
|
private float columnHeight;
|
||||||
private int nColumns;
|
private int nColumns;
|
||||||
|
|
||||||
private String wdays[];
|
|
||||||
private SimpleDateFormat dfMonth;
|
private SimpleDateFormat dfMonth;
|
||||||
private SimpleDateFormat dfYear;
|
private SimpleDateFormat dfYear;
|
||||||
|
|
||||||
private Calendar baseDate;
|
private Calendar baseDate;
|
||||||
private int nDays;
|
private int nDays;
|
||||||
private int todayWeekday;
|
/** 0-based-position of today in the column */
|
||||||
|
private int todayPositionInColumn;
|
||||||
private int colors[];
|
private int colors[];
|
||||||
private RectF baseLocation;
|
private RectF baseLocation;
|
||||||
private int primaryColor;
|
private int primaryColor;
|
||||||
|
|
||||||
private boolean isBackgroundTransparent;
|
private boolean isBackgroundTransparent;
|
||||||
private int textColor;
|
private int textColor;
|
||||||
|
private int reverseTextColor;
|
||||||
private boolean isEditable;
|
private boolean isEditable;
|
||||||
|
|
||||||
public HabitHistoryView(Context context)
|
public HabitHistoryView(Context context)
|
||||||
@@ -92,13 +93,12 @@ public class HabitHistoryView extends ScrollableDataView implements HabitDataVie
|
|||||||
|
|
||||||
private void init()
|
private void init()
|
||||||
{
|
{
|
||||||
createPaints();
|
|
||||||
createColors();
|
createColors();
|
||||||
|
createPaints();
|
||||||
|
|
||||||
isEditable = false;
|
isEditable = false;
|
||||||
checkmarks = new int[0];
|
checkmarks = new int[0];
|
||||||
primaryColor = ColorHelper.palette[7];
|
primaryColor = ColorHelper.getColor(getContext(), 7);
|
||||||
wdays = DateHelper.getShortDayNames();
|
|
||||||
dfMonth = DateHelper.getDateFormat("MMM");
|
dfMonth = DateHelper.getDateFormat("MMM");
|
||||||
dfYear = DateHelper.getDateFormat("yyyy");
|
dfYear = DateHelper.getDateFormat("yyyy");
|
||||||
|
|
||||||
@@ -111,10 +111,11 @@ public class HabitHistoryView extends ScrollableDataView implements HabitDataVie
|
|||||||
baseDate.add(Calendar.DAY_OF_YEAR, -(getDataOffset() - 1) * 7);
|
baseDate.add(Calendar.DAY_OF_YEAR, -(getDataOffset() - 1) * 7);
|
||||||
|
|
||||||
nDays = (nColumns - 1) * 7;
|
nDays = (nColumns - 1) * 7;
|
||||||
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, -nDays);
|
||||||
baseDate.add(Calendar.DAY_OF_YEAR, -todayWeekday);
|
baseDate.add(Calendar.DAY_OF_YEAR, -todayPositionInColumn);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -133,8 +134,9 @@ public class HabitHistoryView extends ScrollableDataView implements HabitDataVie
|
|||||||
setScrollerBucketSize((int) baseSize);
|
setScrollerBucketSize((int) baseSize);
|
||||||
|
|
||||||
squareSpacing = UIHelper.dpToPixels(getContext(), 1.0f);
|
squareSpacing = UIHelper.dpToPixels(getContext(), 1.0f);
|
||||||
float maxTextSize = getResources().getDimensionPixelSize(R.dimen.regularTextSize);
|
float maxTextSize = getResources().getDimension(R.dimen.regularTextSize);
|
||||||
float textSize = Math.min(baseSize * 0.5f, maxTextSize);
|
float textSize = height * 0.06f;
|
||||||
|
textSize = Math.min(textSize, maxTextSize);
|
||||||
|
|
||||||
pSquareFg.setTextSize(textSize);
|
pSquareFg.setTextSize(textSize);
|
||||||
pTextHeader.setTextSize(textSize);
|
pTextHeader.setTextSize(textSize);
|
||||||
@@ -155,7 +157,7 @@ public class HabitHistoryView extends ScrollableDataView implements HabitDataVie
|
|||||||
{
|
{
|
||||||
float width = 0;
|
float width = 0;
|
||||||
|
|
||||||
for(String w : wdays)
|
for(String w : DateHelper.getLocaleDayNames(Calendar.SHORT))
|
||||||
width = Math.max(width, pSquareFg.measureText(w));
|
width = Math.max(width, pSquareFg.measureText(w));
|
||||||
|
|
||||||
return width;
|
return width;
|
||||||
@@ -164,7 +166,7 @@ public class HabitHistoryView extends ScrollableDataView implements HabitDataVie
|
|||||||
private void createColors()
|
private void createColors()
|
||||||
{
|
{
|
||||||
if(habit != null)
|
if(habit != null)
|
||||||
this.primaryColor = habit.color;
|
this.primaryColor = ColorHelper.getColor(getContext(), habit.color);
|
||||||
|
|
||||||
if(isBackgroundTransparent)
|
if(isBackgroundTransparent)
|
||||||
primaryColor = ColorHelper.setMinValue(primaryColor, 0.75f);
|
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[0] = Color.argb(16, 255, 255, 255);
|
||||||
colors[1] = Color.argb(128, red, green, blue);
|
colors[1] = Color.argb(128, red, green, blue);
|
||||||
colors[2] = primaryColor;
|
colors[2] = primaryColor;
|
||||||
textColor = Color.rgb(255, 255, 255);
|
textColor = Color.WHITE;
|
||||||
|
reverseTextColor = Color.WHITE;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
colors = new int[3];
|
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[1] = Color.argb(127, red, green, blue);
|
||||||
colors[2] = primaryColor;
|
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);
|
pTextHeader.setAntiAlias(true);
|
||||||
|
|
||||||
pSquareBg = new Paint();
|
pSquareBg = new Paint();
|
||||||
pSquareBg.setColor(primaryColor);
|
|
||||||
|
|
||||||
pSquareFg = new Paint();
|
pSquareFg = new Paint();
|
||||||
pSquareFg.setColor(Color.WHITE);
|
|
||||||
pSquareFg.setAntiAlias(true);
|
pSquareFg.setAntiAlias(true);
|
||||||
pSquareFg.setTextAlign(Align.CENTER);
|
pSquareFg.setTextAlign(Align.CENTER);
|
||||||
}
|
}
|
||||||
@@ -274,9 +276,10 @@ public class HabitHistoryView extends ScrollableDataView implements HabitDataVie
|
|||||||
|
|
||||||
for (int j = 0; j < 7; j++)
|
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);
|
drawSquare(canvas, location, date, checkmarkOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -291,6 +294,7 @@ public class HabitHistoryView extends ScrollableDataView implements HabitDataVie
|
|||||||
if (checkmarkOffset >= checkmarks.length) pSquareBg.setColor(colors[0]);
|
if (checkmarkOffset >= checkmarks.length) pSquareBg.setColor(colors[0]);
|
||||||
else pSquareBg.setColor(colors[checkmarks[checkmarkOffset]]);
|
else pSquareBg.setColor(colors[checkmarks[checkmarkOffset]]);
|
||||||
|
|
||||||
|
pSquareFg.setColor(reverseTextColor);
|
||||||
canvas.drawRect(location, pSquareBg);
|
canvas.drawRect(location, pSquareBg);
|
||||||
String text = Integer.toString(date.get(Calendar.DAY_OF_MONTH));
|
String text = Integer.toString(date.get(Calendar.DAY_OF_MONTH));
|
||||||
canvas.drawText(text, location.centerX(), location.centerY() + squareTextOffset, pSquareFg);
|
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)
|
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);
|
location.offset(0, columnWidth);
|
||||||
canvas.drawText(wdays[i], location.left + headerTextOffset,
|
canvas.drawText(day, location.left + headerTextOffset,
|
||||||
location.bottom - headerTextOffset, pTextHeader);
|
location.centerY() + verticalOffset, pTextHeader);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
package org.isoron.uhabits.views;
|
package org.isoron.uhabits.views;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
@@ -32,6 +33,7 @@ import android.util.AttributeSet;
|
|||||||
import org.isoron.uhabits.R;
|
import org.isoron.uhabits.R;
|
||||||
import org.isoron.uhabits.helpers.ColorHelper;
|
import org.isoron.uhabits.helpers.ColorHelper;
|
||||||
import org.isoron.uhabits.helpers.DateHelper;
|
import org.isoron.uhabits.helpers.DateHelper;
|
||||||
|
import org.isoron.uhabits.helpers.UIHelper;
|
||||||
import org.isoron.uhabits.models.Habit;
|
import org.isoron.uhabits.models.Habit;
|
||||||
import org.isoron.uhabits.models.Score;
|
import org.isoron.uhabits.models.Score;
|
||||||
|
|
||||||
@@ -47,6 +49,8 @@ public class HabitScoreView extends ScrollableDataView implements HabitDataView
|
|||||||
public static final PorterDuffXfermode XFERMODE_SRC =
|
public static final PorterDuffXfermode XFERMODE_SRC =
|
||||||
new PorterDuffXfermode(PorterDuff.Mode.SRC);
|
new PorterDuffXfermode(PorterDuff.Mode.SRC);
|
||||||
|
|
||||||
|
public static int DEFAULT_BUCKET_SIZES[] = { 1, 7, 31, 92, 365 };
|
||||||
|
|
||||||
private Paint pGrid;
|
private Paint pGrid;
|
||||||
private float em;
|
private float em;
|
||||||
private Habit habit;
|
private Habit habit;
|
||||||
@@ -65,15 +69,19 @@ public class HabitScoreView extends ScrollableDataView implements HabitDataView
|
|||||||
private int nColumns;
|
private int nColumns;
|
||||||
|
|
||||||
private int textColor;
|
private int textColor;
|
||||||
private int dimmedTextColor;
|
private int gridColor;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private int[] scores;
|
private int[] scores;
|
||||||
|
|
||||||
private int primaryColor;
|
private int primaryColor;
|
||||||
private boolean isBackgroundTransparent;
|
|
||||||
private int bucketSize = 7;
|
private int bucketSize = 7;
|
||||||
private int footerHeight;
|
private int footerHeight;
|
||||||
|
private int backgroundColor;
|
||||||
|
|
||||||
|
private Bitmap drawingCache;
|
||||||
|
private Canvas cacheCanvas;
|
||||||
|
private boolean isTransparencyEnabled;
|
||||||
|
|
||||||
public HabitScoreView(Context context)
|
public HabitScoreView(Context context)
|
||||||
{
|
{
|
||||||
@@ -84,7 +92,7 @@ public class HabitScoreView extends ScrollableDataView implements HabitDataView
|
|||||||
public HabitScoreView(Context context, AttributeSet attrs)
|
public HabitScoreView(Context context, AttributeSet attrs)
|
||||||
{
|
{
|
||||||
super(context, attrs);
|
super(context, attrs);
|
||||||
this.primaryColor = ColorHelper.palette[7];
|
this.primaryColor = ColorHelper.getColor(getContext(), 7);
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,21 +118,11 @@ public class HabitScoreView extends ScrollableDataView implements HabitDataView
|
|||||||
private void createColors()
|
private void createColors()
|
||||||
{
|
{
|
||||||
if(habit != null)
|
if(habit != null)
|
||||||
this.primaryColor = habit.color;
|
this.primaryColor = ColorHelper.getColor(getContext(), habit.color);
|
||||||
|
|
||||||
if (isBackgroundTransparent)
|
textColor = UIHelper.getStyledColor(getContext(), R.attr.mediumContrastTextColor);
|
||||||
{
|
gridColor = UIHelper.getStyledColor(getContext(), R.attr.lowContrastTextColor);
|
||||||
primaryColor = ColorHelper.setSaturation(primaryColor, 0.75f);
|
backgroundColor = UIHelper.getStyledColor(getContext(), R.attr.cardBackgroundColor);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void createPaints()
|
protected void createPaints()
|
||||||
@@ -153,8 +151,9 @@ public class HabitScoreView extends ScrollableDataView implements HabitDataView
|
|||||||
{
|
{
|
||||||
if(height < 9) height = 200;
|
if(height < 9) height = 200;
|
||||||
|
|
||||||
int maxTextSize = getResources().getDimensionPixelSize(R.dimen.regularTextSize);
|
float maxTextSize = getResources().getDimension(R.dimen.tinyTextSize);
|
||||||
pText.setTextSize(Math.min(height * 0.047f, maxTextSize));
|
float textSize = height * 0.06f;
|
||||||
|
pText.setTextSize(Math.min(textSize, maxTextSize));
|
||||||
em = pText.getFontSpacing();
|
em = pText.getFontSpacing();
|
||||||
|
|
||||||
footerHeight = (int)(3 * em);
|
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, getMaxDayWidth() * 1.5f);
|
||||||
columnWidth = Math.max(columnWidth, getMaxMonthWidth() * 1.2f);
|
columnWidth = Math.max(columnWidth, getMaxMonthWidth() * 1.2f);
|
||||||
|
|
||||||
columnHeight = 8 * baseSize;
|
|
||||||
nColumns = (int) (width / columnWidth);
|
nColumns = (int) (width / columnWidth);
|
||||||
|
columnWidth = (float) width / nColumns;
|
||||||
|
|
||||||
|
columnHeight = 8 * baseSize;
|
||||||
|
|
||||||
|
float minStrokeWidth = UIHelper.dpToPixels(getContext(), 1);
|
||||||
pGraph.setTextSize(baseSize * 0.5f);
|
pGraph.setTextSize(baseSize * 0.5f);
|
||||||
pGraph.setStrokeWidth(baseSize * 0.1f);
|
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()
|
public void refreshData()
|
||||||
@@ -211,12 +223,26 @@ public class HabitScoreView extends ScrollableDataView implements HabitDataView
|
|||||||
protected void onDraw(Canvas canvas)
|
protected void onDraw(Canvas canvas)
|
||||||
{
|
{
|
||||||
super.onDraw(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;
|
if (habit == null || scores == null) return;
|
||||||
|
|
||||||
rect.set(0, 0, nColumns * columnWidth, columnHeight);
|
rect.set(0, 0, nColumns * columnWidth, columnHeight);
|
||||||
rect.offset(0, paddingTop);
|
rect.offset(0, paddingTop);
|
||||||
|
|
||||||
drawGrid(canvas, rect);
|
drawGrid(activeCanvas, rect);
|
||||||
|
|
||||||
pText.setColor(textColor);
|
pText.setColor(textColor);
|
||||||
pGraph.setColor(primaryColor);
|
pGraph.setColor(primaryColor);
|
||||||
@@ -241,24 +267,28 @@ public class HabitScoreView extends ScrollableDataView implements HabitDataView
|
|||||||
int height = (int) (columnHeight * relativeScore);
|
int height = (int) (columnHeight * relativeScore);
|
||||||
|
|
||||||
rect.set(0, 0, baseSize, baseSize);
|
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())
|
if (!prevRect.isEmpty())
|
||||||
{
|
{
|
||||||
drawLine(canvas, prevRect, rect);
|
drawLine(activeCanvas, prevRect, rect);
|
||||||
drawMarker(canvas, prevRect);
|
drawMarker(activeCanvas, prevRect);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (k == nColumns - 1) drawMarker(canvas, rect);
|
if (k == nColumns - 1) drawMarker(activeCanvas, rect);
|
||||||
|
|
||||||
prevRect.set(rect);
|
prevRect.set(rect);
|
||||||
rect.set(0, 0, columnWidth, columnHeight);
|
rect.set(0, 0, columnWidth, columnHeight);
|
||||||
rect.offset(k * columnWidth, paddingTop);
|
rect.offset(k * columnWidth, paddingTop);
|
||||||
|
|
||||||
drawFooter(canvas, rect, currentDate);
|
drawFooter(activeCanvas, rect, currentDate);
|
||||||
|
|
||||||
currentDate += bucketSize * DateHelper.millisecondsInOneDay;
|
currentDate += bucketSize * DateHelper.millisecondsInOneDay;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(activeCanvas != canvas)
|
||||||
|
canvas.drawBitmap(drawingCache, 0, 0, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int skipYear = 0;
|
private int skipYear = 0;
|
||||||
@@ -322,7 +352,7 @@ public class HabitScoreView extends ScrollableDataView implements HabitDataView
|
|||||||
|
|
||||||
pText.setTextAlign(Paint.Align.LEFT);
|
pText.setTextAlign(Paint.Align.LEFT);
|
||||||
pText.setColor(textColor);
|
pText.setColor(textColor);
|
||||||
pGrid.setColor(dimmedTextColor);
|
pGrid.setColor(gridColor);
|
||||||
|
|
||||||
for (int i = 0; i < nRows; i++)
|
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)
|
private void drawMarker(Canvas canvas, RectF rect)
|
||||||
{
|
{
|
||||||
rect.inset(baseSize * 0.15f, baseSize * 0.15f);
|
rect.inset(baseSize * 0.15f, baseSize * 0.15f);
|
||||||
setModeOrColor(pGraph, XFERMODE_CLEAR, Color.WHITE);
|
setModeOrColor(pGraph, XFERMODE_CLEAR, backgroundColor);
|
||||||
canvas.drawOval(rect, pGraph);
|
canvas.drawOval(rect, pGraph);
|
||||||
|
|
||||||
rect.inset(baseSize * 0.1f, baseSize * 0.1f);
|
rect.inset(baseSize * 0.1f, baseSize * 0.1f);
|
||||||
@@ -353,22 +383,23 @@ public class HabitScoreView extends ScrollableDataView implements HabitDataView
|
|||||||
canvas.drawOval(rect, pGraph);
|
canvas.drawOval(rect, pGraph);
|
||||||
|
|
||||||
rect.inset(baseSize * 0.1f, baseSize * 0.1f);
|
rect.inset(baseSize * 0.1f, baseSize * 0.1f);
|
||||||
setModeOrColor(pGraph, XFERMODE_CLEAR, Color.WHITE);
|
setModeOrColor(pGraph, XFERMODE_CLEAR, backgroundColor);
|
||||||
canvas.drawOval(rect, pGraph);
|
canvas.drawOval(rect, pGraph);
|
||||||
|
|
||||||
if(isBackgroundTransparent)
|
if(isTransparencyEnabled)
|
||||||
pGraph.setXfermode(XFERMODE_SRC);
|
pGraph.setXfermode(XFERMODE_SRC);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setIsBackgroundTransparent(boolean isBackgroundTransparent)
|
public void setIsTransparencyEnabled(boolean enabled)
|
||||||
{
|
{
|
||||||
this.isBackgroundTransparent = isBackgroundTransparent;
|
this.isTransparencyEnabled = enabled;
|
||||||
createColors();
|
createColors();
|
||||||
|
requestLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setModeOrColor(Paint p, PorterDuffXfermode mode, int color)
|
private void setModeOrColor(Paint p, PorterDuffXfermode mode, int color)
|
||||||
{
|
{
|
||||||
if(isBackgroundTransparent)
|
if(isTransparencyEnabled)
|
||||||
p.setXfermode(mode);
|
p.setXfermode(mode);
|
||||||
else
|
else
|
||||||
p.setColor(color);
|
p.setColor(color);
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import android.view.View;
|
|||||||
|
|
||||||
import org.isoron.uhabits.R;
|
import org.isoron.uhabits.R;
|
||||||
import org.isoron.uhabits.helpers.ColorHelper;
|
import org.isoron.uhabits.helpers.ColorHelper;
|
||||||
|
import org.isoron.uhabits.helpers.UIHelper;
|
||||||
import org.isoron.uhabits.models.Habit;
|
import org.isoron.uhabits.models.Habit;
|
||||||
import org.isoron.uhabits.models.Streak;
|
import org.isoron.uhabits.models.Streak;
|
||||||
|
|
||||||
@@ -53,7 +54,6 @@ public class HabitStreakView extends View implements HabitDataView
|
|||||||
private List<Streak> streaks;
|
private List<Streak> streaks;
|
||||||
|
|
||||||
private boolean isBackgroundTransparent;
|
private boolean isBackgroundTransparent;
|
||||||
private int textColor;
|
|
||||||
private DateFormat dateFormat;
|
private DateFormat dateFormat;
|
||||||
private int width;
|
private int width;
|
||||||
private float em;
|
private float em;
|
||||||
@@ -61,6 +61,8 @@ public class HabitStreakView extends View implements HabitDataView
|
|||||||
private float textMargin;
|
private float textMargin;
|
||||||
private boolean shouldShowLabels;
|
private boolean shouldShowLabels;
|
||||||
private int maxStreakCount;
|
private int maxStreakCount;
|
||||||
|
private int textColor;
|
||||||
|
private int reverseTextColor;
|
||||||
|
|
||||||
public HabitStreakView(Context context)
|
public HabitStreakView(Context context)
|
||||||
{
|
{
|
||||||
@@ -71,7 +73,7 @@ public class HabitStreakView extends View implements HabitDataView
|
|||||||
public HabitStreakView(Context context, AttributeSet attrs)
|
public HabitStreakView(Context context, AttributeSet attrs)
|
||||||
{
|
{
|
||||||
super(context, attrs);
|
super(context, attrs);
|
||||||
this.primaryColor = ColorHelper.palette[7];
|
this.primaryColor = ColorHelper.getColor(getContext(), 7);
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,10 +111,11 @@ public class HabitStreakView extends View implements HabitDataView
|
|||||||
maxStreakCount = height / baseSize;
|
maxStreakCount = height / baseSize;
|
||||||
this.width = width;
|
this.width = width;
|
||||||
|
|
||||||
int maxTextSize = getResources().getDimensionPixelSize(R.dimen.regularTextSize);
|
float minTextSize = getResources().getDimension(R.dimen.tinyTextSize);
|
||||||
float regularTextSize = Math.min(baseSize * 0.56f, maxTextSize);
|
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();
|
em = paint.getFontSpacing();
|
||||||
textMargin = 0.5f * em;
|
textMargin = 0.5f * em;
|
||||||
|
|
||||||
@@ -122,36 +125,19 @@ public class HabitStreakView extends View implements HabitDataView
|
|||||||
private void createColors()
|
private void createColors()
|
||||||
{
|
{
|
||||||
if(habit != null)
|
if(habit != null)
|
||||||
this.primaryColor = habit.color;
|
this.primaryColor = ColorHelper.getColor(getContext(), habit.color);
|
||||||
|
|
||||||
if(isBackgroundTransparent)
|
|
||||||
{
|
|
||||||
primaryColor = ColorHelper.setSaturation(primaryColor, 0.75f);
|
|
||||||
primaryColor = ColorHelper.setValue(primaryColor, 1.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
int red = Color.red(primaryColor);
|
int red = Color.red(primaryColor);
|
||||||
int green = Color.green(primaryColor);
|
int green = Color.green(primaryColor);
|
||||||
int blue = Color.blue(primaryColor);
|
int blue = Color.blue(primaryColor);
|
||||||
|
|
||||||
if(isBackgroundTransparent)
|
colors = new int[4];
|
||||||
{
|
colors[3] = primaryColor;
|
||||||
colors = new int[4];
|
colors[2] = Color.argb(192, red, green, blue);
|
||||||
colors[3] = primaryColor;
|
colors[1] = Color.argb(96, red, green, blue);
|
||||||
colors[2] = Color.argb(213, red, green, blue);
|
colors[0] = UIHelper.getStyledColor(getContext(), R.attr.lowContrastTextColor);
|
||||||
colors[1] = Color.argb(170, red, green, blue);
|
textColor = UIHelper.getStyledColor(getContext(), R.attr.mediumContrastTextColor);
|
||||||
colors[0] = Color.argb(128, red, green, blue);
|
reverseTextColor = UIHelper.getStyledColor(getContext(), R.attr.highContrastReverseTextColor);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void createPaints()
|
protected void createPaints()
|
||||||
@@ -216,7 +202,7 @@ public class HabitStreakView extends View implements HabitDataView
|
|||||||
if(shouldShowLabels) availableWidth -= 2 * textMargin;
|
if(shouldShowLabels) availableWidth -= 2 * textMargin;
|
||||||
|
|
||||||
float barWidth = percentage * availableWidth;
|
float barWidth = percentage * availableWidth;
|
||||||
float minBarWidth = paint.measureText(streak.length.toString());
|
float minBarWidth = paint.measureText(streak.length.toString()) + em;
|
||||||
barWidth = Math.max(barWidth, minBarWidth);
|
barWidth = Math.max(barWidth, minBarWidth);
|
||||||
|
|
||||||
float gap = (width - barWidth) / 2;
|
float gap = (width - barWidth) / 2;
|
||||||
@@ -229,7 +215,7 @@ public class HabitStreakView extends View implements HabitDataView
|
|||||||
|
|
||||||
float yOffset = rect.centerY() + 0.3f * em;
|
float yOffset = rect.centerY() + 0.3f * em;
|
||||||
|
|
||||||
paint.setColor(Color.WHITE);
|
paint.setColor(reverseTextColor);
|
||||||
paint.setTextAlign(Paint.Align.CENTER);
|
paint.setTextAlign(Paint.Align.CENTER);
|
||||||
canvas.drawText(streak.length.toString(), rect.centerX(), yOffset, paint);
|
canvas.drawText(streak.length.toString(), rect.centerX(), yOffset, paint);
|
||||||
|
|
||||||
|
|||||||
117
app/src/main/java/org/isoron/uhabits/views/HabitWidgetView.java
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.views;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.drawable.InsetDrawable;
|
||||||
|
import android.graphics.drawable.ShapeDrawable;
|
||||||
|
import android.graphics.drawable.shapes.RoundRectShape;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.R;
|
||||||
|
import org.isoron.uhabits.helpers.UIHelper;
|
||||||
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
public abstract class HabitWidgetView extends FrameLayout implements HabitDataView
|
||||||
|
{
|
||||||
|
@Nullable
|
||||||
|
protected InsetDrawable background;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
protected Paint backgroundPaint;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
protected Habit habit;
|
||||||
|
protected ViewGroup frame;
|
||||||
|
|
||||||
|
public void setShadowAlpha(int shadowAlpha)
|
||||||
|
{
|
||||||
|
this.shadowAlpha = shadowAlpha;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int shadowAlpha;
|
||||||
|
|
||||||
|
public HabitWidgetView(Context context)
|
||||||
|
{
|
||||||
|
super(context);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public HabitWidgetView(Context context, AttributeSet attrs)
|
||||||
|
{
|
||||||
|
super(context, attrs);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init()
|
||||||
|
{
|
||||||
|
inflate(getContext(), getInnerLayoutId(), this);
|
||||||
|
shadowAlpha = (int) (255 * UIHelper.getStyledFloat(getContext(), R.attr.widgetShadowAlpha));
|
||||||
|
rebuildBackground();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract @NonNull Integer getInnerLayoutId();
|
||||||
|
|
||||||
|
protected void rebuildBackground()
|
||||||
|
{
|
||||||
|
Context context = getContext();
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -71,7 +71,7 @@ public class NumberView extends View
|
|||||||
this.textSize = UIHelper.getFloatAttribute(context, attrs, "textSize",
|
this.textSize = UIHelper.getFloatAttribute(context, attrs, "textSize",
|
||||||
getResources().getDimension(R.dimen.regularTextSize));
|
getResources().getDimension(R.dimen.regularTextSize));
|
||||||
|
|
||||||
this.color = ColorHelper.palette[7];
|
this.color = ColorHelper.getColor(getContext(), 7);
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import android.content.Context;
|
|||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
|
|
||||||
import org.isoron.uhabits.R;
|
import org.isoron.uhabits.R;
|
||||||
|
import org.isoron.uhabits.helpers.ColorHelper;
|
||||||
import org.isoron.uhabits.helpers.DateHelper;
|
import org.isoron.uhabits.helpers.DateHelper;
|
||||||
import org.isoron.uhabits.helpers.UIHelper;
|
import org.isoron.uhabits.helpers.UIHelper;
|
||||||
import org.isoron.uhabits.models.Habit;
|
import org.isoron.uhabits.models.Habit;
|
||||||
@@ -79,6 +80,6 @@ public class RepetitionCountView extends NumberView implements HabitDataView
|
|||||||
public void setHabit(Habit habit)
|
public void setHabit(Habit habit)
|
||||||
{
|
{
|
||||||
this.habit = habit;
|
this.habit = habit;
|
||||||
setColor(habit.color);
|
setColor(ColorHelper.getColor(getContext(), habit.color));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,42 +19,59 @@
|
|||||||
|
|
||||||
package org.isoron.uhabits.views;
|
package org.isoron.uhabits.views;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.PorterDuff;
|
||||||
|
import android.graphics.PorterDuffXfermode;
|
||||||
import android.graphics.RectF;
|
import android.graphics.RectF;
|
||||||
import android.text.Layout;
|
|
||||||
import android.text.StaticLayout;
|
|
||||||
import android.text.TextPaint;
|
import android.text.TextPaint;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import org.isoron.uhabits.R;
|
import org.isoron.uhabits.R;
|
||||||
|
import org.isoron.uhabits.helpers.ColorHelper;
|
||||||
import org.isoron.uhabits.helpers.UIHelper;
|
import org.isoron.uhabits.helpers.UIHelper;
|
||||||
|
|
||||||
public class RingView extends View
|
public class RingView extends View
|
||||||
{
|
{
|
||||||
|
public static final PorterDuffXfermode XFERMODE_CLEAR =
|
||||||
|
new PorterDuffXfermode(PorterDuff.Mode.CLEAR);
|
||||||
|
|
||||||
private int color;
|
private int color;
|
||||||
|
private float precision;
|
||||||
private float percentage;
|
private float percentage;
|
||||||
private float labelMarginTop;
|
private int diameter;
|
||||||
private TextPaint pRing;
|
private float thickness;
|
||||||
private String label;
|
|
||||||
private RectF rect;
|
private RectF rect;
|
||||||
private StaticLayout labelLayout;
|
private TextPaint pRing;
|
||||||
|
|
||||||
private int width;
|
private Integer backgroundColor;
|
||||||
private int height;
|
private Integer inactiveColor;
|
||||||
private float diameter;
|
|
||||||
|
|
||||||
private float maxDiameter;
|
private float em;
|
||||||
|
private String text;
|
||||||
private float textSize;
|
private float textSize;
|
||||||
private int fadedTextColor;
|
private boolean enableFontAwesome;
|
||||||
|
|
||||||
|
private Bitmap drawingCache;
|
||||||
|
private Canvas cacheCanvas;
|
||||||
|
private boolean isTransparencyEnabled;
|
||||||
|
|
||||||
public RingView(Context context)
|
public RingView(Context context)
|
||||||
{
|
{
|
||||||
super(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();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,9 +79,24 @@ public class RingView extends View
|
|||||||
{
|
{
|
||||||
super(context, attrs);
|
super(context, attrs);
|
||||||
|
|
||||||
this.label = UIHelper.getAttribute(context, attrs, "label", "Label");
|
percentage = UIHelper.getFloatAttribute(context, attrs, "percentage", 0);
|
||||||
this.maxDiameter = UIHelper.getFloatAttribute(context, attrs, "maxDiameter", 100);
|
precision = UIHelper.getFloatAttribute(context, attrs, "precision", 0.01f);
|
||||||
this.maxDiameter = UIHelper.dpToPixels(context, maxDiameter);
|
|
||||||
|
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();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,14 +106,11 @@ public class RingView extends View
|
|||||||
postInvalidate();
|
postInvalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setMaxDiameter(float maxDiameter)
|
@Override
|
||||||
|
public void setBackgroundColor(int backgroundColor)
|
||||||
{
|
{
|
||||||
this.maxDiameter = maxDiameter;
|
this.backgroundColor = backgroundColor;
|
||||||
}
|
postInvalidate();
|
||||||
|
|
||||||
public void setLabel(String label)
|
|
||||||
{
|
|
||||||
this.label = label;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPercentage(float percentage)
|
public void setPercentage(float percentage)
|
||||||
@@ -90,6 +119,24 @@ public class RingView extends View
|
|||||||
postInvalidate();
|
postInvalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setPrecision(float precision)
|
||||||
|
{
|
||||||
|
this.precision = precision;
|
||||||
|
postInvalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setThickness(float thickness)
|
||||||
|
{
|
||||||
|
this.thickness = thickness;
|
||||||
|
postInvalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setText(String text)
|
||||||
|
{
|
||||||
|
this.text = text;
|
||||||
|
postInvalidate();
|
||||||
|
}
|
||||||
|
|
||||||
private void init()
|
private void init()
|
||||||
{
|
{
|
||||||
pRing = new TextPaint();
|
pRing = new TextPaint();
|
||||||
@@ -97,59 +144,94 @@ public class RingView extends View
|
|||||||
pRing.setColor(color);
|
pRing.setColor(color);
|
||||||
pRing.setTextAlign(Paint.Align.CENTER);
|
pRing.setTextAlign(Paint.Align.CENTER);
|
||||||
|
|
||||||
fadedTextColor = getResources().getColor(R.color.fadedTextColor);
|
if(backgroundColor == null)
|
||||||
textSize = getResources().getDimension(R.dimen.smallTextSize);
|
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();
|
rect = new RectF();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressLint("DrawAllocation")
|
|
||||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
|
||||||
{
|
{
|
||||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||||
|
|
||||||
width = MeasureSpec.getSize(widthMeasureSpec);
|
int width = MeasureSpec.getSize(widthMeasureSpec);
|
||||||
height = MeasureSpec.getSize(heightMeasureSpec);
|
int height = MeasureSpec.getSize(heightMeasureSpec);
|
||||||
|
diameter = Math.min(height, width);
|
||||||
diameter = Math.min(maxDiameter, width);
|
|
||||||
|
|
||||||
pRing.setTextSize(textSize);
|
pRing.setTextSize(textSize);
|
||||||
labelMarginTop = textSize * 0.80f;
|
em = pRing.measureText("M");
|
||||||
labelLayout = new StaticLayout(label, pRing, width, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0f,
|
|
||||||
false);
|
|
||||||
|
|
||||||
width = Math.max(width, labelLayout.getWidth());
|
setMeasuredDimension(diameter, diameter);
|
||||||
height = (int) (diameter + labelLayout.getHeight() + labelMarginTop);
|
}
|
||||||
|
|
||||||
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
|
@Override
|
||||||
protected void onDraw(Canvas canvas)
|
protected void onDraw(Canvas canvas)
|
||||||
{
|
{
|
||||||
super.onDraw(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);
|
pRing.setColor(color);
|
||||||
rect.set(0, 0, diameter, diameter);
|
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));
|
float angle = 360 * Math.round(percentage / precision) * precision;
|
||||||
canvas.drawArc(rect, 360 * percentage - 90 + 2, 360 * (1 - percentage) - 4, true, pRing);
|
|
||||||
|
|
||||||
pRing.setColor(Color.WHITE);
|
activeCanvas.drawArc(rect, -90, angle, true, pRing);
|
||||||
rect.inset(thickness, thickness);
|
|
||||||
canvas.drawArc(rect, -90, 360, true, pRing);
|
|
||||||
|
|
||||||
pRing.setColor(fadedTextColor);
|
pRing.setColor(inactiveColor);
|
||||||
pRing.setTextSize(textSize);
|
activeCanvas.drawArc(rect, angle - 90, 360 - angle, true, pRing);
|
||||||
float lineHeight = pRing.getFontSpacing();
|
|
||||||
canvas.drawText(String.format("%.0f%%", percentage * 100), rect.centerX(),
|
if(thickness > 0)
|
||||||
rect.centerY() + lineHeight / 3, pRing);
|
{
|
||||||
pRing.setTextSize(textSize);
|
if(isTransparencyEnabled)
|
||||||
canvas.translate(width / 2, diameter + labelMarginTop);
|
pRing.setXfermode(XFERMODE_CLEAR);
|
||||||
labelLayout.draw(canvas);
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import android.view.LayoutInflater;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.RemoteViews;
|
import android.widget.RemoteViews;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
import org.isoron.uhabits.R;
|
import org.isoron.uhabits.R;
|
||||||
import org.isoron.uhabits.helpers.UIHelper;
|
import org.isoron.uhabits.helpers.UIHelper;
|
||||||
@@ -45,7 +46,8 @@ import java.io.IOException;
|
|||||||
public abstract class BaseWidgetProvider extends AppWidgetProvider
|
public abstract class BaseWidgetProvider extends AppWidgetProvider
|
||||||
{
|
{
|
||||||
|
|
||||||
private int width, height;
|
private int portraitWidth, portraitHeight;
|
||||||
|
private int landscapeWidth, landscapeHeight;
|
||||||
|
|
||||||
protected abstract int getDefaultHeight();
|
protected abstract int getDefaultHeight();
|
||||||
|
|
||||||
@@ -122,15 +124,19 @@ public abstract class BaseWidgetProvider extends AppWidgetProvider
|
|||||||
|
|
||||||
protected abstract void refreshCustomViewData(View widgetView);
|
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
|
try
|
||||||
{
|
{
|
||||||
LayoutInflater inflater = LayoutInflater.from(context);
|
LayoutInflater inflater = LayoutInflater.from(context);
|
||||||
View view = inflater.inflate(getLayoutId(), null);
|
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);
|
ImageView iv = (ImageView) view.findViewById(R.id.imageView);
|
||||||
iv.setImageBitmap(widgetCache);
|
if(iv != null) iv.setImageBitmap(widgetCache);
|
||||||
|
|
||||||
view.measure(width, height);
|
view.measure(width, height);
|
||||||
view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
|
view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
|
||||||
@@ -138,7 +144,7 @@ public abstract class BaseWidgetProvider extends AppWidgetProvider
|
|||||||
view.buildDrawingCache();
|
view.buildDrawingCache();
|
||||||
Bitmap previewCache = view.getDrawingCache();
|
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));
|
Log.d("BaseWidgetProvider", String.format("Writing %s", filename));
|
||||||
FileOutputStream out = new FileOutputStream(filename);
|
FileOutputStream out = new FileOutputStream(filename);
|
||||||
|
|
||||||
@@ -172,8 +178,11 @@ public abstract class BaseWidgetProvider extends AppWidgetProvider
|
|||||||
options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT));
|
options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT));
|
||||||
}
|
}
|
||||||
|
|
||||||
width = maxWidth;
|
portraitWidth = minWidth;
|
||||||
height = maxHeight;
|
portraitHeight = maxHeight;
|
||||||
|
|
||||||
|
landscapeWidth = maxWidth;
|
||||||
|
landscapeHeight = minHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void measureCustomView(Context context, int w, int h, View customView)
|
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 Context context;
|
||||||
private final Habit habit;
|
private final Habit habit;
|
||||||
private final AppWidgetManager manager;
|
private final AppWidgetManager manager;
|
||||||
public RemoteViews remoteViews;
|
public RemoteViews portraitRemoteViews, landscapeRemoteViews;
|
||||||
public View widgetView;
|
public View portraitWidgetView, landscapeWidgetView;
|
||||||
|
|
||||||
public RenderWidgetTask(int widgetId, Context context, Habit habit,
|
public RenderWidgetTask(int widgetId, Context context, Habit habit,
|
||||||
AppWidgetManager manager)
|
AppWidgetManager manager)
|
||||||
@@ -219,17 +228,31 @@ public abstract class BaseWidgetProvider extends AppWidgetProvider
|
|||||||
protected void onPreExecute()
|
protected void onPreExecute()
|
||||||
{
|
{
|
||||||
super.onPreExecute();
|
super.onPreExecute();
|
||||||
|
context.setTheme(R.style.TransparentWidgetTheme);
|
||||||
|
|
||||||
remoteViews = new RemoteViews(context.getPackageName(), getLayoutId());
|
portraitRemoteViews = new RemoteViews(context.getPackageName(), getLayoutId());
|
||||||
widgetView = buildCustomView(context, habit);
|
portraitWidgetView = buildCustomView(context, habit);
|
||||||
measureCustomView(context, width, height, widgetView);
|
measureCustomView(context, portraitWidth, portraitHeight, portraitWidgetView);
|
||||||
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
|
@Override
|
||||||
protected void doInBackground()
|
protected void doInBackground()
|
||||||
{
|
{
|
||||||
refreshCustomViewData(widgetView);
|
refreshCustomViewData(portraitWidgetView);
|
||||||
|
refreshCustomViewData(landscapeWidgetView);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -237,20 +260,9 @@ public abstract class BaseWidgetProvider extends AppWidgetProvider
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
widgetView.invalidate();
|
buildRemoteViews(portraitWidgetView, portraitRemoteViews, portraitWidth, portraitHeight);
|
||||||
widgetView.setDrawingCacheEnabled(true);
|
buildRemoteViews(landscapeWidgetView, landscapeRemoteViews, landscapeWidth, landscapeHeight);
|
||||||
widgetView.buildDrawingCache(true);
|
updateAppWidget();
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@@ -260,5 +272,39 @@ public abstract class BaseWidgetProvider extends AppWidgetProvider
|
|||||||
|
|
||||||
super.onPostExecute(aVoid);
|
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 };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import android.view.View;
|
|||||||
import org.isoron.uhabits.HabitBroadcastReceiver;
|
import org.isoron.uhabits.HabitBroadcastReceiver;
|
||||||
import org.isoron.uhabits.R;
|
import org.isoron.uhabits.R;
|
||||||
import org.isoron.uhabits.models.Habit;
|
import org.isoron.uhabits.models.Habit;
|
||||||
import org.isoron.uhabits.views.CheckmarkView;
|
import org.isoron.uhabits.views.CheckmarkWidgetView;
|
||||||
import org.isoron.uhabits.views.HabitDataView;
|
import org.isoron.uhabits.views.HabitDataView;
|
||||||
|
|
||||||
public class CheckmarkWidgetProvider extends BaseWidgetProvider
|
public class CheckmarkWidgetProvider extends BaseWidgetProvider
|
||||||
@@ -33,7 +33,7 @@ public class CheckmarkWidgetProvider extends BaseWidgetProvider
|
|||||||
@Override
|
@Override
|
||||||
protected View buildCustomView(Context context, Habit habit)
|
protected View buildCustomView(Context context, Habit habit)
|
||||||
{
|
{
|
||||||
CheckmarkView view = new CheckmarkView(context);
|
CheckmarkWidgetView view = new CheckmarkWidgetView(context);
|
||||||
view.setHabit(habit);
|
view.setHabit(habit);
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
@@ -53,19 +53,19 @@ public class CheckmarkWidgetProvider extends BaseWidgetProvider
|
|||||||
@Override
|
@Override
|
||||||
protected int getDefaultHeight()
|
protected int getDefaultHeight()
|
||||||
{
|
{
|
||||||
return 200;
|
return 125;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected int getDefaultWidth()
|
protected int getDefaultWidth()
|
||||||
{
|
{
|
||||||
return 200;
|
return 125;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected int getLayoutId()
|
protected int getLayoutId()
|
||||||
{
|
{
|
||||||
return R.layout.widget_checkmark;
|
return R.layout.widget_wrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import android.view.View;
|
|||||||
import org.isoron.uhabits.HabitBroadcastReceiver;
|
import org.isoron.uhabits.HabitBroadcastReceiver;
|
||||||
import org.isoron.uhabits.R;
|
import org.isoron.uhabits.R;
|
||||||
import org.isoron.uhabits.models.Habit;
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
import org.isoron.uhabits.views.GraphWidgetView;
|
||||||
import org.isoron.uhabits.views.HabitDataView;
|
import org.isoron.uhabits.views.HabitDataView;
|
||||||
import org.isoron.uhabits.views.HabitFrequencyView;
|
import org.isoron.uhabits.views.HabitFrequencyView;
|
||||||
|
|
||||||
@@ -34,8 +35,8 @@ public class FrequencyWidgetProvider extends BaseWidgetProvider
|
|||||||
@Override
|
@Override
|
||||||
protected View buildCustomView(Context context, Habit habit)
|
protected View buildCustomView(Context context, Habit habit)
|
||||||
{
|
{
|
||||||
HabitFrequencyView view = new HabitFrequencyView(context, null);
|
HabitFrequencyView dataView = new HabitFrequencyView(context);
|
||||||
view.setIsBackgroundTransparent(true);
|
GraphWidgetView view = new GraphWidgetView(context, dataView);
|
||||||
view.setHabit(habit);
|
view.setHabit(habit);
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
@@ -67,6 +68,6 @@ public class FrequencyWidgetProvider extends BaseWidgetProvider
|
|||||||
@Override
|
@Override
|
||||||
protected int getLayoutId()
|
protected int getLayoutId()
|
||||||
{
|
{
|
||||||
return R.layout.widget_graph;
|
return R.layout.widget_wrapper;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import android.view.View;
|
|||||||
import org.isoron.uhabits.HabitBroadcastReceiver;
|
import org.isoron.uhabits.HabitBroadcastReceiver;
|
||||||
import org.isoron.uhabits.R;
|
import org.isoron.uhabits.R;
|
||||||
import org.isoron.uhabits.models.Habit;
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
import org.isoron.uhabits.views.GraphWidgetView;
|
||||||
import org.isoron.uhabits.views.HabitDataView;
|
import org.isoron.uhabits.views.HabitDataView;
|
||||||
import org.isoron.uhabits.views.HabitHistoryView;
|
import org.isoron.uhabits.views.HabitHistoryView;
|
||||||
|
|
||||||
@@ -33,9 +34,9 @@ public class HistoryWidgetProvider extends BaseWidgetProvider
|
|||||||
@Override
|
@Override
|
||||||
protected View buildCustomView(Context context, Habit habit)
|
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.setHabit(habit);
|
||||||
view.setIsBackgroundTransparent(true);
|
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,18 +55,18 @@ public class HistoryWidgetProvider extends BaseWidgetProvider
|
|||||||
@Override
|
@Override
|
||||||
protected int getDefaultHeight()
|
protected int getDefaultHeight()
|
||||||
{
|
{
|
||||||
return 200;
|
return 250;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected int getDefaultWidth()
|
protected int getDefaultWidth()
|
||||||
{
|
{
|
||||||
return 200;
|
return 250;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected int getLayoutId()
|
protected int getLayoutId()
|
||||||
{
|
{
|
||||||
return R.layout.widget_graph;
|
return R.layout.widget_wrapper;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,17 +24,25 @@ import android.view.View;
|
|||||||
|
|
||||||
import org.isoron.uhabits.HabitBroadcastReceiver;
|
import org.isoron.uhabits.HabitBroadcastReceiver;
|
||||||
import org.isoron.uhabits.R;
|
import org.isoron.uhabits.R;
|
||||||
|
import org.isoron.uhabits.helpers.UIHelper;
|
||||||
import org.isoron.uhabits.models.Habit;
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
import org.isoron.uhabits.views.GraphWidgetView;
|
||||||
import org.isoron.uhabits.views.HabitDataView;
|
import org.isoron.uhabits.views.HabitDataView;
|
||||||
import org.isoron.uhabits.views.HabitScoreView;
|
import org.isoron.uhabits.views.HabitScoreView;
|
||||||
|
|
||||||
public class ScoreWidgetProvider extends BaseWidgetProvider
|
public class ScoreWidgetProvider extends BaseWidgetProvider
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
protected View buildCustomView(Context context, Habit habit)
|
protected View buildCustomView(Context context, Habit habit)
|
||||||
{
|
{
|
||||||
HabitScoreView view = new HabitScoreView(context, null);
|
int defaultScoreInterval = UIHelper.getDefaultScoreInterval(context);
|
||||||
view.setIsBackgroundTransparent(true);
|
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);
|
view.setHabit(habit);
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
@@ -54,18 +62,18 @@ public class ScoreWidgetProvider extends BaseWidgetProvider
|
|||||||
@Override
|
@Override
|
||||||
protected int getDefaultHeight()
|
protected int getDefaultHeight()
|
||||||
{
|
{
|
||||||
return 200;
|
return 300;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected int getDefaultWidth()
|
protected int getDefaultWidth()
|
||||||
{
|
{
|
||||||
return 200;
|
return 300;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected int getLayoutId()
|
protected int getLayoutId()
|
||||||
{
|
{
|
||||||
return R.layout.widget_graph;
|
return R.layout.widget_wrapper;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import android.view.View;
|
|||||||
import org.isoron.uhabits.HabitBroadcastReceiver;
|
import org.isoron.uhabits.HabitBroadcastReceiver;
|
||||||
import org.isoron.uhabits.R;
|
import org.isoron.uhabits.R;
|
||||||
import org.isoron.uhabits.models.Habit;
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
import org.isoron.uhabits.views.GraphWidgetView;
|
||||||
import org.isoron.uhabits.views.HabitDataView;
|
import org.isoron.uhabits.views.HabitDataView;
|
||||||
import org.isoron.uhabits.views.HabitStreakView;
|
import org.isoron.uhabits.views.HabitStreakView;
|
||||||
|
|
||||||
@@ -33,8 +34,8 @@ public class StreakWidgetProvider extends BaseWidgetProvider
|
|||||||
@Override
|
@Override
|
||||||
protected View buildCustomView(Context context, Habit habit)
|
protected View buildCustomView(Context context, Habit habit)
|
||||||
{
|
{
|
||||||
HabitStreakView view = new HabitStreakView(context, null);
|
HabitStreakView dataView = new HabitStreakView(context);
|
||||||
view.setIsBackgroundTransparent(true);
|
GraphWidgetView view = new GraphWidgetView(context, dataView);
|
||||||
view.setHabit(habit);
|
view.setHabit(habit);
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
@@ -66,6 +67,6 @@ public class StreakWidgetProvider extends BaseWidgetProvider
|
|||||||
@Override
|
@Override
|
||||||
protected int getLayoutId()
|
protected int getLayoutId()
|
||||||
{
|
{
|
||||||
return R.layout.widget_graph;
|
return R.layout.widget_wrapper;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 196 B |
|
Before Width: | Height: | Size: 187 B |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 290 B |
|
Before Width: | Height: | Size: 181 B |
|
Before Width: | Height: | Size: 167 B After Width: | Height: | Size: 155 B |
BIN
app/src/main/res/drawable-hdpi/ic_action_add_light.png
Normal file
|
After Width: | Height: | Size: 170 B |
|
Before Width: | Height: | Size: 715 B After Width: | Height: | Size: 304 B |
|
Before Width: | Height: | Size: 822 B After Width: | Height: | Size: 354 B |
BIN
app/src/main/res/drawable-hdpi/ic_alarm_black.png
Normal file
|
After Width: | Height: | Size: 610 B |
BIN
app/src/main/res/drawable-hdpi/ic_alarm_white.png
Normal file
|
After Width: | Height: | Size: 610 B |
BIN
app/src/main/res/drawable-hdpi/ic_repeat_black.png
Normal file
|
After Width: | Height: | Size: 183 B |
BIN
app/src/main/res/drawable-hdpi/ic_repeat_white.png
Normal file
|
After Width: | Height: | Size: 198 B |
|
Before Width: | Height: | Size: 164 B |