pull/1676/merge
AL-Session 6 months ago committed by GitHub
commit fd9f591aba
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -3,11 +3,9 @@ package org.thoughtcrime.securesms.components
import android.content.Context
import android.util.AttributeSet
import androidx.preference.CheckBoxPreference
import com.squareup.phrase.Phrase
import network.loki.messenger.R
import org.session.libsession.utilities.StringSubstitutionConstants.APP_NAME_KEY
import org.thoughtcrime.securesms.ui.getSubbedCharSequence
import org.thoughtcrime.securesms.ui.getSubbedString
class SwitchPreferenceCompat : CheckBoxPreference {
private var listener: OnPreferenceClickListener? = null

@ -19,7 +19,6 @@ import org.session.libsession.utilities.getExpirationTypeDisplayValue
import org.thoughtcrime.securesms.database.model.MessageRecord
import org.thoughtcrime.securesms.showSessionDialog
import org.thoughtcrime.securesms.ui.getSubbedCharSequence
import org.thoughtcrime.securesms.ui.getSubbedString
import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds

@ -1,6 +1,5 @@
package org.thoughtcrime.securesms.preferences;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Typeface;
@ -9,11 +8,9 @@ import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.core.view.ViewCompat;
import androidx.fragment.app.DialogFragment;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceFragmentCompat;
@ -21,8 +18,8 @@ import androidx.preference.PreferenceGroupAdapter;
import androidx.preference.PreferenceScreen;
import androidx.preference.PreferenceViewHolder;
import androidx.recyclerview.widget.RecyclerView;
import org.thoughtcrime.securesms.conversation.v2.ViewUtil;
import network.loki.messenger.R;
import org.thoughtcrime.securesms.conversation.v2.ViewUtil;
public abstract class CorrectedPreferenceFragment extends PreferenceFragmentCompat {

@ -20,10 +20,8 @@ import android.annotation.SuppressLint
import android.content.Context
import android.os.Build
import android.text.format.DateFormat
import androidx.compose.ui.text.capitalize
import org.session.libsession.utilities.TextSecurePreferences
import org.session.libsignal.utilities.Log
import java.text.DateFormat.SHORT
import java.text.DateFormat.getTimeInstance
import java.text.ParseException
import java.text.SimpleDateFormat
import java.util.Calendar
@ -44,6 +42,20 @@ object DateUtils : android.text.format.DateUtils() {
private val DAY_PRECISION_DATE_FORMAT = SimpleDateFormat("yyyyMMdd")
private val HOUR_PRECISION_DATE_FORMAT = SimpleDateFormat("yyyyMMddHH")
// Preferred date and time formats for the user - these are stored in the user's preferences.
// We set them as invalid on startup (range is 0..8 and 0..2 respectively) so we only have to
// retrieve the preference once rather than every time we wish to format a date or time.
// See: TextSecurePreferences.DATE_FORMAT_PREF and TextSecurePreferences for further details.
// Note: For testing you can just set these values to something which isn't -1 and it will
// bypass the retrieve-from-pref operation and use the associated format.
private var userPreferredTimeFormat: Int = -1
private var userPreferredDateFormat: Int = 7
// String for the actual date format pattern that `userPreferredDataFormat` equates to - we'll
// start it off as a sane default, and update it to either follow the system or a specific
// format as the user desires.
private var userPreferredDateFormatPattern = "dd/MM/YYYY"
private fun isWithin(millis: Long, span: Long, unit: TimeUnit): Boolean {
return System.currentTimeMillis() - millis <= unit.toMillis(span)
}
@ -81,27 +93,169 @@ object DateUtils : android.text.format.DateUtils() {
return temp
}
fun getFormattedDateTime(time: Long, template: String, locale: Locale): String {
// THIS IS THE METHOD THAT ACTUALLY GETS USED TO GET THE DATE TIME
fun getFormattedDateTime(timestamp: Long, template: String, locale: Locale): String {
Log.w("ACL", "Getting formatted datetime - template is: " + template)
val localizedPattern = getLocalizedPattern(template, locale)
return SimpleDateFormat(localizedPattern, locale).format(Date(time))
Log.w("ACL", "Which turns into localizedPattern: " + localizedPattern)
return SimpleDateFormat(template, locale).format(Date(timestamp))
}
// Method to get the user's preferred time format, whether that's 12-hour or 24-hour
fun getHourFormat(c: Context): String {
// If this is the first run..
if (userPreferredTimeFormat == -1) {
// ..update our preferred time format (will return -1 if no saved pref).
userPreferredTimeFormat = TextSecurePreferences.getTimeFormatPref(c)
// If no saved value was written we'll write 0 for "Follow system setting" - this will only run on first install
if (userPreferredTimeFormat == -1) {
userPreferredTimeFormat = 0
TextSecurePreferences.setTimeFormatPref(c, userPreferredTimeFormat)
}
// If the preferred time format is "Follow system setting" then we need to find out what the system setting is!
if (userPreferredTimeFormat == 0) {
val is24HourFormat = DateFormat.is24HourFormat(c)
// Set the time format we'll use to either 24 or 12 hours.
// IMPORTANT: We don't WRITE this to the pref - we just use it while the app is running!
// Note: See TextSecurePreferences.TIME_FORMAT_PREF for further details of available formats.
userPreferredTimeFormat = if (is24HourFormat) 2 else 1
}
}
// At this point userPreferredTimeFormat will ALWAYS be either 1 or 2 - regardless of if the saved
// pref is 0 to "Follow system settings".
return if (userPreferredTimeFormat == 1) "hh:mm a" else "HH:mm"
}
fun getHourFormat(c: Context?): String {
return if ((DateFormat.is24HourFormat(c))) "HH:mm" else "hh:mm a"
// Method to take the SimpleDateFormat.getPattern() string and set the user's preferred date format appropriately
private fun updateDateFormatSettingsFromPattern(dateFormatPattern: String) {
when (dateFormatPattern) {
"M/d/yy" -> { userPreferredDateFormat = 1; userPreferredDateFormatPattern = dateFormatPattern }
"d/M/yy" -> { userPreferredDateFormat = 2; userPreferredDateFormatPattern = dateFormatPattern }
"dd/MM/yyyy" -> { userPreferredDateFormat = 3; userPreferredDateFormatPattern = dateFormatPattern }
"dd.MM.yyyy" -> { userPreferredDateFormat = 4; userPreferredDateFormatPattern = dateFormatPattern }
"dd-MM-yyyy" -> { userPreferredDateFormat = 5; userPreferredDateFormatPattern = dateFormatPattern }
"yyyy/M/d" -> { userPreferredDateFormat = 6; userPreferredDateFormatPattern = dateFormatPattern }
"yyyy.M.d" -> { userPreferredDateFormat = 7; userPreferredDateFormatPattern = dateFormatPattern }
"yyyy-M-d" -> { userPreferredDateFormat = 8; userPreferredDateFormatPattern = dateFormatPattern }
else -> {
// Sane fallback for unrecognised date format patten
userPreferredDateFormat = 3; userPreferredDateFormatPattern = "dd/MM/yyyy"
}
}
}
// Method to take an int from the user's dateFormatPref and update our date format pattern accordingly
private fun updateDateFormatSettingsFromInt(dateFormatPrefInt: Int) {
var mutableDateFormatPrefInt = dateFormatPrefInt
// There's probably a more elegant way to do this - but we can't use `.also { }` because
// the else block
when (mutableDateFormatPrefInt) {
1 -> { userPreferredDateFormatPattern = "M/d/yy" }
2 -> { userPreferredDateFormatPattern = "d/M/yy" }
3 -> { userPreferredDateFormatPattern = "dd/MM/yyyy" }
4 -> { userPreferredDateFormatPattern = "dd.MM.yyyy" }
5 -> { userPreferredDateFormatPattern = "dd-MM-yyyy" }
6 -> { userPreferredDateFormatPattern = "yyyy/M/d" }
7 -> { userPreferredDateFormatPattern = "yyyy.M.d" }
8 -> { userPreferredDateFormatPattern = "yyyy-M-d" }
else -> {
// Sane fallback for unrecognised date format pattern ("dd/MM/yyyy")
Log.w("DateUtils", "Bad dateFormatPrefInt - falling back to dd/MM/yyyy")
mutableDateFormatPrefInt = 3 // Because we 'also' update our setting from this!
userPreferredDateFormatPattern = "dd/MM/yyyy"
}
}.also {
// Typically we pass in `userPreferredDataFormat` TO this method - but because we perform
// sanitisation in the case of a bad value we'll also write a cleaned version back to the var.
userPreferredDateFormat = mutableDateFormatPrefInt
}
}
private fun isWithinOneWeek(timestamp: Long) = System.currentTimeMillis() - timestamp <= TimeUnit.DAYS.toMillis(7)
private fun isWithinOneYear(timestamp: Long) = System.currentTimeMillis() - timestamp <= TimeUnit.DAYS.toMillis(365)
fun getDisplayFormattedTimeSpanString(c: Context, locale: Locale, timestamp: Long): String {
// If the timestamp is within the last 24 hours we just give the time, e.g, "1:23 PM" or
// "13:23" depending on 12/24 hour formatting.
return if (isToday(timestamp)) {
// Note: Date patterns are in TR-35 format.
// See: https://www.unicode.org/reports/tr35/tr35-dates.html#Date_Format_Patterns
val t = if (isToday(timestamp)) {
Log.w("ACL", "Within today")
// If it's within the last 24 hours we just give the time in 24-hour format, such as "13:27" for 1:27pm
getFormattedDateTime(timestamp, getHourFormat(c), locale)
} else if (isWithin(timestamp, 6, TimeUnit.DAYS)) {
} else if (isWithinOneWeek(timestamp)) {
Log.w("ACL", "Within week")
// If it's within the last week we give the day as 3 letters then the time in 24-hour format, such as "Fri 13:27" for Friday 1:27pm
getFormattedDateTime(timestamp, "EEE " + getHourFormat(c), locale)
} else if (isWithin(timestamp, 365, TimeUnit.DAYS)) {
} else if (isWithinOneYear(timestamp)) {
Log.w("ACL", "Within year")
// If it's within the last year we give the month as 3 letters then the time in 24-hour format, such as "Mar 13:27" for March 1:27pm
// CAREFUL: "MMM d + getHourFormat(c)" actually turns out to be "8 July, 17:14" etc. - it is DAY-NUMBER and then MONTH (which can go up to 4 chars) - and THEN the time. Wild.
getFormattedDateTime(timestamp, "MMM d " + getHourFormat(c), locale)
} else {
getFormattedDateTime(timestamp, "MMM d " + getHourFormat(c) + ", yyyy", locale)
// NOTE: The `userPreferredDateFormat` is ONLY ever used on dates which exceed one year!
// See the Figma linked in ticket SES-360 for details.
Log.w("ACL", "More than 1 year")
// If the date is more than a year ago then we get "19 July 2023, 16:19" type format
// If the app has just started..
if (userPreferredDateFormat == -1) {
// ..update our preferred date format (will return -1 if no saved pref).
userPreferredDateFormat = TextSecurePreferences.getDateFormatPref(c)
// If we don't have a saved date format pref we'll write 0 for "Follow system setting".
// Note: This will only execute on first install & run where the pref doesn't exist.
if (userPreferredDateFormat == -1) {
userPreferredDateFormat = 0
TextSecurePreferences.setDateFormatPref(c, userPreferredDateFormat)
}
}
// --- At this point we will always have _some_ preferred date format ---
// If the preferred date format is "Follow system setting" then we need to find out what the system setting is!
if (userPreferredDateFormat == 0) {
val dateFormat = DateFormat.getDateFormat(c)
// Check if the DateFormat instance is a SimpleDateFormat
if (dateFormat is SimpleDateFormat) {
val dateFormatPattern = dateFormat.toLocalizedPattern()
Log.w("ACL", "Date pattern: " + dateFormat.toPattern())
// System setting has a date format pattern? Cool - let's use it, whatever it might be
userPreferredDateFormatPattern = dateFormatPattern
// Update our userPreferredDateFormat & pattern from the system setting
//updateDateFormatSettingsFromPattern(dateFormatPattern)
} else {
// If the dateFormat ISN'T a SimpleDateFormat from which we can extract a pattern then the best
// we can do is pick a sensible default like dd/MM/YYYY - which equates to option 3 out of our
// available options (see TextSecurePreferences.DATE_FORMAT_PREF for further details).
userPreferredDateFormat = 3
userPreferredDateFormatPattern = "dd/MM/yyyy"
}
// IMPORTANT: As we've updated the `userPreferredDataFormat` from "follow system setting" to
// "whatever that system setting is" we DO NOT write that back to the pref - we leave the
// saved value as is so that it always uses that system settings, whatever that may be.
} else {
// If the user has asked for a specific date format that isn't "Follow system setting"
// then update our date formatting settings from that preference.
updateDateFormatSettingsFromInt(userPreferredDateFormat)
}
//userPreferredDateFormatPattern
getFormattedDateTime(timestamp, userPreferredDateFormatPattern, locale)
//getFormattedDateTime(timestamp, "MMM d " + getHourFormat(c) + ", yyyy", locale)
}
Log.w("ACL", "t is: $t")
return t
}
fun getDetailedDateFormatter(context: Context?, locale: Locale): SimpleDateFormat {
@ -126,7 +280,7 @@ object DateUtils : android.text.format.DateUtils() {
} else if (isYesterday(timestamp)) {
getLocalisedRelativeDayString(RelativeDay.YESTERDAY)
} else {
getFormattedDateTime(timestamp, "EEE, MMM d, yyyy", locale)
getFormattedDateTime(timestamp, userPreferredDateFormatPattern, locale)
}
}

@ -33,7 +33,7 @@
android:key="pref_typing_indicators"
android:title="@string/typingIndicators"
android:summary="@string/typingIndicatorsDescription" />
<!-- TODO ACL: Need to show a live typing indicator here & remove the "(...)" from the above string (if it's there - it might have been removed now) -->
<!-- TODO ACL: Need to show a live typing indicator here! -->
</PreferenceCategory>

@ -308,6 +308,26 @@ interface TextSecurePreferences {
// for the lifetime of the Session installation.
const val HAVE_WARNED_USER_ABOUT_SAVING_ATTACHMENTS = "libsession.HAVE_WARNED_USER_ABOUT_SAVING_ATTACHMENTS"
// Key name for the user's preferred date format as an Int. See ticket SES-360 for further details & Figma design.
// Values for various formats are as follows:
// 0 - Follow system settings (default)
// 1 - M/D/YY - example: 1/2/24 (which is 2nd Jan 2024), or 12/25/24 (which is 25th Dec 2024)
// 2 - D/M/YY - example: 2/1/24 (which is 2nd Jan 2024), or 25/12/24 (which is 25th Dec 2024)
// 3 - DD/MM/YYYY - example: 02/01/2024 (which is 2nd Jan 2024), or 25/12/2024 (which is 25th Dec 2024)
// 4 - DD.MM.YYYY - example: 02.01.2024 (which is 2nd Jan 2024), or 25.12.2024 (which is 25th Dec 2024)
// 5 - DD-MM-YYYY - example: 02-01-2024 (which is 2nd Jan 2024), or 25-12-2024 (which is 25th Dec 2024)
// 6 - YYYY/M/D - example: 2024/1/2 (which is 2nd Jan 2024), or 2024/12/25 (which is 25th Dec 2024)
// 7 - YYYY.M.D - example: 2024.1.2 (which is 2nd Jan 2024), or 2024.12.25 (which is 25th Dec 2024)
// 8 - YYYY-M-D - example: 2024-1-2 (which is 2nd Jan 2024), or 2024-12-25 (which is 25th Dec 2024)
const val DATE_FORMAT_PREF = "libsession.DATE_FORMAT_PREF"
// Key name for the user's preferred time format as an Int
// Values for various formats are as follows:
// 0 - Follow system settings (default)
// 1 - 12h - example: 3:45 PM
// 2 - 24h - example: 15:45
const val TIME_FORMAT_PREF = "libsession.TIME_FORMAT_PREF"
@JvmStatic
fun getLastConfigurationSyncTime(context: Context): Long {
return getLongPreference(context, LAST_CONFIGURATION_SYNC_TIME, 0)
@ -983,8 +1003,14 @@ interface TextSecurePreferences {
setBooleanPreference(context, FINGERPRINT_KEY_GENERATED, true)
}
@JvmStatic
fun clearAll(context: Context) {
getDefaultSharedPreferences(context).edit().clear().commit()
}
// ----- Get / set methods for if we have already warned the user that saving attachments will allow other apps to access them -----
// Note: We only ever show the warning dialog about this ONCE - when the user accepts this fact we write true to the flag & never show again.
@JvmStatic
fun getHaveWarnedUserAboutSavingAttachments(context: Context): Boolean {
return getBooleanPreference(context, HAVE_WARNED_USER_ABOUT_SAVING_ATTACHMENTS, false)
@ -994,12 +1020,26 @@ interface TextSecurePreferences {
fun setHaveWarnedUserAboutSavingAttachments(context: Context) {
setBooleanPreference(context, HAVE_WARNED_USER_ABOUT_SAVING_ATTACHMENTS, true)
}
// ---------------------------------------------------------------------------------------------------------------------------------
// ----- Get / set methods for the user's date format preference -----
@JvmStatic
fun clearAll(context: Context) {
getDefaultSharedPreferences(context).edit().clear().commit()
fun getDateFormatPref(context: Context): Int {
// Note: 0 means "follow system setting" (default) - go to the declaration of DATE_FORMAT_PREF for further details.
return getIntegerPreference(context, DATE_FORMAT_PREF, -1)
}
@JvmStatic
fun setDateFormatPref(context: Context, value: Int) { setIntegerPreference(context, DATE_FORMAT_PREF, value) }
// ----- Get / set methods for the user's time format preference -----
@JvmStatic
fun getTimeFormatPref(context: Context): Int {
// Note: 0 means "follow system setting" (default) - go to the declaration of TIME_FORMAT_PREF for further details.
return getIntegerPreference(context, TIME_FORMAT_PREF, -1)
}
@JvmStatic
fun setTimeFormatPref(context: Context, value: Int) { setIntegerPreference(context, TIME_FORMAT_PREF, value) }
}
}

Loading…
Cancel
Save