From 045d19f46a9a79971ab9b72cad62a6b0e27bac89 Mon Sep 17 00:00:00 2001 From: Jelena Petrovic Date: Mon, 21 Nov 2022 06:58:25 +0100 Subject: omogucena komunikacija preko soketa (slanje i primanje poruka), pisanje poruka u sqllite bazu na telefonu, dodate minimalno odradjene komponente neophodne za pregled i slanje poruka #37 --- Backend/Api/Api/Controllers/UserController.cs | 2 +- Backend/Api/Api/Services/ChatHub.cs | 2 +- Client/BrzoDoLokacije/app/build.gradle | 2 +- .../app/src/main/AndroidManifest.xml | 6 +- .../brzodolokacije/Activities/ChatActivity.kt | 17 +++ .../Activities/ChatActivityConversation.kt | 128 +++++++++++++++++++++ .../brzodolokacije/Fragments/FragmentShowPosts.kt | 24 ++-- .../brzodolokacije/Interfaces/IBackendApi.kt | 3 +- .../com/example/brzodolokacije/MainActivity.kt | 3 +- .../java/com/example/brzodolokacije/Models/Chat.kt | 22 ++++ .../com/example/brzodolokacije/chat/DBHelper.kt | 46 ++++++-- .../example/brzodolokacije/chat/SignalRListener.kt | 27 ++++- .../app/src/main/res/layout/activity_chat.xml | 42 +++++++ .../main/res/layout/activity_chat_conversation.xml | 82 +++++++++++++ .../app/src/main/res/layout/chat_preview.xml | 18 +++ .../src/main/res/layout/fragment_show_posts.xml | 13 +++ 16 files changed, 402 insertions(+), 35 deletions(-) create mode 100644 Client/BrzoDoLokacije/app/src/main/java/com/example/brzodolokacije/Activities/ChatActivityConversation.kt create mode 100644 Client/BrzoDoLokacije/app/src/main/java/com/example/brzodolokacije/Models/Chat.kt create mode 100644 Client/BrzoDoLokacije/app/src/main/res/layout/activity_chat_conversation.xml create mode 100644 Client/BrzoDoLokacije/app/src/main/res/layout/chat_preview.xml diff --git a/Backend/Api/Api/Controllers/UserController.cs b/Backend/Api/Api/Controllers/UserController.cs index 1bc395f..dd081cb 100644 --- a/Backend/Api/Api/Controllers/UserController.cs +++ b/Backend/Api/Api/Controllers/UserController.cs @@ -55,7 +55,7 @@ namespace Api.Controllers return Ok(rez); return BadRequest(); } - [HttpGet("{id}/profile")] + [HttpGet("{id}/id/profile")] [Authorize(Roles = "User")] public async Task> GetUserById(string id) { diff --git a/Backend/Api/Api/Services/ChatHub.cs b/Backend/Api/Api/Services/ChatHub.cs index e0bf5df..639a585 100644 --- a/Backend/Api/Api/Services/ChatHub.cs +++ b/Backend/Api/Api/Services/ChatHub.cs @@ -13,7 +13,7 @@ namespace Api.Services } public override async Task OnConnectedAsync() { - string token = Context.GetHttpContext().Request.Query["access_token"]; + string token = Context.GetHttpContext().Request.Headers["access_token"]; if (token == null) return; string id = _jwtService.TokenToId(token); diff --git a/Client/BrzoDoLokacije/app/build.gradle b/Client/BrzoDoLokacije/app/build.gradle index 8437053..f81ba4b 100644 --- a/Client/BrzoDoLokacije/app/build.gradle +++ b/Client/BrzoDoLokacije/app/build.gradle @@ -54,7 +54,7 @@ dependencies { implementation 'androidx.recyclerview:recyclerview:1.2.1' implementation "androidx.paging:paging-runtime:3.0.0-alpha03" implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1" - implementation 'com.microsoft.signalr:signalr:6.0.0' + implementation 'com.microsoft.signalr:signalr:6.0.8' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' diff --git a/Client/BrzoDoLokacije/app/src/main/AndroidManifest.xml b/Client/BrzoDoLokacije/app/src/main/AndroidManifest.xml index 8469bd3..745f813 100644 --- a/Client/BrzoDoLokacije/app/src/main/AndroidManifest.xml +++ b/Client/BrzoDoLokacije/app/src/main/AndroidManifest.xml @@ -37,15 +37,15 @@ android:usesCleartextTraffic="true" tools:targetApi="31"> + android:name=".Activities.ChatActivity" + android:exported="true"> diff --git a/Client/BrzoDoLokacije/app/src/main/java/com/example/brzodolokacije/Activities/ChatActivity.kt b/Client/BrzoDoLokacije/app/src/main/java/com/example/brzodolokacije/Activities/ChatActivity.kt index 96c8ea7..4cc7cc1 100644 --- a/Client/BrzoDoLokacije/app/src/main/java/com/example/brzodolokacije/Activities/ChatActivity.kt +++ b/Client/BrzoDoLokacije/app/src/main/java/com/example/brzodolokacije/Activities/ChatActivity.kt @@ -1,17 +1,34 @@ package com.example.brzodolokacije.Activities +import android.content.Intent import android.os.Bundle +import android.widget.ImageButton import androidx.appcompat.app.AppCompatActivity import com.example.brzodolokacije.R import com.example.brzodolokacije.chat.DBHelper +import com.example.brzodolokacije.chat.SignalRListener +import com.example.brzodolokacije.databinding.ActivityChatBinding class ChatActivity : AppCompatActivity() { private var dbConnection:DBHelper?=null + lateinit var binding: ActivityChatBinding + var ws:SignalRListener?=null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_chat) + binding=ActivityChatBinding.inflate(layoutInflater) dbConnection= DBHelper(this@ChatActivity,null) + ws=SignalRListener.getInstance(this@ChatActivity) + setListeners() + + } + fun setListeners(){ + findViewById(R.id.addNewMessage).setOnClickListener { + val intent: Intent = Intent(this@ChatActivity,ChatActivityConversation::class.java) + intent.putExtra("receiverId","") + startActivity(intent) + } } } \ No newline at end of file diff --git a/Client/BrzoDoLokacije/app/src/main/java/com/example/brzodolokacije/Activities/ChatActivityConversation.kt b/Client/BrzoDoLokacije/app/src/main/java/com/example/brzodolokacije/Activities/ChatActivityConversation.kt new file mode 100644 index 0000000..715f5f3 --- /dev/null +++ b/Client/BrzoDoLokacije/app/src/main/java/com/example/brzodolokacije/Activities/ChatActivityConversation.kt @@ -0,0 +1,128 @@ +package com.example.brzodolokacije.Activities + +import android.os.Bundle +import android.util.Log +import android.widget.EditText +import android.widget.ImageButton +import android.widget.Toast +import androidx.appcompat.app.AppCompatActivity +import androidx.cardview.widget.CardView +import androidx.core.view.isVisible +import com.example.brzodolokacije.Models.Message +import com.example.brzodolokacije.Models.MessageSend +import com.example.brzodolokacije.Models.UserReceive +import com.example.brzodolokacije.R +import com.example.brzodolokacije.Services.RetrofitHelper +import com.example.brzodolokacije.Services.SharedPreferencesHelper +import com.example.brzodolokacije.chat.DBHelper +import com.example.brzodolokacije.chat.SignalRListener +import retrofit2.Call +import retrofit2.Response + +class ChatActivityConversation : AppCompatActivity() { + + var receiverId:String?=null + var receiverUsername:String?="jelena" + var dbConnection:DBHelper?=null + var webSocketConnection:SignalRListener?=null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_chat_conversation) + receiverId=intent.extras?.get("receiverId").toString() + if(receiverId.isNullOrEmpty()){ + findViewById(R.id.cvParentMessageEdit).isVisible=true + findViewById(R.id.cvParentMessageEdit).invalidate() + } + else{ + findViewById(R.id.cvParentUsername).isVisible=true + findViewById(R.id.cvParentUsername).invalidate() + } + dbConnection=DBHelper.getInstance(this@ChatActivityConversation) + webSocketConnection=SignalRListener.getInstance(this@ChatActivityConversation) + setListeners() + } + + private fun setListeners() { + findViewById(R.id.btnSendMessage).setOnClickListener { + var token=SharedPreferencesHelper.getValue("jwt",this@ChatActivityConversation) + var messageContent=findViewById(R.id.etNewMessage).text.toString() + Log.d("main",token!!) + val Api= RetrofitHelper.getInstance() + if(receiverId.isNullOrEmpty()){ + //zahtev sa username=om + receiverUsername=findViewById(R.id.etReceiverUsername).text.toString() + val request=Api.getProfile("Bearer "+token, + receiverUsername!! + ) + request.enqueue(object : retrofit2.Callback { + override fun onResponse(call: Call, response: Response) { + if(response.isSuccessful()){ + //zahtev da se posalje poruka + receiverId=response.body()?._id + var message= MessageSend(receiverId!!,messageContent) + val request2=Api.sendMessage("Bearer "+token, + message + ) + request2.enqueue(object : retrofit2.Callback { + override fun onResponse(call: Call, response: Response) { + if(response.isSuccessful()){ + //zahtev da se posalje poruka + var responseMessage=response.body() + dbConnection?.addMessage(responseMessage!!) + dbConnection?.getMessages() + webSocketConnection?.getConnectionState() + + + } + else{ + Toast.makeText(this@ChatActivityConversation,"Pogresno korisnicko ime1.",Toast.LENGTH_LONG).show() + } + } + + override fun onFailure(call: Call, t: Throwable) { + Toast.makeText(this@ChatActivityConversation,"Pogresno korisnicko ime2.",Toast.LENGTH_LONG).show() + } + }) + } + else{ + Log.d("main",response.message()) + //Toast.makeText(this@ChatActivityConversation,"Pogresno korisnicko ime3.",Toast.LENGTH_LONG).show() + } + } + + override fun onFailure(call: Call, t: Throwable) { + Toast.makeText(this@ChatActivityConversation,"fail.",Toast.LENGTH_LONG).show() + } + }) + } + else{ + //zahtev da se posalje poruka + var message= MessageSend(receiverId!!,messageContent) + val request2=Api.sendMessage("Bearer "+token, + message + ) + request2.enqueue(object : retrofit2.Callback { + override fun onResponse(call: Call, response: Response) { + if(response.isSuccessful()){ + //zahtev da se posalje poruka + var responseMessage=response.body() + dbConnection?.addMessage(responseMessage!!) + dbConnection?.getMessages() + + + } + else{ + Toast.makeText(this@ChatActivityConversation,"Pogresno korisnicko ime.",Toast.LENGTH_LONG).show() + } + } + + override fun onFailure(call: Call, t: Throwable) { + Toast.makeText(this@ChatActivityConversation,"Pogresno korisnicko ime.",Toast.LENGTH_LONG).show() + } + }) + } + } + + } +} \ No newline at end of file diff --git a/Client/BrzoDoLokacije/app/src/main/java/com/example/brzodolokacije/Fragments/FragmentShowPosts.kt b/Client/BrzoDoLokacije/app/src/main/java/com/example/brzodolokacije/Fragments/FragmentShowPosts.kt index e78e1d6..c8ffb90 100644 --- a/Client/BrzoDoLokacije/app/src/main/java/com/example/brzodolokacije/Fragments/FragmentShowPosts.kt +++ b/Client/BrzoDoLokacije/app/src/main/java/com/example/brzodolokacije/Fragments/FragmentShowPosts.kt @@ -1,43 +1,30 @@ package com.example.brzodolokacije.Fragments -import android.content.Context import android.content.Intent import android.os.Bundle import android.util.Log -import androidx.fragment.app.Fragment import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.ImageButton -import android.widget.Toast +import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope -import androidx.paging.PagingDataAdapter -import androidx.paging.cachedIn import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import androidx.swiperefreshlayout.widget.SwipeRefreshLayout -import com.example.brzodolokacije.Activities.NavigationActivity -import com.example.brzodolokacije.Adapters.SampleAdapter +import com.example.brzodolokacije.Activities.ChatActivity import com.example.brzodolokacije.Adapters.ShowPostsAdapter -import com.example.brzodolokacije.Models.* +import com.example.brzodolokacije.Models.SearchParams import com.example.brzodolokacije.R import com.example.brzodolokacije.Services.RetrofitHelper -import com.example.brzodolokacije.Services.SharedPreferencesHelper -import com.example.brzodolokacije.databinding.FragmentHomeBinding import com.example.brzodolokacije.databinding.FragmentShowPostsBinding -import com.example.brzodolokacije.paging.SearchPostsRepository import com.example.brzodolokacije.paging.SearchPostsViewModel import com.example.brzodolokacije.paging.SearchPostsViewModelFactory -import kotlinx.android.synthetic.main.fragment_show_posts.* -import kotlinx.android.synthetic.main.fragment_show_posts.view.* import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.launch -import okhttp3.ResponseBody -import retrofit2.Call -import retrofit2.Response class FragmentShowPosts : Fragment(), SwipeRefreshLayout.OnRefreshListener { @@ -80,6 +67,11 @@ class FragmentShowPosts : Fragment(), SwipeRefreshLayout.OnRefreshListener { } Log.d("main","klik") } + + rootView?.findViewById(R.id.btnChat)?.setOnClickListener() { + val intent: Intent = Intent(activity, ChatActivity::class.java) + requireActivity().startActivity(intent) + } } fun requestToBack(searchParams: SearchParams){ diff --git a/Client/BrzoDoLokacije/app/src/main/java/com/example/brzodolokacije/Interfaces/IBackendApi.kt b/Client/BrzoDoLokacije/app/src/main/java/com/example/brzodolokacije/Interfaces/IBackendApi.kt index 9d94013..9f53522 100644 --- a/Client/BrzoDoLokacije/app/src/main/java/com/example/brzodolokacije/Interfaces/IBackendApi.kt +++ b/Client/BrzoDoLokacije/app/src/main/java/com/example/brzodolokacije/Interfaces/IBackendApi.kt @@ -6,7 +6,6 @@ import com.example.brzodolokacije.Models.Auth.Login import com.example.brzodolokacije.Models.Auth.Register import com.example.brzodolokacije.Models.Auth.ResetPass import okhttp3.MultipartBody -import okhttp3.Request import okhttp3.RequestBody import okhttp3.ResponseBody import retrofit2.Call @@ -63,6 +62,8 @@ interface IBackendApi { @Query("sorttype") sorttype:Int, @Query("filterdate") filterdate:Int ):PagedPosts + @POST("/api/message/add") + fun sendMessage(@Header("Authorization") authHeader:String,@Body message:MessageSend):Call //@POST("putanja") //fun add(@Body obj:Post,@Header("Authorization") authHeader:String):Call } \ No newline at end of file diff --git a/Client/BrzoDoLokacije/app/src/main/java/com/example/brzodolokacije/MainActivity.kt b/Client/BrzoDoLokacije/app/src/main/java/com/example/brzodolokacije/MainActivity.kt index 515a41b..b3183b8 100644 --- a/Client/BrzoDoLokacije/app/src/main/java/com/example/brzodolokacije/MainActivity.kt +++ b/Client/BrzoDoLokacije/app/src/main/java/com/example/brzodolokacije/MainActivity.kt @@ -9,7 +9,6 @@ import com.example.brzodolokacije.Activities.ActivityLoginRegister import com.example.brzodolokacije.Activities.NavigationActivity import com.example.brzodolokacije.Services.RetrofitHelper import com.example.brzodolokacije.Services.SharedPreferencesHelper -import com.example.brzodolokacije.chat.SignalRListener import retrofit2.Call import retrofit2.Response @@ -21,7 +20,7 @@ class MainActivity : AppCompatActivity() { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val intent:Intent - SignalRListener.getInstance(this@MainActivity) + if(checkLoggedIn()) { intent = Intent(this, NavigationActivity::class.java) } diff --git a/Client/BrzoDoLokacije/app/src/main/java/com/example/brzodolokacije/Models/Chat.kt b/Client/BrzoDoLokacije/app/src/main/java/com/example/brzodolokacije/Models/Chat.kt new file mode 100644 index 0000000..4b87d51 --- /dev/null +++ b/Client/BrzoDoLokacije/app/src/main/java/com/example/brzodolokacije/Models/Chat.kt @@ -0,0 +1,22 @@ +package com.example.brzodolokacije.Models + +import java.util.* + +data class MessageReceive( + var senderId:String, + var messagge:String, + var timestamp:Date +) + +data class MessageSend( + var receiverId:String, + var messagge:String +) + +data class Message( + var _id:String, + var senderId: String, + var receiverId: String, + var messagge: String, + var timestamp: Date +) \ No newline at end of file diff --git a/Client/BrzoDoLokacije/app/src/main/java/com/example/brzodolokacije/chat/DBHelper.kt b/Client/BrzoDoLokacije/app/src/main/java/com/example/brzodolokacije/chat/DBHelper.kt index 60e870e..d4ee046 100644 --- a/Client/BrzoDoLokacije/app/src/main/java/com/example/brzodolokacije/chat/DBHelper.kt +++ b/Client/BrzoDoLokacije/app/src/main/java/com/example/brzodolokacije/chat/DBHelper.kt @@ -1,41 +1,54 @@ package com.example.brzodolokacije.chat +import android.app.Activity import android.content.Context import android.database.Cursor import android.database.sqlite.SQLiteDatabase import android.database.sqlite.SQLiteOpenHelper +import android.util.Log +import com.example.brzodolokacije.Models.Message + class DBHelper : - SQLiteOpenHelper { + SQLiteOpenHelper{ var db:SQLiteDatabase?=null - constructor(context: Context, factory: SQLiteDatabase.CursorFactory?):super(context, DATABASE_NAME, factory,2){ + constructor(context: Context, factory: SQLiteDatabase.CursorFactory?):super(context, DATABASE_NAME, factory,3){ db=readableDatabase } + companion object{ //database name private val DATABASE_NAME = "chatHistory" //database tables val CONTACTS_TABLE_NAME = "contacts" val MESSAGES_TABLE_NAME = "messages" + private var instance:DBHelper?=null + fun getInstance(activity: Activity):DBHelper{ + if(instance==null){ + instance= DBHelper(activity,null) + } + return instance as DBHelper } + } override fun onCreate(db: SQLiteDatabase?) { if(!doesTableExist(CONTACTS_TABLE_NAME,db)){ var sql:String="CREATE TABLE "+ CONTACTS_TABLE_NAME+"(" + - "userId" +"TEXT PRIMARY KEY,"+ - "read" +"INT"+ + "userId " +"TEXT PRIMARY KEY,"+ + "read " +"INT"+ ")" db?.execSQL(sql) } if(!doesTableExist(MESSAGES_TABLE_NAME,db)){ var sql:String="CREATE TABLE "+ MESSAGES_TABLE_NAME+"(" + - "senderId" +"TEXT,"+ - "receiverId"+"TEXT,"+ - "message" +"TEXT,"+ - "dateTime"+"TEXT"+ + "_id "+"TEXT PRIMARY KEY,"+ + "senderId " +"TEXT,"+ + "receiverId "+"TEXT,"+ + "messagge " +"TEXT,"+ + "timestamp "+"TEXT"+ ")" db?.execSQL(sql) } @@ -60,4 +73,21 @@ class DBHelper : db?.execSQL("DROP TABLE IF EXISTS " + MESSAGES_TABLE_NAME) onCreate(db) } + + fun addMessage(message: Message){ + if(message._id.isNullOrEmpty()){ + message._id=message.senderId+message.timestamp + } + var sql="INSERT INTO "+ MESSAGES_TABLE_NAME+"(_id,senderId,receiverid,messagge,timestamp) VALUES('"+message._id+"','"+ + message.senderId+"','"+ + message.receiverId+"','"+ + message.messagge+ "','"+ + message.timestamp+ "')" + db?.execSQL(sql) + } + fun getMessages(){ + var sql="SELECT * FROM "+ MESSAGES_TABLE_NAME + var cursor=db?.rawQuery(sql,null) + Log.d("main",cursor?.count.toString()) + } } \ No newline at end of file diff --git a/Client/BrzoDoLokacije/app/src/main/java/com/example/brzodolokacije/chat/SignalRListener.kt b/Client/BrzoDoLokacije/app/src/main/java/com/example/brzodolokacije/chat/SignalRListener.kt index ee0c86d..1aa6afa 100644 --- a/Client/BrzoDoLokacije/app/src/main/java/com/example/brzodolokacije/chat/SignalRListener.kt +++ b/Client/BrzoDoLokacije/app/src/main/java/com/example/brzodolokacije/chat/SignalRListener.kt @@ -2,19 +2,28 @@ package com.example.brzodolokacije.chat import android.app.Activity import android.util.Log +import com.example.brzodolokacije.Models.MessageReceive import com.example.brzodolokacije.Services.RetrofitHelper import com.example.brzodolokacije.Services.SharedPreferencesHelper +import com.microsoft.signalr.Action1 import com.microsoft.signalr.HubConnection import com.microsoft.signalr.HubConnectionBuilder -import io.reactivex.rxjava3.core.Single +import com.microsoft.signalr.HubConnectionState + class SignalRListener private constructor(val activity: Activity){ private var hubConnection:HubConnection init{ hubConnection=HubConnectionBuilder.create(RetrofitHelper.baseUrl+"/chathub") - .withAccessTokenProvider(Single.defer{ Single.just(SharedPreferencesHelper.getValue("jwt", activity).toString())}) + .withHeader("access_token",SharedPreferencesHelper.getValue("jwt",activity)) .build() + hubConnection.keepAliveInterval=120 + hubConnection.on("Message", + Action1 {message:MessageReceive->Log.d("main",message.messagge)}, + MessageReceive::class.java + ) hubConnection.start().blockingAwait() + Log.d("main", hubConnection.connectionState.toString()) } @@ -27,6 +36,20 @@ class SignalRListener private constructor(val activity: Activity){ } return instance as SignalRListener } + + } + fun stopHubConnection(){ + if(hubConnection.connectionState == HubConnectionState.CONNECTED){ + hubConnection.stop() + } + } + + fun getConnectionState(){ + Log.d("main",hubConnection.connectionState.toString()) + } + + fun log(){ + Log.d("Debug infor siganlR ", hubConnection.connectionId) } } \ No newline at end of file diff --git a/Client/BrzoDoLokacije/app/src/main/res/layout/activity_chat.xml b/Client/BrzoDoLokacije/app/src/main/res/layout/activity_chat.xml index 093a95a..694e972 100644 --- a/Client/BrzoDoLokacije/app/src/main/res/layout/activity_chat.xml +++ b/Client/BrzoDoLokacije/app/src/main/res/layout/activity_chat.xml @@ -5,5 +5,47 @@ android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".Activities.ChatActivity"> + + + + + + + + + \ No newline at end of file diff --git a/Client/BrzoDoLokacije/app/src/main/res/layout/activity_chat_conversation.xml b/Client/BrzoDoLokacije/app/src/main/res/layout/activity_chat_conversation.xml new file mode 100644 index 0000000..ea7d3e7 --- /dev/null +++ b/Client/BrzoDoLokacije/app/src/main/res/layout/activity_chat_conversation.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Client/BrzoDoLokacije/app/src/main/res/layout/chat_preview.xml b/Client/BrzoDoLokacije/app/src/main/res/layout/chat_preview.xml new file mode 100644 index 0000000..95c036a --- /dev/null +++ b/Client/BrzoDoLokacije/app/src/main/res/layout/chat_preview.xml @@ -0,0 +1,18 @@ + + + + + + \ No newline at end of file diff --git a/Client/BrzoDoLokacije/app/src/main/res/layout/fragment_show_posts.xml b/Client/BrzoDoLokacije/app/src/main/res/layout/fragment_show_posts.xml index 5dfbc98..6e22456 100644 --- a/Client/BrzoDoLokacije/app/src/main/res/layout/fragment_show_posts.xml +++ b/Client/BrzoDoLokacije/app/src/main/res/layout/fragment_show_posts.xml @@ -72,6 +72,19 @@ app:layout_constraintStart_toEndOf="@+id/btnSortType" app:layout_constraintTop_toTopOf="parent" /> + +