Compare commits

..

5 Commits

Author SHA1 Message Date
bl00dymarie 20ace28e13 Merge branch 'Chore_Upgrade-for-Android14' into 'main'
Chore upgrade for android14

See merge request bloodyhealth/drip!692
2024-11-05 12:57:05 +00:00
bl00dymarie 97e76959d9 Merge branch '732-chore-update-sympto-package' into 'main'
Chore: Update sympto package to v3.0.2

Closes #732

See merge request bloodyhealth/drip!691
2024-10-30 11:12:42 +00:00
bl00dymarie a9fdcdb1f3 Chore: Update sympto package to v3.0.2 2024-10-30 12:10:21 +01:00
bl00dymarie 8b101ec952 Release: 1.2410.29 2024-10-29 19:29:18 +01:00
bl00dymarie 5bb6bc05e7 Update Sdk to comply with Android14 2024-10-29 19:21:55 +01:00
14 changed files with 100 additions and 249 deletions
+2 -2
View File
@@ -146,8 +146,8 @@ android {
applicationId "com.drip" applicationId "com.drip"
minSdkVersion rootProject.ext.minSdkVersion minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 38 versionCode 39
versionName "1.2410.22" versionName "1.2410.29"
ndk { ndk {
abiFilters "armeabi-v7a", "x86", "arm64-v8a", "x86_64" abiFilters "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
} }
+3 -3
View File
@@ -49,10 +49,10 @@ allprojects {
} }
ext { ext {
buildToolsVersion = "33.0.2"
minSdkVersion = 21 minSdkVersion = 21
compileSdkVersion = 33 compileSdkVersion = 34
targetSdkVersion = 33 targetSdkVersion = 34
buildToolsVersion = "34.0.0"
soLoaderVersion = "0.10.4+" soLoaderVersion = "0.10.4+"
if (System.properties['os.arch'] == "aarch64") { if (System.properties['os.arch'] == "aarch64") {
@@ -1,56 +0,0 @@
import React from 'react'
import { View } from 'react-native'
import PropTypes from 'prop-types'
import Slider from '@ptomasroos/react-native-multi-slider'
import SliderLabel from './slider-label'
import { styles } from './slider-styles'
import {
ADVANCE_PERIOD_NOTICE_DAYS_MIN,
ADVANCE_PERIOD_NOTICE_DAYS_MAX,
} from '../../../config'
const AdvanceNoticeDaysSlider = ({
disabled,
advanceNoticeDays,
onAdvanceNoticeDaysChange,
}) => {
const sliderAccentBackground = disabled
? styles.disabledSliderAccentBackground
: styles.sliderAccentBackground
const sliderBackground = disabled
? styles.disabledSliderBackground
: styles.sliderBackground
return (
<View style={styles.container}>
<Slider
customLabel={SliderLabel}
enableLabel={true}
markerStyle={styles.marker}
markerOffsetY={styles.markerOffsetY}
max={ADVANCE_PERIOD_NOTICE_DAYS_MAX}
min={ADVANCE_PERIOD_NOTICE_DAYS_MIN}
onValuesChange={onAdvanceNoticeDaysChange}
step={1}
showSteps={true}
snapped={true}
trackStyle={styles.slider}
values={[advanceNoticeDays]}
enabledOne={!disabled}
enabledTwo={false}
selectedStyle={sliderAccentBackground}
unselectedStyle={sliderBackground}
/>
</View>
)
}
export default AdvanceNoticeDaysSlider
AdvanceNoticeDaysSlider.propTypes = {
disabled: PropTypes.bool,
advanceNoticeDays: PropTypes.number,
onAdvanceNoticeDaysChange: PropTypes.func,
}
@@ -1,34 +0,0 @@
import { StyleSheet } from 'react-native'
import { Colors, Sizes } from '../../../styles'
export const styles = StyleSheet.create({
container: {
alignItems: 'center',
paddingTop: Sizes.base,
},
marker: {
backgroundColor: Colors.turquoiseDark,
borderRadius: 50,
elevation: 4,
height: Sizes.subtitle,
width: Sizes.subtitle,
},
slider: {
borderRadius: 25,
height: Sizes.small,
paddingTop: Sizes.base,
},
sliderAccentBackground: {
backgroundColor: Colors.turquoiseDark,
},
disabledSliderAccentBackground: {
backgroundColor: Colors.grey,
},
sliderBackground: {
backgroundColor: Colors.turquoise,
},
disabledSliderBackground: {
backgroundColor: Colors.greyLight,
},
markerOffsetY: Sizes.tiny,
})
@@ -1,12 +1,13 @@
import React, { useState } from 'react' import React, { useState } from 'react'
import { View } from 'react-native' import { StyleSheet, View } from 'react-native'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import Slider from '@ptomasroos/react-native-multi-slider' import Slider from '@ptomasroos/react-native-multi-slider'
import SliderLabel from './slider-label'
import { styles } from './slider-styles'
import alertError from '../common/alert-error' import alertError from '../common/alert-error'
import SliderLabel from './slider-label'
import { scaleObservable, saveTempScale } from '../../../local-storage' import { scaleObservable, saveTempScale } from '../../../local-storage'
import { Colors, Sizes } from '../../../styles'
import labels from '../../../i18n/en/settings' import labels from '../../../i18n/en/settings'
import { TEMP_MIN, TEMP_MAX, TEMP_SLIDER_STEP } from '../../../config' import { TEMP_MIN, TEMP_MAX, TEMP_SLIDER_STEP } from '../../../config'
@@ -39,7 +40,7 @@ const TemperatureSlider = ({ disabled }) => {
customLabel={SliderLabel} customLabel={SliderLabel}
enableLabel={true} enableLabel={true}
markerStyle={styles.marker} markerStyle={styles.marker}
markerOffsetY={styles.markerOffsetY} markerOffsetY={Sizes.tiny}
max={TEMP_MAX} max={TEMP_MAX}
min={TEMP_MIN} min={TEMP_MIN}
onValuesChange={onTemperatureSliderChange} onValuesChange={onTemperatureSliderChange}
@@ -60,3 +61,34 @@ export default TemperatureSlider
TemperatureSlider.propTypes = { TemperatureSlider.propTypes = {
disabled: PropTypes.bool, disabled: PropTypes.bool,
} }
const styles = StyleSheet.create({
container: {
alignItems: 'center',
paddingTop: Sizes.base,
},
marker: {
backgroundColor: Colors.turquoiseDark,
borderRadius: 50,
elevation: 4,
height: Sizes.subtitle,
width: Sizes.subtitle,
},
slider: {
borderRadius: 25,
height: Sizes.small,
},
sliderAccentBackground: {
backgroundColor: Colors.turquoiseDark,
},
disabledSliderAccentBackground: {
backgroundColor: Colors.grey,
},
sliderBackground: {
backgroundColor: Colors.turquoise,
},
disabledSliderBackground: {
backgroundColor: Colors.greyLight,
},
})
@@ -1,59 +0,0 @@
import React, { useState } from 'react'
import AppSwitch from '../../common/app-switch'
import AdvanceNoticeDaysSlider from '../customization/advance-notice-days-slider'
import {
periodReminderObservable,
savePeriodReminder,
periodPredictionObservable,
saveAdvanceNoticeDays,
advanceNoticeDaysObservable,
} from '../../../local-storage'
import labels from '../../../i18n/en/settings'
const PeriodReminder = () => {
const isPeriodPredictionDisabled = !periodPredictionObservable.value
const [isPeriodReminderEnabled, setIsPeriodReminderEnabled] = useState(
periodReminderObservable.value.enabled
)
const [advanceNoticeDays, setAdvanceNoticeDays] = useState(
advanceNoticeDaysObservable.value
)
const periodReminderToggle = (isEnabled) => {
setIsPeriodReminderEnabled(isEnabled)
savePeriodReminder({ enabled: isEnabled })
}
const handleAdvanceNoticeDaysChange = (days) => {
setAdvanceNoticeDays(days)
saveAdvanceNoticeDays(days)
}
const reminderText =
advanceNoticeDays == 1
? labels.periodReminder.reminderTextSingular
: labels.periodReminder.reminderTextPlural(advanceNoticeDays)
return (
<>
<AppSwitch
onToggle={periodReminderToggle}
text={reminderText}
value={isPeriodReminderEnabled}
disabled={isPeriodPredictionDisabled}
/>
{isPeriodReminderEnabled && (
<AdvanceNoticeDaysSlider
disabled={isPeriodPredictionDisabled}
advanceNoticeDays={parseInt(advanceNoticeDays)}
onAdvanceNoticeDaysChange={handleAdvanceNoticeDaysChange}
/>
)}
</>
)
}
export default PeriodReminder
+22 -6
View File
@@ -1,11 +1,13 @@
import React from 'react' import React, { useState } from 'react'
import AppPage from '../../common/app-page' import AppPage from '../../common/app-page'
import AppSwitch from '../../common/app-switch'
import Segment from '../../common/segment' import Segment from '../../common/segment'
import TemperatureReminder from './temperature-reminder' import TemperatureReminder from './temperature-reminder'
import PeriodReminder from './period-reminder'
import { import {
periodReminderObservable,
savePeriodReminder,
periodPredictionObservable, periodPredictionObservable,
temperatureTrackingCategoryObservable, temperatureTrackingCategoryObservable,
} from '../../../local-storage' } from '../../../local-storage'
@@ -14,7 +16,17 @@ import labels from '../../../i18n/en/settings'
import { Alert, Pressable } from 'react-native' import { Alert, Pressable } from 'react-native'
const Reminders = () => { const Reminders = () => {
const periodReminderDisabledPrompt = () => { const isPeriodPredictionDisabled = !periodPredictionObservable.value
const [isPeriodReminderEnabled, setIsPeriodReminderEnabled] = useState(
periodReminderObservable.value.enabled
)
const periodReminderToggle = (isEnabled) => {
setIsPeriodReminderEnabled(isEnabled)
savePeriodReminder({ enabled: isEnabled })
}
const reminderDisabledPrompt = () => {
if (!periodPredictionObservable.value) { if (!periodPredictionObservable.value) {
Alert.alert( Alert.alert(
labels.periodReminder.alertNoPeriodReminder.title, labels.periodReminder.alertNoPeriodReminder.title,
@@ -31,12 +43,16 @@ const Reminders = () => {
) )
} }
} }
return ( return (
<AppPage> <AppPage>
<Pressable onPress={periodReminderDisabledPrompt}> <Pressable onPress={reminderDisabledPrompt}>
<Segment title={labels.periodReminder.title}> <Segment title={labels.periodReminder.title}>
<PeriodReminder /> <AppSwitch
onToggle={periodReminderToggle}
text={labels.periodReminder.reminderText}
value={isPeriodReminderEnabled}
disabled={isPeriodPredictionDisabled}
/>
</Segment> </Segment>
</Pressable> </Pressable>
<Pressable onPress={tempReminderDisabledPrompt}> <Pressable onPress={tempReminderDisabledPrompt}>
-4
View File
@@ -33,10 +33,6 @@ export const TEMP_MAX = 39
export const TEMP_MIN = 35 export const TEMP_MIN = 35
export const TEMP_SLIDER_STEP = 0.5 export const TEMP_SLIDER_STEP = 0.5
export const ADVANCE_PERIOD_NOTICE_DAYS_MIN = 1
export const ADVANCE_PERIOD_NOTICE_DAYS_MAX = 7
export const ADVANCE_PERIOD_NOTICE_DAYS_INIT_VALUE = 3
export const HIT_SLOP = { export const HIT_SLOP = {
top: verticalScale(20), top: verticalScale(20),
bottom: verticalScale(20), bottom: verticalScale(20),
+4 -6
View File
@@ -60,12 +60,10 @@ export default {
}, },
periodReminder: { periodReminder: {
title: 'Next period reminder', title: 'Next period reminder',
reminderTextSingular: reminderText:
'Get a notification 1 day before your next period is likely to start.', 'Get a notification 3 days before your next period is likely to start.',
reminderTextPlural: (days) => notification: (daysToEndOfPrediction) =>
`Get a notification ${days} days before your next period is likely to start.`, `Your next period is likely to start in 3 to ${daysToEndOfPrediction} days.`,
notification: (advanceNoticeDays, daysToEndOfPrediction) =>
`Your next period is likely to start in ${advanceNoticeDays} to ${daysToEndOfPrediction} days.`,
alertNoPeriodReminder: { alertNoPeriodReminder: {
title: 'Period predictions turned off', title: 'Period predictions turned off',
message: message:
+1 -1
View File
@@ -19,7 +19,7 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>1.2410.22</string> <string>1.2410.29</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
+25 -53
View File
@@ -2,7 +2,6 @@ import { Platform } from 'react-native'
import { import {
tempReminderObservable, tempReminderObservable,
periodReminderObservable, periodReminderObservable,
advanceNoticeDaysObservable,
} from '../local-storage' } from '../local-storage'
import * as PN from 'react-native-push-notification' import * as PN from 'react-native-push-notification'
import { requestNotifications } from 'react-native-permissions' import { requestNotifications } from 'react-native-permissions'
@@ -14,19 +13,12 @@ import { getBleedingDaysSortedByDate } from '../db'
import cycleModule from './cycle' import cycleModule from './cycle'
import nothingChanged from '../db/db-unchanged' import nothingChanged from '../db/db-unchanged'
const DRIP_CHANNEL_ID = 'drip-channel-id'
const TEMPERATURE_REMINDER_ID = '1'
const PERIOD_REMINDER_ID = '2'
const PushNotification = Platform.OS === 'ios' ? PN : PN.default
export default function setupNotifications(navigate, setDate) { export default function setupNotifications(navigate, setDate) {
// for Android, this method call is necessary Platform.OS === 'android' ? requestNotifications() : null
if (Platform.OS === 'android') { const PushNotification = Platform.OS === 'ios' ? PN : PN.default
requestNotifications()
}
PushNotification.createChannel({ PushNotification.createChannel({
channelId: DRIP_CHANNEL_ID, // (required) channelId: 'drip-channel-id', // (required)
channelName: 'drip reminder', // (required) channelName: 'drip reminder', // (required)
playSound: false, // (optional) default: true playSound: false, // (optional) default: true
}) })
@@ -34,10 +26,7 @@ export default function setupNotifications(navigate, setDate) {
PushNotification.configure({ PushNotification.configure({
onNotification: (notification) => { onNotification: (notification) => {
// https://github.com/zo0r/react-native-push-notification/issues/966#issuecomment-479069106 // https://github.com/zo0r/react-native-push-notification/issues/966#issuecomment-479069106
if ( if (notification.data?.id === '1' || notification.id === '1') {
notification.data?.id === TEMPERATURE_REMINDER_ID ||
notification.id === TEMPERATURE_REMINDER_ID
) {
const todayDate = LocalDate.now().toString() const todayDate = LocalDate.now().toString()
setDate(todayDate) setDate(todayDate)
navigate('TemperatureEditView') navigate('TemperatureEditView')
@@ -48,7 +37,7 @@ export default function setupNotifications(navigate, setDate) {
}) })
tempReminderObservable((reminder) => { tempReminderObservable((reminder) => {
PushNotification.cancelLocalNotification({ id: TEMPERATURE_REMINDER_ID }) PushNotification.cancelLocalNotification({ id: '1' })
if (reminder.enabled) { if (reminder.enabled) {
const [hours, minutes] = reminder.time.split(':') const [hours, minutes] = reminder.time.split(':')
let target = new Moment() let target = new Moment()
@@ -61,74 +50,57 @@ export default function setupNotifications(navigate, setDate) {
} }
PushNotification.localNotificationSchedule({ PushNotification.localNotificationSchedule({
id: TEMPERATURE_REMINDER_ID, id: '1',
userInfo: { id: TEMPERATURE_REMINDER_ID }, userInfo: { id: '1' },
message: labels.tempReminder.notification, message: labels.tempReminder.notification,
date: target.toDate(), date: target.toDate(),
vibrate: false, vibrate: false,
repeatType: 'day', repeatType: 'day',
channelId: DRIP_CHANNEL_ID, channelId: 'drip-channel-id',
allowWhileIdle: true, allowWhileIdle: true,
}) })
} }
}, false) }, false)
periodReminderObservable(() => updatePeriodNotification(), false) periodReminderObservable((reminder) => {
advanceNoticeDaysObservable(() => updatePeriodNotification(), false) PushNotification.cancelLocalNotification({ id: '2' })
if (reminder.enabled) setupPeriodReminder()
}, false)
getBleedingDaysSortedByDate().addListener((_, changes) => { getBleedingDaysSortedByDate().addListener((_, changes) => {
// the listener fires on setup, so we check if there were actually any changes // the listener fires on setup, so we check if there were actually any changes
if (nothingChanged(changes)) { if (nothingChanged(changes)) return
return PushNotification.cancelLocalNotification({ id: '2' })
} if (periodReminderObservable.value.enabled) setupPeriodReminder()
updatePeriodNotification()
}) })
} }
const updatePeriodNotification = () => { function setupPeriodReminder() {
// Cancel any existing period reminder const PushNotification = Platform.OS === 'ios' ? PN : PN.default
PushNotification.cancelLocalNotification({ id: PERIOD_REMINDER_ID })
// Set up a new period reminder if enabled
if (periodReminderObservable.value.enabled) {
schedulePeriodNotification()
}
}
function schedulePeriodNotification() {
const bleedingPrediction = cycleModule().getPredictedMenses() const bleedingPrediction = cycleModule().getPredictedMenses()
if (bleedingPrediction.length > 0) { if (bleedingPrediction.length > 0) {
const predictedBleedingStart = Moment( const predictedBleedingStart = Moment(
bleedingPrediction[0][0], bleedingPrediction[0][0],
'YYYY-MM-DD' 'YYYY-MM-DD'
) )
// 3 days before and at 6 am
const advanceNoticeDays = parseInt(advanceNoticeDaysObservable.value)
// ${advanceNoticeDays} days before and at 6 am
const reminderDate = predictedBleedingStart const reminderDate = predictedBleedingStart
.subtract(advanceNoticeDays, 'days') .subtract(3, 'days')
.hours(6) .hours(6)
.minutes(0) .minutes(0)
.seconds(0) .seconds(0)
if (reminderDate.isAfter()) { if (reminderDate.isAfter()) {
// period is likely to start in advanceNoticeDays to advanceNoticeDays + (length of prediction - 1) days // period is likely to start in 3 to 3 + (length of prediction - 1) days
const daysToEndOfPrediction = const daysToEndOfPrediction = bleedingPrediction[0].length + 2
advanceNoticeDays + bleedingPrediction[0].length - 1
PushNotification.localNotificationSchedule({ PushNotification.localNotificationSchedule({
id: PERIOD_REMINDER_ID, id: '2',
userInfo: { id: PERIOD_REMINDER_ID }, userInfo: { id: '2' },
message: labels.periodReminder.notification( message: labels.periodReminder.notification(daysToEndOfPrediction),
advanceNoticeDays,
daysToEndOfPrediction
),
date: reminderDate.toDate(), date: reminderDate.toDate(),
vibrate: false, vibrate: false,
channelId: DRIP_CHANNEL_ID, channelId: 'drip-channel-id',
allowWhileIdle: true, allowWhileIdle: true,
}) })
} }
+1 -15
View File
@@ -2,8 +2,6 @@ import AsyncStorage from '@react-native-async-storage/async-storage'
import Observable from 'obv' import Observable from 'obv'
import { TEMP_SCALE_MIN, TEMP_SCALE_MAX, TEMP_SCALE_UNITS } from './config' import { TEMP_SCALE_MIN, TEMP_SCALE_MAX, TEMP_SCALE_UNITS } from './config'
import { ADVANCE_PERIOD_NOTICE_DAYS_INIT_VALUE } from './config'
export const scaleObservable = Observable() export const scaleObservable = Observable()
setObvWithInitValue('tempScale', scaleObservable, { setObvWithInitValue('tempScale', scaleObservable, {
min: TEMP_SCALE_MIN, min: TEMP_SCALE_MIN,
@@ -61,18 +59,6 @@ export async function savePeriodPrediction(bool) {
} }
} }
export const advanceNoticeDaysObservable = Observable()
setObvWithInitValue(
'advanceNoticeDays',
advanceNoticeDaysObservable,
parseInt(ADVANCE_PERIOD_NOTICE_DAYS_INIT_VALUE)
)
export async function saveAdvanceNoticeDays(days) {
await AsyncStorage.setItem('advanceNoticeDays', JSON.stringify(days))
advanceNoticeDaysObservable.set(days)
}
export const useCervixAsSecondarySymptomObservable = Observable() export const useCervixAsSecondarySymptomObservable = Observable()
setObvWithInitValue( setObvWithInitValue(
'useCervixAsSecondarySymptom', 'useCervixAsSecondarySymptom',
@@ -123,7 +109,7 @@ export async function saveTemperatureTrackingCategory(bool) {
if (!temperatureTrackingCategoryObservable.value) { if (!temperatureTrackingCategoryObservable.value) {
// if temperature tracking is turned off, the temperature reminder gets disabled // if temperature tracking is turned off, the temperature reminder gets disabled
const tempReminderResult = await AsyncStorage.getItem('tempReminder') const tempReminderResult = await AsyncStorage.getItem('tempReminder')
if (tempReminderResult && JSON.parse(tempReminderResult).enabled) { if (tempReminderResult && JSON.parse(tempReminderResult).enabled) {
tempReminderObservable.set(false) tempReminderObservable.set(false)
} }
} }
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "drip.", "name": "drip.",
"version": "1.2410.22", "version": "1.2410.29",
"contributors": [ "contributors": [
"Julia Friesel <julia.friesel@gmail.com>", "Julia Friesel <julia.friesel@gmail.com>",
"Marie Kochsiek", "Marie Kochsiek",
@@ -60,7 +60,7 @@
"react-native-size-matters": "^0.4.0", "react-native-size-matters": "^0.4.0",
"react-native-vector-icons": "^9.2.0", "react-native-vector-icons": "^9.2.0",
"realm": "^10.16.0", "realm": "^10.16.0",
"sympto": "3.0.1" "sympto": "3.0.2"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.20.2", "@babel/core": "^7.20.2",
+4 -4
View File
@@ -7573,10 +7573,10 @@ supports-preserve-symlinks-flag@^1.0.0:
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
sympto@3.0.1: sympto@3.0.2:
version "3.0.1" version "3.0.2"
resolved "https://registry.yarnpkg.com/sympto/-/sympto-3.0.1.tgz#14a089ee9114f5eb6611c0dd95a1cb6b86701d52" resolved "https://registry.yarnpkg.com/sympto/-/sympto-3.0.2.tgz#8510fe64b2177cd8f9ba3b3af1aaee772dca8a0b"
integrity sha512-eskK/6ZUHyQtl/JPPb1ujYU8xCxek5vMFm6hr8kllu/GaYKi2W2wvFzLU2aQAo3RAyEqewhdXz1//DaK2TAneg== integrity sha512-0C1/aXZFXBhA6LHLch+7jhoO4WXchhOskmCMr83CzFMnQ1z3xaS86t7w7hpWha07tK97QpQPmRsr8g74lEb9jQ==
dependencies: dependencies:
assert "^2.0.0" assert "^2.0.0"
js-joda "^1.9.2" js-joda "^1.9.2"