Translation of toots in the device locale

This commit is contained in:
tom79 2017-07-03 11:30:36 +02:00
parent a17df0318e
commit a14e05bb32
19 changed files with 307 additions and 11 deletions

View File

@ -91,6 +91,7 @@ public class MainActivity extends AppCompatActivity
private float downX, downY;
private int currentScreen = 1;
private actionSwipe currentAction;
public static String currentLocale;
private enum actionSwipe{
RIGHT_TO_LEFT,
@ -123,6 +124,8 @@ public class MainActivity extends AppCompatActivity
//Here, the user is authenticated
toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
//Defines the current locale of the device in a static variable
currentLocale = Helper.currentLocale(getApplicationContext());
toot = (FloatingActionButton) findViewById(R.id.toot);
toot.setOnClickListener(new View.OnClickListener() {

View File

@ -19,11 +19,11 @@ import android.Manifest;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.RectF;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
@ -35,7 +35,6 @@ import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.URLUtil;
import android.widget.ImageView;
import android.widget.MediaController;
@ -43,6 +42,9 @@ import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.VideoView;
import com.github.chrisbanes.photoview.OnMatrixChangedListener;
import com.github.chrisbanes.photoview.OnPhotoTapListener;
import com.github.chrisbanes.photoview.OnScaleChangedListener;
import com.github.chrisbanes.photoview.PhotoView;
import com.loopj.android.http.AsyncHttpClient;
import com.loopj.android.http.FileAsyncHttpResponseHandler;
@ -93,6 +95,7 @@ public class MediaActivity extends AppCompatActivity {
private Bitmap downloadedImage;
private File fileVideo;
private TextView progress;
private boolean canSwipe;
private enum actionSwipe{
RIGHT_TO_LEFT,
LEFT_TO_RIGHT,
@ -124,7 +127,7 @@ public class MediaActivity extends AppCompatActivity {
}else {
main_container_media.setBackgroundResource(R.color.colorPrimaryD);
}
canSwipe = true;
loader = (RelativeLayout) findViewById(R.id.loader);
imageView = (PhotoView) findViewById(R.id.media_picture);
videoView = (VideoView) findViewById(R.id.media_video);
@ -144,10 +147,15 @@ public class MediaActivity extends AppCompatActivity {
displayMediaAtPosition(actionSwipe.POP);
}
});
imageView.setOnMatrixChangeListener(new OnMatrixChangedListener() {
@Override
public void onMatrixChanged(RectF rect) {
canSwipe = (imageView.getScale() == 1 );
}
});
progress = (TextView) findViewById(R.id.loader_progress);
setTitle("");
final ViewGroup videoLayout = (ViewGroup) findViewById(R.id.videoLayout); // Your own view, read class comments
isHiding = false;
imageLoader = ImageLoader.getInstance();
@ -196,7 +204,7 @@ public class MediaActivity extends AppCompatActivity {
*/
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
if( mediaPosition > attachments.size() || mediaPosition < 1 || attachments.size() <= 1)
if( !canSwipe || mediaPosition > attachments.size() || mediaPosition < 1 || attachments.size() <= 1)
return super.dispatchTouchEvent(event);
switch(event.getAction()){
case MotionEvent.ACTION_DOWN: {

View File

@ -53,6 +53,7 @@ import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.display.SimpleBitmapDisplayer;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.ArrayList;
@ -648,8 +649,6 @@ public class TootActivity extends AppCompatActivity implements OnRetrieveSearcAc
}
@Override
public void onRetrieveSearchAccounts(APIResponse apiResponse) {
if( apiResponse.getError() != null){
@ -667,4 +666,5 @@ public class TootActivity extends AppCompatActivity implements OnRetrieveSearcAc
toot_show_accounts.setVisibility(View.VISIBLE);
}
}
}

View File

@ -1153,7 +1153,7 @@ public class API {
status.setSensitive(Boolean.parseBoolean(resobj.get("sensitive").toString()));
status.setSpoiler_text(resobj.get("spoiler_text").toString());
status.setVisibility(resobj.get("visibility").toString());
status.setLanguage(resobj.get("language").toString());
//TODO: replace by the value
status.setApplication(new Application());

View File

@ -17,7 +17,6 @@ package fr.gouv.etalab.mastodon.client.Entities;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@ -37,6 +36,7 @@ public class Status implements Parcelable {
private String in_reply_to_account_id;
private Status reblog;
private String content;
private String content_translated;
private Date created_at;
private int reblogs_count;
private int favourites_count;
@ -51,7 +51,9 @@ public class Status implements Parcelable {
private List<Mention> mentions;
private List<Tag> tags;
private Application application;
private String language;
private boolean isTranslated = false;
private boolean isTranslationShown = false;
protected Status(Parcel in) {
id = in.readString();
@ -63,6 +65,7 @@ public class Status implements Parcelable {
account = in.readParcelable(Account.class.getClassLoader());
mentions = in.readArrayList(Mention.class.getClassLoader());
content = in.readString();
content_translated = in.readString();
reblogs_count = in.readInt();
favourites_count = in.readInt();
reblogged = in.readByte() != 0;
@ -70,8 +73,11 @@ public class Status implements Parcelable {
sensitive = in.readByte() != 0;
spoiler_text = in.readString();
visibility = in.readString();
language = in.readString();
attachmentShown = in.readByte() != 0;
spoilerShown = in.readByte() != 0;
isTranslated = in.readByte() != 0;
isTranslationShown = in.readByte() != 0;
}
public Status(){}
@ -274,6 +280,7 @@ public class Status implements Parcelable {
dest.writeParcelable(account, flags);
dest.writeList(mentions);
dest.writeString(content);
dest.writeString(content_translated);
dest.writeInt(reblogs_count);
dest.writeInt(favourites_count);
dest.writeByte((byte) (reblogged ? 1 : 0));
@ -281,8 +288,11 @@ public class Status implements Parcelable {
dest.writeByte((byte) (sensitive ? 1 : 0));
dest.writeString(spoiler_text);
dest.writeString(visibility);
dest.writeString(language);
dest.writeByte((byte) (attachmentShown ? 1 : 0));
dest.writeByte((byte) (spoilerShown ? 1 : 0));
dest.writeByte((byte) (isTranslated ? 1 : 0));
dest.writeByte((byte) (isTranslationShown ? 1 : 0));
}
public boolean isSpoilerShown() {
@ -292,4 +302,36 @@ public class Status implements Parcelable {
public void setSpoilerShown(boolean spoilerShown) {
this.spoilerShown = spoilerShown;
}
public String getLanguage() {
return language;
}
public void setLanguage(String language) {
this.language = language;
}
public boolean isTranslated() {
return isTranslated;
}
public void setTranslated(boolean translated) {
isTranslated = translated;
}
public boolean isTranslationShown() {
return isTranslationShown;
}
public void setTranslationShown(boolean translationShown) {
isTranslationShown = translationShown;
}
public String getContent_translated() {
return content_translated;
}
public void setContent_translated(String content_translated) {
this.content_translated = content_translated;
}
}

View File

@ -27,6 +27,7 @@ import android.os.Build;
import android.os.Bundle;
import android.support.v4.content.ContextCompat;
import android.text.Html;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@ -45,6 +46,12 @@ import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.display.SimpleBitmapDisplayer;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;
@ -53,6 +60,8 @@ import fr.gouv.etalab.mastodon.activities.ShowConversationActivity;
import fr.gouv.etalab.mastodon.activities.TootActivity;
import fr.gouv.etalab.mastodon.client.Entities.Error;
import fr.gouv.etalab.mastodon.helper.Helper;
import fr.gouv.etalab.mastodon.interfaces.OnTranslatedInterface;
import fr.gouv.etalab.mastodon.translation.YandexQuery;
import mastodon.etalab.gouv.fr.mastodon.R;
import fr.gouv.etalab.mastodon.activities.ShowAccountActivity;
import fr.gouv.etalab.mastodon.asynctasks.PostActionAsyncTask;
@ -62,6 +71,7 @@ import fr.gouv.etalab.mastodon.client.Entities.Attachment;
import fr.gouv.etalab.mastodon.client.Entities.Status;
import fr.gouv.etalab.mastodon.interfaces.OnPostActionInterface;
import static fr.gouv.etalab.mastodon.activities.MainActivity.currentLocale;
import static fr.gouv.etalab.mastodon.helper.Helper.changeDrawableColor;
@ -69,7 +79,7 @@ import static fr.gouv.etalab.mastodon.helper.Helper.changeDrawableColor;
* Created by Thomas on 24/04/2017.
* Adapter for Status
*/
public class StatusListAdapter extends BaseAdapter implements OnPostActionInterface {
public class StatusListAdapter extends BaseAdapter implements OnPostActionInterface, OnTranslatedInterface {
private Context context;
private List<Status> statuses;
@ -124,6 +134,7 @@ public class StatusListAdapter extends BaseAdapter implements OnPostActionInterf
holder = new ViewHolder();
holder.status_document_container = (LinearLayout) convertView.findViewById(R.id.status_document_container);
holder.status_content = (TextView) convertView.findViewById(R.id.status_content);
holder.status_content_translated = (TextView) convertView.findViewById(R.id.status_content_translated);
holder.status_account_username = (TextView) convertView.findViewById(R.id.status_account_username);
holder.status_account_displayname = (TextView) convertView.findViewById(R.id.status_account_displayname);
holder.status_account_profile = (ImageView) convertView.findViewById(R.id.status_account_profile);
@ -148,6 +159,7 @@ public class StatusListAdapter extends BaseAdapter implements OnPostActionInterf
holder.status_prev4_container = (RelativeLayout) convertView.findViewById(R.id.status_prev4_container);
holder.status_reply = (ImageView) convertView.findViewById(R.id.status_reply);
holder.status_privacy = (ImageView) convertView.findViewById(R.id.status_privacy);
holder.status_translate = (ImageView) convertView.findViewById(R.id.status_translate);
holder.main_container = (LinearLayout) convertView.findViewById(R.id.main_container);
holder.status_spoiler_container = (LinearLayout) convertView.findViewById(R.id.status_spoiler_container);
holder.status_content_container = (LinearLayout) convertView.findViewById(R.id.status_content_container);
@ -181,6 +193,34 @@ public class StatusListAdapter extends BaseAdapter implements OnPostActionInterf
statusListAdapter.notifyDataSetChanged();
}
});
if( !status.getLanguage().trim().equals(currentLocale) && !status.getLanguage().trim().equals("null")){
holder.status_translate.setVisibility(View.VISIBLE);
}else {
holder.status_translate.setVisibility(View.GONE);
}
holder.status_translate.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
if( !status.isTranslated() ){
new YandexQuery(StatusListAdapter.this).getYandexTextview(position, status.getContent(), currentLocale);
}
status.setTranslationShown(!status.isTranslationShown());
statusListAdapter.notifyDataSetChanged();
} catch (JSONException e) {
Toast.makeText(context, R.string.toast_error_translate, Toast.LENGTH_LONG).show();
}
}
});
//Toot was translated and user asked to see it
if( status.isTranslationShown()){
holder.status_content.setVisibility(View.GONE);
holder.status_content_translated.setVisibility(View.VISIBLE);
}else { //Toot is not translated
holder.status_content.setVisibility(View.VISIBLE);
holder.status_content_translated.setVisibility(View.GONE);
}
//Hides action bottom bar action when looking to status trough accounts
if( type == RetrieveFeedsAsyncTask.Type.USER){
@ -200,6 +240,7 @@ public class StatusListAdapter extends BaseAdapter implements OnPostActionInterf
changeDrawableColor(context, R.drawable.ic_fav_black,R.color.dark_text);
changeDrawableColor(context, R.drawable.ic_photo,R.color.dark_text);
changeDrawableColor(context, R.drawable.ic_remove_red_eye,R.color.dark_text);
changeDrawableColor(context, R.drawable.ic_translate,R.color.dark_text);
}else {
changeDrawableColor(context, R.drawable.ic_reply,R.color.black);
changeDrawableColor(context, R.drawable.ic_action_more,R.color.black);
@ -211,6 +252,7 @@ public class StatusListAdapter extends BaseAdapter implements OnPostActionInterf
changeDrawableColor(context, R.drawable.ic_fav_black,R.color.black);
changeDrawableColor(context, R.drawable.ic_photo,R.color.black);
changeDrawableColor(context, R.drawable.ic_remove_red_eye,R.color.black);
changeDrawableColor(context, R.drawable.ic_translate,R.color.black);
}
@ -293,6 +335,11 @@ public class StatusListAdapter extends BaseAdapter implements OnPostActionInterf
status.getReblog() != null?status.getReblog().getMentions():status.getMentions(),
status.getReblog() != null?status.getReblog().getTags():status.getTags());
if( status.getContent_translated() != null && status.getContent_translated().length() > 0){
holder.status_content_translated = Helper.clickableElements(context, holder.status_content_translated,status.getContent_translated(),
status.getReblog() != null?status.getReblog().getMentions():status.getMentions(),
status.getReblog() != null?status.getReblog().getTags():status.getTags());
}
holder.status_favorite_count.setText(String.valueOf(status.getFavourites_count()));
holder.status_reblog_count.setText(String.valueOf(status.getReblogs_count()));
holder.status_toot_date.setText(Helper.dateDiff(context, status.getCreated_at()));
@ -577,6 +624,27 @@ public class StatusListAdapter extends BaseAdapter implements OnPostActionInterf
}
}
@Override
public void onTranslatedTextview(int position, String translatedResult, Boolean error) {
if( error){
Toast.makeText(context, R.string.toast_error_translate, Toast.LENGTH_LONG).show();
return;
}
if( statuses.size() > position) {
try {
JSONObject translationJson = new JSONObject(translatedResult);
JSONArray aJsonArray = translationJson.getJSONArray("text");
String aJsonString = aJsonArray.get(0).toString();
aJsonString = URLDecoder.decode(aJsonString, "UTF-8");
statuses.get(position).setTranslated(true);
statuses.get(position).setTranslationShown(true);
statuses.get(position).setContent_translated(aJsonString);
statusListAdapter.notifyDataSetChanged();
} catch (JSONException | UnsupportedEncodingException e) {
Toast.makeText(context, R.string.toast_error_translate, Toast.LENGTH_LONG).show();
}
}
}
private class ViewHolder {
@ -586,6 +654,7 @@ public class StatusListAdapter extends BaseAdapter implements OnPostActionInterf
Button status_spoiler_button;
TextView status_content;
TextView status_content_translated;
TextView status_account_username;
TextView status_account_displayname;
ImageView status_account_profile;
@ -609,6 +678,7 @@ public class StatusListAdapter extends BaseAdapter implements OnPostActionInterf
RelativeLayout status_prev4_container;
ImageView status_reply;
ImageView status_privacy;
ImageView status_translate;
LinearLayout status_container2;
LinearLayout status_container3;
LinearLayout main_container;

View File

@ -1125,4 +1125,24 @@ public class Helper {
mDrawable.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
return mDrawable;
}
/**
* Returns the current locale of the device
* @param context Context
* @return String locale
*/
public static String currentLocale(Context context) {
String locale;
Locale current;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
current = context.getResources().getConfiguration().getLocales().get(0);
} else {
//noinspection deprecation
current = context.getResources().getConfiguration().locale;
}
locale = current.toString();
locale = locale.split("_")[0];
return locale;
}
}

View File

@ -0,0 +1,23 @@
/* Copyright 2017 Thomas Schneider
*
* This file is a part of Mastodon Etalab for mastodon.etalab.gouv.fr
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Mastodon Etalab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Thomas Schneider; if not,
* see <http://www.gnu.org/licenses>. */
package fr.gouv.etalab.mastodon.interfaces;
/**
* Created by Thomas on 03/07/2017.
* Yandex client API Handler
*/
public interface OnTranslatedInterface {
void onTranslatedTextview(int position, String translatedResult, Boolean error);
}

View File

@ -0,0 +1,49 @@
/* Copyright 2017 Thomas Schneider
*
* This file is a part of Mastodon Etalab for mastodon.etalab.gouv.fr
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Mastodon Etalab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Thomas Schneider; if not,
* see <http://www.gnu.org/licenses>. */
package fr.gouv.etalab.mastodon.translation;
import com.loopj.android.http.AsyncHttpClient;
import com.loopj.android.http.AsyncHttpResponseHandler;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
class YandexClient {
private static final String BASE_URL = "https://translate.yandex.net/api/v1.5/tr.json/translate?";
private static final String YANDEX_KEY = "trnsl.1.1.20170703T074828Z.a95168c920f61b17.699437a40bbfbddc4cd57f345a75c83f0f30c420";
private static AsyncHttpClient client = new AsyncHttpClient();
public static void get(String text, String toLanguage, AsyncHttpResponseHandler responseHandler) {
client.post(getAbsoluteUrl(text, toLanguage), responseHandler);
}
private static String getAbsoluteUrl(String content, String toLanguage) {
String key = "key=" + YANDEX_KEY + "&";
String lang = "lang=" + toLanguage + "&";
String text;
try {
text = "text=" + URLEncoder.encode(content, "utf-8") + "&";
} catch (UnsupportedEncodingException e) {
text = "text=" + content + "&";
e.printStackTrace();
}
String format = "format=html&";
return BASE_URL + key + lang +format + text ;
}
}

View File

@ -0,0 +1,54 @@
/* Copyright 2017 Thomas Schneider
*
* This file is a part of Mastodon Etalab for mastodon.etalab.gouv.fr
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Mastodon Etalab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Thomas Schneider; if not,
* see <http://www.gnu.org/licenses>. */
package fr.gouv.etalab.mastodon.translation;
import com.loopj.android.http.AsyncHttpResponseHandler;
import org.json.JSONException;
import cz.msebera.android.httpclient.Header;
import fr.gouv.etalab.mastodon.interfaces.OnTranslatedInterface;
/**
* Created by Thomas on 03/07/2017.
* Yandex client API
*/
public class YandexQuery {
private OnTranslatedInterface listener;
public YandexQuery(OnTranslatedInterface listenner) {
this.listener = listenner;
}
public void getYandexTextview(final int position, final String text, final String toLanguage) throws JSONException {
YandexClient.get(text, toLanguage, new AsyncHttpResponseHandler() {
@Override
public void onStart() {
}
@Override
public void onSuccess(int statusCode, Header[] headers, byte[] response) {
String str_response = new String(response);
listener.onTranslatedTextview(position, str_response,false);
}
@Override
public void onFailure(int statusCode, Header[] headers, byte[] errorResponse, Throwable e) {
listener.onTranslatedTextview(position, null, true);
}
});
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 456 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 330 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 319 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 554 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 823 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 939 B

View File

@ -102,6 +102,16 @@
android:layout_width="50dp"
android:layout_height="50dp"
android:indeterminate="true" />
<TextView
android:id="@+id/loader_progress"
android:textSize="12sp"
android:visibility="gone"
android:layout_marginTop="10dp"
android:textColor="?attr/colorAccent"
android:layout_below="@+id/pbar_inf"
android:layout_width="50dp"
android:gravity="center"
android:layout_height="wrap_content" />
</RelativeLayout>
</LinearLayout>
</HorizontalScrollView>

View File

@ -146,6 +146,13 @@
android:autoLink="web"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/status_content_translated"
android:layout_marginTop="10dp"
android:autoLink="web"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<LinearLayout
android:id="@+id/status_document_container"
android:orientation="horizontal"
@ -322,6 +329,15 @@
android:paddingRight="10dp"
android:paddingLeft="10dp"
android:layout_height="wrap_content">
<ImageView
android:layout_marginRight="20dp"
android:layout_marginEnd="20dp"
android:id="@+id/status_translate"
android:layout_gravity="center_vertical"
android:layout_width="25dp"
android:layout_height="25dp"
android:src="@drawable/ic_translate"
tools:ignore="ContentDescription" />
<ImageView
android:layout_marginRight="20dp"
android:layout_marginEnd="20dp"

View File

@ -219,6 +219,7 @@
<string name="toast_update_credential_ok">Les données du profil ont été sauvegardées !</string>
<string name="nothing_to_do">Aucune action ne peut être réalisée</string>
<string name="toast_saved">Le média a été enregistré !</string>
<string name="toast_error_translate">Une erreur est survenue lors de la traduction !</string>
<!-- Settings -->
<string name="settings_title_optimisation">Optimisation du chargement</string>
<string name="set_toots_page">Nombre de pouets par chargement</string>