danger-dangermattic 1.2.2 → 1.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +1 -1
- data/CHANGELOG.md +6 -0
- data/Gemfile.lock +95 -57
- data/lib/dangermattic/gem_version.rb +1 -1
- data/lib/dangermattic/plugins/android_unit_test_checker.rb +7 -4
- data/lib/dangermattic/plugins/manifest_pr_checker.rb +13 -1
- data/lib/dangermattic/plugins/podfile_checker.rb +2 -0
- data/rakelib/console.rake +1 -1
- data/rakelib/git_helpers.rake +1 -1
- data/spec/android_unit_test_checker_spec.rb +93 -3
- data/spec/fixtures/android_unit_test_checker/MixDataPlusNormalClass.kt +14 -0
- data/spec/fixtures/android_unit_test_checker/src/main/java/com/dayoneapp/dayone/api/ApiResult.kt +69 -0
- data/spec/fixtures/android_unit_test_checker/src/main/java/com/dayoneapp/dayone/api/WebRecordApi.kt +49 -0
- data/spec/fixtures/android_unit_test_checker/src/main/java/com/dayoneapp/dayone/domain/drive/UiStates.kt +13 -0
- data/spec/fixtures/android_unit_test_checker/src/main/java/com/dayoneapp/dayone/main/settings/integrations/connect/AppIntegrationModule.kt +29 -0
- data/spec/fixtures/android_unit_test_checker/src/main/java/com/dayoneapp/dayone/main/streaks/StreaksViewModel.kt +377 -0
- data/spec/fixtures/android_unit_test_checker/src/main/java/com/dayoneapp/dayone/mediastorage/MediaStorageConfiguration.kt +31 -0
- data/spec/fixtures/android_unit_test_checker/src/main/java/com/dayoneapp/dayone/utils/AccountType.kt +15 -0
- data/spec/fixtures/android_unit_test_checker/src/main/java/com/dayoneapp/dayone/utils/usecase/SelectPhotoUseCase.kt +76 -0
- data/spec/pr_size_checker_spec.rb +14 -14
- data/spec/spec_helper.rb +8 -2
- data/spec/view_changes_checker_spec.rb +15 -15
- metadata +11 -2
@@ -0,0 +1,29 @@
|
|
1
|
+
package com.dayoneapp.dayone.main.settings.integrations.connect
|
2
|
+
|
3
|
+
import com.dayoneapp.dayone.main.settings.integrations.IntegrationAppId
|
4
|
+
import com.dayoneapp.dayone.main.settings.integrations.strava.StravaAppIntegrationHandler
|
5
|
+
import dagger.Module
|
6
|
+
import dagger.Provides
|
7
|
+
import dagger.hilt.InstallIn
|
8
|
+
import dagger.hilt.components.SingletonComponent
|
9
|
+
import javax.inject.Qualifier
|
10
|
+
import javax.inject.Singleton
|
11
|
+
import kotlin.jvm.JvmWildcard
|
12
|
+
|
13
|
+
@Qualifier
|
14
|
+
@Retention(AnnotationRetention.BINARY)
|
15
|
+
annotation class AppIntegrationHandlers
|
16
|
+
|
17
|
+
@Module
|
18
|
+
@InstallIn(SingletonComponent::class)
|
19
|
+
internal object AppIntegrationModule {
|
20
|
+
@Provides
|
21
|
+
@Singleton
|
22
|
+
@AppIntegrationHandlers
|
23
|
+
fun provideAppIntegrationHandlers(
|
24
|
+
stravaAppIntegrationHandler: StravaAppIntegrationHandler,
|
25
|
+
): Map<IntegrationAppId, @JvmWildcard AppIntegrationHandler> =
|
26
|
+
mapOf(
|
27
|
+
IntegrationAppId.STRAVA to stravaAppIntegrationHandler,
|
28
|
+
)
|
29
|
+
}
|
@@ -0,0 +1,377 @@
|
|
1
|
+
package com.dayoneapp.dayone.main.streaks
|
2
|
+
|
3
|
+
import android.content.Context
|
4
|
+
import android.graphics.Bitmap
|
5
|
+
import androidx.annotation.ColorRes
|
6
|
+
import androidx.annotation.VisibleForTesting
|
7
|
+
import androidx.lifecycle.ViewModel
|
8
|
+
import androidx.lifecycle.viewModelScope
|
9
|
+
import com.dayoneapp.dayone.di.DatabaseThreadDispatcher
|
10
|
+
import com.dayoneapp.dayone.domain.JournalRepository
|
11
|
+
import com.dayoneapp.dayone.domain.SelectedJournalsProvider
|
12
|
+
import com.dayoneapp.dayone.domain.StreakRepository
|
13
|
+
import com.dayoneapp.dayone.domain.UserRepository
|
14
|
+
import com.dayoneapp.dayone.domain.analytics.AnalyticsTracker
|
15
|
+
import com.dayoneapp.dayone.domain.analytics.InitialContent
|
16
|
+
import com.dayoneapp.dayone.domain.entry.EntryRepository
|
17
|
+
import com.dayoneapp.dayone.main.editor.EditorLauncher
|
18
|
+
import com.dayoneapp.dayone.main.editor.MainActivityLauncher
|
19
|
+
import com.dayoneapp.dayone.main.navigation.ActivityEventHandler
|
20
|
+
import com.dayoneapp.dayone.main.navigation.Navigator
|
21
|
+
import com.dayoneapp.dayone.main.sharedjournals.events.OpenJournalOnTimeline
|
22
|
+
import com.dayoneapp.dayone.utils.DOStreakCalculator
|
23
|
+
import com.dayoneapp.dayone.utils.TimeProvider
|
24
|
+
import com.dayoneapp.dayone.utils.UtilsWrapper
|
25
|
+
import dagger.hilt.android.lifecycle.HiltViewModel
|
26
|
+
import kotlinx.coroutines.CoroutineDispatcher
|
27
|
+
import kotlinx.coroutines.flow.MutableStateFlow
|
28
|
+
import kotlinx.coroutines.flow.SharingStarted
|
29
|
+
import kotlinx.coroutines.flow.asStateFlow
|
30
|
+
import kotlinx.coroutines.flow.combine
|
31
|
+
import kotlinx.coroutines.flow.distinctUntilChanged
|
32
|
+
import kotlinx.coroutines.flow.map
|
33
|
+
import kotlinx.coroutines.flow.stateIn
|
34
|
+
import kotlinx.coroutines.launch
|
35
|
+
import java.io.File
|
36
|
+
import java.time.DayOfWeek
|
37
|
+
import java.time.LocalDate
|
38
|
+
import java.util.Calendar
|
39
|
+
import javax.inject.Inject
|
40
|
+
|
41
|
+
@HiltViewModel
|
42
|
+
class StreaksViewModel
|
43
|
+
@Inject
|
44
|
+
constructor(
|
45
|
+
@DatabaseThreadDispatcher private val databaseDispatcher: CoroutineDispatcher,
|
46
|
+
private val entryRepository: EntryRepository,
|
47
|
+
private val journalRepository: JournalRepository,
|
48
|
+
private val streakRepository: StreakRepository,
|
49
|
+
private val streaksCalculator: DOStreakCalculator,
|
50
|
+
private val timeProvider: TimeProvider,
|
51
|
+
private val userRepository: UserRepository,
|
52
|
+
private val navigator: Navigator,
|
53
|
+
private val selectedJournalsProvider: SelectedJournalsProvider,
|
54
|
+
private val utilsWrapper: UtilsWrapper,
|
55
|
+
private val analyticsTracker: AnalyticsTracker,
|
56
|
+
private val mainActivityLauncher: MainActivityLauncher,
|
57
|
+
private val editorLauncher: EditorLauncher,
|
58
|
+
private val activityEventHandler: ActivityEventHandler,
|
59
|
+
) : ViewModel() {
|
60
|
+
val uiState =
|
61
|
+
combine(
|
62
|
+
entryRepository.liveEntryCount().distinctUntilChanged(),
|
63
|
+
entryRepository.liveEntryCountForDate().distinctUntilChanged(),
|
64
|
+
journalRepository
|
65
|
+
.getAllJournalsLive(false)
|
66
|
+
.map { journals ->
|
67
|
+
journals.map { journal -> journal.isHideStreaksEnabled }
|
68
|
+
}.distinctUntilChanged(),
|
69
|
+
) { _, _, _ ->
|
70
|
+
val streakDays = streaksCalculator.calculateStreakWithSharedJournals()
|
71
|
+
val journals = getStreakJournals()
|
72
|
+
val streakWeekDays = getStreakWeekDays()
|
73
|
+
|
74
|
+
UiState.Streaks(
|
75
|
+
streakDays = streakDays,
|
76
|
+
streakWeekDays = streakWeekDays,
|
77
|
+
journals = journals,
|
78
|
+
)
|
79
|
+
}.stateIn(viewModelScope, SharingStarted.Lazily, UiState.Loading)
|
80
|
+
|
81
|
+
private val _journalOptionsState = MutableStateFlow<JournalOptionsState?>(null)
|
82
|
+
val journalOptionsState = _journalOptionsState.asStateFlow()
|
83
|
+
|
84
|
+
private val dayOfWeekList: DaysOfWeekList = DaysOfWeekList(timeProvider.getFirstDayOfWeek())
|
85
|
+
|
86
|
+
fun shareStreaks(
|
87
|
+
bitmap: Bitmap,
|
88
|
+
context: Context,
|
89
|
+
) {
|
90
|
+
viewModelScope.launch(databaseDispatcher) {
|
91
|
+
analyticsTracker.trackButtonTapped("streakView_share")
|
92
|
+
val streaksFile: File = utilsWrapper.saveBitmapToFile(context, bitmap, "streaks_file")
|
93
|
+
utilsWrapper.shareImage(context, streaksFile.absolutePath)
|
94
|
+
}
|
95
|
+
}
|
96
|
+
|
97
|
+
fun close() {
|
98
|
+
viewModelScope.launch {
|
99
|
+
analyticsTracker.trackButtonTapped("streakView_close")
|
100
|
+
navigator.goBack()
|
101
|
+
}
|
102
|
+
}
|
103
|
+
|
104
|
+
fun showJournalOptions(
|
105
|
+
journalId: Int,
|
106
|
+
journalName: String,
|
107
|
+
) {
|
108
|
+
viewModelScope.launch {
|
109
|
+
analyticsTracker.trackButtonTapped("streakView_journal")
|
110
|
+
_journalOptionsState.value = JournalOptionsState(journalId, journalName)
|
111
|
+
}
|
112
|
+
}
|
113
|
+
|
114
|
+
fun openJournal(journalId: Int) {
|
115
|
+
viewModelScope.launch {
|
116
|
+
_journalOptionsState.value = null
|
117
|
+
|
118
|
+
analyticsTracker.trackButtonTapped("streakView_journalMenu_openJournal")
|
119
|
+
|
120
|
+
activityEventHandler.sendEvent(
|
121
|
+
OpenJournalOnTimeline(
|
122
|
+
journalId = journalId,
|
123
|
+
selectedJournalsProvider = selectedJournalsProvider,
|
124
|
+
mainActivityLauncher = mainActivityLauncher,
|
125
|
+
),
|
126
|
+
)
|
127
|
+
}
|
128
|
+
}
|
129
|
+
|
130
|
+
fun createEntry(journalId: Int) {
|
131
|
+
viewModelScope.launch {
|
132
|
+
_journalOptionsState.value = null
|
133
|
+
|
134
|
+
analyticsTracker.trackButtonTapped("streakView_journalMenu_createEntry")
|
135
|
+
|
136
|
+
val newEntry =
|
137
|
+
entryRepository.createNewEntry(
|
138
|
+
journalId = journalId,
|
139
|
+
)
|
140
|
+
val newEntryInformation =
|
141
|
+
EditorLauncher.NewEntryInformation(
|
142
|
+
AnalyticsTracker.NewEntrySource.STREAKS,
|
143
|
+
InitialContent.BLANK,
|
144
|
+
)
|
145
|
+
editorLauncher.editNewEntry(
|
146
|
+
entry = newEntry,
|
147
|
+
newEntryInformation = newEntryInformation,
|
148
|
+
navigateTo = navigator::navigateTo,
|
149
|
+
)
|
150
|
+
}
|
151
|
+
}
|
152
|
+
|
153
|
+
fun closeJournalOptions() {
|
154
|
+
_journalOptionsState.value = null
|
155
|
+
}
|
156
|
+
|
157
|
+
private suspend fun getStreakWeekDays(): List<StreakWeekDay> {
|
158
|
+
val week = mutableListOf<StreakWeekDay>()
|
159
|
+
// First day of the week can vary based on the user's locale. Generally, SUNDAY is the default in countries
|
160
|
+
// influenced by the US, MONDAY is standard in most of Europe and Asia, and SATURDAY can be common in some
|
161
|
+
// Middle Eastern countries due to the weekend structure.
|
162
|
+
val firstDayOfWeek = timeProvider.getFirstDayOfWeek()
|
163
|
+
if (firstDayOfWeek == Calendar.SATURDAY) {
|
164
|
+
week.add(getStreakWeekDay(DayOfWeek.SATURDAY))
|
165
|
+
}
|
166
|
+
if (firstDayOfWeek == Calendar.SUNDAY || firstDayOfWeek == Calendar.SATURDAY) {
|
167
|
+
week.add(getStreakWeekDay(DayOfWeek.SUNDAY))
|
168
|
+
}
|
169
|
+
week.add(getStreakWeekDay(DayOfWeek.MONDAY))
|
170
|
+
week.add(getStreakWeekDay(DayOfWeek.TUESDAY))
|
171
|
+
week.add(getStreakWeekDay(DayOfWeek.WEDNESDAY))
|
172
|
+
week.add(getStreakWeekDay(DayOfWeek.THURSDAY))
|
173
|
+
week.add(getStreakWeekDay(DayOfWeek.FRIDAY))
|
174
|
+
if (firstDayOfWeek != Calendar.SATURDAY) {
|
175
|
+
week.add(getStreakWeekDay(DayOfWeek.SATURDAY))
|
176
|
+
}
|
177
|
+
if (firstDayOfWeek != Calendar.SUNDAY && firstDayOfWeek != Calendar.SATURDAY) {
|
178
|
+
week.add(getStreakWeekDay(DayOfWeek.SUNDAY))
|
179
|
+
}
|
180
|
+
|
181
|
+
return week
|
182
|
+
}
|
183
|
+
|
184
|
+
@VisibleForTesting
|
185
|
+
suspend fun getStreakWeekDay(dayOfWeek: DayOfWeek): StreakWeekDay {
|
186
|
+
val currentDate = timeProvider.currentLocalDate()
|
187
|
+
val daysToDayOfWeek =
|
188
|
+
dayOfWeekList.daysToPreviousDay(
|
189
|
+
currentDay = currentDate.dayOfWeek,
|
190
|
+
previousDay = dayOfWeek,
|
191
|
+
)
|
192
|
+
// Go to the previous date based on [dateOfWeek] and current date.
|
193
|
+
val currentDateForDayOfWeek = currentDate.minusDays(daysToDayOfWeek.daysToPreviousDay.toLong())
|
194
|
+
// From that date, go back DAYS_MINUS_WEEK * 7 days to cover DAYS_MINUS_WEEK (18) instances of the [dayOfWeek].
|
195
|
+
// For example,if [dayOfWeek] is MONDAY and current date is 2024-10-8 (a TUESDAY), then [daysToDayOfWeek] is 1
|
196
|
+
// and [currentDateForDayOfWeek] is 2024-10-7 and since becomes 2024-6-7 which represents the 18th MONDAY from
|
197
|
+
// the current date.
|
198
|
+
val previousWeeks = if (daysToDayOfWeek.wasInPreviousWeek) DAYS_MINUS_WEEK - 1 else DAYS_MINUS_WEEK
|
199
|
+
val since = currentDateForDayOfWeek.minusDays(previousWeeks * 7)
|
200
|
+
// Generates a sequences of dates that represents the [dayOfWeek] for the previous 18 weeks. For the previous
|
201
|
+
// example, represent dates for each MONDAY from 2024-6-7 to 2024-10-7.
|
202
|
+
val dateRange: Sequence<LocalDate> =
|
203
|
+
generateSequence(since) { date ->
|
204
|
+
if (date < currentDateForDayOfWeek) date.plusDays(7) else null
|
205
|
+
}
|
206
|
+
// We query all the dates with entries since 2024-6-7 and check if the [dayOfWeek] for each week has an entry.
|
207
|
+
val userId = userRepository.getUser()?.id
|
208
|
+
val datesWithEntries = streakRepository.getDatesThatHaveEntriesSince(since, userId)
|
209
|
+
val days =
|
210
|
+
dateRange
|
211
|
+
.map { date ->
|
212
|
+
DayJournaled(datesWithEntries.contains(date))
|
213
|
+
}.toMutableList()
|
214
|
+
.also {
|
215
|
+
if (daysToDayOfWeek.wasInPreviousWeek) {
|
216
|
+
it.add(DayJournaled(null))
|
217
|
+
}
|
218
|
+
}
|
219
|
+
|
220
|
+
return StreakWeekDay(dayOfWeek = dayOfWeek, days = days)
|
221
|
+
}
|
222
|
+
|
223
|
+
@VisibleForTesting
|
224
|
+
suspend fun getStreakJournals(): List<StreakJournal> {
|
225
|
+
val currentDate = timeProvider.currentLocalDate()
|
226
|
+
val since = currentDate.minusDays(DAYS_MINUS_JOURNAL)
|
227
|
+
val dateRange: Sequence<LocalDate> =
|
228
|
+
generateSequence(since) { date ->
|
229
|
+
if (date < currentDate) date.plusDays(1) else null
|
230
|
+
}
|
231
|
+
return journalRepository
|
232
|
+
.getAllJournals(includeHidden = true)
|
233
|
+
.filter {
|
234
|
+
!it.isHideFromStreakEnabledNonNull() && !it.isPlaceholderForEncryptedJournalNonNull()
|
235
|
+
}.sortedBy { it.sortOrder ?: 0 }
|
236
|
+
.map { journal ->
|
237
|
+
val datesWithEntries =
|
238
|
+
if (journal.isShared == true) {
|
239
|
+
streakRepository.getDatesThatHaveEntriesSinceForSharedJournal(
|
240
|
+
journalId = journal.id,
|
241
|
+
since = since,
|
242
|
+
userId = userRepository.getUser()?.id ?: "",
|
243
|
+
)
|
244
|
+
} else {
|
245
|
+
streakRepository.getDatesThatHaveEntriesSinceForJournal(journalId = journal.id, since = since)
|
246
|
+
}
|
247
|
+
val days =
|
248
|
+
dateRange
|
249
|
+
.map { date ->
|
250
|
+
DayJournaled(datesWithEntries.contains(date))
|
251
|
+
}.toList()
|
252
|
+
val entriesToday =
|
253
|
+
if (journal.isShared == true) {
|
254
|
+
streakRepository.getNumEntriesForSharedJournalOnDate(
|
255
|
+
journalId = journal.id,
|
256
|
+
date = currentDate,
|
257
|
+
userId = userRepository.getUser()?.id ?: "",
|
258
|
+
)
|
259
|
+
} else {
|
260
|
+
streakRepository.getNumEntriesOnDate(journalId = journal.id, date = currentDate)
|
261
|
+
}
|
262
|
+
|
263
|
+
StreakJournal(
|
264
|
+
journalId = journal.id,
|
265
|
+
journalName = journal.name ?: "",
|
266
|
+
entriesToday = entriesToday,
|
267
|
+
color = journal.journalColor.backgroundColorRes,
|
268
|
+
days = days,
|
269
|
+
)
|
270
|
+
}
|
271
|
+
}
|
272
|
+
|
273
|
+
companion object {
|
274
|
+
private const val DAYS_MINUS_JOURNAL = 18L
|
275
|
+
private const val DAYS_MINUS_WEEK = 17L
|
276
|
+
}
|
277
|
+
|
278
|
+
sealed interface UiState {
|
279
|
+
data object Loading : UiState
|
280
|
+
|
281
|
+
data class Streaks(
|
282
|
+
val streakDays: Int,
|
283
|
+
val streakWeekDays: List<StreakWeekDay>,
|
284
|
+
val journals: List<StreakJournal>,
|
285
|
+
) : UiState
|
286
|
+
}
|
287
|
+
|
288
|
+
class JournalOptionsState(
|
289
|
+
val journalId: Int,
|
290
|
+
val journalName: String,
|
291
|
+
)
|
292
|
+
}
|
293
|
+
|
294
|
+
data class StreakWeekDay(
|
295
|
+
val dayOfWeek: DayOfWeek,
|
296
|
+
val days: List<DayJournaled>,
|
297
|
+
)
|
298
|
+
|
299
|
+
@JvmInline
|
300
|
+
value class DayJournaled(
|
301
|
+
val isFilled: Boolean?,
|
302
|
+
)
|
303
|
+
|
304
|
+
data class StreakJournal(
|
305
|
+
val journalId: Int,
|
306
|
+
val journalName: String,
|
307
|
+
val entriesToday: Int,
|
308
|
+
@ColorRes
|
309
|
+
val color: Int,
|
310
|
+
val days: List<DayJournaled>,
|
311
|
+
)
|
312
|
+
|
313
|
+
private class DaysOfWeekList(
|
314
|
+
private val firstDayOfWeek: Int,
|
315
|
+
) {
|
316
|
+
private val days =
|
317
|
+
buildList {
|
318
|
+
if (firstDayOfWeek == Calendar.SATURDAY) {
|
319
|
+
add(DayOfWeek.SATURDAY)
|
320
|
+
}
|
321
|
+
if (firstDayOfWeek == Calendar.SUNDAY || firstDayOfWeek == Calendar.SATURDAY) {
|
322
|
+
add(DayOfWeek.SUNDAY)
|
323
|
+
}
|
324
|
+
add(DayOfWeek.MONDAY)
|
325
|
+
add(DayOfWeek.TUESDAY)
|
326
|
+
add(DayOfWeek.WEDNESDAY)
|
327
|
+
add(DayOfWeek.THURSDAY)
|
328
|
+
add(DayOfWeek.FRIDAY)
|
329
|
+
if (firstDayOfWeek != Calendar.SATURDAY) {
|
330
|
+
add(DayOfWeek.SATURDAY)
|
331
|
+
}
|
332
|
+
if (firstDayOfWeek != Calendar.SUNDAY && firstDayOfWeek != Calendar.SATURDAY) {
|
333
|
+
add(DayOfWeek.SUNDAY)
|
334
|
+
}
|
335
|
+
}
|
336
|
+
|
337
|
+
/**
|
338
|
+
* Return the number of days between the current day of the week and the previous day of the week.
|
339
|
+
*
|
340
|
+
* Example: If the current day of the week is MONDAY and the previous day of the week is FRIDAY, then it would
|
341
|
+
* return 3 because we have to go through SUNDAY, SATURDAY to FRIDAY.
|
342
|
+
*
|
343
|
+
* @param currentDay The current day of the week.
|
344
|
+
* @param previousDay The previous day of the week.
|
345
|
+
* @return Number of days between the current day of the week and the previous day of the week.
|
346
|
+
*/
|
347
|
+
fun daysToPreviousDay(
|
348
|
+
currentDay: DayOfWeek,
|
349
|
+
previousDay: DayOfWeek,
|
350
|
+
): PreviousDays {
|
351
|
+
if (currentDay == previousDay) {
|
352
|
+
return PreviousDays(0, false)
|
353
|
+
}
|
354
|
+
var indexCurrentDay = days.indexOf(currentDay)
|
355
|
+
var daysToPreviousDay = 0
|
356
|
+
var wasInPreviousWeek = false
|
357
|
+
while (true) {
|
358
|
+
indexCurrentDay -= 1
|
359
|
+
if (indexCurrentDay < 0) {
|
360
|
+
wasInPreviousWeek = true
|
361
|
+
indexCurrentDay = days.size - 1
|
362
|
+
}
|
363
|
+
daysToPreviousDay += 1
|
364
|
+
|
365
|
+
if (days[indexCurrentDay] == previousDay) {
|
366
|
+
break
|
367
|
+
}
|
368
|
+
}
|
369
|
+
|
370
|
+
return PreviousDays(daysToPreviousDay, wasInPreviousWeek)
|
371
|
+
}
|
372
|
+
|
373
|
+
class PreviousDays(
|
374
|
+
val daysToPreviousDay: Int,
|
375
|
+
val wasInPreviousWeek: Boolean,
|
376
|
+
)
|
377
|
+
}
|
@@ -0,0 +1,31 @@
|
|
1
|
+
package com.dayoneapp.mediastorage
|
2
|
+
|
3
|
+
import android.graphics.Bitmap
|
4
|
+
|
5
|
+
@JvmInline
|
6
|
+
value class CompressQuality(
|
7
|
+
val value: Int,
|
8
|
+
) {
|
9
|
+
init {
|
10
|
+
require(value in 1..100) { }
|
11
|
+
}
|
12
|
+
}
|
13
|
+
|
14
|
+
class MediaStorageConfiguration(
|
15
|
+
val mediaPath: String,
|
16
|
+
val externalImagesPath: String,
|
17
|
+
val externalVideosPath: String,
|
18
|
+
val externalAudiosPath: String,
|
19
|
+
val externalDocumentsPath: String,
|
20
|
+
val avatarsPath: String,
|
21
|
+
val thumbnailsConfiguration: ThumbnailsConfiguration,
|
22
|
+
)
|
23
|
+
|
24
|
+
class ThumbnailsConfiguration(
|
25
|
+
val thumbnailsPath: String,
|
26
|
+
val height: Int,
|
27
|
+
val width: Int,
|
28
|
+
val extension: String,
|
29
|
+
val compression: Bitmap.CompressFormat,
|
30
|
+
val quality: CompressQuality,
|
31
|
+
)
|
data/spec/fixtures/android_unit_test_checker/src/main/java/com/dayoneapp/dayone/utils/AccountType.kt
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
package com.dayoneapp.dayone.utils
|
2
|
+
|
3
|
+
enum class AccountType(
|
4
|
+
val key: String,
|
5
|
+
) {
|
6
|
+
DAY_ONE("Day One"),
|
7
|
+
GOOGLE("Google"),
|
8
|
+
APPLE("Apple ID"),
|
9
|
+
;
|
10
|
+
|
11
|
+
companion object {
|
12
|
+
@JvmStatic
|
13
|
+
fun fromString(key: String): AccountType = values().find { it.key == key }!!
|
14
|
+
}
|
15
|
+
}
|
@@ -0,0 +1,76 @@
|
|
1
|
+
package com.dayoneapp.dayone.utils.usecase
|
2
|
+
|
3
|
+
import android.content.Context
|
4
|
+
import android.content.Intent
|
5
|
+
import androidx.activity.result.ActivityResultLauncher
|
6
|
+
import androidx.activity.result.contract.ActivityResultContracts.GetContent
|
7
|
+
import androidx.activity.result.contract.ActivityResultContracts.GetMultipleContents
|
8
|
+
import androidx.fragment.app.Fragment
|
9
|
+
import com.dayoneapp.dayone.utils.UriWrapper
|
10
|
+
import kotlinx.coroutines.flow.MutableSharedFlow
|
11
|
+
import kotlinx.coroutines.flow.asSharedFlow
|
12
|
+
import javax.inject.Inject
|
13
|
+
|
14
|
+
class SelectPhotoUseCase
|
15
|
+
@Inject
|
16
|
+
constructor() {
|
17
|
+
private lateinit var getContent: ActivityResultLauncher<String>
|
18
|
+
private val _mediaUris = MutableSharedFlow<List<UriWrapper>>(extraBufferCapacity = 5)
|
19
|
+
val mediaUris = _mediaUris.asSharedFlow()
|
20
|
+
|
21
|
+
fun onCreate(
|
22
|
+
fragment: Fragment,
|
23
|
+
singlePhoto: Boolean = false,
|
24
|
+
) {
|
25
|
+
getContent =
|
26
|
+
if (singlePhoto) {
|
27
|
+
fragment.registerForActivityResult(GetSingleImage()) { uri ->
|
28
|
+
uri?.let {
|
29
|
+
_mediaUris.tryEmit(listOf(UriWrapper(it)))
|
30
|
+
}
|
31
|
+
}
|
32
|
+
} else {
|
33
|
+
fragment.registerForActivityResult(GetMultipleImages()) { uris ->
|
34
|
+
_mediaUris.tryEmit(uris.map { UriWrapper(it) })
|
35
|
+
}
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
fun selectPhotos() {
|
40
|
+
getContent.launch("image/*")
|
41
|
+
}
|
42
|
+
|
43
|
+
private class GetMultipleImages : GetMultipleContents() {
|
44
|
+
// Required to remove a lint error due to this method no longer calling super.createIntent()
|
45
|
+
@Suppress("MissingSuperCall")
|
46
|
+
override fun createIntent(
|
47
|
+
context: Context,
|
48
|
+
input: String,
|
49
|
+
): Intent =
|
50
|
+
Intent(
|
51
|
+
Intent.ACTION_PICK,
|
52
|
+
android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
|
53
|
+
).setType(input)
|
54
|
+
.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
|
55
|
+
.apply {
|
56
|
+
putExtra(Intent.EXTRA_MIME_TYPES, arrayOf("image/jpeg", "image/png", "image/jpg"))
|
57
|
+
}
|
58
|
+
}
|
59
|
+
|
60
|
+
private class GetSingleImage : GetContent() {
|
61
|
+
// Required to remove a lint error due to this method no longer calling super.createIntent()
|
62
|
+
@Suppress("MissingSuperCall")
|
63
|
+
override fun createIntent(
|
64
|
+
context: Context,
|
65
|
+
input: String,
|
66
|
+
): Intent =
|
67
|
+
Intent(
|
68
|
+
Intent.ACTION_PICK,
|
69
|
+
android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
|
70
|
+
).setType(input)
|
71
|
+
.apply {
|
72
|
+
putExtra(Intent.EXTRA_MIME_TYPES, arrayOf("image/jpeg", "image/png", "image/jpg"))
|
73
|
+
}
|
74
|
+
}
|
75
|
+
}
|
76
|
+
|
@@ -72,19 +72,19 @@ module Danger
|
|
72
72
|
end
|
73
73
|
|
74
74
|
context 'when fail on error is false and a custom message is given' do
|
75
|
-
|
75
|
+
it_behaves_like 'reporting diff size custom warnings or errors', false, 'this is my custom warning message'
|
76
76
|
end
|
77
77
|
|
78
78
|
context 'when fail on error is false' do
|
79
|
-
|
79
|
+
it_behaves_like 'reporting diff size custom warnings or errors', false
|
80
80
|
end
|
81
81
|
|
82
82
|
context 'when fail on error is true' do
|
83
|
-
|
83
|
+
it_behaves_like 'reporting diff size custom warnings or errors', true
|
84
84
|
end
|
85
85
|
|
86
86
|
context 'when a custom error message is given and fail on error is true' do
|
87
|
-
|
87
|
+
it_behaves_like 'reporting diff size custom warnings or errors', true, 'this is my custom error message'
|
88
88
|
end
|
89
89
|
end
|
90
90
|
end
|
@@ -163,18 +163,18 @@ module Danger
|
|
163
163
|
end
|
164
164
|
|
165
165
|
context 'with the entire diff' do
|
166
|
-
|
167
|
-
|
166
|
+
it_behaves_like 'using the default diff size counter, without a file selector', :all
|
167
|
+
it_behaves_like 'using a file selector to filter and count the changes in a diff', :all, [422, 520, 1541]
|
168
168
|
end
|
169
169
|
|
170
170
|
context 'with the insertions in the diff' do
|
171
|
-
|
172
|
-
|
171
|
+
it_behaves_like 'using the default diff size counter, without a file selector', :insertions
|
172
|
+
it_behaves_like 'using a file selector to filter and count the changes in a diff', :insertions, [200, 139, 384]
|
173
173
|
end
|
174
174
|
|
175
175
|
context 'with the deletions in the diff' do
|
176
|
-
|
177
|
-
|
176
|
+
it_behaves_like 'using the default diff size counter, without a file selector', :deletions
|
177
|
+
it_behaves_like 'using a file selector to filter and count the changes in a diff', :deletions, [221, 380, 1157]
|
178
178
|
end
|
179
179
|
end
|
180
180
|
|
@@ -221,19 +221,19 @@ module Danger
|
|
221
221
|
end
|
222
222
|
|
223
223
|
context 'when fail on error is false and a custom message is given' do
|
224
|
-
|
224
|
+
it_behaves_like 'reporting PR length check custom warnings or errors', false, 'this is my custom warning message'
|
225
225
|
end
|
226
226
|
|
227
227
|
context 'when fail on error is false' do
|
228
|
-
|
228
|
+
it_behaves_like 'reporting PR length check custom warnings or errors', false
|
229
229
|
end
|
230
230
|
|
231
231
|
context 'when fail on error is true' do
|
232
|
-
|
232
|
+
it_behaves_like 'reporting PR length check custom warnings or errors', true
|
233
233
|
end
|
234
234
|
|
235
235
|
context 'when a custom error message is given and fail on error is true' do
|
236
|
-
|
236
|
+
it_behaves_like 'reporting PR length check custom warnings or errors', true, 'this is my custom error message'
|
237
237
|
end
|
238
238
|
end
|
239
239
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -58,8 +58,14 @@ def testing_dangerfile
|
|
58
58
|
Danger::Dangerfile.new(env, testing_ui)
|
59
59
|
end
|
60
60
|
|
61
|
-
|
62
|
-
|
61
|
+
# Returns the full path of the fixture at the given subpath (relative to `spec/fixtures`)
|
62
|
+
def fixture_path(*path_components)
|
63
|
+
File.join('spec', 'fixtures', *path_components)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Returns the content of the fixture at the given path (relative to `spec/fixtures`)
|
67
|
+
def fixture(*path_components)
|
68
|
+
File.read(fixture_path(*path_components))
|
63
69
|
end
|
64
70
|
|
65
71
|
# custom matchers
|