From cd1ceea8606427311a1bc50abff4dd9d463ff155 Mon Sep 17 00:00:00 2001 From: Kacper Ziubryniewicz Date: Thu, 21 Mar 2019 22:55:47 +0100 Subject: [PATCH 01/78] Add messages forwarding (#288) --- .../data/db/migrations/Migration11.kt | 2 +- .../message/preview/MessagePreviewFragment.kt | 16 ++++++++--- .../preview/MessagePreviewPresenter.kt | 19 ++++++++----- .../message/preview/MessagePreviewView.kt | 4 ++- .../message/send/SendMessageActivity.kt | 9 ++++--- .../message/send/SendMessagePresenter.kt | 27 ++++++++++++------- .../res/drawable/ic_message_forward_24dp.xml | 10 +++++++ .../res/menu/action_menu_message_preview.xml | 6 +++++ app/src/main/res/values-pl/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + 10 files changed, 71 insertions(+), 24 deletions(-) create mode 100644 app/src/main/res/drawable/ic_message_forward_24dp.xml diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration11.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration11.kt index cb437c0ee..6d129bca0 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration11.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration11.kt @@ -8,7 +8,7 @@ class Migration11 : Migration(10, 11) { override fun migrate(database: SupportSQLiteDatabase) { database.execSQL(""" CREATE TABLE IF NOT EXISTS Grades_temp ( - id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + id INTEGER PRIMARY KEY NOT NULL, is_read INTEGER NOT NULL, is_notified INTEGER NOT NULL, semester_id INTEGER NOT NULL, diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt index 102567eda..9afb744f0 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt @@ -25,6 +25,7 @@ class MessagePreviewFragment : BaseSessionFragment(), MessagePreviewView, MainVi lateinit var presenter: MessagePreviewPresenter private var menuReplyButton: MenuItem? = null + private var menuForwardButton: MenuItem? = null override val titleStringId: Int get() = R.string.message_title @@ -60,12 +61,16 @@ class MessagePreviewFragment : BaseSessionFragment(), MessagePreviewView, MainVi override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) { inflater?.inflate(R.menu.action_menu_message_preview, menu) menuReplyButton = menu?.findItem(R.id.messagePreviewMenuReply) + menuForwardButton = menu?.findItem(R.id.messagePreviewMenuForward) presenter.onCreateOptionsMenu() } override fun onOptionsItemSelected(item: MenuItem?): Boolean { - return if (item?.itemId == R.id.messagePreviewMenuReply) presenter.onReply() - else false + return when (item?.itemId) { + R.id.messagePreviewMenuReply -> presenter.onReply() + R.id.messagePreviewMenuForward -> presenter.onForward() + else -> false + } } override fun setSubject(subject: String) { @@ -92,8 +97,9 @@ class MessagePreviewFragment : BaseSessionFragment(), MessagePreviewView, MainVi messagePreviewProgress.visibility = if (show) VISIBLE else GONE } - override fun showReplyButton(show: Boolean) { + override fun showOptions(show: Boolean) { menuReplyButton?.isVisible = show + menuForwardButton?.isVisible = show } override fun showMessageError() { @@ -101,6 +107,10 @@ class MessagePreviewFragment : BaseSessionFragment(), MessagePreviewView, MainVi } override fun openMessageReply(message: Message?) { + context?.let { it.startActivity(SendMessageActivity.getStartIntent(it, message, true)) } + } + + override fun openMessageForward(message: Message?) { context?.let { it.startActivity(SendMessageActivity.getStartIntent(it, message)) } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt index a5ed9f28c..534c7ad3d 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt @@ -22,7 +22,7 @@ class MessagePreviewPresenter @Inject constructor( var messageId: Int = 0 - private var replyMessage: Message? = null + private var message: Message? = null fun onAttachView(view: MessagePreviewView, id: Int) { super.onAttachView(view) @@ -41,13 +41,13 @@ class MessagePreviewPresenter @Inject constructor( .doFinally { view?.showProgress(false) } .subscribe({ message -> Timber.i("Loading message $id preview result: Success ") - replyMessage = message + this@MessagePreviewPresenter.message = message view?.run { message.let { setSubject(if (it.subject.isNotBlank()) it.subject else noSubjectString) setDate(it.date.toFormattedString("yyyy-MM-dd HH:mm:ss")) setContent(it.content.orEmpty()) - showReplyButton(true) + showOptions(true) if (it.recipient.isNotBlank()) setRecipient(it.recipient) else setSender(it.sender) @@ -63,13 +63,20 @@ class MessagePreviewPresenter @Inject constructor( } fun onReply(): Boolean { - return if (replyMessage != null) { - view?.openMessageReply(replyMessage) + return if (message != null) { + view?.openMessageReply(message) + true + } else false + } + + fun onForward(): Boolean { + return if (message != null) { + view?.openMessageForward(message) true } else false } fun onCreateOptionsMenu() { - view?.showReplyButton(replyMessage != null) + view?.showOptions(message != null) } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt index 464ea168c..3ff73396e 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt @@ -19,9 +19,11 @@ interface MessagePreviewView : BaseSessionView { fun showProgress(show: Boolean) - fun showReplyButton(show: Boolean) + fun showOptions(show: Boolean) fun showMessageError() fun openMessageReply(message: Message?) + + fun openMessageForward(message: Message?) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt index edf14dd64..6ac7e2264 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt @@ -26,11 +26,14 @@ class SendMessageActivity : BaseActivity(), SendMessageView { companion object { private const val EXTRA_MESSAGE = "EXTRA_MESSAGE" + private const val EXTRA_REPLY = "EXTRA_REPLY" fun getStartIntent(context: Context) = Intent(context, SendMessageActivity::class.java) - fun getStartIntent(context: Context, message: Message?): Intent { - return getStartIntent(context).putExtra(EXTRA_MESSAGE, message) + fun getStartIntent(context: Context, message: Message?, reply: Boolean = false): Intent { + return getStartIntent(context) + .putExtra(EXTRA_MESSAGE, message) + .putExtra(EXTRA_REPLY, reply) } } @@ -59,7 +62,7 @@ class SendMessageActivity : BaseActivity(), SendMessageView { supportActionBar?.setDisplayHomeAsUpEnabled(true) messageContainer = sendMessageContainer - presenter.onAttachView(this, intent.getSerializableExtra(EXTRA_MESSAGE) as? Message) + presenter.onAttachView(this, intent.getSerializableExtra(EXTRA_MESSAGE) as? Message, intent.getSerializableExtra(EXTRA_REPLY) as Boolean) } override fun onCreateOptionsMenu(menu: Menu?): Boolean { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt index a3715aaa6..f57848d61 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt @@ -30,18 +30,25 @@ class SendMessagePresenter @Inject constructor( private val analytics: FirebaseAnalyticsHelper ) : BasePresenter(errorHandler) { - fun onAttachView(view: SendMessageView, message: Message?) { + fun onAttachView(view: SendMessageView, message: Message?, reply: Boolean) { super.onAttachView(view) Timber.i("Send message view is attached") - loadData(message) + loadData(message, reply) view.apply { message?.let { - setSubject("RE: ${message.subject}") - if (preferencesRepository.fillMessageContent) { - setContent(when (message.sender.isNotEmpty()) { - true -> "\n\nOd: ${message.sender}\n" - false -> "\n\nDo: ${message.recipient}\n" - } + "Data: ${message.date.toFormattedString("yyyy-MM-dd HH:mm:ss")}\n\n${message.content}") + setSubject(when (reply) { + true -> "RE: " + false -> "FE: " + } + message.subject) + if (preferencesRepository.fillMessageContent || !reply) { + setContent( + when (reply) { + true -> "\n\n" + false -> "" + } + when (message.sender.isNotEmpty()) { + true -> "Od: ${message.sender}\n" + false -> "Do: ${message.recipient}\n" + } + "Data: ${message.date.toFormattedString("yyyy-MM-dd HH:mm:ss")}\n\n${message.content}") } } } @@ -52,7 +59,7 @@ class SendMessagePresenter @Inject constructor( return true } - private fun loadData(message: Message?) { + private fun loadData(message: Message?, reply: Boolean) { var reportingUnit: ReportingUnit? = null var recipients: List = emptyList() var selectedRecipient: List = emptyList() @@ -69,7 +76,7 @@ class SendMessagePresenter @Inject constructor( recipients = it } .flatMapCompletable { - if (message == null) Completable.complete() + if (message == null || !reply) Completable.complete() else recipientRepository.getMessageRecipients(student, message) .doOnSuccess { Timber.i("Loaded message recipients to reply result: Success, fetched %d recipients", it.size) diff --git a/app/src/main/res/drawable/ic_message_forward_24dp.xml b/app/src/main/res/drawable/ic_message_forward_24dp.xml new file mode 100644 index 000000000..2b7c32c75 --- /dev/null +++ b/app/src/main/res/drawable/ic_message_forward_24dp.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/menu/action_menu_message_preview.xml b/app/src/main/res/menu/action_menu_message_preview.xml index 4c2f59201..6ba2c828e 100644 --- a/app/src/main/res/menu/action_menu_message_preview.xml +++ b/app/src/main/res/menu/action_menu_message_preview.xml @@ -7,4 +7,10 @@ android:orderInCategory="1" android:title="@string/message_reply" app:showAsAction="ifRoom" /> + diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index dcc735267..16ec15ff9 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -140,6 +140,7 @@ Do: Data: %s Odpowiedz + Prześlij dalej Temat Treść Wiadomość wysłana pomyślnie diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1debed7b3..98bef391f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -134,6 +134,7 @@ To: Date: %s Reply + Forward Subject Content Message sent successfully From 2bff468e56aa253ac308e2e50b6d10bdc52b5e26 Mon Sep 17 00:00:00 2001 From: Kacper Ziubryniewicz Date: Sun, 31 Mar 2019 22:01:04 +0200 Subject: [PATCH 02/78] Add deleting messages (#290) --- .../repositories/message/MessageRemote.kt | 4 ++ .../repositories/message/MessageRepository.kt | 17 ++++++ .../ui/modules/message/MessageFragment.kt | 11 +++- .../ui/modules/message/MessagePresenter.kt | 10 ++++ .../ui/modules/message/MessageView.kt | 7 +-- .../message/preview/MessagePreviewFragment.kt | 29 ++++++++++ .../preview/MessagePreviewPresenter.kt | 53 ++++++++++++++++++- .../message/preview/MessagePreviewView.kt | 12 +++++ .../message/send/SendMessageActivity.kt | 2 +- .../message/send/SendMessagePresenter.kt | 12 ++--- .../modules/message/tab/MessageTabFragment.kt | 9 ++-- .../message/tab/MessageTabPresenter.kt | 36 ++++++++----- .../res/drawable/ic_message_delete_24dp.xml | 10 ++++ .../res/layout/fragment_message_preview.xml | 1 + .../res/menu/action_menu_message_preview.xml | 6 +++ app/src/main/res/values-pl/strings.xml | 4 ++ app/src/main/res/values/strings.xml | 4 ++ 17 files changed, 195 insertions(+), 32 deletions(-) create mode 100644 app/src/main/res/drawable/ic_message_delete_24dp.xml diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRemote.kt index 3fb01d30e..e9cff4ab0 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRemote.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRemote.kt @@ -58,4 +58,8 @@ class MessageRemote @Inject constructor(private val api: Api) { } ) } + + fun deleteMessage(message: Message): Single { + return api.deleteMessages(listOf(Pair(message.realId, message.folderId))) + } } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRepository.kt index d319689e4..09f19815e 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRepository.kt @@ -9,6 +9,7 @@ import io.github.wulkanowy.data.db.entities.Recipient import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.repositories.message.MessageFolder.RECEIVED import io.reactivex.Completable +import io.reactivex.Maybe import io.reactivex.Single import java.net.UnknownHostException import javax.inject.Inject @@ -89,4 +90,20 @@ class MessageRepository @Inject constructor( else Single.error(UnknownHostException()) } } + + fun deleteMessage(message: Message): Maybe { + return ReactiveNetwork.checkInternetConnectivity(settings) + .flatMap { + if (it) remote.deleteMessage(message) + else Single.error(UnknownHostException()) + } + .filter { it } + .doOnSuccess { + if (!message.removed) local.updateMessages(listOf(message.copy(removed = true).apply { + id = message.id + content = message.content + })) + else local.deleteMessages(listOf(message)) + } + } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageFragment.kt index d8a6bc84c..7bd35f2d2 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageFragment.kt @@ -7,6 +7,7 @@ import android.view.View.INVISIBLE import android.view.View.VISIBLE import android.view.ViewGroup import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.data.repositories.message.MessageFolder.RECEIVED import io.github.wulkanowy.data.repositories.message.MessageFolder.SENT import io.github.wulkanowy.data.repositories.message.MessageFolder.TRASHED @@ -75,12 +76,20 @@ class MessageFragment : BaseFragment(), MessageView, MainView.TitledView { messageProgress.visibility = if (show) VISIBLE else INVISIBLE } + fun onDeleteMessage(message: Message) { + presenter.onDeleteMessage(message) + } + fun onChildFragmentLoaded() { presenter.onChildViewLoaded() } + override fun notifyChildMessageDeleted(tabId: Int) { + (pagerAdapter.getFragmentInstance(tabId) as? MessageTabFragment)?.onParentDeleteMessage() + } + override fun notifyChildLoadData(index: Int, forceRefresh: Boolean) { - (pagerAdapter.getFragmentInstance(index) as? MessageView.MessageChildView)?.onParentLoadData(forceRefresh) + (pagerAdapter.getFragmentInstance(index) as? MessageTabFragment)?.onParentLoadData(forceRefresh) } override fun openSendMessage() { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessagePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessagePresenter.kt index 332d5b74f..8e7af5124 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessagePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessagePresenter.kt @@ -1,5 +1,6 @@ package io.github.wulkanowy.ui.modules.message +import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.utils.SchedulersProvider @@ -43,6 +44,15 @@ class MessagePresenter @Inject constructor( } } + fun onDeleteMessage(message: Message) { + view?.notifyChildMessageDeleted( + when (message.removed) { + true -> 2 + else -> message.folderId - 1 + } + ) + } + fun onSendMessageButtonClicked() { view?.openSendMessage() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageView.kt index 41257ecc7..2aa4d78ec 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageView.kt @@ -14,10 +14,7 @@ interface MessageView : BaseView { fun notifyChildLoadData(index: Int, forceRefresh: Boolean) + fun notifyChildMessageDeleted(tabId: Int) + fun openSendMessage() - - interface MessageChildView { - - fun onParentLoadData(forceRefresh: Boolean) - } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt index 9afb744f0..4a752401e 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt @@ -13,7 +13,9 @@ import android.view.ViewGroup import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.ui.base.session.BaseSessionFragment +import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.ui.modules.message.MessageFragment import io.github.wulkanowy.ui.modules.message.send.SendMessageActivity import kotlinx.android.synthetic.main.fragment_message_preview.* import javax.inject.Inject @@ -26,6 +28,7 @@ class MessagePreviewFragment : BaseSessionFragment(), MessagePreviewView, MainVi private var menuReplyButton: MenuItem? = null private var menuForwardButton: MenuItem? = null + private var menuDeleteButton: MenuItem? = null override val titleStringId: Int get() = R.string.message_title @@ -33,6 +36,9 @@ class MessagePreviewFragment : BaseSessionFragment(), MessagePreviewView, MainVi override val noSubjectString: String get() = getString(R.string.message_no_subject) + override val deleteMessageSuccessString: String + get() = getString(R.string.message_delete_success) + companion object { const val MESSAGE_ID_KEY = "message_id" @@ -62,6 +68,7 @@ class MessagePreviewFragment : BaseSessionFragment(), MessagePreviewView, MainVi inflater?.inflate(R.menu.action_menu_message_preview, menu) menuReplyButton = menu?.findItem(R.id.messagePreviewMenuReply) menuForwardButton = menu?.findItem(R.id.messagePreviewMenuForward) + menuDeleteButton = menu?.findItem(R.id.messagePreviewMenuDelete) presenter.onCreateOptionsMenu() } @@ -69,6 +76,7 @@ class MessagePreviewFragment : BaseSessionFragment(), MessagePreviewView, MainVi return when (item?.itemId) { R.id.messagePreviewMenuReply -> presenter.onReply() R.id.messagePreviewMenuForward -> presenter.onForward() + R.id.messagePreviewMenuDelete -> presenter.onMessageDelete() else -> false } } @@ -97,9 +105,22 @@ class MessagePreviewFragment : BaseSessionFragment(), MessagePreviewView, MainVi messagePreviewProgress.visibility = if (show) VISIBLE else GONE } + override fun showContent(show: Boolean) { + messagePreviewContentContainer.visibility = if (show) VISIBLE else GONE + } + override fun showOptions(show: Boolean) { menuReplyButton?.isVisible = show menuForwardButton?.isVisible = show + menuDeleteButton?.isVisible = show + } + + override fun setDeletedOptionsLabels() { + menuDeleteButton?.setTitle(R.string.message_delete_forever) + } + + override fun setNotDeletedOptionsLabels() { + menuDeleteButton?.setTitle(R.string.message_move_to_bin) } override fun showMessageError() { @@ -114,6 +135,14 @@ class MessagePreviewFragment : BaseSessionFragment(), MessagePreviewView, MainVi context?.let { it.startActivity(SendMessageActivity.getStartIntent(it, message)) } } + override fun popView() { + (activity as MainActivity).popView() + } + + override fun notifyParentMessageDeleted(message: Message) { + fragmentManager?.fragments?.forEach { if (it is MessageFragment) it.onDeleteMessage(message) } + } + override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) outState.putInt(MESSAGE_ID_KEY, presenter.messageId) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt index 534c7ad3d..397e103b2 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt @@ -47,7 +47,7 @@ class MessagePreviewPresenter @Inject constructor( setSubject(if (it.subject.isNotBlank()) it.subject else noSubjectString) setDate(it.date.toFormattedString("yyyy-MM-dd HH:mm:ss")) setContent(it.content.orEmpty()) - showOptions(true) + initOptions() if (it.recipient.isNotBlank()) setRecipient(it.recipient) else setSender(it.sender) @@ -76,7 +76,56 @@ class MessagePreviewPresenter @Inject constructor( } else false } + private fun deleteMessage() { + message?.let { message -> + disposable.add(messageRepository.deleteMessage(message) + .subscribeOn(schedulers.backgroundThread) + .observeOn(schedulers.mainThread) + .doOnSubscribe { + view?.run { + showContent(false) + showProgress(true) + showOptions(false) + } + } + .doFinally { + view?.showProgress(false) + } + .subscribe({ + view?.run { + notifyParentMessageDeleted(message) + showMessage(deleteMessageSuccessString) + popView() + } + }, { error -> + view?.showMessageError() + errorHandler.dispatch(error) + }, { + view?.showMessageError() + }) + ) + } + } + + fun onMessageDelete(): Boolean { + deleteMessage() + return true + } + + private fun initOptions() { + view?.apply { + showOptions(message != null) + message?.let { + when (it.removed) { + true -> setDeletedOptionsLabels() + false -> setNotDeletedOptionsLabels() + } + } + + } + } + fun onCreateOptionsMenu() { - view?.showOptions(message != null) + initOptions() } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt index 3ff73396e..8bc528a3d 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt @@ -7,6 +7,8 @@ interface MessagePreviewView : BaseSessionView { val noSubjectString: String + val deleteMessageSuccessString: String + fun setSubject(subject: String) fun setRecipient(recipient: String) @@ -19,11 +21,21 @@ interface MessagePreviewView : BaseSessionView { fun showProgress(show: Boolean) + fun showContent(show: Boolean) + fun showOptions(show: Boolean) + fun setDeletedOptionsLabels() + + fun setNotDeletedOptionsLabels() + fun showMessageError() fun openMessageReply(message: Message?) fun openMessageForward(message: Message?) + + fun popView() + + fun notifyParentMessageDeleted(message: Message) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt index 6ac7e2264..3a44ea86f 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt @@ -62,7 +62,7 @@ class SendMessageActivity : BaseActivity(), SendMessageView { supportActionBar?.setDisplayHomeAsUpEnabled(true) messageContainer = sendMessageContainer - presenter.onAttachView(this, intent.getSerializableExtra(EXTRA_MESSAGE) as? Message, intent.getSerializableExtra(EXTRA_REPLY) as Boolean) + presenter.onAttachView(this, intent.getSerializableExtra(EXTRA_MESSAGE) as? Message, intent.getSerializableExtra(EXTRA_REPLY) as? Boolean) } override fun onCreateOptionsMenu(menu: Menu?): Boolean { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt index f57848d61..32e3d5b20 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt @@ -30,7 +30,7 @@ class SendMessagePresenter @Inject constructor( private val analytics: FirebaseAnalyticsHelper ) : BasePresenter(errorHandler) { - fun onAttachView(view: SendMessageView, message: Message?, reply: Boolean) { + fun onAttachView(view: SendMessageView, message: Message?, reply: Boolean?) { super.onAttachView(view) Timber.i("Send message view is attached") loadData(message, reply) @@ -38,13 +38,13 @@ class SendMessagePresenter @Inject constructor( message?.let { setSubject(when (reply) { true -> "RE: " - false -> "FE: " + else -> "FW: " } + message.subject) - if (preferencesRepository.fillMessageContent || !reply) { + if (preferencesRepository.fillMessageContent || reply != true) { setContent( when (reply) { true -> "\n\n" - false -> "" + else -> "" } + when (message.sender.isNotEmpty()) { true -> "Od: ${message.sender}\n" false -> "Do: ${message.recipient}\n" @@ -59,7 +59,7 @@ class SendMessagePresenter @Inject constructor( return true } - private fun loadData(message: Message?, reply: Boolean) { + private fun loadData(message: Message?, reply: Boolean?) { var reportingUnit: ReportingUnit? = null var recipients: List = emptyList() var selectedRecipient: List = emptyList() @@ -76,7 +76,7 @@ class SendMessagePresenter @Inject constructor( recipients = it } .flatMapCompletable { - if (message == null || !reply) Completable.complete() + if (message == null || reply != true) Completable.complete() else recipientRepository.getMessageRecipients(student, message) .doOnSuccess { Timber.i("Loaded message recipients to reply result: Success, fetched %d recipients", it.size) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt index 047f2a34c..1f4595aec 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt @@ -17,13 +17,12 @@ import io.github.wulkanowy.ui.base.session.BaseSessionFragment import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.message.MessageFragment import io.github.wulkanowy.ui.modules.message.MessageItem -import io.github.wulkanowy.ui.modules.message.MessageView import io.github.wulkanowy.ui.modules.message.preview.MessagePreviewFragment import io.github.wulkanowy.utils.setOnItemClickListener import kotlinx.android.synthetic.main.fragment_message_tab.* import javax.inject.Inject -class MessageTabFragment : BaseSessionFragment(), MessageTabView, MessageView.MessageChildView { +class MessageTabFragment : BaseSessionFragment(), MessageTabView { @Inject lateinit var presenter: MessageTabPresenter @@ -115,10 +114,14 @@ class MessageTabFragment : BaseSessionFragment(), MessageTabView, MessageView.Me (parentFragment as? MessageFragment)?.onChildFragmentLoaded() } - override fun onParentLoadData(forceRefresh: Boolean) { + fun onParentLoadData(forceRefresh: Boolean) { presenter.onParentViewLoadData(forceRefresh) } + fun onParentDeleteMessage() { + presenter.onDeleteMessage() + } + override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) outState.putString(MessageTabFragment.MESSAGE_TAB_FOLDER_ID, presenter.folder.name) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt index 7d9fda0d3..4a8415e87 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt @@ -34,7 +34,29 @@ class MessageTabPresenter @Inject constructor( onParentViewLoadData(true) } + fun onDeleteMessage() { + loadData(false) + } + fun onParentViewLoadData(forceRefresh: Boolean) { + loadData(forceRefresh) + } + + fun onMessageItemSelected(item: AbstractFlexibleItem<*>) { + if (item is MessageItem) { + Timber.i("Select message ${item.message.realId} item") + view?.run { + openMessage(item.message.realId) + if (item.message.unread) { + item.message.unread = false + updateItem(item) + updateMessage(item.message) + } + } + } + } + + private fun loadData(forceRefresh: Boolean) { Timber.i("Loading $folder message data started") disposable.apply { clear() @@ -67,20 +89,6 @@ class MessageTabPresenter @Inject constructor( } } - fun onMessageItemSelected(item: AbstractFlexibleItem<*>) { - if (item is MessageItem) { - Timber.i("Select message ${item.message.realId} item") - view?.run { - openMessage(item.message.realId) - if (item.message.unread) { - item.message.unread = false - updateItem(item) - updateMessage(item.message) - } - } - } - } - private fun updateMessage(message: Message) { Timber.i("Attempt to update message ${message.realId}") disposable.add(messageRepository.updateMessage(message) diff --git a/app/src/main/res/drawable/ic_message_delete_24dp.xml b/app/src/main/res/drawable/ic_message_delete_24dp.xml new file mode 100644 index 000000000..3760de238 --- /dev/null +++ b/app/src/main/res/drawable/ic_message_delete_24dp.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/layout/fragment_message_preview.xml b/app/src/main/res/layout/fragment_message_preview.xml index 7150c03aa..962d94ac2 100644 --- a/app/src/main/res/layout/fragment_message_preview.xml +++ b/app/src/main/res/layout/fragment_message_preview.xml @@ -10,6 +10,7 @@ android:layout_height="match_parent"> + diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 16ec15ff9..3caa356e1 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -141,6 +141,10 @@ Data: %s Odpowiedz Prześlij dalej + Usuń + Przenieś do kosza + Usuń trwale + Wiadomość usunięta pomyślnie Temat Treść Wiadomość wysłana pomyślnie diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 98bef391f..6a56b4416 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -135,6 +135,10 @@ Date: %s Reply Forward + Delete + Move to trash + Delete permanently + Message deleted successfully Subject Content Message sent successfully From 333f7bfa162f9f6b34c3a09dbd10bb4f3ee8ab1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Sun, 7 Apr 2019 10:59:27 +0200 Subject: [PATCH 03/78] Add privacy policy link (#320) --- .../ui/modules/login/form/LoginFormFragment.kt | 2 ++ app/src/main/res/layout/fragment_login_form.xml | 12 +++++++++++- app/src/main/res/values-pl/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + 4 files changed, 15 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormFragment.kt index 54fd0704b..5c0a6ed8d 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormFragment.kt @@ -2,6 +2,7 @@ package io.github.wulkanowy.ui.modules.login.form import android.annotation.SuppressLint import android.os.Bundle +import android.text.method.LinkMovementMethod import android.view.LayoutInflater import android.view.View import android.view.View.GONE @@ -53,6 +54,7 @@ class LoginFormFragment : BaseFragment(), LoginFormView { loginFormName.setOnTextChangedListener { presenter.onNameTextChanged() } loginFormPass.setOnTextChangedListener { presenter.onPassTextChanged() } loginFormHost.setOnItemSelectedListener { presenter.onHostSelected() } + loginFormPrivacyPolicyLink.movementMethod = LinkMovementMethod.getInstance() loginFormSignIn.setOnClickListener { presenter.attemptLogin() } loginFormPass.setOnEditorActionListener { _, id, _ -> diff --git a/app/src/main/res/layout/fragment_login_form.xml b/app/src/main/res/layout/fragment_login_form.xml index 81baf4afc..7a82eaf7a 100644 --- a/app/src/main/res/layout/fragment_login_form.xml +++ b/app/src/main/res/layout/fragment_login_form.xml @@ -124,7 +124,8 @@ + android:layout_height="56dp" + android:entries="@array/endpoints_keys" /> + + Hasło Dziennik Symbol + Polityka prywatności Zaloguj To hasło jest za krótkie Dane logowania są niepoprawne diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1debed7b3..e12d94fdc 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -26,6 +26,7 @@ Password Register Symbol + Privacy policy Sign in This password is too short Login details are incorrect From 6cd6cae1e0fa7f6f4b08c50ba212a82862efe2b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Sun, 7 Apr 2019 11:08:46 +0200 Subject: [PATCH 04/78] Version 0.7.6 --- app/build.gradle | 6 +++--- app/src/main/play/release-notes/pl-PL/default.txt | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index c58c1e337..48f0e65ce 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -16,8 +16,8 @@ android { testApplicationId "io.github.tests.wulkanowy" minSdkVersion 15 targetSdkVersion 28 - versionCode 31 - versionName "0.7.5" + versionCode 32 + versionName "0.7.6" multiDexEnabled true testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true @@ -86,7 +86,7 @@ play { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" - implementation('io.github.wulkanowy:api:0.7.5') { exclude module: "threetenbp" } + implementation('io.github.wulkanowy:api:0.7.6') { exclude module: "threetenbp" } implementation "androidx.legacy:legacy-support-v4:1.0.0" implementation "androidx.appcompat:appcompat:1.0.2" diff --git a/app/src/main/play/release-notes/pl-PL/default.txt b/app/src/main/play/release-notes/pl-PL/default.txt index 785769037..d0f12e55a 100644 --- a/app/src/main/play/release-notes/pl-PL/default.txt +++ b/app/src/main/play/release-notes/pl-PL/default.txt @@ -1,7 +1,9 @@ Wersja 0.7.5 +Uwaga! Jeżeli w aplikacji przestały wyświetlać się oceny, prosimy o wylogowanie i zalogowanie się ponownie! + Naprawiliśmy: -- problem z brakiem aktywnego semestru (jeśli doświadczysz jakichś problemów - wyloguj i zaloguj się ponownie) +- problem z brakiem aktywnego semestru - logowanie w niestandardowych dziennikach na vulcan.net.pl - oznaczanie lekcji w planie jako odwołanej, jeśli brak opisu tej zmiany - ładowanie wiadomości, jeśli byli zalogowani jednocześnie uczeń i rodzic From c18877466ffea8848ef47025c2edd945bb48d1c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Borcz?= Date: Mon, 8 Apr 2019 00:18:45 +0200 Subject: [PATCH 05/78] Add account picker for timetable widget (#314) Close #281 --- app/src/main/AndroidManifest.xml | 11 +- .../wulkanowy/data/db/SharedPrefHelper.kt | 6 +- .../io/github/wulkanowy/di/BuilderModule.kt | 6 +- .../widgets/TimetableWidgetService.kt | 2 +- .../TimetableWidgetConfigureActivity.kt | 79 ++++++++++++++ .../TimetableWidgetConfigureItem.kt | 53 ++++++++++ .../TimetableWidgetConfigurePresenter.kt | 65 ++++++++++++ .../TimetableWidgetConfigureView.kt | 18 ++++ .../TimetableWidgetFactory.kt | 52 ++++++---- .../TimetableWidgetProvider.kt | 83 ++++++++++++--- .../github/wulkanowy/utils/TimeExtension.kt | 3 + .../res/drawable-hdpi/ic_widget_account.png | Bin 0 -> 461 bytes .../res/drawable-mdpi/ic_widget_account.png | Bin 0 -> 319 bytes .../res/drawable-xhdpi/ic_widget_account.png | Bin 0 -> 596 bytes .../res/drawable-xxhdpi/ic_widget_account.png | Bin 0 -> 922 bytes .../drawable/img_timetable_widget_preview.png | Bin 3425 -> 5284 bytes .../activity_timetable_widget_configure.xml | 31 ++++++ app/src/main/res/layout/item_account.xml | 4 +- .../main/res/layout/item_widget_timetable.xml | 2 +- app/src/main/res/layout/widget_timetable.xml | 97 ++++++++++-------- app/src/main/res/values-night/styles.xml | 5 - app/src/main/res/values-pl/strings.xml | 2 - app/src/main/res/values/dimens.xml | 3 - app/src/main/res/values/strings.xml | 2 - app/src/main/res/values/styles.xml | 8 ++ .../res/xml/provider_widget_timetable.xml | 7 +- 26 files changed, 437 insertions(+), 102 deletions(-) create mode 100644 app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureActivity.kt create mode 100644 app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureItem.kt create mode 100644 app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigurePresenter.kt create mode 100644 app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureView.kt rename app/src/main/java/io/github/wulkanowy/ui/{widgets/timetable => modules/timetablewidget}/TimetableWidgetFactory.kt (67%) rename app/src/main/java/io/github/wulkanowy/ui/{widgets/timetable => modules/timetablewidget}/TimetableWidgetProvider.kt (57%) create mode 100644 app/src/main/res/drawable-hdpi/ic_widget_account.png create mode 100644 app/src/main/res/drawable-mdpi/ic_widget_account.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_widget_account.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_widget_account.png create mode 100644 app/src/main/res/layout/activity_timetable_widget_configure.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 0c6edab46..f0dd9cb40 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -45,13 +45,22 @@ android:configChanges="orientation|screenSize" android:label="@string/send_message_title" android:theme="@style/WulkanowyTheme.NoActionBar" /> + + + + + diff --git a/app/src/main/java/io/github/wulkanowy/data/db/SharedPrefHelper.kt b/app/src/main/java/io/github/wulkanowy/data/db/SharedPrefHelper.kt index b3b6f5e3e..74f9fa654 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/SharedPrefHelper.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/SharedPrefHelper.kt @@ -6,18 +6,16 @@ import javax.inject.Inject import javax.inject.Singleton @Singleton +@SuppressLint("ApplySharedPref") class SharedPrefHelper @Inject constructor(private val sharedPref: SharedPreferences) { - @SuppressLint("ApplySharedPref") fun putLong(key: String, value: Long, sync: Boolean = false) { sharedPref.edit().putLong(key, value).apply { if (sync) commit() else apply() } } - fun getLong(key: String, defaultValue: Long): Long { - return sharedPref.getLong(key, defaultValue) - } + fun getLong(key: String, defaultValue: Long) = sharedPref.getLong(key, defaultValue) fun delete(key: String) { sharedPref.edit().remove(key).apply() diff --git a/app/src/main/java/io/github/wulkanowy/di/BuilderModule.kt b/app/src/main/java/io/github/wulkanowy/di/BuilderModule.kt index 7f4776304..2b48029e2 100644 --- a/app/src/main/java/io/github/wulkanowy/di/BuilderModule.kt +++ b/app/src/main/java/io/github/wulkanowy/di/BuilderModule.kt @@ -9,7 +9,8 @@ import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainModule import io.github.wulkanowy.ui.modules.message.send.SendMessageActivity import io.github.wulkanowy.ui.modules.splash.SplashActivity -import io.github.wulkanowy.ui.widgets.timetable.TimetableWidgetProvider +import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetConfigureActivity +import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider @Module internal abstract class BuilderModule { @@ -29,6 +30,9 @@ internal abstract class BuilderModule { @ContributesAndroidInjector abstract fun bindMessageSendActivity(): SendMessageActivity + @ContributesAndroidInjector + abstract fun bindTimetableWidgetAccountActivity(): TimetableWidgetConfigureActivity + @ContributesAndroidInjector abstract fun bindTimetableWidgetProvider(): TimetableWidgetProvider } diff --git a/app/src/main/java/io/github/wulkanowy/services/widgets/TimetableWidgetService.kt b/app/src/main/java/io/github/wulkanowy/services/widgets/TimetableWidgetService.kt index 0432ee146..f3429457b 100644 --- a/app/src/main/java/io/github/wulkanowy/services/widgets/TimetableWidgetService.kt +++ b/app/src/main/java/io/github/wulkanowy/services/widgets/TimetableWidgetService.kt @@ -7,7 +7,7 @@ import io.github.wulkanowy.data.db.SharedPrefHelper import io.github.wulkanowy.data.repositories.semester.SemesterRepository import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.timetable.TimetableRepository -import io.github.wulkanowy.ui.widgets.timetable.TimetableWidgetFactory +import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetFactory import io.github.wulkanowy.utils.SchedulersProvider import javax.inject.Inject diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureActivity.kt new file mode 100644 index 000000000..37d0571a4 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureActivity.kt @@ -0,0 +1,79 @@ +package io.github.wulkanowy.ui.modules.timetablewidget + +import android.appwidget.AppWidgetManager.ACTION_APPWIDGET_UPDATE +import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID +import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_IDS +import android.content.Intent +import android.os.Bundle +import android.widget.Toast +import android.widget.Toast.LENGTH_LONG +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import io.github.wulkanowy.R +import io.github.wulkanowy.ui.base.BaseActivity +import io.github.wulkanowy.ui.modules.login.LoginActivity +import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.EXTRA_FROM_PROVIDER +import io.github.wulkanowy.utils.setOnItemClickListener +import kotlinx.android.synthetic.main.activity_timetable_widget_configure.* +import javax.inject.Inject + +class TimetableWidgetConfigureActivity : BaseActivity(), TimetableWidgetConfigureView { + + @Inject + lateinit var configureAdapter: FlexibleAdapter> + + @Inject + lateinit var presenter: TimetableWidgetConfigurePresenter + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setResult(RESULT_CANCELED) + setContentView(R.layout.activity_timetable_widget_configure) + + intent.extras.let { + presenter.onAttachView(this, it?.getInt(EXTRA_APPWIDGET_ID), it?.getBoolean(EXTRA_FROM_PROVIDER)) + } + } + + override fun initView() { + timetableWidgetConfigureRecycler.apply { + adapter = configureAdapter + layoutManager = SmoothScrollLinearLayoutManager(context) + } + configureAdapter.setOnItemClickListener { presenter.onItemSelect(it) } + } + + override fun updateData(data: List) { + configureAdapter.updateDataSet(data) + } + + override fun updateTimetableWidget(widgetId: Int) { + sendBroadcast(Intent(this, TimetableWidgetProvider::class.java) + .apply { + action = ACTION_APPWIDGET_UPDATE + putExtra(EXTRA_APPWIDGET_IDS, intArrayOf(widgetId)) + }) + } + + override fun setSuccessResult(widgetId: Int) { + setResult(RESULT_OK, Intent().apply { putExtra(EXTRA_APPWIDGET_ID, widgetId) }) + } + + override fun showError(text: String, error: Throwable) { + Toast.makeText(this, text, LENGTH_LONG).show() + } + + override fun finishView() { + finish() + } + + override fun openLoginView() { + startActivity(LoginActivity.getStartIntent(this)) + } + + override fun onDestroy() { + super.onDestroy() + presenter.onDetachView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureItem.kt new file mode 100644 index 000000000..f6ec1519f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureItem.kt @@ -0,0 +1,53 @@ +package io.github.wulkanowy.ui.modules.timetablewidget + +import android.annotation.SuppressLint +import android.view.View +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import eu.davidea.flexibleadapter.items.IFlexible +import eu.davidea.viewholders.FlexibleViewHolder +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Student +import kotlinx.android.extensions.LayoutContainer +import kotlinx.android.synthetic.main.item_account.* + +class TimetableWidgetConfigureItem(val student: Student, private val isCurrent: Boolean) : + AbstractFlexibleItem() { + + override fun getLayoutRes() = R.layout.item_account + + override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ViewHolder { + return ViewHolder(view, adapter) + } + + @SuppressLint("SetTextI18n") + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList) { + holder.apply { + accountItemName.text = "${student.studentName} ${student.className}" + accountItemSchool.text = student.schoolName + accountItemImage.setBackgroundResource(if (isCurrent) R.drawable.ic_account_circular_border else 0) + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as TimetableWidgetConfigureItem + + if (student != other.student) return false + + return true + } + + override fun hashCode(): Int { + var result = student.hashCode() + result = 31 * result + student.id.toInt() + return result + } + + class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer { + override val containerView: View + get() = contentView + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigurePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigurePresenter.kt new file mode 100644 index 000000000..5a7bdd7c0 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigurePresenter.kt @@ -0,0 +1,65 @@ +package io.github.wulkanowy.ui.modules.timetablewidget + +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import io.github.wulkanowy.data.db.SharedPrefHelper +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getStudentWidgetKey +import io.github.wulkanowy.utils.SchedulersProvider +import javax.inject.Inject + +class TimetableWidgetConfigurePresenter @Inject constructor( + private val errorHandler: ErrorHandler, + private val schedulers: SchedulersProvider, + private val studentRepository: StudentRepository, + private val sharedPref: SharedPrefHelper +) : BasePresenter(errorHandler) { + + private var appWidgetId: Int? = null + + private var isFromProvider = false + + fun onAttachView(view: TimetableWidgetConfigureView, appWidgetId: Int?, isFromProvider: Boolean?) { + super.onAttachView(view) + this.appWidgetId = appWidgetId + this.isFromProvider = isFromProvider ?: false + view.initView() + loadData() + } + + fun onItemSelect(item: AbstractFlexibleItem<*>) { + if (item is TimetableWidgetConfigureItem) { + registerStudent(item.student) + } + } + + private fun loadData() { + disposable.add(studentRepository.getSavedStudents(false) + .map { it to appWidgetId?.let { id -> sharedPref.getLong(getStudentWidgetKey(id), 0) } } + .map { (students, currentStudentId) -> + students.map { student -> TimetableWidgetConfigureItem(student, student.id == currentStudentId) } + } + .subscribeOn(schedulers.backgroundThread) + .observeOn(schedulers.mainThread) + .subscribe({ + when { + it.isEmpty() -> view?.openLoginView() + it.size == 1 && !isFromProvider -> registerStudent(it.single().student) + else -> view?.updateData(it) + } + }, { errorHandler.dispatch(it) })) + } + + private fun registerStudent(student: Student) { + appWidgetId?.also { + sharedPref.putLong(getStudentWidgetKey(it), student.id) + view?.apply { + updateTimetableWidget(it) + setSuccessResult(it) + } + } + view?.finishView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureView.kt new file mode 100644 index 000000000..98c800d4c --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureView.kt @@ -0,0 +1,18 @@ +package io.github.wulkanowy.ui.modules.timetablewidget + +import io.github.wulkanowy.ui.base.BaseView + +interface TimetableWidgetConfigureView : BaseView { + + fun initView() + + fun updateData(data: List) + + fun updateTimetableWidget(widgetId: Int) + + fun setSuccessResult(widgetId: Int) + + fun finishView() + + fun openLoginView() +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/widgets/timetable/TimetableWidgetFactory.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetFactory.kt similarity index 67% rename from app/src/main/java/io/github/wulkanowy/ui/widgets/timetable/TimetableWidgetFactory.kt rename to app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetFactory.kt index f2d40ba7e..b288f7b79 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/widgets/timetable/TimetableWidgetFactory.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetFactory.kt @@ -1,5 +1,6 @@ -package io.github.wulkanowy.ui.widgets.timetable +package io.github.wulkanowy.ui.modules.timetablewidget +import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID import android.content.Context import android.content.Intent import android.graphics.Paint.ANTI_ALIAS_FLAG @@ -15,9 +16,11 @@ import io.github.wulkanowy.data.db.entities.Timetable import io.github.wulkanowy.data.repositories.semester.SemesterRepository import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.timetable.TimetableRepository +import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getDateWidgetKey +import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getStudentWidgetKey import io.github.wulkanowy.utils.SchedulersProvider import io.github.wulkanowy.utils.toFormattedString -import io.reactivex.Single +import io.reactivex.Maybe import org.threeten.bp.LocalDate import timber.log.Timber @@ -48,28 +51,37 @@ class TimetableWidgetFactory( override fun onDestroy() {} override fun onDataSetChanged() { - intent?.action?.let { LocalDate.ofEpochDay(sharedPref.getLong(it, 0)) } - ?.let { date -> - try { - lessons = studentRepository.isStudentSaved() - .flatMap { isSaved -> - if (isSaved) { - studentRepository.getCurrentStudent() - .flatMap { semesterRepository.getCurrentSemester(it) } - .flatMap { timetableRepository.getTimetable(it, date, date) } - } else Single.just(emptyList()) - } - .map { item -> item.sortedBy { it.number } } - .subscribeOn(schedulers.backgroundThread) - .blockingGet() - } catch (e: Exception) { - Timber.e(e, "An error has occurred while downloading data for the widget") - } + intent?.extras?.getInt(EXTRA_APPWIDGET_ID)?.let { appWidgetId -> + val date = LocalDate.ofEpochDay(sharedPref.getLong(getDateWidgetKey(appWidgetId), 0)) + val studentId = sharedPref.getLong(getStudentWidgetKey(appWidgetId), 0) + + lessons = try { + studentRepository.isStudentSaved() + .filter { true } + .flatMap { studentRepository.getSavedStudents().toMaybe() } + .flatMap { + if (studentId == 0L) throw IllegalArgumentException("Student id is 0") + + it.singleOrNull { student -> student.id == studentId } + .let { student -> + if (student != null) Maybe.just(student) + else Maybe.empty() + } + } + .flatMap { semesterRepository.getCurrentSemester(it).toMaybe() } + .flatMap { timetableRepository.getTimetable(it, date, date).toMaybe() } + .map { item -> item.sortedBy { it.number } } + .subscribeOn(schedulers.backgroundThread) + .blockingGet(emptyList()) + } catch (e: Exception) { + Timber.e(e, "An error has occurred in timetable widget factory") + emptyList() } + } } override fun getViewAt(position: Int): RemoteViews? { - if (position == INVALID_POSITION || lessons.getOrNull(position) === null) return null + if (position == INVALID_POSITION || lessons.getOrNull(position) == null) return null return RemoteViews(context.packageName, R.layout.item_widget_timetable).apply { lessons[position].let { diff --git a/app/src/main/java/io/github/wulkanowy/ui/widgets/timetable/TimetableWidgetProvider.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetProvider.kt similarity index 57% rename from app/src/main/java/io/github/wulkanowy/ui/widgets/timetable/TimetableWidgetProvider.kt rename to app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetProvider.kt index 402366ecf..83334c78f 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/widgets/timetable/TimetableWidgetProvider.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetProvider.kt @@ -1,4 +1,4 @@ -package io.github.wulkanowy.ui.widgets.timetable +package io.github.wulkanowy.ui.modules.timetablewidget import android.app.PendingIntent import android.app.PendingIntent.FLAG_UPDATE_CURRENT @@ -10,21 +10,28 @@ import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_IDS import android.content.BroadcastReceiver import android.content.Context import android.content.Intent +import android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK +import android.content.Intent.FLAG_ACTIVITY_NEW_TASK import android.widget.RemoteViews import dagger.android.AndroidInjection import io.github.wulkanowy.R import io.github.wulkanowy.data.db.SharedPrefHelper +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.services.widgets.TimetableWidgetService import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainActivity.Companion.EXTRA_START_MENU_INDEX import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.SchedulersProvider import io.github.wulkanowy.utils.nextOrSameSchoolDay import io.github.wulkanowy.utils.nextSchoolDay import io.github.wulkanowy.utils.previousSchoolDay +import io.github.wulkanowy.utils.shortcutWeekDayName import io.github.wulkanowy.utils.toFormattedString -import io.github.wulkanowy.utils.weekDayName +import io.reactivex.Maybe import org.threeten.bp.LocalDate import org.threeten.bp.LocalDate.now +import timber.log.Timber import javax.inject.Inject class TimetableWidgetProvider : BroadcastReceiver() { @@ -32,13 +39,21 @@ class TimetableWidgetProvider : BroadcastReceiver() { @Inject lateinit var appWidgetManager: AppWidgetManager + @Inject + lateinit var studentRepository: StudentRepository + @Inject lateinit var sharedPref: SharedPrefHelper + @Inject + lateinit var schedulers: SchedulersProvider + @Inject lateinit var analytics: FirebaseAnalyticsHelper companion object { + const val EXTRA_FROM_PROVIDER = "extraFromProvider" + const val EXTRA_TOGGLED_WIDGET_ID = "extraToggledWidget" const val EXTRA_BUTTON_TYPE = "extraButtonType" @@ -49,7 +64,9 @@ class TimetableWidgetProvider : BroadcastReceiver() { const val BUTTON_RESET = "buttonReset" - fun createWidgetKey(appWidgetId: Int) = "timetable_widget_$appWidgetId" + fun getDateWidgetKey(appWidgetId: Int) = "timetable_widget_date_$appWidgetId" + + fun getStudentWidgetKey(appWidgetId: Int) = "timetable_widget_student_$appWidgetId" } override fun onReceive(context: Context, intent: Intent) { @@ -63,12 +80,14 @@ class TimetableWidgetProvider : BroadcastReceiver() { private fun onUpdate(context: Context, intent: Intent) { if (intent.getStringExtra(EXTRA_BUTTON_TYPE) === null) { intent.getIntArrayExtra(EXTRA_APPWIDGET_IDS)?.forEach { appWidgetId -> - updateWidget(context, appWidgetId, now().nextOrSameSchoolDay) + val student = getStudent(sharedPref.getLong(getStudentWidgetKey(appWidgetId), 0), appWidgetId) + updateWidget(context, appWidgetId, now().nextOrSameSchoolDay, student) } } else { val buttonType = intent.getStringExtra(EXTRA_BUTTON_TYPE) val toggledWidgetId = intent.getIntExtra(EXTRA_TOGGLED_WIDGET_ID, 0) - val savedDate = LocalDate.ofEpochDay(sharedPref.getLong(createWidgetKey(toggledWidgetId), 0)) + val student = getStudent(sharedPref.getLong(getStudentWidgetKey(toggledWidgetId), 0), toggledWidgetId) + val savedDate = LocalDate.ofEpochDay(sharedPref.getLong(getDateWidgetKey(toggledWidgetId), 0)) val date = when (buttonType) { BUTTON_RESET -> now().nextOrSameSchoolDay BUTTON_NEXT -> savedDate.nextSchoolDay @@ -76,35 +95,46 @@ class TimetableWidgetProvider : BroadcastReceiver() { else -> now().nextOrSameSchoolDay } if (!buttonType.isNullOrBlank()) analytics.logEvent("changed_timetable_widget_day", "button" to buttonType) - updateWidget(context, toggledWidgetId, date) + updateWidget(context, toggledWidgetId, date, student) } } private fun onDelete(intent: Intent) { intent.getIntExtra(EXTRA_APPWIDGET_ID, 0).let { - if (it != 0) sharedPref.delete(createWidgetKey(it)) + if (it != 0) { + sharedPref.apply { + delete(getStudentWidgetKey(it)) + delete(getDateWidgetKey(it)) + } + } } } - private fun updateWidget(context: Context, appWidgetId: Int, date: LocalDate) { + private fun updateWidget(context: Context, appWidgetId: Int, date: LocalDate, student: Student?) { RemoteViews(context.packageName, R.layout.widget_timetable).apply { setEmptyView(R.id.timetableWidgetList, R.id.timetableWidgetEmpty) - setTextViewText(R.id.timetableWidgetDay, date.weekDayName.capitalize()) - setTextViewText(R.id.timetableWidgetDate, date.toFormattedString()) + setTextViewText(R.id.timetableWidgetDate, "${date.shortcutWeekDayName.capitalize()} ${date.toFormattedString()}") + setTextViewText(R.id.timetableWidgetName, student?.studentName ?: context.getString(R.string.all_no_data)) setRemoteAdapter(R.id.timetableWidgetList, Intent(context, TimetableWidgetService::class.java) - .apply { action = createWidgetKey(appWidgetId) }) + .apply { putExtra(EXTRA_APPWIDGET_ID, appWidgetId) }) setOnClickPendingIntent(R.id.timetableWidgetNext, createNavIntent(context, appWidgetId, appWidgetId, BUTTON_NEXT)) setOnClickPendingIntent(R.id.timetableWidgetPrev, createNavIntent(context, -appWidgetId, appWidgetId, BUTTON_PREV)) - createNavIntent(context, Int.MAX_VALUE - appWidgetId, appWidgetId, BUTTON_RESET).also { + createNavIntent(context, Int.MAX_VALUE - appWidgetId, appWidgetId, BUTTON_RESET).let { setOnClickPendingIntent(R.id.timetableWidgetDate, it) - setOnClickPendingIntent(R.id.timetableWidgetDay, it) + setOnClickPendingIntent(R.id.timetableWidgetName, it) } + setOnClickPendingIntent(R.id.timetableWidgetAccount, PendingIntent.getActivity(context, -Int.MAX_VALUE + appWidgetId, + Intent(context, TimetableWidgetConfigureActivity::class.java).apply { + addFlags(FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TASK) + putExtra(EXTRA_APPWIDGET_ID, appWidgetId) + putExtra(EXTRA_FROM_PROVIDER, true) + }, FLAG_UPDATE_CURRENT)) setPendingIntentTemplate(R.id.timetableWidgetList, PendingIntent.getActivity(context, 1, MainActivity.getStartIntent(context).apply { putExtra(EXTRA_START_MENU_INDEX, 3) }, FLAG_UPDATE_CURRENT)) }.also { - sharedPref.putLong(createWidgetKey(appWidgetId), date.toEpochDay(), true) + sharedPref.putLong(getDateWidgetKey(appWidgetId), date.toEpochDay(), true) appWidgetManager.apply { notifyAppWidgetViewDataChanged(appWidgetId, R.id.timetableWidgetList) updateAppWidget(appWidgetId, it) @@ -120,4 +150,29 @@ class TimetableWidgetProvider : BroadcastReceiver() { putExtra(EXTRA_TOGGLED_WIDGET_ID, appWidgetId) }, FLAG_UPDATE_CURRENT) } + + private fun getStudent(id: Long, appWidgetId: Int): Student? { + return try { + studentRepository.isStudentSaved() + .filter { true } + .flatMap { studentRepository.getSavedStudents(false).toMaybe() } + .flatMap { students -> + students.singleOrNull { student -> student.id == id } + .let { student -> + if (student != null) { + Maybe.just(student) + } else { + studentRepository.getCurrentStudent(false) + .toMaybe() + .doOnSuccess { sharedPref.putLong(getStudentWidgetKey(appWidgetId), it.id) } + } + } + } + .subscribeOn(schedulers.backgroundThread) + .blockingGet() + } catch (e: Exception) { + Timber.e(e, "An error has occurred in timetable widget provider") + null + } + } } diff --git a/app/src/main/java/io/github/wulkanowy/utils/TimeExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/TimeExtension.kt index 886b3d4da..426816f44 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/TimeExtension.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/TimeExtension.kt @@ -99,6 +99,9 @@ inline val LocalDate.previousOrSameSchoolDay: LocalDate inline val LocalDate.weekDayName: String get() = this.format(ofPattern("EEEE", Locale.getDefault())) +inline val LocalDate.shortcutWeekDayName: String + get() = this.format(ofPattern("EEE", Locale.getDefault())) + inline val LocalDate.monday: LocalDate get() = this.with(MONDAY) diff --git a/app/src/main/res/drawable-hdpi/ic_widget_account.png b/app/src/main/res/drawable-hdpi/ic_widget_account.png new file mode 100644 index 0000000000000000000000000000000000000000..4cb5ac89ee607739a25ef940414c0078935095ee GIT binary patch literal 461 zcmeAS@N?(olHy`uVBq!ia0y~yU{C>J4mJh`hKCF@W-u@?PVsbc45_&Fc80wlQ=q`H zMmDW=4o^kbY*3iT@M7CS#g7aumJJfZI~oEq5;?XX&~|V)GZRw_2v{_;ygu zr-eta9Nb*E{m6DXCl9fh#@>(Hdyky@)gdWtZ^FF&P|uILH!56ycWoQv9ltcktGqXP zeXDDw;BgVne}aWIreQvf9uG7H)4f!x8U?SYeM)dRA}LUK%jaUWOuz*v-FX#}YU>L+ zlz(s9B(@|!FuZ0`)$L!i7IeBQ%+KDRE9`sW&O)Z!V#nT_U1wQ)_F?Yd9>)HEP8}Koo33Hx) zXvgZ(DNGz%k=yx}?%c-2krinkp8DqpLnyytXlJ5*od)wQ>lZiHm`!4m{a7k}p+nH@ z=5DQ)de?nlF3iMHY} zolW|9UC60Ifvx&NllH|RpSsUE=KTje&v-v`QIob#P(P!3-NK5Eb@LC#q77_% zrnCkhdy*!+bKEKAn>B_3(sWtpR6n`k1R-5taGo>H0dm`hS_xSXIogMr| z>x`aHj~BT9MRo)88YSx=J)6?Sbn2USF#)0@gJxE^&e~n zzB4kMF3b}sS<1^R%f9SL?{lFov5UTiJa1Vl7i8L0=lgg=c94ujc8lF?K98Qzqud9| zdpUFT6dTP%b2@L|IkKg{<>k7(pfu&4e@8ryGTv8UePDN@?r?aaLV`!XWl(1=M~F;B z_miZxj~r$supg>_$Z%a~QsHmbojt{zth1!UpS{0+KsX__KaaJdD_PFuf5`z^xA?~C z2b6nLK1x)u);#80ti^s)tb$p`P{c>|%B>>Vly!%SEH_^;v0$$&Tp|>B^3H{%=gAp! z&S);VtlQ1ErRQ>HYg1an#YRSNN8!MrI;P!XJ?|H7tMptMa3$YjuES&VHLiP|qFvp3 z?3zFR?RfEcrk}vo8&xdZOiOP6Shx3>*v73I$74?X`QH#GdVIwz(Vf>cw(k7h^Z5P5 z;u^8~Vsp{AUqfOFmOSgy6rEnPa%bAv35u*Y7@ur98?bm~su}|W1B0ilpUXO@geCxx C#0n4q literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxhdpi/ic_widget_account.png b/app/src/main/res/drawable-xxhdpi/ic_widget_account.png new file mode 100644 index 0000000000000000000000000000000000000000..0f8933b1777630ae0d5574c12408d0e358422a4a GIT binary patch literal 922 zcmeAS@N?(olHy`uVBq!ia0y~yVDJE84mJh`hS0a0-5D5|6FprVLn>~)jWP5O4is@q zwi1Yc;dI%P%_(7pG_%znKb z4>K@lH!ite{Max|`Rl#k=WM@Uny3GB(zA>Eet-PB_xCx=?|1roI9Qk(9TW^&g0e&` z_+uEaHU2(e_QB=vF;2PL$G@+6knYBQbqdo4wj3twQ?jx)?RMdb0zICMybqQxI<7j; zdT-~kDUH@${8ei{`5G+axD)bl!EwD~S$?b)>kc_*uQ-z8!?$DMqvmTp4ssiQXSc#Zz7$S`?5o0RLBD*Qe1I|`z752lLA zbIi^9!~I#0ZEe+>i!EMZ>kqH$b}T*Ls`IYM-Tq1}PnKcm9}BLu|IVk6F4!g|w%a z@s3%ZIlj85J!&EIQRW9yALJZ2Suxgd+3?6}1TB$z-@tF9=WNTekAG@bif7pVA12SF z7WeI#9D2Rzxc7s(3};26-b-&QYEM4+rjq5amwp6S8lUR**avGlcy{$hMR=_FFxGZJy7jC6aWx zY5DU;)%hDI+L`wy2H#oz>oQG@7eS|`qqOTb7F5btS{ix7VL>XRD82+ z?uRXm-@m-rVK(V?e(lXPrQ@6xkvq$GeKei)y<^#pt^%$a?ihBv!wl=Y)?fdazg+V8 zgZo_{u1{5&9m(@dM8 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/img_timetable_widget_preview.png b/app/src/main/res/drawable/img_timetable_widget_preview.png index 60a35ab6ac5a6e0f5a7b30055844efacf36e4a7c..550260258a82ae1fcb8c54530efff52985580153 100644 GIT binary patch literal 5284 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 diff --git a/app/src/main/res/layout/activity_timetable_widget_configure.xml b/app/src/main/res/layout/activity_timetable_widget_configure.xml new file mode 100644 index 000000000..2a95e01d1 --- /dev/null +++ b/app/src/main/res/layout/activity_timetable_widget_configure.xml @@ -0,0 +1,31 @@ + + + + + + + diff --git a/app/src/main/res/layout/item_account.xml b/app/src/main/res/layout/item_account.xml index 56f553e54..5f76dded7 100644 --- a/app/src/main/res/layout/item_account.xml +++ b/app/src/main/res/layout/item_account.xml @@ -33,7 +33,8 @@ android:ellipsize="end" android:maxLines="1" android:textSize="16sp" - tools:text="@tools:sample/lorem/random" /> + tools:text="@tools:sample/lorem/random" + android:textColor="?android:textColorSecondary"/> diff --git a/app/src/main/res/layout/item_widget_timetable.xml b/app/src/main/res/layout/item_widget_timetable.xml index af0552807..aa8088640 100644 --- a/app/src/main/res/layout/item_widget_timetable.xml +++ b/app/src/main/res/layout/item_widget_timetable.xml @@ -7,7 +7,7 @@ android:background="@drawable/ic_all_divider" android:minHeight="45dp" android:orientation="horizontal" - tools:context=".ui.widgets.timetable.TimetableWidgetFactory"> + tools:context=".ui.modules.timetablewidget.TimetableWidgetFactory"> + tools:context=".ui.modules.timetablewidget.TimetableWidgetProvider"> + + + + + + + + + - - - - - @color/about_libraries_dividerDark_openSource_dark @color/about_libraries_dividerLight_openSource_dark - - diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 3caa356e1..144772a5b 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -233,8 +233,6 @@ Brak lekcji - Dziś - Jutro diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index e0708095e..a4bf80ed6 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -2,7 +2,4 @@ 8dp 8dp - - - 60dp diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6a56b4416..a00514be4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -218,8 +218,6 @@ No lessons - Today - Tomorrow diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 4bbe3bc1b..58cd99eb9 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -40,4 +40,12 @@ + + diff --git a/app/src/main/res/xml/provider_widget_timetable.xml b/app/src/main/res/xml/provider_widget_timetable.xml index 48c6ba395..1e0eedcb3 100644 --- a/app/src/main/res/xml/provider_widget_timetable.xml +++ b/app/src/main/res/xml/provider_widget_timetable.xml @@ -1,8 +1,11 @@ Date: Tue, 9 Apr 2019 23:33:53 +0200 Subject: [PATCH 06/78] Change style of privacy policy link (#321) --- .../modules/login/form/LoginFormFragment.kt | 14 +++++-- .../modules/login/form/LoginFormPresenter.kt | 9 ++++- .../ui/modules/login/form/LoginFormView.kt | 4 ++ .../main/res/layout/fragment_login_form.xml | 37 +++++++++++-------- app/src/main/res/values-pl/strings.xml | 2 +- app/src/main/res/values/strings.xml | 2 +- .../login/form/LoginFormPresenterTest.kt | 16 ++++---- 7 files changed, 53 insertions(+), 31 deletions(-) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormFragment.kt index 5c0a6ed8d..2a0467d9f 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormFragment.kt @@ -1,8 +1,8 @@ package io.github.wulkanowy.ui.modules.login.form import android.annotation.SuppressLint +import android.content.Intent import android.os.Bundle -import android.text.method.LinkMovementMethod import android.view.LayoutInflater import android.view.View import android.view.View.GONE @@ -54,8 +54,8 @@ class LoginFormFragment : BaseFragment(), LoginFormView { loginFormName.setOnTextChangedListener { presenter.onNameTextChanged() } loginFormPass.setOnTextChangedListener { presenter.onPassTextChanged() } loginFormHost.setOnItemSelectedListener { presenter.onHostSelected() } - loginFormPrivacyPolicyLink.movementMethod = LinkMovementMethod.getInstance() - loginFormSignIn.setOnClickListener { presenter.attemptLogin() } + loginFormSignIn.setOnClickListener { presenter.onSignInClick() } + loginFormPrivacyLink.setOnClickListener { presenter.onPrivacyLinkClick() } loginFormPass.setOnEditorActionListener { _, id, _ -> if (id == IME_ACTION_DONE || id == IME_NULL) loginFormSignIn.callOnClick() else false @@ -132,6 +132,10 @@ class LoginFormFragment : BaseFragment(), LoginFormView { } } + override fun showPrivacyPolicy() { + loginFormPrivacyLink.visibility = VISIBLE + } + override fun notifyParentAccountLogged(students: List) { (activity as? LoginActivity)?.onFormFragmentAccountLogged(students, Triple( loginFormName.text.toString(), @@ -140,6 +144,10 @@ class LoginFormFragment : BaseFragment(), LoginFormView { )) } + override fun openPrivacyPolicyPage() { + startActivity(Intent.parseUri("https://wulkanowy.github.io/polityka-prywatnosci.html", 0)) + } + override fun onDestroyView() { super.onDestroyView() presenter.onDetachView() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenter.kt index a0717649a..3ab47c29b 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenter.kt @@ -22,7 +22,8 @@ class LoginFormPresenter @Inject constructor( super.onAttachView(view) view.run { initView() - if (isDebug) showVersion() + if (isDebug) showVersion() else showPrivacyPolicy() + errorHandler.onBadCredentials = { setErrorPassIncorrect() showSoftKeyboard() @@ -31,6 +32,10 @@ class LoginFormPresenter @Inject constructor( } } + fun onPrivacyLinkClick() { + view?.openPrivacyPolicyPage() + } + fun onHostSelected() { view?.apply { clearPassError() @@ -47,7 +52,7 @@ class LoginFormPresenter @Inject constructor( view?.clearNameError() } - fun attemptLogin() { + fun onSignInClick() { val email = view?.formNameValue.orEmpty() val password = view?.formPassValue.orEmpty() val endpoint = view?.formHostValue.orEmpty() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormView.kt index 69672780d..80a7b5e9c 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormView.kt @@ -37,5 +37,9 @@ interface LoginFormView : BaseView { fun showVersion() + fun showPrivacyPolicy() + fun notifyParentAccountLogged(students: List) + + fun openPrivacyPolicyPage() } diff --git a/app/src/main/res/layout/fragment_login_form.xml b/app/src/main/res/layout/fragment_login_form.xml index 7a82eaf7a..197db570f 100644 --- a/app/src/main/res/layout/fragment_login_form.xml +++ b/app/src/main/res/layout/fragment_login_form.xml @@ -39,7 +39,6 @@ app:fontFamily="sans-serif-light" app:layout_constraintBottom_toTopOf="@+id/loginFormNameLayout" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_chainStyle="packed" /> @@ -59,7 +58,6 @@ app:errorEnabled="true" app:layout_constraintBottom_toTopOf="@+id/loginFormPassLayout" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/loginFormHeader"> @@ -87,7 +85,6 @@ app:errorEnabled="true" app:layout_constraintBottom_toTopOf="@+id/loginFormHostLayout" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/loginFormNameLayout" app:passwordToggleEnabled="true"> @@ -117,7 +114,6 @@ android:orientation="vertical" app:layout_constraintBottom_toTopOf="@+id/loginFormSignIn" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/loginFormPassLayout"> @@ -145,15 +141,6 @@ app:layout_constraintHorizontal_bias="0.0" app:layout_constraintStart_toStartOf="@+id/loginFormHostLayout" /> - - + app:layout_constraintTop_toBottomOf="@+id/loginFormHostLayout" /> + + diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index a5b8b66a4..04a4a2ea4 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -26,7 +26,6 @@ Hasło Dziennik Symbol - Polityka prywatności Zaloguj To hasło jest za krótkie Dane logowania są niepoprawne @@ -34,6 +33,7 @@ To pole jest wymagane Ten student jest już zalogowany Symbol znajduje się na stronie dziennika w zakładce Dostęp Mobilny + Polityka prywatności diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a4089a460..69481ecf4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -26,7 +26,6 @@ Password Register Symbol - Privacy policy Sign in This password is too short Login details are incorrect @@ -34,6 +33,7 @@ This field is required This student has already been logged in The symbol is located on the register page in the Mobile Access tab + Privacy policy diff --git a/app/src/test/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenterTest.kt b/app/src/test/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenterTest.kt index 83047e02b..b643a5501 100644 --- a/app/src/test/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenterTest.kt +++ b/app/src/test/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenterTest.kt @@ -53,7 +53,7 @@ class LoginFormPresenterTest { `when`(loginFormView.formNameValue).thenReturn("") `when`(loginFormView.formPassValue).thenReturn("test123") `when`(loginFormView.formHostValue).thenReturn("https://fakelog.cf") - presenter.attemptLogin() + presenter.onSignInClick() verify(loginFormView).setErrorNameRequired() verify(loginFormView, never()).setErrorPassRequired(false) @@ -65,7 +65,7 @@ class LoginFormPresenterTest { `when`(loginFormView.formNameValue).thenReturn("@") `when`(loginFormView.formPassValue).thenReturn("") `when`(loginFormView.formHostValue).thenReturn("https://fakelog.cf") - presenter.attemptLogin() + presenter.onSignInClick() verify(loginFormView, never()).setErrorNameRequired() verify(loginFormView).setErrorPassRequired(true) @@ -77,7 +77,7 @@ class LoginFormPresenterTest { `when`(loginFormView.formNameValue).thenReturn("@") `when`(loginFormView.formPassValue).thenReturn("123") `when`(loginFormView.formHostValue).thenReturn("https://fakelog.cf") - presenter.attemptLogin() + presenter.onSignInClick() verify(loginFormView, never()).setErrorNameRequired() verify(loginFormView, never()).setErrorPassRequired(true) @@ -93,7 +93,7 @@ class LoginFormPresenterTest { `when`(loginFormView.formNameValue).thenReturn("@") `when`(loginFormView.formPassValue).thenReturn("123456") `when`(loginFormView.formHostValue).thenReturn("https://fakelog.cf") - presenter.attemptLogin() + presenter.onSignInClick() verify(loginFormView).hideSoftKeyboard() verify(loginFormView).showProgress(true) @@ -109,7 +109,7 @@ class LoginFormPresenterTest { `when`(loginFormView.formNameValue).thenReturn("@") `when`(loginFormView.formPassValue).thenReturn("123456") `when`(loginFormView.formHostValue).thenReturn("https://fakelog.cf") - presenter.attemptLogin() + presenter.onSignInClick() verify(loginFormView).hideSoftKeyboard() verify(loginFormView).showProgress(true) @@ -125,8 +125,8 @@ class LoginFormPresenterTest { `when`(loginFormView.formNameValue).thenReturn("@") `when`(loginFormView.formPassValue).thenReturn("123456") `when`(loginFormView.formHostValue).thenReturn("https://fakelog.cf") - presenter.attemptLogin() - presenter.attemptLogin() + presenter.onSignInClick() + presenter.onSignInClick() verify(loginFormView, times(2)).hideSoftKeyboard() verify(loginFormView, times(2)).showProgress(true) @@ -143,7 +143,7 @@ class LoginFormPresenterTest { `when`(loginFormView.formNameValue).thenReturn("@") `when`(loginFormView.formPassValue).thenReturn("123456") `when`(loginFormView.formHostValue).thenReturn("https://fakelog.cf") - presenter.attemptLogin() + presenter.onSignInClick() verify(loginFormView).hideSoftKeyboard() verify(loginFormView).showProgress(true) From 034b99c7ab990ae81d781c692e3eacb41bc58a93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Borcz?= Date: Thu, 18 Apr 2019 00:32:43 +0200 Subject: [PATCH 07/78] Add counting of the full-year average to the summary of grades (#322) --- .../preferences/PreferencesRepository.kt | 6 +- .../ui/modules/grade/GradeAverageProvider.kt | 54 ++++++++ .../grade/details/GradeDetailsPresenter.kt | 61 +++++---- .../grade/summary/GradeSummaryPresenter.kt | 68 +++++----- app/src/main/res/values-pl/strings.xml | 4 +- .../main/res/values-pl/value_prefernces.xml | 5 + app/src/main/res/values/preferences_keys.xml | 1 + app/src/main/res/values/strings.xml | 4 +- app/src/main/res/values/value_prefernces.xml | 9 ++ app/src/main/res/xml/scheme_preferences.xml | 10 +- .../modules/grade/GradeAverageProviderTest.kt | 124 ++++++++++++++++++ 11 files changed, 275 insertions(+), 71 deletions(-) create mode 100644 app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProvider.kt create mode 100644 app/src/test/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProviderTest.kt diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/preferences/PreferencesRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/preferences/PreferencesRepository.kt index d51fc4957..b29e5143b 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/preferences/PreferencesRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/preferences/PreferencesRepository.kt @@ -17,6 +17,9 @@ class PreferencesRepository @Inject constructor( val isShowPresent: Boolean get() = sharedPref.getBoolean(context.getString(R.string.pref_key_attendance_present), true) + val gradeAverageMode: String + get() = sharedPref.getString(context.getString(R.string.pref_key_grade_average_mode), "only_one_semester") ?: "only_one_semester" + val isGradeExpandable: Boolean get() = !sharedPref.getBoolean(context.getString(R.string.pref_key_expand_grade), false) @@ -50,8 +53,7 @@ class PreferencesRepository @Inject constructor( get() = sharedPref.getString(context.getString(R.string.pref_key_grade_modifier_plus), "0.0")?.toDouble() ?: 0.0 val gradeMinusModifier: Double - get() = sharedPref.getString(context.getString(R.string.pref_key_grade_modifier_minus), "0.0")?.toDouble() - ?: 0.0 + get() = sharedPref.getString(context.getString(R.string.pref_key_grade_modifier_minus), "0.0")?.toDouble() ?: 0.0 val fillMessageContent: Boolean get() = sharedPref.getBoolean(context.getString(R.string.pref_key_fill_message_content), true) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProvider.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProvider.kt new file mode 100644 index 000000000..a76f3d5ac --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProvider.kt @@ -0,0 +1,54 @@ +package io.github.wulkanowy.ui.modules.grade + +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.repositories.grade.GradeRepository +import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository +import io.github.wulkanowy.utils.calcAverage +import io.github.wulkanowy.utils.changeModifier +import io.reactivex.Single +import javax.inject.Inject + +class GradeAverageProvider @Inject constructor( + private val preferencesRepository: PreferencesRepository, + private val gradeRepository: GradeRepository +) { + fun getGradeAverage(student: Student, semesters: List, selectedSemesterId: Int, forceRefresh: Boolean): Single> { + return when (preferencesRepository.gradeAverageMode) { + "all_year" -> getAllYearAverage(student, semesters, selectedSemesterId, forceRefresh) + "only_one_semester" -> getOnlyOneSemesterAverage(student, semesters, selectedSemesterId, forceRefresh) + else -> throw IllegalArgumentException("Incorrect grade average mode: ${preferencesRepository.gradeAverageMode} ") + } + } + + private fun getAllYearAverage(student: Student, semesters: List, semesterId: Int, forceRefresh: Boolean): Single> { + val selectedSemester = semesters.single { it.semesterId == semesterId } + val firstSemester = semesters.single { it.diaryId == selectedSemester.diaryId && it.semesterName == 1 } + val plusModifier = preferencesRepository.gradePlusModifier + val minusModifier = preferencesRepository.gradeMinusModifier + + return gradeRepository.getGrades(student, selectedSemester, forceRefresh) + .flatMap { firstGrades -> + if (selectedSemester == firstSemester) Single.just(firstGrades) + else gradeRepository.getGrades(student, firstSemester) + .map { secondGrades -> secondGrades + firstGrades } + }.map { grades -> + grades.map { it.changeModifier(plusModifier, minusModifier) } + .groupBy { it.subject } + .mapValues { it.value.calcAverage() } + } + } + + private fun getOnlyOneSemesterAverage(student: Student, semesters: List, semesterId: Int, forceRefresh: Boolean): Single> { + val selectedSemester = semesters.single { it.semesterId == semesterId } + val plusModifier = preferencesRepository.gradePlusModifier + val minusModifier = preferencesRepository.gradeMinusModifier + + return gradeRepository.getGrades(student, selectedSemester, forceRefresh) + .map { grades -> + grades.map { it.changeModifier(plusModifier, minusModifier) } + .groupBy { it.subject } + .mapValues { it.value.calcAverage() } + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt index 1be0b36f9..12e606873 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt @@ -8,10 +8,9 @@ import io.github.wulkanowy.data.repositories.semester.SemesterRepository import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.ui.base.session.BaseSessionPresenter import io.github.wulkanowy.ui.base.session.SessionErrorHandler +import io.github.wulkanowy.ui.modules.grade.GradeAverageProvider import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.SchedulersProvider -import io.github.wulkanowy.utils.calcAverage -import io.github.wulkanowy.utils.changeModifier import io.github.wulkanowy.utils.getBackgroundColor import timber.log.Timber import javax.inject.Inject @@ -23,6 +22,7 @@ class GradeDetailsPresenter @Inject constructor( private val studentRepository: StudentRepository, private val semesterRepository: SemesterRepository, private val preferencesRepository: PreferencesRepository, + private val averageProvider: GradeAverageProvider, private val analytics: FirebaseAnalyticsHelper ) : BaseSessionPresenter(errorHandler) { @@ -109,11 +109,16 @@ class GradeDetailsPresenter @Inject constructor( private fun loadData(semesterId: Int, forceRefresh: Boolean) { Timber.i("Loading grade details data started") disposable.add(studentRepository.getCurrentStudent() - .flatMap { semesterRepository.getSemesters(it).map { semester -> semester to it } } - .flatMap { gradeRepository.getGrades(it.second, it.first.first { item -> item.semesterId == semesterId }, forceRefresh) } - .map { it.sortedByDescending { grade -> grade.date } } - .map { it.map { item -> item.changeModifier(preferencesRepository.gradePlusModifier, preferencesRepository.gradeMinusModifier) } } - .map { createGradeItems(it.groupBy { grade -> grade.subject }.toSortedMap()) } + .flatMap { semesterRepository.getSemesters(it).map { semester -> it to semester } } + .flatMap { (student, semesters) -> + averageProvider.getGradeAverage(student, semesters, semesterId, forceRefresh) + .flatMap { averages -> + gradeRepository.getGrades(student, semesters.first { semester -> semester.semesterId == semesterId }) + .map { it.sortedByDescending { grade -> grade.date } } + .map { it.groupBy { grade -> grade.subject }.toSortedMap() } + .map { createGradeItems(it, averages) } + } + } .subscribeOn(schedulers.backgroundThread) .observeOn(schedulers.mainThread) .doFinally { @@ -139,32 +144,36 @@ class GradeDetailsPresenter @Inject constructor( }) } - private fun createGradeItems(items: Map>): List { + private fun createGradeItems(items: Map>, averages: Map): List { + val isGradeExpandable = preferencesRepository.isGradeExpandable + val gradeColorTheme = preferencesRepository.gradeColorTheme + + val noDescriptionString = view?.noDescriptionString.orEmpty() + val weightString = view?.weightString.orEmpty() + return items.map { - it.value.calcAverage().let { average -> - GradeDetailsHeader( - subject = it.key, - average = formatAverage(average), - number = view?.getGradeNumberString(it.value.size).orEmpty(), - newGrades = it.value.filter { grade -> !grade.isRead }.size, - isExpandable = preferencesRepository.isGradeExpandable - ).apply { - subItems = it.value.map { item -> - GradeDetailsItem( - grade = item, - valueBgColor = item.getBackgroundColor(preferencesRepository.gradeColorTheme), - weightString = view?.weightString.orEmpty(), - noDescriptionString = view?.noDescriptionString.orEmpty() - ) - } + GradeDetailsHeader( + subject = it.key, + average = formatAverage(averages[it.key]), + number = view?.getGradeNumberString(it.value.size).orEmpty(), + newGrades = it.value.filter { grade -> !grade.isRead }.size, + isExpandable = isGradeExpandable + ).apply { + subItems = it.value.map { item -> + GradeDetailsItem( + grade = item, + valueBgColor = item.getBackgroundColor(gradeColorTheme), + weightString = weightString, + noDescriptionString = noDescriptionString + ) } } } } - private fun formatAverage(average: Double): String { + private fun formatAverage(average: Double?): String { return view?.run { - if (average == 0.0) emptyAverageString + if (average == null || average == .0) emptyAverageString else averageString.format(average) }.orEmpty() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt index 5dbf75134..cbd4169d7 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt @@ -1,17 +1,15 @@ package io.github.wulkanowy.ui.modules.grade.summary import io.github.wulkanowy.data.db.entities.GradeSummary -import io.github.wulkanowy.data.repositories.grade.GradeRepository import io.github.wulkanowy.data.repositories.gradessummary.GradeSummaryRepository -import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository import io.github.wulkanowy.data.repositories.semester.SemesterRepository import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.ui.base.session.BaseSessionPresenter import io.github.wulkanowy.ui.base.session.SessionErrorHandler +import io.github.wulkanowy.ui.modules.grade.GradeAverageProvider import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.SchedulersProvider import io.github.wulkanowy.utils.calcAverage -import io.github.wulkanowy.utils.changeModifier import timber.log.Timber import java.lang.String.format import java.util.Locale.FRANCE @@ -20,10 +18,9 @@ import javax.inject.Inject class GradeSummaryPresenter @Inject constructor( private val errorHandler: SessionErrorHandler, private val gradeSummaryRepository: GradeSummaryRepository, - private val gradeRepository: GradeRepository, private val studentRepository: StudentRepository, private val semesterRepository: SemesterRepository, - private val preferencesRepository: PreferencesRepository, + private val averageProvider: GradeAverageProvider, private val schedulers: SchedulersProvider, private val analytics: FirebaseAnalyticsHelper ) : BaseSessionPresenter(errorHandler) { @@ -36,25 +33,12 @@ class GradeSummaryPresenter @Inject constructor( fun onParentViewLoadData(semesterId: Int, forceRefresh: Boolean) { Timber.i("Loading grade summary data started") disposable.add(studentRepository.getCurrentStudent() - .flatMap { semesterRepository.getSemesters(it).map { semester -> semester to it } } - .map { pair -> pair.first.first { it.semesterId == semesterId } to pair.second } - .flatMap { - gradeSummaryRepository.getGradesSummary(it.first, forceRefresh) + .flatMap { semesterRepository.getSemesters(it).map { semesters -> it to semesters } } + .flatMap { (student, semesters) -> + gradeSummaryRepository.getGradesSummary(semesters.first { it.semesterId == semesterId }, forceRefresh) .flatMap { gradesSummary -> - gradeRepository.getGrades(it.second, it.first, forceRefresh) - .map { grades -> - grades.map { item -> item.changeModifier(preferencesRepository.gradePlusModifier, preferencesRepository.gradeMinusModifier) } - .groupBy { grade -> grade.subject } - .mapValues { entry -> entry.value.calcAverage() } - .filterValues { value -> value != 0.0 } - .let { averages -> - createGradeSummaryItems(gradesSummary, averages) to - GradeSummaryScrollableHeader( - formatAverage(gradesSummary.calcAverage()), - formatAverage(averages.values.average()) - ) - } - } + averageProvider.getGradeAverage(student, semesters, semesterId, forceRefresh) + .map { averages -> createGradeSummaryItemsAndHeader(gradesSummary, averages) } } } .subscribeOn(schedulers.backgroundThread) @@ -66,14 +50,14 @@ class GradeSummaryPresenter @Inject constructor( enableSwipe(true) notifyParentDataLoaded(semesterId) } - }.subscribe({ + }.subscribe({ (gradeSummaryItems, gradeSummaryHeader) -> Timber.i("Loading grade summary result: Success") view?.run { - showEmpty(it.first.isEmpty()) - showContent(it.first.isNotEmpty()) - updateData(it.first, it.second) + showEmpty(gradeSummaryItems.isEmpty()) + showContent(gradeSummaryItems.isNotEmpty()) + updateData(gradeSummaryItems, gradeSummaryHeader) } - analytics.logEvent("load_grade_summary", "items" to it.first.size, "force_refresh" to forceRefresh) + analytics.logEvent("load_grade_summary", "items" to gradeSummaryItems.size, "force_refresh" to forceRefresh) }) { Timber.i("Loading grade summary result: An exception occurred") view?.run { showEmpty(isViewEmpty) } @@ -104,16 +88,24 @@ class GradeSummaryPresenter @Inject constructor( disposable.clear() } - private fun createGradeSummaryItems(gradesSummary: List, averages: Map) - : List { - return gradesSummary.filter { !checkEmpty(it, averages) }.map { it -> - GradeSummaryItem( - title = it.subject, - average = formatAverage(averages.getOrElse(it.subject) { 0.0 }, ""), - predictedGrade = it.predictedGrade, - finalGrade = it.finalGrade - ) - } + private fun createGradeSummaryItemsAndHeader(gradesSummary: List, averages: Map) + : Pair, GradeSummaryScrollableHeader> { + return averages.filterValues { value -> value != 0.0 } + .let { filteredAverages -> + gradesSummary.filter { !checkEmpty(it, filteredAverages) } + .map { + GradeSummaryItem( + title = it.subject, + average = formatAverage(filteredAverages.getOrElse(it.subject) { 0.0 }, ""), + predictedGrade = it.predictedGrade, + finalGrade = it.finalGrade + ) + }.let { + it to GradeSummaryScrollableHeader( + formatAverage(gradesSummary.calcAverage()), + formatAverage(filteredAverages.values.average())) + } + } } private fun checkEmpty(gradeSummary: GradeSummary, averages: Map): Boolean { diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 04a4a2ea4..aee503b82 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -239,11 +239,11 @@ Wygląd Domyślny widok - Pokazuj podsumowanie w ocenach + Obliczanie średniej końcoworocznej Pokazuj obecność we frekwencji Ciemny motyw (Beta) Rozwiń oceny - Schemat kolorów ocen + Schemat kolorów ocen Powiadomienia Pokazuj powiadomienia diff --git a/app/src/main/res/values-pl/value_prefernces.xml b/app/src/main/res/values-pl/value_prefernces.xml index 2231cc0b1..34a109091 100644 --- a/app/src/main/res/values-pl/value_prefernces.xml +++ b/app/src/main/res/values-pl/value_prefernces.xml @@ -30,4 +30,9 @@ Wulkanowy Kolory ocen w dzienniku + + + Średnia ocen z 2 semestru + Średnia ocen z całego roku + diff --git a/app/src/main/res/values/preferences_keys.xml b/app/src/main/res/values/preferences_keys.xml index 427cca56b..d03b4fbbb 100644 --- a/app/src/main/res/values/preferences_keys.xml +++ b/app/src/main/res/values/preferences_keys.xml @@ -5,6 +5,7 @@ theme grade_color_scheme expand_grade + grade_average_mode services_enable services_interval services_disable_wifi_only diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 69481ecf4..8dc6a25cb 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -224,11 +224,11 @@ Appearance Default view - Show the summary in the grades + Calculation of the end-of-year average Show presence in attendance Dark theme (Beta) Expand grades - Grades color scheme + Grades color scheme Notifications Show notifications diff --git a/app/src/main/res/values/value_prefernces.xml b/app/src/main/res/values/value_prefernces.xml index 661615e14..3921b4e2a 100644 --- a/app/src/main/res/values/value_prefernces.xml +++ b/app/src/main/res/values/value_prefernces.xml @@ -70,4 +70,13 @@ material grade_color + + + Average grades from the 2nd semester + Average of grades from the whole year + + + only_one_semester + all_year + diff --git a/app/src/main/res/xml/scheme_preferences.xml b/app/src/main/res/xml/scheme_preferences.xml index 08c241246..5c2a8ddae 100644 --- a/app/src/main/res/xml/scheme_preferences.xml +++ b/app/src/main/res/xml/scheme_preferences.xml @@ -36,7 +36,7 @@ android:entryValues="@array/grade_color_scheme_values" android:key="@string/pref_key_grade_color_scheme" android:summary="%s" - android:title="@string/pref_grade_color_scheme" + android:title="@string/pref_view_grade_color_scheme" app:iconSpaceReserved="false" /> + Date: Thu, 18 Apr 2019 12:18:58 +0200 Subject: [PATCH 08/78] Add a selection of multiple students to login (#318) --- app/build.gradle | 2 +- .../repositories/student/StudentLocalTest.kt | 2 +- app/src/main/AndroidManifest.xml | 1 + .../wulkanowy/data/db/dao/StudentDao.kt | 2 +- .../data/repositories/student/StudentLocal.kt | 4 +- .../repositories/student/StudentRepository.kt | 4 +- .../LoginStudentSelectFragment.kt | 10 +-- .../studentselect/LoginStudentSelectItem.kt | 16 ++++- .../LoginStudentSelectPresenter.kt | 33 ++++++---- .../studentselect/LoginStudentSelectView.kt | 2 +- .../main/res/layout/fragment_login_form.xml | 4 +- .../layout/fragment_login_student_select.xml | 61 +++++++++++++++++-- .../main/res/layout/fragment_login_symbol.xml | 8 +-- .../res/layout/item_login_student_select.xml | 50 +++++++-------- app/src/main/res/values-pl/strings.xml | 5 +- app/src/main/res/values/strings.xml | 5 +- .../LoginStudentSelectPresenterTest.kt | 6 +- 17 files changed, 141 insertions(+), 74 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 590370339..5780d02be 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -86,7 +86,7 @@ play { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" - implementation('com.github.wulkanowy:api:fe4ffeb') { exclude module: "threetenbp" } + implementation 'com.github.wulkanowy:api:3335bd6' implementation "androidx.legacy:legacy-support-v4:1.0.0" implementation "androidx.appcompat:appcompat:1.0.2" diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/student/StudentLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/student/StudentLocalTest.kt index b27446faf..cecd80992 100644 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/student/StudentLocalTest.kt +++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/student/StudentLocalTest.kt @@ -39,7 +39,7 @@ class StudentLocalTest { @Test fun saveAndReadTest() { - studentLocal.saveStudent(Student(email = "test", password = "test123", schoolSymbol = "23", endpoint = "fakelog.cf", loginType = "AUTO", isCurrent = true, studentName = "", schoolName = "", studentId = 0, classId = 1, symbol = "", registrationDate = now(), className = "")) + studentLocal.saveStudents(listOf(Student(email = "test", password = "test123", schoolSymbol = "23", endpoint = "fakelog.cf", loginType = "AUTO", isCurrent = true, studentName = "", schoolName = "", studentId = 0, classId = 1, symbol = "", registrationDate = now(), className = ""))) .blockingGet() val student = studentLocal.getCurrentStudent(true).blockingGet() diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index f0dd9cb40..5b2819263 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -34,6 +34,7 @@ android:name=".ui.modules.login.LoginActivity" android:configChanges="orientation|screenSize" android:label="@string/login_title" + android:theme="@style/WulkanowyTheme.NoActionBar" android:windowSoftInputMode="adjustResize" /> ): List @Delete fun delete(student: Student) diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentLocal.kt index 7bbd283fb..e6d744213 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentLocal.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentLocal.kt @@ -17,8 +17,8 @@ class StudentLocal @Inject constructor( private val context: Context ) { - fun saveStudent(student: Student): Single { - return Single.fromCallable { studentDb.insert(student.copy(password = encrypt(student.password, context))) } + fun saveStudents(students: List): Single> { + return Single.fromCallable { studentDb.insertAll(students.map { it.copy(password = encrypt(it.password, context)) }) } } fun getStudents(decryptPass: Boolean): Maybe> { diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentRepository.kt index b4b7c8289..e0ab6bf67 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentRepository.kt @@ -41,8 +41,8 @@ class StudentRepository @Inject constructor( .toSingle() } - fun saveStudent(student: Student): Single { - return local.saveStudent(student) + fun saveStudents(students: List): Single> { + return local.saveStudents(students) } fun switchStudent(student: Student): Completable { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt index 2bcb6b4e9..9f3217759 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt @@ -8,7 +8,6 @@ import android.view.View import android.view.View.GONE import android.view.View.VISIBLE import android.view.ViewGroup -import androidx.appcompat.app.AppCompatActivity import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager import eu.davidea.flexibleadapter.items.AbstractFlexibleItem @@ -45,6 +44,7 @@ class LoginStudentSelectFragment : BaseFragment(), LoginStudentSelectView { } override fun initView() { + loginStudentSelectSignIn.setOnClickListener { presenter.onSignIn() } loginAdapter.apply { setOnItemClickListener { presenter.onItemSelected(it) } } loginStudentSelectRecycler.apply { @@ -54,7 +54,7 @@ class LoginStudentSelectFragment : BaseFragment(), LoginStudentSelectView { } override fun updateData(data: List) { - loginAdapter.updateDataSet(data, true) + loginAdapter.updateDataSet(data) } override fun openMainView() { @@ -69,11 +69,11 @@ class LoginStudentSelectFragment : BaseFragment(), LoginStudentSelectView { } override fun showContent(show: Boolean) { - loginStudentSelectRecycler.visibility = if (show) VISIBLE else GONE + loginStudentSelectContent.visibility = if (show) VISIBLE else GONE } - override fun showActionBar(show: Boolean) { - (activity as? AppCompatActivity)?.supportActionBar?.run { if (show) show() else hide() } + override fun enableSignIn(enable: Boolean) { + loginStudentSelectSignIn.isEnabled = enable } fun onParentInitStudentSelectFragment(students: List) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectItem.kt index 65940a980..27723c538 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectItem.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectItem.kt @@ -13,15 +13,15 @@ import kotlinx.android.synthetic.main.item_login_student_select.* class LoginStudentSelectItem(val student: Student) : AbstractFlexibleItem() { - override fun getLayoutRes(): Int = R.layout.item_login_student_select + override fun getLayoutRes() = R.layout.item_login_student_select override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ItemViewHolder { return ItemViewHolder(view, adapter) } @SuppressLint("SetTextI18n") - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ItemViewHolder, position: Int, payloads: MutableList?) { - holder.run { + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ItemViewHolder, position: Int, payloads: MutableList) { + holder.apply { loginItemName.text = "${student.studentName} ${student.className}" loginItemSchool.text = student.schoolName } @@ -43,7 +43,17 @@ class LoginStudentSelectItem(val student: Student) : AbstractFlexibleItem) : FlexibleViewHolder(view, adapter), LayoutContainer { + override val containerView: View get() = itemView + + init { + loginItemCheck.setOnClickListener { super.onClick(loginItemContainer) } + } + + override fun onClick(view: View?) { + super.onClick(view) + loginItemCheck.apply { isChecked = !isChecked } + } } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenter.kt index 6c2acb3bd..21dde7b86 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenter.kt @@ -8,7 +8,6 @@ import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.modules.login.LoginErrorHandler import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.SchedulersProvider -import io.reactivex.Single import timber.log.Timber import java.io.Serializable import javax.inject.Inject @@ -22,10 +21,13 @@ class LoginStudentSelectPresenter @Inject constructor( var students = emptyList() + var selectedStudents = mutableListOf() + fun onAttachView(view: LoginStudentSelectView, students: Serializable?) { super.onAttachView(view) view.run { initView() + enableSignIn(false) errorHandler.onStudentDuplicate = { showMessage(it) Timber.i("The student already registered in the app was selected") @@ -37,13 +39,21 @@ class LoginStudentSelectPresenter @Inject constructor( } } + fun onSignIn() { + registerStudents(selectedStudents) + } + fun onParentInitStudentSelectView(students: List) { loadData(students) + if (students.size == 1) registerStudents(students) } fun onItemSelected(item: AbstractFlexibleItem<*>?) { if (item is LoginStudentSelectItem) { - registerStudent(item.student) + selectedStudents.removeAll { it == item.student } + .let { if (!it) selectedStudents.add(item.student) } + + view?.enableSignIn(selectedStudents.isNotEmpty()) } } @@ -54,33 +64,30 @@ class LoginStudentSelectPresenter @Inject constructor( } } - private fun registerStudent(student: Student) { - disposable.add(studentRepository.saveStudent(student) - .map { student.apply { id = it } } - .onErrorResumeNext { studentRepository.logoutStudent(student).andThen(Single.error(it)) } - .flatMapCompletable { studentRepository.switchStudent(student) } + private fun registerStudents(students: List) { + disposable.add(studentRepository.saveStudents(students) + .map { students.first().apply { id = it.first() } } + .flatMapCompletable { studentRepository.switchStudent(it) } .subscribeOn(schedulers.backgroundThread) .observeOn(schedulers.mainThread) .doOnSubscribe { view?.apply { showProgress(true) showContent(false) - showActionBar(false) } Timber.i("Registration started") } .subscribe({ - analytics.logEvent("registration_student_select", SUCCESS to true, "endpoint" to student.endpoint, "symbol" to student.symbol, "error" to "No error") + students.forEach { analytics.logEvent("registration_student_select", SUCCESS to true, "endpoint" to it.endpoint, "symbol" to it.symbol, "error" to "No error") } Timber.i("Registration result: Success") view?.openMainView() - }, { - analytics.logEvent("registration_student_select", SUCCESS to false, "endpoint" to student.endpoint, "symbol" to student.symbol, "error" to it.localizedMessage) + }, { error -> + students.forEach { analytics.logEvent("registration_student_select", SUCCESS to false, "endpoint" to it.endpoint, "symbol" to it.symbol, "error" to error.localizedMessage) } Timber.i("Registration result: An exception occurred ") - errorHandler.dispatch(it) + errorHandler.dispatch(error) view?.apply { showProgress(false) showContent(true) - showActionBar(true) } })) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectView.kt index 824f421fe..3967313c3 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectView.kt @@ -14,5 +14,5 @@ interface LoginStudentSelectView : BaseView { fun showContent(show: Boolean) - fun showActionBar(show: Boolean) + fun enableSignIn(enable: Boolean) } diff --git a/app/src/main/res/layout/fragment_login_form.xml b/app/src/main/res/layout/fragment_login_form.xml index 197db570f..34f1f9959 100644 --- a/app/src/main/res/layout/fragment_login_form.xml +++ b/app/src/main/res/layout/fragment_login_form.xml @@ -30,7 +30,7 @@ android:layout_height="wrap_content" android:layout_marginStart="32dp" android:layout_marginLeft="32dp" - android:layout_marginTop="48dp" + android:layout_marginTop="32dp" android:layout_marginEnd="32dp" android:layout_marginRight="32dp" android:gravity="center_horizontal" @@ -150,7 +150,7 @@ android:layout_marginTop="48dp" android:layout_marginEnd="24dp" android:layout_marginRight="24dp" - android:layout_marginBottom="48dp" + android:layout_marginBottom="16dp" android:text="@string/login_sign_in" android:textAppearance="?android:textAppearanceSmall" android:textColor="@android:color/white" diff --git a/app/src/main/res/layout/fragment_login_student_select.xml b/app/src/main/res/layout/fragment_login_student_select.xml index 72af7293a..a94978bbf 100644 --- a/app/src/main/res/layout/fragment_login_student_select.xml +++ b/app/src/main/res/layout/fragment_login_student_select.xml @@ -1,4 +1,5 @@ - + android:layout_height="match_parent"> + + + + + + + diff --git a/app/src/main/res/layout/fragment_login_symbol.xml b/app/src/main/res/layout/fragment_login_symbol.xml index 6cdf7ef03..6c820889f 100644 --- a/app/src/main/res/layout/fragment_login_symbol.xml +++ b/app/src/main/res/layout/fragment_login_symbol.xml @@ -29,7 +29,7 @@ android:layout_height="wrap_content" android:layout_marginStart="32dp" android:layout_marginLeft="32dp" - android:layout_marginTop="48dp" + android:layout_marginTop="32dp" android:layout_marginEnd="32dp" android:layout_marginRight="32dp" android:gravity="center_horizontal" @@ -79,11 +79,10 @@ style="@style/Widget.MaterialComponents.Button" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_gravity="center_vertical" android:layout_marginTop="48dp" android:layout_marginEnd="24dp" android:layout_marginRight="24dp" - android:layout_marginBottom="48dp" + android:layout_marginBottom="16dp" android:text="@string/login_sign_in" android:textAppearance="?android:textAppearanceSmall" android:textColor="@android:color/white" @@ -91,8 +90,7 @@ app:backgroundTint="@color/colorPrimary" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toBottomOf="@+id/loginSymbolNameLayout" - app:layout_constraintVertical_bias="1" /> + app:layout_constraintTop_toBottomOf="@+id/loginSymbolNameLayout" /> diff --git a/app/src/main/res/layout/item_login_student_select.xml b/app/src/main/res/layout/item_login_student_select.xml index f13eb6010..eb74cebbe 100644 --- a/app/src/main/res/layout/item_login_student_select.xml +++ b/app/src/main/res/layout/item_login_student_select.xml @@ -1,46 +1,42 @@ - + android:background="?selectableItemBackground" + android:orientation="horizontal"> - + + app:firstBaselineToTopHeight="32dp" + tools:text="Jan Kowalski" /> - + app:firstBaselineToTopHeight="20dp" + tools:text="Szkoła Wulkanowego nr 1" /> diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index aee503b82..b2689133e 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -3,7 +3,7 @@ - Wybierz ucznia + Logowanie Wulkanowy Oceny Frekwencja @@ -31,8 +31,9 @@ Dane logowania są niepoprawne Nie znaleziono ucznia. Sprawdź symbol To pole jest wymagane - Ten student jest już zalogowany + Wybrany uczeń jest już zalogowany Symbol znajduje się na stronie dziennika w zakładce Dostęp Mobilny + Wybierz uczniów do zalogowania w aplikacji Polityka prywatności diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8dc6a25cb..6195c36cb 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -3,7 +3,7 @@ - Select student + Login Wulkanowy Grades Attendance @@ -31,8 +31,9 @@ Login details are incorrect Student not found. Check the symbol This field is required - This student has already been logged in + The selected student is already logged in The symbol is located on the register page in the Mobile Access tab + Select students to log in to the application Privacy policy diff --git a/app/src/test/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenterTest.kt b/app/src/test/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenterTest.kt index 192602b78..17c16f415 100644 --- a/app/src/test/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenterTest.kt +++ b/app/src/test/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenterTest.kt @@ -51,9 +51,10 @@ class LoginStudentSelectPresenterTest { @Test fun onSelectedStudentTest() { - doReturn(Single.just(1L)).`when`(studentRepository).saveStudent(testStudent) + doReturn(Single.just(listOf(1L))).`when`(studentRepository).saveStudents(listOf(testStudent)) doReturn(Completable.complete()).`when`(studentRepository).switchStudent(testStudent) presenter.onItemSelected(LoginStudentSelectItem(testStudent)) + presenter.onSignIn() verify(loginStudentSelectView).showContent(false) verify(loginStudentSelectView).showProgress(true) verify(loginStudentSelectView).openMainView() @@ -61,9 +62,10 @@ class LoginStudentSelectPresenterTest { @Test fun onSelectedStudentErrorTest() { - doReturn(Single.error(testException)).`when`(studentRepository).saveStudent(testStudent) + doReturn(Single.error(testException)).`when`(studentRepository).saveStudents(listOf(testStudent)) doReturn(Completable.complete()).`when`(studentRepository).logoutStudent(testStudent) presenter.onItemSelected(LoginStudentSelectItem(testStudent)) + presenter.onSignIn() verify(loginStudentSelectView).showContent(false) verify(loginStudentSelectView).showProgress(true) verify(errorHandler).dispatch(testException) From 269af4b7ba47b6e1541cd6762b6732b064a1012c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Borcz?= Date: Thu, 18 Apr 2019 16:38:49 +0200 Subject: [PATCH 09/78] Update dependencies (#323) --- .idea/codeStyles/Project.xml | 25 ----------- app/build.gradle | 43 ++++++++++--------- .../java/io/github/wulkanowy/WulkanowyApp.kt | 5 +-- .../io/github/wulkanowy/di/AppComponent.kt | 4 +- build.gradle | 8 ++-- gradle/wrapper/gradle-wrapper.properties | 4 +- 6 files changed, 32 insertions(+), 57 deletions(-) diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index a8407c848..43e9b544a 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -21,31 +21,6 @@