mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-26 20:33:53 +01:00
Merge branch 'main' into added-button
This commit is contained in:
commit
5c05e7d0f6
137 changed files with 1920 additions and 1420 deletions
|
|
@ -1,7 +1,9 @@
|
|||
package fr.free.nrw.commons
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Intent
|
||||
import android.content.Intent.ACTION_VIEW
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
|
|
@ -16,6 +18,9 @@ import fr.free.nrw.commons.theme.BaseActivity
|
|||
import fr.free.nrw.commons.utils.ConfigUtils.getVersionNameWithSha
|
||||
import fr.free.nrw.commons.utils.DialogUtil.showAlertDialog
|
||||
import java.util.Collections
|
||||
import androidx.core.net.toUri
|
||||
import fr.free.nrw.commons.utils.handleWebUrl
|
||||
import fr.free.nrw.commons.utils.setUnderlinedText
|
||||
|
||||
/**
|
||||
* Represents about screen of this app
|
||||
|
|
@ -59,30 +64,12 @@ class AboutActivity : BaseActivity() {
|
|||
binding!!.aboutImprove.setHtmlText(improveText)
|
||||
binding!!.aboutVersion.text = applicationContext.getVersionNameWithSha()
|
||||
|
||||
Utils.setUnderlinedText(
|
||||
binding!!.aboutFaq, R.string.about_faq,
|
||||
applicationContext
|
||||
)
|
||||
Utils.setUnderlinedText(
|
||||
binding!!.aboutRateUs, R.string.about_rate_us,
|
||||
applicationContext
|
||||
)
|
||||
Utils.setUnderlinedText(
|
||||
binding!!.aboutUserGuide, R.string.user_guide,
|
||||
applicationContext
|
||||
)
|
||||
Utils.setUnderlinedText(
|
||||
binding!!.aboutPrivacyPolicy, R.string.about_privacy_policy,
|
||||
applicationContext
|
||||
)
|
||||
Utils.setUnderlinedText(
|
||||
binding!!.aboutTranslate, R.string.about_translate,
|
||||
applicationContext
|
||||
)
|
||||
Utils.setUnderlinedText(
|
||||
binding!!.aboutCredits, R.string.about_credits,
|
||||
applicationContext
|
||||
)
|
||||
binding!!.aboutFaq.setUnderlinedText(R.string.about_faq)
|
||||
binding!!.aboutRateUs.setUnderlinedText(R.string.about_rate_us)
|
||||
binding!!.aboutUserGuide.setUnderlinedText(R.string.user_guide)
|
||||
binding!!.aboutPrivacyPolicy.setUnderlinedText(R.string.about_privacy_policy)
|
||||
binding!!.aboutTranslate.setUnderlinedText(R.string.about_translate)
|
||||
binding!!.aboutCredits.setUnderlinedText(R.string.about_credits)
|
||||
|
||||
/*
|
||||
To set listeners, we can create a separate method and use lambda syntax.
|
||||
|
|
@ -106,47 +93,56 @@ class AboutActivity : BaseActivity() {
|
|||
fun launchFacebook(view: View?) {
|
||||
val intent: Intent
|
||||
try {
|
||||
intent = Intent(Intent.ACTION_VIEW, Uri.parse(Urls.FACEBOOK_APP_URL))
|
||||
intent = Intent(ACTION_VIEW, Urls.FACEBOOK_APP_URL.toUri())
|
||||
intent.setPackage(Urls.FACEBOOK_PACKAGE_NAME)
|
||||
startActivity(intent)
|
||||
} catch (e: Exception) {
|
||||
Utils.handleWebUrl(this, Uri.parse(Urls.FACEBOOK_WEB_URL))
|
||||
handleWebUrl(this, Urls.FACEBOOK_WEB_URL.toUri())
|
||||
}
|
||||
}
|
||||
|
||||
fun launchGithub(view: View?) {
|
||||
val intent: Intent
|
||||
try {
|
||||
intent = Intent(Intent.ACTION_VIEW, Uri.parse(Urls.GITHUB_REPO_URL))
|
||||
intent = Intent(ACTION_VIEW, Urls.GITHUB_REPO_URL.toUri())
|
||||
intent.setPackage(Urls.GITHUB_PACKAGE_NAME)
|
||||
startActivity(intent)
|
||||
} catch (e: Exception) {
|
||||
Utils.handleWebUrl(this, Uri.parse(Urls.GITHUB_REPO_URL))
|
||||
handleWebUrl(this, Urls.GITHUB_REPO_URL.toUri())
|
||||
}
|
||||
}
|
||||
|
||||
fun launchWebsite(view: View?) {
|
||||
Utils.handleWebUrl(this, Uri.parse(Urls.WEBSITE_URL))
|
||||
handleWebUrl(this, Urls.WEBSITE_URL.toUri())
|
||||
}
|
||||
|
||||
fun launchRatings(view: View?) {
|
||||
Utils.rateApp(this)
|
||||
try {
|
||||
startActivity(
|
||||
Intent(
|
||||
ACTION_VIEW,
|
||||
(Urls.PLAY_STORE_PREFIX + packageName).toUri()
|
||||
)
|
||||
)
|
||||
} catch (_: ActivityNotFoundException) {
|
||||
handleWebUrl(this, (Urls.PLAY_STORE_URL_PREFIX + packageName).toUri())
|
||||
}
|
||||
}
|
||||
|
||||
fun launchCredits(view: View?) {
|
||||
Utils.handleWebUrl(this, Uri.parse(Urls.CREDITS_URL))
|
||||
handleWebUrl(this, Urls.CREDITS_URL.toUri())
|
||||
}
|
||||
|
||||
fun launchUserGuide(view: View?) {
|
||||
Utils.handleWebUrl(this, Uri.parse(Urls.USER_GUIDE_URL))
|
||||
handleWebUrl(this, Urls.USER_GUIDE_URL.toUri())
|
||||
}
|
||||
|
||||
fun launchPrivacyPolicy(view: View?) {
|
||||
Utils.handleWebUrl(this, Uri.parse(BuildConfig.PRIVACY_POLICY_URL))
|
||||
handleWebUrl(this, BuildConfig.PRIVACY_POLICY_URL.toUri())
|
||||
}
|
||||
|
||||
fun launchFrequentlyAskedQuesions(view: View?) {
|
||||
Utils.handleWebUrl(this, Uri.parse(Urls.FAQ_URL))
|
||||
handleWebUrl(this, Urls.FAQ_URL.toUri())
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
|
|
@ -193,7 +189,7 @@ class AboutActivity : BaseActivity() {
|
|||
|
||||
val positiveButtonRunnable = Runnable {
|
||||
val langCode = instance.languageLookUpTable!!.getCodes()[spinner.selectedItemPosition]
|
||||
Utils.handleWebUrl(this@AboutActivity, Uri.parse(Urls.TRANSLATE_WIKI_URL + langCode))
|
||||
handleWebUrl(this@AboutActivity, (Urls.TRANSLATE_WIKI_URL + langCode).toUri())
|
||||
}
|
||||
showAlertDialog(
|
||||
this,
|
||||
|
|
|
|||
|
|
@ -1,18 +0,0 @@
|
|||
package fr.free.nrw.commons;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
/**
|
||||
* Base presenter, enforcing contracts to atach and detach view
|
||||
*/
|
||||
public interface BasePresenter<T> {
|
||||
/**
|
||||
* Until a view is attached, it is open to listen events from the presenter
|
||||
*/
|
||||
void onAttachView(@NonNull T view);
|
||||
|
||||
/**
|
||||
* Detaching a view makes sure that the view no more receives events from the presenter
|
||||
*/
|
||||
void onDetachView();
|
||||
}
|
||||
10
app/src/main/java/fr/free/nrw/commons/BasePresenter.kt
Normal file
10
app/src/main/java/fr/free/nrw/commons/BasePresenter.kt
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
package fr.free.nrw.commons
|
||||
|
||||
/**
|
||||
* Base presenter, enforcing contracts to attach and detach view
|
||||
*/
|
||||
interface BasePresenter<T> {
|
||||
fun onAttachView(view: T)
|
||||
|
||||
fun onDetachView()
|
||||
}
|
||||
|
|
@ -1,79 +0,0 @@
|
|||
package fr.free.nrw.commons;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* represents Licence object
|
||||
*/
|
||||
public class License {
|
||||
private String key;
|
||||
private String template;
|
||||
private String url;
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* Constructs a new instance of License.
|
||||
*
|
||||
* @param key license key
|
||||
* @param template license template
|
||||
* @param url license URL
|
||||
* @param name licence name
|
||||
*
|
||||
* @throws RuntimeException if License.key or Licence.template is null
|
||||
*/
|
||||
public License(String key, String template, String url, String name) {
|
||||
if (key == null) {
|
||||
throw new RuntimeException("License.key must not be null");
|
||||
}
|
||||
if (template == null) {
|
||||
throw new RuntimeException("License.template must not be null");
|
||||
}
|
||||
this.key = key;
|
||||
this.template = template;
|
||||
this.url = url;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the license key.
|
||||
* @return license key as a String.
|
||||
*/
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the license template.
|
||||
* @return license template as a String.
|
||||
*/
|
||||
public String getTemplate() {
|
||||
return template;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the license name. If name is null, return license key.
|
||||
* @return license name as string. if name null, license key as String
|
||||
*/
|
||||
public String getName() {
|
||||
if (name == null) {
|
||||
// hack
|
||||
return getKey();
|
||||
} else {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the license URL
|
||||
*
|
||||
* @param language license language
|
||||
* @return URL
|
||||
*/
|
||||
public @Nullable String getUrl(String language) {
|
||||
if (url == null) {
|
||||
return null;
|
||||
} else {
|
||||
return url.replace("$lang", language);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
package fr.free.nrw.commons;
|
||||
|
||||
import fr.free.nrw.commons.location.LatLng;
|
||||
import fr.free.nrw.commons.nearby.Place;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class MapController {
|
||||
|
||||
/**
|
||||
* We pass this variable as a group of placeList and boundaryCoordinates
|
||||
*/
|
||||
public class NearbyPlacesInfo {
|
||||
public List<Place> placeList; // List of nearby places
|
||||
public LatLng[] boundaryCoordinates; // Corners of nearby area
|
||||
public LatLng currentLatLng; // Current location when this places are populated
|
||||
public LatLng searchLatLng; // Search location for finding this places
|
||||
public List<Media> mediaList; // Search location for finding this places
|
||||
}
|
||||
|
||||
/**
|
||||
* We pass this variable as a group of placeList and boundaryCoordinates
|
||||
*/
|
||||
public class ExplorePlacesInfo {
|
||||
public List<Place> explorePlaceList; // List of nearby places
|
||||
public LatLng[] boundaryCoordinates; // Corners of nearby area
|
||||
public LatLng currentLatLng; // Current location when this places are populated
|
||||
public LatLng searchLatLng; // Search location for finding this places
|
||||
public List<Media> mediaList; // Search location for finding this places
|
||||
}
|
||||
}
|
||||
46
app/src/main/java/fr/free/nrw/commons/MapController.kt
Normal file
46
app/src/main/java/fr/free/nrw/commons/MapController.kt
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
package fr.free.nrw.commons
|
||||
|
||||
import fr.free.nrw.commons.location.LatLng
|
||||
import fr.free.nrw.commons.nearby.Place
|
||||
|
||||
abstract class MapController {
|
||||
/**
|
||||
* We pass this variable as a group of placeList and boundaryCoordinates
|
||||
*/
|
||||
inner class NearbyPlacesInfo {
|
||||
@JvmField
|
||||
var placeList: List<Place> = emptyList() // List of nearby places
|
||||
|
||||
@JvmField
|
||||
var boundaryCoordinates: Array<LatLng> = emptyArray() // Corners of nearby area
|
||||
|
||||
@JvmField
|
||||
var currentLatLng: LatLng? = null // Current location when this places are populated
|
||||
|
||||
@JvmField
|
||||
var searchLatLng: LatLng? = null // Search location for finding this places
|
||||
|
||||
@JvmField
|
||||
var mediaList: List<Media>? = null // Search location for finding this places
|
||||
}
|
||||
|
||||
/**
|
||||
* We pass this variable as a group of placeList and boundaryCoordinates
|
||||
*/
|
||||
inner class ExplorePlacesInfo {
|
||||
@JvmField
|
||||
var explorePlaceList: List<Place> = emptyList() // List of nearby places
|
||||
|
||||
@JvmField
|
||||
var boundaryCoordinates: Array<LatLng> = emptyArray() // Corners of nearby area
|
||||
|
||||
@JvmField
|
||||
var currentLatLng: LatLng? = null // Current location when this places are populated
|
||||
|
||||
@JvmField
|
||||
var searchLatLng: LatLng? = null // Search location for finding this places
|
||||
|
||||
@JvmField
|
||||
var mediaList: List<Media> = emptyList() // Search location for finding this places
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,9 @@
|
|||
package fr.free.nrw.commons
|
||||
|
||||
import android.os.Parcelable
|
||||
import fr.free.nrw.commons.BuildConfig.COMMONS_URL
|
||||
import fr.free.nrw.commons.location.LatLng
|
||||
import fr.free.nrw.commons.wikidata.model.WikiSite
|
||||
import fr.free.nrw.commons.wikidata.model.page.PageTitle
|
||||
import kotlinx.parcelize.IgnoredOnParcel
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
|
@ -173,7 +175,8 @@ class Media constructor(
|
|||
* Gets file page title
|
||||
* @return New media page title
|
||||
*/
|
||||
val pageTitle: PageTitle get() = Utils.getPageTitle(filename!!)
|
||||
val pageTitle: PageTitle
|
||||
get() = PageTitle(filename!!, WikiSite(COMMONS_URL))
|
||||
|
||||
/**
|
||||
* Returns wikicode to use the media file on a MediaWiki site
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
package fr.free.nrw.commons;
|
||||
|
||||
/**
|
||||
* Base interface for all the views
|
||||
*/
|
||||
public interface MvpView {
|
||||
void showMessage(String message);
|
||||
}
|
||||
|
|
@ -1,154 +0,0 @@
|
|||
package fr.free.nrw.commons;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import fr.free.nrw.commons.wikidata.cookies.CommonsCookieJar;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import okhttp3.Cache;
|
||||
import okhttp3.Interceptor;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import okhttp3.ResponseBody;
|
||||
import okhttp3.logging.HttpLoggingInterceptor;
|
||||
import okhttp3.logging.HttpLoggingInterceptor.Level;
|
||||
import timber.log.Timber;
|
||||
|
||||
public final class OkHttpConnectionFactory {
|
||||
private static final String CACHE_DIR_NAME = "okhttp-cache";
|
||||
private static final long NET_CACHE_SIZE = 64 * 1024 * 1024;
|
||||
|
||||
public static OkHttpClient CLIENT;
|
||||
|
||||
@NonNull public static OkHttpClient getClient(final CommonsCookieJar cookieJar) {
|
||||
if (CLIENT == null) {
|
||||
CLIENT = createClient(cookieJar);
|
||||
}
|
||||
return CLIENT;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static OkHttpClient createClient(final CommonsCookieJar cookieJar) {
|
||||
return new OkHttpClient.Builder()
|
||||
.cookieJar(cookieJar)
|
||||
.cache((CommonsApplication.getInstance()!=null) ? new Cache(new File(CommonsApplication.getInstance().getCacheDir(), CACHE_DIR_NAME), NET_CACHE_SIZE) : null)
|
||||
.connectTimeout(120, TimeUnit.SECONDS)
|
||||
.writeTimeout(120, TimeUnit.SECONDS)
|
||||
.readTimeout(120, TimeUnit.SECONDS)
|
||||
.addInterceptor(getLoggingInterceptor())
|
||||
.addInterceptor(new UnsuccessfulResponseInterceptor())
|
||||
.addInterceptor(new CommonHeaderRequestInterceptor())
|
||||
.build();
|
||||
}
|
||||
|
||||
private static HttpLoggingInterceptor getLoggingInterceptor() {
|
||||
final HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor()
|
||||
.setLevel(Level.BASIC);
|
||||
|
||||
httpLoggingInterceptor.redactHeader("Authorization");
|
||||
httpLoggingInterceptor.redactHeader("Cookie");
|
||||
|
||||
return httpLoggingInterceptor;
|
||||
}
|
||||
|
||||
private static class CommonHeaderRequestInterceptor implements Interceptor {
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public Response intercept(@NonNull final Chain chain) throws IOException {
|
||||
final Request request = chain.request().newBuilder()
|
||||
.header("User-Agent", CommonsApplication.getInstance().getUserAgent())
|
||||
.build();
|
||||
return chain.proceed(request);
|
||||
}
|
||||
}
|
||||
|
||||
public static class UnsuccessfulResponseInterceptor implements Interceptor {
|
||||
private static final String SUPPRESS_ERROR_LOG = "x-commons-suppress-error-log";
|
||||
public static final String SUPPRESS_ERROR_LOG_HEADER = SUPPRESS_ERROR_LOG+": true";
|
||||
private static final List<String> DO_NOT_INTERCEPT = Collections.singletonList(
|
||||
"api.php?format=json&formatversion=2&errorformat=plaintext&action=upload&ignorewarnings=1");
|
||||
|
||||
private static final String ERRORS_PREFIX = "{\"error";
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public Response intercept(@NonNull final Chain chain) throws IOException {
|
||||
final Request rq = chain.request();
|
||||
|
||||
// If the request contains our special "suppress errors" header, make note of it
|
||||
// but don't pass that on to the server.
|
||||
final boolean suppressErrors = rq.headers().names().contains(SUPPRESS_ERROR_LOG);
|
||||
final Request request = rq.newBuilder()
|
||||
.removeHeader(SUPPRESS_ERROR_LOG)
|
||||
.build();
|
||||
|
||||
final Response rsp = chain.proceed(request);
|
||||
|
||||
// Do not intercept certain requests and let the caller handle the errors
|
||||
if(isExcludedUrl(chain.request())) {
|
||||
return rsp;
|
||||
}
|
||||
if (rsp.isSuccessful()) {
|
||||
try (final ResponseBody responseBody = rsp.peekBody(ERRORS_PREFIX.length())) {
|
||||
if (ERRORS_PREFIX.equals(responseBody.string())) {
|
||||
try (final ResponseBody body = rsp.body()) {
|
||||
throw new IOException(body.string());
|
||||
}
|
||||
}
|
||||
} catch (final IOException e) {
|
||||
// Log the error as debug (and therefore, "expected") or at error level
|
||||
if (suppressErrors) {
|
||||
Timber.d(e, "Suppressed (known / expected) error");
|
||||
} else {
|
||||
Timber.e(e);
|
||||
}
|
||||
}
|
||||
return rsp;
|
||||
}
|
||||
throw new HttpStatusException(rsp);
|
||||
}
|
||||
|
||||
private boolean isExcludedUrl(final Request request) {
|
||||
final String requestUrl = request.url().toString();
|
||||
for(final String url: DO_NOT_INTERCEPT) {
|
||||
if(requestUrl.contains(url)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private OkHttpConnectionFactory() {
|
||||
}
|
||||
|
||||
public static class HttpStatusException extends IOException {
|
||||
private final int code;
|
||||
private final String url;
|
||||
public HttpStatusException(@NonNull Response rsp) {
|
||||
this.code = rsp.code();
|
||||
this.url = rsp.request().url().uri().toString();
|
||||
try {
|
||||
if (rsp.body() != null && rsp.body().contentType() != null
|
||||
&& rsp.body().contentType().toString().contains("json")) {
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Log?
|
||||
}
|
||||
}
|
||||
|
||||
public int code() {
|
||||
return code;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
String str = "Code: " + code + ", URL: " + url;
|
||||
return str;
|
||||
}
|
||||
}
|
||||
}
|
||||
122
app/src/main/java/fr/free/nrw/commons/OkHttpConnectionFactory.kt
Normal file
122
app/src/main/java/fr/free/nrw/commons/OkHttpConnectionFactory.kt
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
package fr.free.nrw.commons
|
||||
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import fr.free.nrw.commons.wikidata.cookies.CommonsCookieJar
|
||||
import okhttp3.Cache
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import okhttp3.logging.HttpLoggingInterceptor
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
object OkHttpConnectionFactory {
|
||||
private const val CACHE_DIR_NAME = "okhttp-cache"
|
||||
private const val NET_CACHE_SIZE = (64 * 1024 * 1024).toLong()
|
||||
|
||||
@VisibleForTesting
|
||||
var CLIENT: OkHttpClient? = null
|
||||
|
||||
fun getClient(cookieJar: CommonsCookieJar): OkHttpClient {
|
||||
if (CLIENT == null) {
|
||||
CLIENT = createClient(cookieJar)
|
||||
}
|
||||
return CLIENT!!
|
||||
}
|
||||
|
||||
private fun createClient(cookieJar: CommonsCookieJar): OkHttpClient {
|
||||
return OkHttpClient.Builder()
|
||||
.cookieJar(cookieJar)
|
||||
.cache(
|
||||
if (CommonsApplication.instance != null) Cache(
|
||||
File(CommonsApplication.instance.cacheDir, CACHE_DIR_NAME),
|
||||
NET_CACHE_SIZE
|
||||
) else null
|
||||
)
|
||||
.connectTimeout(120, TimeUnit.SECONDS)
|
||||
.writeTimeout(120, TimeUnit.SECONDS)
|
||||
.readTimeout(120, TimeUnit.SECONDS)
|
||||
.addInterceptor(HttpLoggingInterceptor().apply {
|
||||
setLevel(HttpLoggingInterceptor.Level.BASIC)
|
||||
redactHeader("Authorization")
|
||||
redactHeader("Cookie")
|
||||
})
|
||||
.addInterceptor(UnsuccessfulResponseInterceptor())
|
||||
.addInterceptor(CommonHeaderRequestInterceptor())
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
private class CommonHeaderRequestInterceptor : Interceptor {
|
||||
@Throws(IOException::class)
|
||||
override fun intercept(chain: Interceptor.Chain): Response {
|
||||
val request = chain.request().newBuilder()
|
||||
.header("User-Agent", CommonsApplication.instance.userAgent)
|
||||
.build()
|
||||
return chain.proceed(request)
|
||||
}
|
||||
}
|
||||
|
||||
private const val SUPPRESS_ERROR_LOG = "x-commons-suppress-error-log"
|
||||
const val SUPPRESS_ERROR_LOG_HEADER: String = "$SUPPRESS_ERROR_LOG: true"
|
||||
|
||||
private class UnsuccessfulResponseInterceptor : Interceptor {
|
||||
@Throws(IOException::class)
|
||||
override fun intercept(chain: Interceptor.Chain): Response {
|
||||
val rq = chain.request()
|
||||
|
||||
// If the request contains our special "suppress errors" header, make note of it
|
||||
// but don't pass that on to the server.
|
||||
val suppressErrors = rq.headers.names().contains(SUPPRESS_ERROR_LOG)
|
||||
val request = rq.newBuilder()
|
||||
.removeHeader(SUPPRESS_ERROR_LOG)
|
||||
.build()
|
||||
|
||||
val rsp = chain.proceed(request)
|
||||
|
||||
// Do not intercept certain requests and let the caller handle the errors
|
||||
if (isExcludedUrl(chain.request())) {
|
||||
return rsp
|
||||
}
|
||||
if (rsp.isSuccessful) {
|
||||
try {
|
||||
rsp.peekBody(ERRORS_PREFIX.length.toLong()).use { responseBody ->
|
||||
if (ERRORS_PREFIX == responseBody.string()) {
|
||||
rsp.body.use { body ->
|
||||
throw IOException(body!!.string())
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
// Log the error as debug (and therefore, "expected") or at error level
|
||||
if (suppressErrors) {
|
||||
Timber.d(e, "Suppressed (known / expected) error")
|
||||
} else {
|
||||
Timber.e(e)
|
||||
}
|
||||
}
|
||||
return rsp
|
||||
}
|
||||
throw IOException("Unsuccessful response")
|
||||
}
|
||||
|
||||
private fun isExcludedUrl(request: Request): Boolean {
|
||||
val requestUrl = request.url.toString()
|
||||
for (url in DO_NOT_INTERCEPT) {
|
||||
if (requestUrl.contains(url)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
companion object {
|
||||
val DO_NOT_INTERCEPT = listOf(
|
||||
"api.php?format=json&formatversion=2&errorformat=plaintext&action=upload&ignorewarnings=1"
|
||||
)
|
||||
const val ERRORS_PREFIX = "{\"error"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,264 +0,0 @@
|
|||
package fr.free.nrw.commons;
|
||||
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
import android.text.SpannableString;
|
||||
import android.text.style.UnderlineSpan;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.browser.customtabs.CustomTabColorSchemeParams;
|
||||
import androidx.browser.customtabs.CustomTabsIntent;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import fr.free.nrw.commons.wikidata.model.WikiSite;
|
||||
import fr.free.nrw.commons.wikidata.model.page.PageTitle;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import fr.free.nrw.commons.location.LatLng;
|
||||
import fr.free.nrw.commons.settings.Prefs;
|
||||
import fr.free.nrw.commons.utils.ViewUtil;
|
||||
import timber.log.Timber;
|
||||
|
||||
public class Utils {
|
||||
|
||||
public static PageTitle getPageTitle(@NonNull String title) {
|
||||
return new PageTitle(title, new WikiSite(BuildConfig.COMMONS_URL));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates licence name with given ID
|
||||
* @param license License ID
|
||||
* @return Name of license
|
||||
*/
|
||||
public static int licenseNameFor(String license) {
|
||||
switch (license) {
|
||||
case Prefs.Licenses.CC_BY_3:
|
||||
return R.string.license_name_cc_by;
|
||||
case Prefs.Licenses.CC_BY_4:
|
||||
return R.string.license_name_cc_by_four;
|
||||
case Prefs.Licenses.CC_BY_SA_3:
|
||||
return R.string.license_name_cc_by_sa;
|
||||
case Prefs.Licenses.CC_BY_SA_4:
|
||||
return R.string.license_name_cc_by_sa_four;
|
||||
case Prefs.Licenses.CC0:
|
||||
return R.string.license_name_cc0;
|
||||
}
|
||||
throw new IllegalStateException("Unrecognized license value: " + license);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates license url with given ID
|
||||
* @param license License ID
|
||||
* @return Url of license
|
||||
*/
|
||||
|
||||
|
||||
@NonNull
|
||||
public static String licenseUrlFor(String license) {
|
||||
switch (license) {
|
||||
case Prefs.Licenses.CC_BY_3:
|
||||
return "https://creativecommons.org/licenses/by/3.0/";
|
||||
case Prefs.Licenses.CC_BY_4:
|
||||
return "https://creativecommons.org/licenses/by/4.0/";
|
||||
case Prefs.Licenses.CC_BY_SA_3:
|
||||
return "https://creativecommons.org/licenses/by-sa/3.0/";
|
||||
case Prefs.Licenses.CC_BY_SA_4:
|
||||
return "https://creativecommons.org/licenses/by-sa/4.0/";
|
||||
case Prefs.Licenses.CC0:
|
||||
return "https://creativecommons.org/publicdomain/zero/1.0/";
|
||||
default:
|
||||
throw new IllegalStateException("Unrecognized license value: " + license);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds extension to filename. Converts to .jpg if system provides .jpeg, adds .jpg if no extension detected
|
||||
* @param title File name
|
||||
* @param extension Correct extension
|
||||
* @return File with correct extension
|
||||
*/
|
||||
public static String fixExtension(String title, String extension) {
|
||||
Pattern jpegPattern = Pattern.compile("\\.jpeg$", Pattern.CASE_INSENSITIVE);
|
||||
|
||||
// People are used to ".jpg" more than ".jpeg" which the system gives us.
|
||||
if (extension != null && extension.toLowerCase(Locale.ENGLISH).equals("jpeg")) {
|
||||
extension = "jpg";
|
||||
}
|
||||
title = jpegPattern.matcher(title).replaceFirst(".jpg");
|
||||
if (extension != null && !title.toLowerCase(Locale.getDefault())
|
||||
.endsWith("." + extension.toLowerCase(Locale.ENGLISH))) {
|
||||
title += "." + extension;
|
||||
}
|
||||
|
||||
// If extension is still null, make it jpg. (Hotfix for https://github.com/commons-app/apps-android-commons/issues/228)
|
||||
// If title has an extension in it, if won't be true
|
||||
if (extension == null && title.lastIndexOf(".")<=0) {
|
||||
extension = "jpg";
|
||||
title += "." + extension;
|
||||
}
|
||||
|
||||
return title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Launches intent to rate app
|
||||
* @param context
|
||||
*/
|
||||
public static void rateApp(Context context) {
|
||||
final String appPackageName = context.getPackageName();
|
||||
try {
|
||||
context.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(Urls.PLAY_STORE_PREFIX + appPackageName)));
|
||||
}
|
||||
catch (android.content.ActivityNotFoundException anfe) {
|
||||
handleWebUrl(context, Uri.parse(Urls.PLAY_STORE_URL_PREFIX + appPackageName));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens Custom Tab Activity with in-app browser for the specified URL.
|
||||
* Launches intent for web URL
|
||||
* @param context
|
||||
* @param url
|
||||
*/
|
||||
public static void handleWebUrl(Context context, Uri url) {
|
||||
Timber.d("Launching web url %s", url.toString());
|
||||
|
||||
final CustomTabColorSchemeParams color = new CustomTabColorSchemeParams.Builder()
|
||||
.setToolbarColor(ContextCompat.getColor(context, R.color.primaryColor))
|
||||
.setSecondaryToolbarColor(ContextCompat.getColor(context, R.color.primaryDarkColor))
|
||||
.build();
|
||||
|
||||
CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();
|
||||
builder.setDefaultColorSchemeParams(color);
|
||||
builder.setExitAnimations(context, android.R.anim.slide_in_left, android.R.anim.slide_out_right);
|
||||
CustomTabsIntent customTabsIntent = builder.build();
|
||||
// Clear previous browser tasks, so that back/exit buttons work as intended.
|
||||
customTabsIntent.intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||
customTabsIntent.launchUrl(context, url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Util function to handle geo coordinates. It no longer depends on google maps and any app
|
||||
* capable of handling the map intent can handle it
|
||||
*
|
||||
* @param context The context for launching intent
|
||||
* @param latLng The latitude and longitude of the location
|
||||
*/
|
||||
public static void handleGeoCoordinates(final Context context, final LatLng latLng) {
|
||||
handleGeoCoordinates(context, latLng, 16);
|
||||
}
|
||||
|
||||
/**
|
||||
* Util function to handle geo coordinates with specified zoom level. It no longer depends on
|
||||
* google maps and any app capable of handling the map intent can handle it
|
||||
*
|
||||
* @param context The context for launching intent
|
||||
* @param latLng The latitude and longitude of the location
|
||||
* @param zoomLevel The zoom level
|
||||
*/
|
||||
public static void handleGeoCoordinates(final Context context, final LatLng latLng,
|
||||
final double zoomLevel) {
|
||||
final Intent mapIntent = new Intent(Intent.ACTION_VIEW, latLng.getGmmIntentUri(zoomLevel));
|
||||
if (mapIntent.resolveActivity(context.getPackageManager()) != null) {
|
||||
context.startActivity(mapIntent);
|
||||
} else {
|
||||
ViewUtil.showShortToast(context, context.getString(R.string.map_application_missing));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* To take screenshot of the screen and return it in Bitmap format
|
||||
*
|
||||
* @param view
|
||||
* @return
|
||||
*/
|
||||
public static Bitmap getScreenShot(View view) {
|
||||
View screenView = view.getRootView();
|
||||
screenView.setDrawingCacheEnabled(true);
|
||||
Bitmap drawingCache = screenView.getDrawingCache();
|
||||
if (drawingCache != null) {
|
||||
Bitmap bitmap = Bitmap.createBitmap(drawingCache);
|
||||
screenView.setDrawingCacheEnabled(false);
|
||||
return bitmap;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
*Copies the content to the clipboard
|
||||
*
|
||||
*/
|
||||
public static void copy(String label,String text, Context context){
|
||||
ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
ClipData clip = ClipData.newPlainText(label, text);
|
||||
clipboard.setPrimaryClip(clip);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method sets underlined string text to a TextView
|
||||
*
|
||||
* @param textView TextView associated with string resource
|
||||
* @param stringResourceName string resource name
|
||||
* @param context
|
||||
*/
|
||||
public static void setUnderlinedText(TextView textView, int stringResourceName, Context context) {
|
||||
SpannableString content = new SpannableString(context.getString(stringResourceName));
|
||||
content.setSpan(new UnderlineSpan(), 0, content.length(), 0);
|
||||
textView.setText(content);
|
||||
}
|
||||
|
||||
/**
|
||||
* For now we are enabling the monuments only when the date lies between 1 Sept & 31 OCt
|
||||
* @param date
|
||||
* @return
|
||||
*/
|
||||
public static boolean isMonumentsEnabled(final Date date) {
|
||||
if (date.getMonth() == 8) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Util function to get the start date of wlm monument
|
||||
* For this release we are hardcoding it to be 1st September
|
||||
* @return
|
||||
*/
|
||||
public static String getWLMStartDate() {
|
||||
return "1 Sep";
|
||||
}
|
||||
|
||||
/***
|
||||
* Util function to get the end date of wlm monument
|
||||
* For this release we are hardcoding it to be 31st October
|
||||
* @return
|
||||
*/
|
||||
public static String getWLMEndDate() {
|
||||
return "30 Sep";
|
||||
}
|
||||
|
||||
/***
|
||||
* Function to get the current WLM year
|
||||
* It increments at the start of September in line with the other WLM functions
|
||||
* (No consideration of locales for now)
|
||||
* @param calendar
|
||||
* @return
|
||||
*/
|
||||
public static int getWikiLovesMonumentsYear(Calendar calendar) {
|
||||
int year = calendar.get(Calendar.YEAR);
|
||||
if (calendar.get(Calendar.MONTH) < Calendar.SEPTEMBER) {
|
||||
year -= 1;
|
||||
}
|
||||
return year;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
package fr.free.nrw.commons;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
public interface ViewHolder<T> {
|
||||
void bindModel(Context context, T model);
|
||||
}
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
package fr.free.nrw.commons;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentPagerAdapter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This adapter will be used to display fragments in a ViewPager
|
||||
*/
|
||||
public class ViewPagerAdapter extends FragmentPagerAdapter {
|
||||
private List<Fragment> fragmentList = new ArrayList<>();
|
||||
private List<String> fragmentTitleList = new ArrayList<>();
|
||||
|
||||
public ViewPagerAdapter(FragmentManager manager) {
|
||||
super(manager);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a ViewPagerAdapter with a specified Fragment Manager and Fragment resume behavior.
|
||||
*
|
||||
* @param manager The FragmentManager
|
||||
* @param behavior An integer which represents the behavior of non visible fragments. See
|
||||
* FragmentPagerAdapter.java for options.
|
||||
*/
|
||||
public ViewPagerAdapter(FragmentManager manager, int behavior) {
|
||||
super(manager, behavior);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the fragment of the viewpager at a particular position
|
||||
* @param position
|
||||
*/
|
||||
@Override
|
||||
public Fragment getItem(int position) {
|
||||
return fragmentList.get(position);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the total number of fragments in the viewpager.
|
||||
* @return size
|
||||
*/
|
||||
@Override
|
||||
public int getCount() {
|
||||
return fragmentList.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method sets the fragment and title list in the viewpager
|
||||
* @param fragmentList List of all fragments to be displayed in the viewpager
|
||||
* @param fragmentTitleList List of all titles of the fragments
|
||||
*/
|
||||
public void setTabData(List<Fragment> fragmentList, List<String> fragmentTitleList) {
|
||||
this.fragmentList = fragmentList;
|
||||
this.fragmentTitleList = fragmentTitleList;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the title of the page at a particular position
|
||||
* @param position
|
||||
*/
|
||||
@Override
|
||||
public CharSequence getPageTitle(int position) {
|
||||
return fragmentTitleList.get(position);
|
||||
}
|
||||
}
|
||||
44
app/src/main/java/fr/free/nrw/commons/ViewPagerAdapter.kt
Normal file
44
app/src/main/java/fr/free/nrw/commons/ViewPagerAdapter.kt
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
package fr.free.nrw.commons
|
||||
|
||||
import android.content.Context
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.fragment.app.FragmentPagerAdapter
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* This adapter will be used to display fragments in a ViewPager
|
||||
*/
|
||||
class ViewPagerAdapter : FragmentPagerAdapter {
|
||||
private val context: Context
|
||||
private var fragmentList: List<Fragment> = emptyList()
|
||||
private var fragmentTitleList: List<String> = emptyList()
|
||||
|
||||
constructor(context: Context, manager: FragmentManager) : super(manager) {
|
||||
this.context = context
|
||||
}
|
||||
|
||||
constructor(context: Context, manager: FragmentManager, behavior: Int) : super(manager, behavior) {
|
||||
this.context = context
|
||||
}
|
||||
|
||||
override fun getItem(position: Int): Fragment = fragmentList[position]
|
||||
|
||||
override fun getPageTitle(position: Int): CharSequence = fragmentTitleList[position]
|
||||
|
||||
override fun getCount(): Int = fragmentList.size
|
||||
|
||||
fun setTabs(vararg titlesToFragments: Pair<Int, Fragment>) {
|
||||
// Enforce that every title must come from strings.xml and all will consistently be uppercase
|
||||
fragmentTitleList = titlesToFragments.map {
|
||||
context.getString(it.first).uppercase(Locale.ROOT)
|
||||
}
|
||||
fragmentList = titlesToFragments.map { it.second }
|
||||
}
|
||||
|
||||
companion object {
|
||||
// Convenience method for Java callers, can be removed when everything is migrated
|
||||
@JvmStatic
|
||||
fun pairOf(first: Int, second: Fragment) = first to second
|
||||
}
|
||||
}
|
||||
|
|
@ -1,109 +0,0 @@
|
|||
package fr.free.nrw.commons;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import fr.free.nrw.commons.databinding.ActivityWelcomeBinding;
|
||||
import fr.free.nrw.commons.databinding.PopupForCopyrightBinding;
|
||||
import fr.free.nrw.commons.quiz.QuizActivity;
|
||||
import fr.free.nrw.commons.theme.BaseActivity;
|
||||
import fr.free.nrw.commons.utils.ConfigUtils;
|
||||
|
||||
public class WelcomeActivity extends BaseActivity {
|
||||
|
||||
private ActivityWelcomeBinding binding;
|
||||
private PopupForCopyrightBinding copyrightBinding;
|
||||
|
||||
private final WelcomePagerAdapter adapter = new WelcomePagerAdapter();
|
||||
private boolean isQuiz;
|
||||
private AlertDialog.Builder dialogBuilder;
|
||||
private AlertDialog dialog;
|
||||
|
||||
/**
|
||||
* Initialises exiting fields and dependencies
|
||||
*
|
||||
* @param savedInstanceState WelcomeActivity bundled data
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
binding = ActivityWelcomeBinding.inflate(getLayoutInflater());
|
||||
final View view = binding.getRoot();
|
||||
setContentView(view);
|
||||
|
||||
if (getIntent() != null) {
|
||||
final Bundle bundle = getIntent().getExtras();
|
||||
if (bundle != null) {
|
||||
isQuiz = bundle.getBoolean("isQuiz");
|
||||
}
|
||||
} else {
|
||||
isQuiz = false;
|
||||
}
|
||||
|
||||
// Enable skip button if beta flavor
|
||||
if (ConfigUtils.isBetaFlavour()) {
|
||||
binding.finishTutorialButton.setVisibility(View.VISIBLE);
|
||||
|
||||
dialogBuilder = new AlertDialog.Builder(this);
|
||||
copyrightBinding = PopupForCopyrightBinding.inflate(getLayoutInflater());
|
||||
final View contactPopupView = copyrightBinding.getRoot();
|
||||
dialogBuilder.setView(contactPopupView);
|
||||
dialogBuilder.setCancelable(false);
|
||||
dialog = dialogBuilder.create();
|
||||
dialog.show();
|
||||
|
||||
copyrightBinding.buttonOk.setOnClickListener(v -> dialog.dismiss());
|
||||
}
|
||||
|
||||
binding.welcomePager.setAdapter(adapter);
|
||||
binding.welcomePagerIndicator.setViewPager(binding.welcomePager);
|
||||
|
||||
binding.finishTutorialButton.setOnClickListener(v -> finishTutorial());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* References WelcomePageAdapter to null before the activity is destroyed
|
||||
*/
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
if (isQuiz) {
|
||||
final Intent i = new Intent(this, QuizActivity.class);
|
||||
startActivity(i);
|
||||
}
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a way to change current activity to WelcomeActivity
|
||||
*
|
||||
* @param context Activity context
|
||||
*/
|
||||
public static void startYourself(final Context context) {
|
||||
final Intent welcomeIntent = new Intent(context, WelcomeActivity.class);
|
||||
context.startActivity(welcomeIntent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Override onBackPressed() to go to previous tutorial 'pages' if not on first page
|
||||
*/
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (binding.welcomePager.getCurrentItem() != 0) {
|
||||
binding.welcomePager.setCurrentItem(binding.welcomePager.getCurrentItem() - 1, true);
|
||||
} else {
|
||||
if (defaultKvStore.getBoolean("firstrun", true)) {
|
||||
finishAffinity();
|
||||
} else {
|
||||
super.onBackPressed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void finishTutorial() {
|
||||
defaultKvStore.putBoolean("firstrun", false);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
78
app/src/main/java/fr/free/nrw/commons/WelcomeActivity.kt
Normal file
78
app/src/main/java/fr/free/nrw/commons/WelcomeActivity.kt
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
package fr.free.nrw.commons
|
||||
|
||||
import android.app.AlertDialog
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import fr.free.nrw.commons.databinding.ActivityWelcomeBinding
|
||||
import fr.free.nrw.commons.databinding.PopupForCopyrightBinding
|
||||
import fr.free.nrw.commons.quiz.QuizActivity
|
||||
import fr.free.nrw.commons.theme.BaseActivity
|
||||
import fr.free.nrw.commons.utils.ConfigUtils.isBetaFlavour
|
||||
|
||||
class WelcomeActivity : BaseActivity() {
|
||||
private var binding: ActivityWelcomeBinding? = null
|
||||
private var isQuiz = false
|
||||
|
||||
/**
|
||||
* Initialises exiting fields and dependencies
|
||||
*
|
||||
* @param savedInstanceState WelcomeActivity bundled data
|
||||
*/
|
||||
public override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityWelcomeBinding.inflate(layoutInflater)
|
||||
setContentView(binding!!.root)
|
||||
|
||||
isQuiz = intent?.extras?.getBoolean("isQuiz", false) ?: false
|
||||
|
||||
// Enable skip button if beta flavor
|
||||
if (isBetaFlavour) {
|
||||
binding!!.finishTutorialButton.visibility = View.VISIBLE
|
||||
|
||||
val copyrightBinding = PopupForCopyrightBinding.inflate(layoutInflater)
|
||||
|
||||
val dialog = AlertDialog.Builder(this)
|
||||
.setView(copyrightBinding.root)
|
||||
.setCancelable(false)
|
||||
.create()
|
||||
dialog.show()
|
||||
|
||||
copyrightBinding.buttonOk.setOnClickListener { v: View? -> dialog.dismiss() }
|
||||
}
|
||||
|
||||
val adapter = WelcomePagerAdapter()
|
||||
binding!!.welcomePager.adapter = adapter
|
||||
binding!!.welcomePagerIndicator.setViewPager(binding!!.welcomePager)
|
||||
binding!!.finishTutorialButton.setOnClickListener { v: View? -> finishTutorial() }
|
||||
}
|
||||
|
||||
public override fun onDestroy() {
|
||||
if (isQuiz) {
|
||||
startActivity(Intent(this, QuizActivity::class.java))
|
||||
}
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
if (binding!!.welcomePager.currentItem != 0) {
|
||||
binding!!.welcomePager.setCurrentItem(binding!!.welcomePager.currentItem - 1, true)
|
||||
} else {
|
||||
if (defaultKvStore.getBoolean("firstrun", true)) {
|
||||
finishAffinity()
|
||||
} else {
|
||||
super.onBackPressed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun finishTutorial() {
|
||||
defaultKvStore.putBoolean("firstrun", false)
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
fun Context.startWelcome() {
|
||||
startActivity(Intent(this, WelcomeActivity::class.java))
|
||||
}
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
package fr.free.nrw.commons;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.viewpager.widget.PagerAdapter;
|
||||
|
||||
public class WelcomePagerAdapter extends PagerAdapter {
|
||||
private static final int[] PAGE_LAYOUTS = new int[]{
|
||||
R.layout.welcome_wikipedia,
|
||||
R.layout.welcome_do_upload,
|
||||
R.layout.welcome_dont_upload,
|
||||
R.layout.welcome_image_example,
|
||||
R.layout.welcome_final
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets total number of layouts
|
||||
* @return Number of layouts
|
||||
*/
|
||||
@Override
|
||||
public int getCount() {
|
||||
return PAGE_LAYOUTS.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares given view with provided object
|
||||
* @param view Adapter view
|
||||
* @param object Adapter object
|
||||
* @return Equality between view and object
|
||||
*/
|
||||
@Override
|
||||
public boolean isViewFromObject(View view, Object object) {
|
||||
return (view == object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object instantiateItem(ViewGroup container, int position) {
|
||||
LayoutInflater inflater = LayoutInflater.from(container.getContext());
|
||||
ViewGroup layout = (ViewGroup) inflater.inflate(PAGE_LAYOUTS[position], container, false);
|
||||
|
||||
// If final page
|
||||
if (position == PAGE_LAYOUTS.length - 1) {
|
||||
// Add link to more information
|
||||
TextView moreInfo = layout.findViewById(R.id.welcomeInfo);
|
||||
Utils.setUnderlinedText(moreInfo, R.string.welcome_help_button_text, container.getContext());
|
||||
moreInfo.setOnClickListener(view -> Utils.handleWebUrl(
|
||||
container.getContext(),
|
||||
Uri.parse("https://commons.wikimedia.org/wiki/Help:Contents")
|
||||
));
|
||||
|
||||
// Handle click of finishTutorialButton ("YES!" button) inside layout
|
||||
layout.findViewById(R.id.finishTutorialButton)
|
||||
.setOnClickListener(view -> ((WelcomeActivity) container.getContext()).finishTutorial());
|
||||
}
|
||||
|
||||
container.addView(layout);
|
||||
return layout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a way to remove an item from container
|
||||
* @param container Adapter view group container
|
||||
* @param position Index of item
|
||||
* @param obj Adapter object
|
||||
*/
|
||||
@Override
|
||||
public void destroyItem(ViewGroup container, int position, Object obj) {
|
||||
container.removeView((View) obj);
|
||||
}
|
||||
}
|
||||
70
app/src/main/java/fr/free/nrw/commons/WelcomePagerAdapter.kt
Normal file
70
app/src/main/java/fr/free/nrw/commons/WelcomePagerAdapter.kt
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
package fr.free.nrw.commons
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.core.net.toUri
|
||||
import androidx.viewpager.widget.PagerAdapter
|
||||
import fr.free.nrw.commons.utils.UnderlineUtils.setUnderlinedText
|
||||
import fr.free.nrw.commons.utils.handleWebUrl
|
||||
|
||||
class WelcomePagerAdapter : PagerAdapter() {
|
||||
/**
|
||||
* Gets total number of layouts
|
||||
* @return Number of layouts
|
||||
*/
|
||||
override fun getCount(): Int = PAGE_LAYOUTS.size
|
||||
|
||||
/**
|
||||
* Compares given view with provided object
|
||||
* @param view Adapter view
|
||||
* @param obj Adapter object
|
||||
* @return Equality between view and object
|
||||
*/
|
||||
override fun isViewFromObject(view: View, obj: Any): Boolean = (view === obj)
|
||||
|
||||
/**
|
||||
* Provides a way to remove an item from container
|
||||
* @param container Adapter view group container
|
||||
* @param position Index of item
|
||||
* @param obj Adapter object
|
||||
*/
|
||||
override fun destroyItem(container: ViewGroup, position: Int, obj: Any) =
|
||||
container.removeView(obj as View)
|
||||
|
||||
override fun instantiateItem(container: ViewGroup, position: Int): Any {
|
||||
val inflater = LayoutInflater.from(container.context)
|
||||
val layout = inflater.inflate(PAGE_LAYOUTS[position], container, false) as ViewGroup
|
||||
|
||||
// If final page
|
||||
if (position == PAGE_LAYOUTS.size - 1) {
|
||||
// Add link to more information
|
||||
val moreInfo = layout.findViewById<TextView>(R.id.welcomeInfo)
|
||||
setUnderlinedText(moreInfo, R.string.welcome_help_button_text)
|
||||
moreInfo.setOnClickListener {
|
||||
handleWebUrl(
|
||||
container.context,
|
||||
"https://commons.wikimedia.org/wiki/Help:Contents".toUri()
|
||||
)
|
||||
}
|
||||
|
||||
// Handle click of finishTutorialButton ("YES!" button) inside layout
|
||||
layout.findViewById<View>(R.id.finishTutorialButton)
|
||||
.setOnClickListener { view: View? -> (container.context as WelcomeActivity).finishTutorial() }
|
||||
}
|
||||
|
||||
container.addView(layout)
|
||||
return layout
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val PAGE_LAYOUTS = intArrayOf(
|
||||
R.layout.welcome_wikipedia,
|
||||
R.layout.welcome_do_upload,
|
||||
R.layout.welcome_dont_upload,
|
||||
R.layout.welcome_image_example,
|
||||
R.layout.welcome_final
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -25,7 +25,6 @@ import androidx.core.content.ContextCompat
|
|||
import fr.free.nrw.commons.BuildConfig
|
||||
import fr.free.nrw.commons.CommonsApplication
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.Utils
|
||||
import fr.free.nrw.commons.auth.login.LoginCallback
|
||||
import fr.free.nrw.commons.auth.login.LoginClient
|
||||
import fr.free.nrw.commons.auth.login.LoginResult
|
||||
|
|
@ -38,6 +37,7 @@ import fr.free.nrw.commons.utils.ActivityUtils.startActivityWithFlags
|
|||
import fr.free.nrw.commons.utils.ConfigUtils.isBetaFlavour
|
||||
import fr.free.nrw.commons.utils.SystemThemeUtils
|
||||
import fr.free.nrw.commons.utils.ViewUtil.hideKeyboard
|
||||
import fr.free.nrw.commons.utils.handleWebUrl
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import timber.log.Timber
|
||||
import java.util.Locale
|
||||
|
|
@ -254,10 +254,10 @@ class LoginActivity : AccountAuthenticatorActivity() {
|
|||
}
|
||||
|
||||
private fun forgotPassword() =
|
||||
Utils.handleWebUrl(this, Uri.parse(BuildConfig.FORGOT_PASSWORD_URL))
|
||||
handleWebUrl(this, Uri.parse(BuildConfig.FORGOT_PASSWORD_URL))
|
||||
|
||||
private fun onPrivacyPolicyClicked() =
|
||||
Utils.handleWebUrl(this, Uri.parse(BuildConfig.PRIVACY_POLICY_URL))
|
||||
handleWebUrl(this, Uri.parse(BuildConfig.PRIVACY_POLICY_URL))
|
||||
|
||||
private fun signUp() =
|
||||
startActivity(Intent(this, SignupActivity::class.java))
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import android.view.LayoutInflater
|
|||
import android.view.View
|
||||
import androidx.core.content.ContextCompat
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.Utils
|
||||
import fr.free.nrw.commons.campaigns.models.Campaign
|
||||
import fr.free.nrw.commons.contributions.MainActivity
|
||||
import fr.free.nrw.commons.databinding.LayoutCampaginBinding
|
||||
|
|
@ -16,6 +15,7 @@ import fr.free.nrw.commons.utils.CommonsDateUtil.getIso8601DateFormatShort
|
|||
import fr.free.nrw.commons.utils.DateUtil.getExtraShortDateString
|
||||
import fr.free.nrw.commons.utils.SwipableCardView
|
||||
import fr.free.nrw.commons.utils.ViewUtil.showLongToast
|
||||
import fr.free.nrw.commons.utils.handleWebUrl
|
||||
import timber.log.Timber
|
||||
import java.text.ParseException
|
||||
|
||||
|
|
@ -74,7 +74,7 @@ class CampaignView : SwipableCardView {
|
|||
if (it.isWLMCampaign) {
|
||||
((context) as MainActivity).showNearby()
|
||||
} else {
|
||||
Utils.handleWebUrl(context, Uri.parse(it.link))
|
||||
handleWebUrl(context, Uri.parse(it.link))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ class CampaignsPresenter @Inject constructor(
|
|||
private val okHttpJsonApiClient: OkHttpJsonApiClient?,
|
||||
@param:Named(IO_THREAD) private val ioScheduler: Scheduler,
|
||||
@param:Named(MAIN_THREAD) private val mainThreadScheduler: Scheduler
|
||||
) : BasePresenter<ICampaignsView?> {
|
||||
) : BasePresenter<ICampaignsView> {
|
||||
private var view: ICampaignsView? = null
|
||||
private var disposable: Disposable? = null
|
||||
private var campaign: Campaign? = null
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
package fr.free.nrw.commons.campaigns
|
||||
|
||||
import fr.free.nrw.commons.MvpView
|
||||
import fr.free.nrw.commons.campaigns.models.Campaign
|
||||
|
||||
/**
|
||||
* Interface which defines the view contracts of the campaign view
|
||||
*/
|
||||
interface ICampaignsView : MvpView {
|
||||
interface ICampaignsView {
|
||||
fun showCampaigns(campaign: Campaign?)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,9 +13,9 @@ import androidx.fragment.app.FragmentManager
|
|||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import fr.free.nrw.commons.BuildConfig.COMMONS_URL
|
||||
import fr.free.nrw.commons.Media
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.Utils
|
||||
import fr.free.nrw.commons.ViewPagerAdapter
|
||||
import fr.free.nrw.commons.databinding.ActivityCategoryDetailsBinding
|
||||
import fr.free.nrw.commons.explore.categories.media.CategoriesMediaFragment
|
||||
|
|
@ -23,6 +23,9 @@ import fr.free.nrw.commons.explore.categories.parent.ParentCategoriesFragment
|
|||
import fr.free.nrw.commons.explore.categories.sub.SubCategoriesFragment
|
||||
import fr.free.nrw.commons.media.MediaDetailPagerFragment
|
||||
import fr.free.nrw.commons.theme.BaseActivity
|
||||
import fr.free.nrw.commons.utils.handleWebUrl
|
||||
import fr.free.nrw.commons.wikidata.model.WikiSite
|
||||
import fr.free.nrw.commons.wikidata.model.page.PageTitle
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
@ -56,7 +59,7 @@ class CategoryDetailsActivity : BaseActivity(),
|
|||
val view = binding.root
|
||||
setContentView(view)
|
||||
supportFragmentManager = getSupportFragmentManager()
|
||||
viewPagerAdapter = ViewPagerAdapter(supportFragmentManager)
|
||||
viewPagerAdapter = ViewPagerAdapter(this, supportFragmentManager)
|
||||
binding.viewPager.adapter = viewPagerAdapter
|
||||
binding.viewPager.offscreenPageLimit = 2
|
||||
binding.tabLayout.setupWithViewPager(binding.viewPager)
|
||||
|
|
@ -80,8 +83,6 @@ class CategoryDetailsActivity : BaseActivity(),
|
|||
* Set the fragments according to the tab selected in the viewPager.
|
||||
*/
|
||||
private fun setTabs() {
|
||||
val fragmentList = mutableListOf<Fragment>()
|
||||
val titleList = mutableListOf<String>()
|
||||
categoriesMediaFragment = CategoriesMediaFragment()
|
||||
val subCategoryListFragment = SubCategoriesFragment()
|
||||
val parentCategoriesFragment = ParentCategoriesFragment()
|
||||
|
|
@ -96,13 +97,12 @@ class CategoryDetailsActivity : BaseActivity(),
|
|||
|
||||
viewModel.onCheckIfBookmarked(categoryName!!)
|
||||
}
|
||||
fragmentList.add(categoriesMediaFragment)
|
||||
titleList.add("MEDIA")
|
||||
fragmentList.add(subCategoryListFragment)
|
||||
titleList.add("SUBCATEGORIES")
|
||||
fragmentList.add(parentCategoriesFragment)
|
||||
titleList.add("PARENT CATEGORIES")
|
||||
viewPagerAdapter.setTabData(fragmentList, titleList)
|
||||
|
||||
viewPagerAdapter.setTabs(
|
||||
R.string.title_for_media to categoriesMediaFragment,
|
||||
R.string.title_for_subcategories to subCategoryListFragment,
|
||||
R.string.title_for_parent_categories to parentCategoriesFragment
|
||||
)
|
||||
viewPagerAdapter.notifyDataSetChanged()
|
||||
}
|
||||
|
||||
|
|
@ -199,8 +199,9 @@ class CategoryDetailsActivity : BaseActivity(),
|
|||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return when (item.itemId) {
|
||||
R.id.menu_browser_current_category -> {
|
||||
val title = Utils.getPageTitle(CATEGORY_PREFIX + categoryName)
|
||||
Utils.handleWebUrl(this, Uri.parse(title.canonicalUri))
|
||||
val title = PageTitle(CATEGORY_PREFIX + categoryName, WikiSite(COMMONS_URL))
|
||||
|
||||
handleWebUrl(this, Uri.parse(title.canonicalUri))
|
||||
true
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import fr.free.nrw.commons.BasePresenter
|
|||
interface ContributionsContract {
|
||||
|
||||
interface View {
|
||||
fun showMessage(localizedMessage: String)
|
||||
fun getContext(): Context?
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ import androidx.work.WorkManager
|
|||
import fr.free.nrw.commons.MapController.NearbyPlacesInfo
|
||||
import fr.free.nrw.commons.Media
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.Utils
|
||||
import fr.free.nrw.commons.auth.SessionManager
|
||||
import fr.free.nrw.commons.campaigns.CampaignView
|
||||
import fr.free.nrw.commons.campaigns.CampaignsPresenter
|
||||
|
|
@ -64,6 +63,9 @@ import fr.free.nrw.commons.utils.LengthUtils.formatDistanceBetween
|
|||
import fr.free.nrw.commons.utils.NetworkUtils.isInternetConnectionEstablished
|
||||
import fr.free.nrw.commons.utils.PermissionUtils.hasPermission
|
||||
import fr.free.nrw.commons.utils.ViewUtil.showLongToast
|
||||
import fr.free.nrw.commons.utils.isMonumentsEnabled
|
||||
import fr.free.nrw.commons.utils.wLMEndDate
|
||||
import fr.free.nrw.commons.utils.wLMStartDate
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
|
|
@ -139,7 +141,7 @@ class ContributionsFragment : CommonsDaggerSupportFragment(), FragmentManager.On
|
|||
|
||||
private var wlmCampaign: Campaign? = null
|
||||
|
||||
var userName: String? = null
|
||||
private var userName: String? = null
|
||||
private var isUserProfile = false
|
||||
|
||||
private var mSensorManager: SensorManager? = null
|
||||
|
|
@ -242,8 +244,8 @@ class ContributionsFragment : CommonsDaggerSupportFragment(), FragmentManager.On
|
|||
private fun initWLMCampaign() {
|
||||
wlmCampaign = Campaign(
|
||||
getString(R.string.wlm_campaign_title),
|
||||
getString(R.string.wlm_campaign_description), Utils.getWLMStartDate().toString(),
|
||||
Utils.getWLMEndDate().toString(), NearbyParentFragment.WLM_URL, true
|
||||
getString(R.string.wlm_campaign_description), wLMStartDate,
|
||||
wLMEndDate, NearbyParentFragment.WLM_URL, true
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -729,7 +731,7 @@ class ContributionsFragment : CommonsDaggerSupportFragment(), FragmentManager.On
|
|||
* of campaigns on the campaigns card
|
||||
*/
|
||||
private fun fetchCampaigns() {
|
||||
if (Utils.isMonumentsEnabled(Date())) {
|
||||
if (isMonumentsEnabled) {
|
||||
if (binding != null) {
|
||||
binding!!.campaignsView.setCampaign(wlmCampaign)
|
||||
binding!!.campaignsView.visibility = View.VISIBLE
|
||||
|
|
@ -743,10 +745,6 @@ class ContributionsFragment : CommonsDaggerSupportFragment(), FragmentManager.On
|
|||
}
|
||||
}
|
||||
|
||||
override fun showMessage(message: String) {
|
||||
Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
override fun showCampaigns(campaign: Campaign?) {
|
||||
if (campaign != null && !isUserProfile) {
|
||||
if (binding != null) {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ class ContributionsListContract {
|
|||
fun showNoContributionsUI(shouldShow: Boolean)
|
||||
}
|
||||
|
||||
interface UserActionListener : BasePresenter<View?> {
|
||||
interface UserActionListener : BasePresenter<View> {
|
||||
fun refreshList(swipeRefreshLayout: SwipeRefreshLayout?)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ import androidx.recyclerview.widget.SimpleItemAnimator
|
|||
import fr.free.nrw.commons.Media
|
||||
import fr.free.nrw.commons.MediaDataExtractor
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.Utils
|
||||
import fr.free.nrw.commons.auth.SessionManager
|
||||
import fr.free.nrw.commons.contributions.WikipediaInstructionsDialogFragment.Companion.newInstance
|
||||
import fr.free.nrw.commons.databinding.FragmentContributionsListBinding
|
||||
|
|
@ -32,6 +31,9 @@ import fr.free.nrw.commons.profile.ProfileActivity
|
|||
import fr.free.nrw.commons.ui.CustomFabController
|
||||
import fr.free.nrw.commons.utils.DialogUtil.showAlertDialog
|
||||
import fr.free.nrw.commons.utils.SystemThemeUtils
|
||||
import fr.free.nrw.commons.utils.ViewUtil.showShortToast
|
||||
import fr.free.nrw.commons.utils.copyToClipboard
|
||||
import fr.free.nrw.commons.utils.handleWebUrl
|
||||
import fr.free.nrw.commons.wikidata.model.WikiSite
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
import javax.inject.Inject
|
||||
|
|
@ -393,14 +395,13 @@ open class ContributionsListFragment : CommonsDaggerSupportFragment(), Contribut
|
|||
*/
|
||||
override fun onConfirmClicked(contribution: Contribution?, copyWikicode: Boolean) {
|
||||
if (copyWikicode) {
|
||||
val wikicode = contribution!!.media.wikiCode
|
||||
Utils.copy("wikicode", wikicode, context)
|
||||
requireContext().copyToClipboard("wikicode", contribution!!.media.wikiCode)
|
||||
}
|
||||
|
||||
val url =
|
||||
languageWikipediaSite!!.mobileUrl() + "/wiki/" + (contribution!!.wikidataPlace
|
||||
?.getWikipediaPageTitle())
|
||||
Utils.handleWebUrl(context, Uri.parse(url))
|
||||
handleWebUrl(requireContext(), Uri.parse(url))
|
||||
}
|
||||
|
||||
fun getContributionStateAt(position: Int): Int {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import androidx.fragment.app.FragmentManager
|
|||
import androidx.work.ExistingWorkPolicy
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.WelcomeActivity
|
||||
import fr.free.nrw.commons.auth.SessionManager
|
||||
import fr.free.nrw.commons.bookmarks.BookmarkFragment
|
||||
import fr.free.nrw.commons.contributions.ContributionsFragment.Companion.newInstance
|
||||
|
|
@ -33,6 +32,7 @@ import fr.free.nrw.commons.notification.NotificationActivity.Companion.startYour
|
|||
import fr.free.nrw.commons.notification.NotificationController
|
||||
import fr.free.nrw.commons.quiz.QuizChecker
|
||||
import fr.free.nrw.commons.settings.SettingsFragment
|
||||
import fr.free.nrw.commons.startWelcome
|
||||
import fr.free.nrw.commons.theme.BaseActivity
|
||||
import fr.free.nrw.commons.upload.UploadProgressActivity
|
||||
import fr.free.nrw.commons.upload.worker.WorkRequestHelper.Companion.makeOneTimeWorkRequest
|
||||
|
|
@ -517,7 +517,7 @@ after opening the app.
|
|||
(!applicationKvStore!!.getBoolean("login_skipped"))
|
||||
) {
|
||||
defaultKvStore.putBoolean("inAppCameraFirstRun", true)
|
||||
WelcomeActivity.startYourself(this)
|
||||
startWelcome()
|
||||
}
|
||||
|
||||
retryAllFailedUploads()
|
||||
|
|
|
|||
|
|
@ -53,7 +53,6 @@ class DeleteHelper @Inject constructor(
|
|||
media: Media?,
|
||||
reason: String?
|
||||
): Single<Boolean>? {
|
||||
|
||||
if(context == null && media == null) {
|
||||
return null
|
||||
}
|
||||
|
|
@ -86,7 +85,6 @@ class DeleteHelper @Inject constructor(
|
|||
* @return
|
||||
*/
|
||||
private fun delete(media: Media, reason: String): Observable<Boolean> {
|
||||
Timber.d("thread is delete %s", Thread.currentThread().name)
|
||||
val summary = "Nominating ${media.filename} for deletion."
|
||||
val calendar = Calendar.getInstance()
|
||||
val fileDeleteString = """
|
||||
|
|
|
|||
|
|
@ -2,21 +2,19 @@ package fr.free.nrw.commons.delete
|
|||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
|
||||
import fr.free.nrw.commons.utils.DateUtil
|
||||
import java.util.Locale
|
||||
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
import fr.free.nrw.commons.Media
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.profile.achievements.FeedbackResponse
|
||||
import fr.free.nrw.commons.auth.SessionManager
|
||||
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient
|
||||
import fr.free.nrw.commons.utils.DateUtil
|
||||
import fr.free.nrw.commons.utils.ViewUtilWrapper
|
||||
import io.reactivex.Single
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.rx2.rxSingle
|
||||
import timber.log.Timber
|
||||
import java.util.Locale
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
/**
|
||||
* This class handles the reason for deleting a Media object
|
||||
|
|
@ -29,6 +27,8 @@ class ReasonBuilder @Inject constructor(
|
|||
private val viewUtilWrapper: ViewUtilWrapper
|
||||
) {
|
||||
|
||||
private val defaultFileUsagePageSize = 10
|
||||
|
||||
/**
|
||||
* To process the reason and append the media's upload date and uploaded_by_me string
|
||||
* @param media
|
||||
|
|
@ -39,7 +39,7 @@ class ReasonBuilder @Inject constructor(
|
|||
if (media == null || reason == null) {
|
||||
return Single.just("Not known")
|
||||
}
|
||||
return fetchArticleNumber(media, reason)
|
||||
return getAndAppendFileUsage(media, reason)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -54,27 +54,36 @@ class ReasonBuilder @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun fetchArticleNumber(media: Media, reason: String): Single<String> {
|
||||
return if (checkAccount()) {
|
||||
okHttpJsonApiClient
|
||||
.getAchievements(sessionManager.userName)
|
||||
.map { feedbackResponse -> appendArticlesUsed(feedbackResponse, media, reason) }
|
||||
} else {
|
||||
Single.just("")
|
||||
private fun getAndAppendFileUsage(media: Media, reason: String): Single<String> {
|
||||
return rxSingle(context = Dispatchers.IO) {
|
||||
if (!checkAccount()) return@rxSingle ""
|
||||
|
||||
try {
|
||||
val globalFileUsage = okHttpJsonApiClient.getGlobalFileUsages(
|
||||
fileName = media.filename,
|
||||
pageSize = defaultFileUsagePageSize
|
||||
)
|
||||
val globalUsages = globalFileUsage?.query?.pages?.sumOf { it.fileUsage.size } ?: 0
|
||||
|
||||
appendArticlesUsed(globalUsages, media, reason)
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "Error fetching file usage")
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the uploaded_by_me string, the upload date, name of articles using images
|
||||
* Takes the uploaded_by_me string, the upload date, no. of articles using images
|
||||
* and appends it to the received reason
|
||||
* @param feedBack object
|
||||
* @param fileUsages No. of files/articles using this image
|
||||
* @param media whose upload data is to be fetched
|
||||
* @param reason
|
||||
* @param reason string to be appended
|
||||
*/
|
||||
@SuppressLint("StringFormatInvalid")
|
||||
private fun appendArticlesUsed(feedBack: FeedbackResponse, media: Media, reason: String): String {
|
||||
private fun appendArticlesUsed(fileUsages: Int, media: Media, reason: String): String {
|
||||
val reason1Template = context.getString(R.string.uploaded_by_myself)
|
||||
return reason + String.format(Locale.getDefault(), reason1Template, prettyUploadedDate(media), feedBack.articlesUsingImages)
|
||||
return reason + String.format(Locale.getDefault(), reason1Template, prettyUploadedDate(media), fileUsages)
|
||||
.also { Timber.i("New Reason %s", it) }
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package fr.free.nrw.commons.explore;
|
||||
|
||||
import static androidx.viewpager.widget.ViewPager.SCROLL_STATE_IDLE;
|
||||
import static fr.free.nrw.commons.ViewPagerAdapter.pairOf;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
|
|
@ -23,10 +24,12 @@ import fr.free.nrw.commons.kvstore.JsonKvStore;
|
|||
import fr.free.nrw.commons.theme.BaseActivity;
|
||||
import fr.free.nrw.commons.utils.ActivityUtils;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import kotlin.Pair;
|
||||
|
||||
public class ExploreFragment extends CommonsDaggerSupportFragment {
|
||||
|
||||
|
|
@ -70,7 +73,7 @@ public class ExploreFragment extends CommonsDaggerSupportFragment {
|
|||
loadNearbyMapData();
|
||||
binding = FragmentExploreBinding.inflate(inflater, container, false);
|
||||
|
||||
viewPagerAdapter = new ViewPagerAdapter(getChildFragmentManager(),
|
||||
viewPagerAdapter = new ViewPagerAdapter(requireContext(), getChildFragmentManager(),
|
||||
FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
|
||||
|
||||
binding.viewPager.setAdapter(viewPagerAdapter);
|
||||
|
|
@ -111,9 +114,6 @@ public class ExploreFragment extends CommonsDaggerSupportFragment {
|
|||
* Sets the titles in the tabLayout and fragments in the viewPager
|
||||
*/
|
||||
public void setTabs() {
|
||||
List<Fragment> fragmentList = new ArrayList<>();
|
||||
List<String> titleList = new ArrayList<>();
|
||||
|
||||
Bundle featuredArguments = new Bundle();
|
||||
featuredArguments.putString("categoryName", FEATURED_IMAGES_CATEGORY);
|
||||
|
||||
|
|
@ -133,19 +133,15 @@ public class ExploreFragment extends CommonsDaggerSupportFragment {
|
|||
featuredRootFragment = new ExploreListRootFragment(featuredArguments);
|
||||
mobileRootFragment = new ExploreListRootFragment(mobileArguments);
|
||||
mapRootFragment = new ExploreMapRootFragment(mapArguments);
|
||||
fragmentList.add(featuredRootFragment);
|
||||
titleList.add(getString(R.string.explore_tab_title_featured).toUpperCase(Locale.ROOT));
|
||||
|
||||
fragmentList.add(mobileRootFragment);
|
||||
titleList.add(getString(R.string.explore_tab_title_mobile).toUpperCase(Locale.ROOT));
|
||||
|
||||
fragmentList.add(mapRootFragment);
|
||||
titleList.add(getString(R.string.explore_tab_title_map).toUpperCase(Locale.ROOT));
|
||||
|
||||
((MainActivity) getActivity()).showTabs();
|
||||
((BaseActivity) getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(false);
|
||||
|
||||
viewPagerAdapter.setTabData(fragmentList, titleList);
|
||||
viewPagerAdapter.setTabs(
|
||||
pairOf(R.string.explore_tab_title_featured, featuredRootFragment),
|
||||
pairOf(R.string.explore_tab_title_mobile, mobileRootFragment),
|
||||
pairOf(R.string.explore_tab_title_map, mapRootFragment)
|
||||
);
|
||||
viewPagerAdapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
package fr.free.nrw.commons.explore;
|
||||
|
||||
import static fr.free.nrw.commons.ViewPagerAdapter.pairOf;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
|
|
@ -26,11 +28,13 @@ import fr.free.nrw.commons.utils.FragmentUtils;
|
|||
import fr.free.nrw.commons.utils.ViewUtil;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import javax.inject.Inject;
|
||||
import kotlin.Pair;
|
||||
import timber.log.Timber;
|
||||
|
||||
/**
|
||||
|
|
@ -65,7 +69,7 @@ public class SearchActivity extends BaseActivity
|
|||
binding.toolbarSearch.setNavigationOnClickListener(v->onBackPressed());
|
||||
supportFragmentManager = getSupportFragmentManager();
|
||||
setSearchHistoryFragment();
|
||||
viewPagerAdapter = new ViewPagerAdapter(getSupportFragmentManager());
|
||||
viewPagerAdapter = new ViewPagerAdapter(this, getSupportFragmentManager());
|
||||
binding.viewPager.setAdapter(viewPagerAdapter);
|
||||
binding.viewPager.setOffscreenPageLimit(2); // Because we want all the fragments to be alive
|
||||
binding.tabLayout.setupWithViewPager(binding.viewPager);
|
||||
|
|
@ -90,19 +94,15 @@ public class SearchActivity extends BaseActivity
|
|||
* Sets the titles in the tabLayout and fragments in the viewPager
|
||||
*/
|
||||
public void setTabs() {
|
||||
List<Fragment> fragmentList = new ArrayList<>();
|
||||
List<String> titleList = new ArrayList<>();
|
||||
searchMediaFragment = new SearchMediaFragment();
|
||||
searchDepictionsFragment = new SearchDepictionsFragment();
|
||||
searchCategoryFragment= new SearchCategoryFragment();
|
||||
fragmentList.add(searchMediaFragment);
|
||||
titleList.add(getResources().getString(R.string.search_tab_title_media).toUpperCase(Locale.ROOT));
|
||||
fragmentList.add(searchCategoryFragment);
|
||||
titleList.add(getResources().getString(R.string.search_tab_title_categories).toUpperCase(Locale.ROOT));
|
||||
fragmentList.add(searchDepictionsFragment);
|
||||
titleList.add(getResources().getString(R.string.search_tab_title_depictions).toUpperCase(Locale.ROOT));
|
||||
|
||||
viewPagerAdapter.setTabData(fragmentList, titleList);
|
||||
viewPagerAdapter.setTabs(
|
||||
pairOf(R.string.search_tab_title_media, searchMediaFragment),
|
||||
pairOf(R.string.search_tab_title_categories, searchCategoryFragment),
|
||||
pairOf(R.string.search_tab_title_depictions, searchDepictionsFragment)
|
||||
);
|
||||
viewPagerAdapter.notifyDataSetChanged();
|
||||
getCompositeDisposable().add(RxSearchView.queryTextChanges(binding.searchBox)
|
||||
.takeUntil(RxView.detaches(binding.searchBox))
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
package fr.free.nrw.commons.explore.depictions;
|
||||
|
||||
import static fr.free.nrw.commons.ViewPagerAdapter.pairOf;
|
||||
import static fr.free.nrw.commons.utils.UrlUtilsKt.handleWebUrl;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
|
|
@ -8,16 +11,11 @@ import android.view.Menu;
|
|||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
import com.google.android.material.tabs.TabLayout;
|
||||
import fr.free.nrw.commons.Media;
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.Utils;
|
||||
import fr.free.nrw.commons.ViewPagerAdapter;
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao;
|
||||
import fr.free.nrw.commons.category.CategoryImagesCallback;
|
||||
|
|
@ -34,8 +32,10 @@ import io.reactivex.android.schedulers.AndroidSchedulers;
|
|||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import javax.inject.Inject;
|
||||
import kotlin.Pair;
|
||||
|
||||
/**
|
||||
* Activity to show depiction media, parent classes and child classes of depicted items in Explore
|
||||
|
|
@ -69,7 +69,7 @@ public class WikidataItemDetailsActivity extends BaseActivity implements MediaDe
|
|||
setContentView(binding.getRoot());
|
||||
compositeDisposable = new CompositeDisposable();
|
||||
supportFragmentManager = getSupportFragmentManager();
|
||||
viewPagerAdapter = new ViewPagerAdapter(getSupportFragmentManager());
|
||||
viewPagerAdapter = new ViewPagerAdapter(this, getSupportFragmentManager());
|
||||
binding.viewPager.setAdapter(viewPagerAdapter);
|
||||
binding.viewPager.setOffscreenPageLimit(2);
|
||||
binding.tabLayout.setupWithViewPager(binding.viewPager);
|
||||
|
|
@ -108,8 +108,6 @@ public class WikidataItemDetailsActivity extends BaseActivity implements MediaDe
|
|||
* Set the fragments according to the tab selected in the viewPager.
|
||||
*/
|
||||
private void setTabs() {
|
||||
List<Fragment> fragmentList = new ArrayList<>();
|
||||
List<String> titleList = new ArrayList<>();
|
||||
depictionImagesListFragment = new DepictedImagesFragment();
|
||||
ChildDepictionsFragment childDepictionsFragment = new ChildDepictionsFragment();
|
||||
ParentDepictionsFragment parentDepictionsFragment = new ParentDepictionsFragment();
|
||||
|
|
@ -123,13 +121,12 @@ public class WikidataItemDetailsActivity extends BaseActivity implements MediaDe
|
|||
parentDepictionsFragment.setArguments(arguments);
|
||||
childDepictionsFragment.setArguments(arguments);
|
||||
}
|
||||
fragmentList.add(depictionImagesListFragment);
|
||||
titleList.add(getResources().getString(R.string.title_for_media));
|
||||
fragmentList.add(childDepictionsFragment);
|
||||
titleList.add(getResources().getString(R.string.title_for_child_classes));
|
||||
fragmentList.add(parentDepictionsFragment);
|
||||
titleList.add(getResources().getString(R.string.title_for_parent_classes));
|
||||
viewPagerAdapter.setTabData(fragmentList, titleList);
|
||||
|
||||
viewPagerAdapter.setTabs(
|
||||
pairOf(R.string.title_for_media, depictionImagesListFragment),
|
||||
pairOf(R.string.title_for_subcategories, childDepictionsFragment),
|
||||
pairOf(R.string.title_for_parent_categories, parentDepictionsFragment)
|
||||
);
|
||||
binding.viewPager.setOffscreenPageLimit(2);
|
||||
viewPagerAdapter.notifyDataSetChanged();
|
||||
|
||||
|
|
@ -249,7 +246,7 @@ public class WikidataItemDetailsActivity extends BaseActivity implements MediaDe
|
|||
case R.id.browser_actions_menu_items:
|
||||
String entityId=getIntent().getStringExtra("entityId");
|
||||
Uri uri = Uri.parse("https://www.wikidata.org/wiki/" + entityId);
|
||||
Utils.handleWebUrl(this, uri);
|
||||
handleWebUrl(this, uri);
|
||||
return true;
|
||||
case R.id.menu_bookmark_current_item:
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
package fr.free.nrw.commons.explore.map;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import fr.free.nrw.commons.Media;
|
||||
import fr.free.nrw.commons.location.LatLng;
|
||||
import fr.free.nrw.commons.media.MediaClient;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
|
@ -23,6 +25,7 @@ public class ExploreMapCalls {
|
|||
* @param currentLatLng coordinates of search location
|
||||
* @return list of places obtained
|
||||
*/
|
||||
@NonNull
|
||||
List<Media> callCommonsQuery(final LatLng currentLatLng) {
|
||||
String coordinates = currentLatLng.getLatitude() + "|" + currentLatLng.getLongitude();
|
||||
return mediaClient.getMediaListFromGeoSearch(coordinates).blockingGet();
|
||||
|
|
|
|||
|
|
@ -2,7 +2,9 @@ package fr.free.nrw.commons.explore.map;
|
|||
|
||||
import static fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED;
|
||||
import static fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType.LOCATION_SLIGHTLY_CHANGED;
|
||||
import static fr.free.nrw.commons.utils.GeoCoordinatesKt.handleGeoCoordinates;
|
||||
import static fr.free.nrw.commons.utils.MapUtils.ZOOM_LEVEL;
|
||||
import static fr.free.nrw.commons.utils.UrlUtilsKt.handleWebUrl;
|
||||
|
||||
import android.Manifest.permission;
|
||||
import android.annotation.SuppressLint;
|
||||
|
|
@ -36,7 +38,6 @@ import fr.free.nrw.commons.BaseMarker;
|
|||
import fr.free.nrw.commons.MapController;
|
||||
import fr.free.nrw.commons.Media;
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.Utils;
|
||||
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao;
|
||||
import fr.free.nrw.commons.contributions.MainActivity;
|
||||
import fr.free.nrw.commons.databinding.FragmentExploreMapBinding;
|
||||
|
|
@ -639,13 +640,13 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment
|
|||
*/
|
||||
private void passInfoToSheet(final Place place) {
|
||||
binding.bottomSheetDetailsBinding.directionsButton.setOnClickListener(
|
||||
view -> Utils.handleGeoCoordinates(getActivity(),
|
||||
view -> handleGeoCoordinates(requireActivity(),
|
||||
place.getLocation(), binding.mapView.getZoomLevelDouble()));
|
||||
|
||||
binding.bottomSheetDetailsBinding.commonsButton.setVisibility(
|
||||
place.hasCommonsLink() ? View.VISIBLE : View.GONE);
|
||||
binding.bottomSheetDetailsBinding.commonsButton.setOnClickListener(
|
||||
view -> Utils.handleWebUrl(getContext(), place.siteLinks.getCommonsLink()));
|
||||
view -> handleWebUrl(getContext(), place.siteLinks.getCommonsLink()));
|
||||
|
||||
int index = 0;
|
||||
for (Media media : mediaList) {
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import fr.free.nrw.commons.explore.map.ExploreMapController.NearbyBaseMarkerThum
|
|||
import fr.free.nrw.commons.kvstore.JsonKvStore;
|
||||
import fr.free.nrw.commons.location.LatLng;
|
||||
import fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType;
|
||||
import fr.free.nrw.commons.nearby.Place;
|
||||
import io.reactivex.Observable;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.List;
|
||||
|
|
@ -182,7 +183,7 @@ public class ExploreMapPresenter
|
|||
exploreMapController
|
||||
.loadAttractionsFromLocationToBaseMarkerOptions(explorePlacesInfo.currentLatLng,
|
||||
// Curlatlang will be used to calculate distances
|
||||
explorePlacesInfo.explorePlaceList,
|
||||
(List<Place>) explorePlacesInfo.explorePlaceList,
|
||||
exploreMapFragmentView.getContext(),
|
||||
this,
|
||||
explorePlacesInfo);
|
||||
|
|
@ -230,11 +231,7 @@ public class ExploreMapPresenter
|
|||
mylocation.setLongitude(exploreMapFragmentView.getLastMapFocus().getLongitude());
|
||||
Float distance = mylocation.distanceTo(dest_location);
|
||||
|
||||
if (distance > 2000.0 * 3 / 4) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
return !(distance > 2000.0 * 3 / 4);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ import fr.free.nrw.commons.CameraPosition
|
|||
import fr.free.nrw.commons.CommonsApplication
|
||||
import fr.free.nrw.commons.Media
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.Utils
|
||||
import fr.free.nrw.commons.auth.SessionManager
|
||||
import fr.free.nrw.commons.auth.csrf.CsrfTokenClient
|
||||
import fr.free.nrw.commons.coordinates.CoordinateEditHelper
|
||||
|
|
@ -45,6 +44,7 @@ import fr.free.nrw.commons.upload.mediaDetails.UploadMediaDetailFragment.Compani
|
|||
import fr.free.nrw.commons.upload.mediaDetails.UploadMediaDetailFragment.Companion.LAST_ZOOM
|
||||
import fr.free.nrw.commons.utils.DialogUtil
|
||||
import fr.free.nrw.commons.utils.MapUtils.ZOOM_LEVEL
|
||||
import fr.free.nrw.commons.utils.handleGeoCoordinates
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import org.osmdroid.tileprovider.tilesource.TileSourceFactory
|
||||
|
|
@ -432,8 +432,8 @@ class LocationPickerActivity : BaseActivity(), LocationPermissionCallback {
|
|||
|
||||
position?.let {
|
||||
mapView?.zoomLevelDouble?.let { zoomLevel ->
|
||||
Utils.handleGeoCoordinates(this, it, zoomLevel)
|
||||
} ?: Utils.handleGeoCoordinates(this, it)
|
||||
handleGeoCoordinates(this, it, zoomLevel)
|
||||
} ?: handleGeoCoordinates(this, it)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -74,11 +74,10 @@ import fr.free.nrw.commons.BuildConfig
|
|||
import fr.free.nrw.commons.CameraPosition
|
||||
import fr.free.nrw.commons.CommonsApplication
|
||||
import fr.free.nrw.commons.CommonsApplication.Companion.instance
|
||||
import fr.free.nrw.commons.locationpicker.LocationPicker
|
||||
import fr.free.nrw.commons.Media
|
||||
import fr.free.nrw.commons.MediaDataExtractor
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.Utils
|
||||
import fr.free.nrw.commons.utils.UnderlineUtils
|
||||
import fr.free.nrw.commons.actions.ThanksClient
|
||||
import fr.free.nrw.commons.auth.SessionManager
|
||||
import fr.free.nrw.commons.auth.csrf.InvalidLoginTokenException
|
||||
|
|
@ -102,6 +101,7 @@ import fr.free.nrw.commons.explore.depictions.WikidataItemDetailsActivity
|
|||
import fr.free.nrw.commons.kvstore.JsonKvStore
|
||||
import fr.free.nrw.commons.language.AppLanguageLookUpTable
|
||||
import fr.free.nrw.commons.location.LocationServiceManager
|
||||
import fr.free.nrw.commons.locationpicker.LocationPicker
|
||||
import fr.free.nrw.commons.media.MediaDetailPagerFragment.MediaDetailProvider
|
||||
import fr.free.nrw.commons.profile.ProfileActivity
|
||||
import fr.free.nrw.commons.review.ReviewHelper
|
||||
|
|
@ -116,8 +116,13 @@ import fr.free.nrw.commons.utils.LangCodeUtils.getLocalizedResources
|
|||
import fr.free.nrw.commons.utils.PermissionUtils.PERMISSIONS_STORAGE
|
||||
import fr.free.nrw.commons.utils.PermissionUtils.checkPermissionsAndPerformAction
|
||||
import fr.free.nrw.commons.utils.PermissionUtils.hasPermission
|
||||
import fr.free.nrw.commons.utils.ViewUtil
|
||||
import fr.free.nrw.commons.utils.ViewUtil.showShortToast
|
||||
import fr.free.nrw.commons.utils.ViewUtilWrapper
|
||||
import fr.free.nrw.commons.utils.copyToClipboard
|
||||
import fr.free.nrw.commons.utils.handleGeoCoordinates
|
||||
import fr.free.nrw.commons.utils.handleWebUrl
|
||||
import fr.free.nrw.commons.utils.setUnderlinedText
|
||||
import fr.free.nrw.commons.wikidata.mwapi.MwQueryPage.Revision
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.Single
|
||||
|
|
@ -125,6 +130,7 @@ import io.reactivex.android.schedulers.AndroidSchedulers
|
|||
import io.reactivex.schedulers.Schedulers
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
import timber.log.Timber
|
||||
import java.lang.String.format
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
import java.util.Objects
|
||||
|
|
@ -314,8 +320,7 @@ class MediaDetailFragment : CommonsDaggerSupportFragment(), CategoryEditHelper.C
|
|||
_binding = FragmentMediaDetailBinding.inflate(inflater, container, false)
|
||||
val view: View = binding.root
|
||||
|
||||
|
||||
Utils.setUnderlinedText(binding.seeMore, R.string.nominated_see_more, requireContext())
|
||||
binding.seeMore.setUnderlinedText(R.string.nominated_see_more)
|
||||
|
||||
if (isCategoryImage) {
|
||||
binding.authorLinearLayout.visibility = View.VISIBLE
|
||||
|
|
@ -907,7 +912,7 @@ class MediaDetailFragment : CommonsDaggerSupportFragment(), CategoryEditHelper.C
|
|||
private fun onMediaDetailLicenceClicked() {
|
||||
val url: String? = media!!.licenseUrl
|
||||
if (!StringUtils.isBlank(url) && activity != null) {
|
||||
Utils.handleWebUrl(activity, Uri.parse(url))
|
||||
handleWebUrl(requireContext(), Uri.parse(url))
|
||||
} else {
|
||||
viewUtil.showShortToast(requireActivity(), getString(R.string.null_url))
|
||||
}
|
||||
|
|
@ -915,17 +920,17 @@ class MediaDetailFragment : CommonsDaggerSupportFragment(), CategoryEditHelper.C
|
|||
|
||||
private fun onMediaDetailCoordinatesClicked() {
|
||||
if (media!!.coordinates != null && activity != null) {
|
||||
Utils.handleGeoCoordinates(activity, media!!.coordinates)
|
||||
handleGeoCoordinates(requireContext(), media!!.coordinates!!)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onCopyWikicodeClicked() {
|
||||
val data: String =
|
||||
"[[" + media!!.filename + "|thumb|" + media!!.fallbackDescription + "]]"
|
||||
Utils.copy("wikiCode", data, context)
|
||||
requireContext().copyToClipboard("wikiCode", data)
|
||||
Timber.d("Generated wikidata copy code: %s", data)
|
||||
|
||||
Toast.makeText(context, getString(R.string.wikicode_copied), Toast.LENGTH_SHORT)
|
||||
Toast.makeText(requireContext(), getString(R.string.wikicode_copied), Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
}
|
||||
|
||||
|
|
@ -1646,7 +1651,7 @@ class MediaDetailFragment : CommonsDaggerSupportFragment(), CategoryEditHelper.C
|
|||
getString(R.string.cancel),
|
||||
{
|
||||
val reason: String = input.text.toString()
|
||||
onDeleteClickeddialogtext(reason)
|
||||
onDeleteClickedDialogText(reason)
|
||||
},
|
||||
{},
|
||||
input
|
||||
|
|
@ -1700,26 +1705,48 @@ class MediaDetailFragment : CommonsDaggerSupportFragment(), CategoryEditHelper.C
|
|||
resultSingle
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe { _ ->
|
||||
if (applicationKvStore.getBoolean(
|
||||
String.format(
|
||||
NOMINATING_FOR_DELETION_MEDIA, media!!.imageUrl
|
||||
), false
|
||||
)
|
||||
) {
|
||||
applicationKvStore.remove(
|
||||
String.format(
|
||||
NOMINATING_FOR_DELETION_MEDIA,
|
||||
media!!.imageUrl
|
||||
)
|
||||
)
|
||||
callback!!.nominatingForDeletion(index)
|
||||
}
|
||||
}
|
||||
.subscribe(this::handleDeletionResult, this::handleDeletionError);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables Progress Bar and Update delete button text.
|
||||
*/
|
||||
private fun disableProgressBar() {
|
||||
activity?.run {
|
||||
runOnUiThread(Runnable {
|
||||
binding.progressBarDeletion.visibility = View.GONE
|
||||
})
|
||||
} ?: return // Prevent NullPointerException when fragment is not attached to activity
|
||||
}
|
||||
|
||||
private fun handleDeletionResult(success: Boolean) {
|
||||
if (success) {
|
||||
binding.nominateDeletion.text = getString(R.string.nominated_for_deletion_btn)
|
||||
ViewUtil.showLongSnackbar(requireView(), getString(R.string.nominated_for_deletion))
|
||||
disableProgressBar()
|
||||
checkAndClearDeletionFlag()
|
||||
} else {
|
||||
disableProgressBar()
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleDeletionError(throwable: Throwable) {
|
||||
throwable.printStackTrace()
|
||||
disableProgressBar()
|
||||
checkAndClearDeletionFlag()
|
||||
}
|
||||
|
||||
private fun checkAndClearDeletionFlag() {
|
||||
if (applicationKvStore
|
||||
.getBoolean(format(NOMINATING_FOR_DELETION_MEDIA, media!!.imageUrl), false)
|
||||
) {
|
||||
applicationKvStore.remove(format(NOMINATING_FOR_DELETION_MEDIA, media!!.imageUrl))
|
||||
callback!!.nominatingForDeletion(index)
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
private fun onDeleteClickeddialogtext(reason: String) {
|
||||
private fun onDeleteClickedDialogText(reason: String) {
|
||||
applicationKvStore.putBoolean(
|
||||
String.format(
|
||||
NOMINATING_FOR_DELETION_MEDIA,
|
||||
|
|
@ -1736,27 +1763,12 @@ class MediaDetailFragment : CommonsDaggerSupportFragment(), CategoryEditHelper.C
|
|||
resultSingletext
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe { _ ->
|
||||
if (applicationKvStore.getBoolean(
|
||||
String.format(
|
||||
NOMINATING_FOR_DELETION_MEDIA, media!!.imageUrl
|
||||
), false
|
||||
)
|
||||
) {
|
||||
applicationKvStore.remove(
|
||||
String.format(
|
||||
NOMINATING_FOR_DELETION_MEDIA,
|
||||
media!!.imageUrl
|
||||
)
|
||||
)
|
||||
callback!!.nominatingForDeletion(index)
|
||||
}
|
||||
}
|
||||
.subscribe(this::handleDeletionResult, this::handleDeletionError);
|
||||
}
|
||||
|
||||
private fun onSeeMoreClicked() {
|
||||
if (binding.nominatedDeletionBanner.visibility == View.VISIBLE && activity != null) {
|
||||
Utils.handleWebUrl(activity, Uri.parse(media!!.pageTitle.mobileUri))
|
||||
handleWebUrl(requireContext(), Uri.parse(media!!.pageTitle.mobileUri))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package fr.free.nrw.commons.media;
|
||||
|
||||
import static fr.free.nrw.commons.Utils.handleWebUrl;
|
||||
import static fr.free.nrw.commons.utils.UrlUtilsKt.handleWebUrl;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
|
|
@ -31,7 +31,7 @@ import com.google.android.material.snackbar.Snackbar;
|
|||
import fr.free.nrw.commons.CommonsApplication;
|
||||
import fr.free.nrw.commons.Media;
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.Utils;
|
||||
import fr.free.nrw.commons.utils.ClipboardUtils;
|
||||
import fr.free.nrw.commons.auth.SessionManager;
|
||||
import fr.free.nrw.commons.bookmarks.models.Bookmark;
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesContentProvider;
|
||||
|
|
@ -216,7 +216,7 @@ public class MediaDetailPagerFragment extends CommonsDaggerSupportFragment imple
|
|||
return true;
|
||||
case R.id.menu_copy_link:
|
||||
String uri = m.getPageTitle().getCanonicalUri();
|
||||
Utils.copy("shareLink", uri, requireContext());
|
||||
ClipboardUtils.copy("shareLink", uri, requireContext());
|
||||
Timber.d("Copied share link to clipboard: %s", uri);
|
||||
Toast.makeText(requireContext(), getString(R.string.menu_link_copied),
|
||||
Toast.LENGTH_SHORT).show();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package fr.free.nrw.commons.media
|
||||
|
||||
import fr.free.nrw.commons.OkHttpConnectionFactory.UnsuccessfulResponseInterceptor.SUPPRESS_ERROR_LOG_HEADER
|
||||
import fr.free.nrw.commons.SUPPRESS_ERROR_LOG_HEADER
|
||||
import fr.free.nrw.commons.wikidata.mwapi.MwQueryResponse
|
||||
import io.reactivex.Single
|
||||
import retrofit2.http.GET
|
||||
|
|
|
|||
|
|
@ -281,6 +281,7 @@ class OkHttpJsonApiClient @Inject constructor(
|
|||
FeedbackResponse::class.java
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
return@fromCallable FeedbackResponse(0, 0, 0, FeaturedImages(0, 0), 0, "")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ import fr.free.nrw.commons.BuildConfig
|
|||
import fr.free.nrw.commons.CommonsApplication
|
||||
import fr.free.nrw.commons.CommonsApplication.ActivityLogoutListener
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.WelcomeActivity
|
||||
import fr.free.nrw.commons.actions.PageEditClient
|
||||
import fr.free.nrw.commons.databinding.FragmentMoreBottomSheetBinding
|
||||
import fr.free.nrw.commons.di.ApplicationlessInjection
|
||||
|
|
@ -32,6 +31,7 @@ import fr.free.nrw.commons.logging.CommonsLogSender
|
|||
import fr.free.nrw.commons.profile.ProfileActivity
|
||||
import fr.free.nrw.commons.review.ReviewActivity
|
||||
import fr.free.nrw.commons.settings.SettingsActivity
|
||||
import fr.free.nrw.commons.startWelcome
|
||||
import io.reactivex.Single
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
|
|
@ -241,7 +241,7 @@ class MoreBottomSheetFragment : BottomSheetDialogFragment() {
|
|||
}
|
||||
|
||||
fun onTutorialClicked() {
|
||||
WelcomeActivity.startYourself(requireActivity())
|
||||
requireContext().startWelcome()
|
||||
}
|
||||
|
||||
fun onSettingsClicked() {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,14 @@
|
|||
package fr.free.nrw.commons.nearby;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
|
||||
import android.location.Location;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import fr.free.nrw.commons.nearby.model.NearbyQueryParams;
|
||||
import fr.free.nrw.commons.nearby.model.NearbyQueryParams.Radial;
|
||||
import fr.free.nrw.commons.nearby.model.NearbyQueryParams.Rectangular;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
|
@ -46,13 +50,14 @@ public class NearbyPlaces {
|
|||
* @param customQuery
|
||||
* @return list of places obtained
|
||||
*/
|
||||
@NonNull
|
||||
List<Place> radiusExpander(final LatLng currentLatLng, final String lang,
|
||||
final boolean returnClosestResult, @Nullable final String customQuery) throws Exception {
|
||||
|
||||
final int minResults;
|
||||
final double maxRadius;
|
||||
|
||||
List<Place> places = Collections.emptyList();
|
||||
List<Place> places = emptyList();
|
||||
|
||||
// If returnClosestResult is true, then this means that we are trying to get closest point
|
||||
// to use in cardView in Contributions fragment
|
||||
|
|
@ -113,6 +118,7 @@ public class NearbyPlaces {
|
|||
* @return A list of places obtained from the Wikidata query.
|
||||
* @throws Exception If an error occurs during the retrieval process.
|
||||
*/
|
||||
@NonNull
|
||||
public List<Place> getFromWikidataQuery(
|
||||
final fr.free.nrw.commons.location.LatLng centerPoint,
|
||||
final fr.free.nrw.commons.location.LatLng screenTopRight,
|
||||
|
|
@ -120,11 +126,11 @@ public class NearbyPlaces {
|
|||
final boolean shouldQueryForMonuments,
|
||||
@Nullable final String customQuery) throws Exception {
|
||||
if (customQuery != null) {
|
||||
return okHttpJsonApiClient
|
||||
.getNearbyPlaces(
|
||||
new NearbyQueryParams.Rectangular(screenTopRight, screenBottomLeft), lang,
|
||||
final List<Place> nearbyPlaces = okHttpJsonApiClient.getNearbyPlaces(
|
||||
new Rectangular(screenTopRight, screenBottomLeft), lang,
|
||||
shouldQueryForMonuments,
|
||||
customQuery);
|
||||
return nearbyPlaces != null ? nearbyPlaces : emptyList();
|
||||
}
|
||||
|
||||
final int lowerLimit = 1000, upperLimit = 1500;
|
||||
|
|
@ -141,9 +147,10 @@ public class NearbyPlaces {
|
|||
final int itemCount = okHttpJsonApiClient.getNearbyItemCount(
|
||||
new NearbyQueryParams.Rectangular(screenTopRight, screenBottomLeft));
|
||||
if (itemCount < upperLimit) {
|
||||
return okHttpJsonApiClient.getNearbyPlaces(
|
||||
new NearbyQueryParams.Rectangular(screenTopRight, screenBottomLeft), lang,
|
||||
final List<Place> nearbyPlaces = okHttpJsonApiClient.getNearbyPlaces(
|
||||
new Rectangular(screenTopRight, screenBottomLeft), lang,
|
||||
shouldQueryForMonuments, null);
|
||||
return nearbyPlaces != null ? nearbyPlaces : emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -175,9 +182,10 @@ public class NearbyPlaces {
|
|||
maxRadius = targetRadius - 1;
|
||||
}
|
||||
}
|
||||
return okHttpJsonApiClient.getNearbyPlaces(
|
||||
new NearbyQueryParams.Radial(centerPoint, targetRadius / 100f), lang, shouldQueryForMonuments,
|
||||
final List<Place> nearbyPlaces = okHttpJsonApiClient.getNearbyPlaces(
|
||||
new Radial(centerPoint, targetRadius / 100f), lang, shouldQueryForMonuments,
|
||||
null);
|
||||
return nearbyPlaces != null ? nearbyPlaces : emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -10,12 +10,13 @@ import androidx.activity.result.ActivityResultLauncher
|
|||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.widget.PopupMenu
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.Utils
|
||||
import fr.free.nrw.commons.auth.LoginActivity
|
||||
import fr.free.nrw.commons.contributions.ContributionController
|
||||
import fr.free.nrw.commons.kvstore.JsonKvStore
|
||||
import fr.free.nrw.commons.nearby.Place
|
||||
import fr.free.nrw.commons.utils.ActivityUtils
|
||||
import fr.free.nrw.commons.utils.handleGeoCoordinates
|
||||
import fr.free.nrw.commons.utils.handleWebUrl
|
||||
import fr.free.nrw.commons.wikidata.WikidataConstants
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
|
@ -104,7 +105,7 @@ class CommonPlaceClickActions
|
|||
|
||||
fun onDirectionsClicked(): (Place) -> Unit =
|
||||
{
|
||||
Utils.handleGeoCoordinates(activity, it.getLocation())
|
||||
handleGeoCoordinates(activity, it.getLocation())
|
||||
}
|
||||
|
||||
private fun storeSharedPrefs(selectedPlace: Place) {
|
||||
|
|
@ -113,7 +114,7 @@ class CommonPlaceClickActions
|
|||
}
|
||||
|
||||
private fun openWebView(link: Uri): Boolean {
|
||||
Utils.handleWebUrl(activity, link)
|
||||
handleWebUrl(activity, link)
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -58,12 +58,10 @@ import fr.free.nrw.commons.CommonsApplication
|
|||
import fr.free.nrw.commons.MapController.NearbyPlacesInfo
|
||||
import fr.free.nrw.commons.Media
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.Utils
|
||||
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao
|
||||
import fr.free.nrw.commons.contributions.ContributionController
|
||||
import fr.free.nrw.commons.contributions.MainActivity
|
||||
import fr.free.nrw.commons.contributions.MainActivity.ActiveFragment
|
||||
import fr.free.nrw.commons.customselector.ui.selector.ImageLoader
|
||||
import fr.free.nrw.commons.databinding.FragmentNearbyParentBinding
|
||||
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment
|
||||
import fr.free.nrw.commons.filepicker.FilePicker
|
||||
|
|
@ -76,7 +74,6 @@ import fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType
|
|||
import fr.free.nrw.commons.location.LocationUpdateListener
|
||||
import fr.free.nrw.commons.media.MediaClient
|
||||
import fr.free.nrw.commons.media.MediaDetailPagerFragment
|
||||
import fr.free.nrw.commons.media.MediaDetailPagerFragment.MediaDetailProvider
|
||||
import fr.free.nrw.commons.navtab.NavTab
|
||||
import fr.free.nrw.commons.nearby.BottomSheetAdapter
|
||||
import fr.free.nrw.commons.nearby.BottomSheetAdapter.ItemClickListener
|
||||
|
|
@ -105,6 +102,10 @@ import fr.free.nrw.commons.utils.NearbyFABUtils.removeAnchorFromFAB
|
|||
import fr.free.nrw.commons.utils.NetworkUtils.isInternetConnectionEstablished
|
||||
import fr.free.nrw.commons.utils.SystemThemeUtils
|
||||
import fr.free.nrw.commons.utils.ViewUtil.showLongToast
|
||||
import fr.free.nrw.commons.utils.copyToClipboard
|
||||
import fr.free.nrw.commons.utils.handleGeoCoordinates
|
||||
import fr.free.nrw.commons.utils.handleWebUrl
|
||||
import fr.free.nrw.commons.utils.isMonumentsEnabled
|
||||
import fr.free.nrw.commons.wikidata.WikidataConstants
|
||||
import fr.free.nrw.commons.wikidata.WikidataEditListener
|
||||
import fr.free.nrw.commons.wikidata.WikidataEditListener.WikidataP18EditListener
|
||||
|
|
@ -123,6 +124,7 @@ import org.osmdroid.views.CustomZoomButtonsController
|
|||
import org.osmdroid.views.MapView
|
||||
import org.osmdroid.views.overlay.MapEventsOverlay
|
||||
import org.osmdroid.views.overlay.Marker
|
||||
import org.osmdroid.views.overlay.Overlay
|
||||
import org.osmdroid.views.overlay.ScaleBarOverlay
|
||||
import org.osmdroid.views.overlay.ScaleDiskOverlay
|
||||
import org.osmdroid.views.overlay.TilesOverlay
|
||||
|
|
@ -139,7 +141,6 @@ import java.util.UUID
|
|||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Named
|
||||
import javax.sql.DataSource
|
||||
import kotlin.concurrent.Volatile
|
||||
|
||||
|
||||
|
|
@ -267,6 +268,9 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(),
|
|||
private var dataList: MutableList<BottomSheetItem>? = null
|
||||
private var bottomSheetAdapter: BottomSheetAdapter? = null
|
||||
|
||||
private var userLocationOverlay: Overlay? = null
|
||||
private var userLocationErrorOverlay: Overlay? = null
|
||||
|
||||
private val galleryPickLauncherForResult =
|
||||
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||
controller?.handleActivityResultWithCallback(
|
||||
|
|
@ -463,7 +467,7 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(),
|
|||
}
|
||||
}
|
||||
_isDarkTheme = systemThemeUtils?.isDeviceInNightMode() == true
|
||||
if (Utils.isMonumentsEnabled(Date())) {
|
||||
if (isMonumentsEnabled) {
|
||||
binding?.rlContainerWlmMonthMessage?.visibility = View.VISIBLE
|
||||
} else {
|
||||
binding?.rlContainerWlmMonthMessage?.visibility = View.GONE
|
||||
|
|
@ -724,7 +728,7 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(),
|
|||
val targetP = GeoPoint(target.latitude, target.longitude)
|
||||
mapCenter = targetP
|
||||
binding?.map?.controller?.setCenter(targetP)
|
||||
recenterMarkerToPosition(targetP)
|
||||
updateUserLocationOverlays(targetP, true)
|
||||
if (!isCameFromExploreMap()) {
|
||||
moveCameraToPosition(targetP)
|
||||
}
|
||||
|
|
@ -832,7 +836,7 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(),
|
|||
loadAnimations()
|
||||
setBottomSheetCallbacks()
|
||||
addActionToTitle()
|
||||
if (!Utils.isMonumentsEnabled(Date())) {
|
||||
if (!isMonumentsEnabled) {
|
||||
NearbyFilterState.setWlmSelected(false)
|
||||
}
|
||||
}
|
||||
|
|
@ -1013,11 +1017,10 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(),
|
|||
*/
|
||||
private fun addActionToTitle() {
|
||||
binding!!.bottomSheetDetails.title.setOnLongClickListener { view ->
|
||||
Utils.copy(
|
||||
"place", binding!!.bottomSheetDetails.title.text.toString(),
|
||||
context
|
||||
requireContext().copyToClipboard(
|
||||
"place", binding!!.bottomSheetDetails.title.text.toString()
|
||||
)
|
||||
Toast.makeText(context, fr.free.nrw.commons.R.string.text_copy, Toast.LENGTH_SHORT)
|
||||
Toast.makeText(requireContext(), fr.free.nrw.commons.R.string.text_copy, Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
true
|
||||
}
|
||||
|
|
@ -1576,7 +1579,7 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(),
|
|||
searchLatLng,
|
||||
false,
|
||||
true,
|
||||
Utils.isMonumentsEnabled(Date()),
|
||||
isMonumentsEnabled,
|
||||
customQuery
|
||||
)
|
||||
}
|
||||
|
|
@ -1629,7 +1632,7 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(),
|
|||
searchLatLng,
|
||||
false,
|
||||
true,
|
||||
Utils.isMonumentsEnabled(Date()),
|
||||
isMonumentsEnabled,
|
||||
customQuery
|
||||
)
|
||||
}
|
||||
|
|
@ -1863,6 +1866,8 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(),
|
|||
lastKnownLocation = latLng
|
||||
NearbyController.currentLocation = lastKnownLocation
|
||||
presenter!!.updateMapAndList(locationChangeType)
|
||||
|
||||
updateUserLocationOverlays(GeoPoint(latLng.latitude, latLng.longitude), true)
|
||||
}
|
||||
|
||||
override fun onLocationChangedSignificantly(latLng: LatLng) {
|
||||
|
|
@ -2644,43 +2649,14 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(),
|
|||
*/
|
||||
override fun clearAllMarkers() {
|
||||
binding!!.map.overlayManager.clear()
|
||||
binding!!.map.invalidate()
|
||||
val geoPoint = mapCenter
|
||||
if (geoPoint != null) {
|
||||
val diskOverlay =
|
||||
ScaleDiskOverlay(
|
||||
this.context,
|
||||
geoPoint, 2000, UnitOfMeasure.foot
|
||||
)
|
||||
val circlePaint = Paint()
|
||||
circlePaint.color = Color.rgb(128, 128, 128)
|
||||
circlePaint.style = Paint.Style.STROKE
|
||||
circlePaint.strokeWidth = 2f
|
||||
diskOverlay.setCirclePaint2(circlePaint)
|
||||
val diskPaint = Paint()
|
||||
diskPaint.color = Color.argb(40, 128, 128, 128)
|
||||
diskPaint.style = Paint.Style.FILL_AND_STROKE
|
||||
diskOverlay.setCirclePaint1(diskPaint)
|
||||
diskOverlay.setDisplaySizeMin(900)
|
||||
diskOverlay.setDisplaySizeMax(1700)
|
||||
binding!!.map.overlays.add(diskOverlay)
|
||||
val startMarker = Marker(
|
||||
binding!!.map
|
||||
)
|
||||
startMarker.position = geoPoint
|
||||
startMarker.setAnchor(
|
||||
Marker.ANCHOR_CENTER,
|
||||
Marker.ANCHOR_BOTTOM
|
||||
)
|
||||
startMarker.icon =
|
||||
getDrawable(
|
||||
this.requireContext(),
|
||||
fr.free.nrw.commons.R.drawable.current_location_marker
|
||||
)
|
||||
startMarker.title = "Your Location"
|
||||
startMarker.textLabelFontSize = 24
|
||||
binding!!.map.overlays.add(startMarker)
|
||||
|
||||
var geoPoint = mapCenter
|
||||
val lastLatLng = locationManager.getLastLocation()
|
||||
if (lastLatLng != null) {
|
||||
geoPoint = GeoPoint(lastLatLng.latitude, lastLatLng.longitude)
|
||||
}
|
||||
updateUserLocationOverlays(geoPoint, false)
|
||||
|
||||
val scaleBarOverlay = ScaleBarOverlay(binding!!.map)
|
||||
scaleBarOverlay.setScaleBarOffset(15, 25)
|
||||
val barPaint = Paint()
|
||||
|
|
@ -2690,6 +2666,7 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(),
|
|||
binding!!.map.overlays.add(scaleBarOverlay)
|
||||
binding!!.map.overlays.add(mapEventsOverlay)
|
||||
binding!!.map.setMultiTouchControls(true)
|
||||
binding!!.map.invalidate()
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -2700,45 +2677,149 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(),
|
|||
private fun recenterMarkerToPosition(geoPoint: GeoPoint?) {
|
||||
geoPoint?.let {
|
||||
binding?.map?.controller?.setCenter(it)
|
||||
val overlays = binding?.map?.overlays ?: return@let
|
||||
|
||||
// Remove markers and disks using index-based removal
|
||||
var i = 0
|
||||
while (i < overlays.size) {
|
||||
when (overlays[i]) {
|
||||
is Marker, is ScaleDiskOverlay -> overlays.removeAt(i)
|
||||
else -> i++
|
||||
}
|
||||
}
|
||||
|
||||
// Add disk overlay
|
||||
ScaleDiskOverlay(context, it, 2000, UnitOfMeasure.foot).apply {
|
||||
setCirclePaint2(Paint().apply {
|
||||
color = Color.rgb(128, 128, 128)
|
||||
style = Paint.Style.STROKE
|
||||
strokeWidth = 2f
|
||||
})
|
||||
setCirclePaint1(Paint().apply {
|
||||
color = Color.argb(40, 128, 128, 128)
|
||||
style = Paint.Style.FILL_AND_STROKE
|
||||
})
|
||||
setDisplaySizeMin(900)
|
||||
setDisplaySizeMax(1700)
|
||||
overlays.add(this)
|
||||
}
|
||||
|
||||
// Add marker
|
||||
Marker(binding?.map).apply {
|
||||
position = it
|
||||
setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_BOTTOM)
|
||||
icon = getDrawable(context, R.drawable.current_location_marker)
|
||||
title = "Your Location"
|
||||
textLabelFontSize = 24
|
||||
overlays.add(this)
|
||||
}
|
||||
updateUserLocationOverlays(it, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the user current location overlays (both the location and error overlays) by
|
||||
* replacing any existing location overlays with new overlays at the given GeoPoint. If there
|
||||
* are no existing location and error overlays, then new overlays are added.
|
||||
*
|
||||
* @param geoPoint The GeoPoint representing the user's current location.
|
||||
* @param invalidate If true, the map overlays will be invalidated after the user
|
||||
* location overlays are updated/added. If false, the overlays will not be invalidated.
|
||||
*/
|
||||
private fun updateUserLocationOverlays(geoPoint: GeoPoint?, invalidate: Boolean) {
|
||||
geoPoint?.let{
|
||||
updateUserLocationOverlay(geoPoint)
|
||||
updateUserLocationErrorOverlay(geoPoint)
|
||||
}
|
||||
|
||||
if (invalidate) {
|
||||
binding!!.map.invalidate()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the user location error overlay by either replacing it with or adding a new one.
|
||||
*
|
||||
* If the user location error overlay is null, the new overlay is added. If the
|
||||
* overlay is not null, it is replaced by the new overlay.
|
||||
*
|
||||
* @param geoPoint The GeoPoint representing the user's location
|
||||
*/
|
||||
private fun updateUserLocationErrorOverlay(geoPoint: GeoPoint) {
|
||||
val overlays = binding?.map?.overlays ?: return
|
||||
|
||||
// Multiply accuracy by 2 to get 95% confidence interval
|
||||
val accuracy = getCurrentLocationAccuracy() * 2
|
||||
val overlay = createCurrentLocationErrorOverlay(this.context, geoPoint,
|
||||
(accuracy).toInt(), UnitOfMeasure.meter)
|
||||
|
||||
val index = overlays.indexOf(userLocationErrorOverlay)
|
||||
|
||||
if (userLocationErrorOverlay == null || index == -1) {
|
||||
overlays.add(overlay)
|
||||
} else {
|
||||
overlays[index] = overlay
|
||||
}
|
||||
|
||||
userLocationErrorOverlay = overlay
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the user location overlay by either replacing it with or adding a new one.
|
||||
*
|
||||
* If the user location overlay is null, the new overlay is added. If the
|
||||
* overlay is not null, it is replaced by the new overlay.
|
||||
*
|
||||
* @param geoPoint The GeoPoint representing the user's location
|
||||
*/
|
||||
private fun updateUserLocationOverlay(geoPoint: GeoPoint) {
|
||||
val overlays = binding?.map?.overlays ?: return
|
||||
|
||||
val overlay = createCurrentLocationOverlay(geoPoint)
|
||||
|
||||
val index = overlays.indexOf(userLocationOverlay)
|
||||
|
||||
if (userLocationOverlay == null || index == -1) {
|
||||
overlays.add(overlay)
|
||||
} else {
|
||||
overlays[index] = overlay
|
||||
}
|
||||
|
||||
userLocationOverlay = overlay
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The accuracy of the current location with a confidence at the 68th percentile.
|
||||
* Units are in meters. Returning 0 may indicate failure.
|
||||
*/
|
||||
private fun getCurrentLocationAccuracy(): Float {
|
||||
var accuracy = 0f
|
||||
val lastLocation = locationManager.getLastLocation()
|
||||
if (lastLocation != null) {
|
||||
accuracy = lastLocation.accuracy
|
||||
}
|
||||
|
||||
return accuracy
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the current location overlay
|
||||
*
|
||||
* @param geoPoint The GeoPoint where the current location overlay will be placed.
|
||||
*
|
||||
* @return The current location overlay as a Marker
|
||||
*/
|
||||
private fun createCurrentLocationOverlay(geoPoint: GeoPoint): Marker {
|
||||
val currentLocationOverlay = Marker(
|
||||
binding!!.map
|
||||
)
|
||||
currentLocationOverlay.position = geoPoint
|
||||
currentLocationOverlay.icon =
|
||||
getDrawable(
|
||||
this.requireContext(),
|
||||
fr.free.nrw.commons.R.drawable.current_location_marker
|
||||
)
|
||||
currentLocationOverlay.title = "Your Location"
|
||||
currentLocationOverlay.textLabelFontSize = 24
|
||||
currentLocationOverlay.setAnchor(0.5f, 0.5f)
|
||||
|
||||
return currentLocationOverlay
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the location error overlay to show the user how accurate the current location
|
||||
* overlay is. The edge of the disk is the 95% confidence interval.
|
||||
*
|
||||
* @param context The Android context
|
||||
* @param point The user's location as a GeoPoint
|
||||
* @param value The radius of the disk
|
||||
* @param unitOfMeasure The unit of measurement of the value/disk radius.
|
||||
*
|
||||
* @return The location error overlay as a ScaleDiskOverlay.
|
||||
*/
|
||||
private fun createCurrentLocationErrorOverlay(context: Context?, point: GeoPoint, value: Int,
|
||||
unitOfMeasure: UnitOfMeasure): ScaleDiskOverlay {
|
||||
val scaleDisk = ScaleDiskOverlay(context, point, value, unitOfMeasure)
|
||||
|
||||
scaleDisk.setCirclePaint2(Paint().apply {
|
||||
color = Color.rgb(128, 128, 128)
|
||||
style = Paint.Style.STROKE
|
||||
strokeWidth = 2f
|
||||
})
|
||||
|
||||
scaleDisk.setCirclePaint1(Paint().apply {
|
||||
color = Color.argb(40, 128, 128, 128)
|
||||
style = Paint.Style.FILL_AND_STROKE
|
||||
})
|
||||
|
||||
return scaleDisk
|
||||
}
|
||||
|
||||
private fun moveCameraToPosition(geoPoint: GeoPoint) {
|
||||
binding!!.map.controller.animateTo(geoPoint)
|
||||
}
|
||||
|
|
@ -2772,14 +2853,14 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(),
|
|||
|
||||
R.drawable.ic_directions_black_24dp -> {
|
||||
selectedPlace?.let {
|
||||
Utils.handleGeoCoordinates(this.context, it.getLocation())
|
||||
handleGeoCoordinates(requireContext(), it.getLocation())
|
||||
binding?.map?.zoomLevelDouble ?: 0.0
|
||||
}
|
||||
}
|
||||
|
||||
R.drawable.ic_wikidata_logo_24dp -> {
|
||||
selectedPlace?.siteLinks?.wikidataLink?.let {
|
||||
Utils.handleWebUrl(this.context, it)
|
||||
handleWebUrl(requireContext(), it)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2797,13 +2878,13 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(),
|
|||
|
||||
R.drawable.ic_wikipedia_logo_24dp -> {
|
||||
selectedPlace?.siteLinks?.wikipediaLink?.let {
|
||||
Utils.handleWebUrl(this.context, it)
|
||||
handleWebUrl(requireContext(), it)
|
||||
}
|
||||
}
|
||||
|
||||
R.drawable.ic_commons_icon_vector -> {
|
||||
selectedPlace?.siteLinks?.commonsLink?.let {
|
||||
Utils.handleWebUrl(this.context, it)
|
||||
handleWebUrl(requireContext(), it)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -351,6 +351,7 @@ class NearbyParentFragmentPresenter
|
|||
pic = repoPlace.pic ?: ""
|
||||
exists = repoPlace.exists ?: true
|
||||
longDescription = repoPlace.longDescription ?: ""
|
||||
language = repoPlace.language
|
||||
}
|
||||
} else {
|
||||
indicesToUpdate.add(i)
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
|||
import com.google.android.material.snackbar.Snackbar
|
||||
import fr.free.nrw.commons.CommonsApplication
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.Utils
|
||||
import fr.free.nrw.commons.auth.SessionManager
|
||||
import fr.free.nrw.commons.auth.csrf.InvalidLoginTokenException
|
||||
import fr.free.nrw.commons.databinding.ActivityNotificationBinding
|
||||
|
|
@ -22,6 +21,7 @@ import fr.free.nrw.commons.notification.models.NotificationType
|
|||
import fr.free.nrw.commons.theme.BaseActivity
|
||||
import fr.free.nrw.commons.utils.NetworkUtils
|
||||
import fr.free.nrw.commons.utils.ViewUtil
|
||||
import fr.free.nrw.commons.utils.handleWebUrl
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
|
|
@ -197,7 +197,7 @@ class NotificationActivity : BaseActivity() {
|
|||
|
||||
private fun handleUrl(url: String?) {
|
||||
if (url.isNullOrEmpty()) return
|
||||
Utils.handleWebUrl(this, Uri.parse(url))
|
||||
handleWebUrl(this, Uri.parse(url))
|
||||
}
|
||||
|
||||
private fun setItems(notificationList: List<Notification>?) {
|
||||
|
|
|
|||
|
|
@ -3,16 +3,17 @@ package fr.free.nrw.commons.profile
|
|||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.*
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.FileProvider
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.Fragment
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.Utils
|
||||
import fr.free.nrw.commons.ViewPagerAdapter
|
||||
import fr.free.nrw.commons.auth.SessionManager
|
||||
import fr.free.nrw.commons.contributions.ContributionsFragment
|
||||
|
|
@ -23,7 +24,7 @@ import fr.free.nrw.commons.theme.BaseActivity
|
|||
import fr.free.nrw.commons.utils.DialogUtil
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.util.*
|
||||
import java.util.Locale
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
|
|
@ -71,7 +72,7 @@ class ProfileActivity : BaseActivity() {
|
|||
title = userName
|
||||
shouldShowContributions = intent.getBooleanExtra(KEY_SHOULD_SHOW_CONTRIBUTIONS, false)
|
||||
|
||||
viewPagerAdapter = ViewPagerAdapter(supportFragmentManager)
|
||||
viewPagerAdapter = ViewPagerAdapter(this, supportFragmentManager)
|
||||
binding.viewPager.adapter = viewPagerAdapter
|
||||
binding.tabLayout.setupWithViewPager(binding.viewPager)
|
||||
setTabs()
|
||||
|
|
@ -83,39 +84,23 @@ class ProfileActivity : BaseActivity() {
|
|||
}
|
||||
|
||||
private fun setTabs() {
|
||||
val fragmentList = mutableListOf<Fragment>()
|
||||
val titleList = mutableListOf<String>()
|
||||
|
||||
// Add Achievements tab
|
||||
achievementsFragment = AchievementsFragment().apply {
|
||||
arguments = Bundle().apply {
|
||||
putString(KEY_USERNAME, userName)
|
||||
}
|
||||
arguments = bundleOf(KEY_USERNAME to userName)
|
||||
}
|
||||
fragmentList.add(achievementsFragment)
|
||||
titleList.add(resources.getString(R.string.achievements_tab_title).uppercase())
|
||||
|
||||
// Add Leaderboard tab
|
||||
leaderboardFragment = LeaderboardFragment().apply {
|
||||
arguments = Bundle().apply {
|
||||
putString(KEY_USERNAME, userName)
|
||||
}
|
||||
arguments = bundleOf(KEY_USERNAME to userName)
|
||||
}
|
||||
fragmentList.add(leaderboardFragment)
|
||||
titleList.add(resources.getString(R.string.leaderboard_tab_title).uppercase(Locale.ROOT))
|
||||
|
||||
// Add Contributions tab
|
||||
contributionsFragment = ContributionsFragment().apply {
|
||||
arguments = Bundle().apply {
|
||||
putString(KEY_USERNAME, userName)
|
||||
}
|
||||
}
|
||||
contributionsFragment?.let {
|
||||
fragmentList.add(it)
|
||||
titleList.add(getString(R.string.contributions_fragment).uppercase(Locale.ROOT))
|
||||
arguments = bundleOf(KEY_USERNAME to userName)
|
||||
}
|
||||
|
||||
viewPagerAdapter.setTabData(fragmentList, titleList)
|
||||
viewPagerAdapter.setTabs(
|
||||
R.string.achievements_tab_title to achievementsFragment,
|
||||
R.string.leaderboard_tab_title to leaderboardFragment,
|
||||
R.string.contributions_fragment to contributionsFragment!!
|
||||
)
|
||||
viewPagerAdapter.notifyDataSetChanged()
|
||||
}
|
||||
|
||||
|
|
@ -133,7 +118,7 @@ class ProfileActivity : BaseActivity() {
|
|||
return when (item.itemId) {
|
||||
R.id.share_app_icon -> {
|
||||
val rootView = window.decorView.findViewById<View>(android.R.id.content)
|
||||
val screenShot = Utils.getScreenShot(rootView)
|
||||
val screenShot = getScreenShot(rootView)
|
||||
if (screenShot == null) {
|
||||
Log.e("ERROR", "ScreenShot is null")
|
||||
return false
|
||||
|
|
@ -212,6 +197,24 @@ class ProfileActivity : BaseActivity() {
|
|||
binding.tabLayout.visibility = if (isVisible) View.VISIBLE else View.GONE
|
||||
}
|
||||
|
||||
/**
|
||||
* To take screenshot of the screen and return it in Bitmap format
|
||||
*
|
||||
* @param view
|
||||
* @return
|
||||
*/
|
||||
fun getScreenShot(view: View): Bitmap? {
|
||||
val screenView = view.rootView
|
||||
screenView.isDrawingCacheEnabled = true
|
||||
val drawingCache = screenView.drawingCache
|
||||
if (drawingCache != null) {
|
||||
val bitmap = Bitmap.createBitmap(drawingCache)
|
||||
screenView.isDrawingCacheEnabled = false
|
||||
return bitmap
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val KEY_USERNAME = "username"
|
||||
const val KEY_SHOULD_SHOW_CONTRIBUTIONS = "shouldShowContributions"
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ import com.google.android.material.badge.BadgeDrawable
|
|||
import com.google.android.material.badge.BadgeUtils
|
||||
import com.google.android.material.badge.ExperimentalBadgeUtils
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.Utils
|
||||
import fr.free.nrw.commons.auth.SessionManager
|
||||
import fr.free.nrw.commons.databinding.FragmentAchievementsBinding
|
||||
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment
|
||||
|
|
@ -27,6 +26,7 @@ import fr.free.nrw.commons.utils.ConfigUtils.isBetaFlavour
|
|||
import fr.free.nrw.commons.utils.DialogUtil.showAlertDialog
|
||||
import fr.free.nrw.commons.utils.ViewUtil.showDismissibleSnackBar
|
||||
import fr.free.nrw.commons.utils.ViewUtil.showLongToast
|
||||
import fr.free.nrw.commons.utils.handleWebUrl
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
|
|
@ -524,7 +524,7 @@ class AchievementsFragment : CommonsDaggerSupportFragment(){
|
|||
getString(R.string.ok),
|
||||
getString(R.string.read_help_link),
|
||||
{},
|
||||
{ Utils.handleWebUrl(requireContext(), Uri.parse(helpLinkUrl)) },
|
||||
{ handleWebUrl(requireContext(), Uri.parse(helpLinkUrl)) },
|
||||
null
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,9 +33,7 @@ import com.karumi.dexter.MultiplePermissionsReport
|
|||
import com.karumi.dexter.PermissionToken
|
||||
import com.karumi.dexter.listener.PermissionRequest
|
||||
import com.karumi.dexter.listener.multi.MultiplePermissionsListener
|
||||
import fr.free.nrw.commons.BuildConfig.MOBILE_META_URL
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.Utils
|
||||
import fr.free.nrw.commons.activity.SingleWebViewActivity
|
||||
import fr.free.nrw.commons.campaigns.CampaignView
|
||||
import fr.free.nrw.commons.contributions.ContributionController
|
||||
|
|
@ -53,6 +51,7 @@ import fr.free.nrw.commons.utils.DialogUtil
|
|||
import fr.free.nrw.commons.utils.PermissionUtils
|
||||
import fr.free.nrw.commons.utils.StringUtil
|
||||
import fr.free.nrw.commons.utils.ViewUtil
|
||||
import fr.free.nrw.commons.utils.handleWebUrl
|
||||
import java.util.Locale
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Named
|
||||
|
|
@ -239,7 +238,10 @@ class SettingsFragment : PreferenceFragmentCompat() {
|
|||
|
||||
val betaTesterPreference: Preference? = findPreference("becomeBetaTester")
|
||||
betaTesterPreference?.setOnPreferenceClickListener {
|
||||
Utils.handleWebUrl(requireActivity(), Uri.parse(getString(R.string.beta_opt_in_link)))
|
||||
handleWebUrl(
|
||||
requireActivity(),
|
||||
Uri.parse(getString(R.string.beta_opt_in_link))
|
||||
)
|
||||
true
|
||||
}
|
||||
|
||||
|
|
@ -296,7 +298,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
|
|||
getString(R.string.ok),
|
||||
getString(R.string.read_help_link),
|
||||
{ },
|
||||
{ Utils.handleWebUrl(requireContext(), Uri.parse(GET_CONTENT_PICKER_HELP_URL)) },
|
||||
{ handleWebUrl(requireContext(), Uri.parse(GET_CONTENT_PICKER_HELP_URL)) },
|
||||
null
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
package fr.free.nrw.commons.upload
|
||||
|
||||
import android.content.Context
|
||||
import fr.free.nrw.commons.Utils
|
||||
import fr.free.nrw.commons.contributions.Contribution
|
||||
import fr.free.nrw.commons.filepicker.UploadableFile.DateTimeWithSource
|
||||
import fr.free.nrw.commons.settings.Prefs.Licenses
|
||||
import fr.free.nrw.commons.utils.ConfigUtils.getVersionNameWithSha
|
||||
import fr.free.nrw.commons.utils.getWikiLovesMonumentsYear
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Calendar
|
||||
|
|
@ -49,7 +49,7 @@ class PageContentsCreator @Inject constructor(private val context: Context) {
|
|||
String.format(
|
||||
Locale.ENGLISH,
|
||||
"{{Wiki Loves Monuments %d|1= %s}}\n",
|
||||
Utils.getWikiLovesMonumentsYear(Calendar.getInstance()),
|
||||
getWikiLovesMonumentsYear(Calendar.getInstance()),
|
||||
contribution.countryCode
|
||||
)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
package fr.free.nrw.commons.upload
|
||||
|
||||
import android.net.Uri
|
||||
import fr.free.nrw.commons.Utils
|
||||
import fr.free.nrw.commons.filepicker.MimeTypeMapWrapper.Companion.getExtensionFromMimeType
|
||||
import fr.free.nrw.commons.nearby.Place
|
||||
import fr.free.nrw.commons.utils.ImageUtils
|
||||
import fr.free.nrw.commons.utils.fixExtension
|
||||
|
||||
class UploadItem(
|
||||
var mediaUri: Uri?,
|
||||
|
|
@ -32,7 +32,7 @@ class UploadItem(
|
|||
* languages have been entered, the first language is used.
|
||||
*/
|
||||
val filename: String
|
||||
get() = Utils.fixExtension(
|
||||
get() = fixExtension(
|
||||
uploadMediaDetails[0].captionText,
|
||||
getExtensionFromMimeType(mimeType)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -28,8 +28,6 @@ class UploadProgressActivity : BaseActivity() {
|
|||
@Inject
|
||||
lateinit var contributionDao: ContributionDao
|
||||
|
||||
val fragmentList: MutableList<Fragment> = ArrayList()
|
||||
val titleList: MutableList<String> = ArrayList()
|
||||
var isPaused = true
|
||||
var isPendingIconsVisible = true
|
||||
var isErrorIconsVisisble = false
|
||||
|
|
@ -38,7 +36,7 @@ class UploadProgressActivity : BaseActivity() {
|
|||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityUploadProgressBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
viewPagerAdapter = ViewPagerAdapter(supportFragmentManager)
|
||||
viewPagerAdapter = ViewPagerAdapter(this, supportFragmentManager)
|
||||
binding.uploadProgressViewPager.setAdapter(viewPagerAdapter)
|
||||
binding.uploadProgressViewPager.setId(R.id.upload_progress_view_pager)
|
||||
binding.uploadProgressTabLayout.setupWithViewPager(binding.uploadProgressViewPager)
|
||||
|
|
@ -81,11 +79,10 @@ class UploadProgressActivity : BaseActivity() {
|
|||
pendingUploadsFragment = PendingUploadsFragment()
|
||||
failedUploadsFragment = FailedUploadsFragment()
|
||||
|
||||
fragmentList.add(pendingUploadsFragment!!)
|
||||
titleList.add(getString(R.string.pending))
|
||||
fragmentList.add(failedUploadsFragment!!)
|
||||
titleList.add(getString(R.string.failed))
|
||||
viewPagerAdapter!!.setTabData(fragmentList, titleList)
|
||||
viewPagerAdapter!!.setTabs(
|
||||
R.string.pending to pendingUploadsFragment!!,
|
||||
R.string.failed to failedUploadsFragment!!
|
||||
)
|
||||
viewPagerAdapter!!.notifyDataSetChanged()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,11 +16,13 @@ import android.widget.AdapterView
|
|||
import android.widget.ArrayAdapter
|
||||
import android.widget.TextView
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.Utils
|
||||
import fr.free.nrw.commons.databinding.FragmentMediaLicenseBinding
|
||||
import fr.free.nrw.commons.upload.UploadActivity
|
||||
import fr.free.nrw.commons.upload.UploadBaseFragment
|
||||
import fr.free.nrw.commons.utils.DialogUtil.showAlertDialog
|
||||
import fr.free.nrw.commons.utils.handleWebUrl
|
||||
import fr.free.nrw.commons.utils.toLicenseName
|
||||
import fr.free.nrw.commons.utils.toLicenseUrl
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
@ -126,20 +128,20 @@ class MediaLicenseFragment : UploadBaseFragment(), MediaLicenseContract.View {
|
|||
}
|
||||
|
||||
override fun setSelectedLicense(license: String?) {
|
||||
var position = licenses!!.indexOf(getString(Utils.licenseNameFor(license)))
|
||||
var position = license?.let { licenses!!.indexOf(getString(it.toLicenseName())) } ?: -1
|
||||
// Check if position is valid
|
||||
if (position < 0) {
|
||||
Timber.d("Invalid position: %d. Using default licenses", position)
|
||||
position = licenses!!.size - 1
|
||||
} else {
|
||||
Timber.d("Position: %d %s", position, getString(Utils.licenseNameFor(license)))
|
||||
}
|
||||
binding.spinnerLicenseList.setSelection(position)
|
||||
}
|
||||
|
||||
override fun updateLicenseSummary(selectedLicense: String?, numberOfItems: Int) {
|
||||
val licenseHyperLink = "<a href='" + Utils.licenseUrlFor(selectedLicense) + "'>" +
|
||||
getString(Utils.licenseNameFor(selectedLicense)) + "</a><br>"
|
||||
if (selectedLicense == null) return
|
||||
|
||||
val licenseHyperLink = "<a href='" + selectedLicense.toLicenseUrl() + "'>" +
|
||||
getString(selectedLicense.toLicenseName()) + "</a><br>"
|
||||
|
||||
setTextViewHTML(
|
||||
binding.tvShareLicenseSummary, resources
|
||||
|
|
@ -184,7 +186,7 @@ class MediaLicenseFragment : UploadBaseFragment(), MediaLicenseContract.View {
|
|||
}
|
||||
|
||||
private fun launchBrowser(hyperLink: String) =
|
||||
Utils.handleWebUrl(context, Uri.parse(hyperLink))
|
||||
handleWebUrl(requireContext(), Uri.parse(hyperLink))
|
||||
|
||||
override fun onDestroyView() {
|
||||
presenter.onDetachView()
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
package fr.free.nrw.commons.upload.license
|
||||
|
||||
import fr.free.nrw.commons.Utils
|
||||
import fr.free.nrw.commons.kvstore.JsonKvStore
|
||||
import fr.free.nrw.commons.repository.UploadRepository
|
||||
import fr.free.nrw.commons.settings.Prefs
|
||||
import fr.free.nrw.commons.utils.toLicenseName
|
||||
import timber.log.Timber
|
||||
import java.lang.reflect.Method
|
||||
import java.lang.reflect.Proxy
|
||||
|
|
@ -34,12 +34,14 @@ class MediaLicensePresenter @Inject constructor(
|
|||
val licenses = repository.getLicenses()
|
||||
view.setLicenses(licenses)
|
||||
|
||||
var selectedLicense = defaultKVStore.getString(
|
||||
//CC_BY_SA_4 is the default one used by the commons web app
|
||||
var selectedLicense: String = defaultKVStore.getString(
|
||||
Prefs.DEFAULT_LICENSE,
|
||||
Prefs.Licenses.CC_BY_SA_4
|
||||
) //CC_BY_SA_4 is the default one used by the commons web app
|
||||
) ?: Prefs.Licenses.CC_BY_SA_4
|
||||
|
||||
try { //I have to make sure that the stored default license was not one of the deprecated one's
|
||||
Utils.licenseNameFor(selectedLicense)
|
||||
selectedLicense.toLicenseName()
|
||||
} catch (exception: IllegalStateException) {
|
||||
Timber.e(exception)
|
||||
selectedLicense = Prefs.Licenses.CC_BY_SA_4
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ interface UploadMediaDetailsContract {
|
|||
fun showBadImagePopup(errorCode: Int, index: Int, uploadItem: UploadItem)
|
||||
}
|
||||
|
||||
interface UserActionListener : BasePresenter<View?> {
|
||||
interface UserActionListener : BasePresenter<View> {
|
||||
fun setupBasicKvStoreFactory(factory: (String) -> BasicKvStore)
|
||||
|
||||
fun receiveImage(
|
||||
|
|
|
|||
|
|
@ -107,7 +107,10 @@ class UploadMediaPresenter @Inject constructor(
|
|||
view.showProgress(false)
|
||||
val gpsCoords = uploadItem.gpsCoords
|
||||
val hasImageCoordinates = gpsCoords != null && gpsCoords.imageCoordsExists
|
||||
if (hasImageCoordinates && place == null) {
|
||||
|
||||
// Only check for nearby places if image has coordinates AND no place was pre-selected
|
||||
// This prevents the popup from appearing when uploading from Nearby feature
|
||||
if (hasImageCoordinates && place == null && uploadItem.place == null) {
|
||||
checkNearbyPlaces(uploadItem)
|
||||
}
|
||||
}, { throwable: Throwable? ->
|
||||
|
|
|
|||
|
|
@ -472,7 +472,10 @@ class UploadWorker(
|
|||
if (wikiDataPlace != null) {
|
||||
if (!contribution.hasInvalidLocation()) {
|
||||
var revisionID: Long? = null
|
||||
val p18WasSkipped = !wikiDataPlace.imageValue.isNullOrBlank()
|
||||
try {
|
||||
if (!p18WasSkipped) {
|
||||
// Only set P18 if the place does not already have a picture
|
||||
revisionID =
|
||||
wikidataEditService.createClaim(
|
||||
wikiDataPlace,
|
||||
|
|
@ -489,9 +492,11 @@ class UploadWorker(
|
|||
.subscribeOn(Schedulers.io())
|
||||
.blockingAwait()
|
||||
Timber.d("Updated WikiItem place ${place.name} with image ${place.pic}")
|
||||
}
|
||||
}
|
||||
showSuccessNotification(contribution)
|
||||
}
|
||||
// Always show success notification, whether P18 was set or skipped
|
||||
showSuccessNotification(contribution)
|
||||
} catch (exception: Exception) {
|
||||
Timber.e(exception)
|
||||
}
|
||||
|
|
@ -500,6 +505,7 @@ class UploadWorker(
|
|||
wikidataEditService.handleImageClaimResult(
|
||||
contribution.wikidataPlace!!,
|
||||
revisionID,
|
||||
p18WasSkipped = p18WasSkipped
|
||||
)
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
package fr.free.nrw.commons.utils
|
||||
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.content.Context.CLIPBOARD_SERVICE
|
||||
|
||||
object ClipboardUtils {
|
||||
// Convenience for Java usages - remove when they are converted.
|
||||
@JvmStatic
|
||||
fun copy(label: String?, text: String?, context: Context) {
|
||||
context.copyToClipboard(label, text)
|
||||
}
|
||||
}
|
||||
|
||||
fun Context.copyToClipboard(label: String?, text: String?) {
|
||||
with(getSystemService(CLIPBOARD_SERVICE) as ClipboardManager) {
|
||||
setPrimaryClip(ClipData.newPlainText(label, text))
|
||||
}
|
||||
}
|
||||
38
app/src/main/java/fr/free/nrw/commons/utils/FixExtension.kt
Normal file
38
app/src/main/java/fr/free/nrw/commons/utils/FixExtension.kt
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
package fr.free.nrw.commons.utils
|
||||
|
||||
import java.util.Locale
|
||||
import java.util.regex.Pattern
|
||||
|
||||
private val jpegPattern = Pattern.compile("\\.jpeg$", Pattern.CASE_INSENSITIVE)
|
||||
|
||||
/**
|
||||
* Adds extension to filename. Converts to .jpg if system provides .jpeg, adds .jpg if no extension detected
|
||||
* @param theTitle File name
|
||||
* @param ext Correct extension
|
||||
* @return File with correct extension
|
||||
*/
|
||||
fun fixExtension(theTitle: String, ext: String?): String {
|
||||
var result = theTitle
|
||||
var extension = ext
|
||||
|
||||
// People are used to ".jpg" more than ".jpeg" which the system gives us.
|
||||
if (extension != null && extension.lowercase() == "jpeg") {
|
||||
extension = "jpg"
|
||||
}
|
||||
|
||||
result = jpegPattern.matcher(result).replaceFirst(".jpg")
|
||||
if (extension != null &&
|
||||
!result.lowercase(Locale.getDefault()).endsWith("." + extension.lowercase())
|
||||
) {
|
||||
result += ".$extension"
|
||||
}
|
||||
|
||||
// If extension is still null, make it jpg. (Hotfix for https://github.com/commons-app/apps-android-commons/issues/228)
|
||||
// If title has an extension in it, if won't be true
|
||||
if (extension == null && result.lastIndexOf(".") <= 0) {
|
||||
extension = "jpg"
|
||||
result += ".$extension"
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package fr.free.nrw.commons.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.location.LatLng
|
||||
import fr.free.nrw.commons.utils.ViewUtil.showShortToast
|
||||
|
||||
/**
|
||||
* Util function to handle geo coordinates with specified zoom level. It no longer depends on
|
||||
* google maps and any app capable of handling the map intent can handle it
|
||||
*
|
||||
* @param context The context for launching intent
|
||||
* @param latLng The latitude and longitude of the location
|
||||
* @param zoomLevel The zoom level
|
||||
*/
|
||||
fun handleGeoCoordinates(
|
||||
context: Context, latLng: LatLng,
|
||||
zoomLevel: Double = 16.0
|
||||
) {
|
||||
val mapIntent = Intent(Intent.ACTION_VIEW, latLng.getGmmIntentUri(zoomLevel))
|
||||
if (mapIntent.resolveActivity(context.packageManager) != null) {
|
||||
context.startActivity(mapIntent)
|
||||
} else {
|
||||
showShortToast(context, context.getString(R.string.map_application_missing))
|
||||
}
|
||||
}
|
||||
31
app/src/main/java/fr/free/nrw/commons/utils/Licenses.kt
Normal file
31
app/src/main/java/fr/free/nrw/commons/utils/Licenses.kt
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
package fr.free.nrw.commons.utils
|
||||
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.settings.Prefs
|
||||
|
||||
/**
|
||||
* Generates licence name with given ID
|
||||
* @return Name of license
|
||||
*/
|
||||
fun String.toLicenseName(): Int = when (this) {
|
||||
Prefs.Licenses.CC_BY_3 -> R.string.license_name_cc_by
|
||||
Prefs.Licenses.CC_BY_4 -> R.string.license_name_cc_by_four
|
||||
Prefs.Licenses.CC_BY_SA_3 -> R.string.license_name_cc_by_sa
|
||||
Prefs.Licenses.CC_BY_SA_4 -> R.string.license_name_cc_by_sa_four
|
||||
Prefs.Licenses.CC0 -> R.string.license_name_cc0
|
||||
else -> throw IllegalStateException("Unrecognized license value: $this")
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates license url with given ID
|
||||
* @return Url of license
|
||||
*/
|
||||
fun String.toLicenseUrl(): String = when (this) {
|
||||
Prefs.Licenses.CC_BY_3 -> "https://creativecommons.org/licenses/by/3.0/"
|
||||
Prefs.Licenses.CC_BY_4 -> "https://creativecommons.org/licenses/by/4.0/"
|
||||
Prefs.Licenses.CC_BY_SA_3 -> "https://creativecommons.org/licenses/by-sa/3.0/"
|
||||
Prefs.Licenses.CC_BY_SA_4 -> "https://creativecommons.org/licenses/by-sa/4.0/"
|
||||
Prefs.Licenses.CC0 -> "https://creativecommons.org/publicdomain/zero/1.0/"
|
||||
else -> throw IllegalStateException("Unrecognized license value: $this")
|
||||
}
|
||||
|
||||
39
app/src/main/java/fr/free/nrw/commons/utils/Monuments.kt
Normal file
39
app/src/main/java/fr/free/nrw/commons/utils/Monuments.kt
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
package fr.free.nrw.commons.utils
|
||||
|
||||
import java.util.Calendar
|
||||
import java.util.Date
|
||||
|
||||
/**
|
||||
* Get the start date of wlm monument
|
||||
* For this release we are hardcoding it to be 1st September
|
||||
* @return
|
||||
*/
|
||||
const val wLMStartDate: String = "1 Sep"
|
||||
|
||||
/***
|
||||
* Get the end date of wlm monument
|
||||
* For this release we are hardcoding it to be 31st October
|
||||
* @return
|
||||
*/
|
||||
const val wLMEndDate: String = "30 Sep"
|
||||
|
||||
/**
|
||||
* For now we are enabling the monuments only when the date lies between 1 Sept & 31 OCt
|
||||
*/
|
||||
val isMonumentsEnabled: Boolean
|
||||
get() = Date().month == 8
|
||||
|
||||
/***
|
||||
* Function to get the current WLM year
|
||||
* It increments at the start of September in line with the other WLM functions
|
||||
* (No consideration of locales for now)
|
||||
* @param calendar
|
||||
* @return
|
||||
*/
|
||||
fun getWikiLovesMonumentsYear(calendar: Calendar): Int {
|
||||
var year = calendar[Calendar.YEAR]
|
||||
if (calendar[Calendar.MONTH] < Calendar.SEPTEMBER) {
|
||||
year -= 1
|
||||
}
|
||||
return year
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package fr.free.nrw.commons.utils
|
||||
|
||||
import android.widget.TextView
|
||||
import androidx.core.text.buildSpannedString
|
||||
import androidx.core.text.underline
|
||||
|
||||
object UnderlineUtils {
|
||||
// Convenience method for Java usages - remove when those classes are converted
|
||||
@JvmStatic
|
||||
fun setUnderlinedText(textView: TextView, stringResourceName: Int) {
|
||||
textView.setUnderlinedText(stringResourceName)
|
||||
}
|
||||
}
|
||||
|
||||
fun TextView.setUnderlinedText(stringResourceName: Int) {
|
||||
text = buildSpannedString {
|
||||
underline { append(context.getString(stringResourceName)) }
|
||||
}
|
||||
}
|
||||
33
app/src/main/java/fr/free/nrw/commons/utils/UrlUtils.kt
Normal file
33
app/src/main/java/fr/free/nrw/commons/utils/UrlUtils.kt
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
package fr.free.nrw.commons.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import androidx.browser.customtabs.CustomTabColorSchemeParams
|
||||
import androidx.browser.customtabs.CustomTabsIntent
|
||||
import androidx.core.content.ContextCompat
|
||||
import fr.free.nrw.commons.R
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
* Opens Custom Tab Activity with in-app browser for the specified URL.
|
||||
* Launches intent for web URL
|
||||
*/
|
||||
fun handleWebUrl(context: Context, url: Uri) {
|
||||
Timber.d("Launching web url %s", url.toString())
|
||||
|
||||
val color = CustomTabColorSchemeParams.Builder()
|
||||
.setToolbarColor(ContextCompat.getColor(context, R.color.primaryColor))
|
||||
.setSecondaryToolbarColor(ContextCompat.getColor(context, R.color.primaryDarkColor))
|
||||
.build()
|
||||
|
||||
val customTabsIntent = CustomTabsIntent.Builder()
|
||||
.setDefaultColorSchemeParams(color)
|
||||
.setExitAnimations(
|
||||
context, android.R.anim.slide_in_left, android.R.anim.slide_out_right
|
||||
).build()
|
||||
|
||||
// Clear previous browser tasks, so that back/exit buttons work as intended.
|
||||
customTabsIntent.intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
|
||||
customTabsIntent.launchUrl(context, url)
|
||||
}
|
||||
|
|
@ -196,13 +196,16 @@ class WikidataEditService @Inject constructor(
|
|||
return wikidataClient.setClaim(claim, COMMONS_APP_TAG).blockingSingle()
|
||||
}
|
||||
|
||||
fun handleImageClaimResult(wikidataItem: WikidataItem, revisionId: Long?) {
|
||||
fun handleImageClaimResult(wikidataItem: WikidataItem, revisionId: Long?, p18WasSkipped: Boolean = false) {
|
||||
if (revisionId != null) {
|
||||
wikidataEditListener?.onSuccessfulWikidataEdit()
|
||||
showSuccessToast(wikidataItem.name)
|
||||
} else {
|
||||
} else if (!p18WasSkipped) {
|
||||
Timber.d("Unable to make wiki data edit for entity %s", wikidataItem)
|
||||
showLongToast(context, context.getString(R.string.wikidata_edit_failure))
|
||||
} else {
|
||||
Timber.d("Wikidata edit skipped for entity %s because P18 already exists", wikidataItem)
|
||||
// No error shown to user, as this is not a failure
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue