...
 
Commits (18)
......@@ -5,8 +5,8 @@ buildscript {
'targetSdk' : 28,
'minSdk' : 21,
'versionCode' : 43,
'pEpEngineRevision' : 4467,
'pEpJNIAdapterRevision': 653,
'pEpEngineRevision' : 4499,
'pEpJNIAdapterRevision': 660,
'versionName' : '1.0.234',
'libpEpAdapterRevision': 158
]
......@@ -16,7 +16,7 @@ buildscript {
'ndk' : '20.0',
'supportLibrary' : '28.0.0',
'preferencesFix' : '28.0.0.0',
'preferencesFix' : '1.0.0',
'androidX' : '1.0.0',
'constraintLayout' : '1.1.3',
'lifecycleExtensions': '2.0.0',
......
......@@ -13,6 +13,7 @@ import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
......@@ -34,6 +35,9 @@ import com.fsck.k9.mail.internet.MimeHeader;
import com.fsck.k9.mail.internet.MimeMessageHelper;
import com.fsck.k9.mail.internet.MimeMultipart;
import com.fsck.k9.mail.internet.MimeUtility;
import org.jetbrains.annotations.NotNull;
import timber.log.Timber;
import static com.fsck.k9.mail.store.imap.ImapUtility.getLastResponse;
......@@ -46,6 +50,13 @@ class ImapFolder extends Folder<ImapMessage> {
return new SimpleDateFormat("dd-MMM-yyyy", Locale.US);
}
};
private static final ThreadLocal<SimpleDateFormat> RFC2822_DATE = new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss Z", Locale.US);
}
};
private static final int MORE_MESSAGES_WINDOW_SIZE = 500;
private static final int FETCH_WINDOW_SIZE = 100;
......@@ -1155,8 +1166,7 @@ class ImapFolder extends Folder<ImapMessage> {
String encodeFolderName = folderNameCodec.encode(getPrefixedName());
String escapedFolderName = ImapUtility.encodeString(encodeFolderName);
String command = String.format(Locale.US, "APPEND %s (%s) {%d}", escapedFolderName,
combineFlags(message.getFlags()), messageSize);
String command = generateAppendCommand(message, messageSize, escapedFolderName);
connection.sendCommand(command, false);
ImapResponse response;
......@@ -1225,6 +1235,19 @@ class ImapFolder extends Folder<ImapMessage> {
}
}
@NotNull
private String generateAppendCommand(Message message, long messageSize, String escapedFolderName) {
if (message.getInternalDate() == null) {
return String.format(Locale.US, "APPEND %s (%s) {%d}", escapedFolderName,
combineFlags(message.getFlags()), messageSize);
} else {
String internalDate = Objects.requireNonNull(RFC2822_DATE.get()).format(message.getInternalDate());
String escapedInternalDate = ImapUtility.encodeString(internalDate);
return String.format(Locale.US, "APPEND %s (%s) %s {%d}", escapedFolderName,
combineFlags(message.getFlags()), escapedInternalDate, messageSize);
}
}
@NonNull
private List<Message> filterAppendingMessages(List<? extends Message> messages) {
List<Message> filteredMessages = new ArrayList<>();
......
......@@ -75,10 +75,12 @@ dependencies {
implementation "org.jsoup:jsoup:${versions.jsoup}"
implementation "uk.co.chrisjenx:calligraphy:${versions.callygraphy}"
//implementation "com.klinkerapps:floating-tutorial:1.0.4"
implementation "com.takisoft.fix:preference-v7:${versions.preferencesFix}"
implementation "com.takisoft.fix:preference-v7-datetimepicker:${versions.preferencesFix}"
implementation "com.takisoft.fix:preference-v7-colorpicker:${versions.preferencesFix}"
implementation "com.takisoft.fix:preference-v7-ringtone:${versions.preferencesFix}"
implementation "androidx.preference:preference:${versions.androidX}"
implementation "com.takisoft.preferencex:preferencex:${versions.preferencesFix}"
implementation "com.takisoft.preferencex:preferencex-ringtone:${versions.preferencesFix}"
implementation "com.takisoft.preferencex:preferencex-datetimepicker:${versions.preferencesFix}"
implementation "com.takisoft.preferencex:preferencex-colorpicker:${versions.preferencesFix}"
implementation "com.evernote:android-job:${versions.androidJob}"
implementation "androidx.work:work-runtime:${versions.workRuntime}"
......
......@@ -360,7 +360,7 @@ public class Account implements BaseAccount, StoreConfig {
notificationSetting.setVibratePattern(0);
notificationSetting.setVibrateTimes(5);
notificationSetting.setRingEnabled(true);
notificationSetting.setRingtone("content://settings/system/notification_sound");
notificationSetting.setRingtone("");
notificationSetting.setLedColor(chipColor);
pEpUntrustedServer = DEFAULT_PEP_ENC_ON_SERVER;
......@@ -470,8 +470,7 @@ public class Account implements BaseAccount, StoreConfig {
notificationSetting.setVibratePattern(storage.getInt(accountUuid + ".vibratePattern", 0));
notificationSetting.setVibrateTimes(storage.getInt(accountUuid + ".vibrateTimes", 5));
notificationSetting.setRingEnabled(storage.getBoolean(accountUuid + ".ring", true));
notificationSetting.setRingtone(storage.getString(accountUuid + ".ringtone",
"content://settings/system/notification_sound"));
notificationSetting.setRingtone(storage.getString(accountUuid + ".ringtone", ""));
notificationSetting.setLed(storage.getBoolean(accountUuid + ".led", true));
notificationSetting.setLedColor(storage.getInt(accountUuid + ".ledColor", chipColor));
......@@ -1683,6 +1682,13 @@ public class Account implements BaseAccount, StoreConfig {
return notificationSetting;
}
public synchronized String getAccountRingtone() {
String ringtone = getNotificationSetting().getRingtone();
if (ringtone != null && ringtone.contains("notification_sound"))
return "";
else
return ringtone;
}
// TODO: pEp: do we really *need* synchronized here?!
public synchronized boolean isUntrustedSever() {
......
......@@ -32,9 +32,6 @@ import butterknife.OnClick;
import butterknife.OnEditorAction;
import butterknife.OnTextChanged;
import static android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
public abstract class K9Activity extends AppCompatActivity implements K9ActivityMagic {
@Nullable @Bind(R.id.toolbar) Toolbar toolbar;
......
......@@ -2370,7 +2370,13 @@ public class MessageList extends PepActivity implements MessageListFragmentListe
@Override
public void goBack() {
if (Intent.ACTION_SEARCH.equals(getIntent().getAction())) {
if (mDisplayMode == DisplayMode.MESSAGE_VIEW && mMessageListWasDisplayed) {
if(!isThreadDisplayed) {
updateToolbarColorToOriginal();
}
showMessageList();
}
else if (Intent.ACTION_SEARCH.equals(getIntent().getAction())) {
if(isBackstackClear()) {
if (mAccount != null) {
Router.onOpenAccount(this, mAccount);
......@@ -2381,11 +2387,6 @@ public class MessageList extends PepActivity implements MessageListFragmentListe
finish();
}
} else if (mDisplayMode == DisplayMode.MESSAGE_VIEW && mMessageListWasDisplayed) {
if(!isThreadDisplayed) {
updateToolbarColorToOriginal();
}
showMessageList();
} else if (isThreadDisplayed) {
actionDisplaySearch(this, mSearch, false, false);
} else if (getIntent().getBooleanExtra(EXTRA_FOLDER, false)) {
......
......@@ -62,6 +62,7 @@ class TrustedMessageController {
localMessage.getFlags().contains(Flag.X_PEP_NEVER_UNSECURE)) { //Untrusted server
encryptedMessage = encryptUntrustedMessage(context, pEpProvider, account, localMessage);
} else { // Trusted
localMessage.setInternalDate(localMessage.getSentDate());
return localMessage;
}
return encryptedMessage;
......
package com.fsck.k9.notification
import android.annotation.TargetApi
import android.app.NotificationChannel
import android.app.NotificationChannelGroup
import android.app.NotificationManager
import android.content.Context
import android.net.Uri
import android.os.Build
import androidx.annotation.RequiresApi
import com.fsck.k9.Account
import com.fsck.k9.K9
import com.fsck.k9.Preferences
import com.fsck.k9.R
import timber.log.Timber
import java.util.concurrent.Executors
import javax.inject.Inject
import javax.inject.Named
class NotificationChannelManager @Inject constructor(@Named("AppContext") private val context: Context, private val preferences: Preferences) {
enum class ChannelType {
MESSAGES, MISCELLANEOUS
}
fun updateChannels() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
return
......@@ -28,22 +35,30 @@ class NotificationChannelManager @Inject constructor(@Named("AppContext") privat
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE)
as NotificationManager
val accounts = preferences.accounts
removeChannelsForNonExistingOrChangedAccounts(notificationManager, accounts)
addChannelsForAccounts(notificationManager, accounts)
val newId = getChannelsNewUniqueId()
removeAllChannels(notificationManager)
addChannelsForAccounts(notificationManager, accounts, newId)
}
}
@RequiresApi(Build.VERSION_CODES.O)
private fun removeAllChannels(notificationManager: NotificationManager) {
notificationManager
.notificationChannelGroups
.forEach { group -> notificationManager.deleteNotificationChannelGroup(group.id) }
}
@RequiresApi(api = Build.VERSION_CODES.O)
private fun addChannelsForAccounts(
notificationManager: NotificationManager, accounts: List<Account>) {
notificationManager: NotificationManager, accounts: List<Account>, newId: String) {
for (account in accounts) {
val groupId = account.uuid
val group = NotificationChannelGroup(groupId, displayName(account))
val channelMessages = getChannelMessages(account)
val channelMiscellaneous = getChannelMiscellaneous(account)
val channelMessages = getChannelMessages(account, newId)
val channelMiscellaneous = getChannelMiscellaneous(account, newId)
notificationManager.createNotificationChannelGroup(group)
notificationManager.createNotificationChannel(channelMessages)
......@@ -52,53 +67,58 @@ class NotificationChannelManager @Inject constructor(@Named("AppContext") privat
}
@RequiresApi(api = Build.VERSION_CODES.O)
private fun removeChannelsForNonExistingOrChangedAccounts(
notificationManager: NotificationManager, accounts: List<Account>) {
val existingAccounts = HashMap<String, Account>()
for (account in accounts) {
existingAccounts[account.uuid] = account
}
val groups = notificationManager.notificationChannelGroups
for (group in groups) {
val groupId = group.id
var shouldDelete = false
if (!existingAccounts.containsKey(groupId)) {
shouldDelete = true
} else if (displayName(existingAccounts[groupId]) != group.name.toString()) {
// There is no way to change group names. Deleting group, so it is re-generated.
shouldDelete = true
}
if (shouldDelete) {
notificationManager.deleteNotificationChannelGroup(groupId)
}
}
}
@RequiresApi(api = Build.VERSION_CODES.O)
private fun getChannelMessages(account: Account): NotificationChannel {
// TODO: Use String resource file to support language translations
val channelName = "Messages"
val channelDescription = "Notifications related to messages"
val channelId = getChannelIdFor(account, ChannelType.MESSAGES)
private fun getChannelMessages(account: Account, newId: String): NotificationChannel {
val channelName = context.getString(R.string.notification_channel_messages_title)
val channelDescription = context.getString(R.string.notification_channel_messages_description)
val channelId = getNewChannelIdFor(account, ChannelType.MESSAGES, newId)
val importance = NotificationManager.IMPORTANCE_DEFAULT
val channelGroupId = account.uuid
val messagesChannel = NotificationChannel(channelId, channelName, importance)
messagesChannel.description = channelDescription
messagesChannel.group = channelGroupId
configChannel(account, messagesChannel)
return messagesChannel
}
@TargetApi(Build.VERSION_CODES.O)
fun configChannel(account: Account, messagesChannel: NotificationChannel) {
if (K9.isQuietTime()) {
return
}
val notificationSetting = account.notificationSetting
if (!account.isRingNotified) {
account.isRingNotified = true
}
if (account.isRingNotified) {
val ringtone = if (notificationSetting.isRingEnabled) notificationSetting.ringtone else null
ringtone?.isNotEmpty().let {
Timber.e("sound add: $ringtone")
messagesChannel.setSound(Uri.parse(ringtone), null)
}
val vibrationPattern = if (notificationSetting.isVibrateEnabled) notificationSetting.vibration else null
vibrationPattern?.isNotEmpty().let {
messagesChannel.enableVibration(true)
messagesChannel.vibrationPattern = vibrationPattern
}
}
val ledColor = if (notificationSetting.isLedEnabled) notificationSetting.ledColor else null
ledColor?.let {
messagesChannel.enableLights(true)
messagesChannel.lightColor = ledColor
}
}
@RequiresApi(api = Build.VERSION_CODES.O)
private fun getChannelMiscellaneous(account: Account): NotificationChannel {
// TODO: Use String resource file to support language translations
val channelName = "Miscellaneous"
val channelDescription = "Miscellaneous notifications like errors etc."
val channelId = getChannelIdFor(account, ChannelType.MISCELLANEOUS)
private fun getChannelMiscellaneous(account: Account, id: String): NotificationChannel {
val channelName = context.getString(R.string.notification_channel_miscellaneous_title)
val channelDescription = context.getString(R.string.notification_channel_miscellaneous_description)
val channelId = getNewChannelIdFor(account, ChannelType.MISCELLANEOUS, id)
val importance = NotificationManager.IMPORTANCE_LOW
val channelGroupId = account.uuid
......@@ -109,17 +129,57 @@ class NotificationChannelManager @Inject constructor(@Named("AppContext") privat
return miscellaneousChannel
}
fun getChannelIdFor(account: Account, channelType: ChannelType): String {
private fun getChannelsCurrentUniqueId(): String {
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE)
as NotificationManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channelGroups = notificationManager.notificationChannelGroups
return channelGroups
.ifEmpty { return "1" }
.first()
.channels
.ifEmpty { return "1" }
.first()
.id
.substringAfter(".", "1")
} else {
return "1"
}
}
private fun getChannelsNewUniqueId(): String {
return (getChannelsCurrentUniqueId() + 1).toString()
}
fun getChannelIdFor(
account: Account,
channelType: ChannelType): String {
val accountUuid = account.uuid
val id = getChannelsCurrentUniqueId()
return buildChannelId(accountUuid, channelType, id)
}
private fun getNewChannelIdFor(account: Account, channelType: ChannelType, id: String): String {
val accountUuid = account.uuid
return buildChannelId(accountUuid, channelType, id)
}
return if (channelType == ChannelType.MESSAGES) {
"messages_channel_$accountUuid"
private fun buildChannelId(accountUuid: String, channelType: ChannelType, id: String): String {
val builder = StringBuilder()
if (channelType == ChannelType.MESSAGES) {
builder.append("messages_channel_$accountUuid")
} else {
"miscellaneous_channel_$accountUuid"
builder.append("miscellaneous_channel_$accountUuid")
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
builder.append(".$id")
}
return builder.toString()
}
fun displayName(account: Account?): String {
return account?.name + " (" +account?.email +")"
return account?.name + " (" + account?.email + ")"
}
}
\ No newline at end of file
......@@ -70,7 +70,7 @@ class PEpMessageBuilder {
if(!(b instanceof MimeMultipart)) { //FIXME: Don't do this assumption (if not Multipart then plain or html text)
String disposition = MimeUtility.unfoldAndDecode(mm.getDisposition());
if (("attachment".equalsIgnoreCase(MessageExtractor.getContentDisposition(mm)))) {
if ((isAnAttachment(mm))) {
Log.i("PEpMessageBuilder", "addBody 1 " + disposition);
String filename = MimeUtility.getHeaderParameter(disposition, "filename");
addAttachment(attachments, mm.getContentType(), filename, PEpUtils.extractBodyContent(b));
......@@ -122,7 +122,7 @@ class PEpMessageBuilder {
//FIXME> Deal with non text and non multipart message and non attachments
boolean plain = mbp.isMimeType("text/plain");
if (plain || mbp.isMimeType("text/html")) {
if (!isAnAttachment(mbp) && (plain || mbp.isMimeType("text/html"))) {
String charset = getMessageCharset();
String text = new String(PEpUtils.extractBodyContent(mbp_body), charset);
......@@ -244,7 +244,7 @@ class PEpMessageBuilder {
if (part.getMimeType().equalsIgnoreCase("message/rfc822")) return "ForwardedMessage.eml";
if (filename == null) {
String disposition = MimeUtility.unfoldAndDecode(part.getDisposition());
if (("attachment".equalsIgnoreCase(MessageExtractor.getContentDisposition(part)))) {
if (isAnAttachment(part)) {
Log.i("PEpMessageBuilder", "addBody 1 " + disposition);
filename = MimeUtility.getHeaderParameter(disposition, "filename");
}
......@@ -253,4 +253,8 @@ class PEpMessageBuilder {
return filename != null ? filename : DEFAULT_FILENAME;
}
private boolean isAnAttachment(Part part) {
return "attachment".equalsIgnoreCase(MessageExtractor.getContentDisposition(part));
}
}
......@@ -161,7 +161,7 @@ public class AccountSettings {
new V(1, new BooleanSetting(true))
));
s.put("ringtone", Settings.versions(
new V(1, new RingtoneSetting("content://settings/system/notification_sound"))
new V(1, new RingtoneSetting(""))
));
s.put("searchableFolders", Settings.versions(
new V(1, new EnumSetting<>(Searchable.class, Searchable.ALL))
......
......@@ -138,7 +138,7 @@ class AccountSettingsDataStore(
"account_vibrate_times" -> account.notificationSetting.vibrateTimes.toString()
"account_remote_search_num_results" -> account.remoteSearchNumResults.toString()
"local_storage_provider" -> account.localStorageProviderId
"account_ringtone" -> account.notificationSetting.ringtone
"account_ringtone" -> account.accountRingtone
else -> defValue
}
}
......
......@@ -23,7 +23,7 @@ import com.fsck.k9.ui.settings.onClick
import com.fsck.k9.ui.settings.remove
import com.fsck.k9.ui.settings.removeEntry
import com.fsck.k9.ui.withArguments
import com.takisoft.fix.support.v7.preference.PreferenceFragmentCompat
import com.takisoft.preferencex.PreferenceFragmentCompat
import kotlinx.android.synthetic.main.preference_loading_widget.*
import kotlinx.coroutines.*
import org.koin.android.architecture.ext.sharedViewModel
......@@ -64,8 +64,6 @@ class AccountSettingsFragment : PreferenceFragmentCompat() {
initializeCryptoSettings(account)
initializeFolderSettings(account)
initializeAccountpEpKeyReset(account)
initializeNewRingtoneOptions()
if (!BuildConfig.WITH_KEY_SYNC) {
hideKeySyncOptions()
}
......@@ -176,12 +174,6 @@ class AccountSettingsFragment : PreferenceFragmentCompat() {
findPreference(PREFERENCE_PEP_ENABLE_SYNC_ACCOUNT)?.remove()
}
private fun initializeNewRingtoneOptions() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
findPreference(PREFERENCE_RINGTONE)?.remove()
}
}
private fun dopEpKeyReset(account: Account) {
disableKeyResetClickListener()
loading?.visibility = View.VISIBLE
......
......@@ -7,7 +7,6 @@ import androidx.preference.PreferenceViewHolder
import androidx.preference.TwoStatePreference
import android.util.AttributeSet
import com.fsck.k9.R
import com.takisoft.fix.support.v7.preference.PreferenceFragmentCompat
@SuppressLint("RestrictedApi")
class AutocryptPreferEncryptPreference
......
......@@ -4,8 +4,8 @@ import android.annotation.SuppressLint
import android.content.Context
import androidx.core.content.res.TypedArrayUtils
import android.util.AttributeSet
import com.takisoft.fix.support.v7.preference.ColorPickerPreference
import com.takisoft.fix.support.v7.preference.PreferenceFragmentCompat
import com.takisoft.preferencex.ColorPickerPreference
import com.takisoft.preferencex.PreferenceFragmentCompat
@SuppressLint("RestrictedApi")
class HoloColorPickerPreference
......
......@@ -20,7 +20,7 @@ import com.fsck.k9.ui.settings.onClick
import com.fsck.k9.ui.settings.remove
import com.fsck.k9.ui.settings.removeEntry
import com.fsck.k9.ui.withArguments
import com.takisoft.fix.support.v7.preference.PreferenceFragmentCompat
import com.takisoft.preferencex.PreferenceFragmentCompat
import kotlinx.android.synthetic.main.preference_loading_widget.*
import kotlinx.coroutines.*
import org.koin.android.ext.android.inject
......
......@@ -23,6 +23,7 @@
android:measureAllChildren="false"
android:animateLayoutChanges="true">
<TextView
android:id="@+id/shortTrustwords"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="?android:attr/textColorPrimary"
......@@ -32,6 +33,7 @@
android:breakStrategy="simple" />
<TextView
android:id="@+id/longTrustwords"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="?android:attr/textColorPrimary"
......
......@@ -262,4 +262,9 @@
<string name="message_failed_to_encrypt">We could not protect this message.</string>
<string name="notification_failed_to_encrypt_title">We could not protect this message.</string>
<string name="notification_failed_to_encrypt_text">We didn\'t send it and saved it to the draft folder instead.</string>
<!-- Notification Channels -->
<string name="notification_channel_messages_title">Messages</string>
<string name="notification_channel_messages_description">Notifications related to messages</string>
<string name="notification_channel_miscellaneous_title">Miscellaneous</string>
<string name="notification_channel_miscellaneous_description">Miscellaneous notifications like errors etc.</string>
</resources>
\ No newline at end of file
......@@ -11,7 +11,7 @@
android:title="@string/account_settings_general_title"
android:icon="?attr/iconActionSettings">
<com.takisoft.fix.support.v7.preference.AutoSummaryEditTextPreference
<com.takisoft.preferencex.AutoSummaryEditTextPreference
android:dialogTitle="@string/account_settings_description_label"
android:key="account_description"
android:title="@string/account_settings_description_label" />
......@@ -222,7 +222,7 @@
android:summary="@string/account_settings_strip_signature_summary"
android:title="@string/account_settings_strip_signature_label" />
<com.takisoft.fix.support.v7.preference.AutoSummaryEditTextPreference
<com.takisoft.preferencex.AutoSummaryEditTextPreference
android:dependency="quote_style"
android:dialogTitle="@string/account_settings_quote_prefix_label"
android:key="account_quote_prefix"
......@@ -312,12 +312,12 @@
android:summary="@string/account_notify_contacts_mail_only_summary"
android:title="@string/account_notify_contacts_mail_only_label" />
<com.takisoft.fix.support.v7.preference.RingtonePreference
android:defaultValue="content://settings/system/notification_sound"
<com.takisoft.preferencex.RingtonePreference
android:dependency="account_notify"
android:key="account_ringtone"
android:ringtoneType="notification"
android:showSilent="true"
android:showDefault="false"
android:title="@string/account_settings_ringtone"
app:pref_summaryHasRingtone="%s" />
......@@ -355,7 +355,7 @@
android:summary="@string/account_settings_led_summary"
android:title="@string/account_settings_led_label" />
<com.takisoft.fix.support.v7.preference.ColorPickerPreference
<com.takisoft.preferencex.ColorPickerPreference
android:dependency="account_led"
android:key="led_color"
android:summary="@string/account_settings_led_color_summary"
......
......@@ -141,7 +141,7 @@
android:summary="@string/global_settings_registered_name_color_changed"
android:title="@string/global_settings_registered_name_color_label" />
<!--com.takisoft.fix.support.v7.preference.ColorPickerPreference
<!--com.takisoft.preferencex.ColorPickerPreference
android:dependency="messagelist_change_contact_name_color"
android:key="messagelist_contact_name_color"
android:title="Contact name color" /-->
......@@ -275,14 +275,14 @@
android:summary="@string/quiet_time_notification_description"
android:title="@string/quiet_time_notification" />
<com.takisoft.fix.support.v7.preference.TimePickerPreference
<com.takisoft.preferencex.TimePickerPreference
android:dependency="quiet_time_enabled"
android:key="quiet_time_starts"
android:title="@string/quiet_time_starts"
app:pref_hourFormat="h24"
app:pref_summaryHasTime="%s" />
<com.takisoft.fix.support.v7.preference.TimePickerPreference
<com.takisoft.preferencex.TimePickerPreference
android:dependency="quiet_time_enabled"
android:key="quiet_time_ends"
android:title="@string/quiet_time_ends"
......
......@@ -16,5 +16,6 @@ android {
dependencies {
implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0'
implementation "com.jakewharton.timber:timber:${versions.timber}"
implementation "com.takisoft.fix:preference-v7:${versions.preferencesFix}"
implementation "androidx.preference:preference:${versions.androidX}"
implementation "com.takisoft.preferencex:preferencex:${versions.preferencesFix}"
}