Skip to content

Feature: Bookmark Categories #6035

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Dec 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import fr.free.nrw.commons.Media;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.bookmarks.category.BookmarkCategoriesFragment;
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsFragment;
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsFragment;
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesFragment;
Expand Down Expand Up @@ -48,14 +48,21 @@ public BookmarkListRootFragment(Bundle bundle, BookmarksPagerAdapter bookmarksPa
String title = bundle.getString("categoryName");
int order = bundle.getInt("order");
final int orderItem = bundle.getInt("orderItem");
if (order == 0) {
listFragment = new BookmarkPicturesFragment();
} else {
listFragment = new BookmarkLocationsFragment();

switch (order){
case 0: listFragment = new BookmarkPicturesFragment();
break;

case 1: listFragment = new BookmarkLocationsFragment();
break;

case 3: listFragment = new BookmarkCategoriesFragment();
break;
}
if(orderItem == 2) {
listFragment = new BookmarkItemsFragment();
}
}

Bundle featuredArguments = new Bundle();
featuredArguments.putString("categoryName", title);
listFragment.setArguments(featuredArguments);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@ public class BookmarksPagerAdapter extends FragmentPagerAdapter {
new BookmarkListRootFragment(locationBundle, this),
context.getString(R.string.title_page_bookmarks_items)));
}
final Bundle categoriesBundle = new Bundle();
categoriesBundle.putString("categoryName",
context.getString(R.string.title_page_bookmarks_categories));
categoriesBundle.putInt("order", 3);
pages.add(new BookmarkPages(
new BookmarkListRootFragment(categoriesBundle, this),
context.getString(R.string.title_page_bookmarks_categories)));
notifyDataSetChanged();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package fr.free.nrw.commons.bookmarks.category

import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import kotlinx.coroutines.flow.Flow

/**
* Bookmark categories dao
*
* @constructor Create empty Bookmark categories dao
*/
@Dao
interface BookmarkCategoriesDao {

/**
* Insert or Delete category bookmark into DB
*
* @param bookmarksCategoryModal
*/
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(bookmarksCategoryModal: BookmarksCategoryModal)


/**
* Delete category bookmark from DB
*
* @param bookmarksCategoryModal
*/
@Delete
suspend fun delete(bookmarksCategoryModal: BookmarksCategoryModal)

/**
* Checks if given category exist in DB
*
* @param categoryName
* @return
*/
@Query("SELECT EXISTS (SELECT 1 FROM bookmarks_categories WHERE categoryName = :categoryName)")
suspend fun doesExist(categoryName: String): Boolean

/**
* Get all categories
*
* @return
*/
@Query("SELECT * FROM bookmarks_categories")
fun getAllCategories(): Flow<List<BookmarksCategoryModal>>

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
package fr.free.nrw.commons.bookmarks.category

import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import dagger.android.support.DaggerFragment
import fr.free.nrw.commons.R
import fr.free.nrw.commons.category.CategoryDetailsActivity
import javax.inject.Inject

/**
* Tab fragment to show list of bookmarked Categories
*/
class BookmarkCategoriesFragment : DaggerFragment() {

@Inject
lateinit var bookmarkCategoriesDao: BookmarkCategoriesDao

override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
MaterialTheme(
colorScheme = if (isSystemInDarkTheme()) darkColorScheme(
primary = colorResource(R.color.primaryDarkColor),
surface = colorResource(R.color.main_background_dark),
background = colorResource(R.color.main_background_dark)
) else lightColorScheme(
primary = colorResource(R.color.primaryColor),
surface = colorResource(R.color.main_background_light),
background = colorResource(R.color.main_background_light)
)
) {
val listOfBookmarks by bookmarkCategoriesDao.getAllCategories()
.collectAsStateWithLifecycle(initialValue = emptyList())
Surface(modifier = Modifier.fillMaxSize()) {
Box(contentAlignment = Alignment.Center) {
if (listOfBookmarks.isEmpty()) {
Text(
text = stringResource(R.string.bookmark_empty),
style = MaterialTheme.typography.bodyMedium,
color = if (isSystemInDarkTheme()) Color(0xB3FFFFFF)
else Color(
0x8A000000
)
)
} else {
LazyColumn(modifier = Modifier.fillMaxSize()) {
items(items = listOfBookmarks) { bookmarkItem ->
CategoryItem(
categoryName = bookmarkItem.categoryName,
onClick = {
val categoryDetailsIntent = Intent(
requireContext(),
CategoryDetailsActivity::class.java
).putExtra("categoryName", it)
startActivity(categoryDetailsIntent)
}
)
}
}
}
}
}
}
}
}
}


@Composable
fun CategoryItem(
modifier: Modifier = Modifier,
onClick: (String) -> Unit,
categoryName: String
) {
Row(modifier = modifier.clickable {
onClick(categoryName)
}) {
ListItem(
leadingContent = {
Image(
modifier = Modifier.size(48.dp),
painter = painterResource(R.drawable.commons),
contentDescription = null
)
},
headlineContent = {
Text(
text = categoryName,
maxLines = 2,
color = if (isSystemInDarkTheme()) Color.White else Color.Black,
style = MaterialTheme.typography.bodyMedium,
fontWeight = FontWeight.SemiBold
)
}
)
}
}

@Preview
@Composable
private fun CategoryItemPreview() {
CategoryItem(
onClick = {},
categoryName = "Test Category"
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package fr.free.nrw.commons.bookmarks.category

import androidx.room.Entity
import androidx.room.PrimaryKey

/**
* Data class representing bookmarked category in DB
*
* @property categoryName
* @constructor Create empty Bookmarks category modal
*/
@Entity(tableName = "bookmarks_categories")
data class BookmarksCategoryModal(
@PrimaryKey val categoryName: String
)
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,12 @@ import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import android.view.View
import androidx.activity.viewModels
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import fr.free.nrw.commons.Media
import fr.free.nrw.commons.R
import fr.free.nrw.commons.Utils
Expand All @@ -19,6 +23,8 @@ import fr.free.nrw.commons.explore.categories.parent.ParentCategoriesFragment
import fr.free.nrw.commons.explore.categories.sub.SubCategoriesFragment
import fr.free.nrw.commons.media.MediaDetailPagerFragment
import fr.free.nrw.commons.theme.BaseActivity
import kotlinx.coroutines.launch
import javax.inject.Inject


/**
Expand All @@ -38,6 +44,11 @@ class CategoryDetailsActivity : BaseActivity(),

private lateinit var binding: ActivityCategoryDetailsBinding

@Inject
lateinit var categoryViewModelFactory: CategoryDetailsViewModel.ViewModelFactory

private val viewModel: CategoryDetailsViewModel by viewModels<CategoryDetailsViewModel> { categoryViewModelFactory }

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

Expand All @@ -53,6 +64,15 @@ class CategoryDetailsActivity : BaseActivity(),
supportActionBar?.setDisplayHomeAsUpEnabled(true)
setTabs()
setPageTitle()

lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED){
viewModel.bookmarkState.collect {
invalidateOptionsMenu()
}
}
}

}

/**
Expand All @@ -73,6 +93,8 @@ class CategoryDetailsActivity : BaseActivity(),
categoriesMediaFragment.arguments = arguments
subCategoryListFragment.arguments = arguments
parentCategoriesFragment.arguments = arguments

viewModel.onCheckIfBookmarked(categoryName!!)
}
fragmentList.add(categoriesMediaFragment)
titleList.add("MEDIA")
Expand Down Expand Up @@ -181,6 +203,14 @@ class CategoryDetailsActivity : BaseActivity(),
Utils.handleWebUrl(this, Uri.parse(title.canonicalUri))
true
}

R.id.menu_bookmark_current_category -> {
categoryName?.let {
viewModel.onBookmarkClick(categoryName = it)
}
true
}

android.R.id.home -> {
onBackPressed()
true
Expand All @@ -189,6 +219,22 @@ class CategoryDetailsActivity : BaseActivity(),
}
}

override fun onPrepareOptionsMenu(menu: Menu?): Boolean {
menu?.run {
val bookmarkMenuItem = findItem(R.id.menu_bookmark_current_category)
if (bookmarkMenuItem != null) {
val icon = if(viewModel.bookmarkState.value){
R.drawable.menu_ic_round_star_filled_24px
} else {
R.drawable.menu_ic_round_star_border_24px
}

bookmarkMenuItem.setIcon(icon)
}
}
return super.onPrepareOptionsMenu(menu)
}

/**
* This method is called on backPressed of anyFragment in the activity.
* If condition is called when mediaDetailFragment is opened.
Expand Down
Loading
Loading