Data loading improvements

Signed-off-by: Mario Danic <mario@lovelyhq.com>
This commit is contained in:
Mario Danic 2019-10-16 16:24:07 +02:00
parent 8a3008ef25
commit f99dac5f45
12 changed files with 172 additions and 80 deletions

View File

@ -143,6 +143,7 @@ ext {
work_version = "1.0.1"
koin_version = "2.1.0-alpha-1"
lifecycle_version = "2.1.0"
coil_version = "0.7.0"
}
@ -269,6 +270,10 @@ dependencies {
implementation 'com.github.mario.fresco:animated-gif:111'
implementation 'com.github.mario.fresco:imagepipeline-okhttp3:111'
implementation "io.coil-kt:coil:${coil_version}"
implementation "io.coil-kt:coil-gif:${coil_version}"
implementation "io.coil-kt:coil-svg:${coil_version}"
implementation 'com.github.natario1:Autocomplete:v1.1.0'
implementation 'com.github.cotechde.hwsecurity:hwsecurity-fido:2.4.5'

View File

@ -151,7 +151,6 @@ class NextcloudTalkApplication : Application(), LifecycleObserver {
Security.insertProviderAt(Conscrypt.newProvider(), 1)
ClosedInterfaceImpl().providerInstallerInstallIfNeededAsync()
DeviceUtils.ignoreSpecialBatteryFeatures()
val pushRegistrationWork = OneTimeWorkRequest.Builder(PushRegistrationWorker::class.java).build()
val accountRemovalWork = OneTimeWorkRequest.Builder(AccountRemovalWorker::class.java).build()

View File

@ -48,6 +48,7 @@ import com.nextcloud.talk.jobs.CapabilitiesWorker;
import com.nextcloud.talk.jobs.PushRegistrationWorker;
import com.nextcloud.talk.jobs.SignalingSettingsWorker;
import com.nextcloud.talk.models.database.UserEntity;
import com.nextcloud.talk.newarch.features.conversationsList.ConversationsListView;
import com.nextcloud.talk.utils.ApiUtils;
import com.nextcloud.talk.utils.ClosedInterfaceImpl;
import com.nextcloud.talk.utils.bundle.BundleKeys;
@ -407,7 +408,7 @@ public class AccountVerificationController extends BaseController {
getActivity().runOnUiThread(() -> {
if (userUtils.getUsers().size() == 1) {
getRouter().setRoot(RouterTransaction.with(new
ConversationsListController())
ConversationsListView())
.pushChangeHandler(new HorizontalChangeHandler())
.popChangeHandler(new HorizontalChangeHandler()));
} else {
@ -474,7 +475,7 @@ public class AccountVerificationController extends BaseController {
} else {
if (userUtils.anyUserExists()) {
getRouter().setRoot(RouterTransaction.with(new ConversationsListController())
getRouter().setRoot(RouterTransaction.with(new ConversationsListView())
.pushChangeHandler(new HorizontalChangeHandler())
.popChangeHandler(new HorizontalChangeHandler()));
} else {

View File

@ -142,9 +142,6 @@ public class ConversationsListController extends BaseController implements Searc
@BindView(R.id.progressBar)
ProgressBar progressBarView;
@BindView(R.id.emptyLayout)
RelativeLayout emptyLayoutView;
@BindView(R.id.fast_scroller)
FastScroller fastScroller;
@ -319,7 +316,7 @@ public class ConversationsListController extends BaseController implements Searc
progressBarView.setVisibility(View.GONE);
}
if (roomsOverall.getOcs().getData().size() > 0) {
/*if (roomsOverall.getOcs().getData().size() > 0) {
if (emptyLayoutView.getVisibility() != View.GONE) {
emptyLayoutView.setVisibility(View.GONE);
}
@ -335,7 +332,7 @@ public class ConversationsListController extends BaseController implements Searc
if (swipeRefreshLayout.getVisibility() != View.GONE) {
swipeRefreshLayout.setVisibility(View.GONE);
}
}
}*/
Conversation conversation;
for (int i = 0; i < roomsOverall.getOcs().getData().size(); i++) {
@ -386,14 +383,14 @@ public class ConversationsListController extends BaseController implements Searc
HttpException exception = (HttpException) throwable;
switch (exception.code()) {
case 401:
if (getParentController() != null &&
/*if (getParentController() != null &&
getParentController().getRouter() != null) {
getParentController().getRouter().pushController((RouterTransaction.with
(new WebViewLoginController(currentUser.getBaseUrl(),
true))
.pushChangeHandler(new VerticalChangeHandler())
.popChangeHandler(new VerticalChangeHandler())));
}
}*/
break;
default:
break;
@ -432,7 +429,7 @@ public class ConversationsListController extends BaseController implements Searc
swipeRefreshLayout.setOnRefreshListener(() -> fetchData(false));
swipeRefreshLayout.setColorSchemeResources(R.color.colorPrimary);
emptyLayoutView.setOnClickListener(v -> showNewConversationsScreen());
//emptyLayoutView.setOnClickListener(v -> showNewConversationsScreen());
floatingActionButton.setOnClickListener(v -> {
showNewConversationsScreen();
});

View File

@ -251,3 +251,9 @@ fun createNextcloudTalkRepository(apiService: ApiService): NextcloudTalkReposito
return NextcloudTalkRepositoryImpl(apiService)
}
fun createGetConversationsUseCase(
nextcloudTalkRepository: NextcloudTalkRepository,
apiErrorHandler: ApiErrorHandler
): GetConversationsUseCase {
return GetConversationsUseCase(nextcloudTalkRepository, apiErrorHandler)
}

View File

@ -22,6 +22,7 @@ package com.nextcloud.talk.newarch.features.conversationsList
import android.app.SearchManager
import android.content.Context
import android.content.res.Resources
import android.graphics.Bitmap
import android.os.Build
import android.os.Bundle
@ -60,6 +61,7 @@ import com.nextcloud.talk.models.json.conversations.Conversation
import com.nextcloud.talk.newarch.conversationsList.mvp.BaseView
import com.nextcloud.talk.newarch.mvvm.ViewState.FAILED
import com.nextcloud.talk.newarch.mvvm.ViewState.LOADED
import com.nextcloud.talk.newarch.mvvm.ViewState.LOADED_EMPTY
import com.nextcloud.talk.newarch.mvvm.ViewState.LOADING
import com.nextcloud.talk.newarch.mvvm.ext.initRecyclerView
import com.nextcloud.talk.utils.ApiUtils
@ -72,12 +74,14 @@ import eu.davidea.flexibleadapter.FlexibleAdapter.OnItemClickListener
import eu.davidea.flexibleadapter.FlexibleAdapter.OnItemLongClickListener
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
import eu.davidea.flexibleadapter.items.IFlexible
import kotlinx.android.synthetic.main.controller_conversations_rv.view.emptyLayout
import kotlinx.android.synthetic.main.controller_conversations_rv.view.dataStateView
import kotlinx.android.synthetic.main.controller_conversations_rv.view.floatingActionButton
import kotlinx.android.synthetic.main.controller_conversations_rv.view.progressBar
import kotlinx.android.synthetic.main.controller_conversations_rv.view.recyclerView
import kotlinx.android.synthetic.main.controller_conversations_rv.view.swipeRefreshLayoutView
import kotlinx.android.synthetic.main.fast_scroller.view.fast_scroller
import kotlinx.android.synthetic.main.view_states.view.errorStateImageView
import kotlinx.android.synthetic.main.view_states.view.errorStateTextView
import kotlinx.android.synthetic.main.view_states.view.loadingStateView
import kotlinx.android.synthetic.main.view_states.view.stateWithMessageView
import org.koin.android.ext.android.inject
import org.parceler.Parcels
import java.util.ArrayList
@ -178,18 +182,36 @@ class ConversationsListView() : BaseView(), OnQueryTextListener,
viewState.observe(this@ConversationsListView, Observer { value ->
when (value) {
LOADING -> {
view?.recyclerView?.visibility = View.GONE
view?.emptyLayout?.visibility = View.GONE
view?.swipeRefreshLayoutView?.visibility = View.GONE
view?.progressBar?.visibility = View.VISIBLE
view?.loadingStateView?.visibility = View.VISIBLE
view?.stateWithMessageView?.visibility = View.GONE
view?.dataStateView?.visibility = View.GONE
view?.floatingActionButton?.visibility = View.GONE
searchItem?.setVisible(false)
searchItem?.isVisible = false
}
LOADED, FAILED -> {
view?.recyclerView?.visibility = View.VISIBLE
// The rest is handled in an actual network call
view?.progressBar?.visibility = View.GONE
view?.floatingActionButton?.visibility = View.VISIBLE
LOADED -> {
view?.loadingStateView?.visibility = View.GONE
view?.stateWithMessageView?.visibility = View.GONE
view!!.dataStateView.visibility = View.VISIBLE
view?.floatingActionButton?.visibility = View.GONE
searchItem?.isVisible = true
}
LOADED_EMPTY, FAILED -> {
view?.loadingStateView?.visibility = View.GONE
view?.dataStateView?.visibility = View.VISIBLE
view?.floatingActionButton?.visibility = View.GONE
searchItem?.isVisible = false
if (value.equals(FAILED)) {
view?.stateWithMessageView?.errorStateTextView?.text = messageData
view?.stateWithMessageView?.errorStateImageView?.setImageResource(
R.drawable.ic_announcement_white_24dp
)
} else {
view?.stateWithMessageView?.errorStateTextView?.text = resources?.getText(R.string.nc_conversations_empty)
view?.stateWithMessageView?.errorStateImageView?.setImageResource(R.drawable.ic_logo)
}
view?.stateWithMessageView?.visibility = View.VISIBLE
}
else -> {
// We should not be here
@ -208,16 +230,6 @@ class ConversationsListView() : BaseView(), OnQueryTextListener,
}
recyclerViewAdapter.updateDataSet(newConversations as List<IFlexible<ViewHolder>>?)
if (it.isNotEmpty()) {
view?.emptyLayout?.visibility = View.GONE
view?.swipeRefreshLayoutView?.visibility = View.VISIBLE
searchItem?.setVisible(true)
} else {
view?.emptyLayout?.visibility = View.VISIBLE
view?.swipeRefreshLayoutView?.visibility = View.GONE
searchItem?.setVisible(false)
}
})
})
@ -243,7 +255,8 @@ class ConversationsListView() : BaseView(), OnQueryTextListener,
dataSource.subscribe(object : BaseBitmapDataSubscriber() {
override fun onNewResultImpl(bitmap: Bitmap?) {
if (bitmap != null && resources != null) {
val roundedBitmapDrawable = RoundedBitmapDrawableFactory.create(resources!!, bitmap)
val roundedBitmapDrawable = RoundedBitmapDrawableFactory.create(resources as Resources,
bitmap)
roundedBitmapDrawable.isCircular = true
roundedBitmapDrawable.setAntiAlias(true)
menuItem.icon = roundedBitmapDrawable
@ -261,8 +274,19 @@ class ConversationsListView() : BaseView(), OnQueryTextListener,
return R.layout.controller_conversations_rv
}
@OnClick(R.id.floatingActionButton, R.id.emptyLayout)
@OnClick(R.id.floatingActionButton)
fun onFloatingActionButtonClick() {
openNewConversationScreen()
}
@OnClick(R.id.stateWithMessageView)
fun onStateWithMessageViewClick() {
if (viewModel.viewState.equals(LOADED_EMPTY)) {
openNewConversationScreen()
}
}
private fun openNewConversationScreen() {
val bundle = Bundle()
bundle.putBoolean(BundleKeys.KEY_NEW_CONVERSATION, true)
router.pushController(
@ -273,7 +297,7 @@ class ConversationsListView() : BaseView(), OnQueryTextListener,
}
override fun getTitle(): String? {
return resources!!.getString(R.string.nc_app_name)
return resources?.getString(R.string.nc_app_name)
}
override fun onAttach(view: View) {

View File

@ -30,6 +30,7 @@ import com.nextcloud.talk.newarch.domain.usecases.base.UseCaseResponse
import com.nextcloud.talk.newarch.mvvm.ViewState
import com.nextcloud.talk.newarch.mvvm.ViewState.FAILED
import com.nextcloud.talk.newarch.mvvm.ViewState.LOADED
import com.nextcloud.talk.newarch.mvvm.ViewState.LOADED_EMPTY
import com.nextcloud.talk.newarch.mvvm.ViewState.LOADING
import com.nextcloud.talk.utils.database.user.UserUtils
import org.apache.commons.lang3.builder.CompareToBuilder
@ -41,7 +42,7 @@ class ConversationsListViewModel constructor(
val conversationsListData = MutableLiveData<List<Conversation>>()
val viewState = MutableLiveData<ViewState>(LOADING)
val messageData = MutableLiveData<String>()
var messageData : String? = null
val searchQuery = MutableLiveData<String>()
lateinit var currentUser: UserEntity
@ -66,11 +67,12 @@ class ConversationsListViewModel constructor(
})
conversationsListData.value = newConversations
viewState.value = LOADED
viewState.value = if (newConversations.isNotEmpty()) LOADED else LOADED_EMPTY
}
override fun onError(errorModel: ErrorModel?) {
messageData.value = errorModel?.message
messageData = errorModel?.message
viewState.value = FAILED
}

View File

@ -22,6 +22,7 @@ package com.nextcloud.talk.newarch.mvvm
enum class ViewState {
LOADING,
LOADED_EMPTY,
LOADED,
FAILED
}

View File

@ -47,7 +47,7 @@ class NetworkUtils {
.header("OCS-APIRequest", "true")
.method(original.method, original.body)
.build()
val response = chain.proceed(request)
if (request.url.encodedPath.contains("/avatar/")) {

View File

@ -0,0 +1,25 @@
<!--
~ Nextcloud Talk application
~
~ @author Mario Danic
~ Copyright (C) 2017-2019 Mario Danic <mario@lovelyhq.com>
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M20,2L4,2c-1.1,0 -1.99,0.9 -1.99,2L2,22l4,-4h14c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2zM13,11h-2L11,5h2v6zM13,15h-2v-2h2v2z"/>
</vector>

View File

@ -25,54 +25,20 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="@dimen/item_height"
android:layout_height="@dimen/item_height"
android:layout_gravity="center"
android:layout_marginStart="@dimen/activity_horizontal_margin"
android:layout_marginLeft="@dimen/activity_horizontal_margin"
android:layout_marginEnd="@dimen/activity_horizontal_margin"
android:layout_marginRight="@dimen/activity_horizontal_margin"
android:indeterminate="true"
android:indeterminateTint="@color/colorPrimary"
android:indeterminateTintMode="src_in" />
<RelativeLayout
android:id="@+id/emptyLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone">
<ImageView
android:id="@+id/noConversationsImageView"
android:layout_width="72dp"
android:layout_height="72dp"
android:layout_centerInParent="true"
android:src="@drawable/ic_logo"
android:tint="@color/colorPrimary" />
<TextView
android:id="@+id/sendHiTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/noConversationsImageView"
android:layout_margin="8dp"
android:text="@string/nc_conversations_empty"
android:textAlignment="center"
android:textSize="20sp" />
</RelativeLayout>
<include layout="@layout/view_states"/>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipeRefreshLayoutView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"
android:visibility="visible"
app:layout_behavior="com.nextcloud.talk.utils.FABAwareScrollingViewBehavior">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
android:id="@+id/dataStateView"
android:visibility="invisible">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"

View File

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Nextcloud Talk application
~
~ @author Mario Danic
~ Copyright (C) 2017-2019 Mario Danic <mario@lovelyhq.com>
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<ProgressBar
android:id="@+id/loadingStateView"
android:layout_width="@dimen/item_height"
android:layout_height="@dimen/item_height"
android:layout_gravity="center"
android:layout_margin="@dimen/activity_horizontal_margin"
android:indeterminate="true"
android:layout_centerInParent="true"
android:indeterminateTint="@color/colorPrimary"
android:indeterminateTintMode="src_in"
android:visibility="gone"
/>
<RelativeLayout
android:id="@+id/stateWithMessageView"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<ImageView
android:id="@+id/errorStateImageView"
android:layout_width="@dimen/item_height"
android:layout_height="@dimen/item_height"
android:layout_above="@id/errorStateTextView"
android:layout_centerHorizontal="true"
android:src="@drawable/ic_announcement_white_24dp"
android:tint="@color/colorPrimary"
/>
<TextView
android:id="@+id/errorStateTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_margin="8dp"
android:textAlignment="center"
android:textSize="20sp"
/>
</RelativeLayout>
</RelativeLayout>