Merge remote-tracking branch 'origin/master'

This commit is contained in:
mebr1416 2017-08-03 15:01:37 +03:00
commit ece14bd86a
172 changed files with 955 additions and 809 deletions

View file

@ -33,8 +33,6 @@ after_success:
after_failure:
- echo '*** Connected Test Rsults ***'
- w3m -dump ${TRAVIS_BUILD_DIR}/app/build/reports/androidTests/connected/*Test.html
- echo '*** Lint Results ***'
- cat ${TRAVIS_BUILD_DIR}/app/build/reports/lint-results.xml
jdk:
# - openjdk8 # not yet available
- oraclejdk8

View file

@ -1,5 +1,19 @@
# Wikimedia Commons for Android
## v2.5.0 beta
- Added one-time popup for beta users to provide feedback on IEG renewal proposal
- Added link to Commons policies in ShareActivity
- Various string fixes
- Switched to using vector icons for map markers
- Added filter for irrelevant categories
- Fixed various crashes
- Incremented target SDK to 25
- Improved appearance of navigation drawer
- Replaced proprietary app image in tutorial with one that isn't Telegram
- Fixed camera issue with FileProvider
- Added RxJava library, migrated to Java 8
- Various code and continuous integration optimizations
## v2.4.2 beta
- Added option to launch tutorial again from nav drawer
- Added marker for current location in Nearby map

View file

@ -24,6 +24,8 @@ their contribution to the product.
* Dmitry Brant
* Adam Shorland
* John Lubbock
* Mikel Pascual
* Jan Piotrowski
3rd party open source libraries used:
* Butterknife

View file

@ -1,4 +1,5 @@
apply plugin: 'com.android.application'
apply plugin: 'me.tatarka.retrolambda'
apply plugin: 'jacoco-android'
apply from: 'quality.gradle'
apply plugin: 'com.getkeepsafe.dexcount'
@ -51,8 +52,8 @@ android {
defaultConfig {
applicationId 'fr.free.nrw.commons'
versionCode 73
versionName '2.4.2'
versionCode 74
versionName '2.5.0'
minSdkVersion project.minSdkVersion
targetSdkVersion project.targetSdkVersion
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
@ -75,6 +76,11 @@ android {
abortOnError false
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
//FIXME: Temporary fix for https://github.com/commons-app/apps-android-commons/issues/709
configurations.all {
resolutionStrategy.force 'com.android.support:support-annotations:25.2.0'

View file

@ -11,6 +11,7 @@ import android.content.pm.PackageManager;
import android.database.sqlite.SQLiteDatabase;
import android.preference.PreferenceManager;
import android.support.v4.util.LruCache;
import android.util.Log;
import com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.stetho.Stetho;
@ -139,6 +140,10 @@ public class CommonsApplication extends Application {
System.setProperty("in.yuvi.http.fluent.PROGRESS_TRIGGER_THRESHOLD", "3.0");
Fresco.initialize(this);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(
CommonsApplication.getInstance());
// Increase counter by one, starts from 1
prefs.edit().putInt("app_start_counter", prefs.getInt("app_start_counter" ,0) + 1).commit();
//For caching area -> categories
cacheData = new CacheController();
@ -197,8 +202,8 @@ public class CommonsApplication extends Application {
AccountManager accountManager = AccountManager.get(this);
Account[] allAccounts = accountManager.getAccountsByType(AccountUtil.accountType());
for (int index = 0; index < allAccounts.length; index++) {
accountManager.removeAccount(allAccounts[index], null, null);
for (Account allAccount : allAccounts) {
accountManager.removeAccount(allAccount, null, null);
}
//TODO: fix preference manager

View file

@ -142,7 +142,7 @@ public class Media implements Parcelable {
return coordinates;
}
public void setCoordinates(LatLng coordinates) {
public void setCoordinates(@Nullable LatLng coordinates) {
this.coordinates = coordinates;
}
@ -201,7 +201,7 @@ public class Media implements Parcelable {
this.filename = filename;
}
public Media(Uri localUri, String imageUrl, String filename, String description, long dataLength, Date dateCreated, Date dateUploaded, String creator) {
public Media(Uri localUri, String imageUrl, String filename, String description, long dataLength, Date dateCreated, @Nullable Date dateUploaded, String creator) {
this();
this.localUri = localUri;
this.imageUrl = imageUrl;

View file

@ -27,12 +27,7 @@ public class WelcomeActivity extends BaseActivity {
pager.setAdapter(adapter);
indicator.setViewPager(pager);
adapter.setCallback(new WelcomePagerAdapter.Callback() {
@Override
public void onYesClicked() {
finish();
}
});
adapter.setCallback(this::finish);
}
@Override

View file

@ -26,6 +26,9 @@ import fr.free.nrw.commons.PageTitle;
import fr.free.nrw.commons.contributions.ContributionsActivity;
import timber.log.Timber;
import static android.view.KeyEvent.KEYCODE_ENTER;
import static android.view.inputmethod.EditorInfo.IME_ACTION_DONE;
public class LoginActivity extends AccountAuthenticatorActivity {
@ -63,18 +66,8 @@ public class LoginActivity extends AccountAuthenticatorActivity {
twoFactorEdit.addTextChangedListener(textWatcher);
passwordEdit.setOnEditorActionListener(newLoginInputActionListener());
loginButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
performLogin();
}
});
signupButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
signUp(v);
}
});
loginButton.setOnClickListener(this::performLogin);
signupButton.setOnClickListener(this::signUp);
}
private class LoginTextWatcher implements TextWatcher {
@ -98,20 +91,17 @@ public class LoginActivity extends AccountAuthenticatorActivity {
}
private TextView.OnEditorActionListener newLoginInputActionListener() {
return new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView textView, int actionId, KeyEvent keyEvent) {
return (textView, actionId, keyEvent) -> {
if (loginButton.isEnabled()) {
if (actionId == EditorInfo.IME_ACTION_DONE) {
performLogin();
if (actionId == IME_ACTION_DONE) {
performLogin(textView);
return true;
} else if ((keyEvent != null) && keyEvent.getKeyCode() == KeyEvent.KEYCODE_ENTER) {
performLogin();
} else if ((keyEvent != null) && keyEvent.getKeyCode() == KEYCODE_ENTER) {
performLogin(textView);
return true;
}
}
return false;
}
};
}
@ -142,7 +132,7 @@ public class LoginActivity extends AccountAuthenticatorActivity {
super.onDestroy();
}
private void performLogin() {
private void performLogin(View view) {
Timber.d("Login to start!");
LoginTask task = getLoginTask();
task.execute();

View file

@ -1,67 +0,0 @@
package fr.free.nrw.commons.category;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.CheckedTextView;
import java.util.ArrayList;
import fr.free.nrw.commons.R;
public class CategoriesAdapter extends BaseAdapter {
private LayoutInflater mInflater;
private ArrayList<CategorizationFragment.CategoryItem> items;
public CategoriesAdapter(Context context, ArrayList<CategorizationFragment.CategoryItem> items) {
this.items = items;
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
@Override
public int getCount() {
return items.size();
}
@Override
public Object getItem(int i) {
return items.get(i);
}
public ArrayList<CategorizationFragment.CategoryItem> getItems() {
return items;
}
public void setItems(ArrayList<CategorizationFragment.CategoryItem> items) {
this.items = items;
}
@Override
public long getItemId(int i) {
return i;
}
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
CheckedTextView checkedView;
if(view == null) {
checkedView = (CheckedTextView) mInflater.inflate(R.layout.layout_categories_item, null);
} else {
checkedView = (CheckedTextView) view;
}
CategorizationFragment.CategoryItem item = (CategorizationFragment.CategoryItem) this.getItem(i);
checkedView.setChecked(item.selected);
checkedView.setText(item.name);
checkedView.setTag(i);
return checkedView;
}
}

View file

@ -0,0 +1,24 @@
package fr.free.nrw.commons.category;
import com.pedrogomez.renderers.ListAdapteeCollection;
import com.pedrogomez.renderers.RVRendererAdapter;
import com.pedrogomez.renderers.RendererBuilder;
import java.util.Collections;
import java.util.List;
class CategoriesAdapterFactory {
private final CategoriesRenderer.CategoryClickedListener listener;
CategoriesAdapterFactory(CategoriesRenderer.CategoryClickedListener listener) {
this.listener = listener;
}
public RVRendererAdapter<CategoryItem> create(List<CategoryItem> placeList) {
RendererBuilder<CategoryItem> builder = new RendererBuilder<CategoryItem>()
.bind(CategoryItem.class, new CategoriesRenderer(listener));
ListAdapteeCollection<CategoryItem> collection = new ListAdapteeCollection<>(
placeList != null ? placeList : Collections.<CategoryItem>emptyList());
return new RVRendererAdapter<>(builder, collection);
}
}

View file

@ -0,0 +1,57 @@
package fr.free.nrw.commons.category;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckedTextView;
import com.pedrogomez.renderers.Renderer;
import butterknife.BindView;
import butterknife.ButterKnife;
import fr.free.nrw.commons.R;
class CategoriesRenderer extends Renderer<CategoryItem> {
@BindView(R.id.tvName) CheckedTextView checkedView;
private final CategoryClickedListener listener;
CategoriesRenderer(CategoryClickedListener listener) {
this.listener = listener;
}
@Override
protected View inflate(LayoutInflater layoutInflater, ViewGroup viewGroup) {
return layoutInflater.inflate(R.layout.layout_categories_item, viewGroup, false);
}
@Override
protected void setUpView(View view) {
ButterKnife.bind(this, view);
}
@Override
protected void hookListeners(View view) {
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
CategoryItem item = getContent();
item.setSelected(!item.isSelected());
checkedView.setChecked(item.isSelected());
if (listener != null) {
listener.categoryClicked(item);
}
}
});
}
@Override
public void render() {
CategoryItem item = getContent();
checkedView.setChecked(item.isSelected());
checkedView.setText(item.getName());
}
interface CategoryClickedListener {
void categoryClicked(CategoryItem item);
}
}

View file

@ -1,36 +1,33 @@
package fr.free.nrw.commons.category;
import android.content.ContentProviderClient;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
import android.preference.PreferenceManager;
import android.support.v4.app.Fragment;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.CheckedTextView;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.pedrogomez.renderers.ListAdapteeCollection;
import com.pedrogomez.renderers.RVRendererAdapter;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
@ -40,90 +37,191 @@ import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import butterknife.BindView;
import butterknife.ButterKnife;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.category.CategoriesRenderer.CategoryClickedListener;
import fr.free.nrw.commons.upload.MwVolleyApi;
import timber.log.Timber;
import static android.view.KeyEvent.ACTION_UP;
import static android.view.KeyEvent.KEYCODE_BACK;
import static fr.free.nrw.commons.category.CategoryContentProvider.AUTHORITY;
/**
* Displays the category suggestion and selection screen. Category search is initiated here.
*/
public class CategorizationFragment extends Fragment {
public interface OnCategoriesSaveHandler {
void onCategoriesSave(ArrayList<String> categories);
}
public class CategorizationFragment extends Fragment implements CategoryClickedListener {
public static final int SEARCH_CATS_LIMIT = 25;
ListView categoriesList;
protected EditText categoriesFilter;
@BindView(R.id.categoriesListBox)
RecyclerView categoriesList;
@BindView(R.id.categoriesSearchBox)
EditText categoriesFilter;
@BindView(R.id.categoriesSearchInProgress)
ProgressBar categoriesSearchInProgress;
@BindView(R.id.categoriesNotFound)
TextView categoriesNotFoundView;
@BindView(R.id.categoriesExplanation)
TextView categoriesSkip;
private CategoryTextWatcher textWatcher = new CategoryTextWatcher();
CategoriesAdapter categoriesAdapter;
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(2);
private RVRendererAdapter<CategoryItem> categoriesAdapter;
private OnCategoriesSaveHandler onCategoriesSaveHandler;
protected HashMap<String, ArrayList<String>> categoriesCache;
private HashMap<String, ArrayList<String>> categoriesCache;
private ArrayList<String> selectedCategories = new ArrayList<>();
private ContentProviderClient client;
private PrefixUpdater prefixUpdaterSub;
private MethodAUpdater methodAUpdaterSub;
private final CategoryTextWatcher textWatcher = new CategoryTextWatcher();
private final CategoriesAdapterFactory adapterFactory = new CategoriesAdapterFactory(this);
private final ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(2);
private final ArrayList<String> titleCatItems = new ArrayList<>();
private final CountDownLatch mergeLatch = new CountDownLatch(1);
// LHS guarantees ordered insertions, allowing for prioritized method A results
private final Set<String> results = new LinkedHashSet<>();
PrefixUpdater prefixUpdaterSub;
MethodAUpdater methodAUpdaterSub;
private final ArrayList<String> titleCatItems = new ArrayList<>();
final CountDownLatch mergeLatch = new CountDownLatch(1);
private ContentProviderClient client;
protected final static int SEARCH_CATS_LIMIT = 25;
public static class CategoryItem implements Parcelable {
public String name;
public boolean selected;
public static Creator<CategoryItem> CREATOR = new Creator<CategoryItem>() {
@SuppressWarnings("unchecked")
@Override
public CategoryItem createFromParcel(Parcel parcel) {
return new CategoryItem(parcel);
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_categorization, container, false);
ButterKnife.bind(this, rootView);
categoriesList.setLayoutManager(new LinearLayoutManager(getContext()));
categoriesSkip.setOnClickListener(view -> {
getActivity().onBackPressed();
getActivity().finish();
});
ArrayList<CategoryItem> items;
if (savedInstanceState == null) {
items = new ArrayList<>();
categoriesCache = new HashMap<>();
} else {
items = savedInstanceState.getParcelableArrayList("currentCategories");
categoriesCache = (HashMap<String, ArrayList<String>>) savedInstanceState
.getSerializable("categoriesCache");
}
categoriesAdapter = adapterFactory.create(items);
categoriesList.setAdapter(categoriesAdapter);
categoriesFilter.addTextChangedListener(textWatcher);
startUpdatingCategoryList();
return rootView;
}
@Override
public CategoryItem[] newArray(int i) {
return new CategoryItem[0];
}
};
public CategoryItem(String name, boolean selected) {
this.name = name;
this.selected = selected;
}
public CategoryItem(Parcel in) {
name = in.readString();
selected = in.readInt() == 1;
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
menu.clear();
inflater.inflate(R.menu.fragment_categorization, menu);
}
@Override
public int describeContents() {
return 0;
public void onResume() {
super.onResume();
View rootView = getView();
if (rootView != null) {
rootView.setFocusableInTouchMode(true);
rootView.requestFocus();
rootView.setOnKeyListener((v, keyCode, event) -> {
if (event.getAction() == ACTION_UP && keyCode == KEYCODE_BACK) {
backButtonDialog();
return true;
}
return false;
});
}
}
@Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeString(name);
parcel.writeInt(selected ? 1 : 0);
public void onDestroyView() {
categoriesFilter.removeTextChangedListener(textWatcher);
super.onDestroyView();
}
@Override
public void onDestroy() {
super.onDestroy();
client.release();
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
int itemCount = categoriesAdapter.getItemCount();
ArrayList<CategoryItem> items = new ArrayList<>(itemCount);
for (int i = 0; i < itemCount; i++) {
items.add(categoriesAdapter.getItem(i));
}
outState.putParcelableArrayList("currentCategories", items);
outState.putSerializable("categoriesCache", categoriesCache);
}
@Override
public boolean onOptionsItemSelected(MenuItem menuItem) {
switch (menuItem.getItemId()) {
case R.id.menu_save_categories:
int numberSelected = 0;
selectedCategories = new ArrayList<>();
int count = categoriesAdapter.getItemCount();
for (int i = 0; i < count; i++) {
CategoryItem item = categoriesAdapter.getItem(i);
if (item.isSelected()) {
selectedCategories.add(item.getName());
numberSelected++;
}
}
//If no categories selected, display warning to user
if (numberSelected == 0) {
new AlertDialog.Builder(getActivity())
.setMessage("Images without categories are rarely usable. "
+ "Are you sure you want to submit without selecting "
+ "categories?")
.setTitle("No Categories Selected")
.setPositiveButton("No, go back", (dialog, id) -> {
//Exit menuItem so user can select their categories
})
.setNegativeButton("Yes, submit", (dialog, id) -> {
//Proceed to submission
onCategoriesSaveHandler.onCategoriesSave(selectedCategories);
})
.create()
.show();
} else {
//Proceed to submission
onCategoriesSaveHandler.onCategoriesSave(selectedCategories);
return true;
}
}
return super.onOptionsItemSelected(menuItem);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setHasOptionsMenu(true);
onCategoriesSaveHandler = (OnCategoriesSaveHandler) getActivity();
getActivity().setTitle(R.string.categories_activity_title);
client = getActivity().getContentResolver().acquireContentProviderClient(AUTHORITY);
}
public HashMap<String, ArrayList<String>> getCategoriesCache() {
return categoriesCache;
}
/**
* Retrieves category suggestions from title input
*
* @return a list containing title-related categories
*/
protected ArrayList<String> titleCatQuery() {
private ArrayList<String> titleCatQuery() {
TitleCategories titleCategoriesSub;
//Retrieve the title that was saved when user tapped submit icon
@ -157,37 +255,42 @@ public class CategorizationFragment extends Fragment {
/**
* Retrieves recently-used categories
*
* @return a list containing recent categories
*/
protected ArrayList<String> recentCatQuery() {
private ArrayList<String> recentCatQuery() {
ArrayList<String> items = new ArrayList<>();
Cursor cursor = null;
try {
Cursor cursor = client.query(
cursor = client.query(
CategoryContentProvider.BASE_URI,
Category.Table.ALL_FIELDS,
null,
new String[]{},
Category.Table.COLUMN_LAST_USED + " DESC");
// fixme add a limit on the original query instead of falling out of the loop?
while (cursor.moveToNext() && cursor.getPosition() < SEARCH_CATS_LIMIT) {
while (cursor != null && cursor.moveToNext()
&& cursor.getPosition() < SEARCH_CATS_LIMIT) {
Category cat = Category.fromCursor(cursor);
items.add(cat.getName());
}
} catch (RemoteException e) {
throw new RuntimeException(e);
} finally {
if (cursor != null) {
cursor.close();
}
catch (RemoteException e) {
throw new RuntimeException(e);
}
return items;
}
/**
* Merges nearby categories, categories suggested based on title, and recent categories... without duplicates.
* Merges nearby categories, categories suggested based on title, and recent categories...
* without duplicates.
*
* @return a list containing merged categories
*/
protected ArrayList<String> mergeItems() {
ArrayList<String> mergeItems() {
Set<String> mergedItems = new LinkedHashSet<>();
Timber.d("Calling APIs for GPS cats, title cats and recent cats...");
@ -214,7 +317,8 @@ public class CategorizationFragment extends Fragment {
mergedItems.addAll(recentItems);
Timber.d("Adding recent items: %s", recentItems);
//Needs to be an ArrayList and not a List unless we want to modify a big portion of preexisting code
// Needs to be an ArrayList and not a List unless we want to modify a big portion
// of preexisting code
ArrayList<String> mergedItemsList = new ArrayList<>(mergedItems);
Timber.d("Merged item list: %s", mergedItemsList);
@ -223,18 +327,20 @@ public class CategorizationFragment extends Fragment {
/**
* Displays categories found to the user as they type in the search box
*
* @param categories a list of all categories found for the search string
* @param filter the search string
*/
protected void setCatsAfterAsync(ArrayList<String> categories, String filter) {
private void setCatsAfterAsync(ArrayList<String> categories, String filter) {
if (getActivity() != null) {
ArrayList<CategoryItem> items = new ArrayList<>();
HashSet<String> existingKeys = new HashSet<>();
for (CategoryItem item : categoriesAdapter.getItems()) {
if (item.selected) {
int count = categoriesAdapter.getItemCount();
for (int i = 0; i < count; i++) {
CategoryItem item = categoriesAdapter.getItem(i);
if (item.isSelected()) {
items.add(item);
existingKeys.add(item.name);
existingKeys.add(item.getName());
}
}
for (String category : categories) {
@ -243,8 +349,8 @@ public class CategorizationFragment extends Fragment {
}
}
categoriesAdapter.setItems(items);
categoriesAdapter.notifyDataSetInvalidated();
categoriesAdapter.setCollection(new ListAdapteeCollection<>(items));
categoriesAdapter.notifyDataSetChanged();
categoriesSearchInProgress.setVisibility(View.GONE);
if (categories.isEmpty()) {
@ -258,8 +364,7 @@ public class CategorizationFragment extends Fragment {
} else {
categoriesList.smoothScrollToPosition(existingKeys.size());
}
}
else {
} else {
Timber.e("Error: Fragment is null");
}
}
@ -272,7 +377,6 @@ public class CategorizationFragment extends Fragment {
* above Prefix results.
*/
private void requestSearchResults() {
final CountDownLatch latch = new CountDownLatch(1);
prefixUpdaterSub = new PrefixUpdater(this) {
@ -282,8 +386,7 @@ public class CategorizationFragment extends Fragment {
try {
result = super.doInBackground();
latch.await();
}
catch (InterruptedException e) {
} catch (InterruptedException e) {
Timber.w(e);
//Thread.currentThread().interrupt();
}
@ -325,7 +428,6 @@ public class CategorizationFragment extends Fragment {
}
private void startUpdatingCategoryList() {
if (prefixUpdaterSub != null) {
prefixUpdaterSub.cancel(true);
}
@ -339,238 +441,34 @@ public class CategorizationFragment extends Fragment {
public int getCurrentSelectedCount() {
int count = 0;
for(CategoryItem item: categoriesAdapter.getItems()) {
if(item.selected) {
int numberOfItems = categoriesAdapter.getItemCount();
for (int i = 0; i < numberOfItems; i++) {
CategoryItem item = categoriesAdapter.getItem(i);
if (item.isSelected()) {
count++;
}
}
return count;
}
private Category lookupCategory(String name) {
Cursor cursor = null;
try {
cursor = client.query(
CategoryContentProvider.BASE_URI,
Category.Table.ALL_FIELDS,
Category.Table.COLUMN_NAME + "=?",
new String[] {name},
null);
if (cursor.moveToFirst()) {
return Category.fromCursor(cursor);
}
} catch (RemoteException e) {
// This feels lazy, but to hell with checked exceptions. :)
throw new RuntimeException(e);
} finally {
if ( cursor != null ) {
cursor.close();
}
}
// Newly used category...
Category cat = new Category();
cat.setName(name);
cat.setLastUsed(new Date());
cat.setTimesUsed(0);
return cat;
}
private class CategoryCountUpdater extends AsyncTask<Void, Void, Void> {
private String name;
public CategoryCountUpdater(String name) {
this.name = name;
}
@Override
protected Void doInBackground(Void... voids) {
Category cat = lookupCategory(name);
cat.incTimesUsed();
cat.setContentProviderClient(client);
cat.save();
return null; // Make the compiler happy.
}
}
private void updateCategoryCount(String name) {
new CategoryCountUpdater(name).executeOnExecutor(executor);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_categorization, null);
categoriesList = (ListView) rootView.findViewById(R.id.categoriesListBox);
categoriesFilter = (EditText) rootView.findViewById(R.id.categoriesSearchBox);
categoriesSearchInProgress = (ProgressBar) rootView.findViewById(R.id.categoriesSearchInProgress);
categoriesNotFoundView = (TextView) rootView.findViewById(R.id.categoriesNotFound);
categoriesSkip = (TextView) rootView.findViewById(R.id.categoriesExplanation);
categoriesSkip.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
getActivity().onBackPressed();
getActivity().finish();
}
});
ArrayList<CategoryItem> items;
if(savedInstanceState == null) {
items = new ArrayList<>();
categoriesCache = new HashMap<>();
} else {
items = savedInstanceState.getParcelableArrayList("currentCategories");
categoriesCache = (HashMap<String, ArrayList<String>>) savedInstanceState.getSerializable("categoriesCache");
}
categoriesAdapter = new CategoriesAdapter(getActivity(), items);
categoriesList.setAdapter(categoriesAdapter);
categoriesList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int index, long id) {
CheckedTextView checkedView = (CheckedTextView) view;
CategoryItem item = (CategoryItem) adapterView.getAdapter().getItem(index);
item.selected = !item.selected;
checkedView.setChecked(item.selected);
if (item.selected) {
updateCategoryCount(item.name);
}
}
});
categoriesFilter.addTextChangedListener(textWatcher);
startUpdatingCategoryList();
return rootView;
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
menu.clear();
inflater.inflate(R.menu.fragment_categorization, menu);
}
@Override
public void onResume() {
super.onResume();
View rootView = getView();
if (rootView != null) {
rootView.setFocusableInTouchMode(true);
rootView.requestFocus();
rootView.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) {
backButtonDialog();
return true;
}
return false;
}
});
}
}
@Override
public void onDestroyView() {
categoriesFilter.removeTextChangedListener(textWatcher);
super.onDestroyView();
}
public void backButtonDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setMessage("Are you sure you want to go back? The image will not have any categories saved.")
.setTitle("Warning");
builder.setPositiveButton("No", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
new AlertDialog.Builder(getActivity())
.setMessage("Are you sure you want to go back? The image will not "
+ "have any categories saved.")
.setTitle("Warning")
.setPositiveButton("No", (dialog, id) -> {
//No need to do anything, user remains on categorization screen
}
});
builder.setNegativeButton("Yes", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
getActivity().finish();
}
});
AlertDialog dialog = builder.create();
dialog.show();
})
.setNegativeButton("Yes", (dialog, id) -> getActivity().finish())
.create()
.show();
}
@Override
public void onDestroy() {
super.onDestroy();
client.release();
public void categoryClicked(CategoryItem item) {
if (item.isSelected()) {
new CategoryCountUpdater(item.getName(), client).executeOnExecutor(executor);
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelableArrayList("currentCategories", categoriesAdapter.getItems());
outState.putSerializable("categoriesCache", categoriesCache);
}
@Override
public boolean onOptionsItemSelected(MenuItem menuItem) {
switch(menuItem.getItemId()) {
case R.id.menu_save_categories:
int numberSelected = 0;
for(CategoryItem item: categoriesAdapter.getItems()) {
if(item.selected) {
selectedCategories.add(item.name);
numberSelected++;
}
}
//If no categories selected, display warning to user
if (numberSelected == 0) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setMessage("Images without categories are rarely usable. Are you sure you want to submit without selecting categories?")
.setTitle("No Categories Selected");
builder.setPositiveButton("No, go back", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
//Exit menuItem so user can select their categories
return;
}
});
builder.setNegativeButton("Yes, submit", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
//Proceed to submission
onCategoriesSaveHandler.onCategoriesSave(selectedCategories);
return;
}
});
AlertDialog dialog = builder.create();
dialog.show();
} else {
//Proceed to submission
onCategoriesSaveHandler.onCategoriesSave(selectedCategories);
return true;
}
}
return super.onOptionsItemSelected(menuItem);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setHasOptionsMenu(true);
onCategoriesSaveHandler = (OnCategoriesSaveHandler) getActivity();
getActivity().setTitle(R.string.categories_activity_title);
client = getActivity().getContentResolver().acquireContentProviderClient(CategoryContentProvider.AUTHORITY);
}
private class CategoryTextWatcher implements TextWatcher {

View file

@ -26,7 +26,7 @@ public class Category {
this.name = name;
}
public Date getLastUsed() {
private Date getLastUsed() {
// warning: Date objects are mutable.
return (Date)lastUsed.clone();
}
@ -36,11 +36,11 @@ public class Category {
this.lastUsed = (Date)lastUsed.clone();
}
public void touch() {
private void touch() {
lastUsed = new Date();
}
public int getTimesUsed() {
private int getTimesUsed() {
return timesUsed;
}
@ -70,7 +70,7 @@ public class Category {
}
}
public ContentValues toContentValues() {
private ContentValues toContentValues() {
ContentValues cv = new ContentValues();
cv.put(Table.COLUMN_NAME, getName());
cv.put(Table.COLUMN_LAST_USED, getLastUsed().getTime());

View file

@ -7,6 +7,7 @@ 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 fr.free.nrw.commons.CommonsApplication;
@ -41,8 +42,10 @@ public class CategoryContentProvider extends ContentProvider {
return false;
}
@SuppressWarnings("ConstantConditions")
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
public Cursor query(@NonNull Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
queryBuilder.setTables(Category.Table.TABLE_NAME);
@ -53,7 +56,8 @@ public class CategoryContentProvider extends ContentProvider {
switch(uriType) {
case CATEGORIES:
cursor = queryBuilder.query(db, projection, selection, selectionArgs, null, null, sortOrder);
cursor = queryBuilder.query(db, projection, selection, selectionArgs,
null, null, sortOrder);
break;
case CATEGORIES_ID:
cursor = queryBuilder.query(db,
@ -75,15 +79,16 @@ public class CategoryContentProvider extends ContentProvider {
}
@Override
public String getType(Uri uri) {
public String getType(@NonNull Uri uri) {
return null;
}
@SuppressWarnings("ConstantConditions")
@Override
public Uri insert(Uri uri, ContentValues contentValues) {
public Uri insert(@NonNull Uri uri, ContentValues contentValues) {
int uriType = uriMatcher.match(uri);
SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase();
long id = 0;
long id;
switch (uriType) {
case CATEGORIES:
id = sqlDB.insert(Category.Table.TABLE_NAME, null, contentValues);
@ -96,12 +101,13 @@ public class CategoryContentProvider extends ContentProvider {
}
@Override
public int delete(Uri uri, String s, String[] strings) {
public int delete(@NonNull Uri uri, String s, String[] strings) {
return 0;
}
@SuppressWarnings("ConstantConditions")
@Override
public int bulkInsert(Uri uri, ContentValues[] values) {
public int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] values) {
Timber.d("Hello, bulk insert! (CategoryContentProvider)");
int uriType = uriMatcher.match(uri);
SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase();
@ -122,8 +128,10 @@ public class CategoryContentProvider extends ContentProvider {
return values.length;
}
@SuppressWarnings("ConstantConditions")
@Override
public int update(Uri uri, ContentValues contentValues, String selection, String[] selectionArgs) {
public int update(@NonNull Uri uri, ContentValues contentValues, String selection,
String[] selectionArgs) {
/*
SQL Injection warnings: First, note that we're not exposing this to the outside world (exported="false")
Even then, we should make sure to sanitize all user input appropriately. Input that passes through ContentValues
@ -133,7 +141,7 @@ public class CategoryContentProvider extends ContentProvider {
*/
int uriType = uriMatcher.match(uri);
SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase();
int rowsUpdated = 0;
int rowsUpdated;
switch (uriType) {
case CATEGORIES_ID:
int id = Integer.valueOf(uri.getLastPathSegment());
@ -144,7 +152,8 @@ public class CategoryContentProvider extends ContentProvider {
Category.Table.COLUMN_ID + " = ?",
new String[] { String.valueOf(id) } );
} else {
throw new IllegalArgumentException("Parameter `selection` should be empty when updating an ID");
throw new IllegalArgumentException(
"Parameter `selection` should be empty when updating an ID");
}
break;
default:

View file

@ -0,0 +1,59 @@
package fr.free.nrw.commons.category;
import android.content.ContentProviderClient;
import android.database.Cursor;
import android.os.AsyncTask;
import android.os.RemoteException;
import java.util.Date;
class CategoryCountUpdater extends AsyncTask<Void, Void, Void> {
private final String name;
private final ContentProviderClient client;
CategoryCountUpdater(String name, ContentProviderClient client) {
this.name = name;
this.client = client;
}
@Override
protected Void doInBackground(Void... voids) {
Category cat = lookupCategory(name);
cat.incTimesUsed();
cat.setContentProviderClient(client);
cat.save();
return null; // Make the compiler happy.
}
private Category lookupCategory(String name) {
Cursor cursor = null;
try {
cursor = client.query(
CategoryContentProvider.BASE_URI,
Category.Table.ALL_FIELDS,
Category.Table.COLUMN_NAME + "=?",
new String[]{name},
null);
if (cursor != null && cursor.moveToFirst()) {
return Category.fromCursor(cursor);
}
} catch (RemoteException e) {
// This feels lazy, but to hell with checked exceptions. :)
throw new RuntimeException(e);
} finally {
if (cursor != null) {
cursor.close();
}
}
// Newly used category...
Category cat = new Category();
cat.setName(name);
cat.setLastUsed(new Date());
cat.setTimesUsed(0);
return cat;
}
}

View file

@ -0,0 +1,54 @@
package fr.free.nrw.commons.category;
import android.os.Parcel;
import android.os.Parcelable;
class CategoryItem implements Parcelable {
private final String name;
private boolean selected;
public static Creator<CategoryItem> CREATOR = new Creator<CategoryItem>() {
@Override
public CategoryItem createFromParcel(Parcel parcel) {
return new CategoryItem(parcel);
}
@Override
public CategoryItem[] newArray(int i) {
return new CategoryItem[0];
}
};
CategoryItem(String name, boolean selected) {
this.name = name;
this.selected = selected;
}
private CategoryItem(Parcel in) {
name = in.readString();
selected = in.readInt() == 1;
}
public String getName() {
return name;
}
public boolean isSelected() {
return selected;
}
public void setSelected(boolean selected) {
this.selected = selected;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeString(name);
parcel.writeInt(selected ? 1 : 0);
}
}

View file

@ -13,6 +13,8 @@ import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.mwapi.MediaWikiApi;
import timber.log.Timber;
import static fr.free.nrw.commons.category.CategorizationFragment.SEARCH_CATS_LIMIT;
/**
* Sends asynchronous queries to the Commons MediaWiki API to retrieve categories that are close to
* the keyword typed in by the user. The 'srsearch' action-specific parameter is used for this
@ -20,8 +22,8 @@ import timber.log.Timber;
*/
class MethodAUpdater extends AsyncTask<Void, Void, List<String>> {
private final CategorizationFragment catFragment;
private String filter;
private CategorizationFragment catFragment;
MethodAUpdater(CategorizationFragment catFragment) {
this.catFragment = catFragment;
@ -84,7 +86,7 @@ class MethodAUpdater extends AsyncTask<Void, Void, List<String>> {
//URL https://commons.wikimedia.org/w/api.php?action=query&format=xml&list=search&srwhat=text&srenablerewrites=1&srnamespace=14&srlimit=10&srsearch=
try {
categories = api.searchCategories(CategorizationFragment.SEARCH_CATS_LIMIT, filter);
categories = api.searchCategories(SEARCH_CATS_LIMIT, filter);
Timber.d("Method A URL filter %s", categories);
} catch (IOException e) {
Timber.e(e, "IO Exception: ");

View file

@ -0,0 +1,7 @@
package fr.free.nrw.commons.category;
import java.util.ArrayList;
public interface OnCategoriesSaveHandler {
void onCategoriesSave(ArrayList<String> categories);
}

View file

@ -7,6 +7,7 @@ import android.view.View;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@ -14,18 +15,20 @@ import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.mwapi.MediaWikiApi;
import timber.log.Timber;
import static fr.free.nrw.commons.category.CategorizationFragment.SEARCH_CATS_LIMIT;
/**
* Sends asynchronous queries to the Commons MediaWiki API to retrieve categories that share the
* same prefix as the keyword typed in by the user. The 'acprefix' action-specific parameter is used
* for this purpose. This class should be subclassed in CategorizationFragment.java to aggregate
* the results.
*/
public class PrefixUpdater extends AsyncTask<Void, Void, List<String>> {
class PrefixUpdater extends AsyncTask<Void, Void, List<String>> {
private final CategorizationFragment catFragment;
private String filter;
private CategorizationFragment catFragment;
public PrefixUpdater(CategorizationFragment catFragment) {
PrefixUpdater(CategorizationFragment catFragment) {
this.catFragment = catFragment;
}
@ -90,8 +93,9 @@ public class PrefixUpdater extends AsyncTask<Void, Void, List<String>> {
}
//if user types in something that is in cache, return cached category
if (catFragment.categoriesCache.containsKey(filter)) {
ArrayList<String> cachedItems = new ArrayList<>(catFragment.categoriesCache.get(filter));
HashMap<String, ArrayList<String>> categoriesCache = catFragment.getCategoriesCache();
if (categoriesCache.containsKey(filter)) {
ArrayList<String> cachedItems = new ArrayList<>(categoriesCache.get(filter));
Timber.d("Found cache items, waiting for filter");
return new ArrayList<>(filterIrrelevantResults(cachedItems));
}
@ -101,7 +105,7 @@ public class PrefixUpdater extends AsyncTask<Void, Void, List<String>> {
MediaWikiApi api = CommonsApplication.getInstance().getMWApi();
List<String> categories = new ArrayList<>();
try {
categories = api.allCategories(CategorizationFragment.SEARCH_CATS_LIMIT, this.filter);
categories = api.allCategories(SEARCH_CATS_LIMIT, this.filter);
Timber.d("Prefix URL filter %s", categories);
} catch (IOException e) {
Timber.e(e, "IO Exception: ");

View file

@ -19,17 +19,12 @@ class TitleCategories extends AsyncTask<Void, Void, List<String>> {
private final static int SEARCH_CATS_LIMIT = 25;
private String title;
private final String title;
TitleCategories(String title) {
this.title = title;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected List<String> doInBackground(Void... voids) {

View file

@ -9,11 +9,8 @@ public class BackgroundPoolExceptionHandler implements ExceptionHandler {
public void onException(@NonNull final Throwable t) {
//Crash for debug build
if (BuildConfig.DEBUG) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
Thread thread = new Thread(() -> {
throw new RuntimeException(t);
}
});
thread.start();
}

View file

@ -11,17 +11,13 @@ class ThreadFactoryMaker {
private int count = 0;
@Override
public Thread newThread(final Runnable runnable) {
public Thread newThread(@NonNull final Runnable runnable) {
count++;
Runnable wrapperRunnable = new Runnable() {
@Override
public void run() {
Runnable wrapperRunnable = () -> {
Process.setThreadPriority(priority);
runnable.run();
}
};
Thread t = new Thread(wrapperRunnable, String.format("%s-%s", name, count));
return t;
return new Thread(wrapperRunnable, String.format("%s-%s", name, count));
}
};
}

View file

@ -39,7 +39,7 @@ public class ThreadPoolExecutorService implements Executor {
}
@Override
public void execute(Runnable command) {
public void execute(@NonNull Runnable command) {
backgroundPool.execute(command);
}

View file

@ -3,11 +3,13 @@ package fr.free.nrw.commons.contributions;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.DataSetObserver;
import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
import android.preference.PreferenceManager;
@ -17,6 +19,7 @@ import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v4.widget.CursorAdapter;
import android.support.v7.app.AlertDialog;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
@ -27,7 +30,10 @@ import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import butterknife.ButterKnife;
import fr.free.nrw.commons.CommonsApplication;
@ -110,6 +116,12 @@ public class ContributionsActivity
super.onPause();
}
@Override
protected void onStart() {
super.onStart();
displayFeedbackPopup();
}
@Override
protected void onAuthCookieAcquired(String authCookie) {
// Do a sync everytime we get here!
@ -342,4 +354,56 @@ public class ContributionsActivity
Intent contributionsIntent = new Intent(context, ContributionsActivity.class);
context.startActivity(contributionsIntent);
}
private void displayFeedbackPopup() {
Date popupMessageEndDate = null;
try {
String validUntil = "23/08/2017";
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
popupMessageEndDate = sdf.parse(validUntil);
} catch (ParseException e) {
e.printStackTrace();
}
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(
CommonsApplication.getInstance());
// boolean to save users request about displaying popup
boolean displayFeedbackPopup = prefs.getBoolean("display_feedbak_popup", true);
// boolean to recognize is application re-started. Will be used for "remind me later" option
int appStartCounter = prefs.getInt("app_start_counter" ,0);
// if time is valid and shared pref says display
if (new Date().before(popupMessageEndDate) && displayFeedbackPopup && (appStartCounter == 4)) {
new AlertDialog.Builder(this)
.setTitle(getResources().getString(R.string.feedback_popup_title))
.setMessage(getResources().getString(R.string.feedback_popup_description))
.setPositiveButton(getResources().getString(R.string.feedback_popup_accept),
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// Go to the page
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri
.parse(getResources()
.getString(R.string.feedback_page_url)));
startActivity(browserIntent);
// No need to dislay this window to the user again.
prefs.edit().putBoolean("display_feedbak_popup" , false).commit();
dialog.dismiss();
}
})
.setNegativeButton(getResources().getString(R.string.feedback_popup_decline),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// Dismiss the dialog and not to show it later
prefs.edit().putBoolean("display_feedbak_popup", false).commit();
dialog.dismiss();
}
})
.create().show();
}
}
}

View file

@ -7,6 +7,7 @@ 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 fr.free.nrw.commons.CommonsApplication;
@ -38,7 +39,7 @@ public class ContributionsContentProvider extends ContentProvider{
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
public Cursor query(@NonNull Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
queryBuilder.setTables(Contribution.Table.TABLE_NAME);
@ -71,12 +72,12 @@ public class ContributionsContentProvider extends ContentProvider{
}
@Override
public String getType(Uri uri) {
public String getType(@NonNull Uri uri) {
return null;
}
@Override
public Uri insert(Uri uri, ContentValues contentValues) {
public Uri insert(@NonNull Uri uri, ContentValues contentValues) {
int uriType = uriMatcher.match(uri);
SQLiteDatabase sqlDB = CommonsApplication.getInstance().getDBOpenHelper().getWritableDatabase();
long id = 0;
@ -92,7 +93,7 @@ public class ContributionsContentProvider extends ContentProvider{
}
@Override
public int delete(Uri uri, String s, String[] strings) {
public int delete(@NonNull Uri uri, String s, String[] strings) {
int rows = 0;
int uriType = uriMatcher.match(uri);
@ -114,7 +115,7 @@ public class ContributionsContentProvider extends ContentProvider{
}
@Override
public int bulkInsert(Uri uri, ContentValues[] values) {
public int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] values) {
Timber.d("Hello, bulk insert! (ContributionsContentProvider)");
int uriType = uriMatcher.match(uri);
SQLiteDatabase sqlDB = CommonsApplication.getInstance().getDBOpenHelper().getWritableDatabase();
@ -136,7 +137,7 @@ public class ContributionsContentProvider extends ContentProvider{
}
@Override
public int update(Uri uri, ContentValues contentValues, String selection, String[] selectionArgs) {
public int update(@NonNull Uri uri, ContentValues contentValues, String selection, String[] selectionArgs) {
/*
SQL Injection warnings: First, note that we're not exposing this to the outside world (exported="false")
Even then, we should make sure to sanitize all user input appropriately. Input that passes through ContentValues

View file

@ -8,6 +8,7 @@ import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
@ -29,6 +30,7 @@ import fr.free.nrw.commons.R;
import fr.free.nrw.commons.nearby.NearbyActivity;
import timber.log.Timber;
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
import static android.app.Activity.RESULT_OK;
public class ContributionsListFragment extends Fragment {
@ -110,11 +112,11 @@ public class ContributionsListFragment extends Fragment {
// Here, thisActivity is the current activity
if (ContextCompat.checkSelfPermission(getActivity(),
Manifest.permission.READ_EXTERNAL_STORAGE)
READ_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
// Should we show an explanation?
if (shouldShowRequestPermissionRationale(Manifest.permission.READ_EXTERNAL_STORAGE)) {
if (shouldShowRequestPermissionRationale(READ_EXTERNAL_STORAGE)) {
// Show an explanation to the user *asynchronously* -- don't block
// this thread waiting for the user's response! After the user
@ -122,15 +124,9 @@ public class ContributionsListFragment extends Fragment {
new AlertDialog.Builder(getActivity())
.setMessage(getString(R.string.storage_permission_rationale))
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
requestPermissions(
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
1);
.setPositiveButton("OK", (dialog, which) -> {
requestPermissions(new String[]{READ_EXTERNAL_STORAGE}, 1);
dialog.dismiss();
}
})
.setNegativeButton("Cancel", null)
.create()
@ -140,7 +136,7 @@ public class ContributionsListFragment extends Fragment {
// No explanation needed, we can request the permission.
requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
requestPermissions(new String[]{READ_EXTERNAL_STORAGE},
1);
// MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
@ -167,7 +163,7 @@ public class ContributionsListFragment extends Fragment {
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
Timber.d("onRequestPermissionsResult: req code = " + " perm = " + permissions + " grant =" + grantResults);

View file

@ -14,10 +14,10 @@ import fr.free.nrw.commons.concurrency.BackgroundPoolExceptionHandler;
import fr.free.nrw.commons.concurrency.ThreadPoolExecutorService;
import timber.log.Timber;
public class UploadCountClient {
class UploadCountClient {
private ThreadPoolExecutorService threadPoolExecutor;
public UploadCountClient() {
UploadCountClient() {
threadPoolExecutor = new ThreadPoolExecutorService.Builder("bg-pool")
.setPoolSize(Runtime.getRuntime().availableProcessors())
.setExceptionHandler(new BackgroundPoolExceptionHandler())
@ -27,11 +27,9 @@ public class UploadCountClient {
private static final String UPLOAD_COUNT_URL_TEMPLATE =
"https://tools.wmflabs.org/urbanecmbot/uploadsbyuser/uploadsbyuser.py?user=%s";
public ListenableFuture<Integer> getUploadCount(final String userName) {
ListenableFuture<Integer> getUploadCount(final String userName) {
final SettableFuture<Integer> future = SettableFuture.create();
threadPoolExecutor.schedule(new Runnable() {
@Override
public void run() {
threadPoolExecutor.schedule(() -> {
URL url;
try {
url = new URL(String.format(Locale.ENGLISH, UPLOAD_COUNT_URL_TEMPLATE,
@ -50,7 +48,6 @@ public class UploadCountClient {
Timber.e("Error getting upload count Error", e);
future.setException(e);
}
}
});
return future;
}

View file

@ -119,12 +119,7 @@ public class MediaDetailFragment extends Fragment {
licenseList = new LicenseList(getActivity());
// Progressively darken the image in the background when we scroll detail pane up
scrollListener = new ViewTreeObserver.OnScrollChangedListener() {
@Override
public void onScrollChanged() {
updateTheDarkness();
}
};
scrollListener = this::updateTheDarkness;
view.getViewTreeObserver().addOnScrollChangedListener(scrollListener);
// Layout layoutListener to size the spacer item relative to the available space.
@ -280,15 +275,12 @@ public class MediaDetailFragment extends Fragment {
textView.setText(catName);
if (categoriesLoaded && categoriesPresent) {
textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
textView.setOnClickListener(view -> {
String selectedCategoryTitle = "Category:" + catName;
Intent viewIntent = new Intent();
viewIntent.setAction(Intent.ACTION_VIEW);
viewIntent.setData(new PageTitle(selectedCategoryTitle).getCanonicalUri());
startActivity(viewIntent);
}
});
}
return item;

View file

@ -72,12 +72,7 @@ public class MediaDetailPagerFragment extends Fragment implements ViewPager.OnPa
public Fragment getItem(int i) {
if (i == 0) {
// See bug https://code.google.com/p/android/issues/detail?id=27526
pager.postDelayed(new Runnable() {
@Override
public void run() {
getActivity().supportInvalidateOptionsMenu();
}
}, 5);
pager.postDelayed(() -> getActivity().supportInvalidateOptionsMenu(), 5);
}
return MediaDetailFragment.forMedia(i, editable);
}
@ -100,14 +95,11 @@ public class MediaDetailPagerFragment extends Fragment implements ViewPager.OnPa
final int pageNumber = savedInstanceState.getInt("current-page");
// Adapter doesn't seem to be loading immediately.
// Dear God, please forgive us for our sins
view.postDelayed(new Runnable() {
@Override
public void run() {
view.postDelayed(() -> {
pager.setAdapter(adapter);
pager.setCurrentItem(pageNumber, false);
getActivity().supportInvalidateOptionsMenu();
adapter.notifyDataSetChanged();
}
}, 100);
} else {
pager.setAdapter(adapter);
@ -196,13 +188,8 @@ public class MediaDetailPagerFragment extends Fragment implements ViewPager.OnPa
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !(ContextCompat.checkSelfPermission(getContext(), Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED)) {
Snackbar.make(getView(), R.string.storage_permission_rationale,
Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.ok, new View.OnClickListener() {
@Override
public void onClick(View view) {
ActivityCompat.requestPermissions(getActivity(),
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 1);
}
}).show();
.setAction(R.string.ok, view -> ActivityCompat.requestPermissions(getActivity(),
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 1)).show();
} else {
final DownloadManager manager = (DownloadManager)getActivity().getSystemService(Context.DOWNLOAD_SERVICE);
manager.enqueue(req);

View file

@ -7,6 +7,7 @@ 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 fr.free.nrw.commons.CommonsApplication;
@ -39,7 +40,7 @@ public class ModificationsContentProvider extends ContentProvider{
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
public Cursor query(@NonNull Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
queryBuilder.setTables(ModifierSequence.Table.TABLE_NAME);
@ -61,12 +62,12 @@ public class ModificationsContentProvider extends ContentProvider{
}
@Override
public String getType(Uri uri) {
public String getType(@NonNull Uri uri) {
return null;
}
@Override
public Uri insert(Uri uri, ContentValues contentValues) {
public Uri insert(@NonNull Uri uri, ContentValues contentValues) {
int uriType = uriMatcher.match(uri);
SQLiteDatabase sqlDB = CommonsApplication.getInstance().getDBOpenHelper().getWritableDatabase();
long id = 0;
@ -82,7 +83,7 @@ public class ModificationsContentProvider extends ContentProvider{
}
@Override
public int delete(Uri uri, String s, String[] strings) {
public int delete(@NonNull Uri uri, String s, String[] strings) {
int uriType = uriMatcher.match(uri);
SQLiteDatabase sqlDB = CommonsApplication.getInstance().getDBOpenHelper().getWritableDatabase();
switch (uriType) {
@ -99,7 +100,7 @@ public class ModificationsContentProvider extends ContentProvider{
}
@Override
public int bulkInsert(Uri uri, ContentValues[] values) {
public int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] values) {
Timber.d("Hello, bulk insert! (ModificationsContentProvider)");
int uriType = uriMatcher.match(uri);
SQLiteDatabase sqlDB = CommonsApplication.getInstance().getDBOpenHelper().getWritableDatabase();
@ -121,7 +122,7 @@ public class ModificationsContentProvider extends ContentProvider{
}
@Override
public int update(Uri uri, ContentValues contentValues, String selection, String[] selectionArgs) {
public int update(@NonNull Uri uri, ContentValues contentValues, String selection, String[] selectionArgs) {
/*
SQL Injection warnings: First, note that we're not exposing this to the outside world (exported="false")
Even then, we should make sure to sanitize all user input appropriately. Input that passes through ContentValues

View file

@ -351,12 +351,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
@Override
@NonNull
public UploadResult uploadFile(String filename, InputStream file, long dataLength, String pageContents, String editSummary, final ProgressListener progressListener) throws IOException {
ApiResult result = api.upload(filename, file, dataLength, pageContents, editSummary, new in.yuvi.http.fluent.ProgressListener() {
@Override
public void onProgress(long transferred, long total) {
progressListener.onProgress(transferred, total);
}
});
ApiResult result = api.upload(filename, file, dataLength, pageContents, editSummary, progressListener::onProgress);
Log.e("WTF", "Result: "+result.toString());

View file

@ -2,7 +2,6 @@ package fr.free.nrw.commons.nearby;
import android.Manifest;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.location.LocationManager;
@ -41,7 +40,8 @@ import timber.log.Timber;
public class NearbyActivity extends NavigationBaseActivity {
@BindView(R.id.progressBar) ProgressBar progressBar;
@BindView(R.id.progressBar)
ProgressBar progressBar;
private boolean isMapViewActive = false;
private static final int LOCATION_REQUEST = 1;
@ -115,15 +115,11 @@ public class NearbyActivity extends NavigationBaseActivity {
new AlertDialog.Builder(this)
.setMessage(getString(R.string.location_permission_rationale))
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
.setPositiveButton("OK", (dialog, which) -> {
ActivityCompat.requestPermissions(NearbyActivity.this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
LOCATION_REQUEST);
dialog.dismiss();
}
})
.setNegativeButton("Cancel", null)
.create()
@ -175,26 +171,19 @@ public class NearbyActivity extends NavigationBaseActivity {
LocationManager manager = (LocationManager) getSystemService(LOCATION_SERVICE);
if (!manager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
Timber.d("GPS is not enabled");
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this);
alertDialogBuilder.setMessage(R.string.gps_disabled)
new AlertDialog.Builder(this)
.setMessage(R.string.gps_disabled)
.setCancelable(false)
.setPositiveButton(R.string.enable_gps,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
(dialog, id) -> {
Intent callGPSSettingIntent = new Intent(
android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS);
Timber.d("Loaded settings page");
startActivityForResult(callGPSSettingIntent, 1);
}
});
alertDialogBuilder.setNegativeButton(R.string.menu_cancel_upload,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
});
AlertDialog alert = alertDialogBuilder.create();
alert.show();
})
.setNegativeButton(R.string.menu_cancel_upload, (dialog, id) -> dialog.cancel())
.create()
.show();
} else {
Timber.d("GPS is enabled");
}

View file

@ -1,5 +1,7 @@
package fr.free.nrw.commons.nearby;
import android.support.annotation.NonNull;
import com.pedrogomez.renderers.ListAdapteeCollection;
import com.pedrogomez.renderers.RVRendererAdapter;
import com.pedrogomez.renderers.RendererBuilder;
@ -10,7 +12,7 @@ import java.util.List;
class NearbyAdapterFactory {
private PlaceRenderer.PlaceClickedListener listener;
NearbyAdapterFactory(PlaceRenderer.PlaceClickedListener listener) {
NearbyAdapterFactory(@NonNull PlaceRenderer.PlaceClickedListener listener) {
this.listener = listener;
}

View file

@ -52,14 +52,11 @@ public class NearbyController {
distances.put(place, computeDistanceBetween(place.location, curLatLng));
}
Collections.sort(places,
new Comparator<Place>() {
@Override
public int compare(Place lhs, Place rhs) {
(lhs, rhs) -> {
double lhsDistance = distances.get(lhs);
double rhsDistance = distances.get(rhs);
return (int) (lhsDistance - rhsDistance);
}
}
);
}
return places;

View file

@ -63,15 +63,10 @@ public class NearbyInfoDialog extends OverlayDialog {
overflowButton.setVisibility(showMenu() ? View.VISIBLE : View.GONE);
overflowButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
popupMenuListener();
}
});
overflowButton.setOnClickListener(this::popupMenuListener);
}
private void popupMenuListener() {
private void popupMenuListener(View v) {
PopupMenu popupMenu = new PopupMenu(getActivity(), overflowButton);
popupMenu.inflate(R.menu.nearby_info_dialog_options);

View file

@ -48,12 +48,7 @@ public class NearbyListFragment extends Fragment {
View view = inflater.inflate(R.layout.fragment_nearby, container, false);
recyclerView = (RecyclerView) view.findViewById(R.id.listView);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
adapterFactory = new NearbyAdapterFactory(new PlaceRenderer.PlaceClickedListener() {
@Override
public void placeClicked(Place place) {
NearbyInfoDialog.showYourself(getActivity(), place);
}
});
adapterFactory = new NearbyAdapterFactory(place -> NearbyInfoDialog.showYourself(getActivity(), place));
return view;
}

View file

@ -88,25 +88,19 @@ public class NearbyMapFragment extends android.support.v4.app.Fragment {
// create map
mapView = new MapView(getActivity(), options);
mapView.onCreate(savedInstanceState);
mapView.getMapAsync(new OnMapReadyCallback() {
@Override
public void onMapReady(MapboxMap mapboxMap) {
mapView.getMapAsync(mapboxMap -> {
mapboxMap.addMarkers(baseMarkerOptions);
mapboxMap.setOnMarkerClickListener(new MapboxMap.OnMarkerClickListener() {
@Override
public boolean onMarkerClick(@NonNull Marker marker) {
mapboxMap.setOnMarkerClickListener(marker -> {
if (marker instanceof NearbyMarker) {
NearbyMarker nearbyMarker = (NearbyMarker) marker;
Place place = nearbyMarker.getNearbyBaseMarker().getPlace();
NearbyInfoDialog.showYourself(getActivity(), place);
}
return false;
}
});
addCurrentLocationMarker(mapboxMap);
}
});
if (PreferenceManager.getDefaultSharedPreferences(getActivity()).getBoolean("theme",true)) {
mapView.setStyleUrl(getResources().getString(R.string.map_theme_dark));

View file

@ -1,5 +1,6 @@
package fr.free.nrw.commons.nearby;
import android.support.annotation.NonNull;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@ -19,7 +20,7 @@ class PlaceRenderer extends Renderer<Place> {
@BindView(R.id.icon) ImageView icon;
private final PlaceClickedListener listener;
PlaceRenderer(PlaceClickedListener listener) {
PlaceRenderer(@NonNull PlaceClickedListener listener) {
this.listener = listener;
}
@ -35,14 +36,7 @@ class PlaceRenderer extends Renderer<Place> {
@Override
protected void hookListeners(View view) {
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (listener != null) {
listener.placeClicked(getContent());
}
}
});
view.setOnClickListener(v -> listener.placeClicked(getContent()));
}
@Override

View file

@ -28,21 +28,15 @@ public class SettingsFragment extends PreferenceFragment {
licensePreference.setSummary(getString(Utils.licenseNameFor(licensePreference.getValue())));
// Keep summary updated when changing value
licensePreference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
licensePreference.setOnPreferenceChangeListener((preference, newValue) -> {
preference.setSummary(getString(Utils.licenseNameFor((String) newValue)));
return true;
}
});
CheckBoxPreference themePreference = (CheckBoxPreference) findPreference("theme");
themePreference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
themePreference.setOnPreferenceChangeListener((preference, newValue) -> {
getActivity().recreate();
return true;
}
});
final EditTextPreference uploadLimit = (EditTextPreference) findPreference("uploads");
@ -51,21 +45,14 @@ public class SettingsFragment extends PreferenceFragment {
int uploads = sharedPref.getInt(Prefs.UPLOADS_SHOWING, 100);
uploadLimit.setText(uploads + "");
uploadLimit.setSummary(uploads + "");
uploadLimit.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
uploadLimit.setOnPreferenceChangeListener((preference, newValue) -> {
int value = Integer.parseInt(newValue.toString());
final SharedPreferences.Editor editor = sharedPref.edit();
if (value > 500) {
new AlertDialog.Builder(getActivity())
.setTitle(R.string.maximum_limit)
.setMessage(R.string.maximum_limit_alert)
.setPositiveButton(android.R.string.yes,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
}
})
.setPositiveButton(android.R.string.yes, (dialog, which) -> {})
.setIcon(android.R.drawable.ic_dialog_alert)
.show();
editor.putInt(Prefs.UPLOADS_SHOWING, 500);
@ -79,8 +66,6 @@ public class SettingsFragment extends PreferenceFragment {
}
editor.apply();
return true;
}
});

View file

@ -3,6 +3,7 @@ package fr.free.nrw.commons.theme;
import android.content.ActivityNotFoundException;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.design.widget.NavigationView;
import android.support.v4.widget.DrawerLayout;
@ -55,12 +56,7 @@ public class NavigationBaseActivity extends BaseActivity
public void initBackButton() {
int backStackEntryCount = getSupportFragmentManager().getBackStackEntryCount();
toggle.setDrawerIndicatorEnabled(backStackEntryCount == 0);
toggle.setToolbarNavigationClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onBackPressed();
}
});
toggle.setToolbarNavigationClickListener(v -> onBackPressed());
}
public void initBack() {
@ -114,13 +110,19 @@ public class NavigationBaseActivity extends BaseActivity
Toast.makeText(this, R.string.no_email_client, Toast.LENGTH_SHORT).show();
}
return true;
case R.id.action_developer_plans:
drawerLayout.closeDrawer(navigationView);
// Go to the page
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri
.parse(getResources()
.getString(R.string.feedback_page_url)));
startActivity(browserIntent);
return true;
case R.id.action_logout:
new AlertDialog.Builder(this)
.setMessage(R.string.logout_verification)
.setCancelable(false)
.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
.setPositiveButton(R.string.yes, (dialog, which) -> {
((CommonsApplication) getApplicationContext())
.clearApplicationData(NavigationBaseActivity.this);
Intent nearbyIntent = new Intent(
@ -129,14 +131,8 @@ public class NavigationBaseActivity extends BaseActivity
nearbyIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(nearbyIntent);
finish();
}
})
.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
})
.setNegativeButton(R.string.no, (dialog, which) -> dialog.cancel())
.show();
return true;
default:

View file

@ -72,21 +72,13 @@ public class ExistingFileAsync extends AsyncTask<Void, Void, Boolean> {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setMessage(R.string.file_exists)
.setTitle(R.string.warning);
builder.setPositiveButton(R.string.no, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
builder.setPositiveButton(R.string.no, (dialog, id) -> {
//Go back to ContributionsActivity
Intent intent = new Intent(context, ContributionsActivity.class);
context.startActivity(intent);
callback.onResult(Result.DUPLICATE_CANCELLED);
}
});
builder.setNegativeButton(R.string.yes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
callback.onResult(Result.DUPLICATE_PROCEED);
}
});
builder.setNegativeButton(R.string.yes, (dialog, id) -> callback.onResult(Result.DUPLICATE_PROCEED));
AlertDialog dialog = builder.create();
dialog.show();

View file

@ -11,6 +11,7 @@ import android.database.DataSetObserver;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.FragmentManager;
import android.support.v4.content.ContextCompat;
@ -28,6 +29,7 @@ import fr.free.nrw.commons.Media;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.auth.AuthenticatedActivity;
import fr.free.nrw.commons.category.CategorizationFragment;
import fr.free.nrw.commons.category.OnCategoriesSaveHandler;
import fr.free.nrw.commons.contributions.Contribution;
import fr.free.nrw.commons.media.MediaDetailPagerFragment;
import fr.free.nrw.commons.modifications.CategoryModifier;
@ -43,7 +45,7 @@ public class MultipleShareActivity
AdapterView.OnItemClickListener,
FragmentManager.OnBackStackChangedListener,
MultipleUploadListFragment.OnMultipleUploadInitiatedHandler,
CategorizationFragment.OnCategoriesSaveHandler {
OnCategoriesSaveHandler {
private CommonsApplication app;
private ArrayList<Contribution> photosList = null;
@ -104,7 +106,7 @@ public class MultipleShareActivity
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (requestCode == 1 && grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
multipleUploadBegins();
}
@ -125,9 +127,7 @@ public class MultipleShareActivity
Contribution up = photosList.get(i);
final int uploadCount = i + 1; // Goddamn Java
uploadController.startUpload(up, new UploadController.ContributionUploadProgress() {
@Override
public void onUploadStarted(Contribution contribution) {
uploadController.startUpload(up, contribution -> {
dialog.setProgress(uploadCount);
if (uploadCount == photosList.size()) {
dialog.dismiss();
@ -138,7 +138,6 @@ public class MultipleShareActivity
);
startingToast.show();
}
}
});
}

View file

@ -57,7 +57,7 @@ public class MwVolleyApi {
Timber.d("URL: %s", apiUrl);
JsonRequest<QueryResponse> request = new QueryRequest(apiUrl,
new LogResponseListener<QueryResponse>(), new LogResponseErrorListener());
new LogResponseListener<>(), new LogResponseErrorListener());
getQueue().add(request);
}

View file

@ -9,6 +9,7 @@ import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.support.design.widget.Snackbar;
@ -36,6 +37,7 @@ import fr.free.nrw.commons.R;
import fr.free.nrw.commons.Utils;
import fr.free.nrw.commons.auth.AuthenticatedActivity;
import fr.free.nrw.commons.category.CategorizationFragment;
import fr.free.nrw.commons.category.OnCategoriesSaveHandler;
import fr.free.nrw.commons.contributions.Contribution;
import fr.free.nrw.commons.modifications.CategoryModifier;
import fr.free.nrw.commons.modifications.ModificationsContentProvider;
@ -44,6 +46,9 @@ import fr.free.nrw.commons.modifications.TemplateRemoveModifier;
import fr.free.nrw.commons.mwapi.EventLog;
import timber.log.Timber;
import static fr.free.nrw.commons.upload.ExistingFileAsync.Result.DUPLICATE_PROCEED;
import static fr.free.nrw.commons.upload.ExistingFileAsync.Result.NO_DUPLICATE;
/**
* Activity for the title/desc screen after image is selected. Also starts processing image
* GPS coordinates or user location (if enabled in Settings) for category suggestions.
@ -51,7 +56,7 @@ import timber.log.Timber;
public class ShareActivity
extends AuthenticatedActivity
implements SingleUploadFragment.OnUploadActionInitiated,
CategorizationFragment.OnCategoriesSaveHandler {
OnCategoriesSaveHandler {
private static final int REQUEST_PERM_ON_CREATE_STORAGE = 1;
private static final int REQUEST_PERM_ON_CREATE_LOCATION = 2;
@ -133,12 +138,9 @@ public class ShareActivity
Timber.d("Cache the categories found");
}
uploadController.startUpload(title, mediaUri, description, mimeType, source, decimalCoords, new UploadController.ContributionUploadProgress() {
@Override
public void onUploadStarted(Contribution contribution) {
ShareActivity.this.contribution = contribution;
uploadController.startUpload(title, mediaUri, description, mimeType, source, decimalCoords, c -> {
ShareActivity.this.contribution = c;
showPostUpload();
}
});
}
@ -317,7 +319,7 @@ public class ShareActivity
@Override
public void onRequestPermissionsResult(int requestCode,
String[] permissions, int[] grantResults) {
@NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode) {
case REQUEST_PERM_ON_CREATE_STORAGE: {
if (grantResults.length >= 1
@ -379,14 +381,10 @@ public class ShareActivity
Timber.d("File SHA1 is: %s", fileSHA1);
ExistingFileAsync fileAsyncTask =
new ExistingFileAsync(fileSHA1, this, new ExistingFileAsync.Callback() {
@Override
public void onResult(ExistingFileAsync.Result result) {
new ExistingFileAsync(fileSHA1, this, result -> {
Timber.d("%s duplicate check: %s", mediaUri.toString(), result);
duplicateCheckPassed =
result == ExistingFileAsync.Result.DUPLICATE_PROCEED
|| result == ExistingFileAsync.Result.NO_DUPLICATE;
}
duplicateCheckPassed = (result == DUPLICATE_PROCEED
|| result == NO_DUPLICATE);
});
fileAsyncTask.execute();
} catch (IOException e) {
@ -401,17 +399,12 @@ public class ShareActivity
}
}
private Snackbar requestPermissionUsingSnackBar(
String rationale, final String[] perms, final int code) {
private Snackbar requestPermissionUsingSnackBar(String rationale,
final String[] perms,
final int code) {
Snackbar snackbar = Snackbar.make(findViewById(android.R.id.content), rationale,
Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.ok, new View.OnClickListener() {
@Override
public void onClick(View view) {
ActivityCompat.requestPermissions(ShareActivity.this,
perms, code);
}
});
Snackbar.LENGTH_INDEFINITE).setAction(R.string.ok,
view -> ActivityCompat.requestPermissions(ShareActivity.this, perms, code));
snackbar.show();
return snackbar;
}

View file

@ -7,15 +7,12 @@ import java.util.concurrent.Executor;
public class ExecutorUtils {
private static final Executor uiExecutor = new Executor() {
@Override
public void execute(Runnable command) {
private static final Executor uiExecutor = command -> {
if (Looper.myLooper() == Looper.getMainLooper()) {
command.run();
} else {
new Handler(Looper.getMainLooper()).post(command);
}
}
};
public static Executor uiExecutor () { return uiExecutor;}

View file

@ -42,8 +42,8 @@ public class FileUtils {
if (file != null) {
if (file.isDirectory()) {
String[] children = file.list();
for (int i = 0; i < children.length; i++) {
deletedAll = deleteFile(new File(file, children[i])) && deletedAll;
for (String child : children) {
deletedAll = deleteFile(new File(file, child)) && deletedAll;
}
} else {
deletedAll = file.delete();

Binary file not shown.

Before

Width:  |  Height:  |  Size: 251 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 149 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 139 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 214 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 487 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 317 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 256 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 364 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 261 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 247 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 251 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 453 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 274 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 255 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 207 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 352 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 446 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 590 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 171 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 127 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 114 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 177 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 323 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 231 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 183 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 240 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 185 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 168 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 208 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 322 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 196 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 246 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 187 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 144 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 239 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 640 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 373 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 295 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 446 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 304 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 273 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 344 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 557 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 339 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 354 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 246 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 175 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 303 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 940 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 528 B

Some files were not shown because too many files have changed in this diff Show more