Skip to content

Commit

Permalink
Merge pull request #1768 from novasamatech/master
Browse files Browse the repository at this point in the history
Master
  • Loading branch information
antonijzelinskij authored Jan 17, 2025
2 parents ba9b143 + e4d1e28 commit 226a6cd
Show file tree
Hide file tree
Showing 14 changed files with 116 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class SplitScreenInteractor(
}

suspend fun removeAllTabs() {
repository.removeAllTabs()
val metaAccount = accountRepository.getSelectedMetaAccount()
repository.removeTabsForMetaAccount(metaAccount.id)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ import io.novafoundation.nova.feature_dapp_impl.presentation.DAppRouter
class SplitScreenFragmentModule {

@Provides
fun provideInteractor(repository: BrowserTabExternalRepository, accountRepository: AccountRepository): SplitScreenInteractor {
fun provideInteractor(
repository: BrowserTabExternalRepository,
accountRepository: AccountRepository
): SplitScreenInteractor {
return SplitScreenInteractor(repository, accountRepository)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,34 @@ import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.Transaction
import io.novafoundation.nova.core_db.model.BrowserTabLocal
import kotlinx.coroutines.flow.Flow

@Dao
abstract class BrowserTabsDao {

@Query("SELECT id FROM browser_tabs WHERE metaId = :metaId")
abstract fun getTabIdsFor(metaId: Long): List<String>

@Query("SELECT * FROM browser_tabs WHERE metaId = :metaId ORDER BY creationTime DESC")
abstract fun observeTabsByMetaId(metaId: Long): Flow<List<BrowserTabLocal>>

@Insert(onConflict = OnConflictStrategy.REPLACE)
abstract suspend fun insertTab(tab: BrowserTabLocal)

@Transaction
open suspend fun removeTabsByMetaId(metaId: Long): List<String> {
val tabIds = getTabIdsFor(metaId)
removeTabsByIds(tabIds)
return tabIds
}

@Query("DELETE FROM browser_tabs WHERE id = :tabId")
abstract suspend fun removeTab(tabId: String)

@Query("DELETE FROM browser_tabs")
abstract suspend fun removeAllTabs()
@Query("DELETE FROM browser_tabs WHERE id IN (:tabIds)")
abstract suspend fun removeTabsByIds(tabIds: List<String>)

@Query("UPDATE browser_tabs SET pageName = :pageName, pageIconPath = :pageIconPath, pagePicturePath = :pagePicturePath WHERE id = :tabId")
abstract suspend fun updatePageSnapshot(tabId: String, pageName: String?, pageIconPath: String?, pagePicturePath: String?)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ interface BrowserTabExternalRepository {

fun observeTabsWithNames(metaId: Long): Flow<List<SimpleTabModel>>

suspend fun removeAllTabs()
suspend fun removeTabsForMetaAccount(metaId: Long): List<String>
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ import io.novafoundation.nova.feature_dapp_api.data.model.SimpleTabModel
import io.novafoundation.nova.feature_dapp_impl.utils.tabs.models.BrowserTab
import io.novafoundation.nova.feature_dapp_impl.utils.tabs.models.PageSnapshot
import java.util.Date
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.withContext

class RealBrowserTabRepository(
private val browserTabsDao: BrowserTabsDao
Expand All @@ -28,8 +30,10 @@ class RealBrowserTabRepository(
}
}

override suspend fun removeAllTabs() {
browserTabsDao.removeAllTabs()
override suspend fun removeTabsForMetaAccount(metaId: Long): List<String> {
return withContext(Dispatchers.Default) {
browserTabsDao.removeTabsByMetaId(metaId)
}
}

override suspend fun savePageSnapshot(tabId: String, snapshot: PageSnapshot) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import io.novafoundation.nova.common.mixin.actionAwaitable.ActionAwaitableMixin
import io.novafoundation.nova.common.resources.ContextManager
import io.novafoundation.nova.common.resources.ResourceManager
import io.novafoundation.nova.common.utils.coroutines.RootScope
import io.novafoundation.nova.common.utils.permissions.PermissionsAskerFactory
import io.novafoundation.nova.core_db.dao.BrowserHostSettingsDao
import io.novafoundation.nova.core_db.dao.BrowserTabsDao
import io.novafoundation.nova.core_db.dao.DappAuthorizationDao
Expand Down Expand Up @@ -51,6 +52,8 @@ interface DAppFeatureDependencies {

val rootScope: RootScope

val permissionsAskerFactory: PermissionsAskerFactory

fun currencyRepository(): CurrencyRepository

fun accountRepository(): AccountRepository
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import androidx.activity.OnBackPressedCallback
import androidx.core.app.SharedElementCallback
import androidx.core.os.bundleOf
import androidx.core.transition.addListener
import androidx.lifecycle.viewModelScope
import coil.ImageLoader
import io.novafoundation.nova.common.base.BaseFragment
import io.novafoundation.nova.common.di.FeatureUtils
Expand All @@ -37,6 +38,7 @@ import io.novafoundation.nova.feature_dapp_impl.web3.webview.CompoundWeb3Injecto
import io.novafoundation.nova.feature_dapp_impl.web3.webview.Web3WebViewClient
import io.novafoundation.nova.feature_dapp_impl.web3.webview.WebViewFileChooser
import io.novafoundation.nova.feature_dapp_impl.web3.webview.WebViewHolder
import io.novafoundation.nova.feature_dapp_impl.web3.webview.WebViewPermissionAsker
import io.novafoundation.nova.feature_external_sign_api.presentation.externalSign.AuthorizeDappBottomSheet
import kotlinx.android.synthetic.main.fragment_dapp_browser.dappBrowserAddressBar
import kotlinx.android.synthetic.main.fragment_dapp_browser.dappBrowserAddressBarGroup
Expand Down Expand Up @@ -76,6 +78,9 @@ class DAppBrowserFragment : BaseFragment<DAppBrowserViewModel>(), OptionsBottomS
@Inject
lateinit var fileChooser: WebViewFileChooser

@Inject
lateinit var permissionAsker: WebViewPermissionAsker

@Inject
lateinit var imageLoader: ImageLoader

Expand Down Expand Up @@ -265,7 +270,7 @@ class DAppBrowserFragment : BaseFragment<DAppBrowserViewModel>(), OptionsBottomS
dappBrowserProgress.progress = 0
}

private fun createChromeClient() = Web3ChromeClient(fileChooser, dappBrowserProgress)
private fun createChromeClient() = Web3ChromeClient(permissionAsker, fileChooser, dappBrowserProgress, viewModel.viewModelScope)

private fun updateButtonsState() {
dappBrowserForward.isEnabled = dappBrowserWebView?.canGoForward() ?: false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import io.novafoundation.nova.common.di.scope.ScreenScope
import io.novafoundation.nova.common.di.viewmodel.ViewModelKey
import io.novafoundation.nova.common.di.viewmodel.ViewModelModule
import io.novafoundation.nova.common.mixin.actionAwaitable.ActionAwaitableMixin
import io.novafoundation.nova.common.utils.permissions.PermissionsAsker
import io.novafoundation.nova.common.utils.permissions.PermissionsAskerFactory
import io.novafoundation.nova.core_db.dao.BrowserHostSettingsDao
import io.novafoundation.nova.feature_account_api.domain.interfaces.SelectedAccountUseCase
import io.novafoundation.nova.feature_dapp_api.data.repository.BrowserHostSettingsRepository
Expand All @@ -25,6 +27,7 @@ import io.novafoundation.nova.feature_dapp_impl.presentation.search.DAppSearchCo
import io.novafoundation.nova.feature_dapp_impl.utils.tabs.BrowserTabService
import io.novafoundation.nova.feature_dapp_impl.web3.states.ExtensionStoreFactory
import io.novafoundation.nova.feature_dapp_impl.web3.webview.WebViewFileChooser
import io.novafoundation.nova.feature_dapp_impl.web3.webview.WebViewPermissionAsker
import io.novafoundation.nova.feature_external_sign_api.model.ExternalSignCommunicator
import io.novafoundation.nova.runtime.multiNetwork.ChainRegistry

Expand Down Expand Up @@ -55,6 +58,20 @@ class DAppBrowserModule {
fragment: Fragment
) = WebViewFileChooser(fragment)

@Provides
@ScreenScope
fun providePermissionAsker(
permissionsAskerFactory: PermissionsAskerFactory,
fragment: Fragment,
router: DAppRouter
) = permissionsAskerFactory.create(fragment, router)

@Provides
@ScreenScope
fun provideWebViewPermissionAsker(permissionsAsker: PermissionsAsker.Presentation): WebViewPermissionAsker {
return WebViewPermissionAsker(permissionsAsker)
}

@Provides
internal fun provideViewModel(fragment: Fragment, factory: ViewModelProvider.Factory): DAppBrowserViewModel {
return ViewModelProvider(fragment, factory).get(DAppBrowserViewModel::class.java)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import io.novafoundation.nova.common.mixin.actionAwaitable.awaitAction
import io.novafoundation.nova.common.mixin.actionAwaitable.confirmingAction
import io.novafoundation.nova.common.utils.Urls
import io.novafoundation.nova.common.utils.mapList
import io.novafoundation.nova.feature_account_api.domain.interfaces.SelectedAccountUseCase
import io.novafoundation.nova.feature_dapp_impl.presentation.DAppRouter
import io.novafoundation.nova.feature_dapp_api.presentation.browser.main.DAppBrowserPayload
import io.novafoundation.nova.feature_dapp_impl.utils.tabs.BrowserTabService
Expand All @@ -16,7 +17,8 @@ import kotlinx.coroutines.launch
class BrowserTabsViewModel(
private val router: DAppRouter,
private val browserTabService: BrowserTabService,
private val actionAwaitableMixinFactory: ActionAwaitableMixin.Factory
private val actionAwaitableMixinFactory: ActionAwaitableMixin.Factory,
private val accountUseCase: SelectedAccountUseCase
) : BaseViewModel() {

val closeAllTabsConfirmation = actionAwaitableMixinFactory.confirmingAction<Unit>()
Expand All @@ -43,7 +45,8 @@ class BrowserTabsViewModel(
fun closeAllTabs() = launch {
closeAllTabsConfirmation.awaitAction()

browserTabService.removeAllTabs()
val metaAccount = accountUseCase.getSelectedMetaAccount()
browserTabService.removeTabsForMetaAccount(metaAccount.id)
router.closeTabsScreen()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import dagger.multibindings.IntoMap
import io.novafoundation.nova.common.di.viewmodel.ViewModelKey
import io.novafoundation.nova.common.di.viewmodel.ViewModelModule
import io.novafoundation.nova.common.mixin.actionAwaitable.ActionAwaitableMixin
import io.novafoundation.nova.feature_account_api.domain.interfaces.SelectedAccountUseCase
import io.novafoundation.nova.feature_dapp_impl.presentation.DAppRouter
import io.novafoundation.nova.feature_dapp_impl.presentation.tab.BrowserTabsViewModel
import io.novafoundation.nova.feature_dapp_impl.utils.tabs.BrowserTabService
Expand All @@ -27,12 +28,14 @@ class BrowserTabsModule {
fun provideViewModel(
router: DAppRouter,
browserTabService: BrowserTabService,
actionAwaitableMixinFactory: ActionAwaitableMixin.Factory
actionAwaitableMixinFactory: ActionAwaitableMixin.Factory,
accountUseCase: SelectedAccountUseCase
): ViewModel {
return BrowserTabsViewModel(
router = router,
browserTabService = browserTabService,
actionAwaitableMixinFactory = actionAwaitableMixinFactory
actionAwaitableMixinFactory = actionAwaitableMixinFactory,
accountUseCase = accountUseCase
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ interface BrowserTabService {

suspend fun removeTab(tabId: String)

suspend fun removeAllTabs()
suspend fun removeTabsForMetaAccount(metaId: Long)
}

suspend fun BrowserTabService.createAndSelectTab(url: String) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,13 @@ class RealBrowserTabService(
activeSessions.remove(tabId)
}

override suspend fun removeAllTabs() {
override suspend fun removeTabsForMetaAccount(metaId: Long) {
selectTab(null)
browserTabInternalRepository.removeAllTabs()
activeSessions.removeAll()

val removedTabs = browserTabInternalRepository.removeTabsForMetaAccount(metaId)
removedTabs.forEach {
activeSessions.remove(it)
}
}

override fun makeCurrentTabSnapshot() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
package io.novafoundation.nova.feature_dapp_impl.web3.webview

import android.net.Uri
import android.webkit.PermissionRequest
import android.webkit.ValueCallback
import android.webkit.WebChromeClient
import android.webkit.WebView
import android.widget.ProgressBar
import io.novafoundation.nova.common.utils.setVisible
import kotlinx.coroutines.CoroutineScope

private const val MAX_PROGRESS = 100

class Web3ChromeClient(
private val permissionAsker: WebViewPermissionAsker,
private val fileChooser: WebViewFileChooser,
private val progressBar: ProgressBar
private val progressBar: ProgressBar,
private val coroutineScope: CoroutineScope
) : WebChromeClient() {

override fun onPermissionRequest(request: PermissionRequest) {
permissionAsker.requestPermission(coroutineScope, request)
}

override fun onProgressChanged(view: WebView, newProgress: Int) {
progressBar.progress = newProgress

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package io.novafoundation.nova.feature_dapp_impl.web3.webview

import android.Manifest
import android.webkit.PermissionRequest
import io.novafoundation.nova.common.utils.permissions.PermissionsAsker
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch

class WebViewPermissionAsker(
private val permissionsAsker: PermissionsAsker.Presentation
) {

fun requestPermission(coroutineScope: CoroutineScope, request: PermissionRequest) {
coroutineScope.launch {
val permissions = mapPermissionRequest(request)

if (permissions.isNotEmpty()) {
val result = permissionsAsker.requirePermissionsOrExit(*permissions)

if (result) {
request.grant(request.resources)
} else {
request.deny()
}
} else {
request.deny()
}
}
}

private fun mapPermissionRequest(request: PermissionRequest) = request.resources.flatMap { resource ->
when (resource) {
PermissionRequest.RESOURCE_VIDEO_CAPTURE -> listOf(Manifest.permission.CAMERA)
PermissionRequest.RESOURCE_PROTECTED_MEDIA_ID -> listOf(Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.CAMERA)
else -> emptyList()
}
}.toTypedArray()
}

0 comments on commit 226a6cd

Please sign in to comment.