mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-26 12:23:58 +01:00
* Add bookmark star images * Add bookmark item in navigation menu * Add Activity for bookmarks * Implement bookmarks viewpager * Bookmark object and bookmarkDao * Implement Bookmark Picture Fragment and Controller * Implement image detail bookmark menu action UI * contentProvider + config + dao rework * Fix Dao and Content Provider crashes * Link bookmark controllers and dao * Implement bookmark location fragment, controller * Add bookmark icon to location items * Add empty bookmark list behavior and refactoring * bookmarkLocation dao and contentProvider * Fix bookmarks location crashes * Rename and refactor classes * Implement location list refresh * Fix picture list update When user come back from detail picture fragment, it solve the refresh bug. * full test coverage * Refactor bookmarks classes * Fix bookmarks pictures loading * Fix bookmark locations list display * Java Documetation * Fix Code review quality * Fix DB version update * Remove forgotten todo * Update bookmark activity base class Change from AuthenticatedActivity to BaseNavigationActivity
This commit is contained in:
parent
89d2d0cfe0
commit
80a9c94653
42 changed files with 2361 additions and 12 deletions
|
|
@ -153,6 +153,8 @@ android {
|
||||||
buildConfigField "String", "MODIFICATION_AUTHORITY", "\"fr.free.nrw.commons.modifications.contentprovider\""
|
buildConfigField "String", "MODIFICATION_AUTHORITY", "\"fr.free.nrw.commons.modifications.contentprovider\""
|
||||||
buildConfigField "String", "CATEGORY_AUTHORITY", "\"fr.free.nrw.commons.categories.contentprovider\""
|
buildConfigField "String", "CATEGORY_AUTHORITY", "\"fr.free.nrw.commons.categories.contentprovider\""
|
||||||
buildConfigField "String", "RECENT_SEARCH_AUTHORITY", "\"fr.free.nrw.commons.explore.recentsearches.contentprovider\""
|
buildConfigField "String", "RECENT_SEARCH_AUTHORITY", "\"fr.free.nrw.commons.explore.recentsearches.contentprovider\""
|
||||||
|
buildConfigField "String", "BOOKMARK_AUTHORITY", "\"fr.free.nrw.commons.beta.bookmarks.contentprovider\""
|
||||||
|
buildConfigField "String", "BOOKMARK_LOCATIONS_AUTHORITY", "\"fr.free.nrw.commons.beta.bookmarks.locations.contentprovider\""
|
||||||
|
|
||||||
dimension 'tier'
|
dimension 'tier'
|
||||||
}
|
}
|
||||||
|
|
@ -180,6 +182,8 @@ android {
|
||||||
buildConfigField "String", "MODIFICATION_AUTHORITY", "\"fr.free.nrw.commons.beta.modifications.contentprovider\""
|
buildConfigField "String", "MODIFICATION_AUTHORITY", "\"fr.free.nrw.commons.beta.modifications.contentprovider\""
|
||||||
buildConfigField "String", "CATEGORY_AUTHORITY", "\"fr.free.nrw.commons.beta.categories.contentprovider\""
|
buildConfigField "String", "CATEGORY_AUTHORITY", "\"fr.free.nrw.commons.beta.categories.contentprovider\""
|
||||||
buildConfigField "String", "RECENT_SEARCH_AUTHORITY", "\"fr.free.nrw.commons.beta.explore.recentsearches.contentprovider\""
|
buildConfigField "String", "RECENT_SEARCH_AUTHORITY", "\"fr.free.nrw.commons.beta.explore.recentsearches.contentprovider\""
|
||||||
|
buildConfigField "String", "BOOKMARK_AUTHORITY", "\"fr.free.nrw.commons.beta.bookmarks.contentprovider\""
|
||||||
|
buildConfigField "String", "BOOKMARK_LOCATIONS_AUTHORITY", "\"fr.free.nrw.commons.beta.bookmarks.locations.contentprovider\""
|
||||||
|
|
||||||
dimension 'tier'
|
dimension 'tier'
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -122,6 +122,10 @@
|
||||||
android:name=".achievements.AchievementsActivity"
|
android:name=".achievements.AchievementsActivity"
|
||||||
android:label="@string/Achievements" />
|
android:label="@string/Achievements" />
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".bookmarks.BookmarksActivity"
|
||||||
|
android:label="@string/title_activity_bookmarks" />
|
||||||
|
|
||||||
<activity android:name="com.github.pedrovgs.lynx.LynxActivity"/>
|
<activity android:name="com.github.pedrovgs.lynx.LynxActivity"/>
|
||||||
|
|
||||||
<service android:name=".upload.UploadService" />
|
<service android:name=".upload.UploadService" />
|
||||||
|
|
@ -202,6 +206,20 @@
|
||||||
android:label="@string/provider_searches"
|
android:label="@string/provider_searches"
|
||||||
android:syncable="false" />
|
android:syncable="false" />
|
||||||
|
|
||||||
|
<provider
|
||||||
|
android:name=".bookmarks.pictures.BookmarkPicturesContentProvider"
|
||||||
|
android:authorities="${applicationId}.bookmarks.contentprovider"
|
||||||
|
android:exported="false"
|
||||||
|
android:label="@string/provider_bookmarks"
|
||||||
|
android:syncable="false" />
|
||||||
|
|
||||||
|
<provider
|
||||||
|
android:name=".bookmarks.locations.BookmarkLocationsContentProvider"
|
||||||
|
android:authorities="${applicationId}.bookmarks.locations.contentprovider"
|
||||||
|
android:exported="false"
|
||||||
|
android:label="@string/provider_bookmarks_location"
|
||||||
|
android:syncable="false" />
|
||||||
|
|
||||||
<receiver android:name=".widget.PicOfDayAppWidget">
|
<receiver android:name=".widget.PicOfDayAppWidget">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,8 @@ import javax.inject.Inject;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
|
|
||||||
import fr.free.nrw.commons.auth.SessionManager;
|
import fr.free.nrw.commons.auth.SessionManager;
|
||||||
|
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao;
|
||||||
|
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao;
|
||||||
import fr.free.nrw.commons.category.CategoryDao;
|
import fr.free.nrw.commons.category.CategoryDao;
|
||||||
import fr.free.nrw.commons.concurrency.BackgroundPoolExceptionHandler;
|
import fr.free.nrw.commons.concurrency.BackgroundPoolExceptionHandler;
|
||||||
import fr.free.nrw.commons.concurrency.ThreadPoolService;
|
import fr.free.nrw.commons.concurrency.ThreadPoolService;
|
||||||
|
|
@ -245,6 +247,8 @@ public class CommonsApplication extends Application {
|
||||||
ModifierSequenceDao.Table.onDelete(db);
|
ModifierSequenceDao.Table.onDelete(db);
|
||||||
CategoryDao.Table.onDelete(db);
|
CategoryDao.Table.onDelete(db);
|
||||||
ContributionDao.Table.onDelete(db);
|
ContributionDao.Table.onDelete(db);
|
||||||
|
BookmarkPicturesDao.Table.onDelete(db);
|
||||||
|
BookmarkLocationsDao.Table.onDelete(db);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
package fr.free.nrw.commons.bookmarks;
|
||||||
|
|
||||||
|
import android.net.Uri;
|
||||||
|
|
||||||
|
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesContentProvider;
|
||||||
|
|
||||||
|
public class Bookmark {
|
||||||
|
private Uri contentUri;
|
||||||
|
private String mediaName;
|
||||||
|
private String mediaCreator;
|
||||||
|
|
||||||
|
public Bookmark(String mediaName, String mediaCreator) {
|
||||||
|
this.contentUri = BookmarkPicturesContentProvider.uriForName(mediaName);
|
||||||
|
this.mediaName = mediaName == null ? "" : mediaName;
|
||||||
|
this.mediaCreator = mediaCreator == null ? "" : mediaCreator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the media name
|
||||||
|
* @return the media name
|
||||||
|
*/
|
||||||
|
public String getMediaName() {
|
||||||
|
return mediaName;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets media creator
|
||||||
|
* @return creator name
|
||||||
|
*/
|
||||||
|
public String getMediaCreator() { return mediaCreator; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the content URI for this bookmark
|
||||||
|
* @return content URI
|
||||||
|
*/
|
||||||
|
public Uri getContentUri() {
|
||||||
|
return contentUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modifies the content URI - marking this bookmark as already saved in the database
|
||||||
|
* @param contentUri the content URI
|
||||||
|
*/
|
||||||
|
public void setContentUri(Uri contentUri) {
|
||||||
|
this.contentUri = contentUri;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
package fr.free.nrw.commons.bookmarks;
|
||||||
|
|
||||||
|
import android.support.v4.app.Fragment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data class for handling a bookmark fragment and it title
|
||||||
|
*/
|
||||||
|
public class BookmarkPages {
|
||||||
|
private Fragment page;
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
BookmarkPages(Fragment fragment, String title) {
|
||||||
|
this.title = title;
|
||||||
|
this.page = fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the fragment
|
||||||
|
* @return fragment object
|
||||||
|
*/
|
||||||
|
public Fragment getPage() {
|
||||||
|
return page;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the fragment title
|
||||||
|
* @return title
|
||||||
|
*/
|
||||||
|
public String getTitle() {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,154 @@
|
||||||
|
package fr.free.nrw.commons.bookmarks;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.database.DataSetObserver;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.design.widget.TabLayout;
|
||||||
|
import android.support.v4.app.FragmentManager;
|
||||||
|
import android.support.v4.view.ViewPager;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.AdapterView;
|
||||||
|
|
||||||
|
import butterknife.BindView;
|
||||||
|
import butterknife.ButterKnife;
|
||||||
|
import fr.free.nrw.commons.Media;
|
||||||
|
import fr.free.nrw.commons.R;
|
||||||
|
import fr.free.nrw.commons.auth.AuthenticatedActivity;
|
||||||
|
import fr.free.nrw.commons.media.MediaDetailPagerFragment;
|
||||||
|
import fr.free.nrw.commons.theme.NavigationBaseActivity;
|
||||||
|
|
||||||
|
public class BookmarksActivity extends NavigationBaseActivity
|
||||||
|
implements FragmentManager.OnBackStackChangedListener,
|
||||||
|
MediaDetailPagerFragment.MediaDetailProvider,
|
||||||
|
AdapterView.OnItemClickListener {
|
||||||
|
|
||||||
|
private FragmentManager supportFragmentManager;
|
||||||
|
private BookmarksPagerAdapter adapter;
|
||||||
|
private MediaDetailPagerFragment mediaDetails;
|
||||||
|
@BindView(R.id.viewPagerBookmarks)
|
||||||
|
ViewPager viewPager;
|
||||||
|
@BindView(R.id.tabLayoutBookmarks)
|
||||||
|
TabLayout tabLayout;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_bookmarks);
|
||||||
|
ButterKnife.bind(this);
|
||||||
|
|
||||||
|
// Activity can call methods in the fragment by acquiring a
|
||||||
|
// reference to the Fragment from FragmentManager, using findFragmentById()
|
||||||
|
supportFragmentManager = getSupportFragmentManager();
|
||||||
|
supportFragmentManager.addOnBackStackChangedListener(this);
|
||||||
|
initDrawer();
|
||||||
|
|
||||||
|
adapter = new BookmarksPagerAdapter(supportFragmentManager, this);
|
||||||
|
viewPager.setAdapter(adapter);
|
||||||
|
tabLayout.setupWithViewPager(viewPager);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Consumers should be simply using this method to use this activity.
|
||||||
|
* @param context A Context of the application package implementing this class.
|
||||||
|
*/
|
||||||
|
public static void startYourself(Context context) {
|
||||||
|
Intent intent = new Intent(context, BookmarksActivity.class);
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
|
||||||
|
context.startActivity(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBackStackChanged() {
|
||||||
|
if (supportFragmentManager.getBackStackEntryCount() == 0) {
|
||||||
|
// The activity has the focus
|
||||||
|
adapter.requestPictureListUpdate();
|
||||||
|
initDrawer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called onClick of media inside category details (CategoryImageListFragment).
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
|
||||||
|
if (mediaDetails == null || !mediaDetails.isVisible()) {
|
||||||
|
mediaDetails = new MediaDetailPagerFragment(false, true);
|
||||||
|
supportFragmentManager
|
||||||
|
.beginTransaction()
|
||||||
|
.hide(supportFragmentManager.getFragments().get(supportFragmentManager.getBackStackEntryCount()))
|
||||||
|
.add(R.id.fragmentContainer, mediaDetails)
|
||||||
|
.addToBackStack(null)
|
||||||
|
.commit();
|
||||||
|
supportFragmentManager.executePendingTransactions();
|
||||||
|
}
|
||||||
|
mediaDetails.showImage(i);
|
||||||
|
forceInitBackButton();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called on success of API call for featured Images.
|
||||||
|
* The viewpager will notified that number of items have changed.
|
||||||
|
*/
|
||||||
|
public void viewPagerNotifyDataSetChanged() {
|
||||||
|
if (mediaDetails!=null){
|
||||||
|
mediaDetails.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 (adapter.getMediaAdapter() == null) {
|
||||||
|
// not yet ready to return data
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return (Media) adapter.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 (adapter.getMediaAdapter() == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return adapter.getMediaAdapter().getCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is never called but it was in MediaDetailProvider Interface
|
||||||
|
* so it needs to be overrided.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void notifyDatasetChanged() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is never called but it was in MediaDetailProvider Interface
|
||||||
|
* so it needs to be overrided.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void registerDataSetObserver(DataSetObserver observer) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is never called but it was in MediaDetailProvider Interface
|
||||||
|
* so it needs to be overrided.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void unregisterDataSetObserver(DataSetObserver observer) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
package fr.free.nrw.commons.bookmarks;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.v4.app.Fragment;
|
||||||
|
import android.support.v4.app.FragmentManager;
|
||||||
|
import android.support.v4.app.FragmentPagerAdapter;
|
||||||
|
import android.widget.ListAdapter;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import fr.free.nrw.commons.R;
|
||||||
|
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsFragment;
|
||||||
|
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesFragment;
|
||||||
|
|
||||||
|
public class BookmarksPagerAdapter extends FragmentPagerAdapter {
|
||||||
|
|
||||||
|
private ArrayList<BookmarkPages> pages;
|
||||||
|
|
||||||
|
BookmarksPagerAdapter(FragmentManager fm, Context context) {
|
||||||
|
super(fm);
|
||||||
|
pages = new ArrayList<>();
|
||||||
|
pages.add(new BookmarkPages(
|
||||||
|
BookmarkPicturesFragment.newInstance(),
|
||||||
|
context.getString(R.string.title_page_bookmarks_pictures)
|
||||||
|
));
|
||||||
|
pages.add(new BookmarkPages(
|
||||||
|
BookmarkLocationsFragment.newInstance(),
|
||||||
|
context.getString(R.string.title_page_bookmarks_locations)
|
||||||
|
));
|
||||||
|
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)(pages.get(0).getPage());
|
||||||
|
return fragment.getAdapter();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the pictures list for the bookmark fragment
|
||||||
|
*/
|
||||||
|
public void requestPictureListUpdate() {
|
||||||
|
BookmarkPicturesFragment fragment = (BookmarkPicturesFragment)(pages.get(0).getPage());
|
||||||
|
fragment.onResume();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,93 @@
|
||||||
|
package fr.free.nrw.commons.bookmarks.locations;
|
||||||
|
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
import android.database.sqlite.SQLiteQueryBuilder;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
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.locations.BookmarkLocationsDao.Table.COLUMN_NAME;
|
||||||
|
import static fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao.Table.TABLE_NAME;
|
||||||
|
|
||||||
|
public class BookmarkLocationsContentProvider extends CommonsDaggerContentProvider {
|
||||||
|
|
||||||
|
private static final String BASE_PATH = "bookmarksLocations";
|
||||||
|
public static final Uri BASE_URI = Uri.parse("content://" + BuildConfig.BOOKMARK_LOCATIONS_AUTHORITY + "/" + BASE_PATH);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@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_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("ConstantConditions")
|
||||||
|
@Override
|
||||||
|
public Uri insert(@NonNull Uri uri, ContentValues contentValues) {
|
||||||
|
SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase();
|
||||||
|
long id = sqlDB.insert(BookmarkLocationsDao.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,
|
||||||
|
"location_name = ?",
|
||||||
|
new String[]{uri.getLastPathSegment()}
|
||||||
|
);
|
||||||
|
getContext().getContentResolver().notifyChange(uri, null);
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
package fr.free.nrw.commons.bookmarks.locations;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
import fr.free.nrw.commons.nearby.Place;
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
public class BookmarkLocationsController {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
BookmarkLocationsDao bookmarkLocationDao;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public BookmarkLocationsController() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load from DB the bookmarked locations
|
||||||
|
* @return a list of Place objects.
|
||||||
|
*/
|
||||||
|
public List<Place> loadFavoritesLocations() {
|
||||||
|
return bookmarkLocationDao.getAllBookmarksLocations();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,261 @@
|
||||||
|
package fr.free.nrw.commons.bookmarks.locations;
|
||||||
|
|
||||||
|
import android.content.ContentProviderClient;
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.RemoteException;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
import javax.inject.Provider;
|
||||||
|
|
||||||
|
import fr.free.nrw.commons.location.LatLng;
|
||||||
|
import fr.free.nrw.commons.nearby.Place;
|
||||||
|
import fr.free.nrw.commons.nearby.Sitelinks;
|
||||||
|
|
||||||
|
import static fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsContentProvider.BASE_URI;
|
||||||
|
|
||||||
|
public class BookmarkLocationsDao {
|
||||||
|
|
||||||
|
private final Provider<ContentProviderClient> clientProvider;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public BookmarkLocationsDao(@Named("bookmarksLocation") Provider<ContentProviderClient> clientProvider) {
|
||||||
|
this.clientProvider = clientProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find all persisted locations bookmarks on database
|
||||||
|
*
|
||||||
|
* @return list of Place
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public List<Place> getAllBookmarksLocations() {
|
||||||
|
List<Place> items = new ArrayList<>();
|
||||||
|
Cursor cursor = null;
|
||||||
|
ContentProviderClient db = clientProvider.get();
|
||||||
|
try {
|
||||||
|
cursor = db.query(
|
||||||
|
BookmarkLocationsContentProvider.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 place in bookmarks table in order to insert or delete it
|
||||||
|
*
|
||||||
|
* @param bookmarkLocation : Place object
|
||||||
|
* @return is Place now fav ?
|
||||||
|
*/
|
||||||
|
public boolean updateBookmarkLocation(Place bookmarkLocation) {
|
||||||
|
boolean bookmarkExists = findBookmarkLocation(bookmarkLocation);
|
||||||
|
if (bookmarkExists) {
|
||||||
|
deleteBookmarkLocation(bookmarkLocation);
|
||||||
|
} else {
|
||||||
|
addBookmarkLocation(bookmarkLocation);
|
||||||
|
}
|
||||||
|
return !bookmarkExists;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a Place to bookmarks table
|
||||||
|
*
|
||||||
|
* @param bookmarkLocation : Place to add
|
||||||
|
*/
|
||||||
|
private void addBookmarkLocation(Place bookmarkLocation) {
|
||||||
|
ContentProviderClient db = clientProvider.get();
|
||||||
|
try {
|
||||||
|
db.insert(BASE_URI, toContentValues(bookmarkLocation));
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
} finally {
|
||||||
|
db.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a Place from bookmarks table
|
||||||
|
*
|
||||||
|
* @param bookmarkLocation : Place to delete
|
||||||
|
*/
|
||||||
|
private void deleteBookmarkLocation(Place bookmarkLocation) {
|
||||||
|
ContentProviderClient db = clientProvider.get();
|
||||||
|
try {
|
||||||
|
db.delete(BookmarkLocationsContentProvider.uriForName(bookmarkLocation.name), null, null);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
} finally {
|
||||||
|
db.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a Place from database based on its name
|
||||||
|
*
|
||||||
|
* @param bookmarkLocation : Place to find
|
||||||
|
* @return boolean : is Place in database ?
|
||||||
|
*/
|
||||||
|
public boolean findBookmarkLocation(Place bookmarkLocation) {
|
||||||
|
Cursor cursor = null;
|
||||||
|
ContentProviderClient db = clientProvider.get();
|
||||||
|
try {
|
||||||
|
cursor = db.query(
|
||||||
|
BookmarkLocationsContentProvider.BASE_URI,
|
||||||
|
Table.ALL_FIELDS,
|
||||||
|
Table.COLUMN_NAME + "=?",
|
||||||
|
new String[]{bookmarkLocation.name},
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
Place fromCursor(Cursor cursor) {
|
||||||
|
LatLng location = new LatLng(cursor.getDouble(cursor.getColumnIndex(Table.COLUMN_LAT)),
|
||||||
|
cursor.getDouble(cursor.getColumnIndex(Table.COLUMN_LONG)), 1F);
|
||||||
|
|
||||||
|
Sitelinks.Builder builder = new Sitelinks.Builder();
|
||||||
|
builder.setWikipediaLink(cursor.getString(cursor.getColumnIndex(Table.COLUMN_WIKIPEDIA_LINK)));
|
||||||
|
builder.setWikidataLink(cursor.getString(cursor.getColumnIndex(Table.COLUMN_WIKIDATA_LINK)));
|
||||||
|
builder.setCommonsLink(cursor.getString(cursor.getColumnIndex(Table.COLUMN_COMMONS_LINK)));
|
||||||
|
|
||||||
|
Uri uri = Uri.parse(cursor.getString(cursor.getColumnIndex(Table.COLUMN_IMAGE_URL)));
|
||||||
|
|
||||||
|
return new Place(
|
||||||
|
cursor.getString(cursor.getColumnIndex(Table.COLUMN_NAME)),
|
||||||
|
Place.Label.fromText((cursor.getString(cursor.getColumnIndex(Table.COLUMN_LABEL_TEXT)))),
|
||||||
|
cursor.getString(cursor.getColumnIndex(Table.COLUMN_DESCRIPTION)),
|
||||||
|
uri,
|
||||||
|
location,
|
||||||
|
cursor.getString(cursor.getColumnIndex(Table.COLUMN_CATEGORY)),
|
||||||
|
builder.build()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ContentValues toContentValues(Place bookmarkLocation) {
|
||||||
|
ContentValues cv = new ContentValues();
|
||||||
|
cv.put(BookmarkLocationsDao.Table.COLUMN_NAME, bookmarkLocation.getName());
|
||||||
|
cv.put(BookmarkLocationsDao.Table.COLUMN_DESCRIPTION, bookmarkLocation.getLongDescription());
|
||||||
|
cv.put(BookmarkLocationsDao.Table.COLUMN_CATEGORY, bookmarkLocation.getCategory());
|
||||||
|
cv.put(BookmarkLocationsDao.Table.COLUMN_LABEL_TEXT, bookmarkLocation.getLabel().getText());
|
||||||
|
cv.put(BookmarkLocationsDao.Table.COLUMN_LABEL_ICON, bookmarkLocation.getLabel().getIcon());
|
||||||
|
cv.put(BookmarkLocationsDao.Table.COLUMN_IMAGE_URL, bookmarkLocation.getSecondaryImageUrl().toString());
|
||||||
|
cv.put(BookmarkLocationsDao.Table.COLUMN_WIKIPEDIA_LINK, bookmarkLocation.siteLinks.getWikipediaLink().toString());
|
||||||
|
cv.put(BookmarkLocationsDao.Table.COLUMN_WIKIDATA_LINK, bookmarkLocation.siteLinks.getWikidataLink().toString());
|
||||||
|
cv.put(BookmarkLocationsDao.Table.COLUMN_COMMONS_LINK, bookmarkLocation.siteLinks.getCommonsLink().toString());
|
||||||
|
cv.put(BookmarkLocationsDao.Table.COLUMN_LAT, bookmarkLocation.location.getLatitude());
|
||||||
|
cv.put(BookmarkLocationsDao.Table.COLUMN_LONG, bookmarkLocation.location.getLongitude());
|
||||||
|
return cv;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Table {
|
||||||
|
public static final String TABLE_NAME = "bookmarksLocations";
|
||||||
|
|
||||||
|
static final String COLUMN_NAME = "location_name";
|
||||||
|
static final String COLUMN_DESCRIPTION = "location_description";
|
||||||
|
static final String COLUMN_LAT = "location_lat";
|
||||||
|
static final String COLUMN_LONG = "location_long";
|
||||||
|
static final String COLUMN_CATEGORY = "location_category";
|
||||||
|
static final String COLUMN_LABEL_TEXT = "location_label_text";
|
||||||
|
static final String COLUMN_LABEL_ICON = "location_label_icon";
|
||||||
|
static final String COLUMN_IMAGE_URL = "location_image_url";
|
||||||
|
static final String COLUMN_WIKIPEDIA_LINK = "location_wikipedia_link";
|
||||||
|
static final String COLUMN_WIKIDATA_LINK = "location_wikidata_link";
|
||||||
|
static final String COLUMN_COMMONS_LINK = "location_commons_link";
|
||||||
|
|
||||||
|
// NOTE! KEEP IN SAME ORDER AS THEY ARE DEFINED UP THERE. HELPS HARD CODE COLUMN INDICES.
|
||||||
|
public static final String[] ALL_FIELDS = {
|
||||||
|
COLUMN_NAME,
|
||||||
|
COLUMN_DESCRIPTION,
|
||||||
|
COLUMN_CATEGORY,
|
||||||
|
COLUMN_LABEL_TEXT,
|
||||||
|
COLUMN_LABEL_ICON,
|
||||||
|
COLUMN_LAT,
|
||||||
|
COLUMN_LONG,
|
||||||
|
COLUMN_IMAGE_URL,
|
||||||
|
COLUMN_WIKIPEDIA_LINK,
|
||||||
|
COLUMN_WIKIDATA_LINK,
|
||||||
|
COLUMN_COMMONS_LINK
|
||||||
|
};
|
||||||
|
|
||||||
|
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 PRIMARY KEY,"
|
||||||
|
+ COLUMN_DESCRIPTION + " STRING,"
|
||||||
|
+ COLUMN_CATEGORY + " STRING,"
|
||||||
|
+ COLUMN_LABEL_TEXT + " STRING,"
|
||||||
|
+ COLUMN_LABEL_ICON + " INTEGER,"
|
||||||
|
+ COLUMN_LAT + " DOUBLE,"
|
||||||
|
+ COLUMN_LONG + " DOUBLE,"
|
||||||
|
+ COLUMN_IMAGE_URL + " STRING,"
|
||||||
|
+ COLUMN_WIKIPEDIA_LINK + " STRING,"
|
||||||
|
+ COLUMN_WIKIDATA_LINK + " STRING,"
|
||||||
|
+ COLUMN_COMMONS_LINK + " 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,152 @@
|
||||||
|
package fr.free.nrw.commons.bookmarks.locations;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.v7.widget.LinearLayoutManager;
|
||||||
|
import android.support.v7.widget.RecyclerView;
|
||||||
|
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 com.pedrogomez.renderers.RVRendererAdapter;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
|
||||||
|
import butterknife.BindView;
|
||||||
|
import butterknife.ButterKnife;
|
||||||
|
import dagger.android.support.DaggerFragment;
|
||||||
|
import fr.free.nrw.commons.R;
|
||||||
|
import fr.free.nrw.commons.contributions.ContributionController;
|
||||||
|
import fr.free.nrw.commons.nearby.NearbyAdapterFactory;
|
||||||
|
import fr.free.nrw.commons.nearby.Place;
|
||||||
|
import timber.log.Timber;
|
||||||
|
|
||||||
|
import static android.app.Activity.RESULT_OK;
|
||||||
|
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
||||||
|
|
||||||
|
public class BookmarkLocationsFragment extends DaggerFragment {
|
||||||
|
|
||||||
|
@BindView(R.id.statusMessage) TextView statusTextView;
|
||||||
|
@BindView(R.id.loadingImagesProgressBar) ProgressBar progressBar;
|
||||||
|
@BindView(R.id.listView) RecyclerView recyclerView;
|
||||||
|
@BindView(R.id.parentLayout) RelativeLayout parentLayout;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
BookmarkLocationsController controller;
|
||||||
|
@Inject @Named("direct_nearby_upload_prefs") SharedPreferences directPrefs;
|
||||||
|
private NearbyAdapterFactory adapterFactory;
|
||||||
|
private ContributionController contributionController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an instance of the fragment with the right bundle parameters
|
||||||
|
* @return an instance of the fragment
|
||||||
|
*/
|
||||||
|
public static BookmarkLocationsFragment newInstance() {
|
||||||
|
return new BookmarkLocationsFragment();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(
|
||||||
|
@NonNull LayoutInflater inflater,
|
||||||
|
ViewGroup container,
|
||||||
|
Bundle savedInstanceState
|
||||||
|
) {
|
||||||
|
View v = inflater.inflate(R.layout.fragment_bookmarks_locations, container, false);
|
||||||
|
ButterKnife.bind(this, v);
|
||||||
|
contributionController = new ContributionController(this);
|
||||||
|
adapterFactory = new NearbyAdapterFactory(this, contributionController);
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
progressBar.setVisibility(View.VISIBLE);
|
||||||
|
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||||
|
recyclerView.setAdapter(
|
||||||
|
adapterFactory.create(
|
||||||
|
new ArrayList<Place>(),
|
||||||
|
() -> {
|
||||||
|
initList();
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
initList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the recycler view with bookmarked locations
|
||||||
|
*/
|
||||||
|
private void initList() {
|
||||||
|
List<Place> places = controller.loadFavoritesLocations();
|
||||||
|
adapterFactory.updateAdapterData(places, (RVRendererAdapter<Place>) recyclerView.getAdapter());
|
||||||
|
progressBar.setVisibility(View.GONE);
|
||||||
|
if (places.size() <= 0) {
|
||||||
|
statusTextView.setText(R.string.bookmark_empty);
|
||||||
|
statusTextView.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
statusTextView.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||||
|
Timber.d("onRequestPermissionsResult: req code = " + " perm = " + permissions + " grant =" + grantResults);
|
||||||
|
|
||||||
|
switch (requestCode) {
|
||||||
|
// 4 = "Read external storage" allowed when gallery selected
|
||||||
|
case 4: {
|
||||||
|
if (grantResults.length > 0 && grantResults[0] == PERMISSION_GRANTED) {
|
||||||
|
Timber.d("Call controller.startGalleryPick()");
|
||||||
|
contributionController.startGalleryPick();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
// 5 = "Write external storage" allowed when camera selected
|
||||||
|
case 5: {
|
||||||
|
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||||
|
Timber.d("Call controller.startCameraCapture()");
|
||||||
|
contributionController.startCameraCapture();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
|
|
||||||
|
if (resultCode == RESULT_OK) {
|
||||||
|
Timber.d("OnActivityResult() parameters: Req code: %d Result code: %d Data: %s",
|
||||||
|
requestCode, resultCode, data);
|
||||||
|
String wikidataEntityId = directPrefs.getString("WikiDataEntityId", null);
|
||||||
|
if (requestCode == ContributionController.SELECT_FROM_CAMERA) {
|
||||||
|
// If coming from camera, pass null as uri. Because camera photos get saved to a
|
||||||
|
// fixed directory
|
||||||
|
contributionController.handleImagePicked(requestCode, null, true, wikidataEntityId);
|
||||||
|
} else {
|
||||||
|
contributionController.handleImagePicked(requestCode, data.getData(), true, wikidataEntityId);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Timber.e("OnActivityResult() parameters: Req code: %d Result code: %d Data: %s",
|
||||||
|
requestCode, resultCode, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,94 @@
|
||||||
|
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;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@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,66 @@
|
||||||
|
package fr.free.nrw.commons.bookmarks.pictures;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
import fr.free.nrw.commons.Media;
|
||||||
|
import fr.free.nrw.commons.bookmarks.Bookmark;
|
||||||
|
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
public class BookmarkPicturesController {
|
||||||
|
|
||||||
|
private MediaWikiApi mediaWikiApi;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
BookmarkPicturesDao bookmarkDao;
|
||||||
|
|
||||||
|
private List<Bookmark> currentBookmarks;
|
||||||
|
|
||||||
|
@Inject public BookmarkPicturesController(MediaWikiApi mediaWikiApi) {
|
||||||
|
this.mediaWikiApi = mediaWikiApi;
|
||||||
|
currentBookmarks = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the Media objects from the raw data stored in DB and the API.
|
||||||
|
* @return a list of bookmarked Media object
|
||||||
|
*/
|
||||||
|
List<Media> loadBookmarkedPictures() {
|
||||||
|
List<Bookmark> bookmarks = bookmarkDao.getAllBookmarks();
|
||||||
|
currentBookmarks = bookmarks;
|
||||||
|
ArrayList<Media> medias = new ArrayList<Media>();
|
||||||
|
for (Bookmark bookmark : bookmarks) {
|
||||||
|
List<Media> tmpMedias = mediaWikiApi.searchImages(bookmark.getMediaName(), 0);
|
||||||
|
for (Media m : tmpMedias) {
|
||||||
|
if (m.getCreator().equals(bookmark.getMediaCreator())) {
|
||||||
|
medias.add(m);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return medias;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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();
|
||||||
|
if (bookmarks.size() == currentBookmarks.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancels the requests to the API and the DB
|
||||||
|
*/
|
||||||
|
void stop() {
|
||||||
|
//noop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,216 @@
|
||||||
|
package fr.free.nrw.commons.bookmarks.pictures;
|
||||||
|
|
||||||
|
import android.content.ContentProviderClient;
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
import android.os.RemoteException;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
import javax.inject.Provider;
|
||||||
|
|
||||||
|
import fr.free.nrw.commons.bookmarks.Bookmark;
|
||||||
|
|
||||||
|
import static fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesContentProvider.BASE_URI;
|
||||||
|
|
||||||
|
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) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
Bookmark fromCursor(Cursor cursor) {
|
||||||
|
return new Bookmark(
|
||||||
|
cursor.getString(cursor.getColumnIndex(Table.COLUMN_MEDIA_NAME)),
|
||||||
|
cursor.getString(cursor.getColumnIndex(Table.COLUMN_CREATOR))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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,230 @@
|
||||||
|
package fr.free.nrw.commons.bookmarks.pictures;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.AdapterView;
|
||||||
|
import android.widget.GridView;
|
||||||
|
import android.widget.ListAdapter;
|
||||||
|
import android.widget.ProgressBar;
|
||||||
|
import android.widget.RelativeLayout;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import butterknife.BindView;
|
||||||
|
import butterknife.ButterKnife;
|
||||||
|
import dagger.android.support.DaggerFragment;
|
||||||
|
import fr.free.nrw.commons.Media;
|
||||||
|
import fr.free.nrw.commons.R;
|
||||||
|
import fr.free.nrw.commons.bookmarks.BookmarksActivity;
|
||||||
|
import fr.free.nrw.commons.category.GridViewAdapter;
|
||||||
|
import fr.free.nrw.commons.utils.NetworkUtils;
|
||||||
|
import fr.free.nrw.commons.utils.ViewUtil;
|
||||||
|
import io.reactivex.Observable;
|
||||||
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
|
import io.reactivex.schedulers.Schedulers;
|
||||||
|
import timber.log.Timber;
|
||||||
|
|
||||||
|
import static android.view.View.GONE;
|
||||||
|
import static android.view.View.VISIBLE;
|
||||||
|
|
||||||
|
public class BookmarkPicturesFragment extends DaggerFragment {
|
||||||
|
|
||||||
|
private static final int TIMEOUT_SECONDS = 15;
|
||||||
|
|
||||||
|
private GridViewAdapter gridAdapter;
|
||||||
|
|
||||||
|
@BindView(R.id.statusMessage) TextView statusTextView;
|
||||||
|
@BindView(R.id.loadingImagesProgressBar) ProgressBar progressBar;
|
||||||
|
@BindView(R.id.bookmarkedPicturesList) GridView gridView;
|
||||||
|
@BindView(R.id.parentLayout) RelativeLayout parentLayout;
|
||||||
|
|
||||||
|
@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
|
||||||
|
) {
|
||||||
|
View v = inflater.inflate(R.layout.fragment_bookmarks_pictures, container, false);
|
||||||
|
ButterKnife.bind(this, v);
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
gridView.setOnItemClickListener((AdapterView.OnItemClickListener) getActivity());
|
||||||
|
initList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStop() {
|
||||||
|
super.onStop();
|
||||||
|
controller.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
if (controller.needRefreshBookmarkedPictures()) {
|
||||||
|
gridView.setVisibility(GONE);
|
||||||
|
if (gridAdapter != null) {
|
||||||
|
gridAdapter.clear();
|
||||||
|
try {
|
||||||
|
((BookmarksActivity) getContext()).viewPagerNotifyDataSetChanged();
|
||||||
|
}catch (Exception e){
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
progressBar.setVisibility(VISIBLE);
|
||||||
|
statusTextView.setVisibility(GONE);
|
||||||
|
|
||||||
|
Observable.fromCallable(() -> controller.loadBookmarkedPictures())
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.timeout(TIMEOUT_SECONDS, TimeUnit.SECONDS)
|
||||||
|
.subscribe(this::handleSuccess, this::handleError);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the UI updates for no internet scenario
|
||||||
|
*/
|
||||||
|
private void handleNoInternet() {
|
||||||
|
progressBar.setVisibility(GONE);
|
||||||
|
if (gridAdapter == null || gridAdapter.isEmpty()) {
|
||||||
|
statusTextView.setVisibility(VISIBLE);
|
||||||
|
statusTextView.setText(getString(R.string.no_internet));
|
||||||
|
} else {
|
||||||
|
ViewUtil.showSnackbar(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.showSnackbar(parentLayout, R.string.error_loading_images);
|
||||||
|
initErrorView();
|
||||||
|
}catch (Exception e){
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the UI updates for a error scenario
|
||||||
|
*/
|
||||||
|
private void initErrorView() {
|
||||||
|
progressBar.setVisibility(GONE);
|
||||||
|
if (gridAdapter == null || gridAdapter.isEmpty()) {
|
||||||
|
statusTextView.setVisibility(VISIBLE);
|
||||||
|
statusTextView.setText(getString(R.string.no_images_found));
|
||||||
|
} else {
|
||||||
|
statusTextView.setVisibility(GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the UI updates when there is no bookmarks
|
||||||
|
*/
|
||||||
|
private void initEmptyBookmarkListView() {
|
||||||
|
progressBar.setVisibility(GONE);
|
||||||
|
if (gridAdapter == null || gridAdapter.isEmpty()) {
|
||||||
|
statusTextView.setVisibility(VISIBLE);
|
||||||
|
statusTextView.setText(getString(R.string.bookmark_empty));
|
||||||
|
} else {
|
||||||
|
statusTextView.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)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
gridAdapter.addItems(collection);
|
||||||
|
try {
|
||||||
|
((BookmarksActivity) getContext()).viewPagerNotifyDataSetChanged();
|
||||||
|
}catch (Exception e){
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
progressBar.setVisibility(GONE);
|
||||||
|
statusTextView.setVisibility(GONE);
|
||||||
|
gridView.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
|
||||||
|
);
|
||||||
|
gridView.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 gridView.getAdapter();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -49,10 +49,19 @@ public class GridViewAdapter extends ArrayAdapter {
|
||||||
* @param images
|
* @param images
|
||||||
*/
|
*/
|
||||||
public boolean containsAll(List<Media> images){
|
public boolean containsAll(List<Media> images){
|
||||||
|
if (images == null || images.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (data == null) {
|
if (data == null) {
|
||||||
data = new ArrayList<>();
|
data = new ArrayList<>();
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return images.get(0).getFilename().equals(data.get(0).getFilename());
|
if (data.size() <= 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String fileName = data.get(0).getFilename();
|
||||||
|
String imageName = images.get(0).getFilename();
|
||||||
|
return imageName.equals(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@ import android.content.Context;
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
import android.database.sqlite.SQLiteOpenHelper;
|
import android.database.sqlite.SQLiteOpenHelper;
|
||||||
|
|
||||||
|
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao;
|
||||||
|
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao;
|
||||||
import fr.free.nrw.commons.category.CategoryDao;
|
import fr.free.nrw.commons.category.CategoryDao;
|
||||||
import fr.free.nrw.commons.contributions.ContributionDao;
|
import fr.free.nrw.commons.contributions.ContributionDao;
|
||||||
import fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao;
|
import fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao;
|
||||||
|
|
@ -12,7 +14,7 @@ import fr.free.nrw.commons.modifications.ModifierSequenceDao;
|
||||||
public class DBOpenHelper extends SQLiteOpenHelper {
|
public class DBOpenHelper extends SQLiteOpenHelper {
|
||||||
|
|
||||||
private static final String DATABASE_NAME = "commons.db";
|
private static final String DATABASE_NAME = "commons.db";
|
||||||
private static final int DATABASE_VERSION = 7;
|
private static final int DATABASE_VERSION = 8;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Do not use directly - @Inject an instance where it's needed and let
|
* Do not use directly - @Inject an instance where it's needed and let
|
||||||
|
|
@ -27,6 +29,8 @@ public class DBOpenHelper extends SQLiteOpenHelper {
|
||||||
ContributionDao.Table.onCreate(sqLiteDatabase);
|
ContributionDao.Table.onCreate(sqLiteDatabase);
|
||||||
ModifierSequenceDao.Table.onCreate(sqLiteDatabase);
|
ModifierSequenceDao.Table.onCreate(sqLiteDatabase);
|
||||||
CategoryDao.Table.onCreate(sqLiteDatabase);
|
CategoryDao.Table.onCreate(sqLiteDatabase);
|
||||||
|
BookmarkPicturesDao.Table.onCreate(sqLiteDatabase);
|
||||||
|
BookmarkLocationsDao.Table.onCreate(sqLiteDatabase);
|
||||||
RecentSearchesDao.Table.onCreate(sqLiteDatabase);
|
RecentSearchesDao.Table.onCreate(sqLiteDatabase);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -35,6 +39,8 @@ public class DBOpenHelper extends SQLiteOpenHelper {
|
||||||
ContributionDao.Table.onUpdate(sqLiteDatabase, from, to);
|
ContributionDao.Table.onUpdate(sqLiteDatabase, from, to);
|
||||||
ModifierSequenceDao.Table.onUpdate(sqLiteDatabase, from, to);
|
ModifierSequenceDao.Table.onUpdate(sqLiteDatabase, from, to);
|
||||||
CategoryDao.Table.onUpdate(sqLiteDatabase, from, to);
|
CategoryDao.Table.onUpdate(sqLiteDatabase, from, to);
|
||||||
|
BookmarkPicturesDao.Table.onUpdate(sqLiteDatabase, from, to);
|
||||||
|
BookmarkLocationsDao.Table.onUpdate(sqLiteDatabase, from, to);
|
||||||
RecentSearchesDao.Table.onUpdate(sqLiteDatabase, from, to);
|
RecentSearchesDao.Table.onUpdate(sqLiteDatabase, from, to);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import fr.free.nrw.commons.WelcomeActivity;
|
||||||
import fr.free.nrw.commons.achievements.AchievementsActivity;
|
import fr.free.nrw.commons.achievements.AchievementsActivity;
|
||||||
import fr.free.nrw.commons.auth.LoginActivity;
|
import fr.free.nrw.commons.auth.LoginActivity;
|
||||||
import fr.free.nrw.commons.auth.SignupActivity;
|
import fr.free.nrw.commons.auth.SignupActivity;
|
||||||
|
import fr.free.nrw.commons.bookmarks.BookmarksActivity;
|
||||||
import fr.free.nrw.commons.category.CategoryDetailsActivity;
|
import fr.free.nrw.commons.category.CategoryDetailsActivity;
|
||||||
import fr.free.nrw.commons.category.CategoryImagesActivity;
|
import fr.free.nrw.commons.category.CategoryImagesActivity;
|
||||||
import fr.free.nrw.commons.contributions.ContributionsActivity;
|
import fr.free.nrw.commons.contributions.ContributionsActivity;
|
||||||
|
|
@ -63,4 +64,7 @@ public abstract class ActivityBuilderModule {
|
||||||
@ContributesAndroidInjector
|
@ContributesAndroidInjector
|
||||||
abstract AchievementsActivity bindAchievementsActivity();
|
abstract AchievementsActivity bindAchievementsActivity();
|
||||||
|
|
||||||
|
@ContributesAndroidInjector
|
||||||
|
abstract BookmarksActivity bindBookmarksActivity();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,18 @@ public class CommonsApplicationModule {
|
||||||
return context.getContentResolver().acquireContentProviderClient(BuildConfig.MODIFICATION_AUTHORITY);
|
return context.getContentResolver().acquireContentProviderClient(BuildConfig.MODIFICATION_AUTHORITY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Named("bookmarks")
|
||||||
|
public ContentProviderClient provideBookmarkContentProviderClient(Context context) {
|
||||||
|
return context.getContentResolver().acquireContentProviderClient(BuildConfig.BOOKMARK_AUTHORITY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Named("bookmarksLocation")
|
||||||
|
public ContentProviderClient provideBookmarkLocationContentProviderClient(Context context) {
|
||||||
|
return context.getContentResolver().acquireContentProviderClient(BuildConfig.BOOKMARK_LOCATIONS_AUTHORITY);
|
||||||
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Named("application_preferences")
|
@Named("application_preferences")
|
||||||
public SharedPreferences providesApplicationSharedPreferences(Context context) {
|
public SharedPreferences providesApplicationSharedPreferences(Context context) {
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@ package fr.free.nrw.commons.di;
|
||||||
|
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
import dagger.android.ContributesAndroidInjector;
|
import dagger.android.ContributesAndroidInjector;
|
||||||
|
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsContentProvider;
|
||||||
|
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesContentProvider;
|
||||||
import fr.free.nrw.commons.category.CategoryContentProvider;
|
import fr.free.nrw.commons.category.CategoryContentProvider;
|
||||||
import fr.free.nrw.commons.contributions.ContributionsContentProvider;
|
import fr.free.nrw.commons.contributions.ContributionsContentProvider;
|
||||||
import fr.free.nrw.commons.explore.recentsearches.RecentSearchesContentProvider;
|
import fr.free.nrw.commons.explore.recentsearches.RecentSearchesContentProvider;
|
||||||
|
|
@ -23,4 +25,10 @@ public abstract class ContentProviderBuilderModule {
|
||||||
@ContributesAndroidInjector
|
@ContributesAndroidInjector
|
||||||
abstract RecentSearchesContentProvider bindRecentSearchesContentProvider();
|
abstract RecentSearchesContentProvider bindRecentSearchesContentProvider();
|
||||||
|
|
||||||
|
@ContributesAndroidInjector
|
||||||
|
abstract BookmarkPicturesContentProvider bindBookmarkContentProvider();
|
||||||
|
|
||||||
|
@ContributesAndroidInjector
|
||||||
|
abstract BookmarkLocationsContentProvider bindBookmarkLocationContentProvider();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@ package fr.free.nrw.commons.di;
|
||||||
|
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
import dagger.android.ContributesAndroidInjector;
|
import dagger.android.ContributesAndroidInjector;
|
||||||
|
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsFragment;
|
||||||
|
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesFragment;
|
||||||
import fr.free.nrw.commons.category.CategorizationFragment;
|
import fr.free.nrw.commons.category.CategorizationFragment;
|
||||||
import fr.free.nrw.commons.category.CategoryImagesListFragment;
|
import fr.free.nrw.commons.category.CategoryImagesListFragment;
|
||||||
import fr.free.nrw.commons.category.SubCategoryListFragment;
|
import fr.free.nrw.commons.category.SubCategoryListFragment;
|
||||||
|
|
@ -67,4 +69,10 @@ public abstract class FragmentBuilderModule {
|
||||||
@ContributesAndroidInjector
|
@ContributesAndroidInjector
|
||||||
abstract RecentSearchesFragment bindRecentSearchesFragment();
|
abstract RecentSearchesFragment bindRecentSearchesFragment();
|
||||||
|
|
||||||
|
@ContributesAndroidInjector
|
||||||
|
abstract BookmarkPicturesFragment bindBookmarkPictureListFragment();
|
||||||
|
|
||||||
|
@ContributesAndroidInjector
|
||||||
|
abstract BookmarkLocationsFragment bindBookmarkLocationListFragment();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,8 @@ import butterknife.ButterKnife;
|
||||||
import fr.free.nrw.commons.Media;
|
import fr.free.nrw.commons.Media;
|
||||||
import fr.free.nrw.commons.R;
|
import fr.free.nrw.commons.R;
|
||||||
import fr.free.nrw.commons.auth.SessionManager;
|
import fr.free.nrw.commons.auth.SessionManager;
|
||||||
|
import fr.free.nrw.commons.bookmarks.Bookmark;
|
||||||
|
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao;
|
||||||
import fr.free.nrw.commons.category.CategoryDetailsActivity;
|
import fr.free.nrw.commons.category.CategoryDetailsActivity;
|
||||||
import fr.free.nrw.commons.category.CategoryImagesActivity;
|
import fr.free.nrw.commons.category.CategoryImagesActivity;
|
||||||
import fr.free.nrw.commons.contributions.Contribution;
|
import fr.free.nrw.commons.contributions.Contribution;
|
||||||
|
|
@ -59,11 +61,15 @@ public class MediaDetailPagerFragment extends CommonsDaggerSupportFragment imple
|
||||||
@Named("default_preferences")
|
@Named("default_preferences")
|
||||||
SharedPreferences prefs;
|
SharedPreferences prefs;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
BookmarkPicturesDao bookmarkDao;
|
||||||
|
|
||||||
@BindView(R.id.mediaDetailsPager)
|
@BindView(R.id.mediaDetailsPager)
|
||||||
ViewPager pager;
|
ViewPager pager;
|
||||||
private Boolean editable;
|
private Boolean editable;
|
||||||
private boolean isFeaturedImage;
|
private boolean isFeaturedImage;
|
||||||
MediaDetailAdapter adapter;
|
MediaDetailAdapter adapter;
|
||||||
|
private Bookmark bookmark;
|
||||||
|
|
||||||
public MediaDetailPagerFragment() {
|
public MediaDetailPagerFragment() {
|
||||||
this(false, false);
|
this(false, false);
|
||||||
|
|
@ -134,6 +140,10 @@ public class MediaDetailPagerFragment extends CommonsDaggerSupportFragment imple
|
||||||
MediaDetailProvider provider = (MediaDetailProvider) getActivity();
|
MediaDetailProvider provider = (MediaDetailProvider) getActivity();
|
||||||
Media m = provider.getMediaAtPosition(pager.getCurrentItem());
|
Media m = provider.getMediaAtPosition(pager.getCurrentItem());
|
||||||
switch (item.getItemId()) {
|
switch (item.getItemId()) {
|
||||||
|
case R.id.menu_bookmark_current_image:
|
||||||
|
bookmarkDao.updateBookmark(bookmark);
|
||||||
|
updateBookmarkState(item);
|
||||||
|
return true;
|
||||||
case R.id.menu_share_current_image:
|
case R.id.menu_share_current_image:
|
||||||
Intent shareIntent = new Intent(Intent.ACTION_SEND);
|
Intent shareIntent = new Intent(Intent.ACTION_SEND);
|
||||||
shareIntent.setType("text/plain");
|
shareIntent.setType("text/plain");
|
||||||
|
|
@ -257,6 +267,14 @@ public class MediaDetailPagerFragment extends CommonsDaggerSupportFragment imple
|
||||||
menu.findItem(R.id.menu_browser_current_image).setEnabled(true).setVisible(true);
|
menu.findItem(R.id.menu_browser_current_image).setEnabled(true).setVisible(true);
|
||||||
menu.findItem(R.id.menu_share_current_image).setEnabled(true).setVisible(true);
|
menu.findItem(R.id.menu_share_current_image).setEnabled(true).setVisible(true);
|
||||||
menu.findItem(R.id.menu_download_current_image).setEnabled(true).setVisible(true);
|
menu.findItem(R.id.menu_download_current_image).setEnabled(true).setVisible(true);
|
||||||
|
menu.findItem(R.id.menu_bookmark_current_image).setEnabled(true).setVisible(true);
|
||||||
|
|
||||||
|
// Initialize bookmark object
|
||||||
|
bookmark = new Bookmark(
|
||||||
|
m.getFilename(),
|
||||||
|
m.getCreator()
|
||||||
|
);
|
||||||
|
updateBookmarkState(menu.findItem(R.id.menu_bookmark_current_image));
|
||||||
|
|
||||||
if (m instanceof Contribution ) {
|
if (m instanceof Contribution ) {
|
||||||
Contribution c = (Contribution) m;
|
Contribution c = (Contribution) m;
|
||||||
|
|
@ -267,6 +285,7 @@ public class MediaDetailPagerFragment extends CommonsDaggerSupportFragment imple
|
||||||
menu.findItem(R.id.menu_browser_current_image).setEnabled(false).setVisible(false);
|
menu.findItem(R.id.menu_browser_current_image).setEnabled(false).setVisible(false);
|
||||||
menu.findItem(R.id.menu_share_current_image).setEnabled(false).setVisible(false);
|
menu.findItem(R.id.menu_share_current_image).setEnabled(false).setVisible(false);
|
||||||
menu.findItem(R.id.menu_download_current_image).setEnabled(false).setVisible(false);
|
menu.findItem(R.id.menu_download_current_image).setEnabled(false).setVisible(false);
|
||||||
|
menu.findItem(R.id.menu_bookmark_current_image).setEnabled(false).setVisible(false);
|
||||||
break;
|
break;
|
||||||
case Contribution.STATE_IN_PROGRESS:
|
case Contribution.STATE_IN_PROGRESS:
|
||||||
case Contribution.STATE_QUEUED:
|
case Contribution.STATE_QUEUED:
|
||||||
|
|
@ -275,6 +294,7 @@ public class MediaDetailPagerFragment extends CommonsDaggerSupportFragment imple
|
||||||
menu.findItem(R.id.menu_browser_current_image).setEnabled(false).setVisible(false);
|
menu.findItem(R.id.menu_browser_current_image).setEnabled(false).setVisible(false);
|
||||||
menu.findItem(R.id.menu_share_current_image).setEnabled(false).setVisible(false);
|
menu.findItem(R.id.menu_share_current_image).setEnabled(false).setVisible(false);
|
||||||
menu.findItem(R.id.menu_download_current_image).setEnabled(false).setVisible(false);
|
menu.findItem(R.id.menu_download_current_image).setEnabled(false).setVisible(false);
|
||||||
|
menu.findItem(R.id.menu_bookmark_current_image).setEnabled(false).setVisible(false);
|
||||||
break;
|
break;
|
||||||
case Contribution.STATE_COMPLETED:
|
case Contribution.STATE_COMPLETED:
|
||||||
// Default set of menu items works fine. Treat same as regular media object
|
// Default set of menu items works fine. Treat same as regular media object
|
||||||
|
|
@ -286,6 +306,12 @@ public class MediaDetailPagerFragment extends CommonsDaggerSupportFragment imple
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateBookmarkState(MenuItem item) {
|
||||||
|
boolean isBookmarked = bookmarkDao.findBookmark(bookmark);
|
||||||
|
int icon = isBookmarked ? R.drawable.ic_round_star_filled_24px : R.drawable.ic_round_star_border_24px;
|
||||||
|
item.setIcon(icon);
|
||||||
|
}
|
||||||
|
|
||||||
public void showImage(int i) {
|
public void showImage(int i) {
|
||||||
Handler handler = new Handler();
|
Handler handler = new Handler();
|
||||||
handler.postDelayed(() -> pager.setCurrentItem(i), 5);
|
handler.postDelayed(() -> pager.setCurrentItem(i), 5);
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import java.util.List;
|
||||||
|
|
||||||
import fr.free.nrw.commons.contributions.ContributionController;
|
import fr.free.nrw.commons.contributions.ContributionController;
|
||||||
|
|
||||||
class NearbyAdapterFactory {
|
public class NearbyAdapterFactory {
|
||||||
|
|
||||||
private Fragment fragment;
|
private Fragment fragment;
|
||||||
private ContributionController controller;
|
private ContributionController controller;
|
||||||
|
|
@ -21,14 +21,21 @@ class NearbyAdapterFactory {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NearbyAdapterFactory(Fragment fragment, ContributionController controller) {
|
public NearbyAdapterFactory(Fragment fragment, ContributionController controller) {
|
||||||
this.fragment = fragment;
|
this.fragment = fragment;
|
||||||
this.controller = controller;
|
this.controller = controller;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RVRendererAdapter<Place> create(List<Place> placeList) {
|
public RVRendererAdapter<Place> create(List<Place> placeList) {
|
||||||
|
return create(placeList, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RVRendererAdapter<Place> create(
|
||||||
|
List<Place> placeList,
|
||||||
|
PlaceRenderer.OnBookmarkClick onBookmarkClick
|
||||||
|
) {
|
||||||
RendererBuilder<Place> builder = new RendererBuilder<Place>()
|
RendererBuilder<Place> builder = new RendererBuilder<Place>()
|
||||||
.bind(Place.class, new PlaceRenderer(fragment, controller));
|
.bind(Place.class, new PlaceRenderer(fragment, controller, onBookmarkClick));
|
||||||
ListAdapteeCollection<Place> collection = new ListAdapteeCollection<>(
|
ListAdapteeCollection<Place> collection = new ListAdapteeCollection<>(
|
||||||
placeList != null ? placeList : Collections.emptyList());
|
placeList != null ? placeList : Collections.emptyList());
|
||||||
return new RVRendererAdapter<>(builder, collection);
|
return new RVRendererAdapter<>(builder, collection);
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,7 @@ import fr.free.nrw.commons.CommonsApplication;
|
||||||
import fr.free.nrw.commons.R;
|
import fr.free.nrw.commons.R;
|
||||||
import fr.free.nrw.commons.Utils;
|
import fr.free.nrw.commons.Utils;
|
||||||
import fr.free.nrw.commons.auth.LoginActivity;
|
import fr.free.nrw.commons.auth.LoginActivity;
|
||||||
|
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao;
|
||||||
import fr.free.nrw.commons.contributions.ContributionController;
|
import fr.free.nrw.commons.contributions.ContributionController;
|
||||||
import fr.free.nrw.commons.utils.UriDeserializer;
|
import fr.free.nrw.commons.utils.UriDeserializer;
|
||||||
import fr.free.nrw.commons.utils.ViewUtil;
|
import fr.free.nrw.commons.utils.ViewUtil;
|
||||||
|
|
@ -86,6 +87,7 @@ public class NearbyMapFragment extends DaggerFragment {
|
||||||
private LinearLayout wikidataButton;
|
private LinearLayout wikidataButton;
|
||||||
private LinearLayout directionsButton;
|
private LinearLayout directionsButton;
|
||||||
private LinearLayout commonsButton;
|
private LinearLayout commonsButton;
|
||||||
|
private LinearLayout bookmarkButton;
|
||||||
private FloatingActionButton fabPlus;
|
private FloatingActionButton fabPlus;
|
||||||
private FloatingActionButton fabCamera;
|
private FloatingActionButton fabCamera;
|
||||||
private FloatingActionButton fabGallery;
|
private FloatingActionButton fabGallery;
|
||||||
|
|
@ -95,6 +97,7 @@ public class NearbyMapFragment extends DaggerFragment {
|
||||||
private TextView title;
|
private TextView title;
|
||||||
private TextView distance;
|
private TextView distance;
|
||||||
private ImageView icon;
|
private ImageView icon;
|
||||||
|
private ImageView bookmarkButtonImage;
|
||||||
|
|
||||||
private TextView wikipediaButtonText;
|
private TextView wikipediaButtonText;
|
||||||
private TextView wikidataButtonText;
|
private TextView wikidataButtonText;
|
||||||
|
|
@ -131,6 +134,8 @@ public class NearbyMapFragment extends DaggerFragment {
|
||||||
@Inject
|
@Inject
|
||||||
@Named("direct_nearby_upload_prefs")
|
@Named("direct_nearby_upload_prefs")
|
||||||
SharedPreferences directPrefs;
|
SharedPreferences directPrefs;
|
||||||
|
@Inject
|
||||||
|
BookmarkLocationsDao bookmarkLocationDao;
|
||||||
|
|
||||||
public NearbyMapFragment() {
|
public NearbyMapFragment() {
|
||||||
}
|
}
|
||||||
|
|
@ -374,6 +379,9 @@ public class NearbyMapFragment extends DaggerFragment {
|
||||||
directionsButtonText = getActivity().findViewById(R.id.directionsButtonText);
|
directionsButtonText = getActivity().findViewById(R.id.directionsButtonText);
|
||||||
commonsButtonText = getActivity().findViewById(R.id.commonsButtonText);
|
commonsButtonText = getActivity().findViewById(R.id.commonsButtonText);
|
||||||
|
|
||||||
|
bookmarkButton = getActivity().findViewById(R.id.bookmarkButton);
|
||||||
|
bookmarkButtonImage = getActivity().findViewById(R.id.bookmarkButtonImage);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setListeners() {
|
private void setListeners() {
|
||||||
|
|
@ -721,6 +729,20 @@ public class NearbyMapFragment extends DaggerFragment {
|
||||||
|
|
||||||
private void passInfoToSheet(Place place) {
|
private void passInfoToSheet(Place place) {
|
||||||
this.place = place;
|
this.place = place;
|
||||||
|
|
||||||
|
int bookmarkIcon;
|
||||||
|
if (bookmarkLocationDao.findBookmarkLocation(place)) {
|
||||||
|
bookmarkIcon = R.drawable.ic_round_star_filled_24px;
|
||||||
|
} else {
|
||||||
|
bookmarkIcon = R.drawable.ic_round_star_border_24px;
|
||||||
|
}
|
||||||
|
bookmarkButtonImage.setImageResource(bookmarkIcon);
|
||||||
|
bookmarkButton.setOnClickListener(view -> {
|
||||||
|
boolean isBookmarked = bookmarkLocationDao.updateBookmarkLocation(place);
|
||||||
|
int updatedIcon = isBookmarked ? R.drawable.ic_round_star_filled_24px : R.drawable.ic_round_star_border_24px;
|
||||||
|
bookmarkButtonImage.setImageResource(updatedIcon);
|
||||||
|
});
|
||||||
|
|
||||||
wikipediaButton.setEnabled(place.hasWikipediaLink());
|
wikipediaButton.setEnabled(place.hasWikipediaLink());
|
||||||
wikipediaButton.setOnClickListener(view -> openWebView(place.siteLinks.getWikipediaLink()));
|
wikipediaButton.setOnClickListener(view -> openWebView(place.siteLinks.getWikipediaLink()));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,8 @@ public class Place {
|
||||||
this.distance = distance;
|
this.distance = distance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Uri getSecondaryImageUrl() { return this.secondaryImageUrl; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extracts the entity id from the wikidata link
|
* Extracts the entity id from the wikidata link
|
||||||
* @return returns the entity id if wikidata link exists
|
* @return returns the entity id if wikidata link exists
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ import butterknife.ButterKnife;
|
||||||
import fr.free.nrw.commons.R;
|
import fr.free.nrw.commons.R;
|
||||||
import fr.free.nrw.commons.Utils;
|
import fr.free.nrw.commons.Utils;
|
||||||
import fr.free.nrw.commons.auth.LoginActivity;
|
import fr.free.nrw.commons.auth.LoginActivity;
|
||||||
|
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao;
|
||||||
import fr.free.nrw.commons.contributions.ContributionController;
|
import fr.free.nrw.commons.contributions.ContributionController;
|
||||||
import fr.free.nrw.commons.di.ApplicationlessInjection;
|
import fr.free.nrw.commons.di.ApplicationlessInjection;
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
@ -50,6 +51,9 @@ public class PlaceRenderer extends Renderer<Place> {
|
||||||
@BindView(R.id.iconOverflow) LinearLayout iconOverflow;
|
@BindView(R.id.iconOverflow) LinearLayout iconOverflow;
|
||||||
@BindView(R.id.cameraButtonText) TextView cameraButtonText;
|
@BindView(R.id.cameraButtonText) TextView cameraButtonText;
|
||||||
@BindView(R.id.galleryButtonText) TextView galleryButtonText;
|
@BindView(R.id.galleryButtonText) TextView galleryButtonText;
|
||||||
|
@BindView(R.id.bookmarkButton) LinearLayout bookmarkButton;
|
||||||
|
@BindView(R.id.bookmarkButtonText) TextView bookmarkButtonText;
|
||||||
|
@BindView(R.id.bookmarkButtonImage) ImageView bookmarkButtonImage;
|
||||||
|
|
||||||
@BindView(R.id.directionsButtonText) TextView directionsButtonText;
|
@BindView(R.id.directionsButtonText) TextView directionsButtonText;
|
||||||
@BindView(R.id.iconOverflowText) TextView iconOverflowText;
|
@BindView(R.id.iconOverflowText) TextView iconOverflowText;
|
||||||
|
|
@ -60,8 +64,10 @@ public class PlaceRenderer extends Renderer<Place> {
|
||||||
|
|
||||||
private Fragment fragment;
|
private Fragment fragment;
|
||||||
private ContributionController controller;
|
private ContributionController controller;
|
||||||
|
private OnBookmarkClick onBookmarkClick;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
BookmarkLocationsDao bookmarkLocationDao;
|
||||||
@Inject @Named("prefs") SharedPreferences prefs;
|
@Inject @Named("prefs") SharedPreferences prefs;
|
||||||
@Inject @Named("direct_nearby_upload_prefs") SharedPreferences directPrefs;
|
@Inject @Named("direct_nearby_upload_prefs") SharedPreferences directPrefs;
|
||||||
|
|
||||||
|
|
@ -69,10 +75,15 @@ public class PlaceRenderer extends Renderer<Place> {
|
||||||
openedItems = new ArrayList<>();
|
openedItems = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public PlaceRenderer(Fragment fragment, ContributionController controller) {
|
public PlaceRenderer(
|
||||||
|
Fragment fragment,
|
||||||
|
ContributionController controller,
|
||||||
|
OnBookmarkClick onBookmarkClick
|
||||||
|
) {
|
||||||
this.fragment = fragment;
|
this.fragment = fragment;
|
||||||
this.controller = controller;
|
this.controller = controller;
|
||||||
openedItems = new ArrayList<>();
|
openedItems = new ArrayList<>();
|
||||||
|
this.onBookmarkClick = onBookmarkClick;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -84,6 +95,7 @@ public class PlaceRenderer extends Renderer<Place> {
|
||||||
@Override
|
@Override
|
||||||
protected void setUpView(View view) {
|
protected void setUpView(View view) {
|
||||||
ButterKnife.bind(this, view);
|
ButterKnife.bind(this, view);
|
||||||
|
closeLayout(buttonLayout);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -151,6 +163,27 @@ public class PlaceRenderer extends Renderer<Place> {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
bookmarkButton.setOnClickListener(view4 -> {
|
||||||
|
if (applicationPrefs.getBoolean("login_skipped", false)) {
|
||||||
|
// prompt the user to login
|
||||||
|
new AlertDialog.Builder(getContext())
|
||||||
|
.setMessage(R.string.login_alert_message)
|
||||||
|
.setPositiveButton(R.string.login, (dialog, which) -> {
|
||||||
|
startActivityWithFlags( getContext(), LoginActivity.class, Intent.FLAG_ACTIVITY_CLEAR_TOP,
|
||||||
|
Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||||
|
prefs.edit().putBoolean("login_skipped", false).apply();
|
||||||
|
fragment.getActivity().finish();
|
||||||
|
})
|
||||||
|
.show();
|
||||||
|
} else {
|
||||||
|
boolean isBookmarked = bookmarkLocationDao.updateBookmarkLocation(place);
|
||||||
|
int icon = isBookmarked ? R.drawable.ic_round_star_filled_24px : R.drawable.ic_round_star_border_24px;
|
||||||
|
bookmarkButtonImage.setImageResource(icon);
|
||||||
|
if (onBookmarkClick != null) {
|
||||||
|
onBookmarkClick.onClick();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void storeSharedPrefs() {
|
private void storeSharedPrefs() {
|
||||||
|
|
@ -197,6 +230,13 @@ public class PlaceRenderer extends Renderer<Place> {
|
||||||
iconOverflow.setVisibility(showMenu() ? View.VISIBLE : View.GONE);
|
iconOverflow.setVisibility(showMenu() ? View.VISIBLE : View.GONE);
|
||||||
iconOverflow.setOnClickListener(v -> popupMenuListener());
|
iconOverflow.setOnClickListener(v -> popupMenuListener());
|
||||||
|
|
||||||
|
int icon;
|
||||||
|
if (bookmarkLocationDao.findBookmarkLocation(place)) {
|
||||||
|
icon = R.drawable.ic_round_star_filled_24px;
|
||||||
|
} else {
|
||||||
|
icon = R.drawable.ic_round_star_border_24px;
|
||||||
|
}
|
||||||
|
bookmarkButtonImage.setImageResource(icon);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void popupMenuListener() {
|
private void popupMenuListener() {
|
||||||
|
|
@ -241,4 +281,8 @@ public class PlaceRenderer extends Renderer<Place> {
|
||||||
return place.hasCommonsLink() || place.hasWikidataLink();
|
return place.hasCommonsLink() || place.hasWikidataLink();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface OnBookmarkClick {
|
||||||
|
void onClick();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,8 @@ import fr.free.nrw.commons.R;
|
||||||
import fr.free.nrw.commons.WelcomeActivity;
|
import fr.free.nrw.commons.WelcomeActivity;
|
||||||
import fr.free.nrw.commons.achievements.AchievementsActivity;
|
import fr.free.nrw.commons.achievements.AchievementsActivity;
|
||||||
import fr.free.nrw.commons.auth.LoginActivity;
|
import fr.free.nrw.commons.auth.LoginActivity;
|
||||||
|
import fr.free.nrw.commons.bookmarks.BookmarksActivity;
|
||||||
|
import fr.free.nrw.commons.contributions.ContributionsActivity;
|
||||||
import fr.free.nrw.commons.category.CategoryImagesActivity;
|
import fr.free.nrw.commons.category.CategoryImagesActivity;
|
||||||
import fr.free.nrw.commons.contributions.ContributionsActivity;
|
import fr.free.nrw.commons.contributions.ContributionsActivity;
|
||||||
import fr.free.nrw.commons.nearby.NearbyActivity;
|
import fr.free.nrw.commons.nearby.NearbyActivity;
|
||||||
|
|
@ -77,6 +79,7 @@ public abstract class NavigationBaseActivity extends BaseActivity
|
||||||
nav_Menu.findItem(R.id.action_notifications).setVisible(false);
|
nav_Menu.findItem(R.id.action_notifications).setVisible(false);
|
||||||
nav_Menu.findItem(R.id.action_settings).setVisible(false);
|
nav_Menu.findItem(R.id.action_settings).setVisible(false);
|
||||||
nav_Menu.findItem(R.id.action_logout).setVisible(false);
|
nav_Menu.findItem(R.id.action_logout).setVisible(false);
|
||||||
|
nav_Menu.findItem(R.id.action_bookmarks).setVisible(true);
|
||||||
}else {
|
}else {
|
||||||
userIcon.setVisibility(View.VISIBLE);
|
userIcon.setVisibility(View.VISIBLE);
|
||||||
nav_Menu.findItem(R.id.action_login).setVisible(false);
|
nav_Menu.findItem(R.id.action_login).setVisible(false);
|
||||||
|
|
@ -84,6 +87,7 @@ public abstract class NavigationBaseActivity extends BaseActivity
|
||||||
nav_Menu.findItem(R.id.action_notifications).setVisible(true);
|
nav_Menu.findItem(R.id.action_notifications).setVisible(true);
|
||||||
nav_Menu.findItem(R.id.action_settings).setVisible(true);
|
nav_Menu.findItem(R.id.action_settings).setVisible(true);
|
||||||
nav_Menu.findItem(R.id.action_logout).setVisible(true);
|
nav_Menu.findItem(R.id.action_logout).setVisible(true);
|
||||||
|
nav_Menu.findItem(R.id.action_bookmarks).setVisible(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -210,6 +214,10 @@ public abstract class NavigationBaseActivity extends BaseActivity
|
||||||
drawerLayout.closeDrawer(navigationView);
|
drawerLayout.closeDrawer(navigationView);
|
||||||
CategoryImagesActivity.startYourself(this, getString(R.string.title_activity_explore), FEATURED_IMAGES_CATEGORY);
|
CategoryImagesActivity.startYourself(this, getString(R.string.title_activity_explore), FEATURED_IMAGES_CATEGORY);
|
||||||
return true;
|
return true;
|
||||||
|
case R.id.action_bookmarks:
|
||||||
|
drawerLayout.closeDrawer(navigationView);
|
||||||
|
BookmarksActivity.startYourself(this);
|
||||||
|
return true;
|
||||||
default:
|
default:
|
||||||
Timber.e("Unknown option [%s] selected from the navigation menu", itemId);
|
Timber.e("Unknown option [%s] selected from the navigation menu", itemId);
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
9
app/src/main/res/drawable/ic_round_star_border_24px.xml
Normal file
9
app/src/main/res/drawable/ic_round_star_border_24px.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@color/white"
|
||||||
|
android:pathData="M19.65,9.04l-4.84,-0.42 -1.89,-4.45c-0.34,-0.81 -1.5,-0.81 -1.84,0L9.19,8.63l-4.83,0.41c-0.88,0.07 -1.24,1.17 -0.57,1.75l3.67,3.18 -1.1,4.72c-0.2,0.86 0.73,1.54 1.49,1.08l4.15,-2.5 4.15,2.51c0.76,0.46 1.69,-0.22 1.49,-1.08l-1.1,-4.73 3.67,-3.18c0.67,-0.58 0.32,-1.68 -0.56,-1.75zM12,15.4l-3.76,2.27 1,-4.28 -3.32,-2.88 4.38,-0.38L12,6.1l1.71,4.04 4.38,0.38 -3.32,2.88 1,4.28L12,15.4z"/>
|
||||||
|
</vector>
|
||||||
9
app/src/main/res/drawable/ic_round_star_filled_24px.xml
Normal file
9
app/src/main/res/drawable/ic_round_star_filled_24px.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@color/white"
|
||||||
|
android:pathData="M19.65,9.04l-4.84,-0.42 -1.89,-4.45c-0.34,-0.81 -1.5,-0.81 -1.84,0L9.19,8.63l-4.83,0.41c-0.88,0.07 -1.24,1.17 -0.57,1.75l3.67,3.18 -1.1,4.72c-0.2,0.86 0.73,1.54 1.49,1.08l4.15,-2.5 4.15,2.51c0.76,0.46 1.69,-0.22 1.49,-1.08l-1.1,-4.73 3.67,-3.18c0.67,-0.58 0.32,-1.68 -0.56,-1.75z"/>
|
||||||
|
</vector>
|
||||||
58
app/src/main/res/layout/activity_bookmarks.xml
Normal file
58
app/src/main/res/layout/activity_bookmarks.xml
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:id="@+id/drawer_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<android.support.constraint.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
layout="@layout/toolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_scrollFlags="scroll" />
|
||||||
|
|
||||||
|
<android.support.design.widget.TabLayout
|
||||||
|
android:id="@+id/tabLayoutBookmarks"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/toolbar" />
|
||||||
|
|
||||||
|
<android.support.v4.view.ViewPager
|
||||||
|
android:id="@+id/viewPagerBookmarks"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/tabLayoutBookmarks" />
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/fragmentContainer"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/toolbar"/>
|
||||||
|
|
||||||
|
</android.support.constraint.ConstraintLayout>
|
||||||
|
|
||||||
|
|
||||||
|
<android.support.design.widget.NavigationView
|
||||||
|
android:id="@+id/navigation_view"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_gravity="start"
|
||||||
|
app:headerLayout="@layout/drawer_header"
|
||||||
|
app:menu="@menu/drawer" />
|
||||||
|
|
||||||
|
</android.support.v4.widget.DrawerLayout>
|
||||||
|
|
@ -61,6 +61,34 @@
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
>
|
>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/bookmarkButton"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="16dp"
|
||||||
|
android:clickable="true"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:background="@drawable/button_background_selector"
|
||||||
|
>
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/bookmarkButtonImage"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
app:srcCompat="@drawable/ic_round_star_border_24px"
|
||||||
|
android:tint="@color/button_blue"/>
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/bookmarkButtonText"
|
||||||
|
android:paddingTop="8dp"
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:text="CAMERA"
|
||||||
|
android:visibility="gone"
|
||||||
|
/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/directionsButton"
|
android:id="@+id/directionsButton"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
|
|
|
||||||
32
app/src/main/res/layout/fragment_bookmarks_locations.xml
Normal file
32
app/src/main/res/layout/fragment_bookmarks_locations.xml
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/parentLayout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/statusMessage"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_centerHorizontal="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:text="@string/waiting_first_sync"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/loadingImagesProgressBar"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_centerInParent="true"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
<android.support.v7.widget.RecyclerView
|
||||||
|
android:id="@+id/listView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
/>
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
37
app/src/main/res/layout/fragment_bookmarks_pictures.xml
Normal file
37
app/src/main/res/layout/fragment_bookmarks_pictures.xml
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/parentLayout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/statusMessage"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_centerHorizontal="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:text="@string/waiting_first_sync"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/loadingImagesProgressBar"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_centerInParent="true"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
<GridView
|
||||||
|
android:id="@+id/bookmarkedPicturesList"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:columnWidth="240dp"
|
||||||
|
android:fadingEdge="none"
|
||||||
|
android:fastScrollEnabled="true"
|
||||||
|
android:listSelector="@null"
|
||||||
|
android:numColumns="auto_fit"
|
||||||
|
android:stretchMode="columnWidth" />
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
|
|
@ -10,6 +10,34 @@
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
>
|
>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/bookmarkButton"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="16dp"
|
||||||
|
android:clickable="true"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:background="@drawable/button_background_selector"
|
||||||
|
>
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/bookmarkButtonImage"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
app:srcCompat="@drawable/ic_round_star_border_24px"
|
||||||
|
android:tint="@color/button_blue"/>
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/bookmarkButtonText"
|
||||||
|
android:paddingTop="8dp"
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:text="CAMERA"
|
||||||
|
android:visibility="gone"
|
||||||
|
/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/cameraButton"
|
android:id="@+id/cameraButton"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,11 @@
|
||||||
android:icon="@drawable/ic_explore_24dp"
|
android:icon="@drawable/ic_explore_24dp"
|
||||||
android:title="@string/navigation_item_explore"/>
|
android:title="@string/navigation_item_explore"/>
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_bookmarks"
|
||||||
|
android:icon="@drawable/ic_round_star_filled_24px"
|
||||||
|
android:title="@string/navigation_item_bookmarks"/>
|
||||||
|
|
||||||
</group>
|
</group>
|
||||||
<group android:id="@+id/drawer_account">
|
<group android:id="@+id/drawer_account">
|
||||||
<item
|
<item
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,16 @@
|
||||||
|
|
||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
<item
|
||||||
|
android:id="@+id/menu_bookmark_current_image"
|
||||||
|
android:icon="@drawable/ic_round_star_border_24px"
|
||||||
|
android:title="@string/menu_bookmark"
|
||||||
|
app:showAsAction="always" />
|
||||||
<item
|
<item
|
||||||
android:id="@+id/menu_share_current_image"
|
android:id="@+id/menu_share_current_image"
|
||||||
android:icon="@drawable/ic_share_black_24dp"
|
android:icon="@drawable/ic_share_black_24dp"
|
||||||
android:title="@string/menu_share"
|
android:title="@string/menu_share"
|
||||||
app:showAsAction="ifRoom" />
|
app:showAsAction="always" />
|
||||||
<item
|
<item
|
||||||
android:id="@+id/menu_browser_current_image"
|
android:id="@+id/menu_browser_current_image"
|
||||||
android:title="@string/menu_open_in_browser"
|
android:title="@string/menu_open_in_browser"
|
||||||
|
|
|
||||||
|
|
@ -361,6 +361,8 @@
|
||||||
<string name="notifications_channel_name_all">Notification de Commons</string>
|
<string name="notifications_channel_name_all">Notification de Commons</string>
|
||||||
<string name="storage_permission">Droit de stockage</string>
|
<string name="storage_permission">Droit de stockage</string>
|
||||||
<string name="write_storage_permission_rationale_for_image_share">Nous avons besoin de votre autorisation pour accéder au stockage externe de votre appareil, afin de téléverser des images.</string>
|
<string name="write_storage_permission_rationale_for_image_share">Nous avons besoin de votre autorisation pour accéder au stockage externe de votre appareil, afin de téléverser des images.</string>
|
||||||
|
<string name="navigation_item_bookmarks">Favoris</string>
|
||||||
|
<string name="title_activity_bookmarks">Favoris</string>
|
||||||
<string name="log_collection_started">Enregistrement dans le journal, commencé. Veuillez REDEMARRER l\'application, exécutez l\'action que vous souhaitez tracer, et cliquez de nouveau sur \'Envoyer les journaux\'</string>
|
<string name="log_collection_started">Enregistrement dans le journal, commencé. Veuillez REDEMARRER l\'application, exécutez l\'action que vous souhaitez tracer, et cliquez de nouveau sur \'Envoyer les journaux\'</string>
|
||||||
<string name="no_uploads">Bienvenue sur Commons !\n\nTéléversez votre premier média en cliquant sur l’icône de l’appareil photo ou sur la galerie ci-dessus.</string>
|
<string name="no_uploads">Bienvenue sur Commons !\n\nTéléversez votre premier média en cliquant sur l’icône de l’appareil photo ou sur la galerie ci-dessus.</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
||||||
|
|
@ -77,8 +77,8 @@
|
||||||
<item quantity="other">Starting %1$d uploads</item>
|
<item quantity="other">Starting %1$d uploads</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
<plurals name="multiple_uploads_title">
|
<plurals name="multiple_uploads_title">
|
||||||
<item quantity="one">%1$d upload</item>
|
<item quantity="one">%1$d upload</item>
|
||||||
<item quantity="other">%1$d uploads</item>
|
<item quantity="other">%1$d uploads</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
<string name="categories_not_found">No categories matching %1$s found</string>
|
<string name="categories_not_found">No categories matching %1$s found</string>
|
||||||
<string name="categories_skip_explanation">Add categories to make your images more discoverable on Wikimedia Commons.\nStart typing to add categories.</string>
|
<string name="categories_skip_explanation">Add categories to make your images more discoverable on Wikimedia Commons.\nStart typing to add categories.</string>
|
||||||
|
|
@ -192,7 +192,7 @@
|
||||||
<string name="maximum_limit_alert">Unable to display more than 500</string>
|
<string name="maximum_limit_alert">Unable to display more than 500</string>
|
||||||
<string name="set_limit">Set Recent Upload Limit</string>
|
<string name="set_limit">Set Recent Upload Limit</string>
|
||||||
<string name="login_failed_2fa_not_supported">Two factor authentication is currently not supported.</string>
|
<string name="login_failed_2fa_not_supported">Two factor authentication is currently not supported.</string>
|
||||||
<string name="logout_verification">Do you really want to logout?</string>
|
<string name="logout_verification">Do you really want to logout?</string>
|
||||||
<string name="commons_logo">Commons Logo</string>
|
<string name="commons_logo">Commons Logo</string>
|
||||||
<string name="commons_website">Commons Website</string>
|
<string name="commons_website">Commons Website</string>
|
||||||
<string name="commons_facebook">Commons Facebook Page</string>
|
<string name="commons_facebook">Commons Facebook Page</string>
|
||||||
|
|
@ -356,7 +356,14 @@
|
||||||
<string name="notifications_channel_name_all">Commons Notification</string>
|
<string name="notifications_channel_name_all">Commons Notification</string>
|
||||||
<string name="storage_permission">Storage Permission</string>
|
<string name="storage_permission">Storage Permission</string>
|
||||||
<string name="write_storage_permission_rationale_for_image_share">We need your permission to access the external storage of your device in order to upload images.</string>
|
<string name="write_storage_permission_rationale_for_image_share">We need your permission to access the external storage of your device in order to upload images.</string>
|
||||||
|
<string name="navigation_item_bookmarks">Bookmarks</string>
|
||||||
|
<string name="title_activity_bookmarks">Bookmarks</string>
|
||||||
|
<string name="title_page_bookmarks_pictures">Pictures</string>
|
||||||
|
<string name="title_page_bookmarks_locations">Locations</string>
|
||||||
|
<string name="menu_bookmark">Add/Remove to bookmarks</string>
|
||||||
|
<string name="provider_bookmarks">Bookmarks</string>
|
||||||
|
<string name="bookmark_empty">You haven\'t added any bookmarks</string>
|
||||||
|
<string name="provider_bookmarks_location">Bookmarks</string>
|
||||||
<string name="log_collection_started">Log collection started. Please RESTART the app, perform action that you wish to log, and then tap \'Send log file\' again</string>
|
<string name="log_collection_started">Log collection started. Please RESTART the app, perform action that you wish to log, and then tap \'Send log file\' again</string>
|
||||||
<string name="no_uploads">Welcome to Commons!\n
|
<string name="no_uploads">Welcome to Commons!\n
|
||||||
Upload your first media by touching the camera or gallery icon above.</string>
|
Upload your first media by touching the camera or gallery icon above.</string>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,262 @@
|
||||||
|
package fr.free.nrw.commons.bookmarks.locations
|
||||||
|
|
||||||
|
import android.content.ContentProviderClient
|
||||||
|
import android.content.ContentValues
|
||||||
|
import android.database.Cursor
|
||||||
|
import android.database.MatrixCursor
|
||||||
|
import android.database.sqlite.SQLiteDatabase
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.RemoteException
|
||||||
|
import com.nhaarman.mockito_kotlin.*
|
||||||
|
import fr.free.nrw.commons.BuildConfig
|
||||||
|
import fr.free.nrw.commons.TestCommonsApplication
|
||||||
|
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsContentProvider.BASE_URI
|
||||||
|
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao.Table.*
|
||||||
|
import fr.free.nrw.commons.location.LatLng
|
||||||
|
import fr.free.nrw.commons.nearby.Place
|
||||||
|
import fr.free.nrw.commons.nearby.Sitelinks
|
||||||
|
import junit.framework.Assert.*
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.robolectric.RobolectricTestRunner
|
||||||
|
import org.robolectric.annotation.Config
|
||||||
|
|
||||||
|
@RunWith(RobolectricTestRunner::class)
|
||||||
|
@Config(constants = BuildConfig::class, sdk = [21], application = TestCommonsApplication::class)
|
||||||
|
class BookMarkLocationDaoTest {
|
||||||
|
private val columns = arrayOf(COLUMN_NAME,
|
||||||
|
COLUMN_DESCRIPTION,
|
||||||
|
COLUMN_CATEGORY,
|
||||||
|
COLUMN_LABEL_TEXT,
|
||||||
|
COLUMN_LABEL_ICON,
|
||||||
|
COLUMN_IMAGE_URL,
|
||||||
|
COLUMN_WIKIPEDIA_LINK,
|
||||||
|
COLUMN_WIKIDATA_LINK,
|
||||||
|
COLUMN_COMMONS_LINK,
|
||||||
|
COLUMN_LAT,
|
||||||
|
COLUMN_LONG)
|
||||||
|
private val client: ContentProviderClient = mock()
|
||||||
|
private val database: SQLiteDatabase = mock()
|
||||||
|
private val captor = argumentCaptor<ContentValues>()
|
||||||
|
|
||||||
|
private lateinit var testObject: BookmarkLocationsDao
|
||||||
|
private lateinit var examplePlaceBookmark: Place
|
||||||
|
private lateinit var exampleLabel: Place.Label
|
||||||
|
private lateinit var exampleUri: Uri
|
||||||
|
private lateinit var exampleLocation: LatLng
|
||||||
|
private lateinit var builder: Sitelinks.Builder
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
exampleLabel = Place.Label.FOREST
|
||||||
|
exampleUri = Uri.parse("wikimedia/uri")
|
||||||
|
exampleLocation = LatLng(40.0,51.4, 1f)
|
||||||
|
|
||||||
|
builder = Sitelinks.Builder()
|
||||||
|
builder.setWikipediaLink("wikipediaLink")
|
||||||
|
builder.setWikidataLink("wikidataLink")
|
||||||
|
builder.setCommonsLink("commonsLink")
|
||||||
|
|
||||||
|
|
||||||
|
examplePlaceBookmark = Place("placeName", exampleLabel, "placeDescription",
|
||||||
|
exampleUri, exampleLocation, "placeCategory", builder.build())
|
||||||
|
testObject = BookmarkLocationsDao { client }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun createTable() {
|
||||||
|
onCreate(database)
|
||||||
|
verify(database).execSQL(CREATE_TABLE_STATEMENT)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun deleteTable() {
|
||||||
|
onDelete(database)
|
||||||
|
inOrder(database) {
|
||||||
|
verify(database).execSQL(DROP_TABLE_STATEMENT)
|
||||||
|
verify(database).execSQL(CREATE_TABLE_STATEMENT)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun createFromCursor() {
|
||||||
|
createCursor(1).let { cursor ->
|
||||||
|
cursor.moveToFirst()
|
||||||
|
testObject.fromCursor(cursor).let {
|
||||||
|
assertEquals("placeName", it.name)
|
||||||
|
assertEquals(Place.Label.FOREST, it.label)
|
||||||
|
assertEquals("placeDescription", it.longDescription)
|
||||||
|
assertEquals(exampleUri, it.secondaryImageUrl)
|
||||||
|
assertEquals(40.0, it.location.latitude)
|
||||||
|
assertEquals(51.4, it.location.longitude)
|
||||||
|
assertEquals("placeCategory", it.category)
|
||||||
|
assertEquals(builder.build().wikipediaLink, it.siteLinks.wikipediaLink)
|
||||||
|
assertEquals(builder.build().wikidataLink, it.siteLinks.wikidataLink)
|
||||||
|
assertEquals(builder.build().commonsLink, it.siteLinks.commonsLink)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getAllLocationBookmarks() {
|
||||||
|
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull())).thenReturn(createCursor(14))
|
||||||
|
|
||||||
|
var result = testObject.allBookmarksLocations
|
||||||
|
|
||||||
|
assertEquals(14,(result.size))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = RuntimeException::class)
|
||||||
|
fun getAllLocationBookmarksTranslatesExceptions() {
|
||||||
|
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull())).thenThrow(RemoteException(""))
|
||||||
|
testObject.allBookmarksLocations
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getAllLocationBookmarksReturnsEmptyList_emptyCursor() {
|
||||||
|
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull())).thenReturn(createCursor(0))
|
||||||
|
assertTrue(testObject.allBookmarksLocations.isEmpty())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getAllLocationBookmarksReturnsEmptyList_nullCursor() {
|
||||||
|
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull())).thenReturn(null)
|
||||||
|
assertTrue(testObject.allBookmarksLocations.isEmpty())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun cursorsAreClosedAfterGetAllLocationBookmarksQuery() {
|
||||||
|
val mockCursor: Cursor = mock()
|
||||||
|
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull())).thenReturn(mockCursor)
|
||||||
|
whenever(mockCursor.moveToFirst()).thenReturn(false)
|
||||||
|
|
||||||
|
testObject.allBookmarksLocations
|
||||||
|
|
||||||
|
verify(mockCursor).close()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun updateNewLocationBookmark() {
|
||||||
|
whenever(client.insert(any(), any())).thenReturn(Uri.EMPTY)
|
||||||
|
whenever(client.query(any(), any(), any(), any(), anyOrNull())).thenReturn(null)
|
||||||
|
|
||||||
|
assertTrue(testObject.updateBookmarkLocation(examplePlaceBookmark))
|
||||||
|
verify(client).insert(eq(BASE_URI), captor.capture())
|
||||||
|
captor.firstValue.let { cv ->
|
||||||
|
assertEquals(11, cv.size())
|
||||||
|
assertEquals(examplePlaceBookmark.name, cv.getAsString(COLUMN_NAME))
|
||||||
|
assertEquals(examplePlaceBookmark.longDescription, cv.getAsString(COLUMN_DESCRIPTION))
|
||||||
|
assertEquals(examplePlaceBookmark.label.text, cv.getAsString(COLUMN_LABEL_TEXT))
|
||||||
|
assertEquals(examplePlaceBookmark.category, cv.getAsString(COLUMN_CATEGORY))
|
||||||
|
assertEquals(examplePlaceBookmark.location.latitude, cv.getAsDouble(COLUMN_LAT))
|
||||||
|
assertEquals(examplePlaceBookmark.location.longitude, cv.getAsDouble(COLUMN_LONG))
|
||||||
|
assertEquals(examplePlaceBookmark.siteLinks.wikipediaLink.toString(), cv.getAsString(COLUMN_WIKIPEDIA_LINK))
|
||||||
|
assertEquals(examplePlaceBookmark.siteLinks.wikidataLink.toString(), cv.getAsString(COLUMN_WIKIDATA_LINK))
|
||||||
|
assertEquals(examplePlaceBookmark.siteLinks.commonsLink.toString(), cv.getAsString(COLUMN_COMMONS_LINK))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun updateExistingLocationBookmark() {
|
||||||
|
whenever(client.delete(isA(), isNull(), isNull())).thenReturn(1)
|
||||||
|
whenever(client.query(any(), any(), any(), any(), anyOrNull())).thenReturn(createCursor(1))
|
||||||
|
|
||||||
|
assertFalse(testObject.updateBookmarkLocation(examplePlaceBookmark))
|
||||||
|
verify(client).delete(eq(BookmarkLocationsContentProvider.uriForName(examplePlaceBookmark.name)), isNull(), isNull())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun findExistingLocationBookmark() {
|
||||||
|
whenever(client.query(any(), any(), any(), any(), anyOrNull())).thenReturn(createCursor(1))
|
||||||
|
assertTrue(testObject.findBookmarkLocation(examplePlaceBookmark))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = RuntimeException::class)
|
||||||
|
fun findLocationBookmarkTranslatesExceptions() {
|
||||||
|
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull())).thenThrow(RemoteException(""))
|
||||||
|
testObject.findBookmarkLocation(examplePlaceBookmark)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun findNotExistingLocationBookmarkReturnsNull_emptyCursor() {
|
||||||
|
whenever(client.query(any(), any(), any(), any(), anyOrNull())).thenReturn(createCursor(0))
|
||||||
|
assertFalse(testObject.findBookmarkLocation(examplePlaceBookmark))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun findNotExistingLocationBookmarkReturnsNull_nullCursor() {
|
||||||
|
whenever(client.query(any(), any(), any(), any(), anyOrNull())).thenReturn(null)
|
||||||
|
assertFalse(testObject.findBookmarkLocation(examplePlaceBookmark))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun cursorsAreClosedAfterFindLocationBookmarkQuery() {
|
||||||
|
val mockCursor: Cursor = mock()
|
||||||
|
whenever(client.query(any(), any(), any(), any(), anyOrNull())).thenReturn(mockCursor)
|
||||||
|
whenever(mockCursor.moveToFirst()).thenReturn(false)
|
||||||
|
|
||||||
|
testObject.findBookmarkLocation(examplePlaceBookmark)
|
||||||
|
|
||||||
|
verify(mockCursor).close()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun migrateTableVersionFrom_v1_to_v2() {
|
||||||
|
onUpdate(database, 1, 2)
|
||||||
|
// Table didnt exist before v5
|
||||||
|
verifyZeroInteractions(database)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun migrateTableVersionFrom_v2_to_v3() {
|
||||||
|
onUpdate(database, 2, 3)
|
||||||
|
// Table didnt exist before v5
|
||||||
|
verifyZeroInteractions(database)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun migrateTableVersionFrom_v3_to_v4() {
|
||||||
|
onUpdate(database, 3, 4)
|
||||||
|
// Table didnt exist before v5
|
||||||
|
verifyZeroInteractions(database)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun migrateTableVersionFrom_v4_to_v5() {
|
||||||
|
onUpdate(database, 4, 5)
|
||||||
|
// Table didnt change in version 5
|
||||||
|
verifyZeroInteractions(database)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun migrateTableVersionFrom_v5_to_v6() {
|
||||||
|
onUpdate(database, 5, 6)
|
||||||
|
// Table didnt change in version 6
|
||||||
|
verifyZeroInteractions(database)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun migrateTableVersionFrom_v6_to_v7() {
|
||||||
|
onUpdate(database, 6, 7)
|
||||||
|
// Table didnt change in version 7
|
||||||
|
verifyZeroInteractions(database)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun migrateTableVersionFrom_v7_to_v8() {
|
||||||
|
onUpdate(database, 7, 8)
|
||||||
|
verify(database).execSQL(CREATE_TABLE_STATEMENT)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createCursor(rowCount: Int) = MatrixCursor(columns, rowCount).apply {
|
||||||
|
|
||||||
|
for (i in 0 until rowCount) {
|
||||||
|
addRow(listOf("placeName", "placeDescription","placeCategory", exampleLabel.text, exampleLabel.icon,
|
||||||
|
exampleUri, builder.build().wikipediaLink, builder.build().wikidataLink, builder.build().commonsLink,
|
||||||
|
exampleLocation.latitude, exampleLocation.longitude))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,217 @@
|
||||||
|
package fr.free.nrw.commons.bookmarks.pictures
|
||||||
|
|
||||||
|
import android.content.ContentProviderClient
|
||||||
|
import android.content.ContentValues
|
||||||
|
import android.database.Cursor
|
||||||
|
import android.database.MatrixCursor
|
||||||
|
import android.database.sqlite.SQLiteDatabase
|
||||||
|
import android.os.RemoteException
|
||||||
|
import com.nhaarman.mockito_kotlin.*
|
||||||
|
import fr.free.nrw.commons.BuildConfig
|
||||||
|
import fr.free.nrw.commons.TestCommonsApplication
|
||||||
|
import fr.free.nrw.commons.bookmarks.Bookmark
|
||||||
|
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesContentProvider.BASE_URI
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.robolectric.RobolectricTestRunner
|
||||||
|
import org.robolectric.annotation.Config
|
||||||
|
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao.Table.*
|
||||||
|
import org.junit.Assert.*
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
@RunWith(RobolectricTestRunner::class)
|
||||||
|
@Config(constants = BuildConfig::class, sdk = [21], application = TestCommonsApplication::class)
|
||||||
|
class BookmarkPictureDaoTest {
|
||||||
|
|
||||||
|
private val columns = arrayOf(COLUMN_MEDIA_NAME, COLUMN_CREATOR)
|
||||||
|
private val client: ContentProviderClient = mock()
|
||||||
|
private val database: SQLiteDatabase = mock()
|
||||||
|
private val captor = argumentCaptor<ContentValues>()
|
||||||
|
|
||||||
|
private lateinit var testObject: BookmarkPicturesDao
|
||||||
|
private lateinit var exampleBookmark: Bookmark
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
exampleBookmark = Bookmark("mediaName", "creatorName")
|
||||||
|
testObject = BookmarkPicturesDao { client }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun createTable() {
|
||||||
|
onCreate(database)
|
||||||
|
verify(database).execSQL(CREATE_TABLE_STATEMENT)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun deleteTable() {
|
||||||
|
onDelete(database)
|
||||||
|
inOrder(database) {
|
||||||
|
verify(database).execSQL(DROP_TABLE_STATEMENT)
|
||||||
|
verify(database).execSQL(CREATE_TABLE_STATEMENT)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun createFromCursor() {
|
||||||
|
createCursor(1).let { cursor ->
|
||||||
|
cursor.moveToFirst()
|
||||||
|
testObject.fromCursor(cursor).let {
|
||||||
|
assertEquals("mediaName", it.mediaName)
|
||||||
|
assertEquals("creatorName", it.mediaCreator)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getAllBookmarks() {
|
||||||
|
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull())).thenReturn(createCursor(14))
|
||||||
|
|
||||||
|
var result = testObject.allBookmarks
|
||||||
|
|
||||||
|
assertEquals(14,(result.size))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = RuntimeException::class)
|
||||||
|
fun getAllBookmarksTranslatesExceptions() {
|
||||||
|
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull())).thenThrow(RemoteException(""))
|
||||||
|
testObject.allBookmarks
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getAllBookmarksReturnsEmptyList_emptyCursor() {
|
||||||
|
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull())).thenReturn(createCursor(0))
|
||||||
|
assertTrue(testObject.allBookmarks.isEmpty())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getAllBookmarksReturnsEmptyList_nullCursor() {
|
||||||
|
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull())).thenReturn(null)
|
||||||
|
assertTrue(testObject.allBookmarks.isEmpty())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun cursorsAreClosedAfterGetAllBookmarksQuery() {
|
||||||
|
val mockCursor: Cursor = mock()
|
||||||
|
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull())).thenReturn(mockCursor)
|
||||||
|
whenever(mockCursor.moveToFirst()).thenReturn(false)
|
||||||
|
|
||||||
|
testObject.allBookmarks
|
||||||
|
|
||||||
|
verify(mockCursor).close()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun updateNewBookmark() {
|
||||||
|
whenever(client.insert(any(), any())).thenReturn(exampleBookmark.contentUri)
|
||||||
|
whenever(client.query(any(), any(), any(), any(), anyOrNull())).thenReturn(null)
|
||||||
|
|
||||||
|
assertTrue(testObject.updateBookmark(exampleBookmark))
|
||||||
|
verify(client).insert(eq(BASE_URI), captor.capture())
|
||||||
|
captor.firstValue.let { cv ->
|
||||||
|
assertEquals(2, cv.size())
|
||||||
|
assertEquals(exampleBookmark.mediaName, cv.getAsString(COLUMN_MEDIA_NAME))
|
||||||
|
assertEquals(exampleBookmark.mediaCreator, cv.getAsString(COLUMN_CREATOR))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun updateExistingBookmark() {
|
||||||
|
whenever(client.delete(isA(), isNull(), isNull())).thenReturn(1)
|
||||||
|
whenever(client.query(any(), any(), any(), any(), anyOrNull())).thenReturn(createCursor(1))
|
||||||
|
|
||||||
|
assertFalse(testObject.updateBookmark(exampleBookmark))
|
||||||
|
verify(client).delete(eq(exampleBookmark.contentUri), isNull(), isNull())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun findExistingBookmark() {
|
||||||
|
whenever(client.query(any(), any(), any(), any(), anyOrNull())).thenReturn(createCursor(1))
|
||||||
|
assertTrue(testObject.findBookmark(exampleBookmark))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = RuntimeException::class)
|
||||||
|
fun findBookmarkTranslatesExceptions() {
|
||||||
|
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull())).thenThrow(RemoteException(""))
|
||||||
|
testObject.findBookmark(exampleBookmark)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun findNotExistingBookmarkReturnsNull_emptyCursor() {
|
||||||
|
whenever(client.query(any(), any(), any(), any(), anyOrNull())).thenReturn(createCursor(0))
|
||||||
|
assertFalse(testObject.findBookmark(exampleBookmark))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun findNotExistingBookmarkReturnsNull_nullCursor() {
|
||||||
|
whenever(client.query(any(), any(), any(), any(), anyOrNull())).thenReturn(null)
|
||||||
|
assertFalse(testObject.findBookmark(exampleBookmark))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun cursorsAreClosedAfterFindBookmarkQuery() {
|
||||||
|
val mockCursor: Cursor = mock()
|
||||||
|
whenever(client.query(any(), any(), any(), any(), anyOrNull())).thenReturn(mockCursor)
|
||||||
|
whenever(mockCursor.moveToFirst()).thenReturn(false)
|
||||||
|
|
||||||
|
testObject.findBookmark(exampleBookmark)
|
||||||
|
|
||||||
|
verify(mockCursor).close()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun migrateTableVersionFrom_v1_to_v2() {
|
||||||
|
onUpdate(database, 1, 2)
|
||||||
|
// Table didnt exist before v5
|
||||||
|
verifyZeroInteractions(database)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun migrateTableVersionFrom_v2_to_v3() {
|
||||||
|
onUpdate(database, 2, 3)
|
||||||
|
// Table didnt exist before v5
|
||||||
|
verifyZeroInteractions(database)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun migrateTableVersionFrom_v3_to_v4() {
|
||||||
|
onUpdate(database, 3, 4)
|
||||||
|
// Table didnt exist before v5
|
||||||
|
verifyZeroInteractions(database)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun migrateTableVersionFrom_v4_to_v5() {
|
||||||
|
onUpdate(database, 4, 5)
|
||||||
|
// Table didnt change in version 5
|
||||||
|
verifyZeroInteractions(database)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun migrateTableVersionFrom_v5_to_v6() {
|
||||||
|
onUpdate(database, 5, 6)
|
||||||
|
// Table didnt change in version 6
|
||||||
|
verifyZeroInteractions(database)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun migrateTableVersionFrom_v6_to_v7() {
|
||||||
|
onUpdate(database, 6, 7)
|
||||||
|
// Table didnt change in version 7
|
||||||
|
verifyZeroInteractions(database)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun migrateTableVersionFrom_v7_to_v8() {
|
||||||
|
onUpdate(database, 7, 8)
|
||||||
|
verify(database).execSQL(CREATE_TABLE_STATEMENT)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createCursor(rowCount: Int) = MatrixCursor(columns, rowCount).apply {
|
||||||
|
for (i in 0 until rowCount) {
|
||||||
|
addRow(listOf("mediaName", "creatorName"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -86,6 +86,20 @@ class CategoryDaoTest {
|
||||||
verifyZeroInteractions(database)
|
verifyZeroInteractions(database)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun migrateTableVersionFrom_v6_to_v7() {
|
||||||
|
onUpdate(database, 6, 7)
|
||||||
|
// Table didnt change in version 7
|
||||||
|
verifyZeroInteractions(database)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun migrateTableVersionFrom_v7_to_v8() {
|
||||||
|
onUpdate(database, 7, 8)
|
||||||
|
// Table didnt change in version 8
|
||||||
|
verifyZeroInteractions(database)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun createFromCursor() {
|
fun createFromCursor() {
|
||||||
createCursor(1).let { cursor ->
|
createCursor(1).let { cursor ->
|
||||||
|
|
|
||||||
|
|
@ -105,6 +105,20 @@ class ContributionDaoTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun migrateTableVersionFrom_v6_to_v7() {
|
||||||
|
Table.onUpdate(database, 6, 7)
|
||||||
|
// Table didnt change in version 7
|
||||||
|
verifyZeroInteractions(database)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun migrateTableVersionFrom_v7_to_v8() {
|
||||||
|
Table.onUpdate(database, 7, 8)
|
||||||
|
// Table didnt change in version 8
|
||||||
|
verifyZeroInteractions(database)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun saveNewContribution_nonNullFields() {
|
fun saveNewContribution_nonNullFields() {
|
||||||
whenever(client.insert(isA(), isA())).thenReturn(contentUri)
|
whenever(client.insert(isA(), isA())).thenReturn(contentUri)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue