mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-27 04:43:54 +01:00
Merge pull request #1297 from diddypod/delete-request
Fix #1139: Nominate uploaded image for deletion
This commit is contained in:
commit
31ac50616e
8 changed files with 295 additions and 2 deletions
174
app/src/main/java/fr/free/nrw/commons/delete/DeleteTask.java
Normal file
174
app/src/main/java/fr/free/nrw/commons/delete/DeleteTask.java
Normal file
|
|
@ -0,0 +1,174 @@
|
||||||
|
package fr.free.nrw.commons.delete;
|
||||||
|
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import fr.free.nrw.commons.Media;
|
||||||
|
import fr.free.nrw.commons.R;
|
||||||
|
import fr.free.nrw.commons.auth.SessionManager;
|
||||||
|
import fr.free.nrw.commons.di.ApplicationlessInjection;
|
||||||
|
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||||
|
import timber.log.Timber;
|
||||||
|
|
||||||
|
import static android.support.v4.content.ContextCompat.startActivity;
|
||||||
|
|
||||||
|
public class DeleteTask extends AsyncTask<Void, Void, Integer> {
|
||||||
|
|
||||||
|
private static final int SUCCESS = 0;
|
||||||
|
private static final int FAILED = -1;
|
||||||
|
private static final int ALREADY_DELETED = -2;
|
||||||
|
|
||||||
|
@Inject MediaWikiApi mwApi;
|
||||||
|
@Inject SessionManager sessionManager;
|
||||||
|
|
||||||
|
private Context context;
|
||||||
|
private Media media;
|
||||||
|
private String reason;
|
||||||
|
|
||||||
|
public DeleteTask (Context context, Media media, String reason){
|
||||||
|
this.context = context;
|
||||||
|
this.media = media;
|
||||||
|
this.reason = reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPreExecute(){
|
||||||
|
ApplicationlessInjection
|
||||||
|
.getInstance(context.getApplicationContext())
|
||||||
|
.getCommonsApplicationComponent()
|
||||||
|
.inject(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Integer doInBackground(Void ...voids) {
|
||||||
|
String editToken;
|
||||||
|
String authCookie;
|
||||||
|
String summary = "Nominating " + media.getFilename() +" for deletion.";
|
||||||
|
|
||||||
|
authCookie = sessionManager.getAuthCookie();
|
||||||
|
mwApi.setAuthCookie(authCookie);
|
||||||
|
|
||||||
|
try{
|
||||||
|
if (mwApi.pageExists("Commons:Deletion_requests/"+media.getFilename())){
|
||||||
|
return ALREADY_DELETED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
Timber.d(e.getMessage());
|
||||||
|
return FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
editToken = mwApi.getEditToken();
|
||||||
|
}
|
||||||
|
catch (Exception e){
|
||||||
|
Timber.d(e.getMessage());
|
||||||
|
return FAILED;
|
||||||
|
}
|
||||||
|
if (editToken.equals("+\\")) {
|
||||||
|
return FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
Calendar calendar = Calendar.getInstance();
|
||||||
|
String fileDeleteString = "{{delete|reason=" + reason +
|
||||||
|
"|subpage=" +media.getFilename() +
|
||||||
|
"|day=" + calendar.get(Calendar.DAY_OF_MONTH) +
|
||||||
|
"|month=" + calendar.getDisplayName(Calendar.MONTH,Calendar.LONG, Locale.getDefault()) +
|
||||||
|
"|year=" + calendar.get(Calendar.YEAR) +
|
||||||
|
"}}";
|
||||||
|
try{
|
||||||
|
mwApi.prependEdit(editToken,fileDeleteString+"\n",
|
||||||
|
media.getFilename(),summary);
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
Timber.d(e.getMessage());
|
||||||
|
return FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
String subpageString = "=== [[:" + media.getFilename() + "]] ===\n" +
|
||||||
|
reason +
|
||||||
|
" ~~~~";
|
||||||
|
try{
|
||||||
|
mwApi.edit(editToken,subpageString+"\n",
|
||||||
|
"Commons:Deletion_requests/"+media.getFilename(),summary);
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
Timber.d(e.getMessage());
|
||||||
|
return FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
String logPageString = "\n{{Commons:Deletion requests/" + media.getFilename() +
|
||||||
|
"}}\n";
|
||||||
|
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
|
||||||
|
String date = sdf.format(calendar.getTime());
|
||||||
|
try{
|
||||||
|
mwApi.appendEdit(editToken,logPageString+"\n",
|
||||||
|
"Commons:Deletion_requests/"+date,summary);
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
Timber.d(e.getMessage());
|
||||||
|
return FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
String userPageString = "\n{{subst:idw|" + media.getFilename() +
|
||||||
|
"}} ~~~~";
|
||||||
|
try{
|
||||||
|
mwApi.appendEdit(editToken,userPageString+"\n",
|
||||||
|
"User_Talk:"+sessionManager.getCurrentAccount().name,summary);
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
Timber.d(e.getMessage());
|
||||||
|
return FAILED;
|
||||||
|
}
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(Integer result) {
|
||||||
|
String message = "";
|
||||||
|
String title = "";
|
||||||
|
switch (result){
|
||||||
|
case SUCCESS:
|
||||||
|
title = "Success";
|
||||||
|
message = "Successfully nominated " + media.getDisplayTitle() + " deletion.\n" +
|
||||||
|
"Check the webpage for more details";
|
||||||
|
break;
|
||||||
|
case FAILED:
|
||||||
|
title = "Failed";
|
||||||
|
message = "Could not request deletion. Something went wrong.";
|
||||||
|
break;
|
||||||
|
case ALREADY_DELETED:
|
||||||
|
title = "Already Nominated";
|
||||||
|
message = media.getDisplayTitle() + " has already been nominated for deletion.\n" +
|
||||||
|
"Check the webpage for more details";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
AlertDialog alert;
|
||||||
|
AlertDialog.Builder builder = new AlertDialog.Builder(context);
|
||||||
|
builder.setTitle(title);
|
||||||
|
builder.setMessage(message);
|
||||||
|
builder.setCancelable(true);
|
||||||
|
builder.setPositiveButton(
|
||||||
|
R.string.ok,
|
||||||
|
new DialogInterface.OnClickListener() {
|
||||||
|
public void onClick(DialogInterface dialog, int id) {}
|
||||||
|
});
|
||||||
|
builder.setNeutralButton(R.string.view_browser,
|
||||||
|
new DialogInterface.OnClickListener() {
|
||||||
|
public void onClick(DialogInterface dialog, int id) {
|
||||||
|
Intent browserIntent = new Intent(Intent.ACTION_VIEW, media.getFilePageTitle().getMobileUri());
|
||||||
|
startActivity(context,browserIntent,null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
alert = builder.create();
|
||||||
|
alert.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -9,7 +9,10 @@ import dagger.android.support.AndroidSupportInjectionModule;
|
||||||
import fr.free.nrw.commons.CommonsApplication;
|
import fr.free.nrw.commons.CommonsApplication;
|
||||||
import fr.free.nrw.commons.MediaWikiImageView;
|
import fr.free.nrw.commons.MediaWikiImageView;
|
||||||
import fr.free.nrw.commons.auth.LoginActivity;
|
import fr.free.nrw.commons.auth.LoginActivity;
|
||||||
|
import fr.free.nrw.commons.contributions.Contribution;
|
||||||
|
import fr.free.nrw.commons.contributions.ContributionsActivity;
|
||||||
import fr.free.nrw.commons.contributions.ContributionsSyncAdapter;
|
import fr.free.nrw.commons.contributions.ContributionsSyncAdapter;
|
||||||
|
import fr.free.nrw.commons.delete.DeleteTask;
|
||||||
import fr.free.nrw.commons.modifications.ModificationsSyncAdapter;
|
import fr.free.nrw.commons.modifications.ModificationsSyncAdapter;
|
||||||
import fr.free.nrw.commons.settings.SettingsFragment;
|
import fr.free.nrw.commons.settings.SettingsFragment;
|
||||||
|
|
||||||
|
|
@ -34,6 +37,8 @@ public interface CommonsApplicationComponent extends AndroidInjector<Application
|
||||||
|
|
||||||
void inject(LoginActivity activity);
|
void inject(LoginActivity activity);
|
||||||
|
|
||||||
|
void inject(DeleteTask deleteTask);
|
||||||
|
|
||||||
void inject(SettingsFragment fragment);
|
void inject(SettingsFragment fragment);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,23 @@
|
||||||
package fr.free.nrw.commons.media;
|
package fr.free.nrw.commons.media;
|
||||||
|
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.database.DataSetObserver;
|
import android.database.DataSetObserver;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
|
import android.text.Editable;
|
||||||
|
import android.text.TextWatcher;
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.ViewTreeObserver;
|
import android.view.ViewTreeObserver;
|
||||||
|
import android.view.WindowManager;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.EditText;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
import android.widget.ScrollView;
|
import android.widget.ScrollView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
@ -32,6 +39,7 @@ import fr.free.nrw.commons.MediaDataExtractor;
|
||||||
import fr.free.nrw.commons.MediaWikiImageView;
|
import fr.free.nrw.commons.MediaWikiImageView;
|
||||||
import fr.free.nrw.commons.PageTitle;
|
import fr.free.nrw.commons.PageTitle;
|
||||||
import fr.free.nrw.commons.R;
|
import fr.free.nrw.commons.R;
|
||||||
|
import fr.free.nrw.commons.delete.DeleteTask;
|
||||||
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
|
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
|
||||||
import fr.free.nrw.commons.location.LatLng;
|
import fr.free.nrw.commons.location.LatLng;
|
||||||
import fr.free.nrw.commons.ui.widget.CompatTextView;
|
import fr.free.nrw.commons.ui.widget.CompatTextView;
|
||||||
|
|
@ -72,6 +80,7 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment {
|
||||||
private TextView coordinates;
|
private TextView coordinates;
|
||||||
private TextView uploadedDate;
|
private TextView uploadedDate;
|
||||||
private LinearLayout categoryContainer;
|
private LinearLayout categoryContainer;
|
||||||
|
private Button delete;
|
||||||
private ScrollView scrollView;
|
private ScrollView scrollView;
|
||||||
private ArrayList<String> categoryNames;
|
private ArrayList<String> categoryNames;
|
||||||
private boolean categoriesLoaded = false;
|
private boolean categoriesLoaded = false;
|
||||||
|
|
@ -124,6 +133,7 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment {
|
||||||
license = (TextView) view.findViewById(R.id.mediaDetailLicense);
|
license = (TextView) view.findViewById(R.id.mediaDetailLicense);
|
||||||
coordinates = (TextView) view.findViewById(R.id.mediaDetailCoordinates);
|
coordinates = (TextView) view.findViewById(R.id.mediaDetailCoordinates);
|
||||||
uploadedDate = (TextView) view.findViewById(R.id.mediaDetailuploadeddate);
|
uploadedDate = (TextView) view.findViewById(R.id.mediaDetailuploadeddate);
|
||||||
|
delete = (Button) view.findViewById(R.id.nominateDeletion);
|
||||||
categoryContainer = (LinearLayout) view.findViewById(R.id.mediaDetailCategoryContainer);
|
categoryContainer = (LinearLayout) view.findViewById(R.id.mediaDetailCategoryContainer);
|
||||||
|
|
||||||
licenseList = new LicenseList(getActivity());
|
licenseList = new LicenseList(getActivity());
|
||||||
|
|
@ -273,6 +283,8 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment {
|
||||||
categoryNames.add(getString(R.string.detail_panel_cats_none));
|
categoryNames.add(getString(R.string.detail_panel_cats_none));
|
||||||
}
|
}
|
||||||
rebuildCatList();
|
rebuildCatList();
|
||||||
|
|
||||||
|
delete.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setOnClickListeners(final Media media) {
|
private void setOnClickListeners(final Media media) {
|
||||||
|
|
@ -285,6 +297,52 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment {
|
||||||
if (media.getCoordinates() != null) {
|
if (media.getCoordinates() != null) {
|
||||||
coordinates.setOnClickListener(v -> openMap(media.getCoordinates()));
|
coordinates.setOnClickListener(v -> openMap(media.getCoordinates()));
|
||||||
}
|
}
|
||||||
|
if (delete.getVisibility()==View.VISIBLE){
|
||||||
|
delete.setOnClickListener(v -> {
|
||||||
|
AlertDialog.Builder alert = new AlertDialog.Builder(getActivity());
|
||||||
|
alert.setMessage("Why should this file be deleted?");
|
||||||
|
final EditText input = new EditText(getActivity());
|
||||||
|
alert.setView(input);
|
||||||
|
input.requestFocus();
|
||||||
|
alert.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
|
||||||
|
public void onClick(DialogInterface dialog, int whichButton) {
|
||||||
|
String reason = input.getText().toString();
|
||||||
|
DeleteTask deleteTask = new DeleteTask(getActivity(), media, reason);
|
||||||
|
deleteTask.execute();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
alert.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
|
||||||
|
public void onClick(DialogInterface dialog, int whichButton) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
AlertDialog d = alert.create();
|
||||||
|
input.addTextChangedListener(new TextWatcher() {
|
||||||
|
private void handleText() {
|
||||||
|
final Button okButton = d.getButton(AlertDialog.BUTTON_POSITIVE);
|
||||||
|
if (input.getText().length() == 0) {
|
||||||
|
okButton.setEnabled(false);
|
||||||
|
} else {
|
||||||
|
okButton.setEnabled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterTextChanged(Editable arg0) {
|
||||||
|
handleText();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
d.show();
|
||||||
|
d.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void rebuildCatList() {
|
private void rebuildCatList() {
|
||||||
|
|
|
||||||
|
|
@ -205,6 +205,14 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
|
||||||
.getNodes("/api/query/pages/page/imageinfo").size() > 0;
|
.getNodes("/api/query/pages/page/imageinfo").size() > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean pageExists(String pageName) throws IOException {
|
||||||
|
return Double.parseDouble( api.action("query")
|
||||||
|
.param("titles", pageName)
|
||||||
|
.get()
|
||||||
|
.getString("/api/query/pages/page/@_idx")) != -1;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
public String edit(String editToken, String processedPageContent, String filename, String summary) throws IOException {
|
public String edit(String editToken, String processedPageContent, String filename, String summary) throws IOException {
|
||||||
|
|
@ -217,6 +225,31 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
|
||||||
.getString("/api/edit/@result");
|
.getString("/api/edit/@result");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
public String appendEdit(String editToken, String processedPageContent, String filename, String summary) throws IOException {
|
||||||
|
return api.action("edit")
|
||||||
|
.param("title", filename)
|
||||||
|
.param("token", editToken)
|
||||||
|
.param("appendtext", processedPageContent)
|
||||||
|
.param("summary", summary)
|
||||||
|
.post()
|
||||||
|
.getString("/api/edit/@result");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
public String prependEdit(String editToken, String processedPageContent, String filename, String summary) throws IOException {
|
||||||
|
return api.action("edit")
|
||||||
|
.param("title", filename)
|
||||||
|
.param("token", editToken)
|
||||||
|
.param("prependtext", processedPageContent)
|
||||||
|
.param("summary", summary)
|
||||||
|
.post()
|
||||||
|
.getString("/api/edit/@result");
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String findThumbnailByFilename(String filename) throws IOException {
|
public String findThumbnailByFilename(String filename) throws IOException {
|
||||||
return api.action("query")
|
return api.action("query")
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,8 @@ public interface MediaWikiApi {
|
||||||
|
|
||||||
boolean fileExistsWithName(String fileName) throws IOException;
|
boolean fileExistsWithName(String fileName) throws IOException;
|
||||||
|
|
||||||
|
boolean pageExists(String pageName) throws IOException;
|
||||||
|
|
||||||
String findThumbnailByFilename(String filename) throws IOException;
|
String findThumbnailByFilename(String filename) throws IOException;
|
||||||
|
|
||||||
boolean logEvents(LogBuilder[] logBuilders);
|
boolean logEvents(LogBuilder[] logBuilders);
|
||||||
|
|
@ -38,6 +40,12 @@ public interface MediaWikiApi {
|
||||||
@Nullable
|
@Nullable
|
||||||
String edit(String editToken, String processedPageContent, String filename, String summary) throws IOException;
|
String edit(String editToken, String processedPageContent, String filename, String summary) throws IOException;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
String prependEdit(String editToken, String processedPageContent, String filename, String summary) throws IOException;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
String appendEdit(String editToken, String processedPageContent, String filename, String summary) throws IOException;
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
MediaResult fetchMediaByFilename(String filename) throws IOException;
|
MediaResult fetchMediaByFilename(String filename) throws IOException;
|
||||||
|
|
||||||
|
|
@ -58,8 +66,6 @@ public interface MediaWikiApi {
|
||||||
|
|
||||||
boolean existingFile(String fileSha1) throws IOException;
|
boolean existingFile(String fileSha1) throws IOException;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
LogEventResult logEvents(String user, String lastModified, String queryContinue, int limit) throws IOException;
|
LogEventResult logEvents(String user, String lastModified, String queryContinue, int limit) throws IOException;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -247,6 +247,19 @@
|
||||||
android:textSize="@dimen/description_text_size" />
|
android:textSize="@dimen/description_text_size" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<fr.free.nrw.commons.media.MediaDetailSpacer
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/small_gap" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/nominateDeletion"
|
||||||
|
style="?android:attr/buttonBarButtonStyle"
|
||||||
|
android:textColor="@color/deleteTextColor"
|
||||||
|
android:layout_margin="@dimen/standard_gap"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/nominate_deletion"
|
||||||
|
android:visibility="gone"/>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,8 @@
|
||||||
<color name="primaryTextColor">#ffffff</color>
|
<color name="primaryTextColor">#ffffff</color>
|
||||||
<color name="secondaryTextColor">#000000</color>
|
<color name="secondaryTextColor">#000000</color>
|
||||||
|
|
||||||
|
<color name="deleteTextColor">#d50000</color>
|
||||||
|
|
||||||
<!-- Some colours are same for dark/light themes. They are written two times in case
|
<!-- Some colours are same for dark/light themes. They are written two times in case
|
||||||
we want to change light ones later.
|
we want to change light ones later.
|
||||||
-->
|
-->
|
||||||
|
|
|
||||||
|
|
@ -230,6 +230,8 @@
|
||||||
<string name="send_log_file_description">Send log file to developers via email</string>
|
<string name="send_log_file_description">Send log file to developers via email</string>
|
||||||
<string name="no_web_browser">No web browser found to open URL</string>
|
<string name="no_web_browser">No web browser found to open URL</string>
|
||||||
<string name="null_url">Error! URL not found</string>
|
<string name="null_url">Error! URL not found</string>
|
||||||
|
<string name="nominate_deletion">Nominate for Deletion</string>
|
||||||
|
<string name="view_browser">View in Browser</string>
|
||||||
|
|
||||||
<string name="nearby_location_has_not_changed">Location has not changed.</string>
|
<string name="nearby_location_has_not_changed">Location has not changed.</string>
|
||||||
<string name="nearby_location_not_available">Location not available.</string>
|
<string name="nearby_location_not_available">Location not available.</string>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue