diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 7b7d472..e2c6abd 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -44,6 +44,7 @@
+
+
+ @GET("hashtags/trending")
+ fun getTrendingHashtags(): Call
+
+ @GET("Search/newestUsers")
+ fun getNewestUsers(@Query("limit") limit: Int, @Query("after") after: Int?): Call
}
\ No newline at end of file
diff --git a/app/src/main/java/com/isolaatti/search/data/SearchDao.kt b/app/src/main/java/com/isolaatti/search/data/SearchDao.kt
index 938a7b5..6e291f3 100644
--- a/app/src/main/java/com/isolaatti/search/data/SearchDao.kt
+++ b/app/src/main/java/com/isolaatti/search/data/SearchDao.kt
@@ -7,15 +7,32 @@ import androidx.room.Delete
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
+import androidx.room.Transaction
@Dao
interface SearchDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertTerm(searchTerm: SearchHistoryEntity)
- @Delete
- fun deleteTerm(searchTerm: SearchHistoryEntity)
+ @Query("SELECT COUNT(*) FROM search_history WHERE `query` = :query")
+ fun itemCount(query: String): Int
- @Query("SELECT * FROM search_history ORDER BY time DESC")
- fun getTerms(): LiveData>
+ @Transaction
+ fun insertTermTransaction(searchTerm: SearchHistoryEntity) {
+ if(itemCount(searchTerm.query) > 0) {
+ deleteTerm(searchTerm.query)
+ }
+ insertTerm(searchTerm)
+ }
+
+ @Query("DELETE FROM search_history WHERE `query` = :searchTerm")
+ fun deleteTerm(searchTerm: String)
+
+ @Query("SELECT *, `rowid` FROM search_history ORDER BY time DESC")
+ fun getTerms(): List
+
+ @Query("""
+ SELECT *, `rowid` FROM search_history WHERE `query` MATCH :searchQuery
+ """)
+ fun getTermsForQuery(searchQuery: String): List
}
\ No newline at end of file
diff --git a/app/src/main/java/com/isolaatti/search/data/SearchDto.kt b/app/src/main/java/com/isolaatti/search/data/SearchDto.kt
new file mode 100644
index 0000000..fbf44c6
--- /dev/null
+++ b/app/src/main/java/com/isolaatti/search/data/SearchDto.kt
@@ -0,0 +1,15 @@
+package com.isolaatti.search.data
+
+import com.isolaatti.posting.posts.data.remote.FeedDto
+
+
+data class ProfileSearchDto(val id: Int, val name: String, val imageId: String?, val following: Boolean)
+data class SearchDto(
+ val profiles: List,
+ val posts: List
+ // TODO add the other types
+)
+
+data class HashtagsDto(val result: List)
+
+data class NewestUsersDto(val result: List)
\ No newline at end of file
diff --git a/app/src/main/java/com/isolaatti/search/data/SearchHistoryEntity.kt b/app/src/main/java/com/isolaatti/search/data/SearchHistoryEntity.kt
index 0bb5177..b0d7b72 100644
--- a/app/src/main/java/com/isolaatti/search/data/SearchHistoryEntity.kt
+++ b/app/src/main/java/com/isolaatti/search/data/SearchHistoryEntity.kt
@@ -1,10 +1,17 @@
package com.isolaatti.search.data
+import androidx.room.ColumnInfo
import androidx.room.Entity
+import androidx.room.Fts4
+import androidx.room.Index
import androidx.room.PrimaryKey
@Entity(tableName = "search_history")
+@Fts4
data class SearchHistoryEntity(
- @PrimaryKey val query: String,
+ @PrimaryKey
+ @ColumnInfo(name = "rowid")
+ val rowId: Int,
+ val query: String,
val time: Long
)
diff --git a/app/src/main/java/com/isolaatti/search/data/SearchRepositoryImpl.kt b/app/src/main/java/com/isolaatti/search/data/SearchRepositoryImpl.kt
new file mode 100644
index 0000000..54bd9fe
--- /dev/null
+++ b/app/src/main/java/com/isolaatti/search/data/SearchRepositoryImpl.kt
@@ -0,0 +1,88 @@
+package com.isolaatti.search.data
+
+import android.util.Log
+import com.isolaatti.search.domain.SearchRepository
+import com.isolaatti.utils.Resource
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flow
+import retrofit2.awaitResponse
+
+class SearchRepositoryImpl(private val searchApi: SearchApi, private val searchDao: SearchDao) : SearchRepository {
+ companion object {
+ const val LOG_TAG = "SearchRepositoryImpl"
+ }
+ override fun search(query: String): Flow> = flow {
+ searchDao.insertTermTransaction(SearchHistoryEntity(0, query, System.currentTimeMillis()))
+
+ try {
+ val response = searchApi.quickSearch(query).awaitResponse()
+ if(response.isSuccessful) {
+ val dto = response.body()
+ if(dto == null) {
+ Log.e(LOG_TAG, "Emitting error, could not get body")
+ emit(Resource.Error(Resource.Error.ErrorType.OtherError))
+ return@flow
+ }
+ emit(Resource.Success(dto))
+
+ } else {
+ emit(Resource.Error(Resource.Error.mapErrorCode(response.code())))
+ }
+ } catch(e: Exception) {
+ Log.e(LOG_TAG, "Error searching: ${e.message}")
+ emit(Resource.Error(Resource.Error.ErrorType.NetworkError))
+ }
+ }
+
+ override fun getSuggestions(query: String): Flow>> = flow {
+ emit(Resource.Loading())
+
+ if(query.isEmpty()) {
+ emit(Resource.Success(searchDao.getTerms()))
+ } else {
+ emit(Resource.Success(searchDao.getTermsForQuery(query)))
+ }
+
+ }
+
+ override fun removeSuggestion(query: String): Flow> = flow {
+ searchDao.deleteTerm(query)
+ emit(Resource.Success(true))
+ }
+
+ override fun getTrendingHashtags(): Flow> = flow {
+ emit(Resource.Loading())
+
+ try {
+ val result = searchApi.getTrendingHashtags().awaitResponse()
+ if(result.isSuccessful) {
+ val dto = result.body()
+ dto?.also { emit(Resource.Success(it)) }
+ } else {
+ emit(Resource.Error(Resource.Error.mapErrorCode(result.code())))
+ }
+ } catch(e: Exception) {
+ Log.e(LOG_TAG, "Error getting trending hashtags: ${e.message}")
+ }
+ }
+
+ override fun getNewestUsers(): Flow> = flow {
+ emit(Resource.Loading())
+
+ try {
+ val result = searchApi.getNewestUsers(10, null).awaitResponse()
+
+ if(result.isSuccessful) {
+ val dto = result.body()
+ if(dto != null) {
+ emit(Resource.Success(dto))
+ }
+
+ } else {
+ emit(Resource.Error(Resource.Error.mapErrorCode(result.code())))
+ }
+ } catch(e: Exception) {
+ Log.e(LOG_TAG, "Error getting newest users: ${e.message}")
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/isolaatti/search/domain/SearchRepository.kt b/app/src/main/java/com/isolaatti/search/domain/SearchRepository.kt
index 81e4611..9e80e38 100644
--- a/app/src/main/java/com/isolaatti/search/domain/SearchRepository.kt
+++ b/app/src/main/java/com/isolaatti/search/domain/SearchRepository.kt
@@ -1,8 +1,19 @@
package com.isolaatti.search.domain
+import com.isolaatti.search.data.HashtagsDto
+import com.isolaatti.search.data.NewestUsersDto
+import com.isolaatti.search.data.SearchDto
+import com.isolaatti.search.data.SearchHistoryEntity
import com.isolaatti.utils.Resource
import kotlinx.coroutines.flow.Flow
interface SearchRepository {
+ fun search(query: String): Flow>
+ fun getSuggestions(query: String): Flow>>
+
+ fun removeSuggestion(query: String): Flow>
+
+ fun getTrendingHashtags(): Flow>
+ fun getNewestUsers(): Flow>
}
\ No newline at end of file
diff --git a/app/src/main/java/com/isolaatti/search/presentation/SearchSuggestionsAdapter.kt b/app/src/main/java/com/isolaatti/search/presentation/SearchSuggestionsAdapter.kt
new file mode 100644
index 0000000..81f3dcb
--- /dev/null
+++ b/app/src/main/java/com/isolaatti/search/presentation/SearchSuggestionsAdapter.kt
@@ -0,0 +1,46 @@
+package com.isolaatti.search.presentation
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.ListAdapter
+import androidx.recyclerview.widget.RecyclerView.ViewHolder
+import com.isolaatti.databinding.SearchSuggestionItemBinding
+import com.isolaatti.search.data.SearchHistoryEntity
+
+class SearchSuggestionsAdapter(
+ private val onItemClick: (item: SearchHistoryEntity) -> Unit = {},
+ private val onItemDeleteClick: (item: SearchHistoryEntity) -> Unit = {}
+) : ListAdapter(itemCallback) {
+
+ inner class SearchSuggestionItemViewHolder(val searchSuggestionItemBinding: SearchSuggestionItemBinding) : ViewHolder(searchSuggestionItemBinding.root)
+
+ companion object {
+ val itemCallback = object: DiffUtil.ItemCallback() {
+ override fun areItemsTheSame(oldItem: SearchHistoryEntity, newItem: SearchHistoryEntity): Boolean {
+ return oldItem == newItem
+ }
+
+ override fun areContentsTheSame(oldItem: SearchHistoryEntity, newItem: SearchHistoryEntity): Boolean {
+ return oldItem.query == newItem.query
+ }
+
+ }
+ }
+
+ override fun onCreateViewHolder(
+ parent: ViewGroup,
+ viewType: Int
+ ): SearchSuggestionItemViewHolder {
+ return SearchSuggestionItemViewHolder(SearchSuggestionItemBinding.inflate(LayoutInflater.from(parent.context), parent, false))
+ }
+
+ override fun onBindViewHolder(holder: SearchSuggestionItemViewHolder, position: Int) {
+ val searchSuggestion = getItem(position)
+
+ holder.searchSuggestionItemBinding.suggestionText.text = searchSuggestion.query
+
+ holder.searchSuggestionItemBinding.root.setOnClickListener { onItemClick(searchSuggestion) }
+ holder.searchSuggestionItemBinding.deleteButton.setOnClickListener { onItemDeleteClick(searchSuggestion) }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/isolaatti/search/presentation/SearchViewModel.kt b/app/src/main/java/com/isolaatti/search/presentation/SearchViewModel.kt
index d4a0e80..198ec5c 100644
--- a/app/src/main/java/com/isolaatti/search/presentation/SearchViewModel.kt
+++ b/app/src/main/java/com/isolaatti/search/presentation/SearchViewModel.kt
@@ -1,20 +1,111 @@
package com.isolaatti.search.presentation
-import androidx.lifecycle.LiveData
+import android.util.Log
+import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
-import com.isolaatti.search.data.SearchApi
-import com.isolaatti.search.data.SearchDao
+import com.isolaatti.search.data.HashtagsDto
+import com.isolaatti.search.data.NewestUsersDto
+import com.isolaatti.search.data.SearchDto
+import com.isolaatti.search.data.SearchHistoryEntity
+import com.isolaatti.search.domain.SearchRepository
+import com.isolaatti.utils.Resource
import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
-class SearchViewModel @Inject constructor() : ViewModel() {
- fun search(query: String) {
+class SearchViewModel @Inject constructor(private val searchRepository: SearchRepository) : ViewModel() {
+ companion object {
+ const val LOG_TAG = "SearchViewModel"
+ }
+
+ val searchSuggestions: MutableLiveData> = MutableLiveData()
+ val searchResults: MutableLiveData = MutableLiveData()
+ val trendingHashtags: MutableLiveData = MutableLiveData()
+ val newestUsers: MutableLiveData = MutableLiveData()
+
+ var searchQuery = ""
+
+ fun search() {
+ Log.d(LOG_TAG, searchQuery)
viewModelScope.launch {
+ if(searchQuery.isNotEmpty()) {
+ searchRepository.search(searchQuery).onEach {
+ when(it) {
+ is Resource.Error -> {}
+ is Resource.Loading -> {}
+ is Resource.Success -> {
+ searchResults.postValue(it.data!!)
+ }
+ }
+ }.flowOn(Dispatchers.IO).launchIn(this)
+ }
+
+
}
}
+ fun getSuggestions(query: String) {
+ searchQuery = query
+ viewModelScope.launch {
+ searchRepository.getSuggestions(query).onEach {
+ when(it) {
+ is Resource.Error -> {}
+ is Resource.Loading -> {}
+ is Resource.Success -> {
+ searchSuggestions.postValue(it.data!!)
+ }
+ }
+ }.flowOn(Dispatchers.IO).launchIn(this)
+ }
+ }
+
+ fun deleteSuggestion(suggestion: SearchHistoryEntity) {
+ viewModelScope.launch {
+ searchRepository.removeSuggestion(suggestion.query).onEach { resource ->
+ if(resource is Resource.Success && resource.data == true) {
+ val searchSuggestionsMutable = searchSuggestions.value?.toMutableList()
+ searchSuggestionsMutable?.remove(suggestion)
+
+ searchSuggestionsMutable?.also { searchSuggestions.postValue(it) }
+ }
+ }.flowOn(Dispatchers.IO).launchIn(this)
+ }
+ }
+
+ fun loadTendingHashtags() {
+ viewModelScope.launch {
+ searchRepository.getTrendingHashtags().onEach { resource ->
+ when(resource) {
+ is Resource.Error -> {}
+ is Resource.Loading -> {}
+ is Resource.Success -> {
+ resource.data?.also { trendingHashtags.postValue(it) }
+ }
+ }
+ }.flowOn(Dispatchers.IO).launchIn(this)
+ }
+ }
+
+ fun loadNewestUsers() {
+ viewModelScope.launch {
+ searchRepository.getNewestUsers().onEach { resource ->
+ when(resource) {
+ is Resource.Error -> {}
+ is Resource.Loading -> {}
+ is Resource.Success -> {
+ resource.data?.also { newestUsers.postValue(it) }
+
+ }
+ }
+ }.flowOn(Dispatchers.IO).launchIn(this)
+ }
+ }
+
}
\ No newline at end of file
diff --git a/app/src/main/java/com/isolaatti/search/presentation/UserCarouselAdapter.kt b/app/src/main/java/com/isolaatti/search/presentation/UserCarouselAdapter.kt
new file mode 100644
index 0000000..a8a21ca
--- /dev/null
+++ b/app/src/main/java/com/isolaatti/search/presentation/UserCarouselAdapter.kt
@@ -0,0 +1,58 @@
+package com.isolaatti.search.presentation
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.ListAdapter
+import androidx.recyclerview.widget.RecyclerView.ViewHolder
+import coil.load
+import com.isolaatti.R
+import com.isolaatti.databinding.UsersCarouselItemBinding
+import com.isolaatti.search.data.ProfileSearchDto
+import com.isolaatti.utils.UrlGen
+
+class UserCarouselAdapter(
+ private val onProfileClick: (profileId: Int) -> Unit = {}
+) : ListAdapter(itemCallback) {
+
+ companion object {
+ val itemCallback = object: DiffUtil.ItemCallback() {
+ override fun areItemsTheSame(
+ oldItem: ProfileSearchDto,
+ newItem: ProfileSearchDto
+ ): Boolean {
+ return oldItem.id == newItem.id
+ }
+
+ override fun areContentsTheSame(
+ oldItem: ProfileSearchDto,
+ newItem: ProfileSearchDto
+ ): Boolean {
+ return oldItem == newItem
+ }
+
+ }
+ }
+
+ inner class UserCarouselItemViewHolder(val usersCarouselItemBinding: UsersCarouselItemBinding) : ViewHolder(usersCarouselItemBinding.root)
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserCarouselItemViewHolder {
+ return UserCarouselItemViewHolder(UsersCarouselItemBinding.inflate(LayoutInflater.from(parent.context), parent, false))
+ }
+
+ override fun onBindViewHolder(holder: UserCarouselItemViewHolder, position: Int) {
+ val user = getItem(position)
+
+ holder.usersCarouselItemBinding.userCarouselName.text = user.name
+ if(user.imageId != null) {
+ holder.usersCarouselItemBinding.userCarouselImageView.load(UrlGen.imageUrl(user.imageId))
+ } else {
+ holder.usersCarouselItemBinding.userCarouselImageView.load(R.drawable.avatar)
+ }
+
+ holder.usersCarouselItemBinding.userCarouselCard.setOnClickListener { onProfileClick(user.id) }
+
+ }
+
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/isolaatti/search/ui/SearchFragment.kt b/app/src/main/java/com/isolaatti/search/ui/SearchFragment.kt
index f3e85bb..d67e85d 100644
--- a/app/src/main/java/com/isolaatti/search/ui/SearchFragment.kt
+++ b/app/src/main/java/com/isolaatti/search/ui/SearchFragment.kt
@@ -1,19 +1,76 @@
package com.isolaatti.search.ui
+import android.content.Intent
import android.os.Bundle
+import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.widget.doAfterTextChanged
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
+import androidx.lifecycle.Observer
+import androidx.recyclerview.widget.LinearLayoutManager
+import com.google.android.material.carousel.CarouselLayoutManager
+import com.google.android.material.carousel.UncontainedCarouselStrategy
+import com.google.android.material.chip.Chip
+import com.isolaatti.R
import com.isolaatti.databinding.FragmentSearchBinding
+import com.isolaatti.hashtags.ui.HashtagsActivity
+import com.isolaatti.profile.ui.ProfileActivity
+import com.isolaatti.search.data.HashtagsDto
+import com.isolaatti.search.data.NewestUsersDto
+import com.isolaatti.search.data.SearchDto
+import com.isolaatti.search.data.SearchHistoryEntity
+import com.isolaatti.search.presentation.SearchSuggestionsAdapter
import com.isolaatti.search.presentation.SearchViewModel
+import com.isolaatti.search.presentation.UserCarouselAdapter
+import dagger.hilt.android.AndroidEntryPoint
+@AndroidEntryPoint
class SearchFragment : Fragment() {
+ companion object {
+ const val LOG_TAG = "SearchFragment"
+ }
+
lateinit var viewBinding: FragmentSearchBinding
private val viewModel: SearchViewModel by viewModels()
+ private var searchSuggestionsAdapter: SearchSuggestionsAdapter? = null
+ private var newestUsersAdapter: UserCarouselAdapter? = null
+
+ private val searchSuggestionsObserver: Observer> = Observer {
+ searchSuggestionsAdapter?.submitList(it)
+ }
+
+ private val searchResultsObserver: Observer = Observer {
+ Log.d(LOG_TAG, it.toString())
+ }
+
+ private val trendingHashtagsObserver: Observer = Observer {
+ Log.d(LOG_TAG, it.toString())
+
+ viewBinding.chipGroup.removeAllViews()
+ it.result.forEach { hashtag ->
+ viewBinding.chipGroup.addView(Chip(requireContext()).apply {
+ text = "#$hashtag"
+ setOnClickListener {
+ requireContext().startActivity(Intent(requireContext(), HashtagsActivity::class.java))
+ }
+ })
+ }
+ }
+
+ private val newestUsersObserver: Observer = Observer {
+ newestUsersAdapter?.submitList(it.result)
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ viewModel.loadTendingHashtags()
+ viewModel.loadNewestUsers()
+ }
override fun onCreateView(
inflater: LayoutInflater,
@@ -24,23 +81,83 @@ class SearchFragment : Fragment() {
return viewBinding.root
}
+ private fun showResults(show: Boolean) {
+ if(show) {
+ viewBinding.viewAnimator.displayedChild = 1
+ viewBinding.searchBar.menu.findItem(R.id.close_button)?.setVisible(true)
+ } else {
+ viewBinding.viewAnimator.displayedChild = 0
+ viewModel.searchQuery = ""
+ viewBinding.searchBar.setText("")
+ viewBinding.searchBar.menu.findItem(R.id.close_button)?.setVisible(false)
+ }
+ }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupListeners()
setupObservers()
+ searchSuggestionsAdapter = SearchSuggestionsAdapter(
+ onItemClick = {
+ viewBinding.searchBar.setText(it.query)
+ viewModel.searchQuery = it.query
+ viewBinding.searchView.hide()
+ viewModel.search()
+ showResults(true)
+ },
+ onItemDeleteClick = {
+ viewModel.deleteSuggestion(it)
+ }
+ )
+ viewBinding.searchHistoryRecyclerView.apply {
+ adapter = searchSuggestionsAdapter
+ layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false)
+ }
+
+ newestUsersAdapter = UserCarouselAdapter(
+ onProfileClick = {
+ ProfileActivity.startActivity(requireContext(), it)
+ }
+ )
+ viewBinding.newUsersRecyclerView.apply {
+ adapter = newestUsersAdapter
+ layoutManager = CarouselLayoutManager(UncontainedCarouselStrategy())
+ }
+
}
private fun setupListeners() {
viewBinding.searchView.editText.doAfterTextChanged { searchText ->
if(searchText != null) {
- viewModel.search(searchText.toString())
+ viewModel.getSuggestions(searchText.toString())
}
}
+
+ viewBinding.searchView.editText.setOnEditorActionListener { v, actionId, event ->
+ Log.d(LOG_TAG, "actionId: $actionId; event: $event")
+ viewBinding.searchBar.setText(viewBinding.searchView.text)
+ viewBinding.searchView.hide()
+ viewModel.search()
+ showResults(true)
+ true
+ }
+
+ viewBinding.searchBar.setOnMenuItemClickListener {
+ if(it.itemId == R.id.close_button) {
+ showResults(false)
+ true
+ }
+ false
+ }
+
+ viewBinding.searchView.addTransitionListener { searchView, transitionState, transitionState2 -> }
}
private fun setupObservers() {
-
+ viewModel.searchSuggestions.observe(viewLifecycleOwner, searchSuggestionsObserver)
+ viewModel.searchResults.observe(viewLifecycleOwner, searchResultsObserver)
+ viewModel.trendingHashtags.observe(viewLifecycleOwner, trendingHashtagsObserver)
+ viewModel.newestUsers.observe(viewLifecycleOwner, newestUsersObserver)
}
}
\ No newline at end of file
diff --git a/app/src/main/res/drawable/avatar.xml b/app/src/main/res/drawable/avatar.xml
new file mode 100644
index 0000000..65ae8f9
--- /dev/null
+++ b/app/src/main/res/drawable/avatar.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/clock_rotate_left_solid.xml b/app/src/main/res/drawable/clock_rotate_left_solid.xml
new file mode 100644
index 0000000..8a2984d
--- /dev/null
+++ b/app/src/main/res/drawable/clock_rotate_left_solid.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/markdown.xml b/app/src/main/res/drawable/markdown.xml
new file mode 100644
index 0000000..8984b3e
--- /dev/null
+++ b/app/src/main/res/drawable/markdown.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/layout/fragment_search.xml b/app/src/main/res/layout/fragment_search.xml
index fbac861..c253351 100644
--- a/app/src/main/res/layout/fragment_search.xml
+++ b/app/src/main/res/layout/fragment_search.xml
@@ -2,16 +2,111 @@
-
-
-
+ app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ android:hint="@string/searchbar_hint"
+ app:menu="@menu/search_bar_menu"/>
diff --git a/app/src/main/res/layout/search_suggestion_item.xml b/app/src/main/res/layout/search_suggestion_item.xml
new file mode 100644
index 0000000..5efbb83
--- /dev/null
+++ b/app/src/main/res/layout/search_suggestion_item.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/users_carousel_item.xml b/app/src/main/res/layout/users_carousel_item.xml
new file mode 100644
index 0000000..79ee15e
--- /dev/null
+++ b/app/src/main/res/layout/users_carousel_item.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/search_bar_menu.xml b/app/src/main/res/menu/search_bar_menu.xml
new file mode 100644
index 0000000..92ed0c5
--- /dev/null
+++ b/app/src/main/res/menu/search_bar_menu.xml
@@ -0,0 +1,9 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 5e56918..031087b 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -198,4 +198,7 @@
Delete notification
Remove this notification?
An error ocurred
+ Hashtags
+ Newest profiles
+ See all
\ No newline at end of file