mirror of
https://github.com/iSoron/uhabits.git
synced 2025-12-08 10:08:51 -06:00
Compare commits
5 Commits
8c0655c352
...
fix/stackw
| Author | SHA1 | Date | |
|---|---|---|---|
|
2154d8c192
|
|||
|
4c5a722dc5
|
|||
| ee39ff0eda | |||
| 13af054214 | |||
| fcbb586e80 |
@@ -1,11 +1,11 @@
|
|||||||
plugins {
|
plugins {
|
||||||
val kotlinVersion = "1.7.10"
|
val kotlinVersion = "1.6.10"
|
||||||
id("com.android.application") version ("7.3.0-rc01") apply (false)
|
id("com.android.application") version ("7.0.3") apply (false)
|
||||||
id("org.jetbrains.kotlin.android") version kotlinVersion apply (false)
|
id("org.jetbrains.kotlin.android") version kotlinVersion apply (false)
|
||||||
id("org.jetbrains.kotlin.kapt") version kotlinVersion apply (false)
|
id("org.jetbrains.kotlin.kapt") version kotlinVersion apply (false)
|
||||||
id("org.jetbrains.kotlin.android.extensions") version kotlinVersion apply (false)
|
id("org.jetbrains.kotlin.android.extensions") version kotlinVersion apply (false)
|
||||||
id("org.jetbrains.kotlin.multiplatform") version kotlinVersion apply (false)
|
id("org.jetbrains.kotlin.multiplatform") version kotlinVersion apply (false)
|
||||||
id("org.jlleitschuh.gradle.ktlint") version "11.0.0"
|
id("org.jlleitschuh.gradle.ktlint") version "10.2.1"
|
||||||
}
|
}
|
||||||
|
|
||||||
apply {
|
apply {
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ The repository will be downloaded to the directory `uhabits`.
|
|||||||
2. When the IDE asks you for the project location, select `uhabits` and click "Ok".
|
2. When the IDE asks you for the project location, select `uhabits` and click "Ok".
|
||||||
3. Android Studio will spend some time indexing the project. When this is complete, click the toolbar icon "Sync Project with Gradle File", located near the right corner of the top toolbar.
|
3. Android Studio will spend some time indexing the project. When this is complete, click the toolbar icon "Sync Project with Gradle File", located near the right corner of the top toolbar.
|
||||||
4. The operation will likely fail several times due to missing Android SDK components. Each time it fails, click the link "Install missing platforms", "Install build tools", etc, and try again.
|
4. The operation will likely fail several times due to missing Android SDK components. Each time it fails, click the link "Install missing platforms", "Install build tools", etc, and try again.
|
||||||
5. To test the application, create a virtual Android device using the menu "Tools" and "AVD Manager". The default options should work fine, but feel free to customize the device.
|
5. To test the application, create a virtual Android device using the menu "Tools" and "AVD Manager". The default options should work fine, but free to customize the device.
|
||||||
6. Click the menu "Run" and "uhabits-android". The application should launch.
|
6. Click the menu "Run" and "uhabits-android". The application should launch.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +1,5 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ tasks.compileLint {
|
|||||||
|
|
||||||
android {
|
android {
|
||||||
|
|
||||||
compileSdk = 32
|
compileSdk = 31
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
versionCode = 20100
|
versionCode = 20100
|
||||||
@@ -68,6 +68,12 @@ android {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lint {
|
||||||
|
isCheckReleaseBuilds = false
|
||||||
|
isAbortOnError = false
|
||||||
|
disable("GoogleAppIndexingWarning")
|
||||||
|
}
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
isCoreLibraryDesugaringEnabled = true
|
isCoreLibraryDesugaringEnabled = true
|
||||||
targetCompatibility(JavaVersion.VERSION_1_8)
|
targetCompatibility(JavaVersion.VERSION_1_8)
|
||||||
@@ -80,25 +86,25 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
val daggerVersion = "2.43.2"
|
val daggerVersion = "2.41"
|
||||||
val kotlinVersion = "1.7.10"
|
val kotlinVersion = "1.6.21"
|
||||||
val kxCoroutinesVersion = "1.6.4"
|
val kxCoroutinesVersion = "1.6.1"
|
||||||
val ktorVersion = "1.6.8"
|
val ktorVersion = "1.6.8"
|
||||||
val espressoVersion = "3.4.0"
|
val espressoVersion = "3.4.0"
|
||||||
|
|
||||||
androidTestImplementation("androidx.test.espresso:espresso-contrib:$espressoVersion")
|
androidTestImplementation("androidx.test.espresso:espresso-contrib:$espressoVersion")
|
||||||
androidTestImplementation("androidx.test.espresso:espresso-core:$espressoVersion")
|
androidTestImplementation("androidx.test.espresso:espresso-core:$espressoVersion")
|
||||||
androidTestImplementation("com.google.dagger:dagger:$daggerVersion")
|
androidTestImplementation("com.google.dagger:dagger:$daggerVersion")
|
||||||
androidTestImplementation("com.linkedin.dexmaker:dexmaker-mockito:2.28.3")
|
androidTestImplementation("com.linkedin.dexmaker:dexmaker-mockito:2.28.1")
|
||||||
androidTestImplementation("io.ktor:ktor-client-mock:$ktorVersion")
|
androidTestImplementation("io.ktor:ktor-client-mock:$ktorVersion")
|
||||||
androidTestImplementation("io.ktor:ktor-jackson:$ktorVersion")
|
androidTestImplementation("io.ktor:ktor-jackson:$ktorVersion")
|
||||||
androidTestImplementation("androidx.annotation:annotation:1.4.0")
|
androidTestImplementation("androidx.annotation:annotation:1.3.0")
|
||||||
androidTestImplementation("androidx.test.ext:junit:1.1.3")
|
androidTestImplementation("androidx.test.ext:junit:1.1.3")
|
||||||
androidTestImplementation("androidx.test.uiautomator:uiautomator:2.2.0")
|
androidTestImplementation("androidx.test.uiautomator:uiautomator:2.2.0")
|
||||||
androidTestImplementation("androidx.test:rules:1.4.0")
|
androidTestImplementation("androidx.test:rules:1.4.0")
|
||||||
androidTestImplementation("com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0")
|
androidTestImplementation("com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0")
|
||||||
compileOnly("javax.annotation:jsr250-api:1.0")
|
compileOnly("javax.annotation:jsr250-api:1.0")
|
||||||
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:1.2.2")
|
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:1.1.5")
|
||||||
implementation("com.github.AppIntro:AppIntro:6.2.0")
|
implementation("com.github.AppIntro:AppIntro:6.2.0")
|
||||||
implementation("com.google.code.findbugs:jsr305:3.0.2")
|
implementation("com.google.code.findbugs:jsr305:3.0.2")
|
||||||
implementation("com.google.dagger:dagger:$daggerVersion")
|
implementation("com.google.dagger:dagger:$daggerVersion")
|
||||||
@@ -110,10 +116,10 @@ dependencies {
|
|||||||
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion")
|
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion")
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:$kxCoroutinesVersion")
|
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:$kxCoroutinesVersion")
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$kxCoroutinesVersion")
|
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$kxCoroutinesVersion")
|
||||||
implementation("androidx.appcompat:appcompat:1.5.0")
|
implementation("androidx.appcompat:appcompat:1.4.1")
|
||||||
implementation("androidx.legacy:legacy-preference-v14:1.0.0")
|
implementation("androidx.legacy:legacy-preference-v14:1.0.0")
|
||||||
implementation("androidx.legacy:legacy-support-v4:1.0.0")
|
implementation("androidx.legacy:legacy-support-v4:1.0.0")
|
||||||
implementation("com.google.android.material:material:1.6.1")
|
implementation("com.google.android.material:material:1.5.0")
|
||||||
implementation("com.opencsv:opencsv:5.6")
|
implementation("com.opencsv:opencsv:5.6")
|
||||||
implementation(project(":uhabits-core"))
|
implementation(project(":uhabits-core"))
|
||||||
kapt("com.google.dagger:dagger-compiler:$daggerVersion")
|
kapt("com.google.dagger:dagger-compiler:$daggerVersion")
|
||||||
|
|||||||
@@ -53,7 +53,6 @@ object ListHabitsSteps {
|
|||||||
clickViewWithId(R.id.action_filter)
|
clickViewWithId(R.id.action_filter)
|
||||||
CommonSteps.clickText(R.string.hide_completed)
|
CommonSteps.clickText(R.string.hide_completed)
|
||||||
}
|
}
|
||||||
else -> throw RuntimeException()
|
|
||||||
}
|
}
|
||||||
device.waitForIdle()
|
device.waitForIdle()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import org.isoron.uhabits.utils.toPaletteColor
|
|||||||
class ColorPickerDialog : ColorPickerDialog() {
|
class ColorPickerDialog : ColorPickerDialog() {
|
||||||
fun setListener(callback: OnColorPickedCallback) {
|
fun setListener(callback: OnColorPickedCallback) {
|
||||||
super.setOnColorSelectedListener { c: Int ->
|
super.setOnColorSelectedListener { c: Int ->
|
||||||
val pc = c.toPaletteColor(requireContext())
|
val pc = c.toPaletteColor(context!!)
|
||||||
callback.onColorPicked(pc)
|
callback.onColorPicked(pc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,12 +51,12 @@ class HistoryEditorDialog : AppCompatDialogFragment(), CommandRunner.Listener {
|
|||||||
|
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
clearCurrentDialog()
|
clearCurrentDialog()
|
||||||
val component = (requireActivity().application as HabitsApplication).component
|
val component = (activity!!.application as HabitsApplication).component
|
||||||
commandRunner = component.commandRunner
|
commandRunner = component.commandRunner
|
||||||
habit = component.habitList.getById(requireArguments().getLong("habit"))!!
|
habit = component.habitList.getById(arguments!!.getLong("habit"))!!
|
||||||
preferences = component.preferences
|
preferences = component.preferences
|
||||||
|
|
||||||
val themeSwitcher = AndroidThemeSwitcher(requireActivity(), preferences)
|
val themeSwitcher = AndroidThemeSwitcher(activity!!, preferences)
|
||||||
themeSwitcher.apply()
|
themeSwitcher.apply()
|
||||||
|
|
||||||
chart = HistoryChart(
|
chart = HistoryChart(
|
||||||
@@ -71,10 +71,10 @@ class HistoryEditorDialog : AppCompatDialogFragment(), CommandRunner.Listener {
|
|||||||
onDateClickedListener = onDateClickedListener ?: object : OnDateClickedListener {},
|
onDateClickedListener = onDateClickedListener ?: object : OnDateClickedListener {},
|
||||||
padding = 10.0,
|
padding = 10.0,
|
||||||
)
|
)
|
||||||
dataView = AndroidDataView(requireContext(), null)
|
dataView = AndroidDataView(context!!, null)
|
||||||
dataView.view = chart!!
|
dataView.view = chart!!
|
||||||
|
|
||||||
val dialog = Dialog(requireContext()).apply {
|
val dialog = Dialog(context!!).apply {
|
||||||
val metrics = resources.displayMetrics
|
val metrics = resources.displayMetrics
|
||||||
val maxHeight = resources.getDimensionPixelSize(R.dimen.history_editor_max_height)
|
val maxHeight = resources.getDimensionPixelSize(R.dimen.history_editor_max_height)
|
||||||
setContentView(dataView)
|
setContentView(dataView)
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ class WeekdayPickerDialog :
|
|||||||
|
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
val builder = AlertDialog.Builder(
|
val builder = AlertDialog.Builder(
|
||||||
requireActivity()
|
activity!!
|
||||||
)
|
)
|
||||||
builder
|
builder
|
||||||
.setTitle(R.string.select_weekdays)
|
.setTitle(R.string.select_weekdays)
|
||||||
|
|||||||
@@ -40,13 +40,13 @@ class HabitTypeDialog : AppCompatDialogFragment() {
|
|||||||
val binding = SelectHabitTypeBinding.inflate(inflater, container, false)
|
val binding = SelectHabitTypeBinding.inflate(inflater, container, false)
|
||||||
|
|
||||||
binding.buttonYesNo.setOnClickListener {
|
binding.buttonYesNo.setOnClickListener {
|
||||||
val intent = IntentFactory().startEditActivity(requireActivity(), HabitType.YES_NO.value)
|
val intent = IntentFactory().startEditActivity(activity!!, HabitType.YES_NO.value)
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.buttonMeasurable.setOnClickListener {
|
binding.buttonMeasurable.setOnClickListener {
|
||||||
val intent = IntentFactory().startEditActivity(requireActivity(), HabitType.NUMERICAL.value)
|
val intent = IntentFactory().startEditActivity(activity!!, HabitType.NUMERICAL.value)
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,7 +85,6 @@ class ListHabitsActivity : AppCompatActivity(), Preferences.Listener {
|
|||||||
Thread.setDefaultUncaughtExceptionHandler(BaseExceptionHandler(this))
|
Thread.setDefaultUncaughtExceptionHandler(BaseExceptionHandler(this))
|
||||||
component.listHabitsBehavior.onStartup()
|
component.listHabitsBehavior.onStartup()
|
||||||
setContentView(rootView)
|
setContentView(rootView)
|
||||||
parseIntents()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPause() {
|
override fun onPause() {
|
||||||
@@ -110,6 +109,7 @@ class ListHabitsActivity : AppCompatActivity(), Preferences.Listener {
|
|||||||
if (prefs.theme == THEME_DARK && prefs.isPureBlackEnabled != pureBlack) {
|
if (prefs.theme == THEME_DARK && prefs.isPureBlackEnabled != pureBlack) {
|
||||||
restartWithFade(ListHabitsActivity::class.java)
|
restartWithFade(ListHabitsActivity::class.java)
|
||||||
}
|
}
|
||||||
|
parseIntents()
|
||||||
super.onResume()
|
super.onResume()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,6 +129,7 @@ class ListHabitsActivity : AppCompatActivity(), Preferences.Listener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun parseIntents() {
|
private fun parseIntents() {
|
||||||
|
if (intent == null) return
|
||||||
if (intent.action == ACTION_EDIT) {
|
if (intent.action == ACTION_EDIT) {
|
||||||
val habitId = intent.extras?.getLong("habit")
|
val habitId = intent.extras?.getLong("habit")
|
||||||
val timestamp = intent.extras?.getLong("timestamp")
|
val timestamp = intent.extras?.getLong("timestamp")
|
||||||
@@ -137,6 +138,12 @@ class ListHabitsActivity : AppCompatActivity(), Preferences.Listener {
|
|||||||
component.listHabitsBehavior.onEdit(habit, Timestamp(timestamp))
|
component.listHabitsBehavior.onEdit(habit, Timestamp(timestamp))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
intent = null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onNewIntent(intent: Intent?) {
|
||||||
|
super.onNewIntent(intent)
|
||||||
|
setIntent(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
@@ -221,7 +221,6 @@ class ShowHabitActivity : AppCompatActivity(), CommandRunner.Listener {
|
|||||||
ShowHabitMenuPresenter.Message.COULD_NOT_EXPORT -> {
|
ShowHabitMenuPresenter.Message.COULD_NOT_EXPORT -> {
|
||||||
showMessage(resources.getString(R.string.could_not_export))
|
showMessage(resources.getString(R.string.could_not_export))
|
||||||
}
|
}
|
||||||
else -> {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ class SettingsFragment : PreferenceFragmentCompat(), OnSharedPreferenceChangeLis
|
|||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
addPreferencesFromResource(R.xml.preferences)
|
addPreferencesFromResource(R.xml.preferences)
|
||||||
val appContext = requireContext().applicationContext
|
val appContext = context!!.applicationContext
|
||||||
if (appContext is HabitsApplication) {
|
if (appContext is HabitsApplication) {
|
||||||
prefs = appContext.component.preferences
|
prefs = appContext.component.preferences
|
||||||
widgetUpdater = appContext.component.widgetUpdater
|
widgetUpdater = appContext.component.widgetUpdater
|
||||||
@@ -99,9 +99,9 @@ class SettingsFragment : PreferenceFragmentCompat(), OnSharedPreferenceChangeLis
|
|||||||
return true
|
return true
|
||||||
} else if (key == "reminderCustomize") {
|
} else if (key == "reminderCustomize") {
|
||||||
if (SDK_INT < Build.VERSION_CODES.O) return true
|
if (SDK_INT < Build.VERSION_CODES.O) return true
|
||||||
createAndroidNotificationChannel(requireContext())
|
createAndroidNotificationChannel(context!!)
|
||||||
val intent = Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
|
val intent = Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
|
||||||
intent.putExtra(Settings.EXTRA_APP_PACKAGE, requireContext().packageName)
|
intent.putExtra(Settings.EXTRA_APP_PACKAGE, context!!.packageName)
|
||||||
intent.putExtra(Settings.EXTRA_CHANNEL_ID, NotificationTray.REMINDERS_CHANNEL_ID)
|
intent.putExtra(Settings.EXTRA_CHANNEL_ID, NotificationTray.REMINDERS_CHANNEL_ID)
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
return true
|
return true
|
||||||
@@ -111,7 +111,7 @@ class SettingsFragment : PreferenceFragmentCompat(), OnSharedPreferenceChangeLis
|
|||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
ringtoneManager = RingtoneManager(requireActivity())
|
ringtoneManager = RingtoneManager(activity!!)
|
||||||
sharedPrefs = preferenceManager.sharedPreferences
|
sharedPrefs = preferenceManager.sharedPreferences
|
||||||
sharedPrefs!!.registerOnSharedPreferenceChangeListener(this)
|
sharedPrefs!!.registerOnSharedPreferenceChangeListener(this)
|
||||||
if (!prefs.isDeveloper) {
|
if (!prefs.isDeveloper) {
|
||||||
@@ -154,8 +154,8 @@ class SettingsFragment : PreferenceFragmentCompat(), OnSharedPreferenceChangeLis
|
|||||||
val pref = findPreference(key)
|
val pref = findPreference(key)
|
||||||
pref.onPreferenceClickListener =
|
pref.onPreferenceClickListener =
|
||||||
Preference.OnPreferenceClickListener {
|
Preference.OnPreferenceClickListener {
|
||||||
requireActivity().setResult(result)
|
activity!!.setResult(result)
|
||||||
requireActivity().finish()
|
activity!!.finish()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,13 +21,16 @@ package org.isoron.uhabits.intents
|
|||||||
|
|
||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
import android.app.PendingIntent.FLAG_IMMUTABLE
|
import android.app.PendingIntent.FLAG_IMMUTABLE
|
||||||
|
import android.app.PendingIntent.FLAG_MUTABLE
|
||||||
import android.app.PendingIntent.FLAG_UPDATE_CURRENT
|
import android.app.PendingIntent.FLAG_UPDATE_CURRENT
|
||||||
import android.app.PendingIntent.getActivity
|
import android.app.PendingIntent.getActivity
|
||||||
import android.app.PendingIntent.getBroadcast
|
import android.app.PendingIntent.getBroadcast
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
import org.isoron.uhabits.activities.habits.list.ListHabitsActivity
|
import org.isoron.uhabits.activities.habits.list.ListHabitsActivity
|
||||||
|
import org.isoron.uhabits.activities.habits.show.ShowHabitActivity
|
||||||
import org.isoron.uhabits.core.AppScope
|
import org.isoron.uhabits.core.AppScope
|
||||||
import org.isoron.uhabits.core.models.Habit
|
import org.isoron.uhabits.core.models.Habit
|
||||||
import org.isoron.uhabits.core.models.Timestamp
|
import org.isoron.uhabits.core.models.Timestamp
|
||||||
@@ -89,6 +92,20 @@ class PendingIntentFactory
|
|||||||
)
|
)
|
||||||
.getPendingIntent(0, FLAG_IMMUTABLE or FLAG_UPDATE_CURRENT)!!
|
.getPendingIntent(0, FLAG_IMMUTABLE or FLAG_UPDATE_CURRENT)!!
|
||||||
|
|
||||||
|
fun showHabitTemplate(): PendingIntent {
|
||||||
|
return getActivity(
|
||||||
|
context,
|
||||||
|
0,
|
||||||
|
Intent(context, ShowHabitActivity::class.java),
|
||||||
|
getIntentTemplateFlags()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun showHabitFillIn(habit: Habit) =
|
||||||
|
Intent().apply {
|
||||||
|
data = Uri.parse(habit.uriString)
|
||||||
|
}
|
||||||
|
|
||||||
fun showReminder(
|
fun showReminder(
|
||||||
habit: Habit,
|
habit: Habit,
|
||||||
reminderTime: Long?,
|
reminderTime: Long?,
|
||||||
@@ -142,7 +159,7 @@ class PendingIntentFactory
|
|||||||
fun showNumberPicker(habit: Habit, timestamp: Timestamp): PendingIntent? {
|
fun showNumberPicker(habit: Habit, timestamp: Timestamp): PendingIntent? {
|
||||||
return getActivity(
|
return getActivity(
|
||||||
context,
|
context,
|
||||||
0,
|
(habit.id!! % Integer.MAX_VALUE).toInt() + 1,
|
||||||
Intent(context, ListHabitsActivity::class.java).apply {
|
Intent(context, ListHabitsActivity::class.java).apply {
|
||||||
action = ListHabitsActivity.ACTION_EDIT
|
action = ListHabitsActivity.ACTION_EDIT
|
||||||
putExtra("habit", habit.id)
|
putExtra("habit", habit.id)
|
||||||
@@ -151,4 +168,43 @@ class PendingIntentFactory
|
|||||||
FLAG_IMMUTABLE or FLAG_UPDATE_CURRENT
|
FLAG_IMMUTABLE or FLAG_UPDATE_CURRENT
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun showNumberPickerTemplate(): PendingIntent {
|
||||||
|
return getActivity(
|
||||||
|
context,
|
||||||
|
1,
|
||||||
|
Intent(context, ListHabitsActivity::class.java).apply {
|
||||||
|
action = ListHabitsActivity.ACTION_EDIT
|
||||||
|
},
|
||||||
|
getIntentTemplateFlags()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun showNumberPickerFillIn(habit: Habit, timestamp: Timestamp) = Intent().apply {
|
||||||
|
putExtra("habit", habit.id)
|
||||||
|
putExtra("timestamp", timestamp.unixTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getIntentTemplateFlags(): Int {
|
||||||
|
var flags = 0
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
|
flags = flags or FLAG_MUTABLE
|
||||||
|
}
|
||||||
|
return flags
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toggleCheckmarkTemplate(): PendingIntent =
|
||||||
|
getBroadcast(
|
||||||
|
context,
|
||||||
|
2,
|
||||||
|
Intent(context, WidgetReceiver::class.java).apply {
|
||||||
|
action = WidgetReceiver.ACTION_TOGGLE_REPETITION
|
||||||
|
},
|
||||||
|
getIntentTemplateFlags()
|
||||||
|
)
|
||||||
|
|
||||||
|
fun toggleCheckmarkFillIn(habit: Habit, timestamp: Timestamp) = Intent().apply {
|
||||||
|
data = Uri.parse(habit.uriString)
|
||||||
|
putExtra("timestamp", timestamp.unixTime)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,6 +73,10 @@ class StackWidget(
|
|||||||
StackWidgetType.getStackWidgetAdapterViewId(widgetType),
|
StackWidgetType.getStackWidgetAdapterViewId(widgetType),
|
||||||
StackWidgetType.getStackWidgetEmptyViewId(widgetType)
|
StackWidgetType.getStackWidgetEmptyViewId(widgetType)
|
||||||
)
|
)
|
||||||
|
remoteViews.setPendingIntentTemplate(
|
||||||
|
StackWidgetType.getStackWidgetAdapterViewId(widgetType),
|
||||||
|
StackWidgetType.getPendingIntentTemplate(pendingIntentFactory, widgetType, habits)
|
||||||
|
)
|
||||||
return remoteViews
|
return remoteViews
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,11 +29,14 @@ import android.widget.RemoteViewsService
|
|||||||
import android.widget.RemoteViewsService.RemoteViewsFactory
|
import android.widget.RemoteViewsService.RemoteViewsFactory
|
||||||
import org.isoron.platform.utils.StringUtils.Companion.splitLongs
|
import org.isoron.platform.utils.StringUtils.Companion.splitLongs
|
||||||
import org.isoron.uhabits.HabitsApplication
|
import org.isoron.uhabits.HabitsApplication
|
||||||
|
import org.isoron.uhabits.R
|
||||||
import org.isoron.uhabits.core.models.Habit
|
import org.isoron.uhabits.core.models.Habit
|
||||||
import org.isoron.uhabits.core.models.HabitNotFoundException
|
import org.isoron.uhabits.core.models.HabitNotFoundException
|
||||||
import org.isoron.uhabits.core.preferences.Preferences
|
import org.isoron.uhabits.core.preferences.Preferences
|
||||||
|
import org.isoron.uhabits.core.utils.DateUtils.Companion.getToday
|
||||||
|
import org.isoron.uhabits.intents.IntentFactory
|
||||||
|
import org.isoron.uhabits.intents.PendingIntentFactory
|
||||||
import org.isoron.uhabits.utils.InterfaceUtils.dpToPixels
|
import org.isoron.uhabits.utils.InterfaceUtils.dpToPixels
|
||||||
import java.util.ArrayList
|
|
||||||
|
|
||||||
class StackWidgetService : RemoteViewsService() {
|
class StackWidgetService : RemoteViewsService() {
|
||||||
override fun onGetViewFactory(intent: Intent): RemoteViewsFactory {
|
override fun onGetViewFactory(intent: Intent): RemoteViewsFactory {
|
||||||
@@ -54,7 +57,6 @@ internal class StackRemoteViewsFactory(private val context: Context, intent: Int
|
|||||||
)
|
)
|
||||||
private val habitIds: LongArray
|
private val habitIds: LongArray
|
||||||
private val widgetType: StackWidgetType
|
private val widgetType: StackWidgetType
|
||||||
private var remoteViews = ArrayList<RemoteViews>()
|
|
||||||
override fun onCreate() {}
|
override fun onCreate() {}
|
||||||
override fun onDestroy() {}
|
override fun onDestroy() {}
|
||||||
override fun getCount(): Int {
|
override fun getCount(): Int {
|
||||||
@@ -85,8 +87,26 @@ internal class StackRemoteViewsFactory(private val context: Context, intent: Int
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun getViewAt(position: Int): RemoteViews? {
|
override fun getViewAt(position: Int): RemoteViews? {
|
||||||
Log.i("StackRemoteViewsFactory", "getViewAt $position")
|
Log.i("StackRemoteViewsFactory", "getViewAt $position started")
|
||||||
return if (0 <= position && position < remoteViews.size) remoteViews[position] else null
|
if (position < 0 || position >= habitIds.size) return null
|
||||||
|
val app = context.applicationContext as HabitsApplication
|
||||||
|
val prefs = app.component.preferences
|
||||||
|
val habitList = app.component.habitList
|
||||||
|
val options = AppWidgetManager.getInstance(context).getAppWidgetOptions(widgetId)
|
||||||
|
if (Looper.myLooper() == null) Looper.prepare()
|
||||||
|
val habits = habitIds.map { habitList.getById(it) ?: throw HabitNotFoundException() }
|
||||||
|
val h = habits[position]
|
||||||
|
val widget = constructWidget(h, prefs)
|
||||||
|
widget.setDimensions(getDimensionsFromOptions(context, options))
|
||||||
|
val landscapeViews = widget.landscapeRemoteViews
|
||||||
|
val portraitViews = widget.portraitRemoteViews
|
||||||
|
val factory = PendingIntentFactory(context, IntentFactory())
|
||||||
|
val intent = StackWidgetType.getIntentFillIn(factory, widgetType, h, habits, getToday())
|
||||||
|
landscapeViews.setOnClickFillInIntent(R.id.button, intent)
|
||||||
|
portraitViews.setOnClickFillInIntent(R.id.button, intent)
|
||||||
|
val remoteViews = RemoteViews(landscapeViews, portraitViews)
|
||||||
|
Log.i("StackRemoteViewsFactory", "getViewAt $position ended")
|
||||||
|
return remoteViews
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun constructWidget(
|
private fun constructWidget(
|
||||||
@@ -131,24 +151,6 @@ internal class StackRemoteViewsFactory(private val context: Context, intent: Int
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onDataSetChanged() {
|
override fun onDataSetChanged() {
|
||||||
Log.i("StackRemoteViewsFactory", "onDataSetChanged started")
|
|
||||||
val app = context.applicationContext as HabitsApplication
|
|
||||||
val prefs = app.component.preferences
|
|
||||||
val habitList = app.component.habitList
|
|
||||||
val options = AppWidgetManager.getInstance(context).getAppWidgetOptions(widgetId)
|
|
||||||
val newRemoteViews = ArrayList<RemoteViews>()
|
|
||||||
if (Looper.myLooper() == null) Looper.prepare()
|
|
||||||
for (id in habitIds) {
|
|
||||||
val h = habitList.getById(id) ?: throw HabitNotFoundException()
|
|
||||||
val widget = constructWidget(h, prefs)
|
|
||||||
widget.setDimensions(getDimensionsFromOptions(context, options))
|
|
||||||
val landscapeViews = widget.landscapeRemoteViews
|
|
||||||
val portraitViews = widget.portraitRemoteViews
|
|
||||||
newRemoteViews.add(RemoteViews(landscapeViews, portraitViews))
|
|
||||||
Log.i("StackRemoteViewsFactory", "onDataSetChanged constructed widget $id")
|
|
||||||
}
|
|
||||||
remoteViews = newRemoteViews
|
|
||||||
Log.i("StackRemoteViewsFactory", "onDataSetChanged ended")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
|||||||
@@ -18,7 +18,12 @@
|
|||||||
*/
|
*/
|
||||||
package org.isoron.uhabits.widgets
|
package org.isoron.uhabits.widgets
|
||||||
|
|
||||||
|
import android.app.PendingIntent
|
||||||
|
import android.content.Intent
|
||||||
import org.isoron.uhabits.R
|
import org.isoron.uhabits.R
|
||||||
|
import org.isoron.uhabits.core.models.Habit
|
||||||
|
import org.isoron.uhabits.core.models.Timestamp
|
||||||
|
import org.isoron.uhabits.intents.PendingIntentFactory
|
||||||
import java.lang.IllegalStateException
|
import java.lang.IllegalStateException
|
||||||
|
|
||||||
enum class StackWidgetType(val value: Int) {
|
enum class StackWidgetType(val value: Int) {
|
||||||
@@ -73,5 +78,39 @@ enum class StackWidgetType(val value: Int) {
|
|||||||
else -> throw IllegalStateException()
|
else -> throw IllegalStateException()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getPendingIntentTemplate(
|
||||||
|
factory: PendingIntentFactory,
|
||||||
|
widgetType: StackWidgetType,
|
||||||
|
habits: List<Habit>
|
||||||
|
): PendingIntent {
|
||||||
|
val containsNumerical = habits.any { it.isNumerical }
|
||||||
|
return when (widgetType) {
|
||||||
|
CHECKMARK -> if (containsNumerical) {
|
||||||
|
factory.showNumberPickerTemplate()
|
||||||
|
} else {
|
||||||
|
factory.toggleCheckmarkTemplate()
|
||||||
|
}
|
||||||
|
FREQUENCY, SCORE, HISTORY, STREAKS, TARGET -> factory.showHabitTemplate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getIntentFillIn(
|
||||||
|
factory: PendingIntentFactory,
|
||||||
|
widgetType: StackWidgetType,
|
||||||
|
habit: Habit,
|
||||||
|
allHabitsInStackWidget: List<Habit>,
|
||||||
|
timestamp: Timestamp
|
||||||
|
): Intent {
|
||||||
|
val containsNumerical = allHabitsInStackWidget.any { it.isNumerical }
|
||||||
|
return when (widgetType) {
|
||||||
|
CHECKMARK -> if (containsNumerical) {
|
||||||
|
factory.showNumberPickerFillIn(habit, timestamp)
|
||||||
|
} else {
|
||||||
|
factory.toggleCheckmarkFillIn(habit, timestamp)
|
||||||
|
}
|
||||||
|
FREQUENCY, SCORE, HISTORY, STREAKS, TARGET -> factory.showHabitFillIn(habit)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,11 +43,11 @@ kotlin {
|
|||||||
val jvmMain by getting {
|
val jvmMain by getting {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(kotlin("stdlib-jdk8"))
|
implementation(kotlin("stdlib-jdk8"))
|
||||||
compileOnly("com.google.dagger:dagger:2.43.2")
|
compileOnly("com.google.dagger:dagger:2.41")
|
||||||
implementation("com.google.guava:guava:31.1-android")
|
implementation("com.google.guava:guava:31.1-android")
|
||||||
implementation("org.jetbrains.kotlin:kotlin-stdlib:1.7.10")
|
implementation("org.jetbrains.kotlin:kotlin-stdlib:1.6.21")
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.6.4")
|
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.6.1")
|
||||||
implementation("androidx.annotation:annotation:1.4.0")
|
implementation("androidx.annotation:annotation:1.3.0")
|
||||||
implementation("com.google.code.findbugs:jsr305:3.0.2")
|
implementation("com.google.code.findbugs:jsr305:3.0.2")
|
||||||
implementation("com.opencsv:opencsv:5.6")
|
implementation("com.opencsv:opencsv:5.6")
|
||||||
implementation("commons-codec:commons-codec:1.15")
|
implementation("commons-codec:commons-codec:1.15")
|
||||||
@@ -59,7 +59,7 @@ kotlin {
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation(kotlin("test"))
|
implementation(kotlin("test"))
|
||||||
implementation(kotlin("test-junit"))
|
implementation(kotlin("test-junit"))
|
||||||
implementation("org.xerial:sqlite-jdbc:3.39.2.1")
|
implementation("org.xerial:sqlite-jdbc:3.36.0.3")
|
||||||
implementation("org.hamcrest:hamcrest:2.2")
|
implementation("org.hamcrest:hamcrest:2.2")
|
||||||
implementation("org.apache.commons:commons-io:1.3.2")
|
implementation("org.apache.commons:commons-io:1.3.2")
|
||||||
implementation("com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0")
|
implementation("com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0")
|
||||||
|
|||||||
@@ -25,6 +25,12 @@ class StringUtils {
|
|||||||
|
|
||||||
fun joinLongs(values: LongArray): String = values.joinToString(separator = ",")
|
fun joinLongs(values: LongArray): String = values.joinToString(separator = ",")
|
||||||
|
|
||||||
fun splitLongs(str: String): LongArray = str.split(",").map { it.toLong() }.toLongArray()
|
fun splitLongs(str: String): LongArray {
|
||||||
|
return try {
|
||||||
|
str.split(",").map { it.toLong() }.toLongArray()
|
||||||
|
} catch (e: NumberFormatException) {
|
||||||
|
LongArray(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,8 +34,8 @@ application {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
val ktorVersion = "1.6.8"
|
val ktorVersion = "1.6.8"
|
||||||
val kotlinVersion = "1.7.10"
|
val kotlinVersion = "1.6.21"
|
||||||
val logbackVersion = "1.4.0"
|
val logbackVersion = "1.2.11"
|
||||||
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion")
|
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion")
|
||||||
implementation("io.ktor:ktor-server-netty:$ktorVersion")
|
implementation("io.ktor:ktor-server-netty:$ktorVersion")
|
||||||
implementation("ch.qos.logback:logback-classic:$logbackVersion")
|
implementation("ch.qos.logback:logback-classic:$logbackVersion")
|
||||||
@@ -43,9 +43,9 @@ dependencies {
|
|||||||
implementation("io.ktor:ktor-html-builder:$ktorVersion")
|
implementation("io.ktor:ktor-html-builder:$ktorVersion")
|
||||||
implementation("io.ktor:ktor-jackson:$ktorVersion")
|
implementation("io.ktor:ktor-jackson:$ktorVersion")
|
||||||
implementation("org.jetbrains:kotlin-css-jvm:1.0.0-pre.148-kotlin-1.4.30")
|
implementation("org.jetbrains:kotlin-css-jvm:1.0.0-pre.148-kotlin-1.4.30")
|
||||||
implementation("io.prometheus:simpleclient:0.16.0")
|
implementation("io.prometheus:simpleclient:0.15.0")
|
||||||
implementation("io.prometheus:simpleclient_httpserver:0.16.0")
|
implementation("io.prometheus:simpleclient_httpserver:0.15.0")
|
||||||
implementation("io.prometheus:simpleclient_hotspot:0.16.0")
|
implementation("io.prometheus:simpleclient_hotspot:0.15.0")
|
||||||
testImplementation("io.ktor:ktor-server-tests:$ktorVersion")
|
testImplementation("io.ktor:ktor-server-tests:$ktorVersion")
|
||||||
testImplementation("com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0")
|
testImplementation("com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0")
|
||||||
testImplementation(kotlin("test"))
|
testImplementation(kotlin("test"))
|
||||||
|
|||||||
Reference in New Issue
Block a user