diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
new file mode 100644
index 000000000..64043843f
--- /dev/null
+++ b/.github/workflows/main.yml
@@ -0,0 +1,31 @@
+name: Build & Test
+
+on:
+ push:
+ branches:
+ - dev
+ pull_request:
+ branches:
+ - dev
+
+jobs:
+ build:
+ runs-on: macOS-latest
+ steps:
+ - uses: actions/checkout@v1
+ - name: Install Java Development Kit 1.8
+ uses: actions/setup-java@v1
+ with:
+ java-version: 1.8
+ - name: Build APK & Run small tests
+ run: android/build.sh build
+ - name: Run medium tests
+ uses: ReactiveCircus/android-emulator-runner@v2.2.0
+ with:
+ api-level: 29
+ script: android/build.sh medium-tests
+ - name: Upload artifacts
+ uses: actions/upload-artifact@v1
+ with:
+ name: Build
+ path: android/uhabits-android/build/outputs/
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
new file mode 100644
index 000000000..d52e446ab
--- /dev/null
+++ b/.github/workflows/publish.yml
@@ -0,0 +1,40 @@
+name: Build, Test & Publish
+
+on:
+ push:
+ branches:
+ - master
+
+jobs:
+ build:
+ runs-on: macOS-latest
+ steps:
+ - uses: actions/checkout@v1
+ - name: Install GPG
+ uses: olafurpg/setup-gpg@v2
+ - name: Decrypt secrets
+ env:
+ GPG_PASSWORD: ${{ secrets.GPG_PASSWORD }}
+ run: .secret/decrypt.sh
+ - name: Install Java Development Kit 1.8
+ uses: actions/setup-java@v1
+ with:
+ java-version: 1.8
+ - name: Build APK & Run small tests
+ env:
+ RELEASE: 1
+ run: android/build.sh build
+ - name: Run medium tests
+ uses: ReactiveCircus/android-emulator-runner@v2.2.0
+ env:
+ RELEASE: 1
+ with:
+ api-level: 29
+ script: android/build.sh medium-tests
+ - name: Upload build to GitHub
+ uses: actions/upload-artifact@v1
+ with:
+ name: Build
+ path: android/uhabits-android/build/outputs/
+ - name: Upload APK to Google Play
+ run: cd android && ./gradlew publishReleaseApk
diff --git a/.gitignore b/.gitignore
index a8c758cef..ac138eede 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,13 +4,20 @@
*.perspectivev3
*.swp
*~.nib
+*.hprof
.DS_Store
+._.DS_Store
.externalNativeBuild
.gradle
.idea
+.secret
build
build/
captures
local.properties
node_modules
*xcuserdata*
+*.sketch
+/design
+/releases
+/screenshots
diff --git a/.secret/decrypt.sh b/.secret/decrypt.sh
new file mode 100755
index 000000000..bde9192bb
--- /dev/null
+++ b/.secret/decrypt.sh
@@ -0,0 +1,16 @@
+#!/bin/sh
+cd "$(dirname "$0")"
+if [ -z "$GPG_PASSWORD" ]; then
+ echo Env variable GPG_PASSWORD must be defined
+ exit 1
+fi
+gpg \
+ --quiet \
+ --batch \
+ --yes \
+ --decrypt \
+ --passphrase="$GPG_PASSWORD" \
+ --output secret.tar.gz \
+ secret
+tar -xzf secret.tar.gz
+rm secret.tar.gz
diff --git a/.secret/secret b/.secret/secret
new file mode 100644
index 000000000..e3f06089b
Binary files /dev/null and b/.secret/secret differ
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5c7278a60..e976b8d35 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,31 @@
# Changelog
+### 1.8.8 (June 21, 2020)
+
+* Make small changes to the habit scheduling algorithm, so that "1 time every x days" habits work more predictably.
+* Fix crash when saving habit
+
+### 1.8.0 (Jan 1, 2020)
+
+* New bar chart showing number of repetitions performed in each week, month, quarter or year.
+* Improved calculation of streaks for non-daily habits: performing habits on irregular weekdays will no longer break your streak.
+* Many more colors to choose from (now 20 in total).
+* Ability to customize how transparent the widgets are on your home screen.
+* Ability to customize the first day of the week.
+* Yes/No buttons on notifications, instead of just "Check".
+* Automatic dark theme according to phone settings (Android 10).
+* Smaller APK and backup files.
+* Many other internal code changes improving performance and stability.
+
+### 1.7.11 (Aug 10, 2019)
+
+* Fix bug that produced corrupted CSV files in some countries
+
+### 1.7.10 (June 15, 2019)
+
+* Fix bug that prevented some devices from showing notifications.
+* Update targetSdk to Android Pie (API level 28)
+
### 1.7.8 (April 21, 2018)
* Add support for adaptive icons (Oreo)
diff --git a/README.md b/README.md
index abc463653..72836df95 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@ source.
-
+
## Screenshots
@@ -21,34 +21,32 @@ source.
## Features
-* **Simple, beautiful and modern interface.** Loop has a minimalistic interface
- that is easy to use and follows the material design guidelines.
+* Beautiful, minimalistic and lightweight interface.
+Loop has an elegant and minimalistic interface that is very easy to use, even for first-time users. Highly optimized for speed, the app works well even on older phones.
-* **Habit score.** In addition to showing your current streak, Loop has an
- advanced algorithm for calculating the strength of your habits. Every
- repetition makes your habit stronger, and every missed day makes it weaker. A
- few missed days after a long streak, however, will not completely destroy
- your entire progress.
+* Habit score.
+Loop has an advanced formula for calculating the strength of your habits. Every repetition makes your habit stronger and every missed day makes it weaker. A few missed days after a long streak, however, will not completely destroy your progress, unlike many other don't-break-the-chain apps.
-* **Detailed graphs and statistics.** Clearly see how your habits improved over
- time with beautiful and detailed graphs. Scroll back to see the complete
- history of your habits.
+* Flexible schedules.
+In addition to daily habits, Loop supports habits with more complex schedules, such as 3 times per week or every other day.
-* **Flexible schedules.** Supports both daily habits and habits with more
- complex schedules, such as 3 times every week; one time every other week; or
- every other day.
+* Reminders.
+Schedule notifications to remind you of your habits. Each habit can have its own reminder, at a chosen time of the day. Easily check or dismiss your habit directly from the notification.
-* **Reminders.** Create an individual reminder for each habit, at a chosen hour
- of the day. Easily check, dismiss or snooze your habit directly from the
- notification, without opening the app.
+* Widgets.
+Be reminded of your habits whenever you unlock your phone. Colorful widgets allow you to track your habits directly from your home screen, without even opening the app.
-* **Optimized for smartwatches.** Reminders can be checked, snoozed or
- dismissed directly from your Android Wear watch.
+* Take control of your data.
+If you want to further analyze your data, or move it to another service, Loop allows you to export it to spreadsheets (CSV) or to a database file (SQLite). For power users, checkmarks can be added through other apps, such as Tasker.
-* **Completely ad-free and open source.** There are absolutely no
- advertisements, annoying notifications or intrusive permissions in this app,
- and there will never be. The complete source code is available under the
- GPLv3.
+* No limitations.
+Track as many habits as you wish. Loop imposes no artificial limits on how many habits you can have. All features are available to all users. There are no in-app purchases.
+
+* Completely ad-free and open source.
+There are no advertisements, annoying notifications or intrusive permissions in this app, and there will never be. The app is completely open-source (GPLv3).
+
+* Works offline and respects your privacy.
+Loop doesn't require an Internet connection or online account registration. Your confidential data is never sent to anyone. Neither the developers nor any third-parties have access to it.
## Installing
@@ -85,33 +83,33 @@ contribute, even if you are not a software developer.
- Copyright (C) 2016-2019 Álinson Santos Xavier
-
- 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 .
-
-[screen1]: screenshots/original/uhabits1.png
-[screen2]: screenshots/original/uhabits2.png
-[screen3]: screenshots/original/uhabits3.png
-[screen4]: screenshots/original/uhabits4.png
-[screen5]: screenshots/original/uhabits5.png
-[screen6]: screenshots/original/uhabits6.png
-[screen1th]: screenshots/thumbs/uhabits1.png
-[screen2th]: screenshots/thumbs/uhabits2.png
-[screen3th]: screenshots/thumbs/uhabits3.png
-[screen4th]: screenshots/thumbs/uhabits4.png
-[screen5th]: screenshots/thumbs/uhabits5.png
-[screen6th]: screenshots/thumbs/uhabits6.png
+ Copyright (C) 2016-2019 Álinson Santos Xavier
+
+ 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 .
+
+[screen1]: screenshots/uhabits1.png
+[screen2]: screenshots/uhabits2.png
+[screen3]: screenshots/uhabits3.png
+[screen4]: screenshots/uhabits4.png
+[screen5]: screenshots/uhabits5.png
+[screen6]: screenshots/uhabits6.png
+[screen1th]: screenshots/uhabits1_th.png
+[screen2th]: screenshots/uhabits2_th.png
+[screen3th]: screenshots/uhabits3_th.png
+[screen4th]: screenshots/uhabits4_th.png
+[screen5th]: screenshots/uhabits5_th.png
+[screen6th]: screenshots/uhabits6_th.png
[poedit]: http://translate.loophabits.org
[playstore]: https://play.google.com/store/apps/details?id=org.isoron.uhabits
[releases]: https://github.com/iSoron/uhabits/releases
diff --git a/android/.gitignore b/android/.gitignore
index 9ad9f8ab3..29f35cce8 100644
--- a/android/.gitignore
+++ b/android/.gitignore
@@ -13,6 +13,7 @@
.gradle
.idea
.project
+.secret
Thumbs.db
art/
bin/
diff --git a/android/android-base/build.gradle b/android/android-base/build.gradle
index a86283c2d..f10169233 100644
--- a/android/android-base/build.gradle
+++ b/android/android-base/build.gradle
@@ -1,23 +1,14 @@
apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android'
android {
compileSdkVersion COMPILE_SDK_VERSION as Integer
- buildToolsVersion BUILD_TOOLS_VERSION
defaultConfig {
minSdkVersion MIN_SDK_VERSION as Integer
targetSdkVersion TARGET_SDK_VERSION as Integer
- versionCode 1
- versionName "1.0"
- testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
-
- }
-
- buildTypes {
- release {
- minifyEnabled false
- proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
- }
+ versionCode VERSION_CODE as Integer
+ versionName "$VERSION_NAME"
}
compileOptions {
@@ -29,25 +20,14 @@ android {
checkReleaseBuilds false
abortOnError false
}
-
- buildToolsVersion '26.0.2'
}
dependencies {
implementation "com.google.dagger:dagger:$DAGGER_VERSION"
- implementation "com.android.support:design:$SUPPORT_LIBRARY_VERSION"
- implementation "com.android.support:appcompat-v7:$SUPPORT_LIBRARY_VERSION"
+ implementation 'com.google.android.material:material:1.0.0'
+ implementation 'androidx.appcompat:appcompat:1.0.0'
implementation "org.apache.commons:commons-lang3:3.5"
annotationProcessor "com.google.dagger:dagger-compiler:$DAGGER_VERSION"
- androidTestAnnotationProcessor "com.google.dagger:dagger-compiler:$DAGGER_VERSION"
- androidTestImplementation "com.google.dagger:dagger:$DAGGER_VERSION"
- testAnnotationProcessor "com.google.dagger:dagger-compiler:$DAGGER_VERSION"
- testImplementation "junit:junit:4.12"
-
- androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', {
- exclude group: 'com.android.support', module: 'support-annotations'
- })
-
-
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$KOTLIN_VERSION"
}
diff --git a/android/android-base/src/main/java/org/isoron/androidbase/AndroidBugReporter.java b/android/android-base/src/main/java/org/isoron/androidbase/AndroidBugReporter.java
deleted file mode 100644
index e50e2b04d..000000000
--- a/android/android-base/src/main/java/org/isoron/androidbase/AndroidBugReporter.java
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * Copyright (C) 2017 Álinson Santos Xavier
- *
- * This file is part of Loop Habit Tracker.
- *
- * Loop Habit Tracker is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or (at your
- * option) any later version.
- *
- * Loop Habit Tracker is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see .
- */
-
-package org.isoron.androidbase;
-
-import android.content.*;
-import android.os.*;
-import android.support.annotation.*;
-import android.view.*;
-
-import java.io.*;
-import java.text.*;
-import java.util.*;
-
-import javax.inject.*;
-
-public class AndroidBugReporter
-{
- private final Context context;
-
- @Inject
- public AndroidBugReporter(@NonNull @AppContext Context context)
- {
- this.context = context;
- }
-
- /**
- * Captures and returns a bug report. The bug report contains some device
- * information and the logcat.
- *
- * @return a String containing the bug report.
- * @throws IOException when any I/O error occur.
- */
- @NonNull
- public String getBugReport() throws IOException
- {
- String logcat = getLogcat();
- String deviceInfo = getDeviceInfo();
-
- String log = "---------- BUG REPORT BEGINS ----------\n";
- log += deviceInfo + "\n" + logcat;
- log += "---------- BUG REPORT ENDS ------------\n";
-
- return log;
- }
-
- public String getDeviceInfo()
- {
- if (context == null) return "null context\n";
-
- WindowManager wm =
- (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
-
- return
- String.format("App Version Name: %s\n", BuildConfig.VERSION_NAME) +
- String.format("App Version Code: %s\n", BuildConfig.VERSION_CODE) +
- String.format("OS Version: %s (%s)\n",
- System.getProperty("os.version"), Build.VERSION.INCREMENTAL) +
- String.format("OS API Level: %s\n", Build.VERSION.SDK) +
- String.format("Device: %s\n", Build.DEVICE) +
- String.format("Model (Product): %s (%s)\n", Build.MODEL,
- Build.PRODUCT) +
- String.format("Manufacturer: %s\n", Build.MANUFACTURER) +
- String.format("Other tags: %s\n", Build.TAGS) +
- String.format("Screen Width: %s\n",
- wm.getDefaultDisplay().getWidth()) +
- String.format("Screen Height: %s\n",
- wm.getDefaultDisplay().getHeight()) +
- String.format("External storage state: %s\n\n",
- Environment.getExternalStorageState());
- }
-
- public String getLogcat() throws IOException
- {
- int maxLineCount = 250;
- StringBuilder builder = new StringBuilder();
-
- String[] command = new String[]{ "logcat", "-d" };
- java.lang.Process process = Runtime.getRuntime().exec(command);
-
- InputStreamReader in = new InputStreamReader(process.getInputStream());
- BufferedReader bufferedReader = new BufferedReader(in);
-
- LinkedList log = new LinkedList<>();
-
- String line;
- while ((line = bufferedReader.readLine()) != null)
- {
- log.addLast(line);
- if (log.size() > maxLineCount) log.removeFirst();
- }
-
- for (String l : log)
- {
- builder.append(l);
- builder.append('\n');
- }
-
- return builder.toString();
- }
-
- /**
- * Captures a bug report and saves it to a file in the SD card.
- *
- * The contents of the file are generated by the method {@link
- * #getBugReport()}. The file is saved in the apps's external private
- * storage.
- *
- * @return the generated file.
- * @throws IOException when I/O errors occur.
- */
- @NonNull
- public void dumpBugReportToFile()
- {
- try
- {
-
- String date =
- new SimpleDateFormat("yyyy-MM-dd HHmmss", Locale.US).format(
- new Date());
-
- if (context == null) throw new IllegalStateException();
-
- File dir = new AndroidDirFinder(context).getFilesDir("Logs");
- if (dir == null)
- throw new IOException("log dir should not be null");
-
- File logFile =
- new File(String.format("%s/Log %s.txt", dir.getPath(), date));
- FileWriter output = new FileWriter(logFile);
- output.write(getBugReport());
- output.close();
- }
- catch (IOException e)
- {
- e.printStackTrace();
- }
- }
-}
diff --git a/android/android-base/src/main/java/org/isoron/androidbase/AndroidBugReporter.kt b/android/android-base/src/main/java/org/isoron/androidbase/AndroidBugReporter.kt
new file mode 100644
index 000000000..ea013ceef
--- /dev/null
+++ b/android/android-base/src/main/java/org/isoron/androidbase/AndroidBugReporter.kt
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2017 Álinson Santos Xavier
+ *
+ * This file is part of Loop Habit Tracker.
+ *
+ * Loop Habit Tracker is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * Loop Habit Tracker is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see .
+ */
+package org.isoron.androidbase
+
+import android.content.Context
+import android.os.Build
+import android.os.Environment
+import android.view.WindowManager
+import java.io.*
+import java.text.SimpleDateFormat
+import java.util.*
+import javax.inject.Inject
+
+open class AndroidBugReporter @Inject constructor(@AppContext private val context: Context) {
+
+ /**
+ * Captures and returns a bug report. The bug report contains some device
+ * information and the logcat.
+ *
+ * @return a String containing the bug report.
+ * @throws IOException when any I/O error occur.
+ */
+ @Throws(IOException::class)
+ fun getBugReport(): String {
+ var log = "---------- BUG REPORT BEGINS ----------\n"
+ log += "${getLogcat()}\n"
+ log += "${getDeviceInfo()}\n"
+ log += "---------- BUG REPORT ENDS ------------\n"
+ return log
+ }
+
+ @Throws(IOException::class)
+ fun getLogcat(): String {
+ val maxLineCount = 250
+ val builder = StringBuilder()
+ val process = Runtime.getRuntime().exec(arrayOf("logcat", "-d"))
+ val inputReader = InputStreamReader(process.inputStream)
+ val bufferedReader = BufferedReader(inputReader)
+ val log = LinkedList()
+ var line: String?
+ while (true) {
+ line = bufferedReader.readLine()
+ if (line == null) break;
+ log.addLast(line)
+ if (log.size > maxLineCount) log.removeFirst()
+ }
+ for (l in log) {
+ builder.appendln(l)
+ }
+ return builder.toString()
+ }
+
+ /**
+ * Captures a bug report and saves it to a file in the SD card.
+ *
+ * The contents of the file are generated by the method [ ][.getBugReport]. The file is saved
+ * in the apps's external private storage.
+ *
+ * @return the generated file.
+ * @throws IOException when I/O errors occur.
+ */
+ fun dumpBugReportToFile() {
+ try {
+ val date = SimpleDateFormat("yyyy-MM-dd HHmmss", Locale.US).format(Date())
+ val dir = AndroidDirFinder(context).getFilesDir("Logs")
+ ?: throw IOException("log dir should not be null")
+ val logFile = File(String.format("%s/Log %s.txt", dir.path, date))
+ val output = FileWriter(logFile)
+ output.write(getBugReport())
+ output.close()
+ } catch (e: IOException) {
+ e.printStackTrace()
+ }
+ }
+
+ private fun getDeviceInfo(): String {
+ val wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
+ return buildString {
+ appendln("App Version Name: ${BuildConfig.VERSION_NAME}")
+ appendln("App Version Code: ${BuildConfig.VERSION_CODE}")
+ appendln("OS Version: ${System.getProperty("os.version")} (${Build.VERSION.INCREMENTAL})")
+ appendln("OS API Level: ${Build.VERSION.SDK}")
+ appendln("Device: ${Build.DEVICE}")
+ appendln("Model (Product): ${Build.MODEL} (${Build.PRODUCT})")
+ appendln("Manufacturer: ${Build.MANUFACTURER}")
+ appendln("Other tags: ${Build.TAGS}")
+ appendln("Screen Width: ${wm.defaultDisplay.width}")
+ appendln("Screen Height: ${wm.defaultDisplay.height}")
+ appendln("External storage state: ${Environment.getExternalStorageState()}")
+ appendln()
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/android/android-base/src/main/java/org/isoron/androidbase/AndroidDirFinder.java b/android/android-base/src/main/java/org/isoron/androidbase/AndroidDirFinder.java
deleted file mode 100644
index 3e40aed4d..000000000
--- a/android/android-base/src/main/java/org/isoron/androidbase/AndroidDirFinder.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2017 Álinson Santos Xavier
- *
- * This file is part of Loop Habit Tracker.
- *
- * Loop Habit Tracker is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or (at your
- * option) any later version.
- *
- * Loop Habit Tracker is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see .
- */
-
-package org.isoron.androidbase;
-
-import android.content.*;
-import android.support.annotation.*;
-import android.support.v4.content.*;
-import android.util.*;
-
-import org.isoron.androidbase.utils.*;
-
-import java.io.*;
-
-import javax.inject.*;
-
-public class AndroidDirFinder
-{
- @NonNull
- private Context context;
-
- @Inject
- public AndroidDirFinder(@NonNull @AppContext Context context)
- {
- this.context = context;
- }
-
- @Nullable
- public File getFilesDir(@Nullable String relativePath)
- {
- File externalFilesDirs[] =
- ContextCompat.getExternalFilesDirs(context, null);
- if (externalFilesDirs == null)
- {
- Log.e("BaseSystem",
- "getFilesDir: getExternalFilesDirs returned null");
- return null;
- }
-
- return FileUtils.getDir(externalFilesDirs, relativePath);
- }
-}
diff --git a/android/android-base/src/main/java/org/isoron/androidbase/AndroidDirFinder.kt b/android/android-base/src/main/java/org/isoron/androidbase/AndroidDirFinder.kt
new file mode 100644
index 000000000..9dbeeeb65
--- /dev/null
+++ b/android/android-base/src/main/java/org/isoron/androidbase/AndroidDirFinder.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2017 Álinson Santos Xavier
+ *
+ * This file is part of Loop Habit Tracker.
+ *
+ * Loop Habit Tracker is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * Loop Habit Tracker is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see .
+ */
+package org.isoron.androidbase
+
+import android.content.Context
+import androidx.core.content.ContextCompat
+import org.isoron.androidbase.utils.FileUtils
+import java.io.File
+import javax.inject.Inject
+
+class AndroidDirFinder @Inject constructor(@param:AppContext private val context: Context) {
+ fun getFilesDir(relativePath: String?): File? {
+ return FileUtils.getDir(
+ ContextCompat.getExternalFilesDirs(context, null),
+ relativePath
+ )
+ }
+}
\ No newline at end of file
diff --git a/android/android-base/src/main/java/org/isoron/androidbase/AppContext.java b/android/android-base/src/main/java/org/isoron/androidbase/AppContext.kt
similarity index 80%
rename from android/android-base/src/main/java/org/isoron/androidbase/AppContext.java
rename to android/android-base/src/main/java/org/isoron/androidbase/AppContext.kt
index d7521e26d..3146da5e2 100644
--- a/android/android-base/src/main/java/org/isoron/androidbase/AppContext.java
+++ b/android/android-base/src/main/java/org/isoron/androidbase/AppContext.kt
@@ -16,16 +16,14 @@
* You should have received a copy of the GNU General Public License along
* with this program. If not, see .
*/
+package org.isoron.androidbase
-package org.isoron.androidbase;
-
-import java.lang.annotation.*;
-
-import javax.inject.*;
+import java.lang.annotation.Documented
+import java.lang.annotation.Retention
+import java.lang.annotation.RetentionPolicy
+import javax.inject.Qualifier
@Qualifier
@Documented
@Retention(RetentionPolicy.RUNTIME)
-public @interface AppContext
-{
-}
+annotation class AppContext
\ No newline at end of file
diff --git a/android/android-base/src/main/java/org/isoron/androidbase/BaseExceptionHandler.java b/android/android-base/src/main/java/org/isoron/androidbase/BaseExceptionHandler.java
deleted file mode 100644
index 3a04b8b4f..000000000
--- a/android/android-base/src/main/java/org/isoron/androidbase/BaseExceptionHandler.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2017 Álinson Santos Xavier
- *
- * This file is part of Loop Habit Tracker.
- *
- * Loop Habit Tracker is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or (at your
- * option) any later version.
- *
- * Loop Habit Tracker is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see .
- */
-
-package org.isoron.androidbase;
-
-import android.support.annotation.*;
-
-import org.isoron.androidbase.activities.*;
-
-public class BaseExceptionHandler implements Thread.UncaughtExceptionHandler
-{
- @Nullable
- private Thread.UncaughtExceptionHandler originalHandler;
-
- @NonNull
- private BaseActivity activity;
-
- public BaseExceptionHandler(@NonNull BaseActivity activity)
- {
- this.activity = activity;
- originalHandler = Thread.getDefaultUncaughtExceptionHandler();
- }
-
- @Override
- public void uncaughtException(@Nullable Thread thread,
- @Nullable Throwable ex)
- {
- if (ex == null) return;
-
- try
- {
- ex.printStackTrace();
- new AndroidBugReporter(activity).dumpBugReportToFile();
- }
- catch (Exception e)
- {
- e.printStackTrace();
- }
-
-// if (ex.getCause() instanceof InconsistentDatabaseException)
-// {
-// HabitsApplication app = (HabitsApplication) activity.getApplication();
-// HabitList habits = app.getComponent().getHabitList();
-// habits.repair();
-// System.exit(0);
-// }
-
- if (originalHandler != null)
- originalHandler.uncaughtException(thread, ex);
- }
-}
diff --git a/android/android-base/src/main/java/org/isoron/androidbase/BaseExceptionHandler.kt b/android/android-base/src/main/java/org/isoron/androidbase/BaseExceptionHandler.kt
new file mode 100644
index 000000000..c6907f3ec
--- /dev/null
+++ b/android/android-base/src/main/java/org/isoron/androidbase/BaseExceptionHandler.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2017 Álinson Santos Xavier
+ *
+ * This file is part of Loop Habit Tracker.
+ *
+ * Loop Habit Tracker is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * Loop Habit Tracker is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see .
+ */
+package org.isoron.androidbase
+
+import org.isoron.androidbase.activities.BaseActivity
+
+class BaseExceptionHandler(private val activity: BaseActivity) : Thread.UncaughtExceptionHandler {
+
+ private val originalHandler: Thread.UncaughtExceptionHandler? =
+ Thread.getDefaultUncaughtExceptionHandler()
+
+ override fun uncaughtException(thread: Thread?, ex: Throwable?) {
+ if (ex == null) return
+ if (thread == null) return
+ try {
+ ex.printStackTrace()
+ AndroidBugReporter(activity).dumpBugReportToFile()
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+ originalHandler?.uncaughtException(thread, ex)
+ }
+}
\ No newline at end of file
diff --git a/android/android-base/src/main/java/org/isoron/androidbase/SSLContextProvider.java b/android/android-base/src/main/java/org/isoron/androidbase/SSLContextProvider.java
deleted file mode 100644
index c488bbc16..000000000
--- a/android/android-base/src/main/java/org/isoron/androidbase/SSLContextProvider.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2017 Álinson Santos Xavier
- *
- * This file is part of Loop Habit Tracker.
- *
- * Loop Habit Tracker is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or (at your
- * option) any later version.
- *
- * Loop Habit Tracker is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see .
- */
-
-package org.isoron.androidbase;
-
-import android.content.*;
-import android.support.annotation.*;
-
-import org.isoron.androidbase.*;
-
-import java.io.*;
-import java.security.*;
-import java.security.cert.Certificate;
-import java.security.cert.*;
-
-import javax.inject.*;
-import javax.net.ssl.*;
-
-public class SSLContextProvider
-{
- private Context context;
-
- @Inject
- public SSLContextProvider(@NonNull @AppContext Context context)
- {
- this.context = context;
- }
-
- public SSLContext getCACertSSLContext()
- {
- try
- {
- CertificateFactory cf = CertificateFactory.getInstance("X.509");
- InputStream caInput = context.getAssets().open("cacert.pem");
- Certificate ca = cf.generateCertificate(caInput);
-
- KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
- ks.load(null, null);
- ks.setCertificateEntry("ca", ca);
-
- TrustManagerFactory tmf = TrustManagerFactory.getInstance(
- TrustManagerFactory.getDefaultAlgorithm());
- tmf.init(ks);
-
- SSLContext ctx = SSLContext.getInstance("TLS");
- ctx.init(null, tmf.getTrustManagers(), null);
-
- return ctx;
- }
- catch (Exception e)
- {
- throw new RuntimeException(e);
- }
- }
-}
diff --git a/android/android-base/src/main/java/org/isoron/androidbase/SSLContextProvider.kt b/android/android-base/src/main/java/org/isoron/androidbase/SSLContextProvider.kt
new file mode 100644
index 000000000..797d6c93d
--- /dev/null
+++ b/android/android-base/src/main/java/org/isoron/androidbase/SSLContextProvider.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2017 Álinson Santos Xavier
+ *
+ * This file is part of Loop Habit Tracker.
+ *
+ * Loop Habit Tracker is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * Loop Habit Tracker is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see .
+ */
+package org.isoron.androidbase
+
+import android.content.Context
+import java.security.KeyStore
+import java.security.cert.CertificateFactory
+import javax.inject.Inject
+import javax.net.ssl.SSLContext
+import javax.net.ssl.TrustManagerFactory
+
+class SSLContextProvider @Inject constructor(@param:AppContext private val context: Context) {
+ fun getCACertSSLContext(): SSLContext {
+ try {
+ val cf = CertificateFactory.getInstance("X.509")
+ val ca = cf.generateCertificate(context.assets.open("cacert.pem"))
+ val ks = KeyStore.getInstance(KeyStore.getDefaultType()).apply {
+ load(null, null)
+ setCertificateEntry("ca", ca)
+ }
+ val alg = TrustManagerFactory.getDefaultAlgorithm()
+ val tmf = TrustManagerFactory.getInstance(alg).apply {
+ init(ks)
+ }
+ return SSLContext.getInstance("TLS").apply {
+ init(null, tmf.trustManagers, null)
+ }
+ } catch (e: Exception) {
+ throw RuntimeException(e)
+ }
+ }
+}
\ No newline at end of file
diff --git a/android/android-base/src/main/java/org/isoron/androidbase/activities/BaseActivity.java b/android/android-base/src/main/java/org/isoron/androidbase/activities/BaseActivity.java
index fae8c82ba..2dea22e50 100644
--- a/android/android-base/src/main/java/org/isoron/androidbase/activities/BaseActivity.java
+++ b/android/android-base/src/main/java/org/isoron/androidbase/activities/BaseActivity.java
@@ -21,8 +21,9 @@ package org.isoron.androidbase.activities;
import android.content.*;
import android.os.*;
-import android.support.annotation.*;
-import android.support.v7.app.*;
+
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.*;
import android.view.*;
import org.isoron.androidbase.*;
diff --git a/android/android-base/src/main/java/org/isoron/androidbase/activities/BaseMenu.java b/android/android-base/src/main/java/org/isoron/androidbase/activities/BaseMenu.java
index 7cba01ddc..c5113a3fa 100644
--- a/android/android-base/src/main/java/org/isoron/androidbase/activities/BaseMenu.java
+++ b/android/android-base/src/main/java/org/isoron/androidbase/activities/BaseMenu.java
@@ -19,9 +19,11 @@
package org.isoron.androidbase.activities;
-import android.support.annotation.*;
import android.view.*;
+import androidx.annotation.MenuRes;
+import androidx.annotation.NonNull;
+
/**
* Base class for all the menus in the application.
*
diff --git a/android/android-base/src/main/java/org/isoron/androidbase/activities/BaseRootView.java b/android/android-base/src/main/java/org/isoron/androidbase/activities/BaseRootView.java
index 21c0322f7..3ff339387 100644
--- a/android/android-base/src/main/java/org/isoron/androidbase/activities/BaseRootView.java
+++ b/android/android-base/src/main/java/org/isoron/androidbase/activities/BaseRootView.java
@@ -20,8 +20,10 @@
package org.isoron.androidbase.activities;
import android.content.*;
-import android.support.annotation.*;
-import android.support.v7.widget.Toolbar;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.widget.Toolbar;
import android.view.*;
import android.widget.*;
diff --git a/android/android-base/src/main/java/org/isoron/androidbase/activities/BaseScreen.java b/android/android-base/src/main/java/org/isoron/androidbase/activities/BaseScreen.java
index f135a6e62..331d91742 100644
--- a/android/android-base/src/main/java/org/isoron/androidbase/activities/BaseScreen.java
+++ b/android/android-base/src/main/java/org/isoron/androidbase/activities/BaseScreen.java
@@ -24,15 +24,19 @@ import android.graphics.*;
import android.graphics.drawable.*;
import android.net.*;
import android.os.*;
-import android.support.annotation.*;
-import android.support.design.widget.*;
-import android.support.v4.content.res.*;
-import android.support.v7.app.*;
-import android.support.v7.view.ActionMode;
-import android.support.v7.widget.Toolbar;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
+import androidx.core.content.res.*;
+import androidx.appcompat.app.*;
+import androidx.appcompat.view.ActionMode;
+import androidx.appcompat.widget.Toolbar;
import android.view.*;
import android.widget.*;
+import com.google.android.material.snackbar.Snackbar;
+
import org.isoron.androidbase.*;
import org.isoron.androidbase.utils.*;
@@ -40,7 +44,7 @@ import java.io.*;
import static android.os.Build.VERSION.SDK_INT;
import static android.os.Build.VERSION_CODES.LOLLIPOP;
-import static android.support.v4.content.FileProvider.getUriForFile;
+import static androidx.core.content.FileProvider.getUriForFile;
/**
* Base class for all screens in the application.
@@ -214,7 +218,7 @@ public class BaseScreen
if (snackbar == null)
{
snackbar = Snackbar.make(rootView, stringId, Snackbar.LENGTH_SHORT);
- int tvId = android.support.design.R.id.snackbar_text;
+ int tvId = R.id.snackbar_text;
TextView tv = (TextView) snackbar.getView().findViewById(tvId);
tv.setTextColor(Color.WHITE);
}
diff --git a/android/android-base/src/main/java/org/isoron/androidbase/activities/BaseSelectionMenu.java b/android/android-base/src/main/java/org/isoron/androidbase/activities/BaseSelectionMenu.java
index 87396e216..a1b486555 100644
--- a/android/android-base/src/main/java/org/isoron/androidbase/activities/BaseSelectionMenu.java
+++ b/android/android-base/src/main/java/org/isoron/androidbase/activities/BaseSelectionMenu.java
@@ -19,8 +19,9 @@
package org.isoron.androidbase.activities;
-import android.support.annotation.*;
-import android.support.v7.view.ActionMode;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.view.ActionMode;
import android.view.*;
/**
diff --git a/android/android-base/src/main/java/org/isoron/androidbase/utils/FileUtils.java b/android/android-base/src/main/java/org/isoron/androidbase/utils/FileUtils.java
index 59ca4a9b4..b04f969b8 100644
--- a/android/android-base/src/main/java/org/isoron/androidbase/utils/FileUtils.java
+++ b/android/android-base/src/main/java/org/isoron/androidbase/utils/FileUtils.java
@@ -20,9 +20,11 @@
package org.isoron.androidbase.utils;
import android.os.*;
-import android.support.annotation.*;
import android.util.*;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import java.io.*;
public abstract class FileUtils
diff --git a/android/android-base/src/main/java/org/isoron/androidbase/utils/InterfaceUtils.java b/android/android-base/src/main/java/org/isoron/androidbase/utils/InterfaceUtils.java
index e1fea418b..e9d484cb1 100644
--- a/android/android-base/src/main/java/org/isoron/androidbase/utils/InterfaceUtils.java
+++ b/android/android-base/src/main/java/org/isoron/androidbase/utils/InterfaceUtils.java
@@ -22,8 +22,10 @@ package org.isoron.androidbase.utils;
import android.content.*;
import android.content.res.*;
import android.graphics.*;
-import android.support.annotation.*;
-import android.support.v4.view.*;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.core.view.*;
import android.util.*;
import android.view.*;
import android.widget.*;
diff --git a/android/android-base/src/main/java/org/isoron/androidbase/utils/StyledResources.java b/android/android-base/src/main/java/org/isoron/androidbase/utils/StyledResources.java
index d15766f95..d00588a90 100644
--- a/android/android-base/src/main/java/org/isoron/androidbase/utils/StyledResources.java
+++ b/android/android-base/src/main/java/org/isoron/androidbase/utils/StyledResources.java
@@ -22,7 +22,9 @@ package org.isoron.androidbase.utils;
import android.content.*;
import android.content.res.*;
import android.graphics.drawable.*;
-import android.support.annotation.*;
+
+import androidx.annotation.AttrRes;
+import androidx.annotation.NonNull;
import org.isoron.androidbase.*;
diff --git a/android/android-pickers/build.gradle b/android/android-pickers/build.gradle
index 067d2a567..0627c16ca 100644
--- a/android/android-pickers/build.gradle
+++ b/android/android-pickers/build.gradle
@@ -2,24 +2,12 @@ apply plugin: 'com.android.library'
android {
compileSdkVersion COMPILE_SDK_VERSION as Integer
- buildToolsVersion BUILD_TOOLS_VERSION
defaultConfig {
minSdkVersion MIN_SDK_VERSION as Integer
targetSdkVersion TARGET_SDK_VERSION as Integer
- versionCode 1
- versionName "1.0"
- testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
- buildTypes {
- release {
- minifyEnabled false
- proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
- }
- }
- buildToolsVersion '26.0.2'
-
compileOptions {
targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility JavaVersion.VERSION_1_8
@@ -32,5 +20,5 @@ android {
}
dependencies {
- implementation "com.android.support:appcompat-v7:$SUPPORT_LIBRARY_VERSION"
+ implementation 'androidx.appcompat:appcompat:1.0.0'
}
diff --git a/android/android-pickers/src/main/java/com/android/colorpicker/ColorPickerDialog.java b/android/android-pickers/src/main/java/com/android/colorpicker/ColorPickerDialog.java
index 1060faf5b..a4f503392 100644
--- a/android/android-pickers/src/main/java/com/android/colorpicker/ColorPickerDialog.java
+++ b/android/android-pickers/src/main/java/com/android/colorpicker/ColorPickerDialog.java
@@ -18,8 +18,8 @@ package com.android.colorpicker;
import android.app.*;
import android.os.*;
-import android.support.v7.app.AlertDialog;
-import android.support.v7.app.*;
+import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.app.*;
import android.view.*;
import android.widget.*;
diff --git a/android/android-pickers/src/main/java/com/android/datetimepicker/date/MonthView.java b/android/android-pickers/src/main/java/com/android/datetimepicker/date/MonthView.java
index eff8b8186..79cda6b80 100644
--- a/android/android-pickers/src/main/java/com/android/datetimepicker/date/MonthView.java
+++ b/android/android-pickers/src/main/java/com/android/datetimepicker/date/MonthView.java
@@ -21,13 +21,15 @@ import android.content.res.*;
import android.graphics.*;
import android.graphics.Paint.*;
import android.os.*;
-import android.support.v4.view.*;
-import android.support.v4.view.accessibility.*;
-import android.support.v4.widget.*;
+import androidx.core.view.*;
+import androidx.core.view.accessibility.*;
+import androidx.core.widget.*;
import android.text.format.*;
import android.view.*;
import android.view.accessibility.*;
+import androidx.customview.widget.ExploreByTouchHelper;
+
import com.android.*;
import com.android.datetimepicker.*;
import com.android.datetimepicker.date.MonthAdapter.*;
diff --git a/android/android-pickers/src/main/java/com/android/datetimepicker/time/AmPmCirclesView.java b/android/android-pickers/src/main/java/com/android/datetimepicker/time/AmPmCirclesView.java
index f310a1d85..3c6316521 100644
--- a/android/android-pickers/src/main/java/com/android/datetimepicker/time/AmPmCirclesView.java
+++ b/android/android-pickers/src/main/java/com/android/datetimepicker/time/AmPmCirclesView.java
@@ -41,8 +41,8 @@ public class AmPmCirclesView extends View {
private final Paint mPaint = new Paint();
private int mSelectedAlpha;
private int mUnselectedColor;
- private int mAmPmTextColor;
- private int mSelectedColor;
+ protected int mAmPmTextColor = Color.WHITE;
+ protected int mSelectedColor = Color.BLUE;
private float mCircleRadiusMultiplier;
private float mAmPmCircleRadiusMultiplier;
private String mAmText;
@@ -73,8 +73,8 @@ public class AmPmCirclesView extends View {
Resources res = context.getResources();
mUnselectedColor = res.getColor(R.color.white);
- mSelectedColor = res.getColor(R.color.blue);
- mAmPmTextColor = res.getColor(R.color.ampm_text_color);
+ //mSelectedColor = res.getColor(R.color.blue);
+ //mAmPmTextColor = res.getColor(R.color.ampm_text_color);
mSelectedAlpha = SELECTED_ALPHA;
String typefaceFamily = res.getString(R.string.sans_serif);
Typeface tf = Typeface.create(typefaceFamily, Typeface.NORMAL);
@@ -105,8 +105,8 @@ public class AmPmCirclesView extends View {
mSelectedAlpha = SELECTED_ALPHA_THEME_DARK;
} else {
mUnselectedColor = res.getColor(R.color.white);
- mSelectedColor = res.getColor(R.color.blue);
- mAmPmTextColor = res.getColor(R.color.ampm_text_color);
+ //mSelectedColor = res.getColor(R.color.blue);
+ //mAmPmTextColor = res.getColor(R.color.ampm_text_color);
mSelectedAlpha = SELECTED_ALPHA;
}
}
diff --git a/android/android-pickers/src/main/java/com/android/datetimepicker/time/RadialPickerLayout.java b/android/android-pickers/src/main/java/com/android/datetimepicker/time/RadialPickerLayout.java
index 265b98cf8..307424a3e 100644
--- a/android/android-pickers/src/main/java/com/android/datetimepicker/time/RadialPickerLayout.java
+++ b/android/android-pickers/src/main/java/com/android/datetimepicker/time/RadialPickerLayout.java
@@ -84,6 +84,14 @@ public class RadialPickerLayout extends FrameLayout implements OnTouchListener {
private AnimatorSet mTransition;
private Handler mHandler = new Handler();
+ public void setColor(int selectedColor)
+ {
+ mHourRadialSelectorView.mPaint.setColor(selectedColor);
+ mMinuteRadialSelectorView.mPaint.setColor(selectedColor);
+ mAmPmCirclesView.mSelectedColor = selectedColor;
+ mAmPmCirclesView.mAmPmTextColor = selectedColor;
+ }
+
public interface OnValueSelectedListener {
void onValueSelected(int pickerIndex, int newValue, boolean autoAdvance);
}
diff --git a/android/android-pickers/src/main/java/com/android/datetimepicker/time/RadialSelectorView.java b/android/android-pickers/src/main/java/com/android/datetimepicker/time/RadialSelectorView.java
index 48e4385b5..018147efa 100644
--- a/android/android-pickers/src/main/java/com/android/datetimepicker/time/RadialSelectorView.java
+++ b/android/android-pickers/src/main/java/com/android/datetimepicker/time/RadialSelectorView.java
@@ -40,7 +40,7 @@ public class RadialSelectorView extends View {
// Alpha level for the line.
private static final int FULL_ALPHA = Utils.FULL_ALPHA;
- private final Paint mPaint = new Paint();
+ protected final Paint mPaint = new Paint();
private boolean mIsInitialized;
private boolean mDrawValuesReady;
@@ -96,8 +96,6 @@ public class RadialSelectorView extends View {
Resources res = context.getResources();
- int blue = res.getColor(R.color.blue);
- mPaint.setColor(blue);
mPaint.setAntiAlias(true);
mSelectionAlpha = SELECTED_ALPHA;
@@ -139,15 +137,11 @@ public class RadialSelectorView extends View {
/* package */ void setTheme(Context context, boolean themeDark) {
Resources res = context.getResources();
- int color;
if (themeDark) {
- color = res.getColor(R.color.red);
mSelectionAlpha = SELECTED_ALPHA_THEME_DARK;
} else {
- color = res.getColor(R.color.blue);
mSelectionAlpha = SELECTED_ALPHA;
}
- mPaint.setColor(color);
}
/**
diff --git a/android/android-pickers/src/main/java/com/android/datetimepicker/time/TimePickerDialog.java b/android/android-pickers/src/main/java/com/android/datetimepicker/time/TimePickerDialog.java
index 06c121b3c..0a8411bc5 100644
--- a/android/android-pickers/src/main/java/com/android/datetimepicker/time/TimePickerDialog.java
+++ b/android/android-pickers/src/main/java/com/android/datetimepicker/time/TimePickerDialog.java
@@ -23,7 +23,9 @@ import android.app.*;
import android.content.*;
import android.content.res.*;
import android.os.*;
-import android.support.v7.app.*;
+
+import androidx.appcompat.app.*;
+
import android.util.*;
import android.view.*;
import android.view.View.*;
@@ -39,7 +41,8 @@ import java.util.*;
/**
* Dialog to set a time.
*/
-public class TimePickerDialog extends AppCompatDialogFragment implements OnValueSelectedListener{
+public class TimePickerDialog extends AppCompatDialogFragment implements OnValueSelectedListener
+{
private static final String TAG = "TimePickerDialog";
private static final String KEY_HOUR_OF_DAY = "hour_of_day";
@@ -49,6 +52,7 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
private static final String KEY_IN_KB_MODE = "in_kb_mode";
private static final String KEY_TYPED_TIMES = "typed_times";
private static final String KEY_DARK_THEME = "dark_theme";
+ private static final String KEY_SELECTED_COLOR = "selected_color";
public static final int HOUR_INDEX = 0;
public static final int MINUTE_INDEX = 1;
@@ -108,37 +112,50 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
* The callback interface used to indicate the user is done filling in
* the time (they clicked on the 'Set' button).
*/
- public interface OnTimeSetListener {
+ public interface OnTimeSetListener
+ {
/**
- * @param view The view associated with this listener.
+ * @param view The view associated with this listener.
* @param hourOfDay The hour that was set.
- * @param minute The minute that was set.
+ * @param minute The minute that was set.
*/
void onTimeSet(RadialPickerLayout view, int hourOfDay, int minute);
-
- default void onTimeCleared(RadialPickerLayout view) {}
+
+ default void onTimeCleared(RadialPickerLayout view)
+ {
+ }
}
- public TimePickerDialog() {
+ public TimePickerDialog()
+ {
// Empty constructor required for dialog fragment.
}
@SuppressLint("Java")
public TimePickerDialog(Context context, int theme, OnTimeSetListener callback,
- int hourOfDay, int minute, boolean is24HourMode) {
+ int hourOfDay, int minute, boolean is24HourMode)
+ {
// Empty constructor required for dialog fragment.
}
public static TimePickerDialog newInstance(OnTimeSetListener callback,
- int hourOfDay, int minute, boolean is24HourMode) {
+ int hourOfDay,
+ int minute,
+ boolean is24HourMode,
+ int color)
+ {
TimePickerDialog ret = new TimePickerDialog();
- ret.initialize(callback, hourOfDay, minute, is24HourMode);
+ ret.initialize(callback, hourOfDay, minute, is24HourMode, color);
return ret;
}
public void initialize(OnTimeSetListener callback,
- int hourOfDay, int minute, boolean is24HourMode) {
+ int hourOfDay,
+ int minute,
+ boolean is24HourMode,
+ int color)
+ {
mCallback = callback;
mInitialHourOfDay = hourOfDay;
@@ -146,40 +163,47 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
mIs24HourMode = is24HourMode;
mInKbMode = false;
mThemeDark = false;
+ mSelectedColor = color;
}
/**
* Set a dark or light theme. NOTE: this will only take effect for the next onCreateView.
*/
- public void setThemeDark(boolean dark) {
+ public void setThemeDark(boolean dark)
+ {
mThemeDark = dark;
}
- public boolean isThemeDark() {
+ public boolean isThemeDark()
+ {
return mThemeDark;
}
- public void setOnTimeSetListener(OnTimeSetListener callback) {
+ public void setOnTimeSetListener(OnTimeSetListener callback)
+ {
mCallback = callback;
}
- public void setStartTime(int hourOfDay, int minute) {
+ public void setStartTime(int hourOfDay, int minute)
+ {
mInitialHourOfDay = hourOfDay;
mInitialMinute = minute;
mInKbMode = false;
}
@Override
- public void onCreate(Bundle savedInstanceState) {
+ public void onCreate(Bundle savedInstanceState)
+ {
super.onCreate(savedInstanceState);
if (savedInstanceState != null && savedInstanceState.containsKey(KEY_HOUR_OF_DAY)
- && savedInstanceState.containsKey(KEY_MINUTE)
- && savedInstanceState.containsKey(KEY_IS_24_HOUR_VIEW)) {
+ && savedInstanceState.containsKey(KEY_MINUTE)
+ && savedInstanceState.containsKey(KEY_IS_24_HOUR_VIEW)) {
mInitialHourOfDay = savedInstanceState.getInt(KEY_HOUR_OF_DAY);
mInitialMinute = savedInstanceState.getInt(KEY_MINUTE);
mIs24HourMode = savedInstanceState.getBoolean(KEY_IS_24_HOUR_VIEW);
mInKbMode = savedInstanceState.getBoolean(KEY_IN_KB_MODE);
mThemeDark = savedInstanceState.getBoolean(KEY_DARK_THEME);
+ mSelectedColor = savedInstanceState.getInt(KEY_SELECTED_COLOR);
}
}
@@ -191,7 +215,8 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
+ Bundle savedInstanceState)
+ {
getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);
View view = inflater.inflate(R.layout.time_picker_dialog, null);
@@ -203,8 +228,8 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
mSelectHours = res.getString(R.string.select_hours);
mMinutePickerDescription = res.getString(R.string.minute_picker_description);
mSelectMinutes = res.getString(R.string.select_minutes);
- mSelectedColor = res.getColor(mThemeDark? R.color.red : R.color.blue);
- mUnselectedColor = res.getColor(mThemeDark? R.color.white : R.color.numbers_text_color);
+ //mSelectedColor = res.getColor(mThemeDark ? R.color.red : R.color.blue);
+ mUnselectedColor = res.getColor(mThemeDark ? R.color.white : R.color.numbers_text_color);
mHourView = (TextView) view.findViewById(R.id.hours);
mHourView.setOnKeyListener(keyboardListener);
@@ -223,8 +248,9 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
mTimePicker = (RadialPickerLayout) view.findViewById(R.id.time_picker);
mTimePicker.setOnValueSelectedListener(this);
mTimePicker.setOnKeyListener(keyboardListener);
+ mTimePicker.setColor(mSelectedColor);
mTimePicker.initialize(getActivity(), mHapticFeedbackController, mInitialHourOfDay,
- mInitialMinute, mIs24HourMode);
+ mInitialMinute, mIs24HourMode);
int currentItemShowing = HOUR_INDEX;
if (savedInstanceState != null &&
@@ -234,25 +260,31 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
setCurrentItemShowing(currentItemShowing, false, true, true);
mTimePicker.invalidate();
- mHourView.setOnClickListener(new OnClickListener() {
+ mHourView.setOnClickListener(new OnClickListener()
+ {
@Override
- public void onClick(View v) {
+ public void onClick(View v)
+ {
setCurrentItemShowing(HOUR_INDEX, true, false, true);
tryVibrate();
}
});
- mMinuteView.setOnClickListener(new OnClickListener() {
+ mMinuteView.setOnClickListener(new OnClickListener()
+ {
@Override
- public void onClick(View v) {
+ public void onClick(View v)
+ {
setCurrentItemShowing(MINUTE_INDEX, true, false, true);
tryVibrate();
}
});
mDoneButton = (TextView) view.findViewById(R.id.done_button);
- mDoneButton.setOnClickListener(new OnClickListener() {
+ mDoneButton.setOnClickListener(new OnClickListener()
+ {
@Override
- public void onClick(View v) {
+ public void onClick(View v)
+ {
if (mInKbMode && isTypedTimeFullyLegal()) {
finishKbMode(false);
} else {
@@ -260,25 +292,25 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
}
if (mCallback != null) {
mCallback.onTimeSet(mTimePicker,
- mTimePicker.getHours(), mTimePicker.getMinutes());
+ mTimePicker.getHours(), mTimePicker.getMinutes());
}
dismiss();
}
});
mDoneButton.setOnKeyListener(keyboardListener);
-
+
mClearButton = (TextView) view.findViewById(R.id.clear_button);
mClearButton.setOnClickListener(new OnClickListener()
- {
- @Override
- public void onClick(View v)
- {
- if(mCallback != null) {
- mCallback.onTimeCleared(mTimePicker);
- }
- dismiss();
- }
- });
+ {
+ @Override
+ public void onClick(View v)
+ {
+ if (mCallback != null) {
+ mCallback.onTimeCleared(mTimePicker);
+ }
+ dismiss();
+ }
+ });
mClearButton.setOnKeyListener(keyboardListener);
// Enable or disable the AM/PM view.
@@ -293,15 +325,17 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
separatorView.setLayoutParams(paramsSeparator);
} else {
mAmPmTextView.setVisibility(View.VISIBLE);
- updateAmPmDisplay(mInitialHourOfDay < 12? AM : PM);
- mAmPmHitspace.setOnClickListener(new OnClickListener() {
+ updateAmPmDisplay(mInitialHourOfDay < 12 ? AM : PM);
+ mAmPmHitspace.setOnClickListener(new OnClickListener()
+ {
@Override
- public void onClick(View v) {
+ public void onClick(View v)
+ {
tryVibrate();
int amOrPm = mTimePicker.getIsCurrentlyAmOrPm();
if (amOrPm == AM) {
amOrPm = PM;
- } else if (amOrPm == PM){
+ } else if (amOrPm == PM) {
amOrPm = AM;
}
updateAmPmDisplay(amOrPm);
@@ -328,56 +362,61 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
mTypedTimes = new ArrayList();
}
- // Set the theme at the end so that the initialize()s above don't counteract the theme.
- mTimePicker.setTheme(getActivity().getApplicationContext(), mThemeDark);
- // Prepare some palette to use.
- int white = res.getColor(R.color.white);
- int circleBackground = res.getColor(R.color.circle_background);
- int line = res.getColor(R.color.line_background);
- int timeDisplay = res.getColor(R.color.numbers_text_color);
- ColorStateList doneTextColor = res.getColorStateList(R.color.done_text_color);
- int doneBackground = R.drawable.done_background_color;
- int darkGray = res.getColor(R.color.dark_gray);
- int lightGray = res.getColor(R.color.light_gray);
- int darkLine = res.getColor(R.color.line_dark);
- ColorStateList darkDoneTextColor = res.getColorStateList(R.color.done_text_color_dark);
- int darkDoneBackground = R.drawable.done_background_color_dark;
+// // Set the theme at the end so that the initialize()s above don't counteract the theme.
+// mTimePicker.setTheme(getActivity().getApplicationContext(), mThemeDark);
+// // Prepare some palette to use.
+// int white = res.getColor(R.color.white);
+// int circleBackground = res.getColor(R.color.circle_background);
+// int line = res.getColor(R.color.line_background);
+// int timeDisplay = res.getColor(R.color.numbers_text_color);
+// ColorStateList doneTextColor = res.getColorStateList(R.color.done_text_color);
+// int doneBackground = R.drawable.done_background_color;
+//
+// int darkGray = res.getColor(R.color.dark_gray);
+// int lightGray = res.getColor(R.color.light_gray);
+// int darkLine = res.getColor(R.color.line_dark);
+// ColorStateList darkDoneTextColor = res.getColorStateList(R.color.done_text_color_dark);
+// int darkDoneBackground = R.drawable.done_background_color_dark;
// Set the palette for each view based on the theme.
- view.findViewById(R.id.time_display_background).setBackgroundColor(mThemeDark? darkGray : white);
- view.findViewById(R.id.time_display).setBackgroundColor(mThemeDark? darkGray : white);
- ((TextView) view.findViewById(R.id.separator)).setTextColor(mThemeDark? white : timeDisplay);
- ((TextView) view.findViewById(R.id.ampm_label)).setTextColor(mThemeDark? white : timeDisplay);
- view.findViewById(R.id.line).setBackgroundColor(mThemeDark? darkLine : line);
- mDoneButton.setTextColor(mThemeDark? darkDoneTextColor : doneTextColor);
- mTimePicker.setBackgroundColor(mThemeDark? lightGray : circleBackground);
- mDoneButton.setBackgroundResource(mThemeDark? darkDoneBackground : doneBackground);
+// view.findViewById(R.id.time_display_background).setBackgroundColor(mThemeDark? darkGray : white);
+// view.findViewById(R.id.time_display).setBackgroundColor(mThemeDark? darkGray : white);
+// ((TextView) view.findViewById(R.id.separator)).setTextColor(mThemeDark? white : timeDisplay);
+// ((TextView) view.findViewById(R.id.ampm_label)).setTextColor(mThemeDark? white : timeDisplay);
+// view.findViewById(R.id.line).setBackgroundColor(mThemeDark? darkLine : line);
+// mDoneButton.setTextColor(mThemeDark? darkDoneTextColor : doneTextColor);
+// mTimePicker.setBackgroundColor(mThemeDark? lightGray : circleBackground);
+// mDoneButton.setBackgroundResource(mThemeDark? darkDoneBackground : doneBackground);
return view;
}
@Override
- public void onResume() {
+ public void onResume()
+ {
super.onResume();
mHapticFeedbackController.start();
}
@Override
- public void onPause() {
+ public void onPause()
+ {
super.onPause();
mHapticFeedbackController.stop();
}
- public void tryVibrate() {
+ public void tryVibrate()
+ {
mHapticFeedbackController.tryVibrate();
}
- private void updateAmPmDisplay(int amOrPm) {
+ private void updateAmPmDisplay(int amOrPm)
+ {
if (amOrPm == AM) {
mAmPmTextView.setText(mAmText);
Utils.tryAccessibilityAnnounce(mTimePicker, mAmText);
mAmPmHitspace.setContentDescription(mAmText);
- } else if (amOrPm == PM){
+ } else if (amOrPm == PM) {
mAmPmTextView.setText(mPmText);
Utils.tryAccessibilityAnnounce(mTimePicker, mPmText);
mAmPmHitspace.setContentDescription(mPmText);
@@ -387,7 +426,8 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
}
@Override
- public void onSaveInstanceState(Bundle outState) {
+ public void onSaveInstanceState(Bundle outState)
+ {
if (mTimePicker != null) {
outState.putInt(KEY_HOUR_OF_DAY, mTimePicker.getHours());
outState.putInt(KEY_MINUTE, mTimePicker.getMinutes());
@@ -398,6 +438,7 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
outState.putIntegerArrayList(KEY_TYPED_TIMES, mTypedTimes);
}
outState.putBoolean(KEY_DARK_THEME, mThemeDark);
+ outState.putInt(KEY_SELECTED_COLOR, mSelectedColor);
}
}
@@ -405,7 +446,8 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
* Called by the picker for updating the header display.
*/
@Override
- public void onValueSelected(int pickerIndex, int newValue, boolean autoAdvance) {
+ public void onValueSelected(int pickerIndex, int newValue, boolean autoAdvance)
+ {
if (pickerIndex == HOUR_INDEX) {
setHour(newValue, false);
String announcement = String.format("%d", newValue);
@@ -417,7 +459,7 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
}
Utils.tryAccessibilityAnnounce(mTimePicker, announcement);
- } else if (pickerIndex == MINUTE_INDEX){
+ } else if (pickerIndex == MINUTE_INDEX) {
setMinute(newValue);
mTimePicker.setContentDescription(mMinutePickerDescription + ": " + newValue);
} else if (pickerIndex == AMPM_INDEX) {
@@ -430,7 +472,8 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
}
}
- private void setHour(int value, boolean announce) {
+ private void setHour(int value, boolean announce)
+ {
String format;
if (mIs24HourMode) {
format = "%02d";
@@ -450,7 +493,8 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
}
}
- private void setMinute(int value) {
+ private void setMinute(int value)
+ {
if (value == 60) {
value = 0;
}
@@ -462,7 +506,8 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
// Show either Hours or Minutes.
private void setCurrentItemShowing(int index, boolean animateCircle, boolean delayLabelAnimate,
- boolean announce) {
+ boolean announce)
+ {
mTimePicker.setCurrentItemShowing(index, animateCircle);
TextView labelToAnimate;
@@ -485,8 +530,8 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
labelToAnimate = mMinuteView;
}
- int hourColor = (index == HOUR_INDEX)? mSelectedColor : mUnselectedColor;
- int minuteColor = (index == MINUTE_INDEX)? mSelectedColor : mUnselectedColor;
+ int hourColor = (index == HOUR_INDEX) ? mSelectedColor : mUnselectedColor;
+ int minuteColor = (index == MINUTE_INDEX) ? mSelectedColor : mUnselectedColor;
mHourView.setTextColor(hourColor);
mMinuteView.setTextColor(minuteColor);
@@ -499,15 +544,17 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
/**
* For keyboard mode, processes key events.
+ *
* @param keyCode the pressed key.
* @return true if the key was successfully processed, false otherwise.
*/
- private boolean processKeyUp(int keyCode) {
+ private boolean processKeyUp(int keyCode)
+ {
if (keyCode == KeyEvent.KEYCODE_ESCAPE || keyCode == KeyEvent.KEYCODE_BACK) {
dismiss();
return true;
} else if (keyCode == KeyEvent.KEYCODE_TAB) {
- if(mInKbMode) {
+ if (mInKbMode) {
if (isTypedTimeFullyLegal()) {
finishKbMode(true);
}
@@ -522,7 +569,7 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
}
if (mCallback != null) {
mCallback.onTimeSet(mTimePicker,
- mTimePicker.getHours(), mTimePicker.getMinutes());
+ mTimePicker.getHours(), mTimePicker.getMinutes());
}
dismiss();
return true;
@@ -539,7 +586,7 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
deletedKeyStr = String.format("%d", getValFromKeyCode(deleted));
}
Utils.tryAccessibilityAnnounce(mTimePicker,
- String.format(mDeletedKeyFormat, deletedKeyStr));
+ String.format(mDeletedKeyFormat, deletedKeyStr));
updateDisplay(true);
}
}
@@ -549,7 +596,7 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
|| keyCode == KeyEvent.KEYCODE_6 || keyCode == KeyEvent.KEYCODE_7
|| keyCode == KeyEvent.KEYCODE_8 || keyCode == KeyEvent.KEYCODE_9
|| (!mIs24HourMode &&
- (keyCode == getAmOrPmKeyCode(AM) || keyCode == getAmOrPmKeyCode(PM)))) {
+ (keyCode == getAmOrPmKeyCode(AM) || keyCode == getAmOrPmKeyCode(PM)))) {
if (!mInKbMode) {
if (mTimePicker == null) {
// Something's wrong, because time picker should definitely not be null.
@@ -572,11 +619,13 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
/**
* Try to start keyboard mode with the specified key, as long as the timepicker is not in the
* middle of a touch-event.
+ *
* @param keyCode The key to use as the first press. Keyboard mode will not be started if the
- * key is not legal to start with. Or, pass in -1 to get into keyboard mode without a starting
- * key.
+ * key is not legal to start with. Or, pass in -1 to get into keyboard mode without a starting
+ * key.
*/
- private void tryStartingKbMode(int keyCode) {
+ private void tryStartingKbMode(int keyCode)
+ {
if (mTimePicker.trySettingInputEnabled(false) &&
(keyCode == -1 || addKeyIfLegal(keyCode))) {
mInKbMode = true;
@@ -585,7 +634,8 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
}
}
- private boolean addKeyIfLegal(int keyCode) {
+ private boolean addKeyIfLegal(int keyCode)
+ {
// If we're in 24hour mode, we'll need to check if the input is full. If in AM/PM mode,
// we'll need to see if AM/PM have been typed.
if ((mIs24HourMode && mTypedTimes.size() == 4) ||
@@ -617,7 +667,8 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
* Traverse the tree to see if the keys that have been typed so far are legal as is,
* or may become legal as more keys are typed (excluding backspace).
*/
- private boolean isTypedTimeLegalSoFar() {
+ private boolean isTypedTimeLegalSoFar()
+ {
Node node = mLegalTimesTree;
for (int keyCode : mTypedTimes) {
node = node.canReach(keyCode);
@@ -631,7 +682,8 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
/**
* Check if the time that has been typed so far is completely legal, as is.
*/
- private boolean isTypedTimeFullyLegal() {
+ private boolean isTypedTimeFullyLegal()
+ {
if (mIs24HourMode) {
// For 24-hour mode, the time is legal if the hours and minutes are each legal. Note:
// getEnteredTime() will ONLY call isTypedTimeFullyLegal() when NOT in 24hour mode.
@@ -645,7 +697,8 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
}
}
- private int deleteLastTypedKey() {
+ private int deleteLastTypedKey()
+ {
int deleted = mTypedTimes.remove(mTypedTimes.size() - 1);
if (!isTypedTimeFullyLegal()) {
mDoneButton.setEnabled(false);
@@ -655,9 +708,11 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
/**
* Get out of keyboard mode. If there is nothing in typedTimes, revert to TimePicker's time.
+ *
* @param changeDisplays If true, update the displays with the relevant time.
*/
- private void finishKbMode(boolean updateDisplays) {
+ private void finishKbMode(boolean updateDisplays)
+ {
mInKbMode = false;
if (!mTypedTimes.isEmpty()) {
int values[] = getEnteredTime(null);
@@ -677,29 +732,31 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
* Update the hours, minutes, and AM/PM displays with the typed times. If the typedTimes is
* empty, either show an empty display (filled with the placeholder text), or update from the
* timepicker's values.
+ *
* @param allowEmptyDisplay if true, then if the typedTimes is empty, use the placeholder text.
- * Otherwise, revert to the timepicker's values.
+ * Otherwise, revert to the timepicker's values.
*/
- private void updateDisplay(boolean allowEmptyDisplay) {
+ private void updateDisplay(boolean allowEmptyDisplay)
+ {
if (!allowEmptyDisplay && mTypedTimes.isEmpty()) {
int hour = mTimePicker.getHours();
int minute = mTimePicker.getMinutes();
setHour(hour, true);
setMinute(minute);
if (!mIs24HourMode) {
- updateAmPmDisplay(hour < 12? AM : PM);
+ updateAmPmDisplay(hour < 12 ? AM : PM);
}
setCurrentItemShowing(mTimePicker.getCurrentItemShowing(), true, true, true);
mDoneButton.setEnabled(true);
} else {
Boolean[] enteredZeros = {false, false};
int[] values = getEnteredTime(enteredZeros);
- String hourFormat = enteredZeros[0]? "%02d" : "%2d";
- String minuteFormat = (enteredZeros[1])? "%02d" : "%2d";
- String hourStr = (values[0] == -1)? mDoublePlaceholderText :
- String.format(hourFormat, values[0]).replace(' ', mPlaceholderText);
- String minuteStr = (values[1] == -1)? mDoublePlaceholderText :
- String.format(minuteFormat, values[1]).replace(' ', mPlaceholderText);
+ String hourFormat = enteredZeros[0] ? "%02d" : "%2d";
+ String minuteFormat = (enteredZeros[1]) ? "%02d" : "%2d";
+ String hourStr = (values[0] == -1) ? mDoublePlaceholderText :
+ String.format(hourFormat, values[0]).replace(' ', mPlaceholderText);
+ String minuteStr = (values[1] == -1) ? mDoublePlaceholderText :
+ String.format(minuteFormat, values[1]).replace(' ', mPlaceholderText);
mHourView.setText(hourStr);
mHourSpaceView.setText(hourStr);
mHourView.setTextColor(mUnselectedColor);
@@ -712,7 +769,8 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
}
}
- private static int getValFromKeyCode(int keyCode) {
+ private static int getValFromKeyCode(int keyCode)
+ {
switch (keyCode) {
case KeyEvent.KEYCODE_0:
return 0;
@@ -741,20 +799,22 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
/**
* Get the currently-entered time, as integer values of the hours and minutes typed.
+ *
* @param enteredZeros A size-2 boolean array, which the caller should initialize, and which
- * may then be used for the caller to know whether zeros had been explicitly entered as either
- * hours of minutes. This is helpful for deciding whether to show the dashes, or actual 0's.
+ * may then be used for the caller to know whether zeros had been explicitly entered as either
+ * hours of minutes. This is helpful for deciding whether to show the dashes, or actual 0's.
* @return A size-3 int array. The first value will be the hours, the second value will be the
* minutes, and the third will be either TimePickerDialog.AM or TimePickerDialog.PM.
*/
- private int[] getEnteredTime(Boolean[] enteredZeros) {
+ private int[] getEnteredTime(Boolean[] enteredZeros)
+ {
int amOrPm = -1;
int startIndex = 1;
if (!mIs24HourMode && isTypedTimeFullyLegal()) {
int keyCode = mTypedTimes.get(mTypedTimes.size() - 1);
if (keyCode == getAmOrPmKeyCode(AM)) {
amOrPm = AM;
- } else if (keyCode == getAmOrPmKeyCode(PM)){
+ } else if (keyCode == getAmOrPmKeyCode(PM)) {
amOrPm = PM;
}
startIndex = 2;
@@ -765,15 +825,15 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
int val = getValFromKeyCode(mTypedTimes.get(mTypedTimes.size() - i));
if (i == startIndex) {
minute = val;
- } else if (i == startIndex+1) {
- minute += 10*val;
+ } else if (i == startIndex + 1) {
+ minute += 10 * val;
if (enteredZeros != null && val == 0) {
enteredZeros[1] = true;
}
- } else if (i == startIndex+2) {
+ } else if (i == startIndex + 2) {
hour = val;
- } else if (i == startIndex+3) {
- hour += 10*val;
+ } else if (i == startIndex + 3) {
+ hour += 10 * val;
if (enteredZeros != null && val == 0) {
enteredZeros[0] = true;
}
@@ -787,7 +847,8 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
/**
* Get the keycode value for AM and PM in the current language.
*/
- private int getAmOrPmKeyCode(int amOrPm) {
+ private int getAmOrPmKeyCode(int amOrPm)
+ {
// Cache the codes.
if (mAmKeyCode == -1 || mPmKeyCode == -1) {
// Find the first character in the AM/PM text that is unique.
@@ -822,7 +883,8 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
/**
* Create a tree for deciding what keys can legally be typed.
*/
- private void generateLegalTimesTree() {
+ private void generateLegalTimesTree()
+ {
// Create a quick cache of numbers to their keycodes.
int k0 = KeyEvent.KEYCODE_0;
int k1 = KeyEvent.KEYCODE_1;
@@ -878,7 +940,7 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
// When the first digit is 2, the second digit may be 4-5.
secondDigit = new Node(k4, k5);
firstDigit.addChild(secondDigit);
- // We must now be followd by the last minute digit. E.g. 2:40, 2:53.
+ // We must now be followed by the last minute digit. E.g. 2:40, 2:53.
secondDigit.addChild(minuteSecondDigit);
// The first digit may be 3-9.
@@ -955,20 +1017,24 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
* mLegalKeys represents the keys that can be typed to get to the node.
* mChildren are the children that can be reached from this node.
*/
- private class Node {
+ private class Node
+ {
private int[] mLegalKeys;
private ArrayList mChildren;
- public Node(int... legalKeys) {
+ public Node(int... legalKeys)
+ {
mLegalKeys = legalKeys;
mChildren = new ArrayList();
}
- public void addChild(Node child) {
+ public void addChild(Node child)
+ {
mChildren.add(child);
}
- public boolean containsKey(int key) {
+ public boolean containsKey(int key)
+ {
for (int i = 0; i < mLegalKeys.length; i++) {
if (mLegalKeys[i] == key) {
return true;
@@ -977,7 +1043,8 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
return false;
}
- public Node canReach(int key) {
+ public Node canReach(int key)
+ {
if (mChildren == null) {
return null;
}
@@ -990,9 +1057,11 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
}
}
- private class KeyboardListener implements OnKeyListener {
+ private class KeyboardListener implements OnKeyListener
+ {
@Override
- public boolean onKey(View v, int keyCode, KeyEvent event) {
+ public boolean onKey(View v, int keyCode, KeyEvent event)
+ {
if (event.getAction() == KeyEvent.ACTION_UP) {
return processKeyUp(keyCode);
}
@@ -1000,14 +1069,16 @@ public class TimePickerDialog extends AppCompatDialogFragment implements OnValue
}
}
- public void setDismissListener( DialogInterface.OnDismissListener listener ) {
+ public void setDismissListener(DialogInterface.OnDismissListener listener)
+ {
dismissListener = listener;
}
@Override
- public void onDismiss(DialogInterface dialog) {
+ public void onDismiss(DialogInterface dialog)
+ {
super.onDismiss(dialog);
- if( dismissListener != null )
+ if (dismissListener != null)
dismissListener.onDismiss(dialog);
}
}
diff --git a/android/android-pickers/src/main/res/layout/time_picker_dialog.xml b/android/android-pickers/src/main/res/layout/time_picker_dialog.xml
index 44393dca6..cefb56099 100644
--- a/android/android-pickers/src/main/res/layout/time_picker_dialog.xml
+++ b/android/android-pickers/src/main/res/layout/time_picker_dialog.xml
@@ -49,33 +49,33 @@
android:layout_height="1dip"
android:background="@color/line_background" />
-
-
-
-
+
\ No newline at end of file
diff --git a/android/build.gradle b/android/build.gradle
index 4820f0f80..83c5ba2f7 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -1,24 +1,20 @@
buildscript {
repositories {
- maven { url 'https://maven.google.com' }
+ google()
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.0.0'
- classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
- classpath 'com.getkeepsafe.dexcount:dexcount-gradle-plugin:0.6.4'
- classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
- classpath 'org.jacoco:org.jacoco.core:+'
+ classpath "com.android.tools.build:gradle:$BUILD_TOOLS_VERSION"
+ classpath "com.neenbedankt.gradle.plugins:android-apt:1.8"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$KOTLIN_VERSION"
- classpath 'org.ajoberstar:grgit:1.5.0'
- classpath 'com.github.triplet.gradle:play-publisher:1.2.0'
+ classpath "org.ajoberstar:grgit:1.5.0"
}
}
allprojects {
repositories {
- maven { url 'https://maven.google.com' }
+ google()
maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
jcenter()
}
diff --git a/android/build.sh b/android/build.sh
index 877a35101..bb15f5541 100755
--- a/android/build.sh
+++ b/android/build.sh
@@ -15,304 +15,266 @@
# You should have received a copy of the GNU General Public License along
# with this program. If not, see .
+cd "$(dirname "$0")"
+
ADB="${ANDROID_HOME}/platform-tools/adb"
EMULATOR="${ANDROID_HOME}/tools/emulator"
GRADLE="./gradlew --stacktrace"
PACKAGE_NAME=org.isoron.uhabits
OUTPUTS_DIR=uhabits-android/build/outputs
+VERSION=$(cat gradle.properties | grep VERSION_NAME | sed -e 's/.*=//g;s/ //g')
if [ ! -f "${ANDROID_HOME}/platform-tools/adb" ]; then
- echo "Error: ANDROID_HOME is not set correctly"
- exit 1
+ echo "Error: ANDROID_HOME is not set correctly"
+ exit 1
fi
log_error() {
- if [ ! -z "$TEAMCITY_VERSION" ]; then
- echo "###teamcity[progressMessage '$1']"
- else
- local COLOR='\033[1;31m'
- local NC='\033[0m'
- echo -e "$COLOR>>> $1 $NC"
- fi
+ if [ ! -z "$TEAMCITY_VERSION" ]; then
+ echo "###teamcity[progressMessage '$1']"
+ else
+ local COLOR='\033[1;31m'
+ local NC='\033[0m'
+ echo -e "$COLOR>>> $1 $NC"
+ fi
}
log_info() {
- if [ ! -z "$TEAMCITY_VERSION" ]; then
- echo "###teamcity[progressMessage '$1']"
- else
- local COLOR='\033[1;32m'
- local NC='\033[0m'
- echo -e "$COLOR>>> $1 $NC"
- fi
+ if [ ! -z "$TEAMCITY_VERSION" ]; then
+ echo "###teamcity[progressMessage '$1']"
+ else
+ local COLOR='\033[1;32m'
+ local NC='\033[0m'
+ echo -e "$COLOR>>> $1 $NC"
+ fi
}
fail() {
- if [ ! -z ${AVD_NAME} ]; then
- stop_emulator
- stop_gradle_daemon
- fi
- log_error "BUILD FAILED"
- exit 1
-}
-
-start_emulator() {
- log_info "Starting emulator ($AVD_NAME)"
- $EMULATOR -avd ${AVD_NAME} -port ${AVD_SERIAL} -no-audio -no-window &
- $ADB wait-for-device shell 'while [[ -z $(getprop sys.boot_completed) ]]; do sleep 1; done; input keyevent 82'
+ log_error "BUILD FAILED"
+ exit 1
}
-stop_emulator() {
- log_info "Stopping emulator"
- $ADB emu kill
-}
+if [ ! -z $RELEASE ]; then
+ log_info "Reading secret env variables from ../.secret/env"
+ source ../.secret/env || fail
+fi
-stop_gradle_daemon() {
- log_info "Stopping gradle daemon"
- $GRADLE --stop
-}
run_adb_as_root() {
- log_info "Running adb as root"
- $ADB root
+ log_info "Running adb as root"
+ $ADB root
}
build_apk() {
- if [ ! -z $RELEASE ]; then
- if [ -z "$KEY_FILE" -o -z "$STORE_PASSWORD" -o -z "$KEY_ALIAS" -o -z "$KEY_PASSWORD" ]; then
- log_error "Environment variables KEY_FILE, KEY_ALIAS, KEY_PASSWORD and STORE_PASSWORD must be defined"
- exit 1
- fi
- log_info "Building release APK"
- ./gradlew assembleRelease \
- -Pandroid.injected.signing.store.file=$KEY_FILE \
- -Pandroid.injected.signing.store.password=$STORE_PASSWORD \
- -Pandroid.injected.signing.key.alias=$KEY_ALIAS \
- -Pandroid.injected.signing.key.password=$KEY_PASSWORD || fail
- else
- log_info "Building debug APK"
- ./gradlew assembleDebug || fail
- fi
+ log_info "Removing old APKs..."
+ rm -vf build/*.apk
+
+ if [ ! -z $RELEASE ]; then
+ log_info "Building release APK"
+ ./gradlew assembleRelease
+ cp -v uhabits-android/build/outputs/apk/release/uhabits-android-release.apk build/loop-$VERSION-release.apk
+ fi
+
+ log_info "Building debug APK"
+ ./gradlew assembleDebug --stacktrace || fail
+ cp -v uhabits-android/build/outputs/apk/debug/uhabits-android-debug.apk build/loop-$VERSION-debug.apk
}
build_instrumentation_apk() {
- log_info "Building instrumentation APK"
- if [ ! -z $RELEASE ]; then
- $GRADLE assembleAndroidTest \
- -Pandroid.injected.signing.store.file=$KEY_FILE \
- -Pandroid.injected.signing.store.password=$STORE_PASSWORD \
- -Pandroid.injected.signing.key.alias=$KEY_ALIAS \
- -Pandroid.injected.signing.key.password=$KEY_PASSWORD || fail
- else
- $GRADLE assembleAndroidTest || fail
- fi
-}
-
-clean_output_dir() {
- log_info "Cleaning output directory"
- rm -rf ${OUTPUTS_DIR}
- mkdir -p ${OUTPUTS_DIR}
+ log_info "Building instrumentation APK"
+ if [ ! -z $RELEASE ]; then
+ $GRADLE assembleAndroidTest \
+ -Pandroid.injected.signing.store.file=$LOOP_KEY_STORE \
+ -Pandroid.injected.signing.store.password=$LOOP_STORE_PASSWORD \
+ -Pandroid.injected.signing.key.alias=$LOOP_KEY_ALIAS \
+ -Pandroid.injected.signing.key.password=$LOOP_KEY_PASSWORD || fail
+ else
+ $GRADLE assembleAndroidTest || fail
+ fi
}
uninstall_apk() {
- log_info "Uninstalling existing APK"
- $ADB uninstall ${PACKAGE_NAME}
+ log_info "Uninstalling existing APK"
+ $ADB uninstall ${PACKAGE_NAME}
}
install_test_butler() {
- log_info "Installing Test Butler"
- $ADB uninstall com.linkedin.android.testbutler
- $ADB install tools/test-butler-app-1.3.1.apk
+ log_info "Installing Test Butler"
+ $ADB uninstall com.linkedin.android.testbutler
+ $ADB install tools/test-butler-app-2.0.2.apk
}
install_apk() {
- if [ ! -z $UNINSTALL_FIRST ]; then
- uninstall_apk
- fi
-
- log_info "Installing APK"
-
- if [ ! -z $RELEASE ]; then
- $ADB install -r ${OUTPUTS_DIR}/apk/release/uhabits-android-release.apk || fail
- else
- $ADB install -r ${OUTPUTS_DIR}/apk/debug/uhabits-android-debug.apk || fail
- fi
+ log_info "Installing APK"
+ if [ ! -z $RELEASE ]; then
+ $ADB install -r ${OUTPUTS_DIR}/apk/release/uhabits-android-release.apk || fail
+ else
+ $ADB install -t -r ${OUTPUTS_DIR}/apk/debug/uhabits-android-debug.apk || fail
+ fi
}
install_test_apk() {
- log_info "Uninstalling existing test APK"
- $ADB uninstall ${PACKAGE_NAME}.test
+ log_info "Uninstalling existing test APK"
+ $ADB uninstall ${PACKAGE_NAME}.test
- log_info "Installing test APK"
- $ADB install -r ${OUTPUTS_DIR}/apk/androidTest/debug/uhabits-android-debug-androidTest.apk || fail
+ log_info "Installing test APK"
+ $ADB install -r ${OUTPUTS_DIR}/apk/androidTest/debug/uhabits-android-debug-androidTest.apk || fail
}
run_instrumented_tests() {
- log_info "Running instrumented tests"
- $ADB shell am instrument \
- -r -e coverage true -e size medium \
- -w ${PACKAGE_NAME}.test/android.support.test.runner.AndroidJUnitRunner \
- | tee ${OUTPUTS_DIR}/instrument.txt
-
- mkdir -p ${OUTPUTS_DIR}/code-coverage/connected/
- $ADB pull /data/user/0/${PACKAGE_NAME}/files/coverage.ec \
- ${OUTPUTS_DIR}/code-coverage/connected/ \
- || log_error "COVERAGE REPORT NOT AVAILABLE"
+ SIZE=$1
+ log_info "Running instrumented tests"
+ $ADB shell am instrument \
+ -r -e coverage true -e size $SIZE \
+ -w ${PACKAGE_NAME}.test/androidx.test.runner.AndroidJUnitRunner \
+ | tee ${OUTPUTS_DIR}/instrument.txt
+
+ if grep "\(INSTRUMENTATION_STATUS_CODE.*-1\|FAILURES\)" $OUTPUTS_DIR/instrument.txt; then
+ log_error "Some instrumented tests failed"
+ fetch_images
+ fetch_logcat
+ exit 1
+ fi
+
+ #mkdir -p ${OUTPUTS_DIR}/code-coverage/connected/
+ #$ADB pull /data/user/0/${PACKAGE_NAME}/files/coverage.ec \
+ # ${OUTPUTS_DIR}/code-coverage/connected/ \
+ # || log_error "COVERAGE REPORT NOT AVAILABLE"
}
parse_instrumentation_results() {
- log_info "Parsing instrumented test results"
- java -jar tools/automator-log-converter-1.5.0.jar ${OUTPUTS_DIR}/instrument.txt || fail
+ log_info "Parsing instrumented test results"
+ java -jar tools/automator-log-converter-1.5.0.jar ${OUTPUTS_DIR}/instrument.txt || fail
}
generate_coverage_badge() {
- log_info "Generating code coverage badge"
- CORE_REPORT=uhabits-core/build/reports/jacoco/test/jacocoTestReport.xml
- rm -f ${OUTPUTS_DIR}/coverage-badge.svg
- python3 tools/coverage-badge/badge.py -i $CORE_REPORT -o ${OUTPUTS_DIR}/coverage-badge
-}
-
-fetch_artifacts() {
- log_info "Fetching generated artifacts"
- mkdir -p ${OUTPUTS_DIR}/failed
- $ADB pull /mnt/sdcard/test-screenshots/ ${OUTPUTS_DIR}/failed
- $ADB pull /storage/sdcard/test-screenshots/ ${OUTPUTS_DIR}/failed
- $ADB pull /sdcard/Android/data/${PACKAGE_NAME}/files/test-screenshots/ ${OUTPUTS_DIR}/failed
+ log_info "Generating code coverage badge"
+ CORE_REPORT=uhabits-core/build/reports/jacoco/test/jacocoTestReport.xml
+ rm -f ${OUTPUTS_DIR}/coverage-badge.svg
+ python3 tools/coverage-badge/badge.py -i $CORE_REPORT -o ${OUTPUTS_DIR}/coverage-badge
}
fetch_logcat() {
- log_info "Fetching logcat"
- $ADB logcat -d > ${OUTPUTS_DIR}/logcat.txt
+ log_info "Fetching logcat"
+ $ADB logcat -d > ${OUTPUTS_DIR}/logcat.txt
}
run_jvm_tests() {
- log_info "Running JVM tests"
- if [ ! -z $RELEASE ]; then
- $GRADLE testReleaseUnitTest :uhabits-core:check || fail
- else
- $GRADLE testDebugUnitTest :uhabits-core:check || fail
- fi
+ log_info "Running JVM tests"
+ if [ ! -z $RELEASE ]; then
+ $GRADLE testReleaseUnitTest :uhabits-core:check || fail
+ else
+ $GRADLE testDebugUnitTest :uhabits-core:check || fail
+ fi
}
uninstall_test_apk() {
- log_info "Uninstalling test APK"
- $ADB uninstall ${PACKAGE_NAME}.test
+ log_info "Uninstalling test APK"
+ $ADB uninstall ${PACKAGE_NAME}.test
}
fetch_images() {
- rm -rf tmp/test-screenshots > /dev/null
- mkdir -p tmp/
- $ADB pull /mnt/sdcard/test-screenshots/ tmp/
- $ADB pull /storage/sdcard/test-screenshots/ tmp/
- $ADB pull /sdcard/Android/data/${PACKAGE_NAME}/files/test-screenshots/ tmp/
-
- $ADB shell rm -r /mnt/sdcard/test-screenshots/
- $ADB shell rm -r /storage/sdcard/test-screenshots/
- $ADB shell rm -r /sdcard/Android/data/${PACKAGE_NAME}/files/test-screenshots/
+ log_info "Fetching images"
+ rm -rf $OUTPUTS_DIR/test-screenshots
+ $ADB pull /sdcard/Android/data/${PACKAGE_NAME}/files/test-screenshots/ $OUTPUTS_DIR
+ $ADB shell rm -r /sdcard/Android/data/${PACKAGE_NAME}/files/test-screenshots/
}
accept_images() {
- find tmp/test-screenshots -name '*.expected*' -delete
- rsync -av tmp/test-screenshots/ uhabits-android/src/androidTest/assets/
+ find $OUTPUTS_DIR/test-screenshots -name '*.expected*' -delete
+ rsync -av $OUTPUTS_DIR/test-screenshots/ uhabits-android/src/androidTest/assets/
}
-run_local_tests() {
- #clean_output_dir
- run_adb_as_root
- install_test_butler
- install_apk
- install_test_apk
- run_instrumented_tests
- parse_instrumentation_results
- fetch_artifacts
- fetch_logcat
- uninstall_test_apk
+run_tests() {
+ SIZE=$1
+ run_adb_as_root
+ install_test_butler
+ uninstall_apk
+ install_apk
+ install_test_apk
+ run_instrumented_tests $SIZE
+ parse_instrumentation_results
+ fetch_logcat
+ uninstall_test_apk
}
parse_opts() {
- OPTS=`getopt -o ur --long uninstall-first,release -n 'build.sh' -- "$@"`
- if [ $? != 0 ] ; then exit 1; fi
- eval set -- "$OPTS"
-
- while true; do
- case "$1" in
- -u | --uninstall-first ) UNINSTALL_FIRST=1; shift ;;
- -r | --release ) RELEASE=1; shift ;;
- * ) break ;;
- esac
- done
+ OPTS=`getopt -o r --long release -n 'build.sh' -- "$@"`
+ if [ $? != 0 ] ; then exit 1; fi
+ eval set -- "$OPTS"
+
+ while true; do
+ case "$1" in
+ -r | --release ) RELEASE=1; shift ;;
+ * ) break ;;
+ esac
+ done
+}
+
+remove_build_dir() {
+ rm -rfv .gradle
+ rm -rfv build
+ rm -rfv android-base/build
+ rm -rfv android-pickers/build
+ rm -rfv uhabits-android/build
+ rm -rfv uhabits-core/build
}
case "$1" in
- build)
- shift; parse_opts $*
-
- build_apk
- build_instrumentation_apk
- run_jvm_tests
- generate_coverage_badge
- ;;
-
- ci-tests)
- if [ -z $3 ]; then
- cat <<- END
- Usage: $0 ci-tests AVD_NAME AVD_SERIAL [options]
-
- Parameters:
- AVD_NAME name of the virtual android device to start
- AVD_SERIAL adb port to use (e.g. 5560)
-
- Options:
- -u --uninstall-first Uninstall existing APK first
- -r --release Build and install release version, instead of debug
- END
- exit 1
- fi
-
- shift; AVD_NAME=$1
- shift; AVD_SERIAL=$1
- shift; parse_opts $*
- ADB="${ADB} -s emulator-${AVD_SERIAL}"
-
- start_emulator
- run_local_tests
- stop_emulator
- stop_gradle_daemon
- ;;
-
- local-tests)
- shift; parse_opts $*
- run_local_tests
- ;;
-
- fetch-images)
- fetch_images
- ;;
-
- accept-images)
- accept_images
- ;;
-
- install)
- shift; parse_opts $*
- build_apk
- install_apk
- ;;
-
- *)
- cat <<- END
- Usage: $0 [options]
- Builds, installs and tests Loop Habit Tracker
-
- Commands:
- ci-tests Start emulator silently, run tests then kill emulator
- local-tests Run all tests on connected device
- install Install app on connected device
- fetch-images Fetches failed view test images from device
- accept-images Copies fetched images to corresponding assets folder
-
- Options:
- -u --uninstall-first Uninstall existing APK first
- -r --release Build and install release version, instead of debug
- END
- exit 1
+ build)
+ shift; parse_opts $*
+
+ build_apk
+ build_instrumentation_apk
+ run_jvm_tests
+ #generate_coverage_badge
+ ;;
+
+ medium-tests)
+ shift; parse_opts $*
+ run_tests medium
+ ;;
+
+ large-tests)
+ shift; parse_opts $*
+ run_tests large
+ ;;
+
+ fetch-images)
+ fetch_images
+ ;;
+
+ accept-images)
+ accept_images
+ ;;
+
+ install)
+ shift; parse_opts $*
+ build_apk
+ install_apk
+ ;;
+
+ clean)
+ remove_build_dir
+ ;;
+
+ *)
+cat < [options]
+Builds, installs and tests Loop Habit Tracker
+
+Commands:
+ accept-images Copies fetched images to corresponding assets folder
+ build Build APK and run JVM tests
+ clean Remove build directory
+ fetch-images Fetches failed view test images from device
+ install Install app on connected device
+ large-tests Run large-sized tests on connected device
+ medium-tests Run medium-sized tests on connected device
+
+Options:
+ -r --release Build and test release APK, instead of debug
+END
+ exit 1
+ ;;
esac
diff --git a/android/gradle.properties b/android/gradle.properties
index 00b5b964a..6e98ecfec 100644
--- a/android/gradle.properties
+++ b/android/gradle.properties
@@ -1,15 +1,18 @@
-VERSION_CODE = 35
-VERSION_NAME = 1.7.8
+VERSION_CODE = 51
+VERSION_NAME = 1.8.8
-MIN_SDK_VERSION = 19
-TARGET_SDK_VERSION = 27
-COMPILE_SDK_VERSION = 27
+MIN_SDK_VERSION = 21
+TARGET_SDK_VERSION = 29
+COMPILE_SDK_VERSION = 29
-DAGGER_VERSION = 2.9
-BUILD_TOOLS_VERSION = 27.0.3
-KOTLIN_VERSION = 1.2.41
-SUPPORT_LIBRARY_VERSION = 27.1.1
+DAGGER_VERSION = 2.25.4
+KOTLIN_VERSION = 1.3.61
+SUPPORT_LIBRARY_VERSION = 28.0.0
+AUTO_FACTORY_VERSION = 1.0-beta6
+BUILD_TOOLS_VERSION = 4.0.0
org.gradle.parallel=false
org.gradle.daemon=true
org.gradle.jvmargs=-Xms2048m -Xmx2048m -XX:MaxPermSize=2048m
+android.useAndroidX=true
+android.enableJetifier=true
diff --git a/android/gradle/wrapper/gradle-wrapper.jar b/android/gradle/wrapper/gradle-wrapper.jar
index 8c0fb64a8..cc4fdc293 100644
Binary files a/android/gradle/wrapper/gradle-wrapper.jar and b/android/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties
index 01c2ed76a..84337ad35 100644
--- a/android/gradle/wrapper/gradle-wrapper.properties
+++ b/android/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,5 @@
-#Sun Sep 24 06:01:27 CDT 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
diff --git a/android/gradlew b/android/gradlew
index 91a7e269e..2fe81a7d9 100755
--- a/android/gradlew
+++ b/android/gradlew
@@ -1,4 +1,20 @@
-#!/usr/bin/env bash
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
##############################################################################
##
@@ -6,20 +22,38 @@
##
##############################################################################
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS=""
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
-warn ( ) {
+warn () {
echo "$*"
}
-die ( ) {
+die () {
echo
echo "$*"
echo
@@ -30,6 +64,7 @@ die ( ) {
cygwin=false
msys=false
darwin=false
+nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
@@ -40,31 +75,11 @@ case "`uname`" in
MINGW* )
msys=true
;;
+ NONSTOP* )
+ nonstop=true
+ ;;
esac
-# For Cygwin, ensure paths are in UNIX format before anything is touched.
-if $cygwin ; then
- [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
-fi
-
-# Attempt to set APP_HOME
-# Resolve links: $0 may be a link
-PRG="$0"
-# Need this for relative symlinks.
-while [ -h "$PRG" ] ; do
- ls=`ls -ld "$PRG"`
- link=`expr "$ls" : '.*-> \(.*\)$'`
- if expr "$link" : '/.*' > /dev/null; then
- PRG="$link"
- else
- PRG=`dirname "$PRG"`"/$link"
- fi
-done
-SAVED="`pwd`"
-cd "`dirname \"$PRG\"`/" >&-
-APP_HOME="`pwd -P`"
-cd "$SAVED" >&-
-
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
@@ -90,7 +105,7 @@ location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
-if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
@@ -110,10 +125,11 @@ if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
-# For Cygwin, switch paths to Windows format before running java
-if $cygwin ; then
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
@@ -138,27 +154,30 @@ if $cygwin ; then
else
eval `echo args$i`="\"$arg\""
fi
- i=$((i+1))
+ i=`expr $i + 1`
done
case $i in
- (0) set -- ;;
- (1) set -- "$args0" ;;
- (2) set -- "$args0" "$args1" ;;
- (3) set -- "$args0" "$args1" "$args2" ;;
- (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
- (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
- (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
- (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
- (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
- (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
-# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
-function splitJvmOpts() {
- JVM_OPTS=("$@")
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
}
-eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
-JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
-exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
+exec "$JAVACMD" "$@"
diff --git a/android/gradlew.bat b/android/gradlew.bat
index aec99730b..24467a141 100644
--- a/android/gradlew.bat
+++ b/android/gradlew.bat
@@ -1,3 +1,19 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@@ -8,14 +24,14 @@
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS=
-
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
@@ -46,10 +62,9 @@ echo location of your Java installation.
goto fail
:init
-@rem Get command-line arguments, handling Windowz variants
+@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
-if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
@@ -60,11 +75,6 @@ set _SKIP=2
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
-goto execute
-
-:4NT_args
-@rem Get arguments from the 4NT Shell from JP Software
-set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
diff --git a/android/play b/android/play
new file mode 120000
index 000000000..1ecaa16de
--- /dev/null
+++ b/android/play
@@ -0,0 +1 @@
+uhabits-android/src/main/play/
\ No newline at end of file
diff --git a/android/tools/coverage-badge/badge.py b/android/tools/coverage-badge/badge.py
index 0e1ffe421..3e6bae8bb 100644
--- a/android/tools/coverage-badge/badge.py
+++ b/android/tools/coverage-badge/badge.py
@@ -55,7 +55,7 @@ def get_total(report):
def get_color(total):
"""
- Return color for current coverage precent
+ Return color for current coverage percent
"""
try:
xtotal = int(total)
diff --git a/android/tools/test-butler-app-1.3.1.apk b/android/tools/test-butler-app-1.3.1.apk
deleted file mode 100644
index c17cabad5..000000000
Binary files a/android/tools/test-butler-app-1.3.1.apk and /dev/null differ
diff --git a/android/tools/test-butler-app-2.0.2.apk b/android/tools/test-butler-app-2.0.2.apk
new file mode 100644
index 000000000..1f9055082
Binary files /dev/null and b/android/tools/test-butler-app-2.0.2.apk differ
diff --git a/android/uhabits-android/build.gradle b/android/uhabits-android/build.gradle
index 85df89fa2..45d2a0416 100644
--- a/android/uhabits-android/build.gradle
+++ b/android/uhabits-android/build.gradle
@@ -1,55 +1,38 @@
-apply plugin: 'idea'
-apply plugin: 'com.android.application'
-apply plugin: 'kotlin-android'
-apply plugin: 'kotlin-kapt'
-apply plugin: 'jacoco'
-apply plugin: 'com.github.triplet.play'
-import org.ajoberstar.grgit.Grgit
-
-ext {
- git = Grgit.open(currentDir: projectDir)
- GIT_COMMIT = git.head().id.substring(0, 8)
- GIT_BRANCH = git.branch.current.name
+plugins {
+ id 'idea'
+ id 'com.android.application'
+ id 'kotlin-android'
+ id 'kotlin-kapt'
+ id 'com.github.triplet.play' version '2.6.2'
+ id 'kotlin-android-extensions'
}
android {
compileSdkVersion COMPILE_SDK_VERSION as Integer
- buildToolsVersion BUILD_TOOLS_VERSION
- if(project.hasProperty("LOOP_STORE_FILE")) {
+ def secretPropsFile = file("../../.secret/gradle.properties")
+ if (secretPropsFile.exists()) {
+ def secrets = new Properties()
+ secretPropsFile.withInputStream { secrets.load(it) }
signingConfigs {
release {
- storeFile file(LOOP_STORE_FILE)
- storePassword LOOP_STORE_PASSWORD
- keyAlias LOOP_KEY_ALIAS
- keyPassword LOOP_KEY_PASSWORD
+ storeFile file(secrets.LOOP_KEY_STORE)
+ storePassword secrets.LOOP_STORE_PASSWORD
+ keyAlias secrets.LOOP_KEY_ALIAS
+ keyPassword secrets.LOOP_KEY_PASSWORD
}
}
buildTypes.release.signingConfig signingConfigs.release
}
- playAccountConfigs {
- defaultAccountConfig {
- jsonFile = file('../secret/playstore.json')
- }
- }
-
defaultConfig {
versionCode VERSION_CODE as Integer
- versionName "$VERSION_NAME ($GIT_BRANCH $GIT_COMMIT)"
+ versionName "$VERSION_NAME"
minSdkVersion MIN_SDK_VERSION as Integer
targetSdkVersion TARGET_SDK_VERSION as Integer
applicationId "org.isoron.uhabits"
- testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
-
- javaCompileOptions {
- annotationProcessorOptions {
- includeCompileClasspath false
- }
- }
-
- playAccountConfig = playAccountConfigs.defaultAccountConfig
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
@@ -66,6 +49,7 @@ android {
lintOptions {
checkReleaseBuilds false
abortOnError false
+ disable 'GoogleAppIndexingWarning'
}
compileOptions {
@@ -80,17 +64,16 @@ android {
outputs.upToDateWhen { false }
showStandardStreams = true
}
-
- jacoco {
- includeNoLocationClasses = true
- }
}
}
sourceSets {
main.assets.srcDirs += '../uhabits-core/src/main/resources/'
}
- buildToolsVersion '26.0.2'
+
+ buildFeatures {
+ viewBinding true
+ }
}
dependencies {
@@ -98,49 +81,52 @@ dependencies {
implementation project(":android-base")
implementation project(":android-pickers")
- implementation "com.android.support:appcompat-v7:$SUPPORT_LIBRARY_VERSION"
- implementation "com.android.support:design:$SUPPORT_LIBRARY_VERSION"
- implementation "com.android.support:preference-v14:$SUPPORT_LIBRARY_VERSION"
- implementation "com.android.support:support-v4:$SUPPORT_LIBRARY_VERSION"
+ implementation 'androidx.appcompat:appcompat:1.0.0'
+ implementation 'com.google.android.material:material:1.0.0'
+ implementation 'androidx.legacy:legacy-preference-v14:1.0.0'
+ implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation "com.github.paolorotolo:appintro:3.4.0"
implementation "com.google.dagger:dagger:$DAGGER_VERSION"
implementation "com.jakewharton:butterknife:8.6.1-SNAPSHOT"
implementation "org.apmem.tools:layouts:1.10"
- implementation "com.google.code.gson:gson:2.7"
+ implementation "com.google.code.gson:gson:2.8.5"
implementation "com.google.code.findbugs:jsr305:3.0.2"
- implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$KOTLIN_VERSION"
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$KOTLIN_VERSION"
+ implementation "androidx.constraintlayout:constraintlayout:2.0.0-beta4"
+ implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
compileOnly "javax.annotation:jsr250-api:1.0"
- compileOnly "com.google.auto.factory:auto-factory:1.0-beta3"
+ compileOnly "com.google.auto.factory:auto-factory:$AUTO_FACTORY_VERSION"
kapt "com.google.dagger:dagger-compiler:$DAGGER_VERSION"
- kapt "com.jakewharton:butterknife-compiler:8.6.1-SNAPSHOT"
- kapt "com.google.auto.factory:auto-factory:1.0-beta3"
+ kapt "com.jakewharton:butterknife-compiler:10.2.1"
+ annotationProcessor "com.google.auto.factory:auto-factory:$AUTO_FACTORY_VERSION"
- androidTestImplementation "com.android.support.test.espresso:espresso-contrib:2.2.2"
- androidTestImplementation "com.android.support.test.espresso:espresso-core:2.2.2"
- androidTestImplementation "com.android.support.test.uiautomator:uiautomator-v18:2.1.1"
+ androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.1.0'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0'
+ androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.2.0'
androidTestImplementation "com.google.dagger:dagger:$DAGGER_VERSION"
androidTestImplementation "com.linkedin.testbutler:test-butler-library:1.3.1"
- androidTestCompileOnly "com.google.auto.factory:auto-factory:1.0-beta3"
- androidTestImplementation "com.android.support:support-annotations:$SUPPORT_LIBRARY_VERSION"
- androidTestImplementation "com.android.support.test:rules:0.5"
- androidTestImplementation "com.android.support.test:runner:0.5"
- androidTestImplementation "com.google.guava:guava:20.0"
+ androidTestCompileOnly "com.google.auto.factory:auto-factory:$AUTO_FACTORY_VERSION"
+ androidTestAnnotationProcessor "com.google.auto.factory:auto-factory:$AUTO_FACTORY_VERSION"
+ androidTestImplementation 'androidx.annotation:annotation:1.0.0'
+ androidTestImplementation 'androidx.test:rules:1.1.1'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.1'
+ androidTestImplementation "com.google.guava:guava:24.1-android"
androidTestImplementation project(":uhabits-core")
kaptAndroidTest "com.google.dagger:dagger-compiler:$DAGGER_VERSION"
// mockito-android 2+ includes net.bytebuddy, which causes tests to fail.
// Excluding the package net.bytebuddy on AndroidManifest.xml breaks some
// AndroidJUnitRunner functionality, such as running individual methods.
- androidTestImplementation "org.mockito:mockito-core:1+"
- androidTestImplementation "com.google.dexmaker:dexmaker-mockito:+"
+ androidTestImplementation "org.mockito:mockito-core:1.10.19"
+ androidTestImplementation "com.google.dexmaker:dexmaker-mockito:1.2"
testImplementation "com.google.dagger:dagger:$DAGGER_VERSION"
testImplementation "org.mockito:mockito-core:2.8.9"
testImplementation "org.mockito:mockito-inline:2.8.9"
- testImplementation "junit:junit:4+"
+ testImplementation "junit:junit:4.12"
- implementation('com.opencsv:opencsv:3.9') {
+ implementation('com.opencsv:opencsv:3.10') {
exclude group: 'commons-logging', module: 'commons-logging'
}
implementation('io.socket:socket.io-client:0.8.3') {
@@ -148,45 +134,11 @@ dependencies {
}
}
-repositories {
- mavenCentral()
-}
-
kapt {
correctErrorTypes = true
}
-task coverageReport(type: JacocoReport) {
- def excludes = [
- '**/R.class',
- '**/R$*.class',
- '**/BuildConfig.*',
- '**/Manifest*',
- '**/*Test*.*',
- '**/*$Lambda$*',
- '**/*$ViewBinder*',
- '**/*MembersInjector*',
- '**/*_Provide*',
- '**/*Module_*',
- '**/com/android/**/*',
- '**/*Dagger*',
- '**/*_Factory*'
- ]
- def androidSrc = "${project.projectDir}/src/main/java"
- def androidClasses = "${buildDir}/intermediates/classes/debug"
- def jvmExecData = "${buildDir}/jacoco/testDebugUnitTest.exec"
- def connectedExecData = "${buildDir}/outputs/code-coverage/connected/coverage.ec"
- sourceDirectories = files(androidSrc)
- classDirectories = files(fileTree(dir: androidClasses, excludes: excludes))
- executionData = files(jvmExecData, connectedExecData)
- jacocoClasspath = configurations['androidJacocoAnt']
-
- reports {
- html.enabled = true
- xml.enabled = true
- }
-}
-
play {
- track = 'alpha'
+ serviceAccountCredentials = file("../../.secret/gcp-key.json")
+ track = "alpha"
}
diff --git a/android/uhabits-android/src/androidTest/assets/views-v26/common/HistoryChart/render.png b/android/uhabits-android/src/androidTest/assets/views-v26/common/HistoryChart/render.png
index 95c71e671..d7a66cc79 100644
Binary files a/android/uhabits-android/src/androidTest/assets/views-v26/common/HistoryChart/render.png and b/android/uhabits-android/src/androidTest/assets/views-v26/common/HistoryChart/render.png differ
diff --git a/android/uhabits-android/src/androidTest/assets/views-v26/common/HistoryChart/renderDataOffset.png b/android/uhabits-android/src/androidTest/assets/views-v26/common/HistoryChart/renderDataOffset.png
index 9fbec7ea3..b094d1e3d 100644
Binary files a/android/uhabits-android/src/androidTest/assets/views-v26/common/HistoryChart/renderDataOffset.png and b/android/uhabits-android/src/androidTest/assets/views-v26/common/HistoryChart/renderDataOffset.png differ
diff --git a/android/uhabits-android/src/androidTest/assets/views-v26/common/HistoryChart/renderDifferentSize.png b/android/uhabits-android/src/androidTest/assets/views-v26/common/HistoryChart/renderDifferentSize.png
index f2a712b89..4507f30ba 100644
Binary files a/android/uhabits-android/src/androidTest/assets/views-v26/common/HistoryChart/renderDifferentSize.png and b/android/uhabits-android/src/androidTest/assets/views-v26/common/HistoryChart/renderDifferentSize.png differ
diff --git a/android/uhabits-android/src/androidTest/assets/views-v26/common/HistoryChart/renderTransparent.png b/android/uhabits-android/src/androidTest/assets/views-v26/common/HistoryChart/renderTransparent.png
index 860b326d4..5d997f32c 100644
Binary files a/android/uhabits-android/src/androidTest/assets/views-v26/common/HistoryChart/renderTransparent.png and b/android/uhabits-android/src/androidTest/assets/views-v26/common/HistoryChart/renderTransparent.png differ
diff --git a/android/uhabits-android/src/androidTest/assets/views-v26/common/ScoreChart/render.png b/android/uhabits-android/src/androidTest/assets/views-v26/common/ScoreChart/render.png
index 4ea430a67..c80bd6b5e 100644
Binary files a/android/uhabits-android/src/androidTest/assets/views-v26/common/ScoreChart/render.png and b/android/uhabits-android/src/androidTest/assets/views-v26/common/ScoreChart/render.png differ
diff --git a/android/uhabits-android/src/androidTest/assets/views-v26/common/ScoreChart/renderDataOffset.png b/android/uhabits-android/src/androidTest/assets/views-v26/common/ScoreChart/renderDataOffset.png
index e0bfe6fd3..3575b52c1 100644
Binary files a/android/uhabits-android/src/androidTest/assets/views-v26/common/ScoreChart/renderDataOffset.png and b/android/uhabits-android/src/androidTest/assets/views-v26/common/ScoreChart/renderDataOffset.png differ
diff --git a/android/uhabits-android/src/androidTest/assets/views-v26/common/ScoreChart/renderDifferentSize.png b/android/uhabits-android/src/androidTest/assets/views-v26/common/ScoreChart/renderDifferentSize.png
index b550f9814..be33abc00 100644
Binary files a/android/uhabits-android/src/androidTest/assets/views-v26/common/ScoreChart/renderDifferentSize.png and b/android/uhabits-android/src/androidTest/assets/views-v26/common/ScoreChart/renderDifferentSize.png differ
diff --git a/android/uhabits-android/src/androidTest/assets/views-v26/common/ScoreChart/renderMonthly.png b/android/uhabits-android/src/androidTest/assets/views-v26/common/ScoreChart/renderMonthly.png
index 11ec0904b..85d4cc2ff 100644
Binary files a/android/uhabits-android/src/androidTest/assets/views-v26/common/ScoreChart/renderMonthly.png and b/android/uhabits-android/src/androidTest/assets/views-v26/common/ScoreChart/renderMonthly.png differ
diff --git a/android/uhabits-android/src/androidTest/assets/views-v26/common/ScoreChart/renderTransparent.png b/android/uhabits-android/src/androidTest/assets/views-v26/common/ScoreChart/renderTransparent.png
index e34f4d671..ffe41bb0a 100644
Binary files a/android/uhabits-android/src/androidTest/assets/views-v26/common/ScoreChart/renderTransparent.png and b/android/uhabits-android/src/androidTest/assets/views-v26/common/ScoreChart/renderTransparent.png differ
diff --git a/android/uhabits-android/src/androidTest/assets/views-v26/common/ScoreChart/renderYearly.png b/android/uhabits-android/src/androidTest/assets/views-v26/common/ScoreChart/renderYearly.png
index df13dae29..301d43f0a 100644
Binary files a/android/uhabits-android/src/androidTest/assets/views-v26/common/ScoreChart/renderYearly.png and b/android/uhabits-android/src/androidTest/assets/views-v26/common/ScoreChart/renderYearly.png differ
diff --git a/android/uhabits-android/src/androidTest/assets/views-v26/common/StreakChart/render.png b/android/uhabits-android/src/androidTest/assets/views-v26/common/StreakChart/render.png
index 9eb67f547..657d433fa 100644
Binary files a/android/uhabits-android/src/androidTest/assets/views-v26/common/StreakChart/render.png and b/android/uhabits-android/src/androidTest/assets/views-v26/common/StreakChart/render.png differ
diff --git a/android/uhabits-android/src/androidTest/assets/views-v26/common/StreakChart/renderSmallSize.png b/android/uhabits-android/src/androidTest/assets/views-v26/common/StreakChart/renderSmallSize.png
index 6701961bc..4014e47cf 100644
Binary files a/android/uhabits-android/src/androidTest/assets/views-v26/common/StreakChart/renderSmallSize.png and b/android/uhabits-android/src/androidTest/assets/views-v26/common/StreakChart/renderSmallSize.png differ
diff --git a/android/uhabits-android/src/androidTest/assets/views-v26/common/StreakChart/renderTransparent.png b/android/uhabits-android/src/androidTest/assets/views-v26/common/StreakChart/renderTransparent.png
index 9eb67f547..657d433fa 100644
Binary files a/android/uhabits-android/src/androidTest/assets/views-v26/common/StreakChart/renderTransparent.png and b/android/uhabits-android/src/androidTest/assets/views-v26/common/StreakChart/renderTransparent.png differ
diff --git a/android/uhabits-android/src/androidTest/assets/views-v26/habits/show/HistoryCard/render.png b/android/uhabits-android/src/androidTest/assets/views-v26/habits/show/HistoryCard/render.png
index 8c0cbc9d1..295b4e1be 100644
Binary files a/android/uhabits-android/src/androidTest/assets/views-v26/habits/show/HistoryCard/render.png and b/android/uhabits-android/src/androidTest/assets/views-v26/habits/show/HistoryCard/render.png differ
diff --git a/android/uhabits-android/src/androidTest/assets/views-v26/habits/show/NotesCard/render-empty-description.png b/android/uhabits-android/src/androidTest/assets/views-v26/habits/show/NotesCard/render-empty-description.png
new file mode 100644
index 000000000..f9b378f6c
Binary files /dev/null and b/android/uhabits-android/src/androidTest/assets/views-v26/habits/show/NotesCard/render-empty-description.png differ
diff --git a/android/uhabits-android/src/androidTest/assets/views-v26/habits/show/NotesCard/render.png b/android/uhabits-android/src/androidTest/assets/views-v26/habits/show/NotesCard/render.png
new file mode 100644
index 000000000..dafb53f4f
Binary files /dev/null and b/android/uhabits-android/src/androidTest/assets/views-v26/habits/show/NotesCard/render.png differ
diff --git a/android/uhabits-android/src/androidTest/assets/views-v26/habits/show/ScoreCard/render.png b/android/uhabits-android/src/androidTest/assets/views-v26/habits/show/ScoreCard/render.png
index 12aaa2785..9a7eacb9c 100644
Binary files a/android/uhabits-android/src/androidTest/assets/views-v26/habits/show/ScoreCard/render.png and b/android/uhabits-android/src/androidTest/assets/views-v26/habits/show/ScoreCard/render.png differ
diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/BaseAndroidTest.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/BaseAndroidTest.java
index 5aede98f5..f7b351e36 100644
--- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/BaseAndroidTest.java
+++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/BaseAndroidTest.java
@@ -23,11 +23,15 @@ import android.appwidget.*;
import android.content.*;
import android.content.res.*;
import android.os.*;
-import android.support.annotation.*;
-import android.support.test.*;
-import android.support.test.filters.*;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.StyleRes;
+import androidx.test.*;
+import androidx.test.filters.*;
import android.util.*;
+import androidx.test.platform.app.InstrumentationRegistry;
+
import junit.framework.*;
import org.isoron.androidbase.*;
@@ -62,8 +66,6 @@ public class BaseAndroidTest extends TestCase
protected TaskRunner taskRunner;
- protected HabitLogger logger;
-
protected HabitFixtures fixtures;
protected CountDownLatch latch;
@@ -82,8 +84,8 @@ public class BaseAndroidTest extends TestCase
{
if (Looper.myLooper() == null) Looper.prepare();
- targetContext = InstrumentationRegistry.getTargetContext();
- testContext = InstrumentationRegistry.getContext();
+ targetContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ testContext = InstrumentationRegistry.getInstrumentation().getContext();
DateUtils.setFixedLocalTime(FIXED_LOCAL_TIME);
setResolution(2.0f);
@@ -101,7 +103,6 @@ public class BaseAndroidTest extends TestCase
prefs = appComponent.getPreferences();
habitList = appComponent.getHabitList();
taskRunner = appComponent.getTaskRunner();
- logger = appComponent.getHabitsLogger();
modelFactory = appComponent.getModelFactory();
prefs.clear();
diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/BaseUserInterfaceTest.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/BaseUserInterfaceTest.java
index e2dfb6b1f..6b5a1931c 100644
--- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/BaseUserInterfaceTest.java
+++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/BaseUserInterfaceTest.java
@@ -20,7 +20,8 @@
package org.isoron.uhabits;
import android.content.*;
-import android.support.test.uiautomator.*;
+
+import androidx.test.uiautomator.*;
import com.linkedin.android.testbutler.*;
@@ -30,12 +31,15 @@ import org.isoron.uhabits.core.ui.screens.habits.list.*;
import org.isoron.uhabits.core.utils.*;
import org.junit.*;
-import static android.support.test.InstrumentationRegistry.*;
-import static android.support.test.uiautomator.UiDevice.*;
+import static androidx.test.InstrumentationRegistry.getContext;
+import static androidx.test.InstrumentationRegistry.getTargetContext;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import static androidx.test.uiautomator.UiDevice.*;
public class BaseUserInterfaceTest
{
private static final String PKG = "org.isoron.uhabits";
+ public static final String EMPTY_DESCRIPTION_HABIT_NAME = "Read books";
public static UiDevice device;
@@ -54,6 +58,7 @@ public class BaseUserInterfaceTest
Intent intent = new Intent();
intent.setComponent(new ComponentName(PKG, cls.getCanonicalName()));
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
getContext().startActivity(intent);
}
@@ -77,8 +82,7 @@ public class BaseUserInterfaceTest
@After
public void tearDown() throws Exception
{
- device.pressHome();
- device.waitForIdle();
+ for (int i = 0; i < 10; i++) device.pressBack();
TestButler.teardown(getTargetContext());
}
@@ -93,26 +97,36 @@ public class BaseUserInterfaceTest
Habit h1 = fixtures.createEmptyHabit();
h1.setName("Wake up early");
- h1.setDescription("Did you wake up early today?");
+ h1.setQuestion("Did you wake up early today?");
+ h1.setDescription("test description 1");
h1.setColor(5);
habitList.update(h1);
Habit h2 = fixtures.createShortHabit();
h2.setName("Track time");
- h2.setDescription("Did you track your time?");
+ h2.setQuestion("Did you track your time?");
+ h2.setDescription("test description 2");
h2.setColor(5);
habitList.update(h2);
Habit h3 = fixtures.createLongHabit();
h3.setName("Meditate");
- h3.setDescription("Did meditate today?");
+ h3.setQuestion("Did meditate today?");
+ h3.setDescription("test description 3");
h3.setColor(10);
habitList.update(h3);
Habit h4 = fixtures.createEmptyHabit();
- h4.setName("Read books");
- h4.setDescription("Did you read books today?");
+ h4.setName(EMPTY_DESCRIPTION_HABIT_NAME);
+ h4.setQuestion("Did you read books today?");
+ h4.setDescription("");
h4.setColor(2);
habitList.update(h4);
}
+
+ protected void rotateDevice() throws Exception
+ {
+ device.setOrientationLeft();
+ device.setOrientationNatural();
+ }
}
diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/BaseViewTest.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/BaseViewTest.java
index 62f4841d4..fcd6ff9f6 100644
--- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/BaseViewTest.java
+++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/BaseViewTest.java
@@ -20,11 +20,14 @@
package org.isoron.uhabits;
import android.graphics.*;
-import android.support.annotation.*;
-import android.support.test.*;
+
+import androidx.annotation.NonNull;
+import androidx.test.*;
import android.view.*;
import android.widget.*;
+import androidx.test.platform.app.InstrumentationRegistry;
+
import org.isoron.androidbase.*;
import org.isoron.androidbase.utils.*;
import org.isoron.uhabits.widgets.*;
diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/HabitFixtures.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/HabitFixtures.java
index c636f682b..364166de7 100644
--- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/HabitFixtures.java
+++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/HabitFixtures.java
@@ -52,7 +52,8 @@ public class HabitFixtures
{
Habit habit = modelFactory.buildHabit();
habit.setName("Meditate");
- habit.setDescription("Did you meditate this morning?");
+ habit.setQuestion("Did you meditate this morning?");
+ habit.setDescription("This is a test description");
habit.setColor(5);
habit.setFrequency(Frequency.DAILY);
habit.setId(id);
@@ -81,7 +82,7 @@ public class HabitFixtures
{
Habit habit = modelFactory.buildHabit();
habit.setName("Take a walk");
- habit.setDescription("How many steps did you walk today?");
+ habit.setQuestion("How many steps did you walk today?");
habit.setType(Habit.NUMBER_HABIT);
habit.setTargetType(Habit.AT_LEAST);
habit.setTargetValue(200.0);
@@ -103,7 +104,7 @@ public class HabitFixtures
{
Habit habit = modelFactory.buildHabit();
habit.setName("Wake up early");
- habit.setDescription("Did you wake up before 6am?");
+ habit.setQuestion("Did you wake up before 6am?");
habit.setFrequency(new Frequency(2, 3));
habitList.add(habit);
diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/HabitLoggerTest.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/HabitLoggerTest.java
deleted file mode 100644
index ddf71ab1e..000000000
--- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/HabitLoggerTest.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2016 Álinson Santos Xavier
- *
- * This file is part of Loop Habit Tracker.
- *
- * Loop Habit Tracker is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or (at your
- * option) any later version.
- *
- * Loop Habit Tracker is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see .
- */
-
-package org.isoron.uhabits;
-
-import android.os.*;
-import android.support.test.runner.*;
-import android.test.suitebuilder.annotation.*;
-
-import org.isoron.androidbase.*;
-import org.isoron.uhabits.core.models.*;
-import org.junit.*;
-import org.junit.runner.*;
-
-import java.io.*;
-
-import static org.hamcrest.MatcherAssert.*;
-import static org.hamcrest.CoreMatchers.*;
-
-@RunWith(AndroidJUnit4.class)
-@MediumTest
-public class HabitLoggerTest extends BaseAndroidTest
-{
- @Test
- public void testLogReminderScheduled() throws IOException
- {
- if (!isLogcatAvailable()) return;
-
- long time = 1422277200000L; // 13:00 jan 26, 2015 (UTC)
- Habit habit = fixtures.createEmptyHabit();
- habit.setName("Write journal");
-
- logger.logReminderScheduled(habit, time);
-
- String expectedMsg = "Setting alarm (2015-01-26 130000): Wri\n";
- assertLogcatContains(expectedMsg);
- }
-
- protected void assertLogcatContains(String expectedMsg) throws IOException
- {
- String logcat = new AndroidBugReporter(targetContext).getLogcat();
- assertThat(logcat, containsString(expectedMsg));
- }
-
- protected boolean isLogcatAvailable()
- {
- return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
- }
-}
diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/HabitsActivityTestComponent.kt b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/HabitsActivityTestComponent.kt
index dde1b5a90..508077ac7 100644
--- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/HabitsActivityTestComponent.kt
+++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/HabitsActivityTestComponent.kt
@@ -37,7 +37,6 @@ class TestModule {
@ActivityScope
@Component(modules = arrayOf(
ActivityContextModule::class,
- AboutModule::class,
HabitsActivityModule::class,
ListHabitsModule::class,
ShowHabitModule::class,
diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/HabitsApplicationTest.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/HabitsApplicationTest.java
index cb6a83b6c..222d7e93f 100644
--- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/HabitsApplicationTest.java
+++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/HabitsApplicationTest.java
@@ -19,8 +19,10 @@
package org.isoron.uhabits;
-import android.support.test.runner.*;
-import android.test.suitebuilder.annotation.*;
+import androidx.test.filters.*;
+import androidx.test.runner.*;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.isoron.androidbase.*;
import org.junit.*;
diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/AboutTest.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/AboutTest.java
index 9ae821e39..6ee63aba6 100644
--- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/AboutTest.java
+++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/AboutTest.java
@@ -19,8 +19,10 @@
package org.isoron.uhabits.acceptance;
-import android.support.test.filters.*;
-import android.support.test.runner.*;
+import androidx.test.filters.*;
+import androidx.test.runner.*;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.isoron.uhabits.*;
import org.junit.*;
@@ -35,15 +37,17 @@ import static org.isoron.uhabits.acceptance.steps.ListHabitsSteps.*;
public class AboutTest extends BaseUserInterfaceTest
{
@Test
- public void shouldDisplayAboutScreen()
- {
+ public void shouldDisplayAboutScreen() {
launchApp();
clickMenu(ABOUT);
verifyDisplaysText("Loop Habit Tracker");
verifyDisplaysText("Rate this app on Google Play");
verifyDisplaysText("Developers");
verifyDisplaysText("Translators");
+ }
+ @Test
+ public void shouldDisplayAboutScreenFromSettings() {
launchApp();
clickMenu(SETTINGS);
clickText("About");
diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/HabitsTest.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/HabitsTest.java
index 035e0c150..d8950ef04 100644
--- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/HabitsTest.java
+++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/HabitsTest.java
@@ -19,8 +19,9 @@
package org.isoron.uhabits.acceptance;
-import android.support.test.filters.*;
-import android.support.test.runner.*;
+import androidx.test.filters.*;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.isoron.uhabits.*;
import org.junit.*;
@@ -37,22 +38,36 @@ import static org.isoron.uhabits.acceptance.steps.ListHabitsSteps.*;
public class HabitsTest extends BaseUserInterfaceTest
{
@Test
- public void shouldCreateHabit() throws Exception
+ public void shouldCreateHabit() throws Exception {
+ shouldCreateHabit("this is a test description");
+ }
+
+ @Test
+ public void shouldCreateHabitBlankDescription() throws Exception {
+ shouldCreateHabit("");
+ }
+
+ private void shouldCreateHabit(String description) throws Exception
{
launchApp();
verifyShowsScreen(LIST_HABITS);
clickMenu(ADD);
+ verifyShowsScreen(SELECT_HABIT_TYPE);
+ clickText("Yes or No");
+
verifyShowsScreen(EDIT_HABIT);
- typeName("Hello world");
+ String testName = "Hello world";
+ typeName(testName);
typeQuestion("Did you say hello to the world today?");
- pickFrequency("Every week");
+ typeDescription(description);
+ pickFrequency();
pickColor(5);
clickSave();
verifyShowsScreen(LIST_HABITS);
- verifyDisplaysText("Hello world");
+ verifyDisplaysText(testName);
}
@Test
@@ -79,7 +94,16 @@ public class HabitsTest extends BaseUserInterfaceTest
}
@Test
- public void shouldEditHabit() throws Exception
+ public void shouldEditHabit() throws Exception {
+ shouldEditHabit("this is a test description");
+ }
+
+ @Test
+ public void shouldEditHabitBlankDescription() throws Exception {
+ shouldEditHabit("");
+ }
+
+ private void shouldEditHabit(String description) throws Exception
{
launchApp();
@@ -90,6 +114,7 @@ public class HabitsTest extends BaseUserInterfaceTest
verifyShowsScreen(EDIT_HABIT);
typeName("Take a walk");
typeQuestion("Did you take a walk today?");
+ typeDescription(description);
clickSave();
verifyShowsScreen(LIST_HABITS);
@@ -172,4 +197,12 @@ public class HabitsTest extends BaseUserInterfaceTest
verifyDisplaysText("Track time");
verifyDisplaysText("Wake up early");
}
+
+ @Test
+ public void shouldHideNotesCard() throws Exception
+ {
+ launchApp();
+ clickText(EMPTY_DESCRIPTION_HABIT_NAME);
+ verifyShowsScreen(SHOW_HABIT, false);
+ }
}
diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/LinksTest.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/LinksTest.java
index ef5166838..e229b34e2 100644
--- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/LinksTest.java
+++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/LinksTest.java
@@ -19,8 +19,10 @@
package org.isoron.uhabits.acceptance;
-import android.support.test.filters.*;
-import android.support.test.runner.*;
+import androidx.test.filters.*;
+import androidx.test.runner.*;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.isoron.uhabits.*;
import org.junit.*;
@@ -43,22 +45,24 @@ public class LinksTest extends BaseUserInterfaceTest
verifyOpensWebsite("https://github.com/iSoron/uhabits");
}
- @Test
- public void shouldLinkToTranslationWebsite() throws Exception
- {
- launchApp();
- clickMenu(ABOUT);
- clickText("Help translate this app");
- verifyOpensWebsite("translate.loophabits.org");
- }
+// @Test
+// public void shouldLinkToTranslationWebsite() throws Exception
+// {
+// launchApp();
+// clickMenu(ABOUT);
+// clickText("Help translate this app");
+// verifyOpensWebsite("translate.loophabits.org");
+// }
@Test
- public void shouldLinkToHelp() throws Exception
- {
+ public void shouldLinkToHelp() throws Exception {
launchApp();
clickMenu(HELP);
verifyOpensWebsite("loophabits.org/faq.html");
+ }
+ @Test
+ public void shouldLinkToHelpFromSettings() throws Exception {
launchApp();
clickMenu(SETTINGS);
clickText("Help & FAQ");
diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/WidgetTest.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/WidgetTest.java
index 3035dfd4f..2c0f89527 100644
--- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/WidgetTest.java
+++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/WidgetTest.java
@@ -19,6 +19,8 @@
package org.isoron.uhabits.acceptance;
+import androidx.test.filters.*;
+
import org.isoron.uhabits.*;
import org.junit.*;
@@ -26,15 +28,13 @@ import static org.isoron.uhabits.acceptance.steps.CommonSteps.*;
import static org.isoron.uhabits.acceptance.steps.WidgetSteps.*;
import static org.isoron.uhabits.acceptance.steps.WidgetSteps.clickText;
+@LargeTest
public class WidgetTest extends BaseUserInterfaceTest
{
@Test
public void shouldCreateAndToggleCheckmarkWidget() throws Exception
{
- longPressHomeScreen();
- clickWidgets();
- scrollToHabits();
- dragWidgetToHomescreen();
+ dragCheckmarkWidgetToHomeScreen();
clickText("Wake up early");
verifyCheckmarkWidgetIsShown();
clickCheckmarkWidget();
diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/steps/CommonSteps.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/steps/CommonSteps.java
index 14b16144b..28e09a1b3 100644
--- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/steps/CommonSteps.java
+++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/steps/CommonSteps.java
@@ -19,21 +19,26 @@
package org.isoron.uhabits.acceptance.steps;
-import android.support.annotation.*;
-import android.support.test.espresso.*;
-import android.support.test.espresso.contrib.*;
-import android.support.test.uiautomator.*;
-import android.support.v7.widget.*;
+import android.view.View;
+import androidx.annotation.StringRes;
+import androidx.test.espresso.*;
+import androidx.test.espresso.contrib.*;
+import androidx.test.uiautomator.*;
+
+import androidx.recyclerview.widget.RecyclerView;
+
+import org.hamcrest.Matcher;
import org.isoron.uhabits.*;
import org.isoron.uhabits.R;
import org.isoron.uhabits.activities.habits.list.*;
-import static android.support.test.espresso.Espresso.*;
-import static android.support.test.espresso.action.ViewActions.*;
-import static android.support.test.espresso.assertion.PositionAssertions.*;
-import static android.support.test.espresso.assertion.ViewAssertions.*;
-import static android.support.test.espresso.matcher.ViewMatchers.*;
+import static android.os.Build.VERSION.*;
+import static androidx.test.espresso.Espresso.*;
+import static androidx.test.espresso.action.ViewActions.*;
+import static androidx.test.espresso.assertion.PositionAssertions.*;
+import static androidx.test.espresso.assertion.ViewAssertions.*;
+import static androidx.test.espresso.matcher.ViewMatchers.*;
import static junit.framework.Assert.*;
import static org.hamcrest.CoreMatchers.*;
@@ -136,18 +141,25 @@ public class CommonSteps extends BaseUserInterfaceTest
public static void verifyOpensWebsite(String url) throws Exception
{
- assertTrue(
- device.wait(Until.hasObject(By.pkg("com.android.chrome")), 5000));
+ String browser_pkg = "org.chromium.webview_shell";
+ if(SDK_INT <= 23) {
+ browser_pkg = "com.android.browser";
+ }
+ assertTrue(device.wait(Until.hasObject(By.pkg(browser_pkg)), 5000));
device.waitForIdle();
- assertTrue(device.findObject(new UiSelector().text(url)).exists());
+ assertTrue(device.findObject(new UiSelector().textContains(url)).exists());
}
public enum Screen
{
- LIST_HABITS, SHOW_HABIT, EDIT_HABIT
+ LIST_HABITS, SHOW_HABIT, EDIT_HABIT, SELECT_HABIT_TYPE
}
- public static void verifyShowsScreen(Screen screen)
+ public static void verifyShowsScreen(Screen screen) {
+ verifyShowsScreen(screen, true);
+ }
+
+ public static void verifyShowsScreen(Screen screen, boolean notesCardVisibleExpected)
{
switch(screen)
{
@@ -157,12 +169,22 @@ public class CommonSteps extends BaseUserInterfaceTest
break;
case SHOW_HABIT:
+ Matcher noteCardViewMatcher = notesCardVisibleExpected ? isDisplayed() :
+ withEffectiveVisibility(Visibility.GONE);
onView(withId(R.id.subtitleCard)).check(matches(isDisplayed()));
+ onView(withId(R.id.notesCard)).check(matches(noteCardViewMatcher));
break;
case EDIT_HABIT:
- onView(withId(R.id.tvDescription)).check(matches(isDisplayed()));
+ onView(withId(R.id.questionInput)).check(matches(isDisplayed()));
+ break;
+
+ case SELECT_HABIT_TYPE:
+ onView(withText(R.string.yes_or_no_example)).check(matches(isDisplayed()));
break;
+
+ default:
+ throw new IllegalStateException();
}
}
}
diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/steps/EditHabitSteps.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/steps/EditHabitSteps.java
index 467c33959..af17ecd00 100644
--- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/steps/EditHabitSteps.java
+++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/steps/EditHabitSteps.java
@@ -19,14 +19,14 @@
package org.isoron.uhabits.acceptance.steps;
-import android.support.test.uiautomator.*;
+import androidx.test.uiautomator.*;
import org.isoron.uhabits.*;
-import static android.support.test.espresso.Espresso.*;
-import static android.support.test.espresso.action.ViewActions.*;
-import static android.support.test.espresso.action.ViewActions.closeSoftKeyboard;
-import static android.support.test.espresso.matcher.ViewMatchers.*;
+import static androidx.test.espresso.Espresso.*;
+import static androidx.test.espresso.action.ViewActions.*;
+import static androidx.test.espresso.action.ViewActions.closeSoftKeyboard;
+import static androidx.test.espresso.matcher.ViewMatchers.*;
import static org.isoron.uhabits.BaseUserInterfaceTest.*;
public class EditHabitSteps
@@ -36,26 +36,53 @@ public class EditHabitSteps
onView(withId(R.id.buttonSave)).perform(click());
}
- public static void pickFrequency(String freq)
+ public static void pickFrequency()
{
- onView(withId(R.id.spinner)).perform(click());
- device.findObject(By.text(freq)).click();
+ onView(withId(R.id.boolean_frequency_picker)).perform(click());
+ onView(withText("SAVE")).perform(click());
}
public static void pickColor(int color)
{
- onView(withId(R.id.buttonPickColor)).perform(click());
+ onView(withId(R.id.colorButton)).perform(click());
device.findObject(By.descStartsWith(String.format("Color %d", color))).click();
}
public static void typeName(String name)
{
- typeTextWithId(R.id.tvName, name);
+ typeTextWithId(R.id.nameInput, name);
}
public static void typeQuestion(String name)
{
- typeTextWithId(R.id.tvDescription, name);
+ typeTextWithId(R.id.questionInput, name);
+ }
+
+ public static void typeDescription(String description)
+ {
+ typeTextWithId(R.id.notesInput, description);
+ }
+
+ public static void setReminder()
+ {
+ onView(withId(R.id.reminderTimePicker)).perform(click());
+ onView(withId(R.id.done_button)).perform(click());
+ }
+
+ public static void clickReminderDays()
+ {
+ onView(withId(R.id.reminderDatePicker)).perform(click());
+ }
+
+ public static void unselectAllDays()
+ {
+ onView(withText("Saturday")).perform(click());
+ onView(withText("Sunday")).perform(click());
+ onView(withText("Monday")).perform(click());
+ onView(withText("Tuesday")).perform(click());
+ onView(withText("Wednesday")).perform(click());
+ onView(withText("Thursday")).perform(click());
+ onView(withText("Friday")).perform(click());
}
private static void typeTextWithId(int id, String name)
diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/steps/ListHabitsSteps.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/steps/ListHabitsSteps.java
index c8784138e..39154b529 100644
--- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/steps/ListHabitsSteps.java
+++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/steps/ListHabitsSteps.java
@@ -19,7 +19,7 @@
package org.isoron.uhabits.acceptance.steps;
-import android.support.test.espresso.*;
+import androidx.test.espresso.*;
import android.view.*;
import org.hamcrest.*;
@@ -28,15 +28,15 @@ import org.isoron.uhabits.activities.habits.list.views.*;
import java.util.*;
-import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.action.ViewActions.click;
-import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
-import static android.support.test.espresso.matcher.ViewMatchers.isEnabled;
-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.withId;
-import static android.support.test.espresso.matcher.ViewMatchers.withParent;
-import static android.support.test.espresso.matcher.ViewMatchers.withText;
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant;
+import static androidx.test.espresso.matcher.ViewMatchers.isEnabled;
+import static androidx.test.espresso.matcher.ViewMatchers.withClassName;
+import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withParent;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.CoreMatchers.*;
import static org.isoron.uhabits.BaseUserInterfaceTest.device;
import static org.isoron.uhabits.acceptance.steps.CommonSteps.clickText;
@@ -60,7 +60,7 @@ public abstract class ListHabitsSteps
break;
case ADD:
- clickViewWithId(R.id.actionAdd);
+ clickViewWithId(R.id.actionCreateHabit);
break;
case EDIT:
@@ -91,11 +91,8 @@ public abstract class ListHabitsSteps
}
}
- private static void clickTextInsideOverflowMenu(int id)
- {
- onView(allOf(withContentDescription("More options"), withParent(
- withParent(withClassName(endsWith("Toolbar")))))).perform(click());
-
+ private static void clickTextInsideOverflowMenu(int id) {
+ onView(allOf(withContentDescription("More options"), withParent(withParent(withClassName(endsWith("Toolbar")))))).perform(click());
onView(withText(id)).perform(click());
}
diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/steps/WidgetSteps.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/steps/WidgetSteps.java
index ea3d6ea48..3cb5a201c 100644
--- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/steps/WidgetSteps.java
+++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/acceptance/steps/WidgetSteps.java
@@ -19,67 +19,69 @@
package org.isoron.uhabits.acceptance.steps;
-import android.support.test.uiautomator.*;
+import androidx.test.uiautomator.*;
+import static android.os.Build.VERSION.SDK_INT;
import static junit.framework.Assert.*;
import static org.isoron.uhabits.BaseUserInterfaceTest.*;
-public class WidgetSteps
-{
- public static void clickCheckmarkWidget() throws Exception
- {
- device
- .findObject(
- new UiSelector().resourceId("org.isoron.uhabits:id/imageView"))
- .click();
+public class WidgetSteps {
+ public static void clickCheckmarkWidget() throws Exception {
+ String view_id = "org.isoron.uhabits:id/imageView";
+ device.findObject(new UiSelector().resourceId(view_id)).click();
}
- public static void clickText(String s) throws Exception
- {
- device.findObject(new UiSelector().text(s)).click();
+ public static void clickText(String s) throws Exception {
+ UiObject object = device.findObject(new UiSelector().text(s));
+ if (!object.waitForExists(1000)) {
+ object = device.findObject(new UiSelector().text(s.toUpperCase()));
+ }
+ object.click();
}
- public static void clickWidgets() throws Exception
- {
- device.findObject(new UiSelector().text("WIDGETS")).click();
+ public static void dragCheckmarkWidgetToHomeScreen() throws Exception {
+ openWidgetScreen();
+ dragWidgetToHomeScreen();
}
- public static void dragWidgetToHomescreen() throws Exception
- {
+ private static void dragWidgetToHomeScreen() throws Exception {
int height = device.getDisplayHeight();
int width = device.getDisplayWidth();
- device
- .findObject(new UiSelector().text("Checkmark"))
- .dragTo(width / 2, height / 2, 8);
+ device.findObject(new UiSelector().text("Checkmark"))
+ .dragTo(width / 2, height / 2, 8);
}
- public static void longPressHomeScreen() throws Exception
- {
- device.pressHome();
- device.waitForIdle();
- device
- .findObject(new UiSelector().resourceId(
- "com.google.android.apps.nexuslauncher:id/workspace"))
- .longClick();
+ private static void openWidgetScreen() throws Exception {
+ int h = device.getDisplayHeight();
+ int w = device.getDisplayWidth();
+ if (SDK_INT <= 21) {
+ device.pressHome();
+ device.waitForIdle();
+ device.findObject(new UiSelector().description("Apps")).click();
+ device.findObject(new UiSelector().description("Apps")).click();
+ device.findObject(new UiSelector().description("Widgets")).click();
+ } else {
+ String list_id = "com.android.launcher3:id/widgets_list_view";
+ device.pressHome();
+ device.waitForIdle();
+ device.drag(w / 2, h / 2, w / 2, h / 2, 8);
+ UiObject button = device.findObject(new UiSelector().text("WIDGETS"));
+ if(!button.waitForExists(1000)) {
+ button = device.findObject(new UiSelector().text("Widgets"));
+ }
+ button.click();
+ if (SDK_INT == 28) {
+ new UiScrollable(new UiSelector().resourceId(list_id))
+ .scrollForward();
+ }
+ new UiScrollable(new UiSelector().resourceId(list_id))
+ .scrollIntoView(new UiSelector().text("Checkmark"));
+ }
}
- public static void scrollToHabits() throws Exception
- {
- new UiScrollable(new UiSelector().resourceId(
- "com.google.android.apps.nexuslauncher:id/widgets_list_view")).scrollIntoView(
- new UiSelector().text("Habits"));
-
- }
-
- public static void verifyCheckmarkWidgetIsShown() throws Exception
- {
- assertTrue(device
- .findObject(
- new UiSelector().resourceId("org.isoron.uhabits:id/imageView"))
- .exists());
-
- assertFalse(device
- .findObject(new UiSelector().textStartsWith("Habit deleted"))
- .exists());
+ public static void verifyCheckmarkWidgetIsShown() throws Exception {
+ String view_id = "org.isoron.uhabits:id/imageView";
+ assertTrue(device.findObject(new UiSelector().resourceId(view_id)).exists());
+ assertFalse(device.findObject(new UiSelector().textStartsWith("Habit deleted")).exists());
}
}
diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/common/views/BarChartTest.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/common/views/BarChartTest.java
index 5a5b4255c..832b6c78f 100644
--- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/common/views/BarChartTest.java
+++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/common/views/BarChartTest.java
@@ -19,8 +19,10 @@
package org.isoron.uhabits.activities.common.views;
-import android.support.test.filters.*;
-import android.support.test.runner.*;
+import androidx.test.filters.*;
+import androidx.test.runner.*;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.isoron.uhabits.*;
import org.isoron.uhabits.core.models.*;
diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/common/views/FrequencyChartTest.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/common/views/FrequencyChartTest.java
index eaf1c2568..ff492ef41 100644
--- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/common/views/FrequencyChartTest.java
+++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/common/views/FrequencyChartTest.java
@@ -19,8 +19,10 @@
package org.isoron.uhabits.activities.common.views;
-import android.support.test.filters.*;
-import android.support.test.runner.*;
+import androidx.test.filters.*;
+import androidx.test.runner.*;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.isoron.uhabits.*;
import org.isoron.uhabits.core.models.*;
diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/common/views/HistoryChartTest.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/common/views/HistoryChartTest.java
index e4d1ba763..d3fef3755 100644
--- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/common/views/HistoryChartTest.java
+++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/common/views/HistoryChartTest.java
@@ -19,8 +19,10 @@
package org.isoron.uhabits.activities.common.views;
-import android.support.test.filters.*;
-import android.support.test.runner.*;
+import androidx.test.filters.*;
+import androidx.test.runner.*;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.isoron.uhabits.*;
import org.isoron.uhabits.core.models.*;
diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/common/views/RingViewTest.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/common/views/RingViewTest.java
index 523d2cc9c..55f994e4a 100644
--- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/common/views/RingViewTest.java
+++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/common/views/RingViewTest.java
@@ -20,8 +20,10 @@
package org.isoron.uhabits.activities.common.views;
import android.graphics.*;
-import android.support.test.runner.*;
-import android.test.suitebuilder.annotation.*;
+import androidx.test.filters.*;
+import androidx.test.runner.*;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.isoron.uhabits.*;
import org.isoron.uhabits.utils.*;
diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/common/views/ScoreChartTest.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/common/views/ScoreChartTest.java
index c9c072c2b..216392129 100644
--- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/common/views/ScoreChartTest.java
+++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/common/views/ScoreChartTest.java
@@ -19,8 +19,10 @@
package org.isoron.uhabits.activities.common.views;
-import android.support.test.runner.*;
-import android.test.suitebuilder.annotation.*;
+import androidx.test.filters.*;
+import androidx.test.runner.*;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.isoron.uhabits.*;
import org.isoron.uhabits.core.models.*;
@@ -29,6 +31,8 @@ import org.isoron.uhabits.utils.*;
import org.junit.*;
import org.junit.runner.*;
+import java.util.*;
+
@RunWith(AndroidJUnit4.class)
@MediumTest
public class ScoreChartTest extends BaseViewTest
@@ -80,7 +84,7 @@ public class ScoreChartTest extends BaseViewTest
@Test
public void testRender_withMonthlyBucket() throws Throwable
{
- view.setScores(habit.getScores().groupBy(DateUtils.TruncateField.MONTH));
+ view.setScores(habit.getScores().groupBy(DateUtils.TruncateField.MONTH, Calendar.SUNDAY));
view.setBucketSize(30);
view.invalidate();
@@ -97,7 +101,7 @@ public class ScoreChartTest extends BaseViewTest
@Test
public void testRender_withYearlyBucket() throws Throwable
{
- view.setScores(habit.getScores().groupBy(DateUtils.TruncateField.YEAR));
+ view.setScores(habit.getScores().groupBy(DateUtils.TruncateField.YEAR, Calendar.SUNDAY));
view.setBucketSize(365);
view.invalidate();
diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/common/views/StreakChartTest.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/common/views/StreakChartTest.java
index cb4761a45..fc82e1b5f 100644
--- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/common/views/StreakChartTest.java
+++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/common/views/StreakChartTest.java
@@ -19,8 +19,10 @@
package org.isoron.uhabits.activities.common.views;
-import android.support.test.runner.*;
-import android.test.suitebuilder.annotation.*;
+import androidx.test.filters.*;
+import androidx.test.runner.*;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.isoron.uhabits.*;
import org.isoron.uhabits.core.models.*;
diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/CheckmarkButtonViewTest.kt b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/CheckmarkButtonViewTest.kt
index 9c6e52533..41dc80c0a 100644
--- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/CheckmarkButtonViewTest.kt
+++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/CheckmarkButtonViewTest.kt
@@ -19,8 +19,9 @@
package org.isoron.uhabits.activities.habits.list.views
-import android.support.test.filters.*
-import android.support.test.runner.*
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.*
+import androidx.test.runner.*
import org.isoron.uhabits.*
import org.isoron.uhabits.core.models.*
import org.isoron.uhabits.utils.*
diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/CheckmarkPanelViewTest.kt b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/CheckmarkPanelViewTest.kt
index b02e7c280..024b0fa7f 100644
--- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/CheckmarkPanelViewTest.kt
+++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/CheckmarkPanelViewTest.kt
@@ -19,8 +19,9 @@
package org.isoron.uhabits.activities.habits.list.views
-import android.support.test.filters.*
-import android.support.test.runner.*
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.*
+import androidx.test.runner.*
import org.hamcrest.CoreMatchers.*
import org.hamcrest.MatcherAssert.*
import org.isoron.uhabits.*
@@ -61,7 +62,6 @@ class CheckmarkPanelViewTest : BaseViewTest() {
@After
public override fun tearDown() {
-// view.onDetachedFromWindow()
super.tearDown()
}
@@ -70,23 +70,26 @@ class CheckmarkPanelViewTest : BaseViewTest() {
assertRenders(view, "$PATH/render.png")
}
- @Test
- fun testRender_withDifferentColor() {
- view.color = PaletteUtils.getAndroidTestColor(1)
- assertRenders(view, "$PATH/render_different_color.png")
- }
+// // Flaky test
+// @Test
+// fun testRender_withDifferentColor() {
+// view.color = PaletteUtils.getAndroidTestColor(1)
+// assertRenders(view, "$PATH/render_different_color.png")
+// }
- @Test
- fun testRender_Reversed() {
- prefs.isCheckmarkSequenceReversed = true
- assertRenders(view, "$PATH/render_reversed.png")
- }
+// // Flaky test
+// @Test
+// fun testRender_Reversed() {
+// prefs.isCheckmarkSequenceReversed = true
+// assertRenders(view, "$PATH/render_reversed.png")
+// }
- @Test
- fun testRender_withOffset() {
- view.dataOffset = 3
- assertRenders(view, "$PATH/render_offset.png")
- }
+// // Flaky test
+// @Test
+// fun testRender_withOffset() {
+// view.dataOffset = 3
+// assertRenders(view, "$PATH/render_offset.png")
+// }
@Test
fun testToggle() {
diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/HabitCardViewTest.kt b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/HabitCardViewTest.kt
index 5660b862e..19a482136 100644
--- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/HabitCardViewTest.kt
+++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/HabitCardViewTest.kt
@@ -19,8 +19,9 @@
package org.isoron.uhabits.activities.habits.list.views
-import android.support.test.filters.*
-import android.support.test.runner.*
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.*
+import androidx.test.runner.*
import org.isoron.uhabits.*
import org.isoron.uhabits.core.models.*
import org.junit.*
diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/HeaderViewTest.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/HeaderViewTest.java
index 4c347c2a5..71f203e40 100644
--- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/HeaderViewTest.java
+++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/HeaderViewTest.java
@@ -19,8 +19,10 @@
package org.isoron.uhabits.activities.habits.list.views;
-import android.support.test.filters.*;
-import android.support.test.runner.*;
+import androidx.test.filters.*;
+import androidx.test.runner.*;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.isoron.uhabits.*;
import org.isoron.uhabits.core.preferences.*;
diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/HintViewTest.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/HintViewTest.java
index 44f512568..b6baabb30 100644
--- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/HintViewTest.java
+++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/HintViewTest.java
@@ -19,8 +19,10 @@
package org.isoron.uhabits.activities.habits.list.views;
-import android.support.test.runner.*;
-import android.test.suitebuilder.annotation.*;
+import androidx.test.filters.*;
+import androidx.test.runner.*;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.isoron.uhabits.*;
import org.isoron.uhabits.core.ui.screens.habits.list.*;
@@ -28,7 +30,7 @@ import org.junit.*;
import org.junit.runner.*;
import static org.hamcrest.CoreMatchers.*;
-import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.MatcherAssert.*;
import static org.mockito.Mockito.*;
@RunWith(AndroidJUnit4.class)
diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/NumberButtonViewTest.kt b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/NumberButtonViewTest.kt
index d64830e21..1c83fdb4b 100644
--- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/NumberButtonViewTest.kt
+++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/NumberButtonViewTest.kt
@@ -19,8 +19,9 @@
package org.isoron.uhabits.activities.habits.list.views
-import android.support.test.filters.*
-import android.support.test.runner.*
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.*
+import androidx.test.runner.*
import org.hamcrest.CoreMatchers.*
import org.hamcrest.MatcherAssert.*
import org.isoron.uhabits.*
diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/NumberPanelViewTest.kt b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/NumberPanelViewTest.kt
index 489f65247..2a660b2a0 100644
--- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/NumberPanelViewTest.kt
+++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/list/views/NumberPanelViewTest.kt
@@ -19,8 +19,9 @@
package org.isoron.uhabits.activities.habits.list.views
-import android.support.test.filters.*
-import android.support.test.runner.*
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.*
+import androidx.test.runner.*
import org.hamcrest.CoreMatchers.*
import org.hamcrest.MatcherAssert.*
import org.isoron.uhabits.*
@@ -65,23 +66,26 @@ class NumberPanelViewTest : BaseViewTest() {
assertRenders(view, "$PATH/render.png")
}
- @Test
- fun testRender_withDifferentColor() {
- view.color = PaletteUtils.getAndroidTestColor(1)
- assertRenders(view, "$PATH/render_different_color.png")
- }
+// // Flaky test
+// @Test
+// fun testRender_withDifferentColor() {
+// view.color = PaletteUtils.getAndroidTestColor(1)
+// assertRenders(view, "$PATH/render_different_color.png")
+// }
- @Test
- fun testRender_Reversed() {
- prefs.isCheckmarkSequenceReversed = true
- assertRenders(view, "$PATH/render_reversed.png")
- }
+// // Flaky test
+// @Test
+// fun testRender_Reversed() {
+// prefs.isCheckmarkSequenceReversed = true
+// assertRenders(view, "$PATH/render_reversed.png")
+// }
- @Test
- fun testRender_withOffset() {
- view.dataOffset = 3
- assertRenders(view, "$PATH/render_offset.png")
- }
+// // Flaky test
+// @Test
+// fun testRender_withOffset() {
+// view.dataOffset = 3
+// assertRenders(view, "$PATH/render_offset.png")
+// }
@Test
fun testEdit() {
diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/FrequencyCardTest.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/FrequencyCardTest.java
index 9b0b82efa..d8ceac381 100644
--- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/FrequencyCardTest.java
+++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/FrequencyCardTest.java
@@ -19,10 +19,12 @@
package org.isoron.uhabits.activities.habits.show.views;
-import android.support.test.runner.*;
-import android.test.suitebuilder.annotation.*;
+import androidx.test.filters.*;
+import androidx.test.runner.*;
import android.view.*;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
import org.isoron.uhabits.*;
import org.isoron.uhabits.core.models.*;
import org.junit.*;
diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/HistoryCardTest.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/HistoryCardTest.java
index 91bffd89a..a912f24a7 100644
--- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/HistoryCardTest.java
+++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/HistoryCardTest.java
@@ -19,10 +19,12 @@
package org.isoron.uhabits.activities.habits.show.views;
-import android.support.test.runner.*;
-import android.test.suitebuilder.annotation.*;
+import androidx.test.filters.*;
+import androidx.test.runner.*;
import android.view.*;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
import org.isoron.uhabits.*;
import org.isoron.uhabits.core.models.*;
import org.junit.*;
diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/NotesCardTest.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/NotesCardTest.java
new file mode 100644
index 000000000..6f91bf4d6
--- /dev/null
+++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/NotesCardTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2016 Álinson Santos Xavier
+ *
+ * This file is part of Loop Habit Tracker.
+ *
+ * Loop Habit Tracker is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * Loop Habit Tracker is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see .
+ */
+
+package org.isoron.uhabits.activities.habits.show.views;
+
+import android.view.LayoutInflater;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.MediumTest;
+
+import org.isoron.uhabits.BaseViewTest;
+import org.isoron.uhabits.R;
+import org.isoron.uhabits.core.models.Habit;
+import org.isoron.uhabits.core.models.Reminder;
+import org.isoron.uhabits.core.models.WeekdayList;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@MediumTest
+public class NotesCardTest extends BaseViewTest
+{
+ public static final String PATH = "habits/show/NotesCard/";
+
+ private NotesCard view;
+
+ private Habit habit;
+
+ @Before
+ @Override
+ public void setUp()
+ {
+ super.setUp();
+
+ habit = fixtures.createLongHabit();
+ habit.setReminder(new Reminder(8, 30, WeekdayList.EVERY_DAY));
+
+ view = LayoutInflater
+ .from(targetContext)
+ .inflate(R.layout.show_habit, null)
+ .findViewById(R.id.notesCard);
+
+ view.setHabit(habit);
+ view.refreshData();
+
+ measureView(view, 800, 200);
+ }
+
+ @Test
+ public void testRender() throws Exception
+ {
+ assertRenders(view, PATH + "render.png");
+ }
+
+ @Test
+ public void testRenderEmptyDescription() throws Exception
+ {
+ habit.setDescription("");
+ view.refreshData();
+ assertRenders(view, PATH + "render-empty-description.png");
+ }
+}
diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/OverviewCardTest.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/OverviewCardTest.java
index 399cdf8a0..9afe0014d 100644
--- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/OverviewCardTest.java
+++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/OverviewCardTest.java
@@ -19,10 +19,12 @@
package org.isoron.uhabits.activities.habits.show.views;
-import android.support.test.runner.*;
-import android.test.suitebuilder.annotation.*;
+import androidx.test.filters.*;
+import androidx.test.runner.*;
import android.view.*;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
import org.isoron.uhabits.*;
import org.isoron.uhabits.core.models.*;
import org.junit.*;
diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/ScoreCardTest.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/ScoreCardTest.java
index 16f3ae61b..51901e896 100644
--- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/ScoreCardTest.java
+++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/ScoreCardTest.java
@@ -19,10 +19,12 @@
package org.isoron.uhabits.activities.habits.show.views;
-import android.support.test.runner.*;
-import android.test.suitebuilder.annotation.*;
+import androidx.test.filters.*;
+import androidx.test.runner.*;
import android.view.*;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
import org.isoron.uhabits.*;
import org.isoron.uhabits.core.models.*;
import org.junit.*;
diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/StreakCardTest.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/StreakCardTest.java
index ef1a2a9a1..ebe8aed46 100644
--- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/StreakCardTest.java
+++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/StreakCardTest.java
@@ -19,10 +19,12 @@
package org.isoron.uhabits.activities.habits.show.views;
-import android.support.test.runner.*;
-import android.test.suitebuilder.annotation.*;
+import androidx.test.filters.*;
+import androidx.test.runner.*;
import android.view.*;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
import org.isoron.uhabits.*;
import org.isoron.uhabits.core.models.*;
import org.junit.*;
diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/SubtitleCardTest.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/SubtitleCardTest.java
index 74564d8c6..63aa74407 100644
--- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/SubtitleCardTest.java
+++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/activities/habits/show/views/SubtitleCardTest.java
@@ -19,10 +19,12 @@
package org.isoron.uhabits.activities.habits.show.views;
-import android.support.test.runner.*;
-import android.test.suitebuilder.annotation.*;
+import androidx.test.filters.*;
+import androidx.test.runner.*;
import android.view.*;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
import org.isoron.uhabits.*;
import org.isoron.uhabits.core.models.*;
import org.junit.*;
@@ -47,7 +49,7 @@ public class SubtitleCardTest extends BaseViewTest
habit = fixtures.createLongHabit();
habit.setReminder(new Reminder(8, 30, WeekdayList.EVERY_DAY));
- view = (SubtitleCard) LayoutInflater
+ view = LayoutInflater
.from(targetContext)
.inflate(R.layout.show_habit, null)
.findViewById(R.id.subtitleCard);
diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/integration/SavedStateTest.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/integration/SavedStateTest.java
deleted file mode 100644
index 391e4e37b..000000000
--- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/integration/SavedStateTest.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2017 Álinson Santos Xavier
- *
- * This file is part of Loop Habit Tracker.
- *
- * Loop Habit Tracker is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or (at your
- * option) any later version.
- *
- * Loop Habit Tracker is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see .
- */
-
-package org.isoron.uhabits.integration;
-
-import android.support.test.filters.*;
-import android.support.test.runner.*;
-
-import org.isoron.uhabits.*;
-import org.isoron.uhabits.activities.about.*;
-import org.isoron.uhabits.activities.habits.list.*;
-import org.junit.*;
-import org.junit.runner.*;
-
-import static java.lang.Thread.*;
-
-@RunWith(AndroidJUnit4.class)
-@LargeTest
-public class SavedStateTest extends BaseUserInterfaceTest
-{
- /**
- * Make sure that the main activity can be recreated by using
- * BundleSavedState after being destroyed. See bug:
- * https://github.com/iSoron/uhabits/issues/287
- */
- @Test
- public void testBundleSavedState() throws Exception
- {
- startActivity(ListHabitsActivity.class);
- device.waitForIdle();
- startActivity(AboutActivity.class);
- sleep(1000);
- device.pressBack();
- }
-}
diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/performance/PerformanceTest.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/performance/PerformanceTest.java
index 49ea79bc4..0dba9879f 100644
--- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/performance/PerformanceTest.java
+++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/performance/PerformanceTest.java
@@ -19,8 +19,10 @@
package org.isoron.uhabits.performance;
-import android.support.test.filters.*;
-import android.support.test.runner.*;
+import androidx.test.filters.*;
+import androidx.test.runner.*;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.isoron.uhabits.*;
import org.isoron.uhabits.core.models.*;
diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/regression/ListHabitsRegressionTest.kt b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/regression/ListHabitsRegressionTest.kt
new file mode 100644
index 000000000..18ed417ec
--- /dev/null
+++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/regression/ListHabitsRegressionTest.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2020 Álinson Santos Xavier
+ *
+ * This file is part of Loop Habit Tracker.
+ *
+ * Loop Habit Tracker is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * Loop Habit Tracker is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see .
+ */
+
+package org.isoron.uhabits.regression
+
+import androidx.test.filters.*
+import org.isoron.uhabits.*
+import org.isoron.uhabits.acceptance.steps.CommonSteps.*
+import org.isoron.uhabits.acceptance.steps.CommonSteps.Screen.*
+import org.isoron.uhabits.acceptance.steps.EditHabitSteps.*
+import org.isoron.uhabits.acceptance.steps.ListHabitsSteps.*
+import org.isoron.uhabits.acceptance.steps.ListHabitsSteps.MenuItem.*
+import org.junit.*
+
+
+@LargeTest
+class ListHabitsRegressionTest : BaseUserInterfaceTest() {
+ /**
+ * https://github.com/iSoron/uhabits/issues/539
+ */
+ @Test
+ @Throws(Exception::class)
+ fun should_not_crash_after_deleting_then_adding_a_habit() {
+ launchApp()
+ verifyShowsScreen(LIST_HABITS)
+ longClickText("Track time")
+ clickMenu(DELETE)
+ clickOK()
+
+ clickMenu(ADD)
+ verifyShowsScreen(EDIT_HABIT)
+ typeName("Hello world")
+ clickSave()
+
+ verifyDisplaysText("Hello world")
+ longPressCheckmarks("Hello world", 3)
+ }
+}
\ No newline at end of file
diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/regression/SavedStateTest.kt b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/regression/SavedStateTest.kt
new file mode 100644
index 000000000..5d783ad1f
--- /dev/null
+++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/regression/SavedStateTest.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2020 Álinson Santos Xavier
+ *
+ * This file is part of Loop Habit Tracker.
+ *
+ * Loop Habit Tracker is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * Loop Habit Tracker is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see .
+ */
+
+package org.isoron.uhabits.regression
+
+import androidx.test.filters.*
+
+import org.isoron.uhabits.*
+import org.junit.*
+
+import org.isoron.uhabits.acceptance.steps.CommonSteps.*
+import org.isoron.uhabits.acceptance.steps.EditHabitSteps.*
+import org.isoron.uhabits.acceptance.steps.ListHabitsSteps.*
+import org.isoron.uhabits.acceptance.steps.ListHabitsSteps.MenuItem.*
+import org.isoron.uhabits.acceptance.steps.WidgetSteps.clickText
+import org.isoron.uhabits.activities.about.*
+import org.isoron.uhabits.activities.habits.list.*
+import java.lang.Thread.*
+
+@LargeTest
+class SavedStateTest : BaseUserInterfaceTest() {
+
+ @Test
+ @Throws(Exception::class)
+ fun shouldNotCrashWhenRotatingWeekdayPickerDialog() {
+ // https://github.com/iSoron/uhabits/issues/534
+ launchApp()
+ clickMenu(ADD)
+ setReminder()
+ clickReminderDays()
+ unselectAllDays()
+ rotateDevice()
+ clickText("Monday")
+ }
+
+ /**
+ * Make sure that the main activity can be recreated by using
+ * BundleSavedState after being destroyed. See bug:
+ * https://github.com/iSoron/uhabits/issues/287
+ */
+ @Test
+ @Throws(Exception::class)
+ fun testBundleSavedState() {
+ launchApp()
+ startActivity(AboutActivity::class.java)
+ sleep(1000)
+ device.pressBack()
+ }
+}
diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/tasks/ExportCSVTaskTest.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/tasks/ExportCSVTaskTest.java
deleted file mode 100644
index ab60fef88..000000000
--- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/tasks/ExportCSVTaskTest.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2016 Álinson Santos Xavier
- *
- * This file is part of Loop Habit Tracker.
- *
- * Loop Habit Tracker is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or (at your
- * option) any later version.
- *
- * Loop Habit Tracker is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see .
- */
-
-package org.isoron.uhabits.tasks;
-
-import android.support.test.runner.*;
-import android.test.suitebuilder.annotation.*;
-
-import org.isoron.androidbase.*;
-import org.isoron.uhabits.*;
-import org.isoron.uhabits.core.models.*;
-import org.isoron.uhabits.core.tasks.*;
-import org.junit.*;
-import org.junit.runner.*;
-
-import java.io.*;
-import java.util.*;
-
-import static org.hamcrest.CoreMatchers.*;
-import static org.hamcrest.MatcherAssert.*;
-import static org.hamcrest.core.IsNot.not;
-
-@RunWith(AndroidJUnit4.class)
-@MediumTest
-public class ExportCSVTaskTest extends BaseAndroidTest
-{
- @Before
- @Override
- public void setUp()
- {
- super.setUp();
- }
-
-// @Test
-// public void testExportCSV() throws Throwable
-// {
-// fixtures.purgeHabits(habitList);
-// fixtures.createShortHabit();
-//
-// List selected = new LinkedList<>();
-// for (Habit h : habitList) selected.add(h);
-// File outputDir = new AndroidDirFinder(targetContext).getFilesDir("CSV");
-// assertNotNull(outputDir);
-//
-// taskRunner.execute(
-// new ExportCSVTask(habitList, selected, outputDir, archiveFilename -> {
-// assertThat(archiveFilename, is(not(nullValue())));
-// File f = new File(archiveFilename);
-// assertTrue(f.exists());
-// assertTrue(f.canRead());
-// }));
-// }
-}
diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/tasks/ExportDBTaskTest.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/tasks/ExportDBTaskTest.java
deleted file mode 100644
index 7f3fda4d1..000000000
--- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/tasks/ExportDBTaskTest.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2016 Álinson Santos Xavier
- *
- * This file is part of Loop Habit Tracker.
- *
- * Loop Habit Tracker is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or (at your
- * option) any later version.
- *
- * Loop Habit Tracker is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see .
- */
-
-package org.isoron.uhabits.tasks;
-
-import android.support.test.runner.*;
-import android.test.suitebuilder.annotation.*;
-
-import org.isoron.androidbase.*;
-import org.isoron.uhabits.*;
-import org.junit.*;
-import org.junit.runner.*;
-
-import java.io.*;
-
-@RunWith(AndroidJUnit4.class)
-@MediumTest
-public class ExportDBTaskTest extends BaseAndroidTest
-{
- @Override
- @Before
- public void setUp()
- {
- super.setUp();
- }
-
-// @Test
-// public void testExportCSV() throws Throwable
-// {
-// ExportDBTask task =
-// new ExportDBTask(targetContext, new AndroidDirFinder(targetContext),
-// filename ->
-// {
-// assertNotNull(filename);
-// File f = new File(filename);
-// assertTrue(f.exists());
-// assertTrue(f.canRead());
-// });
-//
-// taskRunner.execute(task);
-// }
-}
diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/CheckmarkWidgetTest.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/CheckmarkWidgetTest.java
index 130ec3b68..0aeb4762f 100644
--- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/CheckmarkWidgetTest.java
+++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/CheckmarkWidgetTest.java
@@ -19,17 +19,19 @@
package org.isoron.uhabits.widgets;
-import android.support.test.runner.*;
-import android.test.suitebuilder.annotation.*;
+import androidx.test.filters.*;
+import androidx.test.runner.*;
import android.widget.*;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
import org.isoron.uhabits.*;
import org.isoron.uhabits.core.models.*;
import org.junit.*;
import org.junit.runner.*;
-import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.CoreMatchers.*;
+import static org.hamcrest.MatcherAssert.*;
import static org.isoron.uhabits.core.models.Checkmark.*;
@RunWith(AndroidJUnit4.class)
@@ -48,7 +50,7 @@ public class CheckmarkWidgetTest extends BaseViewTest
public void setUp()
{
super.setUp();
- setTheme(R.style.TransparentWidgetTheme);
+ setTheme(R.style.WidgetTheme);
habit = fixtures.createShortHabit();
checkmarks = habit.getCheckmarks();
diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/FrequencyWidgetTest.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/FrequencyWidgetTest.java
index 7ebd20992..31a459af3 100644
--- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/FrequencyWidgetTest.java
+++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/FrequencyWidgetTest.java
@@ -19,15 +19,19 @@
package org.isoron.uhabits.widgets;
-import android.support.test.runner.*;
-import android.test.suitebuilder.annotation.*;
+import androidx.test.filters.*;
+import androidx.test.runner.*;
import android.widget.*;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
import org.isoron.uhabits.*;
import org.isoron.uhabits.core.models.*;
import org.junit.*;
import org.junit.runner.*;
+import java.util.*;
+
@RunWith(AndroidJUnit4.class)
@MediumTest
public class FrequencyWidgetTest extends BaseViewTest
@@ -42,10 +46,10 @@ public class FrequencyWidgetTest extends BaseViewTest
public void setUp()
{
super.setUp();
- setTheme(R.style.TransparentWidgetTheme);
+ setTheme(R.style.WidgetTheme);
habit = fixtures.createLongHabit();
- FrequencyWidget widget = new FrequencyWidget(targetContext, 0, habit);
+ FrequencyWidget widget = new FrequencyWidget(targetContext, 0, habit, Calendar.SUNDAY);
view = convertToView(widget, 400, 400);
}
diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/HistoryWidgetTest.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/HistoryWidgetTest.java
index 3a6427969..f059d99c1 100644
--- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/HistoryWidgetTest.java
+++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/HistoryWidgetTest.java
@@ -19,15 +19,19 @@
package org.isoron.uhabits.widgets;
-import android.support.test.runner.*;
-import android.test.suitebuilder.annotation.*;
+import androidx.test.filters.*;
+import androidx.test.runner.*;
import android.widget.*;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
import org.isoron.uhabits.*;
import org.isoron.uhabits.core.models.*;
import org.junit.*;
import org.junit.runner.*;
+import java.util.*;
+
@RunWith(AndroidJUnit4.class)
@MediumTest
public class HistoryWidgetTest extends BaseViewTest
@@ -42,10 +46,10 @@ public class HistoryWidgetTest extends BaseViewTest
public void setUp()
{
super.setUp();
- setTheme(R.style.TransparentWidgetTheme);
+ setTheme(R.style.WidgetTheme);
habit = fixtures.createLongHabit();
- HistoryWidget widget = new HistoryWidget(targetContext, 0, habit);
+ HistoryWidget widget = new HistoryWidget(targetContext, 0, habit, Calendar.SUNDAY);
view = convertToView(widget, 400, 400);
}
diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/ScoreWidgetTest.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/ScoreWidgetTest.java
index 15b9e8c22..550984b65 100644
--- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/ScoreWidgetTest.java
+++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/ScoreWidgetTest.java
@@ -19,10 +19,12 @@
package org.isoron.uhabits.widgets;
-import android.support.test.runner.*;
-import android.test.suitebuilder.annotation.*;
+import androidx.test.filters.*;
+import androidx.test.runner.*;
import android.widget.*;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
import org.isoron.uhabits.*;
import org.isoron.uhabits.core.models.*;
import org.junit.*;
@@ -42,7 +44,7 @@ public class ScoreWidgetTest extends BaseViewTest
public void setUp()
{
super.setUp();
- setTheme(R.style.TransparentWidgetTheme);
+ setTheme(R.style.WidgetTheme);
habit = fixtures.createLongHabit();
ScoreWidget widget = new ScoreWidget(targetContext, 0, habit);
diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/StreakWidgetTest.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/StreakWidgetTest.java
index 8e7e41ef2..a9d4e6f8f 100644
--- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/StreakWidgetTest.java
+++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/StreakWidgetTest.java
@@ -19,10 +19,12 @@
package org.isoron.uhabits.widgets;
-import android.support.test.runner.*;
-import android.test.suitebuilder.annotation.*;
+import androidx.test.filters.*;
+import androidx.test.runner.*;
import android.widget.*;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
import org.isoron.uhabits.*;
import org.isoron.uhabits.core.models.*;
import org.junit.*;
@@ -42,7 +44,7 @@ public class StreakWidgetTest extends BaseViewTest
public void setUp()
{
super.setUp();
- setTheme(R.style.TransparentWidgetTheme);
+ setTheme(R.style.WidgetTheme);
habit = fixtures.createLongHabit();
StreakWidget widget = new StreakWidget(targetContext, 0, habit);
diff --git a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/views/CheckmarkWidgetViewTest.java b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/views/CheckmarkWidgetViewTest.java
index c3f573319..f0354a5d5 100644
--- a/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/views/CheckmarkWidgetViewTest.java
+++ b/android/uhabits-android/src/androidTest/java/org/isoron/uhabits/widgets/views/CheckmarkWidgetViewTest.java
@@ -19,8 +19,10 @@
package org.isoron.uhabits.widgets.views;
-import android.support.test.runner.*;
-import android.test.suitebuilder.annotation.*;
+import androidx.test.filters.*;
+import androidx.test.runner.*;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.isoron.uhabits.*;
import org.isoron.uhabits.core.models.*;
@@ -43,7 +45,7 @@ public class CheckmarkWidgetViewTest extends BaseViewTest
public void setUp()
{
super.setUp();
- setTheme(R.style.TransparentWidgetTheme);
+ setTheme(R.style.WidgetTheme);
Habit habit = fixtures.createShortHabit();
view = new CheckmarkWidgetView(targetContext);
diff --git a/android/uhabits-android/src/main/AndroidManifest.xml b/android/uhabits-android/src/main/AndroidManifest.xml
index c26e8f5aa..52be87086 100644
--- a/android/uhabits-android/src/main/AndroidManifest.xml
+++ b/android/uhabits-android/src/main/AndroidManifest.xml
@@ -29,6 +29,9 @@
+
+
+
+
+
+
+
+
+ android:value="AEdPqrEAAAAI6aeWncbnMNo8E5GWeZ44dlc5cQ7tCROwFhOtiw" />
+ android:launchMode="singleTop" />
-
-
+
+
-
+
@@ -66,32 +78,36 @@
android:label="@string/title_activity_show_habit">
+ android:value=".activities.habits.list.ListHabitsActivity" />
+
+ android:value=".activities.habits.list.ListHabitsActivity" />
+
+ android:theme="@style/Theme.AppCompat.Light.NoActionBar" />
+
-
+
+
+ android:value=".activities.habits.list.ListHabitsActivity" />
-
+ android:theme="@android:style/Theme.Translucent.NoTitleBar" />
-
+
+ android:resource="@xml/widget_checkmark_info" />
-
+
+
+
-
+
+ android:resource="@xml/widget_history_info" />
+
-
+
+ android:resource="@xml/widget_score_info" />
+
-
+
+ android:resource="@xml/widget_streak_info" />
+
-
+
+
+
+
+
+
+
+
+
+ android:resource="@xml/widget_frequency_info" />
+
-
+
+
@@ -183,29 +218,29 @@
-
+
+ android:scheme="content" />
-
+
-
+
+ android:scheme="content" />
-
+
-
+
+ android:scheme="content" />
@@ -216,7 +251,7 @@
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name">
-
+
@@ -225,25 +260,25 @@
android:name=".automation.FireSettingReceiver"
android:exported="true">
-
+
+ android:resource="@xml/file_paths" />
-
+ android:exported="false" />
+
\ No newline at end of file
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/HabitsApplicationComponent.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/HabitsApplicationComponent.java
index 3fd635f22..8c110ab26 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/HabitsApplicationComponent.java
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/HabitsApplicationComponent.java
@@ -63,8 +63,6 @@ public interface HabitsApplicationComponent
HabitList getHabitList();
- HabitLogger getHabitsLogger();
-
IntentFactory getIntentFactory();
IntentParser getIntentParser();
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/HabitsDatabaseOpener.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/HabitsDatabaseOpener.kt
index ba3c5565d..6bc86eaaa 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/HabitsDatabaseOpener.kt
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/HabitsDatabaseOpener.kt
@@ -34,13 +34,20 @@ class HabitsDatabaseOpener(
) : SQLiteOpenHelper(context, databaseFilename, null, version) {
override fun onCreate(db: SQLiteDatabase) {
+ db.disableWriteAheadLogging()
db.version = 8
onUpgrade(db, -1, version)
}
+ override fun onOpen(db: SQLiteDatabase) {
+ super.onOpen(db)
+ db.disableWriteAheadLogging()
+ }
+
override fun onUpgrade(db: SQLiteDatabase,
oldVersion: Int,
newVersion: Int) {
+ db.disableWriteAheadLogging()
if (db.version < 8) throw UnsupportedDatabaseVersionException()
val helper = MigrationHelper(AndroidDatabase(db))
helper.migrateTo(newVersion)
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/HabitsModule.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/HabitsModule.kt
index adbb32a11..a1a813d51 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/HabitsModule.kt
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/HabitsModule.kt
@@ -48,9 +48,10 @@ class HabitsModule {
fun getReminderScheduler(
sys: IntentScheduler,
commandRunner: CommandRunner,
- habitList: HabitList
+ habitList: HabitList,
+ widgetPreferences: WidgetPreferences
): ReminderScheduler {
- return ReminderScheduler(commandRunner, habitList, sys)
+ return ReminderScheduler(commandRunner, habitList, sys, widgetPreferences)
}
@Provides
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/AndroidThemeSwitcher.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/AndroidThemeSwitcher.kt
index 9c8c8f6b3..c0d5bd786 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/AndroidThemeSwitcher.kt
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/AndroidThemeSwitcher.kt
@@ -19,6 +19,10 @@
package org.isoron.uhabits.activities
+import android.app.*
+import android.content.res.Configuration.*
+import android.os.Build.VERSION.*
+import androidx.core.content.*
import org.isoron.androidbase.activities.*
import org.isoron.uhabits.*
import org.isoron.uhabits.core.preferences.*
@@ -27,13 +31,25 @@ import javax.inject.*
@ActivityScope
class AndroidThemeSwitcher
-@Inject constructor(
- private val activity: BaseActivity,
+constructor(
+ private val activity: Activity,
preferences: Preferences
) : ThemeSwitcher(preferences) {
+ override fun getSystemTheme(): Int {
+ if(SDK_INT < 29) return THEME_LIGHT;
+ val uiMode = activity.resources.configuration.uiMode
+ return if ((uiMode and UI_MODE_NIGHT_MASK) == UI_MODE_NIGHT_YES) {
+ THEME_DARK;
+ } else {
+ THEME_LIGHT;
+ }
+ }
+
override fun applyDarkTheme() {
activity.setTheme(R.style.AppBaseThemeDark)
+ activity.window.navigationBarColor =
+ ContextCompat.getColor(activity, R.color.grey_900)
}
override fun applyLightTheme() {
@@ -42,5 +58,14 @@ class AndroidThemeSwitcher
override fun applyPureBlackTheme() {
activity.setTheme(R.style.AppBaseThemeDark_PureBlack)
+ activity.window.navigationBarColor =
+ ContextCompat.getColor(activity, R.color.black)
+ }
+
+ fun getDialogTheme(): Int {
+ return when {
+ isNightMode -> R.style.DarkDialogWithTitle
+ else -> R.style.DialogWithTitle
+ }
}
}
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/HabitsActivityComponent.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/HabitsActivityComponent.kt
index ed6906b74..a135b02d4 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/HabitsActivityComponent.kt
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/HabitsActivityComponent.kt
@@ -34,15 +34,12 @@ import org.isoron.uhabits.core.ui.screens.habits.list.*
@Component(modules = arrayOf(
ActivityContextModule::class,
BaseActivityModule::class,
- AboutModule::class,
HabitsActivityModule::class,
ListHabitsModule::class,
ShowHabitModule::class,
HabitModule::class
), dependencies = arrayOf(HabitsApplicationComponent::class))
interface HabitsActivityComponent {
- val aboutRootView: AboutRootView
- val aboutScreen: AboutScreen
val colorPickerDialogFactory: ColorPickerDialogFactory
val habitCardListAdapter: HabitCardListAdapter
val listHabitsBehavior: ListHabitsBehavior
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/HabitsActivityModule.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/HabitsActivityModule.kt
index 77145afbc..e074c17c4 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/HabitsActivityModule.kt
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/HabitsActivityModule.kt
@@ -21,10 +21,17 @@ package org.isoron.uhabits.activities
import dagger.*
import org.isoron.androidbase.activities.*
+import org.isoron.uhabits.core.preferences.*
import org.isoron.uhabits.core.ui.*
@Module
-abstract class HabitsActivityModule {
- @Binds @ActivityScope
- internal abstract fun getThemeSwitcher(t: AndroidThemeSwitcher): ThemeSwitcher
+class HabitsActivityModule {
+
+ @Provides
+ @ActivityScope
+ fun getThemeSwitcher(activity: BaseActivity,
+ prefs: Preferences
+ ): ThemeSwitcher {
+ return AndroidThemeSwitcher(activity, prefs)
+ }
}
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/about/AboutActivity.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/about/AboutActivity.java
index fc098b3c9..58cf49681 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/about/AboutActivity.java
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/about/AboutActivity.java
@@ -21,6 +21,7 @@ package org.isoron.uhabits.activities.about;
import android.os.*;
+import org.isoron.uhabits.*;
import org.isoron.uhabits.activities.*;
/**
@@ -33,8 +34,12 @@ public class AboutActivity extends HabitsActivity
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
- AboutScreen screen = getComponent().getAboutScreen();
- screen.setRootView(getComponent().getAboutRootView());
+ HabitsApplication app = (HabitsApplication) getApplication();
+ AboutScreen screen = new AboutScreen(this,
+ app.getComponent().getIntentFactory(),
+ app.getComponent().getPreferences());
+ AboutRootView rootView = new AboutRootView(this, screen);
+ screen.setRootView(rootView);
setScreen(screen);
}
}
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/about/AboutRootView.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/about/AboutRootView.java
index 1a88668a1..68cb68551 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/about/AboutRootView.java
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/about/AboutRootView.java
@@ -20,14 +20,14 @@
package org.isoron.uhabits.activities.about;
import android.content.*;
-import android.support.annotation.*;
import android.widget.*;
+import androidx.annotation.*;
+
import org.isoron.androidbase.activities.*;
import org.isoron.androidbase.utils.*;
import org.isoron.uhabits.BuildConfig;
import org.isoron.uhabits.R;
-import org.isoron.uhabits.core.ui.screens.about.*;
import javax.inject.*;
@@ -39,14 +39,14 @@ public class AboutRootView extends BaseRootView
TextView tvVersion;
@NonNull
- private final AboutBehavior behavior;
+ private final AboutScreen screen;
@Inject
public AboutRootView(@NonNull @ActivityContext Context context,
- @NonNull AboutBehavior behavior)
+ @NonNull AboutScreen screen)
{
super(context);
- this.behavior = behavior;
+ this.screen = screen;
addView(inflate(getContext(), R.layout.about, null));
ButterKnife.bind(this);
@@ -70,31 +70,43 @@ public class AboutRootView extends BaseRootView
@OnClick(R.id.tvFeedback)
public void onClickFeedback()
{
- behavior.onSendFeedback();
+ screen.showSendFeedbackScreen();
}
@OnClick(R.id.tvVersion)
public void onClickIcon()
{
- behavior.onPressDeveloperCountdown();
+ screen.onPressDeveloperCountdown();
}
@OnClick(R.id.tvRate)
public void onClickRate()
{
- behavior.onRateApp();
+ screen.showRateAppWebsite();
}
@OnClick(R.id.tvSource)
public void onClickSource()
{
- behavior.onViewSourceCode();
+ screen.showSourceCodeWebsite();
}
@OnClick(R.id.tvTranslate)
public void onClickTranslate()
{
- behavior.onTranslateApp();
+ screen.showTranslationWebsite();
+ }
+
+ @OnClick(R.id.tvPrivacy)
+ public void onClickPrivacy()
+ {
+ screen.showPrivacyPolicyWebsite();
+ }
+
+ @OnClick(R.id.tvContributors)
+ public void onClickContributors()
+ {
+ screen.showCodeContributorsWebsite();
}
@Override
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/about/AboutScreen.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/about/AboutScreen.java
index c8c5808c4..c5b668ae8 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/about/AboutScreen.java
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/about/AboutScreen.java
@@ -19,59 +19,69 @@
package org.isoron.uhabits.activities.about;
-import android.support.annotation.*;
import android.widget.*;
+import androidx.annotation.*;
+
import org.isoron.androidbase.activities.*;
-import org.isoron.uhabits.core.ui.screens.about.*;
+import org.isoron.uhabits.core.preferences.*;
import org.isoron.uhabits.intents.*;
-import javax.inject.*;
+public class AboutScreen extends BaseScreen
+{
+ @NonNull
+ private final Preferences prefs;
-import static org.isoron.uhabits.core.ui.screens.about.AboutBehavior.Message.*;
+ private int developerCountdown = 5;
-public class AboutScreen extends BaseScreen implements AboutBehavior.Screen
-{
@NonNull
private final IntentFactory intents;
- @Inject
public AboutScreen(@NonNull BaseActivity activity,
- @NonNull IntentFactory intents)
+ @NonNull IntentFactory intents,
+ @NonNull Preferences prefs)
{
super(activity);
this.intents = intents;
+ this.prefs = prefs;
}
- @Override
- public void showMessage(AboutBehavior.Message message)
- {
- if (message == YOU_ARE_NOW_A_DEVELOPER) Toast
- .makeText(activity, "You are now a developer", Toast.LENGTH_LONG)
- .show();
- }
-
- @Override
public void showRateAppWebsite()
{
activity.startActivity(intents.rateApp(activity));
}
- @Override
public void showSendFeedbackScreen()
{
activity.startActivity(intents.sendFeedback(activity));
}
- @Override
public void showSourceCodeWebsite()
{
activity.startActivity(intents.viewSourceCode(activity));
}
- @Override
public void showTranslationWebsite()
{
activity.startActivity(intents.helpTranslate(activity));
}
+
+ public void showPrivacyPolicyWebsite()
+ {
+ activity.startActivity(intents.privacyPolicy(activity));
+ }
+
+ public void showCodeContributorsWebsite()
+ {
+ activity.startActivity(intents.codeContributors(activity));
+ }
+
+ public void onPressDeveloperCountdown()
+ {
+ developerCountdown--;
+ if (developerCountdown == 0) {
+ prefs.setDeveloper(true);
+ Toast.makeText(activity, "You are now a developer", Toast.LENGTH_LONG).show();
+ }
+ }
}
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/ConfirmDeleteDialog.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/ConfirmDeleteDialog.java
index 445cfb8d0..25bbd2a6c 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/ConfirmDeleteDialog.java
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/ConfirmDeleteDialog.java
@@ -20,8 +20,9 @@
package org.isoron.uhabits.activities.common.dialogs;
import android.content.*;
-import android.support.annotation.*;
-import android.support.v7.app.*;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.*;
import com.google.auto.factory.*;
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/FrequencyPickerDialog.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/FrequencyPickerDialog.kt
new file mode 100644
index 000000000..de40e86bd
--- /dev/null
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/FrequencyPickerDialog.kt
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2016 Álinson Santos Xavier
+ *
+ * This file is part of Loop Habit Tracker.
+ *
+ * Loop Habit Tracker is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * Loop Habit Tracker is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see .
+ */
+
+package org.isoron.uhabits.activities.common.dialogs
+
+import android.app.*
+import android.os.*
+import android.util.*
+import android.view.*
+import android.widget.*
+import androidx.appcompat.app.*
+import androidx.appcompat.app.AlertDialog
+import kotlinx.android.synthetic.main.frequency_picker_dialog.view.*
+import org.isoron.uhabits.*
+
+
+class FrequencyPickerDialog(var freqNumerator: Int,
+ var freqDenominator: Int
+ ) : AppCompatDialogFragment() {
+
+ lateinit var contentView: View
+ var onFrequencyPicked: (num: Int, den: Int) -> Unit = {_,_ -> }
+
+ constructor() : this(1, 1)
+
+ override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+ val inflater = LayoutInflater.from(activity!!)
+ contentView = inflater.inflate(R.layout.frequency_picker_dialog, null)
+
+ contentView.everyDayRadioButton.setOnClickListener {
+ check(contentView.everyDayRadioButton)
+ unfocusAll()
+ }
+
+ contentView.everyXDaysRadioButton.setOnClickListener {
+ check(contentView.everyXDaysRadioButton)
+ val everyXDaysTextView = contentView.everyXDaysTextView
+ focus(everyXDaysTextView)
+ }
+
+ contentView.everyXDaysTextView.setOnFocusChangeListener { v, hasFocus ->
+ if(hasFocus) check(contentView.everyXDaysRadioButton)
+ }
+
+ contentView.xTimesPerWeekRadioButton.setOnClickListener {
+ check(contentView.xTimesPerWeekRadioButton)
+ focus(contentView.xTimesPerWeekTextView)
+ }
+
+ contentView.xTimesPerWeekTextView.setOnFocusChangeListener { v, hasFocus ->
+ if(hasFocus) check(contentView.xTimesPerWeekRadioButton)
+ }
+
+ contentView.xTimesPerMonthRadioButton.setOnClickListener {
+ check(contentView.xTimesPerMonthRadioButton)
+ focus(contentView.xTimesPerMonthTextView)
+ }
+
+ contentView.xTimesPerMonthTextView.setOnFocusChangeListener { v, hasFocus ->
+ if(hasFocus) check(contentView.xTimesPerMonthRadioButton)
+ }
+
+ return AlertDialog.Builder(activity!!)
+ .setView(contentView)
+ .setPositiveButton(R.string.save) { _, _ -> onSaveClicked() }
+ .create()
+ }
+
+ private fun onSaveClicked() {
+ var numerator = 1
+ var denominator = 1
+ when {
+ contentView.everyDayRadioButton.isChecked -> {
+ // NOP
+ }
+ contentView.everyXDaysRadioButton.isChecked -> {
+ if (contentView.everyXDaysTextView.text.isNotEmpty()) {
+ denominator = Integer.parseInt(contentView.everyXDaysTextView.text.toString())
+ }
+ }
+ contentView.xTimesPerWeekRadioButton.isChecked -> {
+ if (contentView.xTimesPerWeekTextView.text.isNotEmpty()) {
+ numerator = Integer.parseInt(contentView.xTimesPerWeekTextView.text.toString())
+ denominator = 7
+ }
+ }
+ else -> {
+ if (contentView.xTimesPerMonthTextView.text.isNotEmpty()) {
+ numerator = Integer.parseInt(contentView.xTimesPerMonthTextView.text.toString())
+ denominator = 30
+ }
+ }
+ }
+ if (numerator >= denominator || numerator < 1) {
+ numerator = 1
+ denominator = 1
+ }
+ onFrequencyPicked(numerator, denominator)
+ dismiss()
+ }
+
+ private fun check(view: RadioButton?) {
+ uncheckAll()
+ view?.isChecked = true
+ view?.requestFocus()
+ }
+
+ override fun onResume() {
+ super.onResume()
+ populateViews()
+ }
+
+ private fun populateViews() {
+ uncheckAll()
+ if (freqNumerator == 1) {
+ if(freqDenominator == 1) {
+ contentView.everyDayRadioButton.isChecked = true
+ } else {
+ contentView.everyXDaysRadioButton.isChecked = true
+ contentView.everyXDaysTextView.setText(freqDenominator.toString())
+ focus(contentView.everyXDaysTextView)
+ }
+ } else {
+ if(freqDenominator == 7) {
+ contentView.xTimesPerWeekRadioButton.isChecked = true
+ contentView.xTimesPerWeekTextView.setText(freqNumerator.toString())
+ focus(contentView.xTimesPerWeekTextView)
+ } else if (freqDenominator == 30 || freqDenominator == 31) {
+ contentView.xTimesPerMonthRadioButton.isChecked = true
+ contentView.xTimesPerMonthTextView.setText(freqNumerator.toString())
+ focus(contentView.xTimesPerMonthTextView)
+ } else {
+ Log.w("FrequencyPickerDialog", "Unknown frequency: $freqNumerator/$freqDenominator")
+ contentView.everyDayRadioButton.isChecked = true
+ }
+ }
+ }
+
+ private fun focus(view: EditText) {
+ view.requestFocus()
+ view.setSelection(view.text.length)
+ }
+
+ private fun uncheckAll() {
+ contentView.everyDayRadioButton.isChecked = false
+ contentView.everyXDaysRadioButton.isChecked = false
+ contentView.xTimesPerWeekRadioButton.isChecked = false
+ contentView.xTimesPerMonthRadioButton.isChecked = false
+ }
+
+ private fun unfocusAll() {
+ contentView.everyXDaysTextView.clearFocus()
+ contentView.xTimesPerWeekTextView.clearFocus()
+ contentView.xTimesPerMonthTextView.clearFocus()
+ }
+}
\ No newline at end of file
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/HistoryEditorDialog.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/HistoryEditorDialog.java
index 0a76b52fc..a3c232cc7 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/HistoryEditorDialog.java
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/HistoryEditorDialog.java
@@ -22,14 +22,17 @@ package org.isoron.uhabits.activities.common.dialogs;
import android.app.*;
import android.content.*;
import android.os.*;
-import android.support.annotation.*;
-import android.support.v7.app.AlertDialog;
-import android.support.v7.app.*;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.app.*;
import android.util.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.activities.common.views.*;
import org.isoron.uhabits.core.models.*;
+import org.isoron.uhabits.core.preferences.*;
import org.isoron.uhabits.core.tasks.*;
import org.isoron.uhabits.utils.*;
@@ -51,6 +54,8 @@ public class HistoryEditorDialog extends AppCompatDialogFragment
private TaskRunner taskRunner;
+ private Preferences prefs;
+
public HistoryEditorDialog()
{
this.controller = new Controller() {};
@@ -72,9 +77,11 @@ public class HistoryEditorDialog extends AppCompatDialogFragment
(HabitsApplication) getActivity().getApplicationContext();
habitList = app.getComponent().getHabitList();
taskRunner = app.getComponent().getTaskRunner();
+ prefs = app.getComponent().getPreferences();
historyChart = new HistoryChart(context);
historyChart.setController(controller);
+ historyChart.setFirstWeekday(prefs.getFirstWeekday());
if (savedInstanceState != null)
{
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/NumberPickerFactory.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/NumberPickerFactory.kt
index cfbacaca5..4bfaed855 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/NumberPickerFactory.kt
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/NumberPickerFactory.kt
@@ -20,7 +20,7 @@
package org.isoron.uhabits.activities.common.dialogs
import android.content.*
-import android.support.v7.app.*
+import androidx.appcompat.app.*
import android.text.*
import android.view.*
import android.view.inputmethod.*
@@ -70,7 +70,7 @@ class NumberPickerFactory
}
.create()
- dialog.window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE)
+ dialog.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE)
InterfaceUtils.setupEditorAction(picker) { _, actionId, _ ->
if (actionId == EditorInfo.IME_ACTION_DONE)
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/WeekdayPickerDialog.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/WeekdayPickerDialog.java
index 91f2012b9..6f5b0ee06 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/WeekdayPickerDialog.java
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/WeekdayPickerDialog.java
@@ -19,15 +19,19 @@
package org.isoron.uhabits.activities.common.dialogs;
-import android.app.*;
-import android.content.*;
-import android.os.*;
-import android.support.v7.app.AlertDialog;
-import android.support.v7.app.*;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.app.AppCompatDialogFragment;
-import org.isoron.uhabits.*;
-import org.isoron.uhabits.core.models.*;
-import org.isoron.uhabits.core.utils.*;
+import org.isoron.uhabits.R;
+import org.isoron.uhabits.core.models.WeekdayList;
+import org.isoron.uhabits.core.utils.DateUtils;
+
+import java.util.Calendar;
/**
* Dialog that allows the user to pick one or more days of the week.
@@ -36,6 +40,7 @@ public class WeekdayPickerDialog extends AppCompatDialogFragment implements
DialogInterface.OnMultiChoiceClickListener,
DialogInterface.OnClickListener
{
+ private static final String KEY_SELECTED_DAYS = "selectedDays";
private boolean[] selectedDays;
private OnWeekdaysPickedListener listener;
@@ -46,6 +51,21 @@ public class WeekdayPickerDialog extends AppCompatDialogFragment implements
selectedDays[which] = isChecked;
}
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if(savedInstanceState != null){
+ selectedDays = savedInstanceState.getBooleanArray(KEY_SELECTED_DAYS);
+ }
+ }
+
+ @Override
+ public void onSaveInstanceState(@NonNull Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putBooleanArray(KEY_SELECTED_DAYS, selectedDays);
+ }
+
@Override
public void onClick(DialogInterface dialog, int which)
{
@@ -59,8 +79,9 @@ public class WeekdayPickerDialog extends AppCompatDialogFragment implements
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder
.setTitle(R.string.select_weekdays)
- .setMultiChoiceItems(DateUtils.getLongDayNames(), selectedDays,
- this)
+ .setMultiChoiceItems(DateUtils.getLongWeekdayNames(Calendar.SATURDAY),
+ selectedDays,
+ this)
.setPositiveButton(android.R.string.yes, this)
.setNegativeButton(android.R.string.cancel, (dialog, which) -> {
dismiss();
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/BarChart.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/BarChart.java
index f49f486fa..9e746b3f0 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/BarChart.java
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/BarChart.java
@@ -21,9 +21,11 @@ package org.isoron.uhabits.activities.common.views;
import android.content.*;
import android.graphics.*;
-import android.support.annotation.*;
import android.util.*;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import org.isoron.androidbase.utils.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.activities.habits.list.views.*;
@@ -267,16 +269,22 @@ public class BarChart extends ScrollableChart
private void drawBar(Canvas canvas, RectF rect, double value)
{
float margin = baseSize * 0.225f;
+ float round = dpToPixels(getContext(), 2);
int color = textColor;
if (Habit.checkMarkValueToDouble(value) >= target) color = primaryColor;
rect.inset(-margin, 0);
setModeOrColor(pGraph, XFERMODE_CLEAR, backgroundColor);
- canvas.drawRect(rect, pGraph);
+ canvas.drawRoundRect(rect, round, round, pGraph);
rect.inset(margin, 0);
setModeOrColor(pGraph, XFERMODE_SRC, color);
+ canvas.drawRoundRect(rect, round, round, pGraph);
+ rect.set(rect.left,
+ rect.top + rect.height() / 2.0f,
+ rect.right,
+ rect.bottom);
canvas.drawRect(rect, pGraph);
if (isTransparencyEnabled) pGraph.setXfermode(XFERMODE_SRC);
@@ -433,7 +441,7 @@ public class BarChart extends ScrollableChart
primaryColor = Color.BLACK;
textColor = res.getColor(R.attr.mediumContrastTextColor);
gridColor = res.getColor(R.attr.lowContrastTextColor);
- backgroundColor = res.getColor(R.attr.cardBackgroundColor);
+ backgroundColor = res.getColor(R.attr.cardBgColor);
}
private void initDateFormats()
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/BundleSavedState.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/BundleSavedState.java
index 6f9e1163d..39fe209fe 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/BundleSavedState.java
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/BundleSavedState.java
@@ -20,9 +20,10 @@
package org.isoron.uhabits.activities.common.views;
import android.os.*;
-import android.support.v4.os.*;
+import androidx.core.os.*;
+import androidx.customview.view.AbsSavedState;
-public class BundleSavedState extends android.support.v4.view.AbsSavedState
+public class BundleSavedState extends AbsSavedState
{
public static final Parcelable.Creator CREATOR =
ParcelableCompat.newCreator(
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/FrequencyChart.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/FrequencyChart.java
index 8b6437638..4d365a663 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/FrequencyChart.java
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/FrequencyChart.java
@@ -21,9 +21,10 @@ package org.isoron.uhabits.activities.common.views;
import android.content.*;
import android.graphics.*;
-import android.support.annotation.*;
import android.util.*;
+import androidx.annotation.NonNull;
+
import org.isoron.androidbase.utils.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.core.models.*;
@@ -69,8 +70,11 @@ public class FrequencyChart extends ScrollableChart
@NonNull
private HashMap frequency;
+
private int maxFreq;
+ private int firstWeekday = Calendar.SUNDAY;
+
public FrequencyChart(Context context)
{
super(context);
@@ -98,6 +102,12 @@ public class FrequencyChart extends ScrollableChart
postInvalidate();
}
+ public void setFirstWeekday(int firstWeekday)
+ {
+ this.firstWeekday = firstWeekday;
+ postInvalidate();
+ }
+
private int getMaxFreq(HashMap frequency)
{
int maxValue = 1;
@@ -144,7 +154,6 @@ public class FrequencyChart extends ScrollableChart
prevRect.setEmpty();
GregorianCalendar currentDate = DateUtils.getStartOfTodayCalendar();
-
currentDate.set(Calendar.DAY_OF_MONTH, 1);
currentDate.add(Calendar.MONTH, -nColumns + 2 - getDataOffset());
@@ -193,11 +202,11 @@ public class FrequencyChart extends ScrollableChart
private void drawColumn(Canvas canvas, RectF rect, GregorianCalendar date)
{
- Integer values[] = frequency.get(new Timestamp(date));
+ Integer[] values = frequency.get(new Timestamp(date));
float rowHeight = rect.height() / 8.0f;
prevRect.set(rect);
- Integer[] localeWeekdayList = DateUtils.getLocaleWeekdayList();
+ int[] localeWeekdayList = DateUtils.getWeekdaySequence(firstWeekday);
for (int j = 0; j < localeWeekdayList.length; j++)
{
rect.set(0, 0, baseSize, baseSize);
@@ -233,7 +242,7 @@ public class FrequencyChart extends ScrollableChart
pText.setColor(textColor);
pGrid.setColor(gridColor);
- for (String day : DateUtils.getLocaleDayNames(Calendar.SHORT))
+ for (String day : DateUtils.getShortWeekdayNames(firstWeekday))
{
canvas.drawText(day, rGrid.right - columnWidth,
rGrid.top + rowHeight / 2 + 0.25f * em, pText);
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/HistoryChart.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/HistoryChart.java
index f2f0f3414..93b488b78 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/HistoryChart.java
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/HistoryChart.java
@@ -22,10 +22,12 @@ package org.isoron.uhabits.activities.common.views;
import android.content.*;
import android.graphics.*;
import android.graphics.Paint.*;
-import android.support.annotation.*;
import android.util.*;
import android.view.*;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import org.isoron.androidbase.utils.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.core.models.*;
@@ -93,6 +95,8 @@ public class HistoryChart extends ScrollableChart
private boolean isNumerical = false;
+ private int firstWeekday = Calendar.SUNDAY;
+
@NonNull
private Controller controller;
@@ -210,6 +214,12 @@ public class HistoryChart extends ScrollableChart
postInvalidate();
}
+ public void setFirstWeekday(int firstWeekday)
+ {
+ this.firstWeekday = firstWeekday;
+ postInvalidate();
+ }
+
protected void initPaints()
{
pTextHeader = new Paint();
@@ -293,7 +303,7 @@ public class HistoryChart extends ScrollableChart
{
float verticalOffset = pTextHeader.getFontSpacing() * 0.4f;
- for (String day : DateUtils.getLocaleDayNames(Calendar.SHORT))
+ for (String day : DateUtils.getShortWeekdayNames(firstWeekday))
{
location.offset(0, columnWidth);
canvas.drawText(day, location.left + headerTextOffset,
@@ -365,7 +375,8 @@ public class HistoryChart extends ScrollableChart
}
pSquareFg.setColor(reverseTextColor);
- canvas.drawRect(location, pSquareBg);
+ float round = dpToPixels(getContext(), 2);
+ canvas.drawRoundRect(location, round, round, pSquareBg);
String text = Integer.toString(date.get(Calendar.DAY_OF_MONTH));
canvas.drawText(text, location.centerX(),
location.centerY() + squareTextOffset, pSquareFg);
@@ -375,7 +386,7 @@ public class HistoryChart extends ScrollableChart
{
float width = 0;
- for (String w : DateUtils.getLocaleDayNames(Calendar.SHORT))
+ for (String w : DateUtils.getShortWeekdayNames(firstWeekday))
width = Math.max(width, pSquareFg.measureText(w));
return width;
@@ -473,7 +484,7 @@ public class HistoryChart extends ScrollableChart
int realWeekday =
DateUtils.getStartOfTodayCalendar().get(Calendar.DAY_OF_WEEK);
todayPositionInColumn =
- (7 + realWeekday - baseDate.getFirstDayOfWeek()) % 7;
+ (7 + realWeekday - firstWeekday) % 7;
baseDate.add(Calendar.DAY_OF_YEAR, -nDays);
baseDate.add(Calendar.DAY_OF_YEAR, -todayPositionInColumn);
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/RingView.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/RingView.java
index 0a29ca599..1e2245eb1 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/RingView.java
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/RingView.java
@@ -21,11 +21,12 @@ package org.isoron.uhabits.activities.common.views;
import android.content.*;
import android.graphics.*;
-import android.support.annotation.*;
import android.text.*;
import android.util.*;
import android.view.*;
+import androidx.annotation.Nullable;
+
import org.isoron.androidbase.utils.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.utils.*;
@@ -242,7 +243,7 @@ public class RingView extends View
StyledResources res = new StyledResources(getContext());
if (backgroundColor == null)
- backgroundColor = res.getColor(R.attr.cardBackgroundColor);
+ backgroundColor = res.getColor(R.attr.cardBgColor);
if (inactiveColor == null)
inactiveColor = res.getColor(R.attr.highContrastTextColor);
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/ScoreChart.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/ScoreChart.java
index 54975059b..74b8ad4b3 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/ScoreChart.java
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/ScoreChart.java
@@ -21,9 +21,11 @@ package org.isoron.uhabits.activities.common.views;
import android.content.*;
import android.graphics.*;
-import android.support.annotation.*;
import android.util.*;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import org.isoron.androidbase.utils.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.core.models.*;
@@ -405,7 +407,7 @@ public class ScoreChart extends ScrollableChart
primaryColor = Color.BLACK;
textColor = res.getColor(R.attr.mediumContrastTextColor);
gridColor = res.getColor(R.attr.lowContrastTextColor);
- backgroundColor = res.getColor(R.attr.cardBackgroundColor);
+ backgroundColor = res.getColor(R.attr.cardBgColor);
}
private void initDateFormats()
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/ScrollableChart.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/ScrollableChart.java
index ae880c264..fe0b68369 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/ScrollableChart.java
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/ScrollableChart.java
@@ -217,6 +217,13 @@ public abstract class ScrollableChart extends View
scrollController = new ScrollController() {};
}
+ public void reset()
+ {
+ scroller.setFinalX(0);
+ scroller.computeScrollOffset();
+ updateDataOffset();
+ }
+
private void updateDataOffset()
{
int newDataOffset = scroller.getCurrX() / scrollerBucketSize;
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/StreakChart.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/StreakChart.java
index 5020f85a4..705537ba4 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/StreakChart.java
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/StreakChart.java
@@ -34,7 +34,7 @@ import java.text.*;
import java.util.*;
import static android.view.View.MeasureSpec.*;
-import static org.isoron.androidbase.utils.InterfaceUtils.getDimension;
+import static org.isoron.androidbase.utils.InterfaceUtils.*;
public class StreakChart extends View
{
@@ -202,8 +202,14 @@ public class StreakChart extends View
paint.setColor(percentageToColor(percentage));
- canvas.drawRect(rect.left + gap, rect.top + paddingTopBottom,
- rect.right - gap, rect.bottom - paddingTopBottom, paint);
+ float round = dpToPixels(getContext(), 2);
+ canvas.drawRoundRect(rect.left + gap,
+ rect.top + paddingTopBottom,
+ rect.right - gap,
+ rect.bottom - paddingTopBottom,
+ round,
+ round,
+ paint);
float yOffset = rect.centerY() + 0.3f * em;
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/TargetChart.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/TargetChart.java
new file mode 100644
index 000000000..30ec17bd9
--- /dev/null
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/views/TargetChart.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2016 Álinson Santos Xavier
+ *
+ * This file is part of Loop Habit Tracker.
+ *
+ * Loop Habit Tracker is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * Loop Habit Tracker is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see .
+ */
+
+package org.isoron.uhabits.activities.common.views;
+
+import android.content.*;
+import android.graphics.*;
+import android.util.*;
+import android.view.*;
+
+import org.isoron.androidbase.utils.*;
+import org.isoron.uhabits.*;
+import org.isoron.uhabits.activities.habits.list.views.*;
+
+import java.util.*;
+
+import static android.view.View.MeasureSpec.*;
+import static org.isoron.androidbase.utils.InterfaceUtils.*;
+
+public class TargetChart extends View
+{
+ private Paint paint;
+ private int baseSize;
+ private int primaryColor;
+ private int mediumContrastTextColor;
+ private int highContrastReverseTextColor;
+ private int lowContrastTextColor;
+ private RectF rect = new RectF();
+ private RectF barRect = new RectF();
+ private List values = Collections.emptyList();
+ private List labels = Collections.emptyList();
+ private List targets = Collections.emptyList();
+ private float maxLabelSize;
+ private float tinyTextSize;
+
+ public TargetChart(Context context)
+ {
+ super(context);
+ init();
+ }
+
+ public TargetChart(Context context, AttributeSet attrs)
+ {
+ super(context, attrs);
+ init();
+ }
+
+ public void populateWithRandomData()
+ {
+ labels = new ArrayList<>();
+ values = new ArrayList<>();
+ targets = new ArrayList<>();
+ for (int i = 0; i < 5; i++) {
+ double percentage = new Random().nextDouble();
+ targets.add(new Random().nextDouble() * 1000.0);
+ values.add(targets.get(i) * percentage * 1.2);
+ labels.add(String.format(Locale.US, "Label %d", i + 1));
+ }
+ }
+
+ public void setColor(int color)
+ {
+ this.primaryColor = color;
+ postInvalidate();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas)
+ {
+ super.onDraw(canvas);
+ if (labels.size() == 0) return;
+
+ maxLabelSize = 0;
+ for (String label : labels) {
+ paint.setTextSize(tinyTextSize);
+ float len = paint.measureText(label);
+ maxLabelSize = Math.max(maxLabelSize, len);
+ }
+
+ float marginTop = (getHeight() - baseSize * labels.size()) / 2.0f;
+ rect.set(0, marginTop, getWidth(), marginTop + baseSize);
+ for (int i = 0; i < labels.size(); i++) {
+ drawRow(canvas, i, rect);
+ rect.offset(0, baseSize);
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthSpec, int heightSpec)
+ {
+ baseSize = getResources().getDimensionPixelSize(R.dimen.baseSize);
+
+ int width = getSize(widthSpec);
+ int height = labels.size() * baseSize;
+
+ ViewGroup.LayoutParams params = getLayoutParams();
+ if (params != null && params.height == ViewGroup.LayoutParams.MATCH_PARENT) {
+ height = getSize(heightSpec);
+ if (labels.size() > 0) baseSize = height / labels.size();
+ }
+
+ heightSpec = makeMeasureSpec(height, EXACTLY);
+ widthSpec = makeMeasureSpec(width, EXACTLY);
+ setMeasuredDimension(widthSpec, heightSpec);
+ }
+
+ private void drawRow(Canvas canvas, int row, RectF rect)
+ {
+ float padding = dpToPixels(getContext(), 4);
+ float round = dpToPixels(getContext(), 2);
+ float stop = maxLabelSize + padding * 2;
+
+ paint.setColor(mediumContrastTextColor);
+
+ // Draw label
+ paint.setTextSize(tinyTextSize);
+ paint.setTextAlign(Paint.Align.RIGHT);
+ float yTextAdjust = (paint.descent() + paint.ascent()) / 2.0f;
+ canvas.drawText(labels.get(row),
+ rect.left + stop - padding,
+ rect.centerY() - yTextAdjust,
+ paint);
+
+ // Draw background box
+ paint.setColor(lowContrastTextColor);
+ barRect.set(rect.left + stop + padding,
+ rect.top + baseSize * 0.05f,
+ rect.right - padding,
+ rect.bottom - baseSize * 0.05f);
+ canvas.drawRoundRect(barRect, round, round, paint);
+
+ float percentage = (float) (values.get(row) / targets.get(row));
+ percentage = Math.min(1.0f, percentage);
+
+ // Draw completed box
+ float completedWidth = percentage * barRect.width();
+ if (completedWidth > 0 && completedWidth < 2 * round) {
+ completedWidth = 2 * round;
+ }
+ float remainingWidth = barRect.width() - completedWidth;
+
+ paint.setColor(primaryColor);
+ barRect.set(barRect.left,
+ barRect.top,
+ barRect.left + completedWidth,
+ barRect.bottom);
+ canvas.drawRoundRect(barRect, round, round, paint);
+
+ // Draw values
+ paint.setColor(Color.WHITE);
+ paint.setTextSize(tinyTextSize);
+ paint.setTextAlign(Paint.Align.CENTER);
+ yTextAdjust = (paint.descent() + paint.ascent()) / 2.0f;
+
+ double remaining = targets.get(row) - values.get(row);
+ String completedText = NumberButtonViewKt.toShortString(values.get(row));
+ String remainingText = NumberButtonViewKt.toShortString(remaining);
+
+ if (completedWidth > paint.measureText(completedText) + 2 * padding) {
+ paint.setColor(highContrastReverseTextColor);
+ canvas.drawText(completedText,
+ barRect.centerX(),
+ barRect.centerY() - yTextAdjust,
+ paint);
+ }
+
+ if (remainingWidth > paint.measureText(remainingText) + 2 * padding) {
+ paint.setColor(mediumContrastTextColor);
+ barRect.set(rect.left + stop + padding + completedWidth,
+ barRect.top,
+ rect.right - padding,
+ barRect.bottom);
+ canvas.drawText(remainingText,
+ barRect.centerX(),
+ barRect.centerY() - yTextAdjust,
+ paint);
+ }
+ }
+
+ private void init()
+ {
+ paint = new Paint();
+ paint.setTextAlign(Paint.Align.CENTER);
+ paint.setAntiAlias(true);
+
+ StyledResources res = new StyledResources(getContext());
+ lowContrastTextColor = res.getColor(R.attr.lowContrastTextColor);
+ mediumContrastTextColor = res.getColor(R.attr.mediumContrastTextColor);
+ highContrastReverseTextColor = res.getColor(R.attr.highContrastReverseTextColor);
+ tinyTextSize = getDimension(getContext(), R.dimen.tinyTextSize);
+ }
+
+ public void setValues(List values)
+ {
+ this.values = values;
+ requestLayout();
+ }
+
+ public void setLabels(List labels)
+ {
+ this.labels = labels;
+ requestLayout();
+ }
+
+ public void setTargets(List targets)
+ {
+ this.targets = targets;
+ requestLayout();
+ }
+}
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/edit/EditHabitActivity.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/edit/EditHabitActivity.kt
new file mode 100644
index 000000000..333cd4dc4
--- /dev/null
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/edit/EditHabitActivity.kt
@@ -0,0 +1,306 @@
+/*
+ * Copyright (C) 2016 Álinson Santos Xavier
+ *
+ * This file is part of Loop Habit Tracker.
+ *
+ * Loop Habit Tracker is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * Loop Habit Tracker is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see .
+ */
+
+package org.isoron.uhabits.activities.habits.edit
+
+import android.content.res.*
+import android.graphics.*
+import android.os.*
+import android.text.format.*
+import android.view.*
+import android.widget.*
+import androidx.appcompat.app.*
+import androidx.fragment.app.*
+import com.android.datetimepicker.time.*
+import kotlinx.android.synthetic.main.activity_edit_habit.*
+import org.isoron.androidbase.utils.*
+import org.isoron.uhabits.*
+import org.isoron.uhabits.activities.*
+import org.isoron.uhabits.activities.common.dialogs.*
+import org.isoron.uhabits.core.commands.*
+import org.isoron.uhabits.core.models.*
+import org.isoron.uhabits.databinding.*
+import org.isoron.uhabits.utils.*
+import kotlin.math.*
+
+
+class EditHabitActivity : AppCompatActivity() {
+
+ private lateinit var themeSwitcher: AndroidThemeSwitcher
+ private lateinit var binding: ActivityEditHabitBinding
+ private lateinit var commandRunner: CommandRunner
+
+ var habitId = -1L
+ var habitType = -1
+ var unit = ""
+ var paletteColor = 11
+ var androidColor = 0
+ var freqNum = 1
+ var freqDen = 1
+ var reminderHour = -1
+ var reminderMin = -1
+ var reminderDays: WeekdayList = WeekdayList.EVERY_DAY
+
+ override fun onCreate(state: Bundle?) {
+ super.onCreate(state)
+
+ val component = (application as HabitsApplication).component
+ themeSwitcher = AndroidThemeSwitcher(this, component.preferences)
+ themeSwitcher.apply()
+
+ binding = ActivityEditHabitBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+
+ if (intent.hasExtra("habitId")) {
+ binding.toolbar.title = getString(R.string.edit_habit)
+ habitId = intent.getLongExtra("habitId", -1)
+ val habit = component.habitList.getById(habitId)!!
+ habitType = habit.type
+ paletteColor = habit.color
+ freqNum = habit.frequency.numerator
+ freqDen = habit.frequency.denominator
+ if (habit.hasReminder()) {
+ reminderHour = habit.reminder.hour
+ reminderMin = habit.reminder.minute
+ reminderDays = habit.reminder.days
+ }
+ binding.nameInput.setText(habit.name)
+ binding.questionInput.setText(habit.question)
+ binding.notesInput.setText(habit.description)
+ binding.unitInput.setText(habit.unit)
+ binding.targetInput.setText(habit.targetValue.toString())
+ } else {
+ habitType = intent.getIntExtra("habitType", Habit.YES_NO_HABIT)
+ }
+
+ if (state != null) {
+ habitId = state.getLong("habitId")
+ habitType = state.getInt("habitType")
+ paletteColor = state.getInt("paletteColor")
+ freqNum = state.getInt("freqNum")
+ freqDen = state.getInt("freqDen")
+ reminderHour = state.getInt("reminderHour")
+ reminderMin = state.getInt("reminderMin")
+ reminderDays = WeekdayList(state.getInt("reminderDays"))
+ }
+
+ updateColors()
+
+ if (habitType == Habit.YES_NO_HABIT) {
+ binding.unitOuterBox.visibility = View.GONE
+ binding.targetOuterBox.visibility = View.GONE
+ } else {
+ binding.nameInput.hint = getString(R.string.measurable_short_example)
+ binding.questionInput.hint = getString(R.string.measurable_question_example)
+ binding.frequencyOuterBox.visibility = View.GONE
+ }
+
+ setSupportActionBar(binding.toolbar)
+ supportActionBar?.setDisplayHomeAsUpEnabled(true)
+ supportActionBar?.setDisplayShowHomeEnabled(true)
+ supportActionBar?.elevation = 10.0f
+
+ val colorPickerDialogFactory = ColorPickerDialogFactory(this)
+ binding.colorButton.setOnClickListener {
+ val dialog = colorPickerDialogFactory.create(paletteColor)
+ dialog.setListener { paletteColor ->
+ this.paletteColor = paletteColor
+ updateColors()
+ }
+ dialog.show(supportFragmentManager, "colorPicker")
+ }
+
+ populateFrequency()
+ binding.booleanFrequencyPicker.setOnClickListener {
+ val dialog = FrequencyPickerDialog(freqNum, freqDen)
+ dialog.onFrequencyPicked = { num, den ->
+ freqNum = num
+ freqDen = den
+ populateFrequency()
+ }
+ dialog.show(supportFragmentManager, "frequencyPicker")
+ }
+
+ binding.numericalFrequencyPicker.setOnClickListener {
+ val builder = AlertDialog.Builder(this)
+ val arrayAdapter = ArrayAdapter(this, android.R.layout.select_dialog_item)
+ arrayAdapter.add(getString(R.string.every_day))
+ arrayAdapter.add(getString(R.string.every_week))
+ arrayAdapter.add(getString(R.string.every_month))
+ builder.setAdapter(arrayAdapter) { dialog, which ->
+ freqDen = when(which) {
+ 1 -> 7
+ 2 -> 30
+ else -> 1
+ }
+ populateFrequency()
+ dialog.dismiss()
+ }
+ builder.show()
+ }
+
+ populateReminder()
+ binding.reminderTimePicker.setOnClickListener {
+ val currentHour = if (reminderHour >= 0) reminderHour else 8
+ val currentMin = if (reminderMin >= 0) reminderMin else 0
+ val is24HourMode = DateFormat.is24HourFormat(this)
+ val dialog = TimePickerDialog.newInstance(object : TimePickerDialog.OnTimeSetListener {
+ override fun onTimeSet(view: RadialPickerLayout?, hourOfDay: Int, minute: Int) {
+ reminderHour = hourOfDay
+ reminderMin = minute
+ populateReminder()
+ }
+ override fun onTimeCleared(view: RadialPickerLayout?) {
+ reminderHour = -1
+ reminderMin = -1
+ reminderDays = WeekdayList.EVERY_DAY
+ populateReminder()
+ }
+ }, currentHour, currentMin, is24HourMode, androidColor)
+ dialog.show(supportFragmentManager, "timePicker")
+ }
+
+ binding.reminderDatePicker.setOnClickListener {
+ val dialog = WeekdayPickerDialog()
+ dialog.setListener { days ->
+ reminderDays = days
+ if (reminderDays.isEmpty) reminderDays = WeekdayList.EVERY_DAY
+ populateReminder()
+ }
+ dialog.setSelectedDays(reminderDays)
+ dialog.show(supportFragmentManager, "dayPicker")
+ }
+
+ binding.buttonSave.setOnClickListener {
+ if(validate()) save()
+ }
+
+ for (fragment in supportFragmentManager.fragments) {
+ (fragment as DialogFragment).dismiss()
+ }
+ }
+
+ private fun save() {
+ val component = (application as HabitsApplication).component
+ val habit = component.modelFactory.buildHabit()
+
+ var original: Habit? = null
+ if (habitId >= 0) {
+ original = component.habitList.getById(habitId)!!
+ habit.copyFrom(original)
+ }
+
+ habit.name = nameInput.text.trim().toString()
+ habit.question = questionInput.text.trim().toString()
+ habit.description = notesInput.text.trim().toString()
+ habit.color = paletteColor
+ if (reminderHour >= 0) {
+ habit.setReminder(Reminder(reminderHour, reminderMin, reminderDays))
+ }
+ habit.frequency = Frequency(freqNum, freqDen)
+ habit.targetValue = targetInput.text.toString().toDouble()
+ habit.targetType = Habit.AT_LEAST
+ habit.unit = unitInput.text.trim().toString()
+ habit.type = habitType
+
+ val command = if (habitId >= 0) {
+ component.editHabitCommandFactory.create(component.habitList, original, habit)
+ } else {
+ component.createHabitCommandFactory.create(component.habitList, habit)
+ }
+ component.commandRunner.execute(command, null)
+ finish()
+ }
+
+ private fun validate(): Boolean {
+ var isValid = true
+ if (nameInput.text.isEmpty()) {
+ nameInput.error = getString(R.string.validation_should_not_be_blank)
+ isValid = false
+ }
+ if (habitType == Habit.NUMBER_HABIT) {
+ if(unitInput.text.isEmpty()) {
+ unitInput.error = getString(R.string.validation_should_not_be_blank)
+ isValid = false
+ }
+ if(targetInput.text.isEmpty()) {
+ targetInput.error = getString(R.string.validation_should_not_be_blank)
+ isValid = false
+ }
+ }
+ return isValid
+ }
+
+ private fun populateReminder() {
+ if (reminderHour < 0) {
+ binding.reminderTimePicker.text = getString(R.string.reminder_off)
+ binding.reminderDatePicker.visibility = View.GONE
+ binding.reminderDivider.visibility = View.GONE
+ } else {
+ val time = AndroidDateUtils.formatTime(this, reminderHour, reminderMin)
+ val daysArray = reminderDays.toArray()
+ binding.reminderTimePicker.text = time
+ binding.reminderDatePicker.visibility = View.VISIBLE
+ binding.reminderDivider.visibility = View.VISIBLE
+ binding.reminderDatePicker.text = AndroidDateUtils.formatWeekdayList(this, daysArray)
+ }
+ }
+
+ private fun populateFrequency() {
+ binding.booleanFrequencyPicker.text = when {
+ freqNum == 1 && freqDen == 1 -> getString(R.string.every_day)
+ freqNum == 1 && freqDen == 7 -> getString(R.string.every_week)
+ freqNum == 1 && freqDen > 1 -> getString(R.string.every_x_days, freqDen)
+ freqDen == 7 -> getString(R.string.x_times_per_week, freqNum)
+ freqDen == 31 -> getString(R.string.x_times_per_month, freqNum)
+ else -> "Unknown"
+ }
+ binding.numericalFrequencyPicker.text = when(freqDen) {
+ 1 -> getString(R.string.every_day)
+ 7 -> getString(R.string.every_week)
+ 30 -> getString(R.string.every_month)
+ else -> "Unknown"
+ }
+ }
+
+ private fun updateColors() {
+ androidColor = PaletteUtils.getColor(this, paletteColor)
+ binding.colorButton.backgroundTintList = ColorStateList.valueOf(androidColor)
+ if (!themeSwitcher.isNightMode) {
+ val darkerAndroidColor = ColorUtils.mixColors(Color.BLACK, androidColor, 0.15f)
+ window.statusBarColor = darkerAndroidColor
+ binding.toolbar.setBackgroundColor(androidColor)
+ }
+ }
+
+ override fun onSaveInstanceState(state: Bundle) {
+ super.onSaveInstanceState(state)
+ with(state) {
+ putLong("habitId", habitId)
+ putInt("habitType", habitType)
+ putInt("paletteColor", paletteColor)
+ putInt("androidColor", androidColor)
+ putInt("freqNum", freqNum)
+ putInt("freqDen", freqDen)
+ putInt("reminderHour", reminderHour)
+ putInt("reminderMin", reminderMin)
+ putInt("reminderDays", reminderDays.toInteger())
+ }
+ }
+}
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/edit/EditHabitDialog.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/edit/EditHabitDialog.java
deleted file mode 100644
index e9e5c5ee3..000000000
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/edit/EditHabitDialog.java
+++ /dev/null
@@ -1,281 +0,0 @@
-/*
- * Copyright (C) 2016 Álinson Santos Xavier
- *
- * This file is part of Loop Habit Tracker.
- *
- * Loop Habit Tracker is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or (at your
- * option) any later version.
- *
- * Loop Habit Tracker is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see .
- */
-
-package org.isoron.uhabits.activities.habits.edit;
-
-import android.app.Dialog;
-import android.content.*;
-import android.os.*;
-import android.support.annotation.*;
-import android.support.v7.app.*;
-import android.text.format.*;
-import android.view.*;
-
-import com.android.datetimepicker.time.*;
-
-import org.isoron.uhabits.*;
-import org.isoron.uhabits.R;
-import org.isoron.uhabits.activities.*;
-import org.isoron.uhabits.activities.common.dialogs.*;
-import org.isoron.uhabits.activities.habits.edit.views.*;
-import org.isoron.uhabits.core.commands.*;
-import org.isoron.uhabits.core.models.*;
-import org.isoron.uhabits.core.preferences.*;
-
-import butterknife.*;
-
-import static android.view.View.GONE;
-import static org.isoron.uhabits.core.ui.ThemeSwitcher.THEME_LIGHT;
-
-public class EditHabitDialog extends AppCompatDialogFragment
-{
- public static final String BUNDLE_HABIT_ID = "habitId";
-
- public static final String BUNDLE_HABIT_TYPE = "habitType";
-
- protected Habit originalHabit;
-
- protected Preferences prefs;
-
- protected CommandRunner commandRunner;
-
- protected HabitList habitList;
-
- protected HabitsApplicationComponent component;
-
- protected ModelFactory modelFactory;
-
- @BindView(R.id.namePanel)
- NameDescriptionPanel namePanel;
-
- @BindView(R.id.reminderPanel)
- ReminderPanel reminderPanel;
-
- @BindView(R.id.frequencyPanel)
- FrequencyPanel frequencyPanel;
-
- @BindView(R.id.targetPanel)
- TargetPanel targetPanel;
-
- private ColorPickerDialogFactory colorPickerDialogFactory;
-
- @Override
- public int getTheme()
- {
- HabitsApplicationComponent component =
- ((HabitsApplication) getContext().getApplicationContext()).getComponent();
-
- if(component.getPreferences().getTheme() == THEME_LIGHT)
- return R.style.DialogWithTitle;
- else
- return R.style.DarkDialogWithTitle;
- }
-
- @Override
- public void onActivityCreated(Bundle savedInstanceState)
- {
- super.onActivityCreated(savedInstanceState);
-
- HabitsActivity activity = (HabitsActivity) getActivity();
- colorPickerDialogFactory =
- activity.getComponent().getColorPickerDialogFactory();
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater,
- ViewGroup container,
- Bundle savedInstanceState)
- {
- View view;
- view = inflater.inflate(R.layout.edit_habit, container, false);
-
- initDependencies();
- ButterKnife.bind(this, view);
-
- originalHabit = parseHabitFromArguments();
- getDialog().setTitle(getTitle());
-
- populateForm();
- setupReminderController();
- setupNameController();
-
- return view;
- }
-
- @NonNull
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- final Dialog dialog = super.onCreateDialog(savedInstanceState);
- final Window window = dialog.getWindow();
- if (window != null) {
- window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
- }
- return dialog;
- }
-
- protected int getTitle()
- {
- if (originalHabit != null) return R.string.edit_habit;
- else return R.string.create_habit;
- }
-
- protected void saveHabit(@NonNull Habit habit)
- {
- if (originalHabit == null)
- {
- commandRunner.execute(component
- .getCreateHabitCommandFactory()
- .create(habitList, habit), null);
- }
- else
- {
- commandRunner.execute(component.getEditHabitCommandFactory().
- create(habitList, originalHabit, habit), originalHabit.getId());
- }
- }
-
- private int getTypeFromArguments()
- {
- return getArguments().getInt(BUNDLE_HABIT_TYPE);
- }
-
- private void initDependencies()
- {
- Context appContext = getContext().getApplicationContext();
- HabitsApplication app = (HabitsApplication) appContext;
-
- component = app.getComponent();
- prefs = component.getPreferences();
- habitList = component.getHabitList();
- commandRunner = component.getCommandRunner();
- modelFactory = component.getModelFactory();
- }
-
- @OnClick(R.id.buttonDiscard)
- void onButtonDiscardClick()
- {
- dismiss();
- }
-
- @OnClick(R.id.buttonSave)
- void onSaveButtonClick()
- {
- int type = getTypeFromArguments();
-
- if (!namePanel.validate()) return;
- if (type == Habit.YES_NO_HABIT && !frequencyPanel.validate()) return;
- if (type == Habit.NUMBER_HABIT && !targetPanel.validate()) return;
-
- Habit habit = modelFactory.buildHabit();
- if( originalHabit != null )
- habit.copyFrom(originalHabit);
- habit.setName(namePanel.getName());
- habit.setDescription(namePanel.getDescription());
- habit.setColor(namePanel.getColor());
- habit.setReminder(reminderPanel.getReminder());
- habit.setFrequency(frequencyPanel.getFrequency());
- habit.setUnit(targetPanel.getUnit());
- habit.setTargetValue(targetPanel.getTargetValue());
- habit.setType(type);
-
- saveHabit(habit);
- dismiss();
- }
-
- @Nullable
- private Habit parseHabitFromArguments()
- {
- Bundle arguments = getArguments();
- if (arguments == null) return null;
-
- Long id = (Long) arguments.get(BUNDLE_HABIT_ID);
- if (id == null) return null;
-
- Habit habit = habitList.getById(id);
- if (habit == null) throw new IllegalStateException();
-
- return habit;
- }
-
- private void populateForm()
- {
- Habit habit = modelFactory.buildHabit();
- habit.setFrequency(Frequency.DAILY);
- habit.setColor(prefs.getDefaultHabitColor(habit.getColor()));
- habit.setType(getTypeFromArguments());
-
- if (originalHabit != null) habit.copyFrom(originalHabit);
-
- if (habit.isNumerical()) frequencyPanel.setVisibility(GONE);
- else targetPanel.setVisibility(GONE);
-
- namePanel.populateFrom(habit);
- frequencyPanel.setFrequency(habit.getFrequency());
- targetPanel.setTargetValue(habit.getTargetValue());
- targetPanel.setUnit(habit.getUnit());
- if (habit.hasReminder()) reminderPanel.setReminder(habit.getReminder());
- }
-
- private void setupNameController()
- {
- namePanel.setController(new NameDescriptionPanel.Controller()
- {
- @Override
- public void onColorPickerClicked(int previousColor)
- {
- ColorPickerDialog picker =
- colorPickerDialogFactory.create(previousColor);
-
- picker.setListener(c ->
- {
- prefs.setDefaultHabitColor(c);
- namePanel.setColor(c);
- });
-
- picker.show(getFragmentManager(), "picker");
- }
- });
- }
-
- private void setupReminderController()
- {
- reminderPanel.setController(new ReminderPanel.Controller()
- {
- @Override
- public void onTimeClicked(int currentHour, int currentMin)
- {
- TimePickerDialog timePicker;
- boolean is24HourMode = DateFormat.is24HourFormat(getContext());
- timePicker =
- TimePickerDialog.newInstance(reminderPanel, currentHour,
- currentMin, is24HourMode);
- timePicker.show(getFragmentManager(), "timePicker");
- }
-
- @Override
- public void onWeekdayClicked(WeekdayList currentDays)
- {
- WeekdayPickerDialog dialog = new WeekdayPickerDialog();
- dialog.setListener(reminderPanel);
- dialog.setSelectedDays(currentDays);
- dialog.show(getFragmentManager(), "weekdayPicker");
- }
- });
- }
-}
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/edit/EditHabitDialogFactory.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/edit/EditHabitDialogFactory.java
deleted file mode 100644
index a91c3b14a..000000000
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/edit/EditHabitDialogFactory.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2016 Álinson Santos Xavier
- *
- * This file is part of Loop Habit Tracker.
- *
- * Loop Habit Tracker is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or (at your
- * option) any later version.
- *
- * Loop Habit Tracker is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see .
- */
-
-package org.isoron.uhabits.activities.habits.edit;
-
-import android.os.*;
-import android.support.annotation.*;
-
-import org.isoron.uhabits.core.models.*;
-
-import javax.inject.*;
-
-import static org.isoron.uhabits.activities.habits.edit.EditHabitDialog.*;
-
-public class EditHabitDialogFactory
-{
- @Inject
- public EditHabitDialogFactory()
- {
- }
-
- public EditHabitDialog createBoolean()
- {
- EditHabitDialog dialog = new EditHabitDialog();
- Bundle args = new Bundle();
- args.putInt(BUNDLE_HABIT_TYPE, Habit.YES_NO_HABIT);
- dialog.setArguments(args);
- return dialog;
- }
-
- public EditHabitDialog createNumerical()
- {
- EditHabitDialog dialog = new EditHabitDialog();
- Bundle args = new Bundle();
- args.putInt(BUNDLE_HABIT_TYPE, Habit.NUMBER_HABIT);
- dialog.setArguments(args);
- return dialog;
- }
-
- public EditHabitDialog edit(@NonNull Habit habit)
- {
- if (habit.getId() == null)
- throw new IllegalArgumentException("habit not saved");
-
- EditHabitDialog dialog = new EditHabitDialog();
- Bundle args = new Bundle();
- args.putLong(BUNDLE_HABIT_ID, habit.getId());
- args.putInt(BUNDLE_HABIT_TYPE, habit.getType());
- dialog.setArguments(args);
- return dialog;
- }
-}
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/edit/HabitTypeDialog.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/edit/HabitTypeDialog.kt
new file mode 100644
index 000000000..39e8dfb4c
--- /dev/null
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/edit/HabitTypeDialog.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2016-2020 Álinson Santos Xavier
+ *
+ * This file is part of Loop Habit Tracker.
+ *
+ * Loop Habit Tracker is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * Loop Habit Tracker is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see .
+ */
+
+package org.isoron.uhabits.activities.habits.edit
+
+import android.os.*
+import android.view.*
+import androidx.appcompat.app.*
+import org.isoron.uhabits.*
+import org.isoron.uhabits.core.models.*
+import org.isoron.uhabits.databinding.*
+import org.isoron.uhabits.intents.*
+
+class HabitTypeDialog : AppCompatDialogFragment() {
+ override fun getTheme() = R.style.Translucent
+
+ override fun onCreateView(inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?): View? {
+ val binding = SelectHabitTypeBinding.inflate(inflater, container, false)
+
+ binding.buttonYesNo.setOnClickListener {
+ val intent = IntentFactory().startEditActivity(activity!!, Habit.YES_NO_HABIT)
+ startActivity(intent)
+ dismiss()
+ }
+
+ binding.buttonMeasurable.setOnClickListener {
+ val intent = IntentFactory().startEditActivity(activity!!, Habit.NUMBER_HABIT)
+ startActivity(intent)
+ dismiss()
+ }
+
+ binding.background.setOnClickListener {
+ dismiss()
+ }
+
+ return binding.root
+ }
+}
\ No newline at end of file
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/edit/views/ExampleEditText.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/edit/views/ExampleEditText.java
deleted file mode 100644
index 1403a0b13..000000000
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/edit/views/ExampleEditText.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright (C) 2016 Álinson Santos Xavier
- *
- * This file is part of Loop Habit Tracker.
- *
- * Loop Habit Tracker is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or (at your
- * option) any later version.
- *
- * Loop Habit Tracker is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see .
- */
-
-package org.isoron.uhabits.activities.habits.edit.views;
-
-import android.content.*;
-import android.support.annotation.*;
-import android.text.*;
-import android.util.*;
-import android.view.*;
-import android.widget.*;
-
-import org.isoron.androidbase.utils.*;
-import org.isoron.uhabits.*;
-
-import static org.isoron.uhabits.utils.AttributeSetUtils.*;
-
-/**
- * An EditText that shows an example usage when there is no text
- * currently set. The example disappears when the widget gains focus.
- */
-public class ExampleEditText extends EditText
- implements View.OnFocusChangeListener
-{
-
- private String example;
-
- private String realText;
-
- private int color;
-
- private int exampleColor;
-
- private int inputType;
-
- public ExampleEditText(Context context, @Nullable AttributeSet attrs)
- {
- super(context, attrs);
-
- if (attrs != null)
- example = getAttribute(context, attrs, "example", "");
-
- inputType = getInputType();
- realText = getText().toString();
- color = getCurrentTextColor();
- init();
- }
-
- public String getRealText()
- {
- if(hasFocus()) return getText().toString();
- else return realText;
- }
-
- @Override
- public void onFocusChange(View v, boolean hasFocus)
- {
- if (!hasFocus) realText = getText().toString();
- updateText();
- }
-
- public void setExample(String example)
- {
- this.example = example;
- updateText();
- }
-
- public void setRealText(String realText)
- {
- this.realText = realText;
- updateText();
- }
-
- private void init()
- {
- StyledResources sr = new StyledResources(getContext());
- exampleColor = sr.getColor(R.attr.mediumContrastTextColor);
- setOnFocusChangeListener(this);
- updateText();
- }
-
- private void updateText()
- {
- if (realText.isEmpty() && !isFocused())
- {
- setTextColor(exampleColor);
- setText(example);
- setInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
- }
- else
- {
- setText(realText);
- setTextColor(color);
- setInputType(inputType);
- }
-
- }
-}
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/edit/views/FrequencyPanel.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/edit/views/FrequencyPanel.java
deleted file mode 100644
index aac5d47e7..000000000
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/edit/views/FrequencyPanel.java
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Copyright (C) 2016 Álinson Santos Xavier
- *
- * This file is part of Loop Habit Tracker.
- *
- * Loop Habit Tracker is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or (at your
- * option) any later version.
- *
- * Loop Habit Tracker is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see .
- */
-
-package org.isoron.uhabits.activities.habits.edit.views;
-
-import android.annotation.*;
-import android.content.*;
-import android.content.res.*;
-import android.support.annotation.*;
-import android.util.*;
-import android.view.*;
-import android.widget.*;
-
-import org.isoron.uhabits.R;
-import org.isoron.uhabits.core.models.*;
-
-import butterknife.*;
-
-import static org.isoron.uhabits.R.id.*;
-
-
-public class FrequencyPanel extends FrameLayout
-{
- @BindView(numerator)
- TextView tvNumerator;
-
- @BindView(R.id.denominator)
- TextView tvDenominator;
-
- @BindView(R.id.spinner)
- Spinner spinner;
-
- @BindView(R.id.customFreqPanel)
- ViewGroup customFreqPanel;
-
- public FrequencyPanel(@NonNull Context context,
- @Nullable AttributeSet attrs)
- {
- super(context, attrs);
-
- View view = inflate(context, R.layout.edit_habit_frequency, null);
- ButterKnife.bind(this, view);
- addView(view);
- }
-
- @NonNull
- public Frequency getFrequency()
- {
- String freqNum = tvNumerator.getText().toString();
- String freqDen = tvDenominator.getText().toString();
-
- if (!freqNum.isEmpty() && !freqDen.isEmpty())
- {
- int numerator = Integer.parseInt(freqNum);
- int denominator = Integer.parseInt(freqDen);
- return new Frequency(numerator, denominator);
- }
-
- return Frequency.DAILY;
- }
-
- @SuppressLint("SetTextI18n")
- public void setFrequency(@NonNull Frequency freq)
- {
- int position = getQuickSelectPosition(freq);
-
- if (position >= 0) showSimplifiedFrequency(position);
- else showCustomFrequency();
-
- tvNumerator.setText(Integer.toString(freq.getNumerator()));
- tvDenominator.setText(Integer.toString(freq.getDenominator()));
- }
-
- @OnItemSelected(R.id.spinner)
- public void onFrequencySelected(int position)
- {
- if (position < 0 || position > 4) throw new IllegalArgumentException();
- int freqNums[] = { 1, 1, 2, 5, 3 };
- int freqDens[] = { 1, 7, 7, 7, 7 };
- setFrequency(new Frequency(freqNums[position], freqDens[position]));
- }
-
- public boolean validate()
- {
- boolean valid = true;
- Resources res = getResources();
-
- String freqNum = tvNumerator.getText().toString();
- String freqDen = tvDenominator.getText().toString();
-
- if (freqDen.isEmpty())
- {
- tvDenominator.setError(
- res.getString(R.string.validation_show_not_be_blank));
- valid = false;
- }
-
- if (freqNum.isEmpty())
- {
- tvNumerator.setError(
- res.getString(R.string.validation_show_not_be_blank));
- valid = false;
- }
-
- if (!valid) return false;
-
- int numerator = Integer.parseInt(freqNum);
- int denominator = Integer.parseInt(freqDen);
-
- if (numerator <= 0)
- {
- tvNumerator.setError(
- res.getString(R.string.validation_number_should_be_positive));
- valid = false;
- }
-
- if (numerator > denominator)
- {
- tvNumerator.setError(
- res.getString(R.string.validation_at_most_one_rep_per_day));
- valid = false;
- }
-
- return valid;
- }
-
- private int getQuickSelectPosition(@NonNull Frequency freq)
- {
- if (freq.equals(Frequency.DAILY)) return 0;
- if (freq.equals(Frequency.WEEKLY)) return 1;
- if (freq.equals(Frequency.TWO_TIMES_PER_WEEK)) return 2;
- if (freq.equals(Frequency.FIVE_TIMES_PER_WEEK)) return 3;
- return -1;
- }
-
- private void showCustomFrequency()
- {
- spinner.setVisibility(View.GONE);
- customFreqPanel.setVisibility(View.VISIBLE);
- }
-
- private void showSimplifiedFrequency(int quickSelectPosition)
- {
- spinner.setVisibility(View.VISIBLE);
- spinner.setSelection(quickSelectPosition);
- customFreqPanel.setVisibility(View.GONE);
- }
-}
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/edit/views/NameDescriptionPanel.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/edit/views/NameDescriptionPanel.java
deleted file mode 100644
index ca7f45715..000000000
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/edit/views/NameDescriptionPanel.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2016 Álinson Santos Xavier
- *
- * This file is part of Loop Habit Tracker.
- *
- * Loop Habit Tracker is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or (at your
- * option) any later version.
- *
- * Loop Habit Tracker is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see .
- */
-
-package org.isoron.uhabits.activities.habits.edit.views;
-
-import android.content.*;
-import android.content.res.*;
-import android.os.*;
-import android.support.annotation.*;
-import android.util.*;
-import android.view.*;
-import android.widget.*;
-
-import org.isoron.uhabits.R;
-import org.isoron.uhabits.activities.common.views.*;
-import org.isoron.uhabits.core.models.*;
-import org.isoron.uhabits.utils.*;
-
-import butterknife.*;
-
-
-public class NameDescriptionPanel extends FrameLayout
-{
- @BindView(R.id.tvName)
- EditText tvName;
-
- @BindView(R.id.tvDescription)
- ExampleEditText tvDescription;
-
- private int color;
-
- @NonNull
- private Controller controller;
-
- public NameDescriptionPanel(@NonNull Context context,
- @Nullable AttributeSet attrs)
- {
- super(context, attrs);
-
- View view = inflate(context, R.layout.edit_habit_name, null);
- ButterKnife.bind(this, view);
- addView(view);
-
- controller = new Controller() {};
- }
-
- public int getColor()
- {
- return color;
- }
-
- public void setColor(int color)
- {
- this.color = color;
- tvName.setTextColor(PaletteUtils.getColor(getContext(), color));
- }
-
- @NonNull
- public String getDescription()
- {
- return tvDescription.getRealText().trim();
- }
-
- @NonNull
- public String getName()
- {
- return tvName.getText().toString().trim();
- }
-
- public void populateFrom(@NonNull Habit habit)
- {
- Resources res = getResources();
-
- if(habit.isNumerical())
- tvDescription.setExample(res.getString(R.string.example_question_numerical));
- else
- tvDescription.setExample(res.getString(R.string.example_question_boolean));
-
- setColor(habit.getColor());
- tvName.setText(habit.getName());
- tvDescription.setRealText(habit.getDescription());
- }
-
- public boolean validate()
- {
- Resources res = getResources();
-
- if (getName().isEmpty())
- {
- tvName.setError(
- res.getString(R.string.validation_name_should_not_be_blank));
- return false;
- }
-
- return true;
- }
-
- @Override
- protected void onRestoreInstanceState(Parcelable state)
- {
- BundleSavedState bss = (BundleSavedState) state;
- setColor(bss.bundle.getInt("color"));
- super.onRestoreInstanceState(bss.getSuperState());
- }
-
- @Override
- protected Parcelable onSaveInstanceState()
- {
- Parcelable superState = super.onSaveInstanceState();
- Bundle bundle = new Bundle();
- bundle.putInt("color", color);
- return new BundleSavedState(superState, bundle);
- }
-
- @OnClick(R.id.buttonPickColor)
- void showColorPicker()
- {
- controller.onColorPickerClicked(color);
- }
-
- public void setController(@NonNull Controller controller)
- {
- this.controller = controller;
- }
-
- public interface Controller
- {
- /**
- * Called when the user has clicked the widget to select a new
- * color for the habit.
- *
- * @param previousColor the color previously selected
- */
- default void onColorPickerClicked(int previousColor) {}
- }
-}
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/edit/views/ReminderPanel.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/edit/views/ReminderPanel.java
deleted file mode 100644
index 6d2c399e7..000000000
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/edit/views/ReminderPanel.java
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- * Copyright (C) 2016 Álinson Santos Xavier
- *
- * This file is part of Loop Habit Tracker.
- *
- * Loop Habit Tracker is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or (at your
- * option) any later version.
- *
- * Loop Habit Tracker is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see .
- */
-
-package org.isoron.uhabits.activities.habits.edit.views;
-
-import android.content.*;
-import android.os.*;
-import android.support.annotation.*;
-import android.util.*;
-import android.view.*;
-import android.widget.*;
-
-import com.android.datetimepicker.time.*;
-
-import org.isoron.uhabits.R;
-import org.isoron.uhabits.activities.common.dialogs.*;
-import org.isoron.uhabits.activities.common.views.*;
-import org.isoron.uhabits.core.models.*;
-import org.isoron.uhabits.utils.*;
-
-import butterknife.*;
-
-public class ReminderPanel extends FrameLayout
- implements TimePickerDialog.OnTimeSetListener,
- WeekdayPickerDialog.OnWeekdaysPickedListener
-{
- @BindView(R.id.tvReminderTime)
- TextView tvReminderTime;
-
- @BindView(R.id.llReminderDays)
- ViewGroup llReminderDays;
-
- @BindView(R.id.tvReminderDays)
- TextView tvReminderDays;
-
- @Nullable
- private Reminder reminder;
-
- @NonNull
- private Controller controller;
-
- public ReminderPanel(@NonNull Context context, @Nullable AttributeSet attrs)
- {
- super(context, attrs);
-
- View view = inflate(context, R.layout.edit_habit_reminder, null);
- ButterKnife.bind(this, view);
- addView(view);
-
- controller = new Controller() {};
- setReminder(null);
- }
-
- @Nullable
- public Reminder getReminder()
- {
- return reminder;
- }
-
- public void setReminder(@Nullable Reminder reminder)
- {
- this.reminder = reminder;
-
- if (reminder == null)
- {
- tvReminderTime.setText(R.string.reminder_off);
- llReminderDays.setVisibility(View.GONE);
- return;
- }
-
- Context ctx = getContext();
- String time = AndroidDateUtils.formatTime(ctx, reminder.getHour(), reminder.getMinute());
- tvReminderTime.setText(time);
- llReminderDays.setVisibility(View.VISIBLE);
-
- boolean weekdays[] = reminder.getDays().toArray();
- tvReminderDays.setText(AndroidDateUtils.formatWeekdayList(ctx, weekdays));
- }
-
- @Override
- public void onTimeCleared(RadialPickerLayout view)
- {
- setReminder(null);
- }
-
- @Override
- public void onTimeSet(RadialPickerLayout view, int hour, int minute)
- {
- WeekdayList days = WeekdayList.EVERY_DAY;
- if (reminder != null) days = reminder.getDays();
- setReminder(new Reminder(hour, minute, days));
- }
-
- @Override
- public void onWeekdaysSet(WeekdayList selectedDays)
- {
- if (reminder == null) return;
- if (selectedDays.isEmpty()) selectedDays = WeekdayList.EVERY_DAY;
-
- setReminder(new Reminder(reminder.getHour(), reminder.getMinute(),
- selectedDays));
- }
-
- public void setController(@NonNull Controller controller)
- {
- this.controller = controller;
- }
-
- @Override
- protected void onRestoreInstanceState(Parcelable state)
- {
- BundleSavedState bss = (BundleSavedState) state;
- if (!bss.bundle.isEmpty())
- {
- int days = bss.bundle.getInt("days");
- int hour = bss.bundle.getInt("hour");
- int minute = bss.bundle.getInt("minute");
- reminder = new Reminder(hour, minute, new WeekdayList(days));
- setReminder(reminder);
- }
- super.onRestoreInstanceState(bss.getSuperState());
- }
-
- @Override
- protected Parcelable onSaveInstanceState()
- {
- Parcelable superState = super.onSaveInstanceState();
- Bundle bundle = new Bundle();
- if (reminder != null)
- {
- bundle.putInt("days", reminder.getDays().toInteger());
- bundle.putInt("hour", reminder.getHour());
- bundle.putInt("minute", reminder.getMinute());
- }
- return new BundleSavedState(superState, bundle);
- }
-
- @OnClick(R.id.tvReminderTime)
- void onDateSpinnerClick()
- {
- int hour = 8;
- int min = 0;
-
- if (reminder != null)
- {
- hour = reminder.getHour();
- min = reminder.getMinute();
- }
-
- controller.onTimeClicked(hour, min);
- }
-
- @OnClick(R.id.tvReminderDays)
- void onWeekdayClicked()
- {
- if (reminder == null) return;
- controller.onWeekdayClicked(reminder.getDays());
- }
-
- public interface Controller
- {
- /**
- * Called when the user has clicked the widget to change the time of
- * the reminder.
- *
- * @param currentHour hour previously picked by the user
- * @param currentMin minute previously picked by the user
- */
- default void onTimeClicked(int currentHour, int currentMin) {}
-
- /**
- * Called when the used has clicked the widget to change the days
- * of the reminder.
- *
- * @param currentDays days previously selected by the user.
- */
- default void onWeekdayClicked(WeekdayList currentDays) {}
- }
-}
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/edit/views/TargetPanel.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/edit/views/TargetPanel.java
deleted file mode 100644
index 8ef88f135..000000000
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/edit/views/TargetPanel.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2016 Álinson Santos Xavier
- *
- * This file is part of Loop Habit Tracker.
- *
- * Loop Habit Tracker is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or (at your
- * option) any later version.
- *
- * Loop Habit Tracker is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see .
- */
-
-package org.isoron.uhabits.activities.habits.edit.views;
-
-import android.content.*;
-import android.content.res.*;
-import android.support.annotation.*;
-import android.util.*;
-import android.view.*;
-import android.widget.*;
-
-import org.isoron.uhabits.R;
-
-import java.text.DecimalFormat;
-
-import butterknife.*;
-
-
-public class TargetPanel extends FrameLayout
-{
- private DecimalFormat valueFormatter = new DecimalFormat("#.##");
-
- @BindView(R.id.tvUnit)
- ExampleEditText tvUnit;
-
- @BindView(R.id.tvTargetCount)
- TextView tvTargetValue;
-
- public TargetPanel(@NonNull Context context, @Nullable AttributeSet attrs)
- {
- super(context, attrs);
-
- View view = inflate(context, R.layout.edit_habit_target, null);
- ButterKnife.bind(this, view);
- addView(view);
- }
-
- public double getTargetValue()
- {
- String sValue = tvTargetValue.getText().toString();
- return Double.parseDouble(sValue);
- }
-
- public void setTargetValue(double targetValue)
- {
- tvTargetValue.setText(valueFormatter.format(targetValue));
- }
-
- public String getUnit()
- {
- return tvUnit.getRealText();
- }
-
- public void setUnit(String unit)
- {
- tvUnit.setRealText(unit);
- }
-
- public boolean validate()
- {
- Resources res = getResources();
- String sValue = tvTargetValue.getText().toString();
- double value = Double.parseDouble(sValue);
-
- if (value <= 0)
- {
- tvTargetValue.setError(
- res.getString(R.string.validation_number_should_be_positive));
- return false;
- }
-
- return true;
- }
-}
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsActivity.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsActivity.kt
index a72b62d5b..1b8a92822 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsActivity.kt
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsActivity.kt
@@ -20,6 +20,7 @@
package org.isoron.uhabits.activities.habits.list
import android.os.*
+import org.isoron.uhabits.*
import org.isoron.uhabits.activities.*
import org.isoron.uhabits.activities.habits.list.views.*
import org.isoron.uhabits.core.preferences.*
@@ -46,6 +47,7 @@ class ListHabitsActivity : HabitsActivity() {
setScreen(screen)
component.listHabitsBehavior.onStartup()
+ setTitle(R.string.main_activity_title)
}
override fun onPause() {
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsRootView.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsRootView.kt
index 25377db0c..268c1e06a 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsRootView.kt
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsRootView.kt
@@ -22,7 +22,7 @@ package org.isoron.uhabits.activities.habits.list
import android.content.*
import android.os.Build.VERSION.*
import android.os.Build.VERSION_CODES.*
-import android.support.v7.widget.Toolbar
+import androidx.appcompat.widget.Toolbar
import android.view.ViewGroup.LayoutParams.*
import android.widget.*
import org.isoron.androidbase.activities.*
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsScreen.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsScreen.kt
index abcd91b02..2a8ed0b2a 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsScreen.kt
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsScreen.kt
@@ -21,7 +21,7 @@ package org.isoron.uhabits.activities.habits.list
import android.app.*
import android.content.*
-import android.support.annotation.*
+import androidx.annotation.*
import dagger.*
import org.isoron.androidbase.activities.*
import org.isoron.androidbase.utils.*
@@ -58,14 +58,12 @@ class ListHabitsScreen
private val commandRunner: CommandRunner,
private val intentFactory: IntentFactory,
private val themeSwitcher: ThemeSwitcher,
- private val preferences: Preferences,
private val adapter: HabitCardListAdapter,
private val taskRunner: TaskRunner,
private val exportDBFactory: ExportDBTaskFactory,
private val importTaskFactory: ImportDataTaskFactory,
private val confirmDeleteDialogFactory: ConfirmDeleteDialogFactory,
private val colorPickerFactory: ColorPickerDialogFactory,
- private val editHabitDialogFactory: EditHabitDialogFactory,
private val numberPickerFactory: NumberPickerFactory,
private val behavior: Lazy,
private val menu: Lazy,
@@ -106,7 +104,7 @@ class ListHabitsScreen
if (data == null) return
if (resultCode != Activity.RESULT_OK) return
try {
- val inStream = activity.contentResolver.openInputStream(data.data)
+ val inStream = activity.contentResolver.openInputStream(data.data!!)
val cacheDir = activity.externalCacheDir
val tempFile = File.createTempFile("import", "", cacheDir)
FileUtils.copy(inStream, tempFile)
@@ -152,8 +150,8 @@ class ListHabitsScreen
}
override fun showEditHabitsScreen(habits: List) {
- val dialog = editHabitDialogFactory.edit(habits[0])
- activity.showDialog(dialog, "editNumericalHabit")
+ val intent = intentFactory.startEditActivity(activity!!, habits[0])
+ activity.startActivity(intent)
}
override fun showFAQScreen() {
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardListAdapter.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardListAdapter.java
index 6992391c1..ad4f8013e 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardListAdapter.java
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardListAdapter.java
@@ -19,10 +19,13 @@
package org.isoron.uhabits.activities.habits.list.views;
-import android.support.annotation.*;
-import android.support.v7.widget.*;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.widget.*;
import android.view.*;
+import androidx.recyclerview.widget.RecyclerView;
+
import org.isoron.androidbase.activities.*;
import org.isoron.uhabits.activities.habits.list.*;
import org.isoron.uhabits.core.models.*;
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardListView.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardListView.kt
index 0989fb660..259d1f14a 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardListView.kt
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardListView.kt
@@ -21,9 +21,9 @@ package org.isoron.uhabits.activities.habits.list.views
import android.content.*
import android.os.*
-import android.support.v7.widget.*
-import android.support.v7.widget.helper.*
-import android.support.v7.widget.helper.ItemTouchHelper.*
+import androidx.appcompat.widget.*
+import androidx.recyclerview.widget.*
+import androidx.recyclerview.widget.ItemTouchHelper.*
import android.view.*
import com.google.auto.factory.*
import dagger.*
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardViewHolder.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardViewHolder.kt
index ca820109c..3f0b9d0ca 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardViewHolder.kt
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardViewHolder.kt
@@ -19,7 +19,8 @@
package org.isoron.uhabits.activities.habits.list.views
-import android.support.v7.widget.*
+import androidx.appcompat.widget.*
import android.view.*
+import androidx.recyclerview.widget.RecyclerView
class HabitCardViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitRootView.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitRootView.java
index 55966f0c3..e1a986294 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitRootView.java
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitRootView.java
@@ -21,8 +21,11 @@ package org.isoron.uhabits.activities.habits.show;
import android.content.*;
import android.os.*;
-import android.support.annotation.*;
-import android.support.v7.widget.*;
+import android.view.*;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.widget.*;
import org.isoron.androidbase.activities.*;
import org.isoron.androidbase.utils.*;
@@ -51,6 +54,12 @@ public class ShowHabitRootView extends BaseRootView
@BindView(R.id.subtitleCard)
SubtitleCard subtitleCard;
+ @BindView(R.id.notesCard)
+ NotesCard notesCard;
+
+ @BindView(R.id.habitNotes)
+ TextView habitNotes;
+
@BindView(R.id.overviewCard)
OverviewCard overviewCard;
@@ -66,6 +75,9 @@ public class ShowHabitRootView extends BaseRootView
@BindView(R.id.toolbar)
Toolbar toolbar;
+ @BindView(R.id.targetCard)
+ TargetCard targetCard;
+
@NonNull
private Controller controller;
@@ -135,12 +147,20 @@ public class ShowHabitRootView extends BaseRootView
private void initCards()
{
subtitleCard.setHabit(habit);
+ notesCard.setHabit(habit);
overviewCard.setHabit(habit);
scoreCard.setHabit(habit);
historyCard.setHabit(habit);
streakCard.setHabit(habit);
frequencyCard.setHabit(habit);
barCard.setHabit(habit);
+ targetCard.setHabit(habit);
+
+ if(habit.isNumerical()) {
+ overviewCard.setVisibility(View.GONE);
+ } else {
+ targetCard.setVisibility(View.GONE);
+ }
}
public interface Controller extends HistoryCard.Controller
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitScreen.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitScreen.java
index c74b412da..d19eb7cb5 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitScreen.java
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitScreen.java
@@ -19,7 +19,9 @@
package org.isoron.uhabits.activities.habits.show;
-import android.support.annotation.*;
+import android.content.*;
+
+import androidx.annotation.NonNull;
import org.isoron.androidbase.activities.*;
import org.isoron.uhabits.*;
@@ -28,6 +30,7 @@ import org.isoron.uhabits.activities.habits.edit.*;
import org.isoron.uhabits.core.models.*;
import org.isoron.uhabits.core.ui.callbacks.*;
import org.isoron.uhabits.core.ui.screens.habits.show.*;
+import org.isoron.uhabits.intents.*;
import javax.inject.*;
@@ -43,30 +46,30 @@ public class ShowHabitScreen extends BaseScreen
@NonNull
private final Habit habit;
- @NonNull
- private final EditHabitDialogFactory editHabitDialogFactory;
-
@NonNull
private final ConfirmDeleteDialogFactory confirmDeleteDialogFactory;
private final Lazy behavior;
+ @NonNull
+ private final IntentFactory intentFactory;
+
@Inject
public ShowHabitScreen(@NonNull BaseActivity activity,
@NonNull Habit habit,
@NonNull ShowHabitRootView view,
@NonNull ShowHabitsMenu menu,
- @NonNull EditHabitDialogFactory editHabitDialogFactory,
@NonNull ConfirmDeleteDialogFactory confirmDeleteDialogFactory,
+ @NonNull IntentFactory intentFactory,
@NonNull Lazy behavior)
{
super(activity);
+ this.intentFactory = intentFactory;
setMenu(menu);
setRootView(view);
this.habit = habit;
this.behavior = behavior;
- this.editHabitDialogFactory = editHabitDialogFactory;
this.confirmDeleteDialogFactory = confirmDeleteDialogFactory;
view.setController(this);
}
@@ -102,7 +105,8 @@ public class ShowHabitScreen extends BaseScreen
@Override
public void showEditHabitScreen(@NonNull Habit habit)
{
- activity.showDialog(editHabitDialogFactory.edit(habit), "editHabit");
+ Intent intent = intentFactory.startEditActivity(activity, habit);
+ activity.startActivity(intent);
}
@Override
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitsMenu.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitsMenu.java
index b7560a5d3..39c14c198 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitsMenu.java
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitsMenu.java
@@ -19,9 +19,10 @@
package org.isoron.uhabits.activities.habits.show;
-import android.support.annotation.*;
import android.view.*;
+import androidx.annotation.NonNull;
+
import org.isoron.androidbase.activities.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.core.preferences.Preferences;
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/BarCard.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/BarCard.java
index 135dc0a23..6b5dfe394 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/BarCard.java
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/BarCard.java
@@ -20,14 +20,16 @@
package org.isoron.uhabits.activities.habits.show.views;
import android.content.*;
-import android.support.annotation.*;
import android.util.*;
import android.widget.*;
+import androidx.annotation.Nullable;
+
import org.isoron.uhabits.*;
import org.isoron.uhabits.R;
import org.isoron.uhabits.activities.common.views.*;
import org.isoron.uhabits.core.models.*;
+import org.isoron.uhabits.core.preferences.*;
import org.isoron.uhabits.core.tasks.*;
import org.isoron.uhabits.utils.*;
@@ -54,11 +56,11 @@ public class BarCard extends HabitCard
@BindView(R.id.title)
TextView title;
- @Nullable
- private TaskRunner taskRunner;
-
private int bucketSize;
+ @Nullable
+ private Preferences prefs;
+
public BarCard(Context context)
{
super(context);
@@ -85,29 +87,19 @@ public class BarCard extends HabitCard
refreshData();
}
- @Override
- protected void refreshData()
- {
- if (taskRunner == null) return;
- taskRunner.execute(new RefreshTask(getHabit()));
- }
-
private void init()
{
- inflate(getContext(), R.layout.show_habit_bar, this);
- ButterKnife.bind(this);
-
- boolSpinner.setSelection(1);
- numericalSpinner.setSelection(2);
- bucketSize = 7;
-
Context appContext = getContext().getApplicationContext();
if (appContext instanceof HabitsApplication)
{
HabitsApplication app = (HabitsApplication) appContext;
- taskRunner = app.getComponent().getTaskRunner();
+ prefs = app.getComponent().getPreferences();
}
-
+ inflate(getContext(), R.layout.show_habit_bar, this);
+ ButterKnife.bind(this);
+ boolSpinner.setSelection(1);
+ numericalSpinner.setSelection(2);
+ bucketSize = 7;
if (isInEditMode()) initEditMode();
}
@@ -119,11 +111,17 @@ public class BarCard extends HabitCard
chart.populateWithRandomData();
}
- private class RefreshTask implements Task
+ @Override
+ protected Task createRefreshTask()
+ {
+ return new RefreshTask(getHabit());
+ }
+
+ private class RefreshTask extends CancelableTask
{
private final Habit habit;
- public RefreshTask(Habit habit)
+ RefreshTask(Habit habit)
{
this.habit = habit;
}
@@ -131,9 +129,13 @@ public class BarCard extends HabitCard
@Override
public void doInBackground()
{
+ if (isCanceled()) return;
List checkmarks;
+ int firstWeekday = Calendar.SATURDAY;
+ if (prefs != null) firstWeekday = prefs.getFirstWeekday();
if (bucketSize == 1) checkmarks = habit.getCheckmarks().getAll();
- else checkmarks = habit.getCheckmarks().groupBy(getTruncateField(bucketSize));
+ else checkmarks = habit.getCheckmarks().groupBy(getTruncateField(bucketSize),
+ firstWeekday);
chart.setCheckmarks(checkmarks);
chart.setBucketSize(bucketSize);
}
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/FrequencyCard.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/FrequencyCard.java
index ff24596c3..a87606252 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/FrequencyCard.java
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/FrequencyCard.java
@@ -20,14 +20,16 @@
package org.isoron.uhabits.activities.habits.show.views;
import android.content.*;
-import android.support.annotation.*;
import android.util.*;
import android.widget.*;
+import androidx.annotation.Nullable;
+
import org.isoron.uhabits.*;
import org.isoron.uhabits.R;
import org.isoron.uhabits.activities.common.views.*;
import org.isoron.uhabits.core.models.*;
+import org.isoron.uhabits.core.preferences.*;
import org.isoron.uhabits.core.tasks.*;
import org.isoron.uhabits.utils.*;
@@ -44,7 +46,7 @@ public class FrequencyCard extends HabitCard
FrequencyChart chart;
@Nullable
- private TaskRunner taskRunner;
+ private Preferences prefs;
public FrequencyCard(Context context)
{
@@ -59,24 +61,21 @@ public class FrequencyCard extends HabitCard
}
@Override
- protected void refreshData()
+ protected Task createRefreshTask()
{
- if(taskRunner == null) return;
- taskRunner.execute(new RefreshTask());
+ return new RefreshTask();
}
private void init()
{
- inflate(getContext(), R.layout.show_habit_frequency, this);
- ButterKnife.bind(this);
-
Context appContext = getContext().getApplicationContext();
- if(appContext instanceof HabitsApplication)
+ if (appContext instanceof HabitsApplication)
{
HabitsApplication app = (HabitsApplication) appContext;
- taskRunner = app.getComponent().getTaskRunner();
+ prefs = app.getComponent().getPreferences();
}
-
+ inflate(getContext(), R.layout.show_habit_frequency, this);
+ ButterKnife.bind(this);
if (isInEditMode()) initEditMode();
}
@@ -88,13 +87,15 @@ public class FrequencyCard extends HabitCard
chart.populateWithRandomData();
}
- private class RefreshTask implements Task
+ private class RefreshTask extends CancelableTask
{
@Override
public void doInBackground()
{
+ if (isCanceled()) return;
RepetitionList reps = getHabit().getRepetitions();
HashMap frequency = reps.getWeekdayFrequency();
+ if(prefs != null) chart.setFirstWeekday(prefs.getFirstWeekday());
chart.setFrequency(frequency);
}
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/HabitCard.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/HabitCard.java
index 255b518e1..3e47830d9 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/HabitCard.java
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/HabitCard.java
@@ -20,12 +20,16 @@
package org.isoron.uhabits.activities.habits.show.views;
import android.content.*;
-import android.support.annotation.*;
import android.util.*;
import android.widget.*;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import org.isoron.uhabits.*;
import org.isoron.uhabits.core.models.*;
import org.isoron.uhabits.core.models.memory.*;
+import org.isoron.uhabits.core.tasks.*;
public abstract class HabitCard extends LinearLayout
implements ModelObservable.Listener
@@ -33,6 +37,12 @@ public abstract class HabitCard extends LinearLayout
@NonNull
private Habit habit;
+ @Nullable
+ private TaskRunner taskRunner;
+
+ @Nullable
+ private Task currentRefreshTask;
+
public HabitCard(Context context)
{
super(context);
@@ -82,7 +92,15 @@ public abstract class HabitCard extends LinearLayout
super.onDetachedFromWindow();
}
- protected abstract void refreshData();
+ protected void refreshData()
+ {
+ if(taskRunner == null) return;
+ if(currentRefreshTask != null) currentRefreshTask.cancel();
+ currentRefreshTask = createRefreshTask();
+ taskRunner.execute(currentRefreshTask);
+ }
+
+ protected abstract Task createRefreshTask();
private void attachTo(Habit habit)
{
@@ -99,5 +117,11 @@ public abstract class HabitCard extends LinearLayout
private void init()
{
if(!isInEditMode()) habit = new MemoryModelFactory().buildHabit();
+ Context appContext = getContext().getApplicationContext();
+ if(appContext instanceof HabitsApplication)
+ {
+ HabitsApplication app = (HabitsApplication) appContext;
+ taskRunner = app.getComponent().getTaskRunner();
+ }
}
}
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/HistoryCard.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/HistoryCard.java
index ce5ef1e3a..3aee88b4c 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/HistoryCard.java
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/HistoryCard.java
@@ -20,14 +20,16 @@
package org.isoron.uhabits.activities.habits.show.views;
import android.content.*;
-import android.support.annotation.*;
import android.util.*;
import android.widget.*;
+import androidx.annotation.Nullable;
+
import org.isoron.uhabits.*;
import org.isoron.uhabits.R;
import org.isoron.uhabits.activities.common.views.*;
import org.isoron.uhabits.core.models.*;
+import org.isoron.uhabits.core.preferences.*;
import org.isoron.uhabits.core.tasks.*;
import org.isoron.uhabits.utils.*;
@@ -41,11 +43,11 @@ public class HistoryCard extends HabitCard
@BindView(R.id.title)
TextView title;
- @NonNull
+ @Nullable
private Controller controller;
@Nullable
- private TaskRunner taskRunner;
+ private Preferences prefs;
public HistoryCard(Context context)
{
@@ -62,33 +64,25 @@ public class HistoryCard extends HabitCard
@OnClick(R.id.edit)
public void onClickEditButton()
{
- controller.onEditHistoryButtonClick();
+ if(controller != null) controller.onEditHistoryButtonClick();
}
- public void setController(@NonNull Controller controller)
+ public void setController(@Nullable Controller controller)
{
this.controller = controller;
}
- @Override
- protected void refreshData()
- {
- if(taskRunner == null) return;
- taskRunner.execute(new RefreshTask(getHabit()));
- }
-
private void init()
{
- inflate(getContext(), R.layout.show_habit_history, this);
- ButterKnife.bind(this);
-
Context appContext = getContext().getApplicationContext();
if (appContext instanceof HabitsApplication)
{
HabitsApplication app = (HabitsApplication) appContext;
- taskRunner = app.getComponent().getTaskRunner();
+ prefs = app.getComponent().getPreferences();
}
+ inflate(getContext(), R.layout.show_habit_history, this);
+ ButterKnife.bind(this);
controller = new Controller() {};
if (isInEditMode()) initEditMode();
}
@@ -101,21 +95,32 @@ public class HistoryCard extends HabitCard
chart.populateWithRandomData();
}
+ @Override
+ protected Task createRefreshTask()
+ {
+ return new RefreshTask(getHabit());
+ }
+
public interface Controller
{
default void onEditHistoryButtonClick() {}
}
- private class RefreshTask implements Task
+ private class RefreshTask extends CancelableTask
{
private final Habit habit;
- public RefreshTask(Habit habit) {this.habit = habit;}
+ private RefreshTask(Habit habit)
+ {
+ this.habit = habit;
+ }
@Override
public void doInBackground()
{
- int checkmarks[] = habit.getCheckmarks().getAllValues();
+ if (isCanceled()) return;
+ int[] checkmarks = habit.getCheckmarks().getAllValues();
+ if(prefs != null) chart.setFirstWeekday(prefs.getFirstWeekday());
chart.setCheckmarks(checkmarks);
}
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/NotesCard.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/NotesCard.kt
new file mode 100644
index 000000000..5e9d6864b
--- /dev/null
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/NotesCard.kt
@@ -0,0 +1,26 @@
+package org.isoron.uhabits.activities.habits.show.views
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.View
+import android.widget.TextView
+import org.isoron.uhabits.R
+import org.isoron.uhabits.core.tasks.Task
+
+class NotesCard(context: Context?, attrs: AttributeSet?) : HabitCard(context, attrs) {
+
+ private val notesTextView: TextView
+
+ init {
+ View.inflate(getContext(), R.layout.show_habit_notes, this)
+ notesTextView = findViewById(R.id.habitNotes)
+ }
+
+ override fun refreshData() {
+ notesTextView.text = habit.description
+ visibility = if(habit.description.isEmpty()) View.GONE else View.VISIBLE
+ notesTextView.visibility = visibility
+ }
+
+ override fun createRefreshTask(): Task = error("refresh task should never be called.")
+}
\ No newline at end of file
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/OverviewCard.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/OverviewCard.java
index f1acb1cdd..14327643c 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/OverviewCard.java
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/OverviewCard.java
@@ -20,12 +20,12 @@
package org.isoron.uhabits.activities.habits.show.views;
import android.content.*;
-import android.support.annotation.*;
import android.util.*;
import android.widget.*;
+import androidx.annotation.NonNull;
+
import org.isoron.androidbase.utils.*;
-import org.isoron.uhabits.*;
import org.isoron.uhabits.R;
import org.isoron.uhabits.activities.common.views.*;
import org.isoron.uhabits.core.models.*;
@@ -60,9 +60,6 @@ public class OverviewCard extends HabitCard
private int color;
- @Nullable
- private TaskRunner taskRunner;
-
public OverviewCard(Context context)
{
super(context);
@@ -75,13 +72,6 @@ public class OverviewCard extends HabitCard
init();
}
- @Override
- protected void refreshData()
- {
- if(taskRunner == null) return;
- taskRunner.execute(new RefreshTask());
- }
-
private String formatPercentageDiff(float percentageDiff)
{
return String.format("%s%.0f%%", (percentageDiff >= 0 ? "+" : "\u2212"),
@@ -90,17 +80,9 @@ public class OverviewCard extends HabitCard
private void init()
{
- Context appContext = getContext().getApplicationContext();
- if (appContext instanceof HabitsApplication)
- {
- HabitsApplication app = (HabitsApplication) appContext;
- taskRunner = app.getComponent().getTaskRunner();
- }
-
inflate(getContext(), R.layout.show_habit_overview, this);
ButterKnife.bind(this);
cache = new Cache();
-
if (isInEditMode()) initEditMode();
}
@@ -146,20 +128,27 @@ public class OverviewCard extends HabitCard
private class Cache
{
- public float todayScore;
+ float todayScore;
- public float lastMonthScore;
+ float lastMonthScore;
- public float lastYearScore;
+ float lastYearScore;
- public long totalCount;
+ long totalCount;
+ }
+
+ @Override
+ protected Task createRefreshTask()
+ {
+ return new RefreshTask();
}
- private class RefreshTask implements Task
+ private class RefreshTask extends CancelableTask
{
@Override
public void doInBackground()
{
+ if (isCanceled()) return;
Habit habit = getHabit();
ScoreList scores = habit.getScores();
@@ -177,6 +166,7 @@ public class OverviewCard extends HabitCard
@Override
public void onPostExecute()
{
+ if (isCanceled()) return;
refreshScore();
}
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/ScoreCard.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/ScoreCard.java
index eb81a954e..fc3bbf3c9 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/ScoreCard.java
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/ScoreCard.java
@@ -20,10 +20,12 @@
package org.isoron.uhabits.activities.habits.show.views;
import android.content.*;
-import android.support.annotation.*;
import android.util.*;
import android.widget.*;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import org.isoron.uhabits.*;
import org.isoron.uhabits.R;
import org.isoron.uhabits.activities.common.views.*;
@@ -52,9 +54,6 @@ public class ScoreCard extends HabitCard
private int bucketSize;
- @Nullable
- private TaskRunner taskRunner;
-
@Nullable
private Preferences prefs;
@@ -94,13 +93,6 @@ public class ScoreCard extends HabitCard
refreshData();
}
- @Override
- protected void refreshData()
- {
- if(taskRunner == null) return;
- taskRunner.execute(new RefreshTask());
- }
-
private int getDefaultSpinnerPosition()
{
if(prefs == null) return 0;
@@ -113,7 +105,6 @@ public class ScoreCard extends HabitCard
if (appContext instanceof HabitsApplication)
{
HabitsApplication app = (HabitsApplication) appContext;
- taskRunner = app.getComponent().getTaskRunner();
prefs = app.getComponent().getPreferences();
}
@@ -140,18 +131,29 @@ public class ScoreCard extends HabitCard
bucketSize = BUCKET_SIZES[position];
}
- private class RefreshTask implements Task
+ @Override
+ protected Task createRefreshTask()
+ {
+ return new RefreshTask();
+ }
+
+ private class RefreshTask extends CancelableTask
{
@Override
public void doInBackground()
{
+ if (isCanceled()) return;
List scores;
ScoreList scoreList = getHabit().getScores();
+ int firstWeekday = Calendar.SATURDAY;
+ if (prefs != null) firstWeekday = prefs.getFirstWeekday();
+ Log.d("ScoreCard", "firstWeekday="+firstWeekday);
if (bucketSize == 1) scores = scoreList.toList();
- else scores = scoreList.groupBy(getTruncateField(bucketSize));
+ else scores = scoreList.groupBy(getTruncateField(bucketSize), firstWeekday);
chart.setScores(scores);
+ chart.reset();
chart.setBucketSize(bucketSize);
}
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/StreakCard.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/StreakCard.java
index e4f07e6d5..699e21af3 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/StreakCard.java
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/StreakCard.java
@@ -20,11 +20,9 @@
package org.isoron.uhabits.activities.habits.show.views;
import android.content.*;
-import android.support.annotation.*;
import android.util.*;
import android.widget.*;
-import org.isoron.uhabits.*;
import org.isoron.uhabits.R;
import org.isoron.uhabits.activities.common.views.*;
import org.isoron.uhabits.core.models.*;
@@ -45,9 +43,6 @@ public class StreakCard extends HabitCard
@BindView(R.id.streakChart)
StreakChart streakChart;
- @Nullable
- private TaskRunner taskRunner;
-
public StreakCard(Context context)
{
super(context);
@@ -60,22 +55,8 @@ public class StreakCard extends HabitCard
init();
}
- @Override
- protected void refreshData()
- {
- if(taskRunner == null) return;
- taskRunner.execute(new RefreshTask());
- }
-
private void init()
{
- Context appContext = getContext().getApplicationContext();
- if (appContext instanceof HabitsApplication)
- {
- HabitsApplication app = (HabitsApplication) appContext;
- taskRunner = app.getComponent().getTaskRunner();
- }
-
inflate(getContext(), R.layout.show_habit_streak, this);
ButterKnife.bind(this);
setOrientation(VERTICAL);
@@ -90,13 +71,20 @@ public class StreakCard extends HabitCard
streakChart.populateWithRandomData();
}
- private class RefreshTask implements Task
+ @Override
+ protected Task createRefreshTask()
+ {
+ return new RefreshTask();
+ }
+
+ private class RefreshTask extends CancelableTask
{
- public List bestStreaks;
+ List bestStreaks;
@Override
public void doInBackground()
{
+ if (isCanceled()) return;
StreakList streaks = getHabit().getStreaks();
bestStreaks = streaks.getBest(NUM_STREAKS);
}
@@ -104,6 +92,7 @@ public class StreakCard extends HabitCard
@Override
public void onPostExecute()
{
+ if (isCanceled()) return;
streakChart.setStreaks(bestStreaks);
}
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/SubtitleCard.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/SubtitleCard.java
deleted file mode 100644
index 74e6b131d..000000000
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/SubtitleCard.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright (C) 2016 Álinson Santos Xavier
- *
- * This file is part of Loop Habit Tracker.
- *
- * Loop Habit Tracker is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by the
- * Free Software Foundation, either version 3 of the License, or (at your
- * option) any later version.
- *
- * Loop Habit Tracker is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see .
- */
-
-package org.isoron.uhabits.activities.habits.show.views;
-
-import android.annotation.*;
-import android.content.*;
-import android.content.res.*;
-import android.util.*;
-import android.widget.*;
-
-import org.isoron.uhabits.R;
-import org.isoron.uhabits.core.models.*;
-import org.isoron.uhabits.utils.*;
-
-import butterknife.*;
-
-public class SubtitleCard extends HabitCard
-{
- @BindView(R.id.questionLabel)
- TextView questionLabel;
-
- @BindView(R.id.frequencyLabel)
- TextView frequencyLabel;
-
- @BindView(R.id.reminderLabel)
- TextView reminderLabel;
-
- public SubtitleCard(Context context, AttributeSet attrs)
- {
- super(context, attrs);
- init();
- }
-
- @Override
- protected void refreshData()
- {
- Habit habit = getHabit();
- int color = PaletteUtils.getColor(getContext(), habit.getColor());
-
- reminderLabel.setText(getResources().getString(R.string.reminder_off));
- questionLabel.setVisibility(VISIBLE);
-
- questionLabel.setTextColor(color);
- questionLabel.setText(habit.getDescription());
- frequencyLabel.setText(toText(habit.getFrequency()));
-
- if (habit.hasReminder()) updateReminderText(habit.getReminder());
-
- if (habit.getDescription().isEmpty()) questionLabel.setVisibility(GONE);
-
- invalidate();
- }
-
- private void init()
- {
- inflate(getContext(), R.layout.show_habit_subtitle, this);
- ButterKnife.bind(this);
-
- if (isInEditMode()) initEditMode();
- }
-
- @SuppressLint("SetTextI18n")
- private void initEditMode()
- {
- questionLabel.setTextColor(PaletteUtils.getAndroidTestColor(1));
- questionLabel.setText("Have you meditated today?");
- reminderLabel.setText("08:00");
- }
-
- private String toText(Frequency freq)
- {
- Resources resources = getResources();
- Integer num = freq.getNumerator();
- Integer den = freq.getDenominator();
-
- if (num.equals(den)) return resources.getString(R.string.every_day);
-
- if (num == 1)
- {
- if (den == 7) return resources.getString(R.string.every_week);
- if (den % 7 == 0)
- return resources.getString(R.string.every_x_weeks, den / 7);
- return resources.getString(R.string.every_x_days, den);
- }
-
- String times_every = resources.getString(R.string.times_every);
- return String.format("%d %s %d %s", num, times_every, den,
- resources.getString(R.string.days));
- }
-
- private void updateReminderText(Reminder reminder)
- {
- reminderLabel.setText(
- AndroidDateUtils.formatTime(getContext(), reminder.getHour(),
- reminder.getMinute()));
- }
-}
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/SubtitleCard.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/SubtitleCard.kt
new file mode 100644
index 000000000..869b86fa9
--- /dev/null
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/SubtitleCard.kt
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2016 Álinson Santos Xavier
+ *
+ * This file is part of Loop Habit Tracker.
+ *
+ * Loop Habit Tracker is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * Loop Habit Tracker is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see .
+ */
+package org.isoron.uhabits.activities.habits.show.views
+
+import android.annotation.*
+import android.content.*
+import android.util.*
+import android.view.*
+import org.isoron.androidbase.utils.*
+import org.isoron.uhabits.*
+import org.isoron.uhabits.activities.habits.list.views.*
+import org.isoron.uhabits.core.models.*
+import org.isoron.uhabits.core.tasks.*
+import org.isoron.uhabits.databinding.*
+import org.isoron.uhabits.utils.*
+import org.isoron.uhabits.utils.PaletteUtils.getAndroidTestColor
+import org.isoron.uhabits.utils.PaletteUtils.getColor
+import java.util.*
+
+class SubtitleCard(context: Context?, attrs: AttributeSet?) : HabitCard(context, attrs) {
+
+ init {
+ init()
+ }
+
+ private lateinit var binding: ShowHabitSubtitleBinding
+
+ public override fun refreshData() {
+ val habit = habit
+ val color = getColor(context, habit.color)
+ if (habit.isNumerical) {
+ binding.targetText.text = "${habit.targetValue.toShortString()} ${habit.unit}"
+ } else {
+ binding.targetIcon.visibility = View.GONE
+ binding.targetText.visibility = View.GONE
+ }
+ binding.reminderLabel.text = resources.getString(R.string.reminder_off)
+ binding.questionLabel.visibility = View.VISIBLE
+ binding.questionLabel.setTextColor(color)
+ binding.questionLabel.text = habit.question
+ binding.frequencyLabel.text = toText(habit.frequency)
+ if (habit.hasReminder()) updateReminderText(habit.reminder)
+ if (habit.question.isEmpty()) binding.questionLabel.visibility = View.GONE
+ invalidate()
+ }
+
+ private fun init() {
+ val fontAwesome = InterfaceUtils.getFontAwesome(context)
+ binding = ShowHabitSubtitleBinding.inflate(LayoutInflater.from(context), this)
+ binding.targetIcon.typeface = fontAwesome
+ binding.frequencyIcon.typeface = fontAwesome
+ binding.reminderIcon.typeface = fontAwesome
+ if (isInEditMode) initEditMode()
+ }
+
+ @SuppressLint("SetTextI18n")
+ private fun initEditMode() {
+ binding.questionLabel.setTextColor(getAndroidTestColor(1))
+ binding.questionLabel.text = "Have you meditated today?"
+ binding.reminderLabel.text = "08:00"
+ }
+
+ private fun toText(freq: Frequency): String {
+ val resources = resources
+ val num = freq.numerator
+ val den = freq.denominator
+ if (num == den) return resources.getString(R.string.every_day)
+ if (num == 1) {
+ if (den == 7) return resources.getString(R.string.every_week)
+ if (den % 7 == 0) return resources.getString(R.string.every_x_weeks, den / 7)
+ return if (den >= 30) resources.getString(R.string.every_month) else resources.getString(R.string.every_x_days, den)
+ }
+ val times_every = resources.getString(R.string.times_every)
+ return String.format(Locale.US, "%d %s %d %s", num, times_every, den,
+ resources.getString(R.string.days))
+ }
+
+ private fun updateReminderText(reminder: Reminder) {
+ binding.reminderLabel.text = AndroidDateUtils.formatTime(context, reminder.hour,
+ reminder.minute)
+ }
+
+ override fun createRefreshTask(): Task {
+ // Never called
+ throw IllegalStateException()
+ }
+
+}
\ No newline at end of file
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/TargetCard.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/TargetCard.java
new file mode 100644
index 000000000..464ca1955
--- /dev/null
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/views/TargetCard.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2016 Álinson Santos Xavier
+ *
+ * This file is part of Loop Habit Tracker.
+ *
+ * Loop Habit Tracker is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * Loop Habit Tracker is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see .
+ */
+
+package org.isoron.uhabits.activities.habits.show.views;
+
+import android.content.*;
+import android.content.res.*;
+import android.util.*;
+import android.widget.*;
+
+import androidx.annotation.*;
+
+import org.isoron.uhabits.R;
+import org.isoron.uhabits.*;
+import org.isoron.uhabits.activities.common.views.*;
+import org.isoron.uhabits.core.models.*;
+import org.isoron.uhabits.core.preferences.*;
+import org.isoron.uhabits.core.tasks.*;
+import org.isoron.uhabits.core.utils.*;
+import org.isoron.uhabits.utils.*;
+
+import java.util.*;
+
+import butterknife.*;
+
+public class TargetCard extends HabitCard
+{
+ @BindView(R.id.title)
+ TextView title;
+
+ @BindView(R.id.targetChart)
+ TargetChart targetChart;
+
+ int firstWeekday = Calendar.SATURDAY;
+
+ public TargetCard(Context context)
+ {
+ super(context);
+ init();
+ }
+
+ public TargetCard(Context context, AttributeSet attrs)
+ {
+ super(context, attrs);
+ init();
+ }
+
+ private void init()
+ {
+ inflate(getContext(), R.layout.show_habit_target, this);
+ ButterKnife.bind(this);
+ setOrientation(VERTICAL);
+
+ Context app = getContext().getApplicationContext();
+ if (app instanceof HabitsApplication) {
+ HabitsApplication habitsApp = (HabitsApplication) app;
+ Preferences prefs = habitsApp.getComponent().getPreferences();
+ firstWeekday = prefs.getFirstWeekday();
+ }
+
+ if (isInEditMode()) initEditMode();
+ }
+
+ private void initEditMode()
+ {
+ int color = PaletteUtils.getAndroidTestColor(1);
+ title.setTextColor(color);
+ targetChart.setColor(color);
+ targetChart.populateWithRandomData();
+ }
+
+ @Override
+ protected Task createRefreshTask()
+ {
+ return new RefreshTask(getContext(), getHabit(), firstWeekday, targetChart, title);
+ }
+
+ public static class RefreshTask extends CancelableTask
+ {
+ double todayValue;
+ double thisWeekValue;
+ double thisMonthValue;
+ double thisQuarterValue;
+ double thisYearValue;
+
+ private Context context;
+ private Habit habit;
+ private int firstWeekday;
+ private TargetChart chart;
+ private TextView title;
+
+ public RefreshTask(@NonNull Context context,
+ @NonNull Habit habit,
+ int firstWeekday,
+ @NonNull TargetChart chart,
+ @Nullable TextView title)
+ {
+ this.context = context;
+ this.habit = habit;
+ this.firstWeekday = firstWeekday;
+ this.chart = chart;
+ this.title = title;
+ }
+
+ @Override
+ public void doInBackground()
+ {
+ if (isCanceled()) return;
+ CheckmarkList checkmarks = habit.getCheckmarks();
+ todayValue = checkmarks.getTodayValue() / 1e3;
+ thisWeekValue = checkmarks.getThisWeekValue(firstWeekday) / 1e3;
+ thisMonthValue = checkmarks.getThisMonthValue() / 1e3;
+ thisQuarterValue = checkmarks.getThisQuarterValue() / 1e3;
+ thisYearValue = checkmarks.getThisYearValue() / 1e3;
+ }
+
+ @Override
+ public void onPostExecute()
+ {
+ if (isCanceled()) return;
+ Calendar cal = DateUtils.getStartOfTodayCalendar();
+ int daysInMonth = cal.getActualMaximum(Calendar.DAY_OF_MONTH);
+ int daysInQuarter = 91;
+ int daysInYear = cal.getActualMaximum(Calendar.DAY_OF_YEAR);
+
+ int den = habit.getFrequency().getDenominator();
+ double dailyTarget = habit.getTargetValue() / den;
+ Resources res = context.getResources();
+
+ ArrayList values = new ArrayList<>();
+ if (den <= 1) values.add(todayValue);
+ if (den <= 7) values.add(thisWeekValue);
+ values.add(thisMonthValue);
+ values.add(thisQuarterValue);
+ values.add(thisYearValue);
+ chart.setValues(values);
+
+ ArrayList targets = new ArrayList<>();
+ if (den <= 1) targets.add(dailyTarget);
+ if (den <= 7) targets.add(dailyTarget * 7);
+ targets.add(dailyTarget * daysInMonth);
+ targets.add(dailyTarget * daysInQuarter);
+ targets.add(dailyTarget * daysInYear);
+ chart.setTargets(targets);
+
+ ArrayList labels = new ArrayList<>();
+ if (den <= 1) labels.add(res.getString(R.string.today));
+ if (den <= 7) labels.add(res.getString(R.string.week));
+ labels.add(res.getString(R.string.month));
+ labels.add(res.getString(R.string.quarter));
+ labels.add(res.getString(R.string.year));
+ chart.setLabels(labels);
+ }
+
+ @Override
+ public void onPreExecute()
+ {
+ int color = PaletteUtils.getColor(context, habit.getColor());
+ if(title != null) title.setTextColor(color);
+ chart.setColor(color);
+ }
+ }
+}
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/intro/IntroActivity.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/intro/IntroActivity.kt
similarity index 57%
rename from android/uhabits-android/src/main/java/org/isoron/uhabits/activities/intro/IntroActivity.java
rename to android/uhabits-android/src/main/java/org/isoron/uhabits/activities/intro/IntroActivity.kt
index 4fe80138c..f405a072f 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/intro/IntroActivity.java
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/intro/IntroActivity.kt
@@ -17,52 +17,41 @@
* with this program. If not, see .
*/
-package org.isoron.uhabits.activities.intro;
+package org.isoron.uhabits.activities.intro
-import android.graphics.*;
-import android.os.*;
+import android.graphics.*
+import android.os.*
-import com.github.paolorotolo.appintro.*;
+import com.github.paolorotolo.appintro.*
-import org.isoron.uhabits.R;
+import org.isoron.uhabits.R
/**
* Activity that introduces the app to the user, shown only after the app is
* launched for the first time.
*/
-public class IntroActivity extends AppIntro2
-{
- @Override
- public void init(Bundle savedInstanceState)
- {
- showStatusBar(false);
+class IntroActivity : AppIntro2() {
+ override fun init(savedInstanceState: Bundle?) {
+ showStatusBar(false)
addSlide(AppIntroFragment.newInstance(getString(R.string.intro_title_1),
- getString(R.string.intro_description_1), R.drawable.intro_icon_1,
- Color.parseColor("#194673")));
+ getString(R.string.intro_description_1), R.drawable.intro_icon_1,
+ Color.parseColor("#194673")))
addSlide(AppIntroFragment.newInstance(getString(R.string.intro_title_2),
- getString(R.string.intro_description_2), R.drawable.intro_icon_2,
- Color.parseColor("#ffa726")));
+ getString(R.string.intro_description_2), R.drawable.intro_icon_2,
+ Color.parseColor("#ffa726")))
addSlide(AppIntroFragment.newInstance(getString(R.string.intro_title_4),
- getString(R.string.intro_description_4), R.drawable.intro_icon_4,
- Color.parseColor("#9575cd")));
+ getString(R.string.intro_description_4), R.drawable.intro_icon_4,
+ Color.parseColor("#9575cd")))
}
- @Override
- public void onNextPressed()
- {
- }
+ override fun onNextPressed() {}
- @Override
- public void onDonePressed()
- {
- finish();
+ override fun onDonePressed() {
+ finish()
}
- @Override
- public void onSlideChanged()
- {
- }
+ override fun onSlideChanged() {}
}
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/settings/SettingsFragment.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/settings/SettingsFragment.java
index 1ed257495..049f6e319 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/settings/SettingsFragment.java
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/settings/SettingsFragment.java
@@ -24,17 +24,26 @@ import android.content.*;
import android.net.*;
import android.os.*;
import android.provider.*;
-import android.support.annotation.*;
-import android.support.v7.preference.*;
+import android.util.*;
+
+import androidx.annotation.Nullable;
+import androidx.preference.ListPreference;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceFragmentCompat;
-import org.isoron.uhabits.*;
import org.isoron.uhabits.R;
+import org.isoron.uhabits.*;
import org.isoron.uhabits.core.preferences.*;
import org.isoron.uhabits.core.ui.*;
+import org.isoron.uhabits.core.utils.*;
import org.isoron.uhabits.notifications.*;
+import org.isoron.uhabits.widgets.*;
+
+import java.util.*;
import static android.media.RingtoneManager.*;
-import static android.os.Build.VERSION.SDK_INT;
+import static android.os.Build.VERSION.*;
import static org.isoron.uhabits.activities.habits.list.ListHabitsScreenKt.*;
public class SettingsFragment extends PreferenceFragmentCompat
@@ -49,6 +58,9 @@ public class SettingsFragment extends PreferenceFragmentCompat
@Nullable
private Preferences prefs;
+ @Nullable
+ private WidgetUpdater widgetUpdater;
+
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data)
{
@@ -73,6 +85,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
{
HabitsApplication app = (HabitsApplication) appContext;
prefs = app.getComponent().getPreferences();
+ widgetUpdater = app.getComponent().getWidgetUpdater();
}
setResultOnPreferenceClick("importData", RESULT_IMPORT_DATA);
@@ -137,21 +150,38 @@ public class SettingsFragment extends PreferenceFragmentCompat
devCategory.setVisible(false);
}
- if (SDK_INT < Build.VERSION_CODES.O)
- findPreference("reminderCustomize").setVisible(false);
- else
- {
- findPreference("reminderSound").setVisible(false);
- findPreference("pref_snooze_interval").setVisible(false);
- }
+ updateWeekdayPreference();
+
+ // Temporarily disable this; we now always ask
+ findPreference("reminderSound").setVisible(false);
+ findPreference("pref_snooze_interval").setVisible(false);
updateSync();
}
+ private void updateWeekdayPreference()
+ {
+ if (prefs == null) return;
+ ListPreference weekdayPref = (ListPreference) findPreference("pref_first_weekday");
+ int currentFirstWeekday = prefs.getFirstWeekday();
+ String[] dayNames = DateUtils.getLongWeekdayNames(Calendar.SATURDAY);
+ String[] dayValues = {"7", "1", "2", "3", "4", "5", "6"};
+ weekdayPref.setEntries(dayNames);
+ weekdayPref.setEntryValues(dayValues);
+ weekdayPref.setDefaultValue(Integer.toString(currentFirstWeekday));
+ weekdayPref.setSummary(dayNames[currentFirstWeekday % 7]);
+ }
+
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
String key)
{
+ if (key.equals("pref_widget_opacity") && widgetUpdater != null)
+ {
+ Log.d("SettingsFragment", "updating widgets");
+ widgetUpdater.updateWidgets();
+ }
+ if (key.equals("pref_first_weekday")) updateWeekdayPreference();
BackupManager.dataChanged("org.isoron.uhabits");
updateSync();
}
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/automation/EditSettingActivity.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/automation/EditSettingActivity.kt
index 0c7ef8a2d..9170d8311 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/automation/EditSettingActivity.kt
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/automation/EditSettingActivity.kt
@@ -35,8 +35,10 @@ class EditSettingActivity : BaseActivity() {
.setCompletedAllowed(true)
.build())
+ val args = SettingUtils.parseIntent(this.intent, habits)
+
val controller = EditSettingController(this)
- val rootView = EditSettingRootView(this, habits, controller)
+ val rootView = EditSettingRootView(this, habits, controller, args)
val screen = BaseScreen(this)
screen.setRootView(rootView)
setScreen(screen)
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/automation/EditSettingRootView.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/automation/EditSettingRootView.kt
index df149d946..83de784e1 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/automation/EditSettingRootView.kt
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/automation/EditSettingRootView.kt
@@ -21,8 +21,8 @@ package org.isoron.uhabits.automation
import android.R.layout.*
import android.content.*
-import android.support.v7.widget.*
-import android.support.v7.widget.Toolbar
+import androidx.appcompat.widget.*
+import androidx.appcompat.widget.Toolbar
import android.widget.*
import butterknife.*
import org.isoron.androidbase.activities.*
@@ -34,7 +34,8 @@ import java.util.*
class EditSettingRootView(
context: Context,
private val habitList: HabitList,
- private val controller: EditSettingController
+ private val controller: EditSettingController,
+ args: SettingUtils.Arguments?
) : BaseRootView(context) {
@BindView(R.id.toolbar)
@@ -50,6 +51,11 @@ class EditSettingRootView(
addView(inflate(getContext(), R.layout.automation, null))
ButterKnife.bind(this)
populateHabitSpinner()
+
+ args?.let {
+ habitSpinner.setSelection(habitList.indexOf(it.habit))
+ actionSpinner.setSelection(it.action)
+ }
}
override fun getToolbar(): Toolbar {
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/automation/FireSettingReceiver.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/automation/FireSettingReceiver.kt
index 51a770753..a5d63ade5 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/automation/FireSettingReceiver.kt
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/automation/FireSettingReceiver.kt
@@ -44,7 +44,7 @@ class FireSettingReceiver : BroadcastReceiver() {
.habitsApplicationComponent(app.component)
.build()
allHabits = app.component.habitList
- val args = parseIntent(intent) ?: return
+ val args = SettingUtils.parseIntent(intent, allHabits) ?: return
val timestamp = DateUtils.getToday()
val controller = component.widgetController
@@ -55,19 +55,9 @@ class FireSettingReceiver : BroadcastReceiver() {
}
}
- private fun parseIntent(intent: Intent): Arguments? {
- val bundle = intent.getBundleExtra(EXTRA_BUNDLE) ?: return null
- val action = bundle.getInt("action")
- if (action < 0 || action > 2) return null
- val habit = allHabits.getById(bundle.getLong("habit")) ?: return null
- return Arguments(action, habit)
- }
-
@ReceiverScope
@Component(dependencies = arrayOf(HabitsApplicationComponent::class))
internal interface ReceiverComponent {
val widgetController: WidgetBehavior
}
-
- private class Arguments(var action: Int, var habit: Habit)
}
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/automation/SettingUtils.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/automation/SettingUtils.kt
new file mode 100644
index 000000000..7a8b0310a
--- /dev/null
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/automation/SettingUtils.kt
@@ -0,0 +1,18 @@
+package org.isoron.uhabits.automation
+
+import android.content.Intent
+import org.isoron.uhabits.core.models.Habit
+import org.isoron.uhabits.core.models.HabitList
+
+object SettingUtils {
+ @JvmStatic
+ fun parseIntent(intent: Intent, allHabits: HabitList): Arguments? {
+ val bundle = intent.getBundleExtra(EXTRA_BUNDLE) ?: return null
+ val action = bundle.getInt("action")
+ if (action < 0 || action > 2) return null
+ val habit = allHabits.getById(bundle.getLong("habit")) ?: return null
+ return Arguments(action, habit)
+ }
+
+ class Arguments(var action: Int, var habit: Habit)
+}
\ No newline at end of file
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/intents/IntentFactory.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/intents/IntentFactory.kt
index fd04ee1a4..2287a56d1 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/intents/IntentFactory.kt
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/intents/IntentFactory.kt
@@ -21,8 +21,10 @@ package org.isoron.uhabits.intents
import android.content.*
import android.net.*
+import org.isoron.androidbase.activities.*
import org.isoron.uhabits.*
import org.isoron.uhabits.activities.about.*
+import org.isoron.uhabits.activities.habits.edit.*
import org.isoron.uhabits.activities.habits.show.*
import org.isoron.uhabits.activities.intro.*
import org.isoron.uhabits.activities.settings.*
@@ -46,6 +48,9 @@ class IntentFactory
fun sendFeedback(context: Context) =
buildSendToIntent(context.getString(R.string.feedbackURL))
+ fun privacyPolicy(context: Context) =
+ buildViewIntent(context.getString(R.string.privacyPolicyURL))
+
fun startAboutActivity(context: Context) =
Intent(context, AboutActivity::class.java)
@@ -75,4 +80,24 @@ class IntentFactory
action = Intent.ACTION_VIEW
data = Uri.parse(url)
}
+
+ fun codeContributors(context: Context) =
+ buildViewIntent(context.getString(R.string.codeContributorsURL))
+
+ private fun startEditActivity(context: Context): Intent {
+ return Intent(context, EditHabitActivity::class.java)
+ }
+
+ fun startEditActivity(context: Context, habit: Habit): Intent {
+ val intent = startEditActivity(context)
+ intent.putExtra("habitId", habit.id)
+ intent.putExtra("habitType", habit.type)
+ return intent
+ }
+
+ fun startEditActivity(context: Context, habitType: Int): Intent {
+ val intent = startEditActivity(context)
+ intent.putExtra("habitType", habitType)
+ return intent
+ }
}
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/intents/IntentScheduler.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/intents/IntentScheduler.kt
index 689beb2ec..934942f15 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/intents/IntentScheduler.kt
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/intents/IntentScheduler.kt
@@ -25,25 +25,34 @@ import android.content.*
import android.content.Context.*
import android.os.Build.VERSION.*
import android.os.Build.VERSION_CODES.*
+import android.util.*
import org.isoron.androidbase.*
import org.isoron.uhabits.*
import org.isoron.uhabits.core.*
import org.isoron.uhabits.core.models.*
import org.isoron.uhabits.core.reminders.*
+import org.isoron.uhabits.core.utils.*
+import java.util.*
import javax.inject.*
@AppScope
class IntentScheduler
@Inject constructor(
@AppContext context: Context,
- private val pendingIntents: PendingIntentFactory,
- private val logger: HabitLogger
+ private val pendingIntents: PendingIntentFactory
) : ReminderScheduler.SystemScheduler {
private val manager =
context.getSystemService(ALARM_SERVICE) as AlarmManager
fun schedule(timestamp: Long, intent: PendingIntent) {
+ Log.d("IntentScheduler",
+ "timestamp=" + timestamp + " current=" + System.currentTimeMillis())
+ if (timestamp < System.currentTimeMillis()) {
+ Log.e("IntentScheduler",
+ "Ignoring attempt to schedule intent in the past.")
+ return;
+ }
if (SDK_INT >= M)
manager.setExactAndAllowWhileIdle(RTC_WAKEUP, timestamp, intent)
else
@@ -55,6 +64,19 @@ class IntentScheduler
timestamp: Long) {
val intent = pendingIntents.showReminder(habit, reminderTime, timestamp)
schedule(reminderTime, intent)
- logger.logReminderScheduled(habit, reminderTime)
+ logReminderScheduled(habit, reminderTime)
+ }
+
+ override fun log(componentName: String, msg: String) {
+ Log.d(componentName, msg)
+ }
+
+ private fun logReminderScheduled(habit: Habit, reminderTime: Long) {
+ val min = Math.min(5, habit.name.length)
+ val name = habit.name.substring(0, min)
+ val df = DateFormats.getBackupDateFormat()
+ val time = df.format(Date(reminderTime))
+ Log.i("ReminderHelper",
+ String.format("Setting alarm (%s): %s", time, name))
}
}
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/intents/PendingIntentFactory.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/intents/PendingIntentFactory.kt
index 16d1ac5b1..51c124b96 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/intents/PendingIntentFactory.kt
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/intents/PendingIntentFactory.kt
@@ -64,7 +64,7 @@ class PendingIntentFactory
FLAG_UPDATE_CURRENT)
fun showHabit(habit: Habit): PendingIntent =
- android.support.v4.app.TaskStackBuilder
+ androidx.core.app.TaskStackBuilder
.create(context)
.addNextIntentWithParentStack(
intentFactory.startShowHabitActivity(
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/notifications/AndroidNotificationTray.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/notifications/AndroidNotificationTray.kt
index 468fa678c..3fff2b615 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/notifications/AndroidNotificationTray.kt
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/notifications/AndroidNotificationTray.kt
@@ -25,9 +25,9 @@ import android.graphics.*
import android.graphics.BitmapFactory.*
import android.os.*
import android.os.Build.VERSION.*
-import android.support.annotation.*
-import android.support.v4.app.*
-import android.support.v4.app.NotificationCompat.*
+import androidx.core.app.*
+import androidx.core.app.NotificationCompat.*
+import android.util.*
import org.isoron.androidbase.*
import org.isoron.uhabits.R
import org.isoron.uhabits.core.*
@@ -37,9 +37,6 @@ import org.isoron.uhabits.core.ui.*
import org.isoron.uhabits.intents.*
import javax.inject.*
-
-
-
@AppScope
class AndroidNotificationTray
@Inject constructor(
@@ -47,39 +44,49 @@ class AndroidNotificationTray
private val pendingIntents: PendingIntentFactory,
private val preferences: Preferences,
private val ringtoneManager: RingtoneManager
-) : NotificationTray.SystemTray {
-
+ ) : NotificationTray.SystemTray {
private var active = HashSet()
+ override fun log(msg: String) {
+ Log.d("AndroidNotificationTray", msg)
+ }
+
override fun removeNotification(id: Int) {
val manager = NotificationManagerCompat.from(context)
manager.cancel(id)
active.remove(id)
-
- // Clear the group summary notification
- if(active.isEmpty()) manager.cancelAll()
}
override fun showNotification(habit: Habit,
notificationId: Int,
timestamp: Timestamp,
- reminderTime: Long)
- {
+ reminderTime: Long) {
val notificationManager = NotificationManagerCompat.from(context)
- val summary = buildSummary(reminderTime)
- notificationManager.notify(Int.MAX_VALUE, summary)
+ //val summary = buildSummary(habit, reminderTime)
+ //notificationManager.notify(Int.MAX_VALUE, summary)
val notification = buildNotification(habit, reminderTime, timestamp)
createAndroidNotificationChannel(context)
- notificationManager.notify(notificationId, notification)
+ try {
+ notificationManager.notify(notificationId, notification)
+ } catch (e: RuntimeException) {
+ // Some Xiaomi phones produce a RuntimeException if custom notification sounds are used.
+ Log.i("AndroidNotificationTray",
+ "Failed to show notification. Retrying without sound.")
+ val n = buildNotification(habit,
+ reminderTime,
+ timestamp,
+ disableSound = true)
+ notificationManager.notify(notificationId, n)
+
+ }
active.add(notificationId)
}
- @NonNull
- fun buildNotification(@NonNull habit: Habit,
- @NonNull reminderTime: Long,
- @NonNull timestamp: Timestamp) : Notification
- {
+ fun buildNotification(habit: Habit,
+ reminderTime: Long,
+ timestamp: Timestamp,
+ disableSound: Boolean = false): Notification {
val addRepetitionAction = Action(
R.drawable.ic_action_check,
@@ -101,58 +108,58 @@ class AndroidNotificationTray
.addAction(addRepetitionAction)
.addAction(removeRepetitionAction)
+ val defaultText = context.getString(R.string.default_reminder_question)
val builder = NotificationCompat.Builder(context, REMINDERS_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle(habit.name)
- .setContentText(habit.description)
+ .setContentText(if(habit.question.isBlank()) defaultText else habit.question)
.setContentIntent(pendingIntents.showHabit(habit))
.setDeleteIntent(pendingIntents.dismissNotification(habit))
.addAction(addRepetitionAction)
.addAction(removeRepetitionAction)
- .setSound(ringtoneManager.getURI())
+ .setSound(null)
.setWhen(reminderTime)
.setShowWhen(true)
.setOngoing(preferences.shouldMakeNotificationsSticky())
- .setGroup("default")
+ .setGroup("group" + habit.getId())
+
+ if (!disableSound)
+ builder.setSound(ringtoneManager.getURI())
if (preferences.shouldMakeNotificationsLed())
builder.setLights(Color.RED, 1000, 1000)
- if(SDK_INT < Build.VERSION_CODES.O) {
- val snoozeAction = Action(R.drawable.ic_action_snooze,
- context.getString(R.string.snooze),
- pendingIntents.snoozeNotification(habit))
- wearableExtender.addAction(snoozeAction)
- builder.addAction(snoozeAction)
- }
+ val snoozeAction = Action(R.drawable.ic_action_snooze,
+ context.getString(R.string.snooze),
+ pendingIntents.snoozeNotification(habit))
+ wearableExtender.addAction(snoozeAction)
+ builder.addAction(snoozeAction)
builder.extend(wearableExtender)
- return builder.build()
+ return builder.build()
}
- @NonNull
- private fun buildSummary(@NonNull reminderTime: Long) : Notification
- {
+ private fun buildSummary(habit: Habit,
+ reminderTime: Long): Notification {
return NotificationCompat.Builder(context, REMINDERS_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle(context.getString(R.string.app_name))
.setWhen(reminderTime)
.setShowWhen(true)
- .setGroup("default")
+ .setGroup("group" + habit.getId())
.setGroupSummary(true)
.build()
}
companion object {
- private val REMINDERS_CHANNEL_ID = "REMINDERS"
+ private const val REMINDERS_CHANNEL_ID = "REMINDERS"
fun createAndroidNotificationChannel(context: Context) {
val notificationManager = context.getSystemService(Activity.NOTIFICATION_SERVICE)
as NotificationManager
- if (SDK_INT >= Build.VERSION_CODES.O)
- {
+ if (SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(REMINDERS_CHANNEL_ID,
- context.resources.getString(R.string.reminder),
- NotificationManager.IMPORTANCE_DEFAULT)
+ context.resources.getString(R.string.reminder),
+ NotificationManager.IMPORTANCE_DEFAULT)
notificationManager.createNotificationChannel(channel)
}
}
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/notifications/RingtoneManager.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/notifications/RingtoneManager.kt
index ff91467d0..f29382eac 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/notifications/RingtoneManager.kt
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/notifications/RingtoneManager.kt
@@ -55,7 +55,7 @@ class RingtoneManager
var ringtoneUri: Uri? = null
val defaultRingtoneUri = Settings.System.DEFAULT_NOTIFICATION_URI
val prefRingtoneUri = prefs.getString("pref_ringtone_uri",
- defaultRingtoneUri.toString())
+ defaultRingtoneUri.toString())!!
if (prefRingtoneUri.isNotEmpty())
ringtoneUri = Uri.parse(prefRingtoneUri)
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/notifications/SnoozeDelayPickerActivity.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/notifications/SnoozeDelayPickerActivity.java
index 03d26a707..0beeb9ca2 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/notifications/SnoozeDelayPickerActivity.java
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/notifications/SnoozeDelayPickerActivity.java
@@ -2,13 +2,17 @@ package org.isoron.uhabits.notifications;
import android.app.*;
+import android.graphics.*;
import android.os.*;
-import android.support.annotation.*;
-import android.support.v4.app.*;
+
+import androidx.annotation.Nullable;
+import androidx.core.app.*;
import android.text.format.*;
import android.view.*;
import android.widget.*;
+import androidx.fragment.app.FragmentActivity;
+
import com.android.datetimepicker.time.TimePickerDialog;
import org.isoron.uhabits.*;
@@ -26,6 +30,9 @@ public class SnoozeDelayPickerActivity extends FragmentActivity
private ReminderController reminderController;
+ @Nullable
+ private AlertDialog dialog;
+
@Override
protected void onCreate(@Nullable Bundle bundle)
{
@@ -40,7 +47,7 @@ public class SnoozeDelayPickerActivity extends FragmentActivity
if (habit == null) finish();
int theme = R.style.Theme_AppCompat_Light_Dialog_Alert;
- AlertDialog dialog = new AlertDialog.Builder(new ContextThemeWrapper(this, theme))
+ dialog = new AlertDialog.Builder(new ContextThemeWrapper(this, theme))
.setTitle(R.string.select_snooze_delay)
.setItems(R.array.snooze_picker_names, null)
.create();
@@ -60,7 +67,8 @@ public class SnoozeDelayPickerActivity extends FragmentActivity
},
calendar.get(Calendar.HOUR_OF_DAY),
calendar.get(Calendar.MINUTE),
- DateFormat.is24HourFormat(this));
+ DateFormat.is24HourFormat(this),
+ Color.BLUE);
dialog.show(getSupportFragmentManager(), "timePicker");
}
@@ -82,4 +90,11 @@ public class SnoozeDelayPickerActivity extends FragmentActivity
super.finish();
overridePendingTransition(0, 0);
}
+
+ @Override
+ protected void onPause()
+ {
+ if (dialog != null) dialog.dismiss();
+ super.onPause();
+ }
}
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/preferences/SharedPreferencesStorage.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/preferences/SharedPreferencesStorage.kt
index cc7553deb..55a9defa7 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/preferences/SharedPreferencesStorage.kt
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/preferences/SharedPreferencesStorage.kt
@@ -55,7 +55,7 @@ class SharedPreferencesStorage
sharedPrefs.getLong(key, defValue)
override fun getString(key: String, defValue: String): String =
- sharedPrefs.getString(key, defValue)
+ sharedPrefs.getString(key, defValue)!!
override fun onAttached(preferences: Preferences) {
this.preferences = preferences
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/receivers/ReminderController.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/receivers/ReminderController.java
index c253064f1..046aee432 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/receivers/ReminderController.java
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/receivers/ReminderController.java
@@ -21,7 +21,8 @@ package org.isoron.uhabits.receivers;
import android.content.*;
import android.net.*;
-import android.support.annotation.*;
+
+import androidx.annotation.NonNull;
import org.isoron.uhabits.core.*;
import org.isoron.uhabits.core.models.*;
@@ -70,22 +71,18 @@ public class ReminderController
public void onSnoozePressed(@NonNull Habit habit, final Context context)
{
- long delay = preferences.getSnoozeInterval();
-
- if (delay < 0)
- showSnoozeDelayPicker(habit, context);
- else
- scheduleReminderMinutesFromNow(habit, delay);
+ showSnoozeDelayPicker(habit, context);
}
- public void onSnoozeDelayPicked(Habit habit, int delay)
+ public void onSnoozeDelayPicked(Habit habit, int delayInMinutes)
{
- scheduleReminderMinutesFromNow(habit, delay);
+ reminderScheduler.snoozeReminder(habit, delayInMinutes);
+ notificationTray.cancel(habit);
}
public void onSnoozeTimePicked(Habit habit, int hour, int minute)
{
- Long time = DateUtils.getUpcomingTimeInMillis(hour, minute);
+ long time = DateUtils.getUpcomingTimeInMillis(hour, minute);
reminderScheduler.scheduleAtTime(habit, time);
notificationTray.cancel(habit);
}
@@ -95,12 +92,6 @@ public class ReminderController
notificationTray.cancel(habit);
}
- private void scheduleReminderMinutesFromNow(Habit habit, long minutes)
- {
- reminderScheduler.scheduleMinutesFromNow(habit, minutes);
- notificationTray.cancel(habit);
- }
-
private void showSnoozeDelayPicker(@NonNull Habit habit, Context context)
{
context.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/receivers/ReminderReceiver.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/receivers/ReminderReceiver.java
index dff612345..c6e29a63b 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/receivers/ReminderReceiver.java
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/receivers/ReminderReceiver.java
@@ -20,9 +20,10 @@
package org.isoron.uhabits.receivers;
import android.content.*;
-import android.support.annotation.*;
import android.util.*;
+import androidx.annotation.Nullable;
+
import org.isoron.uhabits.*;
import org.isoron.uhabits.core.models.*;
import org.isoron.uhabits.core.utils.*;
@@ -65,8 +66,8 @@ public class ReminderReceiver extends BroadcastReceiver
if (intent.getData() != null)
habit = habits.getById(parseId(intent.getData()));
- final Long timestamp = intent.getLongExtra("timestamp", today);
- final Long reminderTime = intent.getLongExtra("reminderTime", today);
+ final long timestamp = intent.getLongExtra("timestamp", today);
+ final long reminderTime = intent.getLongExtra("reminderTime", today);
try
{
@@ -74,21 +75,29 @@ public class ReminderReceiver extends BroadcastReceiver
{
case ACTION_SHOW_REMINDER:
if (habit == null) return;
+ Log.d("ReminderReceiver", String.format(
+ "onShowReminder habit=%d timestamp=%d reminderTime=%d",
+ habit.id,
+ timestamp,
+ reminderTime));
reminderController.onShowReminder(habit,
new Timestamp(timestamp), reminderTime);
break;
case ACTION_DISMISS_REMINDER:
if (habit == null) return;
+ Log.d("ReminderReceiver", String.format("onDismiss habit=%d", habit.id));
reminderController.onDismiss(habit);
break;
case ACTION_SNOOZE_REMINDER:
if (habit == null) return;
+ Log.d("ReminderReceiver", String.format("onSnoozePressed habit=%d", habit.id));
reminderController.onSnoozePressed(habit, context);
break;
case Intent.ACTION_BOOT_COMPLETED:
+ Log.d("ReminderReceiver", "onBootCompleted");
reminderController.onBootCompleted();
break;
}
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/receivers/WidgetReceiver.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/receivers/WidgetReceiver.java
new file mode 100644
index 000000000..c13d62d2d
--- /dev/null
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/receivers/WidgetReceiver.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2016 Álinson Santos Xavier
+ *
+ * This file is part of Loop Habit Tracker.
+ *
+ * Loop Habit Tracker is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * Loop Habit Tracker is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see .
+ */
+
+package org.isoron.uhabits.receivers;
+
+import android.content.*;
+import android.util.*;
+
+import org.isoron.uhabits.*;
+import org.isoron.uhabits.core.preferences.*;
+import org.isoron.uhabits.core.ui.widgets.*;
+import org.isoron.uhabits.intents.*;
+import org.isoron.uhabits.sync.*;
+
+import dagger.*;
+
+/**
+ * The Android BroadcastReceiver for Loop Habit Tracker.
+ *
+ * All broadcast messages are received and processed by this class.
+ */
+public class WidgetReceiver extends BroadcastReceiver
+{
+ public static final String ACTION_ADD_REPETITION =
+ "org.isoron.uhabits.ACTION_ADD_REPETITION";
+
+ public static final String ACTION_DISMISS_REMINDER =
+ "org.isoron.uhabits.ACTION_DISMISS_REMINDER";
+
+ public static final String ACTION_REMOVE_REPETITION =
+ "org.isoron.uhabits.ACTION_REMOVE_REPETITION";
+
+ public static final String ACTION_TOGGLE_REPETITION =
+ "org.isoron.uhabits.ACTION_TOGGLE_REPETITION";
+
+ private static final String TAG = "WidgetReceiver";
+
+ @Override
+ public void onReceive(final Context context, Intent intent)
+ {
+ HabitsApplication app =
+ (HabitsApplication) context.getApplicationContext();
+
+ WidgetComponent component = DaggerWidgetReceiver_WidgetComponent
+ .builder()
+ .habitsApplicationComponent(app.getComponent())
+ .build();
+
+ IntentParser parser = app.getComponent().getIntentParser();
+ WidgetBehavior controller = component.getWidgetController();
+ Preferences prefs = app.getComponent().getPreferences();
+
+ Log.i(TAG, String.format("Received intent: %s", intent.toString()));
+
+ if (prefs.isSyncEnabled())
+ context.startService(new Intent(context, SyncService.class));
+
+ try
+ {
+ IntentParser.CheckmarkIntentData data;
+ data = parser.parseCheckmarkIntent(intent);
+
+ switch (intent.getAction())
+ {
+ case ACTION_ADD_REPETITION:
+ Log.d(TAG, String.format(
+ "onAddRepetition habit=%d timestamp=%d",
+ data.getHabit().getId(),
+ data.getTimestamp().getUnixTime()));
+ controller.onAddRepetition(data.getHabit(),
+ data.getTimestamp());
+ break;
+
+ case ACTION_TOGGLE_REPETITION:
+ Log.d(TAG, String.format(
+ "onToggleRepetition habit=%d timestamp=%d",
+ data.getHabit().getId(),
+ data.getTimestamp().getUnixTime()));
+ controller.onToggleRepetition(data.getHabit(),
+ data.getTimestamp());
+ break;
+
+ case ACTION_REMOVE_REPETITION:
+ Log.d(TAG, String.format(
+ "onRemoveRepetition habit=%d timestamp=%d",
+ data.getHabit().getId(),
+ data.getTimestamp().getUnixTime()));
+ controller.onRemoveRepetition(data.getHabit(),
+ data.getTimestamp());
+ break;
+ }
+ }
+ catch (RuntimeException e)
+ {
+ Log.e("WidgetReceiver", "could not process intent", e);
+ }
+ }
+
+ @ReceiverScope
+ @Component(dependencies = HabitsApplicationComponent.class)
+ interface WidgetComponent
+ {
+ WidgetBehavior getWidgetController();
+ }
+}
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/sync/Event.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/sync/Event.java
index dba845cf6..3d15d0d3e 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/sync/Event.java
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/sync/Event.java
@@ -19,7 +19,8 @@
package org.isoron.uhabits.sync;
-import android.support.annotation.*;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import org.isoron.uhabits.core.database.*;
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/sync/SyncManager.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/sync/SyncManager.java
index 5d60009c8..88fecb6bf 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/sync/SyncManager.java
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/sync/SyncManager.java
@@ -19,9 +19,11 @@
package org.isoron.uhabits.sync;
-import android.support.annotation.*;
import android.util.*;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import org.isoron.androidbase.*;
import org.isoron.uhabits.BuildConfig;
import org.isoron.uhabits.core.*;
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/sync/SyncService.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/sync/SyncService.java
index b844e33d8..12adf46f2 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/sync/SyncService.java
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/sync/SyncService.java
@@ -23,7 +23,7 @@ import android.app.*;
import android.content.*;
import android.net.*;
import android.os.*;
-import android.support.v4.app.*;
+import androidx.core.app.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.core.preferences.*;
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/tasks/AndroidTaskRunner.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/tasks/AndroidTaskRunner.java
index 7b60834ba..17df191c7 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/tasks/AndroidTaskRunner.java
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/tasks/AndroidTaskRunner.java
@@ -87,6 +87,7 @@ public class AndroidTaskRunner implements TaskRunner
private class CustomAsyncTask extends AsyncTask
{
private final Task task;
+ private boolean isCancelled = false;
public CustomAsyncTask(Task task)
{
@@ -106,6 +107,7 @@ public class AndroidTaskRunner implements TaskRunner
@Override
protected Void doInBackground(Void... params)
{
+ if(isCancelled) return null;
task.doInBackground();
return null;
}
@@ -113,6 +115,7 @@ public class AndroidTaskRunner implements TaskRunner
@Override
protected void onPostExecute(Void aVoid)
{
+ if(isCancelled) return;
task.onPostExecute();
activeTasks.remove(this);
taskToAsyncTask.remove(task);
@@ -122,6 +125,8 @@ public class AndroidTaskRunner implements TaskRunner
@Override
protected void onPreExecute()
{
+ isCancelled = task.isCanceled();
+ if(isCancelled) return;
for (Listener l : listeners) l.onTaskStarted(task);
activeTasks.add(this);
taskToAsyncTask.put(task, this);
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/tasks/ExportDBTask.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/tasks/ExportDBTask.java
index b74c33915..1bc67d195 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/tasks/ExportDBTask.java
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/tasks/ExportDBTask.java
@@ -20,7 +20,9 @@
package org.isoron.uhabits.tasks;
import android.content.*;
-import android.support.annotation.*;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.google.auto.factory.*;
@@ -66,7 +68,7 @@ public class ExportDBTask implements Task
}
catch (IOException e)
{
- e.printStackTrace();
+ throw new RuntimeException(e);
}
}
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/tasks/ImportDataTask.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/tasks/ImportDataTask.java
index 52e536955..6740e2ab4 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/tasks/ImportDataTask.java
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/tasks/ImportDataTask.java
@@ -19,7 +19,7 @@
package org.isoron.uhabits.tasks;
-import android.support.annotation.*;
+import androidx.annotation.NonNull;
import com.google.auto.factory.*;
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/utils/AndroidDateFormats.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/utils/AndroidDateFormats.java
index 81e72b3b0..4580997aa 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/utils/AndroidDateFormats.java
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/utils/AndroidDateFormats.java
@@ -18,7 +18,7 @@
*/
package org.isoron.uhabits.utils;
-import android.support.annotation.*;
+import androidx.annotation.NonNull;
import org.isoron.uhabits.core.utils.*;
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/utils/AndroidDateUtils.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/utils/AndroidDateUtils.java
index e07f7b708..82d1e56e1 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/utils/AndroidDateUtils.java
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/utils/AndroidDateUtils.java
@@ -41,8 +41,8 @@ public class AndroidDateUtils
public static String formatWeekdayList(Context context, boolean weekday[])
{
- String shortDayNames[] = org.isoron.uhabits.core.utils.DateUtils.getShortDayNames();
- String longDayNames[] = org.isoron.uhabits.core.utils.DateUtils.getLongDayNames();
+ String shortDayNames[] = org.isoron.uhabits.core.utils.DateUtils.getShortWeekdayNames(Calendar.SATURDAY);
+ String longDayNames[] = org.isoron.uhabits.core.utils.DateUtils.getLongWeekdayNames(Calendar.SATURDAY);
StringBuilder buffer = new StringBuilder();
int count = 0;
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/utils/AttributeSetUtils.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/utils/AttributeSetUtils.java
index 67c9de531..35a6553ac 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/utils/AttributeSetUtils.java
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/utils/AttributeSetUtils.java
@@ -20,8 +20,9 @@
package org.isoron.uhabits.utils;
import android.content.*;
-import android.support.annotation.*;
-import android.support.annotation.Nullable;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import android.util.*;
import org.jetbrains.annotations.*;
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/utils/DatabaseUtils.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/utils/DatabaseUtils.java
index 8f99eb42d..050f9295f 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/utils/DatabaseUtils.java
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/utils/DatabaseUtils.java
@@ -21,7 +21,9 @@ package org.isoron.uhabits.utils;
import android.content.*;
import android.database.sqlite.*;
-import android.support.annotation.*;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import org.isoron.androidbase.utils.*;
import org.isoron.uhabits.*;
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/utils/PaletteUtils.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/utils/PaletteUtils.java
deleted file mode 100644
index a3b262ef4..000000000
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/utils/PaletteUtils.java
+++ /dev/null
@@ -1,67 +0,0 @@
-package org.isoron.uhabits.utils;
-
-import android.content.*;
-import android.graphics.*;
-import android.util.*;
-
-import org.isoron.androidbase.utils.*;
-
-public class PaletteUtils
-{
- public static int colorToPaletteIndex(Context context, int color)
- {
- StyledResources res = new StyledResources(context);
- int[] palette = res.getPalette();
-
- for (int k = 0; k < palette.length; k++)
- if (palette[k] == color) return k;
-
- return -1;
- }
-
- public static int getAndroidTestColor(int index)
- {
- int palette[] = {
- Color.parseColor("#D32F2F"), // 0 red
- Color.parseColor("#E64A19"), // 1 deep orange
- Color.parseColor("#F57C00"), // 2 orange
- Color.parseColor("#FF8F00"), // 3 amber
- Color.parseColor("#F9A825"), // 4 yellow
- Color.parseColor("#AFB42B"), // 5 lime
- Color.parseColor("#7CB342"), // 6 light green
- Color.parseColor("#388E3C"), // 7 green
- Color.parseColor("#00897B"), // 8 teal
- Color.parseColor("#00ACC1"), // 9 cyan
- Color.parseColor("#039BE5"), // 10 light blue
- Color.parseColor("#1976D2"), // 11 blue
- Color.parseColor("#303F9F"), // 12 indigo
- Color.parseColor("#5E35B1"), // 13 deep purple
- Color.parseColor("#8E24AA"), // 14 purple
- Color.parseColor("#D81B60"), // 15 pink
- Color.parseColor("#5D4037"), // 16 brown
- Color.parseColor("#303030"), // 17 dark grey
- Color.parseColor("#757575"), // 18 grey
- Color.parseColor("#aaaaaa") // 19 light grey
- };
-
- return palette[index];
- }
-
- public static int getColor(Context context, int paletteColor)
- {
- if (context == null)
- throw new IllegalArgumentException("Context is null");
-
- StyledResources res = new StyledResources(context);
- int palette[] = res.getPalette();
- if (paletteColor < 0 || paletteColor >= palette.length)
- {
- Log.w("ColorHelper",
- String.format("Invalid color: %d. Returning default.",
- paletteColor));
- paletteColor = 0;
- }
-
- return palette[paletteColor];
- }
-}
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/utils/PaletteUtils.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/utils/PaletteUtils.kt
new file mode 100644
index 000000000..6e205a5a6
--- /dev/null
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/utils/PaletteUtils.kt
@@ -0,0 +1,54 @@
+package org.isoron.uhabits.utils
+
+import android.content.Context
+import android.graphics.Color
+import android.util.Log
+import org.isoron.androidbase.utils.StyledResources
+
+object PaletteUtils {
+
+ @JvmStatic
+ fun colorToPaletteIndex(context: Context, color: Int): Int {
+ val palette = StyledResources(context).palette
+ return palette.indexOf(color)
+ }
+
+ @JvmStatic
+ fun getAndroidTestColor(index: Int): Int {
+ val palette = intArrayOf(
+ Color.parseColor("#D32F2F"), // 0 red
+ Color.parseColor("#E64A19"), // 1 deep orange
+ Color.parseColor("#F57C00"), // 2 orange
+ Color.parseColor("#FF8F00"), // 3 amber
+ Color.parseColor("#F9A825"), // 4 yellow
+ Color.parseColor("#AFB42B"), // 5 lime
+ Color.parseColor("#7CB342"), // 6 light green
+ Color.parseColor("#388E3C"), // 7 green
+ Color.parseColor("#00897B"), // 8 teal
+ Color.parseColor("#00ACC1"), // 9 cyan
+ Color.parseColor("#039BE5"), // 10 light blue
+ Color.parseColor("#1976D2"), // 11 blue
+ Color.parseColor("#303F9F"), // 12 indigo
+ Color.parseColor("#5E35B1"), // 13 deep purple
+ Color.parseColor("#8E24AA"), // 14 purple
+ Color.parseColor("#D81B60"), // 15 pink
+ Color.parseColor("#5D4037"), // 16 brown
+ Color.parseColor("#303030"), // 17 dark grey
+ Color.parseColor("#757575"), // 18 grey
+ Color.parseColor("#aaaaaa") // 19 light grey
+ )
+
+ return palette[index]
+ }
+
+ @JvmStatic
+ fun getColor(context: Context, paletteColor: Int): Int {
+ val palette = StyledResources(context).palette
+ return if (paletteColor in palette.indices) {
+ palette[paletteColor]
+ } else {
+ Log.w("ColorHelper", "Invalid color: $paletteColor. Returning default.")
+ palette[0]
+ }
+ }
+}
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/utils/ViewExtensions.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/utils/ViewExtensions.kt
index a613f884d..f2ff5e1f6 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/utils/ViewExtensions.kt
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/utils/ViewExtensions.kt
@@ -20,13 +20,13 @@
package org.isoron.uhabits.utils
import android.graphics.*
-import android.support.annotation.*
-import android.support.design.widget.*
-import android.support.v7.widget.Toolbar
+import androidx.annotation.*
+import androidx.appcompat.widget.Toolbar
import android.view.*
import android.view.ViewGroup.LayoutParams.*
import android.widget.*
import android.widget.RelativeLayout.*
+import com.google.android.material.snackbar.Snackbar
import org.isoron.androidbase.utils.*
import org.isoron.uhabits.*
@@ -74,7 +74,7 @@ fun ViewGroup.buildToolbar(): Toolbar {
fun View.showMessage(@StringRes stringId: Int) {
try {
val snackbar = Snackbar.make(this, stringId, Snackbar.LENGTH_SHORT)
- val tvId = android.support.design.R.id.snackbar_text
+ val tvId = R.id.snackbar_text
val tv = snackbar.view.findViewById(tvId)
tv?.setTextColor(Color.WHITE)
snackbar.show()
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/BaseWidget.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/BaseWidget.java
index af99d4a3a..043892101 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/BaseWidget.java
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/BaseWidget.java
@@ -22,10 +22,11 @@ package org.isoron.uhabits.widgets;
import android.app.*;
import android.content.*;
import android.graphics.*;
-import android.support.annotation.*;
import android.view.*;
import android.widget.*;
+import androidx.annotation.NonNull;
+
import org.isoron.uhabits.*;
import org.isoron.uhabits.core.preferences.*;
import org.isoron.uhabits.intents.*;
@@ -201,4 +202,8 @@ public abstract class BaseWidget
view.measure(specWidth, specHeight);
view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
}
+
+ protected int getPreferedBackgroundAlpha() {
+ return prefs.getWidgetOpacity();
+ }
}
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/BaseWidgetProvider.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/BaseWidgetProvider.java
index 9263035e6..7169c679b 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/BaseWidgetProvider.java
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/BaseWidgetProvider.java
@@ -22,9 +22,11 @@ package org.isoron.uhabits.widgets;
import android.appwidget.*;
import android.content.*;
import android.os.*;
-import android.support.annotation.*;
import android.widget.*;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import org.isoron.uhabits.*;
import org.isoron.uhabits.core.models.*;
import org.isoron.uhabits.core.preferences.*;
@@ -38,6 +40,8 @@ public abstract class BaseWidgetProvider extends AppWidgetProvider
{
private HabitList habits;
+ private Preferences preferences;
+
private WidgetPreferences widgetPrefs;
public static void updateAppWidget(@NonNull Context context, @NonNull AppWidgetManager manager,
@@ -76,9 +80,8 @@ public abstract class BaseWidgetProvider extends AppWidgetProvider
if (context == null) throw new RuntimeException("context is null");
if (manager == null) throw new RuntimeException("manager is null");
if (options == null) throw new RuntimeException("options is null");
- context.setTheme(R.style.OpaqueWidgetTheme);
-
updateDependencies(context);
+ context.setTheme(R.style.WidgetTheme);
BaseWidget widget = getWidgetFromId(context, widgetId);
WidgetDimensions dims = getDimensionsFromOptions(context, options);
@@ -122,9 +125,8 @@ public abstract class BaseWidgetProvider extends AppWidgetProvider
if (context == null) throw new RuntimeException("context is null");
if (manager == null) throw new RuntimeException("manager is null");
if (widgetIds == null) throw new RuntimeException("widgetIds is null");
- context.setTheme(R.style.OpaqueWidgetTheme);
-
updateDependencies(context);
+ context.setTheme(R.style.WidgetTheme);
new Thread(() ->
{
@@ -193,6 +195,12 @@ public abstract class BaseWidgetProvider extends AppWidgetProvider
HabitsApplication app =
(HabitsApplication) context.getApplicationContext();
habits = app.getComponent().getHabitList();
+ preferences = app.getComponent().getPreferences();
widgetPrefs = app.getComponent().getWidgetPreferences();
}
+
+ public Preferences getPreferences()
+ {
+ return preferences;
+ }
}
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/CheckmarkWidget.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/CheckmarkWidget.kt
index d3cf86ccc..ac3e6c98e 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/CheckmarkWidget.kt
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/CheckmarkWidget.kt
@@ -38,6 +38,7 @@ open class CheckmarkWidget(
override fun refreshData(v: View) {
(v as CheckmarkWidgetView).apply {
+ setBackgroundAlpha(preferedBackgroundAlpha)
setPercentage(habit.scores.todayValue.toFloat())
setActiveColor(PaletteUtils.getColor(context, habit.color))
setName(habit.name)
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/FrequencyWidget.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/FrequencyWidget.kt
index 5516f2bc9..bdc60a3f6 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/FrequencyWidget.kt
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/FrequencyWidget.kt
@@ -29,7 +29,8 @@ import org.isoron.uhabits.widgets.views.*
class FrequencyWidget(
context: Context,
widgetId: Int,
- private val habit: Habit
+ private val habit: Habit,
+ private val firstWeekday: Int
) : BaseWidget(context, widgetId) {
override fun getOnClickPendingIntent(context: Context) =
@@ -38,7 +39,9 @@ class FrequencyWidget(
override fun refreshData(v: View) {
val widgetView = v as GraphWidgetView
widgetView.setTitle(habit.name)
+ widgetView.setBackgroundAlpha(preferedBackgroundAlpha)
(widgetView.dataView as FrequencyChart).apply {
+ setFirstWeekday(firstWeekday)
setColor(PaletteUtils.getColor(context, habit.color))
setFrequency(habit.repetitions.weekdayFrequency)
}
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/FrequencyWidgetProvider.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/FrequencyWidgetProvider.kt
index 8c724d17e..83aedc39c 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/FrequencyWidgetProvider.kt
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/FrequencyWidgetProvider.kt
@@ -24,7 +24,10 @@ import android.content.*
class FrequencyWidgetProvider : BaseWidgetProvider() {
override fun getWidgetFromId(context: Context, id: Int): BaseWidget {
val habits = getHabitsFromWidgetId(id)
- if (habits.size == 1) return FrequencyWidget(context, id, habits[0])
+ if (habits.size == 1) return FrequencyWidget(context,
+ id,
+ habits[0],
+ preferences.firstWeekday)
else return StackWidget(context, id, StackWidgetType.FREQUENCY, habits)
}
}
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/HabitPickerDialog.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/HabitPickerDialog.kt
index 3b405b6c3..0ff16d9ef 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/HabitPickerDialog.kt
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/widgets/HabitPickerDialog.kt
@@ -26,28 +26,25 @@ import android.os.*
import android.widget.*
import android.widget.AbsListView.*
import org.isoron.uhabits.*
-import org.isoron.uhabits.core.models.*
import org.isoron.uhabits.core.preferences.*
import java.util.*
class HabitPickerDialog : Activity() {
private var widgetId = 0
- private lateinit var habitList: HabitList
- private lateinit var preferences: WidgetPreferences
- private lateinit var habitIds: ArrayList
+ private lateinit var widgetPreferences: WidgetPreferences
private lateinit var widgetUpdater: WidgetUpdater
- private lateinit var listView: ListView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val component = (applicationContext as HabitsApplication).component
- habitList = component.habitList
- preferences = component.widgetPreferences
+ val habitList = component.habitList
+ val preferences = component.preferences
+ widgetPreferences = component.widgetPreferences
widgetUpdater = component.widgetUpdater
widgetId = intent.extras?.getInt(EXTRA_APPWIDGET_ID, INVALID_APPWIDGET_ID) ?: 0
- habitIds = ArrayList()
+ val habitIds = ArrayList()
val habitNames = ArrayList()
for (h in habitList) {
if (h.isArchived) continue
@@ -56,30 +53,47 @@ class HabitPickerDialog : Activity() {
}
setContentView(R.layout.widget_configure_activity)
- listView = findViewById(R.id.listView) as ListView
+ val listView = findViewById(R.id.listView)
+ val saveButton = findViewById