Compare commits
13 Commits
develop
...
feature/ad
Author | SHA1 | Date | |
---|---|---|---|
|
28c9b0cf4b | ||
|
bc6a0a842f | ||
|
51888e176c | ||
|
d403d16731 | ||
|
92c615af31 | ||
|
a8190cf9c1 | ||
|
c773eda3d6 | ||
|
0e633bad46 | ||
|
d689778470 | ||
|
59cb6a03c8 | ||
|
4e9618fd0d | ||
|
b1035a808e | ||
|
0193695aa3 |
@ -33,6 +33,7 @@ buildscript {
|
||||
classpath "org.jetbrains.dokka:dokka-gradle-plugin:1.7.10"
|
||||
classpath "org.jetbrains.kotlinx:kotlinx-knit:0.4.0"
|
||||
classpath 'com.jakewharton:butterknife-gradle-plugin:10.2.3'
|
||||
classpath 'app.cash.paparazzi:paparazzi-gradle-plugin:1.0.0'
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
}
|
||||
|
@ -42,6 +42,7 @@ ext.groups = [
|
||||
regex: [
|
||||
],
|
||||
group: [
|
||||
'app.cash.paparazzi',
|
||||
'ch.qos.logback',
|
||||
'com.adevinta.android',
|
||||
'com.airbnb.android',
|
||||
@ -145,11 +146,14 @@ ext.groups = [
|
||||
'it.unimi.dsi',
|
||||
'jakarta.activation',
|
||||
'jakarta.xml.bind',
|
||||
'javax.activation',
|
||||
'javax.annotation',
|
||||
'javax.inject',
|
||||
'javax.xml.bind',
|
||||
'jline',
|
||||
'jp.wasabeef',
|
||||
'junit',
|
||||
'kxml2',
|
||||
'me.saket',
|
||||
'net.bytebuddy',
|
||||
'net.java',
|
||||
@ -178,11 +182,13 @@ ext.groups = [
|
||||
'org.hamcrest',
|
||||
'org.jacoco',
|
||||
'org.java-websocket',
|
||||
'org.jcodec',
|
||||
'org.jetbrains',
|
||||
'org.jetbrains.dokka',
|
||||
'org.jetbrains.intellij.deps',
|
||||
'org.jetbrains.kotlin',
|
||||
'org.jetbrains.kotlinx',
|
||||
'org.jetbrains.trove4j',
|
||||
'org.json',
|
||||
'org.jsoup',
|
||||
'org.junit',
|
||||
|
@ -12,10 +12,12 @@ org.gradle.jvmargs=-Xmx4g -Xms512M -XX:MaxPermSize=2048m -XX:MaxMetaspaceSize=1g
|
||||
org.gradle.configureondemand=true
|
||||
org.gradle.parallel=true
|
||||
org.gradle.vfs.watch=true
|
||||
org.gradle.caching=true
|
||||
|
||||
# Android Settings
|
||||
android.enableJetifier=true
|
||||
android.useAndroidX=true
|
||||
android.jetifier.ignorelist=android-base-common,common
|
||||
|
||||
#Project Settings
|
||||
# Change debugPrivateData to true for debugging
|
||||
|
@ -341,6 +341,13 @@ android {
|
||||
]
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
// Add sourceSets for `release` version when building `nightly`
|
||||
nightly {
|
||||
java.srcDirs += "src/release/java"
|
||||
}
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
viewBinding true
|
||||
}
|
||||
@ -349,11 +356,39 @@ android {
|
||||
dependencies {
|
||||
implementation project(':vector')
|
||||
implementation project(':vector-config')
|
||||
debugImplementation project(':library:ui-styles')
|
||||
implementation libs.dagger.hilt
|
||||
implementation 'androidx.multidex:multidex:2.0.1'
|
||||
implementation "androidx.sharetarget:sharetarget:1.1.0"
|
||||
|
||||
// Flipper, debug builds only
|
||||
debugImplementation(libs.flipper.flipper) {
|
||||
exclude group: 'com.facebook.fbjni', module: 'fbjni'
|
||||
}
|
||||
debugImplementation(libs.flipper.flipperNetworkPlugin) {
|
||||
exclude group: 'com.facebook.fbjni', module: 'fbjni'
|
||||
}
|
||||
debugImplementation 'com.facebook.soloader:soloader:0.10.4'
|
||||
debugImplementation "com.kgurgul.flipper:flipper-realm-android:2.2.0"
|
||||
|
||||
gplayImplementation "com.google.android.gms:play-services-location:16.0.0"
|
||||
// UnifiedPush gplay flavor only
|
||||
gplayImplementation('com.github.UnifiedPush:android-embedded_fcm_distributor:2.1.2') {
|
||||
exclude group: 'com.google.firebase', module: 'firebase-core'
|
||||
exclude group: 'com.google.firebase', module: 'firebase-analytics'
|
||||
exclude group: 'com.google.firebase', module: 'firebase-measurement-connector'
|
||||
}
|
||||
|
||||
// Nightly
|
||||
// API-only library
|
||||
gplayImplementation libs.google.appdistributionApi
|
||||
// Full SDK implementation
|
||||
gplayImplementation libs.google.appdistribution
|
||||
|
||||
// OSS License, gplay flavor only
|
||||
gplayImplementation 'com.google.android.gms:play-services-oss-licenses:17.0.0'
|
||||
kapt libs.dagger.hiltCompiler
|
||||
kapt libs.airbnb.epoxyProcessor
|
||||
|
||||
androidTestImplementation libs.androidx.testCore
|
||||
androidTestImplementation libs.androidx.testRunner
|
||||
@ -378,5 +413,6 @@ dependencies {
|
||||
androidTestImplementation libs.androidx.fragmentTesting
|
||||
androidTestImplementation "org.jetbrains.kotlin:kotlin-reflect:1.7.10"
|
||||
debugImplementation libs.androidx.fragmentTesting
|
||||
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.9.1'
|
||||
}
|
||||
|
||||
|
22
vector-app/src/debug/AndroidManifest.xml
Normal file
22
vector-app/src/debug/AndroidManifest.xml
Normal file
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<application>
|
||||
<activity android:name="im.vector.app.features.debug.TestLinkifyActivity" />
|
||||
<activity android:name="im.vector.app.features.debug.DebugPermissionActivity" />
|
||||
<activity android:name="im.vector.app.features.debug.analytics.DebugAnalyticsActivity" />
|
||||
<activity android:name="im.vector.app.features.debug.settings.DebugPrivateSettingsActivity" />
|
||||
<activity android:name="im.vector.app.features.debug.sas.DebugSasEmojiActivity" />
|
||||
<activity android:name="im.vector.app.features.debug.features.DebugFeaturesSettingsActivity" />
|
||||
<activity android:name="im.vector.app.features.debug.DebugMenuActivity" />
|
||||
<activity android:name="im.vector.app.features.debug.leak.DebugMemoryLeaksActivity" />
|
||||
|
||||
<activity
|
||||
android:name="com.facebook.flipper.android.diagnostics.FlipperDiagnosticActivity"
|
||||
android:exported="true" />
|
||||
|
||||
<!-- Used for UI tests to display the BiometricPrompt. It's normal that it appears as an error. -->
|
||||
<activity android:exported="false" android:name=".features.pin.lockscreen.tests.LockScreenTestActivity" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
@ -34,13 +34,13 @@ import im.vector.app.core.utils.PERMISSIONS_FOR_TAKING_PHOTO
|
||||
import im.vector.app.core.utils.checkPermissions
|
||||
import im.vector.app.core.utils.registerForPermissionsResult
|
||||
import im.vector.app.core.utils.toast
|
||||
import im.vector.app.databinding.ActivityDebugMenuBinding
|
||||
import im.vector.app.features.debug.analytics.DebugAnalyticsActivity
|
||||
import im.vector.app.features.debug.features.DebugFeaturesSettingsActivity
|
||||
import im.vector.app.features.debug.leak.DebugMemoryLeaksActivity
|
||||
import im.vector.app.features.debug.sas.DebugSasEmojiActivity
|
||||
import im.vector.app.features.debug.settings.DebugPrivateSettingsActivity
|
||||
import im.vector.app.features.qrcode.QrCodeScannerActivity
|
||||
import im.vector.application.databinding.ActivityDebugMenuBinding
|
||||
import im.vector.lib.ui.styles.debug.DebugMaterialThemeDarkDefaultActivity
|
||||
import im.vector.lib.ui.styles.debug.DebugMaterialThemeDarkTestActivity
|
||||
import im.vector.lib.ui.styles.debug.DebugMaterialThemeDarkVectorActivity
|
@ -23,13 +23,13 @@ import android.widget.Toast
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.platform.VectorBaseActivity
|
||||
import im.vector.app.core.utils.checkPermissions
|
||||
import im.vector.app.core.utils.onPermissionDeniedDialog
|
||||
import im.vector.app.core.utils.onPermissionDeniedSnackbar
|
||||
import im.vector.app.core.utils.registerForPermissionsResult
|
||||
import im.vector.app.databinding.ActivityDebugPermissionBinding
|
||||
import im.vector.application.R
|
||||
import im.vector.application.databinding.ActivityDebugPermissionBinding
|
||||
import timber.log.Timber
|
||||
|
||||
@AndroidEntryPoint
|
@ -20,9 +20,9 @@ import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import im.vector.app.R
|
||||
import im.vector.app.databinding.ActivityTestLinkifyBinding
|
||||
import im.vector.app.databinding.ItemTestLinkifyBinding
|
||||
import im.vector.application.R
|
||||
import im.vector.application.databinding.ActivityTestLinkifyBinding
|
||||
import im.vector.application.databinding.ItemTestLinkifyBinding
|
||||
|
||||
class TestLinkifyActivity : AppCompatActivity() {
|
||||
|
@ -25,7 +25,7 @@ import com.airbnb.mvrx.withState
|
||||
import im.vector.app.core.epoxy.onClick
|
||||
import im.vector.app.core.extensions.toOnOff
|
||||
import im.vector.app.core.platform.VectorBaseFragment
|
||||
import im.vector.app.databinding.FragmentDebugAnalyticsBinding
|
||||
import im.vector.application.databinding.FragmentDebugAnalyticsBinding
|
||||
import me.gujun.android.span.span
|
||||
|
||||
class DebugAnalyticsFragment : VectorBaseFragment<FragmentDebugAnalyticsBinding>() {
|
@ -23,9 +23,9 @@ import android.widget.Spinner
|
||||
import android.widget.TextView
|
||||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.epoxy.VectorEpoxyHolder
|
||||
import im.vector.app.core.epoxy.VectorEpoxyModel
|
||||
import im.vector.application.R
|
||||
|
||||
@EpoxyModelClass
|
||||
abstract class BooleanFeatureItem : VectorEpoxyModel<BooleanFeatureItem.Holder>(R.layout.item_feature) {
|
||||
@ -70,8 +70,8 @@ abstract class BooleanFeatureItem : VectorEpoxyModel<BooleanFeatureItem.Holder>(
|
||||
}
|
||||
|
||||
class Holder : VectorEpoxyHolder() {
|
||||
val label by bind<TextView>(im.vector.app.R.id.feature_label)
|
||||
val optionsSpinner by bind<Spinner>(im.vector.app.R.id.feature_options)
|
||||
val label by bind<TextView>(R.id.feature_label)
|
||||
val optionsSpinner by bind<Spinner>(R.id.feature_options)
|
||||
}
|
||||
|
||||
interface Listener {
|
@ -66,13 +66,13 @@ class DebugVectorOverrides(private val context: Context) : VectorOverrides {
|
||||
suspend fun setHomeserverCapabilities(block: HomeserverCapabilitiesOverride.() -> HomeserverCapabilitiesOverride) {
|
||||
val capabilitiesOverride = block(forceHomeserverCapabilities.firstOrNull() ?: HomeserverCapabilitiesOverride(null, null))
|
||||
context.dataStore.edit { settings ->
|
||||
when (capabilitiesOverride.canChangeDisplayName) {
|
||||
when (val canChangeDisplayName = capabilitiesOverride.canChangeDisplayName) {
|
||||
null -> settings.remove(forceCanChangeDisplayName)
|
||||
else -> settings[forceCanChangeDisplayName] = capabilitiesOverride.canChangeDisplayName
|
||||
else -> settings[forceCanChangeDisplayName] = canChangeDisplayName
|
||||
}
|
||||
when (capabilitiesOverride.canChangeAvatar) {
|
||||
when (val canChangeAvatar = capabilitiesOverride.canChangeAvatar) {
|
||||
null -> settings.remove(forceCanChangeAvatar)
|
||||
else -> settings[forceCanChangeAvatar] = capabilitiesOverride.canChangeAvatar
|
||||
else -> settings[forceCanChangeAvatar] = canChangeAvatar
|
||||
}
|
||||
}
|
||||
}
|
@ -23,9 +23,9 @@ import android.widget.Spinner
|
||||
import android.widget.TextView
|
||||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.epoxy.VectorEpoxyHolder
|
||||
import im.vector.app.core.epoxy.VectorEpoxyModel
|
||||
import im.vector.application.R
|
||||
|
||||
@EpoxyModelClass
|
||||
abstract class EnumFeatureItem : VectorEpoxyModel<EnumFeatureItem.Holder>(R.layout.item_feature) {
|
||||
@ -70,8 +70,8 @@ abstract class EnumFeatureItem : VectorEpoxyModel<EnumFeatureItem.Holder>(R.layo
|
||||
}
|
||||
|
||||
class Holder : VectorEpoxyHolder() {
|
||||
val label by bind<TextView>(im.vector.app.R.id.feature_label)
|
||||
val optionsSpinner by bind<Spinner>(im.vector.app.R.id.feature_options)
|
||||
val label by bind<TextView>(R.id.feature_label)
|
||||
val optionsSpinner by bind<Spinner>(R.id.feature_options)
|
||||
}
|
||||
|
||||
interface Listener {
|
@ -25,7 +25,7 @@ import com.airbnb.mvrx.withState
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import im.vector.app.core.epoxy.onClick
|
||||
import im.vector.app.core.platform.VectorBaseFragment
|
||||
import im.vector.app.databinding.FragmentDebugMemoryLeaksBinding
|
||||
import im.vector.application.databinding.FragmentDebugMemoryLeaksBinding
|
||||
|
||||
@AndroidEntryPoint
|
||||
class DebugMemoryLeaksFragment :
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2021 New Vector Ltd
|
||||
* Copyright (c) 2022 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
@ -21,9 +21,9 @@ import android.widget.TextView
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.epoxy.VectorEpoxyHolder
|
||||
import im.vector.app.core.epoxy.VectorEpoxyModel
|
||||
import im.vector.application.R
|
||||
import me.gujun.android.span.image
|
||||
import me.gujun.android.span.span
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.EmojiRepresentation
|
@ -23,7 +23,7 @@ import android.view.ViewGroup
|
||||
import com.airbnb.mvrx.fragmentViewModel
|
||||
import com.airbnb.mvrx.withState
|
||||
import im.vector.app.core.platform.VectorBaseFragment
|
||||
import im.vector.app.databinding.FragmentDebugPrivateSettingsBinding
|
||||
import im.vector.application.databinding.FragmentDebugPrivateSettingsBinding
|
||||
|
||||
class DebugPrivateSettingsFragment : VectorBaseFragment<FragmentDebugPrivateSettingsBinding>() {
|
||||
|
@ -24,7 +24,7 @@ import android.view.View
|
||||
import android.widget.AdapterView
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.LinearLayout
|
||||
import im.vector.app.databinding.ViewBooleanDropdownBinding
|
||||
import im.vector.application.databinding.ViewBooleanDropdownBinding
|
||||
|
||||
class OverrideDropdownView @JvmOverloads constructor(
|
||||
context: Context,
|
@ -1,7 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="im.vector.app">
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
@ -15,7 +14,7 @@
|
||||
<application>
|
||||
|
||||
<receiver
|
||||
android:name=".fdroid.receiver.OnApplicationUpgradeOrRebootReceiver"
|
||||
android:name="im.vector.app.fdroid.receiver.OnApplicationUpgradeOrRebootReceiver"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
|
||||
@ -24,12 +23,12 @@
|
||||
</receiver>
|
||||
|
||||
<receiver
|
||||
android:name=".fdroid.receiver.AlarmSyncBroadcastReceiver"
|
||||
android:name="im.vector.app.fdroid.receiver.AlarmSyncBroadcastReceiver"
|
||||
android:enabled="true"
|
||||
android:exported="false" />
|
||||
|
||||
<receiver
|
||||
android:name=".fdroid.receiver.KeepInternalDistributor"
|
||||
android:name="im.vector.app.fdroid.receiver.KeepInternalDistributor"
|
||||
android:enabled="true"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
@ -43,7 +42,7 @@
|
||||
</receiver>
|
||||
|
||||
<service
|
||||
android:name=".fdroid.service.GuardAndroidService"
|
||||
android:name="im.vector.app.fdroid.service.GuardAndroidService"
|
||||
android:exported="false"
|
||||
tools:ignore="Instantiatable" />
|
||||
|
@ -3,6 +3,7 @@ apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-parcelize'
|
||||
apply plugin: 'kotlin-kapt'
|
||||
apply plugin: 'dagger.hilt.android.plugin'
|
||||
apply plugin: 'app.cash.paparazzi'
|
||||
|
||||
if (project.hasProperty("coverage")) {
|
||||
apply plugin: 'jacoco'
|
||||
@ -66,30 +67,6 @@ android {
|
||||
testCoverageEnabled = coverage.enableTestCoverage
|
||||
}
|
||||
}
|
||||
nightly {
|
||||
initWith release
|
||||
matchingFallbacks = ['release']
|
||||
}
|
||||
release
|
||||
}
|
||||
|
||||
flavorDimensions "store"
|
||||
|
||||
productFlavors {
|
||||
gplay {
|
||||
dimension "store"
|
||||
isDefault = true
|
||||
|
||||
buildConfigField "String", "SHORT_FLAVOR_DESCRIPTION", "\"G\""
|
||||
buildConfigField "String", "FLAVOR_DESCRIPTION", "\"GooglePlay\""
|
||||
}
|
||||
|
||||
fdroid {
|
||||
dimension "store"
|
||||
|
||||
buildConfigField "String", "SHORT_FLAVOR_DESCRIPTION", "\"F\""
|
||||
buildConfigField "String", "FLAVOR_DESCRIPTION", "\"FDroid\""
|
||||
}
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
@ -118,10 +95,6 @@ android {
|
||||
test {
|
||||
java.srcDirs += "src/sharedTest/java"
|
||||
}
|
||||
// Add sourceSets for `release` version when building `nightly`
|
||||
nightly {
|
||||
java.srcDirs += "src/release/java"
|
||||
}
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
@ -190,12 +163,6 @@ dependencies {
|
||||
// Snap Helper https://github.com/rubensousa/GravitySnapHelper
|
||||
api 'com.github.rubensousa:gravitysnaphelper:2.2.2'
|
||||
|
||||
// Nightly
|
||||
// API-only library
|
||||
gplayImplementation libs.google.appdistributionApi
|
||||
// Full SDK implementation
|
||||
gplayImplementation libs.google.appdistribution
|
||||
|
||||
// Work
|
||||
api libs.androidx.work
|
||||
|
||||
@ -211,7 +178,7 @@ dependencies {
|
||||
// UI
|
||||
implementation 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1'
|
||||
implementation libs.google.material
|
||||
implementation 'me.gujun.android:span:1.7'
|
||||
api 'me.gujun.android:span:1.7'
|
||||
implementation libs.markwon.core
|
||||
implementation libs.markwon.extLatex
|
||||
implementation libs.markwon.inlineParser
|
||||
@ -263,15 +230,6 @@ dependencies {
|
||||
|
||||
// UnifiedPush
|
||||
implementation 'com.github.UnifiedPush:android-connector:2.0.1'
|
||||
// UnifiedPush gplay flavor only
|
||||
gplayImplementation('com.github.UnifiedPush:android-embedded_fcm_distributor:2.1.2') {
|
||||
exclude group: 'com.google.firebase', module: 'firebase-core'
|
||||
exclude group: 'com.google.firebase', module: 'firebase-analytics'
|
||||
exclude group: 'com.google.firebase', module: 'firebase-measurement-connector'
|
||||
}
|
||||
|
||||
// OSS License, gplay flavor only
|
||||
gplayImplementation 'com.google.android.gms:play-services-oss-licenses:17.0.0'
|
||||
|
||||
implementation "androidx.emoji2:emoji2:1.1.0"
|
||||
|
||||
@ -307,14 +265,12 @@ dependencies {
|
||||
implementation 'commons-codec:commons-codec:1.15'
|
||||
|
||||
// MapTiler
|
||||
fdroidApi(libs.maplibre.androidSdk) {
|
||||
api(libs.maplibre.androidSdk) {
|
||||
exclude group: 'com.google.android.gms', module: 'play-services-location'
|
||||
}
|
||||
fdroidApi(libs.maplibre.pluginAnnotation) {
|
||||
api(libs.maplibre.pluginAnnotation) {
|
||||
exclude group: 'com.google.android.gms', module: 'play-services-location'
|
||||
}
|
||||
gplayApi libs.maplibre.androidSdk
|
||||
gplayApi libs.maplibre.pluginAnnotation
|
||||
|
||||
// TESTS
|
||||
testImplementation libs.tests.junit
|
||||
@ -327,19 +283,6 @@ dependencies {
|
||||
exclude group: "org.jetbrains.kotlinx", module: "kotlinx-coroutines-debug"
|
||||
}
|
||||
|
||||
// Flipper, debug builds only
|
||||
debugImplementation(libs.flipper.flipper) {
|
||||
exclude group: 'com.facebook.fbjni', module: 'fbjni'
|
||||
}
|
||||
debugImplementation(libs.flipper.flipperNetworkPlugin) {
|
||||
exclude group: 'com.facebook.fbjni', module: 'fbjni'
|
||||
}
|
||||
debugImplementation 'com.facebook.soloader:soloader:0.10.4'
|
||||
debugImplementation "com.kgurgul.flipper:flipper-realm-android:2.2.0"
|
||||
|
||||
// Activate when you want to check for leaks, from time to time.
|
||||
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.9.1'
|
||||
|
||||
androidTestImplementation libs.androidx.testCore
|
||||
androidTestImplementation libs.androidx.testRunner
|
||||
androidTestImplementation libs.androidx.testRules
|
||||
|
@ -1,23 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="im.vector.app">
|
||||
|
||||
<application>
|
||||
<activity android:name=".features.debug.TestLinkifyActivity" />
|
||||
<activity android:name=".features.debug.DebugPermissionActivity" />
|
||||
<activity android:name=".features.debug.analytics.DebugAnalyticsActivity" />
|
||||
<activity android:name=".features.debug.settings.DebugPrivateSettingsActivity" />
|
||||
<activity android:name=".features.debug.sas.DebugSasEmojiActivity" />
|
||||
<activity android:name=".features.debug.features.DebugFeaturesSettingsActivity" />
|
||||
<activity android:name=".features.debug.DebugMenuActivity" />
|
||||
<activity android:name=".features.debug.leak.DebugMemoryLeaksActivity" />
|
||||
|
||||
<activity
|
||||
android:name="com.facebook.flipper.android.diagnostics.FlipperDiagnosticActivity"
|
||||
android:exported="true" />
|
||||
|
||||
<!-- Used for UI tests to display the BiometricPrompt. It's normal that it appears as an error. -->
|
||||
<activity android:exported="false" android:name=".features.pin.lockscreen.tests.LockScreenTestActivity" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
@ -20,8 +20,11 @@ import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.error.ErrorFormatter
|
||||
import im.vector.app.core.extensions.associateContentStateWith
|
||||
import im.vector.app.core.extensions.clearErrorOnChange
|
||||
import im.vector.app.core.extensions.content
|
||||
@ -30,6 +33,7 @@ import im.vector.app.core.extensions.realignPercentagesToParent
|
||||
import im.vector.app.core.extensions.setOnImeDoneListener
|
||||
import im.vector.app.core.extensions.showKeyboard
|
||||
import im.vector.app.core.extensions.toReducedUrl
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
import im.vector.app.core.utils.ensureProtocol
|
||||
import im.vector.app.core.utils.ensureTrailingSlash
|
||||
import im.vector.app.core.utils.openUrlInExternalBrowser
|
||||
@ -37,49 +41,82 @@ import im.vector.app.databinding.FragmentFtueServerSelectionCombinedBinding
|
||||
import im.vector.app.features.onboarding.OnboardingAction
|
||||
import im.vector.app.features.onboarding.OnboardingFlow
|
||||
import im.vector.app.features.onboarding.OnboardingViewEvents
|
||||
import im.vector.app.features.onboarding.OnboardingViewModel
|
||||
import im.vector.app.features.onboarding.OnboardingViewState
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import org.matrix.android.sdk.api.failure.isHomeserverUnavailable
|
||||
import reactivecircus.flowbinding.android.view.clicks
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class FtueAuthCombinedServerSelectionFragment :
|
||||
AbstractFtueAuthFragment<FragmentFtueServerSelectionCombinedBinding>() {
|
||||
class FtueAuthCombinedServerSelectionFragment : AbstractFtueAuthFragment<FragmentFtueServerSelectionCombinedBinding>() {
|
||||
|
||||
@Inject lateinit var stringProvider: StringProvider
|
||||
private lateinit var controller: Controller
|
||||
|
||||
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentFtueServerSelectionCombinedBinding {
|
||||
return FragmentFtueServerSelectionCombinedBinding.inflate(inflater, container, false)
|
||||
return FragmentFtueServerSelectionCombinedBinding.inflate(inflater, container, false).also {
|
||||
controller = Controller(viewLifecycleOwner, it, stringProvider, errorFormatter)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
setupViews()
|
||||
}
|
||||
|
||||
private fun setupViews() {
|
||||
views.chooseServerRoot.realignPercentagesToParent()
|
||||
views.chooseServerToolbar.setNavigationOnClickListener {
|
||||
controller.listener = object : Controller.Listener {
|
||||
override fun onNavigationClicked() {
|
||||
viewModel.handle(OnboardingAction.PostViewEvent(OnboardingViewEvents.OnBack))
|
||||
}
|
||||
views.chooseServerInput.associateContentStateWith(button = views.chooseServerSubmit, enabledPredicate = { canSubmit(it) })
|
||||
views.chooseServerInput.setOnImeDoneListener {
|
||||
if (canSubmit(views.chooseServerInput.content())) {
|
||||
updateServerUrl()
|
||||
}
|
||||
}
|
||||
views.chooseServerGetInTouch.debouncedClicks { openUrlInExternalBrowser(requireContext(), getString(R.string.ftue_ems_url)) }
|
||||
views.chooseServerSubmit.debouncedClicks { updateServerUrl() }
|
||||
views.chooseServerInput.clearErrorOnChange(viewLifecycleOwner)
|
||||
}
|
||||
|
||||
private fun canSubmit(url: String) = url.isNotEmpty()
|
||||
|
||||
private fun updateServerUrl() {
|
||||
override fun updateServerUrl() {
|
||||
viewModel.handle(OnboardingAction.HomeServerChange.EditHomeServer(views.chooseServerInput.content().ensureProtocol().ensureTrailingSlash()))
|
||||
}
|
||||
|
||||
override fun getInTouchClicked() {
|
||||
openUrlInExternalBrowser(requireContext(), getString(R.string.ftue_ems_url))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun resetViewModel() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
override fun updateWithState(state: OnboardingViewState) {
|
||||
controller.setData(state)
|
||||
}
|
||||
|
||||
override fun onError(throwable: Throwable) {
|
||||
controller.setError(throwable)
|
||||
}
|
||||
|
||||
class Controller(
|
||||
lifecycleOwner: LifecycleOwner,
|
||||
private val views: FragmentFtueServerSelectionCombinedBinding,
|
||||
private val stringProvider: StringProvider,
|
||||
private val errorFormatter: ErrorFormatter,
|
||||
private val viewModel: OnboardingViewModel,
|
||||
) {
|
||||
|
||||
var listener: Listener? = null
|
||||
|
||||
init {
|
||||
views.chooseServerRoot.realignPercentagesToParent()
|
||||
views.chooseServerToolbar.setNavigationOnClickListener { listener?.onNavigationClicked() }
|
||||
views.chooseServerInput.associateContentStateWith(button = views.chooseServerSubmit, enabledPredicate = { canSubmit(it) })
|
||||
views.chooseServerInput.setOnImeDoneListener {
|
||||
if (canSubmit(views.chooseServerInput.content())) {
|
||||
listener?.updateServerUrl()
|
||||
}
|
||||
}
|
||||
views.chooseServerGetInTouch.debouncedClicks(lifecycleOwner) { listener?.getInTouchClicked() }
|
||||
views.chooseServerSubmit.debouncedClicks(lifecycleOwner) { listener?.updateServerUrl() }
|
||||
views.chooseServerInput.clearErrorOnChange(lifecycleOwner)
|
||||
}
|
||||
|
||||
private fun canSubmit(url: String) = url.isNotEmpty()
|
||||
|
||||
fun setData(state: OnboardingViewState) {
|
||||
views.chooseServerHeaderSubtitle.setText(
|
||||
when (state.onboardingFlow) {
|
||||
OnboardingFlow.SignIn -> R.string.ftue_auth_choose_server_sign_in_subtitle
|
||||
@ -97,12 +134,26 @@ class FtueAuthCombinedServerSelectionFragment :
|
||||
views.chooseServerInput.editText().showKeyboard(true)
|
||||
}
|
||||
|
||||
override fun onError(throwable: Throwable) {
|
||||
fun setError(throwable: Throwable) {
|
||||
views.chooseServerInput.error = when {
|
||||
throwable.isHomeserverUnavailable() -> getString(R.string.login_error_homeserver_not_found)
|
||||
throwable.isHomeserverUnavailable() -> stringProvider.getString(R.string.login_error_homeserver_not_found)
|
||||
else -> errorFormatter.toHumanReadable(throwable)
|
||||
}
|
||||
println(views.chooseServerInput.error)
|
||||
}
|
||||
|
||||
private fun String.toReducedUrlKeepingSchemaIfInsecure() = toReducedUrl(keepSchema = this.startsWith("http://"))
|
||||
|
||||
interface Listener {
|
||||
fun onNavigationClicked()
|
||||
fun updateServerUrl()
|
||||
fun getInTouchClicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun View.debouncedClicks(lifecycleOwner: LifecycleOwner, onClicked: () -> Unit) {
|
||||
clicks()
|
||||
.onEach { onClicked() }
|
||||
.launchIn(lifecycleOwner.lifecycleScope)
|
||||
}
|
||||
|
@ -120,7 +120,7 @@
|
||||
app:layout_constraintHeight_percent="0.03"
|
||||
app:layout_constraintTop_toBottomOf="@id/chooseServerInput" />
|
||||
|
||||
<Button
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/chooseServerSubmit"
|
||||
style="@style/Widget.Vector.Button.Login"
|
||||
android:layout_width="0dp"
|
||||
|
181
vector/src/test/java/im/vector/app/PaparazziScreenshotTest.kt
Normal file
181
vector/src/test/java/im/vector/app/PaparazziScreenshotTest.kt
Normal file
@ -0,0 +1,181 @@
|
||||
/*
|
||||
* Copyright (c) 2022 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app
|
||||
|
||||
import android.os.Build
|
||||
import android.view.View
|
||||
import android.widget.FrameLayout
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.LifecycleRegistry
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import app.cash.paparazzi.DeviceConfig.Companion.PIXEL_3
|
||||
import app.cash.paparazzi.Paparazzi
|
||||
import com.airbnb.epoxy.EpoxyController
|
||||
import com.airbnb.mvrx.Success
|
||||
import im.vector.app.core.error.DefaultErrorFormatter
|
||||
import im.vector.app.core.extensions.configureWith
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
import im.vector.app.core.utils.DimensionConverter
|
||||
import im.vector.app.databinding.FragmentFtueServerSelectionCombinedBinding
|
||||
import im.vector.app.features.home.AvatarRenderer
|
||||
import im.vector.app.features.home.room.detail.timeline.format.RoomHistoryVisibilityFormatter
|
||||
import im.vector.app.features.onboarding.OnboardingFlow
|
||||
import im.vector.app.features.onboarding.OnboardingViewState
|
||||
import im.vector.app.features.onboarding.SelectedHomeserverState
|
||||
import im.vector.app.features.onboarding.ftueauth.FtueAuthCombinedServerSelectionFragment
|
||||
import im.vector.app.features.roomprofile.settings.RoomSettingsController
|
||||
import im.vector.app.features.roomprofile.settings.RoomSettingsViewState
|
||||
import im.vector.app.test.fakes.FakeErrorFormatter
|
||||
import im.vector.app.test.fakes.FakeVectorPreferences
|
||||
import io.mockk.coJustRun
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
import org.matrix.android.sdk.api.util.MatrixItem
|
||||
import java.lang.reflect.Field
|
||||
import java.lang.reflect.Modifier
|
||||
|
||||
class PaparazziScreenshotTest {
|
||||
|
||||
@get:Rule
|
||||
val paparazzi = Paparazzi(
|
||||
deviceConfig = PIXEL_3,
|
||||
theme = "Theme.Vector.Light",
|
||||
)
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
setFinalStaticValue(Build::class.java.getDeclaredField("MANUFACTURER"), "GOOGLE")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `empty server selection`() {
|
||||
val (view, binding) = paparazzi.inflate(R.layout.fragment_ftue_server_selection_combined, FragmentFtueServerSelectionCombinedBinding::bind)
|
||||
val stringProvider = StringProvider(paparazzi.resources)
|
||||
val lifecycleOwner = fakeLifecycleOwner()
|
||||
|
||||
FtueAuthCombinedServerSelectionFragment.Controller(lifecycleOwner, binding, stringProvider, FakeErrorFormatter())
|
||||
.setData(
|
||||
OnboardingViewState(
|
||||
onboardingFlow = OnboardingFlow.SignIn,
|
||||
)
|
||||
)
|
||||
|
||||
paparazzi.snapshot(view)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `server selection with content`() {
|
||||
val (view, binding) = paparazzi.inflate(R.layout.fragment_ftue_server_selection_combined, FragmentFtueServerSelectionCombinedBinding::bind)
|
||||
val stringProvider = StringProvider(paparazzi.resources)
|
||||
val lifecycleOwner = fakeLifecycleOwner()
|
||||
|
||||
FtueAuthCombinedServerSelectionFragment.Controller(lifecycleOwner, binding, stringProvider, DefaultErrorFormatter(stringProvider))
|
||||
.setData(
|
||||
OnboardingViewState(
|
||||
onboardingFlow = OnboardingFlow.SignIn,
|
||||
selectedHomeserver = SelectedHomeserverState(userFacingUrl = "matrix.org")
|
||||
)
|
||||
)
|
||||
|
||||
paparazzi.snapshot(view)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `room settings`() {
|
||||
// Material components aren't fully supported yet https://github.com/cashapp/paparazzi/issues/223
|
||||
val strings = StringProvider(paparazzi.resources)
|
||||
val fakeAvatarRender = FakeAvatarRender()
|
||||
val fakeDimensionConverter = FakeDimensionConverter()
|
||||
val fakeVectorPreferences = FakeVectorPreferences().also { it.givenDeveloperMode(false) }
|
||||
val controller = RoomSettingsController(
|
||||
strings,
|
||||
fakeAvatarRender.instance,
|
||||
fakeDimensionConverter.instance,
|
||||
RoomHistoryVisibilityFormatter(strings),
|
||||
fakeVectorPreferences.instance,
|
||||
)
|
||||
|
||||
val view = inflateRecyclerView(controller)
|
||||
|
||||
controller.setData(
|
||||
RoomSettingsViewState(
|
||||
roomId = "room-id",
|
||||
roomSummary = Success(
|
||||
RoomSummary(
|
||||
roomId = "!room-id",
|
||||
isEncrypted = true,
|
||||
encryptionEventTs = null,
|
||||
typingUsers = emptyList(),
|
||||
displayName = "A room name",
|
||||
topic = "A room topic",
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
paparazzi.snapshot(view)
|
||||
}
|
||||
|
||||
private fun inflateRecyclerView(controller: EpoxyController): View {
|
||||
val view = paparazzi.inflate<FrameLayout>(R.layout.fragment_generic_recycler)
|
||||
val recyclerView = view.findViewById<RecyclerView>(R.id.genericRecyclerView)
|
||||
recyclerView.configureWith(controller)
|
||||
return view
|
||||
}
|
||||
}
|
||||
|
||||
fun setFinalStaticValue(field: Field, value: Any) {
|
||||
val modifiersField = Field::class.java.getDeclaredField("modifiers")
|
||||
modifiersField.isAccessible = true
|
||||
modifiersField.setInt(field, field.modifiers and Modifier.FINAL.inv())
|
||||
field.isAccessible = true
|
||||
|
||||
field.set(null, value)
|
||||
}
|
||||
|
||||
private fun <B> Paparazzi.inflate(layoutId: Int, binder: (View) -> B): Pair<View, B> {
|
||||
val view = inflate<View>(layoutId)
|
||||
val binding = binder(view)
|
||||
return view to binding
|
||||
}
|
||||
|
||||
private fun fakeLifecycleOwner(): LifecycleOwner {
|
||||
val lifecycleOwner = mockk<LifecycleOwner>()
|
||||
every { lifecycleOwner.lifecycle } returns LifecycleRegistry(lifecycleOwner)
|
||||
return lifecycleOwner
|
||||
}
|
||||
|
||||
class FakeDimensionConverter {
|
||||
|
||||
val instance = mockk<DimensionConverter>().also {
|
||||
every { it.dpToPx(any()) }.answers { arg ->
|
||||
arg.invocation.args.first() as Int
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class FakeAvatarRender {
|
||||
|
||||
val instance = mockk<AvatarRenderer>().also {
|
||||
coJustRun { it.render(any<MatrixItem>(), any()) }
|
||||
}
|
||||
}
|
||||
|
@ -36,4 +36,8 @@ class FakeVectorPreferences {
|
||||
fun verifySetSpaceBackstack(value: List<String?>, inverse: Boolean = false) {
|
||||
verify(inverse = inverse) { instance.setSpaceBackstack(value) }
|
||||
}
|
||||
|
||||
fun givenDeveloperMode(isDeveloperMode: Boolean) {
|
||||
every { instance.developerMode() } returns isDeveloperMode
|
||||
}
|
||||
}
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
Binary file not shown.
After Width: | Height: | Size: 35 KiB |
Binary file not shown.
After Width: | Height: | Size: 27 KiB |
Loading…
Reference in New Issue
Block a user