Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Dmitry Brant 2017-05-19 15:14:29 +02:00
commit a3f3ee7fdb
85 changed files with 523 additions and 191 deletions

View file

@ -1,9 +1,5 @@
package fr.free.nrw.commons;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.anyOf;
import static org.hamcrest.Matchers.anything;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.support.test.espresso.Espresso;
@ -15,15 +11,18 @@ import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.view.View;
import fr.free.nrw.commons.settings.SettingsActivity;
import java.util.Map;
import org.hamcrest.Matcher;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.Map;
import fr.free.nrw.commons.settings.SettingsActivity;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.anything;
@LargeTest
@RunWith(AndroidJUnit4.class)
public class SettingsActivityTest {

View file

@ -1,5 +1,4 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="fr.free.nrw.commons">
<uses-permission android:name="android.permission.INTERNET" />

View file

@ -5,8 +5,12 @@ import android.accounts.AccountManager;
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
import android.app.Application;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.Build;
import android.database.sqlite.SQLiteDatabase;
import android.preference.PreferenceManager;
import com.android.volley.RequestQueue;
import com.android.volley.toolbox.BasicNetwork;
@ -19,7 +23,14 @@ import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;
import com.nostra13.universalimageloader.utils.StorageUtils;
import fr.free.nrw.commons.caching.CacheController;
import fr.free.nrw.commons.category.Category;
import fr.free.nrw.commons.contributions.Contribution;
import fr.free.nrw.commons.data.DBOpenHelper;
import fr.free.nrw.commons.modifications.ModifierSequence;
import fr.free.nrw.commons.auth.AccountUtil;
import fr.free.nrw.commons.nearby.NearbyPlaces;
import org.acra.ACRA;
import org.acra.ReportingInteractionMode;
import org.acra.annotation.ReportsCrashes;
@ -34,9 +45,10 @@ import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.CoreProtocolPNames;
import java.io.File;
import java.io.IOException;
import fr.free.nrw.commons.caching.CacheController;
import fr.free.nrw.commons.utils.FileUtils;
import timber.log.Timber;
// TODO: Use ProGuard to rip out reporting when publishing
@ -50,7 +62,6 @@ import timber.log.Timber;
)
public class CommonsApplication extends Application {
private MWApi api;
private Account currentAccount = null; // Unlike a savings account...
public static final String API_URL = "https://commons.wikimedia.org/w/api.php";
public static final String IMAGE_URL_BASE = "https://upload.wikimedia.org/wikipedia/commons";
@ -69,13 +80,37 @@ public class CommonsApplication extends Application {
public static final String FEEDBACK_EMAIL = "commons-app-android@googlegroups.com";
public static final String FEEDBACK_EMAIL_SUBJECT = "Commons Android App (%s) Feedback";
public RequestQueue volleyQueue;
private static CommonsApplication instance = null;
private AbstractHttpClient httpClient = null;
private MWApi api = null;
private CacheController cacheData = null;
private RequestQueue volleyQueue = null;
private DBOpenHelper dbOpenHelper = null;
private NearbyPlaces nearbyPlaces = null;
public CacheController cacheData;
/**
* This should not be called by ANY application code (other than the magic Android glue)
* Use CommonsApplication.getInstance() instead to get the singleton.
*/
public CommonsApplication() {
CommonsApplication.instance = this;
}
public static CommonsApplication app;
public static CommonsApplication getInstance() {
if (instance == null) {
instance = new CommonsApplication();
}
return instance;
}
public static AbstractHttpClient createHttpClient() {
public AbstractHttpClient getHttpClient() {
if (httpClient == null) {
httpClient = newHttpClient();
}
return httpClient;
}
private AbstractHttpClient newHttpClient() {
BasicHttpParams params = new BasicHttpParams();
SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
@ -86,14 +121,50 @@ public class CommonsApplication extends Application {
return new DefaultHttpClient(cm, params);
}
public static MWApi createMWApi() {
return new MWApi(API_URL, createHttpClient());
public MWApi getMWApi() {
if (api == null) {
api = newMWApi();
}
return api;
}
private MWApi newMWApi() {
return new MWApi(API_URL, getHttpClient());
}
public CacheController getCacheData() {
if (cacheData == null) {
cacheData = new CacheController();
}
return cacheData;
}
public RequestQueue getVolleyQueue() {
if (volleyQueue == null) {
DiskBasedCache cache = new DiskBasedCache(getCacheDir(), 16 * 1024 * 1024);
volleyQueue = new RequestQueue(cache, new BasicNetwork(new HurlStack()));
volleyQueue.start();
}
return volleyQueue;
}
public synchronized DBOpenHelper getDBOpenHelper() {
if (dbOpenHelper == null) {
dbOpenHelper = new DBOpenHelper(this);
}
return dbOpenHelper;
}
public synchronized NearbyPlaces getNearbyPlaces() {
if (nearbyPlaces == null) {
nearbyPlaces = new NearbyPlaces();
}
return nearbyPlaces;
}
@Override
public void onCreate() {
super.onCreate();
app = this;
Timber.plant(new Timber.DebugTree());
@ -104,9 +175,8 @@ public class CommonsApplication extends Application {
}
// Fire progress callbacks for every 3% of uploaded content
System.setProperty("in.yuvi.http.fluent.PROGRESS_TRIGGER_THRESHOLD", "3.0");
api = createMWApi();
ImageLoaderConfiguration imageLoaderConfiguration = new ImageLoaderConfiguration.Builder(getApplicationContext())
ImageLoaderConfiguration imageLoaderConfiguration = new ImageLoaderConfiguration.Builder(this)
.discCache(new TotalSizeLimitedDiscCache(StorageUtils.getCacheDirectory(this), 128 * 1024 * 1024))
.build();
ImageLoader.getInstance().init(imageLoaderConfiguration);
@ -129,7 +199,7 @@ public class CommonsApplication extends Application {
}
/**
* @return Accout|null
* @return Account|null
*/
public Account getCurrentAccount() {
if(currentAccount == null) {
@ -150,21 +220,12 @@ public class CommonsApplication extends Application {
return false; // This should never happen
}
accountManager.invalidateAuthToken(AccountUtil.accountType(), api.getAuthCookie());
accountManager.invalidateAuthToken(AccountUtil.accountType(), getMWApi().getAuthCookie());
try {
String authCookie = accountManager.blockingGetAuthToken(curAccount, "", false);
api.setAuthCookie(authCookie);
getMWApi().setAuthCookie(authCookie);
return true;
} catch (OperationCanceledException e) {
e.printStackTrace();
return false;
} catch (AuthenticatorException e) {
e.printStackTrace();
return false;
} catch (IOException e) {
e.printStackTrace();
return false;
} catch (NullPointerException e) {
} catch (OperationCanceledException | NullPointerException | IOException | AuthenticatorException e) {
e.printStackTrace();
return false;
}
@ -175,4 +236,47 @@ public class CommonsApplication extends Application {
return pm.hasSystemFeature(PackageManager.FEATURE_CAMERA) ||
pm.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT);
}
public void clearApplicationData(Context context) {
File cacheDirectory = context.getCacheDir();
File applicationDirectory = new File(cacheDirectory.getParent());
if (applicationDirectory.exists()) {
String[] fileNames = applicationDirectory.list();
for (String fileName : fileNames) {
if (!fileName.equals("lib")) {
FileUtils.deleteFile(new File(applicationDirectory, fileName));
}
}
}
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);
}
//TODO: fix preference manager
PreferenceManager.getDefaultSharedPreferences(getInstance()).edit().clear().commit();
SharedPreferences preferences = context
.getSharedPreferences("fr.free.nrw.commons", MODE_PRIVATE);
preferences.edit().clear().commit();
context.getSharedPreferences("prefs", Context.MODE_PRIVATE).edit().clear().commit();
preferences.edit().putBoolean("firstrun", false).apply();
updateAllDatabases(context);
currentAccount = null;
}
/**
* Deletes all tables and re-creates them.
* @param context context
*/
public void updateAllDatabases(Context context) {
DBOpenHelper dbOpenHelper = CommonsApplication.getInstance().getDBOpenHelper();
dbOpenHelper.getReadableDatabase().close();
SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
ModifierSequence.Table.onDelete(db);
Category.Table.onDelete(db);
Contribution.Table.onDelete(db);
}
}

View file

@ -6,6 +6,7 @@ import android.os.Build;
import android.preference.PreferenceManager;
import org.apache.http.HttpResponse;
import org.apache.http.impl.client.AbstractHttpClient;
import org.json.JSONException;
import org.json.JSONObject;
@ -30,10 +31,10 @@ public class EventLog {
boolean allSuccess = true;
// Not using the default URL connection, since that seems to have different behavior than the rest of the code
for(LogBuilder logBuilder: logBuilders) {
HttpURLConnection conn;
try {
URL url = logBuilder.toUrl();
HttpResponse response = Http.get(url.toString()).use(CommonsApplication.createHttpClient()).asResponse();
AbstractHttpClient httpClient = CommonsApplication.getInstance().getHttpClient();
HttpResponse response = Http.get(url.toString()).use(httpClient).asResponse();
if(response.getStatusLine().getStatusCode() != 204) {
allSuccess = false;

View file

@ -173,7 +173,7 @@ public class Media implements Parcelable {
}
public void setCategories(List<String> categories) {
this.categories.removeAll(this.categories);
this.categories.clear();
this.categories.addAll(categories);
}

View file

@ -66,7 +66,7 @@ public class MediaDataExtractor {
throw new IllegalStateException("Tried to call MediaDataExtractor.fetch() again.");
}
MWApi api = CommonsApplication.createMWApi();
MWApi api = CommonsApplication.getInstance().getMWApi();
ApiResult result = api.action("query")
.param("prop", "revisions")
.param("titles", filename)

View file

@ -11,7 +11,6 @@ public class MediaWikiImageView extends SimpleDraweeView {
private ThumbnailFetchTask currentThumbnailTask;
LruCache<String, String> thumbnailUrlCache = new LruCache<>(1024);
public MediaWikiImageView(Context context) {
this(context, null);
}

View file

@ -55,7 +55,7 @@ public class AccountUtil {
@NonNull
private static CommonsApplication app() {
return CommonsApplication.app;
return CommonsApplication.getInstance();
}
}

View file

@ -132,7 +132,7 @@ public abstract class AuthenticatedActivity extends NavigationBaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
app = (CommonsApplication)this.getApplicationContext();
app = CommonsApplication.getInstance();
if(savedInstanceState != null) {
authCookie = savedInstanceState.getString("authCookie");
}

View file

@ -40,7 +40,7 @@ public class LoginActivity extends AccountAuthenticatorActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
app = (CommonsApplication) getApplicationContext();
app = CommonsApplication.getInstance();
setContentView(R.layout.activity_login);
final LoginActivity that = this;
@ -199,7 +199,7 @@ public class LoginActivity extends AccountAuthenticatorActivity {
}
private void showUserToast( int resId ) {
Toast.makeText(getApplicationContext(), resId, Toast.LENGTH_LONG).show();
Toast.makeText(this, resId, Toast.LENGTH_LONG).show();
}
public void showSuccessToastAndDismissDialog() {

View file

@ -3,16 +3,16 @@ package fr.free.nrw.commons.auth;
import android.accounts.AccountAuthenticatorResponse;
import android.accounts.AccountManager;
import android.app.ProgressDialog;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import java.io.IOException;
import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.EventLog;
import fr.free.nrw.commons.R;
import timber.log.Timber;
import java.io.IOException;
class LoginTask extends AsyncTask<String, String, String> {
private LoginActivity loginActivity;
@ -26,7 +26,7 @@ class LoginTask extends AsyncTask<String, String, String> {
this.username = username;
this.password = password;
this.twoFactorCode = twoFactorCode;
app = (CommonsApplication) loginActivity.getApplicationContext();
app = CommonsApplication.getInstance();
}
@Override
@ -44,9 +44,9 @@ class LoginTask extends AsyncTask<String, String, String> {
protected String doInBackground(String... params) {
try {
if (twoFactorCode.isEmpty()) {
return app.getApi().login(username, password);
return app.getMWApi().login(username, password);
} else {
return app.getApi().login(username, password, twoFactorCode);
return app.getMWApi().login(username, password, twoFactorCode);
}
} catch (IOException e) {
// Do something better!

View file

@ -7,6 +7,7 @@ import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Toast;
import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.theme.BaseActivity;
import timber.log.Timber;
@ -39,10 +40,14 @@ public class SignupActivity extends BaseActivity {
//Signup success, so clear cookies, notify user, and load LoginActivity again
Timber.d("Overriding URL %s", url);
Toast toast = Toast.makeText(getApplicationContext(), "Account created!", Toast.LENGTH_LONG);
Toast toast = Toast.makeText(
CommonsApplication.getInstance(),
"Account created!",
Toast.LENGTH_LONG
);
toast.show();
Intent intent = new Intent(getApplicationContext(), LoginActivity.class);
Intent intent = new Intent(CommonsApplication.getInstance(), LoginActivity.class);
startActivity(intent);
return true;
} else {

View file

@ -75,7 +75,7 @@ public class WikiAccountAuthenticator extends AbstractAccountAuthenticator {
}
private String getAuthCookie(String username, String password) throws IOException {
MWApi api = CommonsApplication.createMWApi();
MWApi api = CommonsApplication.getInstance().getMWApi();
//TODO add 2fa support here
String result = api.login(username, password);
if(result.equals("PASS")) {

View file

@ -115,6 +115,11 @@ public class Category {
db.execSQL(CREATE_TABLE_STATEMENT);
}
public static void onDelete(SQLiteDatabase db) {
db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
onCreate(db);
}
public static void onUpdate(SQLiteDatabase db, int from, int to) {
if(from == to) {
return;

View file

@ -9,6 +9,7 @@ import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.text.TextUtils;
import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.data.DBOpenHelper;
import timber.log.Timber;
@ -36,7 +37,7 @@ public class CategoryContentProvider extends ContentProvider {
private DBOpenHelper dbOpenHelper;
@Override
public boolean onCreate() {
dbOpenHelper = DBOpenHelper.getInstance(getContext());
dbOpenHelper = CommonsApplication.getInstance().getDBOpenHelper();
return false;
}

View file

@ -79,7 +79,7 @@ public class MethodAUpdater extends AsyncTask<Void, Void, ArrayList<String>> {
protected ArrayList<String> doInBackground(Void... voids) {
//otherwise if user has typed something in that isn't in cache, search API for matching categories
MWApi api = CommonsApplication.createMWApi();
MWApi api = CommonsApplication.getInstance().getMWApi();
ApiResult result;
ArrayList<String> categories = new ArrayList<>();

View file

@ -95,7 +95,7 @@ public class PrefixUpdater extends AsyncTask<Void, Void, ArrayList<String>> {
//otherwise if user has typed something in that isn't in cache, search API for matching categories
//URL: https://commons.wikimedia.org/w/api.php?action=query&list=allcategories&acprefix=filter&aclimit=25
MWApi api = CommonsApplication.createMWApi();
MWApi api = CommonsApplication.getInstance().getMWApi();
ApiResult result;
ArrayList<String> categories = new ArrayList<>();
try {

View file

@ -34,7 +34,7 @@ public class TitleCategories extends AsyncTask<Void, Void, ArrayList<String>> {
@Override
protected ArrayList<String> doInBackground(Void... voids) {
MWApi api = CommonsApplication.createMWApi();
MWApi api = CommonsApplication.getInstance().getMWApi();
ApiResult result;
ArrayList<String> items = new ArrayList<>();

View file

@ -331,6 +331,11 @@ public class Contribution extends Media {
db.execSQL(CREATE_TABLE_STATEMENT);
}
public static void onDelete(SQLiteDatabase db) {
db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
onCreate(db);
}
public static void onUpdate(SQLiteDatabase db, int from, int to) {
if(from == to) {
return;

View file

@ -109,7 +109,7 @@ public class ContributionsActivity
@Override
protected void onAuthCookieAcquired(String authCookie) {
// Do a sync everytime we get here!
ContentResolver.requestSync(((CommonsApplication) getApplicationContext()).getCurrentAccount(), ContributionsContentProvider.AUTHORITY, new Bundle());
ContentResolver.requestSync(CommonsApplication.getInstance().getCurrentAccount(), ContributionsContentProvider.AUTHORITY, new Bundle());
Intent uploadServiceIntent = new Intent(this, UploadService.class);
uploadServiceIntent.setAction(UploadService.ACTION_START_SERVICE);
startService(uploadServiceIntent);
@ -219,7 +219,7 @@ public class ContributionsActivity
@Override
public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
SharedPreferences sharedPref =
PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
PreferenceManager.getDefaultSharedPreferences(this);
int uploads = sharedPref.getInt(Prefs.UPLOADS_SHOWING, 100);
return new CursorLoader(this, ContributionsContentProvider.BASE_URI,
Contribution.Table.ALL_FIELDS, CONTRIBUTION_SELECTION, null,

View file

@ -9,6 +9,7 @@ import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.text.TextUtils;
import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.data.DBOpenHelper;
import timber.log.Timber;
@ -35,7 +36,7 @@ public class ContributionsContentProvider extends ContentProvider{
private DBOpenHelper dbOpenHelper;
@Override
public boolean onCreate() {
dbOpenHelper = DBOpenHelper.getInstance(getContext());
dbOpenHelper = CommonsApplication.getInstance().getDBOpenHelper();
return false;
}

View file

@ -159,8 +159,7 @@ public class ContributionsListFragment extends Fragment {
menu.clear(); // See http://stackoverflow.com/a/8495697/17865
inflater.inflate(R.menu.fragment_contributions_list, menu);
CommonsApplication app = (CommonsApplication)getActivity().getApplicationContext();
if (!app.deviceHasCamera()) {
if (!CommonsApplication.getInstance().deviceHasCamera()) {
menu.findItem(R.id.menu_from_camera).setEnabled(false);
}
}

View file

@ -61,7 +61,7 @@ public class ContributionsSyncAdapter extends AbstractThreadedSyncAdapter {
public void onPerformSync(Account account, Bundle bundle, String s, ContentProviderClient contentProviderClient, SyncResult syncResult) {
// This code is fraught with possibilities of race conditions, but lalalalala I can't hear you!
String user = account.name;
MWApi api = CommonsApplication.createMWApi();
MWApi api = CommonsApplication.getInstance().getMWApi();
SharedPreferences prefs = this.getContext().getSharedPreferences("prefs", Context.MODE_PRIVATE);
String lastModified = prefs.getString("lastSyncTimestamp", "");
Date curTime = new Date();

View file

@ -15,7 +15,7 @@ public class ContributionsSyncService extends Service {
super.onCreate();
synchronized (sSyncAdapterLock) {
if (sSyncAdapter == null) {
sSyncAdapter = new ContributionsSyncAdapter(getApplicationContext(), true);
sSyncAdapter = new ContributionsSyncAdapter(this, true);
}
}
}

View file

@ -12,16 +12,11 @@ public class DBOpenHelper extends SQLiteOpenHelper{
private static final String DATABASE_NAME = "commons.db";
private static final int DATABASE_VERSION = 6;
private static DBOpenHelper singleton = null;
public static synchronized DBOpenHelper getInstance(Context context) {
if ( singleton == null ) {
singleton = new DBOpenHelper(context);
}
return singleton;
}
private DBOpenHelper(Context context) {
/**
* Do not use, please call CommonsApplication.getDBOpenHelper()
*/
public DBOpenHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}

View file

@ -1,15 +1,18 @@
package fr.free.nrw.commons.hamburger;
import android.content.ActivityNotFoundException;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.app.AlertDialog;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
@ -21,6 +24,7 @@ import fr.free.nrw.commons.AboutActivity;
import fr.free.nrw.commons.BuildConfig;
import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.auth.LoginActivity;
import fr.free.nrw.commons.contributions.ContributionsActivity;
import fr.free.nrw.commons.nearby.NearbyActivity;
import fr.free.nrw.commons.settings.SettingsActivity;
@ -31,22 +35,22 @@ public class NavigationBaseFragment extends Fragment {
ImageView pictureOfTheDay;
@BindView(R.id.upload_item)
TextView uploadItem;
LinearLayout uploadItem;
@BindView(R.id.nearby_item)
TextView nearbyItem;
LinearLayout nearbyItem;
@BindView(R.id.about_item)
TextView aboutItem;
LinearLayout aboutItem;
@BindView(R.id.settings_item)
TextView settingsItem;
LinearLayout settingsItem;
@BindView(R.id.feedback_item)
TextView feedbackItem;
LinearLayout feedbackItem;
@BindView(R.id.logout_item)
TextView logoutItem;
LinearLayout logoutItem;
private DrawerLayout drawerLayout;
private RelativeLayout drawerPane;
@ -120,6 +124,34 @@ public class NavigationBaseFragment extends Fragment {
}
}
@OnClick(R.id.logout_item)
protected void onLogoutItemClicked() {
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(getActivity());
alertDialogBuilder.setMessage(R.string.logout_verification)
.setCancelable(false)
.setPositiveButton(R.string.yes,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
((CommonsApplication)getActivity().getApplicationContext())
.clearApplicationData(getContext());
Intent nearbyIntent = new Intent
(getActivity(), LoginActivity.class);
nearbyIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
nearbyIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(nearbyIntent);
getActivity().finish();
}
});
alertDialogBuilder.setNegativeButton(R.string.no,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
});
AlertDialog alert = alertDialogBuilder.create();
alert.show();
}
private void closeDrawer() {
if (drawerLayout != null) {
drawerLayout.closeDrawer(drawerPane);

View file

@ -6,6 +6,8 @@ public class LatLng {
public final double longitude;
/** Accepts latitude and longitude.
* North and South values are cut off at 90°
*
* @param latitude double value
* @param longitude double value
*/
@ -44,14 +46,15 @@ public class LatLng {
}
/**
* Rounds the float to 4 digits.
* Rounds the float to 4 digits and returns absolute value.
*
* @param coordinate A coordinate value as string.
* @return String of the rounded number.
*/
private String formatCoordinate(double coordinate) {
double roundedNumber = Math.round(coordinate * 10000d) / 10000d;
return String.valueOf(roundedNumber);
double absoluteNumber = Math.abs(roundedNumber);
return String.valueOf(absoluteNumber);
}
/**
@ -73,7 +76,7 @@ public class LatLng {
* @return "E" or "W".
*/
private String getEastWest() {
if (this.longitude < 180) {
if (this.longitude >= 0 && this.longitude < 180) {
return "E";
}

View file

@ -231,7 +231,7 @@ public class MediaDetailFragment extends Fragment {
coordinates.setText(prettyCoordinates(media));
uploadedDate.setText(prettyUploadedDate(media));
categoryNames.removeAll(categoryNames);
categoryNames.clear();
categoryNames.addAll(media.getCategories());
categoriesLoaded = true;
@ -276,7 +276,7 @@ public class MediaDetailFragment extends Fragment {
desc.setText(prettyDescription(media));
license.setText(prettyLicense(media));
categoryNames.removeAll(categoryNames);
categoryNames.clear();
categoryNames.addAll(media.getCategories());
categoriesLoaded = true;
@ -399,8 +399,7 @@ public class MediaDetailFragment extends Fragment {
return "Uploaded date not available";
}
SimpleDateFormat formatter = new SimpleDateFormat("dd MMM yyyy");
String formattedDate = formatter.format(date);
return formattedDate;
return formatter.format(date);
}
/**
@ -409,8 +408,6 @@ public class MediaDetailFragment extends Fragment {
* @return Coordinates as text.
*/
private String prettyCoordinates(Media media) {
String coordinates = media.getCoordinates();
return coordinates;
return media.getCoordinates();
}
}

View file

@ -123,7 +123,7 @@ public class MediaDetailPagerFragment extends Fragment implements ViewPager.OnPa
if(savedInstanceState != null) {
editable = savedInstanceState.getBoolean("editable");
}
app = (CommonsApplication)getActivity().getApplicationContext();
app = CommonsApplication.getInstance();
setHasOptionsMenu(true);
}

View file

@ -9,6 +9,7 @@ import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.text.TextUtils;
import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.data.DBOpenHelper;
import timber.log.Timber;
@ -35,7 +36,7 @@ public class ModificationsContentProvider extends ContentProvider{
private DBOpenHelper dbOpenHelper;
@Override
public boolean onCreate() {
dbOpenHelper = DBOpenHelper.getInstance(getContext());
dbOpenHelper = CommonsApplication.getInstance().getDBOpenHelper();
return false;
}

View file

@ -61,7 +61,7 @@ public class ModificationsSyncAdapter extends AbstractThreadedSyncAdapter {
return;
}
MWApi api = CommonsApplication.createMWApi();
MWApi api = CommonsApplication.getInstance().getMWApi();
api.setAuthCookie(authCookie);
String editToken;

View file

@ -15,7 +15,7 @@ public class ModificationsSyncService extends Service {
super.onCreate();
synchronized (sSyncAdapterLock) {
if (sSyncAdapter == null) {
sSyncAdapter = new ModificationsSyncAdapter(getApplicationContext(), true);
sSyncAdapter = new ModificationsSyncAdapter(this, true);
}
}
}

View file

@ -142,5 +142,10 @@ public class ModifierSequence {
db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
onCreate(db);
}
public static void onDelete(SQLiteDatabase db) {
db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
onCreate(db);
}
}
}

View file

@ -25,6 +25,7 @@ import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.location.LatLng;
import fr.free.nrw.commons.location.LocationServiceManager;
import fr.free.nrw.commons.theme.NavigationBaseActivity;
@ -184,7 +185,7 @@ public class NearbyActivity extends NavigationBaseActivity {
@Override
protected List<Place> doInBackground(Void... params) {
return NearbyController
.loadAttractionsFromLocation(curLatLang, getApplicationContext()
.loadAttractionsFromLocation(curLatLang, CommonsApplication.getInstance()
);
}

View file

@ -15,6 +15,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.location.LatLng;
import timber.log.Timber;
@ -37,13 +38,14 @@ public class NearbyController {
if (curLatLng == null) {
return Collections.emptyList();
}
NearbyPlaces nearbyPlaces = CommonsApplication.getInstance().getNearbyPlaces();
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
List<Place> places = prefs.getBoolean("useWikidata", true)
? NearbyPlaces.getInstance().getFromWikidataQuery(
? nearbyPlaces.getFromWikidataQuery(
context,
curLatLng,
Locale.getDefault().getLanguage())
: NearbyPlaces.getInstance().getFromWikiNeedsPictures();
: nearbyPlaces.getFromWikiNeedsPictures();
if (curLatLng != null) {
Timber.d("Sorting places by distance...");
final Map<Place, Double> distances = new HashMap<>();

View file

@ -1,6 +1,5 @@
package fr.free.nrw.commons.nearby;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
@ -9,22 +8,19 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.ListView;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnItemClick;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.location.LatLng;
import fr.free.nrw.commons.utils.UriDeserializer;
import java.lang.reflect.Type;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnItemClick;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.location.LatLng;
import fr.free.nrw.commons.utils.UriDeserializer;
import timber.log.Timber;
public class NearbyListFragment extends ListFragment {

View file

@ -29,13 +29,9 @@ public class NearbyPlaces {
private static final double MAX_RADIUS = 300.0; // in kilometer
private static final double RADIUS_MULTIPLIER = 1.618;
private static final String WIKIDATA_QUERY_URL = "https://query.wikidata.org/sparql?query=${QUERY}";
private static NearbyPlaces singleton;
private double radius = INITIAL_RADIUS;
private List<Place> places;
private NearbyPlaces(){
}
List<Place> getFromWikidataQuery(Context context,
LatLng curLatLng,
String lang) {
@ -199,17 +195,4 @@ public class NearbyPlaces {
}
return places;
}
/**
* Get the singleton instance of this class.
* The instance is created upon the first invocation of this method, and then reused.
*
* @return The singleton instance
*/
public static synchronized NearbyPlaces getInstance() {
if (singleton == null) {
singleton = new NearbyPlaces();
}
return singleton;
}
}

View file

@ -11,6 +11,7 @@ import android.preference.Preference;
import android.preference.PreferenceFragment;
import android.preference.PreferenceManager;
import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.Utils;
@ -46,7 +47,7 @@ public class SettingsFragment extends PreferenceFragment {
final EditTextPreference uploadLimit = (EditTextPreference) findPreference("uploads");
final SharedPreferences sharedPref = PreferenceManager
.getDefaultSharedPreferences(getActivity().getApplicationContext());
.getDefaultSharedPreferences(CommonsApplication.getInstance());
int uploads = sharedPref.getInt(Prefs.UPLOADS_SHOWING, 100);
uploadLimit.setText(uploads + "");
uploadLimit.setSummary(uploads + "");

View file

@ -38,7 +38,7 @@ public class ExistingFileAsync extends AsyncTask<Void, Void, Boolean> {
@Override
protected Boolean doInBackground(Void... voids) {
MWApi api = CommonsApplication.createMWApi();
MWApi api = CommonsApplication.getInstance().getMWApi();
ApiResult result;
// https://commons.wikimedia.org/w/api.php?action=query&list=allimages&format=xml&aisha1=801957214aba50cb63bb6eb1b0effa50188900ba

View file

@ -1,5 +1,6 @@
package fr.free.nrw.commons.upload;
import android.annotation.SuppressLint;
import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
@ -20,6 +21,8 @@ public class FileUtils {
* @param uri The Uri to query.
* @author paulburke
*/
// Can be safely suppressed, checks for isKitKat before running isDocumentUri
@SuppressLint("NewApi")
public static String getPath(final Context context, final Uri uri) {
final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;

View file

@ -132,7 +132,11 @@ public class MultipleShareActivity
dialog.setProgress(uploadCount);
if(uploadCount == photosList.size()) {
dialog.dismiss();
Toast startingToast = Toast.makeText(getApplicationContext(), R.string.uploading_started, Toast.LENGTH_LONG);
Toast startingToast = Toast.makeText(
CommonsApplication.getInstance(),
R.string.uploading_started,
Toast.LENGTH_LONG
);
startingToast.show();
}
}
@ -202,9 +206,9 @@ public class MultipleShareActivity
uploadController = new UploadController(this);
setContentView(R.layout.activity_multiple_uploads);
app = CommonsApplication.getInstance();
ButterKnife.bind(this);
initDrawer();
app = (CommonsApplication)this.getApplicationContext();
if(savedInstanceState != null) {
photosList = savedInstanceState.getParcelableArrayList("uploadsList");
@ -241,7 +245,7 @@ public class MultipleShareActivity
@Override
protected void onAuthCookieAcquired(String authCookie) {
app.getApi().setAuthCookie(authCookie);
app.getMWApi().setAuthCookie(authCookie);
Intent intent = getIntent();
if(intent.getAction().equals(Intent.ACTION_SEND_MULTIPLE)) {

View file

@ -101,7 +101,7 @@ public class MwVolleyApi {
private static RequestQueue getQueue(Context context) {
if (REQUEST_QUEUE == null) {
REQUEST_QUEUE = Volley.newRequestQueue(context.getApplicationContext());
REQUEST_QUEUE = Volley.newRequestQueue(context);
}
return REQUEST_QUEUE;
}

View file

@ -106,12 +106,16 @@ public class ShareActivity
getFileMetadata(false);
}
Toast startingToast = Toast.makeText(getApplicationContext(), R.string.uploading_started, Toast.LENGTH_LONG);
Toast startingToast = Toast.makeText(
CommonsApplication.getInstance(),
R.string.uploading_started,
Toast.LENGTH_LONG
);
startingToast.show();
if (!cacheFound) {
//Has to be called after apiCall.request()
app.cacheData.cacheCategory();
app.getCacheData().cacheCategory();
Timber.d("Cache the categories found");
}
@ -189,7 +193,7 @@ public class ShareActivity
@Override
protected void onAuthCookieAcquired(String authCookie) {
app.getApi().setAuthCookie(authCookie);
app.getMWApi().setAuthCookie(authCookie);
shareView = (SingleUploadFragment) getSupportFragmentManager().findFragmentByTag("shareView");
categorizationFragment = (CategorizationFragment) getSupportFragmentManager().findFragmentByTag("categorization");
@ -217,7 +221,7 @@ public class ShareActivity
setContentView(R.layout.activity_share);
ButterKnife.bind(this);
initDrawer();
app = (CommonsApplication)this.getApplicationContext();
app = CommonsApplication.getInstance();
backgroundImageView = (ImageView)findViewById(R.id.backgroundImage);
//Receive intent from ContributionController.java when user selects picture to upload
@ -398,12 +402,12 @@ public class ShareActivity
if (imageObj.imageCoordsExists) {
double decLongitude = imageObj.getDecLongitude();
double decLatitude = imageObj.getDecLatitude();
app.cacheData.setQtPoint(decLongitude, decLatitude);
app.getCacheData().setQtPoint(decLongitude, decLatitude);
}
MwVolleyApi apiCall = new MwVolleyApi(this);
List<String> displayCatList = app.cacheData.findCategory();
List<String> displayCatList = app.getCacheData().findCategory();
boolean catListEmpty = displayCatList.isEmpty();
// If no categories found in cache, call MediaWiki API to match image coords with nearby Commons categories

View file

@ -36,7 +36,7 @@ public class UploadController {
public UploadController(Activity activity) {
this.activity = activity;
app = (CommonsApplication)activity.getApplicationContext();
app = CommonsApplication.getInstance();
}
private boolean isUploadServiceConnected;
@ -55,7 +55,7 @@ public class UploadController {
};
public void prepareService() {
Intent uploadServiceIntent = new Intent(activity.getApplicationContext(), UploadService.class);
Intent uploadServiceIntent = new Intent(activity, UploadService.class);
uploadServiceIntent.setAction(UploadService.ACTION_START_SERVICE);
activity.startService(uploadServiceIntent);
activity.bindService(uploadServiceIntent, uploadServiceConnection, Context.BIND_AUTO_CREATE);

View file

@ -115,7 +115,7 @@ public class UploadService extends HandlerService<Contribution> {
super.onCreate();
notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
app = (CommonsApplication) this.getApplicationContext();
app = CommonsApplication.getInstance();
contributionsProviderClient = this.getContentResolver().acquireContentProviderClient(ContributionsContentProvider.AUTHORITY);
}
@ -176,7 +176,7 @@ public class UploadService extends HandlerService<Contribution> {
}
private void uploadContribution(Contribution contribution) {
MWApi api = app.getApi();
MWApi api = app.getMWApi();
ApiResult result;
InputStream file = null;
@ -201,7 +201,7 @@ public class UploadService extends HandlerService<Contribution> {
.setContentText(getResources().getQuantityString(R.plurals.uploads_pending_notification_indicator, toUpload, toUpload))
.setOngoing(true)
.setProgress(100, 0, true)
.setContentIntent(PendingIntent.getActivity(getApplicationContext(), 0, new Intent(this, ContributionsActivity.class), 0))
.setContentIntent(PendingIntent.getActivity(this, 0, new Intent(this, ContributionsActivity.class), 0))
.setTicker(getString(R.string.upload_progress_notification_title_in_progress, contribution.getDisplayTitle()));
this.startForeground(NOTIFICATION_UPLOAD_IN_PROGRESS, curProgressNotification.build());
@ -282,7 +282,7 @@ public class UploadService extends HandlerService<Contribution> {
toUpload--;
if(toUpload == 0) {
// Sync modifications right after all uplaods are processed
ContentResolver.requestSync(((CommonsApplication) getApplicationContext()).getCurrentAccount(), ModificationsContentProvider.AUTHORITY, new Bundle());
ContentResolver.requestSync((CommonsApplication.getInstance()).getCurrentAccount(), ModificationsContentProvider.AUTHORITY, new Bundle());
stopForeground(true);
}
}
@ -304,7 +304,7 @@ public class UploadService extends HandlerService<Contribution> {
}
private String findUniqueFilename(String fileName) throws IOException {
MWApi api = app.getApi();
MWApi api = app.getMWApi();
String sequenceFileName;
for ( int sequenceNumber = 1; true; sequenceNumber++ ) {
if (sequenceNumber == 1) {

View file

@ -3,6 +3,7 @@ package fr.free.nrw.commons.utils;
import android.content.Context;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
@ -32,4 +33,26 @@ public class FileUtils {
}
return stringBuilder;
}
/**
* Deletes files.
* @param file context
*/
public static boolean deleteFile(File file) {
boolean deletedAll = true;
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;
}
} else {
deletedAll = file.delete();
}
}
return deletedAll;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 251 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 487 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 376 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 453 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 323 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 322 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 246 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 640 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 457 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 557 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 354 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 246 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 940 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 683 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 827 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 432 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 311 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 868 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

View file

@ -0,0 +1,15 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<shape android:shape="rectangle">
<solid android:color="@color/hamburger_item_pressed_color" />
</shape>
</item>
<item android:state_enabled="true">
<shape android:shape="rectangle">
<solid android:color="@android:color/transparent" />
</shape>
</item>
</selector>

View file

@ -1,7 +1,5 @@
<vector android:height="24dp" android:viewportHeight="235.0"
android:viewportWidth="235.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#B1B1B1" android:pathData="M0,0h235v235h-235z"/>
<path android:fillColor="#E3E3E3" android:pathData="M117.5,18.5c-54.68,0 -99,44.32 -99,99c0,54.67 44.32,99 99,99c54.67,0 99,-44.32 99,-99C216.5,62.83 172.18,18.5 117.5,18.5zM117.5,202.98c-47.21,0 -85.48,-38.27 -85.48,-85.48c0,-47.21 38.27,-85.48 85.48,-85.48c47.21,0 85.48,38.27 85.48,85.48C202.98,164.71 164.71,202.98 117.5,202.98z"/>
<path android:fillColor="#EDA33F" android:pathData="M197.23,117.5c0,44.03 -35.69,79.72 -79.73,79.72c-44.03,0 -79.73,-35.69 -79.73,-79.72c0,-44.03 35.69,-79.73 79.73,-79.73C161.53,37.77 197.23,73.47 197.23,117.5z"/>
<path android:fillColor="#634207" android:pathData="M143.37,98.87c0,3.36 -0.49,6.32 -1.47,8.9s-2.37,4.81 -4.16,6.7c-1.79,1.89 -3.94,3.59 -6.45,5.08c-2.51,1.5 -5.34,2.86 -8.49,4.09v11.36h-17.53v-16.8c2.36,-0.63 4.49,-1.28 6.4,-1.94c1.91,-0.67 3.91,-1.75 6,-3.24c1.96,-1.33 3.49,-2.88 4.61,-4.64c1.11,-1.76 1.67,-3.76 1.67,-5.98c0,-3.32 -1.07,-5.69 -3.21,-7.11c-2.14,-1.41 -5.15,-2.12 -9.04,-2.12c-2.39,0 -5.09,0.51 -8.09,1.54c-3.01,1.03 -5.75,2.36 -8.24,3.98h-1.99V83.51c2.12,-0.9 5.39,-1.83 9.81,-2.81c4.41,-0.98 8.9,-1.47 13.44,-1.47c8.2,0 14.7,1.81 19.52,5.43S143.37,93.02 143.37,98.87zM124.2,154.91h-20.12V141.77h20.12V154.91z"/>
<vector android:height="24dp" android:viewportHeight="32.0"
android:viewportWidth="32.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#006598" android:pathData="m15.87,3.79c-0.52,0.03 -0.91,0.06 -1.34,0.17 -1.55,0.38 -2.92,1.35 -3.81,2.69 -0.71,1.06 -1.08,2.29 -1.08,3.57 0,0.37 0.02,0.6 0.02,0.88l1.86,0c-0.07,-0.31 -0.12,-0.63 -0.12,-0.95 0,-2.6 2.12,-4.69 4.72,-4.69 2.6,0 4.69,2.1 4.69,4.69 0,2.33 -1.59,4.27 -3.81,4.82 -0.38,0.21 -0.73,0.24 -1,0.51 -0.24,0.24 -0.42,0.44 -0.59,0.73 -0.19,0.33 -0.36,0.76 -0.46,1.17 -0.03,0.12 -0.01,0.12 -0.02,0.12 -0.01,0 -0.16,-0.05 -0.42,-0.15 -0.54,-0.21 -0.68,-0.24 -0.76,-0.24 -0.05,-0 -0.06,0.01 -0.05,0.05 0.01,0.04 0.06,0.1 0.15,0.2 0.32,0.37 0.59,0.88 0.9,1.61 0.26,0.62 0.56,1.43 0.78,2.2 0.03,0.09 0.05,0.19 0.05,0.2 0,0 0.48,-0.79 0.81,-1.34 0.96,-1.63 1.46,-2.62 1.39,-2.71 -0.01,-0.01 -0.01,0.01 -0.05,0.02 -0.02,0.01 -0.07,0.04 -0.15,0.12 -0.07,0.07 -0.16,0.15 -0.2,0.17 -0.11,0.08 -0.27,0.16 -0.42,0.2 -0.13,0.03 -0.26,0.04 -0.27,0.02 -0.01,-0.01 0.03,-0.15 0.07,-0.27 0.17,-0.5 0.38,-0.82 0.76,-1.08 0.22,-0.15 0.44,-0.24 1.05,-0.49 0.48,-0.2 0.61,-0.27 0.86,-0.39 0.52,-0.26 0.92,-0.54 1.3,-0.93 0.54,-0.56 0.97,-1.22 1.27,-1.93 0.34,-0.8 0.51,-1.67 0.51,-2.54 0,-1.27 -0.37,-2.51 -1.08,-3.57 -0.35,-0.52 -0.76,-0.99 -1.25,-1.39 -0.88,-0.73 -1.93,-1.21 -3.06,-1.39 -0.23,-0.04 -0.43,-0.05 -0.68,-0.07 -0.1,-0.01 -0.5,-0 -0.59,0z"/>
<path android:fillColor="#980000" android:pathData="m15.61,25.72c-0.51,-0.04 -0.97,-0.29 -1.29,-0.68 -0.18,-0.23 -0.31,-0.49 -0.37,-0.78 -0.03,-0.15 -0.03,-0.19 -0.03,-0.38 0,-0.16 0,-0.19 0.01,-0.26 0.05,-0.34 0.18,-0.63 0.39,-0.89 0.06,-0.08 0.2,-0.22 0.28,-0.28 0.35,-0.28 0.78,-0.42 1.21,-0.41 0.41,0.01 0.77,0.14 1.09,0.4 0.08,0.06 0.23,0.21 0.29,0.28 0.18,0.22 0.29,0.45 0.36,0.71 0.04,0.15 0.05,0.28 0.05,0.45 0,0.12 -0.01,0.2 -0.02,0.3 -0.1,0.6 -0.48,1.1 -1.03,1.36 -0.29,0.14 -0.62,0.2 -0.94,0.17z"/>
</vector>

View file

@ -2,7 +2,7 @@
<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/text1"
style="?android:attr/spinnerDropDownItemStyle"
android:singleLine="true"
android:maxLines="1"
android:layout_width="match_parent"
android:layout_height="?android:attr/listPreferredItemHeight"
android:ellipsize="marquee"

View file

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
@ -27,65 +28,149 @@
android:layout_height="0.5dp"
android:background="@android:color/black"/>
<TextView
<LinearLayout
android:id="@+id/upload_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="12dp"
android:textSize="20dp"
android:textStyle="bold"
android:textColor="@android:color/black"
android:text="@string/navigation_item_upload"/>
android:layout_height="52dp"
android:background="@drawable/hamburger_item_bg"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:src="@drawable/ic_file_upload_black_24dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="12dp"
android:text="@string/navigation_item_upload"
android:letterSpacing="0.02"
android:textColor="@color/main_background_dark"
android:textSize="@dimen/hamburger_menu_item" />
</LinearLayout>
<LinearLayout
android:id="@+id/nearby_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="12dp"
android:textSize="20dp"
android:textStyle="bold"
android:textColor="@android:color/black"
android:text="@string/navigation_item_nearby"/>
android:layout_height="52dp"
android:background="@drawable/hamburger_item_bg"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:src="@drawable/ic_location_on_black_24dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="12dp"
android:text="@string/navigation_item_nearby"
android:letterSpacing="0.02"
android:textColor="@color/main_background_dark"
android:textSize="@dimen/hamburger_menu_item" />
</LinearLayout>
<LinearLayout
android:id="@+id/about_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="12dp"
android:textSize="20dp"
android:textStyle="bold"
android:textColor="@android:color/black"
android:text="@string/navigation_item_about"/>
android:layout_height="52dp"
android:background="@drawable/hamburger_item_bg"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:src="@drawable/ic_info_outline_black_24dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="12dp"
android:text="@string/navigation_item_about"
android:letterSpacing="0.02"
android:textColor="@color/main_background_dark"
android:textSize="@dimen/hamburger_menu_item" />
</LinearLayout>
<LinearLayout
android:id="@+id/settings_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="12dp"
android:textSize="20dp"
android:textStyle="bold"
android:textColor="@android:color/black"
android:text="@string/navigation_item_settings"/>
android:layout_height="52dp"
android:background="@drawable/hamburger_item_bg"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:src="@drawable/ic_settings_black_24dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="12dp"
android:text="@string/navigation_item_settings"
android:letterSpacing="0.02"
android:textColor="@color/main_background_dark"
android:textSize="@dimen/hamburger_menu_item" />
</LinearLayout>
<LinearLayout
android:id="@+id/feedback_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="12dp"
android:textSize="20dp"
android:textStyle="bold"
android:textColor="@android:color/black"
android:text="@string/navigation_item_feedback"/>
android:layout_height="52dp"
android:background="@drawable/hamburger_item_bg"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:src="@drawable/ic_feedback_black_24dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="12dp"
android:text="@string/navigation_item_feedback"
android:letterSpacing="0.02"
android:textColor="@color/main_background_dark"
android:textSize="@dimen/hamburger_menu_item" />
</LinearLayout>
<LinearLayout
android:id="@+id/logout_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="12dp"
android:textSize="20dp"
android:textStyle="bold"
android:textColor="@android:color/black"
android:text="@string/navigation_item_logout"/>
android:layout_height="52dp"
android:background="@drawable/hamburger_item_bg"
android:gravity="center_vertical"
android:orientation="horizontal">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:src="@drawable/ic_exit_to_app_black_24dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="12dp"
android:text="@string/navigation_item_logout"
android:letterSpacing="0.02"
android:textColor="@color/main_background_dark"
android:textSize="@dimen/hamburger_menu_item" />
</LinearLayout>
</LinearLayout>
</ScrollView>
</LinearLayout>

View file

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"

View file

@ -22,4 +22,5 @@
<color name="upload_overlay_background_dark">#77000000</color>
<color name="upload_overlay_background_light">#44000000</color>
<color name="hamburger_item_pressed_color">#f5f5f5</color>
</resources>

View file

@ -3,4 +3,5 @@
<dimen name="tiny_margin">4dp</dimen>
<dimen name="small_margin">8dp</dimen>
<dimen name="bottom_peak_height">240dp</dimen>
<dimen name="hamburger_menu_item">18sp</dimen>
</resources>

View file

@ -183,6 +183,7 @@ Tap this message (or hit back) to skip this step.</string>
<string name="maximum_limit_alert">Unable to display more than 500</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="logout_verification">Do you really want to logout?</string>
<string name="cancel">Cancel</string>
<string name="navigation_drawer_open">Open</string>

View file

@ -0,0 +1,64 @@
package fr.free.nrw.commons;
import static org.hamcrest.CoreMatchers.is;
import fr.free.nrw.commons.location.LatLng;
import org.junit.Assert;
import org.junit.Test;
public class LatLngTests {
@Test public void testZeroZero() {
LatLng place = new LatLng(0, 0);
String prettyString = place.getPrettyCoordinateString();
Assert.assertThat(prettyString, is("0.0 N, 0.0 E"));
}
@Test public void testAntipode() {
LatLng place = new LatLng(0, 180);
String prettyString = place.getPrettyCoordinateString();
Assert.assertThat(prettyString, is("0.0 N, 180.0 W"));
}
@Test public void testNorthPole() {
LatLng place = new LatLng(90, 0);
String prettyString = place.getPrettyCoordinateString();
Assert.assertThat(prettyString, is("90.0 N, 0.0 E"));
}
@Test public void testSouthPole() {
LatLng place = new LatLng(-90, 0);
String prettyString = place.getPrettyCoordinateString();
Assert.assertThat(prettyString, is("90.0 S, 0.0 E"));
}
@Test public void testLargerNumbers() {
LatLng place = new LatLng(120, 380);
String prettyString = place.getPrettyCoordinateString();
Assert.assertThat(prettyString, is("90.0 N, 20.0 E"));
}
@Test public void testNegativeNumbers() {
LatLng place = new LatLng(-120, -30);
String prettyString = place.getPrettyCoordinateString();
Assert.assertThat(prettyString, is("90.0 S, 30.0 W"));
}
@Test public void testTooBigWestValue() {
LatLng place = new LatLng(20, -190);
String prettyString = place.getPrettyCoordinateString();
Assert.assertThat(prettyString, is("20.0 N, 170.0 E"));
}
@Test public void testRounding() {
LatLng place = new LatLng(0.1234567, -0.33333333);
String prettyString = place.getPrettyCoordinateString();
Assert.assertThat(prettyString, is("0.1235 N, 0.3333 W"));
}
@Test public void testRoundingAgain() {
LatLng place = new LatLng(-0.000001, -0.999999);
String prettyString = place.getPrettyCoordinateString();
Assert.assertThat(prettyString, is("0.0 S, 1.0 W"));
}
}