/* * Nextcloud Talk - Android Client * * SPDX-FileCopyrightText: 2023 Marcel Hibbe * SPDX-FileCopyrightText: 2023 Andy Scherzinger * SPDX-FileCopyrightText: 2017-2019 Mario Danic * SPDX-License-Identifier: GPL-3.0-or-later */ package com.nextcloud.talk.activities import android.annotation.SuppressLint import android.content.Context import android.os.Build import android.os.Bundle import android.util.Log import android.view.View import android.view.ViewGroup import android.view.WindowManager import android.view.inputmethod.EditorInfo import android.webkit.SslErrorHandler import android.widget.EditText import androidx.annotation.RequiresApi import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import androidx.core.content.res.ResourcesCompat import autodagger.AutoInjector import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.nextcloud.talk.R import com.nextcloud.talk.account.AccountVerificationActivity import com.nextcloud.talk.account.ServerSelectionActivity import com.nextcloud.talk.account.SwitchAccountActivity import com.nextcloud.talk.account.WebViewLoginActivity import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.events.CertificateEvent import com.nextcloud.talk.ui.theme.ViewThemeUtils import com.nextcloud.talk.utils.DisplayUtils import com.nextcloud.talk.utils.preferences.AppPreferences import com.nextcloud.talk.utils.ssl.TrustManager import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode import java.security.cert.CertificateParsingException import java.security.cert.X509Certificate import java.text.DateFormat import javax.inject.Inject @AutoInjector(NextcloudTalkApplication::class) open class BaseActivity : AppCompatActivity() { enum class AppBarLayoutType { TOOLBAR, SEARCH_BAR, EMPTY } @Inject lateinit var eventBus: EventBus @Inject lateinit var appPreferences: AppPreferences @Inject lateinit var viewThemeUtils: ViewThemeUtils @Inject lateinit var context: Context open val appBarLayoutType: AppBarLayoutType get() = AppBarLayoutType.TOOLBAR open val view: View? get() = null override fun onCreate(savedInstanceState: Bundle?) { NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) super.onCreate(savedInstanceState) cleanTempCertPreference() } public override fun onStart() { super.onStart() eventBus.register(this) } public override fun onResume() { super.onResume() if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && appPreferences.isKeyboardIncognito) { val viewGroup = (findViewById(android.R.id.content) as ViewGroup).getChildAt(0) as ViewGroup disableKeyboardPersonalisedLearning(viewGroup) } if (appPreferences.isScreenSecured || appPreferences.isScreenLocked) { window.addFlags(WindowManager.LayoutParams.FLAG_SECURE) } else { window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE) } } public override fun onStop() { super.onStop() eventBus.unregister(this) } fun setupSystemColors() { colorizeStatusBar() colorizeNavigationBar() } fun setupPrimaryColors() { if (resources != null) { DisplayUtils.applyColorToStatusBar( this, ResourcesCompat.getColor(resources!!, R.color.colorPrimary, null) ) DisplayUtils.applyColorToNavigationBar( window, ResourcesCompat.getColor(resources!!, R.color.colorPrimary, null) ) } } open fun colorizeStatusBar() { if (resources != null) { if (appBarLayoutType == AppBarLayoutType.SEARCH_BAR) { viewThemeUtils.platform.resetStatusBar(this) } else { viewThemeUtils.platform.themeStatusBar(this) } } } fun colorizeNavigationBar() { if (resources != null) { DisplayUtils.applyColorToNavigationBar( this.window, ResourcesCompat.getColor(resources, R.color.bg_default, null) ) } } @RequiresApi(api = Build.VERSION_CODES.O) private fun disableKeyboardPersonalisedLearning(viewGroup: ViewGroup) { var view: View? var editText: EditText for (i in 0 until viewGroup.childCount) { view = viewGroup.getChildAt(i) if (view is EditText) { editText = view editText.imeOptions = editText.imeOptions or EditorInfo.IME_FLAG_NO_PERSONALIZED_LEARNING } else if (view is ViewGroup) { disableKeyboardPersonalisedLearning(view) } } } @Suppress("Detekt.NestedBlockDepth") private fun showCertificateDialog( cert: X509Certificate, trustManager: TrustManager, sslErrorHandler: SslErrorHandler? ) { val formatter = DateFormat.getDateInstance(DateFormat.LONG) val validFrom = formatter.format(cert.notBefore) val validUntil = formatter.format(cert.notAfter) val issuedBy = cert.issuerDN.toString() val issuedFor: String try { if (cert.subjectAlternativeNames != null) { val stringBuilder = StringBuilder() for (o in cert.subjectAlternativeNames) { val list = o as List<*> val type = list[0] as Int if (type == 2) { val name = list[1] as String stringBuilder.append("[").append(type).append("]").append(name).append(" ") } } issuedFor = stringBuilder.toString() } else { issuedFor = cert.subjectDN.name } @SuppressLint("StringFormatMatches") val dialogText = String.format( resources.getString(R.string.nc_certificate_dialog_text), issuedBy, issuedFor, validFrom, validUntil ) val dialogBuilder = MaterialAlertDialogBuilder(this).setIcon( viewThemeUtils.dialog.colorMaterialAlertDialogIcon( context, R.drawable.ic_security_white_24dp ) ).setTitle(R.string.nc_certificate_dialog_title) .setMessage(dialogText) .setPositiveButton(R.string.nc_yes) { _, _ -> trustManager.addCertInTrustStore(cert) sslErrorHandler?.proceed() }.setNegativeButton(R.string.nc_no) { _, _ -> sslErrorHandler?.cancel() } viewThemeUtils.dialog.colorMaterialAlertDialogBackground(context, dialogBuilder) val dialog = dialogBuilder.show() viewThemeUtils.platform.colorTextButtons( dialog.getButton(AlertDialog.BUTTON_POSITIVE), dialog.getButton(AlertDialog.BUTTON_NEGATIVE) ) } catch (e: CertificateParsingException) { Log.d(TAG, "Failed to parse the certificate") } } private fun cleanTempCertPreference() { val temporaryClassNames: MutableList = ArrayList() temporaryClassNames.add(ServerSelectionActivity::class.java.name) temporaryClassNames.add(AccountVerificationActivity::class.java.name) temporaryClassNames.add(WebViewLoginActivity::class.java.name) temporaryClassNames.add(SwitchAccountActivity::class.java.name) if (!temporaryClassNames.contains(javaClass.name)) { appPreferences.removeTemporaryClientCertAlias() } } @Subscribe(threadMode = ThreadMode.MAIN) fun onMessageEvent(event: CertificateEvent) { showCertificateDialog(event.x509Certificate, event.magicTrustManager, event.sslErrorHandler) } companion object { private val TAG = BaseActivity::class.java.simpleName } }