Add a simple log viewer inside the application.

This commit is contained in:
Julien Wadel 2022-06-09 17:02:32 +02:00
parent cc9099752f
commit fd8747e4aa
24 changed files with 280 additions and 90 deletions

View file

@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Video conference.
- Log viewer.
- Option to set the display name in "using an account" tab of assistant.
### Fixed
- Crash on exit.

View file

@ -2023,6 +2023,10 @@ Klik her: <a href="%1">%1</a>
<extracomment>&apos;No Plugins to load&apos; : Text in combobox</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>viewlogs</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SettingsAudio</name>

View file

@ -2023,6 +2023,10 @@ Klicken Sie hier: &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<extracomment>&apos;No Plugins to load&apos; : Text in combobox</extracomment>
<translation>Keine Plugins zu laden</translation>
</message>
<message>
<source>viewlogs</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SettingsAudio</name>

View file

@ -2023,6 +2023,10 @@ Click here: &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<extracomment>&apos;No Plugins to load&apos; : Text in combobox</extracomment>
<translation>No Plugins to load</translation>
</message>
<message>
<source>viewlogs</source>
<translation>VIEW</translation>
</message>
</context>
<context>
<name>SettingsAudio</name>

View file

@ -2023,6 +2023,10 @@ Haga clic aquí: &lt;a href=&quot;%1&quot;&gt;%1 &lt;/a&gt;
<extracomment>&apos;No Plugins to load&apos; : Text in combobox</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>viewlogs</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SettingsAudio</name>

View file

@ -2023,6 +2023,10 @@ Cliquez ici : &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<extracomment>&apos;No Plugins to load&apos; : Text in combobox</extracomment>
<translation>Pas de plugin à charger</translation>
</message>
<message>
<source>viewlogs</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SettingsAudio</name>

View file

@ -2011,6 +2011,10 @@ Kattintson ide: &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<extracomment>&apos;No Plugins to load&apos; : Text in combobox</extracomment>
<translation>Nincsenek betölthető beépülő modulok</translation>
</message>
<message>
<source>viewlogs</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SettingsAudio</name>

View file

@ -2023,6 +2023,10 @@ Clicca: &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<extracomment>&apos;No Plugins to load&apos; : Text in combobox</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>viewlogs</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SettingsAudio</name>

View file

@ -2011,6 +2011,10 @@
<extracomment>&apos;No Plugins to load&apos; : Text in combobox</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>viewlogs</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SettingsAudio</name>

View file

@ -2035,6 +2035,10 @@ Spustelėkite čia: &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<extracomment>&apos;No Plugins to load&apos; : Text in combobox</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>viewlogs</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SettingsAudio</name>

View file

@ -2023,6 +2023,10 @@ Clique aqui: &lt;a href=&quot;%1&quot;&gt;%1 &lt;/a&gt;
<extracomment>&apos;No Plugins to load&apos; : Text in combobox</extracomment>
<translation>Nenhum plug-in para carregar</translation>
</message>
<message>
<source>viewlogs</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SettingsAudio</name>

View file

@ -2035,6 +2035,10 @@
<extracomment>&apos;No Plugins to load&apos; : Text in combobox</extracomment>
<translation>Нет плагинов для загрузки</translation>
</message>
<message>
<source>viewlogs</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SettingsAudio</name>

View file

@ -2023,6 +2023,10 @@ Klicka här: &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<extracomment>&apos;No Plugins to load&apos; : Text in combobox</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>viewlogs</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SettingsAudio</name>

View file

@ -2011,6 +2011,10 @@ Buraya tıklayın: &lt;a href=&quot;%1&quot;&gt;%1&lt;/a&gt;
<extracomment>&apos;No Plugins to load&apos; : Text in combobox</extracomment>
<translation>Yüklenecek eklenti yok</translation>
</message>
<message>
<source>viewlogs</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SettingsAudio</name>

View file

@ -2035,6 +2035,10 @@
<extracomment>&apos;No Plugins to load&apos; : Text in combobox</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<source>viewlogs</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SettingsAudio</name>

View file

@ -2011,6 +2011,10 @@
<extracomment>&apos;No Plugins to load&apos; : Text in combobox</extracomment>
<translation></translation>
</message>
<message>
<source>viewlogs</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SettingsAudio</name>

View file

@ -209,3 +209,19 @@ void Logger::init (const shared_ptr<linphone::Config> &config) {
mInstance->enable(SettingsModel::getLogsEnabled(config));
}
QString Logger::getLogText()const{
QDir path = QString::fromStdString(linphone::Core::getLogCollectionPath());
QString prefix = QString::fromStdString(linphone::Core::getLogCollectionPrefix());
auto files = path.entryInfoList(QStringList(prefix+"*.log"), QDir::Files | QDir::NoSymLinks | QDir::Readable, QDir::Time);
QString result;
for(auto fileInfo : files){
QFile file(fileInfo.filePath());
if (file.open(QIODevice::ReadOnly)) {
QByteArray arr = file.readAll();
result += QString::fromLatin1(arr);
file.close();
}
}
return result;
}

View file

@ -43,8 +43,9 @@ public:
}
void enable (bool status);
QString getLogText()const;
static void init (const std::shared_ptr<linphone::Config> &config);
static void init (const std::shared_ptr<linphone::Config> &config);
static Logger *getInstance () {
return mInstance;

View file

@ -1462,6 +1462,10 @@ void SettingsModel::accessAdvancedSettings() {
//------------------------------------------------------------------------------
QString SettingsModel::getLogText()const{
return Logger::getInstance()->getLogText();
}
QString SettingsModel::getLogsFolder () const {
return getLogsFolder(mConfig);
}

View file

@ -543,6 +543,8 @@ public:
void accessAdvancedSettings();
Q_INVOKABLE QString getLogText()const;
QString getLogsFolder () const;
void setLogsFolder (const QString &folder);

View file

@ -16,7 +16,9 @@ import 'SettingsAdvanced.js' as Logic
// =============================================================================
TabContainer {
id: mainItem
color: "#00000000"
signal showLogs()
Column {
id: column
spacing: SettingsWindowStyle.forms.spacing
@ -72,6 +74,14 @@ TabContainer {
anchors.right: parent.right
spacing: SettingsAdvancedStyle.buttons.spacing
TextButtonB {
text: qsTr('viewlogs')
onClicked: {
mainItem.showLogs()
}
}
TextButtonB {
text: qsTr('cleanLogs')

View file

@ -2,6 +2,7 @@ import QtQuick 2.7
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
import Clipboard 1.0
import Common 1.0
import Common.Styles 1.0
import Konami 1.0
@ -45,107 +46,173 @@ ApplicationWindow {
// -------------------------------------------------------------------------
// Navigation bar.
// -------------------------------------------------------------------------
RowLayout {
Item{
Layout.fillWidth: true
spacing: 0
TabBar {
id: tabBar
onCurrentIndexChanged: SettingsModel.onSettingsTabChanged(currentIndex)
TabButton {
iconName: TabButtonStyle.icon.sipAccountsIcon
text: qsTr('sipAccountsTab')
width: implicitWidth
}
TabButton {
iconName: TabButtonStyle.icon.audioIcon
text: qsTr('audioTab')
width: implicitWidth
}
TabButton {
enabled: SettingsModel.videoSupported
iconName: TabButtonStyle.icon.videoIcon
text: qsTr('videoTab')
width: implicitWidth
}
TabButton {
iconName: TabButtonStyle.icon.callIcon
text: qsTr('callsAndChatTab')
width: implicitWidth
}
TabButton {
enabled: SettingsModel.showNetworkSettings || SettingsModel.developerSettingsEnabled
iconName: TabButtonStyle.icon.networkIcon
text: qsTr('networkTab')
width: implicitWidth
}
TabButton {
visible: SettingsModel.tunnelAvailable()
enabled: visible
iconName: TabButtonStyle.icon.sipAccountsIcon
//: 'Tunnel' : Tab title for tunnel section in settings.
text: qsTr('tunnelTab')
width: visible ? implicitWidth : 0
}
TabButton {
iconName: TabButtonStyle.icon.advancedIcon
text: qsTr('uiTab')
width: implicitWidth
}
TabButton {
iconName: TabButtonStyle.icon.advancedIcon
text: qsTr('uiAdvanced')
width: implicitWidth
}
}
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: TabButtonStyle.text.height
color: TabButtonStyle.backgroundColor.normal
MouseArea {
anchors.fill: parent
Layout.preferredHeight: TabButtonStyle.text.height
RowLayout {
anchors.fill: parent
spacing: 0
TabBar {
id: tabBar
onClicked: konami.forceActiveFocus()
cursorShape: Qt.ArrowCursor
onCurrentIndexChanged: SettingsModel.onSettingsTabChanged(currentIndex)
Konami {
id: konami
onTriggered: SettingsModel.developerSettingsEnabled = true
TabButton {
iconName: TabButtonStyle.icon.sipAccountsIcon
text: qsTr('sipAccountsTab')
width: implicitWidth
}
TabButton {
iconName: TabButtonStyle.icon.audioIcon
text: qsTr('audioTab')
width: implicitWidth
}
TabButton {
enabled: SettingsModel.videoSupported
iconName: TabButtonStyle.icon.videoIcon
text: qsTr('videoTab')
width: implicitWidth
}
TabButton {
iconName: TabButtonStyle.icon.callIcon
text: qsTr('callsAndChatTab')
width: implicitWidth
}
TabButton {
enabled: SettingsModel.showNetworkSettings || SettingsModel.developerSettingsEnabled
iconName: TabButtonStyle.icon.networkIcon
text: qsTr('networkTab')
width: implicitWidth
}
TabButton {
visible: SettingsModel.tunnelAvailable()
enabled: visible
iconName: TabButtonStyle.icon.sipAccountsIcon
//: 'Tunnel' : Tab title for tunnel section in settings.
text: qsTr('tunnelTab')
width: visible ? implicitWidth : 0
}
TabButton {
iconName: TabButtonStyle.icon.advancedIcon
text: qsTr('uiTab')
width: implicitWidth
}
TabButton {
iconName: TabButtonStyle.icon.advancedIcon
text: qsTr('uiAdvanced')
width: implicitWidth
}
}
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: TabButtonStyle.text.height
color: TabButtonStyle.backgroundColor.normal
MouseArea {
anchors.fill: parent
onClicked: konami.forceActiveFocus()
cursorShape: Qt.ArrowCursor
Konami {
id: konami
onTriggered: SettingsModel.developerSettingsEnabled = true
}
}
}
}
Rectangle{
id: hideBar
anchors.fill: parent
color: TabButtonStyle.backgroundColor.normal
visible: logViewer.active
}
}
// -------------------------------------------------------------------------
// Content.
// -------------------------------------------------------------------------
StackLayout {
Item{
Layout.fillHeight: true
Layout.fillWidth: true
currentIndex: tabBar.currentIndex
SettingsSipAccounts {}
SettingsAudio {}
SettingsVideo {}
SettingsCallsChat {}
SettingsNetwork {}
SettingsTunnel {}
SettingsUi {}
SettingsAdvanced {}
StackLayout {
anchors.fill: parent
currentIndex: tabBar.currentIndex
SettingsSipAccounts {}
SettingsAudio {}
SettingsVideo {}
SettingsCallsChat {}
SettingsNetwork {}
SettingsTunnel {}
SettingsUi {}
SettingsAdvanced {onShowLogs: logViewer.active=true }
}
Loader{
id: logViewer
anchors.fill: parent
active: false
sourceComponent: Component{
Rectangle{
id: logBackground
anchors.fill: parent
property variant stringList: null
function updateText() {
stringList = SettingsModel.getLogText().split('\n')
idContentListView.positionViewAtEnd()
}
Component.onCompleted: updateText()
ColumnLayout{
anchors.fill: parent
RowLayout{// Prepare for other actions
ActionButton{
Layout.topMargin: 5
Layout.leftMargin: 5
backgroundRadius: width/2
isCustom: true
colorSet: SettingsWindowStyle.buttons.back
onClicked: logViewer.active = false
}
ActionButton{
Layout.topMargin: 5
Layout.leftMargin: 5
backgroundRadius: width/2
isCustom: true
colorSet: SettingsWindowStyle.buttons.copy
onClicked: {updating = true ; Clipboard.text = SettingsModel.getLogText();updating=false}
}
}
ListView {
id: idContentListView
model: logBackground.stringList
Layout.fillHeight: true
Layout.fillWidth: true
Layout.topMargin: 20
Layout.leftMargin: 10
Layout.rightMargin: 10
delegate: Text {
width: idContentListView.width
text: model.modelData
font.pointSize: FormTableStyle.entry.text.pointSize
textFormat: Text.PlainText
wrapMode: Text.Wrap
}
ScrollBar.vertical: ScrollBar {}
}
}
}
}
}
}
// -------------------------------------------------------------------------

View file

@ -54,5 +54,33 @@ QtObject {
property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_h_b_fg').color
property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'me_p_b_fg').color
}
property QtObject back: QtObject {
property int iconSize: 35
property string icon : 'back_custom'
property string name : 'back'
property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color
property color backgroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_d', icon, 's_d_b_bg').color
property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color
property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color
property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color
property color foregroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_d', icon, 's_d_b_fg').color
property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color
property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color
}
property QtObject copy: QtObject {
property int iconSize: 30
property string icon : 'copy_custom'
property string name : 'copy'
property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'l_n_b_bg').color
property color backgroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_d', icon, 'l_d_b_bg').color
property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'l_h_b_bg').color
property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'l_p_b_bg').color
property color backgroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_u', icon, 'l_p_b_bg').color
property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'l_n_b_fg').color
property color foregroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_d', icon, 'l_d_b_fg').color
property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'l_h_b_fg').color
property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'l_p_b_fg').color
property color foregroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_u', icon, 'l_p_b_fg').color
}
}
}

@ -1 +1 @@
Subproject commit 9542d335ea525de66d7b5519bac4e1ba808caa3a
Subproject commit 1355eaf8e34f3505f3812743bd2232e01558fde2