add login and board_list #26
@ -57,9 +57,10 @@ dependencies {
|
||||
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
|
||||
|
||||
|
||||
|
||||
// TESTING
|
||||
testImplementation 'junit:junit:4.12'
|
||||
testImplementation "io.mockk:mockk:1.9.3"
|
||||
testImplementation "com.squareup.okhttp3:mockwebserver:4.2.1"
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
||||
|
||||
|
@ -62,15 +62,29 @@ class BoardDaoTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun replaceOnConflict() {
|
||||
val board0 = Board("id", "title0")
|
||||
val board1 = Board("id", "title1")
|
||||
|
||||
runBlocking {
|
||||
dao.insert(board0)
|
||||
dao.insert(board1)
|
||||
}
|
||||
|
||||
dao.getAllBoard().observeOnce {
|
||||
assertEquals(1, it.size)
|
||||
assertEquals("title1", it[0].title)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getInAscendingOrder() {
|
||||
val board0 = Board("id0", "title0")
|
||||
val board1 = Board("id1", "title1")
|
||||
|
||||
runBlocking {
|
||||
dao.insert(board1)
|
||||
}
|
||||
|
||||
runBlocking {
|
||||
dao.insert(board0)
|
||||
}
|
||||
|
||||
@ -81,14 +95,12 @@ class BoardDaoTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun delete() {
|
||||
val board = Board("id", "title")
|
||||
|
||||
runBlocking {
|
||||
dao.insert(board)
|
||||
}
|
||||
|
||||
runBlocking {
|
||||
dao.delete(board)
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="it.unisannio.ding.ids.wedroid.app">
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
@ -11,12 +12,16 @@
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
|
||||
<activity android:name=".view.LoginActivity"></activity>
|
||||
<activity android:name=".view.LoginActivity" />
|
||||
|
||||
<activity
|
||||
android:name=".view.NewBoardActivity"
|
||||
android:label="@string/title_activity_new_board"
|
||||
android:theme="@style/AppTheme.NoActionBar" />
|
||||
|
||||
<activity
|
||||
android:name=".view.BoardsListsActivity"
|
||||
android:theme="@style/AppTheme.NoActionBar">
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
|
@ -7,6 +7,7 @@ import it.unisannio.ding.ids.wedroid.app.data.entity.convert
|
||||
import it.unisannio.ding.ids.wedroid.app.util.PreferenceReader
|
||||
import it.unisannio.ding.ids.wedroid.wrapper.api.BoardService
|
||||
import it.unisannio.ding.ids.wedroid.wrapper.entity.BoardBackgroundColor
|
||||
import it.unisannio.ding.ids.wedroid.wrapper.entity.BoardPermission
|
||||
import it.unisannio.ding.ids.wedroid.wrapper.entity.BoardPrototype
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@ -28,12 +29,52 @@ class BoardRepository(
|
||||
synchronize()
|
||||
}
|
||||
|
||||
fun insertBoard(title: String) {
|
||||
fun synchronize() {
|
||||
service.getBoardsFromUser(reader.userId)
|
||||
.enqueue(object :
|
||||
Callback<MutableList<it.unisannio.ding.ids.wedroid.wrapper.entity.Board>> {
|
||||
override fun onFailure(
|
||||
call: Call<MutableList<it.unisannio.ding.ids.wedroid.wrapper.entity.Board>>,
|
||||
t: Throwable
|
||||
) = logNetworkError(t.message)
|
||||
|
||||
override fun onResponse(
|
||||
call: Call<MutableList<it.unisannio.ding.ids.wedroid.wrapper.entity.Board>>,
|
||||
response: Response<MutableList<it.unisannio.ding.ids.wedroid.wrapper.entity.Board>>
|
||||
) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
synchronizeCallback(response)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private suspend fun synchronizeCallback(
|
||||
response: Response<MutableList<it.unisannio.ding.ids.wedroid.wrapper.entity.Board>>
|
||||
) {
|
||||
if (!response.isSuccessful) {
|
||||
logNetworkError("${response.code()} ${response.message()}")
|
||||
return
|
||||
}
|
||||
|
||||
// read boards from the body
|
||||
val boards = (response.body() ?: return)
|
||||
.map { it.convert() }
|
||||
|
||||
addNewBoardToDb(boards)
|
||||
|
||||
removeOldBoardsFromDb(boards)
|
||||
}
|
||||
|
||||
fun insertBoard(title: String, isPrivate: Boolean, color: BoardBackgroundColor) {
|
||||
val permission = if (isPrivate) BoardPermission.PRIVATE else BoardPermission.PUBLIC
|
||||
|
||||
service.newBoard(
|
||||
BoardPrototype.Builder()
|
||||
.setOwner(reader.userId)
|
||||
.setTitle(title)
|
||||
.setBackgroundColor(BoardBackgroundColor.LIMEGREEN)
|
||||
.setBackgroundColor(color)
|
||||
.setBoardPermission(permission)
|
||||
.build()
|
||||
).enqueue(object : Callback<it.unisannio.ding.ids.wedroid.wrapper.entity.Board> {
|
||||
override fun onFailure(
|
||||
@ -45,53 +86,30 @@ class BoardRepository(
|
||||
call: Call<it.unisannio.ding.ids.wedroid.wrapper.entity.Board>,
|
||||
response: Response<it.unisannio.ding.ids.wedroid.wrapper.entity.Board>
|
||||
) {
|
||||
if (!response.isSuccessful) {
|
||||
logNetworkError("${response.code()} ${response.message()}")
|
||||
return
|
||||
}
|
||||
|
||||
val board = response.body()
|
||||
|
||||
if (board == null) {
|
||||
logNetworkError("empty body")
|
||||
return
|
||||
}
|
||||
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
dao.insert(Board(board.id, title))
|
||||
insertBoardCallback(response, title)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun synchronize() {
|
||||
service.getBoardsFromUser(reader.userId)
|
||||
.enqueue(object :
|
||||
Callback<MutableList<it.unisannio.ding.ids.wedroid.wrapper.entity.Board>> {
|
||||
private suspend fun insertBoardCallback(
|
||||
response: Response<it.unisannio.ding.ids.wedroid.wrapper.entity.Board>,
|
||||
title: String
|
||||
) {
|
||||
if (!response.isSuccessful) {
|
||||
logNetworkError("${response.code()} ${response.message()}")
|
||||
return
|
||||
}
|
||||
|
||||
override fun onFailure(
|
||||
call: Call<MutableList<it.unisannio.ding.ids.wedroid.wrapper.entity.Board>>,
|
||||
t: Throwable
|
||||
) = logNetworkError(t.message)
|
||||
val board = response.body()
|
||||
|
||||
override fun onResponse(
|
||||
call: Call<MutableList<it.unisannio.ding.ids.wedroid.wrapper.entity.Board>>,
|
||||
response: Response<MutableList<it.unisannio.ding.ids.wedroid.wrapper.entity.Board>>
|
||||
) {
|
||||
if (!response.isSuccessful) {
|
||||
logNetworkError("${response.code()} ${response.message()}")
|
||||
return
|
||||
}
|
||||
if (board == null) {
|
||||
logNetworkError("empty body")
|
||||
return
|
||||
}
|
||||
|
||||
// read boards from the body
|
||||
val boards = (response.body() ?: return)
|
||||
.map { it.convert() }
|
||||
|
||||
addNewBoardToDb(boards)
|
||||
|
||||
removeOldBoardsFromDb(boards)
|
||||
}
|
||||
})
|
||||
dao.insert(Board(board.id, title))
|
||||
}
|
||||
|
||||
fun deleteBoard(id: String) {
|
||||
@ -101,33 +119,39 @@ class BoardRepository(
|
||||
logNetworkError(t.message)
|
||||
}
|
||||
|
||||
override fun onResponse(call: Call<Void>, response: Response<Void>) {
|
||||
if (!response.isSuccessful) {
|
||||
logNetworkError("${response.code()}, ${response.message()}")
|
||||
return
|
||||
}
|
||||
|
||||
override fun onResponse(
|
||||
call: Call<Void>,
|
||||
response: Response<Void>
|
||||
) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
dao.delete(Board(id))
|
||||
deleteBoardCallback(response, id)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun addNewBoardToDb(boards: Collection<Board>) {
|
||||
private suspend fun deleteBoardCallback(
|
||||
response: Response<Void>,
|
||||
id: String
|
||||
) {
|
||||
if (!response.isSuccessful) {
|
||||
logNetworkError("${response.code()} ${response.message()}")
|
||||
return
|
||||
}
|
||||
|
||||
dao.delete(Board(id))
|
||||
}
|
||||
|
||||
private suspend fun addNewBoardToDb(boards: Collection<Board>) {
|
||||
boards.forEach {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
dao.insert(it)
|
||||
}
|
||||
dao.insert(it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeOldBoardsFromDb(boards: Collection<Board>) {
|
||||
private suspend fun removeOldBoardsFromDb(boards: Collection<Board>) {
|
||||
allBoards.value?.minus(boards)
|
||||
?.forEach {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
dao.delete(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@ package it.unisannio.ding.ids.wedroid.app.view
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
@ -14,6 +14,7 @@ import it.unisannio.ding.ids.wedroid.app.util.PreferenceReader
|
||||
import it.unisannio.ding.ids.wedroid.app.util.SharedPreferenceHelper
|
||||
import it.unisannio.ding.ids.wedroid.app.view.adapter.BoardsListAdapter
|
||||
import it.unisannio.ding.ids.wedroid.app.viewmodel.BoardsListViewModel
|
||||
import it.unisannio.ding.ids.wedroid.wrapper.entity.BoardBackgroundColor
|
||||
|
||||
import kotlinx.android.synthetic.main.activity_boards_lists.*
|
||||
import kotlinx.android.synthetic.main.content_boards_lists.*
|
||||
@ -25,12 +26,12 @@ class BoardsListsActivity : AppCompatActivity() {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_boards_lists)
|
||||
setSupportActionBar(toolbar)
|
||||
|
||||
val reader: PreferenceReader = SharedPreferenceHelper(this)
|
||||
|
||||
if (reader.token == "")
|
||||
startActivityForResult(
|
||||
Intent(this, LoginActivity::class.java),
|
||||
LoginActivity.LOGIN_REQUEST_CODE
|
||||
LOGIN_CODE
|
||||
)
|
||||
else initializeUi()
|
||||
}
|
||||
@ -42,19 +43,21 @@ class BoardsListsActivity : AppCompatActivity() {
|
||||
boardList.adapter = adapter
|
||||
boardList.layoutManager = LinearLayoutManager(this)
|
||||
|
||||
swipeLeftToDelete()
|
||||
|
||||
pullToRefresh.setColorSchemeColors(getColor(R.color.colorAccent))
|
||||
|
||||
viewModel.allBoards.observe(this, Observer {
|
||||
it.let { adapter.setBoards(it) }
|
||||
pullToRefresh.isRefreshing = false
|
||||
})
|
||||
|
||||
swipeLeftToDelete()
|
||||
|
||||
fab.setOnClickListener {
|
||||
viewModel.insertBoard("New board")
|
||||
startActivityForResult(
|
||||
Intent(this, NewBoardActivity::class.java),
|
||||
NEW_BOARD_CODE
|
||||
)
|
||||
}
|
||||
|
||||
pullToRefresh.setColorSchemeColors(getColor(R.color.colorAccent))
|
||||
pullToRefresh.setOnRefreshListener {
|
||||
viewModel.refresh()
|
||||
}
|
||||
@ -85,15 +88,42 @@ class BoardsListsActivity : AppCompatActivity() {
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
when (requestCode) {
|
||||
LoginActivity.LOGIN_REQUEST_CODE -> {
|
||||
when (resultCode) {
|
||||
LoginActivity.LOGIN_ERROR -> finish()
|
||||
LoginActivity.LOGIN_OK -> initializeUi()
|
||||
else -> finish()
|
||||
}
|
||||
}
|
||||
LOGIN_CODE -> onLoginResult(resultCode)
|
||||
NEW_BOARD_CODE -> if (data != null) onAddBoardResult(resultCode, data)
|
||||
else -> finish()
|
||||
}
|
||||
}
|
||||
|
||||
private fun onLoginResult(resultCode: Int) {
|
||||
when (resultCode) {
|
||||
LoginActivity.LOGIN_OK -> initializeUi()
|
||||
LoginActivity.LOGIN_ERROR -> finish()
|
||||
else -> finish()
|
||||
}
|
||||
}
|
||||
|
||||
private fun onAddBoardResult(resultCode: Int, data: Intent) {
|
||||
if (resultCode != NewBoardActivity.RESULT_OK) {
|
||||
Toast.makeText(this, R.string.on_add_new_board_error, Toast.LENGTH_LONG)
|
||||
.show()
|
||||
return
|
||||
}
|
||||
|
||||
val title = data.getStringExtra(NewBoardActivity.BOARD_NAME)
|
||||
if (title == null) {
|
||||
Toast.makeText(this, R.string.on_null_new_board_name, Toast.LENGTH_LONG)
|
||||
.show()
|
||||
return
|
||||
}
|
||||
|
||||
val isPrivate = data.getBooleanExtra(NewBoardActivity.BOARD_PRIVATE, true)
|
||||
|
||||
viewModel.insertBoard(title, isPrivate, BoardBackgroundColor.LIMEGREEN) //TODO
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val NEW_BOARD_CODE = 17
|
||||
const val LOGIN_CODE = 19
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,6 @@ package it.unisannio.ding.ids.wedroid.app.view
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import it.unisannio.ding.ids.wedroid.app.R
|
||||
@ -88,7 +87,6 @@ class LoginActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val LOGIN_REQUEST_CODE = 13
|
||||
const val LOGIN_OK = 0
|
||||
const val LOGIN_ERROR = 1
|
||||
}
|
||||
|
@ -0,0 +1,45 @@
|
||||
package it.unisannio.ding.ids.wedroid.app.view;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
|
||||
import android.view.View;
|
||||
import android.widget.EditText;
|
||||
import android.widget.Switch;
|
||||
|
||||
import it.unisannio.ding.ids.wedroid.app.R;
|
||||
|
||||
public class NewBoardActivity extends AppCompatActivity {
|
||||
private EditText boardName;
|
||||
private Switch isPrivate;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_new_board);
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
|
||||
boardName = findViewById(R.id.newBoardName);
|
||||
isPrivate = findViewById(R.id.newBoardPermission);
|
||||
}
|
||||
|
||||
public void onDone(View v) {
|
||||
Intent data = new Intent();
|
||||
data.putExtra(BOARD_NAME, boardName.getText().toString());
|
||||
data.putExtra(BOARD_PRIVATE, isPrivate.isChecked());
|
||||
setResult(RESULT_OK, data);
|
||||
finish();
|
||||
}
|
||||
|
||||
public void onCancel(View v) {
|
||||
finish();
|
||||
}
|
||||
|
||||
public static final int RESULT_OK = 17;
|
||||
public static final String BOARD_NAME = "BOARD_NAME";
|
||||
public static final String BOARD_PRIVATE = "BOARD_PRIVATE";
|
||||
}
|
@ -3,7 +3,6 @@ package it.unisannio.ding.ids.wedroid.app.viewmodel;
|
||||
import android.app.Application;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.app.SharedElementCallback;
|
||||
import androidx.lifecycle.AndroidViewModel;
|
||||
import androidx.lifecycle.LiveData;
|
||||
|
||||
@ -15,6 +14,7 @@ import it.unisannio.ding.ids.wedroid.app.data.repository.BoardRepository;
|
||||
import it.unisannio.ding.ids.wedroid.app.util.PreferenceReader;
|
||||
import it.unisannio.ding.ids.wedroid.app.util.ServicesFactory;
|
||||
import it.unisannio.ding.ids.wedroid.app.util.SharedPreferenceHelper;
|
||||
import it.unisannio.ding.ids.wedroid.wrapper.entity.BoardBackgroundColor;
|
||||
|
||||
public class BoardsListViewModel extends AndroidViewModel {
|
||||
private BoardRepository repository;
|
||||
@ -36,12 +36,15 @@ public class BoardsListViewModel extends AndroidViewModel {
|
||||
return allBoards;
|
||||
}
|
||||
|
||||
public void insertBoard(String title) {
|
||||
repository.insertBoard(title);
|
||||
public void insertBoard(String title, boolean isPrivate, BoardBackgroundColor color) {
|
||||
repository.insertBoard(title, isPrivate, color);
|
||||
}
|
||||
|
||||
public void deleteBoard(int position) {
|
||||
repository.deleteBoard(allBoards.getValue().get(position).getId());
|
||||
List<Board> boards = allBoards.getValue();
|
||||
|
||||
if (boards != null)
|
||||
repository.deleteBoard(boards.get(position).getId());
|
||||
}
|
||||
|
||||
public void refresh() {
|
||||
|
25
app/src/main/res/layout/activity_new_board.xml
Normal file
25
app/src/main/res/layout/activity_new_board.xml
Normal file
@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".view.NewBoardActivity">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:theme="@style/AppTheme.AppBarOverlay">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="?attr/colorPrimary"
|
||||
app:popupTheme="@style/AppTheme.PopupOverlay" />
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<include layout="@layout/content_new_board" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
62
app/src/main/res/layout/content_new_board.xml
Normal file
62
app/src/main/res/layout/content_new_board.xml
Normal file
@ -0,0 +1,62 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||
tools:context=".view.NewBoardActivity"
|
||||
tools:showIn="@layout/activity_new_board">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/newBoardName"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ems="10"
|
||||
android:hint="@string/new_board_name_field"
|
||||
android:inputType="textPersonName"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.25"
|
||||
android:importantForAutofill="no" />
|
||||
|
||||
<Switch
|
||||
android:id="@+id/newBoardPermission"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="true"
|
||||
android:text="@string/new_board_switch"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="@+id/newBoardName"
|
||||
app:layout_constraintStart_toStartOf="@+id/newBoardName"
|
||||
app:layout_constraintTop_toBottomOf="@+id/newBoardName"
|
||||
app:layout_constraintVertical_bias="0.1" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/newBoardDone"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="onDone"
|
||||
android:text="@string/new_board_done_button"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/newBoardPermission"
|
||||
app:layout_constraintHorizontal_bias="1.0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/newBoardPermission"
|
||||
app:layout_constraintVertical_bias="0.40" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/newBoardCancel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="onCancel"
|
||||
android:text="@string/new_board_cancel_button"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintStart_toEndOf="@+id/newBoardPermission"
|
||||
app:layout_constraintTop_toBottomOf="@+id/newBoardPermission"
|
||||
app:layout_constraintVertical_bias="0.40" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -1,5 +1,11 @@
|
||||
<resources>
|
||||
<string name="app_name">wedroid</string>
|
||||
<string name="title_activity_boards_list">BoardsListActivity</string>
|
||||
<string name="title_activity_boards_lists">Boards</string>
|
||||
<string name="title_activity_boards_lists">BoardsListActivity</string>
|
||||
<string name="title_activity_new_board">NewBoardActivity</string>
|
||||
<string name="new_board_name_field">Board name</string>
|
||||
<string name="new_board_switch">Private</string>
|
||||
<string name="new_board_cancel_button">Cancel</string>
|
||||
<string name="new_board_done_button">Done</string>
|
||||
<string name="on_null_new_board_name">There was a problem with the name of the new board</string>
|
||||
<string name="on_add_new_board_error">It was not possible to add a new board</string>
|
||||
</resources>
|
||||
|
@ -0,0 +1,13 @@
|
||||
package it.unisannio.ding.ids.wedroid.app
|
||||
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KFunction
|
||||
import kotlin.reflect.full.declaredFunctions
|
||||
import kotlin.reflect.jvm.isAccessible
|
||||
|
||||
fun getPrivateFun(name: String, kClass: KClass<*>): KFunction<*>? {
|
||||
return kClass.declaredFunctions
|
||||
.find { it.name == name }
|
||||
.also { it?.isAccessible = true }
|
||||
}
|
||||
|
@ -0,0 +1,318 @@
|
||||
package it.unisannio.ding.ids.wedroid.app.data.repository
|
||||
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.LiveData
|
||||
import io.mockk.*
|
||||
import it.unisannio.ding.ids.wedroid.app.data.dao.BoardDao
|
||||
import it.unisannio.ding.ids.wedroid.app.data.entity.Board
|
||||
import it.unisannio.ding.ids.wedroid.app.getPrivateFun
|
||||
import it.unisannio.ding.ids.wedroid.app.util.PreferenceReader
|
||||
import it.unisannio.ding.ids.wedroid.wrapper.api.BoardService
|
||||
import junit.framework.TestCase.*
|
||||
import kotlinx.coroutines.*
|
||||
import okhttp3.mockwebserver.MockWebServer
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import retrofit2.Response
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.gson.GsonConverterFactory
|
||||
import kotlin.reflect.full.callSuspend
|
||||
|
||||
class BoardRepositoryTest {
|
||||
private val reader = mockk<PreferenceReader>()
|
||||
private val webServer = MockWebServer()
|
||||
private lateinit var service: BoardService
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
webServer.start()
|
||||
service = Retrofit.Builder()
|
||||
.baseUrl(webServer.url("/"))
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.build()
|
||||
.create(BoardService::class.java)
|
||||
|
||||
mockkStatic(Log::class)
|
||||
every { reader.userId } returns "user id"
|
||||
every { Log.e(any(), any()) } returns 0
|
||||
}
|
||||
|
||||
@After
|
||||
fun teardown() {
|
||||
webServer.close()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun addNewBoardsToDb() {
|
||||
val dao = mockk<BoardDao>()
|
||||
var count = 0
|
||||
|
||||
coEvery {
|
||||
dao.insert(any())
|
||||
} answers {
|
||||
count++
|
||||
}
|
||||
|
||||
val addNewBoardToDb = getPrivateFun(
|
||||
"addNewBoardToDb", BoardRepository::class
|
||||
)
|
||||
|
||||
val repository = BoardRepository(
|
||||
dao, service, reader
|
||||
)
|
||||
|
||||
val board0 = Board("id0", "title0")
|
||||
val board1 = Board("id1", "title1")
|
||||
val board2 = Board("id2", "title2")
|
||||
|
||||
runBlocking {
|
||||
addNewBoardToDb?.callSuspend(
|
||||
repository,
|
||||
listOf(board0, board1, board2)
|
||||
)
|
||||
}
|
||||
|
||||
coVerifyAll {
|
||||
dao.insert(board0)
|
||||
dao.insert(board1)
|
||||
dao.insert(board2)
|
||||
}
|
||||
|
||||
assertEquals(3, count)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun insertBoardCallbackSuccess() {
|
||||
val dao = mockk<BoardDao>()
|
||||
val response =
|
||||
mockk<Response<it.unisannio.ding.ids.wedroid.wrapper.entity.Board>>()
|
||||
val board = mockk<it.unisannio.ding.ids.wedroid.wrapper.entity.Board>()
|
||||
|
||||
every { response.isSuccessful } returns true
|
||||
every { response.body() } returns board
|
||||
every { board.id } returns "id"
|
||||
coEvery { dao.insert(any()) } answers {}
|
||||
|
||||
val insertBoard = getPrivateFun(
|
||||
"insertBoardCallback", BoardRepository::class
|
||||
)
|
||||
|
||||
val repository = BoardRepository(
|
||||
dao, service, reader
|
||||
)
|
||||
|
||||
runBlocking {
|
||||
insertBoard?.callSuspend(
|
||||
repository,
|
||||
response,
|
||||
"title"
|
||||
)
|
||||
}
|
||||
|
||||
coVerify { dao.insert(Board("id", "title")) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun insertBoardCallbackEmptyBody() {
|
||||
val dao = mockk<BoardDao>()
|
||||
val response =
|
||||
mockk<Response<it.unisannio.ding.ids.wedroid.wrapper.entity.Board>>()
|
||||
|
||||
every { response.isSuccessful } returns true
|
||||
every { response.body() } returns null
|
||||
|
||||
val insertBoard = getPrivateFun(
|
||||
"insertBoardCallback", BoardRepository::class
|
||||
)
|
||||
|
||||
val repository = BoardRepository(
|
||||
dao, service, reader
|
||||
)
|
||||
|
||||
runBlocking {
|
||||
insertBoard?.callSuspend(
|
||||
repository,
|
||||
response,
|
||||
"title"
|
||||
)
|
||||
}
|
||||
|
||||
verify { Log.e("RETROFIT", "empty body") }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun insertBoardCallbackError() {
|
||||
val dao = mockk<BoardDao>()
|
||||
val response =
|
||||
mockk<Response<it.unisannio.ding.ids.wedroid.wrapper.entity.Board>>()
|
||||
|
||||
every { response.isSuccessful } returns false
|
||||
every { response.code() } returns 400
|
||||
every { response.message() } returns "Error"
|
||||
|
||||
val insertBoard = getPrivateFun(
|
||||
"insertBoardCallback", BoardRepository::class
|
||||
)
|
||||
|
||||
val repository = BoardRepository(
|
||||
dao, service, reader
|
||||
)
|
||||
|
||||
runBlocking {
|
||||
insertBoard?.callSuspend(
|
||||
repository,
|
||||
response,
|
||||
"title"
|
||||
)
|
||||
}
|
||||
|
||||
verify { Log.e("RETROFIT", "400 Error") }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun deleteBoardCallbackSuccess() {
|
||||
val dao = mockk<BoardDao>()
|
||||
val response =
|
||||
mockk<Response<Void>>()
|
||||
|
||||
every { response.isSuccessful } returns true
|
||||
coEvery { dao.delete(any()) } answers {}
|
||||
|
||||
val deleteBoard = getPrivateFun(
|
||||
"deleteBoardCallback", BoardRepository::class
|
||||
)
|
||||
|
||||
val repository = BoardRepository(
|
||||
dao, service, reader
|
||||
)
|
||||
|
||||
runBlocking {
|
||||
deleteBoard?.callSuspend(
|
||||
repository,
|
||||
response,
|
||||
"id"
|
||||
)
|
||||
}
|
||||
|
||||
coVerify { dao.delete(Board("id")) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun deleteBoardCallbackError() {
|
||||
val dao = mockk<BoardDao>()
|
||||
val response =
|
||||
mockk<Response<Void>>()
|
||||
|
||||
every { response.isSuccessful } returns false
|
||||
every { response.code() } returns 400
|
||||
every { response.message() } returns "Error"
|
||||
|
||||
val deleteBoard = getPrivateFun(
|
||||
"deleteBoardCallback", BoardRepository::class
|
||||
)
|
||||
|
||||
val repository = BoardRepository(
|
||||
dao, service, reader
|
||||
)
|
||||
|
||||
runBlocking {
|
||||
deleteBoard?.callSuspend(
|
||||
repository,
|
||||
response,
|
||||
"id"
|
||||
)
|
||||
}
|
||||
|
||||
verify { Log.e("RETROFIT", "400 Error") }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun deleteOldBoardFromDb() {
|
||||
val dao = mockk<BoardDao>()
|
||||
val dbBoards = mockk<LiveData<List<Board>>>()
|
||||
|
||||
val board0 = Board("id0", "title0")
|
||||
val board1 = Board("id1", "title1")
|
||||
val board2 = Board("id2", "title2")
|
||||
val board3 = Board("id2", "title3")
|
||||
|
||||
every { dbBoards.value } returns listOf(
|
||||
board0, board1, board2, board3
|
||||
)
|
||||
coEvery { dao.getAllBoard() } returns dbBoards
|
||||
coEvery { dao.delete(any()) } answers {}
|
||||
|
||||
val removeOldBoards = getPrivateFun(
|
||||
"removeOldBoardsFromDb", BoardRepository::class
|
||||
)
|
||||
|
||||
val repository = BoardRepository(
|
||||
dao, service, reader
|
||||
)
|
||||
|
||||
runBlocking {
|
||||
removeOldBoards?.callSuspend(
|
||||
repository,
|
||||
listOf(
|
||||
board0, board1, board3
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
coVerify { dao.delete(board2) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun synchronizeCallbackError() {
|
||||
val dao = mockk<BoardDao>()
|
||||
val response =
|
||||
mockk<Response<MutableList<it.unisannio.ding.ids.wedroid.wrapper.entity.Board>>>()
|
||||
|
||||
every { response.isSuccessful } returns false
|
||||
every { response.code() } returns 400
|
||||
every { response.message() } returns "Error"
|
||||
|
||||
val synchronize = getPrivateFun(
|
||||
"synchronizeCallback", BoardRepository::class
|
||||
)
|
||||
|
||||
val repository = BoardRepository(
|
||||
dao, service, reader
|
||||
)
|
||||
|
||||
runBlocking {
|
||||
synchronize?.callSuspend(
|
||||
repository,
|
||||
response
|
||||
)
|
||||
}
|
||||
|
||||
verify { Log.e("RETROFIT", "400 Error") }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun synchronizeCallbackEmptyBody() {
|
||||
val dao = mockk<BoardDao>()
|
||||
val response =
|
||||
mockk<Response<MutableList<it.unisannio.ding.ids.wedroid.wrapper.entity.Board>>>()
|
||||
|
||||
every { response.isSuccessful } returns true
|
||||
every { response.body() } returns null
|
||||
|
||||
val synchronize = getPrivateFun(
|
||||
"synchronizeCallback", BoardRepository::class
|
||||
)
|
||||
|
||||
val repository = BoardRepository(
|
||||
dao, service, reader
|
||||
)
|
||||
|
||||
runBlocking {
|
||||
synchronize?.callSuspend(
|
||||
repository,
|
||||
response
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user