From d0f6cfbc3b2fb03f40cad1f24d4be9204c323eaf Mon Sep 17 00:00:00 2001 From: Janne Koschinski <janne@kuschku.de> Date: Fri, 8 Jun 2018 17:53:30 +0200 Subject: [PATCH] Implement core info menu --- app/src/main/AndroidManifest.xml | 6 + .../quasseldroid/dagger/ActivityModule.kt | 6 + .../ssl/BrowserCompatibleHostnameVerifier.kt | 27 +--- .../de/kuschku/quasseldroid/ssl/X509Helper.kt | 31 +++++ .../ui/chat/info/core/ClientAdapter.kt | 107 +++++++++++++++ .../ui/chat/info/core/CoreInfoActivity.kt | 36 +++++ .../ui/chat/info/core/CoreInfoFragment.kt | 129 ++++++++++++++++++ .../info/core/CoreInfoFragmentProvider.kt | 34 +++++ .../ui/coresettings/CoreSettingsFragment.kt | 8 ++ .../main/res/layout/fragment_info_core.xml | 118 ++++++++++++++++ app/src/main/res/layout/settings_list.xml | 106 ++++---------- app/src/main/res/layout/widget_client.xml | 50 +++++++ .../main/res/values-v17/styles_widgets.xml | 20 +++ app/src/main/res/values/strings.xml | 1 + app/src/main/res/values/strings_info.xml | 10 ++ app/src/main/res/values/styles_widgets.xml | 17 +++ 16 files changed, 601 insertions(+), 105 deletions(-) create mode 100644 app/src/main/java/de/kuschku/quasseldroid/ssl/X509Helper.kt create mode 100644 app/src/main/java/de/kuschku/quasseldroid/ui/chat/info/core/ClientAdapter.kt create mode 100644 app/src/main/java/de/kuschku/quasseldroid/ui/chat/info/core/CoreInfoActivity.kt create mode 100644 app/src/main/java/de/kuschku/quasseldroid/ui/chat/info/core/CoreInfoFragment.kt create mode 100644 app/src/main/java/de/kuschku/quasseldroid/ui/chat/info/core/CoreInfoFragmentProvider.kt create mode 100644 app/src/main/res/layout/fragment_info_core.xml create mode 100644 app/src/main/res/layout/widget_client.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e813df6c3..837924118 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -79,6 +79,12 @@ android:label="@string/label_info_channel" android:parentActivityName=".ui.chat.ChatActivity" android:windowSoftInputMode="adjustResize" /> + <activity + android:name=".ui.chat.info.core.CoreInfoActivity" + android:exported="false" + android:label="@string/label_info_core" + android:parentActivityName=".ui.chat.ChatActivity" + android:windowSoftInputMode="adjustResize" /> <activity android:name=".ui.chat.topic.TopicActivity" android:exported="false" diff --git a/app/src/main/java/de/kuschku/quasseldroid/dagger/ActivityModule.kt b/app/src/main/java/de/kuschku/quasseldroid/dagger/ActivityModule.kt index ebc96cc51..b4deb4bbc 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/dagger/ActivityModule.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/dagger/ActivityModule.kt @@ -28,6 +28,8 @@ import de.kuschku.quasseldroid.ui.chat.ChatActivityModule import de.kuschku.quasseldroid.ui.chat.ChatFragmentProvider import de.kuschku.quasseldroid.ui.chat.info.channel.ChannelInfoActivity import de.kuschku.quasseldroid.ui.chat.info.channel.ChannelInfoFragmentProvider +import de.kuschku.quasseldroid.ui.chat.info.core.CoreInfoActivity +import de.kuschku.quasseldroid.ui.chat.info.core.CoreInfoFragmentProvider import de.kuschku.quasseldroid.ui.chat.info.user.UserInfoActivity import de.kuschku.quasseldroid.ui.chat.info.user.UserInfoFragmentProvider import de.kuschku.quasseldroid.ui.chat.topic.TopicActivity @@ -95,6 +97,10 @@ abstract class ActivityModule { @ContributesAndroidInjector(modules = [ChannelInfoFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class]) abstract fun bindChannelInfoActivity(): ChannelInfoActivity + @ActivityScope + @ContributesAndroidInjector(modules = [CoreInfoFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class]) + abstract fun bindCoreInfoActivity(): CoreInfoActivity + @ActivityScope @ContributesAndroidInjector(modules = [TopicFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class]) abstract fun bindTopicActivity(): TopicActivity diff --git a/app/src/main/java/de/kuschku/quasseldroid/ssl/BrowserCompatibleHostnameVerifier.kt b/app/src/main/java/de/kuschku/quasseldroid/ssl/BrowserCompatibleHostnameVerifier.kt index 660698ee3..effe55b70 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ssl/BrowserCompatibleHostnameVerifier.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ssl/BrowserCompatibleHostnameVerifier.kt @@ -21,6 +21,7 @@ package de.kuschku.quasseldroid.ssl import de.kuschku.libquassel.connection.HostnameVerifier import de.kuschku.libquassel.connection.SocketAddress +import de.kuschku.quasseldroid.ssl.X509Helper.hostnames import java.net.IDN import java.security.cert.X509Certificate import javax.net.ssl.SSLException @@ -59,30 +60,4 @@ class BrowserCompatibleHostnameVerifier : HostnameVerifier { return true } - - private fun hostnames(certificate: X509Certificate): Sequence<String> = - (sequenceOf(commonName(certificate)) + subjectAlternativeNames(certificate)) - .filterNotNull() - .distinct() - - private fun commonName(certificate: X509Certificate): String? { - return COMMON_NAME.find(certificate.subjectX500Principal.name)?.groups?.get(1)?.value - } - - private fun subjectAlternativeNames(certificate: X509Certificate): Sequence<String> = - certificate.subjectAlternativeNames.orEmpty().asSequence().mapNotNull { - val type = it[0] as? Int - val name = it[1] as? String - if (type != null && name != null) Pair(type, name) - else null - }.filter { (type, _) -> - // 2 is DNS Name - type == 2 - }.map { (_, name) -> - name - } - - companion object { - private val COMMON_NAME = Regex("""(?:^|,\s?)(?:CN=("(?:[^"]|"")+"|[^,]+))""") - } } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ssl/X509Helper.kt b/app/src/main/java/de/kuschku/quasseldroid/ssl/X509Helper.kt new file mode 100644 index 000000000..9eaf9b994 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/ssl/X509Helper.kt @@ -0,0 +1,31 @@ +package de.kuschku.quasseldroid.ssl + +import java.security.cert.X509Certificate + +object X509Helper { + fun hostnames(certificate: X509Certificate): Sequence<String> = + (sequenceOf(commonName(certificate)) + subjectAlternativeNames(certificate)) + .filterNotNull() + .distinct() + + fun commonName(certificate: X509Certificate) = + commonName(certificate.subjectX500Principal.name) + + fun commonName(distinguishedName: String) = + COMMON_NAME.find(distinguishedName)?.groups?.get(1)?.value + + fun subjectAlternativeNames(certificate: X509Certificate): Sequence<String> = + certificate.subjectAlternativeNames.orEmpty().asSequence().mapNotNull { + val type = it[0] as? Int + val name = it[1] as? String + if (type != null && name != null) Pair(type, name) + else null + }.filter { (type, _) -> + // 2 is DNS Name + type == 2 + }.map { (_, name) -> + name + } + + private val COMMON_NAME = Regex("""(?:^|,\s?)(?:CN=("(?:[^"]|"")+"|[^,]+))""") +} diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/info/core/ClientAdapter.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/info/core/ClientAdapter.kt new file mode 100644 index 000000000..699643818 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/info/core/ClientAdapter.kt @@ -0,0 +1,107 @@ +package de.kuschku.quasseldroid.ui.chat.info.core + +import android.graphics.drawable.Drawable +import android.support.v7.recyclerview.extensions.ListAdapter +import android.support.v7.util.DiffUtil +import android.support.v7.widget.RecyclerView +import android.text.Html +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Button +import android.widget.ImageView +import android.widget.TextView +import butterknife.BindView +import butterknife.ButterKnife +import de.kuschku.libquassel.quassel.syncables.CoreInfo +import de.kuschku.quasseldroid.R +import de.kuschku.quasseldroid.util.helper.getVectorDrawableCompat +import de.kuschku.quasseldroid.util.helper.styledAttributes +import de.kuschku.quasseldroid.util.helper.tint +import de.kuschku.quasseldroid.util.helper.visibleIf + +class ClientAdapter : ListAdapter<CoreInfo.ConnectedClientData, ClientAdapter.ClientViewHolder>( + object : DiffUtil.ItemCallback<CoreInfo.ConnectedClientData>() { + override fun areItemsTheSame(oldItem: CoreInfo.ConnectedClientData, + newItem: CoreInfo.ConnectedClientData) = oldItem.id == newItem.id + + override fun areContentsTheSame(oldItem: CoreInfo.ConnectedClientData, + newItem: CoreInfo.ConnectedClientData) = oldItem == newItem + } +) { + private var disconnectListener: ((Int) -> Unit)? = null + fun setDisconnectListener(listener: ((Int) -> Unit)?) { + this.disconnectListener = listener + } + + fun disconnect(id: Int) { + disconnectListener?.invoke(id) + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ClientViewHolder( + LayoutInflater.from(parent.context).inflate(R.layout.widget_client, parent, false), + ::disconnect + ) + + override fun onBindViewHolder(holder: ClientViewHolder, position: Int) = + holder.bind(getItem(position)) + + class ClientViewHolder( + itemView: View, + private val disconnectListener: (Int) -> Unit + ) : RecyclerView.ViewHolder(itemView) { + + @BindView(R.id.ip) + lateinit var ip: TextView + + @BindView(R.id.version) + lateinit var version: TextView + + @BindView(R.id.uptime) + lateinit var uptime: TextView + + @BindView(R.id.location) + lateinit var location: TextView + + @BindView(R.id.secure_icon) + lateinit var secureIcon: ImageView + + @BindView(R.id.disconnect) + lateinit var disconnect: Button + + private var id: Int? = null + + private val secure: Drawable? + private val insecure: Drawable? + + init { + ButterKnife.bind(this, itemView) + disconnect.setOnClickListener { + id?.let(disconnectListener::invoke) + } + + secure = itemView.context.getVectorDrawableCompat(R.drawable.ic_lock)?.mutate() + insecure = itemView.context.getVectorDrawableCompat(R.drawable.ic_lock_open)?.mutate() + itemView.context.theme.styledAttributes( + R.attr.colorTintSecure, + R.attr.colorTintInsecure + ) { + secure?.tint(getColor(0, 0)) + insecure?.tint(getColor(1, 0)) + } + } + + fun bind(data: CoreInfo.ConnectedClientData) { + id = data.id + + ip.text = data.remoteAddress + version.text = Html.fromHtml(data.clientVersion) + uptime.text = itemView.context.getString(R.string.label_core_connected_since, + data.connectedSince.toString()) + location.text = data.location + location.visibleIf(data.location.isNotBlank()) + + secureIcon.setImageDrawable(if (data.secure) secure else insecure) + } + } +} diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/info/core/CoreInfoActivity.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/info/core/CoreInfoActivity.kt new file mode 100644 index 000000000..a7e9c5c25 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/info/core/CoreInfoActivity.kt @@ -0,0 +1,36 @@ +/* + * 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.ui.chat.info.core + +import android.content.Context +import android.content.Intent +import de.kuschku.quasseldroid.util.ui.settings.ServiceBoundSettingsActivity + +class CoreInfoActivity : ServiceBoundSettingsActivity(CoreInfoFragment()) { + companion object { + fun launch( + context: Context + ) = context.startActivity(intent(context)) + + fun intent( + context: Context + ) = Intent(context, CoreInfoActivity::class.java) + } +} diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/info/core/CoreInfoFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/info/core/CoreInfoFragment.kt new file mode 100644 index 000000000..0b5af2034 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/info/core/CoreInfoFragment.kt @@ -0,0 +1,129 @@ +/* + * 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.ui.chat.info.core + +import android.arch.lifecycle.Observer +import android.os.Bundle +import android.support.v7.widget.LinearLayoutManager +import android.support.v7.widget.RecyclerView +import android.text.Html +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import android.widget.TextView +import butterknife.BindView +import butterknife.ButterKnife +import de.kuschku.libquassel.util.helpers.value +import de.kuschku.quasseldroid.R +import de.kuschku.quasseldroid.ssl.X509Helper +import de.kuschku.quasseldroid.util.helper.* +import de.kuschku.quasseldroid.util.service.ServiceBoundFragment + +class CoreInfoFragment : ServiceBoundFragment() { + + @BindView(R.id.version) + lateinit var version: TextView + + @BindView(R.id.version_date) + lateinit var versionDate: TextView + + @BindView(R.id.uptime_container) + lateinit var uptimeContainer: View + + @BindView(R.id.uptime) + lateinit var uptime: TextView + + @BindView(R.id.secure_container) + lateinit var secureContainer: View + + @BindView(R.id.secure) + lateinit var secureText: TextView + + @BindView(R.id.secure_icon) + lateinit var secureIcon: ImageView + + @BindView(R.id.clients) + lateinit var clients: RecyclerView + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + val view = inflater.inflate(R.layout.fragment_info_core, container, false) + ButterKnife.bind(this, view) + + viewModel.coreInfo.toLiveData().observe(this, Observer { + it?.orNull().let { + version.text = it?.quasselVersion?.let(Html::fromHtml) + versionDate.text = it?.quasselBuildDate?.let(Html::fromHtml) + + val startTime = it?.startTime?.toString() + uptime.text = requireContext().getString(R.string.label_core_online_since, + startTime.toString()) + uptimeContainer.visibleIf(startTime != null) + } + }) + + val secure = requireContext().getVectorDrawableCompat(R.drawable.ic_lock)?.mutate() + val partiallySecure = requireContext().getVectorDrawableCompat(R.drawable.ic_lock)?.mutate() + val insecure = requireContext().getVectorDrawableCompat(R.drawable.ic_lock_open)?.mutate() + requireContext().theme.styledAttributes( + R.attr.colorTintSecure, + R.attr.colorTintPartiallySecure, + R.attr.colorTintInsecure + ) { + secure?.tint(getColor(0, 0)) + partiallySecure?.tint(getColor(1, 0)) + insecure?.tint(getColor(2, 0)) + } + + viewModel.sslSession.toLiveData().observe(this, Observer { + val sslSession = it?.orNull() + val leafCertificate = sslSession?.peerCertificateChain?.firstOrNull() + val issuerName = leafCertificate?.issuerDN?.name?.let(X509Helper::commonName) + + if (sslSession == null) { + secureText.text = requireContext().getString(R.string.label_core_connection_insecure) + secureIcon.setImageDrawable(insecure) + } else { + secureText.text = requireContext().getString( + R.string.label_core_connection_verified_by, + issuerName + ?: requireContext().getString(R.string.label_core_connection_verified_by_unknown) + ) + secureIcon.setImageDrawable(secure) + } + }) + + clients.layoutManager = LinearLayoutManager(requireContext()) + val adapter = ClientAdapter() + adapter.setDisconnectListener { + val sessionOptional = viewModel.session.value + val session = sessionOptional?.orNull() + val rpcHandler = session?.rpcHandler + rpcHandler?.requestKickClient(it) + } + clients.adapter = adapter + viewModel.coreInfoClients.toLiveData().observe(this, Observer { + adapter.submitList(it) + }) + + return view + } +} diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/info/core/CoreInfoFragmentProvider.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/info/core/CoreInfoFragmentProvider.kt new file mode 100644 index 000000000..59607acc5 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/info/core/CoreInfoFragmentProvider.kt @@ -0,0 +1,34 @@ +/* + * 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.ui.chat.info.core + +import android.support.v4.app.FragmentActivity +import dagger.Binds +import dagger.Module +import dagger.android.ContributesAndroidInjector + +@Module +abstract class CoreInfoFragmentProvider { + @Binds + abstract fun bindFragmentActivity(activity: CoreInfoActivity): FragmentActivity + + @ContributesAndroidInjector + abstract fun bindCoreInfoFragment(): CoreInfoFragment +} diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/CoreSettingsFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/CoreSettingsFragment.kt index e7353fe1b..acd2145c3 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/CoreSettingsFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/CoreSettingsFragment.kt @@ -35,6 +35,7 @@ import de.kuschku.libquassel.quassel.syncables.BufferViewConfig import de.kuschku.libquassel.quassel.syncables.Identity import de.kuschku.libquassel.quassel.syncables.Network import de.kuschku.quasseldroid.R +import de.kuschku.quasseldroid.ui.chat.info.core.CoreInfoActivity import de.kuschku.quasseldroid.ui.coresettings.aliaslist.AliasListActivity import de.kuschku.quasseldroid.ui.coresettings.chatlist.ChatlistCreateActivity import de.kuschku.quasseldroid.ui.coresettings.chatlist.ChatlistEditActivity @@ -51,6 +52,9 @@ import de.kuschku.quasseldroid.util.service.ServiceBoundFragment import io.reactivex.Observable class CoreSettingsFragment : ServiceBoundFragment() { + @BindView(R.id.coreinfo) + lateinit var coreinfo: View + @BindView(R.id.networks) lateinit var networks: RecyclerView @@ -100,6 +104,10 @@ class CoreSettingsFragment : ServiceBoundFragment() { val itemDecoration = DividerItemDecoration(requireContext(), DividerItemDecoration.VERTICAL) + coreinfo.setOnClickListener { + CoreInfoActivity.launch(requireContext()) + } + networks.adapter = networkAdapter networks.layoutManager = LinearLayoutManager(context) networks.addItemDecoration(itemDecoration) diff --git a/app/src/main/res/layout/fragment_info_core.xml b/app/src/main/res/layout/fragment_info_core.xml new file mode 100644 index 000000000..91f8c8caf --- /dev/null +++ b/app/src/main/res/layout/fragment_info_core.xml @@ -0,0 +1,118 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + 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/>. + --> + +<android.support.v4.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:animateLayoutChanges="true" + android:orientation="vertical"> + + <TextView + style="@style/Widget.Info.Section" + android:text="@string/label_core_version" /> + + <LinearLayout style="@style/Widget.Info.Item"> + + <TextView + android:id="@+id/version" + style="@style/Widget.Info.Item.Content" + tools:text="v0.13-pre (0.12.0+617 git-f4c93cad)" /> + + <TextView + android:id="@+id/version_date" + style="@style/Widget.Info.Item.Description" + tools:text="June 8, 2018" /> + </LinearLayout> + + <View + android:layout_width="match_parent" + android:layout_height="1dp" + android:background="?colorDivider" /> + + <TextView + style="@style/Widget.Info.Section" + android:text="@string/label_core_uptime" /> + + <LinearLayout + android:id="@+id/uptime_container" + style="@style/Widget.Info.Item" + android:visibility="gone" + tools:visibility="visible"> + + <TextView + android:id="@+id/uptime" + style="@style/Widget.Info.Item.Content" + tools:text="30 minutes" /> + </LinearLayout> + + <View + android:layout_width="match_parent" + android:layout_height="1dp" + android:background="?colorDivider" /> + + <TextView + style="@style/Widget.Info.Section" + android:text="@string/label_core_security" /> + + <LinearLayout + android:id="@+id/secure_container" + style="@style/Widget.Info.Item" + android:orientation="horizontal"> + + <TextView + android:id="@+id/secure" + style="@style/Widget.Info.Item.Content" + android:layout_width="0dip" + android:layout_weight="1" + tools:text="Connection verified by Let’s Encrypt" /> + + <android.support.v7.widget.AppCompatImageView + android:id="@+id/secure_icon" + android:layout_width="24dp" + android:layout_height="24dp" + android:layout_marginLeft="16dp" + android:layout_marginStart="16dp" + app:srcCompat="@drawable/ic_lock" /> + </LinearLayout> + + <View + android:layout_width="match_parent" + android:layout_height="1dp" + android:background="?colorDivider" /> + + <TextView + style="@style/Widget.Info.Section" + android:text="@string/label_core_clients" /> + + <android.support.v7.widget.RecyclerView + android:id="@+id/clients" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:nestedScrollingEnabled="false" + tools:listitem="@layout/widget_client" /> + + </LinearLayout> +</android.support.v4.widget.NestedScrollView> diff --git a/app/src/main/res/layout/settings_list.xml b/app/src/main/res/layout/settings_list.xml index c8a21afdc..a175a975f 100644 --- a/app/src/main/res/layout/settings_list.xml +++ b/app/src/main/res/layout/settings_list.xml @@ -29,15 +29,7 @@ android:layout_height="wrap_content" android:orientation="vertical"> - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:gravity="center_vertical" - android:minHeight="?listPreferredItemHeightSmall" - android:paddingEnd="?listPreferredItemPaddingRight" - android:paddingLeft="?listPreferredItemPaddingLeft" - android:paddingRight="?listPreferredItemPaddingRight" - android:paddingStart="?listPreferredItemPaddingLeft"> + <LinearLayout style="@style/Widget.CoreSettings.PrimaryItemGroupHeader"> <android.support.v7.widget.AppCompatImageView style="@style/Widget.CoreSettings.PrimaryItemIcon" @@ -48,12 +40,7 @@ android:text="@string/settings_networks_title" /> </LinearLayout> - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginLeft="72dp" - android:layout_marginStart="72dp" - android:orientation="vertical"> + <LinearLayout style="@style/Widget.CoreSettings.PrimaryItemGroup"> <android.support.v7.widget.RecyclerView android:id="@+id/networks" @@ -75,15 +62,7 @@ </LinearLayout> </LinearLayout> - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:gravity="center_vertical" - android:minHeight="?listPreferredItemHeightSmall" - android:paddingEnd="?listPreferredItemPaddingRight" - android:paddingLeft="?listPreferredItemPaddingLeft" - android:paddingRight="?listPreferredItemPaddingRight" - android:paddingStart="?listPreferredItemPaddingLeft"> + <LinearLayout style="@style/Widget.CoreSettings.PrimaryItemGroupHeader"> <android.support.v7.widget.AppCompatImageView style="@style/Widget.CoreSettings.PrimaryItemIcon" @@ -121,15 +100,7 @@ </LinearLayout> </LinearLayout> - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:gravity="center_vertical" - android:minHeight="?listPreferredItemHeightSmall" - android:paddingEnd="?listPreferredItemPaddingRight" - android:paddingLeft="?listPreferredItemPaddingLeft" - android:paddingRight="?listPreferredItemPaddingRight" - android:paddingStart="?listPreferredItemPaddingLeft"> + <LinearLayout style="@style/Widget.CoreSettings.PrimaryItemGroupHeader"> <android.support.v7.widget.AppCompatImageView style="@style/Widget.CoreSettings.PrimaryItemIcon" @@ -140,12 +111,7 @@ android:text="@string/settings_chatlists_title" /> </LinearLayout> - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginLeft="72dp" - android:layout_marginStart="72dp" - android:orientation="vertical"> + <LinearLayout style="@style/Widget.CoreSettings.PrimaryItemGroup"> <android.support.v7.widget.RecyclerView android:id="@+id/chatlists" @@ -169,16 +135,8 @@ <LinearLayout android:id="@+id/ignorelist" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:background="?selectableItemBackground" - android:focusable="true" - android:gravity="center_vertical" - android:minHeight="?listPreferredItemHeightSmall" - android:paddingEnd="?listPreferredItemPaddingRight" - android:paddingLeft="?listPreferredItemPaddingLeft" - android:paddingRight="?listPreferredItemPaddingRight" - android:paddingStart="?listPreferredItemPaddingLeft"> + style="@style/Widget.CoreSettings.PrimaryItemGroupHeader" + android:focusable="true"> <android.support.v7.widget.AppCompatImageView style="@style/Widget.CoreSettings.PrimaryItemIcon" @@ -191,16 +149,8 @@ <LinearLayout android:id="@+id/highlightlist" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:background="?selectableItemBackground" - android:focusable="true" - android:gravity="center_vertical" - android:minHeight="?listPreferredItemHeightSmall" - android:paddingEnd="?listPreferredItemPaddingRight" - android:paddingLeft="?listPreferredItemPaddingLeft" - android:paddingRight="?listPreferredItemPaddingRight" - android:paddingStart="?listPreferredItemPaddingLeft"> + style="@style/Widget.CoreSettings.PrimaryItemGroupHeader" + android:focusable="true"> <android.support.v7.widget.AppCompatImageView style="@style/Widget.CoreSettings.PrimaryItemIcon" @@ -213,16 +163,8 @@ <LinearLayout android:id="@+id/aliaslist" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:background="?selectableItemBackground" - android:focusable="true" - android:gravity="center_vertical" - android:minHeight="?listPreferredItemHeightSmall" - android:paddingEnd="?listPreferredItemPaddingRight" - android:paddingLeft="?listPreferredItemPaddingLeft" - android:paddingRight="?listPreferredItemPaddingRight" - android:paddingStart="?listPreferredItemPaddingLeft"> + style="@style/Widget.CoreSettings.PrimaryItemGroupHeader" + android:focusable="true"> <android.support.v7.widget.AppCompatImageView style="@style/Widget.CoreSettings.PrimaryItemIcon" @@ -235,16 +177,8 @@ <LinearLayout android:id="@+id/networkconfig" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:background="?selectableItemBackground" - android:focusable="true" - android:gravity="center_vertical" - android:minHeight="?listPreferredItemHeightSmall" - android:paddingEnd="?listPreferredItemPaddingRight" - android:paddingLeft="?listPreferredItemPaddingLeft" - android:paddingRight="?listPreferredItemPaddingRight" - android:paddingStart="?listPreferredItemPaddingLeft"> + style="@style/Widget.CoreSettings.PrimaryItemGroupHeader" + android:focusable="true"> <android.support.v7.widget.AppCompatImageView style="@style/Widget.CoreSettings.PrimaryItemIcon" @@ -254,5 +188,19 @@ style="@style/Widget.CoreSettings.PrimaryItemSwitch" android:text="@string/settings_networkconfig_title" /> </LinearLayout> + + <LinearLayout + android:id="@+id/coreinfo" + style="@style/Widget.CoreSettings.PrimaryItemGroupHeader" + android:focusable="true"> + + <android.support.v7.widget.AppCompatImageView + style="@style/Widget.CoreSettings.PrimaryItemIcon" + app:srcCompat="@drawable/ic_info" /> + + <TextView + style="@style/Widget.CoreSettings.PrimaryItemSwitch" + android:text="@string/label_info_core" /> + </LinearLayout> </LinearLayout> </android.support.v4.widget.NestedScrollView> diff --git a/app/src/main/res/layout/widget_client.xml b/app/src/main/res/layout/widget_client.xml new file mode 100644 index 000000000..8dc71aac3 --- /dev/null +++ b/app/src/main/res/layout/widget_client.xml @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + style="@style/Widget.Info.Item" + android:orientation="horizontal"> + + <LinearLayout + android:layout_width="0dip" + android:layout_height="wrap_content" + android:layout_weight="1" + android:orientation="vertical"> + + <TextView + android:id="@+id/ip" + style="@style/Widget.Info.Item.Content" + tools:text="51.15.1.223" /> + + <TextView + android:id="@+id/version" + style="@style/Widget.Info.Item.Description" + tools:text="Quasseldroid v0.4.0-218-g0e6e562c" /> + + <TextView + android:id="@+id/uptime" + style="@style/Widget.Info.Item.Description" + tools:text="Connected since 08.06.18 01:39" /> + + <TextView + android:id="@+id/location" + style="@style/Widget.Info.Item.Description" + tools:text="Kiel, Germany" /> + + <Button + android:id="@+id/disconnect" + style="@style/Widget.Button.Borderless.Colored" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/label_disconnect" /> + </LinearLayout> + + <android.support.v7.widget.AppCompatImageView + android:id="@+id/secure_icon" + android:layout_width="24dp" + android:layout_height="24dp" + android:layout_gravity="top" + android:layout_marginLeft="16dp" + android:layout_marginStart="16dp" + app:srcCompat="@drawable/ic_lock" /> +</LinearLayout> diff --git a/app/src/main/res/values-v17/styles_widgets.xml b/app/src/main/res/values-v17/styles_widgets.xml index 5bdd3b9c1..879904ac4 100644 --- a/app/src/main/res/values-v17/styles_widgets.xml +++ b/app/src/main/res/values-v17/styles_widgets.xml @@ -34,6 +34,26 @@ <item name="android:paddingRight">?listPreferredItemPaddingRight</item> </style> + <style name="Widget.CoreSettings.PrimaryItemGroupHeader" parent=""> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:background">?selectableItemBackground</item> + <item name="android:gravity">center_vertical</item> + <item name="android:minHeight">?listPreferredItemHeightSmall</item> + <item name="android:paddingEnd">?listPreferredItemPaddingRight</item> + <item name="android:paddingLeft">?listPreferredItemPaddingLeft</item> + <item name="android:paddingRight">?listPreferredItemPaddingRight</item> + <item name="android:paddingStart">?listPreferredItemPaddingLeft</item> + </style> + + <style name="Widget.CoreSettings.PrimaryItemGroup" parent=""> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_marginLeft">72dp</item> + <item name="android:layout_marginStart">72dp</item> + <item name="android:orientation">vertical</item> + </style> + <style name="Widget.CoreSettings.PrimaryItemIcon" parent=""> <item name="android:layout_width">24dp</item> <item name="android:layout_height">24dp</item> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 866485c57..2fd0242cf 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -57,6 +57,7 @@ <string name="label_ignore">Ignore</string> <string name="label_ignore_long">Add/remove user to/from ignore list</string> <string name="label_info_channel">Channel Details</string> + <string name="label_info_core">Core Details</string> <string name="label_info_user">User Details</string> <string name="label_input_history">Input History</string> <string name="label_join">Join</string> diff --git a/app/src/main/res/values/strings_info.xml b/app/src/main/res/values/strings_info.xml index 08e2604d1..d5fffa90c 100644 --- a/app/src/main/res/values/strings_info.xml +++ b/app/src/main/res/values/strings_info.xml @@ -26,6 +26,16 @@ <string name="label_user_host">Host</string> <string name="label_user_server">Server</string> + <string name="label_core_version">Version</string> + <string name="label_core_uptime">Uptime</string> + <string name="label_core_security">Security</string> + <string name="label_core_clients">Clients</string> + <string name="label_core_online_since">Online since %1$s</string> + <string name="label_core_connected_since">Connected since %1$s</string> + <string name="label_core_connection_verified_by">Connection verified by %1$s</string> + <string name="label_core_connection_verified_by_unknown">Unknown</string> + <string name="label_core_connection_insecure">Insecure Connection</string> + <string name="property_group_ircchannel_channel">Channel</string> <string name="property_ircchannel_topic">Topic</string> <string name="property_ircchannel_topic_action_edit">Edit Topic</string> diff --git a/app/src/main/res/values/styles_widgets.xml b/app/src/main/res/values/styles_widgets.xml index bedd3bf6d..ccb58f75e 100644 --- a/app/src/main/res/values/styles_widgets.xml +++ b/app/src/main/res/values/styles_widgets.xml @@ -127,6 +127,23 @@ <item name="android:paddingRight">?listPreferredItemPaddingRight</item> </style> + <style name="Widget.CoreSettings.PrimaryItemGroupHeader" parent=""> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:background">?selectableItemBackground</item> + <item name="android:gravity">center_vertical</item> + <item name="android:minHeight">?listPreferredItemHeightSmall</item> + <item name="android:paddingLeft">?listPreferredItemPaddingLeft</item> + <item name="android:paddingRight">?listPreferredItemPaddingRight</item> + </style> + + <style name="Widget.CoreSettings.PrimaryItemGroup" parent=""> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_marginLeft">72dp</item> + <item name="android:orientation">vertical</item> + </style> + <style name="Widget.CoreSettings.PrimaryItem" parent=""> <item name="android:layout_width">match_parent</item> <item name="android:layout_height">wrap_content</item> -- GitLab