Added help layout

This commit is contained in:
Sylvain Berfini 2023-09-13 11:52:12 +02:00
parent 27063c9e45
commit 24a5ae8122
19 changed files with 757 additions and 37 deletions

View file

@ -70,7 +70,9 @@ class LinphoneApplication : Application(), ImageLoaderFactory {
val appName = context.getString(R.string.app_name)
Factory.instance().setLoggerDomain(appName)
Factory.instance().enableLogcatLogs(true)
Factory.instance().loggingService.setLogLevel(LogLevel.Message)
if (corePreferences.debugLogs) {
Factory.instance().loggingService.setLogLevel(LogLevel.Message)
}
coreContext = CoreContext(context)
coreContext.start()

View file

@ -41,6 +41,13 @@ class CorePreferences @UiThread constructor(private val context: Context) {
_config = value
}
@get:WorkerThread @set:WorkerThread
var debugLogs: Boolean
get() = config.getBool("app", "debug", org.linphone.BuildConfig.DEBUG)
set(value) {
config.setBool("app", "debug", value)
}
@get:WorkerThread @set:WorkerThread
var conditionsAndPrivacyPolicyAccepted: Boolean
get() = config.getBool("app", "read_and_agree_terms_and_privacy", false)

View file

@ -83,12 +83,14 @@ class LoginFragment : Fragment() {
}
binding.setForgottenPasswordClickListener {
val url = getString(R.string.users_web_platform)
try {
val url = "https://subscribe.linphone.org"
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
startActivity(browserIntent)
} catch (ise: IllegalStateException) {
Log.e("$TAG Can't start ACTION_VIEW intent, IllegalStateException: $ise")
Log.e(
"$TAG Can't start ACTION_VIEW intent for URL [$url], IllegalStateException: $ise"
)
}
}
@ -199,28 +201,28 @@ class LoginFragment : Fragment() {
model.privacyPolicyClickedEvent.observe(viewLifecycleOwner) {
it.consume {
val browserIntent = Intent(
Intent.ACTION_VIEW,
Uri.parse("https://www.linphone.org/privacy-policy")
)
val url = getString(R.string.privacy_policy_url)
try {
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
startActivity(browserIntent)
} catch (e: Exception) {
Log.e("$TAG Can't start activity: $e")
} catch (ise: IllegalStateException) {
Log.e(
"$TAG Can't start ACTION_VIEW intent for URL [$url], IllegalStateException: $ise"
)
}
}
}
model.generalTermsClickedEvent.observe(viewLifecycleOwner) {
it.consume {
val browserIntent = Intent(
Intent.ACTION_VIEW,
Uri.parse("https://www.linphone.org/general-terms")
)
val url = getString(R.string.terms_and_conditions_url)
try {
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
startActivity(browserIntent)
} catch (e: Exception) {
Log.e("$TAG Can't start activity: $e")
} catch (ise: IllegalStateException) {
Log.e(
"$TAG Can't start ACTION_VIEW intent for URL [$url], IllegalStateException: $ise"
)
}
}
}

View file

@ -85,12 +85,14 @@ class RegisterFragment : Fragment() {
}
binding.setOpenSubscribeWebPageClickListener {
val url = getString(R.string.users_web_platform)
try {
val url = "https://subscribe.linphone.org"
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
startActivity(browserIntent)
} catch (ise: IllegalStateException) {
Log.e("$TAG Can't start ACTION_VIEW intent, IllegalStateException: $ise")
Log.e(
"$TAG Can't start ACTION_VIEW intent for URL [$url], IllegalStateException: $ise"
)
}
}

View file

@ -28,6 +28,7 @@ import android.view.ViewGroup
import androidx.annotation.UiThread
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
import org.linphone.R
import org.linphone.core.tools.Log
import org.linphone.databinding.AssistantThirdPartySipAccountWarningFragmentBinding
@ -58,12 +59,14 @@ class ThirdPartySipAccountWarningFragment : Fragment() {
}
binding.setContactClickListener {
val url = getString(R.string.website_contact_page)
try {
val url = "https://linphone.org/contact"
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
startActivity(browserIntent)
} catch (ise: IllegalStateException) {
Log.e("$TAG Can't start ACTION_VIEW intent, IllegalStateException: $ise")
Log.e(
"$TAG Can't start ACTION_VIEW intent for URL [$url], IllegalStateException: $ise"
)
}
}

View file

@ -24,8 +24,6 @@ import android.annotation.SuppressLint
import android.content.pm.PackageManager
import android.os.Bundle
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.annotation.DrawableRes
import androidx.annotation.UiThread
@ -38,7 +36,7 @@ import androidx.navigation.findNavController
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.R
import org.linphone.databinding.MainActivityBinding
import org.linphone.databinding.ToastBinding
import org.linphone.utils.AppUtils
import org.linphone.utils.slideInToastFromTopForDuration
@UiThread
@ -140,17 +138,7 @@ class MainActivity : AppCompatActivity() {
}
fun showGreenToast(message: String, @DrawableRes icon: Int) {
val greenToast: ToastBinding = DataBindingUtil.inflate(
LayoutInflater.from(this),
R.layout.toast,
binding.toastsArea,
false
)
greenToast.message = message
greenToast.icon = icon
greenToast.shadowColor = R.drawable.shape_toast_green_shadow
greenToast.textColor = R.color.green_online
greenToast.root.visibility = View.GONE
val greenToast = AppUtils.getGreenToast(this, binding.toastsArea, message, icon)
binding.toastsArea.addView(greenToast.root)
greenToast.root.slideInToastFromTopForDuration(
@ -159,6 +147,16 @@ class MainActivity : AppCompatActivity() {
)
}
fun showRedToast(message: String, @DrawableRes icon: Int) {
val redToast = AppUtils.getRedToast(this, binding.toastsArea, message, icon)
binding.toastsArea.addView(redToast.root)
redToast.root.slideInToastFromTopForDuration(
binding.toastsArea as ViewGroup,
lifecycleScope
)
}
private fun loadContacts() {
coreContext.contactsManager.loadContacts(this)

View file

@ -1,17 +1,36 @@
package org.linphone.ui.main.settings.fragment
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.annotation.UiThread
import androidx.navigation.navGraphViewModels
import org.linphone.R
import org.linphone.core.tools.Log
import org.linphone.databinding.HelpFragmentBinding
import org.linphone.ui.main.MainActivity
import org.linphone.ui.main.fragment.GenericFragment
import org.linphone.ui.main.settings.viewmodel.HelpViewModel
import org.linphone.utils.AppUtils
@UiThread
class HelpFragment : GenericFragment() {
companion object {
private const val TAG = "[Help Fragment]"
}
private lateinit var binding: HelpFragmentBinding
val viewModel: HelpViewModel by navGraphViewModels(
R.id.main_nav_graph
)
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
@ -24,9 +43,109 @@ class HelpFragment : GenericFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.lifecycleOwner = viewLifecycleOwner
binding.viewModel = viewModel
binding.setBackClickListener {
goBack()
}
binding.setPrivacyPolicyClickListener {
val url = getString(R.string.privacy_policy_url)
try {
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
startActivity(browserIntent)
} catch (ise: IllegalStateException) {
Log.e(
"$TAG Can't start ACTION_VIEW intent for URL [$url], IllegalStateException: $ise"
)
}
}
binding.setLicensesClickListener {
val url = getString(R.string.open_source_licences_usage_url)
try {
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
startActivity(browserIntent)
} catch (ise: IllegalStateException) {
Log.e(
"$TAG Can't start ACTION_VIEW intent for URL [$url], IllegalStateException: $ise"
)
}
}
binding.setTranslateClickListener {
val url = getString(R.string.translate_weblate_url)
try {
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
startActivity(browserIntent)
} catch (ise: IllegalStateException) {
Log.e(
"$TAG Can't start ACTION_VIEW intent for URL [$url], IllegalStateException: $ise"
)
}
}
viewModel.newVersionAvailableEvent.observe(viewLifecycleOwner) {
it.consume { version ->
(requireActivity() as MainActivity).showGreenToast(
getString(R.string.help_update_available_toast_message, version),
R.drawable.info
)
}
}
viewModel.versionUpToDateEvent.observe(viewLifecycleOwner) {
it.consume {
(requireActivity() as MainActivity).showGreenToast(
getString(R.string.help_version_up_to_date_toast_message),
R.drawable.info
)
}
}
viewModel.errorEvent.observe(viewLifecycleOwner) {
it.consume {
(requireActivity() as MainActivity).showRedToast(
getString(R.string.help_error_checking_version_toast_message),
R.drawable.warning_circle
)
}
}
viewModel.debugLogsCleanedEvent.observe(viewLifecycleOwner) {
it.consume {
(requireActivity() as MainActivity).showGreenToast(
getString(R.string.help_advanced_debug_logs_cleaned_toast_message),
R.drawable.info
)
}
}
viewModel.uploadDebugLogsFinishedEvent.observe(viewLifecycleOwner) {
it.consume { url ->
val clipboard =
requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clip = ClipData.newPlainText("Logs url", url)
clipboard.setPrimaryClip(clip)
(requireActivity() as MainActivity).showGreenToast(
getString(
R.string.help_advanced_debug_logs_url_copied_into_clipboard_toast_message
),
R.drawable.info
)
AppUtils.shareUploadedLogsUrl(requireActivity(), url)
}
}
viewModel.uploadDebugLogsErrorEvent.observe(viewLifecycleOwner) {
it.consume {
(requireActivity() as MainActivity).showRedToast(
getString(R.string.help_advanced_debug_logs_upload_error_toast_message),
R.drawable.warning_circle
)
}
}
}
}

View file

@ -0,0 +1,172 @@
/*
* Copyright (c) 2010-2023 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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 org.linphone.ui.main.settings.viewmodel
import androidx.annotation.UiThread
import androidx.annotation.WorkerThread
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import org.linphone.BuildConfig
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.LinphoneApplication.Companion.corePreferences
import org.linphone.core.Core
import org.linphone.core.CoreListenerStub
import org.linphone.core.Factory
import org.linphone.core.LogLevel
import org.linphone.core.VersionUpdateCheckResult
import org.linphone.core.tools.Log
import org.linphone.utils.Event
class HelpViewModel @UiThread constructor() : ViewModel() {
companion object {
private const val TAG = "[Help ViewModel]"
}
val version = MutableLiveData<String>()
val debugModeEnabled = MutableLiveData<Boolean>()
val newVersionAvailableEvent: MutableLiveData<Event<String>> by lazy {
MutableLiveData<Event<String>>()
}
val versionUpToDateEvent: MutableLiveData<Event<Boolean>> by lazy {
MutableLiveData<Event<Boolean>>()
}
val errorEvent: MutableLiveData<Event<Boolean>> by lazy {
MutableLiveData<Event<Boolean>>()
}
val debugLogsCleanedEvent: MutableLiveData<Event<Boolean>> by lazy {
MutableLiveData<Event<Boolean>>()
}
val uploadDebugLogsFinishedEvent: MutableLiveData<Event<String>> by lazy {
MutableLiveData<Event<String>>()
}
val uploadDebugLogsErrorEvent: MutableLiveData<Event<Boolean>> by lazy {
MutableLiveData<Event<Boolean>>()
}
private val coreListener = object : CoreListenerStub() {
@WorkerThread
override fun onVersionUpdateCheckResultReceived(
core: Core,
result: VersionUpdateCheckResult,
version: String?,
url: String?
) {
when (result) {
VersionUpdateCheckResult.NewVersionAvailable -> {
Log.i("$TAG Update available, version [$version], url [$url]")
if (!version.isNullOrEmpty()) {
newVersionAvailableEvent.postValue(Event(version))
}
}
VersionUpdateCheckResult.UpToDate -> {
Log.i("$TAG This version is up-to-date")
versionUpToDateEvent.postValue(Event(true))
}
else -> {
Log.e("$TAG Can't check for update, an error happened [$result]")
errorEvent.postValue(Event(true))
}
}
}
@WorkerThread
override fun onLogCollectionUploadStateChanged(
core: Core,
state: Core.LogCollectionUploadState,
info: String
) {
Log.i("$TAG Logs upload state changed [$state]")
if (state == Core.LogCollectionUploadState.Delivered) {
uploadDebugLogsFinishedEvent.postValue(Event(info))
} else if (state == Core.LogCollectionUploadState.NotDelivered) {
uploadDebugLogsErrorEvent.postValue(Event(true))
}
}
}
init {
val currentVersion = BuildConfig.VERSION_NAME
version.value = currentVersion
coreContext.postOnCoreThread { core ->
core.addListener(coreListener)
debugModeEnabled.postValue(corePreferences.debugLogs)
}
}
@UiThread
override fun onCleared() {
super.onCleared()
coreContext.postOnCoreThread { core ->
core.removeListener(coreListener)
}
}
@UiThread
fun toggleDebugMode() {
val enabled = debugModeEnabled.value == false
debugModeEnabled.value = enabled
if (!enabled) {
cleanLogs()
}
coreContext.postOnCoreThread {
corePreferences.debugLogs = enabled
val logLevel = if (enabled) LogLevel.Message else LogLevel.Error
Factory.instance().loggingService.setLogLevel(logLevel)
Log.i("$TAG Debug logs have been ${if (enabled) "enabled" else "disabled"}")
}
}
@UiThread
fun cleanLogs() {
coreContext.postOnCoreThread { core ->
core.resetLogCollection()
Log.i("$TAG Debug logs have been cleaned")
debugLogsCleanedEvent.postValue(Event(true))
}
}
@UiThread
fun shareLogs() {
coreContext.postOnCoreThread { core ->
Log.i("$TAG Uploading debug logs for sharing")
core.uploadLogCollection()
}
}
@UiThread
fun checkForUpdate() {
val currentVersion = version.value.orEmpty()
coreContext.postOnCoreThread { core ->
Log.i("[$TAG] Checking for update using current version [$currentVersion]")
core.checkForUpdate(currentVersion)
}
}
}

View file

@ -20,7 +20,9 @@
package org.linphone.utils
import android.app.Activity
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
import android.util.DisplayMetrics
import android.util.Rational
import android.view.LayoutInflater
@ -34,6 +36,7 @@ import androidx.annotation.StringRes
import androidx.databinding.DataBindingUtil
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.R
import org.linphone.core.tools.Log
import org.linphone.databinding.ToastBinding
class AppUtils {
@ -151,5 +154,29 @@ class AppUtils {
blueToast.root.visibility = View.GONE
return blueToast
}
@AnyThread
fun shareUploadedLogsUrl(activity: Activity, info: String) {
val appName = activity.getString(R.string.app_name)
val intent = Intent(Intent.ACTION_SEND)
intent.putExtra(
Intent.EXTRA_EMAIL,
arrayOf(activity.getString(R.string.help_advanced_send_debug_logs_email_address))
)
intent.putExtra(Intent.EXTRA_SUBJECT, "$appName Logs")
intent.putExtra(Intent.EXTRA_TEXT, info)
intent.type = "text/plain"
try {
activity.startActivity(
Intent.createChooser(
intent,
activity.getString(R.string.help_advanced_share_logs_dialog_title)
)
)
} catch (ex: ActivityNotFoundException) {
Log.e(ex)
}
}
}
}

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="256"
android:viewportHeight="256">
<path
android:pathData="M213.66,82.34l-56,-56A8,8 0,0 0,152 24L56,24A16,16 0,0 0,40 40L40,216a16,16 0,0 0,16 16L200,232a16,16 0,0 0,16 -16L216,88A8,8 0,0 0,213.66 82.34ZM160,51.31 L188.69,80L160,80ZM200,216L56,216L56,40h88L144,88a8,8 0,0 0,8 8h48L200,216ZM168,136a8,8 0,0 1,-8 8L96,144a8,8 0,0 1,0 -16h64A8,8 0,0 1,168 136ZM168,168a8,8 0,0 1,-8 8L96,176a8,8 0,0 1,0 -16h64A8,8 0,0 1,168 168Z"
android:fillColor="#4e6074"/>
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="256"
android:viewportHeight="256">
<path
android:pathData="M128,24A104,104 0,1 0,232 128,104.11 104.11,0 0,0 128,24ZM216,128a87.62,87.62 0,0 1,-6.4 32.94l-44.7,-27.49a15.92,15.92 0,0 0,-6.24 -2.23l-22.82,-3.08a16.11,16.11 0,0 0,-16 7.86h-8.72l-3.8,-7.86a15.91,15.91 0,0 0,-11 -8.67l-8,-1.73L96.14,104h16.71a16.06,16.06 0,0 0,7.73 -2l12.25,-6.76a16.62,16.62 0,0 0,3 -2.14l26.91,-24.34A15.93,15.93 0,0 0,166 49.1l-0.36,-0.65A88.11,88.11 0,0 1,216 128ZM143.31,41.34 L152,56.9 125.09,81.24 112.85,88L96.14,88a16,16 0,0 0,-13.88 8l-8.73,15.23L63.38,84.19 74.32,58.32a87.87,87.87 0,0 1,69 -17ZM40,128a87.53,87.53 0,0 1,8.54 -37.8l11.34,30.27a16,16 0,0 0,11.62 10l21.43,4.61L96.74,143a16.09,16.09 0,0 0,14.4 9h1.48l-7.23,16.23a16,16 0,0 0,2.86 17.37l0.14,0.14L128,205.94l-1.94,10A88.11,88.11 0,0 1,40 128ZM142.58,214.78 L143.71,208.97a16.09,16.09 0,0 0,-4 -13.9,1.85 1.85,0 0,1 -0.14,-0.14L120,174.74 133.7,144l22.82,3.08 45.72,28.12A88.18,88.18 0,0 1,142.58 214.78Z"
android:fillColor="#4e6074"/>
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="256"
android:viewportHeight="256">
<path
android:pathData="M226.76,69a8,8 0,0 0,-12.84 -2.88l-40.3,37.19 -17.23,-3.7 -3.7,-17.23 37.19,-40.3A8,8 0,0 0,187 29.24,72 72,0 0,0 88,96 72.34,72.34 0,0 0,94 124.94L33.79,177c-0.15,0.12 -0.29,0.26 -0.43,0.39a32,32 0,0 0,45.26 45.26c0.13,-0.13 0.27,-0.28 0.39,-0.42L131.06,162A72,72 0,0 0,232 96,71.56 71.56,0 0,0 226.76,69ZM160,152a56.14,56.14 0,0 1,-27.07 -7,8 8,0 0,0 -9.92,1.77L67.11,211.51a16,16 0,0 1,-22.62 -22.62L109.18,133a8,8 0,0 0,1.77 -9.93,56 56,0 0,1 58.36,-82.31l-31.2,33.81a8,8 0,0 0,-1.94 7.1L141.83,108a8,8 0,0 0,6.14 6.14l26.35,5.66a8,8 0,0 0,7.1 -1.94l33.81,-31.2A56.06,56.06 0,0 1,160 152Z"
android:fillColor="#4e6074"/>
</vector>

View file

@ -304,7 +304,7 @@
android:layout_height="wrap_content"
android:layout_marginTop="40dp"
android:layout_marginStart="20dp"
android:layout_marginBottom="20dp"
android:layout_marginBottom="32dp"
android:paddingTop="@dimen/primary_secondary_buttons_label_padding"
android:paddingBottom="@dimen/primary_secondary_buttons_label_padding"
android:paddingStart="20dp"
@ -314,6 +314,7 @@
android:textColor="@color/secondary_button_label_color"
android:gravity="center"
android:background="@drawable/secondary_button_background"
app:layout_constraintVertical_bias="1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/no_account_yet"
app:layout_constraintTop_toBottomOf="@id/third_party_sip_account"

View file

@ -33,7 +33,8 @@
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
android:fillViewport="true">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
@ -314,7 +315,7 @@
android:layout_height="wrap_content"
android:layout_marginTop="34dp"
android:layout_marginStart="20dp"
android:layout_marginBottom="20dp"
android:layout_marginBottom="32dp"
android:paddingTop="@dimen/primary_secondary_buttons_label_padding"
android:paddingBottom="@dimen/primary_secondary_buttons_label_padding"
android:paddingStart="20dp"
@ -324,6 +325,7 @@
android:textColor="@color/secondary_button_label_color"
android:gravity="center"
android:background="@drawable/secondary_button_background"
app:layout_constraintVertical_bias="1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/already_an_account"
app:layout_constraintTop_toBottomOf="@id/create_email_account"

View file

@ -245,6 +245,7 @@
android:layout_marginTop="32dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="32dp"
android:paddingTop="@dimen/primary_secondary_buttons_label_padding"
android:paddingBottom="@dimen/primary_secondary_buttons_label_padding"
android:paddingStart="20dp"
@ -255,6 +256,7 @@
android:gravity="center"
android:background="@drawable/primary_button_background"
app:layout_constraintWidth_max="@dimen/button_max_width"
app:layout_constraintVertical_bias="1"
app:layout_constraintTop_toBottomOf="@id/transport"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"

View file

@ -154,7 +154,7 @@
android:layout_marginTop="32dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="20dp"
android:layout_marginBottom="32dp"
android:paddingTop="@dimen/primary_secondary_buttons_label_padding"
android:paddingBottom="@dimen/primary_secondary_buttons_label_padding"
android:paddingStart="20dp"

View file

@ -8,6 +8,18 @@
<variable
name="backClickListener"
type="View.OnClickListener" />
<variable
name="licensesClickListener"
type="View.OnClickListener" />
<variable
name="privacyPolicyClickListener"
type="View.OnClickListener" />
<variable
name="translateClickListener"
type="View.OnClickListener" />
<variable
name="viewModel"
type="org.linphone.ui.main.settings.viewmodel.HelpViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
@ -44,6 +56,319 @@
app:layout_constraintStart_toEndOf="@id/back"
app:layout_constraintTop_toTopOf="parent"/>
<androidx.appcompat.widget.AppCompatTextView
style="@style/default_text_style_800"
android:id="@+id/about_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_marginTop="32dp"
android:text="@string/help_about_title"
android:textSize="16sp"
android:textColor="@color/gray_9"
app:layout_constraintTop_toBottomOf="@id/title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<ImageView
android:onClick="@{privacyPolicyClickListener}"
android:id="@+id/privacy_policy_icon"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_marginStart="16dp"
android:src="@drawable/file_text"
app:tint="@color/primary_color"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/privacy_policy_title"
app:layout_constraintTop_toTopOf="@id/privacy_policy_title"
app:layout_constraintBottom_toBottomOf="@id/privacy_policy_subtitle" />
<androidx.appcompat.widget.AppCompatTextView
style="@style/default_text_style_700"
android:onClick="@{privacyPolicyClickListener}"
android:id="@+id/privacy_policy_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_marginTop="24dp"
android:text="@string/help_about_privacy_policy_title"
android:textSize="13sp"
android:textColor="@color/gray_9"
app:layout_constraintTop_toBottomOf="@id/about_title"
app:layout_constraintStart_toEndOf="@id/privacy_policy_icon"
app:layout_constraintEnd_toEndOf="parent"/>
<androidx.appcompat.widget.AppCompatTextView
style="@style/default_text_style"
android:onClick="@{privacyPolicyClickListener}"
android:id="@+id/privacy_policy_subtitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:text="@string/help_about_privacy_policy_subtitle"
android:textSize="14sp"
android:textColor="@color/gray_9"
app:layout_constraintTop_toBottomOf="@id/privacy_policy_title"
app:layout_constraintStart_toEndOf="@id/privacy_policy_icon"
app:layout_constraintEnd_toEndOf="parent" />
<ImageView
android:id="@+id/version_icon"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_marginStart="16dp"
android:src="@drawable/info"
app:tint="@color/primary_color"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/version_title"
app:layout_constraintTop_toTopOf="@id/version_title"
app:layout_constraintBottom_toBottomOf="@id/version_subtitle" />
<androidx.appcompat.widget.AppCompatTextView
style="@style/default_text_style_700"
android:id="@+id/version_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_marginTop="24dp"
android:text="@string/help_about_version_title"
android:textSize="13sp"
android:textColor="@color/gray_9"
app:layout_constraintTop_toBottomOf="@id/privacy_policy_subtitle"
app:layout_constraintStart_toEndOf="@id/version_icon"
app:layout_constraintEnd_toStartOf="@id/check_for_update"/>
<androidx.appcompat.widget.AppCompatTextView
style="@style/default_text_style"
android:id="@+id/version_subtitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:text="@{viewModel.version, default=`6.0.0`}"
android:textSize="14sp"
android:textColor="@color/gray_9"
app:layout_constraintTop_toBottomOf="@id/version_title"
app:layout_constraintStart_toEndOf="@id/version_icon"
app:layout_constraintEnd_toStartOf="@id/check_for_update" />
<androidx.appcompat.widget.AppCompatTextView
style="@style/default_text_style_600"
android:onClick="@{() -> viewModel.checkForUpdate()}"
android:id="@+id/check_for_update"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="17dp"
android:background="@drawable/tertiary_button_background"
android:paddingStart="12dp"
android:paddingEnd="12dp"
android:paddingTop="6dp"
android:paddingBottom="6dp"
android:gravity="center"
android:text="@string/help_about_check_for_update"
android:textColor="@color/tertiary_button_label_color"
android:textSize="13sp"
android:maxLines="1"
android:ellipsize="end"
app:layout_constraintStart_toEndOf="@id/version_title"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/version_title"
app:layout_constraintBottom_toBottomOf="@id/version_subtitle"/>
<ImageView
android:onClick="@{licensesClickListener}"
android:id="@+id/open_source_licenses_icon"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_marginStart="16dp"
android:src="@drawable/file_text"
app:tint="@color/primary_color"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/open_source_licenses_title"
app:layout_constraintTop_toTopOf="@id/open_source_licenses_title"
app:layout_constraintBottom_toBottomOf="@id/open_source_licenses_subtitle" />
<androidx.appcompat.widget.AppCompatTextView
style="@style/default_text_style_700"
android:onClick="@{licensesClickListener}"
android:id="@+id/open_source_licenses_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_marginTop="24dp"
android:text="@string/help_about_open_source_licenses_title"
android:textSize="13sp"
android:textColor="@color/gray_9"
app:layout_constraintTop_toBottomOf="@id/version_subtitle"
app:layout_constraintStart_toEndOf="@id/open_source_licenses_icon"
app:layout_constraintEnd_toEndOf="parent"/>
<androidx.appcompat.widget.AppCompatTextView
style="@style/default_text_style"
android:onClick="@{licensesClickListener}"
android:id="@+id/open_source_licenses_subtitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:text="@string/help_about_open_source_licenses_subtitle"
android:textSize="14sp"
android:textColor="@color/gray_9"
app:layout_constraintTop_toBottomOf="@id/open_source_licenses_title"
app:layout_constraintStart_toEndOf="@id/open_source_licenses_icon"
app:layout_constraintEnd_toEndOf="parent" />
<ImageView
android:onClick="@{translateClickListener}"
android:id="@+id/translate_icon"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_marginStart="16dp"
android:src="@drawable/globe_hemisphere_west"
app:tint="@color/primary_color"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/translate_title"
app:layout_constraintTop_toTopOf="@id/translate_title"
app:layout_constraintBottom_toBottomOf="@id/translate_title" />
<androidx.appcompat.widget.AppCompatTextView
style="@style/default_text_style_700"
android:onClick="@{translateClickListener}"
android:id="@+id/translate_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_marginTop="24dp"
android:text="@string/help_about_contribute_translations_title"
android:textSize="13sp"
android:textColor="@color/gray_9"
app:layout_constraintTop_toBottomOf="@id/open_source_licenses_subtitle"
app:layout_constraintStart_toEndOf="@id/translate_icon"
app:layout_constraintEnd_toEndOf="parent"/>
<androidx.appcompat.widget.AppCompatTextView
style="@style/default_text_style_800"
android:id="@+id/advanced_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_marginTop="32dp"
android:text="@string/help_about_advanced_title"
android:textSize="16sp"
android:textColor="@color/gray_9"
app:layout_constraintTop_toBottomOf="@id/translate_icon"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<ImageView
android:id="@+id/debug_icon"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_marginStart="16dp"
android:src="@drawable/wrench"
app:tint="@color/primary_color"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/debug_title"
app:layout_constraintTop_toTopOf="@id/debug_title"
app:layout_constraintBottom_toBottomOf="@id/debug_subtitle" />
<androidx.appcompat.widget.AppCompatTextView
style="@style/default_text_style_700"
android:id="@+id/debug_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_marginTop="24dp"
android:text="@string/help_advanced_debug_title"
android:textSize="13sp"
android:textColor="@color/gray_9"
app:layout_constraintTop_toBottomOf="@id/advanced_title"
app:layout_constraintStart_toEndOf="@id/debug_icon"
app:layout_constraintEnd_toStartOf="@id/debug_mode_switch"/>
<androidx.appcompat.widget.AppCompatTextView
style="@style/default_text_style"
android:id="@+id/debug_subtitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:text="@string/help_advanced_debug_subtitle"
android:textSize="14sp"
android:textColor="@color/gray_9"
app:layout_constraintTop_toBottomOf="@id/debug_title"
app:layout_constraintStart_toEndOf="@id/debug_icon"
app:layout_constraintEnd_toStartOf="@id/debug_mode_switch" />
<com.google.android.material.materialswitch.MaterialSwitch
android:onClick="@{() -> viewModel.toggleDebugMode()}"
android:id="@+id/debug_mode_switch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:checked="@{viewModel.debugModeEnabled, default=true}"
app:layout_constraintStart_toEndOf="@id/debug_title"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/debug_title"
app:layout_constraintBottom_toBottomOf="@id/debug_subtitle" />
<androidx.appcompat.widget.AppCompatTextView
style="@style/default_text_style_600"
android:onClick="@{() -> viewModel.cleanLogs()}"
android:id="@+id/clean_logs"
android:enabled="@{viewModel.debugModeEnabled}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_marginTop="32dp"
android:background="@drawable/tertiary_button_background"
android:paddingStart="12dp"
android:paddingEnd="12dp"
android:paddingTop="6dp"
android:paddingBottom="6dp"
android:gravity="center"
android:text="@string/help_advanced_clean_logs"
android:textColor="@color/tertiary_button_label_color"
android:textSize="13sp"
android:maxLines="1"
android:ellipsize="end"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/debug_subtitle"/>
<androidx.appcompat.widget.AppCompatTextView
style="@style/default_text_style_600"
android:onClick="@{() -> viewModel.shareLogs()}"
android:id="@+id/send_logs"
android:enabled="@{viewModel.debugModeEnabled}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_marginTop="32dp"
android:background="@drawable/tertiary_button_background"
android:paddingStart="12dp"
android:paddingEnd="12dp"
android:paddingTop="6dp"
android:paddingBottom="6dp"
android:gravity="center"
android:text="@string/help_advanced_share_logs"
android:textColor="@color/tertiary_button_label_color"
android:textSize="13sp"
android:maxLines="1"
android:ellipsize="end"
app:layout_constraintStart_toEndOf="@id/clean_logs"
app:layout_constraintTop_toBottomOf="@id/debug_subtitle"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View file

@ -28,6 +28,7 @@
<dimen name="top_bar_height">55dp</dimen>
<dimen name="toast_top_margin">70dp</dimen> <!-- 15dp + top_bar_height -->
<dimen name="toast_elevation">5dp</dimen>
<dimen name="in_call_top_bar_text_height">28dp</dimen>
<dimen name="in_call_top_bar_info_height">48dp</dimen> <!-- Size of top text + top & bottom margins -->

View file

@ -14,6 +14,17 @@
<string name="notification_channel_incoming_call_id" translatable="false">linphone_notification_incoming_call_id</string>
<string name="notification_channel_service_id" translatable="false">linphone_notification_service_id</string>
<string name="help_about_open_source_licenses_title" translatable="false">GNU General Public License v3.0</string>
<string name="help_about_open_source_licenses_subtitle" translatable="false">© Belledonne Communications 2010-2023</string>
<string name="help_advanced_send_debug_logs_email_address" translatable="false">linphone-android@belledonne-communications.com</string>
<string name="website_contact_page" translatable="false">https://linphone.org/contact</string>
<string name="privacy_policy_url" translatable="false">https://linphone.org/privacy-policy</string>
<string name="terms_and_conditions_url" translatable="false">https://linphone.org/general-terms</string>
<string name="users_web_platform" translatable="false">https://subscribe.linphone.org</string>
<string name="translate_weblate_url" translatable="false">https://weblate.linphone.org/</string>
<string name="open_source_licences_usage_url" translatable="false">https://wiki.linphone.org/xwiki/wiki/public/view/Linphone/Third%20party%20components%20/#Hlinphone-android</string>
<string name="sip_address">SIP address</string>
<string name="sip_address_display_name">Display name</string>
<string name="sip_address_domain">Domain</string>
@ -106,7 +117,26 @@
<string name="settings_title">Settings</string>
<string name="recordings_title">Recordings</string>
<string name="help_title">Help</string>
<string name="help_about_title">About &appName;</string>
<string name="help_about_privacy_policy_title">Privacy policy</string>
<string name="help_about_privacy_policy_subtitle">What information &appName; collects and uses</string>
<string name="help_about_version_title">Version</string>
<string name="help_about_check_for_update">Check update</string>
<string name="help_about_contribute_translations_title">Contribute on &appName; translation</string>
<string name="help_about_advanced_title">Advanced</string>
<string name="help_advanced_debug_title">Enable/disable debug logs</string>
<string name="help_advanced_debug_subtitle">Blah</string> <!-- TODO -->
<string name="help_advanced_clean_logs">Clean logs</string>
<string name="help_advanced_share_logs">Share logs</string>
<string name="help_advanced_share_logs_dialog_title">Share debug logs link using…</string>
<string name="help_update_available_toast_message">A new version is available: %s</string>
<string name="help_version_up_to_date_toast_message">Your version is up-to-date</string>
<string name="help_error_checking_version_toast_message">An error occurred while checking for update</string>
<string name="help_advanced_debug_logs_cleaned_toast_message">Debug logs have been cleaned</string>
<string name="help_advanced_debug_logs_url_copied_into_clipboard_toast_message">Debug logs URL copied into clipboard</string>
<string name="help_advanced_debug_logs_upload_error_toast_message">Failed to upload debug logs</string>
<string name="manage_account_title">Manage account</string>
<string name="manage_account_details_title">Details</string>