1
0
Fork 0
mirror of https://github.com/anyproto/anytype-kotlin.git synced 2025-06-08 13:57:10 +09:00

App | New menu | 4 (#2119)

This commit is contained in:
Evgenii Kozlov 2022-02-22 17:50:16 +03:00 committed by GitHub
parent 54191bf1db
commit d6c7373814
Signed by: github
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 612 additions and 132 deletions

View file

@ -10,6 +10,7 @@ import com.anytypeio.anytype.di.feature.sets.SelectFilterRelationModule
import com.anytypeio.anytype.di.feature.sets.viewer.ViewerCardSizeSelectModule
import com.anytypeio.anytype.di.feature.sets.viewer.ViewerImagePreviewSelectModule
import com.anytypeio.anytype.di.feature.settings.AboutAppModule
import com.anytypeio.anytype.di.feature.settings.AccountAndDataModule
import com.anytypeio.anytype.di.feature.wallpaper.WallpaperSelectModule
import com.anytypeio.anytype.di.main.MainComponent
@ -603,7 +604,12 @@ class ComponentManager(private val main: MainComponent) {
// Settings
val aboutAppComponent = Component { main.aboutAppComponent().module(AboutAppModule).build() }
val aboutAppComponent = Component {
main.aboutAppComponent().module(AboutAppModule).build()
}
val accountAndDataComponent = Component {
main.accountAndDataComponent().module(AccountAndDataModule).build()
}
class Component<T>(private val builder: () -> T) {

View file

@ -0,0 +1,49 @@
package com.anytypeio.anytype.di.feature.settings
import com.anytypeio.anytype.core_utils.di.scope.PerScreen
import com.anytypeio.anytype.domain.auth.interactor.Logout
import com.anytypeio.anytype.domain.auth.repo.AuthRepository
import com.anytypeio.anytype.domain.block.repo.BlockRepository
import com.anytypeio.anytype.domain.device.ClearFileCache
import com.anytypeio.anytype.ui.settings.AccountAndDataFragment
import com.anytypeio.anytype.ui_settings.account.AccountAndDataViewModel
import dagger.Module
import dagger.Provides
import dagger.Subcomponent
@Subcomponent(modules = [AccountAndDataModule::class])
@PerScreen
interface AccountAndDataSubComponent {
@Subcomponent.Builder
interface Builder {
fun module(module: AccountAndDataModule): Builder
fun build(): AccountAndDataSubComponent
}
fun inject(fragment: AccountAndDataFragment)
}
@Module
object AccountAndDataModule {
@JvmStatic
@Provides
@PerScreen
fun provideViewModelFactory(
clearFileCache: ClearFileCache,
logout: Logout
): AccountAndDataViewModel.Factory = AccountAndDataViewModel.Factory(
clearFileCache = clearFileCache,
logout = logout
)
@JvmStatic
@Provides
@PerScreen
fun clearFileCache(repo: BlockRepository): ClearFileCache = ClearFileCache(repo)
@JvmStatic
@Provides
@PerScreen
fun logout(repo: AuthRepository): Logout = Logout(repo)
}

View file

@ -3,6 +3,7 @@ package com.anytypeio.anytype.di.main
import com.anytypeio.anytype.app.AndroidApplication
import com.anytypeio.anytype.di.feature.*
import com.anytypeio.anytype.di.feature.settings.AboutAppSubComponent
import com.anytypeio.anytype.di.feature.settings.AccountAndDataSubComponent
import com.anytypeio.anytype.di.feature.wallpaper.WallpaperSelectSubComponent
import dagger.Component
import javax.inject.Singleton
@ -27,15 +28,12 @@ interface MainComponent {
fun authComponentBuilder(): AuthSubComponent.Builder
fun profileComponentBuilder(): ProfileSubComponent.Builder
fun aboutAppComponent() : AboutAppSubComponent.Builder
fun splashComponentBuilder(): SplashSubComponent.Builder
fun keychainPhraseComponentBuilder(): KeychainPhraseSubComponent.Builder
fun homeDashboardComponentBuilder(): HomeDashboardSubComponent.Builder
fun editorComponentBuilder(): EditorSubComponent.Builder
fun archiveComponentBuilder(): ArchiveSubComponent.Builder
fun linkAddComponentBuilder(): LinkSubComponent.Builder
fun createBookmarkBuilder(): CreateBookmarkSubComponent.Builder
fun debugSettingsBuilder(): DebugSettingsSubComponent.Builder
fun navigationComponentBuilder(): PageNavigationSubComponent.Builder
fun linkToObjectBuilder(): LinkToObjectSubComponent.Builder
fun linkToObjectOrWebBuilder() : LinkToObjectOrWebSubComponent.Builder
@ -45,8 +43,17 @@ interface MainComponent {
fun createSetComponentBuilder(): CreateSetSubComponent.Builder
fun createObjectTypeComponentBuilder(): CreateObjectTypeSubComponent.Builder
fun objectSetComponentBuilder(): ObjectSetSubComponent.Builder
fun otherSettingsComponentBuilder(): OtherSettingsSubComponent.Builder
fun objectTypeChangeComponent(): ObjectTypeChangeSubComponent.Builder
fun wallpaperSelectComponent(): WallpaperSelectSubComponent.Builder
fun createObjectComponent(): CreateObjectSubComponent.Builder
//region Settings
fun aboutAppComponent() : AboutAppSubComponent.Builder
fun accountAndDataComponent() : AccountAndDataSubComponent.Builder
fun debugSettingsBuilder(): DebugSettingsSubComponent.Builder
fun keychainPhraseComponentBuilder(): KeychainPhraseSubComponent.Builder
fun otherSettingsComponentBuilder(): OtherSettingsSubComponent.Builder
//endregion
}

View file

@ -69,8 +69,8 @@ class Navigator : AppNavigation {
override fun workspace() {}
override fun openProfile() {
navController?.navigate(R.id.action_open_profile)
override fun openSettings() {
navController?.navigate(R.id.action_open_settings)
}
override fun openDocument(id: String, editorSettings: EditorSettings?) {

View file

@ -37,7 +37,7 @@ abstract class NavigationFragment<BINDING : ViewBinding>(
is Command.SetupNewAccountScreen -> navigation.setupNewAccount()
is Command.SetupSelectedAccountScreen -> navigation.setupSelectedAccount(command.id)
is Command.ConfirmPinCodeScreen -> navigation.confirmPinCode(command.code)
is Command.OpenProfile -> navigation.openProfile()
is Command.OpenSettings -> navigation.openSettings()
is Command.OpenObject -> navigation.openDocument(command.id, command.editorSettings)
is Command.OpenArchive -> navigation.openArchive(command.target)
is Command.OpenObjectSet -> navigation.openObjectSet(command.target)

View file

@ -944,7 +944,7 @@ open class EditorFragment : NavigationFragment<FragmentEditorBinding>(R.layout.f
hideKeyboard()
findNavController()
.navigate(
R.id.objectTypeChangeFragment,
R.id.objectTypeChangeScreen,
bundleOf(
ObjectTypeChangeFragment.ARG_SMART_BLOCK_TYPE to command.smartBlockType
)

View file

@ -26,6 +26,7 @@ import com.anytypeio.anytype.ui.base.ViewStateFragment
import kotlinx.coroutines.launch
import javax.inject.Inject
@Deprecated("To be deleted")
class ProfileFragment : ViewStateFragment<ViewState<ProfileView>, FragmentProfileBinding>(R.layout.fragment_profile) {
@Inject
@ -56,7 +57,7 @@ class ProfileFragment : ViewStateFragment<ViewState<ProfileView>, FragmentProfil
is ViewState.Init -> {
with(binding) {
wallpaperText.setOnClickListener {
findNavController().navigate(R.id.wallpaperSetFragment)
findNavController().navigate(R.id.wallpaperSetScreen)
}
logoutButton.setOnClickListener { vm.onLogoutClicked() }
pinCodeText.setOnClickListener {

View file

@ -1,11 +1,15 @@
package com.anytypeio.anytype.ui.settings
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Typography
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.compose.ui.text.TextStyle
@ -16,8 +20,10 @@ import androidx.compose.ui.unit.sp
import androidx.fragment.app.viewModels
import com.anytypeio.anytype.BuildConfig
import com.anytypeio.anytype.R
import com.anytypeio.anytype.core_utils.ext.toast
import com.anytypeio.anytype.core_utils.ui.BaseBottomSheetComposeFragment
import com.anytypeio.anytype.di.common.componentManager
import com.anytypeio.anytype.ui.profile.KeychainPhraseDialog
import com.anytypeio.anytype.ui_settings.about.AboutAppScreen
import com.anytypeio.anytype.ui_settings.about.AboutAppViewModel
import javax.inject.Inject
@ -40,14 +46,41 @@ class AboutAppFragment : BaseBottomSheetComposeFragment() {
setContent {
MaterialTheme(typography = typography) {
AboutAppScreen(
vm = vm,
version = BuildConfig.VERSION_NAME
version = getVersionText(),
libraryVersion = vm.libraryVersion.collectAsState().value,
anytypeId = vm.userId.collectAsState().value,
onAnytypeIdClicked = { copyAnytypeIdToClipboard(vm.userId.value) }
)
}
}
}
}
private fun getVersionText() : String {
val version = BuildConfig.VERSION_NAME
return if (version.isNotEmpty()) {
if (BuildConfig.DEBUG)
"$version-debug"
else
"$version-alpha"
} else {
resources.getString(R.string.unknown)
}
}
private fun copyAnytypeIdToClipboard(id: String) {
try {
val clipboard =
requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clip =
ClipData.newPlainText(KeychainPhraseDialog.MNEMONIC_LABEL, id)
clipboard.setPrimaryClip(clip)
toast("Your Anytype ID is copied to clipboard.")
} catch (e: Exception) {
toast("Could not copy your Anytype ID. Please try again later, or copy it manually.")
}
}
override fun injectDependencies() {
componentManager().aboutAppComponent.get().inject(this)
}

View file

@ -5,13 +5,35 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.findNavController
import com.anytypeio.anytype.R
import com.anytypeio.anytype.core_utils.ext.toast
import com.anytypeio.anytype.core_utils.ui.BaseBottomSheetComposeFragment
import com.anytypeio.anytype.di.common.componentManager
import com.anytypeio.anytype.ui.dashboard.ClearCacheAlertFragment
import com.anytypeio.anytype.ui_settings.account.AccountAndDataScreen
import com.anytypeio.anytype.ui_settings.account.AccountAndDataViewModel
import kotlinx.coroutines.launch
import javax.inject.Inject
class AccountAndDataFragment : BaseBottomSheetComposeFragment() {
@Inject
lateinit var factory: AccountAndDataViewModel.Factory
private val vm by viewModels<AccountAndDataViewModel> { factory }
private val onKeychainPhraseClicked = {
findNavController().navigate(R.id.keychainDialog)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
@ -21,18 +43,48 @@ class AccountAndDataFragment : BaseBottomSheetComposeFragment() {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
MaterialTheme(typography = typography) {
AccountAndDataScreen()
AccountAndDataScreen(
onKeychainPhraseClicked = onKeychainPhraseClicked,
onClearFileCachedClicked = { proceedWithClearFileCacheWarning() },
onDeleteAccountClicked = { toast(resources.getString(R.string.coming_soon)) },
onResetAccountClicked = { toast(resources.getString(R.string.coming_soon)) },
onLogoutClicked = { vm.onLogoutClicked() },
onPinCodeClicked = { toast(resources.getString(R.string.coming_soon)) },
isLogoutInProgress = vm.isLoggingOut.collectAsState().value,
isClearCacheInProgress = vm.isClearFileCacheInProgress.collectAsState().value
)
}
}
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
vm.commands.collect { command ->
when(command) {
AccountAndDataViewModel.Command.Logout -> {
findNavController().navigate(R.id.actionLogout)
}
}
}
}
}
}
private fun proceedWithClearFileCacheWarning() {
val dialog = ClearCacheAlertFragment.new()
dialog.onClearAccepted = { vm.onClearFileCacheAccepted() }
dialog.show(childFragmentManager, null)
}
override fun injectDependencies() {
// TODO in the next PR
componentManager().accountAndDataComponent.get().inject(this)
}
override fun releaseDependencies() {
// TODO in the next PR
componentManager().accountAndDataComponent.release()
}
}

View file

@ -0,0 +1,40 @@
package com.anytypeio.anytype.ui.settings
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.compose.material.MaterialTheme
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.navigation.fragment.findNavController
import com.anytypeio.anytype.R
import com.anytypeio.anytype.core_utils.ui.BaseBottomSheetComposeFragment
import com.anytypeio.anytype.ui_settings.appearance.AppearanceScreen
class AppearanceFragment : BaseBottomSheetComposeFragment() {
private val onWallpaperClicked = {
findNavController().navigate(R.id.wallpaperSetScreen)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
MaterialTheme(typography = typography) {
AppearanceScreen(
onWallpaperClicked = onWallpaperClicked
)
}
}
}
}
override fun injectDependencies() {}
override fun releaseDependencies() {}
}

View file

@ -7,11 +7,29 @@ import android.view.ViewGroup
import androidx.compose.material.MaterialTheme
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.navigation.fragment.findNavController
import com.anytypeio.anytype.R
import com.anytypeio.anytype.core_utils.ui.BaseBottomSheetComposeFragment
import com.anytypeio.anytype.ui_settings.main.MainSettingScreen
class MainSettingFragment : BaseBottomSheetComposeFragment() {
private val onAccountAndDataClicked = {
findNavController().navigate(R.id.actionOpenAccountAndDataScreen)
}
private val onAboutAppClicked = {
findNavController().navigate(R.id.actionOpenAboutAppScreen)
}
private val onPersonalizationClicked = {
findNavController().navigate(R.id.actionOpenPersonalizationScreen)
}
private val onAppearanceClicked = {
findNavController().navigate(R.id.actionOpenAppearanceScreen)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
@ -21,7 +39,12 @@ class MainSettingFragment : BaseBottomSheetComposeFragment() {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
MaterialTheme(typography = typography) {
MainSettingScreen()
MainSettingScreen(
onAccountAndDataClicked = onAccountAndDataClicked,
onAboutAppClicked = onAboutAppClicked,
onAppearanceClicked = onAppearanceClicked,
onPersonalizationClicked = onPersonalizationClicked
)
}
}
}

View file

@ -10,10 +10,8 @@ import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import com.anytypeio.anytype.R
import com.anytypeio.anytype.core_utils.ext.gone
import com.anytypeio.anytype.core_utils.ext.subscribe
import com.anytypeio.anytype.core_utils.ext.toast
import com.anytypeio.anytype.core_utils.ext.visible
import com.anytypeio.anytype.core_utils.ui.BaseBottomSheetFragment
import com.anytypeio.anytype.databinding.FragmentUserSettingsBinding
import com.anytypeio.anytype.di.common.componentManager
@ -42,7 +40,6 @@ class OtherSettingsFragment : BaseBottomSheetFragment<FragmentUserSettingsBindin
super.onViewCreated(view, savedInstanceState)
binding.tvDefaultObjectTypeTitle.setOnClickListener { vm.onObjectTypeClicked() }
binding.btnDefaultObjectType.setOnClickListener { vm.onObjectTypeClicked() }
binding.btnClearFileCache.setOnClickListener { vm.onClearCacheClicked() }
binding.ivArrowForward.setOnClickListener { vm.onObjectTypeClicked() }
}
@ -50,12 +47,6 @@ class OtherSettingsFragment : BaseBottomSheetFragment<FragmentUserSettingsBindin
super.onStart()
with(lifecycleScope) {
jobs += subscribe(vm.commands) { observe(it) }
jobs += subscribe(vm.isClearFileCacheInProgress) { isInProgress ->
if (isInProgress)
binding.clearFileCacheProgressBar.visible()
else
binding.clearFileCacheProgressBar.gone()
}
}
}
@ -64,7 +55,7 @@ class OtherSettingsFragment : BaseBottomSheetFragment<FragmentUserSettingsBindin
is OtherSettingsViewModel.Command.Exit -> dismiss()
is OtherSettingsViewModel.Command.NavigateToObjectTypesScreen -> {
findNavController().navigate(
R.id.objectTypeChangeFragment,
R.id.objectTypeChangeScreen,
bundleOf(
ObjectTypeChangeFragment.ARG_SMART_BLOCK_TYPE to command.smartBlockType
)

View file

@ -25,7 +25,7 @@
android:layout_height="wrap_content"
android:layout_marginTop="18dp"
android:gravity="center"
android:text="@string/other_settings"
android:text="@string/personalization"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/sheet_top" />
@ -91,37 +91,6 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btnDefaultObjectType" />
<FrameLayout
android:id="@+id/btnClearFileCache"
android:layout_width="0dp"
android:layout_height="52dp"
android:background="@drawable/default_ripple"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/divider">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="20dp"
android:fontFamily="@font/inter_regular"
android:text="Clear file cache"
android:textColor="#F55522"
android:textSize="17sp" />
<ProgressBar
android:id="@+id/clearFileCacheProgressBar"
style="?android:attr/progressBarStyleSmall"
android:visibility="gone"
android:indeterminateTint="@color/light_grayish"
android:layout_marginEnd="20dp"
android:layout_gravity="center_vertical|end"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</FrameLayout>
<View
android:id="@+id/divider2"
android:layout_width="0dp"
@ -133,6 +102,6 @@
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btnClearFileCache" />
app:layout_constraintTop_toBottomOf="@+id/btnDefaultObjectType" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -127,12 +127,8 @@
android:label="DesktopFragment"
tools:layout="@layout/fragment_dashboard">
<action
android:id="@+id/action_open_profile"
app:destination="@id/profileScreen"
app:enterAnim="@anim/slide_in_right"
app:exitAnim="@anim/slide_out_left"
app:popEnterAnim="@anim/slide_in_left"
app:popExitAnim="@anim/slide_out_right" />
android:id="@+id/action_open_settings"
app:destination="@id/settingsScreen"/>
<action
android:id="@+id/action_desktopScreen_to_pageNavigationFragment"
app:destination="@id/pageNavigationFragment" />
@ -144,11 +140,14 @@
app:destination="@id/createSetScreen" />
</fragment>
<fragment
android:id="@+id/profileScreen"
android:name="com.anytypeio.anytype.ui.profile.ProfileFragment"
android:label="ProfileScreen"
tools:layout="@layout/fragment_profile">
<dialog
android:id="@+id/settingsScreen"
android:name="com.anytypeio.anytype.ui.settings.MainSettingFragment"
android:label="SettingScreen">
<action android:id="@+id/actionOpenAccountAndDataScreen" app:destination="@id/accountAndDataScreen"/>
<action android:id="@+id/actionOpenAboutAppScreen" app:destination="@id/aboutAppScreen"/>
<action android:id="@+id/actionOpenPersonalizationScreen" app:destination="@+id/personalizationScreen"/>
<action android:id="@+id/actionOpenAppearanceScreen" app:destination="@+id/appearanceScreen"/>
<action
android:id="@+id/action_profileScreen_to_splashFragment"
app:destination="@+id/main_navigation"
@ -169,12 +168,27 @@
app:destination="@id/debugSettingsFragment" />
<action
android:id="@+id/action_profileScreen_to_userSettingsFragment"
app:destination="@id/userSettingsFragment"
app:destination="@id/personalizationScreen"
app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/nav_default_exit_anim"
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
app:popExitAnim="@anim/nav_default_pop_exit_anim" />
</fragment>
</dialog>
<dialog
android:id="@+id/accountAndDataScreen"
android:name="com.anytypeio.anytype.ui.settings.AccountAndDataFragment">
<action
android:id="@+id/actionLogout"
app:destination="@+id/main_navigation"
app:popUpTo="@+id/main_navigation"
app:popUpToInclusive="true" />
</dialog>
<dialog
android:id="@+id/aboutAppScreen"
android:name="com.anytypeio.anytype.ui.settings.AboutAppFragment" />
<fragment
android:id="@+id/createAccountScreen"
android:name="com.anytypeio.anytype.ui.auth.account.CreateAccountFragment"
@ -422,17 +436,22 @@
android:label="CreateSetScreen"
tools:layout="@layout/fragment_create_set" />
<dialog
android:id="@+id/userSettingsFragment"
android:id="@+id/personalizationScreen"
android:name="com.anytypeio.anytype.ui.settings.OtherSettingsFragment"
android:label="fragment_user_settings"
tools:layout="@layout/fragment_user_settings"/>
<dialog
android:id="@+id/wallpaperSetFragment"
android:id="@+id/wallpaperSetScreen"
android:name="com.anytypeio.anytype.ui.dashboard.WallpaperSelectFragment"
android:label="fragment_wallpaper_set"
tools:layout="@layout/fragment_wallpaper_select"/>
<dialog
android:id="@+id/objectTypeChangeFragment"
android:id="@+id/appearanceScreen"
android:name="com.anytypeio.anytype.ui.settings.AppearanceFragment"
android:label="fragment_wallpaper_set"
tools:layout="@layout/fragment_wallpaper_select"/>
<dialog
android:id="@+id/objectTypeChangeScreen"
android:name="com.anytypeio.anytype.ui.objects.ObjectTypeChangeFragment"
android:label="ObjectTypeChangeFragment"
tools:layout="@layout/fragment_object_type_change"/>

View file

@ -3,6 +3,7 @@ package com.anytypeio.anytype.core_ui.foundation
import androidx.annotation.DrawableRes
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.MaterialTheme
@ -59,11 +60,13 @@ fun Divider(
@Composable
fun Option(
@DrawableRes image: Int,
text: String
text: String,
onClick: () -> Unit = {}
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.height(52.dp)
modifier = Modifier.height(52.dp).clickable(onClick = onClick)
) {
Image(
painterResource(image),

View file

@ -406,7 +406,7 @@ class HomeDashboardViewModel(
analytics = analytics,
eventName = EventsDictionary.POPUP_SETTINGS
)
navigation.postValue(EventWrapper(AppNavigation.Command.OpenProfile))
navigation.postValue(EventWrapper(AppNavigation.Command.OpenSettings))
}
fun onPageSearchClicked() {

View file

@ -15,7 +15,7 @@ interface AppNavigation {
fun setupSelectedAccount(id: String)
fun chooseAccount()
fun workspace()
fun openProfile()
fun openSettings()
fun openArchive(target: String)
fun openObjectSet(target: String)
@ -69,7 +69,7 @@ interface AppNavigation {
data class LaunchObjectFromSplash(val target: Id) : Command()
data class LaunchObjectSetFromSplash(val target: Id) : Command()
object OpenProfile : Command()
object OpenSettings : Command()
object OpenKeychainScreen : Command()
object OpenPinCodeScreen : Command()
object OpenUserSettingsScreen : Command()

View file

@ -36,6 +36,7 @@ dependencies {
def unitTestDependencies = rootProject.ext.unitTesting
implementation applicationDependencies.lifecycleViewModel
implementation applicationDependencies.lifecycleRuntime
implementation applicationDependencies.compose
implementation applicationDependencies.composeFoundation
@ -44,6 +45,8 @@ dependencies {
debugImplementation applicationDependencies.composeTooling
implementation applicationDependencies.timber
testImplementation unitTestDependencies.junit
testImplementation unitTestDependencies.kotlinTest
}

View file

@ -1,12 +1,12 @@
package com.anytypeio.anytype.ui_settings.about
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.colorResource
@ -19,8 +19,10 @@ import com.anytypeio.anytype.ui_settings.R
@Composable
fun AboutAppScreen(
vm: AboutAppViewModel,
version: String
libraryVersion: String,
anytypeId: String,
version: String,
onAnytypeIdClicked: () -> Unit
) {
Column {
Box(
@ -87,7 +89,7 @@ fun AboutAppScreen(
contentAlignment = Alignment.CenterEnd
) {
Text(
text = vm.libraryVersion.collectAsState().value,
text = libraryVersion,
fontSize = 17.sp,
color = colorResource(R.color.text_primary)
)
@ -99,7 +101,7 @@ fun AboutAppScreen(
end = 20.dp,
top = 12.dp,
bottom = 32.dp
)
).clickable(onClick = onAnytypeIdClicked)
) {
Box(modifier = Modifier.weight(1.0f, true)) {
Text(
@ -113,7 +115,7 @@ fun AboutAppScreen(
contentAlignment = Alignment.CenterEnd
) {
Text(
text = vm.userId.collectAsState().value,
text = anytypeId,
fontSize = 17.sp,
color = colorResource(R.color.text_primary)
)

View file

@ -1,7 +1,9 @@
package com.anytypeio.anytype.ui_settings.account
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.material.CircularProgressIndicator
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
@ -16,7 +18,16 @@ import com.anytypeio.anytype.core_ui.foundation.*
import com.anytypeio.anytype.ui_settings.R
@Composable
fun AccountAndDataScreen() {
fun AccountAndDataScreen(
onKeychainPhraseClicked: () -> Unit,
onClearFileCachedClicked: () -> Unit,
onResetAccountClicked: () -> Unit,
onDeleteAccountClicked: () -> Unit,
onPinCodeClicked: () -> Unit,
onLogoutClicked: () -> Unit,
isLogoutInProgress: Boolean,
isClearCacheInProgress: Boolean
) {
Column {
Box(Modifier.padding(vertical = 6.dp).align(Alignment.CenterHorizontally)) {
Dragger()
@ -24,33 +35,40 @@ fun AccountAndDataScreen() {
Toolbar(stringResource(R.string.account_and_data))
Section(stringResource(R.string.access))
Option(
image = R.drawable.ic_key,
text = stringResource(R.string.keychain_phrase)
image = R.drawable.ic_keychain_phrase,
text = stringResource(R.string.keychain_phrase),
onClick = onKeychainPhraseClicked
)
Divider(paddingStart = 60.dp)
Pincode()
Pincode(onPinCodeClicked)
Divider(paddingStart = 60.dp)
Section(stringResource(R.string.data))
Action(
ActionWithProgressBar(
name = stringResource(R.string.clear_file_cache),
color = colorResource(R.color.text_primary)
color = colorResource(R.color.text_primary),
onClick = onClearFileCachedClicked,
isInProgress = isClearCacheInProgress
)
Divider()
Section(stringResource(R.string.account))
Action(
name = stringResource(R.string.reset_account),
color = colorResource(R.color.anytype_text_red)
color = colorResource(R.color.anytype_text_red),
onClick = onResetAccountClicked
)
Divider()
Action(
name = stringResource(R.string.delete_account),
color = colorResource(R.color.anytype_text_red)
color = colorResource(R.color.anytype_text_red),
onClick = onDeleteAccountClicked
)
Divider()
Box(Modifier.height(20.dp))
Action(
ActionWithProgressBar(
name = stringResource(R.string.log_out),
color = colorResource(R.color.text_primary)
color = colorResource(R.color.text_primary),
onClick = onLogoutClicked,
isInProgress = isLogoutInProgress
)
Divider()
Box(Modifier.height(54.dp))
@ -76,10 +94,12 @@ fun Section(name: String) {
}
@Composable
fun Pincode() {
fun Pincode(
onClick: () -> Unit
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.height(52.dp)
modifier = Modifier.height(52.dp).clickable(onClick = onClick)
) {
Image(
painterResource(R.drawable.ic_pin_code),
@ -115,10 +135,11 @@ fun Pincode() {
@Composable
fun Action(
name: String,
color: Color = Color.Unspecified
color: Color = Color.Unspecified,
onClick: () -> Unit = {}
) {
Box(
modifier = Modifier.height(52.dp).fillMaxWidth(),
modifier = Modifier.height(52.dp).fillMaxWidth().clickable(onClick = onClick),
contentAlignment = Alignment.CenterStart
) {
Text(
@ -130,4 +151,32 @@ fun Action(
)
)
}
}
@Composable
fun ActionWithProgressBar(
name: String,
color: Color = Color.Unspecified,
onClick: () -> Unit = {},
isInProgress: Boolean
) {
Box(
modifier = Modifier.height(52.dp).fillMaxWidth().clickable(onClick = onClick),
contentAlignment = Alignment.CenterStart
) {
Text(
text = name,
color = color,
fontSize = 17.sp,
modifier = Modifier.padding(
start = 20.dp
)
)
if (isInProgress) {
CircularProgressIndicator(
modifier = Modifier.align(Alignment.CenterEnd).padding(end = 20.dp).size(24.dp),
color = colorResource(R.color.shape_secondary)
)
}
}
}

View file

@ -0,0 +1,82 @@
package com.anytypeio.anytype.ui_settings.account
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import com.anytypeio.anytype.domain.auth.interactor.Logout
import com.anytypeio.anytype.domain.base.BaseUseCase
import com.anytypeio.anytype.domain.base.Interactor
import com.anytypeio.anytype.domain.device.ClearFileCache
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
import timber.log.Timber
class AccountAndDataViewModel(
private val clearFileCache: ClearFileCache,
private val logout: Logout,
) : ViewModel() {
val commands = MutableSharedFlow<Command>(replay = 0)
val isClearFileCacheInProgress = MutableStateFlow(false)
val isLoggingOut = MutableStateFlow(false)
fun onClearFileCacheAccepted() {
viewModelScope.launch {
clearFileCache(BaseUseCase.None).collect { status ->
when (status) {
is Interactor.Status.Started -> {
isClearFileCacheInProgress.value = true
}
is Interactor.Status.Error -> {
isClearFileCacheInProgress.value = false
val msg = "Error while clearing file cache: ${status.throwable.message}"
Timber.e(status.throwable, "Error while clearing file cache")
// TODO send toast
}
Interactor.Status.Success -> {
isClearFileCacheInProgress.value = false
}
}
}
}
}
fun onLogoutClicked() {
viewModelScope.launch {
logout(params = BaseUseCase.None).collect { status ->
when (status) {
is Interactor.Status.Started -> {
isLoggingOut.value = true
}
is Interactor.Status.Success -> {
isLoggingOut.value = false
commands.emit(Command.Logout)
}
is Interactor.Status.Error -> {
isLoggingOut.value = true
Timber.e(status.throwable, "Error while logging out")
}
}
}
}
}
class Factory(
private val clearFileCache: ClearFileCache,
private val logout: Logout,
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return AccountAndDataViewModel(
clearFileCache = clearFileCache,
logout = logout
) as T
}
}
sealed class Command {
object Logout : Command()
}
}

View file

@ -0,0 +1,35 @@
package com.anytypeio.anytype.ui_settings.appearance
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.anytypeio.anytype.core_ui.foundation.Divider
import com.anytypeio.anytype.core_ui.foundation.Dragger
import com.anytypeio.anytype.core_ui.foundation.Option
import com.anytypeio.anytype.core_ui.foundation.Toolbar
import com.anytypeio.anytype.ui_settings.R
@Composable
fun AppearanceScreen(
onWallpaperClicked: () -> Unit
) {
Column {
Box(Modifier.padding(vertical = 6.dp).align(Alignment.CenterHorizontally)) {
Dragger()
}
Toolbar(stringResource(R.string.appearance))
Option(
image = R.drawable.ic_wallpaper,
text = stringResource(R.string.wallpaper),
onClick = onWallpaperClicked
)
Divider(paddingStart = 60.dp)
Box(Modifier.height(54.dp))
}
}

View file

@ -15,7 +15,12 @@ import com.anytypeio.anytype.core_ui.foundation.Option
import com.anytypeio.anytype.ui_settings.R
@Composable
fun MainSettingScreen() {
fun MainSettingScreen(
onAccountAndDataClicked: () -> Unit,
onAboutAppClicked: () -> Unit,
onPersonalizationClicked: () -> Unit,
onAppearanceClicked: () -> Unit
) {
Column {
Box(
modifier = Modifier.padding(vertical = 6.dp).align(Alignment.CenterHorizontally)
@ -23,23 +28,27 @@ fun MainSettingScreen() {
Dragger()
}
Option(
image = R.drawable.ic_key,
text = stringResource(R.string.account_and_data)
image = R.drawable.ic_account_and_data,
text = stringResource(R.string.account_and_data),
onClick = onAccountAndDataClicked
)
Divider()
Option(
image = R.drawable.ic_key,
text = stringResource(R.string.personalization)
image = R.drawable.ic_personalization,
text = stringResource(R.string.personalization),
onClick = onPersonalizationClicked
)
Divider()
Option(
image = R.drawable.ic_key,
text = stringResource(R.string.appearance)
image = R.drawable.ic_appearance,
text = stringResource(R.string.appearance),
onClick = onAppearanceClicked
)
Divider()
Option(
image = R.drawable.ic_key,
text = stringResource(R.string.about)
image = R.drawable.ic_about,
text = stringResource(R.string.about),
onClick = onAboutAppClicked
)
Divider()
Box(modifier = Modifier.height(16.dp))

View file

@ -0,0 +1,18 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="28dp"
android:height="28dp"
android:viewportWidth="28"
android:viewportHeight="28">
<path
android:pathData="M6,0L22,0A6,6 0,0 1,28 6L28,22A6,6 0,0 1,22 28L6,28A6,6 0,0 1,0 22L0,6A6,6 0,0 1,6 0z"
android:fillColor="#FFB522"/>
<path
android:pathData="M11,22C13.7614,22 16,19.7614 16,17C16,14.2386 13.7614,12 11,12C8.2386,12 6,14.2386 6,17C6,19.7614 8.2386,22 11,22Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M18,9h3v13h-3z"
android:fillColor="#ffffff"/>
<path
android:pathData="M18,6V9H8.5V6L18,6Z"
android:fillColor="#ffffff"/>
</vector>

View file

@ -0,0 +1,15 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="28dp"
android:height="28dp"
android:viewportWidth="28"
android:viewportHeight="28">
<path
android:pathData="M6,0L22,0A6,6 0,0 1,28 6L28,22A6,6 0,0 1,22 28L6,28A6,6 0,0 1,0 22L0,6A6,6 0,0 1,6 0z"
android:fillColor="#2AA7EE"/>
<path
android:pathData="M10.4707,9.5a3.5,4 0,1 0,7 0a3.5,4 0,1 0,-7 0z"
android:fillColor="#ffffff"/>
<path
android:pathData="M6.9707,20C6.9707,17.2386 9.2093,15 11.9707,15H15.9707C18.7321,15 20.9707,17.2386 20.9707,20V21C20.9707,21.5523 20.523,22 19.9707,22H7.9707C7.4184,22 6.9707,21.5523 6.9707,21V20Z"
android:fillColor="#ffffff"/>
</vector>

View file

@ -0,0 +1,22 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="28dp"
android:height="28dp"
android:viewportWidth="28"
android:viewportHeight="28">
<path
android:pathData="M6,0L22,0A6,6 0,0 1,28 6L28,22A6,6 0,0 1,22 28L6,28A6,6 0,0 1,0 22L0,6A6,6 0,0 1,6 0z"
android:fillColor="#F55522"/>
<path
android:pathData="M19,11.5C19,10.1193 17.8807,9 16.5,9H11.5C10.2905,9 9.2816,9.8589 9.05,11H13.5C15.433,11 17,12.567 17,14.5V18.95C18.1411,18.7184 19,17.7095 19,16.5V11.5Z"
android:fillColor="#ffffff"
android:fillType="evenOdd"/>
<path
android:pathData="M21.9998,8.5C21.9998,7.1193 20.8805,6 19.4998,6H14.4998C13.2903,6 12.2814,6.8589 12.0498,8H16.4998C18.4328,8 19.9998,9.567 19.9998,11.5V15.95C21.1409,15.7184 21.9998,14.7095 21.9998,13.5V8.5Z"
android:fillColor="#ffffff"
android:fillType="evenOdd"/>
<path
android:strokeWidth="1"
android:pathData="M16.5,14.5L16.5,19.5A3,3 0,0 1,13.5 22.5L8.5,22.5A3,3 0,0 1,5.5 19.5L5.5,14.5A3,3 0,0 1,8.5 11.5L13.5,11.5A3,3 0,0 1,16.5 14.5z"
android:fillColor="#ffffff"
android:strokeColor="#F55522"/>
</vector>

View file

@ -1,18 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="28dp"
android:height="29dp"
android:viewportWidth="28"
android:viewportHeight="29">
<path
android:pathData="M6,0.125L22,0.125A6,6 0,0 1,28 6.125L28,22.125A6,6 0,0 1,22 28.125L6,28.125A6,6 0,0 1,0 22.125L0,6.125A6,6 0,0 1,6 0.125z"
android:fillColor="#2AA7EE"/>
<path
android:pathData="M17,11.125m-5,0a5,5 0,1 1,10 0a5,5 0,1 1,-10 0"
android:fillColor="#ffffff"/>
<path
android:pathData="M13,12.625L15.5,15.125L13,17.625H11V19.625H9V21.625L8.5,22.125H6V19.625L13,12.625Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M20,9.5962C20,10.4246 19.3284,11.0962 18.5,11.0962C17.6716,11.0962 17,10.4246 17,9.5962C17,8.7678 17.6716,8.0962 18.5,8.0962C19.3284,8.0962 20,8.7678 20,9.5962Z"
android:fillColor="#2AA7EE"/>
</vector>

View file

@ -0,0 +1,18 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="28dp"
android:height="28dp"
android:viewportWidth="28"
android:viewportHeight="28">
<path
android:pathData="M6,0L22,0A6,6 0,0 1,28 6L28,22A6,6 0,0 1,22 28L6,28A6,6 0,0 1,0 22L0,6A6,6 0,0 1,6 0z"
android:fillColor="#2AA7EE"/>
<path
android:pathData="M12.0015,13H16.0015V16L14.5015,17.5L16.0015,19L14.5015,20.5L16.0015,22V23L14.0015,25.0312L12.0015,23V13Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M14,9m-5,0a5,5 0,1 1,10 0a5,5 0,1 1,-10 0"
android:fillColor="#ffffff"/>
<path
android:pathData="M14,7.5m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"
android:fillColor="#2AA7EE"/>
</vector>

View file

@ -0,0 +1,33 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="28dp"
android:height="28dp"
android:viewportWidth="28"
android:viewportHeight="28">
<path
android:pathData="M6,0L22,0A6,6 0,0 1,28 6L28,22A6,6 0,0 1,22 28L6,28A6,6 0,0 1,0 22L0,6A6,6 0,0 1,6 0z"
android:fillColor="#AB50CC"/>
<path
android:pathData="M5.9707,20L5.9707,20A0.75,0.75 0,0 1,6.7207 19.25L21.2207,19.25A0.75,0.75 0,0 1,21.9707 20L21.9707,20A0.75,0.75 0,0 1,21.2207 20.75L6.7207,20.75A0.75,0.75 0,0 1,5.9707 20z"
android:fillColor="#ffffff"/>
<path
android:strokeWidth="1"
android:pathData="M15.4707,20L15.4707,20A2,2 0,0 1,17.4707 18L17.4707,18A2,2 0,0 1,19.4707 20L19.4707,20A2,2 0,0 1,17.4707 22L17.4707,22A2,2 0,0 1,15.4707 20z"
android:fillColor="#9F43C1"
android:strokeColor="#ffffff"/>
<path
android:pathData="M5.9707,14L5.9707,14A0.75,0.75 0,0 1,6.7207 13.25L21.2207,13.25A0.75,0.75 0,0 1,21.9707 14L21.9707,14A0.75,0.75 0,0 1,21.2207 14.75L6.7207,14.75A0.75,0.75 0,0 1,5.9707 14z"
android:fillColor="#ffffff"/>
<path
android:pathData="M5.9707,8L5.9707,8A0.75,0.75 0,0 1,6.7207 7.25L21.2207,7.25A0.75,0.75 0,0 1,21.9707 8L21.9707,8A0.75,0.75 0,0 1,21.2207 8.75L6.7207,8.75A0.75,0.75 0,0 1,5.9707 8z"
android:fillColor="#ffffff"/>
<path
android:strokeWidth="1"
android:pathData="M8.4707,14L8.4707,14A2,2 0,0 1,10.4707 12L10.4707,12A2,2 0,0 1,12.4707 14L12.4707,14A2,2 0,0 1,10.4707 16L10.4707,16A2,2 0,0 1,8.4707 14z"
android:fillColor="#9F43C1"
android:strokeColor="#ffffff"/>
<path
android:strokeWidth="1"
android:pathData="M15.4707,8L15.4707,8A2,2 0,0 1,17.4707 6L17.4707,6A2,2 0,0 1,19.4707 8L19.4707,8A2,2 0,0 1,17.4707 10L17.4707,10A2,2 0,0 1,15.4707 8z"
android:fillColor="#9F43C1"
android:strokeColor="#ffffff"/>
</vector>

View file

@ -1,16 +1,16 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="28dp"
android:height="29dp"
android:height="28dp"
android:viewportWidth="28"
android:viewportHeight="29">
android:viewportHeight="28">
<path
android:pathData="M6,0.125L22,0.125A6,6 0,0 1,28 6.125L28,22.125A6,6 0,0 1,22 28.125L6,28.125A6,6 0,0 1,0 22.125L0,6.125A6,6 0,0 1,6 0.125z"
android:pathData="M6,0L22,0A6,6 0,0 1,28 6L28,22A6,6 0,0 1,22 28L6,28A6,6 0,0 1,0 22L0,6A6,6 0,0 1,6 0z"
android:fillColor="#5DD400"/>
<path
android:pathData="M8,12.125L20,12.125A1,1 0,0 1,21 13.125L21,21.125A1,1 0,0 1,20 22.125L8,22.125A1,1 0,0 1,7 21.125L7,13.125A1,1 0,0 1,8 12.125z"
android:pathData="M8,12L20,12A1,1 0,0 1,21 13L21,21A1,1 0,0 1,20 22L8,22A1,1 0,0 1,7 21L7,13A1,1 0,0 1,8 12z"
android:fillColor="#ffffff"/>
<path
android:pathData="M14,5.875L14,5.875A3.75,3.75 0,0 1,17.75 9.625L17.75,15.625A3.75,3.75 0,0 1,14 19.375L14,19.375A3.75,3.75 0,0 1,10.25 15.625L10.25,9.625A3.75,3.75 0,0 1,14 5.875z"
android:pathData="M14,5.75L14,5.75A3.75,3.75 0,0 1,17.75 9.5L17.75,15.5A3.75,3.75 0,0 1,14 19.25L14,19.25A3.75,3.75 0,0 1,10.25 15.5L10.25,9.5A3.75,3.75 0,0 1,14 5.75z"
android:strokeWidth="1.5"
android:fillColor="#00000000"
android:strokeColor="#ffffff"/>

View file

@ -0,0 +1,18 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="28dp"
android:height="28dp"
android:viewportWidth="28"
android:viewportHeight="28">
<path
android:pathData="M6,0L22,0A6,6 0,0 1,28 6L28,22A6,6 0,0 1,22 28L6,28A6,6 0,0 1,0 22L0,6A6,6 0,0 1,6 0z"
android:fillColor="#AB50CC"/>
<path
android:pathData="M18,22.0039C18.5523,22.0039 19,21.5562 19,21.0039V7.0039C19,6.4516 18.5523,6.0039 18,6.0039H10C9.4477,6.0039 9,6.4516 9,7.0039L9,21.0039C9,21.5562 9.4477,22.0039 10,22.0039H18Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M21,20.0039C21.5523,20.0039 22,19.5562 22,19.0039V9.0039C22,8.4516 21.5523,8.0039 21,8.0039H20.5V20.0039H21Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M7,20.0039C6.4477,20.0039 6,19.5562 6,19.0039L6,9.0039C6,8.4516 6.4477,8.0039 7,8.0039H7.5L7.5,20.0039H7Z"
android:fillColor="#ffffff"/>
</vector>

View file

@ -16,4 +16,5 @@
<string name="log_out">Logout</string>
<string name="pin_code">Pin code</string>
<string name="access">Access</string>
<string name="wallpaper">Wallpaper</string>
</resources>