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 com.facebook.imagepipeline.core.ImagePipelineConfig
|
||||||
import fr.free.nrw.commons.auth.LoginActivity
|
import fr.free.nrw.commons.auth.LoginActivity
|
||||||
import fr.free.nrw.commons.auth.SessionManager
|
import fr.free.nrw.commons.auth.SessionManager
|
||||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao
|
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable
|
||||||
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao
|
import fr.free.nrw.commons.bookmarks.pictures.BookmarksTable
|
||||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao
|
|
||||||
import fr.free.nrw.commons.category.CategoryDao
|
import fr.free.nrw.commons.category.CategoryDao
|
||||||
import fr.free.nrw.commons.concurrency.BackgroundPoolExceptionHandler
|
import fr.free.nrw.commons.concurrency.BackgroundPoolExceptionHandler
|
||||||
import fr.free.nrw.commons.concurrency.ThreadPoolService
|
import fr.free.nrw.commons.concurrency.ThreadPoolService
|
||||||
|
|
@ -257,8 +256,8 @@ class CommonsApplication : MultiDexApplication() {
|
||||||
} catch (e: SQLiteException) {
|
} catch (e: SQLiteException) {
|
||||||
Timber.e(e)
|
Timber.e(e)
|
||||||
}
|
}
|
||||||
BookmarkPicturesDao.Table.onDelete(db)
|
BookmarksTable.onDelete(db)
|
||||||
BookmarkItemsDao.Table.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.database.sqlite.SQLiteQueryBuilder
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
import androidx.annotation.NonNull
|
|
||||||
import fr.free.nrw.commons.BuildConfig
|
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.di.CommonsDaggerContentProvider
|
||||||
import timber.log.Timber
|
import androidx.core.net.toUri
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
class CategoryContentProvider : CommonsDaggerContentProvider() {
|
class CategoryContentProvider : CommonsDaggerContentProvider() {
|
||||||
|
|
||||||
|
|
@ -23,9 +20,6 @@ class CategoryContentProvider : CommonsDaggerContentProvider() {
|
||||||
addURI(BuildConfig.CATEGORY_AUTHORITY, "${BASE_PATH}/#", CATEGORIES_ID)
|
addURI(BuildConfig.CATEGORY_AUTHORITY, "${BASE_PATH}/#", CATEGORIES_ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject
|
|
||||||
lateinit var dbOpenHelper: DBOpenHelper
|
|
||||||
|
|
||||||
@SuppressWarnings("ConstantConditions")
|
@SuppressWarnings("ConstantConditions")
|
||||||
override fun query(uri: Uri, projection: Array<String>?, selection: String?,
|
override fun query(uri: Uri, projection: Array<String>?, selection: String?,
|
||||||
selectionArgs: Array<String>?, sortOrder: String?): Cursor? {
|
selectionArgs: Array<String>?, sortOrder: String?): Cursor? {
|
||||||
|
|
@ -34,7 +28,7 @@ class CategoryContentProvider : CommonsDaggerContentProvider() {
|
||||||
}
|
}
|
||||||
|
|
||||||
val uriType = uriMatcher.match(uri)
|
val uriType = uriMatcher.match(uri)
|
||||||
val db = dbOpenHelper.readableDatabase
|
val db = requireDb()
|
||||||
|
|
||||||
val cursor: Cursor? = when (uriType) {
|
val cursor: Cursor? = when (uriType) {
|
||||||
CATEGORIES -> queryBuilder.query(
|
CATEGORIES -> queryBuilder.query(
|
||||||
|
|
@ -58,45 +52,37 @@ class CategoryContentProvider : CommonsDaggerContentProvider() {
|
||||||
else -> throw IllegalArgumentException("Unknown URI $uri")
|
else -> throw IllegalArgumentException("Unknown URI $uri")
|
||||||
}
|
}
|
||||||
|
|
||||||
cursor?.setNotificationUri(context?.contentResolver, uri)
|
cursor?.setNotificationUri(requireContext().contentResolver, uri)
|
||||||
return cursor
|
return cursor
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getType(uri: Uri): String? {
|
override fun getType(uri: Uri): String? = null
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("ConstantConditions")
|
@SuppressWarnings("ConstantConditions")
|
||||||
override fun insert(uri: Uri, contentValues: ContentValues?): Uri? {
|
override fun insert(uri: Uri, contentValues: ContentValues?): Uri {
|
||||||
val uriType = uriMatcher.match(uri)
|
val uriType = uriMatcher.match(uri)
|
||||||
val sqlDB = dbOpenHelper.writableDatabase
|
|
||||||
val id: Long
|
val id: Long
|
||||||
when (uriType) {
|
when (uriType) {
|
||||||
CATEGORIES -> {
|
CATEGORIES -> {
|
||||||
id = sqlDB.insert(TABLE_NAME, null, contentValues)
|
id = requireDb().insert(TABLE_NAME, null, contentValues)
|
||||||
}
|
}
|
||||||
else -> throw IllegalArgumentException("Unknown URI: $uri")
|
else -> throw IllegalArgumentException("Unknown URI: $uri")
|
||||||
}
|
}
|
||||||
context?.contentResolver?.notifyChange(uri, null)
|
requireContext().contentResolver?.notifyChange(uri, null)
|
||||||
return Uri.parse("${Companion.BASE_URI}/$id")
|
return "${BASE_URI}/$id".toUri()
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("ConstantConditions")
|
@SuppressWarnings("ConstantConditions")
|
||||||
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?): Int {
|
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?): Int = 0
|
||||||
// Not implemented
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("ConstantConditions")
|
@SuppressWarnings("ConstantConditions")
|
||||||
override fun bulkInsert(uri: Uri, values: Array<ContentValues>): Int {
|
override fun bulkInsert(uri: Uri, values: Array<ContentValues>): Int {
|
||||||
Timber.d("Hello, bulk insert! (CategoryContentProvider)")
|
|
||||||
val uriType = uriMatcher.match(uri)
|
val uriType = uriMatcher.match(uri)
|
||||||
val sqlDB = dbOpenHelper.writableDatabase
|
val sqlDB = requireDb()
|
||||||
sqlDB.beginTransaction()
|
sqlDB.beginTransaction()
|
||||||
when (uriType) {
|
when (uriType) {
|
||||||
CATEGORIES -> {
|
CATEGORIES -> {
|
||||||
for (value in values) {
|
for (value in values) {
|
||||||
Timber.d("Inserting! %s", value)
|
|
||||||
sqlDB.insert(TABLE_NAME, null, value)
|
sqlDB.insert(TABLE_NAME, null, value)
|
||||||
}
|
}
|
||||||
sqlDB.setTransactionSuccessful()
|
sqlDB.setTransactionSuccessful()
|
||||||
|
|
@ -104,7 +90,7 @@ class CategoryContentProvider : CommonsDaggerContentProvider() {
|
||||||
else -> throw IllegalArgumentException("Unknown URI: $uri")
|
else -> throw IllegalArgumentException("Unknown URI: $uri")
|
||||||
}
|
}
|
||||||
sqlDB.endTransaction()
|
sqlDB.endTransaction()
|
||||||
context?.contentResolver?.notifyChange(uri, null)
|
requireContext().contentResolver?.notifyChange(uri, null)
|
||||||
return values.size
|
return values.size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -112,17 +98,18 @@ class CategoryContentProvider : CommonsDaggerContentProvider() {
|
||||||
override fun update(uri: Uri, contentValues: ContentValues?, selection: String?,
|
override fun update(uri: Uri, contentValues: ContentValues?, selection: String?,
|
||||||
selectionArgs: Array<String>?): Int {
|
selectionArgs: Array<String>?): Int {
|
||||||
val uriType = uriMatcher.match(uri)
|
val uriType = uriMatcher.match(uri)
|
||||||
val sqlDB = dbOpenHelper.writableDatabase
|
|
||||||
val rowsUpdated: Int
|
val rowsUpdated: Int
|
||||||
when (uriType) {
|
when (uriType) {
|
||||||
CATEGORIES_ID -> {
|
CATEGORIES_ID -> {
|
||||||
if (TextUtils.isEmpty(selection)) {
|
if (TextUtils.isEmpty(selection)) {
|
||||||
val id = uri.lastPathSegment?.toInt()
|
val id = uri.lastPathSegment?.toInt()
|
||||||
?: throw IllegalArgumentException("Invalid ID")
|
?: throw IllegalArgumentException("Invalid ID")
|
||||||
rowsUpdated = sqlDB.update(TABLE_NAME,
|
rowsUpdated = requireDb().update(
|
||||||
|
TABLE_NAME,
|
||||||
contentValues,
|
contentValues,
|
||||||
"$COLUMN_ID = ?",
|
"$COLUMN_ID = ?",
|
||||||
arrayOf(id.toString()))
|
arrayOf(id.toString())
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
throw IllegalArgumentException(
|
throw IllegalArgumentException(
|
||||||
"Parameter `selection` should be empty when updating an ID")
|
"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")
|
else -> throw IllegalArgumentException("Unknown URI: $uri with type $uriType")
|
||||||
}
|
}
|
||||||
context?.contentResolver?.notifyChange(uri, null)
|
requireContext().contentResolver?.notifyChange(uri, null)
|
||||||
return rowsUpdated
|
return rowsUpdated
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -165,13 +152,9 @@ class CategoryContentProvider : CommonsDaggerContentProvider() {
|
||||||
"$COLUMN_TIMES_USED INTEGER" +
|
"$COLUMN_TIMES_USED INTEGER" +
|
||||||
");"
|
");"
|
||||||
|
|
||||||
fun uriForId(id: Int): Uri {
|
fun uriForId(id: Int): Uri = Uri.parse("${BASE_URI}/$id")
|
||||||
return Uri.parse("${BASE_URI}/$id")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onCreate(db: SQLiteDatabase) {
|
fun onCreate(db: SQLiteDatabase) = db.execSQL(CREATE_TABLE_STATEMENT)
|
||||||
db.execSQL(CREATE_TABLE_STATEMENT)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onDelete(db: SQLiteDatabase) {
|
fun onDelete(db: SQLiteDatabase) {
|
||||||
db.execSQL(DROP_TABLE_STATEMENT)
|
db.execSQL(DROP_TABLE_STATEMENT)
|
||||||
|
|
@ -200,6 +183,6 @@ class CategoryContentProvider : CommonsDaggerContentProvider() {
|
||||||
private const val CATEGORIES = 1
|
private const val CATEGORIES = 1
|
||||||
private const val CATEGORIES_ID = 2
|
private const val CATEGORIES_ID = 2
|
||||||
private const val BASE_PATH = "categories"
|
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.SQLiteDatabase
|
||||||
import android.database.sqlite.SQLiteException
|
import android.database.sqlite.SQLiteException
|
||||||
import android.database.sqlite.SQLiteOpenHelper
|
import android.database.sqlite.SQLiteOpenHelper
|
||||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao
|
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable
|
||||||
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao
|
import fr.free.nrw.commons.bookmarks.pictures.BookmarksTable
|
||||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao
|
|
||||||
import fr.free.nrw.commons.category.CategoryDao
|
import fr.free.nrw.commons.category.CategoryDao
|
||||||
import fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao
|
import fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao
|
||||||
import fr.free.nrw.commons.recentlanguages.RecentLanguagesDao
|
import fr.free.nrw.commons.recentlanguages.RecentLanguagesDao
|
||||||
|
|
@ -30,16 +29,16 @@ class DBOpenHelper(
|
||||||
*/
|
*/
|
||||||
override fun onCreate(db: SQLiteDatabase) {
|
override fun onCreate(db: SQLiteDatabase) {
|
||||||
CategoryDao.Table.onCreate(db)
|
CategoryDao.Table.onCreate(db)
|
||||||
BookmarkPicturesDao.Table.onCreate(db)
|
BookmarksTable.onCreate(db)
|
||||||
BookmarkItemsDao.Table.onCreate(db)
|
BookmarkItemsTable.onCreate(db)
|
||||||
RecentSearchesDao.Table.onCreate(db)
|
RecentSearchesDao.Table.onCreate(db)
|
||||||
RecentLanguagesDao.Table.onCreate(db)
|
RecentLanguagesDao.Table.onCreate(db)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onUpgrade(db: SQLiteDatabase, from: Int, to: Int) {
|
override fun onUpgrade(db: SQLiteDatabase, from: Int, to: Int) {
|
||||||
CategoryDao.Table.onUpdate(db, from, to)
|
CategoryDao.Table.onUpdate(db, from, to)
|
||||||
BookmarkPicturesDao.Table.onUpdate(db, from, to)
|
BookmarksTable.onUpdate(db, from, to)
|
||||||
BookmarkItemsDao.Table.onUpdate(db, from, to)
|
BookmarkItemsTable.onUpdate(db, from, to)
|
||||||
RecentSearchesDao.Table.onUpdate(db, from, to)
|
RecentSearchesDao.Table.onUpdate(db, from, to)
|
||||||
RecentLanguagesDao.Table.onUpdate(db, from, to)
|
RecentLanguagesDao.Table.onUpdate(db, from, to)
|
||||||
deleteTable(db, CONTRIBUTIONS_TABLE)
|
deleteTable(db, CONTRIBUTIONS_TABLE)
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,25 @@
|
||||||
package fr.free.nrw.commons.di
|
package fr.free.nrw.commons.di
|
||||||
|
|
||||||
import android.content.ContentProvider
|
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 fr.free.nrw.commons.di.ApplicationlessInjection.Companion.getInstance
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
abstract class CommonsDaggerContentProvider : ContentProvider() {
|
abstract class CommonsDaggerContentProvider : ContentProvider() {
|
||||||
|
@JvmField
|
||||||
|
@Inject
|
||||||
|
var dbOpenHelper: DBOpenHelper? = null
|
||||||
|
|
||||||
override fun onCreate(): Boolean {
|
override fun onCreate(): Boolean {
|
||||||
inject()
|
inject()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun requireDbOpenHelper(): DBOpenHelper = dbOpenHelper!!
|
||||||
|
|
||||||
|
fun requireDb(): SQLiteDatabase = requireDbOpenHelper().writableDatabase!!
|
||||||
|
|
||||||
private fun inject() {
|
private fun inject() {
|
||||||
val injection = getInstance(context!!)
|
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
|
val mediaDetailFragment = adapter!!.currentMediaDetailFragment
|
||||||
when (item.itemId) {
|
when (item.itemId) {
|
||||||
R.id.menu_bookmark_current_image -> {
|
R.id.menu_bookmark_current_image -> {
|
||||||
val bookmarkExists = bookmarkDao!!.updateBookmark(bookmark)
|
val bookmarkExists = bookmarkDao!!.updateBookmark(bookmark!!)
|
||||||
val snackbar = if (bookmarkExists) Snackbar.make(
|
val snackbar = if (bookmarkExists) Snackbar.make(
|
||||||
requireView(),
|
requireView(),
|
||||||
R.string.add_bookmark,
|
R.string.add_bookmark,
|
||||||
|
|
@ -436,7 +436,7 @@ ${m.pageTitle.canonicalUri}"""
|
||||||
bookmark = Bookmark(
|
bookmark = Bookmark(
|
||||||
m.filename,
|
m.filename,
|
||||||
m.getAuthorOrUser(),
|
m.getAuthorOrUser(),
|
||||||
BookmarkPicturesContentProvider.uriForName(m.filename)
|
BookmarkPicturesContentProvider.uriForName(m.filename!!)
|
||||||
)
|
)
|
||||||
updateBookmarkState(menu.findItem(R.id.menu_bookmark_current_image))
|
updateBookmarkState(menu.findItem(R.id.menu_bookmark_current_image))
|
||||||
val contributionState = provider.getContributionStateAt(position)
|
val contributionState = provider.getContributionStateAt(position)
|
||||||
|
|
|
||||||
|
|
@ -3,17 +3,13 @@ package fr.free.nrw.commons.recentlanguages
|
||||||
|
|
||||||
import android.content.ContentValues
|
import android.content.ContentValues
|
||||||
import android.database.Cursor
|
import android.database.Cursor
|
||||||
import android.database.sqlite.SQLiteDatabase
|
|
||||||
import android.database.sqlite.SQLiteQueryBuilder
|
import android.database.sqlite.SQLiteQueryBuilder
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.text.TextUtils
|
|
||||||
import fr.free.nrw.commons.BuildConfig
|
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.di.CommonsDaggerContentProvider
|
||||||
import fr.free.nrw.commons.recentlanguages.RecentLanguagesDao.Table.COLUMN_NAME
|
import fr.free.nrw.commons.recentlanguages.RecentLanguagesDao.Table.COLUMN_NAME
|
||||||
import fr.free.nrw.commons.recentlanguages.RecentLanguagesDao.Table.TABLE_NAME
|
import fr.free.nrw.commons.recentlanguages.RecentLanguagesDao.Table.TABLE_NAME
|
||||||
import javax.inject.Inject
|
import androidx.core.net.toUri
|
||||||
import timber.log.Timber
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -23,27 +19,17 @@ class RecentLanguagesContentProvider : CommonsDaggerContentProvider() {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val BASE_PATH = "recent_languages"
|
private const val BASE_PATH = "recent_languages"
|
||||||
val BASE_URI: Uri =
|
val BASE_URI: Uri = "content://${BuildConfig.RECENT_LANGUAGE_AUTHORITY}/$BASE_PATH".toUri()
|
||||||
Uri.parse(
|
|
||||||
"content://${BuildConfig.RECENT_LANGUAGE_AUTHORITY}/$BASE_PATH"
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Append language code to the base URI
|
* Append language code to the base URI
|
||||||
* @param languageCode Code of a language
|
* @param languageCode Code of a language
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun uriForCode(languageCode: String): Uri {
|
fun uriForCode(languageCode: String): Uri = "$BASE_URI/$languageCode".toUri()
|
||||||
return Uri.parse("$BASE_URI/$languageCode")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject
|
override fun getType(uri: Uri): String? = null
|
||||||
lateinit var dbOpenHelper: DBOpenHelper
|
|
||||||
|
|
||||||
override fun getType(uri: Uri): String? {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Queries the SQLite database for the recently used languages
|
* Queries the SQLite database for the recently used languages
|
||||||
|
|
@ -60,11 +46,12 @@ class RecentLanguagesContentProvider : CommonsDaggerContentProvider() {
|
||||||
selectionArgs: Array<String>?,
|
selectionArgs: Array<String>?,
|
||||||
sortOrder: String?
|
sortOrder: String?
|
||||||
): Cursor? {
|
): Cursor? {
|
||||||
val queryBuilder = SQLiteQueryBuilder()
|
val queryBuilder = SQLiteQueryBuilder().apply {
|
||||||
queryBuilder.tables = TABLE_NAME
|
tables = TABLE_NAME
|
||||||
val db = dbOpenHelper.readableDatabase
|
}
|
||||||
|
|
||||||
val cursor = queryBuilder.query(
|
val cursor = queryBuilder.query(
|
||||||
db,
|
requireDb(),
|
||||||
projection,
|
projection,
|
||||||
selection,
|
selection,
|
||||||
selectionArgs,
|
selectionArgs,
|
||||||
|
|
@ -72,7 +59,7 @@ class RecentLanguagesContentProvider : CommonsDaggerContentProvider() {
|
||||||
null,
|
null,
|
||||||
sortOrder
|
sortOrder
|
||||||
)
|
)
|
||||||
cursor.setNotificationUri(context?.contentResolver, uri)
|
cursor.setNotificationUri(requireContext().contentResolver, uri)
|
||||||
return cursor
|
return cursor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -89,12 +76,11 @@ class RecentLanguagesContentProvider : CommonsDaggerContentProvider() {
|
||||||
selection: String?,
|
selection: String?,
|
||||||
selectionArgs: Array<String>?
|
selectionArgs: Array<String>?
|
||||||
): Int {
|
): Int {
|
||||||
val sqlDB = dbOpenHelper.writableDatabase
|
|
||||||
val rowsUpdated: Int
|
val rowsUpdated: Int
|
||||||
if (selection.isNullOrEmpty()) {
|
if (selection.isNullOrEmpty()) {
|
||||||
val id = uri.lastPathSegment?.toInt()
|
val id = uri.lastPathSegment?.toInt()
|
||||||
?: throw IllegalArgumentException("Invalid URI: $uri")
|
?: throw IllegalArgumentException("Invalid URI: $uri")
|
||||||
rowsUpdated = sqlDB.update(
|
rowsUpdated = requireDb().update(
|
||||||
TABLE_NAME,
|
TABLE_NAME,
|
||||||
contentValues,
|
contentValues,
|
||||||
"$COLUMN_NAME = ?",
|
"$COLUMN_NAME = ?",
|
||||||
|
|
@ -104,7 +90,7 @@ class RecentLanguagesContentProvider : CommonsDaggerContentProvider() {
|
||||||
throw IllegalArgumentException("Parameter `selection` should be empty when updating an ID")
|
throw IllegalArgumentException("Parameter `selection` should be empty when updating an ID")
|
||||||
}
|
}
|
||||||
|
|
||||||
context?.contentResolver?.notifyChange(uri, null)
|
requireContext().contentResolver?.notifyChange(uri, null)
|
||||||
return rowsUpdated
|
return rowsUpdated
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -114,14 +100,13 @@ class RecentLanguagesContentProvider : CommonsDaggerContentProvider() {
|
||||||
* @param contentValues : new values to be entered to the database
|
* @param contentValues : new values to be entered to the database
|
||||||
*/
|
*/
|
||||||
override fun insert(uri: Uri, contentValues: ContentValues?): Uri? {
|
override fun insert(uri: Uri, contentValues: ContentValues?): Uri? {
|
||||||
val sqlDB = dbOpenHelper.writableDatabase
|
val id = requireDb().insert(
|
||||||
val id = sqlDB.insert(
|
|
||||||
TABLE_NAME,
|
TABLE_NAME,
|
||||||
null,
|
null,
|
||||||
contentValues
|
contentValues
|
||||||
)
|
)
|
||||||
context?.contentResolver?.notifyChange(uri, null)
|
requireContext().contentResolver?.notifyChange(uri, null)
|
||||||
return Uri.parse("$BASE_URI/$id")
|
return "$BASE_URI/$id".toUri()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -129,14 +114,12 @@ class RecentLanguagesContentProvider : CommonsDaggerContentProvider() {
|
||||||
* @param uri : contains the URI for recently used languages
|
* @param uri : contains the URI for recently used languages
|
||||||
*/
|
*/
|
||||||
override fun delete(uri: Uri, s: String?, strings: Array<String>?): Int {
|
override fun delete(uri: Uri, s: String?, strings: Array<String>?): Int {
|
||||||
val db = dbOpenHelper.readableDatabase
|
val rows = requireDb().delete(
|
||||||
Timber.d("Deleting recently used language %s", uri.lastPathSegment)
|
|
||||||
val rows = db.delete(
|
|
||||||
TABLE_NAME,
|
TABLE_NAME,
|
||||||
"language_code = ?",
|
"language_code = ?",
|
||||||
arrayOf(uri.lastPathSegment)
|
arrayOf(uri.lastPathSegment)
|
||||||
)
|
)
|
||||||
context?.contentResolver?.notifyChange(uri, null)
|
requireContext().contentResolver?.notifyChange(uri, null)
|
||||||
return rows
|
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
|
@Test
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
fun testOnItemClick() {
|
fun testOnItemClick() {
|
||||||
fragment.onItemClick(null, null, 0, 0)
|
fragment.onItemClick(null, view, 0, 0)
|
||||||
verify(childFragmentManager).beginTransaction()
|
verify(childFragmentManager).beginTransaction()
|
||||||
verify(childFragmentTransaction).commit()
|
verify(childFragmentTransaction).commit()
|
||||||
verify(childFragmentManager).executePendingTransactions()
|
verify(childFragmentManager).executePendingTransactions()
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,20 @@ package fr.free.nrw.commons.bookmarks
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.fragment.app.FragmentManager
|
import androidx.fragment.app.FragmentManager
|
||||||
|
import fr.free.nrw.commons.TestCommonsApplication
|
||||||
import org.junit.Assert
|
import org.junit.Assert
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
import org.mockito.Mock
|
import org.mockito.Mock
|
||||||
import org.mockito.MockitoAnnotations
|
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 {
|
class BookmarksPagerAdapterTests {
|
||||||
@Mock
|
@Mock
|
||||||
private lateinit var bookmarksPagerAdapter: BookmarksPagerAdapter
|
private lateinit var bookmarksPagerAdapter: BookmarksPagerAdapter
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,23 @@ package fr.free.nrw.commons.bookmarks
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.fragment.app.FragmentManager
|
import androidx.fragment.app.FragmentManager
|
||||||
|
import fr.free.nrw.commons.TestCommonsApplication
|
||||||
import org.junit.Assert
|
import org.junit.Assert
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
import org.mockito.Mock
|
import org.mockito.Mock
|
||||||
import org.mockito.MockitoAnnotations
|
import org.mockito.MockitoAnnotations
|
||||||
|
import org.robolectric.RobolectricTestRunner
|
||||||
|
import org.robolectric.annotation.Config
|
||||||
|
import org.robolectric.annotation.LooperMode
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BookmarksPagerAdapter when user is not loggedIn.
|
* BookmarksPagerAdapter when user is not loggedIn.
|
||||||
*/
|
*/
|
||||||
|
@RunWith(RobolectricTestRunner::class)
|
||||||
|
@Config(sdk = [21], application = TestCommonsApplication::class)
|
||||||
|
@LooperMode(LooperMode.Mode.PAUSED)
|
||||||
class LoggedOutBookmarksPagerAdapterTests {
|
class LoggedOutBookmarksPagerAdapterTests {
|
||||||
@Mock
|
@Mock
|
||||||
private lateinit var bookmarksPagerAdapter: BookmarksPagerAdapter
|
private lateinit var bookmarksPagerAdapter: BookmarksPagerAdapter
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ class BookmarkItemsControllerTest {
|
||||||
@Before
|
@Before
|
||||||
fun setup() {
|
fun setup() {
|
||||||
MockitoAnnotations.openMocks(this)
|
MockitoAnnotations.openMocks(this)
|
||||||
whenever(bookmarkDao!!.allBookmarksItems)
|
whenever(bookmarkDao!!.getAllBookmarksItems())
|
||||||
.thenReturn(mockBookmarkList)
|
.thenReturn(mockBookmarkList)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,20 +18,20 @@ import com.nhaarman.mockitokotlin2.mock
|
||||||
import com.nhaarman.mockitokotlin2.verify
|
import com.nhaarman.mockitokotlin2.verify
|
||||||
import com.nhaarman.mockitokotlin2.whenever
|
import com.nhaarman.mockitokotlin2.whenever
|
||||||
import fr.free.nrw.commons.TestCommonsApplication
|
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.BookmarkItemsTable.COLUMN_CATEGORIES_DESCRIPTION_LIST
|
||||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.COLUMN_CATEGORIES_NAME_LIST
|
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.COLUMN_CATEGORIES_NAME_LIST
|
||||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.COLUMN_CATEGORIES_THUMBNAIL_LIST
|
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.COLUMN_CATEGORIES_THUMBNAIL_LIST
|
||||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.COLUMN_DESCRIPTION
|
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.COLUMN_DESCRIPTION
|
||||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.COLUMN_ID
|
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.COLUMN_ID
|
||||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.COLUMN_IMAGE
|
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.COLUMN_IMAGE
|
||||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.COLUMN_INSTANCE_LIST
|
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.COLUMN_INSTANCE_LIST
|
||||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.COLUMN_IS_SELECTED
|
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.COLUMN_IS_SELECTED
|
||||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.COLUMN_NAME
|
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.COLUMN_NAME
|
||||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.CREATE_TABLE_STATEMENT
|
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.CREATE_TABLE_STATEMENT
|
||||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.DROP_TABLE_STATEMENT
|
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.DROP_TABLE_STATEMENT
|
||||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.onCreate
|
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.onCreate
|
||||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.onDelete
|
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.onDelete
|
||||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.onUpdate
|
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.onUpdate
|
||||||
import fr.free.nrw.commons.category.CategoryItem
|
import fr.free.nrw.commons.category.CategoryItem
|
||||||
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem
|
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem
|
||||||
import org.junit.Assert
|
import org.junit.Assert
|
||||||
|
|
@ -135,7 +135,7 @@ class BookmarkItemsDaoTest {
|
||||||
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull()))
|
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull()))
|
||||||
.thenReturn(createCursor(14))
|
.thenReturn(createCursor(14))
|
||||||
|
|
||||||
val result = testObject.allBookmarksItems
|
val result = testObject.getAllBookmarksItems()
|
||||||
|
|
||||||
Assert.assertEquals(14, (result.size))
|
Assert.assertEquals(14, (result.size))
|
||||||
}
|
}
|
||||||
|
|
@ -145,20 +145,20 @@ class BookmarkItemsDaoTest {
|
||||||
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull())).thenThrow(
|
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull())).thenThrow(
|
||||||
RemoteException(""),
|
RemoteException(""),
|
||||||
)
|
)
|
||||||
testObject.allBookmarksItems
|
testObject.getAllBookmarksItems()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun getAllItemsBookmarksReturnsEmptyList_emptyCursor() {
|
fun getAllItemsBookmarksReturnsEmptyList_emptyCursor() {
|
||||||
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull()))
|
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull()))
|
||||||
.thenReturn(createCursor(0))
|
.thenReturn(createCursor(0))
|
||||||
Assert.assertTrue(testObject.allBookmarksItems.isEmpty())
|
Assert.assertTrue(testObject.getAllBookmarksItems().isEmpty())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun getAllItemsBookmarksReturnsEmptyList_nullCursor() {
|
fun getAllItemsBookmarksReturnsEmptyList_nullCursor() {
|
||||||
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull())).thenReturn(null)
|
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull())).thenReturn(null)
|
||||||
Assert.assertTrue(testObject.allBookmarksItems.isEmpty())
|
Assert.assertTrue(testObject.getAllBookmarksItems().isEmpty())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -167,7 +167,7 @@ class BookmarkItemsDaoTest {
|
||||||
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull())).thenReturn(mockCursor)
|
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull())).thenReturn(mockCursor)
|
||||||
whenever(mockCursor.moveToFirst()).thenReturn(false)
|
whenever(mockCursor.moveToFirst()).thenReturn(false)
|
||||||
|
|
||||||
testObject.allBookmarksItems
|
testObject.getAllBookmarksItems()
|
||||||
|
|
||||||
verify(mockCursor).close()
|
verify(mockCursor).close()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@ class BookmarkItemsFragmentUnitTest {
|
||||||
context = ApplicationProvider.getApplicationContext()
|
context = ApplicationProvider.getApplicationContext()
|
||||||
OkHttpConnectionFactory.CLIENT = createTestClient()
|
OkHttpConnectionFactory.CLIENT = createTestClient()
|
||||||
val activity = Robolectric.buildActivity(ProfileActivity::class.java).create().get()
|
val activity = Robolectric.buildActivity(ProfileActivity::class.java).create().get()
|
||||||
fragment = BookmarkItemsFragment.newInstance()
|
fragment = BookmarkItemsFragment()
|
||||||
val fragmentManager: FragmentManager = activity.supportFragmentManager
|
val fragmentManager: FragmentManager = activity.supportFragmentManager
|
||||||
val fragmentTransaction: FragmentTransaction = fragmentManager.beginTransaction()
|
val fragmentTransaction: FragmentTransaction = fragmentManager.beginTransaction()
|
||||||
fragmentTransaction.add(fragment, null)
|
fragmentTransaction.add(fragment, null)
|
||||||
|
|
|
||||||
|
|
@ -19,14 +19,14 @@ import com.nhaarman.mockitokotlin2.verify
|
||||||
import com.nhaarman.mockitokotlin2.whenever
|
import com.nhaarman.mockitokotlin2.whenever
|
||||||
import fr.free.nrw.commons.TestCommonsApplication
|
import fr.free.nrw.commons.TestCommonsApplication
|
||||||
import fr.free.nrw.commons.bookmarks.models.Bookmark
|
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.BookmarkPicturesContentProvider.Companion.BASE_URI
|
||||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao.Table.COLUMN_CREATOR
|
import fr.free.nrw.commons.bookmarks.pictures.BookmarksTable.COLUMN_CREATOR
|
||||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao.Table.COLUMN_MEDIA_NAME
|
import fr.free.nrw.commons.bookmarks.pictures.BookmarksTable.COLUMN_MEDIA_NAME
|
||||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao.Table.CREATE_TABLE_STATEMENT
|
import fr.free.nrw.commons.bookmarks.pictures.BookmarksTable.CREATE_TABLE_STATEMENT
|
||||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao.Table.DROP_TABLE_STATEMENT
|
import fr.free.nrw.commons.bookmarks.pictures.BookmarksTable.DROP_TABLE_STATEMENT
|
||||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao.Table.onCreate
|
import fr.free.nrw.commons.bookmarks.pictures.BookmarksTable.onCreate
|
||||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao.Table.onDelete
|
import fr.free.nrw.commons.bookmarks.pictures.BookmarksTable.onDelete
|
||||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao.Table.onUpdate
|
import fr.free.nrw.commons.bookmarks.pictures.BookmarksTable.onUpdate
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Assert.assertFalse
|
import org.junit.Assert.assertFalse
|
||||||
import org.junit.Assert.assertTrue
|
import org.junit.Assert.assertTrue
|
||||||
|
|
@ -84,7 +84,7 @@ class BookmarkPictureDaoTest {
|
||||||
fun getAllBookmarks() {
|
fun getAllBookmarks() {
|
||||||
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull())).thenReturn(createCursor(14))
|
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull())).thenReturn(createCursor(14))
|
||||||
|
|
||||||
var result = testObject.allBookmarks
|
var result = testObject.getAllBookmarks()
|
||||||
|
|
||||||
assertEquals(14, (result.size))
|
assertEquals(14, (result.size))
|
||||||
}
|
}
|
||||||
|
|
@ -92,19 +92,19 @@ class BookmarkPictureDaoTest {
|
||||||
@Test(expected = RuntimeException::class)
|
@Test(expected = RuntimeException::class)
|
||||||
fun getAllBookmarksTranslatesExceptions() {
|
fun getAllBookmarksTranslatesExceptions() {
|
||||||
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull())).thenThrow(RemoteException(""))
|
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull())).thenThrow(RemoteException(""))
|
||||||
testObject.allBookmarks
|
testObject.getAllBookmarks()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun getAllBookmarksReturnsEmptyList_emptyCursor() {
|
fun getAllBookmarksReturnsEmptyList_emptyCursor() {
|
||||||
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull())).thenReturn(createCursor(0))
|
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull())).thenReturn(createCursor(0))
|
||||||
assertTrue(testObject.allBookmarks.isEmpty())
|
assertTrue(testObject.getAllBookmarks().isEmpty())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun getAllBookmarksReturnsEmptyList_nullCursor() {
|
fun getAllBookmarksReturnsEmptyList_nullCursor() {
|
||||||
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull())).thenReturn(null)
|
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull())).thenReturn(null)
|
||||||
assertTrue(testObject.allBookmarks.isEmpty())
|
assertTrue(testObject.getAllBookmarks().isEmpty())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -113,7 +113,7 @@ class BookmarkPictureDaoTest {
|
||||||
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull())).thenReturn(mockCursor)
|
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull())).thenReturn(mockCursor)
|
||||||
whenever(mockCursor.moveToFirst()).thenReturn(false)
|
whenever(mockCursor.moveToFirst()).thenReturn(false)
|
||||||
|
|
||||||
testObject.allBookmarks
|
testObject.getAllBookmarks()
|
||||||
|
|
||||||
verify(mockCursor).close()
|
verify(mockCursor).close()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ class BookmarkPicturesControllerTest {
|
||||||
fun setup() {
|
fun setup() {
|
||||||
MockitoAnnotations.initMocks(this)
|
MockitoAnnotations.initMocks(this)
|
||||||
val mockMedia = mockMedia
|
val mockMedia = mockMedia
|
||||||
whenever(bookmarkDao!!.allBookmarks)
|
whenever(bookmarkDao!!.getAllBookmarks())
|
||||||
.thenReturn(mockBookmarkList)
|
.thenReturn(mockBookmarkList)
|
||||||
whenever(
|
whenever(
|
||||||
mediaClient!!.getMedia(
|
mediaClient!!.getMedia(
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@ class BookmarkPicturesFragmentUnitTests {
|
||||||
context = ApplicationProvider.getApplicationContext()
|
context = ApplicationProvider.getApplicationContext()
|
||||||
OkHttpConnectionFactory.CLIENT = createTestClient()
|
OkHttpConnectionFactory.CLIENT = createTestClient()
|
||||||
val activity = Robolectric.buildActivity(ProfileActivity::class.java).create().get()
|
val activity = Robolectric.buildActivity(ProfileActivity::class.java).create().get()
|
||||||
fragment = BookmarkPicturesFragment.newInstance()
|
fragment = BookmarkPicturesFragment()
|
||||||
val fragmentManager: FragmentManager = activity.supportFragmentManager
|
val fragmentManager: FragmentManager = activity.supportFragmentManager
|
||||||
val fragmentTransaction: FragmentTransaction = fragmentManager.beginTransaction()
|
val fragmentTransaction: FragmentTransaction = fragmentManager.beginTransaction()
|
||||||
fragmentTransaction.add(fragment, null)
|
fragmentTransaction.add(fragment, null)
|
||||||
|
|
@ -156,13 +156,13 @@ class BookmarkPicturesFragmentUnitTests {
|
||||||
val method: Method =
|
val method: Method =
|
||||||
BookmarkPicturesFragment::class.java.getDeclaredMethod("setAdapter", List::class.java)
|
BookmarkPicturesFragment::class.java.getDeclaredMethod("setAdapter", List::class.java)
|
||||||
method.isAccessible = true
|
method.isAccessible = true
|
||||||
method.invoke(fragment, mediaList)
|
method.invoke(fragment, emptyList<Media>())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
fun testGetAdapter() {
|
fun testGetAdapter() {
|
||||||
fragment.adapter
|
fragment.getAdapter()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@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.onCreate
|
||||||
import fr.free.nrw.commons.category.CategoryDao.Table.onDelete
|
import fr.free.nrw.commons.category.CategoryDao.Table.onDelete
|
||||||
import fr.free.nrw.commons.category.CategoryDao.Table.onUpdate
|
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.assertEquals
|
||||||
import org.junit.Assert.assertNotNull
|
import org.junit.Assert.assertNotNull
|
||||||
import org.junit.Assert.assertNull
|
import org.junit.Assert.assertNull
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,8 @@ import com.nhaarman.mockitokotlin2.verify
|
||||||
import com.nhaarman.mockitokotlin2.whenever
|
import com.nhaarman.mockitokotlin2.whenever
|
||||||
import fr.free.nrw.commons.TestCommonsApplication
|
import fr.free.nrw.commons.TestCommonsApplication
|
||||||
import fr.free.nrw.commons.explore.models.RecentSearch
|
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.Companion.BASE_URI
|
||||||
import fr.free.nrw.commons.explore.recentsearches.RecentSearchesContentProvider.uriForId
|
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.ALL_FIELDS
|
||||||
import fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao.Table.COLUMN_ID
|
import fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao.Table.COLUMN_ID
|
||||||
import fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao.Table.COLUMN_LAST_USED
|
import fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao.Table.COLUMN_LAST_USED
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue