diff --git a/app/src/androidTest/java/fr/free/nrw/commons/SettingsActivityTest.java b/app/src/androidTest/java/fr/free/nrw/commons/SettingsActivityTest.java index 7ba617730..2bae89265 100644 --- a/app/src/androidTest/java/fr/free/nrw/commons/SettingsActivityTest.java +++ b/app/src/androidTest/java/fr/free/nrw/commons/SettingsActivityTest.java @@ -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 { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4164cd60a..bce7c0642 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,5 +1,4 @@ diff --git a/app/src/main/java/fr/free/nrw/commons/CommonsApplication.java b/app/src/main/java/fr/free/nrw/commons/CommonsApplication.java index 696561b6a..01477a38e 100644 --- a/app/src/main/java/fr/free/nrw/commons/CommonsApplication.java +++ b/app/src/main/java/fr/free/nrw/commons/CommonsApplication.java @@ -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); + } } diff --git a/app/src/main/java/fr/free/nrw/commons/EventLog.java b/app/src/main/java/fr/free/nrw/commons/EventLog.java index 694c03b6b..dea4d7c23 100644 --- a/app/src/main/java/fr/free/nrw/commons/EventLog.java +++ b/app/src/main/java/fr/free/nrw/commons/EventLog.java @@ -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; diff --git a/app/src/main/java/fr/free/nrw/commons/Media.java b/app/src/main/java/fr/free/nrw/commons/Media.java index f8f90896a..1bd0344b7 100644 --- a/app/src/main/java/fr/free/nrw/commons/Media.java +++ b/app/src/main/java/fr/free/nrw/commons/Media.java @@ -173,7 +173,7 @@ public class Media implements Parcelable { } public void setCategories(List categories) { - this.categories.removeAll(this.categories); + this.categories.clear(); this.categories.addAll(categories); } diff --git a/app/src/main/java/fr/free/nrw/commons/MediaDataExtractor.java b/app/src/main/java/fr/free/nrw/commons/MediaDataExtractor.java index 344546c7a..44e7b8242 100644 --- a/app/src/main/java/fr/free/nrw/commons/MediaDataExtractor.java +++ b/app/src/main/java/fr/free/nrw/commons/MediaDataExtractor.java @@ -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) diff --git a/app/src/main/java/fr/free/nrw/commons/MediaWikiImageView.java b/app/src/main/java/fr/free/nrw/commons/MediaWikiImageView.java index d0913edc3..a80092b3b 100644 --- a/app/src/main/java/fr/free/nrw/commons/MediaWikiImageView.java +++ b/app/src/main/java/fr/free/nrw/commons/MediaWikiImageView.java @@ -11,7 +11,6 @@ public class MediaWikiImageView extends SimpleDraweeView { private ThumbnailFetchTask currentThumbnailTask; LruCache thumbnailUrlCache = new LruCache<>(1024); - public MediaWikiImageView(Context context) { this(context, null); } diff --git a/app/src/main/java/fr/free/nrw/commons/auth/AccountUtil.java b/app/src/main/java/fr/free/nrw/commons/auth/AccountUtil.java index 29f6fcf91..773eb5d63 100644 --- a/app/src/main/java/fr/free/nrw/commons/auth/AccountUtil.java +++ b/app/src/main/java/fr/free/nrw/commons/auth/AccountUtil.java @@ -55,7 +55,7 @@ public class AccountUtil { @NonNull private static CommonsApplication app() { - return CommonsApplication.app; + return CommonsApplication.getInstance(); } } diff --git a/app/src/main/java/fr/free/nrw/commons/auth/AuthenticatedActivity.java b/app/src/main/java/fr/free/nrw/commons/auth/AuthenticatedActivity.java index 6a99764af..7e195ed3e 100644 --- a/app/src/main/java/fr/free/nrw/commons/auth/AuthenticatedActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/auth/AuthenticatedActivity.java @@ -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"); } diff --git a/app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.java b/app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.java index bbadc757a..0bec4bac6 100644 --- a/app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.java @@ -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() { diff --git a/app/src/main/java/fr/free/nrw/commons/auth/LoginTask.java b/app/src/main/java/fr/free/nrw/commons/auth/LoginTask.java index 8785bff71..6db5f8654 100644 --- a/app/src/main/java/fr/free/nrw/commons/auth/LoginTask.java +++ b/app/src/main/java/fr/free/nrw/commons/auth/LoginTask.java @@ -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 { private LoginActivity loginActivity; @@ -26,7 +26,7 @@ class LoginTask extends AsyncTask { 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 { 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! diff --git a/app/src/main/java/fr/free/nrw/commons/auth/SignupActivity.java b/app/src/main/java/fr/free/nrw/commons/auth/SignupActivity.java index 5965c8928..62ccb5731 100644 --- a/app/src/main/java/fr/free/nrw/commons/auth/SignupActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/auth/SignupActivity.java @@ -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 { diff --git a/app/src/main/java/fr/free/nrw/commons/auth/WikiAccountAuthenticator.java b/app/src/main/java/fr/free/nrw/commons/auth/WikiAccountAuthenticator.java index 7233b6be8..ebbd6c285 100644 --- a/app/src/main/java/fr/free/nrw/commons/auth/WikiAccountAuthenticator.java +++ b/app/src/main/java/fr/free/nrw/commons/auth/WikiAccountAuthenticator.java @@ -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")) { diff --git a/app/src/main/java/fr/free/nrw/commons/category/Category.java b/app/src/main/java/fr/free/nrw/commons/category/Category.java index 645b10afc..f290dd741 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/Category.java +++ b/app/src/main/java/fr/free/nrw/commons/category/Category.java @@ -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; diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryContentProvider.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryContentProvider.java index 314ab33f2..de157265b 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryContentProvider.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryContentProvider.java @@ -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; } diff --git a/app/src/main/java/fr/free/nrw/commons/category/MethodAUpdater.java b/app/src/main/java/fr/free/nrw/commons/category/MethodAUpdater.java index 33835119e..9300f640d 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/MethodAUpdater.java +++ b/app/src/main/java/fr/free/nrw/commons/category/MethodAUpdater.java @@ -79,7 +79,7 @@ public class MethodAUpdater extends AsyncTask> { protected ArrayList 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 categories = new ArrayList<>(); diff --git a/app/src/main/java/fr/free/nrw/commons/category/PrefixUpdater.java b/app/src/main/java/fr/free/nrw/commons/category/PrefixUpdater.java index 350c0b21f..05770081d 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/PrefixUpdater.java +++ b/app/src/main/java/fr/free/nrw/commons/category/PrefixUpdater.java @@ -95,7 +95,7 @@ public class PrefixUpdater extends AsyncTask> { //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 categories = new ArrayList<>(); try { diff --git a/app/src/main/java/fr/free/nrw/commons/category/TitleCategories.java b/app/src/main/java/fr/free/nrw/commons/category/TitleCategories.java index a372b353d..6ca46be0f 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/TitleCategories.java +++ b/app/src/main/java/fr/free/nrw/commons/category/TitleCategories.java @@ -34,7 +34,7 @@ public class TitleCategories extends AsyncTask> { @Override protected ArrayList doInBackground(Void... voids) { - MWApi api = CommonsApplication.createMWApi(); + MWApi api = CommonsApplication.getInstance().getMWApi(); ApiResult result; ArrayList items = new ArrayList<>(); diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/Contribution.java b/app/src/main/java/fr/free/nrw/commons/contributions/Contribution.java index c39298810..e58be04e1 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/Contribution.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/Contribution.java @@ -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; diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsActivity.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsActivity.java index d9dd05b26..73b657329 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsActivity.java @@ -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 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, diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsContentProvider.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsContentProvider.java index 37d9dae18..838b9c922 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsContentProvider.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsContentProvider.java @@ -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; } diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.java index 364071e22..be83b3327 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.java @@ -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); } } diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsSyncAdapter.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsSyncAdapter.java index 81510e23b..52b966cfd 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsSyncAdapter.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsSyncAdapter.java @@ -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(); diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsSyncService.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsSyncService.java index 3af92935c..946da6915 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsSyncService.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsSyncService.java @@ -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); } } } diff --git a/app/src/main/java/fr/free/nrw/commons/data/DBOpenHelper.java b/app/src/main/java/fr/free/nrw/commons/data/DBOpenHelper.java index 58ab54a4d..22171857a 100644 --- a/app/src/main/java/fr/free/nrw/commons/data/DBOpenHelper.java +++ b/app/src/main/java/fr/free/nrw/commons/data/DBOpenHelper.java @@ -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); } diff --git a/app/src/main/java/fr/free/nrw/commons/hamburger/NavigationBaseFragment.java b/app/src/main/java/fr/free/nrw/commons/hamburger/NavigationBaseFragment.java index ddfc88d73..a77d672bf 100644 --- a/app/src/main/java/fr/free/nrw/commons/hamburger/NavigationBaseFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/hamburger/NavigationBaseFragment.java @@ -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); diff --git a/app/src/main/java/fr/free/nrw/commons/location/LatLng.java b/app/src/main/java/fr/free/nrw/commons/location/LatLng.java index 47d112991..acd67ebf7 100644 --- a/app/src/main/java/fr/free/nrw/commons/location/LatLng.java +++ b/app/src/main/java/fr/free/nrw/commons/location/LatLng.java @@ -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"; } diff --git a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java index 7cd168478..fc947e5fc 100644 --- a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java @@ -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(); } } diff --git a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java index 9adbdb882..c7e06cefa 100644 --- a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java @@ -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); } diff --git a/app/src/main/java/fr/free/nrw/commons/modifications/ModificationsContentProvider.java b/app/src/main/java/fr/free/nrw/commons/modifications/ModificationsContentProvider.java index baba01770..097651aaf 100644 --- a/app/src/main/java/fr/free/nrw/commons/modifications/ModificationsContentProvider.java +++ b/app/src/main/java/fr/free/nrw/commons/modifications/ModificationsContentProvider.java @@ -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; } diff --git a/app/src/main/java/fr/free/nrw/commons/modifications/ModificationsSyncAdapter.java b/app/src/main/java/fr/free/nrw/commons/modifications/ModificationsSyncAdapter.java index fdc9e28a5..898c41f7d 100644 --- a/app/src/main/java/fr/free/nrw/commons/modifications/ModificationsSyncAdapter.java +++ b/app/src/main/java/fr/free/nrw/commons/modifications/ModificationsSyncAdapter.java @@ -61,7 +61,7 @@ public class ModificationsSyncAdapter extends AbstractThreadedSyncAdapter { return; } - MWApi api = CommonsApplication.createMWApi(); + MWApi api = CommonsApplication.getInstance().getMWApi(); api.setAuthCookie(authCookie); String editToken; diff --git a/app/src/main/java/fr/free/nrw/commons/modifications/ModificationsSyncService.java b/app/src/main/java/fr/free/nrw/commons/modifications/ModificationsSyncService.java index b664aaaa4..bf6878622 100644 --- a/app/src/main/java/fr/free/nrw/commons/modifications/ModificationsSyncService.java +++ b/app/src/main/java/fr/free/nrw/commons/modifications/ModificationsSyncService.java @@ -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); } } } diff --git a/app/src/main/java/fr/free/nrw/commons/modifications/ModifierSequence.java b/app/src/main/java/fr/free/nrw/commons/modifications/ModifierSequence.java index b660c0a72..67152f85b 100644 --- a/app/src/main/java/fr/free/nrw/commons/modifications/ModifierSequence.java +++ b/app/src/main/java/fr/free/nrw/commons/modifications/ModifierSequence.java @@ -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); + } } } diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyActivity.java b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyActivity.java index 2446800d3..181643e79 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyActivity.java @@ -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 doInBackground(Void... params) { return NearbyController - .loadAttractionsFromLocation(curLatLang, getApplicationContext() + .loadAttractionsFromLocation(curLatLang, CommonsApplication.getInstance() ); } diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyController.java b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyController.java index e5fc3182c..0a290784a 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyController.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyController.java @@ -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 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 distances = new HashMap<>(); diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyListFragment.java b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyListFragment.java index 048f1d450..731ee0035 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyListFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyListFragment.java @@ -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 { diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyPlaces.java b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyPlaces.java index 152812fd2..3478e74e9 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyPlaces.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyPlaces.java @@ -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 places; - private NearbyPlaces(){ - } - List 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; - } } diff --git a/app/src/main/java/fr/free/nrw/commons/settings/SettingsFragment.java b/app/src/main/java/fr/free/nrw/commons/settings/SettingsFragment.java index 84c5e1e13..bdac4a0f5 100644 --- a/app/src/main/java/fr/free/nrw/commons/settings/SettingsFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/settings/SettingsFragment.java @@ -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 + ""); diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ExistingFileAsync.java b/app/src/main/java/fr/free/nrw/commons/upload/ExistingFileAsync.java index 14988c60c..27b9ed8d8 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ExistingFileAsync.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ExistingFileAsync.java @@ -38,7 +38,7 @@ public class ExistingFileAsync extends AsyncTask { @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 diff --git a/app/src/main/java/fr/free/nrw/commons/upload/FileUtils.java b/app/src/main/java/fr/free/nrw/commons/upload/FileUtils.java index 8edd68789..0c587d566 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/FileUtils.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/FileUtils.java @@ -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; diff --git a/app/src/main/java/fr/free/nrw/commons/upload/MultipleShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/MultipleShareActivity.java index 8c5e6b6d2..12b38a6ab 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/MultipleShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/MultipleShareActivity.java @@ -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)) { diff --git a/app/src/main/java/fr/free/nrw/commons/upload/MwVolleyApi.java b/app/src/main/java/fr/free/nrw/commons/upload/MwVolleyApi.java index 6544b4fff..367309593 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/MwVolleyApi.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/MwVolleyApi.java @@ -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; } diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index 08ddbe821..2353dd465 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -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 displayCatList = app.cacheData.findCategory(); + List 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 diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadController.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadController.java index 2bdee55f5..4266384c1 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadController.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadController.java @@ -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); diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadService.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadService.java index 523b6a479..853952fae 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadService.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadService.java @@ -115,7 +115,7 @@ public class UploadService extends HandlerService { 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 { } 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 { .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 { 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 { } 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) { diff --git a/app/src/main/java/fr/free/nrw/commons/utils/FileUtils.java b/app/src/main/java/fr/free/nrw/commons/utils/FileUtils.java index 158c13da0..3df50b31f 100644 --- a/app/src/main/java/fr/free/nrw/commons/utils/FileUtils.java +++ b/app/src/main/java/fr/free/nrw/commons/utils/FileUtils.java @@ -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; + } + + } diff --git a/app/src/main/res/drawable-hdpi/ic_exit_to_app_black_24dp.png b/app/src/main/res/drawable-hdpi/ic_exit_to_app_black_24dp.png new file mode 100644 index 000000000..ad0f63e5b Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_exit_to_app_black_24dp.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_feedback_black_24dp.png b/app/src/main/res/drawable-hdpi/ic_feedback_black_24dp.png new file mode 100644 index 000000000..f03168559 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_feedback_black_24dp.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_file_upload_black_24dp.png b/app/src/main/res/drawable-hdpi/ic_file_upload_black_24dp.png new file mode 100644 index 000000000..5e5b9fc4a Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_file_upload_black_24dp.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_info_outline_black_24dp.png b/app/src/main/res/drawable-hdpi/ic_info_outline_black_24dp.png new file mode 100644 index 000000000..4b5ab06e1 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_info_outline_black_24dp.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_location_on_black_24dp.png b/app/src/main/res/drawable-hdpi/ic_location_on_black_24dp.png new file mode 100644 index 000000000..df1f34062 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_location_on_black_24dp.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_settings_black_24dp.png b/app/src/main/res/drawable-hdpi/ic_settings_black_24dp.png new file mode 100644 index 000000000..acf1ddf85 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_settings_black_24dp.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_exit_to_app_black_24dp.png b/app/src/main/res/drawable-mdpi/ic_exit_to_app_black_24dp.png new file mode 100644 index 000000000..dee407b59 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_exit_to_app_black_24dp.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_feedback_black_24dp.png b/app/src/main/res/drawable-mdpi/ic_feedback_black_24dp.png new file mode 100644 index 000000000..8be6849cb Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_feedback_black_24dp.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_file_upload_black_24dp.png b/app/src/main/res/drawable-mdpi/ic_file_upload_black_24dp.png new file mode 100644 index 000000000..c5f2954bd Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_file_upload_black_24dp.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_info_outline_black_24dp.png b/app/src/main/res/drawable-mdpi/ic_info_outline_black_24dp.png new file mode 100644 index 000000000..e0c9fe0eb Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_info_outline_black_24dp.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_location_on_black_24dp.png b/app/src/main/res/drawable-mdpi/ic_location_on_black_24dp.png new file mode 100644 index 000000000..92a073827 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_location_on_black_24dp.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_settings_black_24dp.png b/app/src/main/res/drawable-mdpi/ic_settings_black_24dp.png new file mode 100644 index 000000000..c59419c02 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_settings_black_24dp.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_exit_to_app_black_24dp.png b/app/src/main/res/drawable-xhdpi/ic_exit_to_app_black_24dp.png new file mode 100644 index 000000000..5a536d57c Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_exit_to_app_black_24dp.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_feedback_black_24dp.png b/app/src/main/res/drawable-xhdpi/ic_feedback_black_24dp.png new file mode 100644 index 000000000..63bec2331 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_feedback_black_24dp.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_file_upload_black_24dp.png b/app/src/main/res/drawable-xhdpi/ic_file_upload_black_24dp.png new file mode 100644 index 000000000..41694c101 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_file_upload_black_24dp.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_info_outline_black_24dp.png b/app/src/main/res/drawable-xhdpi/ic_info_outline_black_24dp.png new file mode 100644 index 000000000..b706f0d06 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_info_outline_black_24dp.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_location_on_black_24dp.png b/app/src/main/res/drawable-xhdpi/ic_location_on_black_24dp.png new file mode 100644 index 000000000..b2696b6d4 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_location_on_black_24dp.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_settings_black_24dp.png b/app/src/main/res/drawable-xhdpi/ic_settings_black_24dp.png new file mode 100644 index 000000000..e84e188a1 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_settings_black_24dp.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_exit_to_app_black_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_exit_to_app_black_24dp.png new file mode 100644 index 000000000..53b79d4c7 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_exit_to_app_black_24dp.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_feedback_black_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_feedback_black_24dp.png new file mode 100644 index 000000000..e68ceb906 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_feedback_black_24dp.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_file_upload_black_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_file_upload_black_24dp.png new file mode 100644 index 000000000..bb5d0923b Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_file_upload_black_24dp.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_info_outline_black_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_info_outline_black_24dp.png new file mode 100644 index 000000000..3847a9fe7 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_info_outline_black_24dp.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_location_on_black_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_location_on_black_24dp.png new file mode 100644 index 000000000..5a21dfae6 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_location_on_black_24dp.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_settings_black_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_settings_black_24dp.png new file mode 100644 index 000000000..3023ff8da Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_settings_black_24dp.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_exit_to_app_black_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_exit_to_app_black_24dp.png new file mode 100644 index 000000000..e30632b6b Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_exit_to_app_black_24dp.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_feedback_black_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_feedback_black_24dp.png new file mode 100644 index 000000000..1343fa837 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_feedback_black_24dp.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_file_upload_black_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_file_upload_black_24dp.png new file mode 100644 index 000000000..9ce5b8a7b Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_file_upload_black_24dp.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_info_outline_black_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_info_outline_black_24dp.png new file mode 100644 index 000000000..c1e2a03a4 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_info_outline_black_24dp.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_location_on_black_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_location_on_black_24dp.png new file mode 100644 index 000000000..7c2217e46 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_location_on_black_24dp.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_settings_black_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_settings_black_24dp.png new file mode 100644 index 000000000..476d5c978 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_settings_black_24dp.png differ diff --git a/app/src/main/res/drawable/hamburger_item_bg.xml b/app/src/main/res/drawable/hamburger_item_bg.xml new file mode 100644 index 000000000..cdd545602 --- /dev/null +++ b/app/src/main/res/drawable/hamburger_item_bg.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/round_icon_unknown.xml b/app/src/main/res/drawable/round_icon_unknown.xml index ba0f2e52b..33905d93b 100644 --- a/app/src/main/res/drawable/round_icon_unknown.xml +++ b/app/src/main/res/drawable/round_icon_unknown.xml @@ -1,7 +1,5 @@ - - - - - + + + diff --git a/app/src/main/res/layout/light_simple_spinner_dropdown_item.xml b/app/src/main/res/layout/light_simple_spinner_dropdown_item.xml index 11f95bf2e..be4e086e4 100644 --- a/app/src/main/res/layout/light_simple_spinner_dropdown_item.xml +++ b/app/src/main/res/layout/light_simple_spinner_dropdown_item.xml @@ -2,7 +2,7 @@ - + android:layout_height="52dp" + android:background="@drawable/hamburger_item_bg" + android:gravity="center_vertical" + android:orientation="horizontal"> - + + + + + + android:layout_height="52dp" + android:background="@drawable/hamburger_item_bg" + android:gravity="center_vertical" + android:orientation="horizontal"> - + + + + + + android:layout_height="52dp" + android:background="@drawable/hamburger_item_bg" + android:gravity="center_vertical" + android:orientation="horizontal"> - + + + + + + android:layout_height="52dp" + android:background="@drawable/hamburger_item_bg" + android:gravity="center_vertical" + android:orientation="horizontal"> - + + + + + + android:layout_height="52dp" + android:background="@drawable/hamburger_item_bg" + android:gravity="center_vertical" + android:orientation="horizontal"> - + + + + + + android:layout_height="52dp" + android:background="@drawable/hamburger_item_bg" + android:gravity="center_vertical" + android:orientation="horizontal"> + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/welcome_do_upload.xml b/app/src/main/res/layout/welcome_do_upload.xml index 74d62ec8a..26b945461 100644 --- a/app/src/main/res/layout/welcome_do_upload.xml +++ b/app/src/main/res/layout/welcome_do_upload.xml @@ -1,6 +1,5 @@ #77000000 #44000000 + #f5f5f5 diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index d48714b16..2194587a7 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -3,4 +3,5 @@ 4dp 8dp 240dp + 18sp diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c38710449..5930cbc36 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -183,6 +183,7 @@ Tap this message (or hit back) to skip this step. Unable to display more than 500 Set Recent Upload Limit Two factor authentication is currently not supported. + Do you really want to logout? Cancel Open diff --git a/app/src/test/java/fr/free/nrw/commons/LatLngTests.java b/app/src/test/java/fr/free/nrw/commons/LatLngTests.java new file mode 100644 index 000000000..5267a48bd --- /dev/null +++ b/app/src/test/java/fr/free/nrw/commons/LatLngTests.java @@ -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")); + } +}