Rename package `im.vector.riotx` to `im.vector.app` (IDE)

This commit is contained in:
Benoit Marty 2020-08-03 18:23:05 +02:00 committed by Benoit Marty
parent 67fc2feacb
commit a1f90abcd2
1903 changed files with 93458 additions and 93450 deletions

View File

@ -23,6 +23,7 @@ Other changes:
- Hide Flair settings, this is not implemented yet.
- Rename package `im.vector.riotx.attachmentviewer` to `im.vector.lib.attachmentviewer`
- Rename package `im.vector.riotx.multipicker` to `im.vector.lib.multipicker`
- Rename package `im.vector.riotx` to `im.vector.app`
Changes in Element 1.0.4 (2020-08-03)
===================================================

View File

@ -20,7 +20,7 @@
# hide the original source file name.
#-renamesourcefileattribute SourceFile
-keep class im.vector.riotx.features.** { *; }
-keep class im.vector.app.features.** { *; }
## print all the rules in a file
# -printconfiguration ../proguard_files/full-r8-config.txt

View File

@ -0,0 +1,32 @@
/*
* Copyright (c) 2020 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.content.Context
import androidx.test.core.app.ApplicationProvider
import im.vector.app.test.shared.createTimberTestRule
import org.junit.Rule
interface InstrumentedTest {
@Rule
fun timberTestRule() = createTimberTestRule()
fun context(): Context {
return ApplicationProvider.getApplicationContext()
}
}

View File

@ -0,0 +1,37 @@
/*
* Copyright (c) 2020 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.core.utils
import org.amshove.kluent.shouldBe
import org.junit.Test
import java.lang.Thread.sleep
class TemporaryStoreTest {
@Test
fun testTemporaryStore() {
// Keep the data 30 millis
val store = TemporaryStore<String>(30)
store.data = "test"
store.data shouldBe "test"
sleep(15)
store.data shouldBe "test"
sleep(20)
store.data shouldBe null
}
}

View File

@ -0,0 +1,95 @@
/*
* Copyright (c) 2020 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.features.reactions.data
import im.vector.app.InstrumentedTest
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import org.junit.runners.MethodSorters
import kotlin.system.measureTimeMillis
@RunWith(JUnit4::class)
@FixMethodOrder(MethodSorters.JVM)
class EmojiDataSourceTest : InstrumentedTest {
@Test
fun checkParsingTime() {
val time = measureTimeMillis {
EmojiDataSource(context().resources)
}
assertTrue("Too long to parse", time < 100)
}
@Test
fun checkNumberOfResult() {
val emojiDataSource = EmojiDataSource(context().resources)
assertEquals("Wrong number of emojis", 1545, emojiDataSource.rawData.emojis.size)
assertEquals("Wrong number of categories", 8, emojiDataSource.rawData.categories.size)
assertEquals("Wrong number of aliases", 57, emojiDataSource.rawData.aliases.size)
}
@Test
fun searchTestEmptySearch() {
val emojiDataSource = EmojiDataSource(context().resources)
assertEquals("Empty search should return 1545 results", 1545, emojiDataSource.filterWith("").size)
}
@Test
fun searchTestNoResult() {
val emojiDataSource = EmojiDataSource(context().resources)
assertTrue("Should not have result", emojiDataSource.filterWith("noresult").isEmpty())
}
@Test
fun searchTestOneResult() {
val emojiDataSource = EmojiDataSource(context().resources)
assertEquals("Should have 1 result", 1, emojiDataSource.filterWith("france").size)
}
@Test
fun searchTestManyResult() {
val emojiDataSource = EmojiDataSource(context().resources)
assertTrue("Should have many result", emojiDataSource.filterWith("fra").size > 1)
}
@Test
fun testTada() {
val emojiDataSource = EmojiDataSource(context().resources)
val result = emojiDataSource.filterWith("tada")
assertEquals("Should find tada emoji", 1, result.size)
assertEquals("Should find tada emoji", "🎉", result[0].emoji)
}
@Test
fun testQuickReactions() {
val emojiDataSource = EmojiDataSource(context().resources)
assertEquals("Should have 8 quick reactions", 8, emojiDataSource.getQuickReactions().size)
}
}

View File

@ -1,32 +0,0 @@
/*
* Copyright (c) 2020 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.riotx
import android.content.Context
import androidx.test.core.app.ApplicationProvider
import im.vector.riotx.test.shared.createTimberTestRule
import org.junit.Rule
interface InstrumentedTest {
@Rule
fun timberTestRule() = createTimberTestRule()
fun context(): Context {
return ApplicationProvider.getApplicationContext()
}
}

View File

@ -1,37 +0,0 @@
/*
* Copyright (c) 2020 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.riotx.core.utils
import org.amshove.kluent.shouldBe
import org.junit.Test
import java.lang.Thread.sleep
class TemporaryStoreTest {
@Test
fun testTemporaryStore() {
// Keep the data 30 millis
val store = TemporaryStore<String>(30)
store.data = "test"
store.data shouldBe "test"
sleep(15)
store.data shouldBe "test"
sleep(20)
store.data shouldBe null
}
}

View File

@ -1,95 +0,0 @@
/*
* Copyright (c) 2020 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.riotx.features.reactions.data
import im.vector.riotx.InstrumentedTest
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import org.junit.runners.MethodSorters
import kotlin.system.measureTimeMillis
@RunWith(JUnit4::class)
@FixMethodOrder(MethodSorters.JVM)
class EmojiDataSourceTest : InstrumentedTest {
@Test
fun checkParsingTime() {
val time = measureTimeMillis {
EmojiDataSource(context().resources)
}
assertTrue("Too long to parse", time < 100)
}
@Test
fun checkNumberOfResult() {
val emojiDataSource = EmojiDataSource(context().resources)
assertEquals("Wrong number of emojis", 1545, emojiDataSource.rawData.emojis.size)
assertEquals("Wrong number of categories", 8, emojiDataSource.rawData.categories.size)
assertEquals("Wrong number of aliases", 57, emojiDataSource.rawData.aliases.size)
}
@Test
fun searchTestEmptySearch() {
val emojiDataSource = EmojiDataSource(context().resources)
assertEquals("Empty search should return 1545 results", 1545, emojiDataSource.filterWith("").size)
}
@Test
fun searchTestNoResult() {
val emojiDataSource = EmojiDataSource(context().resources)
assertTrue("Should not have result", emojiDataSource.filterWith("noresult").isEmpty())
}
@Test
fun searchTestOneResult() {
val emojiDataSource = EmojiDataSource(context().resources)
assertEquals("Should have 1 result", 1, emojiDataSource.filterWith("france").size)
}
@Test
fun searchTestManyResult() {
val emojiDataSource = EmojiDataSource(context().resources)
assertTrue("Should have many result", emojiDataSource.filterWith("fra").size > 1)
}
@Test
fun testTada() {
val emojiDataSource = EmojiDataSource(context().resources)
val result = emojiDataSource.filterWith("tada")
assertEquals("Should find tada emoji", 1, result.size)
assertEquals("Should find tada emoji", "🎉", result[0].emoji)
}
@Test
fun testQuickReactions() {
val emojiDataSource = EmojiDataSource(context().resources)
assertEquals("Should have 8 quick reactions", 8, emojiDataSource.getQuickReactions().size)
}
}

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="im.vector.riotx">
package="im.vector.app">
<application>
<activity android:name=".features.debug.TestLinkifyActivity" />

View File

@ -0,0 +1,65 @@
/*
* Copyright 2019 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.features.debug
import android.os.Bundle
import android.view.Menu
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.google.android.material.snackbar.Snackbar
import im.vector.app.R
import im.vector.app.core.utils.toast
import kotlinx.android.synthetic.debug.activity_test_material_theme.*
// Rendering is not the same with VectorBaseActivity
abstract class DebugMaterialThemeActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_test_material_theme)
debugShowSnackbar.setOnClickListener {
Snackbar.make(debugMaterialCoordinator, "Snackbar!", Snackbar.LENGTH_SHORT)
.setAction("Action") { }
.show()
}
debugShowToast.setOnClickListener {
toast("Toast")
}
debugShowDialog.setOnClickListener {
AlertDialog.Builder(this)
.setMessage("Dialog content")
.setIcon(R.drawable.ic_settings_x)
.setPositiveButton("Positive", null)
.setNegativeButton("Negative", null)
.setNeutralButton("Neutral", null)
.show()
}
debugShowBottomSheet.setOnClickListener {
BottomSheetDialogFragment().show(supportFragmentManager, "TAG")
}
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.home, menu)
return true
}
}

View File

@ -0,0 +1,19 @@
/*
* Copyright 2019 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.features.debug
class DebugMaterialThemeDarkActivity : DebugMaterialThemeActivity()

View File

@ -0,0 +1,19 @@
/*
* Copyright 2019 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.features.debug
class DebugMaterialThemeLightActivity : DebugMaterialThemeActivity()

View File

@ -0,0 +1,231 @@
/*
* Copyright 2019 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.features.debug
import android.app.Activity
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import android.content.Intent
import android.os.Build
import androidx.core.app.NotificationCompat
import androidx.core.app.Person
import butterknife.OnClick
import im.vector.app.R
import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.di.ScreenComponent
import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.core.utils.PERMISSIONS_FOR_TAKING_PHOTO
import im.vector.app.core.utils.PERMISSION_REQUEST_CODE_LAUNCH_CAMERA
import im.vector.app.core.utils.allGranted
import im.vector.app.core.utils.checkPermissions
import im.vector.app.core.utils.toast
import im.vector.app.features.debug.sas.DebugSasEmojiActivity
import im.vector.app.features.qrcode.QrCodeScannerActivity
import im.vector.matrix.android.internal.crypto.verification.qrcode.toQrCodeData
import kotlinx.android.synthetic.debug.activity_debug_menu.*
import timber.log.Timber
import javax.inject.Inject
class DebugMenuActivity : VectorBaseActivity() {
override fun getLayoutRes() = R.layout.activity_debug_menu
@Inject
lateinit var activeSessionHolder: ActiveSessionHolder
override fun injectWith(injector: ScreenComponent) {
injector.inject(this)
}
private lateinit var buffer: ByteArray
override fun initUiAndData() {
// renderQrCode("https://www.example.org")
buffer = ByteArray(256)
for (i in buffer.indices) {
buffer[i] = i.toByte()
}
val string = buffer.toString(Charsets.ISO_8859_1)
renderQrCode(string)
}
private fun renderQrCode(text: String) {
debug_qr_code.setData(text, true)
}
@OnClick(R.id.debug_test_text_view_link)
fun testTextViewLink() {
startActivity(Intent(this, TestLinkifyActivity::class.java))
}
@OnClick(R.id.debug_show_sas_emoji)
fun showSasEmoji() {
startActivity(Intent(this, DebugSasEmojiActivity::class.java))
}
@OnClick(R.id.debug_test_notification)
fun testNotification() {
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
// Create channel first
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel =
NotificationChannel(
"CHAN",
"Channel name",
NotificationManager.IMPORTANCE_DEFAULT
)
channel.description = "Channel description"
(getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager).createNotificationChannel(channel)
val channel2 =
NotificationChannel(
"CHAN2",
"Channel name 2",
NotificationManager.IMPORTANCE_DEFAULT
)
channel2.description = "Channel description 2"
(getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager).createNotificationChannel(channel2)
}
val builder = NotificationCompat.Builder(this, "CHAN")
.setWhen(System.currentTimeMillis())
.setContentTitle("Title")
.setContentText("Content")
// No effect because it's a group summary notif
.setNumber(33)
.setSmallIcon(R.drawable.ic_status_bar)
// This provocate the badge issue: no badge for group notification
.setGroup("GroupKey")
.setGroupSummary(true)
val messagingStyle1 = NotificationCompat.MessagingStyle(
Person.Builder()
.setName("User name")
.build()
)
.addMessage("Message 1 - 1", System.currentTimeMillis(), Person.Builder().setName("user 1-1").build())
.addMessage("Message 1 - 2", System.currentTimeMillis(), Person.Builder().setName("user 1-2").build())
val messagingStyle2 = NotificationCompat.MessagingStyle(
Person.Builder()
.setName("User name 2")
.build()
)
.addMessage("Message 2 - 1", System.currentTimeMillis(), Person.Builder().setName("user 1-1").build())
.addMessage("Message 2 - 2", System.currentTimeMillis(), Person.Builder().setName("user 1-2").build())
notificationManager.notify(10, builder.build())
notificationManager.notify(
11,
NotificationCompat.Builder(this, "CHAN")
.setChannelId("CHAN")
.setWhen(System.currentTimeMillis())
.setContentTitle("Title 1")
.setContentText("Content 1")
// For shortcut on long press on launcher icon
.setBadgeIconType(NotificationCompat.BADGE_ICON_NONE)
.setStyle(messagingStyle1)
.setSmallIcon(R.drawable.ic_status_bar)
.setGroup("GroupKey")
.build()
)
notificationManager.notify(
12,
NotificationCompat.Builder(this, "CHAN2")
.setWhen(System.currentTimeMillis())
.setContentTitle("Title 2")
.setContentText("Content 2")
.setStyle(messagingStyle2)
.setSmallIcon(R.drawable.ic_status_bar)
.setGroup("GroupKey")
.build()
)
}
@OnClick(R.id.debug_test_material_theme_light)
fun testMaterialThemeLight() {
startActivity(Intent(this, DebugMaterialThemeLightActivity::class.java))
}
@OnClick(R.id.debug_test_material_theme_dark)
fun testMaterialThemeDark() {
startActivity(Intent(this, DebugMaterialThemeDarkActivity::class.java))
}
@OnClick(R.id.debug_test_crash)
fun testCrash() {
throw RuntimeException("Application crashed from user demand")
}
@OnClick(R.id.debug_scan_qr_code)
fun scanQRCode() {
if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, this, PERMISSION_REQUEST_CODE_LAUNCH_CAMERA)) {
doScanQRCode()
}
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == PERMISSION_REQUEST_CODE_LAUNCH_CAMERA && allGranted(grantResults)) {
doScanQRCode()
}
}
private fun doScanQRCode() {
QrCodeScannerActivity.startForResult(this)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_OK) {
when (requestCode) {
QrCodeScannerActivity.QR_CODE_SCANNER_REQUEST_CODE -> {
toast("QrCode: " + QrCodeScannerActivity.getResultText(data) + " is QRCode: " + QrCodeScannerActivity.getResultIsQrCode(data))
// Also update the current QR Code (reverse operation)
// renderQrCode(QrCodeScannerActivity.getResultText(data) ?: "")
val result = QrCodeScannerActivity.getResultText(data)!!
val qrCodeData = result.toQrCodeData()
Timber.e("qrCodeData: $qrCodeData")
if (result.length != buffer.size) {
Timber.e("Error, length are not the same")
} else {
// Convert to ByteArray
val byteArrayResult = result.toByteArray(Charsets.ISO_8859_1)
for (i in byteArrayResult.indices) {
if (buffer[i] != byteArrayResult[i]) {
Timber.e("Error for byte $i, expecting ${buffer[i]} and get ${byteArrayResult[i]}")
}
}
}
}
}
}
}
}

View File

@ -0,0 +1,131 @@
/*
* Copyright 2019 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.features.debug
import android.os.Bundle
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.coordinatorlayout.widget.CoordinatorLayout
import butterknife.BindView
import butterknife.ButterKnife
import im.vector.app.R
class TestLinkifyActivity : AppCompatActivity() {
@BindView(R.id.test_linkify_content_view)
lateinit var scrollContent: LinearLayout
@BindView(R.id.test_linkify_coordinator)
lateinit var coordinatorLayout: CoordinatorLayout
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_test_linkify)
ButterKnife.bind(this)
scrollContent.removeAllViews()
listOf(
"https://www.html5rocks.com/en/tutorials/webrtc/basics/ |",
"https://www.html5rocks.com/en/tutorials/webrtc/basics/",
"mailto mailto:test@toto.com test@toto.com",
"Here is the link.www.test.com/foo/?23=35 you got it?",
"www.lemonde.fr",
" /www.lemonde.fr",
"://www.lemonde.fr",
"file:///dev/null ",
" ansible/xoxys.matrix#2c0b65eb",
"foo.ansible/xoxys.matrix#2c0b65eb",
"foo.ansible.fpo/xoxys.matrix#2c0b65eb",
"https://foo.ansible.fpo/xoxys.matrix#2c0b65eb",
"@vf:matrix.org",
"+44 207 123 1234",
"+33141437940",
"1234",
"3456.34,089",
"ksks9808",
"For example: geo:48.85828,2.29449?z=16 should be clickable",
"geo:37.786971,-122.399677;u=35",
"37.786971,-122.399677;u=35",
"48.107864,-1.712153",
"synchrone peut tenir la route la",
"that.is.some.sexy.link",
"test overlap 48.107864,0673728392 geo + pn?",
"test overlap 0673728392,48.107864 geo + pn?",
"If I add a link in brackets like (help for Riot: https://about.riot.im/help), the link is usable on Riot for Desktop",
"(help for Riot: https://about.riot.im/help)",
"http://example.com/test(1).html",
"http://example.com/test(1)",
"https://about.riot.im/help)",
"(http://example.com/test(1))",
"http://example.com/test1)",
"http://example.com/test1/, et ca",
"www.example.com/, et ca",
"foo.ansible.toplevel/xoxys.matrix#2c0b65eb",
"foo.ansible.ninja/xoxys.matrix#2c0b65eb",
"in brackets like (help for Riot: https://www.exemple/com/find(1)) , the link is usable ",
"""
In brackets like (help for Riot: https://about.riot.im/help) , the link is usable,
But you can call +44 207 123 1234 and come to 37.786971,-122.399677;u=35 then
see if this mail jhon@riot.im is active but this should not 12345
""".trimIndent()
)
.forEach { textContent ->
val item = LayoutInflater.from(this)
.inflate(R.layout.item_test_linkify, scrollContent, false)
item.findViewById<TextView>(R.id.test_linkify_auto_text)
?.apply {
text = textContent
/* TODO Use BetterLinkMovementMethod when the other PR is merged
movementMethod = MatrixLinkMovementMethod(object : MockMessageAdapterActionListener() {
override fun onURLClick(uri: Uri?) {
Snackbar.make(coordinatorLayout, "URI Clicked: $uri", Snackbar.LENGTH_LONG)
.setAction("open") {
openUrlInExternalBrowser(this@TestLinkifyActivity, uri)
}
.show()
}
})
*/
}
item.findViewById<TextView>(R.id.test_linkify_custom_text)
?.apply {
text = textContent
/* TODO Use BetterLinkMovementMethod when the other PR is merged
movementMethod = MatrixLinkMovementMethod(object : MockMessageAdapterActionListener() {
override fun onURLClick(uri: Uri?) {
Snackbar.make(coordinatorLayout, "URI Clicked: $uri", Snackbar.LENGTH_LONG)
.setAction("open") {
openUrlInExternalBrowser(this@TestLinkifyActivity, uri)
}
.show()
}
})
*/
// TODO Call VectorLinkify.addLinks(text)
}
scrollContent.addView(item, ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT))
}
}
}

View File

@ -0,0 +1,41 @@
/*
* Copyright 2019 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.features.debug.sas
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import im.vector.app.R
import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith
import im.vector.matrix.android.api.crypto.getAllVerificationEmojis
import kotlinx.android.synthetic.main.fragment_generic_recycler.*
class DebugSasEmojiActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.fragment_generic_recycler)
val controller = SasEmojiController()
recyclerView.configureWith(controller)
controller.setData(SasState(getAllVerificationEmojis()))
}
override fun onDestroy() {
recyclerView.cleanup()
super.onDestroy()
}
}

View File

@ -0,0 +1,39 @@
/*
* Copyright 2019 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.features.debug.sas
import com.airbnb.epoxy.TypedEpoxyController
import im.vector.matrix.android.api.session.crypto.verification.EmojiRepresentation
data class SasState(
val emojiList: List<EmojiRepresentation>
)
class SasEmojiController : TypedEpoxyController<SasState>() {
override fun buildModels(data: SasState?) {
if (data == null) return
data.emojiList.forEachIndexed { idx, emojiRepresentation ->
sasEmojiItem {
id(idx)
index(idx)
emojiRepresentation(emojiRepresentation)
}
}
}
}

View File

@ -0,0 +1,58 @@
/*
* Copyright 2019 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.features.debug.sas
import android.annotation.SuppressLint
import android.widget.TextView
import androidx.core.content.ContextCompat
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import im.vector.app.core.epoxy.VectorEpoxyHolder
import im.vector.app.core.epoxy.VectorEpoxyModel
import im.vector.matrix.android.api.session.crypto.verification.EmojiRepresentation
import me.gujun.android.span.image
import me.gujun.android.span.span
@EpoxyModelClass(layout = im.vector.app.R.layout.item_sas_emoji)
abstract class SasEmojiItem : VectorEpoxyModel<SasEmojiItem.Holder>() {
@EpoxyAttribute
var index: Int = 0
@EpoxyAttribute
lateinit var emojiRepresentation: EmojiRepresentation
@SuppressLint("SetTextI18n")
override fun bind(holder: Holder) {
super.bind(holder)
holder.indexView.text = "$index:"
holder.emojiView.text = span {
+emojiRepresentation.emoji
emojiRepresentation.drawableRes?.let {
image(ContextCompat.getDrawable(holder.view.context, it)!!)
}
}
holder.textView.setText(emojiRepresentation.nameResId)
holder.idView.text = holder.idView.resources.getResourceEntryName(emojiRepresentation.nameResId)
}
class Holder : VectorEpoxyHolder() {
val indexView by bind<TextView>(im.vector.app.R.id.sas_emoji_index)
val emojiView by bind<TextView>(im.vector.app.R.id.sas_emoji)
val textView by bind<TextView>(im.vector.app.R.id.sas_emoji_text)
val idView by bind<TextView>(im.vector.app.R.id.sas_emoji_text_id)
}
}

View File

@ -0,0 +1,77 @@
/*
* Copyright 2019 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.receivers
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.SharedPreferences
import androidx.core.content.edit
import androidx.preference.PreferenceManager
import im.vector.app.core.utils.lsFiles
import timber.log.Timber
/**
* Receiver to handle some command from ADB
*/
class DebugReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Timber.v("Received debug action: ${intent.action}")
intent.action?.let {
when {
it.endsWith(DEBUG_ACTION_DUMP_FILESYSTEM) -> lsFiles(context)
it.endsWith(DEBUG_ACTION_DUMP_PREFERENCES) -> dumpPreferences(context)
it.endsWith(DEBUG_ACTION_ALTER_SCALAR_TOKEN) -> alterScalarToken(context)
}
}
}
private fun dumpPreferences(context: Context) {
logPrefs("DefaultSharedPreferences", PreferenceManager.getDefaultSharedPreferences(context))
}
private fun logPrefs(name: String, sharedPreferences: SharedPreferences?) {
Timber.v("SharedPreferences $name:")
sharedPreferences?.let { prefs ->
prefs.all.keys.forEach { key ->
Timber.v("$key : ${prefs.all[key]}")
}
}
}
private fun alterScalarToken(context: Context) {
PreferenceManager.getDefaultSharedPreferences(context).edit {
// putString("SCALAR_TOKEN_PREFERENCE_KEY" + Matrix.getInstance(context).defaultSession.myUserId, "bad_token")
}
}
companion object {
private const val DEBUG_ACTION_DUMP_FILESYSTEM = ".DEBUG_ACTION_DUMP_FILESYSTEM"
private const val DEBUG_ACTION_DUMP_PREFERENCES = ".DEBUG_ACTION_DUMP_PREFERENCES"
private const val DEBUG_ACTION_ALTER_SCALAR_TOKEN = ".DEBUG_ACTION_ALTER_SCALAR_TOKEN"
fun getIntentFilter(context: Context) = IntentFilter().apply {
addAction(context.packageName + DEBUG_ACTION_DUMP_FILESYSTEM)
addAction(context.packageName + DEBUG_ACTION_DUMP_PREFERENCES)
addAction(context.packageName + DEBUG_ACTION_ALTER_SCALAR_TOKEN)
}
}
}

View File

@ -1,65 +0,0 @@
/*
* Copyright 2019 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.riotx.features.debug
import android.os.Bundle
import android.view.Menu
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.google.android.material.snackbar.Snackbar
import im.vector.riotx.R
import im.vector.riotx.core.utils.toast
import kotlinx.android.synthetic.debug.activity_test_material_theme.*
// Rendering is not the same with VectorBaseActivity
abstract class DebugMaterialThemeActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_test_material_theme)
debugShowSnackbar.setOnClickListener {
Snackbar.make(debugMaterialCoordinator, "Snackbar!", Snackbar.LENGTH_SHORT)
.setAction("Action") { }
.show()
}
debugShowToast.setOnClickListener {
toast("Toast")
}
debugShowDialog.setOnClickListener {
AlertDialog.Builder(this)
.setMessage("Dialog content")
.setIcon(R.drawable.ic_settings_x)
.setPositiveButton("Positive", null)
.setNegativeButton("Negative", null)
.setNeutralButton("Neutral", null)
.show()
}
debugShowBottomSheet.setOnClickListener {
BottomSheetDialogFragment().show(supportFragmentManager, "TAG")
}
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.home, menu)
return true
}
}

View File

@ -1,19 +0,0 @@
/*
* Copyright 2019 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.riotx.features.debug
class DebugMaterialThemeDarkActivity : DebugMaterialThemeActivity()

View File

@ -1,19 +0,0 @@
/*
* Copyright 2019 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.riotx.features.debug
class DebugMaterialThemeLightActivity : DebugMaterialThemeActivity()

View File

@ -1,231 +0,0 @@
/*
* Copyright 2019 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.riotx.features.debug
import android.app.Activity
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import android.content.Intent
import android.os.Build
import androidx.core.app.NotificationCompat
import androidx.core.app.Person
import butterknife.OnClick
import im.vector.matrix.android.internal.crypto.verification.qrcode.toQrCodeData
import im.vector.riotx.R
import im.vector.riotx.core.di.ActiveSessionHolder
import im.vector.riotx.core.di.ScreenComponent
import im.vector.riotx.core.platform.VectorBaseActivity
import im.vector.riotx.core.utils.PERMISSIONS_FOR_TAKING_PHOTO
import im.vector.riotx.core.utils.PERMISSION_REQUEST_CODE_LAUNCH_CAMERA
import im.vector.riotx.core.utils.allGranted
import im.vector.riotx.core.utils.checkPermissions
import im.vector.riotx.core.utils.toast
import im.vector.riotx.features.debug.sas.DebugSasEmojiActivity
import im.vector.riotx.features.qrcode.QrCodeScannerActivity
import kotlinx.android.synthetic.debug.activity_debug_menu.*
import timber.log.Timber
import javax.inject.Inject
class DebugMenuActivity : VectorBaseActivity() {
override fun getLayoutRes() = R.layout.activity_debug_menu
@Inject
lateinit var activeSessionHolder: ActiveSessionHolder
override fun injectWith(injector: ScreenComponent) {
injector.inject(this)
}
private lateinit var buffer: ByteArray
override fun initUiAndData() {
// renderQrCode("https://www.example.org")
buffer = ByteArray(256)
for (i in buffer.indices) {
buffer[i] = i.toByte()
}
val string = buffer.toString(Charsets.ISO_8859_1)
renderQrCode(string)
}
private fun renderQrCode(text: String) {
debug_qr_code.setData(text, true)
}
@OnClick(R.id.debug_test_text_view_link)
fun testTextViewLink() {
startActivity(Intent(this, TestLinkifyActivity::class.java))
}
@OnClick(R.id.debug_show_sas_emoji)
fun showSasEmoji() {
startActivity(Intent(this, DebugSasEmojiActivity::class.java))
}
@OnClick(R.id.debug_test_notification)
fun testNotification() {
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
// Create channel first
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel =
NotificationChannel(
"CHAN",
"Channel name",
NotificationManager.IMPORTANCE_DEFAULT
)
channel.description = "Channel description"
(getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager).createNotificationChannel(channel)
val channel2 =
NotificationChannel(
"CHAN2",
"Channel name 2",
NotificationManager.IMPORTANCE_DEFAULT
)
channel2.description = "Channel description 2"
(getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager).createNotificationChannel(channel2)
}
val builder = NotificationCompat.Builder(this, "CHAN")
.setWhen(System.currentTimeMillis())
.setContentTitle("Title")
.setContentText("Content")
// No effect because it's a group summary notif
.setNumber(33)
.setSmallIcon(R.drawable.ic_status_bar)
// This provocate the badge issue: no badge for group notification
.setGroup("GroupKey")
.setGroupSummary(true)
val messagingStyle1 = NotificationCompat.MessagingStyle(
Person.Builder()
.setName("User name")
.build()
)
.addMessage("Message 1 - 1", System.currentTimeMillis(), Person.Builder().setName("user 1-1").build())
.addMessage("Message 1 - 2", System.currentTimeMillis(), Person.Builder().setName("user 1-2").build())
val messagingStyle2 = NotificationCompat.MessagingStyle(
Person.Builder()
.setName("User name 2")
.build()
)
.addMessage("Message 2 - 1", System.currentTimeMillis(), Person.Builder().setName("user 1-1").build())
.addMessage("Message 2 - 2", System.currentTimeMillis(), Person.Builder().setName("user 1-2").build())
notificationManager.notify(10, builder.build())
notificationManager.notify(
11,
NotificationCompat.Builder(this, "CHAN")
.setChannelId("CHAN")
.setWhen(System.currentTimeMillis())
.setContentTitle("Title 1")
.setContentText("Content 1")
// For shortcut on long press on launcher icon
.setBadgeIconType(NotificationCompat.BADGE_ICON_NONE)
.setStyle(messagingStyle1)
.setSmallIcon(R.drawable.ic_status_bar)
.setGroup("GroupKey")
.build()
)
notificationManager.notify(
12,
NotificationCompat.Builder(this, "CHAN2")
.setWhen(System.currentTimeMillis())
.setContentTitle("Title 2")
.setContentText("Content 2")
.setStyle(messagingStyle2)
.setSmallIcon(R.drawable.ic_status_bar)
.setGroup("GroupKey")
.build()
)
}
@OnClick(R.id.debug_test_material_theme_light)
fun testMaterialThemeLight() {
startActivity(Intent(this, DebugMaterialThemeLightActivity::class.java))
}
@OnClick(R.id.debug_test_material_theme_dark)
fun testMaterialThemeDark() {
startActivity(Intent(this, DebugMaterialThemeDarkActivity::class.java))
}
@OnClick(R.id.debug_test_crash)
fun testCrash() {
throw RuntimeException("Application crashed from user demand")
}
@OnClick(R.id.debug_scan_qr_code)
fun scanQRCode() {
if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, this, PERMISSION_REQUEST_CODE_LAUNCH_CAMERA)) {
doScanQRCode()
}
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == PERMISSION_REQUEST_CODE_LAUNCH_CAMERA && allGranted(grantResults)) {
doScanQRCode()
}
}
private fun doScanQRCode() {
QrCodeScannerActivity.startForResult(this)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_OK) {
when (requestCode) {
QrCodeScannerActivity.QR_CODE_SCANNER_REQUEST_CODE -> {
toast("QrCode: " + QrCodeScannerActivity.getResultText(data) + " is QRCode: " + QrCodeScannerActivity.getResultIsQrCode(data))
// Also update the current QR Code (reverse operation)
// renderQrCode(QrCodeScannerActivity.getResultText(data) ?: "")
val result = QrCodeScannerActivity.getResultText(data)!!
val qrCodeData = result.toQrCodeData()
Timber.e("qrCodeData: $qrCodeData")
if (result.length != buffer.size) {
Timber.e("Error, length are not the same")
} else {
// Convert to ByteArray
val byteArrayResult = result.toByteArray(Charsets.ISO_8859_1)
for (i in byteArrayResult.indices) {
if (buffer[i] != byteArrayResult[i]) {
Timber.e("Error for byte $i, expecting ${buffer[i]} and get ${byteArrayResult[i]}")
}
}
}
}
}
}
}
}

View File

@ -1,131 +0,0 @@
/*
* Copyright 2019 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.riotx.features.debug
import android.os.Bundle
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.coordinatorlayout.widget.CoordinatorLayout
import butterknife.BindView
import butterknife.ButterKnife
import im.vector.riotx.R
class TestLinkifyActivity : AppCompatActivity() {
@BindView(R.id.test_linkify_content_view)
lateinit var scrollContent: LinearLayout
@BindView(R.id.test_linkify_coordinator)
lateinit var coordinatorLayout: CoordinatorLayout
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_test_linkify)
ButterKnife.bind(this)
scrollContent.removeAllViews()
listOf(
"https://www.html5rocks.com/en/tutorials/webrtc/basics/ |",
"https://www.html5rocks.com/en/tutorials/webrtc/basics/",
"mailto mailto:test@toto.com test@toto.com",
"Here is the link.www.test.com/foo/?23=35 you got it?",
"www.lemonde.fr",
" /www.lemonde.fr",
"://www.lemonde.fr",
"file:///dev/null ",
" ansible/xoxys.matrix#2c0b65eb",
"foo.ansible/xoxys.matrix#2c0b65eb",
"foo.ansible.fpo/xoxys.matrix#2c0b65eb",
"https://foo.ansible.fpo/xoxys.matrix#2c0b65eb",
"@vf:matrix.org",
"+44 207 123 1234",
"+33141437940",
"1234",
"3456.34,089",
"ksks9808",
"For example: geo:48.85828,2.29449?z=16 should be clickable",
"geo:37.786971,-122.399677;u=35",
"37.786971,-122.399677;u=35",
"48.107864,-1.712153",
"synchrone peut tenir la route la",
"that.is.some.sexy.link",
"test overlap 48.107864,0673728392 geo + pn?",
"test overlap 0673728392,48.107864 geo + pn?",
"If I add a link in brackets like (help for Riot: https://about.riot.im/help), the link is usable on Riot for Desktop",
"(help for Riot: https://about.riot.im/help)",
"http://example.com/test(1).html",
"http://example.com/test(1)",
"https://about.riot.im/help)",
"(http://example.com/test(1))",
"http://example.com/test1)",
"http://example.com/test1/, et ca",
"www.example.com/, et ca",
"foo.ansible.toplevel/xoxys.matrix#2c0b65eb",
"foo.ansible.ninja/xoxys.matrix#2c0b65eb",
"in brackets like (help for Riot: https://www.exemple/com/find(1)) , the link is usable ",
"""
In brackets like (help for Riot: https://about.riot.im/help) , the link is usable,
But you can call +44 207 123 1234 and come to 37.786971,-122.399677;u=35 then
see if this mail jhon@riot.im is active but this should not 12345
""".trimIndent()
)
.forEach { textContent ->
val item = LayoutInflater.from(this)
.inflate(R.layout.item_test_linkify, scrollContent, false)
item.findViewById<TextView>(R.id.test_linkify_auto_text)
?.apply {
text = textContent
/* TODO Use BetterLinkMovementMethod when the other PR is merged
movementMethod = MatrixLinkMovementMethod(object : MockMessageAdapterActionListener() {
override fun onURLClick(uri: Uri?) {
Snackbar.make(coordinatorLayout, "URI Clicked: $uri", Snackbar.LENGTH_LONG)
.setAction("open") {
openUrlInExternalBrowser(this@TestLinkifyActivity, uri)
}
.show()
}
})
*/
}
item.findViewById<TextView>(R.id.test_linkify_custom_text)
?.apply {
text = textContent
/* TODO Use BetterLinkMovementMethod when the other PR is merged
movementMethod = MatrixLinkMovementMethod(object : MockMessageAdapterActionListener() {
override fun onURLClick(uri: Uri?) {
Snackbar.make(coordinatorLayout, "URI Clicked: $uri", Snackbar.LENGTH_LONG)
.setAction("open") {
openUrlInExternalBrowser(this@TestLinkifyActivity, uri)
}
.show()
}
})
*/
// TODO Call VectorLinkify.addLinks(text)
}
scrollContent.addView(item, ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT))
}
}
}

View File

@ -1,41 +0,0 @@
/*
* Copyright 2019 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.riotx.features.debug.sas
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import im.vector.matrix.android.api.crypto.getAllVerificationEmojis
import im.vector.riotx.R
import im.vector.riotx.core.extensions.cleanup
import im.vector.riotx.core.extensions.configureWith
import kotlinx.android.synthetic.main.fragment_generic_recycler.*
class DebugSasEmojiActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.fragment_generic_recycler)
val controller = SasEmojiController()
recyclerView.configureWith(controller)
controller.setData(SasState(getAllVerificationEmojis()))
}
override fun onDestroy() {
recyclerView.cleanup()
super.onDestroy()
}
}

View File

@ -1,39 +0,0 @@
/*
* Copyright 2019 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.riotx.features.debug.sas
import com.airbnb.epoxy.TypedEpoxyController
import im.vector.matrix.android.api.session.crypto.verification.EmojiRepresentation
data class SasState(
val emojiList: List<EmojiRepresentation>
)
class SasEmojiController : TypedEpoxyController<SasState>() {
override fun buildModels(data: SasState?) {
if (data == null) return
data.emojiList.forEachIndexed { idx, emojiRepresentation ->
sasEmojiItem {
id(idx)
index(idx)
emojiRepresentation(emojiRepresentation)
}
}
}
}

View File

@ -1,58 +0,0 @@
/*
* Copyright 2019 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.riotx.features.debug.sas
import android.annotation.SuppressLint
import android.widget.TextView
import androidx.core.content.ContextCompat
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import im.vector.matrix.android.api.session.crypto.verification.EmojiRepresentation
import im.vector.riotx.core.epoxy.VectorEpoxyHolder
import im.vector.riotx.core.epoxy.VectorEpoxyModel
import me.gujun.android.span.image
import me.gujun.android.span.span
@EpoxyModelClass(layout = im.vector.riotx.R.layout.item_sas_emoji)
abstract class SasEmojiItem : VectorEpoxyModel<SasEmojiItem.Holder>() {
@EpoxyAttribute
var index: Int = 0
@EpoxyAttribute
lateinit var emojiRepresentation: EmojiRepresentation
@SuppressLint("SetTextI18n")
override fun bind(holder: Holder) {
super.bind(holder)
holder.indexView.text = "$index:"
holder.emojiView.text = span {
+emojiRepresentation.emoji
emojiRepresentation.drawableRes?.let {
image(ContextCompat.getDrawable(holder.view.context, it)!!)
}
}
holder.textView.setText(emojiRepresentation.nameResId)
holder.idView.text = holder.idView.resources.getResourceEntryName(emojiRepresentation.nameResId)
}
class Holder : VectorEpoxyHolder() {
val indexView by bind<TextView>(im.vector.riotx.R.id.sas_emoji_index)
val emojiView by bind<TextView>(im.vector.riotx.R.id.sas_emoji)
val textView by bind<TextView>(im.vector.riotx.R.id.sas_emoji_text)
val idView by bind<TextView>(im.vector.riotx.R.id.sas_emoji_text_id)
}
}

View File

@ -1,77 +0,0 @@
/*
* Copyright 2019 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.riotx.receivers
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.SharedPreferences
import androidx.preference.PreferenceManager
import androidx.core.content.edit
import im.vector.riotx.core.utils.lsFiles
import timber.log.Timber
/**
* Receiver to handle some command from ADB
*/
class DebugReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Timber.v("Received debug action: ${intent.action}")
intent.action?.let {
when {
it.endsWith(DEBUG_ACTION_DUMP_FILESYSTEM) -> lsFiles(context)
it.endsWith(DEBUG_ACTION_DUMP_PREFERENCES) -> dumpPreferences(context)
it.endsWith(DEBUG_ACTION_ALTER_SCALAR_TOKEN) -> alterScalarToken(context)
}
}
}
private fun dumpPreferences(context: Context) {
logPrefs("DefaultSharedPreferences", PreferenceManager.getDefaultSharedPreferences(context))
}
private fun logPrefs(name: String, sharedPreferences: SharedPreferences?) {
Timber.v("SharedPreferences $name:")
sharedPreferences?.let { prefs ->
prefs.all.keys.forEach { key ->
Timber.v("$key : ${prefs.all[key]}")
}
}
}
private fun alterScalarToken(context: Context) {
PreferenceManager.getDefaultSharedPreferences(context).edit {
// putString("SCALAR_TOKEN_PREFERENCE_KEY" + Matrix.getInstance(context).defaultSession.myUserId, "bad_token")
}
}
companion object {
private const val DEBUG_ACTION_DUMP_FILESYSTEM = ".DEBUG_ACTION_DUMP_FILESYSTEM"
private const val DEBUG_ACTION_DUMP_PREFERENCES = ".DEBUG_ACTION_DUMP_PREFERENCES"
private const val DEBUG_ACTION_ALTER_SCALAR_TOKEN = ".DEBUG_ACTION_ALTER_SCALAR_TOKEN"
fun getIntentFilter(context: Context) = IntentFilter().apply {
addAction(context.packageName + DEBUG_ACTION_DUMP_FILESYSTEM)
addAction(context.packageName + DEBUG_ACTION_DUMP_PREFERENCES)
addAction(context.packageName + DEBUG_ACTION_ALTER_SCALAR_TOKEN)
}
}
}

View File

@ -3,7 +3,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="im.vector.riotx.features.debug.DebugMenuActivity"
tools:context="im.vector.app.features.debug.DebugMenuActivity"
tools:ignore="HardcodedText">
<ScrollView
@ -68,7 +68,7 @@
android:layout_height="wrap_content"
android:text="Scan QR-code" />
<im.vector.riotx.core.ui.views.QrCodeImageView
<im.vector.app.core.ui.views.QrCodeImageView
android:id="@+id/debug_qr_code"
android:layout_width="200dp"
android:layout_height="200dp"

View File

@ -5,7 +5,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/riot_secondary_text_color_status"
tools:context="im.vector.riotx.features.debug.TestLinkifyActivity">
tools:context="im.vector.app.features.debug.TestLinkifyActivity">
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="im.vector.riotx">
package="im.vector.app">
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

View File

@ -0,0 +1,22 @@
/*
* Copyright 2020 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.content.Context
// No op
fun openOssLicensesMenuActivity(@Suppress("UNUSED_PARAMETER") context: Context) = Unit

View File

@ -0,0 +1,47 @@
/*
* Copyright 2018 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.fdroid.features.settings.troubleshoot
import im.vector.app.R
import im.vector.app.core.resources.StringProvider
import im.vector.app.features.settings.VectorPreferences
import im.vector.app.features.settings.troubleshoot.TroubleshootTest
import javax.inject.Inject
/**
* Test that the application is started on boot
*/
class TestAutoStartBoot @Inject constructor(private val vectorPreferences: VectorPreferences,
private val stringProvider: StringProvider)
: TroubleshootTest(R.string.settings_troubleshoot_test_service_boot_title) {
override fun perform() {
if (vectorPreferences.autoStartOnBoot()) {
description = stringProvider.getString(R.string.settings_troubleshoot_test_service_boot_success)
status = TestStatus.SUCCESS
quickFix = null
} else {
description = stringProvider.getString(R.string.settings_troubleshoot_test_service_boot_failed)
quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_service_boot_quickfix) {
override fun doFix() {
vectorPreferences.setAutoStartOnBoot(true)
manager?.retry()
}
}
status = TestStatus.FAILED
}
}
}

View File

@ -0,0 +1,71 @@
/*
* Copyright 2018 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.fdroid.features.settings.troubleshoot
import android.content.Context
import android.net.ConnectivityManager
import androidx.appcompat.app.AppCompatActivity
import androidx.core.net.ConnectivityManagerCompat
import im.vector.app.R
import im.vector.app.core.resources.StringProvider
import im.vector.app.features.settings.troubleshoot.TroubleshootTest
import javax.inject.Inject
class TestBackgroundRestrictions @Inject constructor(private val context: AppCompatActivity,
private val stringProvider: StringProvider)
: TroubleshootTest(R.string.settings_troubleshoot_test_bg_restricted_title) {
override fun perform() {
(context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager).apply {
// Checks if the device is on a metered network
if (isActiveNetworkMetered) {
// Checks users Data Saver settings.
when (ConnectivityManagerCompat.getRestrictBackgroundStatus(this)) {
ConnectivityManagerCompat.RESTRICT_BACKGROUND_STATUS_ENABLED -> {
// Background data usage is blocked for this app. Wherever possible,
// the app should also use less data in the foreground.
description = stringProvider.getString(R.string.settings_troubleshoot_test_bg_restricted_failed,
"RESTRICT_BACKGROUND_STATUS_ENABLED")
status = TestStatus.FAILED
quickFix = null
}
ConnectivityManagerCompat.RESTRICT_BACKGROUND_STATUS_WHITELISTED -> {
// The app is whitelisted. Wherever possible,
// the app should use less data in the foreground and background.
description = stringProvider.getString(R.string.settings_troubleshoot_test_bg_restricted_success,
"RESTRICT_BACKGROUND_STATUS_WHITELISTED")
status = TestStatus.SUCCESS
quickFix = null
}
ConnectivityManagerCompat.RESTRICT_BACKGROUND_STATUS_DISABLED -> {
// Data Saver is disabled. Since the device is connected to a
// metered network, the app should use less data wherever possible.
description = stringProvider.getString(R.string.settings_troubleshoot_test_bg_restricted_success,
"RESTRICT_BACKGROUND_STATUS_DISABLED")
status = TestStatus.SUCCESS
quickFix = null
}
}
} else {
// The device is not on a metered network.
// Use data as required to perform syncs, downloads, and updates.
description = stringProvider.getString(R.string.settings_troubleshoot_test_bg_restricted_success, "")
status = TestStatus.SUCCESS
quickFix = null
}
}
}
}

View File

@ -0,0 +1,46 @@
/*
* Copyright 2018 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.fdroid.features.settings.troubleshoot
import androidx.fragment.app.Fragment
import im.vector.app.R
import im.vector.app.core.utils.isIgnoringBatteryOptimizations
import im.vector.app.core.utils.requestDisablingBatteryOptimization
import im.vector.app.features.settings.troubleshoot.NotificationTroubleshootTestManager
import im.vector.app.features.settings.troubleshoot.TroubleshootTest
// Not used anymore
class TestBatteryOptimization(val fragment: Fragment) : TroubleshootTest(R.string.settings_troubleshoot_test_battery_title) {
override fun perform() {
val context = fragment.context
if (context != null && isIgnoringBatteryOptimizations(context)) {
description = fragment.getString(R.string.settings_troubleshoot_test_battery_success)
status = TestStatus.SUCCESS
quickFix = null
} else {
description = fragment.getString(R.string.settings_troubleshoot_test_battery_failed)
quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_battery_quickfix) {
override fun doFix() {
fragment.activity?.let {
requestDisablingBatteryOptimization(it, fragment, NotificationTroubleshootTestManager.REQ_CODE_FIX)
}
}
}
status = TestStatus.FAILED
}
}
}

View File

@ -0,0 +1,20 @@
/*
* Copyright 2019 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.
*/
/**
* Code exclusively used by the FDroid build and not referenced on the main source code
*/
package im.vector.app.fdroid

View File

@ -0,0 +1,93 @@
/*
* Copyright 2019 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.fdroid.receiver
import android.app.AlarmManager
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.PowerManager
import androidx.core.content.ContextCompat
import im.vector.app.core.di.HasVectorInjector
import im.vector.app.core.services.VectorSyncService
import im.vector.matrix.android.internal.session.sync.job.SyncService
import timber.log.Timber
class AlarmSyncBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val appContext = context.applicationContext
if (appContext is HasVectorInjector) {
val activeSession = appContext.injector().activeSessionHolder().getSafeActiveSession()
if (activeSession == null) {
Timber.v("No active session don't launch sync service.")
return
}
}
// Acquire a lock to give enough time for the sync :/
(context.getSystemService(Context.POWER_SERVICE) as PowerManager).run {
newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "riotx:fdroidSynclock").apply {
acquire((10_000).toLong())
}
}
val sessionId = intent.getStringExtra(SyncService.EXTRA_SESSION_ID) ?: return
// This method is called when the BroadcastReceiver is receiving an Intent broadcast.
Timber.d("RestartBroadcastReceiver received intent")
VectorSyncService.newIntent(context, sessionId).let {
try {
ContextCompat.startForegroundService(context, it)
} catch (ex: Throwable) {
// TODO
Timber.e(ex)
}
}
scheduleAlarm(context, sessionId, 30_000L)
Timber.i("Alarm scheduled to restart service")
}
companion object {
private const val REQUEST_CODE = 0
fun scheduleAlarm(context: Context, sessionId: String, delay: Long) {
// Reschedule
val intent = Intent(context, AlarmSyncBroadcastReceiver::class.java).apply {
putExtra(SyncService.EXTRA_SESSION_ID, sessionId)
}
val pIntent = PendingIntent.getBroadcast(context, REQUEST_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT)
val firstMillis = System.currentTimeMillis() + delay
val alarmMgr = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
alarmMgr.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, firstMillis, pIntent)
} else {
alarmMgr.set(AlarmManager.RTC_WAKEUP, firstMillis, pIntent)
}
}
fun cancelAlarm(context: Context) {
Timber.v("Cancel alarm")
val intent = Intent(context, AlarmSyncBroadcastReceiver::class.java)
val pIntent = PendingIntent.getBroadcast(context, REQUEST_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT)
val alarmMgr = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
alarmMgr.cancel(pIntent)
}
}
}

View File

@ -0,0 +1,38 @@
/*
* Copyright 2018 New Vector Ltd
* Copyright 2019 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.fdroid.receiver
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import im.vector.app.core.di.HasVectorInjector
import timber.log.Timber
class OnApplicationUpgradeOrRebootReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Timber.v("## onReceive() ${intent.action}")
val appContext = context.applicationContext
if (appContext is HasVectorInjector) {
val activeSession = appContext.injector().activeSessionHolder().getSafeActiveSession()
if (activeSession != null) {
AlarmSyncBroadcastReceiver.scheduleAlarm(context, activeSession.sessionId, 10)
}
}
}
}

View File

@ -0,0 +1,76 @@
/*
* Copyright 2014 OpenMarket Ltd
* Copyright 2018 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.
*/
@file:Suppress("UNUSED_PARAMETER")
package im.vector.app.push.fcm
import android.app.Activity
import android.content.Context
import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.pushers.PushersManager
import im.vector.app.fdroid.receiver.AlarmSyncBroadcastReceiver
import im.vector.app.features.settings.VectorPreferences
import timber.log.Timber
/**
* This class has an alter ego in the gplay variant.
*/
object FcmHelper {
fun isPushSupported(): Boolean = false
/**
* Retrieves the FCM registration token.
*
* @return the FCM token or null if not received from FCM
*/
fun getFcmToken(context: Context): String? {
return null
}
/**
* Store FCM token to the SharedPrefs
*
* @param context android context
* @param token the token to store
*/
fun storeFcmToken(context: Context, token: String?) {
// No op
}
/**
* onNewToken may not be called on application upgrade, so ensure my shared pref is set
*
* @param activity the first launch Activity
*/
fun ensureFcmTokenIsRetrieved(activity: Activity, pushersManager: PushersManager, registerPusher: Boolean) {
// No op
}
fun onEnterForeground(context: Context) {
AlarmSyncBroadcastReceiver.cancelAlarm(context)
}
fun onEnterBackground(context: Context, vectorPreferences: VectorPreferences, activeSessionHolder: ActiveSessionHolder) {
// We need to use alarm in this mode
if (vectorPreferences.areNotificationEnabledForDevice() && activeSessionHolder.hasActiveSession()) {
val currentSession = activeSessionHolder.getActiveSession()
AlarmSyncBroadcastReceiver.scheduleAlarm(context, currentSession.sessionId, 4_000L)
Timber.i("Alarm scheduled to restart service")
}
}
}

View File

@ -0,0 +1,45 @@
/*
* Copyright 2018 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.push.fcm
import androidx.fragment.app.Fragment
import im.vector.app.fdroid.features.settings.troubleshoot.TestAutoStartBoot
import im.vector.app.fdroid.features.settings.troubleshoot.TestBackgroundRestrictions
import im.vector.app.features.settings.troubleshoot.NotificationTroubleshootTestManager
import im.vector.app.features.settings.troubleshoot.TestAccountSettings
import im.vector.app.features.settings.troubleshoot.TestDeviceSettings
import im.vector.app.features.settings.troubleshoot.TestPushRulesSettings
import im.vector.app.features.settings.troubleshoot.TestSystemSettings
import javax.inject.Inject
class NotificationTroubleshootTestManagerFactory @Inject constructor(private val testSystemSettings: TestSystemSettings,
private val testAccountSettings: TestAccountSettings,
private val testDeviceSettings: TestDeviceSettings,
private val testPushRulesSettings: TestPushRulesSettings,
private val testAutoStartBoot: TestAutoStartBoot,
private val testBackgroundRestrictions: TestBackgroundRestrictions) {
fun create(fragment: Fragment): NotificationTroubleshootTestManager {
val mgr = NotificationTroubleshootTestManager(fragment)
mgr.addTest(testSystemSettings)
mgr.addTest(testAccountSettings)
mgr.addTest(testDeviceSettings)
mgr.addTest(testPushRulesSettings)
mgr.addTest(testAutoStartBoot)
mgr.addTest(testBackgroundRestrictions)
return mgr
}
}

View File

@ -1,22 +0,0 @@
/*
* Copyright 2020 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.riotx
import android.content.Context
// No op
fun openOssLicensesMenuActivity(@Suppress("UNUSED_PARAMETER") context: Context) = Unit

View File

@ -1,47 +0,0 @@
/*
* Copyright 2018 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.riotx.fdroid.features.settings.troubleshoot
import im.vector.riotx.R
import im.vector.riotx.core.resources.StringProvider
import im.vector.riotx.features.settings.VectorPreferences
import im.vector.riotx.features.settings.troubleshoot.TroubleshootTest
import javax.inject.Inject
/**
* Test that the application is started on boot
*/
class TestAutoStartBoot @Inject constructor(private val vectorPreferences: VectorPreferences,
private val stringProvider: StringProvider)
: TroubleshootTest(R.string.settings_troubleshoot_test_service_boot_title) {
override fun perform() {
if (vectorPreferences.autoStartOnBoot()) {
description = stringProvider.getString(R.string.settings_troubleshoot_test_service_boot_success)
status = TestStatus.SUCCESS
quickFix = null
} else {
description = stringProvider.getString(R.string.settings_troubleshoot_test_service_boot_failed)
quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_service_boot_quickfix) {
override fun doFix() {
vectorPreferences.setAutoStartOnBoot(true)
manager?.retry()
}
}
status = TestStatus.FAILED
}
}
}

View File

@ -1,71 +0,0 @@
/*
* Copyright 2018 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.riotx.fdroid.features.settings.troubleshoot
import android.content.Context
import android.net.ConnectivityManager
import androidx.appcompat.app.AppCompatActivity
import androidx.core.net.ConnectivityManagerCompat
import im.vector.riotx.R
import im.vector.riotx.core.resources.StringProvider
import im.vector.riotx.features.settings.troubleshoot.TroubleshootTest
import javax.inject.Inject
class TestBackgroundRestrictions @Inject constructor(private val context: AppCompatActivity,
private val stringProvider: StringProvider)
: TroubleshootTest(R.string.settings_troubleshoot_test_bg_restricted_title) {
override fun perform() {
(context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager).apply {
// Checks if the device is on a metered network
if (isActiveNetworkMetered) {
// Checks users Data Saver settings.
when (ConnectivityManagerCompat.getRestrictBackgroundStatus(this)) {
ConnectivityManagerCompat.RESTRICT_BACKGROUND_STATUS_ENABLED -> {
// Background data usage is blocked for this app. Wherever possible,
// the app should also use less data in the foreground.
description = stringProvider.getString(R.string.settings_troubleshoot_test_bg_restricted_failed,
"RESTRICT_BACKGROUND_STATUS_ENABLED")
status = TestStatus.FAILED
quickFix = null
}
ConnectivityManagerCompat.RESTRICT_BACKGROUND_STATUS_WHITELISTED -> {
// The app is whitelisted. Wherever possible,
// the app should use less data in the foreground and background.
description = stringProvider.getString(R.string.settings_troubleshoot_test_bg_restricted_success,
"RESTRICT_BACKGROUND_STATUS_WHITELISTED")
status = TestStatus.SUCCESS
quickFix = null
}
ConnectivityManagerCompat.RESTRICT_BACKGROUND_STATUS_DISABLED -> {
// Data Saver is disabled. Since the device is connected to a
// metered network, the app should use less data wherever possible.
description = stringProvider.getString(R.string.settings_troubleshoot_test_bg_restricted_success,
"RESTRICT_BACKGROUND_STATUS_DISABLED")
status = TestStatus.SUCCESS
quickFix = null
}
}
} else {
// The device is not on a metered network.
// Use data as required to perform syncs, downloads, and updates.
description = stringProvider.getString(R.string.settings_troubleshoot_test_bg_restricted_success, "")
status = TestStatus.SUCCESS
quickFix = null
}
}
}
}

View File

@ -1,46 +0,0 @@
/*
* Copyright 2018 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.riotx.fdroid.features.settings.troubleshoot
import androidx.fragment.app.Fragment
import im.vector.riotx.R
import im.vector.riotx.core.utils.isIgnoringBatteryOptimizations
import im.vector.riotx.core.utils.requestDisablingBatteryOptimization
import im.vector.riotx.features.settings.troubleshoot.NotificationTroubleshootTestManager
import im.vector.riotx.features.settings.troubleshoot.TroubleshootTest
// Not used anymore
class TestBatteryOptimization(val fragment: Fragment) : TroubleshootTest(R.string.settings_troubleshoot_test_battery_title) {
override fun perform() {
val context = fragment.context
if (context != null && isIgnoringBatteryOptimizations(context)) {
description = fragment.getString(R.string.settings_troubleshoot_test_battery_success)
status = TestStatus.SUCCESS
quickFix = null
} else {
description = fragment.getString(R.string.settings_troubleshoot_test_battery_failed)
quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_battery_quickfix) {
override fun doFix() {
fragment.activity?.let {
requestDisablingBatteryOptimization(it, fragment, NotificationTroubleshootTestManager.REQ_CODE_FIX)
}
}
}
status = TestStatus.FAILED
}
}
}

View File

@ -1,20 +0,0 @@
/*
* Copyright 2019 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.
*/
/**
* Code exclusively used by the FDroid build and not referenced on the main source code
*/
package im.vector.riotx.fdroid

View File

@ -1,93 +0,0 @@
/*
* Copyright 2019 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.riotx.fdroid.receiver
import android.app.AlarmManager
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.PowerManager
import androidx.core.content.ContextCompat
import im.vector.matrix.android.internal.session.sync.job.SyncService
import im.vector.riotx.core.di.HasVectorInjector
import im.vector.riotx.core.services.VectorSyncService
import timber.log.Timber
class AlarmSyncBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val appContext = context.applicationContext
if (appContext is HasVectorInjector) {
val activeSession = appContext.injector().activeSessionHolder().getSafeActiveSession()
if (activeSession == null) {
Timber.v("No active session don't launch sync service.")
return
}
}
// Acquire a lock to give enough time for the sync :/
(context.getSystemService(Context.POWER_SERVICE) as PowerManager).run {
newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "riotx:fdroidSynclock").apply {
acquire((10_000).toLong())
}
}
val sessionId = intent.getStringExtra(SyncService.EXTRA_SESSION_ID) ?: return
// This method is called when the BroadcastReceiver is receiving an Intent broadcast.
Timber.d("RestartBroadcastReceiver received intent")
VectorSyncService.newIntent(context, sessionId).let {
try {
ContextCompat.startForegroundService(context, it)
} catch (ex: Throwable) {
// TODO
Timber.e(ex)
}
}
scheduleAlarm(context, sessionId, 30_000L)
Timber.i("Alarm scheduled to restart service")
}
companion object {
private const val REQUEST_CODE = 0
fun scheduleAlarm(context: Context, sessionId: String, delay: Long) {
// Reschedule
val intent = Intent(context, AlarmSyncBroadcastReceiver::class.java).apply {
putExtra(SyncService.EXTRA_SESSION_ID, sessionId)
}
val pIntent = PendingIntent.getBroadcast(context, REQUEST_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT)
val firstMillis = System.currentTimeMillis() + delay
val alarmMgr = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
alarmMgr.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, firstMillis, pIntent)
} else {
alarmMgr.set(AlarmManager.RTC_WAKEUP, firstMillis, pIntent)
}
}
fun cancelAlarm(context: Context) {
Timber.v("Cancel alarm")
val intent = Intent(context, AlarmSyncBroadcastReceiver::class.java)
val pIntent = PendingIntent.getBroadcast(context, REQUEST_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT)
val alarmMgr = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
alarmMgr.cancel(pIntent)
}
}
}

View File

@ -1,38 +0,0 @@
/*
* Copyright 2018 New Vector Ltd
* Copyright 2019 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.riotx.fdroid.receiver
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import im.vector.riotx.core.di.HasVectorInjector
import timber.log.Timber
class OnApplicationUpgradeOrRebootReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Timber.v("## onReceive() ${intent.action}")
val appContext = context.applicationContext
if (appContext is HasVectorInjector) {
val activeSession = appContext.injector().activeSessionHolder().getSafeActiveSession()
if (activeSession != null) {
AlarmSyncBroadcastReceiver.scheduleAlarm(context, activeSession.sessionId, 10)
}
}
}
}

View File

@ -1,76 +0,0 @@
/*
* Copyright 2014 OpenMarket Ltd
* Copyright 2018 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.
*/
@file:Suppress("UNUSED_PARAMETER")
package im.vector.riotx.push.fcm
import android.app.Activity
import android.content.Context
import im.vector.riotx.core.di.ActiveSessionHolder
import im.vector.riotx.core.pushers.PushersManager
import im.vector.riotx.fdroid.receiver.AlarmSyncBroadcastReceiver
import im.vector.riotx.features.settings.VectorPreferences
import timber.log.Timber
/**
* This class has an alter ego in the gplay variant.
*/
object FcmHelper {
fun isPushSupported(): Boolean = false
/**
* Retrieves the FCM registration token.
*
* @return the FCM token or null if not received from FCM
*/
fun getFcmToken(context: Context): String? {
return null
}
/**
* Store FCM token to the SharedPrefs
*
* @param context android context
* @param token the token to store
*/
fun storeFcmToken(context: Context, token: String?) {
// No op
}
/**
* onNewToken may not be called on application upgrade, so ensure my shared pref is set
*
* @param activity the first launch Activity
*/
fun ensureFcmTokenIsRetrieved(activity: Activity, pushersManager: PushersManager, registerPusher: Boolean) {
// No op
}
fun onEnterForeground(context: Context) {
AlarmSyncBroadcastReceiver.cancelAlarm(context)
}
fun onEnterBackground(context: Context, vectorPreferences: VectorPreferences, activeSessionHolder: ActiveSessionHolder) {
// We need to use alarm in this mode
if (vectorPreferences.areNotificationEnabledForDevice() && activeSessionHolder.hasActiveSession()) {
val currentSession = activeSessionHolder.getActiveSession()
AlarmSyncBroadcastReceiver.scheduleAlarm(context, currentSession.sessionId, 4_000L)
Timber.i("Alarm scheduled to restart service")
}
}
}

View File

@ -1,45 +0,0 @@
/*
* Copyright 2018 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.riotx.push.fcm
import androidx.fragment.app.Fragment
import im.vector.riotx.fdroid.features.settings.troubleshoot.TestAutoStartBoot
import im.vector.riotx.fdroid.features.settings.troubleshoot.TestBackgroundRestrictions
import im.vector.riotx.features.settings.troubleshoot.NotificationTroubleshootTestManager
import im.vector.riotx.features.settings.troubleshoot.TestAccountSettings
import im.vector.riotx.features.settings.troubleshoot.TestPushRulesSettings
import im.vector.riotx.features.settings.troubleshoot.TestDeviceSettings
import im.vector.riotx.features.settings.troubleshoot.TestSystemSettings
import javax.inject.Inject
class NotificationTroubleshootTestManagerFactory @Inject constructor(private val testSystemSettings: TestSystemSettings,
private val testAccountSettings: TestAccountSettings,
private val testDeviceSettings: TestDeviceSettings,
private val testPushRulesSettings: TestPushRulesSettings,
private val testAutoStartBoot: TestAutoStartBoot,
private val testBackgroundRestrictions: TestBackgroundRestrictions) {
fun create(fragment: Fragment): NotificationTroubleshootTestManager {
val mgr = NotificationTroubleshootTestManager(fragment)
mgr.addTest(testSystemSettings)
mgr.addTest(testAccountSettings)
mgr.addTest(testDeviceSettings)
mgr.addTest(testPushRulesSettings)
mgr.addTest(testAutoStartBoot)
mgr.addTest(testBackgroundRestrictions)
return mgr
}
}

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="im.vector.riotx">
package="im.vector.app">
<application>

View File

@ -1,7 +1,7 @@
<?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.riotx">
package="im.vector.app">
<!-- Needed for VOIP call to detect and switch to headset-->
<uses-permission android:name="android.permission.BLUETOOTH" />
@ -38,7 +38,7 @@
android:required="false" />
<application
android:name=".VectorApplication"
android:name="im.vector.app.VectorApplication"
android:allowBackup="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
@ -54,13 +54,13 @@
android:value="9.9" />
<activity
android:name=".features.MainActivity"
android:name="im.vector.app.features.MainActivity"
android:theme="@style/AppTheme.Launcher" />
<!-- Activity alias for the launcher Activity (must be declared after the Activity it targets) -->
<activity-alias
android:name=".features.Alias"
android:targetActivity=".features.MainActivity">
android:targetActivity="im.vector.app.features.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@ -68,9 +68,9 @@
</intent-filter>
</activity-alias>
<activity android:name=".features.home.HomeActivity" />
<activity android:name="im.vector.app.features.home.HomeActivity" />
<activity
android:name=".features.login.LoginActivity"
android:name="im.vector.app.features.login.LoginActivity"
android:launchMode="singleTask"
android:windowSoftInputMode="adjustResize">
<!-- Add intent filter to handle redirection URL after SSO login in external browser -->
@ -84,50 +84,50 @@
<data android:host="element" />
</intent-filter>
</activity>
<activity android:name=".features.media.ImageMediaViewerActivity" />
<activity android:name="im.vector.app.features.media.ImageMediaViewerActivity" />
<activity
android:name=".features.media.VectorAttachmentViewerActivity"
android:name="im.vector.app.features.media.VectorAttachmentViewerActivity"
android:theme="@style/AppTheme.Transparent" />
<activity android:name=".features.media.BigImageViewerActivity" />
<activity android:name="im.vector.app.features.media.BigImageViewerActivity" />
<activity
android:name=".features.rageshake.BugReportActivity"
android:name="im.vector.app.features.rageshake.BugReportActivity"
android:label="@string/title_activity_bug_report" />
<activity
android:name=".features.settings.VectorSettingsActivity"
android:name="im.vector.app.features.settings.VectorSettingsActivity"
android:label="@string/title_activity_settings"
android:windowSoftInputMode="adjustResize" />
<activity android:name=".features.media.VideoMediaViewerActivity" />
<activity android:name="im.vector.app.features.media.VideoMediaViewerActivity" />
<activity
android:name=".features.crypto.keysbackup.restore.KeysBackupRestoreActivity"
android:name="im.vector.app.features.crypto.keysbackup.restore.KeysBackupRestoreActivity"
android:label="@string/title_activity_keys_backup_setup" />
<activity
android:name=".features.crypto.keysbackup.setup.KeysBackupSetupActivity"
android:name="im.vector.app.features.crypto.keysbackup.setup.KeysBackupSetupActivity"
android:label="@string/title_activity_keys_backup_restore" />
<activity
android:name=".features.crypto.keysbackup.settings.KeysBackupManageActivity"
android:name="im.vector.app.features.crypto.keysbackup.settings.KeysBackupManageActivity"
android:label="@string/encryption_message_recovery" />
<activity
android:name=".features.reactions.EmojiReactionPickerActivity"
android:name="im.vector.app.features.reactions.EmojiReactionPickerActivity"
android:label="@string/title_activity_emoji_reaction_picker" />
<activity android:name=".features.roomdirectory.createroom.CreateRoomActivity" />
<activity android:name=".features.roomdirectory.RoomDirectoryActivity" />
<activity android:name=".features.roomdirectory.roompreview.RoomPreviewActivity" />
<activity android:name=".features.home.room.filtered.FilteredRoomsActivity" />
<activity android:name="im.vector.app.features.roomdirectory.createroom.CreateRoomActivity" />
<activity android:name="im.vector.app.features.roomdirectory.RoomDirectoryActivity" />
<activity android:name="im.vector.app.features.roomdirectory.roompreview.RoomPreviewActivity" />
<activity android:name="im.vector.app.features.home.room.filtered.FilteredRoomsActivity" />
<activity
android:name=".features.home.room.detail.RoomDetailActivity"
android:parentActivityName=".features.home.HomeActivity">
android:name="im.vector.app.features.home.room.detail.RoomDetailActivity"
android:parentActivityName="im.vector.app.features.home.HomeActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".features.home.HomeActivity" />
android:value="im.vector.app.features.home.HomeActivity" />
</activity>
<activity android:name=".features.debug.DebugMenuActivity" />
<activity android:name="im.vector.riotx.features.createdirect.CreateDirectRoomActivity" />
<activity android:name="im.vector.riotx.features.invite.InviteUsersToRoomActivity" />
<activity android:name=".features.webview.VectorWebViewActivity" />
<activity android:name=".features.link.LinkHandlerActivity">
<activity android:name="im.vector.app.features.debug.DebugMenuActivity" />
<activity android:name="im.vector.app.features.createdirect.CreateDirectRoomActivity" />
<activity android:name="im.vector.app.features.invite.InviteUsersToRoomActivity" />
<activity android:name="im.vector.app.features.webview.VectorWebViewActivity" />
<activity android:name="im.vector.app.features.link.LinkHandlerActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
@ -142,11 +142,11 @@
</activity>
<activity
android:name=".features.share.IncomingShareActivity"
android:parentActivityName=".features.home.HomeActivity">
android:name="im.vector.app.features.share.IncomingShareActivity"
android:parentActivityName="im.vector.app.features.home.HomeActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".features.home.HomeActivity" />
android:value="im.vector.app.features.home.HomeActivity" />
<intent-filter>
<action android:name="android.intent.action.SEND" />
@ -164,13 +164,13 @@
</intent-filter>
</activity>
<activity android:name=".features.roomprofile.RoomProfileActivity" />
<activity android:name="im.vector.app.features.roomprofile.RoomProfileActivity" />
<activity android:name=".features.signout.hard.SignedOutActivity" />
<activity android:name="im.vector.app.features.signout.hard.SignedOutActivity" />
<activity
android:name=".features.signout.soft.SoftLogoutActivity"
android:name="im.vector.app.features.signout.soft.SoftLogoutActivity"
android:windowSoftInputMode="adjustResize" />
<activity android:name=".features.permalink.PermalinkHandlerActivity">
<activity android:name="im.vector.app.features.permalink.PermalinkHandlerActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
@ -184,33 +184,33 @@
</activity>
<activity
android:name=".features.roommemberprofile.RoomMemberProfileActivity"
android:parentActivityName=".features.home.HomeActivity">
android:name="im.vector.app.features.roommemberprofile.RoomMemberProfileActivity"
android:parentActivityName="im.vector.app.features.home.HomeActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".features.home.HomeActivity" />
android:value="im.vector.app.features.home.HomeActivity" />
</activity>
<activity android:name=".features.qrcode.QrCodeScannerActivity" />
<activity android:name="im.vector.app.features.qrcode.QrCodeScannerActivity" />
<activity android:name=".features.crypto.quads.SharedSecureStorageActivity" />
<activity android:name="im.vector.app.features.crypto.quads.SharedSecureStorageActivity" />
<activity
android:name="com.yalantis.ucrop.UCropActivity"
android:screenOrientation="portrait" />
<activity
android:name=".features.attachments.preview.AttachmentsPreviewActivity"
android:name="im.vector.app.features.attachments.preview.AttachmentsPreviewActivity"
android:theme="@style/AppTheme.AttachmentsPreview" />
<activity android:name=".features.call.VectorCallActivity" />
<activity android:name="im.vector.app.features.call.VectorCallActivity" />
<activity android:name=".features.terms.ReviewTermsActivity" />
<activity android:name=".features.widgets.WidgetActivity" />
<activity android:name=".features.pin.PinActivity" />
<activity android:name="im.vector.app.features.terms.ReviewTermsActivity" />
<activity android:name="im.vector.app.features.widgets.WidgetActivity" />
<activity android:name="im.vector.app.features.pin.PinActivity" />
<!-- Services -->
<service
android:name=".core.services.CallService"
android:name="im.vector.app.core.services.CallService"
android:exported="false">
<!-- in order to get headset button events -->
<intent-filter>
@ -219,11 +219,11 @@
</service>
<service
android:name=".core.services.VectorSyncService"
android:name="im.vector.app.core.services.VectorSyncService"
android:exported="false" />
<service
android:name=".features.call.telecom.VectorConnectionService"
android:name="im.vector.app.features.call.telecom.VectorConnectionService"
android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE">
<intent-filter>
<action android:name="android.telecom.ConnectionService" />
@ -233,12 +233,12 @@
<!-- Receivers -->
<receiver
android:name=".features.call.service.CallHeadsUpActionReceiver"
android:name="im.vector.app.features.call.service.CallHeadsUpActionReceiver"
android:exported="false" />
<!-- Exported false, should only be accessible from this app!! -->
<receiver
android:name=".features.notifications.NotificationBroadcastReceiver"
android:name="im.vector.app.features.notifications.NotificationBroadcastReceiver"
android:enabled="true"
android:exported="false" />

View File

@ -0,0 +1,27 @@
/*
* Copyright 2019 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 arrow.core.Option
import im.vector.matrix.android.api.session.Session
import im.vector.app.core.utils.BehaviorDataSource
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class ActiveSessionDataSource @Inject constructor() : BehaviorDataSource<Option<Session>>()

View File

@ -0,0 +1,96 @@
/*
* Copyright 2019 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 androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.OnLifecycleEvent
import arrow.core.Option
import im.vector.matrix.android.api.session.group.model.GroupSummary
import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.matrix.android.api.session.room.roomSummaryQueryParams
import im.vector.matrix.rx.rx
import im.vector.app.features.grouplist.ALL_COMMUNITIES_GROUP_ID
import im.vector.app.features.grouplist.SelectedGroupDataSource
import im.vector.app.features.home.HomeRoomListDataSource
import im.vector.app.features.home.room.list.ChronologicalRoomComparator
import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.functions.BiFunction
import io.reactivex.rxkotlin.addTo
import java.util.concurrent.TimeUnit
import javax.inject.Inject
import javax.inject.Singleton
/**
* This class handles the global app state. At the moment, it only manages room list.
* It requires to be added to ProcessLifecycleOwner.get().lifecycle
*/
@Singleton
class AppStateHandler @Inject constructor(
private val sessionDataSource: ActiveSessionDataSource,
private val homeRoomListDataSource: HomeRoomListDataSource,
private val selectedGroupDataSource: SelectedGroupDataSource,
private val chronologicalRoomComparator: ChronologicalRoomComparator) : LifecycleObserver {
private val compositeDisposable = CompositeDisposable()
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
fun entersForeground() {
observeRoomsAndGroup()
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
fun entersBackground() {
compositeDisposable.clear()
}
private fun observeRoomsAndGroup() {
Observable
.combineLatest<List<RoomSummary>, Option<GroupSummary>, List<RoomSummary>>(
sessionDataSource.observe()
.observeOn(AndroidSchedulers.mainThread())
.switchMap {
val query = roomSummaryQueryParams {}
it.orNull()?.rx()?.liveRoomSummaries(query)
?: Observable.just(emptyList())
}
.throttleLast(300, TimeUnit.MILLISECONDS),
selectedGroupDataSource.observe(),
BiFunction { rooms, selectedGroupOption ->
val selectedGroup = selectedGroupOption.orNull()
val filteredRooms = rooms.filter {
if (selectedGroup == null || selectedGroup.groupId == ALL_COMMUNITIES_GROUP_ID) {
true
} else if (it.isDirect) {
it.otherMemberIds
.intersect(selectedGroup.userIds)
.isNotEmpty()
} else {
selectedGroup.roomIds.contains(it.roomId)
}
}
filteredRooms.sortedWith(chronologicalRoomComparator)
}
)
.subscribe {
homeRoomListDataSource.post(it)
}
.addTo(compositeDisposable)
}
}

View File

@ -0,0 +1,65 @@
/*
* Copyright 2019 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.graphics.Typeface
import androidx.core.provider.FontsContractCompat
import timber.log.Timber
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class EmojiCompatFontProvider @Inject constructor(): FontsContractCompat.FontRequestCallback() {
var typeface: Typeface? = null
set(value) {
if (value != field) {
field = value
listeners.forEach {
try {
it.compatibilityFontUpdate(value)
} catch (t: Throwable) {
Timber.e(t)
}
}
}
}
private val listeners = ArrayList<FontProviderListener>()
override fun onTypefaceRetrieved(typeface: Typeface) {
this.typeface = typeface
}
override fun onTypefaceRequestFailed(reason: Int) {
Timber.e("Failed to load Emoji Compatible font, reason:$reason")
}
fun addListener(listener: FontProviderListener) {
if (listener !in listeners) {
listeners.add(listener)
}
}
fun removeListener(listener: FontProviderListener) {
listeners.remove(listener)
}
interface FontProviderListener {
fun compatibilityFontUpdate(typeface: Typeface?)
}
}

View File

@ -0,0 +1,65 @@
/*
* Copyright 2019 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.content.Context
import androidx.core.provider.FontRequest
import androidx.emoji.text.EmojiCompat
import androidx.emoji.text.FontRequestEmojiCompatConfig
import timber.log.Timber
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class EmojiCompatWrapper @Inject constructor(private val context: Context) {
private var initialized = false
fun init(fontRequest: FontRequest) {
// Use emoji compat for the benefit of emoji spans
val config = FontRequestEmojiCompatConfig(context, fontRequest)
// we want to replace all emojis with selected font
.setReplaceAll(true)
// Debug options
// .setEmojiSpanIndicatorEnabled(true)
// .setEmojiSpanIndicatorColor(Color.GREEN)
EmojiCompat.init(config)
.registerInitCallback(object : EmojiCompat.InitCallback() {
override fun onInitialized() {
Timber.v("Emoji compat onInitialized success ")
initialized = true
}
override fun onFailed(throwable: Throwable?) {
Timber.e(throwable, "Failed to init EmojiCompat")
}
})
}
fun safeEmojiSpanify(sequence: CharSequence): CharSequence {
if (initialized) {
try {
return EmojiCompat.get().process(sequence)
} catch (throwable: Throwable) {
// Defensive coding against error (should not happend as it is initialized)
Timber.e(throwable, "Failed to init EmojiCompat")
return sequence
}
} else {
return sequence
}
}
}

View File

@ -0,0 +1,210 @@
/*
* Copyright 2019 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.app.Application
import android.content.Context
import android.content.res.Configuration
import android.os.Handler
import android.os.HandlerThread
import androidx.core.provider.FontRequest
import androidx.core.provider.FontsContractCompat
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.OnLifecycleEvent
import androidx.lifecycle.ProcessLifecycleOwner
import androidx.multidex.MultiDex
import com.airbnb.epoxy.EpoxyAsyncUtil
import com.airbnb.epoxy.EpoxyController
import com.facebook.stetho.Stetho
import com.gabrielittner.threetenbp.LazyThreeTen
import im.vector.matrix.android.api.Matrix
import im.vector.matrix.android.api.MatrixConfiguration
import im.vector.matrix.android.api.auth.AuthenticationService
import im.vector.matrix.android.api.legacy.LegacySessionImporter
import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.di.DaggerVectorComponent
import im.vector.app.core.di.HasVectorInjector
import im.vector.app.core.di.VectorComponent
import im.vector.app.core.extensions.configureAndStart
import im.vector.app.core.rx.RxConfig
import im.vector.app.features.configuration.VectorConfiguration
import im.vector.app.features.disclaimer.doNotShowDisclaimerDialog
import im.vector.app.features.lifecycle.VectorActivityLifecycleCallbacks
import im.vector.app.features.notifications.NotificationDrawerManager
import im.vector.app.features.notifications.NotificationUtils
import im.vector.app.features.pin.PinLocker
import im.vector.app.features.popup.PopupAlertManager
import im.vector.app.features.rageshake.VectorUncaughtExceptionHandler
import im.vector.app.features.settings.VectorPreferences
import im.vector.app.features.version.VersionProvider
import im.vector.app.push.fcm.FcmHelper
import timber.log.Timber
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import java.util.concurrent.Executors
import javax.inject.Inject
import androidx.work.Configuration as WorkConfiguration
class VectorApplication :
Application(),
HasVectorInjector,
MatrixConfiguration.Provider,
WorkConfiguration.Provider {
lateinit var appContext: Context
@Inject lateinit var legacySessionImporter: LegacySessionImporter
@Inject lateinit var authenticationService: AuthenticationService
@Inject lateinit var vectorConfiguration: VectorConfiguration
@Inject lateinit var emojiCompatFontProvider: EmojiCompatFontProvider
@Inject lateinit var emojiCompatWrapper: EmojiCompatWrapper
@Inject lateinit var vectorUncaughtExceptionHandler: VectorUncaughtExceptionHandler
@Inject lateinit var activeSessionHolder: ActiveSessionHolder
@Inject lateinit var notificationDrawerManager: NotificationDrawerManager
@Inject lateinit var vectorPreferences: VectorPreferences
@Inject lateinit var versionProvider: VersionProvider
@Inject lateinit var notificationUtils: NotificationUtils
@Inject lateinit var appStateHandler: AppStateHandler
@Inject lateinit var rxConfig: RxConfig
@Inject lateinit var popupAlertManager: PopupAlertManager
@Inject lateinit var pinLocker: PinLocker
lateinit var vectorComponent: VectorComponent
// font thread handler
private var fontThreadHandler: Handler? = null
override fun onCreate() {
super.onCreate()
appContext = this
vectorComponent = DaggerVectorComponent.factory().create(this)
vectorComponent.inject(this)
vectorUncaughtExceptionHandler.activate(this)
rxConfig.setupRxPlugin()
if (BuildConfig.DEBUG) {
Timber.plant(Timber.DebugTree())
}
Timber.plant(vectorComponent.vectorFileLogger())
if (BuildConfig.DEBUG) {
Stetho.initializeWithDefaults(this)
}
logInfo()
LazyThreeTen.init(this)
EpoxyController.defaultDiffingHandler = EpoxyAsyncUtil.getAsyncBackgroundHandler()
EpoxyController.defaultModelBuildingHandler = EpoxyAsyncUtil.getAsyncBackgroundHandler()
registerActivityLifecycleCallbacks(VectorActivityLifecycleCallbacks(popupAlertManager))
val fontRequest = FontRequest(
"com.google.android.gms.fonts",
"com.google.android.gms",
"Noto Color Emoji Compat",
R.array.com_google_android_gms_fonts_certs
)
FontsContractCompat.requestFont(this, fontRequest, emojiCompatFontProvider, getFontThreadHandler())
vectorConfiguration.initConfiguration()
emojiCompatWrapper.init(fontRequest)
notificationUtils.createNotificationChannels()
// It can takes time, but do we care?
val sessionImported = legacySessionImporter.process()
if (!sessionImported) {
// Do not display the name change popup
doNotShowDisclaimerDialog(this)
}
if (authenticationService.hasAuthenticatedSessions() && !activeSessionHolder.hasActiveSession()) {
val lastAuthenticatedSession = authenticationService.getLastAuthenticatedSession()!!
activeSessionHolder.setActiveSession(lastAuthenticatedSession)
lastAuthenticatedSession.configureAndStart(applicationContext)
}
ProcessLifecycleOwner.get().lifecycle.addObserver(object : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
fun entersForeground() {
Timber.i("App entered foreground")
FcmHelper.onEnterForeground(appContext)
activeSessionHolder.getSafeActiveSession()?.also {
it.stopAnyBackgroundSync()
}
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
fun entersBackground() {
Timber.i("App entered background") // call persistInfo
notificationDrawerManager.persistInfo()
FcmHelper.onEnterBackground(appContext, vectorPreferences, activeSessionHolder)
}
})
ProcessLifecycleOwner.get().lifecycle.addObserver(appStateHandler)
ProcessLifecycleOwner.get().lifecycle.addObserver(pinLocker)
// This should be done as early as possible
// initKnownEmojiHashSet(appContext)
}
override fun providesMatrixConfiguration() = MatrixConfiguration(BuildConfig.FLAVOR_DESCRIPTION)
override fun getWorkManagerConfiguration(): WorkConfiguration {
return WorkConfiguration.Builder()
.setExecutor(Executors.newCachedThreadPool())
.build()
}
override fun injector(): VectorComponent {
return vectorComponent
}
private fun logInfo() {
val appVersion = versionProvider.getVersion(longFormat = true, useBuildNumber = true)
val sdkVersion = Matrix.getSdkVersion()
val date = SimpleDateFormat("MM-dd HH:mm:ss.SSSZ", Locale.US).format(Date())
Timber.v("----------------------------------------------------------------")
Timber.v("----------------------------------------------------------------")
Timber.v(" Application version: $appVersion")
Timber.v(" SDK version: $sdkVersion")
Timber.v(" Local time: $date")
Timber.v("----------------------------------------------------------------")
Timber.v("----------------------------------------------------------------\n\n\n\n")
}
override fun attachBaseContext(base: Context) {
super.attachBaseContext(base)
MultiDex.install(this)
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
vectorConfiguration.onConfigurationChanged()
}
private fun getFontThreadHandler(): Handler {
return fontThreadHandler ?: createFontThreadHandler().also {
fontThreadHandler = it
}
}
private fun createFontThreadHandler(): Handler {
val handlerThread = HandlerThread("fonts")
handlerThread.start()
return Handler(handlerThread.looper)
}
}

View File

@ -0,0 +1,53 @@
/*
* Copyright 2020 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.core.animations
import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.appbar.AppBarLayout.OnOffsetChangedListener
import kotlin.math.abs
abstract class AppBarStateChangeListener : OnOffsetChangedListener {
enum class State {
EXPANDED, COLLAPSED, IDLE
}
var currentState = State.IDLE
private set
override fun onOffsetChanged(appBarLayout: AppBarLayout, i: Int) {
currentState = if (i == 0) {
if (currentState != State.EXPANDED) {
onStateChanged(appBarLayout, State.EXPANDED)
}
State.EXPANDED
} else if (abs(i) >= appBarLayout.totalScrollRange) {
if (currentState != State.COLLAPSED) {
onStateChanged(appBarLayout, State.COLLAPSED)
}
State.COLLAPSED
} else {
if (currentState != State.IDLE) {
onStateChanged(appBarLayout, State.IDLE)
}
State.IDLE
}
}
abstract fun onStateChanged(appBarLayout: AppBarLayout, state: State)
}

View File

@ -0,0 +1,19 @@
/*
* Copyright 2019 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.core.animations
const val ANIMATION_DURATION_SHORT = 200L

View File

@ -0,0 +1,38 @@
/*
* Copyright 2020 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.core.animations
import android.view.View
import com.google.android.material.appbar.AppBarLayout
class MatrixItemAppBarStateChangeListener(private val headerView: View, private val toolbarViews: List<View>) : AppBarStateChangeListener() {
override fun onStateChanged(appBarLayout: AppBarLayout, state: State) {
if (state == State.COLLAPSED) {
headerView.visibility = View.INVISIBLE
toolbarViews.forEach {
it.animate().alpha(1f).duration = 150
}
} else {
headerView.visibility = View.VISIBLE
toolbarViews.forEach {
it.animate().alpha(0f).duration = 150
}
}
}
}

View File

@ -0,0 +1,37 @@
/*
* Copyright 2019 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.core.animations
import android.animation.Animator
open class SimpleAnimatorListener : Animator.AnimatorListener {
override fun onAnimationRepeat(animation: Animator?) {
// No op
}
override fun onAnimationEnd(animation: Animator?) {
// No op
}
override fun onAnimationCancel(animation: Animator?) {
// No op
}
override fun onAnimationStart(animation: Animator?) {
// No op
}
}

View File

@ -0,0 +1,41 @@
/*
* Copyright 2019 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.core.animations
import androidx.transition.Transition
open class SimpleTransitionListener : Transition.TransitionListener {
override fun onTransitionEnd(transition: Transition) {
// No op
}
override fun onTransitionResume(transition: Transition) {
// No op
}
override fun onTransitionPause(transition: Transition) {
// No op
}
override fun onTransitionCancel(transition: Transition) {
// No op
}
override fun onTransitionStart(transition: Transition) {
// No op
}
}

View File

@ -0,0 +1,43 @@
/*
* Copyright 2019 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.core.animations
import android.content.Context
import android.util.AttributeSet
import androidx.transition.ChangeBounds
import androidx.transition.ChangeTransform
import androidx.transition.Fade
import androidx.transition.TransitionSet
class VectorFullTransitionSet : TransitionSet {
constructor() {
init()
}
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
init()
}
private fun init() {
ordering = ORDERING_TOGETHER
addTransition(Fade(Fade.OUT))
.addTransition(ChangeBounds())
.addTransition(ChangeTransform())
.addTransition(Fade(Fade.IN))
}
}

View File

@ -0,0 +1,222 @@
/*
* Copyright 2020 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.core.animations.behavior
import android.animation.ArgbEvaluator
import android.content.Context
import android.graphics.drawable.ColorDrawable
import android.util.AttributeSet
import android.view.View
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.content.withStyledAttributes
import im.vector.app.R
import kotlin.math.abs
private const val UNSPECIFIED_INT = Integer.MAX_VALUE
private val UNSPECIFIED_FLOAT = Float.MAX_VALUE
private const val DEPEND_TYPE_HEIGHT = 0
private const val DEPEND_TYPE_WIDTH = 1
private const val DEPEND_TYPE_X = 2
private const val DEPEND_TYPE_Y = 3
class PercentViewBehavior<V : View>(context: Context, attrs: AttributeSet) : CoordinatorLayout.Behavior<V>(context, attrs) {
private var dependType: Int = 0
private var dependViewId: Int = 0
private var dependTarget: Int = 0
private var dependStartX: Int = 0
private var dependStartY: Int = 0
private var dependStartWidth: Int = 0
private var dependStartHeight: Int = 0
private var startX: Int = 0
private var startY: Int = 0
private var startWidth: Int = 0
private var startHeight: Int = 0
private var startBackgroundColor: Int = 0
private var startAlpha: Float = 0f
private var startRotateX: Float = 0f
private var startRotateY: Float = 0f
private var targetX: Int = 0
private var targetY: Int = 0
private var targetWidth: Int = 0
private var targetHeight: Int = 0
private var targetBackgroundColor: Int = 0
private var targetAlpha: Float = 0f
private var targetRotateX: Float = 0f
private var targetRotateY: Float = 0f
/**
* Is the values prepared to be use
*/
private var isPrepared: Boolean = false
init {
context.withStyledAttributes(attrs, R.styleable.PercentViewBehavior) {
dependViewId = getResourceId(R.styleable.PercentViewBehavior_behavior_dependsOn, 0)
dependType = getInt(R.styleable.PercentViewBehavior_behavior_dependType, DEPEND_TYPE_WIDTH)
dependTarget = getDimensionPixelOffset(R.styleable.PercentViewBehavior_behavior_dependTarget, UNSPECIFIED_INT)
targetX = getDimensionPixelOffset(R.styleable.PercentViewBehavior_behavior_targetX, UNSPECIFIED_INT)
targetY = getDimensionPixelOffset(R.styleable.PercentViewBehavior_behavior_targetY, UNSPECIFIED_INT)
targetWidth = getDimensionPixelOffset(R.styleable.PercentViewBehavior_behavior_targetWidth, UNSPECIFIED_INT)
targetHeight = getDimensionPixelOffset(R.styleable.PercentViewBehavior_behavior_targetHeight, UNSPECIFIED_INT)
targetBackgroundColor = getColor(R.styleable.PercentViewBehavior_behavior_targetBackgroundColor, UNSPECIFIED_INT)
targetAlpha = getFloat(R.styleable.PercentViewBehavior_behavior_targetAlpha, UNSPECIFIED_FLOAT)
targetRotateX = getFloat(R.styleable.PercentViewBehavior_behavior_targetRotateX, UNSPECIFIED_FLOAT)
targetRotateY = getFloat(R.styleable.PercentViewBehavior_behavior_targetRotateY, UNSPECIFIED_FLOAT)
}
}
private fun prepare(parent: CoordinatorLayout, child: View, dependency: View) {
dependStartX = dependency.x.toInt()
dependStartY = dependency.y.toInt()
dependStartWidth = dependency.width
dependStartHeight = dependency.height
startX = child.x.toInt()
startY = child.y.toInt()
startWidth = child.width
startHeight = child.height
startAlpha = child.alpha
startRotateX = child.rotationX
startRotateY = child.rotationY
// only set the start background color when the background is color drawable
val background = child.background
if (background is ColorDrawable) {
startBackgroundColor = background.color
}
// if parent fitsSystemWindows is true, add status bar height to target y if specified
if (parent.fitsSystemWindows && targetY != UNSPECIFIED_INT) {
var result = 0
val resources = parent.context.resources
val resourceId = resources.getIdentifier("status_bar_height", "dimen", "android")
if (resourceId > 0) {
result = resources.getDimensionPixelSize(resourceId)
}
targetY += result
}
isPrepared = true
}
override fun layoutDependsOn(parent: CoordinatorLayout, child: V, dependency: View): Boolean {
return dependency.id == dependViewId
}
override fun onDependentViewChanged(parent: CoordinatorLayout, child: V, dependency: View): Boolean {
// first time, prepare values before continue
if (!isPrepared) {
prepare(parent, child, dependency)
}
updateView(child, dependency)
return false
}
override fun onLayoutChild(parent: CoordinatorLayout, child: V, layoutDirection: Int): Boolean {
val bool = super.onLayoutChild(parent, child, layoutDirection)
if (isPrepared) {
updateView(child, parent.getDependencies(child)[0])
}
return bool
}
/**
* Update the child view from the dependency states
*
* @param child child view
* @param dependency dependency view
*/
private fun updateView(child: V, dependency: View) {
var percent = 0f
var start = 0f
var current = 0f
var end = UNSPECIFIED_INT.toFloat()
when (dependType) {
DEPEND_TYPE_WIDTH -> {
start = dependStartWidth.toFloat()
current = dependency.width.toFloat()
end = dependTarget.toFloat()
}
DEPEND_TYPE_HEIGHT -> {
start = dependStartHeight.toFloat()
current = dependency.height.toFloat()
end = dependTarget.toFloat()
}
DEPEND_TYPE_X -> {
start = dependStartX.toFloat()
current = dependency.x
end = dependTarget.toFloat()
}
DEPEND_TYPE_Y -> {
start = dependStartY.toFloat()
current = dependency.y
end = dependTarget.toFloat()
}
}
// need to define target value according to the depend type, if not then skip
if (end != UNSPECIFIED_INT.toFloat()) {
percent = abs(current - start) / abs(end - start)
}
updateViewWithPercent(child, if (percent > 1f) 1f else percent)
}
private fun updateViewWithPercent(child: View, percent: Float) {
var newX = if (targetX == UNSPECIFIED_INT) 0f else (targetX - startX) * percent
var newY = if (targetY == UNSPECIFIED_INT) 0f else (targetY - startY) * percent
// set scale
if (targetWidth != UNSPECIFIED_INT) {
val newWidth = startWidth + (targetWidth - startWidth) * percent
child.scaleX = newWidth / startWidth
newX -= (startWidth - newWidth) / 2
}
if (targetHeight != UNSPECIFIED_INT) {
val newHeight = startHeight + (targetHeight - startHeight) * percent
child.scaleY = newHeight / startHeight
newY -= (startHeight - newHeight) / 2
}
// set new position
child.translationX = newX
child.translationY = newY
// set alpha
if (targetAlpha != UNSPECIFIED_FLOAT) {
child.alpha = startAlpha + (targetAlpha - startAlpha) * percent
}
// set background color
if (targetBackgroundColor != UNSPECIFIED_INT && startBackgroundColor != 0) {
val evaluator = ArgbEvaluator()
val color = evaluator.evaluate(percent, startBackgroundColor, targetBackgroundColor) as Int
child.setBackgroundColor(color)
}
// set rotation
if (targetRotateX != UNSPECIFIED_FLOAT) {
child.rotationX = startRotateX + (targetRotateX - startRotateX) * percent
}
if (targetRotateY != UNSPECIFIED_FLOAT) {
child.rotationY = startRotateY + (targetRotateY - startRotateY) * percent
}
child.requestLayout()
}
}

View File

@ -0,0 +1,155 @@
/*
* Copyright (c) 2020 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.core.contacts
import android.content.Context
import android.database.Cursor
import android.net.Uri
import android.provider.ContactsContract
import androidx.annotation.WorkerThread
import timber.log.Timber
import javax.inject.Inject
import kotlin.system.measureTimeMillis
class ContactsDataSource @Inject constructor(
private val context: Context
) {
/**
* Will return a list of contact from the contacts book of the device, with at least one email or phone.
* If both param are false, you will get en empty list.
* Note: The return list does not contain any matrixId.
*/
@WorkerThread
fun getContacts(
withEmails: Boolean,
withMsisdn: Boolean
): List<MappedContact> {
val map = mutableMapOf<Long, MappedContactBuilder>()
val contentResolver = context.contentResolver
measureTimeMillis {
contentResolver.query(
ContactsContract.Contacts.CONTENT_URI,
arrayOf(
ContactsContract.Contacts._ID,
ContactsContract.Data.DISPLAY_NAME,
ContactsContract.Data.PHOTO_URI
),
null,
null,
// Sort by Display name
ContactsContract.Data.DISPLAY_NAME
)
?.use { cursor ->
if (cursor.count > 0) {
while (cursor.moveToNext()) {
val id = cursor.getLong(ContactsContract.Contacts._ID) ?: continue
val displayName = cursor.getString(ContactsContract.Contacts.DISPLAY_NAME) ?: continue
val mappedContactBuilder = MappedContactBuilder(
id = id,
displayName = displayName
)
cursor.getString(ContactsContract.Data.PHOTO_URI)
?.let { Uri.parse(it) }
?.let { mappedContactBuilder.photoURI = it }
map[id] = mappedContactBuilder
}
}
}
// Get the phone numbers
if (withMsisdn) {
contentResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
arrayOf(
ContactsContract.CommonDataKinds.Phone.CONTACT_ID,
ContactsContract.CommonDataKinds.Phone.NUMBER
),
null,
null,
null)
?.use { innerCursor ->
while (innerCursor.moveToNext()) {
val mappedContactBuilder = innerCursor.getLong(ContactsContract.CommonDataKinds.Phone.CONTACT_ID)
?.let { map[it] }
?: continue
innerCursor.getString(ContactsContract.CommonDataKinds.Phone.NUMBER)
?.let {
mappedContactBuilder.msisdns.add(
MappedMsisdn(
phoneNumber = it,
matrixId = null
)
)
}
}
}
}
// Get Emails
if (withEmails) {
contentResolver.query(
ContactsContract.CommonDataKinds.Email.CONTENT_URI,
arrayOf(
ContactsContract.CommonDataKinds.Email.CONTACT_ID,
ContactsContract.CommonDataKinds.Email.DATA
),
null,
null,
null)
?.use { innerCursor ->
while (innerCursor.moveToNext()) {
// This would allow you get several email addresses
// if the email addresses were stored in an array
val mappedContactBuilder = innerCursor.getLong(ContactsContract.CommonDataKinds.Email.CONTACT_ID)
?.let { map[it] }
?: continue
innerCursor.getString(ContactsContract.CommonDataKinds.Email.DATA)
?.let {
mappedContactBuilder.emails.add(
MappedEmail(
email = it,
matrixId = null
)
)
}
}
}
}
}.also { Timber.d("Took ${it}ms to fetch ${map.size} contact(s)") }
return map
.values
.filter { it.emails.isNotEmpty() || it.msisdns.isNotEmpty() }
.map { it.build() }
}
private fun Cursor.getString(column: String): String? {
return getColumnIndex(column)
.takeIf { it != -1 }
?.let { getString(it) }
}
private fun Cursor.getLong(column: String): Long? {
return getColumnIndex(column)
.takeIf { it != -1 }
?.let { getLong(it) }
}
}

View File

@ -0,0 +1,56 @@
/*
* Copyright (c) 2020 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.core.contacts
import android.net.Uri
class MappedContactBuilder(
val id: Long,
val displayName: String
) {
var photoURI: Uri? = null
val msisdns = mutableListOf<MappedMsisdn>()
val emails = mutableListOf<MappedEmail>()
fun build(): MappedContact {
return MappedContact(
id = id,
displayName = displayName,
photoURI = photoURI,
msisdns = msisdns,
emails = emails
)
}
}
data class MappedContact(
val id: Long,
val displayName: String,
val photoURI: Uri? = null,
val msisdns: List<MappedMsisdn> = emptyList(),
val emails: List<MappedEmail> = emptyList()
)
data class MappedEmail(
val email: String,
val matrixId: String?
)
data class MappedMsisdn(
val phoneNumber: String,
val matrixId: String?
)

View File

@ -0,0 +1,56 @@
/*
* Copyright 2019 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.core.date
import android.content.Context
import android.text.format.DateUtils
import im.vector.app.core.resources.LocaleProvider
import org.threeten.bp.LocalDateTime
import org.threeten.bp.format.DateTimeFormatter
import javax.inject.Inject
class VectorDateFormatter @Inject constructor(private val context: Context,
private val localeProvider: LocaleProvider) {
private val messageHourFormatter by lazy {
DateTimeFormatter.ofPattern("H:mm", localeProvider.current())
}
private val messageDayFormatter by lazy {
DateTimeFormatter.ofPattern("EEE d MMM", localeProvider.current())
}
fun formatMessageHour(localDateTime: LocalDateTime): String {
return messageHourFormatter.format(localDateTime)
}
fun formatMessageDay(localDateTime: LocalDateTime): String {
return messageDayFormatter.format(localDateTime)
}
fun formatRelativeDateTime(time: Long?): String {
if (time == null) {
return ""
}
return DateUtils.getRelativeDateTimeString(
context,
time,
DateUtils.DAY_IN_MILLIS,
2 * DateUtils.DAY_IN_MILLIS,
DateUtils.FORMAT_SHOW_WEEKDAY or DateUtils.FORMAT_SHOW_TIME
).toString()
}
}

View File

@ -0,0 +1,93 @@
/*
* Copyright 2019 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.core.di
import arrow.core.Option
import im.vector.app.ActiveSessionDataSource
import im.vector.app.features.call.WebRtcPeerConnectionManager
import im.vector.app.features.crypto.keysrequest.KeyRequestHandler
import im.vector.app.features.crypto.verification.IncomingVerificationRequestHandler
import im.vector.app.features.notifications.PushRuleTriggerListener
import im.vector.app.features.session.SessionListener
import im.vector.matrix.android.api.auth.AuthenticationService
import im.vector.matrix.android.api.session.Session
import timber.log.Timber
import java.util.concurrent.atomic.AtomicReference
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class ActiveSessionHolder @Inject constructor(private val authenticationService: AuthenticationService,
private val sessionObservableStore: ActiveSessionDataSource,
private val keyRequestHandler: KeyRequestHandler,
private val incomingVerificationRequestHandler: IncomingVerificationRequestHandler,
private val webRtcPeerConnectionManager: WebRtcPeerConnectionManager,
private val pushRuleTriggerListener: PushRuleTriggerListener,
private val sessionListener: SessionListener,
private val imageManager: ImageManager
) {
private var activeSession: AtomicReference<Session?> = AtomicReference()
fun setActiveSession(session: Session) {
Timber.w("setActiveSession of ${session.myUserId}")
activeSession.set(session)
sessionObservableStore.post(Option.just(session))
keyRequestHandler.start(session)
incomingVerificationRequestHandler.start(session)
session.addListener(sessionListener)
pushRuleTriggerListener.startWithSession(session)
session.callSignalingService().addCallListener(webRtcPeerConnectionManager)
imageManager.onSessionStarted(session)
}
fun clearActiveSession() {
// Do some cleanup first
getSafeActiveSession()?.let {
Timber.w("clearActiveSession of ${it.myUserId}")
it.callSignalingService().removeCallListener(webRtcPeerConnectionManager)
it.removeListener(sessionListener)
}
activeSession.set(null)
sessionObservableStore.post(Option.empty())
keyRequestHandler.stop()
incomingVerificationRequestHandler.stop()
pushRuleTriggerListener.stop()
}
fun hasActiveSession(): Boolean {
return activeSession.get() != null
}
fun getSafeActiveSession(): Session? {
return activeSession.get()
}
fun getActiveSession(): Session {
return activeSession.get()
?: throw IllegalStateException("You should authenticate before using this")
}
// TODO: Stop sync ?
// fun switchToSession(sessionParams: SessionParams) {
// val newActiveSession = authenticationService.getSession(sessionParams)
// activeSession.set(newActiveSession)
// }
}

View File

@ -0,0 +1,24 @@
/*
* Copyright 2019 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.core.di
import com.squareup.inject.assisted.dagger2.AssistedModule
import dagger.Module
@AssistedModule
@Module(includes = [AssistedInject_AssistedInjectModule::class])
interface AssistedInjectModule

View File

@ -0,0 +1,27 @@
/*
* Copyright 2019 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.core.di
import androidx.fragment.app.Fragment
import dagger.MapKey
import kotlin.reflect.KClass
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
@Retention(AnnotationRetention.RUNTIME)
@MapKey
annotation class FragmentKey(val value: KClass<out Fragment>)

View File

@ -0,0 +1,549 @@
/*
* Copyright 2019 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.core.di
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentFactory
import dagger.Binds
import dagger.Module
import dagger.multibindings.IntoMap
import im.vector.app.features.attachments.preview.AttachmentsPreviewFragment
import im.vector.app.features.contactsbook.ContactsBookFragment
import im.vector.app.features.crypto.keysbackup.settings.KeysBackupSettingsFragment
import im.vector.app.features.crypto.quads.SharedSecuredStorageKeyFragment
import im.vector.app.features.crypto.quads.SharedSecuredStoragePassphraseFragment
import im.vector.app.features.crypto.recover.BootstrapAccountPasswordFragment
import im.vector.app.features.crypto.recover.BootstrapConclusionFragment
import im.vector.app.features.crypto.recover.BootstrapConfirmPassphraseFragment
import im.vector.app.features.crypto.recover.BootstrapEnterPassphraseFragment
import im.vector.app.features.crypto.recover.BootstrapMigrateBackupFragment
import im.vector.app.features.crypto.recover.BootstrapSaveRecoveryKeyFragment
import im.vector.app.features.crypto.recover.BootstrapSetupRecoveryKeyFragment
import im.vector.app.features.crypto.recover.BootstrapWaitingFragment
import im.vector.app.features.crypto.verification.cancel.VerificationCancelFragment
import im.vector.app.features.crypto.verification.cancel.VerificationNotMeFragment
import im.vector.app.features.crypto.verification.choose.VerificationChooseMethodFragment
import im.vector.app.features.crypto.verification.conclusion.VerificationConclusionFragment
import im.vector.app.features.crypto.verification.emoji.VerificationEmojiCodeFragment
import im.vector.app.features.crypto.verification.qrconfirmation.VerificationQRWaitingFragment
import im.vector.app.features.crypto.verification.qrconfirmation.VerificationQrScannedByOtherFragment
import im.vector.app.features.crypto.verification.request.VerificationRequestFragment
import im.vector.app.features.discovery.DiscoverySettingsFragment
import im.vector.app.features.discovery.change.SetIdentityServerFragment
import im.vector.app.features.grouplist.GroupListFragment
import im.vector.app.features.home.HomeDetailFragment
import im.vector.app.features.home.HomeDrawerFragment
import im.vector.app.features.home.LoadingFragment
import im.vector.app.features.home.room.breadcrumbs.BreadcrumbsFragment
import im.vector.app.features.home.room.detail.RoomDetailFragment
import im.vector.app.features.home.room.list.RoomListFragment
import im.vector.app.features.login.LoginCaptchaFragment
import im.vector.app.features.login.LoginFragment
import im.vector.app.features.login.LoginGenericTextInputFormFragment
import im.vector.app.features.login.LoginResetPasswordFragment
import im.vector.app.features.login.LoginResetPasswordMailConfirmationFragment
import im.vector.app.features.login.LoginResetPasswordSuccessFragment
import im.vector.app.features.login.LoginServerSelectionFragment
import im.vector.app.features.login.LoginServerUrlFormFragment
import im.vector.app.features.login.LoginSignUpSignInSelectionFragment
import im.vector.app.features.login.LoginSignUpSignInSsoFragment
import im.vector.app.features.login.LoginSplashFragment
import im.vector.app.features.login.LoginWaitForEmailFragment
import im.vector.app.features.login.LoginWebFragment
import im.vector.app.features.login.terms.LoginTermsFragment
import im.vector.app.features.pin.PinFragment
import im.vector.app.features.qrcode.QrCodeScannerFragment
import im.vector.app.features.reactions.EmojiChooserFragment
import im.vector.app.features.reactions.EmojiSearchResultFragment
import im.vector.app.features.roomdirectory.PublicRoomsFragment
import im.vector.app.features.roomdirectory.createroom.CreateRoomFragment
import im.vector.app.features.roomdirectory.picker.RoomDirectoryPickerFragment
import im.vector.app.features.roomdirectory.roompreview.RoomPreviewNoPreviewFragment
import im.vector.app.features.roommemberprofile.RoomMemberProfileFragment
import im.vector.app.features.roommemberprofile.devices.DeviceListFragment
import im.vector.app.features.roommemberprofile.devices.DeviceTrustInfoActionFragment
import im.vector.app.features.roomprofile.RoomProfileFragment
import im.vector.app.features.roomprofile.banned.RoomBannedMemberListFragment
import im.vector.app.features.roomprofile.members.RoomMemberListFragment
import im.vector.app.features.roomprofile.settings.RoomSettingsFragment
import im.vector.app.features.roomprofile.uploads.RoomUploadsFragment
import im.vector.app.features.roomprofile.uploads.files.RoomUploadsFilesFragment
import im.vector.app.features.roomprofile.uploads.media.RoomUploadsMediaFragment
import im.vector.app.features.settings.VectorSettingsAdvancedNotificationPreferenceFragment
import im.vector.app.features.settings.VectorSettingsHelpAboutFragment
import im.vector.app.features.settings.VectorSettingsLabsFragment
import im.vector.app.features.settings.VectorSettingsNotificationPreferenceFragment
import im.vector.app.features.settings.VectorSettingsNotificationsTroubleshootFragment
import im.vector.app.features.settings.VectorSettingsPreferencesFragment
import im.vector.app.features.settings.VectorSettingsSecurityPrivacyFragment
import im.vector.app.features.settings.account.deactivation.DeactivateAccountFragment
import im.vector.app.features.settings.crosssigning.CrossSigningSettingsFragment
import im.vector.app.features.settings.devices.VectorSettingsDevicesFragment
import im.vector.app.features.settings.devtools.AccountDataFragment
import im.vector.app.features.settings.devtools.GossipingEventsPaperTrailFragment
import im.vector.app.features.settings.devtools.IncomingKeyRequestListFragment
import im.vector.app.features.settings.devtools.KeyRequestsFragment
import im.vector.app.features.settings.devtools.OutgoingKeyRequestListFragment
import im.vector.app.features.settings.ignored.VectorSettingsIgnoredUsersFragment
import im.vector.app.features.settings.locale.LocalePickerFragment
import im.vector.app.features.settings.push.PushGatewaysFragment
import im.vector.app.features.share.IncomingShareFragment
import im.vector.app.features.signout.soft.SoftLogoutFragment
import im.vector.app.features.terms.ReviewTermsFragment
import im.vector.app.features.userdirectory.KnownUsersFragment
import im.vector.app.features.userdirectory.UserDirectoryFragment
import im.vector.app.features.widgets.WidgetFragment
@Module
interface FragmentModule {
/**
* Fragments with @IntoMap will be injected by this factory
*/
@Binds
fun bindFragmentFactory(factory: VectorFragmentFactory): FragmentFactory
@Binds
@IntoMap
@FragmentKey(RoomListFragment::class)
fun bindRoomListFragment(fragment: RoomListFragment): Fragment
@Binds
@IntoMap
@FragmentKey(LocalePickerFragment::class)
fun bindLocalePickerFragment(fragment: LocalePickerFragment): Fragment
@Binds
@IntoMap
@FragmentKey(GroupListFragment::class)
fun bindGroupListFragment(fragment: GroupListFragment): Fragment
@Binds
@IntoMap
@FragmentKey(RoomDetailFragment::class)
fun bindRoomDetailFragment(fragment: RoomDetailFragment): Fragment
@Binds
@IntoMap
@FragmentKey(RoomDirectoryPickerFragment::class)
fun bindRoomDirectoryPickerFragment(fragment: RoomDirectoryPickerFragment): Fragment
@Binds
@IntoMap
@FragmentKey(CreateRoomFragment::class)
fun bindCreateRoomFragment(fragment: CreateRoomFragment): Fragment
@Binds
@IntoMap
@FragmentKey(RoomPreviewNoPreviewFragment::class)
fun bindRoomPreviewNoPreviewFragment(fragment: RoomPreviewNoPreviewFragment): Fragment
@Binds
@IntoMap
@FragmentKey(KeysBackupSettingsFragment::class)
fun bindKeysBackupSettingsFragment(fragment: KeysBackupSettingsFragment): Fragment
@Binds
@IntoMap
@FragmentKey(LoadingFragment::class)
fun bindLoadingFragment(fragment: LoadingFragment): Fragment
@Binds
@IntoMap
@FragmentKey(HomeDrawerFragment::class)
fun bindHomeDrawerFragment(fragment: HomeDrawerFragment): Fragment
@Binds
@IntoMap
@FragmentKey(HomeDetailFragment::class)
fun bindHomeDetailFragment(fragment: HomeDetailFragment): Fragment
@Binds
@IntoMap
@FragmentKey(EmojiSearchResultFragment::class)
fun bindEmojiSearchResultFragment(fragment: EmojiSearchResultFragment): Fragment
@Binds
@IntoMap
@FragmentKey(LoginFragment::class)
fun bindLoginFragment(fragment: LoginFragment): Fragment
@Binds
@IntoMap
@FragmentKey(LoginCaptchaFragment::class)
fun bindLoginCaptchaFragment(fragment: LoginCaptchaFragment): Fragment
@Binds
@IntoMap
@FragmentKey(LoginTermsFragment::class)
fun bindLoginTermsFragment(fragment: LoginTermsFragment): Fragment
@Binds
@IntoMap
@FragmentKey(LoginServerUrlFormFragment::class)
fun bindLoginServerUrlFormFragment(fragment: LoginServerUrlFormFragment): Fragment
@Binds
@IntoMap
@FragmentKey(LoginResetPasswordMailConfirmationFragment::class)
fun bindLoginResetPasswordMailConfirmationFragment(fragment: LoginResetPasswordMailConfirmationFragment): Fragment
@Binds
@IntoMap
@FragmentKey(LoginResetPasswordFragment::class)
fun bindLoginResetPasswordFragment(fragment: LoginResetPasswordFragment): Fragment
@Binds
@IntoMap
@FragmentKey(LoginResetPasswordSuccessFragment::class)
fun bindLoginResetPasswordSuccessFragment(fragment: LoginResetPasswordSuccessFragment): Fragment
@Binds
@IntoMap
@FragmentKey(LoginServerSelectionFragment::class)
fun bindLoginServerSelectionFragment(fragment: LoginServerSelectionFragment): Fragment
@Binds
@IntoMap
@FragmentKey(LoginSignUpSignInSelectionFragment::class)
fun bindLoginSignUpSignInSelectionFragment(fragment: LoginSignUpSignInSelectionFragment): Fragment
@Binds
@IntoMap
@FragmentKey(LoginSignUpSignInSsoFragment::class)
fun bindLoginSignUpSignInSsoFragment(fragment: LoginSignUpSignInSsoFragment): Fragment
@Binds
@IntoMap
@FragmentKey(LoginSplashFragment::class)
fun bindLoginSplashFragment(fragment: LoginSplashFragment): Fragment
@Binds
@IntoMap
@FragmentKey(LoginWebFragment::class)
fun bindLoginWebFragment(fragment: LoginWebFragment): Fragment
@Binds
@IntoMap
@FragmentKey(LoginGenericTextInputFormFragment::class)
fun bindLoginGenericTextInputFormFragment(fragment: LoginGenericTextInputFormFragment): Fragment
@Binds
@IntoMap
@FragmentKey(LoginWaitForEmailFragment::class)
fun bindLoginWaitForEmailFragment(fragment: LoginWaitForEmailFragment): Fragment
@Binds
@IntoMap
@FragmentKey(UserDirectoryFragment::class)
fun bindUserDirectoryFragment(fragment: UserDirectoryFragment): Fragment
@Binds
@IntoMap
@FragmentKey(KnownUsersFragment::class)
fun bindKnownUsersFragment(fragment: KnownUsersFragment): Fragment
@Binds
@IntoMap
@FragmentKey(PushGatewaysFragment::class)
fun bindPushGatewaysFragment(fragment: PushGatewaysFragment): Fragment
@Binds
@IntoMap
@FragmentKey(VectorSettingsNotificationsTroubleshootFragment::class)
fun bindVectorSettingsNotificationsTroubleshootFragment(fragment: VectorSettingsNotificationsTroubleshootFragment): Fragment
@Binds
@IntoMap
@FragmentKey(VectorSettingsAdvancedNotificationPreferenceFragment::class)
fun bindVectorSettingsAdvancedNotificationPreferenceFragment(fragment: VectorSettingsAdvancedNotificationPreferenceFragment): Fragment
@Binds
@IntoMap
@FragmentKey(VectorSettingsNotificationPreferenceFragment::class)
fun bindVectorSettingsNotificationPreferenceFragment(fragment: VectorSettingsNotificationPreferenceFragment): Fragment
@Binds
@IntoMap
@FragmentKey(VectorSettingsLabsFragment::class)
fun bindVectorSettingsLabsFragment(fragment: VectorSettingsLabsFragment): Fragment
@Binds
@IntoMap
@FragmentKey(VectorSettingsPreferencesFragment::class)
fun bindVectorSettingsPreferencesFragment(fragment: VectorSettingsPreferencesFragment): Fragment
@Binds
@IntoMap
@FragmentKey(VectorSettingsSecurityPrivacyFragment::class)
fun bindVectorSettingsSecurityPrivacyFragment(fragment: VectorSettingsSecurityPrivacyFragment): Fragment
@Binds
@IntoMap
@FragmentKey(VectorSettingsHelpAboutFragment::class)
fun bindVectorSettingsHelpAboutFragment(fragment: VectorSettingsHelpAboutFragment): Fragment
@Binds
@IntoMap
@FragmentKey(VectorSettingsIgnoredUsersFragment::class)
fun bindVectorSettingsIgnoredUsersFragment(fragment: VectorSettingsIgnoredUsersFragment): Fragment
@Binds
@IntoMap
@FragmentKey(VectorSettingsDevicesFragment::class)
fun bindVectorSettingsDevicesFragment(fragment: VectorSettingsDevicesFragment): Fragment
@Binds
@IntoMap
@FragmentKey(PublicRoomsFragment::class)
fun bindPublicRoomsFragment(fragment: PublicRoomsFragment): Fragment
@Binds
@IntoMap
@FragmentKey(RoomProfileFragment::class)
fun bindRoomProfileFragment(fragment: RoomProfileFragment): Fragment
@Binds
@IntoMap
@FragmentKey(RoomMemberListFragment::class)
fun bindRoomMemberListFragment(fragment: RoomMemberListFragment): Fragment
@Binds
@IntoMap
@FragmentKey(RoomUploadsFragment::class)
fun bindRoomUploadsFragment(fragment: RoomUploadsFragment): Fragment
@Binds
@IntoMap
@FragmentKey(RoomUploadsMediaFragment::class)
fun bindRoomUploadsMediaFragment(fragment: RoomUploadsMediaFragment): Fragment
@Binds
@IntoMap
@FragmentKey(RoomUploadsFilesFragment::class)
fun bindRoomUploadsFilesFragment(fragment: RoomUploadsFilesFragment): Fragment
@Binds
@IntoMap
@FragmentKey(RoomSettingsFragment::class)
fun bindRoomSettingsFragment(fragment: RoomSettingsFragment): Fragment
@Binds
@IntoMap
@FragmentKey(RoomMemberProfileFragment::class)
fun bindRoomMemberProfileFragment(fragment: RoomMemberProfileFragment): Fragment
@Binds
@IntoMap
@FragmentKey(BreadcrumbsFragment::class)
fun bindBreadcrumbsFragment(fragment: BreadcrumbsFragment): Fragment
@Binds
@IntoMap
@FragmentKey(EmojiChooserFragment::class)
fun bindEmojiChooserFragment(fragment: EmojiChooserFragment): Fragment
@Binds
@IntoMap
@FragmentKey(SoftLogoutFragment::class)
fun bindSoftLogoutFragment(fragment: SoftLogoutFragment): Fragment
@Binds
@IntoMap
@FragmentKey(VerificationRequestFragment::class)
fun bindVerificationRequestFragment(fragment: VerificationRequestFragment): Fragment
@Binds
@IntoMap
@FragmentKey(VerificationChooseMethodFragment::class)
fun bindVerificationChooseMethodFragment(fragment: VerificationChooseMethodFragment): Fragment
@Binds
@IntoMap
@FragmentKey(VerificationEmojiCodeFragment::class)
fun bindVerificationEmojiCodeFragment(fragment: VerificationEmojiCodeFragment): Fragment
@Binds
@IntoMap
@FragmentKey(VerificationQrScannedByOtherFragment::class)
fun bindVerificationQrScannedByOtherFragment(fragment: VerificationQrScannedByOtherFragment): Fragment
@Binds
@IntoMap
@FragmentKey(VerificationQRWaitingFragment::class)
fun bindVerificationQRWaitingFragment(fragment: VerificationQRWaitingFragment): Fragment
@Binds
@IntoMap
@FragmentKey(VerificationConclusionFragment::class)
fun bindVerificationConclusionFragment(fragment: VerificationConclusionFragment): Fragment
@Binds
@IntoMap
@FragmentKey(VerificationCancelFragment::class)
fun bindVerificationCancelFragment(fragment: VerificationCancelFragment): Fragment
@Binds
@IntoMap
@FragmentKey(VerificationNotMeFragment::class)
fun bindVerificationNotMeFragment(fragment: VerificationNotMeFragment): Fragment
@Binds
@IntoMap
@FragmentKey(QrCodeScannerFragment::class)
fun bindQrCodeScannerFragment(fragment: QrCodeScannerFragment): Fragment
@Binds
@IntoMap
@FragmentKey(DeviceListFragment::class)
fun bindDeviceListFragment(fragment: DeviceListFragment): Fragment
@Binds
@IntoMap
@FragmentKey(DeviceTrustInfoActionFragment::class)
fun bindDeviceTrustInfoActionFragment(fragment: DeviceTrustInfoActionFragment): Fragment
@Binds
@IntoMap
@FragmentKey(CrossSigningSettingsFragment::class)
fun bindCrossSigningSettingsFragment(fragment: CrossSigningSettingsFragment): Fragment
@Binds
@IntoMap
@FragmentKey(AttachmentsPreviewFragment::class)
fun bindAttachmentsPreviewFragment(fragment: AttachmentsPreviewFragment): Fragment
@Binds
@IntoMap
@FragmentKey(IncomingShareFragment::class)
fun bindIncomingShareFragment(fragment: IncomingShareFragment): Fragment
@Binds
@IntoMap
@FragmentKey(AccountDataFragment::class)
fun bindAccountDataFragment(fragment: AccountDataFragment): Fragment
@Binds
@IntoMap
@FragmentKey(OutgoingKeyRequestListFragment::class)
fun bindOutgoingKeyRequestListFragment(fragment: OutgoingKeyRequestListFragment): Fragment
@Binds
@IntoMap
@FragmentKey(IncomingKeyRequestListFragment::class)
fun bindIncomingKeyRequestListFragment(fragment: IncomingKeyRequestListFragment): Fragment
@Binds
@IntoMap
@FragmentKey(KeyRequestsFragment::class)
fun bindKeyRequestsFragment(fragment: KeyRequestsFragment): Fragment
@Binds
@IntoMap
@FragmentKey(GossipingEventsPaperTrailFragment::class)
fun bindGossipingEventsPaperTrailFragment(fragment: GossipingEventsPaperTrailFragment): Fragment
@Binds
@IntoMap
@FragmentKey(BootstrapEnterPassphraseFragment::class)
fun bindBootstrapEnterPassphraseFragment(fragment: BootstrapEnterPassphraseFragment): Fragment
@Binds
@IntoMap
@FragmentKey(BootstrapConfirmPassphraseFragment::class)
fun bindBootstrapConfirmPassphraseFragment(fragment: BootstrapConfirmPassphraseFragment): Fragment
@Binds
@IntoMap
@FragmentKey(BootstrapWaitingFragment::class)
fun bindBootstrapWaitingFragment(fragment: BootstrapWaitingFragment): Fragment
@Binds
@IntoMap
@FragmentKey(BootstrapSetupRecoveryKeyFragment::class)
fun bindBootstrapSetupRecoveryKeyFragment(fragment: BootstrapSetupRecoveryKeyFragment): Fragment
@Binds
@IntoMap
@FragmentKey(BootstrapSaveRecoveryKeyFragment::class)
fun bindBootstrapSaveRecoveryKeyFragment(fragment: BootstrapSaveRecoveryKeyFragment): Fragment
@Binds
@IntoMap
@FragmentKey(BootstrapConclusionFragment::class)
fun bindBootstrapConclusionFragment(fragment: BootstrapConclusionFragment): Fragment
@Binds
@IntoMap
@FragmentKey(BootstrapAccountPasswordFragment::class)
fun bindBootstrapAccountPasswordFragment(fragment: BootstrapAccountPasswordFragment): Fragment
@Binds
@IntoMap
@FragmentKey(BootstrapMigrateBackupFragment::class)
fun bindBootstrapMigrateBackupFragment(fragment: BootstrapMigrateBackupFragment): Fragment
@Binds
@IntoMap
@FragmentKey(DeactivateAccountFragment::class)
fun bindDeactivateAccountFragment(fragment: DeactivateAccountFragment): Fragment
@Binds
@IntoMap
@FragmentKey(SharedSecuredStoragePassphraseFragment::class)
fun bindSharedSecuredStoragePassphraseFragment(fragment: SharedSecuredStoragePassphraseFragment): Fragment
@Binds
@IntoMap
@FragmentKey(SharedSecuredStorageKeyFragment::class)
fun bindSharedSecuredStorageKeyFragment(fragment: SharedSecuredStorageKeyFragment): Fragment
@Binds
@IntoMap
@FragmentKey(SetIdentityServerFragment::class)
fun bindSetIdentityServerFragment(fragment: SetIdentityServerFragment): Fragment
@Binds
@IntoMap
@FragmentKey(DiscoverySettingsFragment::class)
fun bindDiscoverySettingsFragment(fragment: DiscoverySettingsFragment): Fragment
@Binds
@IntoMap
@FragmentKey(ReviewTermsFragment::class)
fun bindReviewTermsFragment(fragment: ReviewTermsFragment): Fragment
@Binds
@IntoMap
@FragmentKey(WidgetFragment::class)
fun bindWidgetFragment(fragment: WidgetFragment): Fragment
@Binds
@IntoMap
@FragmentKey(ContactsBookFragment::class)
fun bindPhoneBookFragment(fragment: ContactsBookFragment): Fragment
@Binds
@IntoMap
@FragmentKey(PinFragment::class)
fun bindPinFragment(fragment: PinFragment): Fragment
@Binds
@IntoMap
@FragmentKey(RoomBannedMemberListFragment::class)
fun bindRoomBannedMemberListFragment(fragment: RoomBannedMemberListFragment): Fragment
}

View File

@ -0,0 +1,22 @@
/*
* Copyright 2019 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.core.di
interface HasScreenInjector {
fun injector(): ScreenComponent
}

View File

@ -0,0 +1,22 @@
/*
* Copyright 2019 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.core.di
interface HasVectorInjector {
fun injector(): VectorComponent
}

View File

@ -0,0 +1,47 @@
/*
* Copyright (c) 2020 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.core.di
import android.content.Context
import com.bumptech.glide.Glide
import com.bumptech.glide.load.model.GlideUrl
import com.github.piasy.biv.BigImageViewer
import com.github.piasy.biv.loader.glide.GlideImageLoader
import im.vector.app.ActiveSessionDataSource
import im.vector.app.core.glide.FactoryUrl
import im.vector.matrix.android.api.session.Session
import java.io.InputStream
import javax.inject.Inject
/**
* This class is used to configure the library we use for images
*/
class ImageManager @Inject constructor(
private val context: Context,
private val activeSessionDataSource: ActiveSessionDataSource
) {
fun onSessionStarted(session: Session) {
// Do this call first
BigImageViewer.initialize(GlideImageLoader.with(context, session.getOkHttpClient()))
val glide = Glide.get(context)
// And this one. FIXME But are losing what BigImageViewer has done to add a Progress listener
glide.registry.replace(GlideUrl::class.java, InputStream::class.java, FactoryUrl(activeSessionDataSource))
}
}

View File

@ -0,0 +1,180 @@
/*
* Copyright 2019 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.core.di
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.FragmentFactory
import androidx.lifecycle.ViewModelProvider
import dagger.BindsInstance
import dagger.Component
import im.vector.app.core.dialogs.UnrecognizedCertificateDialog
import im.vector.app.core.error.ErrorFormatter
import im.vector.app.core.preference.UserAvatarPreference
import im.vector.app.features.MainActivity
import im.vector.app.features.call.CallControlsBottomSheet
import im.vector.app.features.call.VectorCallActivity
import im.vector.app.features.createdirect.CreateDirectRoomActivity
import im.vector.app.features.crypto.keysbackup.settings.KeysBackupManageActivity
import im.vector.app.features.crypto.quads.SharedSecureStorageActivity
import im.vector.app.features.crypto.recover.BootstrapBottomSheet
import im.vector.app.features.crypto.verification.VerificationBottomSheet
import im.vector.app.features.debug.DebugMenuActivity
import im.vector.app.features.home.HomeActivity
import im.vector.app.features.home.HomeModule
import im.vector.app.features.home.room.detail.RoomDetailActivity
import im.vector.app.features.home.room.detail.readreceipts.DisplayReadReceiptsBottomSheet
import im.vector.app.features.home.room.detail.timeline.action.MessageActionsBottomSheet
import im.vector.app.features.home.room.detail.timeline.edithistory.ViewEditHistoryBottomSheet
import im.vector.app.features.home.room.detail.timeline.reactions.ViewReactionsBottomSheet
import im.vector.app.features.home.room.detail.widget.RoomWidgetsBottomSheet
import im.vector.app.features.home.room.filtered.FilteredRoomsActivity
import im.vector.app.features.home.room.list.RoomListModule
import im.vector.app.features.home.room.list.actions.RoomListQuickActionsBottomSheet
import im.vector.app.features.invite.InviteUsersToRoomActivity
import im.vector.app.features.invite.VectorInviteView
import im.vector.app.features.link.LinkHandlerActivity
import im.vector.app.features.login.LoginActivity
import im.vector.app.features.media.BigImageViewerActivity
import im.vector.app.features.media.ImageMediaViewerActivity
import im.vector.app.features.media.VectorAttachmentViewerActivity
import im.vector.app.features.media.VideoMediaViewerActivity
import im.vector.app.features.navigation.Navigator
import im.vector.app.features.permalink.PermalinkHandlerActivity
import im.vector.app.features.pin.PinLocker
import im.vector.app.features.qrcode.QrCodeScannerActivity
import im.vector.app.features.rageshake.BugReportActivity
import im.vector.app.features.rageshake.BugReporter
import im.vector.app.features.rageshake.RageShake
import im.vector.app.features.reactions.EmojiReactionPickerActivity
import im.vector.app.features.reactions.widget.ReactionButton
import im.vector.app.features.roomdirectory.RoomDirectoryActivity
import im.vector.app.features.roomdirectory.createroom.CreateRoomActivity
import im.vector.app.features.roommemberprofile.RoomMemberProfileActivity
import im.vector.app.features.roommemberprofile.devices.DeviceListBottomSheet
import im.vector.app.features.roomprofile.RoomProfileActivity
import im.vector.app.features.settings.VectorSettingsActivity
import im.vector.app.features.settings.devices.DeviceVerificationInfoBottomSheet
import im.vector.app.features.share.IncomingShareActivity
import im.vector.app.features.signout.soft.SoftLogoutActivity
import im.vector.app.features.terms.ReviewTermsActivity
import im.vector.app.features.ui.UiStateRepository
import im.vector.app.features.widgets.WidgetActivity
import im.vector.app.features.widgets.permissions.RoomWidgetPermissionBottomSheet
import im.vector.app.features.workers.signout.SignOutBottomSheetDialogFragment
@Component(
dependencies = [
VectorComponent::class
],
modules = [
AssistedInjectModule::class,
ViewModelModule::class,
FragmentModule::class,
HomeModule::class,
RoomListModule::class,
ScreenModule::class
]
)
@ScreenScope
interface ScreenComponent {
/* ==========================================================================================
* Shortcut to VectorComponent elements
* ========================================================================================== */
fun activeSessionHolder(): ActiveSessionHolder
fun fragmentFactory(): FragmentFactory
fun viewModelFactory(): ViewModelProvider.Factory
fun bugReporter(): BugReporter
fun rageShake(): RageShake
fun navigator(): Navigator
fun pinLocker(): PinLocker
fun errorFormatter(): ErrorFormatter
fun uiStateRepository(): UiStateRepository
fun unrecognizedCertificateDialog(): UnrecognizedCertificateDialog
/* ==========================================================================================
* Activities
* ========================================================================================== */
fun inject(activity: HomeActivity)
fun inject(activity: RoomDetailActivity)
fun inject(activity: RoomProfileActivity)
fun inject(activity: RoomMemberProfileActivity)
fun inject(activity: VectorSettingsActivity)
fun inject(activity: KeysBackupManageActivity)
fun inject(activity: EmojiReactionPickerActivity)
fun inject(activity: LoginActivity)
fun inject(activity: LinkHandlerActivity)
fun inject(activity: MainActivity)
fun inject(activity: RoomDirectoryActivity)
fun inject(activity: BugReportActivity)
fun inject(activity: ImageMediaViewerActivity)
fun inject(activity: FilteredRoomsActivity)
fun inject(activity: CreateRoomActivity)
fun inject(activity: VideoMediaViewerActivity)
fun inject(activity: CreateDirectRoomActivity)
fun inject(activity: IncomingShareActivity)
fun inject(activity: SoftLogoutActivity)
fun inject(activity: PermalinkHandlerActivity)
fun inject(activity: QrCodeScannerActivity)
fun inject(activity: DebugMenuActivity)
fun inject(activity: SharedSecureStorageActivity)
fun inject(activity: BigImageViewerActivity)
fun inject(activity: InviteUsersToRoomActivity)
fun inject(activity: ReviewTermsActivity)
fun inject(activity: WidgetActivity)
fun inject(activity: VectorCallActivity)
fun inject(activity: VectorAttachmentViewerActivity)
/* ==========================================================================================
* BottomSheets
* ========================================================================================== */
fun inject(bottomSheet: MessageActionsBottomSheet)
fun inject(bottomSheet: ViewReactionsBottomSheet)
fun inject(bottomSheet: ViewEditHistoryBottomSheet)
fun inject(bottomSheet: DisplayReadReceiptsBottomSheet)
fun inject(bottomSheet: RoomListQuickActionsBottomSheet)
fun inject(bottomSheet: VerificationBottomSheet)
fun inject(bottomSheet: DeviceVerificationInfoBottomSheet)
fun inject(bottomSheet: DeviceListBottomSheet)
fun inject(bottomSheet: BootstrapBottomSheet)
fun inject(bottomSheet: RoomWidgetPermissionBottomSheet)
fun inject(bottomSheet: RoomWidgetsBottomSheet)
fun inject(bottomSheet: CallControlsBottomSheet)
fun inject(bottomSheet: SignOutBottomSheetDialogFragment)
/* ==========================================================================================
* Others
* ========================================================================================== */
fun inject(view: VectorInviteView)
fun inject(preference: UserAvatarPreference)
fun inject(button: ReactionButton)
/* ==========================================================================================
* Factory
* ========================================================================================== */
@Component.Factory
interface Factory {
fun create(vectorComponent: VectorComponent,
@BindsInstance context: AppCompatActivity
): ScreenComponent
}
}

View File

@ -0,0 +1,36 @@
/*
* Copyright 2019 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.core.di
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.RecyclerView
import dagger.Module
import dagger.Provides
import im.vector.app.core.glide.GlideApp
@Module
object ScreenModule {
@Provides
@JvmStatic
fun providesGlideRequests(context: AppCompatActivity) = GlideApp.with(context)
@Provides
@JvmStatic
@ScreenScope
fun providesSharedViewPool() = RecyclerView.RecycledViewPool()
}

View File

@ -0,0 +1,24 @@
/*
* Copyright 2019 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.core.di
import javax.inject.Scope
@Scope
@MustBeDocumented
@Retention(AnnotationRetention.RUNTIME)
annotation class ScreenScope

View File

@ -0,0 +1,22 @@
/*
* Copyright 2019 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.core.di
/*
@Module(includes = [AssistedInject_VectorAssistedModule::class])
@AssistedModule
class VectorAssistedModule*/

View File

@ -0,0 +1,153 @@
/*
* Copyright 2019 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.core.di
import android.content.Context
import android.content.res.Resources
import dagger.BindsInstance
import dagger.Component
import im.vector.app.ActiveSessionDataSource
import im.vector.app.EmojiCompatFontProvider
import im.vector.app.EmojiCompatWrapper
import im.vector.app.VectorApplication
import im.vector.app.core.dialogs.UnrecognizedCertificateDialog
import im.vector.app.core.error.ErrorFormatter
import im.vector.app.core.pushers.PushersManager
import im.vector.app.core.utils.AssetReader
import im.vector.app.core.utils.DimensionConverter
import im.vector.app.features.call.WebRtcPeerConnectionManager
import im.vector.app.features.configuration.VectorConfiguration
import im.vector.app.features.crypto.keysrequest.KeyRequestHandler
import im.vector.app.features.crypto.verification.IncomingVerificationRequestHandler
import im.vector.app.features.grouplist.SelectedGroupDataSource
import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.home.HomeRoomListDataSource
import im.vector.app.features.html.EventHtmlRenderer
import im.vector.app.features.html.VectorHtmlCompressor
import im.vector.app.features.login.ReAuthHelper
import im.vector.app.features.navigation.Navigator
import im.vector.app.features.notifications.NotifiableEventResolver
import im.vector.app.features.notifications.NotificationBroadcastReceiver
import im.vector.app.features.notifications.NotificationDrawerManager
import im.vector.app.features.notifications.NotificationUtils
import im.vector.app.features.notifications.PushRuleTriggerListener
import im.vector.app.features.pin.PinCodeStore
import im.vector.app.features.pin.PinLocker
import im.vector.app.features.popup.PopupAlertManager
import im.vector.app.features.rageshake.BugReporter
import im.vector.app.features.rageshake.VectorFileLogger
import im.vector.app.features.rageshake.VectorUncaughtExceptionHandler
import im.vector.app.features.reactions.data.EmojiDataSource
import im.vector.app.features.session.SessionListener
import im.vector.app.features.settings.VectorPreferences
import im.vector.app.features.ui.UiStateRepository
import im.vector.matrix.android.api.Matrix
import im.vector.matrix.android.api.auth.AuthenticationService
import im.vector.matrix.android.api.session.Session
import javax.inject.Singleton
@Component(modules = [VectorModule::class])
@Singleton
interface VectorComponent {
fun inject(notificationBroadcastReceiver: NotificationBroadcastReceiver)
fun inject(vectorApplication: VectorApplication)
fun matrix(): Matrix
fun sessionListener(): SessionListener
fun currentSession(): Session
fun notificationUtils(): NotificationUtils
fun notificationDrawerManager(): NotificationDrawerManager
fun appContext(): Context
fun resources(): Resources
fun assetReader(): AssetReader
fun dimensionConverter(): DimensionConverter
fun vectorConfiguration(): VectorConfiguration
fun avatarRenderer(): AvatarRenderer
fun activeSessionHolder(): ActiveSessionHolder
fun unrecognizedCertificateDialog(): UnrecognizedCertificateDialog
fun emojiCompatFontProvider(): EmojiCompatFontProvider
fun emojiCompatWrapper(): EmojiCompatWrapper
fun eventHtmlRenderer(): EventHtmlRenderer
fun vectorHtmlCompressor(): VectorHtmlCompressor
fun navigator(): Navigator
fun errorFormatter(): ErrorFormatter
fun homeRoomListObservableStore(): HomeRoomListDataSource
fun selectedGroupStore(): SelectedGroupDataSource
fun activeSessionObservableStore(): ActiveSessionDataSource
fun incomingVerificationRequestHandler(): IncomingVerificationRequestHandler
fun incomingKeyRequestHandler(): KeyRequestHandler
fun authenticationService(): AuthenticationService
fun bugReporter(): BugReporter
fun vectorUncaughtExceptionHandler(): VectorUncaughtExceptionHandler
fun pushRuleTriggerListener(): PushRuleTriggerListener
fun pusherManager(): PushersManager
fun notifiableEventResolver(): NotifiableEventResolver
fun vectorPreferences(): VectorPreferences
fun vectorFileLogger(): VectorFileLogger
fun uiStateRepository(): UiStateRepository
fun pinCodeStore(): PinCodeStore
fun emojiDataSource(): EmojiDataSource
fun alertManager(): PopupAlertManager
fun reAuthHelper(): ReAuthHelper
fun pinLocker(): PinLocker
fun webRtcPeerConnectionManager(): WebRtcPeerConnectionManager
@Component.Factory
interface Factory {
fun create(@BindsInstance context: Context): VectorComponent
}
}

View File

@ -0,0 +1,43 @@
/*
* Copyright 2019 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.core.di
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentFactory
import timber.log.Timber
import javax.inject.Inject
import javax.inject.Provider
/**
* FragmentFactory which uses Dagger to create the instances.
*/
class VectorFragmentFactory @Inject constructor(
private val creators: @JvmSuppressWildcards Map<Class<out Fragment>, Provider<Fragment>>
) : FragmentFactory() {
override fun instantiate(classLoader: ClassLoader, className: String): Fragment {
val fragmentClass = loadFragmentClass(classLoader, className)
val creator: Provider<out Fragment>? = creators[fragmentClass]
return if (creator == null) {
Timber.v("Unknown model class: $className, fallback to default instance")
super.instantiate(classLoader, className)
} else {
creator.get()
}
}
}

View File

@ -0,0 +1,94 @@
/*
* Copyright 2019 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.core.di
import android.content.Context
import android.content.Context.MODE_PRIVATE
import android.content.SharedPreferences
import android.content.res.Resources
import dagger.Binds
import dagger.Module
import dagger.Provides
import im.vector.app.core.error.DefaultErrorFormatter
import im.vector.app.core.error.ErrorFormatter
import im.vector.app.features.navigation.DefaultNavigator
import im.vector.app.features.navigation.Navigator
import im.vector.app.features.pin.PinCodeStore
import im.vector.app.features.pin.SharedPrefPinCodeStore
import im.vector.app.features.ui.SharedPreferencesUiStateRepository
import im.vector.app.features.ui.UiStateRepository
import im.vector.matrix.android.api.Matrix
import im.vector.matrix.android.api.auth.AuthenticationService
import im.vector.matrix.android.api.legacy.LegacySessionImporter
import im.vector.matrix.android.api.session.Session
@Module
abstract class VectorModule {
@Module
companion object {
@Provides
@JvmStatic
fun providesResources(context: Context): Resources {
return context.resources
}
@Provides
@JvmStatic
fun providesSharedPreferences(context: Context): SharedPreferences {
return context.getSharedPreferences("im.vector.riot", MODE_PRIVATE)
}
@Provides
@JvmStatic
fun providesMatrix(context: Context): Matrix {
return Matrix.getInstance(context)
}
@Provides
@JvmStatic
fun providesCurrentSession(activeSessionHolder: ActiveSessionHolder): Session {
// TODO: handle session injection better
return activeSessionHolder.getActiveSession()
}
@Provides
@JvmStatic
fun providesLegacySessionImporter(matrix: Matrix): LegacySessionImporter {
return matrix.legacySessionImporter()
}
@Provides
@JvmStatic
fun providesAuthenticationService(matrix: Matrix): AuthenticationService {
return matrix.authenticationService()
}
}
@Binds
abstract fun bindNavigator(navigator: DefaultNavigator): Navigator
@Binds
abstract fun bindErrorFormatter(formatter: DefaultErrorFormatter): ErrorFormatter
@Binds
abstract fun bindUiStateRepository(repository: SharedPreferencesUiStateRepository): UiStateRepository
@Binds
abstract fun bindPinCodeStore(store: SharedPrefPinCodeStore): PinCodeStore
}

View File

@ -0,0 +1,50 @@
/*
* Copyright 2019 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.core.di
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import javax.inject.Inject
import javax.inject.Provider
/**
* ViewModelFactory which uses Dagger to create the instances.
*/
class VectorViewModelFactory @Inject constructor(
private val creators: @JvmSuppressWildcards Map<Class<out ViewModel>, Provider<ViewModel>>
) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
var creator: Provider<out ViewModel>? = creators[modelClass]
if (creator == null) {
for ((key, value) in creators) {
if (modelClass.isAssignableFrom(key)) {
creator = value
break
}
}
}
if (creator == null) {
throw IllegalArgumentException("Unknown model class: $modelClass")
}
try {
@Suppress("UNCHECKED_CAST")
return creator.get() as T
} catch (e: Exception) {
throw RuntimeException(e)
}
}
}

View File

@ -0,0 +1,26 @@
/*
* Copyright 2019 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.core.di
import androidx.lifecycle.ViewModel
import dagger.MapKey
import kotlin.reflect.KClass
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
@Retention(AnnotationRetention.RUNTIME)
@MapKey
annotation class ViewModelKey(val value: KClass<out ViewModel>)

View File

@ -0,0 +1,127 @@
/*
* Copyright 2019 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.core.di
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import dagger.Binds
import dagger.Module
import dagger.multibindings.IntoMap
import im.vector.app.core.platform.ConfigurationViewModel
import im.vector.app.features.call.SharedActiveCallViewModel
import im.vector.app.features.crypto.keysbackup.restore.KeysBackupRestoreFromKeyViewModel
import im.vector.app.features.crypto.keysbackup.restore.KeysBackupRestoreFromPassphraseViewModel
import im.vector.app.features.crypto.keysbackup.restore.KeysBackupRestoreSharedViewModel
import im.vector.app.features.crypto.keysbackup.setup.KeysBackupSetupSharedViewModel
import im.vector.app.features.discovery.DiscoverySharedViewModel
import im.vector.app.features.home.HomeSharedActionViewModel
import im.vector.app.features.home.room.detail.RoomDetailSharedActionViewModel
import im.vector.app.features.home.room.detail.timeline.action.MessageSharedActionViewModel
import im.vector.app.features.home.room.list.actions.RoomListQuickActionsSharedActionViewModel
import im.vector.app.features.reactions.EmojiChooserViewModel
import im.vector.app.features.roomdirectory.RoomDirectorySharedActionViewModel
import im.vector.app.features.roomprofile.RoomProfileSharedActionViewModel
import im.vector.app.features.userdirectory.UserDirectorySharedActionViewModel
@Module
interface ViewModelModule {
/**
* ViewModels with @IntoMap will be injected by this factory
*/
@Binds
fun bindViewModelFactory(factory: VectorViewModelFactory): ViewModelProvider.Factory
/**
* Below are bindings for the androidx view models (which extend ViewModel). Will be converted to MvRx ViewModel in the future.
*/
@Binds
@IntoMap
@ViewModelKey(EmojiChooserViewModel::class)
fun bindEmojiChooserViewModel(viewModel: EmojiChooserViewModel): ViewModel
@Binds
@IntoMap
@ViewModelKey(KeysBackupRestoreFromKeyViewModel::class)
fun bindKeysBackupRestoreFromKeyViewModel(viewModel: KeysBackupRestoreFromKeyViewModel): ViewModel
@Binds
@IntoMap
@ViewModelKey(KeysBackupRestoreSharedViewModel::class)
fun bindKeysBackupRestoreSharedViewModel(viewModel: KeysBackupRestoreSharedViewModel): ViewModel
@Binds
@IntoMap
@ViewModelKey(KeysBackupRestoreFromPassphraseViewModel::class)
fun bindKeysBackupRestoreFromPassphraseViewModel(viewModel: KeysBackupRestoreFromPassphraseViewModel): ViewModel
@Binds
@IntoMap
@ViewModelKey(KeysBackupSetupSharedViewModel::class)
fun bindKeysBackupSetupSharedViewModel(viewModel: KeysBackupSetupSharedViewModel): ViewModel
@Binds
@IntoMap
@ViewModelKey(ConfigurationViewModel::class)
fun bindConfigurationViewModel(viewModel: ConfigurationViewModel): ViewModel
@Binds
@IntoMap
@ViewModelKey(SharedActiveCallViewModel::class)
fun bindSharedActiveCallViewModel(viewModel: SharedActiveCallViewModel): ViewModel
@Binds
@IntoMap
@ViewModelKey(UserDirectorySharedActionViewModel::class)
fun bindUserDirectorySharedActionViewModel(viewModel: UserDirectorySharedActionViewModel): ViewModel
@Binds
@IntoMap
@ViewModelKey(HomeSharedActionViewModel::class)
fun bindHomeSharedActionViewModel(viewModel: HomeSharedActionViewModel): ViewModel
@Binds
@IntoMap
@ViewModelKey(MessageSharedActionViewModel::class)
fun bindMessageSharedActionViewModel(viewModel: MessageSharedActionViewModel): ViewModel
@Binds
@IntoMap
@ViewModelKey(RoomListQuickActionsSharedActionViewModel::class)
fun bindRoomListQuickActionsSharedActionViewModel(viewModel: RoomListQuickActionsSharedActionViewModel): ViewModel
@Binds
@IntoMap
@ViewModelKey(RoomDirectorySharedActionViewModel::class)
fun bindRoomDirectorySharedActionViewModel(viewModel: RoomDirectorySharedActionViewModel): ViewModel
@Binds
@IntoMap
@ViewModelKey(RoomDetailSharedActionViewModel::class)
fun bindRoomDetailSharedActionViewModel(viewModel: RoomDetailSharedActionViewModel): ViewModel
@Binds
@IntoMap
@ViewModelKey(RoomProfileSharedActionViewModel::class)
fun bindRoomProfileSharedActionViewModel(viewModel: RoomProfileSharedActionViewModel): ViewModel
@Binds
@IntoMap
@ViewModelKey(DiscoverySharedViewModel::class)
fun bindDiscoverySharedViewModel(viewModel: DiscoverySharedViewModel): ViewModel
}

View File

@ -0,0 +1,61 @@
/*
* Copyright (c) 2020 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.core.dialogs
import android.app.Activity
import androidx.annotation.StringRes
import androidx.appcompat.app.AlertDialog
import androidx.core.view.isVisible
import im.vector.app.R
import kotlinx.android.synthetic.main.dialog_confirmation_with_reason.view.*
object ConfirmationDialogBuilder {
fun show(activity: Activity,
askForReason: Boolean,
@StringRes titleRes: Int,
@StringRes confirmationRes: Int,
@StringRes positiveRes: Int,
@StringRes reasonHintRes: Int,
confirmation: (String?) -> Unit) {
val layout = activity.layoutInflater.inflate(R.layout.dialog_confirmation_with_reason, null)
layout.dialogConfirmationText.setText(confirmationRes)
layout.dialogReasonCheck.isVisible = askForReason
layout.dialogReasonTextInputLayout.isVisible = askForReason
layout.dialogReasonCheck.setOnCheckedChangeListener { _, isChecked ->
layout.dialogReasonTextInputLayout.isEnabled = isChecked
}
if (askForReason && reasonHintRes != 0) {
layout.dialogReasonInput.setHint(reasonHintRes)
}
AlertDialog.Builder(activity)
.setTitle(titleRes)
.setView(layout)
.setPositiveButton(positiveRes) { _, _ ->
val reason = layout.dialogReasonInput.text.toString()
.takeIf { askForReason }
?.takeIf { layout.dialogReasonCheck.isChecked }
?.takeIf { it.isNotBlank() }
confirmation(reason)
}
.setNegativeButton(R.string.cancel, null)
.show()
}
}

View File

@ -0,0 +1,65 @@
/*
* Copyright 2019 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.core.dialogs
import android.os.Bundle
import androidx.appcompat.app.AlertDialog
import im.vector.app.core.platform.Restorable
import timber.log.Timber
private const val KEY_DIALOG_IS_DISPLAYED = "DialogLocker.KEY_DIALOG_IS_DISPLAYED"
/**
* Class to avoid displaying twice the same dialog
*/
class DialogLocker(savedInstanceState: Bundle?) : Restorable {
private var isDialogDisplayed = savedInstanceState?.getBoolean(KEY_DIALOG_IS_DISPLAYED, false) == true
private fun unlock() {
isDialogDisplayed = false
}
private fun lock() {
isDialogDisplayed = true
}
fun displayDialog(builder: () -> AlertDialog.Builder): AlertDialog? {
return if (isDialogDisplayed) {
Timber.w("Filtered dialog request")
null
} else {
builder
.invoke()
.create()
.apply {
setOnShowListener { lock() }
setOnCancelListener { unlock() }
setOnDismissListener { unlock() }
show()
}
}
}
override fun onSaveInstanceState(outState: Bundle) {
outState.putBoolean(KEY_DIALOG_IS_DISPLAYED, isDialogDisplayed)
}
override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
isDialogDisplayed = savedInstanceState?.getBoolean(KEY_DIALOG_IS_DISPLAYED, false) == true
}
}

View File

@ -0,0 +1,86 @@
/*
* Copyright 2019 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.core.dialogs
import android.app.Activity
import android.text.Editable
import android.widget.Button
import android.widget.ImageView
import androidx.appcompat.app.AlertDialog
import com.google.android.material.textfield.TextInputEditText
import com.google.android.material.textfield.TextInputLayout
import im.vector.app.R
import im.vector.app.core.extensions.showPassword
import im.vector.app.core.platform.SimpleTextWatcher
class ExportKeysDialog {
private var passwordVisible = false
fun show(activity: Activity, exportKeyDialogListener: ExportKeyDialogListener) {
val dialogLayout = activity.layoutInflater.inflate(R.layout.dialog_export_e2e_keys, null)
val builder = AlertDialog.Builder(activity)
.setTitle(R.string.encryption_export_room_keys)
.setView(dialogLayout)
val passPhrase1EditText = dialogLayout.findViewById<TextInputEditText>(R.id.exportDialogEt)
val passPhrase2EditText = dialogLayout.findViewById<TextInputEditText>(R.id.exportDialogEtConfirm)
val passPhrase2Til = dialogLayout.findViewById<TextInputLayout>(R.id.exportDialogTilConfirm)
val exportButton = dialogLayout.findViewById<Button>(R.id.exportDialogSubmit)
val textWatcher = object : SimpleTextWatcher() {
override fun afterTextChanged(s: Editable) {
when {
passPhrase1EditText.text.isNullOrEmpty() -> {
exportButton.isEnabled = false
passPhrase2Til.error = null
}
passPhrase1EditText.text.toString() == passPhrase2EditText.text.toString() -> {
exportButton.isEnabled = true
passPhrase2Til.error = null
}
else -> {
exportButton.isEnabled = false
passPhrase2Til.error = activity.getString(R.string.passphrase_passphrase_does_not_match)
}
}
}
}
passPhrase1EditText.addTextChangedListener(textWatcher)
passPhrase2EditText.addTextChangedListener(textWatcher)
val showPassword = dialogLayout.findViewById<ImageView>(R.id.exportDialogShowPassword)
showPassword.setOnClickListener {
passwordVisible = !passwordVisible
passPhrase1EditText.showPassword(passwordVisible)
passPhrase2EditText.showPassword(passwordVisible)
showPassword.setImageResource(if (passwordVisible) R.drawable.ic_eye_closed else R.drawable.ic_eye)
}
val exportDialog = builder.show()
exportButton.setOnClickListener {
exportKeyDialogListener.onPassphrase(passPhrase1EditText.text.toString())
exportDialog.dismiss()
}
}
interface ExportKeyDialogListener {
fun onPassphrase(passphrase: String)
}
}

View File

@ -0,0 +1,27 @@
/*
* Copyright 2019 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.core.dialogs
import androidx.annotation.ColorRes
import androidx.appcompat.app.AlertDialog
import androidx.core.content.ContextCompat
import im.vector.app.R
fun AlertDialog.withColoredButton(whichButton: Int, @ColorRes color: Int = R.color.vector_error_color): AlertDialog {
getButton(whichButton)?.setTextColor(ContextCompat.getColor(context, color))
return this
}

View File

@ -0,0 +1,52 @@
/*
* Copyright (c) 2020 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.core.dialogs
import android.app.Activity
import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import im.vector.app.R
import im.vector.matrix.android.api.extensions.getFingerprintHumanReadable
import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo
object ManuallyVerifyDialog {
fun show(activity: Activity, cryptoDeviceInfo: CryptoDeviceInfo, onVerified: (() -> Unit)) {
val dialogLayout = activity.layoutInflater.inflate(R.layout.dialog_device_verify, null)
val builder = AlertDialog.Builder(activity)
.setTitle(R.string.cross_signing_verify_by_text)
.setView(dialogLayout)
.setPositiveButton(R.string.encryption_information_verify) { _, _ ->
onVerified()
}
.setNegativeButton(R.string.cancel, null)
dialogLayout.findViewById<TextView>(R.id.encrypted_device_info_device_name)?.let {
it.text = cryptoDeviceInfo.displayName()
}
dialogLayout.findViewById<TextView>(R.id.encrypted_device_info_device_id)?.let {
it.text = cryptoDeviceInfo.deviceId
}
dialogLayout.findViewById<TextView>(R.id.encrypted_device_info_device_key)?.let {
it.text = cryptoDeviceInfo.getFingerprintHumanReadable()
}
builder.show()
}
}

View File

@ -0,0 +1,87 @@
/*
* Copyright 2020 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.core.dialogs
import android.app.Activity
import android.content.DialogInterface
import android.text.Editable
import android.view.KeyEvent
import android.widget.ImageView
import androidx.appcompat.app.AlertDialog
import com.google.android.material.textfield.TextInputEditText
import com.google.android.material.textfield.TextInputLayout
import im.vector.app.R
import im.vector.app.core.extensions.hideKeyboard
import im.vector.app.core.extensions.showPassword
import im.vector.app.core.platform.SimpleTextWatcher
class PromptPasswordDialog {
private var passwordVisible = false
fun show(activity: Activity, listener: (String) -> Unit) {
val dialogLayout = activity.layoutInflater.inflate(R.layout.dialog_prompt_password, null)
val passwordTil = dialogLayout.findViewById<TextInputLayout>(R.id.promptPasswordTil)
val passwordEditText = dialogLayout.findViewById<TextInputEditText>(R.id.promptPassword)
val textWatcher = object : SimpleTextWatcher() {
override fun afterTextChanged(s: Editable) {
passwordTil.error = null
}
}
passwordEditText.addTextChangedListener(textWatcher)
val showPassword = dialogLayout.findViewById<ImageView>(R.id.promptPasswordPasswordReveal)
showPassword.setOnClickListener {
passwordVisible = !passwordVisible
passwordEditText.showPassword(passwordVisible)
showPassword.setImageResource(if (passwordVisible) R.drawable.ic_eye_closed else R.drawable.ic_eye)
}
AlertDialog.Builder(activity)
.setIcon(android.R.drawable.ic_dialog_alert)
.setTitle(R.string.devices_delete_dialog_title)
.setView(dialogLayout)
.setPositiveButton(R.string.auth_submit, null)
.setNegativeButton(R.string.cancel, null)
.setOnKeyListener(DialogInterface.OnKeyListener { dialog, keyCode, event ->
if (event.action == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) {
dialog.cancel()
return@OnKeyListener true
}
false
})
.setOnDismissListener {
dialogLayout.hideKeyboard()
}
.create()
.apply {
setOnShowListener {
getButton(AlertDialog.BUTTON_POSITIVE)
.setOnClickListener {
if (passwordEditText.text.toString().isEmpty()) {
passwordTil.error = activity.getString(R.string.error_empty_field_your_password)
} else {
listener.invoke(passwordEditText.text.toString())
dismiss()
}
}
}
}
.show()
}
}

View File

@ -0,0 +1,170 @@
/*
* Copyright (c) 2020 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.core.dialogs
import android.app.Activity
import android.view.View
import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import im.vector.app.R
import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.resources.StringProvider
import im.vector.matrix.android.internal.network.ssl.Fingerprint
import timber.log.Timber
import java.util.HashMap
import java.util.HashSet
import javax.inject.Inject
/**
* This class displays the unknown certificate dialog
*/
class UnrecognizedCertificateDialog @Inject constructor(
private val activeSessionHolder: ActiveSessionHolder,
private val stringProvider: StringProvider
) {
private val ignoredFingerprints: MutableMap<String, MutableSet<Fingerprint>> = HashMap()
private val openDialogIds: MutableSet<String> = HashSet()
/**
* Display a certificate dialog box, asking the user about an unknown certificate
* To use when user is currently logged in
*
* @param unrecognizedFingerprint the fingerprint for the unknown certificate
* @param callback callback to fire when the user makes a decision
*/
fun show(activity: Activity,
unrecognizedFingerprint: Fingerprint,
callback: Callback) {
val userId = activeSessionHolder.getSafeActiveSession()?.myUserId
val hsConfig = activeSessionHolder.getSafeActiveSession()?.sessionParams?.homeServerConnectionConfig ?: return
internalShow(activity, unrecognizedFingerprint, true, callback, userId, hsConfig.homeServerUri.toString(), hsConfig.allowedFingerprints.isNotEmpty())
}
/**
* To use during login flow
*/
fun show(activity: Activity,
unrecognizedFingerprint: Fingerprint,
homeServerUrl: String,
callback: Callback) {
internalShow(activity, unrecognizedFingerprint, false, callback, null, homeServerUrl, false)
}
/**
* Display a certificate dialog box, asking the user about an unknown certificate
*
* @param unrecognizedFingerprint the fingerprint for the unknown certificate
* @param existing the current session already exist, so it mean that something has changed server side
* @param callback callback to fire when the user makes a decision
*/
private fun internalShow(activity: Activity,
unrecognizedFingerprint: Fingerprint,
existing: Boolean,
callback: Callback,
userId: String?,
homeServerUrl: String,
homeServerConnectionConfigHasFingerprints: Boolean) {
val dialogId = userId ?: homeServerUrl + unrecognizedFingerprint.displayableHexRepr
if (openDialogIds.contains(dialogId)) {
Timber.i("Not opening dialog $dialogId as one is already open.")
return
}
if (userId != null) {
val f: Set<Fingerprint>? = ignoredFingerprints[userId]
if (f != null && f.contains(unrecognizedFingerprint)) {
callback.onIgnore()
return
}
}
val builder = AlertDialog.Builder(activity)
val inflater = activity.layoutInflater
val layout: View = inflater.inflate(R.layout.dialog_ssl_fingerprint, null)
val sslFingerprintTitle = layout.findViewById<TextView>(R.id.ssl_fingerprint_title)
sslFingerprintTitle.text = stringProvider.getString(R.string.ssl_fingerprint_hash, unrecognizedFingerprint.hashType.toString())
val sslFingerprint = layout.findViewById<TextView>(R.id.ssl_fingerprint)
sslFingerprint.text = unrecognizedFingerprint.displayableHexRepr
val sslUserId = layout.findViewById<TextView>(R.id.ssl_user_id)
if (userId != null) {
sslUserId.text = stringProvider.getString(R.string.generic_label_and_value,
stringProvider.getString(R.string.username),
userId)
} else {
sslUserId.text = stringProvider.getString(R.string.generic_label_and_value,
stringProvider.getString(R.string.hs_url),
homeServerUrl)
}
val sslExpl = layout.findViewById<TextView>(R.id.ssl_explanation)
if (existing) {
if (homeServerConnectionConfigHasFingerprints) {
sslExpl.text = stringProvider.getString(R.string.ssl_expected_existing_expl)
} else {
sslExpl.text = stringProvider.getString(R.string.ssl_unexpected_existing_expl)
}
} else {
sslExpl.text = stringProvider.getString(R.string.ssl_cert_new_account_expl)
}
builder.setView(layout)
builder.setTitle(R.string.ssl_could_not_verify)
builder.setPositiveButton(R.string.ssl_trust) { _, _ ->
callback.onAccept()
}
if (existing) {
builder.setNegativeButton(R.string.ssl_remain_offline) { _, _ ->
if (userId != null) {
var f = ignoredFingerprints[userId]
if (f == null) {
f = HashSet()
ignoredFingerprints[userId] = f
}
f.add(unrecognizedFingerprint)
}
callback.onIgnore()
}
builder.setNeutralButton(R.string.ssl_logout_account) { _, _ -> callback.onReject() }
} else {
builder.setNegativeButton(R.string.cancel) { _, _ -> callback.onReject() }
}
builder.setOnDismissListener {
Timber.d("Dismissed!")
openDialogIds.remove(dialogId)
}
builder.show()
openDialogIds.add(dialogId)
}
interface Callback {
/**
* The certificate was explicitly accepted
*/
fun onAccept()
/**
* The warning was ignored by the user
*/
fun onIgnore()
/**
* The unknown certificate was explicitly rejected
*/
fun onReject()
}
}

View File

@ -0,0 +1,39 @@
/*
* Copyright 2019 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.core.epoxy
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import im.vector.app.R
/**
* Default background color is for the bottom sheets (R.attr.vctr_list_bottom_sheet_divider_color).
* To use in fragment, set color using R.attr.riotx_list_divider_color
*/
@EpoxyModelClass(layout = R.layout.item_divider)
abstract class DividerItem : VectorEpoxyModel<DividerItem.Holder>() {
@EpoxyAttribute var color: Int = -1
override fun bind(holder: Holder) {
super.bind(holder)
if (color != -1) {
holder.view.setBackgroundColor(color)
}
}
class Holder : VectorEpoxyHolder()
}

View File

@ -0,0 +1,25 @@
/*
* Copyright 2019 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.core.epoxy
import com.airbnb.epoxy.EpoxyModelClass
import im.vector.app.R
@EpoxyModelClass(layout = R.layout.item_empty)
abstract class EmptyItem : VectorEpoxyModel<EmptyItem.Holder>() {
class Holder : VectorEpoxyHolder()
}

View File

@ -0,0 +1,46 @@
/*
* Copyright 2019 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.core.epoxy
import android.widget.Button
import android.widget.TextView
import androidx.core.view.isVisible
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import im.vector.app.R
@EpoxyModelClass(layout = R.layout.item_error_retry)
abstract class ErrorWithRetryItem : VectorEpoxyModel<ErrorWithRetryItem.Holder>() {
@EpoxyAttribute
var text: String? = null
@EpoxyAttribute
var listener: (() -> Unit)? = null
override fun bind(holder: Holder) {
super.bind(holder)
holder.textView.text = text
holder.buttonView.isVisible = listener != null
holder.buttonView.setOnClickListener { listener?.invoke() }
}
class Holder : VectorEpoxyHolder() {
val textView by bind<TextView>(R.id.itemErrorRetryText)
val buttonView by bind<Button>(R.id.itemErrorRetryButton)
}
}

View File

@ -0,0 +1,38 @@
/*
* Copyright 2019 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.core.epoxy
import android.widget.TextView
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import im.vector.app.R
@EpoxyModelClass(layout = R.layout.item_help_footer)
abstract class HelpFooterItem : VectorEpoxyModel<HelpFooterItem.Holder>() {
@EpoxyAttribute
var text: String? = null
override fun bind(holder: Holder) {
super.bind(holder)
holder.textView.text = text
}
class Holder : VectorEpoxyHolder() {
val textView by bind<TextView>(R.id.itemHelpText)
}
}

View File

@ -0,0 +1,56 @@
/*
* Copyright 2019 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.core.epoxy
import android.os.Bundle
import android.os.Parcelable
import androidx.recyclerview.widget.RecyclerView
import im.vector.app.core.platform.DefaultListUpdateCallback
import im.vector.app.core.platform.Restorable
import java.util.concurrent.atomic.AtomicReference
private const val LAYOUT_MANAGER_STATE = "LAYOUT_MANAGER_STATE"
class LayoutManagerStateRestorer(layoutManager: RecyclerView.LayoutManager) : Restorable, DefaultListUpdateCallback {
private var layoutManager: RecyclerView.LayoutManager? = null
private var layoutManagerState = AtomicReference<Parcelable?>()
init {
this.layoutManager = layoutManager
}
fun clear() {
layoutManager = null
}
override fun onSaveInstanceState(outState: Bundle) {
val layoutManagerState = layoutManager?.onSaveInstanceState()
outState.putParcelable(LAYOUT_MANAGER_STATE, layoutManagerState)
}
override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
val parcelable = savedInstanceState?.getParcelable<Parcelable>(LAYOUT_MANAGER_STATE)
layoutManagerState.set(parcelable)
}
override fun onInserted(position: Int, count: Int) {
layoutManagerState.getAndSet(null)?.also {
layoutManager?.onRestoreInstanceState(it)
}
}
}

View File

@ -0,0 +1,28 @@
/*
* Copyright 2020 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.core.epoxy
import android.view.View
/**
* Generally we do not care about the View parameter in [View.OnClickListener.onClick()], so create facility to remove it.
*/
typealias ClickListener = () -> Unit
fun View.onClick(listener: ClickListener?) {
setOnClickListener { listener?.invoke() }
}

View File

@ -0,0 +1,43 @@
/*
* Copyright 2019 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.core.epoxy
import android.widget.ProgressBar
import android.widget.TextView
import androidx.core.view.isVisible
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import im.vector.app.R
import im.vector.app.core.extensions.setTextOrHide
@EpoxyModelClass(layout = R.layout.item_loading)
abstract class LoadingItem : VectorEpoxyModel<LoadingItem.Holder>() {
@EpoxyAttribute var loadingText: String? = null
@EpoxyAttribute var showLoader: Boolean = true
override fun bind(holder: Holder) {
super.bind(holder)
holder.progressBar.isVisible = showLoader
holder.textView.setTextOrHide(loadingText)
}
class Holder : VectorEpoxyHolder() {
val textView by bind<TextView>(R.id.loadingText)
val progressBar by bind<ProgressBar>(R.id.loadingProgress)
}
}

View File

@ -0,0 +1,38 @@
/*
* Copyright 2019 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.core.epoxy
import android.widget.TextView
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import im.vector.app.R
@EpoxyModelClass(layout = R.layout.item_no_result)
abstract class NoResultItem : VectorEpoxyModel<NoResultItem.Holder>() {
@EpoxyAttribute
var text: String? = null
override fun bind(holder: Holder) {
super.bind(holder)
holder.textView.text = text
}
class Holder : VectorEpoxyHolder() {
val textView by bind<TextView>(R.id.itemNoResultText)
}
}

View File

@ -0,0 +1,26 @@
/*
* Copyright (c) 2020 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.core.epoxy
import com.airbnb.epoxy.EpoxyModelClass
import im.vector.app.R
@EpoxyModelClass(layout = R.layout.item_loading_square)
abstract class SquareLoadingItem : VectorEpoxyModel<SquareLoadingItem.Holder>() {
class Holder : VectorEpoxyHolder()
}

View File

@ -0,0 +1,61 @@
/*
* Copyright 2019 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.core.epoxy
import android.view.View
import com.airbnb.epoxy.EpoxyHolder
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
/**
* A pattern for easier view binding with an [EpoxyHolder]
*
* See [SampleKotlinModelWithHolder] for a usage example.
*/
abstract class VectorEpoxyHolder : EpoxyHolder() {
lateinit var view: View
override fun bindView(itemView: View) {
view = itemView
}
protected fun <V : View> bind(id: Int): ReadOnlyProperty<VectorEpoxyHolder, V> =
Lazy { holder: VectorEpoxyHolder, prop ->
holder.view.findViewById(id) as V?
?: throw IllegalStateException("View ID $id for '${prop.name}' not found.")
}
/**
* Taken from Kotterknife.
* https://github.com/JakeWharton/kotterknife
*/
private class Lazy<V>(
private val initializer: (VectorEpoxyHolder, KProperty<*>) -> V
) : ReadOnlyProperty<VectorEpoxyHolder, V> {
private object EMPTY
private var value: Any? = EMPTY
override fun getValue(thisRef: VectorEpoxyHolder, property: KProperty<*>): V {
if (value == EMPTY) {
value = initializer(thisRef, property)
}
@Suppress("UNCHECKED_CAST")
return value as V
}
}
}

View File

@ -0,0 +1,68 @@
/*
* Copyright 2019 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.core.epoxy
import androidx.annotation.CallSuper
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry
import com.airbnb.epoxy.EpoxyModelWithHolder
import com.airbnb.epoxy.VisibilityState
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancelChildren
/**
* EpoxyModelWithHolder which can listen to visibility state change
*/
abstract class VectorEpoxyModel<H : VectorEpoxyHolder> : EpoxyModelWithHolder<H>(), LifecycleOwner {
protected val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
private val lifecycleRegistry: LifecycleRegistry = LifecycleRegistry(this)
override fun getLifecycle() = lifecycleRegistry
private var onModelVisibilityStateChangedListener: OnVisibilityStateChangedListener? = null
@CallSuper
override fun bind(holder: H) {
super.bind(holder)
lifecycleRegistry.currentState = Lifecycle.State.STARTED
}
@CallSuper
override fun unbind(holder: H) {
lifecycleRegistry.currentState = Lifecycle.State.DESTROYED
coroutineScope.coroutineContext.cancelChildren()
super.unbind(holder)
}
override fun onVisibilityStateChanged(visibilityState: Int, view: H) {
onModelVisibilityStateChangedListener?.onVisibilityStateChanged(visibilityState)
super.onVisibilityStateChanged(visibilityState, view)
}
fun setOnVisibilityStateChanged(listener: OnVisibilityStateChangedListener): VectorEpoxyModel<H> {
this.onModelVisibilityStateChangedListener = listener
return this
}
interface OnVisibilityStateChangedListener {
fun onVisibilityStateChanged(@VisibilityState.Visibility visibilityState: Int)
}
}

Some files were not shown because too many files have changed in this diff Show More