Skip to content
Snippets Groups Projects
Verified Commit 7250a3e1 authored by Janne Mareike Koschinski's avatar Janne Mareike Koschinski
Browse files

Allow choosing language in-app

parent a60f920a
No related branches found
No related tags found
No related merge requests found
Pipeline #
Showing
with 187 additions and 49 deletions
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
package de.kuschku.quasseldroid package de.kuschku.quasseldroid
import android.content.Context
import android.os.Build import android.os.Build
import android.os.StrictMode import android.os.StrictMode
import com.squareup.leakcanary.LeakCanary import com.squareup.leakcanary.LeakCanary
...@@ -35,6 +36,7 @@ import de.kuschku.quasseldroid.util.backport.AndroidThreeTenBackport ...@@ -35,6 +36,7 @@ import de.kuschku.quasseldroid.util.backport.AndroidThreeTenBackport
import de.kuschku.quasseldroid.util.compatibility.AndroidCompatibilityUtils import de.kuschku.quasseldroid.util.compatibility.AndroidCompatibilityUtils
import de.kuschku.quasseldroid.util.compatibility.AndroidLoggingHandler import de.kuschku.quasseldroid.util.compatibility.AndroidLoggingHandler
import de.kuschku.quasseldroid.util.compatibility.AndroidStreamChannelFactory import de.kuschku.quasseldroid.util.compatibility.AndroidStreamChannelFactory
import de.kuschku.quasseldroid.util.ui.LocaleHelper
class Quasseldroid : DaggerApplication() { class Quasseldroid : DaggerApplication() {
override fun applicationInjector(): AndroidInjector<Quasseldroid> = override fun applicationInjector(): AndroidInjector<Quasseldroid> =
...@@ -226,4 +228,8 @@ class Quasseldroid : DaggerApplication() { ...@@ -226,4 +228,8 @@ class Quasseldroid : DaggerApplication() {
) )
} }
} }
override fun attachBaseContext(base: Context) {
super.attachBaseContext(LocaleHelper.setLocale(base))
}
} }
...@@ -25,7 +25,8 @@ import de.kuschku.quasseldroid.R ...@@ -25,7 +25,8 @@ import de.kuschku.quasseldroid.R
data class AppearanceSettings( data class AppearanceSettings(
val inputEnter: InputEnterMode = InputEnterMode.EMOJI, val inputEnter: InputEnterMode = InputEnterMode.EMOJI,
val showLag: Boolean = true, val showLag: Boolean = true,
val theme: Theme = Theme.MATERIAL_LIGHT val theme: Theme = Theme.MATERIAL_LIGHT,
val language: String = ""
) { ) {
enum class InputEnterMode { enum class InputEnterMode {
EMOJI, EMOJI,
......
...@@ -43,6 +43,10 @@ object Settings { ...@@ -43,6 +43,10 @@ object Settings {
showLag = getBoolean( showLag = getBoolean(
context.getString(R.string.preference_show_lag_key), context.getString(R.string.preference_show_lag_key),
AppearanceSettings.DEFAULT.showLag AppearanceSettings.DEFAULT.showLag
),
language = getString(
context.getString(R.string.preference_language_key),
AppearanceSettings.DEFAULT.language
) )
) )
} }
......
...@@ -29,6 +29,7 @@ import de.kuschku.libquassel.protocol.Buffer_Type ...@@ -29,6 +29,7 @@ import de.kuschku.libquassel.protocol.Buffer_Type
import de.kuschku.libquassel.quassel.syncables.IrcChannel import de.kuschku.libquassel.quassel.syncables.IrcChannel
import de.kuschku.libquassel.util.IrcUserUtils import de.kuschku.libquassel.util.IrcUserUtils
import de.kuschku.libquassel.util.flag.hasFlag import de.kuschku.libquassel.util.flag.hasFlag
import de.kuschku.libquassel.util.helpers.nullIf
import de.kuschku.libquassel.util.helpers.value import de.kuschku.libquassel.util.helpers.value
import de.kuschku.quasseldroid.R import de.kuschku.quasseldroid.R
import de.kuschku.quasseldroid.settings.AutoCompleteSettings import de.kuschku.quasseldroid.settings.AutoCompleteSettings
...@@ -152,15 +153,15 @@ class AutoCompleteHelper( ...@@ -152,15 +153,15 @@ class AutoCompleteHelper(
}.mapNotNull { info -> }.mapNotNull { info ->
networks[info.networkId]?.let { info to it } networks[info.networkId]?.let { info to it }
}.map { (info, network) -> }.map { (info, network) ->
val channel = network.ircChannel(info.bufferName) ?: IrcChannel.NULL val channel = network.ircChannel(info.bufferName).nullIf { it == IrcChannel.NULL }
AutoCompleteItem.ChannelItem( AutoCompleteItem.ChannelItem(
info = info, info = info,
network = network.networkInfo(), network = network.networkInfo(),
bufferStatus = when (channel) { bufferStatus = when (channel) {
IrcChannel.NULL -> BufferStatus.OFFLINE null -> BufferStatus.OFFLINE
else -> BufferStatus.ONLINE else -> BufferStatus.ONLINE
}, },
description = channel.topic() description = channel?.topic() ?: ""
) )
} }
val nicks = users.asSequence().map { user -> val nicks = users.asSequence().map { user ->
......
...@@ -63,7 +63,9 @@ class ClientSettingsFragment : DaggerPreferenceFragmentCompat(), ...@@ -63,7 +63,9 @@ class ClientSettingsFragment : DaggerPreferenceFragmentCompat(),
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) { override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
updateSummary(findPreference(key)) updateSummary(findPreference(key))
if (appearanceSettings.theme != Settings.appearance(context!!).theme) { val appearanceSettings = Settings.appearance(context!!)
if (this.appearanceSettings.theme != appearanceSettings.theme ||
this.appearanceSettings.language != appearanceSettings.language) {
activity?.recreate() activity?.recreate()
} }
} }
......
...@@ -21,6 +21,8 @@ package de.kuschku.quasseldroid.ui.setup ...@@ -21,6 +21,8 @@ package de.kuschku.quasseldroid.ui.setup
import android.arch.lifecycle.MutableLiveData import android.arch.lifecycle.MutableLiveData
import android.arch.lifecycle.Observer import android.arch.lifecycle.Observer
import android.content.Context
import android.content.pm.PackageManager
import android.os.Bundle import android.os.Bundle
import android.os.Parcelable import android.os.Parcelable
import android.support.annotation.ColorRes import android.support.annotation.ColorRes
...@@ -35,6 +37,7 @@ import android.view.ViewGroup ...@@ -35,6 +37,7 @@ import android.view.ViewGroup
import butterknife.BindView import butterknife.BindView
import butterknife.ButterKnife import butterknife.ButterKnife
import dagger.android.support.DaggerAppCompatActivity import dagger.android.support.DaggerAppCompatActivity
import de.kuschku.libquassel.util.helpers.nullIf
import de.kuschku.quasseldroid.R import de.kuschku.quasseldroid.R
import de.kuschku.quasseldroid.ui.clientsettings.about.AboutActivity import de.kuschku.quasseldroid.ui.clientsettings.about.AboutActivity
import de.kuschku.quasseldroid.ui.clientsettings.client.ClientSettingsActivity import de.kuschku.quasseldroid.ui.clientsettings.client.ClientSettingsActivity
...@@ -44,6 +47,7 @@ import de.kuschku.quasseldroid.util.helper.observeSticky ...@@ -44,6 +47,7 @@ import de.kuschku.quasseldroid.util.helper.observeSticky
import de.kuschku.quasseldroid.util.helper.or import de.kuschku.quasseldroid.util.helper.or
import de.kuschku.quasseldroid.util.helper.switchMap import de.kuschku.quasseldroid.util.helper.switchMap
import de.kuschku.quasseldroid.util.helper.updateRecentsHeaderIfExisting import de.kuschku.quasseldroid.util.helper.updateRecentsHeaderIfExisting
import de.kuschku.quasseldroid.util.ui.LocaleHelper
abstract class SetupActivity : DaggerAppCompatActivity() { abstract class SetupActivity : DaggerAppCompatActivity() {
@BindView(R.id.menu_view) @BindView(R.id.menu_view)
...@@ -104,6 +108,8 @@ abstract class SetupActivity : DaggerAppCompatActivity() { ...@@ -104,6 +108,8 @@ abstract class SetupActivity : DaggerAppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
setTheme(R.style.Theme_SetupTheme) setTheme(R.style.Theme_SetupTheme)
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
packageManager.getActivityInfo(componentName, PackageManager.GET_META_DATA).labelRes
.nullIf { it == 0 }?.let(this::setTitle)
setContentView(R.layout.activity_setup) setContentView(R.layout.activity_setup)
ButterKnife.bind(this) ButterKnife.bind(this)
...@@ -158,6 +164,10 @@ abstract class SetupActivity : DaggerAppCompatActivity() { ...@@ -158,6 +164,10 @@ abstract class SetupActivity : DaggerAppCompatActivity() {
updateRecentsHeader() updateRecentsHeader()
} }
override fun attachBaseContext(newBase: Context) {
super.attachBaseContext(LocaleHelper.setLocale(newBase))
}
private fun onDoneInternal() { private fun onDoneInternal() {
onDone(adapter.result) onDone(adapter.result)
} }
......
/*
* Quasseldroid - Quassel client for Android
*
* Copyright (c) 2018 Janne Koschinski
* Copyright (c) 2018 The Quassel Project
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3 as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.kuschku.quasseldroid.util.ui
import android.content.Context
import android.content.res.Configuration
import android.os.Build
import de.kuschku.quasseldroid.settings.Settings
import java.util.*
object LocaleHelper {
fun setLocale(context: Context): Context {
return updateResources(context, Settings.appearance(context).language)
}
private fun updateResources(context: Context, language: String) = if (language.isNotEmpty()) {
val locale = Locale(language)
Locale.setDefault(locale)
val config = Configuration(context.resources.configuration)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
config.setLocale(locale)
context.createConfigurationContext(config)
} else {
config.locale = locale
context.resources.updateConfiguration(config, context.resources.displayMetrics)
context
}
} else context
}
...@@ -19,6 +19,8 @@ ...@@ -19,6 +19,8 @@
package de.kuschku.quasseldroid.util.ui package de.kuschku.quasseldroid.util.ui
import android.content.Context
import android.content.pm.PackageManager
import android.os.Bundle import android.os.Bundle
import android.support.v4.app.Fragment import android.support.v4.app.Fragment
import android.support.v7.app.AppCompatActivity import android.support.v7.app.AppCompatActivity
...@@ -27,6 +29,7 @@ import dagger.android.AndroidInjector ...@@ -27,6 +29,7 @@ import dagger.android.AndroidInjector
import dagger.android.DispatchingAndroidInjector import dagger.android.DispatchingAndroidInjector
import dagger.android.HasFragmentInjector import dagger.android.HasFragmentInjector
import dagger.android.support.HasSupportFragmentInjector import dagger.android.support.HasSupportFragmentInjector
import de.kuschku.libquassel.util.helpers.nullIf
import de.kuschku.quasseldroid.settings.AppearanceSettings import de.kuschku.quasseldroid.settings.AppearanceSettings
import javax.inject.Inject import javax.inject.Inject
...@@ -45,6 +48,12 @@ abstract class ThemedActivity : AppCompatActivity(), HasSupportFragmentInjector, ...@@ -45,6 +48,12 @@ abstract class ThemedActivity : AppCompatActivity(), HasSupportFragmentInjector,
AndroidInjection.inject(this) AndroidInjection.inject(this)
setTheme(appearanceSettings.theme.style) setTheme(appearanceSettings.theme.style)
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
packageManager.getActivityInfo(componentName, PackageManager.GET_META_DATA).labelRes
.nullIf { it == 0 }?.let(this::setTitle)
}
override fun attachBaseContext(newBase: Context) {
super.attachBaseContext(LocaleHelper.setLocale(newBase))
} }
override fun supportFragmentInjector(): AndroidInjector<Fragment>? { override fun supportFragmentInjector(): AndroidInjector<Fragment>? {
......
...@@ -74,6 +74,25 @@ ...@@ -74,6 +74,25 @@
<string name="preference_show_lag_title">Show lag</string> <string name="preference_show_lag_title">Show lag</string>
<string name="preference_show_lag_summary">Displays the lag between client and core in the action bar</string> <string name="preference_show_lag_summary">Displays the lag between client and core in the action bar</string>
<string name="preference_language_key" translatable="false">language</string>
<string name="preference_language_title">Language</string>
<string name="preference_language_entry_auto">System Default</string>
<string name="preference_language_entry_en" translatable="false">English</string>
<string name="preference_language_entry_de" translatable="false">Deutsch</string>
<string name="preference_language_entry_lt" translatable="false">Lithuanian</string>
<string-array name="preference_language_entries">
<item>@string/preference_language_entry_auto</item>
<item>@string/preference_language_entry_en</item>
<item>@string/preference_language_entry_de</item>
<item>@string/preference_language_entry_lt</item>
</string-array>
<string-array name="preference_language_entryvalues" translatable="false">
<item />
<item>en</item>
<item>de</item>
<item>lt</item>
</string-array>
<string name="preference_notifications_title">Notifications</string> <string name="preference_notifications_title">Notifications</string>
......
...@@ -39,6 +39,13 @@ ...@@ -39,6 +39,13 @@
android:key="@string/preference_show_lag_key" android:key="@string/preference_show_lag_key"
android:summary="@string/preference_show_lag_summary" android:summary="@string/preference_show_lag_summary"
android:title="@string/preference_show_lag_title" /> android:title="@string/preference_show_lag_title" />
<DropDownPreference
android:defaultValue=""
android:entries="@array/preference_language_entries"
android:entryValues="@array/preference_language_entryvalues"
android:key="@string/preference_language_key"
android:title="@string/preference_language_title" />
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory android:title="@string/preference_notifications_title"> <PreferenceCategory android:title="@string/preference_notifications_title">
......
/*
* Quasseldroid - Quassel client for Android
*
* Copyright (c) 2018 Janne Koschinski
* Copyright (c) 2018 The Quassel Project
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3 as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.kuschku.libquassel.util.helpers
inline fun <T> T.nullIf(f: (T) -> Boolean): T? = if (f(this)) null else this
...@@ -41,6 +41,18 @@ fun <T : Any, U : Any> Observable<Optional<T>>.mapMapNullable( ...@@ -41,6 +41,18 @@ fun <T : Any, U : Any> Observable<Optional<T>>.mapMapNullable(
} }
} }
fun <T : Any, U : Any> Observable<T>.mapNullable(
nullableValue: T,
mapper: (T?) -> U): Observable<U> = map {
mapper(it.nullIf { it == nullableValue })
}
fun <T : Any, U : Any> Observable<T>.switchMapNullable(
nullableValue: T,
mapper: (T?) -> Observable<U>): Observable<U> = switchMap {
mapper(it.nullIf { it == nullableValue })
}
fun <T : Any, U : Any> Observable<Optional<T>>.mapSwitchMap( fun <T : Any, U : Any> Observable<Optional<T>>.mapSwitchMap(
mapper: (T) -> Observable<U>): Observable<Optional<U>> = switchMap { mapper: (T) -> Observable<U>): Observable<Optional<U>> = switchMap {
if (it.isPresent()) { if (it.isPresent()) {
......
...@@ -26,6 +26,7 @@ import de.kuschku.libquassel.quassel.syncables.IrcUser ...@@ -26,6 +26,7 @@ import de.kuschku.libquassel.quassel.syncables.IrcUser
import de.kuschku.libquassel.session.ISession import de.kuschku.libquassel.session.ISession
import de.kuschku.libquassel.util.Optional import de.kuschku.libquassel.util.Optional
import de.kuschku.libquassel.util.flag.hasFlag import de.kuschku.libquassel.util.flag.hasFlag
import de.kuschku.libquassel.util.helpers.mapNullable
import de.kuschku.quasseldroid.util.helper.combineLatest import de.kuschku.quasseldroid.util.helper.combineLatest
import de.kuschku.quasseldroid.viewmodel.data.AutoCompleteItem import de.kuschku.quasseldroid.viewmodel.data.AutoCompleteItem
import de.kuschku.quasseldroid.viewmodel.data.BufferStatus import de.kuschku.quasseldroid.viewmodel.data.BufferStatus
...@@ -76,15 +77,15 @@ class EditorViewModel : ViewModel() { ...@@ -76,15 +77,15 @@ class EditorViewModel : ViewModel() {
network.liveIrcChannel( network.liveIrcChannel(
info.bufferName info.bufferName
).switchMap { channel -> ).switchMap { channel ->
channel.updates().map { channel.updates().mapNullable(IrcChannel.NULL) {
AutoCompleteItem.ChannelItem( AutoCompleteItem.ChannelItem(
info = info, info = info,
network = network.networkInfo(), network = network.networkInfo(),
bufferStatus = when (it) { bufferStatus = when (it) {
IrcChannel.NULL -> BufferStatus.OFFLINE null -> BufferStatus.OFFLINE
else -> BufferStatus.ONLINE else -> BufferStatus.ONLINE
}, },
description = it.topic() description = it?.topic() ?: ""
) )
} }
} }
......
...@@ -201,9 +201,8 @@ class QuasselViewModel : ViewModel() { ...@@ -201,9 +201,8 @@ class QuasselViewModel : ViewModel() {
if (bufferInfo?.type?.hasFlag(Buffer_Type.ChannelBuffer) == true) { if (bufferInfo?.type?.hasFlag(Buffer_Type.ChannelBuffer) == true) {
session.liveNetworks().switchMap { networks -> session.liveNetworks().switchMap { networks ->
val network = networks[bufferInfo.networkId] val network = networks[bufferInfo.networkId]
network?.liveIrcChannel(bufferInfo.bufferName)?.switchMap { ircChannel -> network?.liveIrcChannel(bufferInfo.bufferName)?.switchMapNullable(IrcChannel.NULL) { ircChannel ->
if (ircChannel != IrcChannel.NULL) { ircChannel?.liveIrcUsers()?.switchMap { users ->
ircChannel.liveIrcUsers().switchMap { users ->
combineLatest<IrcUserItem>( combineLatest<IrcUserItem>(
users.map<IrcUser, Observable<IrcUserItem>?> { users.map<IrcUser, Observable<IrcUserItem>?> {
it.updates().map { user -> it.updates().map { user ->
...@@ -226,10 +225,7 @@ class QuasselViewModel : ViewModel() { ...@@ -226,10 +225,7 @@ class QuasselViewModel : ViewModel() {
} }
} }
) )
} } ?: Observable.just(emptyList())
} else {
Observable.just(emptyList())
}
} }
} }
} else { } else {
...@@ -294,10 +290,10 @@ class QuasselViewModel : ViewModel() { ...@@ -294,10 +290,10 @@ class QuasselViewModel : ViewModel() {
} ?: Observable.just(SelectedBufferItem(info, hiddenState = hiddenState)) } ?: Observable.just(SelectedBufferItem(info, hiddenState = hiddenState))
} }
Buffer_Type.ChannelBuffer -> { Buffer_Type.ChannelBuffer -> {
network?.liveIrcChannel(info.bufferName)?.map { network?.liveIrcChannel(info.bufferName)?.mapNullable(IrcChannel.NULL) {
SelectedBufferItem( SelectedBufferItem(
info, info,
joined = it != IrcChannel.NULL, joined = it != null,
hiddenState = hiddenState hiddenState = hiddenState
) )
} ?: Observable.just(SelectedBufferItem(info, hiddenState = hiddenState)) } ?: Observable.just(SelectedBufferItem(info, hiddenState = hiddenState))
...@@ -359,17 +355,17 @@ class QuasselViewModel : ViewModel() { ...@@ -359,17 +355,17 @@ class QuasselViewModel : ViewModel() {
network.liveNetworkInfo().switchMap { networkInfo -> network.liveNetworkInfo().switchMap { networkInfo ->
network.liveConnectionState().switchMap { connectionState -> network.liveConnectionState().switchMap { connectionState ->
network.liveIrcUser(info.bufferName).switchMap { network.liveIrcUser(info.bufferName).switchMap {
it.updates().map { user -> it.updates().mapNullable(IrcUser.NULL) { user ->
BufferProps( BufferProps(
info = info, info = info,
network = networkInfo, network = networkInfo,
networkConnectionState = connectionState, networkConnectionState = connectionState,
bufferStatus = when { bufferStatus = when {
user == IrcUser.NULL -> BufferStatus.OFFLINE user == null -> BufferStatus.OFFLINE
user.isAway() -> BufferStatus.AWAY user.isAway() -> BufferStatus.AWAY
else -> BufferStatus.ONLINE else -> BufferStatus.ONLINE
}, },
description = user.realName(), description = user?.realName() ?: "",
activity = activity, activity = activity,
highlights = highlights, highlights = highlights,
hiddenState = state hiddenState = state
...@@ -383,16 +379,16 @@ class QuasselViewModel : ViewModel() { ...@@ -383,16 +379,16 @@ class QuasselViewModel : ViewModel() {
network.liveNetworkInfo().switchMap { networkInfo -> network.liveNetworkInfo().switchMap { networkInfo ->
network.liveConnectionState().switchMap { connectionState -> network.liveConnectionState().switchMap { connectionState ->
network.liveIrcChannel(info.bufferName).switchMap { channel -> network.liveIrcChannel(info.bufferName).switchMap { channel ->
channel.updates().map { channel.updates().mapNullable(IrcChannel.NULL) {
BufferProps( BufferProps(
info = info, info = info,
network = networkInfo, network = networkInfo,
networkConnectionState = connectionState, networkConnectionState = connectionState,
bufferStatus = when (it) { bufferStatus = when (it) {
IrcChannel.NULL -> BufferStatus.OFFLINE null -> BufferStatus.OFFLINE
else -> BufferStatus.ONLINE else -> BufferStatus.ONLINE
}, },
description = it.topic(), description = it?.topic() ?: "",
activity = activity, activity = activity,
highlights = highlights, highlights = highlights,
hiddenState = state hiddenState = state
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment