diff options
26 files changed, 464 insertions, 39 deletions
diff --git a/Backend/Api/Api/.gitignore b/Backend/Api/Api/.gitignore new file mode 100644 index 0000000..f4b4ba8 --- /dev/null +++ b/Backend/Api/Api/.gitignore @@ -0,0 +1 @@ +Files/*
\ No newline at end of file diff --git a/Backend/Api/Api/Api.csproj b/Backend/Api/Api/Api.csproj index 93e31b7..b09c2fd 100644 --- a/Backend/Api/Api/Api.csproj +++ b/Backend/Api/Api/Api.csproj @@ -15,4 +15,8 @@ <PackageReference Include="MailKit" Version="3.4.2" /> </ItemGroup> + <ItemGroup> + <Folder Include="Files\" /> + </ItemGroup> + </Project> diff --git a/Backend/Api/Api/Controllers/LocationController.cs b/Backend/Api/Api/Controllers/LocationController.cs new file mode 100644 index 0000000..bb0b0ab --- /dev/null +++ b/Backend/Api/Api/Controllers/LocationController.cs @@ -0,0 +1,54 @@ +using Api.Interfaces; +using Api.Models; +using Microsoft.AspNetCore.Authorization; +using System.Data; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; + +namespace Api.Controllers +{ + [Route("api/[controller]")] + [ApiController] + public class LocationController : ControllerBase + { + private readonly ILocationService _locationService; + public LocationController(ILocationService locationService) + { + _locationService = locationService; + } + + [HttpPost("add")] + [Authorize(Roles = "User")] + public async Task<ActionResult<Location>> addPost([FromBody] Location loc) + { + var res = await _locationService.add(loc); + if (res != null) + { + return Ok(res); + } + return BadRequest(); + } + [HttpGet] + [Authorize(Roles = "User")] + public async Task<ActionResult<List<Location>>> getAllPosts() + { + var res = await _locationService.getAllLocation(); + if (res != null) + { + return Ok(res); + } + return BadRequest(); + } + [HttpGet("loc /{id}")] + [Authorize(Roles = "User")] + public async Task<ActionResult<Location>> getLocationByid(string id) + { + var res = await _locationService.getById(id); + if (res != null) + { + return Ok(res); + } + return BadRequest(); + } + } +} diff --git a/Backend/Api/Api/Controllers/PostController.cs b/Backend/Api/Api/Controllers/PostController.cs new file mode 100644 index 0000000..31dbeef --- /dev/null +++ b/Backend/Api/Api/Controllers/PostController.cs @@ -0,0 +1,56 @@ +using Api.Interfaces; +using Api.Models; +using Api.Services; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860 + +namespace Api.Controllers +{ + [Route("api/[controller]")] + [ApiController] + public class PostController : ControllerBase + { + private readonly IPostService _postService; + public PostController(IPostService postService) + { + _postService = postService; + } + + [HttpPost("add")] + [Authorize(Roles ="User")] + public async Task<ActionResult<PostSend>> addPost([FromForm]PostReceive post) + { + var res = await _postService.addPost(post); + if (res != null) + { + return Ok(res); + } + return BadRequest(); + } + [HttpGet] + [Authorize(Roles = "User")] + public async Task<ActionResult<List<PostSend>>> getAllPosts() + { + var res = await _postService.getAllPosts(); + if (res != null) + { + return Ok(res); + } + return BadRequest(); + } + [HttpGet("posts /{id}")] + [Authorize(Roles = "User")] + public async Task<ActionResult<PostSend>> getPostByid(string id) + { + var res = await _postService.getPostById(id); + if (res != null) + { + return Ok(res); + } + return BadRequest(); + } + + } +} diff --git a/Backend/Api/Api/Database/DatabaseConnection.cs b/Backend/Api/Api/Database/DatabaseConnection.cs index 65f4f52..f26b88e 100644 --- a/Backend/Api/Api/Database/DatabaseConnection.cs +++ b/Backend/Api/Api/Database/DatabaseConnection.cs @@ -7,5 +7,8 @@ namespace Api.Database public string ConnectionString { get; set; } = String.Empty; public string DatabaseName { get; set; } = String.Empty; public string UserCollectionName { get; set; } = String.Empty; + public string PostCollectionName { get; set; } = String.Empty; + public string FileCollectionName { get; set; } = String.Empty; + public string LocationCollectionName { get; set; } = String.Empty; } } diff --git a/Backend/Api/Api/Interfaces/IDatabaseConnection.cs b/Backend/Api/Api/Interfaces/IDatabaseConnection.cs index 8938127..17b5262 100644 --- a/Backend/Api/Api/Interfaces/IDatabaseConnection.cs +++ b/Backend/Api/Api/Interfaces/IDatabaseConnection.cs @@ -5,5 +5,8 @@ string ConnectionString { get; set; } string DatabaseName { get; set; } string UserCollectionName { get; set; } + string PostCollectionName { get; set; } + string FileCollectionName { get; set; } + string LocationCollectionName { get; set; } } } diff --git a/Backend/Api/Api/Interfaces/IFileService.cs b/Backend/Api/Api/Interfaces/IFileService.cs new file mode 100644 index 0000000..e736305 --- /dev/null +++ b/Backend/Api/Api/Interfaces/IFileService.cs @@ -0,0 +1,8 @@ +namespace Api.Interfaces +{ + public interface IFileService + { + Task<Models.File> add(Models.File file); + Task<Models.File> getById(string id); + } +}
\ No newline at end of file diff --git a/Backend/Api/Api/Interfaces/ILocationService.cs b/Backend/Api/Api/Interfaces/ILocationService.cs new file mode 100644 index 0000000..16e00a0 --- /dev/null +++ b/Backend/Api/Api/Interfaces/ILocationService.cs @@ -0,0 +1,11 @@ +using Api.Models; + +namespace Api.Interfaces +{ + public interface ILocationService + { + Task<Location> add(Location loc); + Task<Location> getById(string id); + Task<List<Location>> getAllLocation(); + } +}
\ No newline at end of file diff --git a/Backend/Api/Api/Interfaces/IPostService.cs b/Backend/Api/Api/Interfaces/IPostService.cs new file mode 100644 index 0000000..31e80cd --- /dev/null +++ b/Backend/Api/Api/Interfaces/IPostService.cs @@ -0,0 +1,12 @@ +using Api.Models; + +namespace Api.Interfaces +{ + public interface IPostService + { + Task<PostSend> addPost(PostReceive post); + Task<List<PostSend>> getAllPosts(); + Task<PostSend> getPostById(string id); + PostSend postToPostSend(Post post); + } +}
\ No newline at end of file diff --git a/Backend/Api/Api/Models/Location.cs b/Backend/Api/Api/Models/Location.cs index 5e723e4..8cc4377 100644 --- a/Backend/Api/Api/Models/Location.cs +++ b/Backend/Api/Api/Models/Location.cs @@ -11,7 +11,7 @@ namespace Api.Models public String name { get; set; } public String city { get; set; } public String country { get; set; } - public String adress { get; set; } + public String address { get; set; } public double latitude { get; set; } public double longitude { get; set; } public LocationType type { get; set; } diff --git a/Backend/Api/Api/Models/Post.cs b/Backend/Api/Api/Models/Post.cs index 456fcea..ee84e0f 100644 --- a/Backend/Api/Api/Models/Post.cs +++ b/Backend/Api/Api/Models/Post.cs @@ -1,5 +1,6 @@ using MongoDB.Bson.Serialization.Attributes; using MongoDB.Bson; +using System.ComponentModel.DataAnnotations; namespace Api.Models { @@ -9,7 +10,7 @@ namespace Api.Models [BsonRepresentation(BsonType.ObjectId)] public string _id { get; set; } public string ownerId { get; set; } - public Location location { get; set; } + public string locationId { get; set; } public string description { get; set; } public List<string> views { get; set; } public List<string> reports { get; set; } @@ -22,7 +23,7 @@ namespace Api.Models [BsonId] [BsonRepresentation(BsonType.ObjectId)] public string _id { get; set; } - public Location location { get; set; } + public string locationId { get; set; } public string description { get; set; } public List<IFormFile> images { get; set; } diff --git a/Backend/Api/Api/Program.cs b/Backend/Api/Api/Program.cs index 6c96331..16b0241 100644 --- a/Backend/Api/Api/Program.cs +++ b/Backend/Api/Api/Program.cs @@ -22,6 +22,9 @@ builder.Services.AddSingleton<IMongoClient>(s => builder.Services.AddScoped<IUserService, UserService>(); builder.Services.AddScoped<IJwtService, JwtService>(); +builder.Services.AddScoped<IPostService,PostService>(); +builder.Services.AddScoped<IFileService,FileService>(); +builder.Services.AddScoped<ILocationService,LocationService>(); builder.Services.AddHttpContextAccessor(); @@ -69,11 +72,11 @@ if (app.Environment.IsDevelopment()) app.UseSwaggerUI(); } -app.UseAuthorization(); + //Add Authentication app.UseAuthentication(); - +app.UseAuthorization(); app.MapControllers(); app.Run(); diff --git a/Backend/Api/Api/Services/FileService.cs b/Backend/Api/Api/Services/FileService.cs new file mode 100644 index 0000000..1937c10 --- /dev/null +++ b/Backend/Api/Api/Services/FileService.cs @@ -0,0 +1,28 @@ +using Api.Interfaces; +using Api.Models; +using MongoDB.Driver; +using File = Api.Models.File; + +namespace Api.Services +{ + public class FileService : IFileService + { + private readonly MongoClient _client; + private readonly IMongoCollection<File> _files; + private readonly IHttpContextAccessor _httpContext; + public FileService(IDatabaseConnection settings, IMongoClient mongoClient) + { + var database = mongoClient.GetDatabase(settings.DatabaseName); + _files = database.GetCollection<File>(settings.FileCollectionName); + } + public async Task<File> add(File file) + { + await _files.InsertOneAsync(file); + return file; + } + public async Task<File> getById(string id) + { + return await _files.Find(file => file._id == id).FirstOrDefaultAsync(); + } + } +} diff --git a/Backend/Api/Api/Services/JwtService.cs b/Backend/Api/Api/Services/JwtService.cs index fbf5724..c199484 100644 --- a/Backend/Api/Api/Services/JwtService.cs +++ b/Backend/Api/Api/Services/JwtService.cs @@ -24,7 +24,8 @@ namespace Api.Services var key = Encoding.ASCII.GetBytes(_config.GetSection("AppSettings:JwtToken").Value); var tokenDescriptor = new SecurityTokenDescriptor { - Subject = new ClaimsIdentity(new[] { new Claim("id", user._id) }), + Subject = new ClaimsIdentity(new[] { new Claim("id", user._id), + new Claim("role","User")}), Expires = DateTime.UtcNow.AddDays(7), SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature) }; diff --git a/Backend/Api/Api/Services/LocationService.cs b/Backend/Api/Api/Services/LocationService.cs new file mode 100644 index 0000000..629c2a7 --- /dev/null +++ b/Backend/Api/Api/Services/LocationService.cs @@ -0,0 +1,32 @@ +using Api.Interfaces; +using Api.Models; +using MongoDB.Driver; + +namespace Api.Services +{ + public class LocationService : ILocationService + { + private readonly MongoClient _client; + private readonly IMongoCollection<Location> _locations; + private readonly IHttpContextAccessor _httpContext; + public LocationService(IDatabaseConnection settings, IMongoClient mongoClient) + { + var database = mongoClient.GetDatabase(settings.DatabaseName); + _locations = database.GetCollection<Location>(settings.LocationCollectionName); + } + public async Task<Location> add(Location loc) + { + //TODO GOOGLE MAPS API CALL FOR info + await _locations.InsertOneAsync(loc); + return loc; + } + public async Task<Location> getById(string id) + { + return await _locations.Find(loc => loc._id == id).FirstOrDefaultAsync(); + } + public async Task<List<Location>> getAllLocation() + { + return await _locations.Find(_=>true).ToListAsync(); + } + } +} diff --git a/Backend/Api/Api/Services/PostService.cs b/Backend/Api/Api/Services/PostService.cs new file mode 100644 index 0000000..2f29366 --- /dev/null +++ b/Backend/Api/Api/Services/PostService.cs @@ -0,0 +1,104 @@ +using System.Security.Claims; +using Api.Interfaces; +using Api.Models; +using MongoDB.Driver; + +namespace Api.Services +{ + public class PostService : IPostService + { + private readonly MongoClient _client; + private readonly IMongoCollection<Post> _posts; + private readonly IHttpContextAccessor _httpContext; + private readonly IFileService _fileService; + public PostService(IDatabaseConnection settings, IMongoClient mongoClient, IHttpContextAccessor httpContext, IFileService fileService) + { + var database = mongoClient.GetDatabase(settings.DatabaseName); + _posts = database.GetCollection<Post>(settings.PostCollectionName); + _httpContext = httpContext; + _fileService = fileService; + } + + public async Task<PostSend> addPost(PostReceive post) + { + Post p = new Post(); + p._id = ""; + p.ownerId = _httpContext.HttpContext.User.FindFirstValue("id"); + + p.locationId = post.locationId; + p.description = post.description; + p.views = new List<string>(); + p.reports = new List<string>(); + p.ratings = new List<Rating>(); + p.comments = new List<Comment>(); + p.images = new List<Models.File>(); + + var folderPath = Path.Combine(Directory.GetCurrentDirectory(), "Files", p.ownerId); + if (!Directory.Exists(folderPath)) + { + Directory.CreateDirectory(folderPath); + } + + foreach (var image in post.images) + { + var filename = image.FileName; + var ext=Path.GetExtension(filename).ToLowerInvariant(); + var name = Path.GetFileNameWithoutExtension(filename).ToLowerInvariant(); + var fullPath=Path.Combine(folderPath, name); + int i = 0; + while (System.IO.File.Exists(fullPath)) + { + i++; + fullPath=Path.Combine(folderPath, name+i.ToString()+ext); + } + using(var stream=new FileStream(fullPath, FileMode.Create)) + { + await image.CopyToAsync(stream); + } + var f = new Models.File(); + f.path = fullPath; + f._id = ""; + f=await _fileService.add(f); + p.images.Add(f); + + } + await _posts.InsertOneAsync(p); + + + + + + + + + return postToPostSend(p); + + } + public PostSend postToPostSend(Post post) + { + PostSend p = new PostSend(); + //Convert post to post send (TODO) + p._id = post._id; + return p; + } + + public async Task<List<PostSend>> getAllPosts() + { + List<Post> posts = await _posts.Find(_ => true).ToListAsync(); + List<PostSend> temp = new List<PostSend>(); + foreach (var post in posts) + { + temp.Add(postToPostSend(post)); + } + return temp; + } + + public async Task<PostSend> getPostById(string id) + { + Post p = await _posts.Find(post => post._id == id).FirstOrDefaultAsync(); + return postToPostSend(p); + + } + //(TODO) ADD Delete and update + } +} diff --git a/Backend/Api/Api/appsettings.json b/Backend/Api/Api/appsettings.json index 74cfa27..b7f25b2 100644 --- a/Backend/Api/Api/appsettings.json +++ b/Backend/Api/Api/appsettings.json @@ -11,13 +11,16 @@ } }, "AllowedHosts": "*", - "DatabaseSettings": { + "DatabaseSettings": { - "ConnectionString": "mongodb://127.0.0.1:27017/", - "DatabaseName": "Odyssey", - "UserCollectionName": "users" + "ConnectionString": "mongodb://127.0.0.1:27017/", + "DatabaseName": "Odyssey", + "UserCollectionName": "users", + "PostCollectionName": "posts", + "FileCollectionName": "files", + "LocationCollectionname": "locations" - }, + }, "EmailCfg": { "Email": "oddyssey.brzodolokacije@gmail.com", "SmtpServer": "smtp.gmail.com", diff --git a/Client/BrzoDoLokacije/app/src/main/java/com/example/brzodolokacije/Fragments/FragmentAddRecension.kt b/Client/BrzoDoLokacije/app/src/main/java/com/example/brzodolokacije/Fragments/FragmentAddRecension.kt new file mode 100644 index 0000000..16709f7 --- /dev/null +++ b/Client/BrzoDoLokacije/app/src/main/java/com/example/brzodolokacije/Fragments/FragmentAddRecension.kt @@ -0,0 +1,60 @@ +package com.example.brzodolokacije.Fragments + +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.example.brzodolokacije.R + +// TODO: Rename parameter arguments, choose names that match +// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER +private const val ARG_PARAM1 = "param1" +private const val ARG_PARAM2 = "param2" + +/** + * A simple [Fragment] subclass. + * Use the [FragmentAddRecension.newInstance] factory method to + * create an instance of this fragment. + */ +class FragmentAddRecension : Fragment() { + // TODO: Rename and change types of parameters + private var param1: String? = null + private var param2: String? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + arguments?.let { + param1 = it.getString(ARG_PARAM1) + param2 = it.getString(ARG_PARAM2) + } + } + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + // Inflate the layout for this fragment + return inflater.inflate(R.layout.fragment_add_recension, container, false) + } + + companion object { + /** + * Use this factory method to create a new instance of + * this fragment using the provided parameters. + * + * @param param1 Parameter 1. + * @param param2 Parameter 2. + * @return A new instance of fragment FragmentAddRecension. + */ + // TODO: Rename and change types and number of parameters + @JvmStatic + fun newInstance(param1: String, param2: String) = + FragmentAddRecension().apply { + arguments = Bundle().apply { + putString(ARG_PARAM1, param1) + putString(ARG_PARAM2, param2) + } + } + } +}
\ No newline at end of file diff --git a/Client/BrzoDoLokacije/app/src/main/java/com/example/brzodolokacije/Models/File.kt b/Client/BrzoDoLokacije/app/src/main/java/com/example/brzodolokacije/Models/File.kt new file mode 100644 index 0000000..030f658 --- /dev/null +++ b/Client/BrzoDoLokacije/app/src/main/java/com/example/brzodolokacije/Models/File.kt @@ -0,0 +1,6 @@ +package com.example.brzodolokacije.Models + +data class File ( + var _id:String, + var path:String + )
\ No newline at end of file diff --git a/Client/BrzoDoLokacije/app/src/main/java/com/example/brzodolokacije/Models/Location.kt b/Client/BrzoDoLokacije/app/src/main/java/com/example/brzodolokacije/Models/Location.kt index ab47149..25125cd 100644 --- a/Client/BrzoDoLokacije/app/src/main/java/com/example/brzodolokacije/Models/Location.kt +++ b/Client/BrzoDoLokacije/app/src/main/java/com/example/brzodolokacije/Models/Location.kt @@ -1,12 +1,14 @@ package com.example.brzodolokacije.Models -data class Location( - val _id:String, - val name:String, - val city:String, - val country:String, - val address:String, - val latitude:String, - val longitude:String, - val type:LocationTypes -) +import com.example.brzodolokacije.Models.LocationType + +data class Location ( + var _id:String, + var name:String, + var city:String, + var country:String, + var adress:String, + var latitude:Double, + var longitude:Double, + var type:LocationType + ) diff --git a/Client/BrzoDoLokacije/app/src/main/java/com/example/brzodolokacije/Models/LocationType.kt b/Client/BrzoDoLokacije/app/src/main/java/com/example/brzodolokacije/Models/LocationType.kt new file mode 100644 index 0000000..a078863 --- /dev/null +++ b/Client/BrzoDoLokacije/app/src/main/java/com/example/brzodolokacije/Models/LocationType.kt @@ -0,0 +1,7 @@ +package com.example.brzodolokacije.Models + +enum class LocationType { + GRAD,ULICA,JEZERO,REKA,PLAZA,OKEAN, MORE, MOREUZ, MOST,BANJA, + PLANINA, VISORAVAN, PIRAMIDA, LIVADA, SELO, OSTRVO, POLUOSTRVO, KLISURA, ARHIPELAG, + ADA, DELTA, FJORD, GEJZIR, IZVOR, KOTLINA, MINERALNI_IZVOR, PECINA ,SUMA, VODOPAD,VULKAN +}
\ No newline at end of file diff --git a/Client/BrzoDoLokacije/app/src/main/java/com/example/brzodolokacije/Models/LocationTypes.kt b/Client/BrzoDoLokacije/app/src/main/java/com/example/brzodolokacije/Models/LocationTypes.kt deleted file mode 100644 index cc61526..0000000 --- a/Client/BrzoDoLokacije/app/src/main/java/com/example/brzodolokacije/Models/LocationTypes.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.example.brzodolokacije.Models - -enum class LocationTypes { - Plaza, - Grad, - Zgrada, - Setaliste -}
\ No newline at end of file diff --git a/Client/BrzoDoLokacije/app/src/main/java/com/example/brzodolokacije/Models/Post.kt b/Client/BrzoDoLokacije/app/src/main/java/com/example/brzodolokacije/Models/Post.kt index 4359cea..3fa1f70 100644 --- a/Client/BrzoDoLokacije/app/src/main/java/com/example/brzodolokacije/Models/Post.kt +++ b/Client/BrzoDoLokacije/app/src/main/java/com/example/brzodolokacije/Models/Post.kt @@ -1,15 +1,30 @@ package com.example.brzodolokacije.Models -import java.io.File +import java.time.LocalDateTime import java.util.* data class Post ( var _id:String, - var ownerId:String, var location:Location, + var ownerId:String, var description:String, - var ratings:Number, - var views:Int, - var reviews:List<String>, + var views:List<String>, + var reports:List<String>, + var ratings:List<Rating>, + var comments:List<Comment>, var images:List<File> - )
\ No newline at end of file + + + ) + +data class Comment ( + var userId:String, + var comment:String, + var parent:Comment, + var timeStamp: LocalDateTime +) + +data class Rating( + var useridval :String, + var rating:Int +)
\ No newline at end of file diff --git a/Client/BrzoDoLokacije/app/src/main/res/layout/fragment_add_post.xml b/Client/BrzoDoLokacije/app/src/main/res/layout/fragment_add_post.xml index 2cf3c8c..c106fcd 100644 --- a/Client/BrzoDoLokacije/app/src/main/res/layout/fragment_add_post.xml +++ b/Client/BrzoDoLokacije/app/src/main/res/layout/fragment_add_post.xml @@ -6,10 +6,14 @@ tools:context=".Fragments.FragmentAddPost"> <!-- TODO: Update blank fragment layout --> + <TextView android:layout_width="match_parent" - android:layout_height="match_parent" - android:text="Post Add" /> + android:layout_height="20dp"/> + + + + <!-- <Button android:id="@+id/btnFragmentAddLogOut" @@ -18,6 +22,6 @@ android:layout_marginTop="40dp" android:background="@drawable/rounded_cyan_button" android:backgroundTint="#1C789A" - android:text="Log Out" /> + android:text="Log Out" />--> </FrameLayout>
\ No newline at end of file diff --git a/Client/BrzoDoLokacije/app/src/main/res/layout/fragment_add_recension.xml b/Client/BrzoDoLokacije/app/src/main/res/layout/fragment_add_recension.xml new file mode 100644 index 0000000..433ae0e --- /dev/null +++ b/Client/BrzoDoLokacije/app/src/main/res/layout/fragment_add_recension.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + tools:context=".Fragments.FragmentAddRecension"> + + <!-- TODO: Update blank fragment layout --> + <TextView + android:layout_width="match_parent" + android:layout_height="match_parent" /> + +</FrameLayout>
\ No newline at end of file diff --git a/Client/BrzoDoLokacije/app/src/main/res/layout/post_preview.xml b/Client/BrzoDoLokacije/app/src/main/res/layout/post_preview.xml index e8ad501..a3642e2 100644 --- a/Client/BrzoDoLokacije/app/src/main/res/layout/post_preview.xml +++ b/Client/BrzoDoLokacije/app/src/main/res/layout/post_preview.xml @@ -4,7 +4,9 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_margin="5dp" + android:layout_padding="5dp" + android:layout_margin="0dp" + android:layout_margin="0dp" android:background="@drawable/rounded_picture_background" android:clipToOutline="true" > |