mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-26 20:33:53 +01:00
Merge branch 'commons-app:main' into Fix-Nearby-labels
This commit is contained in:
commit
88793d4070
74 changed files with 2047 additions and 2042 deletions
|
|
@ -15,9 +15,8 @@ import com.facebook.drawee.backends.pipeline.Fresco
|
|||
import com.facebook.imagepipeline.core.ImagePipelineConfig
|
||||
import fr.free.nrw.commons.auth.LoginActivity
|
||||
import fr.free.nrw.commons.auth.SessionManager
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao
|
||||
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarksTable
|
||||
import fr.free.nrw.commons.category.CategoryDao
|
||||
import fr.free.nrw.commons.concurrency.BackgroundPoolExceptionHandler
|
||||
import fr.free.nrw.commons.concurrency.ThreadPoolService
|
||||
|
|
@ -257,8 +256,8 @@ class CommonsApplication : MultiDexApplication() {
|
|||
} catch (e: SQLiteException) {
|
||||
Timber.e(e)
|
||||
}
|
||||
BookmarkPicturesDao.Table.onDelete(db)
|
||||
BookmarkItemsDao.Table.onDelete(db)
|
||||
BookmarksTable.onDelete(db)
|
||||
BookmarkItemsTable.onDelete(db)
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,105 +0,0 @@
|
|||
package fr.free.nrw.commons.bookmarks;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import fr.free.nrw.commons.contributions.MainActivity;
|
||||
import fr.free.nrw.commons.databinding.FragmentBookmarksBinding;
|
||||
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
|
||||
import fr.free.nrw.commons.kvstore.JsonKvStore;
|
||||
import fr.free.nrw.commons.theme.BaseActivity;
|
||||
import javax.inject.Inject;
|
||||
import fr.free.nrw.commons.contributions.ContributionController;
|
||||
import javax.inject.Named;
|
||||
|
||||
public class BookmarkFragment extends CommonsDaggerSupportFragment {
|
||||
|
||||
private FragmentManager supportFragmentManager;
|
||||
private BookmarksPagerAdapter adapter;
|
||||
FragmentBookmarksBinding binding;
|
||||
|
||||
@Inject
|
||||
ContributionController controller;
|
||||
/**
|
||||
* To check if the user is loggedIn or not.
|
||||
*/
|
||||
@Inject
|
||||
@Named("default_preferences")
|
||||
public
|
||||
JsonKvStore applicationKvStore;
|
||||
|
||||
@NonNull
|
||||
public static BookmarkFragment newInstance() {
|
||||
BookmarkFragment fragment = new BookmarkFragment();
|
||||
fragment.setRetainInstance(true);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
public void setScroll(boolean canScroll) {
|
||||
if (binding!=null) {
|
||||
binding.viewPagerBookmarks.setCanScroll(canScroll);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull final LayoutInflater inflater,
|
||||
@Nullable final ViewGroup container,
|
||||
@Nullable final Bundle savedInstanceState) {
|
||||
super.onCreateView(inflater, container, savedInstanceState);
|
||||
binding = FragmentBookmarksBinding.inflate(inflater, container, false);
|
||||
|
||||
// Activity can call methods in the fragment by acquiring a
|
||||
// reference to the Fragment from FragmentManager, using findFragmentById()
|
||||
supportFragmentManager = getChildFragmentManager();
|
||||
|
||||
adapter = new BookmarksPagerAdapter(supportFragmentManager, getContext(),
|
||||
applicationKvStore.getBoolean("login_skipped"));
|
||||
binding.viewPagerBookmarks.setAdapter(adapter);
|
||||
binding.tabLayout.setupWithViewPager(binding.viewPagerBookmarks);
|
||||
|
||||
((MainActivity) getActivity()).showTabs();
|
||||
((BaseActivity) getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(false);
|
||||
|
||||
setupTabLayout();
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method sets up the tab layout. If the adapter has only one element it sets the
|
||||
* visibility of tabLayout to gone.
|
||||
*/
|
||||
public void setupTabLayout() {
|
||||
binding.tabLayout.setVisibility(View.VISIBLE);
|
||||
if (adapter.getCount() == 1) {
|
||||
binding.tabLayout.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void onBackPressed() {
|
||||
if (((BookmarkListRootFragment) (adapter.getItem(binding.tabLayout.getSelectedTabPosition())))
|
||||
.backPressed()) {
|
||||
// The event is handled internally by the adapter , no further action required.
|
||||
return;
|
||||
}
|
||||
// Event is not handled by the adapter ( performed back action ) change action bar.
|
||||
((BaseActivity) getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
binding = null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
package fr.free.nrw.commons.bookmarks
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import fr.free.nrw.commons.contributions.ContributionController
|
||||
import fr.free.nrw.commons.contributions.MainActivity
|
||||
import fr.free.nrw.commons.databinding.FragmentBookmarksBinding
|
||||
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment
|
||||
import fr.free.nrw.commons.kvstore.JsonKvStore
|
||||
import fr.free.nrw.commons.theme.BaseActivity
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Named
|
||||
|
||||
class BookmarkFragment : CommonsDaggerSupportFragment() {
|
||||
private var adapter: BookmarksPagerAdapter? = null
|
||||
|
||||
@JvmField
|
||||
var binding: FragmentBookmarksBinding? = null
|
||||
|
||||
@JvmField
|
||||
@Inject
|
||||
var controller: ContributionController? = null
|
||||
|
||||
/**
|
||||
* To check if the user is loggedIn or not.
|
||||
*/
|
||||
@JvmField
|
||||
@Inject
|
||||
@Named("default_preferences")
|
||||
var applicationKvStore: JsonKvStore? = null
|
||||
|
||||
fun setScroll(canScroll: Boolean) {
|
||||
binding?.let {
|
||||
it.viewPagerBookmarks.isCanScroll = canScroll
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
super.onCreateView(inflater, container, savedInstanceState)
|
||||
binding = FragmentBookmarksBinding.inflate(inflater, container, false)
|
||||
|
||||
// Activity can call methods in the fragment by acquiring a
|
||||
// reference to the Fragment from FragmentManager, using findFragmentById()
|
||||
val supportFragmentManager = childFragmentManager
|
||||
|
||||
adapter = BookmarksPagerAdapter(
|
||||
supportFragmentManager, requireContext(),
|
||||
applicationKvStore!!.getBoolean("login_skipped")
|
||||
)
|
||||
binding!!.viewPagerBookmarks.adapter = adapter
|
||||
binding!!.tabLayout.setupWithViewPager(binding!!.viewPagerBookmarks)
|
||||
|
||||
(requireActivity() as MainActivity).showTabs()
|
||||
(requireActivity() as BaseActivity).supportActionBar!!.setDisplayHomeAsUpEnabled(false)
|
||||
|
||||
setupTabLayout()
|
||||
return binding!!.root
|
||||
}
|
||||
|
||||
/**
|
||||
* This method sets up the tab layout. If the adapter has only one element it sets the
|
||||
* visibility of tabLayout to gone.
|
||||
*/
|
||||
fun setupTabLayout() {
|
||||
binding!!.tabLayout.visibility = View.VISIBLE
|
||||
if (adapter!!.count == 1) {
|
||||
binding!!.tabLayout.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun onBackPressed() {
|
||||
if (((adapter!!.getItem(binding!!.tabLayout.selectedTabPosition)) as BookmarkListRootFragment).backPressed()) {
|
||||
// The event is handled internally by the adapter , no further action required.
|
||||
return
|
||||
}
|
||||
|
||||
// Event is not handled by the adapter ( performed back action ) change action bar.
|
||||
(requireActivity() as BaseActivity).supportActionBar!!.setDisplayHomeAsUpEnabled(false)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
binding = null
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun newInstance(): BookmarkFragment = BookmarkFragment().apply {
|
||||
retainInstance = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,267 +0,0 @@
|
|||
package fr.free.nrw.commons.bookmarks;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import fr.free.nrw.commons.Media;
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.bookmarks.category.BookmarkCategoriesFragment;
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsFragment;
|
||||
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsFragment;
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesFragment;
|
||||
import fr.free.nrw.commons.category.CategoryImagesCallback;
|
||||
import fr.free.nrw.commons.category.GridViewAdapter;
|
||||
import fr.free.nrw.commons.contributions.MainActivity;
|
||||
import fr.free.nrw.commons.databinding.FragmentFeaturedRootBinding;
|
||||
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
|
||||
import fr.free.nrw.commons.media.MediaDetailPagerFragment;
|
||||
import fr.free.nrw.commons.media.MediaDetailProvider;
|
||||
import fr.free.nrw.commons.navtab.NavTab;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import timber.log.Timber;
|
||||
|
||||
public class BookmarkListRootFragment extends CommonsDaggerSupportFragment implements
|
||||
FragmentManager.OnBackStackChangedListener,
|
||||
MediaDetailProvider,
|
||||
AdapterView.OnItemClickListener, CategoryImagesCallback {
|
||||
|
||||
private MediaDetailPagerFragment mediaDetails;
|
||||
//private BookmarkPicturesFragment bookmarkPicturesFragment;
|
||||
private BookmarkLocationsFragment bookmarkLocationsFragment;
|
||||
public Fragment listFragment;
|
||||
private BookmarksPagerAdapter bookmarksPagerAdapter;
|
||||
|
||||
FragmentFeaturedRootBinding binding;
|
||||
|
||||
public BookmarkListRootFragment() {
|
||||
//empty constructor necessary otherwise crashes on recreate
|
||||
}
|
||||
|
||||
public BookmarkListRootFragment(Bundle bundle, BookmarksPagerAdapter bookmarksPagerAdapter) {
|
||||
String title = bundle.getString("categoryName");
|
||||
int order = bundle.getInt("order");
|
||||
final int orderItem = bundle.getInt("orderItem");
|
||||
|
||||
switch (order){
|
||||
case 0: listFragment = new BookmarkPicturesFragment();
|
||||
break;
|
||||
|
||||
case 1: listFragment = new BookmarkLocationsFragment();
|
||||
break;
|
||||
|
||||
case 3: listFragment = new BookmarkCategoriesFragment();
|
||||
break;
|
||||
}
|
||||
if(orderItem == 2) {
|
||||
listFragment = new BookmarkItemsFragment();
|
||||
}
|
||||
|
||||
Bundle featuredArguments = new Bundle();
|
||||
featuredArguments.putString("categoryName", title);
|
||||
listFragment.setArguments(featuredArguments);
|
||||
this.bookmarksPagerAdapter = bookmarksPagerAdapter;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull final LayoutInflater inflater,
|
||||
@Nullable final ViewGroup container,
|
||||
@Nullable final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
binding = FragmentFeaturedRootBinding.inflate(inflater, container, false);
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
if (savedInstanceState == null) {
|
||||
setFragment(listFragment, mediaDetails);
|
||||
}
|
||||
}
|
||||
|
||||
public void setFragment(Fragment fragment, Fragment otherFragment) {
|
||||
if (fragment.isAdded() && otherFragment != null) {
|
||||
getChildFragmentManager()
|
||||
.beginTransaction()
|
||||
.hide(otherFragment)
|
||||
.show(fragment)
|
||||
.addToBackStack("CONTRIBUTION_LIST_FRAGMENT_TAG")
|
||||
.commit();
|
||||
getChildFragmentManager().executePendingTransactions();
|
||||
} else if (fragment.isAdded() && otherFragment == null) {
|
||||
getChildFragmentManager()
|
||||
.beginTransaction()
|
||||
.show(fragment)
|
||||
.addToBackStack("CONTRIBUTION_LIST_FRAGMENT_TAG")
|
||||
.commit();
|
||||
getChildFragmentManager().executePendingTransactions();
|
||||
} else if (!fragment.isAdded() && otherFragment != null) {
|
||||
getChildFragmentManager()
|
||||
.beginTransaction()
|
||||
.hide(otherFragment)
|
||||
.add(R.id.explore_container, fragment)
|
||||
.addToBackStack("CONTRIBUTION_LIST_FRAGMENT_TAG")
|
||||
.commit();
|
||||
getChildFragmentManager().executePendingTransactions();
|
||||
} else if (!fragment.isAdded()) {
|
||||
getChildFragmentManager()
|
||||
.beginTransaction()
|
||||
.replace(R.id.explore_container, fragment)
|
||||
.addToBackStack("CONTRIBUTION_LIST_FRAGMENT_TAG")
|
||||
.commit();
|
||||
getChildFragmentManager().executePendingTransactions();
|
||||
}
|
||||
}
|
||||
|
||||
public void removeFragment(Fragment fragment) {
|
||||
getChildFragmentManager()
|
||||
.beginTransaction()
|
||||
.remove(fragment)
|
||||
.commit();
|
||||
getChildFragmentManager().executePendingTransactions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(final Context context) {
|
||||
super.onAttach(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMediaClicked(int position) {
|
||||
Timber.d("on media clicked");
|
||||
/*container.setVisibility(View.VISIBLE);
|
||||
((BookmarkFragment)getParentFragment()).tabLayout.setVisibility(View.GONE);
|
||||
mediaDetails = new MediaDetailPagerFragment(false, true, position);
|
||||
setFragment(mediaDetails, bookmarkPicturesFragment);*/
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called mediaDetailPagerFragment. It returns the Media Object at that Index
|
||||
*
|
||||
* @param i It is the index of which media object is to be returned which is same as current
|
||||
* index of viewPager.
|
||||
* @return Media Object
|
||||
*/
|
||||
@Override
|
||||
public Media getMediaAtPosition(int i) {
|
||||
if (bookmarksPagerAdapter.getMediaAdapter() == null) {
|
||||
// not yet ready to return data
|
||||
return null;
|
||||
} else {
|
||||
return (Media) bookmarksPagerAdapter.getMediaAdapter().getItem(i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called on from getCount of MediaDetailPagerFragment The viewpager will contain
|
||||
* same number of media items as that of media elements in adapter.
|
||||
*
|
||||
* @return Total Media count in the adapter
|
||||
*/
|
||||
@Override
|
||||
public int getTotalMediaCount() {
|
||||
if (bookmarksPagerAdapter.getMediaAdapter() == null) {
|
||||
return 0;
|
||||
}
|
||||
return bookmarksPagerAdapter.getMediaAdapter().getCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getContributionStateAt(int position) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reload media detail fragment once media is nominated
|
||||
*
|
||||
* @param index item position that has been nominated
|
||||
*/
|
||||
@Override
|
||||
public void refreshNominatedMedia(int index) {
|
||||
if (mediaDetails != null && !listFragment.isVisible()) {
|
||||
removeFragment(mediaDetails);
|
||||
mediaDetails = MediaDetailPagerFragment.newInstance(false, true);
|
||||
((BookmarkFragment) getParentFragment()).setScroll(false);
|
||||
setFragment(mediaDetails, listFragment);
|
||||
mediaDetails.showImage(index);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called on success of API call for featured images or mobile uploads. The
|
||||
* viewpager will notified that number of items have changed.
|
||||
*/
|
||||
@Override
|
||||
public void viewPagerNotifyDataSetChanged() {
|
||||
if (mediaDetails != null) {
|
||||
mediaDetails.notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean backPressed() {
|
||||
//check mediaDetailPage fragment is not null then we check mediaDetail.is Visible or not to avoid NullPointerException
|
||||
if (mediaDetails != null) {
|
||||
if (mediaDetails.isVisible()) {
|
||||
// todo add get list fragment
|
||||
((BookmarkFragment) getParentFragment()).setupTabLayout();
|
||||
ArrayList<Integer> removed = mediaDetails.getRemovedItems();
|
||||
removeFragment(mediaDetails);
|
||||
((BookmarkFragment) getParentFragment()).setScroll(true);
|
||||
setFragment(listFragment, mediaDetails);
|
||||
((MainActivity) getActivity()).showTabs();
|
||||
if (listFragment instanceof BookmarkPicturesFragment) {
|
||||
GridViewAdapter adapter = ((GridViewAdapter) ((BookmarkPicturesFragment) listFragment)
|
||||
.getAdapter());
|
||||
Iterator i = removed.iterator();
|
||||
while (i.hasNext()) {
|
||||
adapter.remove(adapter.getItem((int) i.next()));
|
||||
}
|
||||
mediaDetails.clearRemoved();
|
||||
|
||||
}
|
||||
} else {
|
||||
moveToContributionsFragment();
|
||||
}
|
||||
} else {
|
||||
moveToContributionsFragment();
|
||||
}
|
||||
// notify mediaDetails did not handled the backPressed further actions required.
|
||||
return false;
|
||||
}
|
||||
|
||||
void moveToContributionsFragment() {
|
||||
((MainActivity) getActivity()).setSelectedItemId(NavTab.CONTRIBUTIONS.code());
|
||||
((MainActivity) getActivity()).showTabs();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
Timber.d("on media clicked");
|
||||
binding.exploreContainer.setVisibility(View.VISIBLE);
|
||||
((BookmarkFragment) getParentFragment()).binding.tabLayout.setVisibility(View.GONE);
|
||||
mediaDetails = MediaDetailPagerFragment.newInstance(false, true);
|
||||
((BookmarkFragment) getParentFragment()).setScroll(false);
|
||||
setFragment(mediaDetails, listFragment);
|
||||
mediaDetails.showImage(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackStackChanged() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
binding = null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,226 @@
|
|||
package fr.free.nrw.commons.bookmarks
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.AdapterView
|
||||
import android.widget.AdapterView.OnItemClickListener
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import fr.free.nrw.commons.Media
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.bookmarks.category.BookmarkCategoriesFragment
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsFragment
|
||||
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsFragment
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesFragment
|
||||
import fr.free.nrw.commons.category.CategoryImagesCallback
|
||||
import fr.free.nrw.commons.category.GridViewAdapter
|
||||
import fr.free.nrw.commons.contributions.MainActivity
|
||||
import fr.free.nrw.commons.databinding.FragmentFeaturedRootBinding
|
||||
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment
|
||||
import fr.free.nrw.commons.media.MediaDetailPagerFragment
|
||||
import fr.free.nrw.commons.media.MediaDetailPagerFragment.Companion.newInstance
|
||||
import fr.free.nrw.commons.media.MediaDetailProvider
|
||||
import fr.free.nrw.commons.navtab.NavTab
|
||||
import timber.log.Timber
|
||||
|
||||
class BookmarkListRootFragment : CommonsDaggerSupportFragment,
|
||||
FragmentManager.OnBackStackChangedListener, MediaDetailProvider, OnItemClickListener,
|
||||
CategoryImagesCallback {
|
||||
private var mediaDetails: MediaDetailPagerFragment? = null
|
||||
private val bookmarkLocationsFragment: BookmarkLocationsFragment? = null
|
||||
var listFragment: Fragment? = null
|
||||
private var bookmarksPagerAdapter: BookmarksPagerAdapter? = null
|
||||
|
||||
var binding: FragmentFeaturedRootBinding? = null
|
||||
|
||||
constructor()
|
||||
|
||||
constructor(bundle: Bundle, bookmarksPagerAdapter: BookmarksPagerAdapter) {
|
||||
val title = bundle.getString("categoryName")
|
||||
val order = bundle.getInt("order")
|
||||
val orderItem = bundle.getInt("orderItem")
|
||||
|
||||
when (order) {
|
||||
0 -> listFragment = BookmarkPicturesFragment()
|
||||
1 -> listFragment = BookmarkLocationsFragment()
|
||||
3 -> listFragment = BookmarkCategoriesFragment()
|
||||
}
|
||||
if (orderItem == 2) {
|
||||
listFragment = BookmarkItemsFragment()
|
||||
}
|
||||
|
||||
val featuredArguments = Bundle()
|
||||
featuredArguments.putString("categoryName", title)
|
||||
listFragment!!.setArguments(featuredArguments)
|
||||
this.bookmarksPagerAdapter = bookmarksPagerAdapter
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = FragmentFeaturedRootBinding.inflate(inflater, container, false)
|
||||
return binding!!.getRoot()
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
if (savedInstanceState == null) {
|
||||
setFragment(listFragment!!, mediaDetails)
|
||||
}
|
||||
}
|
||||
|
||||
fun setFragment(fragment: Fragment, otherFragment: Fragment?) {
|
||||
if (fragment.isAdded() && otherFragment != null) {
|
||||
getChildFragmentManager()
|
||||
.beginTransaction()
|
||||
.hide(otherFragment)
|
||||
.show(fragment)
|
||||
.addToBackStack("CONTRIBUTION_LIST_FRAGMENT_TAG")
|
||||
.commit()
|
||||
getChildFragmentManager().executePendingTransactions()
|
||||
} else if (fragment.isAdded() && otherFragment == null) {
|
||||
getChildFragmentManager()
|
||||
.beginTransaction()
|
||||
.show(fragment)
|
||||
.addToBackStack("CONTRIBUTION_LIST_FRAGMENT_TAG")
|
||||
.commit()
|
||||
getChildFragmentManager().executePendingTransactions()
|
||||
} else if (!fragment.isAdded() && otherFragment != null) {
|
||||
getChildFragmentManager()
|
||||
.beginTransaction()
|
||||
.hide(otherFragment)
|
||||
.add(R.id.explore_container, fragment)
|
||||
.addToBackStack("CONTRIBUTION_LIST_FRAGMENT_TAG")
|
||||
.commit()
|
||||
getChildFragmentManager().executePendingTransactions()
|
||||
} else if (!fragment.isAdded()) {
|
||||
getChildFragmentManager()
|
||||
.beginTransaction()
|
||||
.replace(R.id.explore_container, fragment)
|
||||
.addToBackStack("CONTRIBUTION_LIST_FRAGMENT_TAG")
|
||||
.commit()
|
||||
getChildFragmentManager().executePendingTransactions()
|
||||
}
|
||||
}
|
||||
|
||||
fun removeFragment(fragment: Fragment) {
|
||||
getChildFragmentManager()
|
||||
.beginTransaction()
|
||||
.remove(fragment)
|
||||
.commit()
|
||||
getChildFragmentManager().executePendingTransactions()
|
||||
}
|
||||
|
||||
override fun onMediaClicked(position: Int) {
|
||||
Timber.d("on media clicked")
|
||||
/*container.setVisibility(View.VISIBLE);
|
||||
((BookmarkFragment)getParentFragment()).tabLayout.setVisibility(View.GONE);
|
||||
mediaDetails = new MediaDetailPagerFragment(false, true, position);
|
||||
setFragment(mediaDetails, bookmarkPicturesFragment);*/
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called mediaDetailPagerFragment. It returns the Media Object at that Index
|
||||
*
|
||||
* @param i It is the index of which media object is to be returned which is same as current
|
||||
* index of viewPager.
|
||||
* @return Media Object
|
||||
*/
|
||||
override fun getMediaAtPosition(i: Int): Media? =
|
||||
bookmarksPagerAdapter!!.mediaAdapter?.getItem(i) as Media?
|
||||
|
||||
/**
|
||||
* This method is called on from getCount of MediaDetailPagerFragment The viewpager will contain
|
||||
* same number of media items as that of media elements in adapter.
|
||||
*
|
||||
* @return Total Media count in the adapter
|
||||
*/
|
||||
override fun getTotalMediaCount(): Int =
|
||||
bookmarksPagerAdapter!!.mediaAdapter?.count ?: 0
|
||||
|
||||
override fun getContributionStateAt(position: Int): Int? {
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Reload media detail fragment once media is nominated
|
||||
*
|
||||
* @param index item position that has been nominated
|
||||
*/
|
||||
override fun refreshNominatedMedia(index: Int) {
|
||||
if (mediaDetails != null && !listFragment!!.isVisible()) {
|
||||
removeFragment(mediaDetails!!)
|
||||
mediaDetails = newInstance(false, true)
|
||||
(parentFragment as BookmarkFragment).setScroll(false)
|
||||
setFragment(mediaDetails!!, listFragment)
|
||||
mediaDetails!!.showImage(index)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called on success of API call for featured images or mobile uploads. The
|
||||
* viewpager will notified that number of items have changed.
|
||||
*/
|
||||
override fun viewPagerNotifyDataSetChanged() {
|
||||
if (mediaDetails != null) {
|
||||
mediaDetails!!.notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
fun backPressed(): Boolean {
|
||||
//check mediaDetailPage fragment is not null then we check mediaDetail.is Visible or not to avoid NullPointerException
|
||||
if (mediaDetails != null) {
|
||||
if (mediaDetails!!.isVisible()) {
|
||||
// todo add get list fragment
|
||||
(parentFragment as BookmarkFragment).setupTabLayout()
|
||||
val removed: ArrayList<Int> = mediaDetails!!.removedItems
|
||||
removeFragment(mediaDetails!!)
|
||||
(parentFragment as BookmarkFragment).setScroll(true)
|
||||
setFragment(listFragment!!, mediaDetails)
|
||||
(requireActivity() as MainActivity).showTabs()
|
||||
if (listFragment is BookmarkPicturesFragment) {
|
||||
val adapter = ((listFragment as BookmarkPicturesFragment)
|
||||
.getAdapter() as GridViewAdapter?)
|
||||
val i: MutableIterator<*> = removed.iterator()
|
||||
while (i.hasNext()) {
|
||||
adapter!!.remove(adapter.getItem(i.next() as Int))
|
||||
}
|
||||
mediaDetails!!.clearRemoved()
|
||||
}
|
||||
} else {
|
||||
moveToContributionsFragment()
|
||||
}
|
||||
} else {
|
||||
moveToContributionsFragment()
|
||||
}
|
||||
// notify mediaDetails did not handled the backPressed further actions required.
|
||||
return false
|
||||
}
|
||||
|
||||
fun moveToContributionsFragment() {
|
||||
(requireActivity() as MainActivity).setSelectedItemId(NavTab.CONTRIBUTIONS.code())
|
||||
(requireActivity() as MainActivity).showTabs()
|
||||
}
|
||||
|
||||
override fun onItemClick(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
|
||||
Timber.d("on media clicked")
|
||||
binding!!.exploreContainer.visibility = View.VISIBLE
|
||||
(parentFragment as BookmarkFragment).binding!!.tabLayout.setVisibility(View.GONE)
|
||||
mediaDetails = newInstance(false, true)
|
||||
(parentFragment as BookmarkFragment).setScroll(false)
|
||||
setFragment(mediaDetails!!, listFragment)
|
||||
mediaDetails!!.showImage(position)
|
||||
}
|
||||
|
||||
override fun onBackStackChanged() = Unit
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
binding = null
|
||||
}
|
||||
}
|
||||
|
|
@ -1,94 +0,0 @@
|
|||
package fr.free.nrw.commons.bookmarks;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.widget.ListAdapter;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentPagerAdapter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesFragment;
|
||||
|
||||
public class BookmarksPagerAdapter extends FragmentPagerAdapter {
|
||||
|
||||
private ArrayList<BookmarkPages> pages;
|
||||
|
||||
/**
|
||||
* Default Constructor
|
||||
* @param fm
|
||||
* @param context
|
||||
* @param onlyPictures is true if the fragment requires only BookmarkPictureFragment
|
||||
* (i.e. when no user is logged in).
|
||||
*/
|
||||
BookmarksPagerAdapter(FragmentManager fm, Context context,boolean onlyPictures) {
|
||||
super(fm);
|
||||
pages = new ArrayList<>();
|
||||
Bundle picturesBundle = new Bundle();
|
||||
picturesBundle.putString("categoryName", context.getString(R.string.title_page_bookmarks_pictures));
|
||||
picturesBundle.putInt("order", 0);
|
||||
pages.add(new BookmarkPages(
|
||||
new BookmarkListRootFragment(picturesBundle, this),
|
||||
context.getString(R.string.title_page_bookmarks_pictures)));
|
||||
if (!onlyPictures) {
|
||||
// if onlyPictures is false we also add the location fragment.
|
||||
Bundle locationBundle = new Bundle();
|
||||
locationBundle.putString("categoryName",
|
||||
context.getString(R.string.title_page_bookmarks_locations));
|
||||
locationBundle.putInt("order", 1);
|
||||
pages.add(new BookmarkPages(
|
||||
new BookmarkListRootFragment(locationBundle, this),
|
||||
context.getString(R.string.title_page_bookmarks_locations)));
|
||||
|
||||
locationBundle.putInt("orderItem", 2);
|
||||
pages.add(new BookmarkPages(
|
||||
new BookmarkListRootFragment(locationBundle, this),
|
||||
context.getString(R.string.title_page_bookmarks_items)));
|
||||
}
|
||||
final Bundle categoriesBundle = new Bundle();
|
||||
categoriesBundle.putString("categoryName",
|
||||
context.getString(R.string.title_page_bookmarks_categories));
|
||||
categoriesBundle.putInt("order", 3);
|
||||
pages.add(new BookmarkPages(
|
||||
new BookmarkListRootFragment(categoriesBundle, this),
|
||||
context.getString(R.string.title_page_bookmarks_categories)));
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fragment getItem(int position) {
|
||||
return pages.get(position).getPage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return pages.size();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public CharSequence getPageTitle(int position) {
|
||||
return pages.get(position).getTitle();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Adapter used to display the picture gridview
|
||||
* @return adapter
|
||||
*/
|
||||
public ListAdapter getMediaAdapter() {
|
||||
BookmarkPicturesFragment fragment = (BookmarkPicturesFragment)(((BookmarkListRootFragment)pages.get(0).getPage()).listFragment);
|
||||
return fragment.getAdapter();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the pictures list for the bookmark fragment
|
||||
*/
|
||||
public void requestPictureListUpdate() {
|
||||
BookmarkPicturesFragment fragment = (BookmarkPicturesFragment)(((BookmarkListRootFragment)pages.get(0).getPage()).listFragment);
|
||||
fragment.onResume();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
package fr.free.nrw.commons.bookmarks
|
||||
|
||||
import android.content.Context
|
||||
import android.widget.ListAdapter
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.fragment.app.FragmentPagerAdapter
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesFragment
|
||||
|
||||
class BookmarksPagerAdapter internal constructor(
|
||||
fm: FragmentManager, context: Context, onlyPictures: Boolean
|
||||
) : FragmentPagerAdapter(fm) {
|
||||
private val pages = mutableListOf<BookmarkPages>()
|
||||
|
||||
/**
|
||||
* Default Constructor
|
||||
* @param fm
|
||||
* @param context
|
||||
* @param onlyPictures is true if the fragment requires only BookmarkPictureFragment
|
||||
* (i.e. when no user is logged in).
|
||||
*/
|
||||
init {
|
||||
pages.add(
|
||||
BookmarkPages(
|
||||
BookmarkListRootFragment(
|
||||
bundleOf(
|
||||
"categoryName" to context.getString(R.string.title_page_bookmarks_pictures),
|
||||
"order" to 0
|
||||
), this
|
||||
), context.getString(R.string.title_page_bookmarks_pictures)
|
||||
)
|
||||
)
|
||||
if (!onlyPictures) {
|
||||
// if onlyPictures is false we also add the location fragment.
|
||||
val locationBundle = bundleOf(
|
||||
"categoryName" to context.getString(R.string.title_page_bookmarks_locations),
|
||||
"order" to 1
|
||||
)
|
||||
|
||||
pages.add(
|
||||
BookmarkPages(
|
||||
BookmarkListRootFragment(locationBundle, this),
|
||||
context.getString(R.string.title_page_bookmarks_locations)
|
||||
)
|
||||
)
|
||||
|
||||
locationBundle.putInt("orderItem", 2)
|
||||
pages.add(
|
||||
BookmarkPages(
|
||||
BookmarkListRootFragment(locationBundle, this),
|
||||
context.getString(R.string.title_page_bookmarks_items)
|
||||
)
|
||||
)
|
||||
}
|
||||
pages.add(
|
||||
BookmarkPages(
|
||||
BookmarkListRootFragment(
|
||||
bundleOf(
|
||||
"categoryName" to context.getString(R.string.title_page_bookmarks_categories),
|
||||
"order" to 3
|
||||
), this),
|
||||
context.getString(R.string.title_page_bookmarks_categories)
|
||||
)
|
||||
)
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun getItem(position: Int): Fragment = pages[position].page!!
|
||||
|
||||
override fun getCount(): Int = pages.size
|
||||
|
||||
override fun getPageTitle(position: Int): CharSequence? = pages[position].title
|
||||
|
||||
/**
|
||||
* Return the Adapter used to display the picture gridview
|
||||
* @return adapter
|
||||
*/
|
||||
val mediaAdapter: ListAdapter?
|
||||
get() = (((pages[0].page as BookmarkListRootFragment).listFragment) as BookmarkPicturesFragment).getAdapter()
|
||||
}
|
||||
|
|
@ -1,129 +0,0 @@
|
|||
package fr.free.nrw.commons.bookmarks.items;
|
||||
|
||||
import static fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.COLUMN_ID;
|
||||
import static fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.TABLE_NAME;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteQueryBuilder;
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
import androidx.annotation.NonNull;
|
||||
import fr.free.nrw.commons.BuildConfig;
|
||||
import fr.free.nrw.commons.data.DBOpenHelper;
|
||||
import fr.free.nrw.commons.di.CommonsDaggerContentProvider;
|
||||
import javax.inject.Inject;
|
||||
import timber.log.Timber;
|
||||
|
||||
/**
|
||||
* Handles private storage for bookmarked items
|
||||
*/
|
||||
public class BookmarkItemsContentProvider extends CommonsDaggerContentProvider {
|
||||
|
||||
private static final String BASE_PATH = "bookmarksItems";
|
||||
public static final Uri BASE_URI =
|
||||
Uri.parse("content://" + BuildConfig.BOOKMARK_ITEMS_AUTHORITY + "/" + BASE_PATH);
|
||||
|
||||
|
||||
/**
|
||||
* Append bookmark items ID to the base uri
|
||||
*/
|
||||
public static Uri uriForName(final String id) {
|
||||
return Uri.parse(BASE_URI + "/" + id);
|
||||
}
|
||||
|
||||
@Inject
|
||||
DBOpenHelper dbOpenHelper;
|
||||
|
||||
@Override
|
||||
public String getType(@NonNull final Uri uri) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries the SQLite database for the bookmark items
|
||||
* @param uri : contains the uri for bookmark items
|
||||
* @param projection : contains the all fields of the table
|
||||
* @param selection : handles Where
|
||||
* @param selectionArgs : the condition of Where clause
|
||||
* @param sortOrder : ascending or descending
|
||||
*/
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Override
|
||||
public Cursor query(@NonNull final Uri uri, final String[] projection, final String selection,
|
||||
final String[] selectionArgs, final String sortOrder) {
|
||||
final SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
|
||||
queryBuilder.setTables(TABLE_NAME);
|
||||
final SQLiteDatabase db = dbOpenHelper.getReadableDatabase();
|
||||
final Cursor cursor = queryBuilder.query(db, projection, selection,
|
||||
selectionArgs, null, null, sortOrder);
|
||||
cursor.setNotificationUri(getContext().getContentResolver(), uri);
|
||||
return cursor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the update query of local SQLite Database
|
||||
* @param uri : contains the uri for bookmark items
|
||||
* @param contentValues : new values to be entered to db
|
||||
* @param selection : handles Where
|
||||
* @param selectionArgs : the condition of Where clause
|
||||
*/
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Override
|
||||
public int update(@NonNull final Uri uri, final ContentValues contentValues,
|
||||
final String selection, final String[] selectionArgs) {
|
||||
final SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase();
|
||||
final int rowsUpdated;
|
||||
if (TextUtils.isEmpty(selection)) {
|
||||
final int id = Integer.parseInt(uri.getLastPathSegment());
|
||||
rowsUpdated = sqlDB.update(TABLE_NAME,
|
||||
contentValues,
|
||||
COLUMN_ID + " = ?",
|
||||
new String[]{String.valueOf(id)});
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"Parameter `selection` should be empty when updating an ID");
|
||||
}
|
||||
|
||||
getContext().getContentResolver().notifyChange(uri, null);
|
||||
return rowsUpdated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the insertion of new bookmark items record to local SQLite Database
|
||||
* @param uri
|
||||
* @param contentValues
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Override
|
||||
public Uri insert(@NonNull final Uri uri, final ContentValues contentValues) {
|
||||
final SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase();
|
||||
final long id = sqlDB.insert(TABLE_NAME, null, contentValues);
|
||||
getContext().getContentResolver().notifyChange(uri, null);
|
||||
return Uri.parse(BASE_URI + "/" + id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the deletion of new bookmark items record to local SQLite Database
|
||||
* @param uri
|
||||
* @param s
|
||||
* @param strings
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Override
|
||||
public int delete(@NonNull final Uri uri, final String s, final String[] strings) {
|
||||
final int rows;
|
||||
final SQLiteDatabase db = dbOpenHelper.getReadableDatabase();
|
||||
Timber.d("Deleting bookmark name %s", uri.getLastPathSegment());
|
||||
rows = db.delete(
|
||||
TABLE_NAME,
|
||||
"item_id = ?",
|
||||
new String[]{uri.getLastPathSegment()}
|
||||
);
|
||||
getContext().getContentResolver().notifyChange(uri, null);
|
||||
return rows;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
package fr.free.nrw.commons.bookmarks.items
|
||||
|
||||
import android.content.ContentValues
|
||||
import android.database.Cursor
|
||||
import android.database.sqlite.SQLiteQueryBuilder
|
||||
import android.net.Uri
|
||||
import fr.free.nrw.commons.BuildConfig
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.TABLE_NAME
|
||||
import fr.free.nrw.commons.di.CommonsDaggerContentProvider
|
||||
import androidx.core.net.toUri
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.COLUMN_ID
|
||||
|
||||
/**
|
||||
* Handles private storage for bookmarked items
|
||||
*/
|
||||
class BookmarkItemsContentProvider : CommonsDaggerContentProvider() {
|
||||
override fun getType(uri: Uri): String? = null
|
||||
|
||||
/**
|
||||
* Queries the SQLite database for the bookmark items
|
||||
* @param uri : contains the uri for bookmark items
|
||||
* @param projection : contains the all fields of the table
|
||||
* @param selection : handles Where
|
||||
* @param selectionArgs : the condition of Where clause
|
||||
* @param sortOrder : ascending or descending
|
||||
*/
|
||||
override fun query(
|
||||
uri: Uri, projection: Array<String>?, selection: String?,
|
||||
selectionArgs: Array<String>?, sortOrder: String?
|
||||
): Cursor {
|
||||
val queryBuilder = SQLiteQueryBuilder().apply {
|
||||
tables = TABLE_NAME
|
||||
}
|
||||
|
||||
return queryBuilder.query(
|
||||
requireDb(), projection, selection,
|
||||
selectionArgs, null, null, sortOrder
|
||||
).apply {
|
||||
setNotificationUri(requireContext().contentResolver, uri)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the update query of local SQLite Database
|
||||
* @param uri : contains the uri for bookmark items
|
||||
* @param contentValues : new values to be entered to db
|
||||
* @param selection : handles Where
|
||||
* @param selectionArgs : the condition of Where clause
|
||||
*/
|
||||
override fun update(
|
||||
uri: Uri, contentValues: ContentValues?,
|
||||
selection: String?, selectionArgs: Array<String>?
|
||||
): Int {
|
||||
val rowsUpdated: Int
|
||||
if (selection.isNullOrEmpty()) {
|
||||
val id = uri.lastPathSegment!!.toInt()
|
||||
rowsUpdated = requireDb().update(
|
||||
TABLE_NAME,
|
||||
contentValues,
|
||||
"$COLUMN_ID = ?",
|
||||
arrayOf(id.toString())
|
||||
)
|
||||
} else {
|
||||
throw IllegalArgumentException(
|
||||
"Parameter `selection` should be empty when updating an ID"
|
||||
)
|
||||
}
|
||||
|
||||
requireContext().contentResolver.notifyChange(uri, null)
|
||||
return rowsUpdated
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the insertion of new bookmark items record to local SQLite Database
|
||||
*/
|
||||
override fun insert(uri: Uri, contentValues: ContentValues?): Uri? {
|
||||
val id = requireDb().insert(TABLE_NAME, null, contentValues)
|
||||
requireContext().contentResolver.notifyChange(uri, null)
|
||||
return "$BASE_URI/$id".toUri()
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handles the deletion of new bookmark items record to local SQLite Database
|
||||
*/
|
||||
override fun delete(uri: Uri, s: String?, strings: Array<String>?): Int {
|
||||
val rows: Int = requireDb().delete(
|
||||
TABLE_NAME,
|
||||
"$COLUMN_ID = ?",
|
||||
arrayOf(uri.lastPathSegment)
|
||||
)
|
||||
requireContext().contentResolver.notifyChange(uri, null)
|
||||
return rows
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val BASE_PATH = "bookmarksItems"
|
||||
val BASE_URI: Uri = "content://${BuildConfig.BOOKMARK_ITEMS_AUTHORITY}/$BASE_PATH".toUri()
|
||||
fun uriForName(id: String) = "$BASE_URI/$id".toUri()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
package fr.free.nrw.commons.bookmarks.items;
|
||||
|
||||
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem;
|
||||
import java.util.List;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
/**
|
||||
* Handles loading bookmarked items from Database
|
||||
*/
|
||||
@Singleton
|
||||
public class BookmarkItemsController {
|
||||
|
||||
@Inject
|
||||
BookmarkItemsDao bookmarkItemsDao;
|
||||
|
||||
@Inject
|
||||
public BookmarkItemsController() {}
|
||||
|
||||
/**
|
||||
* Load from DB the bookmarked items
|
||||
* @return a list of DepictedItem objects.
|
||||
*/
|
||||
public List<DepictedItem> loadFavoritesItems() {
|
||||
return bookmarkItemsDao.getAllBookmarksItems();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package fr.free.nrw.commons.bookmarks.items
|
||||
|
||||
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
/**
|
||||
* Handles loading bookmarked items from Database
|
||||
*/
|
||||
@Singleton
|
||||
class BookmarkItemsController @Inject constructor() {
|
||||
@JvmField
|
||||
@Inject
|
||||
var bookmarkItemsDao: BookmarkItemsDao? = null
|
||||
|
||||
/**
|
||||
* Load from DB the bookmarked items
|
||||
* @return a list of DepictedItem objects.
|
||||
*/
|
||||
fun loadFavoritesItems(): List<DepictedItem> {
|
||||
return bookmarkItemsDao?.getAllBookmarksItems() ?: emptyList()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,329 +0,0 @@
|
|||
package fr.free.nrw.commons.bookmarks.items;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.ContentProviderClient;
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.os.RemoteException;
|
||||
import fr.free.nrw.commons.category.CategoryItem;
|
||||
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Provider;
|
||||
import javax.inject.Singleton;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
/**
|
||||
* Handles database operations for bookmarked items
|
||||
*/
|
||||
@Singleton
|
||||
public class BookmarkItemsDao {
|
||||
|
||||
private final Provider<ContentProviderClient> clientProvider;
|
||||
|
||||
@Inject
|
||||
public BookmarkItemsDao(
|
||||
@Named("bookmarksItem") final Provider<ContentProviderClient> clientProvider) {
|
||||
this.clientProvider = clientProvider;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find all persisted items bookmarks on database
|
||||
* @return list of bookmarks
|
||||
*/
|
||||
public List<DepictedItem> getAllBookmarksItems() {
|
||||
final List<DepictedItem> items = new ArrayList<>();
|
||||
final ContentProviderClient db = clientProvider.get();
|
||||
try (final Cursor cursor = db.query(
|
||||
BookmarkItemsContentProvider.BASE_URI,
|
||||
Table.ALL_FIELDS,
|
||||
null,
|
||||
new String[]{},
|
||||
null)) {
|
||||
while (cursor != null && cursor.moveToNext()) {
|
||||
items.add(fromCursor(cursor));
|
||||
}
|
||||
} catch (final RemoteException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
db.release();
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Look for a bookmark in database and in order to insert or delete it
|
||||
* @param depictedItem : Bookmark object
|
||||
* @return boolean : is bookmark now favorite ?
|
||||
*/
|
||||
public boolean updateBookmarkItem(final DepictedItem depictedItem) {
|
||||
final boolean bookmarkExists = findBookmarkItem(depictedItem.getId());
|
||||
if (bookmarkExists) {
|
||||
deleteBookmarkItem(depictedItem);
|
||||
} else {
|
||||
addBookmarkItem(depictedItem);
|
||||
}
|
||||
return !bookmarkExists;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a Bookmark to database
|
||||
* @param depictedItem : Bookmark to add
|
||||
*/
|
||||
private void addBookmarkItem(final DepictedItem depictedItem) {
|
||||
final ContentProviderClient db = clientProvider.get();
|
||||
try {
|
||||
db.insert(BookmarkItemsContentProvider.BASE_URI, toContentValues(depictedItem));
|
||||
} catch (final RemoteException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
db.release();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a bookmark from database
|
||||
* @param depictedItem : Bookmark to delete
|
||||
*/
|
||||
private void deleteBookmarkItem(final DepictedItem depictedItem) {
|
||||
final ContentProviderClient db = clientProvider.get();
|
||||
try {
|
||||
db.delete(BookmarkItemsContentProvider.uriForName(depictedItem.getId()), null, null);
|
||||
} catch (final RemoteException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
db.release();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a bookmark from database based on its name
|
||||
* @param depictedItemID : Bookmark to find
|
||||
* @return boolean : is bookmark in database ?
|
||||
*/
|
||||
public boolean findBookmarkItem(final String depictedItemID) {
|
||||
if (depictedItemID == null) { //Avoiding NPE's
|
||||
return false;
|
||||
}
|
||||
final ContentProviderClient db = clientProvider.get();
|
||||
try (final Cursor cursor = db.query(
|
||||
BookmarkItemsContentProvider.BASE_URI,
|
||||
Table.ALL_FIELDS,
|
||||
Table.COLUMN_ID + "=?",
|
||||
new String[]{depictedItemID},
|
||||
null
|
||||
)) {
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
return true;
|
||||
}
|
||||
} catch (final RemoteException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
db.release();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recives real data from cursor
|
||||
* @param cursor : Object for storing database data
|
||||
* @return DepictedItem
|
||||
*/
|
||||
@SuppressLint("Range")
|
||||
DepictedItem fromCursor(final Cursor cursor) {
|
||||
final String fileName = cursor.getString(cursor.getColumnIndex(Table.COLUMN_NAME));
|
||||
final String description
|
||||
= cursor.getString(cursor.getColumnIndex(Table.COLUMN_DESCRIPTION));
|
||||
final String imageUrl = cursor.getString(cursor.getColumnIndex(Table.COLUMN_IMAGE));
|
||||
final String instanceListString
|
||||
= cursor.getString(cursor.getColumnIndex(Table.COLUMN_INSTANCE_LIST));
|
||||
final List<String> instanceList = StringToArray(instanceListString);
|
||||
final String categoryNameListString = cursor.getString(cursor
|
||||
.getColumnIndex(Table.COLUMN_CATEGORIES_NAME_LIST));
|
||||
final List<String> categoryNameList = StringToArray(categoryNameListString);
|
||||
final String categoryDescriptionListString = cursor.getString(cursor
|
||||
.getColumnIndex(Table.COLUMN_CATEGORIES_DESCRIPTION_LIST));
|
||||
final List<String> categoryDescriptionList = StringToArray(categoryDescriptionListString);
|
||||
final String categoryThumbnailListString = cursor.getString(cursor
|
||||
.getColumnIndex(Table.COLUMN_CATEGORIES_THUMBNAIL_LIST));
|
||||
final List<String> categoryThumbnailList = StringToArray(categoryThumbnailListString);
|
||||
final List<CategoryItem> categoryList = convertToCategoryItems(categoryNameList,
|
||||
categoryDescriptionList, categoryThumbnailList);
|
||||
final boolean isSelected
|
||||
= Boolean.parseBoolean(cursor.getString(cursor
|
||||
.getColumnIndex(Table.COLUMN_IS_SELECTED)));
|
||||
final String id = cursor.getString(cursor.getColumnIndex(Table.COLUMN_ID));
|
||||
|
||||
return new DepictedItem(
|
||||
fileName,
|
||||
description,
|
||||
imageUrl,
|
||||
instanceList,
|
||||
categoryList,
|
||||
isSelected,
|
||||
id
|
||||
);
|
||||
}
|
||||
|
||||
private List<CategoryItem> convertToCategoryItems(List<String> categoryNameList,
|
||||
List<String> categoryDescriptionList, List<String> categoryThumbnailList) {
|
||||
List<CategoryItem> categoryItems = new ArrayList<>();
|
||||
for(int i=0; i<categoryNameList.size(); i++){
|
||||
categoryItems.add(new CategoryItem(categoryNameList.get(i),
|
||||
categoryDescriptionList.get(i),
|
||||
categoryThumbnailList.get(i), false));
|
||||
}
|
||||
return categoryItems;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts string to List
|
||||
* @param listString comma separated single string from of list items
|
||||
* @return List of string
|
||||
*/
|
||||
private List<String> StringToArray(final String listString) {
|
||||
final String[] elements = listString.split(",");
|
||||
return Arrays.asList(elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts string to List
|
||||
* @param list list of items
|
||||
* @return string comma separated single string of items
|
||||
*/
|
||||
private String ArrayToString(final List<String> list) {
|
||||
if (list != null) {
|
||||
return StringUtils.join(list, ',');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes data from DepictedItem and create a content value object
|
||||
* @param depictedItem depicted item
|
||||
* @return ContentValues
|
||||
*/
|
||||
private ContentValues toContentValues(final DepictedItem depictedItem) {
|
||||
|
||||
final List<String> namesOfCommonsCategories = new ArrayList<>();
|
||||
for (final CategoryItem category :
|
||||
depictedItem.getCommonsCategories()) {
|
||||
namesOfCommonsCategories.add(category.getName());
|
||||
}
|
||||
|
||||
final List<String> descriptionsOfCommonsCategories = new ArrayList<>();
|
||||
for (final CategoryItem category :
|
||||
depictedItem.getCommonsCategories()) {
|
||||
descriptionsOfCommonsCategories.add(category.getDescription());
|
||||
}
|
||||
|
||||
final List<String> thumbnailsOfCommonsCategories = new ArrayList<>();
|
||||
for (final CategoryItem category :
|
||||
depictedItem.getCommonsCategories()) {
|
||||
thumbnailsOfCommonsCategories.add(category.getThumbnail());
|
||||
}
|
||||
|
||||
final ContentValues cv = new ContentValues();
|
||||
cv.put(Table.COLUMN_NAME, depictedItem.getName());
|
||||
cv.put(Table.COLUMN_DESCRIPTION, depictedItem.getDescription());
|
||||
cv.put(Table.COLUMN_IMAGE, depictedItem.getImageUrl());
|
||||
cv.put(Table.COLUMN_INSTANCE_LIST, ArrayToString(depictedItem.getInstanceOfs()));
|
||||
cv.put(Table.COLUMN_CATEGORIES_NAME_LIST, ArrayToString(namesOfCommonsCategories));
|
||||
cv.put(Table.COLUMN_CATEGORIES_DESCRIPTION_LIST,
|
||||
ArrayToString(descriptionsOfCommonsCategories));
|
||||
cv.put(Table.COLUMN_CATEGORIES_THUMBNAIL_LIST,
|
||||
ArrayToString(thumbnailsOfCommonsCategories));
|
||||
cv.put(Table.COLUMN_IS_SELECTED, depictedItem.isSelected());
|
||||
cv.put(Table.COLUMN_ID, depictedItem.getId());
|
||||
return cv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Table of bookmarksItems data
|
||||
*/
|
||||
public static final class Table {
|
||||
public static final String TABLE_NAME = "bookmarksItems";
|
||||
public static final String COLUMN_NAME = "item_name";
|
||||
public static final String COLUMN_DESCRIPTION = "item_description";
|
||||
public static final String COLUMN_IMAGE = "item_image_url";
|
||||
public static final String COLUMN_INSTANCE_LIST = "item_instance_of";
|
||||
public static final String COLUMN_CATEGORIES_NAME_LIST = "item_name_categories";
|
||||
public static final String COLUMN_CATEGORIES_DESCRIPTION_LIST = "item_description_categories";
|
||||
public static final String COLUMN_CATEGORIES_THUMBNAIL_LIST = "item_thumbnail_categories";
|
||||
public static final String COLUMN_IS_SELECTED = "item_is_selected";
|
||||
public static final String COLUMN_ID = "item_id";
|
||||
|
||||
public static final String[] ALL_FIELDS = {
|
||||
COLUMN_NAME,
|
||||
COLUMN_DESCRIPTION,
|
||||
COLUMN_IMAGE,
|
||||
COLUMN_INSTANCE_LIST,
|
||||
COLUMN_CATEGORIES_NAME_LIST,
|
||||
COLUMN_CATEGORIES_DESCRIPTION_LIST,
|
||||
COLUMN_CATEGORIES_THUMBNAIL_LIST,
|
||||
COLUMN_IS_SELECTED,
|
||||
COLUMN_ID
|
||||
};
|
||||
|
||||
static final String DROP_TABLE_STATEMENT = "DROP TABLE IF EXISTS " + TABLE_NAME;
|
||||
static final String CREATE_TABLE_STATEMENT = "CREATE TABLE " + TABLE_NAME + " ("
|
||||
+ COLUMN_NAME + " STRING,"
|
||||
+ COLUMN_DESCRIPTION + " STRING,"
|
||||
+ COLUMN_IMAGE + " STRING,"
|
||||
+ COLUMN_INSTANCE_LIST + " STRING,"
|
||||
+ COLUMN_CATEGORIES_NAME_LIST + " STRING,"
|
||||
+ COLUMN_CATEGORIES_DESCRIPTION_LIST + " STRING,"
|
||||
+ COLUMN_CATEGORIES_THUMBNAIL_LIST + " STRING,"
|
||||
+ COLUMN_IS_SELECTED + " STRING,"
|
||||
+ COLUMN_ID + " STRING PRIMARY KEY"
|
||||
+ ");";
|
||||
|
||||
/**
|
||||
* Creates table
|
||||
* @param db SQLiteDatabase
|
||||
*/
|
||||
public static void onCreate(final SQLiteDatabase db) {
|
||||
db.execSQL(CREATE_TABLE_STATEMENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes database
|
||||
* @param db SQLiteDatabase
|
||||
*/
|
||||
public static void onDelete(final SQLiteDatabase db) {
|
||||
db.execSQL(DROP_TABLE_STATEMENT);
|
||||
onCreate(db);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates database
|
||||
* @param db SQLiteDatabase
|
||||
* @param from starting
|
||||
* @param to end
|
||||
*/
|
||||
public static void onUpdate(final SQLiteDatabase db, int from, final int to) {
|
||||
if (from == to) {
|
||||
return;
|
||||
}
|
||||
if (from < 18) {
|
||||
// doesn't exist yet
|
||||
from++;
|
||||
onUpdate(db, from, to);
|
||||
return;
|
||||
}
|
||||
|
||||
if (from == 18) {
|
||||
// table added in version 19
|
||||
onCreate(db);
|
||||
from++;
|
||||
onUpdate(db, from, to);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,199 @@
|
|||
package fr.free.nrw.commons.bookmarks.items
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.ContentProviderClient
|
||||
import android.content.ContentValues
|
||||
import android.database.Cursor
|
||||
import android.os.RemoteException
|
||||
import androidx.core.content.contentValuesOf
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsContentProvider.Companion.BASE_URI
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsContentProvider.Companion.uriForName
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.COLUMN_CATEGORIES_DESCRIPTION_LIST
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.COLUMN_CATEGORIES_NAME_LIST
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.COLUMN_CATEGORIES_THUMBNAIL_LIST
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.COLUMN_DESCRIPTION
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.COLUMN_ID
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.COLUMN_IMAGE
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.COLUMN_INSTANCE_LIST
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.COLUMN_IS_SELECTED
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.COLUMN_NAME
|
||||
import fr.free.nrw.commons.category.CategoryItem
|
||||
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem
|
||||
import fr.free.nrw.commons.utils.arrayToString
|
||||
import fr.free.nrw.commons.utils.getString
|
||||
import fr.free.nrw.commons.utils.getStringArray
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Named
|
||||
import javax.inject.Provider
|
||||
import javax.inject.Singleton
|
||||
|
||||
/**
|
||||
* Handles database operations for bookmarked items
|
||||
*/
|
||||
@Singleton
|
||||
class BookmarkItemsDao @Inject constructor(
|
||||
@param:Named("bookmarksItem") private val clientProvider: Provider<ContentProviderClient>
|
||||
) {
|
||||
/**
|
||||
* Find all persisted items bookmarks on database
|
||||
* @return list of bookmarks
|
||||
*/
|
||||
fun getAllBookmarksItems(): List<DepictedItem> {
|
||||
val items: MutableList<DepictedItem> = mutableListOf()
|
||||
val db = clientProvider.get()
|
||||
try {
|
||||
db.query(
|
||||
BASE_URI,
|
||||
BookmarkItemsTable.ALL_FIELDS,
|
||||
null,
|
||||
arrayOf(),
|
||||
null
|
||||
).use { cursor ->
|
||||
while (cursor != null && cursor.moveToNext()) {
|
||||
items.add(fromCursor(cursor))
|
||||
}
|
||||
}
|
||||
} catch (e: RemoteException) {
|
||||
throw RuntimeException(e)
|
||||
} finally {
|
||||
db.release()
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Look for a bookmark in database and in order to insert or delete it
|
||||
* @param depictedItem : Bookmark object
|
||||
* @return boolean : is bookmark now favorite ?
|
||||
*/
|
||||
fun updateBookmarkItem(depictedItem: DepictedItem): Boolean {
|
||||
val bookmarkExists = findBookmarkItem(depictedItem.id)
|
||||
if (bookmarkExists) {
|
||||
deleteBookmarkItem(depictedItem)
|
||||
} else {
|
||||
addBookmarkItem(depictedItem)
|
||||
}
|
||||
return !bookmarkExists
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a Bookmark to database
|
||||
* @param depictedItem : Bookmark to add
|
||||
*/
|
||||
private fun addBookmarkItem(depictedItem: DepictedItem) {
|
||||
val db = clientProvider.get()
|
||||
try {
|
||||
db.insert(BASE_URI, toContentValues(depictedItem))
|
||||
} catch (e: RemoteException) {
|
||||
throw RuntimeException(e)
|
||||
} finally {
|
||||
db.release()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a bookmark from database
|
||||
* @param depictedItem : Bookmark to delete
|
||||
*/
|
||||
private fun deleteBookmarkItem(depictedItem: DepictedItem) {
|
||||
val db = clientProvider.get()
|
||||
try {
|
||||
db.delete(uriForName(depictedItem.id), null, null)
|
||||
} catch (e: RemoteException) {
|
||||
throw RuntimeException(e)
|
||||
} finally {
|
||||
db.release()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a bookmark from database based on its name
|
||||
* @param depictedItemID : Bookmark to find
|
||||
* @return boolean : is bookmark in database ?
|
||||
*/
|
||||
fun findBookmarkItem(depictedItemID: String?): Boolean {
|
||||
if (depictedItemID == null) { //Avoiding NPE's
|
||||
return false
|
||||
}
|
||||
val db = clientProvider.get()
|
||||
try {
|
||||
db.query(
|
||||
BASE_URI,
|
||||
BookmarkItemsTable.ALL_FIELDS,
|
||||
COLUMN_ID + "=?",
|
||||
arrayOf(depictedItemID),
|
||||
null
|
||||
).use { cursor ->
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
} catch (e: RemoteException) {
|
||||
throw RuntimeException(e)
|
||||
} finally {
|
||||
db.release()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Recives real data from cursor
|
||||
* @param cursor : Object for storing database data
|
||||
* @return DepictedItem
|
||||
*/
|
||||
@SuppressLint("Range")
|
||||
fun fromCursor(cursor: Cursor) = with(cursor) {
|
||||
DepictedItem(
|
||||
getString(COLUMN_NAME),
|
||||
getString(COLUMN_DESCRIPTION),
|
||||
getString(COLUMN_IMAGE),
|
||||
getStringArray(COLUMN_INSTANCE_LIST),
|
||||
convertToCategoryItems(
|
||||
getStringArray(COLUMN_CATEGORIES_NAME_LIST),
|
||||
getStringArray(COLUMN_CATEGORIES_DESCRIPTION_LIST),
|
||||
getStringArray(COLUMN_CATEGORIES_THUMBNAIL_LIST)
|
||||
),
|
||||
getString(COLUMN_IS_SELECTED).toBoolean(),
|
||||
getString(COLUMN_ID)
|
||||
)
|
||||
}
|
||||
|
||||
private fun convertToCategoryItems(
|
||||
categoryNameList: List<String>,
|
||||
categoryDescriptionList: List<String>,
|
||||
categoryThumbnailList: List<String>
|
||||
): List<CategoryItem> {
|
||||
return buildList {
|
||||
for (i in categoryNameList.indices) {
|
||||
add(
|
||||
CategoryItem(
|
||||
categoryNameList[i],
|
||||
categoryDescriptionList[i],
|
||||
categoryThumbnailList[i],
|
||||
false
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes data from DepictedItem and create a content value object
|
||||
* @param depictedItem depicted item
|
||||
* @return ContentValues
|
||||
*/
|
||||
private fun toContentValues(depictedItem: DepictedItem): ContentValues {
|
||||
return contentValuesOf(
|
||||
COLUMN_NAME to depictedItem.name,
|
||||
COLUMN_DESCRIPTION to depictedItem.description,
|
||||
COLUMN_IMAGE to depictedItem.imageUrl,
|
||||
COLUMN_INSTANCE_LIST to arrayToString(depictedItem.instanceOfs),
|
||||
COLUMN_CATEGORIES_NAME_LIST to arrayToString(depictedItem.commonsCategories.map { it.name }),
|
||||
COLUMN_CATEGORIES_DESCRIPTION_LIST to arrayToString(depictedItem.commonsCategories.map { it.description }),
|
||||
COLUMN_CATEGORIES_THUMBNAIL_LIST to arrayToString(depictedItem.commonsCategories.map { it.thumbnail }),
|
||||
COLUMN_IS_SELECTED to depictedItem.isSelected,
|
||||
COLUMN_ID to depictedItem.id,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
package fr.free.nrw.commons.bookmarks.items;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import dagger.android.support.DaggerFragment;
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.databinding.FragmentBookmarksItemsBinding;
|
||||
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem;
|
||||
import java.util.List;
|
||||
import javax.inject.Inject;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Tab fragment to show list of bookmarked Wikidata Items
|
||||
*/
|
||||
public class BookmarkItemsFragment extends DaggerFragment {
|
||||
|
||||
private FragmentBookmarksItemsBinding binding;
|
||||
|
||||
@Inject
|
||||
BookmarkItemsController controller;
|
||||
|
||||
public static BookmarkItemsFragment newInstance() {
|
||||
return new BookmarkItemsFragment();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(
|
||||
@NonNull final LayoutInflater inflater,
|
||||
final ViewGroup container,
|
||||
final Bundle savedInstanceState
|
||||
) {
|
||||
binding = FragmentBookmarksItemsBinding.inflate(inflater, container, false);
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(final @NotNull View view, @Nullable final Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
initList(requireContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
initList(requireContext());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of DepictedItem and sets to the adapter
|
||||
* @param context context
|
||||
*/
|
||||
private void initList(final Context context) {
|
||||
final List<DepictedItem> depictItems = controller.loadFavoritesItems();
|
||||
final BookmarkItemsAdapter adapter = new BookmarkItemsAdapter(depictItems, context);
|
||||
binding.listView.setAdapter(adapter);
|
||||
binding.loadingImagesProgressBar.setVisibility(View.GONE);
|
||||
if (depictItems.isEmpty()) {
|
||||
binding.statusMessage.setText(R.string.bookmark_empty);
|
||||
binding.statusMessage.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
binding.statusMessage.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
binding = null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
package fr.free.nrw.commons.bookmarks.items
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import dagger.android.support.DaggerFragment
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.databinding.FragmentBookmarksItemsBinding
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Tab fragment to show list of bookmarked Wikidata Items
|
||||
*/
|
||||
class BookmarkItemsFragment : DaggerFragment() {
|
||||
private var binding: FragmentBookmarksItemsBinding? = null
|
||||
|
||||
@JvmField
|
||||
@Inject
|
||||
var controller: BookmarkItemsController? = null
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
binding = FragmentBookmarksItemsBinding.inflate(inflater, container, false)
|
||||
return binding!!.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
initList(requireContext())
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
initList(requireContext())
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of DepictedItem and sets to the adapter
|
||||
* @param context context
|
||||
*/
|
||||
private fun initList(context: Context) {
|
||||
val depictItems = controller!!.loadFavoritesItems()
|
||||
binding!!.listView.adapter = BookmarkItemsAdapter(depictItems, context)
|
||||
binding!!.loadingImagesProgressBar.visibility = View.GONE
|
||||
if (depictItems.isEmpty()) {
|
||||
binding!!.statusMessage.setText(R.string.bookmark_empty)
|
||||
binding!!.statusMessage.visibility = View.VISIBLE
|
||||
} else {
|
||||
binding!!.statusMessage.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
binding = null
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
package fr.free.nrw.commons.bookmarks.items
|
||||
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
|
||||
/**
|
||||
* Table of bookmarksItems data
|
||||
*/
|
||||
object BookmarkItemsTable {
|
||||
const val TABLE_NAME = "bookmarksItems"
|
||||
const val COLUMN_NAME = "item_name"
|
||||
const val COLUMN_DESCRIPTION = "item_description"
|
||||
const val COLUMN_IMAGE = "item_image_url"
|
||||
const val COLUMN_INSTANCE_LIST = "item_instance_of"
|
||||
const val COLUMN_CATEGORIES_NAME_LIST = "item_name_categories"
|
||||
const val COLUMN_CATEGORIES_DESCRIPTION_LIST = "item_description_categories"
|
||||
const val COLUMN_CATEGORIES_THUMBNAIL_LIST = "item_thumbnail_categories"
|
||||
const val COLUMN_IS_SELECTED = "item_is_selected"
|
||||
const val COLUMN_ID = "item_id"
|
||||
|
||||
val ALL_FIELDS = arrayOf(
|
||||
COLUMN_NAME,
|
||||
COLUMN_DESCRIPTION,
|
||||
COLUMN_IMAGE,
|
||||
COLUMN_INSTANCE_LIST,
|
||||
COLUMN_CATEGORIES_NAME_LIST,
|
||||
COLUMN_CATEGORIES_DESCRIPTION_LIST,
|
||||
COLUMN_CATEGORIES_THUMBNAIL_LIST,
|
||||
COLUMN_IS_SELECTED,
|
||||
COLUMN_ID
|
||||
)
|
||||
|
||||
const val DROP_TABLE_STATEMENT = "DROP TABLE IF EXISTS $TABLE_NAME"
|
||||
|
||||
val CREATE_TABLE_STATEMENT =
|
||||
"""CREATE TABLE $TABLE_NAME (
|
||||
$COLUMN_NAME STRING,
|
||||
$COLUMN_DESCRIPTION STRING,
|
||||
$COLUMN_IMAGE STRING,
|
||||
$COLUMN_INSTANCE_LIST STRING,
|
||||
$COLUMN_CATEGORIES_NAME_LIST STRING,
|
||||
$COLUMN_CATEGORIES_DESCRIPTION_LIST STRING,
|
||||
$COLUMN_CATEGORIES_THUMBNAIL_LIST STRING,
|
||||
$COLUMN_IS_SELECTED STRING,
|
||||
$COLUMN_ID STRING PRIMARY KEY
|
||||
);""".trimIndent()
|
||||
|
||||
/**
|
||||
* Creates table
|
||||
*
|
||||
* @param db SQLiteDatabase
|
||||
*/
|
||||
fun onCreate(db: SQLiteDatabase) {
|
||||
db.execSQL(CREATE_TABLE_STATEMENT)
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes database
|
||||
*
|
||||
* @param db SQLiteDatabase
|
||||
*/
|
||||
fun onDelete(db: SQLiteDatabase) {
|
||||
db.execSQL(DROP_TABLE_STATEMENT)
|
||||
onCreate(db)
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates database
|
||||
*
|
||||
* @param db SQLiteDatabase
|
||||
* @param from starting
|
||||
* @param to end
|
||||
*/
|
||||
fun onUpdate(db: SQLiteDatabase, from: Int, to: Int) {
|
||||
if (from == to) {
|
||||
return
|
||||
}
|
||||
|
||||
if (from < 18) {
|
||||
// doesn't exist yet
|
||||
onUpdate(db, from + 1, to)
|
||||
return
|
||||
}
|
||||
|
||||
if (from == 18) {
|
||||
// table added in version 19
|
||||
onCreate(db)
|
||||
onUpdate(db, from + 1, to)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,120 +0,0 @@
|
|||
package fr.free.nrw.commons.bookmarks.pictures;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteQueryBuilder;
|
||||
// We can get uri using java.Net.Uri, but andoid implimentation is faster (but it's forgiving with handling exceptions though)
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import fr.free.nrw.commons.BuildConfig;
|
||||
import fr.free.nrw.commons.data.DBOpenHelper;
|
||||
import fr.free.nrw.commons.di.CommonsDaggerContentProvider;
|
||||
import timber.log.Timber;
|
||||
|
||||
import static fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao.Table.COLUMN_MEDIA_NAME;
|
||||
import static fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao.Table.TABLE_NAME;
|
||||
|
||||
/**
|
||||
* Handles private storage for Bookmark pictures
|
||||
*/
|
||||
public class BookmarkPicturesContentProvider extends CommonsDaggerContentProvider {
|
||||
|
||||
private static final String BASE_PATH = "bookmarks";
|
||||
public static final Uri BASE_URI = Uri.parse("content://" + BuildConfig.BOOKMARK_AUTHORITY + "/" + BASE_PATH);
|
||||
|
||||
/**
|
||||
* Append bookmark pictures name to the base uri
|
||||
*/
|
||||
public static Uri uriForName(String name) {
|
||||
return Uri.parse(BASE_URI.toString() + "/" + name);
|
||||
}
|
||||
|
||||
@Inject
|
||||
DBOpenHelper dbOpenHelper;
|
||||
|
||||
@Override
|
||||
public String getType(@NonNull Uri uri) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries the SQLite database for the bookmark pictures
|
||||
* @param uri : contains the uri for bookmark pictures
|
||||
* @param projection
|
||||
* @param selection : handles Where
|
||||
* @param selectionArgs : the condition of Where clause
|
||||
* @param sortOrder : ascending or descending
|
||||
*/
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Override
|
||||
public Cursor query(@NonNull Uri uri, String[] projection, String selection,
|
||||
String[] selectionArgs, String sortOrder) {
|
||||
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
|
||||
queryBuilder.setTables(TABLE_NAME);
|
||||
|
||||
SQLiteDatabase db = dbOpenHelper.getReadableDatabase();
|
||||
Cursor cursor = queryBuilder.query(db, projection, selection, selectionArgs, null, null, sortOrder);
|
||||
cursor.setNotificationUri(getContext().getContentResolver(), uri);
|
||||
|
||||
return cursor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the update query of local SQLite Database
|
||||
* @param uri : contains the uri for bookmark pictures
|
||||
* @param contentValues : new values to be entered to db
|
||||
* @param selection : handles Where
|
||||
* @param selectionArgs : the condition of Where clause
|
||||
*/
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Override
|
||||
public int update(@NonNull Uri uri, ContentValues contentValues, String selection,
|
||||
String[] selectionArgs) {
|
||||
SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase();
|
||||
int rowsUpdated;
|
||||
if (TextUtils.isEmpty(selection)) {
|
||||
int id = Integer.valueOf(uri.getLastPathSegment());
|
||||
rowsUpdated = sqlDB.update(TABLE_NAME,
|
||||
contentValues,
|
||||
COLUMN_MEDIA_NAME + " = ?",
|
||||
new String[]{String.valueOf(id)});
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"Parameter `selection` should be empty when updating an ID");
|
||||
}
|
||||
getContext().getContentResolver().notifyChange(uri, null);
|
||||
return rowsUpdated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the insertion of new bookmark pictures record to local SQLite Database
|
||||
*/
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Override
|
||||
public Uri insert(@NonNull Uri uri, ContentValues contentValues) {
|
||||
SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase();
|
||||
long id = sqlDB.insert(BookmarkPicturesDao.Table.TABLE_NAME, null, contentValues);
|
||||
getContext().getContentResolver().notifyChange(uri, null);
|
||||
return Uri.parse(BASE_URI + "/" + id);
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Override
|
||||
public int delete(@NonNull Uri uri, String s, String[] strings) {
|
||||
int rows;
|
||||
SQLiteDatabase db = dbOpenHelper.getReadableDatabase();
|
||||
Timber.d("Deleting bookmark name %s", uri.getLastPathSegment());
|
||||
rows = db.delete(TABLE_NAME,
|
||||
"media_name = ?",
|
||||
new String[]{uri.getLastPathSegment()}
|
||||
);
|
||||
getContext().getContentResolver().notifyChange(uri, null);
|
||||
return rows;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
package fr.free.nrw.commons.bookmarks.pictures
|
||||
|
||||
import android.content.ContentValues
|
||||
import android.database.Cursor
|
||||
import android.database.sqlite.SQLiteQueryBuilder
|
||||
import android.net.Uri
|
||||
import fr.free.nrw.commons.BuildConfig
|
||||
import fr.free.nrw.commons.di.CommonsDaggerContentProvider
|
||||
import androidx.core.net.toUri
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarksTable.COLUMN_MEDIA_NAME
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarksTable.TABLE_NAME
|
||||
|
||||
/**
|
||||
* Handles private storage for Bookmark pictures
|
||||
*/
|
||||
class BookmarkPicturesContentProvider : CommonsDaggerContentProvider() {
|
||||
override fun getType(uri: Uri): String? = null
|
||||
|
||||
/**
|
||||
* Queries the SQLite database for the bookmark pictures
|
||||
* @param uri : contains the uri for bookmark pictures
|
||||
* @param projection
|
||||
* @param selection : handles Where
|
||||
* @param selectionArgs : the condition of Where clause
|
||||
* @param sortOrder : ascending or descending
|
||||
*/
|
||||
override fun query(
|
||||
uri: Uri, projection: Array<String>?, selection: String?,
|
||||
selectionArgs: Array<String>?, sortOrder: String?
|
||||
): Cursor {
|
||||
val queryBuilder = SQLiteQueryBuilder().apply {
|
||||
tables = TABLE_NAME
|
||||
}
|
||||
|
||||
val cursor = queryBuilder.query(
|
||||
requireDb(), projection, selection,
|
||||
selectionArgs, null, null, sortOrder
|
||||
)
|
||||
cursor.setNotificationUri(requireContext().contentResolver, uri)
|
||||
|
||||
return cursor
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the update query of local SQLite Database
|
||||
* @param uri : contains the uri for bookmark pictures
|
||||
* @param contentValues : new values to be entered to db
|
||||
* @param selection : handles Where
|
||||
* @param selectionArgs : the condition of Where clause
|
||||
*/
|
||||
override fun update(
|
||||
uri: Uri, contentValues: ContentValues?, selection: String?,
|
||||
selectionArgs: Array<String>?
|
||||
): Int {
|
||||
val rowsUpdated: Int
|
||||
if (selection.isNullOrEmpty()) {
|
||||
val id = uri.lastPathSegment!!.toInt()
|
||||
rowsUpdated = requireDb().update(
|
||||
TABLE_NAME,
|
||||
contentValues,
|
||||
"$COLUMN_MEDIA_NAME = ?",
|
||||
arrayOf(id.toString())
|
||||
)
|
||||
} else {
|
||||
throw IllegalArgumentException(
|
||||
"Parameter `selection` should be empty when updating an ID"
|
||||
)
|
||||
}
|
||||
requireContext().contentResolver.notifyChange(uri, null)
|
||||
return rowsUpdated
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the insertion of new bookmark pictures record to local SQLite Database
|
||||
*/
|
||||
override fun insert(uri: Uri, contentValues: ContentValues?): Uri {
|
||||
val id = requireDb().insert(TABLE_NAME, null, contentValues)
|
||||
requireContext().contentResolver.notifyChange(uri, null)
|
||||
return "$BASE_URI/$id".toUri()
|
||||
}
|
||||
|
||||
override fun delete(uri: Uri, s: String?, strings: Array<String>?): Int {
|
||||
val rows: Int = requireDb().delete(
|
||||
TABLE_NAME,
|
||||
"media_name = ?",
|
||||
arrayOf(uri.lastPathSegment)
|
||||
)
|
||||
requireContext().contentResolver.notifyChange(uri, null)
|
||||
return rows
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val BASE_PATH = "bookmarks"
|
||||
@JvmField
|
||||
val BASE_URI: Uri = "content://${BuildConfig.BOOKMARK_AUTHORITY}/$BASE_PATH".toUri()
|
||||
|
||||
@JvmStatic
|
||||
fun uriForName(name: String): Uri = "$BASE_URI/$name".toUri()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
package fr.free.nrw.commons.bookmarks.pictures;
|
||||
|
||||
import fr.free.nrw.commons.Media;
|
||||
import fr.free.nrw.commons.bookmarks.models.Bookmark;
|
||||
import fr.free.nrw.commons.media.MediaClient;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.ObservableSource;
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.functions.Function;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
@Singleton
|
||||
public class BookmarkPicturesController {
|
||||
|
||||
private final MediaClient mediaClient;
|
||||
private final BookmarkPicturesDao bookmarkDao;
|
||||
|
||||
private List<Bookmark> currentBookmarks;
|
||||
|
||||
@Inject
|
||||
public BookmarkPicturesController(MediaClient mediaClient, BookmarkPicturesDao bookmarkDao) {
|
||||
this.mediaClient = mediaClient;
|
||||
this.bookmarkDao = bookmarkDao;
|
||||
currentBookmarks = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the Media objects from the raw data stored in DB and the API.
|
||||
* @return a list of bookmarked Media object
|
||||
*/
|
||||
Single<List<Media>> loadBookmarkedPictures() {
|
||||
List<Bookmark> bookmarks = bookmarkDao.getAllBookmarks();
|
||||
currentBookmarks = bookmarks;
|
||||
return Observable.fromIterable(bookmarks)
|
||||
.flatMap((Function<Bookmark, ObservableSource<Media>>) this::getMediaFromBookmark)
|
||||
.toList();
|
||||
}
|
||||
|
||||
private Observable<Media> getMediaFromBookmark(Bookmark bookmark) {
|
||||
return mediaClient.getMedia(bookmark.getMediaName())
|
||||
.toObservable()
|
||||
.onErrorResumeNext(Observable.empty());
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the Media objects from the raw data stored in DB and the API.
|
||||
* @return a list of bookmarked Media object
|
||||
*/
|
||||
boolean needRefreshBookmarkedPictures() {
|
||||
List<Bookmark> bookmarks = bookmarkDao.getAllBookmarks();
|
||||
return bookmarks.size() != currentBookmarks.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels the requests to the API and the DB
|
||||
*/
|
||||
void stop() {
|
||||
//noop
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
package fr.free.nrw.commons.bookmarks.pictures
|
||||
|
||||
import fr.free.nrw.commons.Media
|
||||
import fr.free.nrw.commons.bookmarks.models.Bookmark
|
||||
import fr.free.nrw.commons.media.MediaClient
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.Single
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class BookmarkPicturesController @Inject constructor(
|
||||
private val mediaClient: MediaClient,
|
||||
private val bookmarkDao: BookmarkPicturesDao
|
||||
) {
|
||||
private var currentBookmarks: List<Bookmark> = listOf()
|
||||
|
||||
/**
|
||||
* Loads the Media objects from the raw data stored in DB and the API.
|
||||
* @return a list of bookmarked Media object
|
||||
*/
|
||||
fun loadBookmarkedPictures(): Single<List<Media>> {
|
||||
val bookmarks = bookmarkDao.getAllBookmarks()
|
||||
currentBookmarks = bookmarks
|
||||
return Observable.fromIterable(bookmarks).flatMap {
|
||||
mediaClient.getMedia(it.mediaName)
|
||||
.toObservable()
|
||||
.onErrorResumeNext(Observable.empty())
|
||||
}.toList()
|
||||
}
|
||||
|
||||
fun needRefreshBookmarkedPictures(): Boolean {
|
||||
val bookmarks = bookmarkDao.getAllBookmarks()
|
||||
return bookmarks.size != currentBookmarks.size
|
||||
}
|
||||
|
||||
fun stop() = Unit
|
||||
}
|
||||
|
|
@ -1,227 +0,0 @@
|
|||
package fr.free.nrw.commons.bookmarks.pictures;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.ContentProviderClient;
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.os.RemoteException;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Provider;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import fr.free.nrw.commons.bookmarks.models.Bookmark;
|
||||
|
||||
import static fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesContentProvider.BASE_URI;
|
||||
|
||||
@Singleton
|
||||
public class BookmarkPicturesDao {
|
||||
|
||||
private final Provider<ContentProviderClient> clientProvider;
|
||||
|
||||
@Inject
|
||||
public BookmarkPicturesDao(@Named("bookmarks") Provider<ContentProviderClient> clientProvider) {
|
||||
this.clientProvider = clientProvider;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find all persisted pictures bookmarks on database
|
||||
*
|
||||
* @return list of bookmarks
|
||||
*/
|
||||
@NonNull
|
||||
public List<Bookmark> getAllBookmarks() {
|
||||
List<Bookmark> items = new ArrayList<>();
|
||||
Cursor cursor = null;
|
||||
ContentProviderClient db = clientProvider.get();
|
||||
try {
|
||||
cursor = db.query(
|
||||
BookmarkPicturesContentProvider.BASE_URI,
|
||||
Table.ALL_FIELDS,
|
||||
null,
|
||||
new String[]{},
|
||||
null);
|
||||
while (cursor != null && cursor.moveToNext()) {
|
||||
items.add(fromCursor(cursor));
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
db.release();
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Look for a bookmark in database and in order to insert or delete it
|
||||
*
|
||||
* @param bookmark : Bookmark object
|
||||
* @return boolean : is bookmark now fav ?
|
||||
*/
|
||||
public boolean updateBookmark(Bookmark bookmark) {
|
||||
boolean bookmarkExists = findBookmark(bookmark);
|
||||
if (bookmarkExists) {
|
||||
deleteBookmark(bookmark);
|
||||
} else {
|
||||
addBookmark(bookmark);
|
||||
}
|
||||
return !bookmarkExists;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a Bookmark to database
|
||||
*
|
||||
* @param bookmark : Bookmark to add
|
||||
*/
|
||||
private void addBookmark(Bookmark bookmark) {
|
||||
ContentProviderClient db = clientProvider.get();
|
||||
try {
|
||||
db.insert(BASE_URI, toContentValues(bookmark));
|
||||
} catch (RemoteException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
db.release();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a bookmark from database
|
||||
*
|
||||
* @param bookmark : Bookmark to delete
|
||||
*/
|
||||
private void deleteBookmark(Bookmark bookmark) {
|
||||
ContentProviderClient db = clientProvider.get();
|
||||
try {
|
||||
if (bookmark.getContentUri() == null) {
|
||||
throw new RuntimeException("tried to delete item with no content URI");
|
||||
} else {
|
||||
db.delete(bookmark.getContentUri(), null, null);
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
db.release();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a bookmark from database based on its name
|
||||
*
|
||||
* @param bookmark : Bookmark to find
|
||||
* @return boolean : is bookmark in database ?
|
||||
*/
|
||||
public boolean findBookmark(Bookmark bookmark) {
|
||||
if (bookmark == null) {//Avoiding NPE's
|
||||
return false;
|
||||
}
|
||||
|
||||
Cursor cursor = null;
|
||||
ContentProviderClient db = clientProvider.get();
|
||||
try {
|
||||
cursor = db.query(
|
||||
BookmarkPicturesContentProvider.BASE_URI,
|
||||
Table.ALL_FIELDS,
|
||||
Table.COLUMN_MEDIA_NAME + "=?",
|
||||
new String[]{bookmark.getMediaName()},
|
||||
null);
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
return true;
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
// This feels lazy, but to hell with checked exceptions. :)
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
db.release();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressLint("Range")
|
||||
@NonNull
|
||||
Bookmark fromCursor(Cursor cursor) {
|
||||
String fileName = cursor.getString(cursor.getColumnIndex(Table.COLUMN_MEDIA_NAME));
|
||||
return new Bookmark(
|
||||
fileName,
|
||||
cursor.getString(cursor.getColumnIndex(Table.COLUMN_CREATOR)),
|
||||
BookmarkPicturesContentProvider.uriForName(fileName)
|
||||
);
|
||||
}
|
||||
|
||||
private ContentValues toContentValues(Bookmark bookmark) {
|
||||
ContentValues cv = new ContentValues();
|
||||
cv.put(BookmarkPicturesDao.Table.COLUMN_MEDIA_NAME, bookmark.getMediaName());
|
||||
cv.put(BookmarkPicturesDao.Table.COLUMN_CREATOR, bookmark.getMediaCreator());
|
||||
return cv;
|
||||
}
|
||||
|
||||
|
||||
public static class Table {
|
||||
public static final String TABLE_NAME = "bookmarks";
|
||||
|
||||
public static final String COLUMN_MEDIA_NAME = "media_name";
|
||||
public static final String COLUMN_CREATOR = "media_creator";
|
||||
|
||||
// NOTE! KEEP IN SAME ORDER AS THEY ARE DEFINED UP THERE. HELPS HARD CODE COLUMN INDICES.
|
||||
public static final String[] ALL_FIELDS = {
|
||||
COLUMN_MEDIA_NAME,
|
||||
COLUMN_CREATOR
|
||||
};
|
||||
|
||||
public static final String DROP_TABLE_STATEMENT = "DROP TABLE IF EXISTS " + TABLE_NAME;
|
||||
|
||||
public static final String CREATE_TABLE_STATEMENT = "CREATE TABLE " + TABLE_NAME + " ("
|
||||
+ COLUMN_MEDIA_NAME + " STRING PRIMARY KEY,"
|
||||
+ COLUMN_CREATOR + " STRING"
|
||||
+ ");";
|
||||
|
||||
public static void onCreate(SQLiteDatabase db) {
|
||||
db.execSQL(CREATE_TABLE_STATEMENT);
|
||||
}
|
||||
|
||||
public static void onDelete(SQLiteDatabase db) {
|
||||
db.execSQL(DROP_TABLE_STATEMENT);
|
||||
onCreate(db);
|
||||
}
|
||||
|
||||
public static void onUpdate(SQLiteDatabase db, int from, int to) {
|
||||
if (from == to) {
|
||||
return;
|
||||
}
|
||||
if (from < 7) {
|
||||
// doesn't exist yet
|
||||
from++;
|
||||
onUpdate(db, from, to);
|
||||
return;
|
||||
}
|
||||
|
||||
if (from == 7) {
|
||||
// table added in version 8
|
||||
onCreate(db);
|
||||
from++;
|
||||
onUpdate(db, from, to);
|
||||
return;
|
||||
}
|
||||
|
||||
if (from == 8) {
|
||||
from++;
|
||||
onUpdate(db, from, to);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,141 @@
|
|||
package fr.free.nrw.commons.bookmarks.pictures
|
||||
|
||||
import android.content.ContentProviderClient
|
||||
import android.content.ContentValues
|
||||
import android.database.Cursor
|
||||
import android.os.RemoteException
|
||||
import androidx.core.content.contentValuesOf
|
||||
import fr.free.nrw.commons.bookmarks.models.Bookmark
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesContentProvider.Companion.BASE_URI
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesContentProvider.Companion.uriForName
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarksTable.ALL_FIELDS
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarksTable.COLUMN_CREATOR
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarksTable.COLUMN_MEDIA_NAME
|
||||
import fr.free.nrw.commons.utils.getString
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Named
|
||||
import javax.inject.Provider
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class BookmarkPicturesDao @Inject constructor(
|
||||
@param:Named("bookmarks") private val clientProvider: Provider<ContentProviderClient>
|
||||
) {
|
||||
/**
|
||||
* Find all persisted pictures bookmarks on database
|
||||
*
|
||||
* @return list of bookmarks
|
||||
*/
|
||||
fun getAllBookmarks(): List<Bookmark> {
|
||||
val items: MutableList<Bookmark> = mutableListOf()
|
||||
var cursor: Cursor? = null
|
||||
val db = clientProvider.get()
|
||||
try {
|
||||
cursor = db.query(
|
||||
BASE_URI, ALL_FIELDS, null, arrayOf(), null
|
||||
)
|
||||
while (cursor != null && cursor.moveToNext()) {
|
||||
items.add(fromCursor(cursor))
|
||||
}
|
||||
} catch (e: RemoteException) {
|
||||
throw RuntimeException(e)
|
||||
} finally {
|
||||
cursor?.close()
|
||||
db.release()
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
/**
|
||||
* Look for a bookmark in database and in order to insert or delete it
|
||||
*
|
||||
* @param bookmark : Bookmark object
|
||||
* @return boolean : is bookmark now fav ?
|
||||
*/
|
||||
fun updateBookmark(bookmark: Bookmark): Boolean {
|
||||
val bookmarkExists = findBookmark(bookmark)
|
||||
if (bookmarkExists) {
|
||||
deleteBookmark(bookmark)
|
||||
} else {
|
||||
addBookmark(bookmark)
|
||||
}
|
||||
return !bookmarkExists
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a Bookmark to database
|
||||
*
|
||||
* @param bookmark : Bookmark to add
|
||||
*/
|
||||
private fun addBookmark(bookmark: Bookmark) {
|
||||
val db = clientProvider.get()
|
||||
try {
|
||||
db.insert(BASE_URI, toContentValues(bookmark))
|
||||
} catch (e: RemoteException) {
|
||||
throw RuntimeException(e)
|
||||
} finally {
|
||||
db.release()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a bookmark from database
|
||||
*
|
||||
* @param bookmark : Bookmark to delete
|
||||
*/
|
||||
private fun deleteBookmark(bookmark: Bookmark) {
|
||||
val db = clientProvider.get()
|
||||
try {
|
||||
if (bookmark.contentUri == null) {
|
||||
throw RuntimeException("tried to delete item with no content URI")
|
||||
} else {
|
||||
db.delete(bookmark.contentUri!!, null, null)
|
||||
}
|
||||
} catch (e: RemoteException) {
|
||||
throw RuntimeException(e)
|
||||
} finally {
|
||||
db.release()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a bookmark from database based on its name
|
||||
*
|
||||
* @param bookmark : Bookmark to find
|
||||
* @return boolean : is bookmark in database ?
|
||||
*/
|
||||
fun findBookmark(bookmark: Bookmark?): Boolean {
|
||||
if (bookmark == null) {
|
||||
return false
|
||||
}
|
||||
|
||||
var cursor: Cursor? = null
|
||||
val db = clientProvider.get()
|
||||
try {
|
||||
cursor = db.query(
|
||||
BASE_URI, ALL_FIELDS, "$COLUMN_MEDIA_NAME=?", arrayOf(bookmark.mediaName), null
|
||||
)
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
return true
|
||||
}
|
||||
} catch (e: RemoteException) {
|
||||
throw RuntimeException(e)
|
||||
} finally {
|
||||
cursor?.close()
|
||||
db.release()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun fromCursor(cursor: Cursor): Bookmark {
|
||||
val fileName = cursor.getString(COLUMN_MEDIA_NAME)
|
||||
return Bookmark(
|
||||
fileName, cursor.getString(COLUMN_CREATOR), uriForName(fileName)
|
||||
)
|
||||
}
|
||||
|
||||
private fun toContentValues(bookmark: Bookmark): ContentValues = contentValuesOf(
|
||||
COLUMN_MEDIA_NAME to bookmark.mediaName,
|
||||
COLUMN_CREATOR to bookmark.mediaCreator
|
||||
)
|
||||
}
|
||||
|
|
@ -1,218 +0,0 @@
|
|||
package fr.free.nrw.commons.bookmarks.pictures;
|
||||
|
||||
import static android.view.View.GONE;
|
||||
import static android.view.View.VISIBLE;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ListAdapter;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import dagger.android.support.DaggerFragment;
|
||||
import fr.free.nrw.commons.Media;
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.bookmarks.BookmarkListRootFragment;
|
||||
import fr.free.nrw.commons.category.GridViewAdapter;
|
||||
import fr.free.nrw.commons.databinding.FragmentBookmarksPicturesBinding;
|
||||
import fr.free.nrw.commons.utils.NetworkUtils;
|
||||
import fr.free.nrw.commons.utils.ViewUtil;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import java.util.List;
|
||||
import javax.inject.Inject;
|
||||
import timber.log.Timber;
|
||||
|
||||
public class BookmarkPicturesFragment extends DaggerFragment {
|
||||
|
||||
private GridViewAdapter gridAdapter;
|
||||
private CompositeDisposable compositeDisposable = new CompositeDisposable();
|
||||
|
||||
private FragmentBookmarksPicturesBinding binding;
|
||||
@Inject
|
||||
BookmarkPicturesController controller;
|
||||
|
||||
/**
|
||||
* Create an instance of the fragment with the right bundle parameters
|
||||
* @return an instance of the fragment
|
||||
*/
|
||||
public static BookmarkPicturesFragment newInstance() {
|
||||
return new BookmarkPicturesFragment();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(
|
||||
@NonNull LayoutInflater inflater,
|
||||
ViewGroup container,
|
||||
Bundle savedInstanceState
|
||||
) {
|
||||
binding = FragmentBookmarksPicturesBinding.inflate(inflater, container, false);
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
binding.bookmarkedPicturesList.setOnItemClickListener((AdapterView.OnItemClickListener) getParentFragment());
|
||||
initList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
controller.stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
compositeDisposable.clear();
|
||||
binding = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
if (controller.needRefreshBookmarkedPictures()) {
|
||||
binding.bookmarkedPicturesList.setVisibility(GONE);
|
||||
if (gridAdapter != null) {
|
||||
gridAdapter.clear();
|
||||
((BookmarkListRootFragment)getParentFragment()).viewPagerNotifyDataSetChanged();
|
||||
}
|
||||
initList();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for internet connection and then initializes
|
||||
* the recycler view with bookmarked pictures
|
||||
*/
|
||||
@SuppressLint("CheckResult")
|
||||
private void initList() {
|
||||
if (!NetworkUtils.isInternetConnectionEstablished(getContext())) {
|
||||
handleNoInternet();
|
||||
return;
|
||||
}
|
||||
|
||||
binding.loadingImagesProgressBar.setVisibility(VISIBLE);
|
||||
binding.statusMessage.setVisibility(GONE);
|
||||
|
||||
compositeDisposable.add(controller.loadBookmarkedPictures()
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(this::handleSuccess, this::handleError));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the UI updates for no internet scenario
|
||||
*/
|
||||
private void handleNoInternet() {
|
||||
binding.loadingImagesProgressBar.setVisibility(GONE);
|
||||
if (gridAdapter == null || gridAdapter.isEmpty()) {
|
||||
binding.statusMessage.setVisibility(VISIBLE);
|
||||
binding.statusMessage.setText(getString(R.string.no_internet));
|
||||
} else {
|
||||
ViewUtil.showShortSnackbar(binding.parentLayout, R.string.no_internet);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs and handles API error scenario
|
||||
* @param throwable
|
||||
*/
|
||||
private void handleError(Throwable throwable) {
|
||||
Timber.e(throwable, "Error occurred while loading images inside a category");
|
||||
try{
|
||||
ViewUtil.showShortSnackbar(binding.getRoot(), R.string.error_loading_images);
|
||||
initErrorView();
|
||||
}catch (Exception e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the UI updates for a error scenario
|
||||
*/
|
||||
private void initErrorView() {
|
||||
binding.loadingImagesProgressBar.setVisibility(GONE);
|
||||
if (gridAdapter == null || gridAdapter.isEmpty()) {
|
||||
binding.statusMessage.setVisibility(VISIBLE);
|
||||
binding.statusMessage.setText(getString(R.string.no_images_found));
|
||||
} else {
|
||||
binding.statusMessage.setVisibility(GONE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the UI updates when there is no bookmarks
|
||||
*/
|
||||
private void initEmptyBookmarkListView() {
|
||||
binding.loadingImagesProgressBar.setVisibility(GONE);
|
||||
if (gridAdapter == null || gridAdapter.isEmpty()) {
|
||||
binding.statusMessage.setVisibility(VISIBLE);
|
||||
binding.statusMessage.setText(getString(R.string.bookmark_empty));
|
||||
} else {
|
||||
binding.statusMessage.setVisibility(GONE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the success scenario
|
||||
* On first load, it initializes the grid view. On subsequent loads, it adds items to the adapter
|
||||
* @param collection List of new Media to be displayed
|
||||
*/
|
||||
private void handleSuccess(List<Media> collection) {
|
||||
if (collection == null) {
|
||||
initErrorView();
|
||||
return;
|
||||
}
|
||||
if (collection.isEmpty()) {
|
||||
initEmptyBookmarkListView();
|
||||
return;
|
||||
}
|
||||
|
||||
if (gridAdapter == null) {
|
||||
setAdapter(collection);
|
||||
} else {
|
||||
if (gridAdapter.containsAll(collection)) {
|
||||
binding.loadingImagesProgressBar.setVisibility(GONE);
|
||||
binding.statusMessage.setVisibility(GONE);
|
||||
binding.bookmarkedPicturesList.setVisibility(VISIBLE);
|
||||
binding.bookmarkedPicturesList.setAdapter(gridAdapter);
|
||||
return;
|
||||
}
|
||||
gridAdapter.addItems(collection);
|
||||
((BookmarkListRootFragment) getParentFragment()).viewPagerNotifyDataSetChanged();
|
||||
}
|
||||
binding.loadingImagesProgressBar.setVisibility(GONE);
|
||||
binding.statusMessage.setVisibility(GONE);
|
||||
binding.bookmarkedPicturesList.setVisibility(VISIBLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the adapter with a list of Media objects
|
||||
* @param mediaList List of new Media to be displayed
|
||||
*/
|
||||
private void setAdapter(List<Media> mediaList) {
|
||||
gridAdapter = new GridViewAdapter(
|
||||
this.getContext(),
|
||||
R.layout.layout_category_images,
|
||||
mediaList
|
||||
);
|
||||
binding.bookmarkedPicturesList.setAdapter(gridAdapter);
|
||||
}
|
||||
|
||||
/**
|
||||
* It return an instance of gridView adapter which helps in extracting media details
|
||||
* used by the gridView
|
||||
* @return GridView Adapter
|
||||
*/
|
||||
public ListAdapter getAdapter() {
|
||||
return binding.bookmarkedPicturesList.getAdapter();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,201 @@
|
|||
package fr.free.nrw.commons.bookmarks.pictures
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.AdapterView.OnItemClickListener
|
||||
import android.widget.ListAdapter
|
||||
import dagger.android.support.DaggerFragment
|
||||
import fr.free.nrw.commons.Media
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.bookmarks.BookmarkListRootFragment
|
||||
import fr.free.nrw.commons.category.GridViewAdapter
|
||||
import fr.free.nrw.commons.databinding.FragmentBookmarksPicturesBinding
|
||||
import fr.free.nrw.commons.utils.NetworkUtils.isInternetConnectionEstablished
|
||||
import fr.free.nrw.commons.utils.ViewUtil.showShortSnackbar
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import io.reactivex.functions.Consumer
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
class BookmarkPicturesFragment : DaggerFragment() {
|
||||
private var gridAdapter: GridViewAdapter? = null
|
||||
private val compositeDisposable = CompositeDisposable()
|
||||
|
||||
private var binding: FragmentBookmarksPicturesBinding? = null
|
||||
|
||||
@JvmField
|
||||
@Inject
|
||||
var controller: BookmarkPicturesController? = null
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
binding = FragmentBookmarksPicturesBinding.inflate(inflater, container, false)
|
||||
return binding!!.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
binding!!.bookmarkedPicturesList.onItemClickListener =
|
||||
parentFragment as OnItemClickListener?
|
||||
initList()
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
controller!!.stop()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
compositeDisposable.clear()
|
||||
binding = null
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
if (controller!!.needRefreshBookmarkedPictures()) {
|
||||
binding!!.bookmarkedPicturesList.visibility = View.GONE
|
||||
gridAdapter?.let {
|
||||
it.clear()
|
||||
(parentFragment as BookmarkListRootFragment).viewPagerNotifyDataSetChanged()
|
||||
}
|
||||
initList()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for internet connection and then initializes
|
||||
* the recycler view with bookmarked pictures
|
||||
*/
|
||||
@SuppressLint("CheckResult")
|
||||
private fun initList() {
|
||||
if (!isInternetConnectionEstablished(context)) {
|
||||
handleNoInternet()
|
||||
return
|
||||
}
|
||||
|
||||
binding!!.loadingImagesProgressBar.visibility = View.VISIBLE
|
||||
binding!!.statusMessage.visibility = View.GONE
|
||||
|
||||
compositeDisposable.add(
|
||||
controller!!.loadBookmarkedPictures()
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(::handleSuccess, ::handleError)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the UI updates for no internet scenario
|
||||
*/
|
||||
private fun handleNoInternet() {
|
||||
binding!!.loadingImagesProgressBar.visibility = View.GONE
|
||||
if (gridAdapter == null || gridAdapter!!.isEmpty) {
|
||||
binding!!.statusMessage.visibility = View.VISIBLE
|
||||
binding!!.statusMessage.text = getString(R.string.no_internet)
|
||||
} else {
|
||||
showShortSnackbar(binding!!.parentLayout, R.string.no_internet)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs and handles API error scenario
|
||||
* @param throwable
|
||||
*/
|
||||
private fun handleError(throwable: Throwable) {
|
||||
Timber.e(throwable, "Error occurred while loading images inside a category")
|
||||
try {
|
||||
showShortSnackbar(binding!!.root, R.string.error_loading_images)
|
||||
initErrorView()
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the UI updates for a error scenario
|
||||
*/
|
||||
private fun initErrorView() {
|
||||
binding!!.loadingImagesProgressBar.visibility = View.GONE
|
||||
if (gridAdapter == null || gridAdapter!!.isEmpty) {
|
||||
binding!!.statusMessage.visibility = View.VISIBLE
|
||||
binding!!.statusMessage.text = getString(R.string.no_images_found)
|
||||
} else {
|
||||
binding!!.statusMessage.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the UI updates when there is no bookmarks
|
||||
*/
|
||||
private fun initEmptyBookmarkListView() {
|
||||
binding!!.loadingImagesProgressBar.visibility = View.GONE
|
||||
if (gridAdapter == null || gridAdapter!!.isEmpty) {
|
||||
binding!!.statusMessage.visibility = View.VISIBLE
|
||||
binding!!.statusMessage.text = getString(R.string.bookmark_empty)
|
||||
} else {
|
||||
binding!!.statusMessage.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the success scenario
|
||||
* On first load, it initializes the grid view. On subsequent loads, it adds items to the adapter
|
||||
* @param collection List of new Media to be displayed
|
||||
*/
|
||||
private fun handleSuccess(collection: List<Media>?) {
|
||||
if (collection == null) {
|
||||
initErrorView()
|
||||
return
|
||||
}
|
||||
if (collection.isEmpty()) {
|
||||
initEmptyBookmarkListView()
|
||||
return
|
||||
}
|
||||
|
||||
if (gridAdapter == null) {
|
||||
setAdapter(collection)
|
||||
} else {
|
||||
if (gridAdapter!!.containsAll(collection)) {
|
||||
binding!!.loadingImagesProgressBar.visibility = View.GONE
|
||||
binding!!.statusMessage.visibility = View.GONE
|
||||
binding!!.bookmarkedPicturesList.visibility = View.VISIBLE
|
||||
binding!!.bookmarkedPicturesList.adapter = gridAdapter
|
||||
return
|
||||
}
|
||||
gridAdapter!!.addItems(collection)
|
||||
(parentFragment as BookmarkListRootFragment).viewPagerNotifyDataSetChanged()
|
||||
}
|
||||
binding!!.loadingImagesProgressBar.visibility = View.GONE
|
||||
binding!!.statusMessage.visibility = View.GONE
|
||||
binding!!.bookmarkedPicturesList.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the adapter with a list of Media objects
|
||||
* @param mediaList List of new Media to be displayed
|
||||
*/
|
||||
private fun setAdapter(mediaList: List<Media>) {
|
||||
gridAdapter = GridViewAdapter(
|
||||
requireContext(),
|
||||
R.layout.layout_category_images,
|
||||
mediaList.toMutableList()
|
||||
)
|
||||
binding?.let { it.bookmarkedPicturesList.adapter = gridAdapter }
|
||||
}
|
||||
|
||||
/**
|
||||
* It return an instance of gridView adapter which helps in extracting media details
|
||||
* used by the gridView
|
||||
* @return GridView Adapter
|
||||
*/
|
||||
fun getAdapter(): ListAdapter? = binding?.bookmarkedPicturesList?.adapter
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
package fr.free.nrw.commons.bookmarks.pictures
|
||||
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
|
||||
object BookmarksTable {
|
||||
const val TABLE_NAME: String = "bookmarks"
|
||||
const val COLUMN_MEDIA_NAME: String = "media_name"
|
||||
const val COLUMN_CREATOR: String = "media_creator"
|
||||
|
||||
// NOTE! KEEP IN SAME ORDER AS THEY ARE DEFINED UP THERE. HELPS HARD CODE COLUMN INDICES.
|
||||
val ALL_FIELDS = arrayOf(
|
||||
COLUMN_MEDIA_NAME,
|
||||
COLUMN_CREATOR
|
||||
)
|
||||
|
||||
const val DROP_TABLE_STATEMENT: String = "DROP TABLE IF EXISTS $TABLE_NAME"
|
||||
|
||||
const val CREATE_TABLE_STATEMENT: String = ("CREATE TABLE $TABLE_NAME (" +
|
||||
"$COLUMN_MEDIA_NAME STRING PRIMARY KEY, " +
|
||||
"$COLUMN_CREATOR STRING" +
|
||||
");")
|
||||
|
||||
fun onCreate(db: SQLiteDatabase) =
|
||||
db.execSQL(CREATE_TABLE_STATEMENT)
|
||||
|
||||
fun onDelete(db: SQLiteDatabase) {
|
||||
db.execSQL(DROP_TABLE_STATEMENT)
|
||||
onCreate(db)
|
||||
}
|
||||
|
||||
fun onUpdate(db: SQLiteDatabase, from: Int, to: Int) {
|
||||
if (from == to) {
|
||||
return
|
||||
}
|
||||
|
||||
if (from < 7) {
|
||||
// doesn't exist yet
|
||||
onUpdate(db, from+1, to)
|
||||
return
|
||||
}
|
||||
|
||||
if (from == 7) {
|
||||
// table added in version 8
|
||||
onCreate(db)
|
||||
onUpdate(db, from+1, to)
|
||||
return
|
||||
}
|
||||
|
||||
if (from == 8) {
|
||||
onUpdate(db, from+1, to)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -9,12 +9,9 @@ import android.database.sqlite.SQLiteDatabase
|
|||
import android.database.sqlite.SQLiteQueryBuilder
|
||||
import android.net.Uri
|
||||
import android.text.TextUtils
|
||||
import androidx.annotation.NonNull
|
||||
import fr.free.nrw.commons.BuildConfig
|
||||
import fr.free.nrw.commons.data.DBOpenHelper
|
||||
import fr.free.nrw.commons.di.CommonsDaggerContentProvider
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import androidx.core.net.toUri
|
||||
|
||||
class CategoryContentProvider : CommonsDaggerContentProvider() {
|
||||
|
||||
|
|
@ -23,9 +20,6 @@ class CategoryContentProvider : CommonsDaggerContentProvider() {
|
|||
addURI(BuildConfig.CATEGORY_AUTHORITY, "${BASE_PATH}/#", CATEGORIES_ID)
|
||||
}
|
||||
|
||||
@Inject
|
||||
lateinit var dbOpenHelper: DBOpenHelper
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
override fun query(uri: Uri, projection: Array<String>?, selection: String?,
|
||||
selectionArgs: Array<String>?, sortOrder: String?): Cursor? {
|
||||
|
|
@ -34,7 +28,7 @@ class CategoryContentProvider : CommonsDaggerContentProvider() {
|
|||
}
|
||||
|
||||
val uriType = uriMatcher.match(uri)
|
||||
val db = dbOpenHelper.readableDatabase
|
||||
val db = requireDb()
|
||||
|
||||
val cursor: Cursor? = when (uriType) {
|
||||
CATEGORIES -> queryBuilder.query(
|
||||
|
|
@ -58,45 +52,37 @@ class CategoryContentProvider : CommonsDaggerContentProvider() {
|
|||
else -> throw IllegalArgumentException("Unknown URI $uri")
|
||||
}
|
||||
|
||||
cursor?.setNotificationUri(context?.contentResolver, uri)
|
||||
cursor?.setNotificationUri(requireContext().contentResolver, uri)
|
||||
return cursor
|
||||
}
|
||||
|
||||
override fun getType(uri: Uri): String? {
|
||||
return null
|
||||
}
|
||||
override fun getType(uri: Uri): String? = null
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
override fun insert(uri: Uri, contentValues: ContentValues?): Uri? {
|
||||
override fun insert(uri: Uri, contentValues: ContentValues?): Uri {
|
||||
val uriType = uriMatcher.match(uri)
|
||||
val sqlDB = dbOpenHelper.writableDatabase
|
||||
val id: Long
|
||||
when (uriType) {
|
||||
CATEGORIES -> {
|
||||
id = sqlDB.insert(TABLE_NAME, null, contentValues)
|
||||
id = requireDb().insert(TABLE_NAME, null, contentValues)
|
||||
}
|
||||
else -> throw IllegalArgumentException("Unknown URI: $uri")
|
||||
}
|
||||
context?.contentResolver?.notifyChange(uri, null)
|
||||
return Uri.parse("${Companion.BASE_URI}/$id")
|
||||
requireContext().contentResolver?.notifyChange(uri, null)
|
||||
return "${BASE_URI}/$id".toUri()
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?): Int {
|
||||
// Not implemented
|
||||
return 0
|
||||
}
|
||||
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?): Int = 0
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
override fun bulkInsert(uri: Uri, values: Array<ContentValues>): Int {
|
||||
Timber.d("Hello, bulk insert! (CategoryContentProvider)")
|
||||
val uriType = uriMatcher.match(uri)
|
||||
val sqlDB = dbOpenHelper.writableDatabase
|
||||
val sqlDB = requireDb()
|
||||
sqlDB.beginTransaction()
|
||||
when (uriType) {
|
||||
CATEGORIES -> {
|
||||
for (value in values) {
|
||||
Timber.d("Inserting! %s", value)
|
||||
sqlDB.insert(TABLE_NAME, null, value)
|
||||
}
|
||||
sqlDB.setTransactionSuccessful()
|
||||
|
|
@ -104,7 +90,7 @@ class CategoryContentProvider : CommonsDaggerContentProvider() {
|
|||
else -> throw IllegalArgumentException("Unknown URI: $uri")
|
||||
}
|
||||
sqlDB.endTransaction()
|
||||
context?.contentResolver?.notifyChange(uri, null)
|
||||
requireContext().contentResolver?.notifyChange(uri, null)
|
||||
return values.size
|
||||
}
|
||||
|
||||
|
|
@ -112,17 +98,18 @@ class CategoryContentProvider : CommonsDaggerContentProvider() {
|
|||
override fun update(uri: Uri, contentValues: ContentValues?, selection: String?,
|
||||
selectionArgs: Array<String>?): Int {
|
||||
val uriType = uriMatcher.match(uri)
|
||||
val sqlDB = dbOpenHelper.writableDatabase
|
||||
val rowsUpdated: Int
|
||||
when (uriType) {
|
||||
CATEGORIES_ID -> {
|
||||
if (TextUtils.isEmpty(selection)) {
|
||||
val id = uri.lastPathSegment?.toInt()
|
||||
?: throw IllegalArgumentException("Invalid ID")
|
||||
rowsUpdated = sqlDB.update(TABLE_NAME,
|
||||
rowsUpdated = requireDb().update(
|
||||
TABLE_NAME,
|
||||
contentValues,
|
||||
"$COLUMN_ID = ?",
|
||||
arrayOf(id.toString()))
|
||||
arrayOf(id.toString())
|
||||
)
|
||||
} else {
|
||||
throw IllegalArgumentException(
|
||||
"Parameter `selection` should be empty when updating an ID")
|
||||
|
|
@ -130,7 +117,7 @@ class CategoryContentProvider : CommonsDaggerContentProvider() {
|
|||
}
|
||||
else -> throw IllegalArgumentException("Unknown URI: $uri with type $uriType")
|
||||
}
|
||||
context?.contentResolver?.notifyChange(uri, null)
|
||||
requireContext().contentResolver?.notifyChange(uri, null)
|
||||
return rowsUpdated
|
||||
}
|
||||
|
||||
|
|
@ -165,13 +152,9 @@ class CategoryContentProvider : CommonsDaggerContentProvider() {
|
|||
"$COLUMN_TIMES_USED INTEGER" +
|
||||
");"
|
||||
|
||||
fun uriForId(id: Int): Uri {
|
||||
return Uri.parse("${BASE_URI}/$id")
|
||||
}
|
||||
fun uriForId(id: Int): Uri = Uri.parse("${BASE_URI}/$id")
|
||||
|
||||
fun onCreate(db: SQLiteDatabase) {
|
||||
db.execSQL(CREATE_TABLE_STATEMENT)
|
||||
}
|
||||
fun onCreate(db: SQLiteDatabase) = db.execSQL(CREATE_TABLE_STATEMENT)
|
||||
|
||||
fun onDelete(db: SQLiteDatabase) {
|
||||
db.execSQL(DROP_TABLE_STATEMENT)
|
||||
|
|
@ -200,6 +183,6 @@ class CategoryContentProvider : CommonsDaggerContentProvider() {
|
|||
private const val CATEGORIES = 1
|
||||
private const val CATEGORIES_ID = 2
|
||||
private const val BASE_PATH = "categories"
|
||||
val BASE_URI: Uri = Uri.parse("content://${BuildConfig.CATEGORY_AUTHORITY}/${Companion.BASE_PATH}")
|
||||
val BASE_URI: Uri = "content://${BuildConfig.CATEGORY_AUTHORITY}/${BASE_PATH}".toUri()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,9 +4,8 @@ import android.content.Context
|
|||
import android.database.sqlite.SQLiteDatabase
|
||||
import android.database.sqlite.SQLiteException
|
||||
import android.database.sqlite.SQLiteOpenHelper
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao
|
||||
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarksTable
|
||||
import fr.free.nrw.commons.category.CategoryDao
|
||||
import fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao
|
||||
import fr.free.nrw.commons.recentlanguages.RecentLanguagesDao
|
||||
|
|
@ -30,16 +29,16 @@ class DBOpenHelper(
|
|||
*/
|
||||
override fun onCreate(db: SQLiteDatabase) {
|
||||
CategoryDao.Table.onCreate(db)
|
||||
BookmarkPicturesDao.Table.onCreate(db)
|
||||
BookmarkItemsDao.Table.onCreate(db)
|
||||
BookmarksTable.onCreate(db)
|
||||
BookmarkItemsTable.onCreate(db)
|
||||
RecentSearchesDao.Table.onCreate(db)
|
||||
RecentLanguagesDao.Table.onCreate(db)
|
||||
}
|
||||
|
||||
override fun onUpgrade(db: SQLiteDatabase, from: Int, to: Int) {
|
||||
CategoryDao.Table.onUpdate(db, from, to)
|
||||
BookmarkPicturesDao.Table.onUpdate(db, from, to)
|
||||
BookmarkItemsDao.Table.onUpdate(db, from, to)
|
||||
BookmarksTable.onUpdate(db, from, to)
|
||||
BookmarkItemsTable.onUpdate(db, from, to)
|
||||
RecentSearchesDao.Table.onUpdate(db, from, to)
|
||||
RecentLanguagesDao.Table.onUpdate(db, from, to)
|
||||
deleteTable(db, CONTRIBUTIONS_TABLE)
|
||||
|
|
|
|||
|
|
@ -1,14 +1,25 @@
|
|||
package fr.free.nrw.commons.di
|
||||
|
||||
import android.content.ContentProvider
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
import fr.free.nrw.commons.data.DBOpenHelper
|
||||
import fr.free.nrw.commons.di.ApplicationlessInjection.Companion.getInstance
|
||||
import javax.inject.Inject
|
||||
|
||||
abstract class CommonsDaggerContentProvider : ContentProvider() {
|
||||
@JvmField
|
||||
@Inject
|
||||
var dbOpenHelper: DBOpenHelper? = null
|
||||
|
||||
override fun onCreate(): Boolean {
|
||||
inject()
|
||||
return true
|
||||
}
|
||||
|
||||
fun requireDbOpenHelper(): DBOpenHelper = dbOpenHelper!!
|
||||
|
||||
fun requireDb(): SQLiteDatabase = requireDbOpenHelper().writableDatabase!!
|
||||
|
||||
private fun inject() {
|
||||
val injection = getInstance(context!!)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,202 +0,0 @@
|
|||
package fr.free.nrw.commons.explore.recentsearches;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.UriMatcher;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteQueryBuilder;
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import fr.free.nrw.commons.BuildConfig;
|
||||
import fr.free.nrw.commons.data.DBOpenHelper;
|
||||
import fr.free.nrw.commons.di.CommonsDaggerContentProvider;
|
||||
import timber.log.Timber;
|
||||
|
||||
import static android.content.UriMatcher.NO_MATCH;
|
||||
import static fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao.Table.ALL_FIELDS;
|
||||
import static fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao.Table.COLUMN_ID;
|
||||
import static fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao.Table.TABLE_NAME;
|
||||
|
||||
|
||||
/**
|
||||
* This class contains functions for executing queries for
|
||||
* inserting, searching, deleting, editing recent searches in SqLite DB
|
||||
**/
|
||||
public class RecentSearchesContentProvider extends CommonsDaggerContentProvider {
|
||||
|
||||
// For URI matcher
|
||||
private static final int RECENT_SEARCHES = 1;
|
||||
private static final int RECENT_SEARCHES_ID = 2;
|
||||
private static final String BASE_PATH = "recent_searches";
|
||||
public static final Uri BASE_URI = Uri.parse("content://" + BuildConfig.RECENT_SEARCH_AUTHORITY + "/" + BASE_PATH);
|
||||
private static final UriMatcher uriMatcher = new UriMatcher(NO_MATCH);
|
||||
|
||||
static {
|
||||
uriMatcher.addURI(BuildConfig.RECENT_SEARCH_AUTHORITY, BASE_PATH, RECENT_SEARCHES);
|
||||
uriMatcher.addURI(BuildConfig.RECENT_SEARCH_AUTHORITY, BASE_PATH + "/#", RECENT_SEARCHES_ID);
|
||||
}
|
||||
|
||||
public static Uri uriForId(int id) {
|
||||
return Uri.parse(BASE_URI.toString() + "/" + id);
|
||||
}
|
||||
|
||||
@Inject DBOpenHelper dbOpenHelper;
|
||||
|
||||
/**
|
||||
* This functions executes query for searching recent searches in SqLite DB
|
||||
**/
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Override
|
||||
public Cursor query(@NonNull Uri uri, String[] projection, String selection,
|
||||
String[] selectionArgs, String sortOrder) {
|
||||
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
|
||||
queryBuilder.setTables(TABLE_NAME);
|
||||
|
||||
int uriType = uriMatcher.match(uri);
|
||||
|
||||
SQLiteDatabase db = dbOpenHelper.getReadableDatabase();
|
||||
Cursor cursor;
|
||||
|
||||
switch (uriType) {
|
||||
case RECENT_SEARCHES:
|
||||
cursor = queryBuilder.query(db, projection, selection, selectionArgs,
|
||||
null, null, sortOrder);
|
||||
break;
|
||||
case RECENT_SEARCHES_ID:
|
||||
cursor = queryBuilder.query(db,
|
||||
ALL_FIELDS,
|
||||
"_id = ?",
|
||||
new String[]{uri.getLastPathSegment()},
|
||||
null,
|
||||
null,
|
||||
sortOrder
|
||||
);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown URI" + uri);
|
||||
}
|
||||
|
||||
cursor.setNotificationUri(getContext().getContentResolver(), uri);
|
||||
|
||||
return cursor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType(@NonNull Uri uri) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This functions executes query for inserting a recentSearch object in SqLite DB
|
||||
**/
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Override
|
||||
public Uri insert(@NonNull Uri uri, ContentValues contentValues) {
|
||||
int uriType = uriMatcher.match(uri);
|
||||
SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase();
|
||||
long id;
|
||||
switch (uriType) {
|
||||
case RECENT_SEARCHES:
|
||||
id = sqlDB.insert(TABLE_NAME, null, contentValues);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown URI: " + uri);
|
||||
}
|
||||
getContext().getContentResolver().notifyChange(uri, null);
|
||||
return Uri.parse(BASE_URI + "/" + id);
|
||||
}
|
||||
|
||||
/**
|
||||
* This functions executes query for deleting a recentSearch object in SqLite DB
|
||||
**/
|
||||
@Override
|
||||
public int delete(@NonNull Uri uri, String s, String[] strings) {
|
||||
int rows;
|
||||
int uriType = uriMatcher.match(uri);
|
||||
SQLiteDatabase db = dbOpenHelper.getReadableDatabase();
|
||||
switch (uriType) {
|
||||
case RECENT_SEARCHES_ID:
|
||||
Timber.d("Deleting recent searches id %s", uri.getLastPathSegment());
|
||||
rows = db.delete(RecentSearchesDao.Table.TABLE_NAME,
|
||||
"_id = ?",
|
||||
new String[]{uri.getLastPathSegment()}
|
||||
);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown URI" + uri);
|
||||
}
|
||||
getContext().getContentResolver().notifyChange(uri, null);
|
||||
return rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* This functions executes query for inserting multiple recentSearch objects in SqLite DB
|
||||
**/
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Override
|
||||
public int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] values) {
|
||||
Timber.d("Hello, bulk insert! (RecentSearchesContentProvider)");
|
||||
int uriType = uriMatcher.match(uri);
|
||||
SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase();
|
||||
sqlDB.beginTransaction();
|
||||
switch (uriType) {
|
||||
case RECENT_SEARCHES:
|
||||
for (ContentValues value : values) {
|
||||
Timber.d("Inserting! %s", value);
|
||||
sqlDB.insert(TABLE_NAME, null, value);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown URI: " + uri);
|
||||
}
|
||||
sqlDB.setTransactionSuccessful();
|
||||
sqlDB.endTransaction();
|
||||
getContext().getContentResolver().notifyChange(uri, null);
|
||||
return values.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* This functions executes query for updating a particular recentSearch object in SqLite DB
|
||||
**/
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Override
|
||||
public int update(@NonNull Uri uri, ContentValues contentValues, String selection,
|
||||
String[] selectionArgs) {
|
||||
/*
|
||||
SQL Injection warnings: First, note that we're not exposing this to the
|
||||
outside world (exported="false"). Even then, we should make sure to sanitize
|
||||
all user input appropriately. Input that passes through ContentValues
|
||||
should be fine. So only issues are those that pass in via concating.
|
||||
|
||||
In here, the only concat created argument is for id. It is cast to an int,
|
||||
and will error out otherwise.
|
||||
*/
|
||||
int uriType = uriMatcher.match(uri);
|
||||
SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase();
|
||||
int rowsUpdated;
|
||||
switch (uriType) {
|
||||
case RECENT_SEARCHES_ID:
|
||||
if (TextUtils.isEmpty(selection)) {
|
||||
int id = Integer.valueOf(uri.getLastPathSegment());
|
||||
rowsUpdated = sqlDB.update(TABLE_NAME,
|
||||
contentValues,
|
||||
COLUMN_ID + " = ?",
|
||||
new String[]{String.valueOf(id)});
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"Parameter `selection` should be empty when updating an ID");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown URI: " + uri + " with type " + uriType);
|
||||
}
|
||||
getContext().getContentResolver().notifyChange(uri, null);
|
||||
return rowsUpdated;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,174 @@
|
|||
package fr.free.nrw.commons.explore.recentsearches
|
||||
|
||||
import android.content.ContentValues
|
||||
import android.content.UriMatcher
|
||||
import android.database.Cursor
|
||||
import android.database.sqlite.SQLiteQueryBuilder
|
||||
import android.net.Uri
|
||||
import androidx.core.net.toUri
|
||||
import fr.free.nrw.commons.BuildConfig
|
||||
import fr.free.nrw.commons.di.CommonsDaggerContentProvider
|
||||
import fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao.Table.ALL_FIELDS
|
||||
import fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao.Table.COLUMN_ID
|
||||
import fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao.Table.TABLE_NAME
|
||||
|
||||
/**
|
||||
* This class contains functions for executing queries for
|
||||
* inserting, searching, deleting, editing recent searches in SqLite DB
|
||||
*/
|
||||
class RecentSearchesContentProvider : CommonsDaggerContentProvider() {
|
||||
|
||||
/**
|
||||
* This functions executes query for searching recent searches in SqLite DB
|
||||
*/
|
||||
override fun query(
|
||||
uri: Uri, projection: Array<String>?, selection: String?,
|
||||
selectionArgs: Array<String>?, sortOrder: String?
|
||||
): Cursor {
|
||||
val queryBuilder = SQLiteQueryBuilder().apply {
|
||||
tables = TABLE_NAME
|
||||
}
|
||||
|
||||
val uriType = uriMatcher.match(uri)
|
||||
|
||||
val cursor = when (uriType) {
|
||||
RECENT_SEARCHES -> queryBuilder.query(
|
||||
requireDb(), projection, selection, selectionArgs,
|
||||
null, null, sortOrder
|
||||
)
|
||||
|
||||
RECENT_SEARCHES_ID -> queryBuilder.query(
|
||||
requireDb(),
|
||||
ALL_FIELDS,
|
||||
"$COLUMN_ID = ?",
|
||||
arrayOf(uri.lastPathSegment),
|
||||
null,
|
||||
null,
|
||||
sortOrder
|
||||
)
|
||||
|
||||
else -> throw IllegalArgumentException("Unknown URI$uri")
|
||||
}
|
||||
|
||||
cursor.setNotificationUri(requireContext().contentResolver, uri)
|
||||
|
||||
return cursor
|
||||
}
|
||||
|
||||
override fun getType(uri: Uri): String? = null
|
||||
|
||||
/**
|
||||
* This functions executes query for inserting a recentSearch object in SqLite DB
|
||||
*/
|
||||
override fun insert(uri: Uri, contentValues: ContentValues?): Uri? {
|
||||
val uriType = uriMatcher.match(uri)
|
||||
val id: Long = when (uriType) {
|
||||
RECENT_SEARCHES -> requireDb().insert(TABLE_NAME, null, contentValues)
|
||||
|
||||
else -> throw IllegalArgumentException("Unknown URI: $uri")
|
||||
}
|
||||
requireContext().contentResolver.notifyChange(uri, null)
|
||||
return "$BASE_URI/$id".toUri()
|
||||
}
|
||||
|
||||
/**
|
||||
* This functions executes query for deleting a recentSearch object in SqLite DB
|
||||
*/
|
||||
override fun delete(uri: Uri, s: String?, strings: Array<String>?): Int {
|
||||
val rows: Int
|
||||
val uriType = uriMatcher.match(uri)
|
||||
when (uriType) {
|
||||
RECENT_SEARCHES_ID -> {
|
||||
rows = requireDb().delete(
|
||||
TABLE_NAME,
|
||||
"_id = ?",
|
||||
arrayOf(uri.lastPathSegment)
|
||||
)
|
||||
}
|
||||
|
||||
else -> throw IllegalArgumentException("Unknown URI - $uri")
|
||||
}
|
||||
requireContext().contentResolver.notifyChange(uri, null)
|
||||
return rows
|
||||
}
|
||||
|
||||
/**
|
||||
* This functions executes query for inserting multiple recentSearch objects in SqLite DB
|
||||
*/
|
||||
override fun bulkInsert(uri: Uri, values: Array<ContentValues>): Int {
|
||||
val uriType = uriMatcher.match(uri)
|
||||
val sqlDB = requireDb()
|
||||
sqlDB.beginTransaction()
|
||||
when (uriType) {
|
||||
RECENT_SEARCHES -> for (value in values) {
|
||||
sqlDB.insert(TABLE_NAME, null, value)
|
||||
}
|
||||
|
||||
else -> throw IllegalArgumentException("Unknown URI: $uri")
|
||||
}
|
||||
sqlDB.setTransactionSuccessful()
|
||||
sqlDB.endTransaction()
|
||||
requireContext().contentResolver.notifyChange(uri, null)
|
||||
return values.size
|
||||
}
|
||||
|
||||
/**
|
||||
* This functions executes query for updating a particular recentSearch object in SqLite DB
|
||||
*/
|
||||
override fun update(
|
||||
uri: Uri, contentValues: ContentValues?, selection: String?,
|
||||
selectionArgs: Array<String>?
|
||||
): Int {
|
||||
/*
|
||||
SQL Injection warnings: First, note that we're not exposing this to the
|
||||
outside world (exported="false"). Even then, we should make sure to sanitize
|
||||
all user input appropriately. Input that passes through ContentValues
|
||||
should be fine. So only issues are those that pass in via concating.
|
||||
|
||||
In here, the only concat created argument is for id. It is cast to an int,
|
||||
and will error out otherwise.
|
||||
*/
|
||||
val uriType = uriMatcher.match(uri)
|
||||
val rowsUpdated: Int
|
||||
when (uriType) {
|
||||
RECENT_SEARCHES_ID -> if (selection.isNullOrEmpty()) {
|
||||
val id = uri.lastPathSegment!!.toInt()
|
||||
rowsUpdated = requireDb().update(
|
||||
TABLE_NAME,
|
||||
contentValues,
|
||||
"$COLUMN_ID = ?",
|
||||
arrayOf(id.toString())
|
||||
)
|
||||
} else {
|
||||
throw IllegalArgumentException(
|
||||
"Parameter `selection` should be empty when updating an ID"
|
||||
)
|
||||
}
|
||||
|
||||
else -> throw IllegalArgumentException("Unknown URI: $uri with type $uriType")
|
||||
}
|
||||
requireContext().contentResolver.notifyChange(uri, null)
|
||||
return rowsUpdated
|
||||
}
|
||||
|
||||
companion object {
|
||||
// For URI matcher
|
||||
private const val RECENT_SEARCHES = 1
|
||||
private const val RECENT_SEARCHES_ID = 2
|
||||
private const val BASE_PATH = "recent_searches"
|
||||
|
||||
@JvmField
|
||||
val BASE_URI: Uri = "content://${BuildConfig.RECENT_SEARCH_AUTHORITY}/$BASE_PATH".toUri()
|
||||
|
||||
private val uriMatcher = UriMatcher(UriMatcher.NO_MATCH)
|
||||
|
||||
init {
|
||||
uriMatcher.addURI(BuildConfig.RECENT_SEARCH_AUTHORITY, BASE_PATH, RECENT_SEARCHES)
|
||||
uriMatcher.addURI(BuildConfig.RECENT_SEARCH_AUTHORITY, "$BASE_PATH/#", RECENT_SEARCHES_ID)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun uriForId(id: Int): Uri = "$BASE_URI/$id".toUri()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -166,7 +166,7 @@ class MediaDetailPagerFragment : CommonsDaggerSupportFragment(), OnPageChangeLis
|
|||
val mediaDetailFragment = adapter!!.currentMediaDetailFragment
|
||||
when (item.itemId) {
|
||||
R.id.menu_bookmark_current_image -> {
|
||||
val bookmarkExists = bookmarkDao!!.updateBookmark(bookmark)
|
||||
val bookmarkExists = bookmarkDao!!.updateBookmark(bookmark!!)
|
||||
val snackbar = if (bookmarkExists) Snackbar.make(
|
||||
requireView(),
|
||||
R.string.add_bookmark,
|
||||
|
|
@ -436,7 +436,7 @@ ${m.pageTitle.canonicalUri}"""
|
|||
bookmark = Bookmark(
|
||||
m.filename,
|
||||
m.getAuthorOrUser(),
|
||||
BookmarkPicturesContentProvider.uriForName(m.filename)
|
||||
BookmarkPicturesContentProvider.uriForName(m.filename!!)
|
||||
)
|
||||
updateBookmarkState(menu.findItem(R.id.menu_bookmark_current_image))
|
||||
val contributionState = provider.getContributionStateAt(position)
|
||||
|
|
|
|||
|
|
@ -3,17 +3,13 @@ package fr.free.nrw.commons.recentlanguages
|
|||
|
||||
import android.content.ContentValues
|
||||
import android.database.Cursor
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
import android.database.sqlite.SQLiteQueryBuilder
|
||||
import android.net.Uri
|
||||
import android.text.TextUtils
|
||||
import fr.free.nrw.commons.BuildConfig
|
||||
import fr.free.nrw.commons.data.DBOpenHelper
|
||||
import fr.free.nrw.commons.di.CommonsDaggerContentProvider
|
||||
import fr.free.nrw.commons.recentlanguages.RecentLanguagesDao.Table.COLUMN_NAME
|
||||
import fr.free.nrw.commons.recentlanguages.RecentLanguagesDao.Table.TABLE_NAME
|
||||
import javax.inject.Inject
|
||||
import timber.log.Timber
|
||||
import androidx.core.net.toUri
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -23,27 +19,17 @@ class RecentLanguagesContentProvider : CommonsDaggerContentProvider() {
|
|||
|
||||
companion object {
|
||||
private const val BASE_PATH = "recent_languages"
|
||||
val BASE_URI: Uri =
|
||||
Uri.parse(
|
||||
"content://${BuildConfig.RECENT_LANGUAGE_AUTHORITY}/$BASE_PATH"
|
||||
)
|
||||
val BASE_URI: Uri = "content://${BuildConfig.RECENT_LANGUAGE_AUTHORITY}/$BASE_PATH".toUri()
|
||||
|
||||
/**
|
||||
* Append language code to the base URI
|
||||
* @param languageCode Code of a language
|
||||
*/
|
||||
@JvmStatic
|
||||
fun uriForCode(languageCode: String): Uri {
|
||||
return Uri.parse("$BASE_URI/$languageCode")
|
||||
}
|
||||
fun uriForCode(languageCode: String): Uri = "$BASE_URI/$languageCode".toUri()
|
||||
}
|
||||
|
||||
@Inject
|
||||
lateinit var dbOpenHelper: DBOpenHelper
|
||||
|
||||
override fun getType(uri: Uri): String? {
|
||||
return null
|
||||
}
|
||||
override fun getType(uri: Uri): String? = null
|
||||
|
||||
/**
|
||||
* Queries the SQLite database for the recently used languages
|
||||
|
|
@ -60,11 +46,12 @@ class RecentLanguagesContentProvider : CommonsDaggerContentProvider() {
|
|||
selectionArgs: Array<String>?,
|
||||
sortOrder: String?
|
||||
): Cursor? {
|
||||
val queryBuilder = SQLiteQueryBuilder()
|
||||
queryBuilder.tables = TABLE_NAME
|
||||
val db = dbOpenHelper.readableDatabase
|
||||
val queryBuilder = SQLiteQueryBuilder().apply {
|
||||
tables = TABLE_NAME
|
||||
}
|
||||
|
||||
val cursor = queryBuilder.query(
|
||||
db,
|
||||
requireDb(),
|
||||
projection,
|
||||
selection,
|
||||
selectionArgs,
|
||||
|
|
@ -72,7 +59,7 @@ class RecentLanguagesContentProvider : CommonsDaggerContentProvider() {
|
|||
null,
|
||||
sortOrder
|
||||
)
|
||||
cursor.setNotificationUri(context?.contentResolver, uri)
|
||||
cursor.setNotificationUri(requireContext().contentResolver, uri)
|
||||
return cursor
|
||||
}
|
||||
|
||||
|
|
@ -89,12 +76,11 @@ class RecentLanguagesContentProvider : CommonsDaggerContentProvider() {
|
|||
selection: String?,
|
||||
selectionArgs: Array<String>?
|
||||
): Int {
|
||||
val sqlDB = dbOpenHelper.writableDatabase
|
||||
val rowsUpdated: Int
|
||||
if (selection.isNullOrEmpty()) {
|
||||
val id = uri.lastPathSegment?.toInt()
|
||||
?: throw IllegalArgumentException("Invalid URI: $uri")
|
||||
rowsUpdated = sqlDB.update(
|
||||
rowsUpdated = requireDb().update(
|
||||
TABLE_NAME,
|
||||
contentValues,
|
||||
"$COLUMN_NAME = ?",
|
||||
|
|
@ -104,7 +90,7 @@ class RecentLanguagesContentProvider : CommonsDaggerContentProvider() {
|
|||
throw IllegalArgumentException("Parameter `selection` should be empty when updating an ID")
|
||||
}
|
||||
|
||||
context?.contentResolver?.notifyChange(uri, null)
|
||||
requireContext().contentResolver?.notifyChange(uri, null)
|
||||
return rowsUpdated
|
||||
}
|
||||
|
||||
|
|
@ -114,14 +100,13 @@ class RecentLanguagesContentProvider : CommonsDaggerContentProvider() {
|
|||
* @param contentValues : new values to be entered to the database
|
||||
*/
|
||||
override fun insert(uri: Uri, contentValues: ContentValues?): Uri? {
|
||||
val sqlDB = dbOpenHelper.writableDatabase
|
||||
val id = sqlDB.insert(
|
||||
val id = requireDb().insert(
|
||||
TABLE_NAME,
|
||||
null,
|
||||
contentValues
|
||||
)
|
||||
context?.contentResolver?.notifyChange(uri, null)
|
||||
return Uri.parse("$BASE_URI/$id")
|
||||
requireContext().contentResolver?.notifyChange(uri, null)
|
||||
return "$BASE_URI/$id".toUri()
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -129,14 +114,12 @@ class RecentLanguagesContentProvider : CommonsDaggerContentProvider() {
|
|||
* @param uri : contains the URI for recently used languages
|
||||
*/
|
||||
override fun delete(uri: Uri, s: String?, strings: Array<String>?): Int {
|
||||
val db = dbOpenHelper.readableDatabase
|
||||
Timber.d("Deleting recently used language %s", uri.lastPathSegment)
|
||||
val rows = db.delete(
|
||||
val rows = requireDb().delete(
|
||||
TABLE_NAME,
|
||||
"language_code = ?",
|
||||
arrayOf(uri.lastPathSegment)
|
||||
)
|
||||
context?.contentResolver?.notifyChange(uri, null)
|
||||
requireContext().contentResolver?.notifyChange(uri, null)
|
||||
return rows
|
||||
}
|
||||
}
|
||||
|
|
|
|||
32
app/src/main/java/fr/free/nrw/commons/utils/DatabaseUtils.kt
Normal file
32
app/src/main/java/fr/free/nrw/commons/utils/DatabaseUtils.kt
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
package fr.free.nrw.commons.utils
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.database.Cursor
|
||||
|
||||
fun Cursor.getStringArray(name: String): List<String> =
|
||||
stringToArray(getString(name))
|
||||
|
||||
@SuppressLint("Range")
|
||||
fun Cursor.getString(name: String): String =
|
||||
getString(getColumnIndex(name))
|
||||
|
||||
/**
|
||||
* Converts string to List
|
||||
* @param listString comma separated single string from of list items
|
||||
* @return List of string
|
||||
*/
|
||||
fun stringToArray(listString: String?): List<String> {
|
||||
if (listString.isNullOrEmpty()) return emptyList();
|
||||
val elements = listString.split(",".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
||||
return listOf(*elements)
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts string to List
|
||||
* @param list list of items
|
||||
* @return string comma separated single string of items
|
||||
*/
|
||||
fun arrayToString(list: List<String?>?): String? {
|
||||
return list?.joinToString(",")
|
||||
}
|
||||
|
||||
|
|
@ -427,7 +427,6 @@
|
|||
<string name="statistics_wikidata_edits">صور عبر \"الأماكن المجاورة\"</string>
|
||||
<string name="level">المستوى %d</string>
|
||||
<string name="profile_withLevel">%s (المستوى %s)</string>
|
||||
<string name="profile_withoutLevel">%s (%s)</string>
|
||||
<string name="images_uploaded">الصور المرفوعة</string>
|
||||
<string name="image_reverts">لم يتم إرجاع الصور</string>
|
||||
<string name="images_used_by_wiki">الصور المستخدمة</string>
|
||||
|
|
|
|||
233
app/src/main/res/values-b+tt+Cyrl/strings.xml
Normal file
233
app/src/main/res/values-b+tt+Cyrl/strings.xml
Normal file
|
|
@ -0,0 +1,233 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Authors:
|
||||
* Blackisnewyellow
|
||||
* Mansur
|
||||
* Ерней
|
||||
* Ильгиз
|
||||
* Ильнар
|
||||
-->
|
||||
<resources>
|
||||
<string name="commons_facebook">Викиҗыентыкның Facebook бите</string>
|
||||
<string name="commons_github">Викиҗыентыкның гитхабтагы башлангыч кодлары</string>
|
||||
<string name="commons_logo">Викиҗыентык логотипы</string>
|
||||
<string name="commons_website">Викиҗыентыкның веб-сайты</string>
|
||||
<string name="exit_location_picker">Урынны сайлау тәрәзәсеннән чыгарга</string>
|
||||
<string name="submit">Сакларга</string>
|
||||
<string name="add_another_description">Башка тасвирлама өстәргә</string>
|
||||
<string name="add_new_contribution">Яңа кертем өстәргә</string>
|
||||
<string name="add_contribution_from_camera">Камерадан яңа кертем өстәргә</string>
|
||||
<string name="add_contribution_from_photos">Камерадан яңа кертем өстәргә</string>
|
||||
<string name="add_contribution_from_contributions_gallery">Алдагы кертемнәр галереясыннан фото өстәргә</string>
|
||||
<string name="show_captions">Язмалар</string>
|
||||
<string name="row_item_language_description">Тел тасвирламасы</string>
|
||||
<string name="row_item_caption">Язма</string>
|
||||
<string name="show_captions_description">Тасвирлама</string>
|
||||
<string name="nearby_row_image">Сурәт</string>
|
||||
<string name="nearby_all">Барысы</string>
|
||||
<string name="nearby_filter_toggle">Күчертергә</string>
|
||||
<string name="nearby_filter_search">Күренешне эзлә</string>
|
||||
<string name="nearby_filter_state">Урын халәте</string>
|
||||
<string name="appwidget_img">Көн сурәте</string>
|
||||
<plurals name="uploads_pending_notification_indicator">
|
||||
<item quantity="one">%1$d файл йөкләнә</item>
|
||||
<item quantity="few">%1$d файл йөкләнә</item>
|
||||
<item quantity="many">%1$d файл йөкләнә</item>
|
||||
<item quantity="other">%1$d файл йөкләнә</item>
|
||||
</plurals>
|
||||
<plurals name="contributions_subtitle">
|
||||
<item quantity="one">(%1$d)</item>
|
||||
<item quantity="few">(%1$d)</item>
|
||||
<item quantity="many">(%1$d)</item>
|
||||
<item quantity="other">(%1$d)</item>
|
||||
</plurals>
|
||||
<string name="starting_uploads">Йөкләү башлана</string>
|
||||
<plurals name="starting_multiple_uploads">
|
||||
<item quantity="one">%d йөкләүне эшкәртү</item>
|
||||
<item quantity="few">%d йөкләүне эшкәртү</item>
|
||||
<item quantity="many">%d йөкләүне эшкәртү</item>
|
||||
<item quantity="other">%d йөкләүне эшкәртү</item>
|
||||
</plurals>
|
||||
<plurals name="multiple_uploads_title">
|
||||
<item quantity="one">%d йөкләү</item>
|
||||
<item quantity="few">%d йөкләү</item>
|
||||
<item quantity="many">%d йөкләү</item>
|
||||
<item quantity="other">%d йөкләү</item>
|
||||
</plurals>
|
||||
<string name="navigation_item_explore">Тикшерергә</string>
|
||||
<string name="preference_category_appearance">Күренеш</string>
|
||||
<string name="preference_category_general">Гомуми</string>
|
||||
<string name="preference_category_feedback">Кире элемтә</string>
|
||||
<string name="preference_category_privacy">Шәхсилек</string>
|
||||
<string name="app_name">Викиҗыентык</string>
|
||||
<string name="menu_settings">Көйләнмәләр</string>
|
||||
<string name="intent_share_upload_label">Викиҗыентыкка йөкләргә</string>
|
||||
<string name="upload_in_progress">Йөкләү бара...</string>
|
||||
<string name="username">Кулланучы исеме</string>
|
||||
<string name="password">Серсүз</string>
|
||||
<string name="login_credential">Commons Beta хисапъязмагызга керегез</string>
|
||||
<string name="login">Керү</string>
|
||||
<string name="forgot_password">Серсүзне оныттыгызмы?</string>
|
||||
<string name="signup">Теркәлү</string>
|
||||
<string name="logging_in_title">Керү бара…</string>
|
||||
<string name="logging_in_message">Бераз көтегезче...</string>
|
||||
<string name="updating_caption_title">Язмалар һәм тасвирламалар яңартыла</string>
|
||||
<string name="updating_caption_message">Бераз көтегезче...</string>
|
||||
<string name="login_success">Керү уңышлы башкарылды!</string>
|
||||
<string name="login_failed">Системага кереп булмады!</string>
|
||||
<string name="upload_failed">Файл табылмады. Башка файлны кулланып карагызчы.</string>
|
||||
<string name="retry_limit_reached">Кабатлаулар саны нык артып китте! Йөкләүне кире кагыгыз яки яңадан кабатлагыз.</string>
|
||||
<string name="unrestricted_battery_mode">Батәринең оптималь кулланылышын сүндерергәме?</string>
|
||||
<string name="uploading_started">Йөкләү башланды!</string>
|
||||
<string name="upload_completed_notification_title">%1$s төялде!</string>
|
||||
<string name="upload_completed_notification_text">Төялгән файлыгызны карау өчен басыгыз</string>
|
||||
<string name="upload_progress_notification_title_start">Файлны төяү: %s</string>
|
||||
<string name="upload_progress_notification_title_in_progress">%1$s йөкләнә</string>
|
||||
<string name="upload_progress_notification_title_finishing">%1$s йөкләве тәмамлана</string>
|
||||
<string name="upload_failed_notification_title">%1$s йөкләнә алмады</string>
|
||||
<string name="upload_paused_notification_title">%1$s йөкләнүе туктатылып калды</string>
|
||||
<string name="upload_failed_notification_subtitle">Карау өчен басыгыз</string>
|
||||
<string name="upload_paused_notification_subtitle">Карау өчен басыгыз</string>
|
||||
<string name="title_activity_contributions">Минем соңгы төяүләрем</string>
|
||||
<string name="contribution_state_failed">Төяү хатасы</string>
|
||||
<string name="contribution_state_starting">Төяү бара</string>
|
||||
<string name="menu_from_gallery">Галереядан</string>
|
||||
<string name="menu_from_camera">Фото ясарга</string>
|
||||
<string name="menu_nearby">Якында</string>
|
||||
<string name="provider_contributions">Минем төяүләрем</string>
|
||||
<string name="menu_copy_link">Сылтаманы күчереп ал</string>
|
||||
<string name="menu_link_copied">Сылтама алмашу буферына күчереп алынды</string>
|
||||
<string name="menu_share">Уртаклашырга</string>
|
||||
<string name="menu_view_file_page">Файл битен күрсәтергә</string>
|
||||
<string name="share_title_hint">Язма (Зарур)</string>
|
||||
<string name="add_caption_toast">Бу файлның исемен билгеләгезче</string>
|
||||
<string name="share_description_hint">Тасвирлама</string>
|
||||
<string name="share_caption_hint">Язма</string>
|
||||
<string name="login_failed_network">Кереп булмый - челтәр хатасы</string>
|
||||
<string name="login_failed_blocked">Гафу итегез, мондый исемле кулланучы Викиҗыентыкта блокланган булган.</string>
|
||||
<string name="login_failed_generic">Системага кереп булмады!</string>
|
||||
<string name="share_upload_button">Төяү</string>
|
||||
<string name="multiple_share_base_title">Бу файллар төркеме өчен исемне кертегез</string>
|
||||
<string name="menu_upload_single">Төя</string>
|
||||
<string name="categories_search_text_hint">Төркемнәрне сайла</string>
|
||||
<string name="menu_save_categories">Сакла</string>
|
||||
<string name="refresh_button">Яңарту</string>
|
||||
<string name="display_list_button">Исемлек</string>
|
||||
<string name="contributions_subtitle_zero">Төялгән файллар юк әле!</string>
|
||||
<string name="categories_activity_title">Төркемнәр</string>
|
||||
<string name="title_activity_settings">Көйләнмәләр</string>
|
||||
<string name="title_activity_signup">Теркәл</string>
|
||||
<string name="title_activity_featured_images">Сакланган сурәтләр</string>
|
||||
<string name="title_activity_custom_selector">Кулланучы селекторы</string>
|
||||
<string name="title_activity_category_details">Төркем</string>
|
||||
<string name="title_activity_review">Тикшер</string>
|
||||
<string name="menu_about">Кушымта турында</string>
|
||||
<string name="about_privacy_policy">Яшеренлек сәясәте</string>
|
||||
<string name="about_credits">Төзүчеләр</string>
|
||||
<string name="title_activity_about">Кушымта турында</string>
|
||||
<string name="no_email_client">Почта клиенты урнаштырылмаган</string>
|
||||
<string name="provider_categories">Күптән түгел кулланылган төркемнәр</string>
|
||||
<string name="waiting_first_sync">Беренче синхронлаштыруны көтү...</string>
|
||||
<string name="no_uploads_yet">Сез әле бер сурәтне дә төямәдегез.</string>
|
||||
<string name="menu_retry_upload">Кабатла</string>
|
||||
<string name="menu_cancel_upload">Кире как</string>
|
||||
<string name="menu_download">Иңләргә</string>
|
||||
<string name="preference_license">Гадәттәге рөхсәтнамә килешүе</string>
|
||||
<string name="use_previous">Алдагы исемне һәм тамвирламаны куллан</string>
|
||||
<string name="preference_theme">Күренеш</string>
|
||||
<string name="license_name_cc_by_sa_four"> Attribution-ShareAlike 4.0</string>
|
||||
<string name="license_name_cc_by_four"> Attribution 4.0</string>
|
||||
<string name="license_name_cc_by_sa"> Attribution-ShareAlike 3.0</string>
|
||||
<string name="license_name_cc_by"> Attribution 3.0</string>
|
||||
<string name="tutorial_1_text">Викиҗыентыктагы сурәтләр Википедиянең күпчелек күләмендә кулланыла.</string>
|
||||
<string name="tutorial_1_subtext">Төягән сурәтләрегез бөтен дөньядагы кешеләргә белем алырга ярдәм итә ала!</string>
|
||||
<string name="tutorial_2_text">Зинһар, бары тик үзегез ясаган яки төшергән сурәтләрне генә төягез:</string>
|
||||
<string name="tutorial_2_subtext_1">Табигать объектлары (мәсәлән, чәчәкләр, хайваннар, таулар)</string>
|
||||
<string name="tutorial_2_subtext_2">Файдалы җисемнәр (мәсәлән, велосипедлар, вокзаллар)</string>
|
||||
<string name="tutorial_2_subtext_3">Билгеле кешеләр (мәсәлән, мэрыгыз, сез очраткан олимпияче-спортсменнар)</string>
|
||||
<string name="tutorial_3_text">Зинһар, боларны ТӨЯМӘГЕЗ:</string>
|
||||
<string name="tutorial_3_subtext_1">Селфилар яки дусларыгызның фотолары</string>
|
||||
<string name="tutorial_3_subtext_2">Интернеттан иңләгән фотолар</string>
|
||||
<string name="tutorial_3_subtext_3">Ирекле булмаган программаларның скриншотлары</string>
|
||||
<string name="tutorial_4_text">Төяү мисалы:</string>
|
||||
<string name="tutorial_4_subtext_1">Атамасы: Сидней опера театры</string>
|
||||
<string name="welcome_wikipedia_text">Сурәтләрегезне төягез. Википедия мәкаләләрен кызыклырак ясарга булышыгыз!</string>
|
||||
<string name="welcome_wikipedia_subtext">Википедиядә кулланылучы сурәтләр Викиҗыентыкта саклана.</string>
|
||||
<string name="welcome_copyright_text">Төягән сурәтләрегез бөтен дөньядагы кешеләргә белем алырга ярдәм итә ала.</string>
|
||||
<string name="welcome_copyright_subtext">Авторлык хокуклары белән сакланган материалларны, мәсәлән, Интернетта табылган плакатлар, китап тышлары һ. б. ш. сурәтләрне кулланмаска тырышыгыз.</string>
|
||||
<string name="welcome_final_text">Бу сезгә аңлашыламы?</string>
|
||||
<string name="welcome_final_button_text">Әйе!</string>
|
||||
<string name="welcome_help_button_text">Тулырак мәгълүмат</string>
|
||||
<string name="detail_panel_cats_label">Төркемнәр</string>
|
||||
<string name="detail_panel_cats_loading">Йөкләнә...</string>
|
||||
<string name="detail_panel_cats_none">Бернәрсә дә сайланмаган</string>
|
||||
<string name="detail_caption_empty">Язмасыз</string>
|
||||
<string name="detail_description_empty">Тасвирламасыз</string>
|
||||
<string name="detail_discussion_empty">Фикерләшү юк</string>
|
||||
<string name="detail_license_empty">Билгесез лицензия</string>
|
||||
<string name="menu_refresh">Яңарту</string>
|
||||
<string name="storage_permission_title">Тышкы саклагычны куллану рөхсәтен сорау</string>
|
||||
<string name="location_permission_title">Локациягезне табуны сорау</string>
|
||||
<string name="in_app_camera_location_permission_title">Кушымтада ясалган фотолар өчен локацияне яздыр</string>
|
||||
<string name="ok">Ярар</string>
|
||||
<string name="warning">Игътибар</string>
|
||||
<string name="duplicate_file_name">Кабатлана торган файл исеме табылды</string>
|
||||
<string name="upload">Төя</string>
|
||||
<string name="yes">Әйе</string>
|
||||
<string name="no">Юк</string>
|
||||
<string name="media_detail_caption">Язма</string>
|
||||
<string name="media_detail_title">Исем</string>
|
||||
<string name="media_detail_depiction">Тасвирланган феномен</string>
|
||||
<string name="media_detail_description">Тасвирлама</string>
|
||||
<string name="media_detail_discussion">Фикер алышу</string>
|
||||
<string name="media_detail_author">Автор</string>
|
||||
<string name="media_detail_uploader">Төяп куючы</string>
|
||||
<string name="media_detail_uploaded_date">Төяү вакыты</string>
|
||||
<string name="media_detail_license">Лицензия</string>
|
||||
<string name="media_detail_coordinates">Координатлар</string>
|
||||
<string name="media_detail_coordinates_empty">Билгеләнмәгән</string>
|
||||
<string name="become_a_tester_title">Бета-тестерга әйләнергә</string>
|
||||
<string name="logout_verification">Сез чыннан да чыгарга телисезме?</string>
|
||||
<string name="no_subcategory_found">Астөркемнәр табылмады.</string>
|
||||
<string name="cancel">Баш тарту</string>
|
||||
<string name="navigation_drawer_open">Ачарга</string>
|
||||
<string name="navigation_drawer_close">Ябарга</string>
|
||||
<string name="navigation_item_home">Баш бит</string>
|
||||
<string name="navigation_item_upload">Төяргә</string>
|
||||
<string name="navigation_item_nearby">Якын-тирәдә</string>
|
||||
<string name="navigation_item_about">Кушымта турында</string>
|
||||
<string name="navigation_item_settings">Көйләнмәләр</string>
|
||||
<string name="navigation_item_feedback">Кире элемтә</string>
|
||||
<string name="navigation_item_feedback_github">GitHub аркылы кире элемтә</string>
|
||||
<string name="navigation_item_logout">Чыгарга</string>
|
||||
<string name="navigation_item_info">Кулланма</string>
|
||||
<string name="navigation_item_notification">Белдермәләр</string>
|
||||
<string name="navigation_item_review">Тикшерү</string>
|
||||
<string name="no_description_found">тасвирлама табылмады</string>
|
||||
<string name="nearby_info_menu_commons_article">Файлның Викиҗыентыктагы бите</string>
|
||||
<string name="nearby_info_menu_wikidata_article">Викимәгълүмат элементы</string>
|
||||
<string name="nearby_info_menu_wikipedia_article">Википедия мәкаләсе</string>
|
||||
<string name="upload_problem_image_dark">Сурәт артык караңгы.</string>
|
||||
<string name="upload_problem_image_duplicate">Бу сурәт Викиҗыентыкта бар инде.</string>
|
||||
<string name="upload_problem_different_geolocation">Бу сурәт башка урында ясалган булган.</string>
|
||||
<string name="upload_problem_do_you_continue">Барыбер бу сурәтне төяргә телисезме?</string>
|
||||
<string name="upload_connection_error_alert_title">Тоташтыру хатасы</string>
|
||||
<string name="upload_problem_image">Сурәттә читенлекләр табылды</string>
|
||||
<string name="use_external_storage">Кушымтада ясалган фотоларны сакла</string>
|
||||
<string name="use_external_storage_summary">Җайланма камерасы ярдәмендә эшләнгән фотоларны җайланмада сакла</string>
|
||||
<string name="null_url">Хата! Сылтама табылмады</string>
|
||||
<string name="nominate_deletion">Бетерергә тәкъдим ит</string>
|
||||
<string name="nominated_for_deletion">Бу сурәтне бетерергә тәкъдим ителде.</string>
|
||||
<string name="nominated_see_more">Күбрәк мәгълүмат өчен битне карагыз</string>
|
||||
<string name="skip_login">Калдырып үт</string>
|
||||
<string name="navigation_item_login">Керү</string>
|
||||
<string name="wikicode_copied">Викитекст алмашу буферына күчереп алынды</string>
|
||||
<string name="nearby_directions">Юнәлешләр</string>
|
||||
<string name="nearby_wikidata">Викимәгълүмат</string>
|
||||
<string name="nearby_wikipedia">Википедия</string>
|
||||
<string name="nearby_commons">Викиҗыентык</string>
|
||||
<string name="about_rate_us">Безне бәяләгез</string>
|
||||
<string name="about_faq">Еш бирелгән сораулар (ЕБС, ЧаВо)</string>
|
||||
<string name="rotate">Бору</string>
|
||||
<string name="storage_permissions_denied">Саклауга рөхсәтләр кире кагылды</string>
|
||||
<string name="unable_to_share_upload_item">Бу объект белән уртаклашу мөмкин түгел</string>
|
||||
</resources>
|
||||
|
|
@ -271,6 +271,7 @@
|
|||
<string name="preference_author_name_toggle_summary">При качването използвайте персонализирано авторско име вместо потребителското си име</string>
|
||||
<string name="preference_author_name">Персонализирано авторско име</string>
|
||||
<string name="nearby_fragment">Наблизо</string>
|
||||
<string name="notifications">Известия</string>
|
||||
<string name="read_notifications">Известия (прочетени)</string>
|
||||
<string name="list_sheet">Списък</string>
|
||||
<string name="next">Следваща</string>
|
||||
|
|
|
|||
|
|
@ -379,7 +379,7 @@
|
|||
<string name="wikipedia_instructions_step_3">3. Хьайн суьртана догӀу йаззаман дакъа лаха</string>
|
||||
<string name="wikipedia_instructions_step_4">4. «Хийца» иконкин тӀетаӀайе (къоламах тера йу) хӀокху декъана</string>
|
||||
<string name="wikipedia_instructions_step_5">5. Вики-код йогӀучу метте дӀайазйе</string>
|
||||
<string name="wikipedia_instructions_step_7">7. Йаззам дӀайазбе</string>
|
||||
<string name="wikipedia_instructions_step_7">7. Йаззам дӀайазбан</string>
|
||||
<string name="copy_wikicode_to_clipboard">Вики-код буфер чу копийе</string>
|
||||
<string name="pause">пауза</string>
|
||||
<string name="resume">кхидӀа</string>
|
||||
|
|
|
|||
|
|
@ -408,7 +408,6 @@
|
|||
<string name="statistics_wikidata_edits">Obrázky přes „Místa v okolí“</string>
|
||||
<string name="level">Úroveň %d</string>
|
||||
<string name="profile_withLevel">%s (úroveň %s)</string>
|
||||
<string name="profile_withoutLevel">%s (%s)</string>
|
||||
<string name="images_uploaded">Nahrané obrázky</string>
|
||||
<string name="image_reverts">Nerevertované obrázky</string>
|
||||
<string name="images_used_by_wiki">Použitých obrázků</string>
|
||||
|
|
|
|||
|
|
@ -378,7 +378,6 @@
|
|||
<string name="statistics_wikidata_edits">Billeder via \"Steder i nærheden\"</string>
|
||||
<string name="level">Niveau %d</string>
|
||||
<string name="profile_withLevel">%s (Niveau %s)</string>
|
||||
<string name="profile_withoutLevel">%s (%s)</string>
|
||||
<string name="images_uploaded">Uploadede billeder</string>
|
||||
<string name="image_reverts">Billeder, som ikke er blevet trukket tilbage</string>
|
||||
<string name="images_used_by_wiki">Billeder brugt</string>
|
||||
|
|
|
|||
|
|
@ -412,7 +412,6 @@
|
|||
<string name="statistics_wikidata_edits">Bilder über „Orte in der Nähe“</string>
|
||||
<string name="level">Level %d</string>
|
||||
<string name="profile_withLevel">%s (Level %s)</string>
|
||||
<string name="profile_withoutLevel">%s (%s)</string>
|
||||
<string name="images_uploaded">Hochgeladene Bilder</string>
|
||||
<string name="image_reverts">Bilder nicht zurückgesetzt</string>
|
||||
<string name="images_used_by_wiki">Verwendete Bilder</string>
|
||||
|
|
|
|||
|
|
@ -391,7 +391,6 @@
|
|||
<string name="statistics_wikidata_edits">Εικόνες μέσω «Κοντινά μέρη»</string>
|
||||
<string name="level">Επίπεδο %d</string>
|
||||
<string name="profile_withLevel">%s (Επίπεδο %s)</string>
|
||||
<string name="profile_withoutLevel">%s (%s)</string>
|
||||
<string name="images_uploaded">Εικόνες που μεταφορτώθηκαν</string>
|
||||
<string name="image_reverts">Εικόνες που δεν ανεστράφησαν</string>
|
||||
<string name="images_used_by_wiki">Εικόνες που χρησιμοποιήθηκαν</string>
|
||||
|
|
|
|||
|
|
@ -423,7 +423,6 @@
|
|||
<string name="statistics_wikidata_edits">Imágenes vía \"Sitios Cercanos\"</string>
|
||||
<string name="level">Nivel %d</string>
|
||||
<string name="profile_withLevel">%s (Nivel %s)</string>
|
||||
<string name="profile_withoutLevel">%s (%s)</string>
|
||||
<string name="images_uploaded">Imágenes subidas</string>
|
||||
<string name="image_reverts">Imágenes no revertidas</string>
|
||||
<string name="images_used_by_wiki">Imágenes utilizadas</string>
|
||||
|
|
|
|||
|
|
@ -363,7 +363,6 @@
|
|||
<string name="statistics_wikidata_edits">Kuvia läheltä</string>
|
||||
<string name="level">Taso %d</string>
|
||||
<string name="profile_withLevel">%s (taso %s)</string>
|
||||
<string name="profile_withoutLevel">%s (%s)</string>
|
||||
<string name="images_uploaded">Kuvia tallennettu</string>
|
||||
<string name="image_reverts">Kuvia ei palautettu</string>
|
||||
<string name="images_used_by_wiki">Kuvia käytetty</string>
|
||||
|
|
|
|||
|
|
@ -418,7 +418,6 @@
|
|||
<string name="statistics_wikidata_edits">Images par « Lieux à proximité »</string>
|
||||
<string name="level">Niveau %d</string>
|
||||
<string name="profile_withLevel">%s (niveau %s)</string>
|
||||
<string name="profile_withoutLevel">%s (%s)</string>
|
||||
<string name="images_uploaded">Images téléversées</string>
|
||||
<string name="image_reverts">Images non annulées</string>
|
||||
<string name="images_used_by_wiki">Images utilisées</string>
|
||||
|
|
|
|||
|
|
@ -235,7 +235,7 @@
|
|||
<string name="navigation_item_about">A proposito</string>
|
||||
<string name="navigation_item_settings">Parametros</string>
|
||||
<string name="navigation_item_feedback">Commentario</string>
|
||||
<string name="navigation_item_feedback_github">Retroaction per GitHub</string>
|
||||
<string name="navigation_item_feedback_github">Commentarios per GitHub</string>
|
||||
<string name="navigation_item_logout">Clauder session</string>
|
||||
<string name="navigation_item_info">Tutorial</string>
|
||||
<string name="navigation_item_notification">Notificationes</string>
|
||||
|
|
@ -366,7 +366,6 @@
|
|||
<string name="statistics_wikidata_edits">Imagines via “Locos a proximitate”</string>
|
||||
<string name="level">Nivello %d</string>
|
||||
<string name="profile_withLevel">%s (Nivello %s)</string>
|
||||
<string name="profile_withoutLevel">%s (%s)</string>
|
||||
<string name="images_uploaded">Imagines incargate</string>
|
||||
<string name="image_reverts">Imagines non revertite</string>
|
||||
<string name="images_used_by_wiki">Imagines usate</string>
|
||||
|
|
@ -719,8 +718,8 @@
|
|||
<string name="device_model">Modello del apparato</string>
|
||||
<string name="device_name">Nomine del apparato</string>
|
||||
<string name="network_type">Typo de rete</string>
|
||||
<string name="thanks_feedback">Gratias pro dar retroaction</string>
|
||||
<string name="error_feedback">Error durante le invio del retroaction</string>
|
||||
<string name="thanks_feedback">Gratias pro dar commentario</string>
|
||||
<string name="error_feedback">Error durante le invio del commentario</string>
|
||||
<string name="enter_description">Que es tu commentario?</string>
|
||||
<string name="your_feedback">Tu commentario</string>
|
||||
<string name="mark_as_not_for_upload">Marcar como non a incargar</string>
|
||||
|
|
@ -784,7 +783,7 @@
|
|||
<string name="is_at_a_different_place_wikidata">‘%1$s’ se trova in un altere loco.</string>
|
||||
<string name="is_at_a_different_place_please_specify_the_correct_place_below_if_possible_tell_us_the_correct_latitude_longitude">‘%1$s’ es in un altere loco. Per favor specifica le loco correcte hic infra, e si possibile, indica le latitude e longitude correcte.</string>
|
||||
<string name="other_problem_or_information_please_explain_below">Altere problema o information (per favor explica hic infra).</string>
|
||||
<string name="feedback_destination_note">Tu retroaction apparera sur le sequente pagina wiki: <a href=\"https://commons.wikimedia.org/wiki/Commons:Mobile_app/Feedback\">Commons:Mobile app/Feedback</a></string>
|
||||
<string name="feedback_destination_note">Tu commentario apparera sur le sequente pagina wiki: <a href=\"https://commons.wikimedia.org/wiki/Commons:Mobile_app/Feedback\">Commons:Mobile app/Feedback</a></string>
|
||||
<string name="are_you_sure_that_you_want_cancel_all_the_uploads">Es tu secur de voler cancellar tote le incargamentos?</string>
|
||||
<string name="cancelling_all_the_uploads">Cancella tote le incargamentos…</string>
|
||||
<string name="uploads">Incargamentos</string>
|
||||
|
|
|
|||
|
|
@ -369,7 +369,6 @@
|
|||
<string name="statistics_wikidata_edits">Imaji tra \"Loki Vicina\"</string>
|
||||
<string name="level">Nivelo %d</string>
|
||||
<string name="profile_withLevel">%s (Nivelo %s)</string>
|
||||
<string name="profile_withoutLevel">%s (%s)</string>
|
||||
<string name="images_uploaded">Imaji sendita</string>
|
||||
<string name="image_reverts">Imaji ne reversionita</string>
|
||||
<string name="images_used_by_wiki">Imaji uzita</string>
|
||||
|
|
|
|||
|
|
@ -392,7 +392,6 @@
|
|||
<string name="statistics_wikidata_edits">Immagini tramite \"Luoghi nelle vicinanze\"</string>
|
||||
<string name="level">Livello %d</string>
|
||||
<string name="profile_withLevel">%s (Livello %s)</string>
|
||||
<string name="profile_withoutLevel">%s (%s)</string>
|
||||
<string name="images_uploaded">Immagini caricate</string>
|
||||
<string name="image_reverts">Immagini non ripristinate</string>
|
||||
<string name="images_used_by_wiki">Immagini utilizzate</string>
|
||||
|
|
|
|||
|
|
@ -403,7 +403,6 @@
|
|||
<string name="statistics_wikidata_edits">תמונות דרך \"מקומות בסביבה\"</string>
|
||||
<string name="level">רמה %d</string>
|
||||
<string name="profile_withLevel">%s (רמה %s)</string>
|
||||
<string name="profile_withoutLevel">%s (%s)</string>
|
||||
<string name="images_uploaded">תמונות שהועלו</string>
|
||||
<string name="image_reverts">תמונות שלא שוחזרו</string>
|
||||
<string name="images_used_by_wiki">תמונות בשימוש</string>
|
||||
|
|
|
|||
|
|
@ -370,7 +370,6 @@
|
|||
<string name="statistics_wikidata_edits">სურათები „ახლომდებარე ადგილები“ -დან</string>
|
||||
<string name="level">დონე %d</string>
|
||||
<string name="profile_withLevel">%s (დონე %s)</string>
|
||||
<string name="profile_withoutLevel">%s (%s)</string>
|
||||
<string name="images_uploaded">სურათები ატვირთულია</string>
|
||||
<string name="image_reverts">სურათები არ დაბრუნებულა</string>
|
||||
<string name="images_used_by_wiki">სურათები გამოიყენება</string>
|
||||
|
|
|
|||
|
|
@ -222,6 +222,7 @@
|
|||
<string name="media_detail_description">설명</string>
|
||||
<string name="media_detail_discussion">토론</string>
|
||||
<string name="media_detail_author">저자</string>
|
||||
<string name="media_detail_uploader">올린 사람</string>
|
||||
<string name="media_detail_uploaded_date">올린 날짜</string>
|
||||
<string name="media_detail_license">라이선스</string>
|
||||
<string name="media_detail_coordinates">좌표</string>
|
||||
|
|
@ -380,7 +381,6 @@
|
|||
<string name="statistics_wikidata_edits">\"주변 장소\" 경유 이미지</string>
|
||||
<string name="level">레벨 %d</string>
|
||||
<string name="profile_withLevel">%s (레벨 %s)</string>
|
||||
<string name="profile_withoutLevel">%s (%s)</string>
|
||||
<string name="images_uploaded">사진 업로드됨</string>
|
||||
<string name="images_used_by_wiki">사용된 이미지</string>
|
||||
<string name="achievements_share_message">친구와 성과를 공유하세요!</string>
|
||||
|
|
@ -539,6 +539,8 @@
|
|||
<string name="title_for_media">미디어</string>
|
||||
<string name="title_for_child_classes">자식 클래스</string>
|
||||
<string name="title_for_parent_classes">상위 클래스</string>
|
||||
<string name="title_for_subcategories">하위 분류</string>
|
||||
<string name="title_for_parent_categories">상위 분류</string>
|
||||
<string name="upload_nearby_place_found_title">주변 장소 발견</string>
|
||||
<string name="upload_nearby_place_found_description_plural">%1$s의 사진이 맞습니까?</string>
|
||||
<string name="upload_nearby_place_found_description_singular">%1$s의 사진이 맞습니까?</string>
|
||||
|
|
@ -583,8 +585,10 @@
|
|||
<string name="menu_set_avatar">아바타로 설정</string>
|
||||
<string name="leaderboard_yearly">매년</string>
|
||||
<string name="leaderboard_weekly">매주</string>
|
||||
<string name="leaderboard_all_time">항상</string>
|
||||
<string name="leaderboard_upload">업로드</string>
|
||||
<string name="leaderboard_nearby">근처</string>
|
||||
<string name="leaderboard_used">사용됨</string>
|
||||
<string name="leaderboard_my_rank_button_text">내 순위</string>
|
||||
<string name="limited_connection_mode">제한된 연결 모드</string>
|
||||
<string name="statistics_quality">고품질 사진</string>
|
||||
|
|
|
|||
|
|
@ -370,7 +370,6 @@
|
|||
<string name="statistics_wikidata_edits">\"Джууукъдагъы Джерле\" юсю бла суратла</string>
|
||||
<string name="level">Дараджа %d</string>
|
||||
<string name="profile_withLevel">%s (Дараджа %s)</string>
|
||||
<string name="profile_withoutLevel">%s (%s)</string>
|
||||
<string name="images_uploaded">Суратла Джюклендиле</string>
|
||||
<string name="image_reverts">Суратла Кери Алынмадыла</string>
|
||||
<string name="images_used_by_wiki">Суратла Хайырландыла</string>
|
||||
|
|
|
|||
|
|
@ -308,7 +308,6 @@
|
|||
<string name="statistics_featured">Bemierkenswäert Biller</string>
|
||||
<string name="level">Niveau %d</string>
|
||||
<string name="profile_withLevel">%s (Niveau %s)</string>
|
||||
<string name="profile_withoutLevel">%s (%s)</string>
|
||||
<string name="images_uploaded">Eropgeluede Biller</string>
|
||||
<string name="image_reverts">Biller net zréckgesat</string>
|
||||
<string name="images_used_by_wiki">Benotzte Biller</string>
|
||||
|
|
|
|||
|
|
@ -354,7 +354,6 @@
|
|||
<string name="statistics_wikidata_edits">Vaizdai per „Netoliese esančios vietos“</string>
|
||||
<string name="level">Lygis %d</string>
|
||||
<string name="profile_withLevel">%s (%s lygis)</string>
|
||||
<string name="profile_withoutLevel">%s (%s)</string>
|
||||
<string name="images_uploaded">Vaizdai įkelti</string>
|
||||
<string name="image_reverts">Paveikslėliai negrąžinti</string>
|
||||
<string name="images_used_by_wiki">Naudoti vaizdai</string>
|
||||
|
|
|
|||
|
|
@ -372,7 +372,6 @@
|
|||
<string name="statistics_wikidata_edits">Слики преку „Околни места“</string>
|
||||
<string name="level">Степен %d</string>
|
||||
<string name="profile_withLevel">%s (Степен %s)</string>
|
||||
<string name="profile_withoutLevel">%s (%s)</string>
|
||||
<string name="images_uploaded">Подигнати слики</string>
|
||||
<string name="image_reverts">Неоткажани слики</string>
|
||||
<string name="images_used_by_wiki">Употребени слики</string>
|
||||
|
|
|
|||
|
|
@ -394,7 +394,6 @@
|
|||
<string name="statistics_wikidata_edits">Afbeeldingen via \"Plaatsen in de buurt\"</string>
|
||||
<string name="level">Niveau %d</string>
|
||||
<string name="profile_withLevel">%s (Niveau %s)</string>
|
||||
<string name="profile_withoutLevel">%s (%s)</string>
|
||||
<string name="images_uploaded">Geüploade afbeeldingen</string>
|
||||
<string name="image_reverts">Afbeeldingen niet teruggedraaid</string>
|
||||
<string name="images_used_by_wiki">Gebruikte afbeeldingen</string>
|
||||
|
|
|
|||
|
|
@ -410,7 +410,6 @@
|
|||
<string name="statistics_wikidata_edits">Obrazy za pośrednictwem \"Pobliskie miejsca\"</string>
|
||||
<string name="level">Poziom %d</string>
|
||||
<string name="profile_withLevel">%s (Poziom %s)</string>
|
||||
<string name="profile_withoutLevel">%s (%s)</string>
|
||||
<string name="images_uploaded">Przesłane obrazy</string>
|
||||
<string name="image_reverts">Nie wycofane obrazy</string>
|
||||
<string name="images_used_by_wiki">Wykorzystane obrazy</string>
|
||||
|
|
|
|||
|
|
@ -370,7 +370,6 @@
|
|||
<string name="statistics_wikidata_edits">Plance për \"Pòst davzin\"</string>
|
||||
<string name="level">Livel %d</string>
|
||||
<string name="profile_withLevel">%s (Livel %s)</string>
|
||||
<string name="profile_withoutLevel">%s (%s)</string>
|
||||
<string name="images_uploaded">Plance carià</string>
|
||||
<string name="image_reverts">Plance nen anulà</string>
|
||||
<string name="images_used_by_wiki">Plance dovrà</string>
|
||||
|
|
|
|||
|
|
@ -10,42 +10,42 @@
|
|||
-->
|
||||
<resources>
|
||||
<string name="commons_facebook">د خونديځ فيسبوک پاڼه</string>
|
||||
<string name="commons_github">خونديځ ګيټهوب سرچينه کوډ</string>
|
||||
<string name="commons_github">خونديځ گيټهاب سرچينه کوډ</string>
|
||||
<string name="commons_logo">خونديځ نښان</string>
|
||||
<string name="commons_website">خونديځ وېبپاڼه</string>
|
||||
<string name="exit_location_picker">له ځای ټاکونکي وتل</string>
|
||||
<string name="submit">سپارل</string>
|
||||
<string name="add_another_description">بل سپيناوی ورزياتول</string>
|
||||
<string name="add_new_contribution">نوې ونډې ورزياتول</string>
|
||||
<string name="add_contribution_from_camera">د کامرې له لارې ونډه ورزياتول</string>
|
||||
<string name="add_contribution_from_photos">انځورونو له لارې ونډه ورزياتول</string>
|
||||
<string name="add_contribution_from_contributions_gallery">د پخوانيو ونډو له انځورتونه د ونډې ورزياتول</string>
|
||||
<string name="add_another_description">بل څرگنداوی ورگډول</string>
|
||||
<string name="add_new_contribution">نوې ونډې ورگډول</string>
|
||||
<string name="add_contribution_from_camera">د کامرې له لارې ونډه ورگډول</string>
|
||||
<string name="add_contribution_from_photos">انځورونو له لارې ونډه ورگډول</string>
|
||||
<string name="add_contribution_from_contributions_gallery">د پخوانيو ونډو له انځورتونه د ونډې ورگډول</string>
|
||||
<string name="show_captions">نيونگې</string>
|
||||
<string name="row_item_language_description">ژبې سپيناوی</string>
|
||||
<string name="row_item_language_description">ژبې څرگنداوی</string>
|
||||
<string name="row_item_caption">نيونگ</string>
|
||||
<string name="show_captions_description">سپيناوی</string>
|
||||
<string name="show_captions_description">څرگنداوی</string>
|
||||
<string name="nearby_row_image">انځور</string>
|
||||
<string name="nearby_all">ټول</string>
|
||||
<string name="nearby_filter_toggle">پورته کول</string>
|
||||
<string name="nearby_filter_search">لټون ليد</string>
|
||||
<string name="nearby_filter_toggle">پورته بدلول</string>
|
||||
<string name="nearby_filter_search">لټون کتنه</string>
|
||||
<string name="nearby_filter_state">ځای حالت</string>
|
||||
<string name="appwidget_img">ورځې انځور</string>
|
||||
<plurals name="uploads_pending_notification_indicator">
|
||||
<item quantity="one">%1$d دوتنه پورته کول</item>
|
||||
<item quantity="other"> %1$d دوتنې پورته کول</item>
|
||||
<item quantity="one">%1$d دوتنه راپورتهکول</item>
|
||||
<item quantity="other"> %1$d دوتنې راپورتهکول</item>
|
||||
</plurals>
|
||||
<plurals name="contributions_subtitle">
|
||||
<item quantity="one">(%1$d)</item>
|
||||
<item quantity="other">(%1$d)</item>
|
||||
</plurals>
|
||||
<string name="starting_uploads">پورته کولو پيل</string>
|
||||
<string name="starting_uploads">راپورتهکول پيلول</string>
|
||||
<plurals name="starting_multiple_uploads">
|
||||
<item quantity="one">جريان %d پورته کول</item>
|
||||
<item quantity="other">پورته کولو %d جريان</item>
|
||||
<item quantity="one">راپورتهکولو %d بهير</item>
|
||||
<item quantity="other">راپورتهکولو %d بهير</item>
|
||||
</plurals>
|
||||
<plurals name="multiple_uploads_title">
|
||||
<item quantity="one">%d upload</item>
|
||||
<item quantity="other">%d پورته کول</item>
|
||||
<item quantity="other">%d راپورتهکول</item>
|
||||
</plurals>
|
||||
<plurals name="share_license_summary">
|
||||
<item quantity="one">دا انځور به د منښتليک %1$s لاندې وي</item>
|
||||
|
|
@ -100,7 +100,7 @@
|
|||
<string name="contribution_state_queued">لږ</string>
|
||||
<string name="contribution_state_failed">نابريال شو</string>
|
||||
<string name="contribution_state_in_progress">%1$d%% بشپړ</string>
|
||||
<string name="contribution_state_starting">د برسېرېدلو په حال کې…</string>
|
||||
<string name="contribution_state_starting">راپورتهکېږي</string>
|
||||
<string name="menu_from_gallery">له انځورتون څخه</string>
|
||||
<string name="menu_from_camera">انځور اخيستل</string>
|
||||
<string name="menu_nearby">نژدې</string>
|
||||
|
|
@ -114,8 +114,8 @@
|
|||
<string name="share_description_hint">څرگنداوی</string>
|
||||
<string name="share_caption_hint">نيونگ</string>
|
||||
<string name="login_failed_network">غونډال ته ننوتنه ناشونې ده - د جال پاتې راتلنه</string>
|
||||
<string name="login_failed_throttled">ډیری ناکامه هڅې. لطفا څو دقیقې وروسته بیا هڅه وکړئ.</string>
|
||||
<string name="login_failed_blocked">بخښنه غواړو، په دي کارن د کامنز لخوا بنديز ولګول شو</string>
|
||||
<string name="login_failed_throttled">ډېرې ناکامه هڅې. لطفا څو دقیقې وروسته بیا هڅه وکړئ.</string>
|
||||
<string name="login_failed_blocked">بخښنه غواړو، په دې کارن د کامنز لخوا بنديز ولگول شو</string>
|
||||
<string name="login_failed_2fa_needed">تاسو بايد خپل دوه لامليز تاييد کوډ ورکړئ.</string>
|
||||
<string name="login_failed_email_auth_needed">ستاسو برېښليک پتې ته د ننوتلو تاييد کوډ لېږل شوی دی. مهرباني وکړئ د ننوتلو لپاره کوډ ورکړئ.</string>
|
||||
<string name="login_failed_generic">غونډال کې ننوتنه نابريالۍ شوه</string>
|
||||
|
|
@ -187,9 +187,9 @@
|
|||
<string name="detail_panel_cats_loading">رابرسېرېږي...</string>
|
||||
<string name="detail_panel_cats_none">هېڅ هم نه دی ټاکل شوی</string>
|
||||
<string name="detail_caption_empty">هيڅ نيونگ نشته</string>
|
||||
<string name="detail_description_empty">څرگندونه نشته</string>
|
||||
<string name="detail_description_empty">څرگنداوی نشته</string>
|
||||
<string name="detail_discussion_empty">هيڅ شننه نشته</string>
|
||||
<string name="detail_license_empty">نامعلوم جواز</string>
|
||||
<string name="detail_license_empty">ناجوت منښتليک</string>
|
||||
<string name="menu_refresh">تازه کول</string>
|
||||
<string name="storage_permission_title">د زېرمه کولو د پرېښولي غوښتنه کول</string>
|
||||
<string name="read_storage_permission_rationale">اړينه پرېښولی: بهرنۍ زېرمه ولولئ. کاريال ستاسو انځورتونه ته پرته له دې لاسرسی نشي موندلی.</string>
|
||||
|
|
@ -211,7 +211,7 @@
|
|||
<string name="media_detail_coordinates">کورډيناټونه</string>
|
||||
<string name="media_detail_coordinates_empty">هېڅ نه دي چمتو شوي</string>
|
||||
<string name="become_a_tester_title">ازمېښتي ازمايښتگر شئ</string>
|
||||
<string name="welcome_image_welcome_wikipedia">ويکيپېډياښه راغلئ</string>
|
||||
<string name="welcome_image_welcome_wikipedia">ويکيپېډيا ته ښه راغلئ</string>
|
||||
<string name="welcome_image_welcome_copyright">لمېسلرښتې هرکلی</string>
|
||||
<string name="cancel">ناگارل</string>
|
||||
<string name="navigation_drawer_open">پرانېستل</string>
|
||||
|
|
@ -237,6 +237,7 @@
|
|||
<string name="nearby_wikidata">ويکياومتوک</string>
|
||||
<string name="nearby_wikipedia">ويکيپېډيا</string>
|
||||
<string name="nearby_commons">خونديځ</string>
|
||||
<string name="about_rate_us">و مو ارزوئ</string>
|
||||
<string name="about_faq">ډځپ</string>
|
||||
<string name="user_guide">کارن لارښود</string>
|
||||
<string name="welcome_skip_button">ښوونې پرېښودل</string>
|
||||
|
|
@ -250,6 +251,7 @@
|
|||
<string name="about_translate_proceed">پرمخځه</string>
|
||||
<string name="about_translate_cancel">ناگارل</string>
|
||||
<string name="retry">بيا هڅهکول</string>
|
||||
<string name="showcase_view_whole_nearby_activity">دا تاسو ته نږدې ځايونه دي چې د ويکيپېډيا ليکنې يې د ښودلو لپاره انځورونو ته اړتيا لري.\n\n\'د دې ځای پلټل\' باندې کليک کولو سره نخچه تاله کوي او د هغه ځای شاوخوا نږدې سيمې لټون پيلول.</string>
|
||||
<string name="showcase_view_needs_photo">داځای انځور ته اړتيا لري.</string>
|
||||
<string name="showcase_view_has_photo">دا ځای لادمخه انځور لري.</string>
|
||||
<string name="showcase_view_no_longer_exists">دا ځای نور شتون نه لري.</string>
|
||||
|
|
@ -265,5 +267,39 @@
|
|||
<string name="search_recent_header">وروستۍ پلټنې:</string>
|
||||
<string name="provider_searches">وروستۍ پلټل شوې پوښتنې</string>
|
||||
<string name="provider_recent_languages">وروستۍ ژبې پوښتنې</string>
|
||||
<string name="search_tab_title_media">رسنۍ</string>
|
||||
<string name="search_tab_title_categories">وېشنيزې</string>
|
||||
<string name="search_tab_title_depictions">توکي</string>
|
||||
<string name="explore_tab_title_featured">ټاکلې</string>
|
||||
<string name="explore_tab_title_mobile">موبايل له لارې راپورتهشوی</string>
|
||||
<string name="explore_tab_title_map">نخشه</string>
|
||||
<string name="successful_wikidata_edit">انځور په ویکياومتوک کې %1$s ته ورگډ شو!</string>
|
||||
<string name="ends_on">پای ته رسېږي په:</string>
|
||||
<string name="display_campaigns">ټاکنيزېسيالۍ ښکارهکول</string>
|
||||
<string name="display_campaigns_explanation">روانې ټاکنيزېسيالۍ وگورئ</string>
|
||||
<string name="option_allow">پرېښول</string>
|
||||
<string name="option_dismiss">تړل</string>
|
||||
<string name="open_document_photo_picker_title">د لاسوند پربنسټ انځور راخيستونکی کارول</string>
|
||||
<string name="error_processing_image">د انځور پروسسکولو پرمهال تېروتنه رامنځته شوه. مهرباني وکړئ بيا هڅه وکړئ!</string>
|
||||
<string name="getting_edit_token">د سمون لپاره نښه ترلاسه کول</string>
|
||||
<string name="check_category_adding_template">د وېشنيزې سمکتنې لپاره کينډۍ ورگډول</string>
|
||||
<string name="check_category_notification_title">د %1$s وېشنيزې سمکتنې لپاره غوښتنهکول</string>
|
||||
<string name="check_category_edit_summary">وېشنيزې سمکتنې غوښتنهکول</string>
|
||||
<string name="check_category_success_title">وېشنيزې سمکتنې غوښتنه وشوه</string>
|
||||
<string name="check_category_failure_title">د وېشنيزې سمکتنې غوښتنه کار نه کوي</string>
|
||||
<string name="check_category_success_message">د %1$s وېشنيزې سمکتنې لپاره غوښتنه وشوه</string>
|
||||
<string name="check_category_failure_message">د %1$s سمکتنې لپاره غوښتنه نشي کېدای</string>
|
||||
<string name="check_category_toast">د %1$s وېشنيزې سمکتنې لپاره غوښتنهکول</string>
|
||||
<string name="nominate_for_deletion_done">وشو</string>
|
||||
<string name="send_thank_success_title">مننې لېږل: برياليتوب</string>
|
||||
<string name="send_thank_success_message">%1$s ته په برياليتوب سره مننه ولېږل شوه</string>
|
||||
<string name="send_thank_failure_message">%1$s ته د مننې لېږلو کې پاتې راغی</string>
|
||||
<string name="send_thank_failure_title">مننې لېږل: ناکامي</string>
|
||||
<string name="send_thank_toast">%1$s لپاره د مننې لېږل</string>
|
||||
<string name="review_copyright">ايا دا ډ لمېسلرښتو سره سم دی؟</string>
|
||||
<string name="review_category">ايا دا په سمه توگه ډلبندي شوي دي؟</string>
|
||||
<string name="review_spam">ايا دا د منلو وړ دي؟</string>
|
||||
<string name="review_thanks">ايا تاسو غواړئ له ونډهوال نه مننه وکړئ؟</string>
|
||||
<string name="review_spam_explanation">که دا انځور ټولگټی نه وي؛ نو ړنگېدو ته د نوماندولو لپاره يې په نه کليک وکړئ.</string>
|
||||
<string name="account">گڼون</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -173,6 +173,7 @@
|
|||
<string name="delete">{{Identical|Delete}}</string>
|
||||
<string name="statistics">Èstatistik</string>
|
||||
<string name="statistics_featured">To see the correct translation for your language, please go to https://commons.wikimedia.org/wiki/Commons:Featured_pictures and select your language in \"This project page in other languages\".</string>
|
||||
<string name="profile_withoutLevel">{{optional}}</string>
|
||||
<string name="nearby_fragment">{{Identical|Nearby}}</string>
|
||||
<string name="next">Refers to the next \'\'\'step\'\'\' in the uploading process.</string>
|
||||
<string name="previous">Refers to the previous \'\'\'step\'\'\' in the uploading process.</string>
|
||||
|
|
|
|||
|
|
@ -430,7 +430,6 @@
|
|||
<string name="statistics_wikidata_edits">Изображения мест поблизости</string>
|
||||
<string name="level">Уровень %d</string>
|
||||
<string name="profile_withLevel">%s (Уровень %s)</string>
|
||||
<string name="profile_withoutLevel">%s (%s)</string>
|
||||
<string name="images_uploaded">Загружено изображений</string>
|
||||
<string name="image_reverts">Изображения, которые не откатывались</string>
|
||||
<string name="images_used_by_wiki">Использовано изображений</string>
|
||||
|
|
|
|||
|
|
@ -387,7 +387,6 @@
|
|||
<string name="statistics_wikidata_edits">Slike iz »Bližnji kraji«</string>
|
||||
<string name="level">Raven %d</string>
|
||||
<string name="profile_withLevel">%s (raven %s)</string>
|
||||
<string name="profile_withoutLevel">%s (%s)</string>
|
||||
<string name="images_uploaded">Naložene slike</string>
|
||||
<string name="image_reverts">Nevrnjene slike</string>
|
||||
<string name="images_used_by_wiki">Uporabljene slike</string>
|
||||
|
|
|
|||
|
|
@ -219,6 +219,7 @@
|
|||
<string name="media_detail_description">Beskrivning</string>
|
||||
<string name="media_detail_discussion">Diskussion</string>
|
||||
<string name="media_detail_author">Skapare</string>
|
||||
<string name="media_detail_uploader">Uppladdare</string>
|
||||
<string name="media_detail_uploaded_date">Uppladdningsdatum</string>
|
||||
<string name="media_detail_license">Licens</string>
|
||||
<string name="media_detail_coordinates">Koordinater</string>
|
||||
|
|
@ -378,7 +379,6 @@
|
|||
<string name="statistics_wikidata_edits">Bilder via \"Platser i närheten\"</string>
|
||||
<string name="level">Nivå %d</string>
|
||||
<string name="profile_withLevel">%s (Nivå %s)</string>
|
||||
<string name="profile_withoutLevel">%s (%s)</string>
|
||||
<string name="images_uploaded">Uppladdade bilder</string>
|
||||
<string name="image_reverts">Bilder som inte har återställts</string>
|
||||
<string name="images_used_by_wiki">Bilder som används</string>
|
||||
|
|
@ -421,7 +421,7 @@
|
|||
<string name="deletion_reason_bad_for_my_privacy">Jag insåg att den är dålig för mitt privatliv</string>
|
||||
<string name="deletion_reason_no_longer_want_public">Jag ändrade mig, jag vill inte längre att den ska vara synlig offentligt</string>
|
||||
<string name="deletion_reason_not_interesting">Tyvärr, denna bild är inte intressant för en encyklopedi</string>
|
||||
<string name="uploaded_by_myself" fuzzy="true">Laddades upp av mig den %1$s och används i %2$d artiklar.</string>
|
||||
<string name="uploaded_by_myself">Laddades upp av mig den %1$s och används i minst %2$d artiklar.</string>
|
||||
<string name="no_uploads">Välkommen till Commons!\n\nLadda upp din första mediafil genom att trycka på knappen för att lägga till.</string>
|
||||
<string name="no_categories_selected">Inga kategorier har valts</string>
|
||||
<string name="no_categories_selected_warning_desc">Bilder utan kategorier används sällan. Är du säker på att du vill fortsätta utan att välja kategorier?</string>
|
||||
|
|
@ -586,6 +586,8 @@
|
|||
<string name="title_for_media">MEDIA</string>
|
||||
<string name="title_for_child_classes">UNDERORDNADE KLASSER</string>
|
||||
<string name="title_for_parent_classes">ÖVERORDNADE KLASSER</string>
|
||||
<string name="title_for_subcategories">UNDERKATEGORIER</string>
|
||||
<string name="title_for_parent_categories">ÖVERORDNADE KATEGORIER</string>
|
||||
<string name="upload_nearby_place_found_title">Hittade platser i närheten</string>
|
||||
<string name="upload_nearby_place_found_description_plural">Föreställer de här bilderna %1$s?</string>
|
||||
<string name="upload_nearby_place_found_description_singular">Är detta en bild på %1$s?</string>
|
||||
|
|
@ -761,7 +763,7 @@
|
|||
<string name="permissions_are_required_for_functionality">Behörigheter krävs för funktionalitet</string>
|
||||
<string name="learn_how_to_write_a_useful_description">Lär dig hur du skriver en användbar beskrivning</string>
|
||||
<string name="learn_how_to_write_a_useful_caption">Lär dig hur du skriver en användbar bildtext</string>
|
||||
<string name="see_your_achievements" fuzzy="true">Se dina prestationer</string>
|
||||
<string name="see_your_achievements">Se dina prestationer</string>
|
||||
<string name="edit_image">Redigera bild</string>
|
||||
<string name="edit_location">Redigera plats</string>
|
||||
<string name="location_updated">Plats uppdaterades!</string>
|
||||
|
|
@ -830,4 +832,5 @@
|
|||
<string name="show_in_nearby">Visa i \"I närheten\"</string>
|
||||
<string name="image_tag_line_created_and_uploaded_by">Skapades och laddades upp av: %1$s</string>
|
||||
<string name="image_tag_line_created_by_and_uploaded_by">Skapad av %1$s och laddades upp av %2$s</string>
|
||||
<string name="nominated_for_deletion_btn">Nominerad för radering</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -401,7 +401,6 @@
|
|||
<string name="statistics_wikidata_edits">\"Yakındaki Yerler\"den Resimler</string>
|
||||
<string name="level">Seviye %d</string>
|
||||
<string name="profile_withLevel">%s (Seviye %s)</string>
|
||||
<string name="profile_withoutLevel">%s (%s)</string>
|
||||
<string name="images_uploaded">Resimler Yüklendi</string>
|
||||
<string name="image_reverts">Resimler Geri Alınmadı</string>
|
||||
<string name="images_used_by_wiki">Resimler Kullanıldı</string>
|
||||
|
|
|
|||
|
|
@ -288,7 +288,7 @@ class BookmarkListRootFragmentUnitTest {
|
|||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun testOnItemClick() {
|
||||
fragment.onItemClick(null, null, 0, 0)
|
||||
fragment.onItemClick(null, view, 0, 0)
|
||||
verify(childFragmentManager).beginTransaction()
|
||||
verify(childFragmentTransaction).commit()
|
||||
verify(childFragmentManager).executePendingTransactions()
|
||||
|
|
|
|||
|
|
@ -2,12 +2,20 @@ package fr.free.nrw.commons.bookmarks
|
|||
|
||||
import android.content.Context
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import fr.free.nrw.commons.TestCommonsApplication
|
||||
import org.junit.Assert
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mock
|
||||
import org.mockito.MockitoAnnotations
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.annotation.Config
|
||||
import org.robolectric.annotation.LooperMode
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(sdk = [21], application = TestCommonsApplication::class)
|
||||
@LooperMode(LooperMode.Mode.PAUSED)
|
||||
class BookmarksPagerAdapterTests {
|
||||
@Mock
|
||||
private lateinit var bookmarksPagerAdapter: BookmarksPagerAdapter
|
||||
|
|
|
|||
|
|
@ -2,15 +2,23 @@ package fr.free.nrw.commons.bookmarks
|
|||
|
||||
import android.content.Context
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import fr.free.nrw.commons.TestCommonsApplication
|
||||
import org.junit.Assert
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mock
|
||||
import org.mockito.MockitoAnnotations
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.annotation.Config
|
||||
import org.robolectric.annotation.LooperMode
|
||||
|
||||
/**
|
||||
* BookmarksPagerAdapter when user is not loggedIn.
|
||||
*/
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(sdk = [21], application = TestCommonsApplication::class)
|
||||
@LooperMode(LooperMode.Mode.PAUSED)
|
||||
class LoggedOutBookmarksPagerAdapterTests {
|
||||
@Mock
|
||||
private lateinit var bookmarksPagerAdapter: BookmarksPagerAdapter
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ class BookmarkItemsControllerTest {
|
|||
@Before
|
||||
fun setup() {
|
||||
MockitoAnnotations.openMocks(this)
|
||||
whenever(bookmarkDao!!.allBookmarksItems)
|
||||
whenever(bookmarkDao!!.getAllBookmarksItems())
|
||||
.thenReturn(mockBookmarkList)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,20 +18,20 @@ import com.nhaarman.mockitokotlin2.mock
|
|||
import com.nhaarman.mockitokotlin2.verify
|
||||
import com.nhaarman.mockitokotlin2.whenever
|
||||
import fr.free.nrw.commons.TestCommonsApplication
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.COLUMN_CATEGORIES_DESCRIPTION_LIST
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.COLUMN_CATEGORIES_NAME_LIST
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.COLUMN_CATEGORIES_THUMBNAIL_LIST
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.COLUMN_DESCRIPTION
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.COLUMN_ID
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.COLUMN_IMAGE
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.COLUMN_INSTANCE_LIST
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.COLUMN_IS_SELECTED
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.COLUMN_NAME
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.CREATE_TABLE_STATEMENT
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.DROP_TABLE_STATEMENT
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.onCreate
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.onDelete
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table.onUpdate
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.COLUMN_CATEGORIES_DESCRIPTION_LIST
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.COLUMN_CATEGORIES_NAME_LIST
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.COLUMN_CATEGORIES_THUMBNAIL_LIST
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.COLUMN_DESCRIPTION
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.COLUMN_ID
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.COLUMN_IMAGE
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.COLUMN_INSTANCE_LIST
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.COLUMN_IS_SELECTED
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.COLUMN_NAME
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.CREATE_TABLE_STATEMENT
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.DROP_TABLE_STATEMENT
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.onCreate
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.onDelete
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsTable.onUpdate
|
||||
import fr.free.nrw.commons.category.CategoryItem
|
||||
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem
|
||||
import org.junit.Assert
|
||||
|
|
@ -135,7 +135,7 @@ class BookmarkItemsDaoTest {
|
|||
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull()))
|
||||
.thenReturn(createCursor(14))
|
||||
|
||||
val result = testObject.allBookmarksItems
|
||||
val result = testObject.getAllBookmarksItems()
|
||||
|
||||
Assert.assertEquals(14, (result.size))
|
||||
}
|
||||
|
|
@ -145,20 +145,20 @@ class BookmarkItemsDaoTest {
|
|||
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull())).thenThrow(
|
||||
RemoteException(""),
|
||||
)
|
||||
testObject.allBookmarksItems
|
||||
testObject.getAllBookmarksItems()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getAllItemsBookmarksReturnsEmptyList_emptyCursor() {
|
||||
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull()))
|
||||
.thenReturn(createCursor(0))
|
||||
Assert.assertTrue(testObject.allBookmarksItems.isEmpty())
|
||||
Assert.assertTrue(testObject.getAllBookmarksItems().isEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getAllItemsBookmarksReturnsEmptyList_nullCursor() {
|
||||
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull())).thenReturn(null)
|
||||
Assert.assertTrue(testObject.allBookmarksItems.isEmpty())
|
||||
Assert.assertTrue(testObject.getAllBookmarksItems().isEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -167,7 +167,7 @@ class BookmarkItemsDaoTest {
|
|||
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull())).thenReturn(mockCursor)
|
||||
whenever(mockCursor.moveToFirst()).thenReturn(false)
|
||||
|
||||
testObject.allBookmarksItems
|
||||
testObject.getAllBookmarksItems()
|
||||
|
||||
verify(mockCursor).close()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ class BookmarkItemsFragmentUnitTest {
|
|||
context = ApplicationProvider.getApplicationContext()
|
||||
OkHttpConnectionFactory.CLIENT = createTestClient()
|
||||
val activity = Robolectric.buildActivity(ProfileActivity::class.java).create().get()
|
||||
fragment = BookmarkItemsFragment.newInstance()
|
||||
fragment = BookmarkItemsFragment()
|
||||
val fragmentManager: FragmentManager = activity.supportFragmentManager
|
||||
val fragmentTransaction: FragmentTransaction = fragmentManager.beginTransaction()
|
||||
fragmentTransaction.add(fragment, null)
|
||||
|
|
|
|||
|
|
@ -19,14 +19,14 @@ import com.nhaarman.mockitokotlin2.verify
|
|||
import com.nhaarman.mockitokotlin2.whenever
|
||||
import fr.free.nrw.commons.TestCommonsApplication
|
||||
import fr.free.nrw.commons.bookmarks.models.Bookmark
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesContentProvider.BASE_URI
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao.Table.COLUMN_CREATOR
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao.Table.COLUMN_MEDIA_NAME
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao.Table.CREATE_TABLE_STATEMENT
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao.Table.DROP_TABLE_STATEMENT
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao.Table.onCreate
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao.Table.onDelete
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao.Table.onUpdate
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesContentProvider.Companion.BASE_URI
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarksTable.COLUMN_CREATOR
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarksTable.COLUMN_MEDIA_NAME
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarksTable.CREATE_TABLE_STATEMENT
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarksTable.DROP_TABLE_STATEMENT
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarksTable.onCreate
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarksTable.onDelete
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarksTable.onUpdate
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
|
|
@ -84,7 +84,7 @@ class BookmarkPictureDaoTest {
|
|||
fun getAllBookmarks() {
|
||||
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull())).thenReturn(createCursor(14))
|
||||
|
||||
var result = testObject.allBookmarks
|
||||
var result = testObject.getAllBookmarks()
|
||||
|
||||
assertEquals(14, (result.size))
|
||||
}
|
||||
|
|
@ -92,19 +92,19 @@ class BookmarkPictureDaoTest {
|
|||
@Test(expected = RuntimeException::class)
|
||||
fun getAllBookmarksTranslatesExceptions() {
|
||||
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull())).thenThrow(RemoteException(""))
|
||||
testObject.allBookmarks
|
||||
testObject.getAllBookmarks()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getAllBookmarksReturnsEmptyList_emptyCursor() {
|
||||
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull())).thenReturn(createCursor(0))
|
||||
assertTrue(testObject.allBookmarks.isEmpty())
|
||||
assertTrue(testObject.getAllBookmarks().isEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getAllBookmarksReturnsEmptyList_nullCursor() {
|
||||
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull())).thenReturn(null)
|
||||
assertTrue(testObject.allBookmarks.isEmpty())
|
||||
assertTrue(testObject.getAllBookmarks().isEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -113,7 +113,7 @@ class BookmarkPictureDaoTest {
|
|||
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull())).thenReturn(mockCursor)
|
||||
whenever(mockCursor.moveToFirst()).thenReturn(false)
|
||||
|
||||
testObject.allBookmarks
|
||||
testObject.getAllBookmarks()
|
||||
|
||||
verify(mockCursor).close()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ class BookmarkPicturesControllerTest {
|
|||
fun setup() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
val mockMedia = mockMedia
|
||||
whenever(bookmarkDao!!.allBookmarks)
|
||||
whenever(bookmarkDao!!.getAllBookmarks())
|
||||
.thenReturn(mockBookmarkList)
|
||||
whenever(
|
||||
mediaClient!!.getMedia(
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ class BookmarkPicturesFragmentUnitTests {
|
|||
context = ApplicationProvider.getApplicationContext()
|
||||
OkHttpConnectionFactory.CLIENT = createTestClient()
|
||||
val activity = Robolectric.buildActivity(ProfileActivity::class.java).create().get()
|
||||
fragment = BookmarkPicturesFragment.newInstance()
|
||||
fragment = BookmarkPicturesFragment()
|
||||
val fragmentManager: FragmentManager = activity.supportFragmentManager
|
||||
val fragmentTransaction: FragmentTransaction = fragmentManager.beginTransaction()
|
||||
fragmentTransaction.add(fragment, null)
|
||||
|
|
@ -156,13 +156,13 @@ class BookmarkPicturesFragmentUnitTests {
|
|||
val method: Method =
|
||||
BookmarkPicturesFragment::class.java.getDeclaredMethod("setAdapter", List::class.java)
|
||||
method.isAccessible = true
|
||||
method.invoke(fragment, mediaList)
|
||||
method.invoke(fragment, emptyList<Media>())
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun testGetAdapter() {
|
||||
fragment.adapter
|
||||
fragment.getAdapter()
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ import fr.free.nrw.commons.category.CategoryDao.Table.DROP_TABLE_STATEMENT
|
|||
import fr.free.nrw.commons.category.CategoryDao.Table.onCreate
|
||||
import fr.free.nrw.commons.category.CategoryDao.Table.onDelete
|
||||
import fr.free.nrw.commons.category.CategoryDao.Table.onUpdate
|
||||
import fr.free.nrw.commons.explore.recentsearches.RecentSearchesContentProvider.uriForId
|
||||
import fr.free.nrw.commons.explore.recentsearches.RecentSearchesContentProvider.Companion.uriForId
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Assert.assertNull
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@ import com.nhaarman.mockitokotlin2.verify
|
|||
import com.nhaarman.mockitokotlin2.whenever
|
||||
import fr.free.nrw.commons.TestCommonsApplication
|
||||
import fr.free.nrw.commons.explore.models.RecentSearch
|
||||
import fr.free.nrw.commons.explore.recentsearches.RecentSearchesContentProvider.BASE_URI
|
||||
import fr.free.nrw.commons.explore.recentsearches.RecentSearchesContentProvider.uriForId
|
||||
import fr.free.nrw.commons.explore.recentsearches.RecentSearchesContentProvider.Companion.BASE_URI
|
||||
import fr.free.nrw.commons.explore.recentsearches.RecentSearchesContentProvider.Companion.uriForId
|
||||
import fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao.Table.ALL_FIELDS
|
||||
import fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao.Table.COLUMN_ID
|
||||
import fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao.Table.COLUMN_LAST_USED
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue