mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-26 12:23:58 +01:00
Convert bookmarks package to kotlin (#6387)
* Convert BookmarkItemsController to kotlin * Split BookmarkItemsDao apart and converted to Kotlin * Convert and cleanup content providers * Convert BookmarkItemsFragment to kotlin * Convert BookmarkPicturesFragment to kotlin * Convert BookmarkPicturesDao to kotlin and share some useful DB methods * Convert BookmarkPicturesController to kotlin * Convert BookmarkFragment to kotlin * Convert BookmarksPagerAdapter to kotlin * Convert BookmarkListRootFragment to kotlin
This commit is contained in:
parent
869371b485
commit
8de57304bf
44 changed files with 1738 additions and 1988 deletions
|
|
@ -15,9 +15,8 @@ import com.facebook.drawee.backends.pipeline.Fresco
|
|||
import com.facebook.imagepipeline.core.ImagePipelineConfig
|
||||
import fr.free.nrw.commons.auth.LoginActivity
|
||||
import fr.free.nrw.commons.auth.SessionManager
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao
|
||||
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarksTable
|
||||
import fr.free.nrw.commons.category.CategoryDao
|
||||
import fr.free.nrw.commons.concurrency.BackgroundPoolExceptionHandler
|
||||
import fr.free.nrw.commons.concurrency.ThreadPoolService
|
||||
|
|
@ -257,8 +256,8 @@ class CommonsApplication : MultiDexApplication() {
|
|||
} catch (e: SQLiteException) {
|
||||
Timber.e(e)
|
||||
}
|
||||
BookmarkPicturesDao.Table.onDelete(db)
|
||||
BookmarkItemsDao.Table.onDelete(db)
|
||||
BookmarksTable.onDelete(db)
|
||||
BookmarkItemsTable.onDelete(db)
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,105 +0,0 @@
|
|||
package fr.free.nrw.commons.bookmarks;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import fr.free.nrw.commons.contributions.MainActivity;
|
||||
import fr.free.nrw.commons.databinding.FragmentBookmarksBinding;
|
||||
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
|
||||
import fr.free.nrw.commons.kvstore.JsonKvStore;
|
||||
import fr.free.nrw.commons.theme.BaseActivity;
|
||||
import javax.inject.Inject;
|
||||
import fr.free.nrw.commons.contributions.ContributionController;
|
||||
import javax.inject.Named;
|
||||
|
||||
public class BookmarkFragment extends CommonsDaggerSupportFragment {
|
||||
|
||||
private FragmentManager supportFragmentManager;
|
||||
private BookmarksPagerAdapter adapter;
|
||||
FragmentBookmarksBinding binding;
|
||||
|
||||
@Inject
|
||||
ContributionController controller;
|
||||
/**
|
||||
* To check if the user is loggedIn or not.
|
||||
*/
|
||||
@Inject
|
||||
@Named("default_preferences")
|
||||
public
|
||||
JsonKvStore applicationKvStore;
|
||||
|
||||
@NonNull
|
||||
public static BookmarkFragment newInstance() {
|
||||
BookmarkFragment fragment = new BookmarkFragment();
|
||||
fragment.setRetainInstance(true);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
public void setScroll(boolean canScroll) {
|
||||
if (binding!=null) {
|
||||
binding.viewPagerBookmarks.setCanScroll(canScroll);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull final LayoutInflater inflater,
|
||||
@Nullable final ViewGroup container,
|
||||
@Nullable final Bundle savedInstanceState) {
|
||||
super.onCreateView(inflater, container, savedInstanceState);
|
||||
binding = FragmentBookmarksBinding.inflate(inflater, container, false);
|
||||
|
||||
// Activity can call methods in the fragment by acquiring a
|
||||
// reference to the Fragment from FragmentManager, using findFragmentById()
|
||||
supportFragmentManager = getChildFragmentManager();
|
||||
|
||||
adapter = new BookmarksPagerAdapter(supportFragmentManager, getContext(),
|
||||
applicationKvStore.getBoolean("login_skipped"));
|
||||
binding.viewPagerBookmarks.setAdapter(adapter);
|
||||
binding.tabLayout.setupWithViewPager(binding.viewPagerBookmarks);
|
||||
|
||||
((MainActivity) getActivity()).showTabs();
|
||||
((BaseActivity) getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(false);
|
||||
|
||||
setupTabLayout();
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method sets up the tab layout. If the adapter has only one element it sets the
|
||||
* visibility of tabLayout to gone.
|
||||
*/
|
||||
public void setupTabLayout() {
|
||||
binding.tabLayout.setVisibility(View.VISIBLE);
|
||||
if (adapter.getCount() == 1) {
|
||||
binding.tabLayout.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void onBackPressed() {
|
||||
if (((BookmarkListRootFragment) (adapter.getItem(binding.tabLayout.getSelectedTabPosition())))
|
||||
.backPressed()) {
|
||||
// The event is handled internally by the adapter , no further action required.
|
||||
return;
|
||||
}
|
||||
// Event is not handled by the adapter ( performed back action ) change action bar.
|
||||
((BaseActivity) getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
binding = null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
package fr.free.nrw.commons.bookmarks
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import fr.free.nrw.commons.contributions.ContributionController
|
||||
import fr.free.nrw.commons.contributions.MainActivity
|
||||
import fr.free.nrw.commons.databinding.FragmentBookmarksBinding
|
||||
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment
|
||||
import fr.free.nrw.commons.kvstore.JsonKvStore
|
||||
import fr.free.nrw.commons.theme.BaseActivity
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Named
|
||||
|
||||
class BookmarkFragment : CommonsDaggerSupportFragment() {
|
||||
private var adapter: BookmarksPagerAdapter? = null
|
||||
|
||||
@JvmField
|
||||
var binding: FragmentBookmarksBinding? = null
|
||||
|
||||
@JvmField
|
||||
@Inject
|
||||
var controller: ContributionController? = null
|
||||
|
||||
/**
|
||||
* To check if the user is loggedIn or not.
|
||||
*/
|
||||
@JvmField
|
||||
@Inject
|
||||
@Named("default_preferences")
|
||||
var applicationKvStore: JsonKvStore? = null
|
||||
|
||||
fun setScroll(canScroll: Boolean) {
|
||||
binding?.let {
|
||||
it.viewPagerBookmarks.isCanScroll = canScroll
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
super.onCreateView(inflater, container, savedInstanceState)
|
||||
binding = FragmentBookmarksBinding.inflate(inflater, container, false)
|
||||
|
||||
// Activity can call methods in the fragment by acquiring a
|
||||
// reference to the Fragment from FragmentManager, using findFragmentById()
|
||||
val supportFragmentManager = childFragmentManager
|
||||
|
||||
adapter = BookmarksPagerAdapter(
|
||||
supportFragmentManager, requireContext(),
|
||||
applicationKvStore!!.getBoolean("login_skipped")
|
||||
)
|
||||
binding!!.viewPagerBookmarks.adapter = adapter
|
||||
binding!!.tabLayout.setupWithViewPager(binding!!.viewPagerBookmarks)
|
||||
|
||||
(requireActivity() as MainActivity).showTabs()
|
||||
(requireActivity() as BaseActivity).supportActionBar!!.setDisplayHomeAsUpEnabled(false)
|
||||
|
||||
setupTabLayout()
|
||||
return binding!!.root
|
||||
}
|
||||
|
||||
/**
|
||||
* This method sets up the tab layout. If the adapter has only one element it sets the
|
||||
* visibility of tabLayout to gone.
|
||||
*/
|
||||
fun setupTabLayout() {
|
||||
binding!!.tabLayout.visibility = View.VISIBLE
|
||||
if (adapter!!.count == 1) {
|
||||
binding!!.tabLayout.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun onBackPressed() {
|
||||
if (((adapter!!.getItem(binding!!.tabLayout.selectedTabPosition)) as BookmarkListRootFragment).backPressed()) {
|
||||
// The event is handled internally by the adapter , no further action required.
|
||||
return
|
||||
}
|
||||
|
||||
// Event is not handled by the adapter ( performed back action ) change action bar.
|
||||
(requireActivity() as BaseActivity).supportActionBar!!.setDisplayHomeAsUpEnabled(false)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
binding = null
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun newInstance(): BookmarkFragment = BookmarkFragment().apply {
|
||||
retainInstance = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,267 +0,0 @@
|
|||
package fr.free.nrw.commons.bookmarks;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
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;
|
||||
import fr.free.nrw.commons.category.CategoryImagesCallback;
|
||||
import fr.free.nrw.commons.category.GridViewAdapter;
|
||||
import fr.free.nrw.commons.contributions.MainActivity;
|
||||
import fr.free.nrw.commons.databinding.FragmentFeaturedRootBinding;
|
||||
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
|
||||
import fr.free.nrw.commons.media.MediaDetailPagerFragment;
|
||||
import fr.free.nrw.commons.media.MediaDetailProvider;
|
||||
import fr.free.nrw.commons.navtab.NavTab;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import timber.log.Timber;
|
||||
|
||||
public class BookmarkListRootFragment extends CommonsDaggerSupportFragment implements
|
||||
FragmentManager.OnBackStackChangedListener,
|
||||
MediaDetailProvider,
|
||||
AdapterView.OnItemClickListener, CategoryImagesCallback {
|
||||
|
||||
private MediaDetailPagerFragment mediaDetails;
|
||||
//private BookmarkPicturesFragment bookmarkPicturesFragment;
|
||||
private BookmarkLocationsFragment bookmarkLocationsFragment;
|
||||
public Fragment listFragment;
|
||||
private BookmarksPagerAdapter bookmarksPagerAdapter;
|
||||
|
||||
FragmentFeaturedRootBinding binding;
|
||||
|
||||
public BookmarkListRootFragment() {
|
||||
//empty constructor necessary otherwise crashes on recreate
|
||||
}
|
||||
|
||||
public BookmarkListRootFragment(Bundle bundle, BookmarksPagerAdapter bookmarksPagerAdapter) {
|
||||
String title = bundle.getString("categoryName");
|
||||
int order = bundle.getInt("order");
|
||||
final int orderItem = bundle.getInt("orderItem");
|
||||
|
||||
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);
|
||||
this.bookmarksPagerAdapter = bookmarksPagerAdapter;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull final LayoutInflater inflater,
|
||||
@Nullable final ViewGroup container,
|
||||
@Nullable final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
binding = FragmentFeaturedRootBinding.inflate(inflater, container, false);
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
if (savedInstanceState == null) {
|
||||
setFragment(listFragment, mediaDetails);
|
||||
}
|
||||
}
|
||||
|
||||
public void setFragment(Fragment fragment, Fragment otherFragment) {
|
||||
if (fragment.isAdded() && otherFragment != null) {
|
||||
getChildFragmentManager()
|
||||
.beginTransaction()
|
||||
.hide(otherFragment)
|
||||
.show(fragment)
|
||||
.addToBackStack("CONTRIBUTION_LIST_FRAGMENT_TAG")
|
||||
.commit();
|
||||
getChildFragmentManager().executePendingTransactions();
|
||||
} else if (fragment.isAdded() && otherFragment == null) {
|
||||
getChildFragmentManager()
|
||||
.beginTransaction()
|
||||
.show(fragment)
|
||||
.addToBackStack("CONTRIBUTION_LIST_FRAGMENT_TAG")
|
||||
.commit();
|
||||
getChildFragmentManager().executePendingTransactions();
|
||||
} else if (!fragment.isAdded() && otherFragment != null) {
|
||||
getChildFragmentManager()
|
||||
.beginTransaction()
|
||||
.hide(otherFragment)
|
||||
.add(R.id.explore_container, fragment)
|
||||
.addToBackStack("CONTRIBUTION_LIST_FRAGMENT_TAG")
|
||||
.commit();
|
||||
getChildFragmentManager().executePendingTransactions();
|
||||
} else if (!fragment.isAdded()) {
|
||||
getChildFragmentManager()
|
||||
.beginTransaction()
|
||||
.replace(R.id.explore_container, fragment)
|
||||
.addToBackStack("CONTRIBUTION_LIST_FRAGMENT_TAG")
|
||||
.commit();
|
||||
getChildFragmentManager().executePendingTransactions();
|
||||
}
|
||||
}
|
||||
|
||||
public void removeFragment(Fragment fragment) {
|
||||
getChildFragmentManager()
|
||||
.beginTransaction()
|
||||
.remove(fragment)
|
||||
.commit();
|
||||
getChildFragmentManager().executePendingTransactions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(final Context context) {
|
||||
super.onAttach(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMediaClicked(int position) {
|
||||
Timber.d("on media clicked");
|
||||
/*container.setVisibility(View.VISIBLE);
|
||||
((BookmarkFragment)getParentFragment()).tabLayout.setVisibility(View.GONE);
|
||||
mediaDetails = new MediaDetailPagerFragment(false, true, position);
|
||||
setFragment(mediaDetails, bookmarkPicturesFragment);*/
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called mediaDetailPagerFragment. It returns the Media Object at that Index
|
||||
*
|
||||
* @param i It is the index of which media object is to be returned which is same as current
|
||||
* index of viewPager.
|
||||
* @return Media Object
|
||||
*/
|
||||
@Override
|
||||
public Media getMediaAtPosition(int i) {
|
||||
if (bookmarksPagerAdapter.getMediaAdapter() == null) {
|
||||
// not yet ready to return data
|
||||
return null;
|
||||
} else {
|
||||
return (Media) bookmarksPagerAdapter.getMediaAdapter().getItem(i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called on from getCount of MediaDetailPagerFragment The viewpager will contain
|
||||
* same number of media items as that of media elements in adapter.
|
||||
*
|
||||
* @return Total Media count in the adapter
|
||||
*/
|
||||
@Override
|
||||
public int getTotalMediaCount() {
|
||||
if (bookmarksPagerAdapter.getMediaAdapter() == null) {
|
||||
return 0;
|
||||
}
|
||||
return bookmarksPagerAdapter.getMediaAdapter().getCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getContributionStateAt(int position) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reload media detail fragment once media is nominated
|
||||
*
|
||||
* @param index item position that has been nominated
|
||||
*/
|
||||
@Override
|
||||
public void refreshNominatedMedia(int index) {
|
||||
if (mediaDetails != null && !listFragment.isVisible()) {
|
||||
removeFragment(mediaDetails);
|
||||
mediaDetails = MediaDetailPagerFragment.newInstance(false, true);
|
||||
((BookmarkFragment) getParentFragment()).setScroll(false);
|
||||
setFragment(mediaDetails, listFragment);
|
||||
mediaDetails.showImage(index);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called on success of API call for featured images or mobile uploads. The
|
||||
* viewpager will notified that number of items have changed.
|
||||
*/
|
||||
@Override
|
||||
public void viewPagerNotifyDataSetChanged() {
|
||||
if (mediaDetails != null) {
|
||||
mediaDetails.notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean backPressed() {
|
||||
//check mediaDetailPage fragment is not null then we check mediaDetail.is Visible or not to avoid NullPointerException
|
||||
if (mediaDetails != null) {
|
||||
if (mediaDetails.isVisible()) {
|
||||
// todo add get list fragment
|
||||
((BookmarkFragment) getParentFragment()).setupTabLayout();
|
||||
ArrayList<Integer> removed = mediaDetails.getRemovedItems();
|
||||
removeFragment(mediaDetails);
|
||||
((BookmarkFragment) getParentFragment()).setScroll(true);
|
||||
setFragment(listFragment, mediaDetails);
|
||||
((MainActivity) getActivity()).showTabs();
|
||||
if (listFragment instanceof BookmarkPicturesFragment) {
|
||||
GridViewAdapter adapter = ((GridViewAdapter) ((BookmarkPicturesFragment) listFragment)
|
||||
.getAdapter());
|
||||
Iterator i = removed.iterator();
|
||||
while (i.hasNext()) {
|
||||
adapter.remove(adapter.getItem((int) i.next()));
|
||||
}
|
||||
mediaDetails.clearRemoved();
|
||||
|
||||
}
|
||||
} else {
|
||||
moveToContributionsFragment();
|
||||
}
|
||||
} else {
|
||||
moveToContributionsFragment();
|
||||
}
|
||||
// notify mediaDetails did not handled the backPressed further actions required.
|
||||
return false;
|
||||
}
|
||||
|
||||
void moveToContributionsFragment() {
|
||||
((MainActivity) getActivity()).setSelectedItemId(NavTab.CONTRIBUTIONS.code());
|
||||
((MainActivity) getActivity()).showTabs();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
Timber.d("on media clicked");
|
||||
binding.exploreContainer.setVisibility(View.VISIBLE);
|
||||
((BookmarkFragment) getParentFragment()).binding.tabLayout.setVisibility(View.GONE);
|
||||
mediaDetails = MediaDetailPagerFragment.newInstance(false, true);
|
||||
((BookmarkFragment) getParentFragment()).setScroll(false);
|
||||
setFragment(mediaDetails, listFragment);
|
||||
mediaDetails.showImage(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackStackChanged() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
binding = null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,226 @@
|
|||
package fr.free.nrw.commons.bookmarks
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.AdapterView
|
||||
import android.widget.AdapterView.OnItemClickListener
|
||||
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
|
||||
import fr.free.nrw.commons.category.CategoryImagesCallback
|
||||
import fr.free.nrw.commons.category.GridViewAdapter
|
||||
import fr.free.nrw.commons.contributions.MainActivity
|
||||
import fr.free.nrw.commons.databinding.FragmentFeaturedRootBinding
|
||||
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment
|
||||
import fr.free.nrw.commons.media.MediaDetailPagerFragment
|
||||
import fr.free.nrw.commons.media.MediaDetailPagerFragment.Companion.newInstance
|
||||
import fr.free.nrw.commons.media.MediaDetailProvider
|
||||
import fr.free.nrw.commons.navtab.NavTab
|
||||
import timber.log.Timber
|
||||
|
||||
class BookmarkListRootFragment : CommonsDaggerSupportFragment,
|
||||
FragmentManager.OnBackStackChangedListener, MediaDetailProvider, OnItemClickListener,
|
||||
CategoryImagesCallback {
|
||||
private var mediaDetails: MediaDetailPagerFragment? = null
|
||||
private val bookmarkLocationsFragment: BookmarkLocationsFragment? = null
|
||||
var listFragment: Fragment? = null
|
||||
private var bookmarksPagerAdapter: BookmarksPagerAdapter? = null
|
||||
|
||||
var binding: FragmentFeaturedRootBinding? = null
|
||||
|
||||
constructor()
|
||||
|
||||
constructor(bundle: Bundle, bookmarksPagerAdapter: BookmarksPagerAdapter) {
|
||||
val title = bundle.getString("categoryName")
|
||||
val order = bundle.getInt("order")
|
||||
val orderItem = bundle.getInt("orderItem")
|
||||
|
||||
when (order) {
|
||||
0 -> listFragment = BookmarkPicturesFragment()
|
||||
1 -> listFragment = BookmarkLocationsFragment()
|
||||
3 -> listFragment = BookmarkCategoriesFragment()
|
||||
}
|
||||
if (orderItem == 2) {
|
||||
listFragment = BookmarkItemsFragment()
|
||||
}
|
||||
|
||||
val featuredArguments = Bundle()
|
||||
featuredArguments.putString("categoryName", title)
|
||||
listFragment!!.setArguments(featuredArguments)
|
||||
this.bookmarksPagerAdapter = bookmarksPagerAdapter
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = FragmentFeaturedRootBinding.inflate(inflater, container, false)
|
||||
return binding!!.getRoot()
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
if (savedInstanceState == null) {
|
||||
setFragment(listFragment!!, mediaDetails)
|
||||
}
|
||||
}
|
||||
|
||||
fun setFragment(fragment: Fragment, otherFragment: Fragment?) {
|
||||
if (fragment.isAdded() && otherFragment != null) {
|
||||
getChildFragmentManager()
|
||||
.beginTransaction()
|
||||
.hide(otherFragment)
|
||||
.show(fragment)
|
||||
.addToBackStack("CONTRIBUTION_LIST_FRAGMENT_TAG")
|
||||
.commit()
|
||||
getChildFragmentManager().executePendingTransactions()
|
||||
} else if (fragment.isAdded() && otherFragment == null) {
|
||||
getChildFragmentManager()
|
||||
.beginTransaction()
|
||||
.show(fragment)
|
||||
.addToBackStack("CONTRIBUTION_LIST_FRAGMENT_TAG")
|
||||
.commit()
|
||||
getChildFragmentManager().executePendingTransactions()
|
||||
} else if (!fragment.isAdded() && otherFragment != null) {
|
||||
getChildFragmentManager()
|
||||
.beginTransaction()
|
||||
.hide(otherFragment)
|
||||
.add(R.id.explore_container, fragment)
|
||||
.addToBackStack("CONTRIBUTION_LIST_FRAGMENT_TAG")
|
||||
.commit()
|
||||
getChildFragmentManager().executePendingTransactions()
|
||||
} else if (!fragment.isAdded()) {
|
||||
getChildFragmentManager()
|
||||
.beginTransaction()
|
||||
.replace(R.id.explore_container, fragment)
|
||||
.addToBackStack("CONTRIBUTION_LIST_FRAGMENT_TAG")
|
||||
.commit()
|
||||
getChildFragmentManager().executePendingTransactions()
|
||||
}
|
||||
}
|
||||
|
||||
fun removeFragment(fragment: Fragment) {
|
||||
getChildFragmentManager()
|
||||
.beginTransaction()
|
||||
.remove(fragment)
|
||||
.commit()
|
||||
getChildFragmentManager().executePendingTransactions()
|
||||
}
|
||||
|
||||
override fun onMediaClicked(position: Int) {
|
||||
Timber.d("on media clicked")
|
||||
/*container.setVisibility(View.VISIBLE);
|
||||
((BookmarkFragment)getParentFragment()).tabLayout.setVisibility(View.GONE);
|
||||
mediaDetails = new MediaDetailPagerFragment(false, true, position);
|
||||
setFragment(mediaDetails, bookmarkPicturesFragment);*/
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called mediaDetailPagerFragment. It returns the Media Object at that Index
|
||||
*
|
||||
* @param i It is the index of which media object is to be returned which is same as current
|
||||
* index of viewPager.
|
||||
* @return Media Object
|
||||
*/
|
||||
override fun getMediaAtPosition(i: Int): Media? =
|
||||
bookmarksPagerAdapter!!.mediaAdapter?.getItem(i) as Media?
|
||||
|
||||
/**
|
||||
* This method is called on from getCount of MediaDetailPagerFragment The viewpager will contain
|
||||
* same number of media items as that of media elements in adapter.
|
||||
*
|
||||
* @return Total Media count in the adapter
|
||||
*/
|
||||
override fun getTotalMediaCount(): Int =
|
||||
bookmarksPagerAdapter!!.mediaAdapter?.count ?: 0
|
||||
|
||||
override fun getContributionStateAt(position: Int): Int? {
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Reload media detail fragment once media is nominated
|
||||
*
|
||||
* @param index item position that has been nominated
|
||||
*/
|
||||
override fun refreshNominatedMedia(index: Int) {
|
||||
if (mediaDetails != null && !listFragment!!.isVisible()) {
|
||||
removeFragment(mediaDetails!!)
|
||||
mediaDetails = newInstance(false, true)
|
||||
(parentFragment as BookmarkFragment).setScroll(false)
|
||||
setFragment(mediaDetails!!, listFragment)
|
||||
mediaDetails!!.showImage(index)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called on success of API call for featured images or mobile uploads. The
|
||||
* viewpager will notified that number of items have changed.
|
||||
*/
|
||||
override fun viewPagerNotifyDataSetChanged() {
|
||||
if (mediaDetails != null) {
|
||||
mediaDetails!!.notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
fun backPressed(): Boolean {
|
||||
//check mediaDetailPage fragment is not null then we check mediaDetail.is Visible or not to avoid NullPointerException
|
||||
if (mediaDetails != null) {
|
||||
if (mediaDetails!!.isVisible()) {
|
||||
// todo add get list fragment
|
||||
(parentFragment as BookmarkFragment).setupTabLayout()
|
||||
val removed: ArrayList<Int> = mediaDetails!!.removedItems
|
||||
removeFragment(mediaDetails!!)
|
||||
(parentFragment as BookmarkFragment).setScroll(true)
|
||||
setFragment(listFragment!!, mediaDetails)
|
||||
(requireActivity() as MainActivity).showTabs()
|
||||
if (listFragment is BookmarkPicturesFragment) {
|
||||
val adapter = ((listFragment as BookmarkPicturesFragment)
|
||||
.getAdapter() as GridViewAdapter?)
|
||||
val i: MutableIterator<*> = removed.iterator()
|
||||
while (i.hasNext()) {
|
||||
adapter!!.remove(adapter.getItem(i.next() as Int))
|
||||
}
|
||||
mediaDetails!!.clearRemoved()
|
||||
}
|
||||
} else {
|
||||
moveToContributionsFragment()
|
||||
}
|
||||
} else {
|
||||
moveToContributionsFragment()
|
||||
}
|
||||
// notify mediaDetails did not handled the backPressed further actions required.
|
||||
return false
|
||||
}
|
||||
|
||||
fun moveToContributionsFragment() {
|
||||
(requireActivity() as MainActivity).setSelectedItemId(NavTab.CONTRIBUTIONS.code())
|
||||
(requireActivity() as MainActivity).showTabs()
|
||||
}
|
||||
|
||||
override fun onItemClick(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
|
||||
Timber.d("on media clicked")
|
||||
binding!!.exploreContainer.visibility = View.VISIBLE
|
||||
(parentFragment as BookmarkFragment).binding!!.tabLayout.setVisibility(View.GONE)
|
||||
mediaDetails = newInstance(false, true)
|
||||
(parentFragment as BookmarkFragment).setScroll(false)
|
||||
setFragment(mediaDetails!!, listFragment)
|
||||
mediaDetails!!.showImage(position)
|
||||
}
|
||||
|
||||
override fun onBackStackChanged() = Unit
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
binding = null
|
||||
}
|
||||
}
|
||||
|
|
@ -1,94 +0,0 @@
|
|||
package fr.free.nrw.commons.bookmarks;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.widget.ListAdapter;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentPagerAdapter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesFragment;
|
||||
|
||||
public class BookmarksPagerAdapter extends FragmentPagerAdapter {
|
||||
|
||||
private ArrayList<BookmarkPages> pages;
|
||||
|
||||
/**
|
||||
* Default Constructor
|
||||
* @param fm
|
||||
* @param context
|
||||
* @param onlyPictures is true if the fragment requires only BookmarkPictureFragment
|
||||
* (i.e. when no user is logged in).
|
||||
*/
|
||||
BookmarksPagerAdapter(FragmentManager fm, Context context,boolean onlyPictures) {
|
||||
super(fm);
|
||||
pages = new ArrayList<>();
|
||||
Bundle picturesBundle = new Bundle();
|
||||
picturesBundle.putString("categoryName", context.getString(R.string.title_page_bookmarks_pictures));
|
||||
picturesBundle.putInt("order", 0);
|
||||
pages.add(new BookmarkPages(
|
||||
new BookmarkListRootFragment(picturesBundle, this),
|
||||
context.getString(R.string.title_page_bookmarks_pictures)));
|
||||
if (!onlyPictures) {
|
||||
// if onlyPictures is false we also add the location fragment.
|
||||
Bundle locationBundle = new Bundle();
|
||||
locationBundle.putString("categoryName",
|
||||
context.getString(R.string.title_page_bookmarks_locations));
|
||||
locationBundle.putInt("order", 1);
|
||||
pages.add(new BookmarkPages(
|
||||
new BookmarkListRootFragment(locationBundle, this),
|
||||
context.getString(R.string.title_page_bookmarks_locations)));
|
||||
|
||||
locationBundle.putInt("orderItem", 2);
|
||||
pages.add(new BookmarkPages(
|
||||
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();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fragment getItem(int position) {
|
||||
return pages.get(position).getPage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return pages.size();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public CharSequence getPageTitle(int position) {
|
||||
return pages.get(position).getTitle();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Adapter used to display the picture gridview
|
||||
* @return adapter
|
||||
*/
|
||||
public ListAdapter getMediaAdapter() {
|
||||
BookmarkPicturesFragment fragment = (BookmarkPicturesFragment)(((BookmarkListRootFragment)pages.get(0).getPage()).listFragment);
|
||||
return fragment.getAdapter();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the pictures list for the bookmark fragment
|
||||
*/
|
||||
public void requestPictureListUpdate() {
|
||||
BookmarkPicturesFragment fragment = (BookmarkPicturesFragment)(((BookmarkListRootFragment)pages.get(0).getPage()).listFragment);
|
||||
fragment.onResume();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
package fr.free.nrw.commons.bookmarks
|
||||
|
||||
import android.content.Context
|
||||
import android.widget.ListAdapter
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.fragment.app.FragmentPagerAdapter
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesFragment
|
||||
|
||||
class BookmarksPagerAdapter internal constructor(
|
||||
fm: FragmentManager, context: Context, onlyPictures: Boolean
|
||||
) : FragmentPagerAdapter(fm) {
|
||||
private val pages = mutableListOf<BookmarkPages>()
|
||||
|
||||
/**
|
||||
* Default Constructor
|
||||
* @param fm
|
||||
* @param context
|
||||
* @param onlyPictures is true if the fragment requires only BookmarkPictureFragment
|
||||
* (i.e. when no user is logged in).
|
||||
*/
|
||||
init {
|
||||
pages.add(
|
||||
BookmarkPages(
|
||||
BookmarkListRootFragment(
|
||||
bundleOf(
|
||||
"categoryName" to context.getString(R.string.title_page_bookmarks_pictures),
|
||||
"order" to 0
|
||||
), this
|
||||
), context.getString(R.string.title_page_bookmarks_pictures)
|
||||
)
|
||||
)
|
||||
if (!onlyPictures) {
|
||||
// if onlyPictures is false we also add the location fragment.
|
||||
val locationBundle = bundleOf(
|
||||
"categoryName" to context.getString(R.string.title_page_bookmarks_locations),
|
||||
"order" to 1
|
||||
)
|
||||
|
||||
pages.add(
|
||||
BookmarkPages(
|
||||
BookmarkListRootFragment(locationBundle, this),
|
||||
context.getString(R.string.title_page_bookmarks_locations)
|
||||
)
|
||||
)
|
||||
|
||||
locationBundle.putInt("orderItem", 2)
|
||||
pages.add(
|
||||
BookmarkPages(
|
||||
BookmarkListRootFragment(locationBundle, this),
|
||||
context.getString(R.string.title_page_bookmarks_items)
|
||||
)
|
||||
)
|
||||
}
|
||||
pages.add(
|
||||
BookmarkPages(
|
||||
BookmarkListRootFragment(
|
||||
bundleOf(
|
||||
"categoryName" to context.getString(R.string.title_page_bookmarks_categories),
|
||||
"order" to 3
|
||||
), this),
|
||||
context.getString(R.string.title_page_bookmarks_categories)
|
||||
)
|
||||
)
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun getItem(position: Int): Fragment = pages[position].page!!
|
||||
|
||||
override fun getCount(): Int = pages.size
|
||||
|
||||
override fun getPageTitle(position: Int): CharSequence? = pages[position].title
|
||||
|
||||
/**
|
||||
* Return the Adapter used to display the picture gridview
|
||||
* @return adapter
|
||||
*/
|
||||
val mediaAdapter: ListAdapter?
|
||||
get() = (((pages[0].page as BookmarkListRootFragment).listFragment) as BookmarkPicturesFragment).getAdapter()
|
||||
}
|
||||
|
|
@ -1,129 +0,0 @@
|
|||
package fr.free.nrw.commons.bookmarks.items;
|
||||
|
||||
import static fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.COLUMN_ID;
|
||||
import static fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.TABLE_NAME;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteQueryBuilder;
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
import androidx.annotation.NonNull;
|
||||
import fr.free.nrw.commons.BuildConfig;
|
||||
import fr.free.nrw.commons.data.DBOpenHelper;
|
||||
import fr.free.nrw.commons.di.CommonsDaggerContentProvider;
|
||||
import javax.inject.Inject;
|
||||
import timber.log.Timber;
|
||||
|
||||
/**
|
||||
* Handles private storage for bookmarked items
|
||||
*/
|
||||
public class BookmarkItemsContentProvider extends CommonsDaggerContentProvider {
|
||||
|
||||
private static final String BASE_PATH = "bookmarksItems";
|
||||
public static final Uri BASE_URI =
|
||||
Uri.parse("content://" + BuildConfig.BOOKMARK_ITEMS_AUTHORITY + "/" + BASE_PATH);
|
||||
|
||||
|
||||
/**
|
||||
* Append bookmark items ID to the base uri
|
||||
*/
|
||||
public static Uri uriForName(final String id) {
|
||||
return Uri.parse(BASE_URI + "/" + id);
|
||||
}
|
||||
|
||||
@Inject
|
||||
DBOpenHelper dbOpenHelper;
|
||||
|
||||
@Override
|
||||
public String getType(@NonNull final Uri uri) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries the SQLite database for the bookmark items
|
||||
* @param uri : contains the uri for bookmark items
|
||||
* @param projection : contains the all fields of the table
|
||||
* @param selection : handles Where
|
||||
* @param selectionArgs : the condition of Where clause
|
||||
* @param sortOrder : ascending or descending
|
||||
*/
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Override
|
||||
public Cursor query(@NonNull final Uri uri, final String[] projection, final String selection,
|
||||
final String[] selectionArgs, final String sortOrder) {
|
||||
final SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
|
||||
queryBuilder.setTables(TABLE_NAME);
|
||||
final SQLiteDatabase db = dbOpenHelper.getReadableDatabase();
|
||||
final Cursor cursor = queryBuilder.query(db, projection, selection,
|
||||
selectionArgs, null, null, sortOrder);
|
||||
cursor.setNotificationUri(getContext().getContentResolver(), uri);
|
||||
return cursor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the update query of local SQLite Database
|
||||
* @param uri : contains the uri for bookmark items
|
||||
* @param contentValues : new values to be entered to db
|
||||
* @param selection : handles Where
|
||||
* @param selectionArgs : the condition of Where clause
|
||||
*/
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Override
|
||||
public int update(@NonNull final Uri uri, final ContentValues contentValues,
|
||||
final String selection, final String[] selectionArgs) {
|
||||
final SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase();
|
||||
final int rowsUpdated;
|
||||
if (TextUtils.isEmpty(selection)) {
|
||||
final int id = Integer.parseInt(uri.getLastPathSegment());
|
||||
rowsUpdated = sqlDB.update(TABLE_NAME,
|
||||
contentValues,
|
||||
COLUMN_ID + " = ?",
|
||||
new String[]{String.valueOf(id)});
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"Parameter `selection` should be empty when updating an ID");
|
||||
}
|
||||
|
||||
getContext().getContentResolver().notifyChange(uri, null);
|
||||
return rowsUpdated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the insertion of new bookmark items record to local SQLite Database
|
||||
* @param uri
|
||||
* @param contentValues
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Override
|
||||
public Uri insert(@NonNull final Uri uri, final ContentValues contentValues) {
|
||||
final SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase();
|
||||
final long id = sqlDB.insert(TABLE_NAME, null, contentValues);
|
||||
getContext().getContentResolver().notifyChange(uri, null);
|
||||
return Uri.parse(BASE_URI + "/" + id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the deletion of new bookmark items record to local SQLite Database
|
||||
* @param uri
|
||||
* @param s
|
||||
* @param strings
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Override
|
||||
public int delete(@NonNull final Uri uri, final String s, final String[] strings) {
|
||||
final int rows;
|
||||
final SQLiteDatabase db = dbOpenHelper.getReadableDatabase();
|
||||
Timber.d("Deleting bookmark name %s", uri.getLastPathSegment());
|
||||
rows = db.delete(
|
||||
TABLE_NAME,
|
||||
"item_id = ?",
|
||||
new String[]{uri.getLastPathSegment()}
|
||||
);
|
||||
getContext().getContentResolver().notifyChange(uri, null);
|
||||
return rows;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
package fr.free.nrw.commons.bookmarks.items
|
||||
|
||||
import android.content.ContentValues
|
||||
import android.database.Cursor
|
||||
import android.database.sqlite.SQLiteQueryBuilder
|
||||
import android.net.Uri
|
||||
import fr.free.nrw.commons.BuildConfig
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.TABLE_NAME
|
||||
import fr.free.nrw.commons.di.CommonsDaggerContentProvider
|
||||
import androidx.core.net.toUri
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.COLUMN_ID
|
||||
|
||||
/**
|
||||
* Handles private storage for bookmarked items
|
||||
*/
|
||||
class BookmarkItemsContentProvider : CommonsDaggerContentProvider() {
|
||||
override fun getType(uri: Uri): String? = null
|
||||
|
||||
/**
|
||||
* Queries the SQLite database for the bookmark items
|
||||
* @param uri : contains the uri for bookmark items
|
||||
* @param projection : contains the all fields of the table
|
||||
* @param selection : handles Where
|
||||
* @param selectionArgs : the condition of Where clause
|
||||
* @param sortOrder : ascending or descending
|
||||
*/
|
||||
override fun query(
|
||||
uri: Uri, projection: Array<String>?, selection: String?,
|
||||
selectionArgs: Array<String>?, sortOrder: String?
|
||||
): Cursor {
|
||||
val queryBuilder = SQLiteQueryBuilder().apply {
|
||||
tables = TABLE_NAME
|
||||
}
|
||||
|
||||
return queryBuilder.query(
|
||||
requireDb(), projection, selection,
|
||||
selectionArgs, null, null, sortOrder
|
||||
).apply {
|
||||
setNotificationUri(requireContext().contentResolver, uri)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the update query of local SQLite Database
|
||||
* @param uri : contains the uri for bookmark items
|
||||
* @param contentValues : new values to be entered to db
|
||||
* @param selection : handles Where
|
||||
* @param selectionArgs : the condition of Where clause
|
||||
*/
|
||||
override fun update(
|
||||
uri: Uri, contentValues: ContentValues?,
|
||||
selection: String?, selectionArgs: Array<String>?
|
||||
): Int {
|
||||
val rowsUpdated: Int
|
||||
if (selection.isNullOrEmpty()) {
|
||||
val id = uri.lastPathSegment!!.toInt()
|
||||
rowsUpdated = requireDb().update(
|
||||
TABLE_NAME,
|
||||
contentValues,
|
||||
"$COLUMN_ID = ?",
|
||||
arrayOf(id.toString())
|
||||
)
|
||||
} else {
|
||||
throw IllegalArgumentException(
|
||||
"Parameter `selection` should be empty when updating an ID"
|
||||
)
|
||||
}
|
||||
|
||||
requireContext().contentResolver.notifyChange(uri, null)
|
||||
return rowsUpdated
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the insertion of new bookmark items record to local SQLite Database
|
||||
*/
|
||||
override fun insert(uri: Uri, contentValues: ContentValues?): Uri? {
|
||||
val id = requireDb().insert(TABLE_NAME, null, contentValues)
|
||||
requireContext().contentResolver.notifyChange(uri, null)
|
||||
return "$BASE_URI/$id".toUri()
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handles the deletion of new bookmark items record to local SQLite Database
|
||||
*/
|
||||
override fun delete(uri: Uri, s: String?, strings: Array<String>?): Int {
|
||||
val rows: Int = requireDb().delete(
|
||||
TABLE_NAME,
|
||||
"$COLUMN_ID = ?",
|
||||
arrayOf(uri.lastPathSegment)
|
||||
)
|
||||
requireContext().contentResolver.notifyChange(uri, null)
|
||||
return rows
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val BASE_PATH = "bookmarksItems"
|
||||
val BASE_URI: Uri = "content://${BuildConfig.BOOKMARK_ITEMS_AUTHORITY}/$BASE_PATH".toUri()
|
||||
fun uriForName(id: String) = "$BASE_URI/$id".toUri()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
package fr.free.nrw.commons.bookmarks.items;
|
||||
|
||||
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem;
|
||||
import java.util.List;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
/**
|
||||
* Handles loading bookmarked items from Database
|
||||
*/
|
||||
@Singleton
|
||||
public class BookmarkItemsController {
|
||||
|
||||
@Inject
|
||||
BookmarkItemsDao bookmarkItemsDao;
|
||||
|
||||
@Inject
|
||||
public BookmarkItemsController() {}
|
||||
|
||||
/**
|
||||
* Load from DB the bookmarked items
|
||||
* @return a list of DepictedItem objects.
|
||||
*/
|
||||
public List<DepictedItem> loadFavoritesItems() {
|
||||
return bookmarkItemsDao.getAllBookmarksItems();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package fr.free.nrw.commons.bookmarks.items
|
||||
|
||||
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
/**
|
||||
* Handles loading bookmarked items from Database
|
||||
*/
|
||||
@Singleton
|
||||
class BookmarkItemsController @Inject constructor() {
|
||||
@JvmField
|
||||
@Inject
|
||||
var bookmarkItemsDao: BookmarkItemsDao? = null
|
||||
|
||||
/**
|
||||
* Load from DB the bookmarked items
|
||||
* @return a list of DepictedItem objects.
|
||||
*/
|
||||
fun loadFavoritesItems(): List<DepictedItem> {
|
||||
return bookmarkItemsDao?.getAllBookmarksItems() ?: emptyList()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,329 +0,0 @@
|
|||
package fr.free.nrw.commons.bookmarks.items;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.ContentProviderClient;
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.os.RemoteException;
|
||||
import fr.free.nrw.commons.category.CategoryItem;
|
||||
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Provider;
|
||||
import javax.inject.Singleton;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
/**
|
||||
* Handles database operations for bookmarked items
|
||||
*/
|
||||
@Singleton
|
||||
public class BookmarkItemsDao {
|
||||
|
||||
private final Provider<ContentProviderClient> clientProvider;
|
||||
|
||||
@Inject
|
||||
public BookmarkItemsDao(
|
||||
@Named("bookmarksItem") final Provider<ContentProviderClient> clientProvider) {
|
||||
this.clientProvider = clientProvider;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find all persisted items bookmarks on database
|
||||
* @return list of bookmarks
|
||||
*/
|
||||
public List<DepictedItem> getAllBookmarksItems() {
|
||||
final List<DepictedItem> items = new ArrayList<>();
|
||||
final ContentProviderClient db = clientProvider.get();
|
||||
try (final Cursor cursor = db.query(
|
||||
BookmarkItemsContentProvider.BASE_URI,
|
||||
Table.ALL_FIELDS,
|
||||
null,
|
||||
new String[]{},
|
||||
null)) {
|
||||
while (cursor != null && cursor.moveToNext()) {
|
||||
items.add(fromCursor(cursor));
|
||||
}
|
||||
} catch (final RemoteException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
db.release();
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Look for a bookmark in database and in order to insert or delete it
|
||||
* @param depictedItem : Bookmark object
|
||||
* @return boolean : is bookmark now favorite ?
|
||||
*/
|
||||
public boolean updateBookmarkItem(final DepictedItem depictedItem) {
|
||||
final boolean bookmarkExists = findBookmarkItem(depictedItem.getId());
|
||||
if (bookmarkExists) {
|
||||
deleteBookmarkItem(depictedItem);
|
||||
} else {
|
||||
addBookmarkItem(depictedItem);
|
||||
}
|
||||
return !bookmarkExists;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a Bookmark to database
|
||||
* @param depictedItem : Bookmark to add
|
||||
*/
|
||||
private void addBookmarkItem(final DepictedItem depictedItem) {
|
||||
final ContentProviderClient db = clientProvider.get();
|
||||
try {
|
||||
db.insert(BookmarkItemsContentProvider.BASE_URI, toContentValues(depictedItem));
|
||||
} catch (final RemoteException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
db.release();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a bookmark from database
|
||||
* @param depictedItem : Bookmark to delete
|
||||
*/
|
||||
private void deleteBookmarkItem(final DepictedItem depictedItem) {
|
||||
final ContentProviderClient db = clientProvider.get();
|
||||
try {
|
||||
db.delete(BookmarkItemsContentProvider.uriForName(depictedItem.getId()), null, null);
|
||||
} catch (final RemoteException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
db.release();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a bookmark from database based on its name
|
||||
* @param depictedItemID : Bookmark to find
|
||||
* @return boolean : is bookmark in database ?
|
||||
*/
|
||||
public boolean findBookmarkItem(final String depictedItemID) {
|
||||
if (depictedItemID == null) { //Avoiding NPE's
|
||||
return false;
|
||||
}
|
||||
final ContentProviderClient db = clientProvider.get();
|
||||
try (final Cursor cursor = db.query(
|
||||
BookmarkItemsContentProvider.BASE_URI,
|
||||
Table.ALL_FIELDS,
|
||||
Table.COLUMN_ID + "=?",
|
||||
new String[]{depictedItemID},
|
||||
null
|
||||
)) {
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
return true;
|
||||
}
|
||||
} catch (final RemoteException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
db.release();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recives real data from cursor
|
||||
* @param cursor : Object for storing database data
|
||||
* @return DepictedItem
|
||||
*/
|
||||
@SuppressLint("Range")
|
||||
DepictedItem fromCursor(final Cursor cursor) {
|
||||
final String fileName = cursor.getString(cursor.getColumnIndex(Table.COLUMN_NAME));
|
||||
final String description
|
||||
= cursor.getString(cursor.getColumnIndex(Table.COLUMN_DESCRIPTION));
|
||||
final String imageUrl = cursor.getString(cursor.getColumnIndex(Table.COLUMN_IMAGE));
|
||||
final String instanceListString
|
||||
= cursor.getString(cursor.getColumnIndex(Table.COLUMN_INSTANCE_LIST));
|
||||
final List<String> instanceList = StringToArray(instanceListString);
|
||||
final String categoryNameListString = cursor.getString(cursor
|
||||
.getColumnIndex(Table.COLUMN_CATEGORIES_NAME_LIST));
|
||||
final List<String> categoryNameList = StringToArray(categoryNameListString);
|
||||
final String categoryDescriptionListString = cursor.getString(cursor
|
||||
.getColumnIndex(Table.COLUMN_CATEGORIES_DESCRIPTION_LIST));
|
||||
final List<String> categoryDescriptionList = StringToArray(categoryDescriptionListString);
|
||||
final String categoryThumbnailListString = cursor.getString(cursor
|
||||
.getColumnIndex(Table.COLUMN_CATEGORIES_THUMBNAIL_LIST));
|
||||
final List<String> categoryThumbnailList = StringToArray(categoryThumbnailListString);
|
||||
final List<CategoryItem> categoryList = convertToCategoryItems(categoryNameList,
|
||||
categoryDescriptionList, categoryThumbnailList);
|
||||
final boolean isSelected
|
||||
= Boolean.parseBoolean(cursor.getString(cursor
|
||||
.getColumnIndex(Table.COLUMN_IS_SELECTED)));
|
||||
final String id = cursor.getString(cursor.getColumnIndex(Table.COLUMN_ID));
|
||||
|
||||
return new DepictedItem(
|
||||
fileName,
|
||||
description,
|
||||
imageUrl,
|
||||
instanceList,
|
||||
categoryList,
|
||||
isSelected,
|
||||
id
|
||||
);
|
||||
}
|
||||
|
||||
private List<CategoryItem> convertToCategoryItems(List<String> categoryNameList,
|
||||
List<String> categoryDescriptionList, List<String> categoryThumbnailList) {
|
||||
List<CategoryItem> categoryItems = new ArrayList<>();
|
||||
for(int i=0; i<categoryNameList.size(); i++){
|
||||
categoryItems.add(new CategoryItem(categoryNameList.get(i),
|
||||
categoryDescriptionList.get(i),
|
||||
categoryThumbnailList.get(i), false));
|
||||
}
|
||||
return categoryItems;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts string to List
|
||||
* @param listString comma separated single string from of list items
|
||||
* @return List of string
|
||||
*/
|
||||
private List<String> StringToArray(final String listString) {
|
||||
final String[] elements = listString.split(",");
|
||||
return Arrays.asList(elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts string to List
|
||||
* @param list list of items
|
||||
* @return string comma separated single string of items
|
||||
*/
|
||||
private String ArrayToString(final List<String> list) {
|
||||
if (list != null) {
|
||||
return StringUtils.join(list, ',');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes data from DepictedItem and create a content value object
|
||||
* @param depictedItem depicted item
|
||||
* @return ContentValues
|
||||
*/
|
||||
private ContentValues toContentValues(final DepictedItem depictedItem) {
|
||||
|
||||
final List<String> namesOfCommonsCategories = new ArrayList<>();
|
||||
for (final CategoryItem category :
|
||||
depictedItem.getCommonsCategories()) {
|
||||
namesOfCommonsCategories.add(category.getName());
|
||||
}
|
||||
|
||||
final List<String> descriptionsOfCommonsCategories = new ArrayList<>();
|
||||
for (final CategoryItem category :
|
||||
depictedItem.getCommonsCategories()) {
|
||||
descriptionsOfCommonsCategories.add(category.getDescription());
|
||||
}
|
||||
|
||||
final List<String> thumbnailsOfCommonsCategories = new ArrayList<>();
|
||||
for (final CategoryItem category :
|
||||
depictedItem.getCommonsCategories()) {
|
||||
thumbnailsOfCommonsCategories.add(category.getThumbnail());
|
||||
}
|
||||
|
||||
final ContentValues cv = new ContentValues();
|
||||
cv.put(Table.COLUMN_NAME, depictedItem.getName());
|
||||
cv.put(Table.COLUMN_DESCRIPTION, depictedItem.getDescription());
|
||||
cv.put(Table.COLUMN_IMAGE, depictedItem.getImageUrl());
|
||||
cv.put(Table.COLUMN_INSTANCE_LIST, ArrayToString(depictedItem.getInstanceOfs()));
|
||||
cv.put(Table.COLUMN_CATEGORIES_NAME_LIST, ArrayToString(namesOfCommonsCategories));
|
||||
cv.put(Table.COLUMN_CATEGORIES_DESCRIPTION_LIST,
|
||||
ArrayToString(descriptionsOfCommonsCategories));
|
||||
cv.put(Table.COLUMN_CATEGORIES_THUMBNAIL_LIST,
|
||||
ArrayToString(thumbnailsOfCommonsCategories));
|
||||
cv.put(Table.COLUMN_IS_SELECTED, depictedItem.isSelected());
|
||||
cv.put(Table.COLUMN_ID, depictedItem.getId());
|
||||
return cv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Table of bookmarksItems data
|
||||
*/
|
||||
public static final class Table {
|
||||
public static final String TABLE_NAME = "bookmarksItems";
|
||||
public static final String COLUMN_NAME = "item_name";
|
||||
public static final String COLUMN_DESCRIPTION = "item_description";
|
||||
public static final String COLUMN_IMAGE = "item_image_url";
|
||||
public static final String COLUMN_INSTANCE_LIST = "item_instance_of";
|
||||
public static final String COLUMN_CATEGORIES_NAME_LIST = "item_name_categories";
|
||||
public static final String COLUMN_CATEGORIES_DESCRIPTION_LIST = "item_description_categories";
|
||||
public static final String COLUMN_CATEGORIES_THUMBNAIL_LIST = "item_thumbnail_categories";
|
||||
public static final String COLUMN_IS_SELECTED = "item_is_selected";
|
||||
public static final String COLUMN_ID = "item_id";
|
||||
|
||||
public static final String[] ALL_FIELDS = {
|
||||
COLUMN_NAME,
|
||||
COLUMN_DESCRIPTION,
|
||||
COLUMN_IMAGE,
|
||||
COLUMN_INSTANCE_LIST,
|
||||
COLUMN_CATEGORIES_NAME_LIST,
|
||||
COLUMN_CATEGORIES_DESCRIPTION_LIST,
|
||||
COLUMN_CATEGORIES_THUMBNAIL_LIST,
|
||||
COLUMN_IS_SELECTED,
|
||||
COLUMN_ID
|
||||
};
|
||||
|
||||
static final String DROP_TABLE_STATEMENT = "DROP TABLE IF EXISTS " + TABLE_NAME;
|
||||
static final String CREATE_TABLE_STATEMENT = "CREATE TABLE " + TABLE_NAME + " ("
|
||||
+ COLUMN_NAME + " STRING,"
|
||||
+ COLUMN_DESCRIPTION + " STRING,"
|
||||
+ COLUMN_IMAGE + " STRING,"
|
||||
+ COLUMN_INSTANCE_LIST + " STRING,"
|
||||
+ COLUMN_CATEGORIES_NAME_LIST + " STRING,"
|
||||
+ COLUMN_CATEGORIES_DESCRIPTION_LIST + " STRING,"
|
||||
+ COLUMN_CATEGORIES_THUMBNAIL_LIST + " STRING,"
|
||||
+ COLUMN_IS_SELECTED + " STRING,"
|
||||
+ COLUMN_ID + " STRING PRIMARY KEY"
|
||||
+ ");";
|
||||
|
||||
/**
|
||||
* Creates table
|
||||
* @param db SQLiteDatabase
|
||||
*/
|
||||
public static void onCreate(final SQLiteDatabase db) {
|
||||
db.execSQL(CREATE_TABLE_STATEMENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes database
|
||||
* @param db SQLiteDatabase
|
||||
*/
|
||||
public static void onDelete(final SQLiteDatabase db) {
|
||||
db.execSQL(DROP_TABLE_STATEMENT);
|
||||
onCreate(db);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates database
|
||||
* @param db SQLiteDatabase
|
||||
* @param from starting
|
||||
* @param to end
|
||||
*/
|
||||
public static void onUpdate(final SQLiteDatabase db, int from, final int to) {
|
||||
if (from == to) {
|
||||
return;
|
||||
}
|
||||
if (from < 18) {
|
||||
// doesn't exist yet
|
||||
from++;
|
||||
onUpdate(db, from, to);
|
||||
return;
|
||||
}
|
||||
|
||||
if (from == 18) {
|
||||
// table added in version 19
|
||||
onCreate(db);
|
||||
from++;
|
||||
onUpdate(db, from, to);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,199 @@
|
|||
package fr.free.nrw.commons.bookmarks.items
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.ContentProviderClient
|
||||
import android.content.ContentValues
|
||||
import android.database.Cursor
|
||||
import android.os.RemoteException
|
||||
import androidx.core.content.contentValuesOf
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsContentProvider.Companion.BASE_URI
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsContentProvider.Companion.uriForName
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.COLUMN_CATEGORIES_DESCRIPTION_LIST
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.COLUMN_CATEGORIES_NAME_LIST
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.COLUMN_CATEGORIES_THUMBNAIL_LIST
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.COLUMN_DESCRIPTION
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.COLUMN_ID
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.COLUMN_IMAGE
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.COLUMN_INSTANCE_LIST
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.COLUMN_IS_SELECTED
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.COLUMN_NAME
|
||||
import fr.free.nrw.commons.category.CategoryItem
|
||||
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem
|
||||
import fr.free.nrw.commons.utils.arrayToString
|
||||
import fr.free.nrw.commons.utils.getString
|
||||
import fr.free.nrw.commons.utils.getStringArray
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Named
|
||||
import javax.inject.Provider
|
||||
import javax.inject.Singleton
|
||||
|
||||
/**
|
||||
* Handles database operations for bookmarked items
|
||||
*/
|
||||
@Singleton
|
||||
class BookmarkItemsDao @Inject constructor(
|
||||
@param:Named("bookmarksItem") private val clientProvider: Provider<ContentProviderClient>
|
||||
) {
|
||||
/**
|
||||
* Find all persisted items bookmarks on database
|
||||
* @return list of bookmarks
|
||||
*/
|
||||
fun getAllBookmarksItems(): List<DepictedItem> {
|
||||
val items: MutableList<DepictedItem> = mutableListOf()
|
||||
val db = clientProvider.get()
|
||||
try {
|
||||
db.query(
|
||||
BASE_URI,
|
||||
BookmarkItemsTable.ALL_FIELDS,
|
||||
null,
|
||||
arrayOf(),
|
||||
null
|
||||
).use { cursor ->
|
||||
while (cursor != null && cursor.moveToNext()) {
|
||||
items.add(fromCursor(cursor))
|
||||
}
|
||||
}
|
||||
} catch (e: RemoteException) {
|
||||
throw RuntimeException(e)
|
||||
} finally {
|
||||
db.release()
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Look for a bookmark in database and in order to insert or delete it
|
||||
* @param depictedItem : Bookmark object
|
||||
* @return boolean : is bookmark now favorite ?
|
||||
*/
|
||||
fun updateBookmarkItem(depictedItem: DepictedItem): Boolean {
|
||||
val bookmarkExists = findBookmarkItem(depictedItem.id)
|
||||
if (bookmarkExists) {
|
||||
deleteBookmarkItem(depictedItem)
|
||||
} else {
|
||||
addBookmarkItem(depictedItem)
|
||||
}
|
||||
return !bookmarkExists
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a Bookmark to database
|
||||
* @param depictedItem : Bookmark to add
|
||||
*/
|
||||
private fun addBookmarkItem(depictedItem: DepictedItem) {
|
||||
val db = clientProvider.get()
|
||||
try {
|
||||
db.insert(BASE_URI, toContentValues(depictedItem))
|
||||
} catch (e: RemoteException) {
|
||||
throw RuntimeException(e)
|
||||
} finally {
|
||||
db.release()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a bookmark from database
|
||||
* @param depictedItem : Bookmark to delete
|
||||
*/
|
||||
private fun deleteBookmarkItem(depictedItem: DepictedItem) {
|
||||
val db = clientProvider.get()
|
||||
try {
|
||||
db.delete(uriForName(depictedItem.id), null, null)
|
||||
} catch (e: RemoteException) {
|
||||
throw RuntimeException(e)
|
||||
} finally {
|
||||
db.release()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a bookmark from database based on its name
|
||||
* @param depictedItemID : Bookmark to find
|
||||
* @return boolean : is bookmark in database ?
|
||||
*/
|
||||
fun findBookmarkItem(depictedItemID: String?): Boolean {
|
||||
if (depictedItemID == null) { //Avoiding NPE's
|
||||
return false
|
||||
}
|
||||
val db = clientProvider.get()
|
||||
try {
|
||||
db.query(
|
||||
BASE_URI,
|
||||
BookmarkItemsTable.ALL_FIELDS,
|
||||
COLUMN_ID + "=?",
|
||||
arrayOf(depictedItemID),
|
||||
null
|
||||
).use { cursor ->
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
} catch (e: RemoteException) {
|
||||
throw RuntimeException(e)
|
||||
} finally {
|
||||
db.release()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Recives real data from cursor
|
||||
* @param cursor : Object for storing database data
|
||||
* @return DepictedItem
|
||||
*/
|
||||
@SuppressLint("Range")
|
||||
fun fromCursor(cursor: Cursor) = with(cursor) {
|
||||
DepictedItem(
|
||||
getString(COLUMN_NAME),
|
||||
getString(COLUMN_DESCRIPTION),
|
||||
getString(COLUMN_IMAGE),
|
||||
getStringArray(COLUMN_INSTANCE_LIST),
|
||||
convertToCategoryItems(
|
||||
getStringArray(COLUMN_CATEGORIES_NAME_LIST),
|
||||
getStringArray(COLUMN_CATEGORIES_DESCRIPTION_LIST),
|
||||
getStringArray(COLUMN_CATEGORIES_THUMBNAIL_LIST)
|
||||
),
|
||||
getString(COLUMN_IS_SELECTED).toBoolean(),
|
||||
getString(COLUMN_ID)
|
||||
)
|
||||
}
|
||||
|
||||
private fun convertToCategoryItems(
|
||||
categoryNameList: List<String>,
|
||||
categoryDescriptionList: List<String>,
|
||||
categoryThumbnailList: List<String>
|
||||
): List<CategoryItem> {
|
||||
return buildList {
|
||||
for (i in categoryNameList.indices) {
|
||||
add(
|
||||
CategoryItem(
|
||||
categoryNameList[i],
|
||||
categoryDescriptionList[i],
|
||||
categoryThumbnailList[i],
|
||||
false
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes data from DepictedItem and create a content value object
|
||||
* @param depictedItem depicted item
|
||||
* @return ContentValues
|
||||
*/
|
||||
private fun toContentValues(depictedItem: DepictedItem): ContentValues {
|
||||
return contentValuesOf(
|
||||
COLUMN_NAME to depictedItem.name,
|
||||
COLUMN_DESCRIPTION to depictedItem.description,
|
||||
COLUMN_IMAGE to depictedItem.imageUrl,
|
||||
COLUMN_INSTANCE_LIST to arrayToString(depictedItem.instanceOfs),
|
||||
COLUMN_CATEGORIES_NAME_LIST to arrayToString(depictedItem.commonsCategories.map { it.name }),
|
||||
COLUMN_CATEGORIES_DESCRIPTION_LIST to arrayToString(depictedItem.commonsCategories.map { it.description }),
|
||||
COLUMN_CATEGORIES_THUMBNAIL_LIST to arrayToString(depictedItem.commonsCategories.map { it.thumbnail }),
|
||||
COLUMN_IS_SELECTED to depictedItem.isSelected,
|
||||
COLUMN_ID to depictedItem.id,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
package fr.free.nrw.commons.bookmarks.items;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import dagger.android.support.DaggerFragment;
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.databinding.FragmentBookmarksItemsBinding;
|
||||
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem;
|
||||
import java.util.List;
|
||||
import javax.inject.Inject;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Tab fragment to show list of bookmarked Wikidata Items
|
||||
*/
|
||||
public class BookmarkItemsFragment extends DaggerFragment {
|
||||
|
||||
private FragmentBookmarksItemsBinding binding;
|
||||
|
||||
@Inject
|
||||
BookmarkItemsController controller;
|
||||
|
||||
public static BookmarkItemsFragment newInstance() {
|
||||
return new BookmarkItemsFragment();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(
|
||||
@NonNull final LayoutInflater inflater,
|
||||
final ViewGroup container,
|
||||
final Bundle savedInstanceState
|
||||
) {
|
||||
binding = FragmentBookmarksItemsBinding.inflate(inflater, container, false);
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(final @NotNull View view, @Nullable final Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
initList(requireContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
initList(requireContext());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of DepictedItem and sets to the adapter
|
||||
* @param context context
|
||||
*/
|
||||
private void initList(final Context context) {
|
||||
final List<DepictedItem> depictItems = controller.loadFavoritesItems();
|
||||
final BookmarkItemsAdapter adapter = new BookmarkItemsAdapter(depictItems, context);
|
||||
binding.listView.setAdapter(adapter);
|
||||
binding.loadingImagesProgressBar.setVisibility(View.GONE);
|
||||
if (depictItems.isEmpty()) {
|
||||
binding.statusMessage.setText(R.string.bookmark_empty);
|
||||
binding.statusMessage.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
binding.statusMessage.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
binding = null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
package fr.free.nrw.commons.bookmarks.items
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import dagger.android.support.DaggerFragment
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.databinding.FragmentBookmarksItemsBinding
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Tab fragment to show list of bookmarked Wikidata Items
|
||||
*/
|
||||
class BookmarkItemsFragment : DaggerFragment() {
|
||||
private var binding: FragmentBookmarksItemsBinding? = null
|
||||
|
||||
@JvmField
|
||||
@Inject
|
||||
var controller: BookmarkItemsController? = null
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
binding = FragmentBookmarksItemsBinding.inflate(inflater, container, false)
|
||||
return binding!!.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
initList(requireContext())
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
initList(requireContext())
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of DepictedItem and sets to the adapter
|
||||
* @param context context
|
||||
*/
|
||||
private fun initList(context: Context) {
|
||||
val depictItems = controller!!.loadFavoritesItems()
|
||||
binding!!.listView.adapter = BookmarkItemsAdapter(depictItems, context)
|
||||
binding!!.loadingImagesProgressBar.visibility = View.GONE
|
||||
if (depictItems.isEmpty()) {
|
||||
binding!!.statusMessage.setText(R.string.bookmark_empty)
|
||||
binding!!.statusMessage.visibility = View.VISIBLE
|
||||
} else {
|
||||
binding!!.statusMessage.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
binding = null
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
package fr.free.nrw.commons.bookmarks.items
|
||||
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
|
||||
/**
|
||||
* Table of bookmarksItems data
|
||||
*/
|
||||
object BookmarkItemsTable {
|
||||
const val TABLE_NAME = "bookmarksItems"
|
||||
const val COLUMN_NAME = "item_name"
|
||||
const val COLUMN_DESCRIPTION = "item_description"
|
||||
const val COLUMN_IMAGE = "item_image_url"
|
||||
const val COLUMN_INSTANCE_LIST = "item_instance_of"
|
||||
const val COLUMN_CATEGORIES_NAME_LIST = "item_name_categories"
|
||||
const val COLUMN_CATEGORIES_DESCRIPTION_LIST = "item_description_categories"
|
||||
const val COLUMN_CATEGORIES_THUMBNAIL_LIST = "item_thumbnail_categories"
|
||||
const val COLUMN_IS_SELECTED = "item_is_selected"
|
||||
const val COLUMN_ID = "item_id"
|
||||
|
||||
val ALL_FIELDS = arrayOf(
|
||||
COLUMN_NAME,
|
||||
COLUMN_DESCRIPTION,
|
||||
COLUMN_IMAGE,
|
||||
COLUMN_INSTANCE_LIST,
|
||||
COLUMN_CATEGORIES_NAME_LIST,
|
||||
COLUMN_CATEGORIES_DESCRIPTION_LIST,
|
||||
COLUMN_CATEGORIES_THUMBNAIL_LIST,
|
||||
COLUMN_IS_SELECTED,
|
||||
COLUMN_ID
|
||||
)
|
||||
|
||||
const val DROP_TABLE_STATEMENT = "DROP TABLE IF EXISTS $TABLE_NAME"
|
||||
|
||||
val CREATE_TABLE_STATEMENT =
|
||||
"""CREATE TABLE $TABLE_NAME (
|
||||
$COLUMN_NAME STRING,
|
||||
$COLUMN_DESCRIPTION STRING,
|
||||
$COLUMN_IMAGE STRING,
|
||||
$COLUMN_INSTANCE_LIST STRING,
|
||||
$COLUMN_CATEGORIES_NAME_LIST STRING,
|
||||
$COLUMN_CATEGORIES_DESCRIPTION_LIST STRING,
|
||||
$COLUMN_CATEGORIES_THUMBNAIL_LIST STRING,
|
||||
$COLUMN_IS_SELECTED STRING,
|
||||
$COLUMN_ID STRING PRIMARY KEY
|
||||
);""".trimIndent()
|
||||
|
||||
/**
|
||||
* Creates table
|
||||
*
|
||||
* @param db SQLiteDatabase
|
||||
*/
|
||||
fun onCreate(db: SQLiteDatabase) {
|
||||
db.execSQL(CREATE_TABLE_STATEMENT)
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes database
|
||||
*
|
||||
* @param db SQLiteDatabase
|
||||
*/
|
||||
fun onDelete(db: SQLiteDatabase) {
|
||||
db.execSQL(DROP_TABLE_STATEMENT)
|
||||
onCreate(db)
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates database
|
||||
*
|
||||
* @param db SQLiteDatabase
|
||||
* @param from starting
|
||||
* @param to end
|
||||
*/
|
||||
fun onUpdate(db: SQLiteDatabase, from: Int, to: Int) {
|
||||
if (from == to) {
|
||||
return
|
||||
}
|
||||
|
||||
if (from < 18) {
|
||||
// doesn't exist yet
|
||||
onUpdate(db, from + 1, to)
|
||||
return
|
||||
}
|
||||
|
||||
if (from == 18) {
|
||||
// table added in version 19
|
||||
onCreate(db)
|
||||
onUpdate(db, from + 1, to)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,120 +0,0 @@
|
|||
package fr.free.nrw.commons.bookmarks.pictures;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteQueryBuilder;
|
||||
// We can get uri using java.Net.Uri, but andoid implimentation is faster (but it's forgiving with handling exceptions though)
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import fr.free.nrw.commons.BuildConfig;
|
||||
import fr.free.nrw.commons.data.DBOpenHelper;
|
||||
import fr.free.nrw.commons.di.CommonsDaggerContentProvider;
|
||||
import timber.log.Timber;
|
||||
|
||||
import static fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao.Table.COLUMN_MEDIA_NAME;
|
||||
import static fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao.Table.TABLE_NAME;
|
||||
|
||||
/**
|
||||
* Handles private storage for Bookmark pictures
|
||||
*/
|
||||
public class BookmarkPicturesContentProvider extends CommonsDaggerContentProvider {
|
||||
|
||||
private static final String BASE_PATH = "bookmarks";
|
||||
public static final Uri BASE_URI = Uri.parse("content://" + BuildConfig.BOOKMARK_AUTHORITY + "/" + BASE_PATH);
|
||||
|
||||
/**
|
||||
* Append bookmark pictures name to the base uri
|
||||
*/
|
||||
public static Uri uriForName(String name) {
|
||||
return Uri.parse(BASE_URI.toString() + "/" + name);
|
||||
}
|
||||
|
||||
@Inject
|
||||
DBOpenHelper dbOpenHelper;
|
||||
|
||||
@Override
|
||||
public String getType(@NonNull Uri uri) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries the SQLite database for the bookmark pictures
|
||||
* @param uri : contains the uri for bookmark pictures
|
||||
* @param projection
|
||||
* @param selection : handles Where
|
||||
* @param selectionArgs : the condition of Where clause
|
||||
* @param sortOrder : ascending or descending
|
||||
*/
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Override
|
||||
public Cursor query(@NonNull Uri uri, String[] projection, String selection,
|
||||
String[] selectionArgs, String sortOrder) {
|
||||
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
|
||||
queryBuilder.setTables(TABLE_NAME);
|
||||
|
||||
SQLiteDatabase db = dbOpenHelper.getReadableDatabase();
|
||||
Cursor cursor = queryBuilder.query(db, projection, selection, selectionArgs, null, null, sortOrder);
|
||||
cursor.setNotificationUri(getContext().getContentResolver(), uri);
|
||||
|
||||
return cursor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the update query of local SQLite Database
|
||||
* @param uri : contains the uri for bookmark pictures
|
||||
* @param contentValues : new values to be entered to db
|
||||
* @param selection : handles Where
|
||||
* @param selectionArgs : the condition of Where clause
|
||||
*/
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Override
|
||||
public int update(@NonNull Uri uri, ContentValues contentValues, String selection,
|
||||
String[] selectionArgs) {
|
||||
SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase();
|
||||
int rowsUpdated;
|
||||
if (TextUtils.isEmpty(selection)) {
|
||||
int id = Integer.valueOf(uri.getLastPathSegment());
|
||||
rowsUpdated = sqlDB.update(TABLE_NAME,
|
||||
contentValues,
|
||||
COLUMN_MEDIA_NAME + " = ?",
|
||||
new String[]{String.valueOf(id)});
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"Parameter `selection` should be empty when updating an ID");
|
||||
}
|
||||
getContext().getContentResolver().notifyChange(uri, null);
|
||||
return rowsUpdated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the insertion of new bookmark pictures record to local SQLite Database
|
||||
*/
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Override
|
||||
public Uri insert(@NonNull Uri uri, ContentValues contentValues) {
|
||||
SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase();
|
||||
long id = sqlDB.insert(BookmarkPicturesDao.Table.TABLE_NAME, null, contentValues);
|
||||
getContext().getContentResolver().notifyChange(uri, null);
|
||||
return Uri.parse(BASE_URI + "/" + id);
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Override
|
||||
public int delete(@NonNull Uri uri, String s, String[] strings) {
|
||||
int rows;
|
||||
SQLiteDatabase db = dbOpenHelper.getReadableDatabase();
|
||||
Timber.d("Deleting bookmark name %s", uri.getLastPathSegment());
|
||||
rows = db.delete(TABLE_NAME,
|
||||
"media_name = ?",
|
||||
new String[]{uri.getLastPathSegment()}
|
||||
);
|
||||
getContext().getContentResolver().notifyChange(uri, null);
|
||||
return rows;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
package fr.free.nrw.commons.bookmarks.pictures
|
||||
|
||||
import android.content.ContentValues
|
||||
import android.database.Cursor
|
||||
import android.database.sqlite.SQLiteQueryBuilder
|
||||
import android.net.Uri
|
||||
import fr.free.nrw.commons.BuildConfig
|
||||
import fr.free.nrw.commons.di.CommonsDaggerContentProvider
|
||||
import androidx.core.net.toUri
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarksTable.COLUMN_MEDIA_NAME
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarksTable.TABLE_NAME
|
||||
|
||||
/**
|
||||
* Handles private storage for Bookmark pictures
|
||||
*/
|
||||
class BookmarkPicturesContentProvider : CommonsDaggerContentProvider() {
|
||||
override fun getType(uri: Uri): String? = null
|
||||
|
||||
/**
|
||||
* Queries the SQLite database for the bookmark pictures
|
||||
* @param uri : contains the uri for bookmark pictures
|
||||
* @param projection
|
||||
* @param selection : handles Where
|
||||
* @param selectionArgs : the condition of Where clause
|
||||
* @param sortOrder : ascending or descending
|
||||
*/
|
||||
override fun query(
|
||||
uri: Uri, projection: Array<String>?, selection: String?,
|
||||
selectionArgs: Array<String>?, sortOrder: String?
|
||||
): Cursor {
|
||||
val queryBuilder = SQLiteQueryBuilder().apply {
|
||||
tables = TABLE_NAME
|
||||
}
|
||||
|
||||
val cursor = queryBuilder.query(
|
||||
requireDb(), projection, selection,
|
||||
selectionArgs, null, null, sortOrder
|
||||
)
|
||||
cursor.setNotificationUri(requireContext().contentResolver, uri)
|
||||
|
||||
return cursor
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the update query of local SQLite Database
|
||||
* @param uri : contains the uri for bookmark pictures
|
||||
* @param contentValues : new values to be entered to db
|
||||
* @param selection : handles Where
|
||||
* @param selectionArgs : the condition of Where clause
|
||||
*/
|
||||
override fun update(
|
||||
uri: Uri, contentValues: ContentValues?, selection: String?,
|
||||
selectionArgs: Array<String>?
|
||||
): Int {
|
||||
val rowsUpdated: Int
|
||||
if (selection.isNullOrEmpty()) {
|
||||
val id = uri.lastPathSegment!!.toInt()
|
||||
rowsUpdated = requireDb().update(
|
||||
TABLE_NAME,
|
||||
contentValues,
|
||||
"$COLUMN_MEDIA_NAME = ?",
|
||||
arrayOf(id.toString())
|
||||
)
|
||||
} else {
|
||||
throw IllegalArgumentException(
|
||||
"Parameter `selection` should be empty when updating an ID"
|
||||
)
|
||||
}
|
||||
requireContext().contentResolver.notifyChange(uri, null)
|
||||
return rowsUpdated
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the insertion of new bookmark pictures record to local SQLite Database
|
||||
*/
|
||||
override fun insert(uri: Uri, contentValues: ContentValues?): Uri {
|
||||
val id = requireDb().insert(TABLE_NAME, null, contentValues)
|
||||
requireContext().contentResolver.notifyChange(uri, null)
|
||||
return "$BASE_URI/$id".toUri()
|
||||
}
|
||||
|
||||
override fun delete(uri: Uri, s: String?, strings: Array<String>?): Int {
|
||||
val rows: Int = requireDb().delete(
|
||||
TABLE_NAME,
|
||||
"media_name = ?",
|
||||
arrayOf(uri.lastPathSegment)
|
||||
)
|
||||
requireContext().contentResolver.notifyChange(uri, null)
|
||||
return rows
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val BASE_PATH = "bookmarks"
|
||||
@JvmField
|
||||
val BASE_URI: Uri = "content://${BuildConfig.BOOKMARK_AUTHORITY}/$BASE_PATH".toUri()
|
||||
|
||||
@JvmStatic
|
||||
fun uriForName(name: String): Uri = "$BASE_URI/$name".toUri()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
package fr.free.nrw.commons.bookmarks.pictures;
|
||||
|
||||
import fr.free.nrw.commons.Media;
|
||||
import fr.free.nrw.commons.bookmarks.models.Bookmark;
|
||||
import fr.free.nrw.commons.media.MediaClient;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.ObservableSource;
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.functions.Function;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
@Singleton
|
||||
public class BookmarkPicturesController {
|
||||
|
||||
private final MediaClient mediaClient;
|
||||
private final BookmarkPicturesDao bookmarkDao;
|
||||
|
||||
private List<Bookmark> currentBookmarks;
|
||||
|
||||
@Inject
|
||||
public BookmarkPicturesController(MediaClient mediaClient, BookmarkPicturesDao bookmarkDao) {
|
||||
this.mediaClient = mediaClient;
|
||||
this.bookmarkDao = bookmarkDao;
|
||||
currentBookmarks = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the Media objects from the raw data stored in DB and the API.
|
||||
* @return a list of bookmarked Media object
|
||||
*/
|
||||
Single<List<Media>> loadBookmarkedPictures() {
|
||||
List<Bookmark> bookmarks = bookmarkDao.getAllBookmarks();
|
||||
currentBookmarks = bookmarks;
|
||||
return Observable.fromIterable(bookmarks)
|
||||
.flatMap((Function<Bookmark, ObservableSource<Media>>) this::getMediaFromBookmark)
|
||||
.toList();
|
||||
}
|
||||
|
||||
private Observable<Media> getMediaFromBookmark(Bookmark bookmark) {
|
||||
return mediaClient.getMedia(bookmark.getMediaName())
|
||||
.toObservable()
|
||||
.onErrorResumeNext(Observable.empty());
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the Media objects from the raw data stored in DB and the API.
|
||||
* @return a list of bookmarked Media object
|
||||
*/
|
||||
boolean needRefreshBookmarkedPictures() {
|
||||
List<Bookmark> bookmarks = bookmarkDao.getAllBookmarks();
|
||||
return bookmarks.size() != currentBookmarks.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels the requests to the API and the DB
|
||||
*/
|
||||
void stop() {
|
||||
//noop
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
package fr.free.nrw.commons.bookmarks.pictures
|
||||
|
||||
import fr.free.nrw.commons.Media
|
||||
import fr.free.nrw.commons.bookmarks.models.Bookmark
|
||||
import fr.free.nrw.commons.media.MediaClient
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.Single
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class BookmarkPicturesController @Inject constructor(
|
||||
private val mediaClient: MediaClient,
|
||||
private val bookmarkDao: BookmarkPicturesDao
|
||||
) {
|
||||
private var currentBookmarks: List<Bookmark> = listOf()
|
||||
|
||||
/**
|
||||
* Loads the Media objects from the raw data stored in DB and the API.
|
||||
* @return a list of bookmarked Media object
|
||||
*/
|
||||
fun loadBookmarkedPictures(): Single<List<Media>> {
|
||||
val bookmarks = bookmarkDao.getAllBookmarks()
|
||||
currentBookmarks = bookmarks
|
||||
return Observable.fromIterable(bookmarks).flatMap {
|
||||
mediaClient.getMedia(it.mediaName)
|
||||
.toObservable()
|
||||
.onErrorResumeNext(Observable.empty())
|
||||
}.toList()
|
||||
}
|
||||
|
||||
fun needRefreshBookmarkedPictures(): Boolean {
|
||||
val bookmarks = bookmarkDao.getAllBookmarks()
|
||||
return bookmarks.size != currentBookmarks.size
|
||||
}
|
||||
|
||||
fun stop() = Unit
|
||||
}
|
||||
|
|
@ -1,227 +0,0 @@
|
|||
package fr.free.nrw.commons.bookmarks.pictures;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.ContentProviderClient;
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.os.RemoteException;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Provider;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import fr.free.nrw.commons.bookmarks.models.Bookmark;
|
||||
|
||||
import static fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesContentProvider.BASE_URI;
|
||||
|
||||
@Singleton
|
||||
public class BookmarkPicturesDao {
|
||||
|
||||
private final Provider<ContentProviderClient> clientProvider;
|
||||
|
||||
@Inject
|
||||
public BookmarkPicturesDao(@Named("bookmarks") Provider<ContentProviderClient> clientProvider) {
|
||||
this.clientProvider = clientProvider;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find all persisted pictures bookmarks on database
|
||||
*
|
||||
* @return list of bookmarks
|
||||
*/
|
||||
@NonNull
|
||||
public List<Bookmark> getAllBookmarks() {
|
||||
List<Bookmark> items = new ArrayList<>();
|
||||
Cursor cursor = null;
|
||||
ContentProviderClient db = clientProvider.get();
|
||||
try {
|
||||
cursor = db.query(
|
||||
BookmarkPicturesContentProvider.BASE_URI,
|
||||
Table.ALL_FIELDS,
|
||||
null,
|
||||
new String[]{},
|
||||
null);
|
||||
while (cursor != null && cursor.moveToNext()) {
|
||||
items.add(fromCursor(cursor));
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
db.release();
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Look for a bookmark in database and in order to insert or delete it
|
||||
*
|
||||
* @param bookmark : Bookmark object
|
||||
* @return boolean : is bookmark now fav ?
|
||||
*/
|
||||
public boolean updateBookmark(Bookmark bookmark) {
|
||||
boolean bookmarkExists = findBookmark(bookmark);
|
||||
if (bookmarkExists) {
|
||||
deleteBookmark(bookmark);
|
||||
} else {
|
||||
addBookmark(bookmark);
|
||||
}
|
||||
return !bookmarkExists;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a Bookmark to database
|
||||
*
|
||||
* @param bookmark : Bookmark to add
|
||||
*/
|
||||
private void addBookmark(Bookmark bookmark) {
|
||||
ContentProviderClient db = clientProvider.get();
|
||||
try {
|
||||
db.insert(BASE_URI, toContentValues(bookmark));
|
||||
} catch (RemoteException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
db.release();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a bookmark from database
|
||||
*
|
||||
* @param bookmark : Bookmark to delete
|
||||
*/
|
||||
private void deleteBookmark(Bookmark bookmark) {
|
||||
ContentProviderClient db = clientProvider.get();
|
||||
try {
|
||||
if (bookmark.getContentUri() == null) {
|
||||
throw new RuntimeException("tried to delete item with no content URI");
|
||||
} else {
|
||||
db.delete(bookmark.getContentUri(), null, null);
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
db.release();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a bookmark from database based on its name
|
||||
*
|
||||
* @param bookmark : Bookmark to find
|
||||
* @return boolean : is bookmark in database ?
|
||||
*/
|
||||
public boolean findBookmark(Bookmark bookmark) {
|
||||
if (bookmark == null) {//Avoiding NPE's
|
||||
return false;
|
||||
}
|
||||
|
||||
Cursor cursor = null;
|
||||
ContentProviderClient db = clientProvider.get();
|
||||
try {
|
||||
cursor = db.query(
|
||||
BookmarkPicturesContentProvider.BASE_URI,
|
||||
Table.ALL_FIELDS,
|
||||
Table.COLUMN_MEDIA_NAME + "=?",
|
||||
new String[]{bookmark.getMediaName()},
|
||||
null);
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
return true;
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
// This feels lazy, but to hell with checked exceptions. :)
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
db.release();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressLint("Range")
|
||||
@NonNull
|
||||
Bookmark fromCursor(Cursor cursor) {
|
||||
String fileName = cursor.getString(cursor.getColumnIndex(Table.COLUMN_MEDIA_NAME));
|
||||
return new Bookmark(
|
||||
fileName,
|
||||
cursor.getString(cursor.getColumnIndex(Table.COLUMN_CREATOR)),
|
||||
BookmarkPicturesContentProvider.uriForName(fileName)
|
||||
);
|
||||
}
|
||||
|
||||
private ContentValues toContentValues(Bookmark bookmark) {
|
||||
ContentValues cv = new ContentValues();
|
||||
cv.put(BookmarkPicturesDao.Table.COLUMN_MEDIA_NAME, bookmark.getMediaName());
|
||||
cv.put(BookmarkPicturesDao.Table.COLUMN_CREATOR, bookmark.getMediaCreator());
|
||||
return cv;
|
||||
}
|
||||
|
||||
|
||||
public static class Table {
|
||||
public static final String TABLE_NAME = "bookmarks";
|
||||
|
||||
public static final String COLUMN_MEDIA_NAME = "media_name";
|
||||
public static final String COLUMN_CREATOR = "media_creator";
|
||||
|
||||
// NOTE! KEEP IN SAME ORDER AS THEY ARE DEFINED UP THERE. HELPS HARD CODE COLUMN INDICES.
|
||||
public static final String[] ALL_FIELDS = {
|
||||
COLUMN_MEDIA_NAME,
|
||||
COLUMN_CREATOR
|
||||
};
|
||||
|
||||
public static final String DROP_TABLE_STATEMENT = "DROP TABLE IF EXISTS " + TABLE_NAME;
|
||||
|
||||
public static final String CREATE_TABLE_STATEMENT = "CREATE TABLE " + TABLE_NAME + " ("
|
||||
+ COLUMN_MEDIA_NAME + " STRING PRIMARY KEY,"
|
||||
+ COLUMN_CREATOR + " STRING"
|
||||
+ ");";
|
||||
|
||||
public static void onCreate(SQLiteDatabase db) {
|
||||
db.execSQL(CREATE_TABLE_STATEMENT);
|
||||
}
|
||||
|
||||
public static void onDelete(SQLiteDatabase db) {
|
||||
db.execSQL(DROP_TABLE_STATEMENT);
|
||||
onCreate(db);
|
||||
}
|
||||
|
||||
public static void onUpdate(SQLiteDatabase db, int from, int to) {
|
||||
if (from == to) {
|
||||
return;
|
||||
}
|
||||
if (from < 7) {
|
||||
// doesn't exist yet
|
||||
from++;
|
||||
onUpdate(db, from, to);
|
||||
return;
|
||||
}
|
||||
|
||||
if (from == 7) {
|
||||
// table added in version 8
|
||||
onCreate(db);
|
||||
from++;
|
||||
onUpdate(db, from, to);
|
||||
return;
|
||||
}
|
||||
|
||||
if (from == 8) {
|
||||
from++;
|
||||
onUpdate(db, from, to);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,141 @@
|
|||
package fr.free.nrw.commons.bookmarks.pictures
|
||||
|
||||
import android.content.ContentProviderClient
|
||||
import android.content.ContentValues
|
||||
import android.database.Cursor
|
||||
import android.os.RemoteException
|
||||
import androidx.core.content.contentValuesOf
|
||||
import fr.free.nrw.commons.bookmarks.models.Bookmark
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesContentProvider.Companion.BASE_URI
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesContentProvider.Companion.uriForName
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarksTable.ALL_FIELDS
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarksTable.COLUMN_CREATOR
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarksTable.COLUMN_MEDIA_NAME
|
||||
import fr.free.nrw.commons.utils.getString
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Named
|
||||
import javax.inject.Provider
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class BookmarkPicturesDao @Inject constructor(
|
||||
@param:Named("bookmarks") private val clientProvider: Provider<ContentProviderClient>
|
||||
) {
|
||||
/**
|
||||
* Find all persisted pictures bookmarks on database
|
||||
*
|
||||
* @return list of bookmarks
|
||||
*/
|
||||
fun getAllBookmarks(): List<Bookmark> {
|
||||
val items: MutableList<Bookmark> = mutableListOf()
|
||||
var cursor: Cursor? = null
|
||||
val db = clientProvider.get()
|
||||
try {
|
||||
cursor = db.query(
|
||||
BASE_URI, ALL_FIELDS, null, arrayOf(), null
|
||||
)
|
||||
while (cursor != null && cursor.moveToNext()) {
|
||||
items.add(fromCursor(cursor))
|
||||
}
|
||||
} catch (e: RemoteException) {
|
||||
throw RuntimeException(e)
|
||||
} finally {
|
||||
cursor?.close()
|
||||
db.release()
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
/**
|
||||
* Look for a bookmark in database and in order to insert or delete it
|
||||
*
|
||||
* @param bookmark : Bookmark object
|
||||
* @return boolean : is bookmark now fav ?
|
||||
*/
|
||||
fun updateBookmark(bookmark: Bookmark): Boolean {
|
||||
val bookmarkExists = findBookmark(bookmark)
|
||||
if (bookmarkExists) {
|
||||
deleteBookmark(bookmark)
|
||||
} else {
|
||||
addBookmark(bookmark)
|
||||
}
|
||||
return !bookmarkExists
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a Bookmark to database
|
||||
*
|
||||
* @param bookmark : Bookmark to add
|
||||
*/
|
||||
private fun addBookmark(bookmark: Bookmark) {
|
||||
val db = clientProvider.get()
|
||||
try {
|
||||
db.insert(BASE_URI, toContentValues(bookmark))
|
||||
} catch (e: RemoteException) {
|
||||
throw RuntimeException(e)
|
||||
} finally {
|
||||
db.release()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a bookmark from database
|
||||
*
|
||||
* @param bookmark : Bookmark to delete
|
||||
*/
|
||||
private fun deleteBookmark(bookmark: Bookmark) {
|
||||
val db = clientProvider.get()
|
||||
try {
|
||||
if (bookmark.contentUri == null) {
|
||||
throw RuntimeException("tried to delete item with no content URI")
|
||||
} else {
|
||||
db.delete(bookmark.contentUri!!, null, null)
|
||||
}
|
||||
} catch (e: RemoteException) {
|
||||
throw RuntimeException(e)
|
||||
} finally {
|
||||
db.release()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a bookmark from database based on its name
|
||||
*
|
||||
* @param bookmark : Bookmark to find
|
||||
* @return boolean : is bookmark in database ?
|
||||
*/
|
||||
fun findBookmark(bookmark: Bookmark?): Boolean {
|
||||
if (bookmark == null) {
|
||||
return false
|
||||
}
|
||||
|
||||
var cursor: Cursor? = null
|
||||
val db = clientProvider.get()
|
||||
try {
|
||||
cursor = db.query(
|
||||
BASE_URI, ALL_FIELDS, "$COLUMN_MEDIA_NAME=?", arrayOf(bookmark.mediaName), null
|
||||
)
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
return true
|
||||
}
|
||||
} catch (e: RemoteException) {
|
||||
throw RuntimeException(e)
|
||||
} finally {
|
||||
cursor?.close()
|
||||
db.release()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun fromCursor(cursor: Cursor): Bookmark {
|
||||
val fileName = cursor.getString(COLUMN_MEDIA_NAME)
|
||||
return Bookmark(
|
||||
fileName, cursor.getString(COLUMN_CREATOR), uriForName(fileName)
|
||||
)
|
||||
}
|
||||
|
||||
private fun toContentValues(bookmark: Bookmark): ContentValues = contentValuesOf(
|
||||
COLUMN_MEDIA_NAME to bookmark.mediaName,
|
||||
COLUMN_CREATOR to bookmark.mediaCreator
|
||||
)
|
||||
}
|
||||
|
|
@ -1,218 +0,0 @@
|
|||
package fr.free.nrw.commons.bookmarks.pictures;
|
||||
|
||||
import static android.view.View.GONE;
|
||||
import static android.view.View.VISIBLE;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ListAdapter;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import dagger.android.support.DaggerFragment;
|
||||
import fr.free.nrw.commons.Media;
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.bookmarks.BookmarkListRootFragment;
|
||||
import fr.free.nrw.commons.category.GridViewAdapter;
|
||||
import fr.free.nrw.commons.databinding.FragmentBookmarksPicturesBinding;
|
||||
import fr.free.nrw.commons.utils.NetworkUtils;
|
||||
import fr.free.nrw.commons.utils.ViewUtil;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import java.util.List;
|
||||
import javax.inject.Inject;
|
||||
import timber.log.Timber;
|
||||
|
||||
public class BookmarkPicturesFragment extends DaggerFragment {
|
||||
|
||||
private GridViewAdapter gridAdapter;
|
||||
private CompositeDisposable compositeDisposable = new CompositeDisposable();
|
||||
|
||||
private FragmentBookmarksPicturesBinding binding;
|
||||
@Inject
|
||||
BookmarkPicturesController controller;
|
||||
|
||||
/**
|
||||
* Create an instance of the fragment with the right bundle parameters
|
||||
* @return an instance of the fragment
|
||||
*/
|
||||
public static BookmarkPicturesFragment newInstance() {
|
||||
return new BookmarkPicturesFragment();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(
|
||||
@NonNull LayoutInflater inflater,
|
||||
ViewGroup container,
|
||||
Bundle savedInstanceState
|
||||
) {
|
||||
binding = FragmentBookmarksPicturesBinding.inflate(inflater, container, false);
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
binding.bookmarkedPicturesList.setOnItemClickListener((AdapterView.OnItemClickListener) getParentFragment());
|
||||
initList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
controller.stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
compositeDisposable.clear();
|
||||
binding = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
if (controller.needRefreshBookmarkedPictures()) {
|
||||
binding.bookmarkedPicturesList.setVisibility(GONE);
|
||||
if (gridAdapter != null) {
|
||||
gridAdapter.clear();
|
||||
((BookmarkListRootFragment)getParentFragment()).viewPagerNotifyDataSetChanged();
|
||||
}
|
||||
initList();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for internet connection and then initializes
|
||||
* the recycler view with bookmarked pictures
|
||||
*/
|
||||
@SuppressLint("CheckResult")
|
||||
private void initList() {
|
||||
if (!NetworkUtils.isInternetConnectionEstablished(getContext())) {
|
||||
handleNoInternet();
|
||||
return;
|
||||
}
|
||||
|
||||
binding.loadingImagesProgressBar.setVisibility(VISIBLE);
|
||||
binding.statusMessage.setVisibility(GONE);
|
||||
|
||||
compositeDisposable.add(controller.loadBookmarkedPictures()
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(this::handleSuccess, this::handleError));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the UI updates for no internet scenario
|
||||
*/
|
||||
private void handleNoInternet() {
|
||||
binding.loadingImagesProgressBar.setVisibility(GONE);
|
||||
if (gridAdapter == null || gridAdapter.isEmpty()) {
|
||||
binding.statusMessage.setVisibility(VISIBLE);
|
||||
binding.statusMessage.setText(getString(R.string.no_internet));
|
||||
} else {
|
||||
ViewUtil.showShortSnackbar(binding.parentLayout, R.string.no_internet);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs and handles API error scenario
|
||||
* @param throwable
|
||||
*/
|
||||
private void handleError(Throwable throwable) {
|
||||
Timber.e(throwable, "Error occurred while loading images inside a category");
|
||||
try{
|
||||
ViewUtil.showShortSnackbar(binding.getRoot(), R.string.error_loading_images);
|
||||
initErrorView();
|
||||
}catch (Exception e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the UI updates for a error scenario
|
||||
*/
|
||||
private void initErrorView() {
|
||||
binding.loadingImagesProgressBar.setVisibility(GONE);
|
||||
if (gridAdapter == null || gridAdapter.isEmpty()) {
|
||||
binding.statusMessage.setVisibility(VISIBLE);
|
||||
binding.statusMessage.setText(getString(R.string.no_images_found));
|
||||
} else {
|
||||
binding.statusMessage.setVisibility(GONE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the UI updates when there is no bookmarks
|
||||
*/
|
||||
private void initEmptyBookmarkListView() {
|
||||
binding.loadingImagesProgressBar.setVisibility(GONE);
|
||||
if (gridAdapter == null || gridAdapter.isEmpty()) {
|
||||
binding.statusMessage.setVisibility(VISIBLE);
|
||||
binding.statusMessage.setText(getString(R.string.bookmark_empty));
|
||||
} else {
|
||||
binding.statusMessage.setVisibility(GONE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the success scenario
|
||||
* On first load, it initializes the grid view. On subsequent loads, it adds items to the adapter
|
||||
* @param collection List of new Media to be displayed
|
||||
*/
|
||||
private void handleSuccess(List<Media> collection) {
|
||||
if (collection == null) {
|
||||
initErrorView();
|
||||
return;
|
||||
}
|
||||
if (collection.isEmpty()) {
|
||||
initEmptyBookmarkListView();
|
||||
return;
|
||||
}
|
||||
|
||||
if (gridAdapter == null) {
|
||||
setAdapter(collection);
|
||||
} else {
|
||||
if (gridAdapter.containsAll(collection)) {
|
||||
binding.loadingImagesProgressBar.setVisibility(GONE);
|
||||
binding.statusMessage.setVisibility(GONE);
|
||||
binding.bookmarkedPicturesList.setVisibility(VISIBLE);
|
||||
binding.bookmarkedPicturesList.setAdapter(gridAdapter);
|
||||
return;
|
||||
}
|
||||
gridAdapter.addItems(collection);
|
||||
((BookmarkListRootFragment) getParentFragment()).viewPagerNotifyDataSetChanged();
|
||||
}
|
||||
binding.loadingImagesProgressBar.setVisibility(GONE);
|
||||
binding.statusMessage.setVisibility(GONE);
|
||||
binding.bookmarkedPicturesList.setVisibility(VISIBLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the adapter with a list of Media objects
|
||||
* @param mediaList List of new Media to be displayed
|
||||
*/
|
||||
private void setAdapter(List<Media> mediaList) {
|
||||
gridAdapter = new GridViewAdapter(
|
||||
this.getContext(),
|
||||
R.layout.layout_category_images,
|
||||
mediaList
|
||||
);
|
||||
binding.bookmarkedPicturesList.setAdapter(gridAdapter);
|
||||
}
|
||||
|
||||
/**
|
||||
* It return an instance of gridView adapter which helps in extracting media details
|
||||
* used by the gridView
|
||||
* @return GridView Adapter
|
||||
*/
|
||||
public ListAdapter getAdapter() {
|
||||
return binding.bookmarkedPicturesList.getAdapter();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,201 @@
|
|||
package fr.free.nrw.commons.bookmarks.pictures
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.AdapterView.OnItemClickListener
|
||||
import android.widget.ListAdapter
|
||||
import dagger.android.support.DaggerFragment
|
||||
import fr.free.nrw.commons.Media
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.bookmarks.BookmarkListRootFragment
|
||||
import fr.free.nrw.commons.category.GridViewAdapter
|
||||
import fr.free.nrw.commons.databinding.FragmentBookmarksPicturesBinding
|
||||
import fr.free.nrw.commons.utils.NetworkUtils.isInternetConnectionEstablished
|
||||
import fr.free.nrw.commons.utils.ViewUtil.showShortSnackbar
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import io.reactivex.functions.Consumer
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
class BookmarkPicturesFragment : DaggerFragment() {
|
||||
private var gridAdapter: GridViewAdapter? = null
|
||||
private val compositeDisposable = CompositeDisposable()
|
||||
|
||||
private var binding: FragmentBookmarksPicturesBinding? = null
|
||||
|
||||
@JvmField
|
||||
@Inject
|
||||
var controller: BookmarkPicturesController? = null
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
binding = FragmentBookmarksPicturesBinding.inflate(inflater, container, false)
|
||||
return binding!!.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
binding!!.bookmarkedPicturesList.onItemClickListener =
|
||||
parentFragment as OnItemClickListener?
|
||||
initList()
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
controller!!.stop()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
compositeDisposable.clear()
|
||||
binding = null
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
if (controller!!.needRefreshBookmarkedPictures()) {
|
||||
binding!!.bookmarkedPicturesList.visibility = View.GONE
|
||||
gridAdapter?.let {
|
||||
it.clear()
|
||||
(parentFragment as BookmarkListRootFragment).viewPagerNotifyDataSetChanged()
|
||||
}
|
||||
initList()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for internet connection and then initializes
|
||||
* the recycler view with bookmarked pictures
|
||||
*/
|
||||
@SuppressLint("CheckResult")
|
||||
private fun initList() {
|
||||
if (!isInternetConnectionEstablished(context)) {
|
||||
handleNoInternet()
|
||||
return
|
||||
}
|
||||
|
||||
binding!!.loadingImagesProgressBar.visibility = View.VISIBLE
|
||||
binding!!.statusMessage.visibility = View.GONE
|
||||
|
||||
compositeDisposable.add(
|
||||
controller!!.loadBookmarkedPictures()
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(::handleSuccess, ::handleError)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the UI updates for no internet scenario
|
||||
*/
|
||||
private fun handleNoInternet() {
|
||||
binding!!.loadingImagesProgressBar.visibility = View.GONE
|
||||
if (gridAdapter == null || gridAdapter!!.isEmpty) {
|
||||
binding!!.statusMessage.visibility = View.VISIBLE
|
||||
binding!!.statusMessage.text = getString(R.string.no_internet)
|
||||
} else {
|
||||
showShortSnackbar(binding!!.parentLayout, R.string.no_internet)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs and handles API error scenario
|
||||
* @param throwable
|
||||
*/
|
||||
private fun handleError(throwable: Throwable) {
|
||||
Timber.e(throwable, "Error occurred while loading images inside a category")
|
||||
try {
|
||||
showShortSnackbar(binding!!.root, R.string.error_loading_images)
|
||||
initErrorView()
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the UI updates for a error scenario
|
||||
*/
|
||||
private fun initErrorView() {
|
||||
binding!!.loadingImagesProgressBar.visibility = View.GONE
|
||||
if (gridAdapter == null || gridAdapter!!.isEmpty) {
|
||||
binding!!.statusMessage.visibility = View.VISIBLE
|
||||
binding!!.statusMessage.text = getString(R.string.no_images_found)
|
||||
} else {
|
||||
binding!!.statusMessage.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the UI updates when there is no bookmarks
|
||||
*/
|
||||
private fun initEmptyBookmarkListView() {
|
||||
binding!!.loadingImagesProgressBar.visibility = View.GONE
|
||||
if (gridAdapter == null || gridAdapter!!.isEmpty) {
|
||||
binding!!.statusMessage.visibility = View.VISIBLE
|
||||
binding!!.statusMessage.text = getString(R.string.bookmark_empty)
|
||||
} else {
|
||||
binding!!.statusMessage.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the success scenario
|
||||
* On first load, it initializes the grid view. On subsequent loads, it adds items to the adapter
|
||||
* @param collection List of new Media to be displayed
|
||||
*/
|
||||
private fun handleSuccess(collection: List<Media>?) {
|
||||
if (collection == null) {
|
||||
initErrorView()
|
||||
return
|
||||
}
|
||||
if (collection.isEmpty()) {
|
||||
initEmptyBookmarkListView()
|
||||
return
|
||||
}
|
||||
|
||||
if (gridAdapter == null) {
|
||||
setAdapter(collection)
|
||||
} else {
|
||||
if (gridAdapter!!.containsAll(collection)) {
|
||||
binding!!.loadingImagesProgressBar.visibility = View.GONE
|
||||
binding!!.statusMessage.visibility = View.GONE
|
||||
binding!!.bookmarkedPicturesList.visibility = View.VISIBLE
|
||||
binding!!.bookmarkedPicturesList.adapter = gridAdapter
|
||||
return
|
||||
}
|
||||
gridAdapter!!.addItems(collection)
|
||||
(parentFragment as BookmarkListRootFragment).viewPagerNotifyDataSetChanged()
|
||||
}
|
||||
binding!!.loadingImagesProgressBar.visibility = View.GONE
|
||||
binding!!.statusMessage.visibility = View.GONE
|
||||
binding!!.bookmarkedPicturesList.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the adapter with a list of Media objects
|
||||
* @param mediaList List of new Media to be displayed
|
||||
*/
|
||||
private fun setAdapter(mediaList: List<Media>) {
|
||||
gridAdapter = GridViewAdapter(
|
||||
requireContext(),
|
||||
R.layout.layout_category_images,
|
||||
mediaList.toMutableList()
|
||||
)
|
||||
binding?.let { it.bookmarkedPicturesList.adapter = gridAdapter }
|
||||
}
|
||||
|
||||
/**
|
||||
* It return an instance of gridView adapter which helps in extracting media details
|
||||
* used by the gridView
|
||||
* @return GridView Adapter
|
||||
*/
|
||||
fun getAdapter(): ListAdapter? = binding?.bookmarkedPicturesList?.adapter
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
package fr.free.nrw.commons.bookmarks.pictures
|
||||
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
|
||||
object BookmarksTable {
|
||||
const val TABLE_NAME: String = "bookmarks"
|
||||
const val COLUMN_MEDIA_NAME: String = "media_name"
|
||||
const val COLUMN_CREATOR: String = "media_creator"
|
||||
|
||||
// NOTE! KEEP IN SAME ORDER AS THEY ARE DEFINED UP THERE. HELPS HARD CODE COLUMN INDICES.
|
||||
val ALL_FIELDS = arrayOf(
|
||||
COLUMN_MEDIA_NAME,
|
||||
COLUMN_CREATOR
|
||||
)
|
||||
|
||||
const val DROP_TABLE_STATEMENT: String = "DROP TABLE IF EXISTS $TABLE_NAME"
|
||||
|
||||
const val CREATE_TABLE_STATEMENT: String = ("CREATE TABLE $TABLE_NAME (" +
|
||||
"$COLUMN_MEDIA_NAME STRING PRIMARY KEY, " +
|
||||
"$COLUMN_CREATOR STRING" +
|
||||
");")
|
||||
|
||||
fun onCreate(db: SQLiteDatabase) =
|
||||
db.execSQL(CREATE_TABLE_STATEMENT)
|
||||
|
||||
fun onDelete(db: SQLiteDatabase) {
|
||||
db.execSQL(DROP_TABLE_STATEMENT)
|
||||
onCreate(db)
|
||||
}
|
||||
|
||||
fun onUpdate(db: SQLiteDatabase, from: Int, to: Int) {
|
||||
if (from == to) {
|
||||
return
|
||||
}
|
||||
|
||||
if (from < 7) {
|
||||
// doesn't exist yet
|
||||
onUpdate(db, from+1, to)
|
||||
return
|
||||
}
|
||||
|
||||
if (from == 7) {
|
||||
// table added in version 8
|
||||
onCreate(db)
|
||||
onUpdate(db, from+1, to)
|
||||
return
|
||||
}
|
||||
|
||||
if (from == 8) {
|
||||
onUpdate(db, from+1, to)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -9,12 +9,9 @@ import android.database.sqlite.SQLiteDatabase
|
|||
import android.database.sqlite.SQLiteQueryBuilder
|
||||
import android.net.Uri
|
||||
import android.text.TextUtils
|
||||
import androidx.annotation.NonNull
|
||||
import fr.free.nrw.commons.BuildConfig
|
||||
import fr.free.nrw.commons.data.DBOpenHelper
|
||||
import fr.free.nrw.commons.di.CommonsDaggerContentProvider
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import androidx.core.net.toUri
|
||||
|
||||
class CategoryContentProvider : CommonsDaggerContentProvider() {
|
||||
|
||||
|
|
@ -23,9 +20,6 @@ class CategoryContentProvider : CommonsDaggerContentProvider() {
|
|||
addURI(BuildConfig.CATEGORY_AUTHORITY, "${BASE_PATH}/#", CATEGORIES_ID)
|
||||
}
|
||||
|
||||
@Inject
|
||||
lateinit var dbOpenHelper: DBOpenHelper
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
override fun query(uri: Uri, projection: Array<String>?, selection: String?,
|
||||
selectionArgs: Array<String>?, sortOrder: String?): Cursor? {
|
||||
|
|
@ -34,7 +28,7 @@ class CategoryContentProvider : CommonsDaggerContentProvider() {
|
|||
}
|
||||
|
||||
val uriType = uriMatcher.match(uri)
|
||||
val db = dbOpenHelper.readableDatabase
|
||||
val db = requireDb()
|
||||
|
||||
val cursor: Cursor? = when (uriType) {
|
||||
CATEGORIES -> queryBuilder.query(
|
||||
|
|
@ -58,45 +52,37 @@ class CategoryContentProvider : CommonsDaggerContentProvider() {
|
|||
else -> throw IllegalArgumentException("Unknown URI $uri")
|
||||
}
|
||||
|
||||
cursor?.setNotificationUri(context?.contentResolver, uri)
|
||||
cursor?.setNotificationUri(requireContext().contentResolver, uri)
|
||||
return cursor
|
||||
}
|
||||
|
||||
override fun getType(uri: Uri): String? {
|
||||
return null
|
||||
}
|
||||
override fun getType(uri: Uri): String? = null
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
override fun insert(uri: Uri, contentValues: ContentValues?): Uri? {
|
||||
override fun insert(uri: Uri, contentValues: ContentValues?): Uri {
|
||||
val uriType = uriMatcher.match(uri)
|
||||
val sqlDB = dbOpenHelper.writableDatabase
|
||||
val id: Long
|
||||
when (uriType) {
|
||||
CATEGORIES -> {
|
||||
id = sqlDB.insert(TABLE_NAME, null, contentValues)
|
||||
id = requireDb().insert(TABLE_NAME, null, contentValues)
|
||||
}
|
||||
else -> throw IllegalArgumentException("Unknown URI: $uri")
|
||||
}
|
||||
context?.contentResolver?.notifyChange(uri, null)
|
||||
return Uri.parse("${Companion.BASE_URI}/$id")
|
||||
requireContext().contentResolver?.notifyChange(uri, null)
|
||||
return "${BASE_URI}/$id".toUri()
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?): Int {
|
||||
// Not implemented
|
||||
return 0
|
||||
}
|
||||
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?): Int = 0
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
override fun bulkInsert(uri: Uri, values: Array<ContentValues>): Int {
|
||||
Timber.d("Hello, bulk insert! (CategoryContentProvider)")
|
||||
val uriType = uriMatcher.match(uri)
|
||||
val sqlDB = dbOpenHelper.writableDatabase
|
||||
val sqlDB = requireDb()
|
||||
sqlDB.beginTransaction()
|
||||
when (uriType) {
|
||||
CATEGORIES -> {
|
||||
for (value in values) {
|
||||
Timber.d("Inserting! %s", value)
|
||||
sqlDB.insert(TABLE_NAME, null, value)
|
||||
}
|
||||
sqlDB.setTransactionSuccessful()
|
||||
|
|
@ -104,7 +90,7 @@ class CategoryContentProvider : CommonsDaggerContentProvider() {
|
|||
else -> throw IllegalArgumentException("Unknown URI: $uri")
|
||||
}
|
||||
sqlDB.endTransaction()
|
||||
context?.contentResolver?.notifyChange(uri, null)
|
||||
requireContext().contentResolver?.notifyChange(uri, null)
|
||||
return values.size
|
||||
}
|
||||
|
||||
|
|
@ -112,17 +98,18 @@ class CategoryContentProvider : CommonsDaggerContentProvider() {
|
|||
override fun update(uri: Uri, contentValues: ContentValues?, selection: String?,
|
||||
selectionArgs: Array<String>?): Int {
|
||||
val uriType = uriMatcher.match(uri)
|
||||
val sqlDB = dbOpenHelper.writableDatabase
|
||||
val rowsUpdated: Int
|
||||
when (uriType) {
|
||||
CATEGORIES_ID -> {
|
||||
if (TextUtils.isEmpty(selection)) {
|
||||
val id = uri.lastPathSegment?.toInt()
|
||||
?: throw IllegalArgumentException("Invalid ID")
|
||||
rowsUpdated = sqlDB.update(TABLE_NAME,
|
||||
rowsUpdated = requireDb().update(
|
||||
TABLE_NAME,
|
||||
contentValues,
|
||||
"$COLUMN_ID = ?",
|
||||
arrayOf(id.toString()))
|
||||
arrayOf(id.toString())
|
||||
)
|
||||
} else {
|
||||
throw IllegalArgumentException(
|
||||
"Parameter `selection` should be empty when updating an ID")
|
||||
|
|
@ -130,7 +117,7 @@ class CategoryContentProvider : CommonsDaggerContentProvider() {
|
|||
}
|
||||
else -> throw IllegalArgumentException("Unknown URI: $uri with type $uriType")
|
||||
}
|
||||
context?.contentResolver?.notifyChange(uri, null)
|
||||
requireContext().contentResolver?.notifyChange(uri, null)
|
||||
return rowsUpdated
|
||||
}
|
||||
|
||||
|
|
@ -165,13 +152,9 @@ class CategoryContentProvider : CommonsDaggerContentProvider() {
|
|||
"$COLUMN_TIMES_USED INTEGER" +
|
||||
");"
|
||||
|
||||
fun uriForId(id: Int): Uri {
|
||||
return Uri.parse("${BASE_URI}/$id")
|
||||
}
|
||||
fun uriForId(id: Int): Uri = Uri.parse("${BASE_URI}/$id")
|
||||
|
||||
fun onCreate(db: SQLiteDatabase) {
|
||||
db.execSQL(CREATE_TABLE_STATEMENT)
|
||||
}
|
||||
fun onCreate(db: SQLiteDatabase) = db.execSQL(CREATE_TABLE_STATEMENT)
|
||||
|
||||
fun onDelete(db: SQLiteDatabase) {
|
||||
db.execSQL(DROP_TABLE_STATEMENT)
|
||||
|
|
@ -200,6 +183,6 @@ class CategoryContentProvider : CommonsDaggerContentProvider() {
|
|||
private const val CATEGORIES = 1
|
||||
private const val CATEGORIES_ID = 2
|
||||
private const val BASE_PATH = "categories"
|
||||
val BASE_URI: Uri = Uri.parse("content://${BuildConfig.CATEGORY_AUTHORITY}/${Companion.BASE_PATH}")
|
||||
val BASE_URI: Uri = "content://${BuildConfig.CATEGORY_AUTHORITY}/${BASE_PATH}".toUri()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,9 +4,8 @@ import android.content.Context
|
|||
import android.database.sqlite.SQLiteDatabase
|
||||
import android.database.sqlite.SQLiteException
|
||||
import android.database.sqlite.SQLiteOpenHelper
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao
|
||||
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarksTable
|
||||
import fr.free.nrw.commons.category.CategoryDao
|
||||
import fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao
|
||||
import fr.free.nrw.commons.recentlanguages.RecentLanguagesDao
|
||||
|
|
@ -30,16 +29,16 @@ class DBOpenHelper(
|
|||
*/
|
||||
override fun onCreate(db: SQLiteDatabase) {
|
||||
CategoryDao.Table.onCreate(db)
|
||||
BookmarkPicturesDao.Table.onCreate(db)
|
||||
BookmarkItemsDao.Table.onCreate(db)
|
||||
BookmarksTable.onCreate(db)
|
||||
BookmarkItemsTable.onCreate(db)
|
||||
RecentSearchesDao.Table.onCreate(db)
|
||||
RecentLanguagesDao.Table.onCreate(db)
|
||||
}
|
||||
|
||||
override fun onUpgrade(db: SQLiteDatabase, from: Int, to: Int) {
|
||||
CategoryDao.Table.onUpdate(db, from, to)
|
||||
BookmarkPicturesDao.Table.onUpdate(db, from, to)
|
||||
BookmarkItemsDao.Table.onUpdate(db, from, to)
|
||||
BookmarksTable.onUpdate(db, from, to)
|
||||
BookmarkItemsTable.onUpdate(db, from, to)
|
||||
RecentSearchesDao.Table.onUpdate(db, from, to)
|
||||
RecentLanguagesDao.Table.onUpdate(db, from, to)
|
||||
deleteTable(db, CONTRIBUTIONS_TABLE)
|
||||
|
|
|
|||
|
|
@ -1,14 +1,25 @@
|
|||
package fr.free.nrw.commons.di
|
||||
|
||||
import android.content.ContentProvider
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
import fr.free.nrw.commons.data.DBOpenHelper
|
||||
import fr.free.nrw.commons.di.ApplicationlessInjection.Companion.getInstance
|
||||
import javax.inject.Inject
|
||||
|
||||
abstract class CommonsDaggerContentProvider : ContentProvider() {
|
||||
@JvmField
|
||||
@Inject
|
||||
var dbOpenHelper: DBOpenHelper? = null
|
||||
|
||||
override fun onCreate(): Boolean {
|
||||
inject()
|
||||
return true
|
||||
}
|
||||
|
||||
fun requireDbOpenHelper(): DBOpenHelper = dbOpenHelper!!
|
||||
|
||||
fun requireDb(): SQLiteDatabase = requireDbOpenHelper().writableDatabase!!
|
||||
|
||||
private fun inject() {
|
||||
val injection = getInstance(context!!)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,202 +0,0 @@
|
|||
package fr.free.nrw.commons.explore.recentsearches;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.UriMatcher;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteQueryBuilder;
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import fr.free.nrw.commons.BuildConfig;
|
||||
import fr.free.nrw.commons.data.DBOpenHelper;
|
||||
import fr.free.nrw.commons.di.CommonsDaggerContentProvider;
|
||||
import timber.log.Timber;
|
||||
|
||||
import static android.content.UriMatcher.NO_MATCH;
|
||||
import static fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao.Table.ALL_FIELDS;
|
||||
import static fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao.Table.COLUMN_ID;
|
||||
import static fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao.Table.TABLE_NAME;
|
||||
|
||||
|
||||
/**
|
||||
* This class contains functions for executing queries for
|
||||
* inserting, searching, deleting, editing recent searches in SqLite DB
|
||||
**/
|
||||
public class RecentSearchesContentProvider extends CommonsDaggerContentProvider {
|
||||
|
||||
// For URI matcher
|
||||
private static final int RECENT_SEARCHES = 1;
|
||||
private static final int RECENT_SEARCHES_ID = 2;
|
||||
private static final String BASE_PATH = "recent_searches";
|
||||
public static final Uri BASE_URI = Uri.parse("content://" + BuildConfig.RECENT_SEARCH_AUTHORITY + "/" + BASE_PATH);
|
||||
private static final UriMatcher uriMatcher = new UriMatcher(NO_MATCH);
|
||||
|
||||
static {
|
||||
uriMatcher.addURI(BuildConfig.RECENT_SEARCH_AUTHORITY, BASE_PATH, RECENT_SEARCHES);
|
||||
uriMatcher.addURI(BuildConfig.RECENT_SEARCH_AUTHORITY, BASE_PATH + "/#", RECENT_SEARCHES_ID);
|
||||
}
|
||||
|
||||
public static Uri uriForId(int id) {
|
||||
return Uri.parse(BASE_URI.toString() + "/" + id);
|
||||
}
|
||||
|
||||
@Inject DBOpenHelper dbOpenHelper;
|
||||
|
||||
/**
|
||||
* This functions executes query for searching recent searches in SqLite DB
|
||||
**/
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Override
|
||||
public Cursor query(@NonNull Uri uri, String[] projection, String selection,
|
||||
String[] selectionArgs, String sortOrder) {
|
||||
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
|
||||
queryBuilder.setTables(TABLE_NAME);
|
||||
|
||||
int uriType = uriMatcher.match(uri);
|
||||
|
||||
SQLiteDatabase db = dbOpenHelper.getReadableDatabase();
|
||||
Cursor cursor;
|
||||
|
||||
switch (uriType) {
|
||||
case RECENT_SEARCHES:
|
||||
cursor = queryBuilder.query(db, projection, selection, selectionArgs,
|
||||
null, null, sortOrder);
|
||||
break;
|
||||
case RECENT_SEARCHES_ID:
|
||||
cursor = queryBuilder.query(db,
|
||||
ALL_FIELDS,
|
||||
"_id = ?",
|
||||
new String[]{uri.getLastPathSegment()},
|
||||
null,
|
||||
null,
|
||||
sortOrder
|
||||
);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown URI" + uri);
|
||||
}
|
||||
|
||||
cursor.setNotificationUri(getContext().getContentResolver(), uri);
|
||||
|
||||
return cursor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType(@NonNull Uri uri) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This functions executes query for inserting a recentSearch object in SqLite DB
|
||||
**/
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Override
|
||||
public Uri insert(@NonNull Uri uri, ContentValues contentValues) {
|
||||
int uriType = uriMatcher.match(uri);
|
||||
SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase();
|
||||
long id;
|
||||
switch (uriType) {
|
||||
case RECENT_SEARCHES:
|
||||
id = sqlDB.insert(TABLE_NAME, null, contentValues);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown URI: " + uri);
|
||||
}
|
||||
getContext().getContentResolver().notifyChange(uri, null);
|
||||
return Uri.parse(BASE_URI + "/" + id);
|
||||
}
|
||||
|
||||
/**
|
||||
* This functions executes query for deleting a recentSearch object in SqLite DB
|
||||
**/
|
||||
@Override
|
||||
public int delete(@NonNull Uri uri, String s, String[] strings) {
|
||||
int rows;
|
||||
int uriType = uriMatcher.match(uri);
|
||||
SQLiteDatabase db = dbOpenHelper.getReadableDatabase();
|
||||
switch (uriType) {
|
||||
case RECENT_SEARCHES_ID:
|
||||
Timber.d("Deleting recent searches id %s", uri.getLastPathSegment());
|
||||
rows = db.delete(RecentSearchesDao.Table.TABLE_NAME,
|
||||
"_id = ?",
|
||||
new String[]{uri.getLastPathSegment()}
|
||||
);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown URI" + uri);
|
||||
}
|
||||
getContext().getContentResolver().notifyChange(uri, null);
|
||||
return rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* This functions executes query for inserting multiple recentSearch objects in SqLite DB
|
||||
**/
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Override
|
||||
public int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] values) {
|
||||
Timber.d("Hello, bulk insert! (RecentSearchesContentProvider)");
|
||||
int uriType = uriMatcher.match(uri);
|
||||
SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase();
|
||||
sqlDB.beginTransaction();
|
||||
switch (uriType) {
|
||||
case RECENT_SEARCHES:
|
||||
for (ContentValues value : values) {
|
||||
Timber.d("Inserting! %s", value);
|
||||
sqlDB.insert(TABLE_NAME, null, value);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown URI: " + uri);
|
||||
}
|
||||
sqlDB.setTransactionSuccessful();
|
||||
sqlDB.endTransaction();
|
||||
getContext().getContentResolver().notifyChange(uri, null);
|
||||
return values.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* This functions executes query for updating a particular recentSearch object in SqLite DB
|
||||
**/
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Override
|
||||
public int update(@NonNull Uri uri, ContentValues contentValues, String selection,
|
||||
String[] selectionArgs) {
|
||||
/*
|
||||
SQL Injection warnings: First, note that we're not exposing this to the
|
||||
outside world (exported="false"). Even then, we should make sure to sanitize
|
||||
all user input appropriately. Input that passes through ContentValues
|
||||
should be fine. So only issues are those that pass in via concating.
|
||||
|
||||
In here, the only concat created argument is for id. It is cast to an int,
|
||||
and will error out otherwise.
|
||||
*/
|
||||
int uriType = uriMatcher.match(uri);
|
||||
SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase();
|
||||
int rowsUpdated;
|
||||
switch (uriType) {
|
||||
case RECENT_SEARCHES_ID:
|
||||
if (TextUtils.isEmpty(selection)) {
|
||||
int id = Integer.valueOf(uri.getLastPathSegment());
|
||||
rowsUpdated = sqlDB.update(TABLE_NAME,
|
||||
contentValues,
|
||||
COLUMN_ID + " = ?",
|
||||
new String[]{String.valueOf(id)});
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"Parameter `selection` should be empty when updating an ID");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown URI: " + uri + " with type " + uriType);
|
||||
}
|
||||
getContext().getContentResolver().notifyChange(uri, null);
|
||||
return rowsUpdated;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,174 @@
|
|||
package fr.free.nrw.commons.explore.recentsearches
|
||||
|
||||
import android.content.ContentValues
|
||||
import android.content.UriMatcher
|
||||
import android.database.Cursor
|
||||
import android.database.sqlite.SQLiteQueryBuilder
|
||||
import android.net.Uri
|
||||
import androidx.core.net.toUri
|
||||
import fr.free.nrw.commons.BuildConfig
|
||||
import fr.free.nrw.commons.di.CommonsDaggerContentProvider
|
||||
import fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao.Table.ALL_FIELDS
|
||||
import fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao.Table.COLUMN_ID
|
||||
import fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao.Table.TABLE_NAME
|
||||
|
||||
/**
|
||||
* This class contains functions for executing queries for
|
||||
* inserting, searching, deleting, editing recent searches in SqLite DB
|
||||
*/
|
||||
class RecentSearchesContentProvider : CommonsDaggerContentProvider() {
|
||||
|
||||
/**
|
||||
* This functions executes query for searching recent searches in SqLite DB
|
||||
*/
|
||||
override fun query(
|
||||
uri: Uri, projection: Array<String>?, selection: String?,
|
||||
selectionArgs: Array<String>?, sortOrder: String?
|
||||
): Cursor {
|
||||
val queryBuilder = SQLiteQueryBuilder().apply {
|
||||
tables = TABLE_NAME
|
||||
}
|
||||
|
||||
val uriType = uriMatcher.match(uri)
|
||||
|
||||
val cursor = when (uriType) {
|
||||
RECENT_SEARCHES -> queryBuilder.query(
|
||||
requireDb(), projection, selection, selectionArgs,
|
||||
null, null, sortOrder
|
||||
)
|
||||
|
||||
RECENT_SEARCHES_ID -> queryBuilder.query(
|
||||
requireDb(),
|
||||
ALL_FIELDS,
|
||||
"$COLUMN_ID = ?",
|
||||
arrayOf(uri.lastPathSegment),
|
||||
null,
|
||||
null,
|
||||
sortOrder
|
||||
)
|
||||
|
||||
else -> throw IllegalArgumentException("Unknown URI$uri")
|
||||
}
|
||||
|
||||
cursor.setNotificationUri(requireContext().contentResolver, uri)
|
||||
|
||||
return cursor
|
||||
}
|
||||
|
||||
override fun getType(uri: Uri): String? = null
|
||||
|
||||
/**
|
||||
* This functions executes query for inserting a recentSearch object in SqLite DB
|
||||
*/
|
||||
override fun insert(uri: Uri, contentValues: ContentValues?): Uri? {
|
||||
val uriType = uriMatcher.match(uri)
|
||||
val id: Long = when (uriType) {
|
||||
RECENT_SEARCHES -> requireDb().insert(TABLE_NAME, null, contentValues)
|
||||
|
||||
else -> throw IllegalArgumentException("Unknown URI: $uri")
|
||||
}
|
||||
requireContext().contentResolver.notifyChange(uri, null)
|
||||
return "$BASE_URI/$id".toUri()
|
||||
}
|
||||
|
||||
/**
|
||||
* This functions executes query for deleting a recentSearch object in SqLite DB
|
||||
*/
|
||||
override fun delete(uri: Uri, s: String?, strings: Array<String>?): Int {
|
||||
val rows: Int
|
||||
val uriType = uriMatcher.match(uri)
|
||||
when (uriType) {
|
||||
RECENT_SEARCHES_ID -> {
|
||||
rows = requireDb().delete(
|
||||
TABLE_NAME,
|
||||
"_id = ?",
|
||||
arrayOf(uri.lastPathSegment)
|
||||
)
|
||||
}
|
||||
|
||||
else -> throw IllegalArgumentException("Unknown URI - $uri")
|
||||
}
|
||||
requireContext().contentResolver.notifyChange(uri, null)
|
||||
return rows
|
||||
}
|
||||
|
||||
/**
|
||||
* This functions executes query for inserting multiple recentSearch objects in SqLite DB
|
||||
*/
|
||||
override fun bulkInsert(uri: Uri, values: Array<ContentValues>): Int {
|
||||
val uriType = uriMatcher.match(uri)
|
||||
val sqlDB = requireDb()
|
||||
sqlDB.beginTransaction()
|
||||
when (uriType) {
|
||||
RECENT_SEARCHES -> for (value in values) {
|
||||
sqlDB.insert(TABLE_NAME, null, value)
|
||||
}
|
||||
|
||||
else -> throw IllegalArgumentException("Unknown URI: $uri")
|
||||
}
|
||||
sqlDB.setTransactionSuccessful()
|
||||
sqlDB.endTransaction()
|
||||
requireContext().contentResolver.notifyChange(uri, null)
|
||||
return values.size
|
||||
}
|
||||
|
||||
/**
|
||||
* This functions executes query for updating a particular recentSearch object in SqLite DB
|
||||
*/
|
||||
override fun update(
|
||||
uri: Uri, contentValues: ContentValues?, selection: String?,
|
||||
selectionArgs: Array<String>?
|
||||
): Int {
|
||||
/*
|
||||
SQL Injection warnings: First, note that we're not exposing this to the
|
||||
outside world (exported="false"). Even then, we should make sure to sanitize
|
||||
all user input appropriately. Input that passes through ContentValues
|
||||
should be fine. So only issues are those that pass in via concating.
|
||||
|
||||
In here, the only concat created argument is for id. It is cast to an int,
|
||||
and will error out otherwise.
|
||||
*/
|
||||
val uriType = uriMatcher.match(uri)
|
||||
val rowsUpdated: Int
|
||||
when (uriType) {
|
||||
RECENT_SEARCHES_ID -> if (selection.isNullOrEmpty()) {
|
||||
val id = uri.lastPathSegment!!.toInt()
|
||||
rowsUpdated = requireDb().update(
|
||||
TABLE_NAME,
|
||||
contentValues,
|
||||
"$COLUMN_ID = ?",
|
||||
arrayOf(id.toString())
|
||||
)
|
||||
} else {
|
||||
throw IllegalArgumentException(
|
||||
"Parameter `selection` should be empty when updating an ID"
|
||||
)
|
||||
}
|
||||
|
||||
else -> throw IllegalArgumentException("Unknown URI: $uri with type $uriType")
|
||||
}
|
||||
requireContext().contentResolver.notifyChange(uri, null)
|
||||
return rowsUpdated
|
||||
}
|
||||
|
||||
companion object {
|
||||
// For URI matcher
|
||||
private const val RECENT_SEARCHES = 1
|
||||
private const val RECENT_SEARCHES_ID = 2
|
||||
private const val BASE_PATH = "recent_searches"
|
||||
|
||||
@JvmField
|
||||
val BASE_URI: Uri = "content://${BuildConfig.RECENT_SEARCH_AUTHORITY}/$BASE_PATH".toUri()
|
||||
|
||||
private val uriMatcher = UriMatcher(UriMatcher.NO_MATCH)
|
||||
|
||||
init {
|
||||
uriMatcher.addURI(BuildConfig.RECENT_SEARCH_AUTHORITY, BASE_PATH, RECENT_SEARCHES)
|
||||
uriMatcher.addURI(BuildConfig.RECENT_SEARCH_AUTHORITY, "$BASE_PATH/#", RECENT_SEARCHES_ID)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun uriForId(id: Int): Uri = "$BASE_URI/$id".toUri()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -166,7 +166,7 @@ class MediaDetailPagerFragment : CommonsDaggerSupportFragment(), OnPageChangeLis
|
|||
val mediaDetailFragment = adapter!!.currentMediaDetailFragment
|
||||
when (item.itemId) {
|
||||
R.id.menu_bookmark_current_image -> {
|
||||
val bookmarkExists = bookmarkDao!!.updateBookmark(bookmark)
|
||||
val bookmarkExists = bookmarkDao!!.updateBookmark(bookmark!!)
|
||||
val snackbar = if (bookmarkExists) Snackbar.make(
|
||||
requireView(),
|
||||
R.string.add_bookmark,
|
||||
|
|
@ -436,7 +436,7 @@ ${m.pageTitle.canonicalUri}"""
|
|||
bookmark = Bookmark(
|
||||
m.filename,
|
||||
m.getAuthorOrUser(),
|
||||
BookmarkPicturesContentProvider.uriForName(m.filename)
|
||||
BookmarkPicturesContentProvider.uriForName(m.filename!!)
|
||||
)
|
||||
updateBookmarkState(menu.findItem(R.id.menu_bookmark_current_image))
|
||||
val contributionState = provider.getContributionStateAt(position)
|
||||
|
|
|
|||
|
|
@ -3,17 +3,13 @@ package fr.free.nrw.commons.recentlanguages
|
|||
|
||||
import android.content.ContentValues
|
||||
import android.database.Cursor
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
import android.database.sqlite.SQLiteQueryBuilder
|
||||
import android.net.Uri
|
||||
import android.text.TextUtils
|
||||
import fr.free.nrw.commons.BuildConfig
|
||||
import fr.free.nrw.commons.data.DBOpenHelper
|
||||
import fr.free.nrw.commons.di.CommonsDaggerContentProvider
|
||||
import fr.free.nrw.commons.recentlanguages.RecentLanguagesDao.Table.COLUMN_NAME
|
||||
import fr.free.nrw.commons.recentlanguages.RecentLanguagesDao.Table.TABLE_NAME
|
||||
import javax.inject.Inject
|
||||
import timber.log.Timber
|
||||
import androidx.core.net.toUri
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -23,27 +19,17 @@ class RecentLanguagesContentProvider : CommonsDaggerContentProvider() {
|
|||
|
||||
companion object {
|
||||
private const val BASE_PATH = "recent_languages"
|
||||
val BASE_URI: Uri =
|
||||
Uri.parse(
|
||||
"content://${BuildConfig.RECENT_LANGUAGE_AUTHORITY}/$BASE_PATH"
|
||||
)
|
||||
val BASE_URI: Uri = "content://${BuildConfig.RECENT_LANGUAGE_AUTHORITY}/$BASE_PATH".toUri()
|
||||
|
||||
/**
|
||||
* Append language code to the base URI
|
||||
* @param languageCode Code of a language
|
||||
*/
|
||||
@JvmStatic
|
||||
fun uriForCode(languageCode: String): Uri {
|
||||
return Uri.parse("$BASE_URI/$languageCode")
|
||||
}
|
||||
fun uriForCode(languageCode: String): Uri = "$BASE_URI/$languageCode".toUri()
|
||||
}
|
||||
|
||||
@Inject
|
||||
lateinit var dbOpenHelper: DBOpenHelper
|
||||
|
||||
override fun getType(uri: Uri): String? {
|
||||
return null
|
||||
}
|
||||
override fun getType(uri: Uri): String? = null
|
||||
|
||||
/**
|
||||
* Queries the SQLite database for the recently used languages
|
||||
|
|
@ -60,11 +46,12 @@ class RecentLanguagesContentProvider : CommonsDaggerContentProvider() {
|
|||
selectionArgs: Array<String>?,
|
||||
sortOrder: String?
|
||||
): Cursor? {
|
||||
val queryBuilder = SQLiteQueryBuilder()
|
||||
queryBuilder.tables = TABLE_NAME
|
||||
val db = dbOpenHelper.readableDatabase
|
||||
val queryBuilder = SQLiteQueryBuilder().apply {
|
||||
tables = TABLE_NAME
|
||||
}
|
||||
|
||||
val cursor = queryBuilder.query(
|
||||
db,
|
||||
requireDb(),
|
||||
projection,
|
||||
selection,
|
||||
selectionArgs,
|
||||
|
|
@ -72,7 +59,7 @@ class RecentLanguagesContentProvider : CommonsDaggerContentProvider() {
|
|||
null,
|
||||
sortOrder
|
||||
)
|
||||
cursor.setNotificationUri(context?.contentResolver, uri)
|
||||
cursor.setNotificationUri(requireContext().contentResolver, uri)
|
||||
return cursor
|
||||
}
|
||||
|
||||
|
|
@ -89,12 +76,11 @@ class RecentLanguagesContentProvider : CommonsDaggerContentProvider() {
|
|||
selection: String?,
|
||||
selectionArgs: Array<String>?
|
||||
): Int {
|
||||
val sqlDB = dbOpenHelper.writableDatabase
|
||||
val rowsUpdated: Int
|
||||
if (selection.isNullOrEmpty()) {
|
||||
val id = uri.lastPathSegment?.toInt()
|
||||
?: throw IllegalArgumentException("Invalid URI: $uri")
|
||||
rowsUpdated = sqlDB.update(
|
||||
rowsUpdated = requireDb().update(
|
||||
TABLE_NAME,
|
||||
contentValues,
|
||||
"$COLUMN_NAME = ?",
|
||||
|
|
@ -104,7 +90,7 @@ class RecentLanguagesContentProvider : CommonsDaggerContentProvider() {
|
|||
throw IllegalArgumentException("Parameter `selection` should be empty when updating an ID")
|
||||
}
|
||||
|
||||
context?.contentResolver?.notifyChange(uri, null)
|
||||
requireContext().contentResolver?.notifyChange(uri, null)
|
||||
return rowsUpdated
|
||||
}
|
||||
|
||||
|
|
@ -114,14 +100,13 @@ class RecentLanguagesContentProvider : CommonsDaggerContentProvider() {
|
|||
* @param contentValues : new values to be entered to the database
|
||||
*/
|
||||
override fun insert(uri: Uri, contentValues: ContentValues?): Uri? {
|
||||
val sqlDB = dbOpenHelper.writableDatabase
|
||||
val id = sqlDB.insert(
|
||||
val id = requireDb().insert(
|
||||
TABLE_NAME,
|
||||
null,
|
||||
contentValues
|
||||
)
|
||||
context?.contentResolver?.notifyChange(uri, null)
|
||||
return Uri.parse("$BASE_URI/$id")
|
||||
requireContext().contentResolver?.notifyChange(uri, null)
|
||||
return "$BASE_URI/$id".toUri()
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -129,14 +114,12 @@ class RecentLanguagesContentProvider : CommonsDaggerContentProvider() {
|
|||
* @param uri : contains the URI for recently used languages
|
||||
*/
|
||||
override fun delete(uri: Uri, s: String?, strings: Array<String>?): Int {
|
||||
val db = dbOpenHelper.readableDatabase
|
||||
Timber.d("Deleting recently used language %s", uri.lastPathSegment)
|
||||
val rows = db.delete(
|
||||
val rows = requireDb().delete(
|
||||
TABLE_NAME,
|
||||
"language_code = ?",
|
||||
arrayOf(uri.lastPathSegment)
|
||||
)
|
||||
context?.contentResolver?.notifyChange(uri, null)
|
||||
requireContext().contentResolver?.notifyChange(uri, null)
|
||||
return rows
|
||||
}
|
||||
}
|
||||
|
|
|
|||
32
app/src/main/java/fr/free/nrw/commons/utils/DatabaseUtils.kt
Normal file
32
app/src/main/java/fr/free/nrw/commons/utils/DatabaseUtils.kt
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
package fr.free.nrw.commons.utils
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.database.Cursor
|
||||
|
||||
fun Cursor.getStringArray(name: String): List<String> =
|
||||
stringToArray(getString(name))
|
||||
|
||||
@SuppressLint("Range")
|
||||
fun Cursor.getString(name: String): String =
|
||||
getString(getColumnIndex(name))
|
||||
|
||||
/**
|
||||
* Converts string to List
|
||||
* @param listString comma separated single string from of list items
|
||||
* @return List of string
|
||||
*/
|
||||
fun stringToArray(listString: String?): List<String> {
|
||||
if (listString.isNullOrEmpty()) return emptyList();
|
||||
val elements = listString.split(",".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
||||
return listOf(*elements)
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts string to List
|
||||
* @param list list of items
|
||||
* @return string comma separated single string of items
|
||||
*/
|
||||
fun arrayToString(list: List<String?>?): String? {
|
||||
return list?.joinToString(",")
|
||||
}
|
||||
|
||||
|
|
@ -288,7 +288,7 @@ class BookmarkListRootFragmentUnitTest {
|
|||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun testOnItemClick() {
|
||||
fragment.onItemClick(null, null, 0, 0)
|
||||
fragment.onItemClick(null, view, 0, 0)
|
||||
verify(childFragmentManager).beginTransaction()
|
||||
verify(childFragmentTransaction).commit()
|
||||
verify(childFragmentManager).executePendingTransactions()
|
||||
|
|
|
|||
|
|
@ -2,12 +2,20 @@ package fr.free.nrw.commons.bookmarks
|
|||
|
||||
import android.content.Context
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import fr.free.nrw.commons.TestCommonsApplication
|
||||
import org.junit.Assert
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mock
|
||||
import org.mockito.MockitoAnnotations
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.annotation.Config
|
||||
import org.robolectric.annotation.LooperMode
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(sdk = [21], application = TestCommonsApplication::class)
|
||||
@LooperMode(LooperMode.Mode.PAUSED)
|
||||
class BookmarksPagerAdapterTests {
|
||||
@Mock
|
||||
private lateinit var bookmarksPagerAdapter: BookmarksPagerAdapter
|
||||
|
|
|
|||
|
|
@ -2,15 +2,23 @@ package fr.free.nrw.commons.bookmarks
|
|||
|
||||
import android.content.Context
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import fr.free.nrw.commons.TestCommonsApplication
|
||||
import org.junit.Assert
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mock
|
||||
import org.mockito.MockitoAnnotations
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.annotation.Config
|
||||
import org.robolectric.annotation.LooperMode
|
||||
|
||||
/**
|
||||
* BookmarksPagerAdapter when user is not loggedIn.
|
||||
*/
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(sdk = [21], application = TestCommonsApplication::class)
|
||||
@LooperMode(LooperMode.Mode.PAUSED)
|
||||
class LoggedOutBookmarksPagerAdapterTests {
|
||||
@Mock
|
||||
private lateinit var bookmarksPagerAdapter: BookmarksPagerAdapter
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ class BookmarkItemsControllerTest {
|
|||
@Before
|
||||
fun setup() {
|
||||
MockitoAnnotations.openMocks(this)
|
||||
whenever(bookmarkDao!!.allBookmarksItems)
|
||||
whenever(bookmarkDao!!.getAllBookmarksItems())
|
||||
.thenReturn(mockBookmarkList)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,20 +18,20 @@ import com.nhaarman.mockitokotlin2.mock
|
|||
import com.nhaarman.mockitokotlin2.verify
|
||||
import com.nhaarman.mockitokotlin2.whenever
|
||||
import fr.free.nrw.commons.TestCommonsApplication
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.COLUMN_CATEGORIES_DESCRIPTION_LIST
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.COLUMN_CATEGORIES_NAME_LIST
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.COLUMN_CATEGORIES_THUMBNAIL_LIST
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.COLUMN_DESCRIPTION
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.COLUMN_ID
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.COLUMN_IMAGE
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.COLUMN_INSTANCE_LIST
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.COLUMN_IS_SELECTED
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.COLUMN_NAME
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.CREATE_TABLE_STATEMENT
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.DROP_TABLE_STATEMENT
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.onCreate
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.onDelete
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.onUpdate
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.COLUMN_CATEGORIES_DESCRIPTION_LIST
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.COLUMN_CATEGORIES_NAME_LIST
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.COLUMN_CATEGORIES_THUMBNAIL_LIST
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.COLUMN_DESCRIPTION
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.COLUMN_ID
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.COLUMN_IMAGE
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.COLUMN_INSTANCE_LIST
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.COLUMN_IS_SELECTED
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.COLUMN_NAME
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.CREATE_TABLE_STATEMENT
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.DROP_TABLE_STATEMENT
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.onCreate
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.onDelete
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.onUpdate
|
||||
import fr.free.nrw.commons.category.CategoryItem
|
||||
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem
|
||||
import org.junit.Assert
|
||||
|
|
@ -135,7 +135,7 @@ class BookmarkItemsDaoTest {
|
|||
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull()))
|
||||
.thenReturn(createCursor(14))
|
||||
|
||||
val result = testObject.allBookmarksItems
|
||||
val result = testObject.getAllBookmarksItems()
|
||||
|
||||
Assert.assertEquals(14, (result.size))
|
||||
}
|
||||
|
|
@ -145,20 +145,20 @@ class BookmarkItemsDaoTest {
|
|||
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull())).thenThrow(
|
||||
RemoteException(""),
|
||||
)
|
||||
testObject.allBookmarksItems
|
||||
testObject.getAllBookmarksItems()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getAllItemsBookmarksReturnsEmptyList_emptyCursor() {
|
||||
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull()))
|
||||
.thenReturn(createCursor(0))
|
||||
Assert.assertTrue(testObject.allBookmarksItems.isEmpty())
|
||||
Assert.assertTrue(testObject.getAllBookmarksItems().isEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getAllItemsBookmarksReturnsEmptyList_nullCursor() {
|
||||
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull())).thenReturn(null)
|
||||
Assert.assertTrue(testObject.allBookmarksItems.isEmpty())
|
||||
Assert.assertTrue(testObject.getAllBookmarksItems().isEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -167,7 +167,7 @@ class BookmarkItemsDaoTest {
|
|||
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull())).thenReturn(mockCursor)
|
||||
whenever(mockCursor.moveToFirst()).thenReturn(false)
|
||||
|
||||
testObject.allBookmarksItems
|
||||
testObject.getAllBookmarksItems()
|
||||
|
||||
verify(mockCursor).close()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ class BookmarkItemsFragmentUnitTest {
|
|||
context = ApplicationProvider.getApplicationContext()
|
||||
OkHttpConnectionFactory.CLIENT = createTestClient()
|
||||
val activity = Robolectric.buildActivity(ProfileActivity::class.java).create().get()
|
||||
fragment = BookmarkItemsFragment.newInstance()
|
||||
fragment = BookmarkItemsFragment()
|
||||
val fragmentManager: FragmentManager = activity.supportFragmentManager
|
||||
val fragmentTransaction: FragmentTransaction = fragmentManager.beginTransaction()
|
||||
fragmentTransaction.add(fragment, null)
|
||||
|
|
|
|||
|
|
@ -19,14 +19,14 @@ import com.nhaarman.mockitokotlin2.verify
|
|||
import com.nhaarman.mockitokotlin2.whenever
|
||||
import fr.free.nrw.commons.TestCommonsApplication
|
||||
import fr.free.nrw.commons.bookmarks.models.Bookmark
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesContentProvider.BASE_URI
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao.Table.COLUMN_CREATOR
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao.Table.COLUMN_MEDIA_NAME
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao.Table.CREATE_TABLE_STATEMENT
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao.Table.DROP_TABLE_STATEMENT
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao.Table.onCreate
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao.Table.onDelete
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao.Table.onUpdate
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesContentProvider.Companion.BASE_URI
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarksTable.COLUMN_CREATOR
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarksTable.COLUMN_MEDIA_NAME
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarksTable.CREATE_TABLE_STATEMENT
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarksTable.DROP_TABLE_STATEMENT
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarksTable.onCreate
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarksTable.onDelete
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarksTable.onUpdate
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
|
|
@ -84,7 +84,7 @@ class BookmarkPictureDaoTest {
|
|||
fun getAllBookmarks() {
|
||||
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull())).thenReturn(createCursor(14))
|
||||
|
||||
var result = testObject.allBookmarks
|
||||
var result = testObject.getAllBookmarks()
|
||||
|
||||
assertEquals(14, (result.size))
|
||||
}
|
||||
|
|
@ -92,19 +92,19 @@ class BookmarkPictureDaoTest {
|
|||
@Test(expected = RuntimeException::class)
|
||||
fun getAllBookmarksTranslatesExceptions() {
|
||||
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull())).thenThrow(RemoteException(""))
|
||||
testObject.allBookmarks
|
||||
testObject.getAllBookmarks()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getAllBookmarksReturnsEmptyList_emptyCursor() {
|
||||
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull())).thenReturn(createCursor(0))
|
||||
assertTrue(testObject.allBookmarks.isEmpty())
|
||||
assertTrue(testObject.getAllBookmarks().isEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getAllBookmarksReturnsEmptyList_nullCursor() {
|
||||
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull())).thenReturn(null)
|
||||
assertTrue(testObject.allBookmarks.isEmpty())
|
||||
assertTrue(testObject.getAllBookmarks().isEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -113,7 +113,7 @@ class BookmarkPictureDaoTest {
|
|||
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull())).thenReturn(mockCursor)
|
||||
whenever(mockCursor.moveToFirst()).thenReturn(false)
|
||||
|
||||
testObject.allBookmarks
|
||||
testObject.getAllBookmarks()
|
||||
|
||||
verify(mockCursor).close()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ class BookmarkPicturesControllerTest {
|
|||
fun setup() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
val mockMedia = mockMedia
|
||||
whenever(bookmarkDao!!.allBookmarks)
|
||||
whenever(bookmarkDao!!.getAllBookmarks())
|
||||
.thenReturn(mockBookmarkList)
|
||||
whenever(
|
||||
mediaClient!!.getMedia(
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ class BookmarkPicturesFragmentUnitTests {
|
|||
context = ApplicationProvider.getApplicationContext()
|
||||
OkHttpConnectionFactory.CLIENT = createTestClient()
|
||||
val activity = Robolectric.buildActivity(ProfileActivity::class.java).create().get()
|
||||
fragment = BookmarkPicturesFragment.newInstance()
|
||||
fragment = BookmarkPicturesFragment()
|
||||
val fragmentManager: FragmentManager = activity.supportFragmentManager
|
||||
val fragmentTransaction: FragmentTransaction = fragmentManager.beginTransaction()
|
||||
fragmentTransaction.add(fragment, null)
|
||||
|
|
@ -156,13 +156,13 @@ class BookmarkPicturesFragmentUnitTests {
|
|||
val method: Method =
|
||||
BookmarkPicturesFragment::class.java.getDeclaredMethod("setAdapter", List::class.java)
|
||||
method.isAccessible = true
|
||||
method.invoke(fragment, mediaList)
|
||||
method.invoke(fragment, emptyList<Media>())
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun testGetAdapter() {
|
||||
fragment.adapter
|
||||
fragment.getAdapter()
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ import fr.free.nrw.commons.category.CategoryDao.Table.DROP_TABLE_STATEMENT
|
|||
import fr.free.nrw.commons.category.CategoryDao.Table.onCreate
|
||||
import fr.free.nrw.commons.category.CategoryDao.Table.onDelete
|
||||
import fr.free.nrw.commons.category.CategoryDao.Table.onUpdate
|
||||
import fr.free.nrw.commons.explore.recentsearches.RecentSearchesContentProvider.uriForId
|
||||
import fr.free.nrw.commons.explore.recentsearches.RecentSearchesContentProvider.Companion.uriForId
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Assert.assertNull
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@ import com.nhaarman.mockitokotlin2.verify
|
|||
import com.nhaarman.mockitokotlin2.whenever
|
||||
import fr.free.nrw.commons.TestCommonsApplication
|
||||
import fr.free.nrw.commons.explore.models.RecentSearch
|
||||
import fr.free.nrw.commons.explore.recentsearches.RecentSearchesContentProvider.BASE_URI
|
||||
import fr.free.nrw.commons.explore.recentsearches.RecentSearchesContentProvider.uriForId
|
||||
import fr.free.nrw.commons.explore.recentsearches.RecentSearchesContentProvider.Companion.BASE_URI
|
||||
import fr.free.nrw.commons.explore.recentsearches.RecentSearchesContentProvider.Companion.uriForId
|
||||
import fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao.Table.ALL_FIELDS
|
||||
import fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao.Table.COLUMN_ID
|
||||
import fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao.Table.COLUMN_LAST_USED
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue