diff --git a/changelog.d/6646.misc b/changelog.d/6646.misc new file mode 100644 index 0000000000..0f2e3f0881 --- /dev/null +++ b/changelog.d/6646.misc @@ -0,0 +1 @@ +[App Layout] Obsolete settings are not shown when App Layout flag is enabled diff --git a/changelog.d/6876.feature b/changelog.d/6876.feature new file mode 100644 index 0000000000..12a2b78a1e --- /dev/null +++ b/changelog.d/6876.feature @@ -0,0 +1 @@ +[App Layout] - Invites now show empty screen after you reject last invite diff --git a/docs/nightly_build.md b/docs/nightly_build.md index 7750e0466a..77cc676c7f 100644 --- a/docs/nightly_build.md +++ b/docs/nightly_build.md @@ -47,7 +47,7 @@ git checkout develop mv towncrier.toml towncrier.toml.bak sed 's/CHANGES\.md/CHANGES_NIGHTLY\.md/' towncrier.toml.bak > towncrier.toml rm towncrier.toml.bak -yes n | towncrier --version nightly +yes n | towncrier build --version nightly ./gradlew assembleGplayNightly appDistributionUploadGplayNightly $CI_GRADLE_ARG_PROPERTIES ``` diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml index cc63ceb2a9..520843add9 100644 --- a/library/ui-strings/src/main/res/values/strings.xml +++ b/library/ui-strings/src/main/res/values/strings.xml @@ -454,6 +454,9 @@ Invites + Nothing new. + This is where your new requests and invites will be. + Conversations Matrix contacts only @@ -3259,4 +3262,15 @@ Nothing to report. This is where your unread messages will show up, when you have some. + Welcome to a new view! + + To simplify your ${app_name}, tabs are now optional. Manage them using the top-right menu. + Access Spaces + + Access your Spaces (bottom-right) faster and easier than ever before. + Give Feedback + + Tap top right to see the option to feedback. + Try it out + diff --git a/library/ui-styles/src/main/res/values-h720dp/dimens.xml b/library/ui-styles/src/main/res/values-h720dp/dimens.xml index 1a7791720d..2a7b12cf2f 100644 --- a/library/ui-styles/src/main/res/values-h720dp/dimens.xml +++ b/library/ui-styles/src/main/res/values-h720dp/dimens.xml @@ -2,4 +2,8 @@ 0.05 0.40 - \ No newline at end of file + + 16dp + 40dp + 46dp + diff --git a/library/ui-styles/src/main/res/values/dimens.xml b/library/ui-styles/src/main/res/values/dimens.xml index 53f1044a12..758dd6e978 100644 --- a/library/ui-styles/src/main/res/values/dimens.xml +++ b/library/ui-styles/src/main/res/values/dimens.xml @@ -74,4 +74,9 @@ 112dp + + + 8dp + 16dp + 28dp diff --git a/vector/build.gradle b/vector/build.gradle index 4f8db05e73..65adc7089c 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -257,7 +257,7 @@ dependencies { // UnifiedPush implementation 'com.github.UnifiedPush:android-connector:2.0.1' // UnifiedPush gplay flavor only - gplayImplementation('com.github.UnifiedPush:android-embedded_fcm_distributor:2.1.2') { + gplayImplementation('com.github.UnifiedPush:android-embedded_fcm_distributor:2.1.3') { exclude group: 'com.google.firebase', module: 'firebase-core' exclude group: 'com.google.firebase', module: 'firebase-analytics' exclude group: 'com.google.firebase', module: 'firebase-measurement-connector' diff --git a/vector/src/main/AndroidManifest.xml b/vector/src/main/AndroidManifest.xml index e87bbad77a..c4022576c3 100644 --- a/vector/src/main/AndroidManifest.xml +++ b/vector/src/main/AndroidManifest.xml @@ -338,6 +338,7 @@ + diff --git a/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt b/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt index b21b4778e3..a40aeaaa15 100644 --- a/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt +++ b/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt @@ -53,6 +53,7 @@ import im.vector.app.features.home.room.detail.upgrade.MigrateRoomViewModel import im.vector.app.features.home.room.list.RoomListViewModel import im.vector.app.features.home.room.list.home.HomeRoomListViewModel import im.vector.app.features.home.room.list.home.invites.InvitesViewModel +import im.vector.app.features.home.room.list.home.release.ReleaseNotesViewModel import im.vector.app.features.homeserver.HomeServerCapabilitiesViewModel import im.vector.app.features.invite.InviteUsersToRoomViewModel import im.vector.app.features.location.LocationSharingViewModel @@ -624,4 +625,9 @@ interface MavericksViewModelModule { @IntoMap @MavericksViewModelKey(InvitesViewModel::class) fun invitesViewModel(factory: InvitesViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + + @Binds + @IntoMap + @MavericksViewModelKey(ReleaseNotesViewModel::class) + fun releaseNotesViewModel(factory: ReleaseNotesViewModel.Factory): MavericksAssistedViewModelFactory<*, *> } diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt index 7665f95a40..6880b85df4 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt @@ -60,6 +60,7 @@ import im.vector.app.features.disclaimer.showDisclaimerDialog import im.vector.app.features.home.room.list.actions.RoomListSharedAction import im.vector.app.features.home.room.list.actions.RoomListSharedActionViewModel import im.vector.app.features.home.room.list.home.layout.HomeLayoutSettingBottomDialogFragment +import im.vector.app.features.home.room.list.home.release.ReleaseNotesActivity import im.vector.app.features.matrixto.MatrixToBottomSheet import im.vector.app.features.matrixto.OriginOfMatrixTo import im.vector.app.features.navigation.Navigator @@ -268,6 +269,7 @@ class HomeActivity : } is HomeActivityViewEvents.OnCrossSignedInvalidated -> handleCrossSigningInvalidated(it) HomeActivityViewEvents.ShowAnalyticsOptIn -> handleShowAnalyticsOptIn() + HomeActivityViewEvents.ShowReleaseNotes -> handleShowReleaseNotes() HomeActivityViewEvents.NotifyUserForThreadsMigration -> handleNotifyUserForThreadsMigration() is HomeActivityViewEvents.MigrateThreads -> migrateThreadsIfNeeded(it.checkSession) } @@ -282,6 +284,10 @@ class HomeActivity : homeActivityViewModel.handle(HomeActivityViewActions.ViewStarted) } + private fun handleShowReleaseNotes() { + startActivity(Intent(this, ReleaseNotesActivity::class.java)) + } + private fun showSpaceSettings(spaceId: String) { // open bottom sheet SpaceSettingsMenuBottomSheet diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewEvents.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewEvents.kt index 170550d5b4..e0b9e8ceb5 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewEvents.kt @@ -31,6 +31,7 @@ sealed interface HomeActivityViewEvents : VectorViewEvents { data class OnCrossSignedInvalidated(val userItem: MatrixItem.UserItem) : HomeActivityViewEvents object PromptToEnableSessionPush : HomeActivityViewEvents object ShowAnalyticsOptIn : HomeActivityViewEvents + object ShowReleaseNotes : HomeActivityViewEvents object NotifyUserForThreadsMigration : HomeActivityViewEvents data class MigrateThreads(val checkSession: Boolean) : HomeActivityViewEvents object StartRecoverySetupFlow : HomeActivityViewEvents diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt index cfe76706a5..dd54285fb5 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt @@ -26,11 +26,13 @@ import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.platform.VectorViewModel +import im.vector.app.features.VectorFeatures import im.vector.app.features.analytics.AnalyticsConfig import im.vector.app.features.analytics.AnalyticsTracker import im.vector.app.features.analytics.extensions.toAnalyticsType import im.vector.app.features.analytics.plan.Signup import im.vector.app.features.analytics.store.AnalyticsStore +import im.vector.app.features.home.room.list.home.release.ReleaseNotesPreferencesStore import im.vector.app.features.login.ReAuthHelper import im.vector.app.features.onboarding.AuthenticationDescription import im.vector.app.features.raw.wellknown.ElementWellKnown @@ -82,6 +84,8 @@ class HomeActivityViewModel @AssistedInject constructor( private val vectorPreferences: VectorPreferences, private val analyticsTracker: AnalyticsTracker, private val analyticsConfig: AnalyticsConfig, + private val releaseNotesPreferencesStore: ReleaseNotesPreferencesStore, + private val vectorFeatures: VectorFeatures, ) : VectorViewModel(initialState) { @AssistedFactory @@ -110,9 +114,27 @@ class HomeActivityViewModel @AssistedInject constructor( checkSessionPushIsOn() observeCrossSigningReset() observeAnalytics() + observeReleaseNotes() initThreadsMigration() } + private fun observeReleaseNotes() = withState { state -> + // we don't want to show release notes for new users or after relogin + if (state.authenticationDescription == null && vectorFeatures.isNewAppLayoutEnabled()) { + releaseNotesPreferencesStore.appLayoutOnboardingShown.onEach { isAppLayoutOnboardingShown -> + if (!isAppLayoutOnboardingShown) { + releaseNotesPreferencesStore.setAppLayoutOnboardingShown(true) + _viewEvents.post(HomeActivityViewEvents.ShowReleaseNotes) + } + }.launchIn(viewModelScope) + } else { + // we assume that users which came from auth flow either have seen updates already (relogin) or don't need them (new user) + viewModelScope.launch { + releaseNotesPreferencesStore.setAppLayoutOnboardingShown(true) + } + } + } + private fun observeAnalytics() { if (analyticsConfig.isEnabled) { analyticsStore.didAskUserConsentFlow diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/home/invites/InvitesFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/list/home/invites/InvitesFragment.kt index 74b46cec33..f557483289 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/home/invites/InvitesFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/home/invites/InvitesFragment.kt @@ -20,15 +20,18 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.lifecycle.lifecycleScope import com.airbnb.mvrx.fragmentViewModel -import com.airbnb.mvrx.withState import dagger.hilt.android.AndroidEntryPoint import im.vector.app.core.extensions.configureWith +import im.vector.app.core.platform.StateView import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.databinding.FragmentInvitesBinding import im.vector.app.features.analytics.plan.ViewRoom import im.vector.app.features.home.room.list.RoomListListener import im.vector.app.features.notifications.NotificationDrawerManager +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo import javax.inject.Inject @@ -51,6 +54,8 @@ class InvitesFragment : VectorBaseFragment(), RoomListLi setupToolbar(views.invitesToolbar) .allowBack() + views.invitesStateView.contentView = views.invitesRecycler + views.invitesRecycler.configureWith(controller) controller.listener = this @@ -62,13 +67,31 @@ class InvitesFragment : VectorBaseFragment(), RoomListLi when (it) { is InvitesViewEvents.Failure -> showFailure(it.throwable) is InvitesViewEvents.OpenRoom -> handleOpenRoom(it.roomSummary, it.shouldCloseInviteView) - InvitesViewEvents.Close -> handleClose() } } - } - private fun handleClose() { - requireActivity().finish() + viewModel.invites.onEach { + when (it) { + is InvitesContentState.Content -> { + views.invitesStateView.state = StateView.State.Content + controller.submitList(it.content) + } + is InvitesContentState.Empty -> { + views.invitesStateView.state = StateView.State.Empty( + title = it.title, + image = it.image, + message = it.message + ) + } + is InvitesContentState.Error -> { + when (views.invitesStateView.state) { + StateView.State.Content -> showErrorInSnackbar(it.throwable) + else -> views.invitesStateView.state = StateView.State.Error(it.throwable.message) + } + } + InvitesContentState.Loading -> views.invitesStateView.state = StateView.State.Loading + } + }.launchIn(viewLifecycleOwner.lifecycleScope) } private fun handleOpenRoom(roomSummary: RoomSummary, shouldCloseInviteView: Boolean) { @@ -83,14 +106,6 @@ class InvitesFragment : VectorBaseFragment(), RoomListLi } } - override fun invalidate(): Unit = withState(viewModel) { state -> - super.invalidate() - - state.pagedList?.observe(viewLifecycleOwner) { list -> - controller.submitList(list) - } - } - override fun onRejectRoomInvitation(room: RoomSummary) { notificationDrawerManager.updateEvents { it.clearMemberShipNotificationForRoom(room.roomId) } viewModel.handle(InvitesAction.RejectInvitation(room)) diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/home/invites/InvitesViewEvents.kt b/vector/src/main/java/im/vector/app/features/home/room/list/home/invites/InvitesViewEvents.kt index d68577cf95..21310592a4 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/home/invites/InvitesViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/home/invites/InvitesViewEvents.kt @@ -22,5 +22,4 @@ import org.matrix.android.sdk.api.session.room.model.RoomSummary sealed class InvitesViewEvents : VectorViewEvents { data class Failure(val throwable: Throwable) : InvitesViewEvents() data class OpenRoom(val roomSummary: RoomSummary, val shouldCloseInviteView: Boolean) : InvitesViewEvents() - object Close : InvitesViewEvents() } diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/home/invites/InvitesViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/list/home/invites/InvitesViewModel.kt index b0d854be66..b8034d2364 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/home/invites/InvitesViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/home/invites/InvitesViewModel.kt @@ -16,14 +16,25 @@ package im.vector.app.features.home.room.list.home.invites +import androidx.lifecycle.asFlow import androidx.paging.PagedList import com.airbnb.mvrx.MavericksViewModelFactory import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject +import im.vector.app.R import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.platform.VectorViewModel +import im.vector.app.core.resources.DrawableProvider +import im.vector.app.core.resources.StringProvider +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.launch import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.Session @@ -36,6 +47,8 @@ import timber.log.Timber class InvitesViewModel @AssistedInject constructor( @Assisted val initialState: InvitesViewState, private val session: Session, + private val stringProvider: StringProvider, + private val drawableProvider: DrawableProvider ) : VectorViewModel(initialState) { private val pagedListConfig = PagedList.Config.Builder() @@ -52,6 +65,11 @@ class InvitesViewModel @AssistedInject constructor( companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() + private val _invites = MutableSharedFlow(replay = 1) + val invites = _invites.asSharedFlow() + + private var invitesCount = -1 + init { observeInvites() } @@ -72,8 +90,6 @@ class InvitesViewModel @AssistedInject constructor( return@withState } - val shouldCloseInviteView = state.pagedList?.value?.size == 1 - viewModelScope.launch { try { session.roomService().leaveRoom(roomId) @@ -81,9 +97,6 @@ class InvitesViewModel @AssistedInject constructor( // Instead, we wait for the room to be rejected // Known bug: if the user is invited again (after rejecting the first invitation), the loading will be displayed instead of the buttons. // If we update the state, the button will be displayed again, so it's not ideal... - if (shouldCloseInviteView) { - _viewEvents.post(InvitesViewEvents.Close) - } } catch (failure: Throwable) { // Notify the user _viewEvents.post(InvitesViewEvents.Failure(failure)) @@ -101,9 +114,7 @@ class InvitesViewModel @AssistedInject constructor( } // close invites view when navigate to a room from the last one invite - val shouldCloseInviteView = state.pagedList?.value?.size == 1 - - _viewEvents.post(InvitesViewEvents.OpenRoom(action.roomSummary, shouldCloseInviteView)) + val shouldCloseInviteView = invitesCount == 1 // quick echo setState { @@ -117,6 +128,8 @@ class InvitesViewModel @AssistedInject constructor( } ) } + + _viewEvents.post(InvitesViewEvents.OpenRoom(action.roomSummary, shouldCloseInviteView)) } private fun observeInvites() { @@ -129,8 +142,26 @@ class InvitesViewModel @AssistedInject constructor( sortOrder = RoomSortOrder.ACTIVITY ) - setState { - copy(pagedList = pagedList) - } + pagedList.asFlow() + .map { + if (it.isEmpty()) { + InvitesContentState.Empty( + title = stringProvider.getString(R.string.invites_empty_title), + image = drawableProvider.getDrawable(R.drawable.ic_invites_empty), + message = stringProvider.getString(R.string.invites_empty_message) + ) + } else { + invitesCount = it.loadedCount + InvitesContentState.Content(it) + } + } + .catch { + emit(InvitesContentState.Error(it)) + } + .onStart { + emit(InvitesContentState.Loading) + }.onEach { + _invites.emit(it) + }.launchIn(viewModelScope) } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/home/invites/InvitesViewState.kt b/vector/src/main/java/im/vector/app/features/home/room/list/home/invites/InvitesViewState.kt index 708db29604..2f82c3fe76 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/home/invites/InvitesViewState.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/home/invites/InvitesViewState.kt @@ -16,13 +16,24 @@ package im.vector.app.features.home.room.list.home.invites -import androidx.lifecycle.LiveData +import android.graphics.drawable.Drawable import androidx.paging.PagedList import com.airbnb.mvrx.MavericksState import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState import org.matrix.android.sdk.api.session.room.model.RoomSummary data class InvitesViewState( - val pagedList: LiveData>? = null, val roomMembershipChanges: Map = emptyMap(), ) : MavericksState + +sealed interface InvitesContentState { + object Loading : InvitesContentState + data class Empty( + val title: CharSequence, + val image: Drawable?, + val message: CharSequence + ) : InvitesContentState + + data class Content(val content: PagedList) : InvitesContentState + data class Error(val throwable: Throwable) : InvitesContentState +} diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/home/release/ReleaseCarouselData.kt b/vector/src/main/java/im/vector/app/features/home/room/list/home/release/ReleaseCarouselData.kt new file mode 100644 index 0000000000..22431b0bf9 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/list/home/release/ReleaseCarouselData.kt @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.home.room.list.home.release + +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes + +class ReleaseCarouselData( + val items: List +) { + data class Item( + @StringRes val title: Int, + @StringRes val body: Int, + @DrawableRes val image: Int, + ) +} diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/home/release/ReleaseCarouselItem.kt b/vector/src/main/java/im/vector/app/features/home/room/list/home/release/ReleaseCarouselItem.kt new file mode 100644 index 0000000000..49eb0761f7 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/list/home/release/ReleaseCarouselItem.kt @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.home.room.list.home.release + +import android.widget.ImageView +import android.widget.TextView +import com.airbnb.epoxy.EpoxyAttribute +import com.airbnb.epoxy.EpoxyModelClass +import im.vector.app.R +import im.vector.app.core.epoxy.VectorEpoxyHolder +import im.vector.app.core.epoxy.VectorEpoxyModel + +@EpoxyModelClass +abstract class ReleaseCarouselItem : VectorEpoxyModel(R.layout.item_release_carousel) { + + @EpoxyAttribute + lateinit var item: ReleaseCarouselData.Item + + override fun bind(holder: Holder) { + super.bind(holder) + + holder.image.setImageResource(item.image) + holder.title.setText(item.title) + holder.body.setText(item.body) + } + + class Holder : VectorEpoxyHolder() { + val image by bind(R.id.carousel_item_image) + val title by bind(R.id.carousel_item_title) + val body by bind(R.id.carousel_item_body) + } +} diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/home/release/ReleaseNotesAction.kt b/vector/src/main/java/im/vector/app/features/home/room/list/home/release/ReleaseNotesAction.kt new file mode 100644 index 0000000000..7a66d00589 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/list/home/release/ReleaseNotesAction.kt @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.home.room.list.home.release + +import im.vector.app.core.platform.VectorViewModelAction + +sealed class ReleaseNotesAction : VectorViewModelAction { + data class NextPressed( + val isLastItemSelected: Boolean = false + ) : ReleaseNotesAction() + data class PageSelected( + val selectedPageIndex: Int = 0 + ) : ReleaseNotesAction() +} diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/home/release/ReleaseNotesActivity.kt b/vector/src/main/java/im/vector/app/features/home/room/list/home/release/ReleaseNotesActivity.kt new file mode 100644 index 0000000000..c5cc55d7bb --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/list/home/release/ReleaseNotesActivity.kt @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.home.room.list.home.release + +import dagger.hilt.android.AndroidEntryPoint +import im.vector.app.core.extensions.addFragment +import im.vector.app.core.platform.ScreenOrientationLocker +import im.vector.app.core.platform.VectorBaseActivity +import im.vector.app.databinding.ActivitySimpleBinding +import javax.inject.Inject + +@AndroidEntryPoint +class ReleaseNotesActivity : VectorBaseActivity() { + + @Inject lateinit var orientationLocker: ScreenOrientationLocker + + override fun getBinding() = ActivitySimpleBinding.inflate(layoutInflater) + + override fun getCoordinatorLayout() = views.coordinatorLayout + + override fun initUiAndData() { + orientationLocker.lockPhonesToPortrait(this) + if (isFirstCreation()) { + addFragment(views.simpleFragmentContainer, ReleaseNotesFragment::class.java) + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/home/release/ReleaseNotesCarouselController.kt b/vector/src/main/java/im/vector/app/features/home/room/list/home/release/ReleaseNotesCarouselController.kt new file mode 100644 index 0000000000..22d2915c47 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/list/home/release/ReleaseNotesCarouselController.kt @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021 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.home.room.list.home.release + +import com.airbnb.epoxy.TypedEpoxyController +import javax.inject.Inject + +class ReleaseNotesCarouselController @Inject constructor() : TypedEpoxyController() { + override fun buildModels(data: ReleaseCarouselData) { + data.items.forEachIndexed { index, item -> + releaseCarouselItem { + id(index) + item(item) + } + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/home/release/ReleaseNotesFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/list/home/release/ReleaseNotesFragment.kt new file mode 100644 index 0000000000..6b86897dc8 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/list/home/release/ReleaseNotesFragment.kt @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.home.room.list.home.release + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.lifecycle.DefaultLifecycleObserver +import androidx.lifecycle.LifecycleOwner +import androidx.viewpager2.widget.ViewPager2 +import com.airbnb.mvrx.fragmentViewModel +import com.google.android.material.tabs.TabLayoutMediator +import dagger.hilt.android.AndroidEntryPoint +import im.vector.app.R +import im.vector.app.core.epoxy.onClick +import im.vector.app.core.platform.VectorBaseFragment +import im.vector.app.databinding.BottomSheetReleaseNotesBinding +import javax.inject.Inject + +@AndroidEntryPoint +class ReleaseNotesFragment : VectorBaseFragment() { + + @Inject lateinit var carouselController: ReleaseNotesCarouselController + private var tabLayoutMediator: TabLayoutMediator? = null + + private val viewModel by fragmentViewModel(ReleaseNotesViewModel::class) + + override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetReleaseNotesBinding { + return BottomSheetReleaseNotesBinding.inflate(inflater, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + val carouselAdapter = carouselController.adapter + views.releaseNotesCarousel.adapter = carouselAdapter + + tabLayoutMediator = TabLayoutMediator(views.releaseNotesCarouselIndicator, views.releaseNotesCarousel) { _, _ -> } + .also { it.attach() } + + val pageCallback = object : ViewPager2.OnPageChangeCallback() { + override fun onPageSelected(position: Int) { + viewModel.handle(ReleaseNotesAction.PageSelected(position)) + updateButtonText(position) + } + } + + viewLifecycleOwner.lifecycle.addObserver(object : DefaultLifecycleObserver { + override fun onCreate(owner: LifecycleOwner) { + views.releaseNotesCarousel.registerOnPageChangeCallback(pageCallback) + } + + override fun onDestroy(owner: LifecycleOwner) { + views.releaseNotesCarousel.unregisterOnPageChangeCallback(pageCallback) + } + }) + + carouselController.setData(createCarouselData()) + + views.releaseNotesBtnClose.onClick { close() } + views.releaseNotesButtonNext.onClick { + val isLastItemSelected = with(views.releaseNotesCarouselIndicator) { + selectedTabPosition == tabCount - 1 + } + viewModel.handle(ReleaseNotesAction.NextPressed(isLastItemSelected)) + } + + viewModel.observeViewEvents { + when (it) { + is ReleaseNotesViewEvents.SelectPage -> selectPage(it.index) + ReleaseNotesViewEvents.Close -> close() + } + } + } + + private fun createCarouselData(): ReleaseCarouselData { + return ReleaseCarouselData( + listOf( + ReleaseCarouselData.Item( + R.string.onboarding_new_app_layout_welcome_title, + R.string.onboarding_new_app_layout_welcome_message, + R.drawable.ill_app_layout_onboarding_rooms + ), + ReleaseCarouselData.Item( + R.string.onboarding_new_app_layout_spaces_title, + R.string.onboarding_new_app_layout_spaces_message, + R.drawable.ill_app_layout_onboarding_spaces + ), + ReleaseCarouselData.Item( + R.string.onboarding_new_app_layout_feedback_title, + R.string.onboarding_new_app_layout_feedback_message, + R.drawable.ill_app_layout_onboarding_rooms + ), + ) + ) + } + + private fun close() { + requireActivity().finish() + } + + private fun selectPage(index: Int) { + views.releaseNotesCarouselIndicator.selectTab(views.releaseNotesCarouselIndicator.getTabAt(index)) + updateButtonText(index) + } + + private fun updateButtonText(selectedIndex: Int) { + val isLastItem = selectedIndex == views.releaseNotesCarouselIndicator.tabCount - 1 + if (isLastItem) { + views.releaseNotesButtonNext.setText(R.string.onboarding_new_app_layout_button_try) + } else { + views.releaseNotesButtonNext.setText(R.string.action_next) + } + } + + override fun onDestroyView() { + tabLayoutMediator?.detach() + tabLayoutMediator = null + + views.releaseNotesCarousel.adapter = null + super.onDestroyView() + } +} diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/home/release/ReleaseNotesPreferencesStore.kt b/vector/src/main/java/im/vector/app/features/home/room/list/home/release/ReleaseNotesPreferencesStore.kt new file mode 100644 index 0000000000..cefe107905 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/list/home/release/ReleaseNotesPreferencesStore.kt @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.home.room.list.home.release + +import android.content.Context +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.core.booleanPreferencesKey +import androidx.datastore.preferences.core.edit +import androidx.datastore.preferences.preferencesDataStore +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.map +import org.matrix.android.sdk.api.extensions.orFalse +import javax.inject.Inject + +private val Context.dataStore: DataStore by preferencesDataStore(name = "release_notes") + +class ReleaseNotesPreferencesStore @Inject constructor( + private val context: Context +) { + + private val isAppLayoutOnboardingShown = booleanPreferencesKey("SETTINGS_APP_LAYOUT_ONBOARDING_SHOWN") + + val appLayoutOnboardingShown: Flow = context.dataStore.data + .map { preferences -> preferences[isAppLayoutOnboardingShown].orFalse() } + .distinctUntilChanged() + + suspend fun setAppLayoutOnboardingShown(isShown: Boolean) { + context.dataStore.edit { settings -> + settings[isAppLayoutOnboardingShown] = isShown + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/home/release/ReleaseNotesViewEvents.kt b/vector/src/main/java/im/vector/app/features/home/room/list/home/release/ReleaseNotesViewEvents.kt new file mode 100644 index 0000000000..7901a8b28f --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/list/home/release/ReleaseNotesViewEvents.kt @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.home.room.list.home.release + +import im.vector.app.core.platform.VectorViewEvents + +sealed class ReleaseNotesViewEvents : VectorViewEvents { + object Close : ReleaseNotesViewEvents() + data class SelectPage(val index: Int) : ReleaseNotesViewEvents() +} diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/home/release/ReleaseNotesViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/list/home/release/ReleaseNotesViewModel.kt new file mode 100644 index 0000000000..23e2364d0c --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/list/home/release/ReleaseNotesViewModel.kt @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.home.room.list.home.release + +import com.airbnb.mvrx.MavericksViewModelFactory +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import im.vector.app.core.di.MavericksAssistedViewModelFactory +import im.vector.app.core.di.hiltMavericksViewModelFactory +import im.vector.app.core.platform.VectorDummyViewState +import im.vector.app.core.platform.VectorViewModel + +class ReleaseNotesViewModel @AssistedInject constructor( + @Assisted initialState: VectorDummyViewState, +) : VectorViewModel(initialState) { + + @AssistedFactory + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: VectorDummyViewState): ReleaseNotesViewModel + } + + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() + + private var selectedPageIndex = 0 + + init { + _viewEvents.post(ReleaseNotesViewEvents.SelectPage(0)) + } + + override fun handle(action: ReleaseNotesAction) { + when (action) { + is ReleaseNotesAction.NextPressed -> handleNextPressed(action) + is ReleaseNotesAction.PageSelected -> handlePageSelected(action) + } + } + + private fun handlePageSelected(action: ReleaseNotesAction.PageSelected) { + selectedPageIndex = action.selectedPageIndex + } + + private fun handleNextPressed(action: ReleaseNotesAction.NextPressed) { + if (action.isLastItemSelected) { + _viewEvents.post(ReleaseNotesViewEvents.Close) + } else { + _viewEvents.post(ReleaseNotesViewEvents.SelectPage(++selectedPageIndex)) + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt index 9661feb002..52c445f1fa 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt @@ -627,7 +627,7 @@ class OnboardingViewModel @AssistedInject constructor( _viewEvents.post(OnboardingViewEvents.OnAccountCreated) } AuthenticationDescription.Login -> { - setState { copy(isLoading = false) } + setState { copy(isLoading = false, selectedAuthenticationState = SelectedAuthenticationState(authenticationDescription)) } _viewEvents.post(OnboardingViewEvents.OnAccountSignedIn) } } diff --git a/vector/src/main/java/im/vector/app/features/popup/PopupAlertManager.kt b/vector/src/main/java/im/vector/app/features/popup/PopupAlertManager.kt index 40ef6d819e..b1327f0caf 100644 --- a/vector/src/main/java/im/vector/app/features/popup/PopupAlertManager.kt +++ b/vector/src/main/java/im/vector/app/features/popup/PopupAlertManager.kt @@ -28,6 +28,7 @@ import im.vector.app.core.time.Clock import im.vector.app.core.utils.isAnimationEnabled import im.vector.app.features.MainActivity import im.vector.app.features.analytics.ui.consent.AnalyticsOptInActivity +import im.vector.app.features.home.room.list.home.release.ReleaseNotesActivity import im.vector.app.features.pin.PinActivity import im.vector.app.features.signout.hard.SignedOutActivity import im.vector.app.features.themes.ThemeUtils @@ -307,6 +308,7 @@ class PopupAlertManager @Inject constructor( activity !is PinActivity && activity !is SignedOutActivity && activity !is AnalyticsOptInActivity && + activity !is ReleaseNotesActivity && activity is VectorBaseActivity<*> && alert.shouldBeDisplayedIn.invoke(activity) } diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt b/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt index 483b246595..529a6d9b14 100755 --- a/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt @@ -168,6 +168,11 @@ class VectorPreferences @Inject constructor( const val SETTINGS_LABS_AUTO_REPORT_UISI = "SETTINGS_LABS_AUTO_REPORT_UISI" const val SETTINGS_PREF_SPACE_SHOW_ALL_ROOM_IN_HOME = "SETTINGS_PREF_SPACE_SHOW_ALL_ROOM_IN_HOME" + /** + * This is not preference, but category on preferences screen which contains [SETTINGS_PREF_SPACE_SHOW_ALL_ROOM_IN_HOME]. + * Needed to show/hide this category, depending on visibility of [SETTINGS_PREF_SPACE_SHOW_ALL_ROOM_IN_HOME]. */ + const val SETTINGS_PREF_SPACE_CATEGORY = "SETTINGS_PREF_SPACE_CATEGORY" + private const val SETTINGS_DEVELOPER_MODE_PREFERENCE_KEY = "SETTINGS_DEVELOPER_MODE_PREFERENCE_KEY" private const val SETTINGS_LABS_SHOW_HIDDEN_EVENTS_PREFERENCE_KEY = "SETTINGS_LABS_SHOW_HIDDEN_EVENTS_PREFERENCE_KEY" private const val SETTINGS_LABS_ENABLE_SWIPE_TO_REPLY = "SETTINGS_LABS_ENABLE_SWIPE_TO_REPLY" diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsLabsFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsLabsFragment.kt index f43add508c..7e414fc4ec 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsLabsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsLabsFragment.kt @@ -78,6 +78,10 @@ class VectorSettingsLabsFragment : findPreference(VectorPreferences.SETTINGS_LABS_NEW_APP_LAYOUT_KEY)?.let { pref -> pref.isVisible = vectorFeatures.isNewAppLayoutEnabled() } + + findPreference(VectorPreferences.SETTINGS_LABS_UNREAD_NOTIFICATIONS_AS_TAB)?.let { pref -> + pref.isVisible = !vectorFeatures.isNewAppLayoutEnabled() + } } /** diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsPreferencesFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsPreferencesFragment.kt index 0bd5316b8f..135c25cd8d 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsPreferencesFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsPreferencesFragment.kt @@ -31,6 +31,7 @@ import im.vector.app.core.preference.VectorPreference import im.vector.app.core.preference.VectorSwitchPreference import im.vector.app.features.MainActivity import im.vector.app.features.MainActivityArgs +import im.vector.app.features.VectorFeatures import im.vector.app.features.analytics.plan.MobileScreen import im.vector.app.features.settings.font.FontScaleSettingActivity import im.vector.app.features.themes.ThemeUtils @@ -44,6 +45,7 @@ class VectorSettingsPreferencesFragment : @Inject lateinit var vectorPreferences: VectorPreferences @Inject lateinit var fontScalePreferences: FontScalePreferences + @Inject lateinit var vectorFeatures: VectorFeatures override var titleRes = R.string.settings_preferences override val preferenceXmlRes = R.xml.vector_settings_preferences @@ -99,6 +101,10 @@ class VectorSettingsPreferencesFragment : } } + findPreference(VectorPreferences.SETTINGS_PREF_SPACE_CATEGORY)!!.let { pref -> + pref.isVisible = !vectorFeatures.isNewAppLayoutEnabled() + } + // Url preview /* TODO Note: we keep the setting client side for now diff --git a/vector/src/main/res/drawable-hdpi/ill_app_layout_onboarding_rooms.webp b/vector/src/main/res/drawable-hdpi/ill_app_layout_onboarding_rooms.webp new file mode 100644 index 0000000000..5ac890e617 Binary files /dev/null and b/vector/src/main/res/drawable-hdpi/ill_app_layout_onboarding_rooms.webp differ diff --git a/vector/src/main/res/drawable-hdpi/ill_app_layout_onboarding_spaces.webp b/vector/src/main/res/drawable-hdpi/ill_app_layout_onboarding_spaces.webp new file mode 100644 index 0000000000..35f2a04236 Binary files /dev/null and b/vector/src/main/res/drawable-hdpi/ill_app_layout_onboarding_spaces.webp differ diff --git a/vector/src/main/res/drawable-mdpi/ill_app_layout_onboarding_rooms.webp b/vector/src/main/res/drawable-mdpi/ill_app_layout_onboarding_rooms.webp new file mode 100644 index 0000000000..07ece0d947 Binary files /dev/null and b/vector/src/main/res/drawable-mdpi/ill_app_layout_onboarding_rooms.webp differ diff --git a/vector/src/main/res/drawable-mdpi/ill_app_layout_onboarding_spaces.webp b/vector/src/main/res/drawable-mdpi/ill_app_layout_onboarding_spaces.webp new file mode 100644 index 0000000000..a77bce20e8 Binary files /dev/null and b/vector/src/main/res/drawable-mdpi/ill_app_layout_onboarding_spaces.webp differ diff --git a/vector/src/main/res/drawable-xhdpi/ill_app_layout_onboarding_rooms.webp b/vector/src/main/res/drawable-xhdpi/ill_app_layout_onboarding_rooms.webp new file mode 100644 index 0000000000..e86bd5fe6b Binary files /dev/null and b/vector/src/main/res/drawable-xhdpi/ill_app_layout_onboarding_rooms.webp differ diff --git a/vector/src/main/res/drawable-xhdpi/ill_app_layout_onboarding_spaces.webp b/vector/src/main/res/drawable-xhdpi/ill_app_layout_onboarding_spaces.webp new file mode 100644 index 0000000000..9b5b9fa607 Binary files /dev/null and b/vector/src/main/res/drawable-xhdpi/ill_app_layout_onboarding_spaces.webp differ diff --git a/vector/src/main/res/drawable-xxhdpi/ill_app_layout_onboarding_rooms.webp b/vector/src/main/res/drawable-xxhdpi/ill_app_layout_onboarding_rooms.webp new file mode 100644 index 0000000000..f95909eaae Binary files /dev/null and b/vector/src/main/res/drawable-xxhdpi/ill_app_layout_onboarding_rooms.webp differ diff --git a/vector/src/main/res/drawable-xxhdpi/ill_app_layout_onboarding_spaces.webp b/vector/src/main/res/drawable-xxhdpi/ill_app_layout_onboarding_spaces.webp new file mode 100644 index 0000000000..03e31dc9d6 Binary files /dev/null and b/vector/src/main/res/drawable-xxhdpi/ill_app_layout_onboarding_spaces.webp differ diff --git a/vector/src/main/res/drawable-xxxhdpi/ill_app_layout_onboarding_rooms.webp b/vector/src/main/res/drawable-xxxhdpi/ill_app_layout_onboarding_rooms.webp new file mode 100644 index 0000000000..cee3cf512c Binary files /dev/null and b/vector/src/main/res/drawable-xxxhdpi/ill_app_layout_onboarding_rooms.webp differ diff --git a/vector/src/main/res/drawable-xxxhdpi/ill_app_layout_onboarding_spaces.webp b/vector/src/main/res/drawable-xxxhdpi/ill_app_layout_onboarding_spaces.webp new file mode 100644 index 0000000000..b4bf421be5 Binary files /dev/null and b/vector/src/main/res/drawable-xxxhdpi/ill_app_layout_onboarding_spaces.webp differ diff --git a/vector/src/main/res/drawable/ic_invites_empty.xml b/vector/src/main/res/drawable/ic_invites_empty.xml new file mode 100644 index 0000000000..79908ff380 --- /dev/null +++ b/vector/src/main/res/drawable/ic_invites_empty.xml @@ -0,0 +1,14 @@ + + + + diff --git a/vector/src/main/res/layout/activity_home.xml b/vector/src/main/res/layout/activity_home.xml index 9899c15aa6..698aab2340 100644 --- a/vector/src/main/res/layout/activity_home.xml +++ b/vector/src/main/res/layout/activity_home.xml @@ -28,4 +28,4 @@ android:layout_height="match_parent" android:layout_gravity="start" /> - \ No newline at end of file + diff --git a/vector/src/main/res/layout/bottom_sheet_release_notes.xml b/vector/src/main/res/layout/bottom_sheet_release_notes.xml new file mode 100644 index 0000000000..1d14c2c4a2 --- /dev/null +++ b/vector/src/main/res/layout/bottom_sheet_release_notes.xml @@ -0,0 +1,55 @@ + + + + + + + +