diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 5cd512436..a2217adfa 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -64,10 +64,16 @@
android:noHistory="true"
android:label="@string/app_name"
/>
+
. */
-
package fr.gouv.etalab.mastodon.activities;
import android.content.Context;
import android.content.SharedPreferences;
-import android.net.Uri;
-import android.os.AsyncTask;
+import android.graphics.Bitmap;
+
+import android.media.MediaPlayer;
import android.os.Build;
import android.os.Bundle;
-import android.support.v7.app.AlertDialog;
+import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
import android.webkit.CookieManager;
-import android.webkit.CookieSyncManager;
import android.webkit.WebChromeClient;
+import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
import android.widget.ProgressBar;
+import android.widget.TextView;
-import com.loopj.android.http.AsyncHttpResponseHandler;
-import com.loopj.android.http.RequestParams;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import cz.msebera.android.httpclient.Header;
-import fr.gouv.etalab.mastodon.asynctasks.UpdateAccountInfoAsyncTask;
-import fr.gouv.etalab.mastodon.client.OauthClient;
import fr.gouv.etalab.mastodon.helper.Helper;
import mastodon.etalab.gouv.fr.mastodon.R;
+
/**
- * Created by Thomas on 24/04/2017.
- * Webview to connect accounts
+ * Created by Thomas on 24/06/2017.
+ * Webview activity
*/
+
public class WebviewActivity extends AppCompatActivity {
+ private String url;
+ private ProgressBar pbar;
- private WebView webView;
- private AlertDialog alert;
- private String clientId, clientSecret;
- private String instance;
-
- public void onCreate(Bundle savedInstanceState)
- {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_webview);
Bundle b = getIntent().getExtras();
if(b != null)
- instance = b.getString("instance");
- if( instance == null)
+ url = b.getString("url", null);
+ if( url == null)
finish();
+ if( getSupportActionBar() != null)
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ final SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
+ boolean javascript = sharedpreferences.getBoolean(Helper.SET_JAVASCRIPT, true);
- SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
- clientId = sharedpreferences.getString(Helper.CLIENT_ID, null);
- clientSecret = sharedpreferences.getString(Helper.CLIENT_SECRET, null);
- webView = (WebView) findViewById(R.id.webviewConnect);
- clearCookies(getApplicationContext());
- final ProgressBar pbar = (ProgressBar) findViewById(R.id.progress_bar);
- webView.setWebChromeClient(new WebChromeClient() {
+
+ pbar = (ProgressBar) findViewById(R.id.progress_bar);
+ WebView webView = (WebView) findViewById(R.id.webview);
+ webView.getSettings().setJavaScriptEnabled(javascript);
+ webView.getSettings().setUseWideViewPort(true);
+ webView.getSettings().setLoadWithOverviewMode(true);
+ webView.getSettings().setSupportZoom(true);
+ webView.getSettings().setDisplayZoomControls(false);
+ webView.getSettings().setBuiltInZoomControls(true);
+ webView.getSettings().setAllowContentAccess(true);
+ webView.getSettings().setLoadsImagesAutomatically(true);
+ webView.getSettings().setSupportMultipleWindows(false);
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
+ //noinspection deprecation
+ webView.getSettings().setPluginState(WebSettings.PluginState.ON);
+ }
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ webView.getSettings().setMediaPlaybackRequiresUserGesture(true);
+ }
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
+ webView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
+ }
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ boolean cookies = sharedpreferences.getBoolean(Helper.SET_COOKIES, false);
+ CookieManager cookieManager = CookieManager.getInstance();
+ cookieManager.setAcceptThirdPartyCookies(webView, cookies);
+ }
+ webView.getSettings().setAppCacheEnabled(true);
+ webView.getSettings().setDatabaseEnabled(true);
+ webView.getSettings().setCacheMode(WebSettings.LOAD_DEFAULT);
+
+
+ setTitle("");
+ FrameLayout webview_container = (FrameLayout) findViewById(R.id.webview_container);
+ final ViewGroup videoLayout = (ViewGroup) findViewById(R.id.videoLayout); // Your own view, read class comments
+
+
+ MastalabWebChromeClient mastalabWebChromeClient = new MastalabWebChromeClient(webView, webview_container, videoLayout);
+ mastalabWebChromeClient.setOnToggledFullscreen(new ToggledFullscreenCallback() {
@Override
- public void onProgressChanged(WebView view, int progress) {
- if (progress < 100 && pbar.getVisibility() == ProgressBar.GONE) {
- pbar.setVisibility(ProgressBar.VISIBLE);
- }
- pbar.setProgress(progress);
- if (progress == 100) {
- pbar.setVisibility(ProgressBar.GONE);
+ public void toggledFullscreen(boolean fullscreen) {
+
+ if (fullscreen) {
+ videoLayout.setVisibility(View.VISIBLE);
+ WindowManager.LayoutParams attrs = getWindow().getAttributes();
+ attrs.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN;
+ attrs.flags |= WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
+ getWindow().setAttributes(attrs);
+ getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE);
+ } else {
+ WindowManager.LayoutParams attrs = getWindow().getAttributes();
+ attrs.flags &= ~WindowManager.LayoutParams.FLAG_FULLSCREEN;
+ attrs.flags &= ~WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
+ getWindow().setAttributes(attrs);
+ getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
+ videoLayout.setVisibility(View.GONE);
}
}
});
-
-
- webView.setWebViewClient(new WebViewClient() {
- @SuppressWarnings("deprecation")
- @Override
- public boolean shouldOverrideUrlLoading(WebView view, String url){
- super.shouldOverrideUrlLoading(view,url);
- if( url.contains(Helper.REDIRECT_CONTENT_WEB)){
- String val[] = url.split("code=");
- String code = val[1];
-
- String action = "/oauth/token";
- RequestParams parameters = new RequestParams();
- parameters.add(Helper.CLIENT_ID, clientId);
- parameters.add(Helper.CLIENT_SECRET, clientSecret);
- parameters.add(Helper.REDIRECT_URI,Helper.REDIRECT_CONTENT_WEB);
- parameters.add("grant_type", "authorization_code");
- parameters.add("code",code);
- new OauthClient(instance).post(action, parameters, new AsyncHttpResponseHandler() {
- @Override
- public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
- String response = new String(responseBody);
- JSONObject resobj;
- try {
- resobj = new JSONObject(response);
- String token = resobj.get("access_token").toString();
- SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
- SharedPreferences.Editor editor = sharedpreferences.edit();
- editor.putString(Helper.PREF_KEY_OAUTH_TOKEN, token);
- editor.apply();
- //Update the account with the token;
- new UpdateAccountInfoAsyncTask(WebviewActivity.this, token, instance).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
- } catch (JSONException e) {
- e.printStackTrace();
- }
-
- }
-
- @Override
- public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
- error.printStackTrace();
- }
- });
-
-
- return true;
- }
- return false;
- }
-
- });
- webView.loadUrl(redirectUserToAuthorizeAndLogin());
+ webView.setWebChromeClient(mastalabWebChromeClient);
+ webView.setWebViewClient(new MastalabWebViewClient());
+ webView.loadUrl(url);
}
-
@Override
- public void onBackPressed() {
- if (webView != null && webView.canGoBack()) {
- webView.goBack();
- } else {
- super.onBackPressed();
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ finish();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
}
}
-
-
- private String redirectUserToAuthorizeAndLogin() {
-
- String queryString = Helper.CLIENT_ID + "="+ clientId;
- queryString += "&" + Helper.REDIRECT_URI + "="+ Uri.encode(Helper.REDIRECT_CONTENT_WEB);
- queryString += "&" + Helper.RESPONSE_TYPE +"=code";
- queryString += "&" + Helper.SCOPE +"=" + Helper.OAUTH_SCOPES;
- return "https://" + instance + Helper.EP_AUTHORIZE + "?" + queryString;
- }
-
-
@Override
- public void onDestroy() {
+ public void onDestroy(){
super.onDestroy();
- if (alert != null) {
- alert.dismiss();
- alert = null;
+
+ }
+
+ private class MastalabWebViewClient extends WebViewClient{
+ @Override
+ public void onPageFinished(WebView view, String url) {
+ super.onPageFinished(view, url);
+ }
+ @Override
+ public void onPageStarted (WebView view, String url, Bitmap favicon) {
+ super.onPageStarted(view, url, favicon);
+ ActionBar actionBar = getSupportActionBar();
+ LayoutInflater mInflater = LayoutInflater.from(WebviewActivity.this);
+ if( actionBar != null){
+ View webview_actionbar = mInflater.inflate(R.layout.webview_actionbar, null);
+ TextView webview_title = (TextView) webview_actionbar.findViewById(R.id.webview_title);
+ webview_title.setText(url);
+ actionBar.setCustomView(webview_actionbar);
+ actionBar.setDisplayShowCustomEnabled(true);
+ }else {
+ setTitle(url);
+ }
}
}
- @SuppressWarnings("deprecation")
- public static void clearCookies(Context context)
- {
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
- CookieManager.getInstance().removeAllCookies(null);
- CookieManager.getInstance().flush();
- } else {
- CookieSyncManager cookieSyncMngr=CookieSyncManager.createInstance(context);
- cookieSyncMngr.startSync();
- CookieManager cookieManager=CookieManager.getInstance();
- cookieManager.removeAllCookie();
- cookieManager.removeSessionCookie();
- cookieSyncMngr.stopSync();
- cookieSyncMngr.sync();
- }
+ interface ToggledFullscreenCallback {
+ void toggledFullscreen(boolean fullscreen);
}
-}
\ No newline at end of file
+
+
+ private class MastalabWebChromeClient extends WebChromeClient implements MediaPlayer.OnPreparedListener, MediaPlayer.OnCompletionListener, MediaPlayer.OnErrorListener {
+
+ private FrameLayout videoViewContainer;
+ private CustomViewCallback videoViewCallback;
+
+ private ToggledFullscreenCallback toggledFullscreenCallback;
+ private boolean isVideoFullscreen;
+ private WebView webView;
+ private View activityNonVideoView;
+ private ViewGroup activityVideoView;
+ private View loadingView;
+
+ MastalabWebChromeClient(WebView webView, FrameLayout webviewContainer, ViewGroup videoLayout){
+ this.isVideoFullscreen = false;
+ this.webView = webView;
+ this.activityNonVideoView = webviewContainer;
+ this.activityVideoView = videoLayout;
+ }
+
+ @Override
+ public void onProgressChanged(WebView view, int progress) {
+ if (progress < 100 && pbar.getVisibility() == ProgressBar.GONE) {
+ pbar.setVisibility(ProgressBar.VISIBLE);
+ }
+ pbar.setProgress(progress);
+ if (progress == 100) {
+ pbar.setVisibility(ProgressBar.GONE);
+ }
+ }
+ @Override
+ public void onReceivedIcon(WebView view, Bitmap icon) {
+ super.onReceivedIcon(view, icon);
+ LayoutInflater mInflater = LayoutInflater.from(WebviewActivity.this);
+ ActionBar actionBar = getSupportActionBar();
+ if( actionBar != null){
+ View webview_actionbar = mInflater.inflate(R.layout.webview_actionbar, null);
+ TextView webview_title = (TextView) webview_actionbar.findViewById(R.id.webview_title);
+ webview_title.setText(view.getTitle());
+ ImageView webview_favicon = (ImageView) webview_actionbar.findViewById(R.id.webview_favicon);
+ if( icon != null)
+ webview_favicon.setImageBitmap(icon);
+ actionBar.setCustomView(webview_actionbar);
+ actionBar.setDisplayShowCustomEnabled(true);
+ }else {
+ setTitle(view.getTitle());
+ }
+
+ }
+
+ //FULLSCREEN VIDEO
+ //Code from https://stackoverflow.com/a/16179544/3197259
+
+ /**
+ * Set a callback that will be fired when the video starts or finishes displaying using a custom view (typically full-screen)
+ * @param callback A VideoEnabledWebChromeClient.ToggledFullscreenCallback callback
+ */
+ void setOnToggledFullscreen(ToggledFullscreenCallback callback) {
+ this.toggledFullscreenCallback = callback;
+ }
+
+ @Override
+ public void onShowCustomView(View view, CustomViewCallback callback) {
+ if (view instanceof FrameLayout) {
+ if( getSupportActionBar() != null)
+ getSupportActionBar().hide();
+ // A video wants to be shown
+ FrameLayout frameLayout = (FrameLayout) view;
+ View focusedChild = frameLayout.getFocusedChild();
+
+ // Save video related variables
+ isVideoFullscreen = true;
+ this.videoViewContainer = frameLayout;
+ this.videoViewCallback = callback;
+
+ // Hide the non-video view, add the video view, and show it
+ activityNonVideoView.setVisibility(View.INVISIBLE);
+ activityVideoView.addView(videoViewContainer, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
+ activityVideoView.setVisibility(View.VISIBLE);
+ if (focusedChild instanceof android.widget.VideoView) {
+ // android.widget.VideoView (typically API level <11)
+ android.widget.VideoView videoView = (android.widget.VideoView) focusedChild;
+ // Handle all the required events
+ videoView.setOnPreparedListener(this);
+ videoView.setOnCompletionListener(this);
+ videoView.setOnErrorListener(this);
+ } else {
+ // Other classes, including:
+ // - android.webkit.HTML5VideoFullScreen$VideoSurfaceView, which inherits from android.view.SurfaceView (typically API level 11-18)
+ // - android.webkit.HTML5VideoFullScreen$VideoTextureView, which inherits from android.view.TextureView (typically API level 11-18)
+ // - com.android.org.chromium.content.browser.ContentVideoView$VideoSurfaceView, which inherits from android.view.SurfaceView (typically API level 19+)
+
+ // Handle HTML5 video ended event only if the class is a SurfaceView
+ // Test case: TextureView of Sony Xperia T API level 16 doesn't work fullscreen when loading the javascript below
+ if (webView != null && webView.getSettings().getJavaScriptEnabled() && focusedChild instanceof SurfaceView) {
+ // Run javascript code that detects the video end and notifies the Javascript interface
+ String js = "javascript:";
+ js += "var _ytrp_html5_video_last;";
+ js += "var _ytrp_html5_video = document.getElementsByTagName('video')[0];";
+ js += "if (_ytrp_html5_video != undefined && _ytrp_html5_video != _ytrp_html5_video_last) {";
+ {
+ js += "_ytrp_html5_video_last = _ytrp_html5_video;";
+ js += "function _ytrp_html5_video_ended() {";
+ {
+ js += "_VideoEnabledWebView.notifyVideoEnd();"; // Must match Javascript interface name and method of VideoEnableWebView
+ }
+ js += "}";
+ js += "_ytrp_html5_video.addEventListener('ended', _ytrp_html5_video_ended);";
+ }
+ js += "}";
+ webView.loadUrl(js);
+ }
+ }
+ // Notify full-screen change
+ if (toggledFullscreenCallback != null) {
+ toggledFullscreenCallback.toggledFullscreen(true);
+ }
+ }
+ }
+
+ // Available in API level 14+, deprecated in API level 18+
+ @Override @SuppressWarnings("deprecation")
+ public void onShowCustomView(View view, int requestedOrientation, CustomViewCallback callback) {
+ onShowCustomView(view, callback);
+ }
+
+ @Override
+ public void onHideCustomView() {
+ if( getSupportActionBar() != null)
+ getSupportActionBar().show();
+ // This method should be manually called on video end in all cases because it's not always called automatically.
+ // This method must be manually called on back key press (from this class' onBackPressed() method).
+ if (isVideoFullscreen) {
+ // Hide the video view, remove it, and show the non-video view
+ activityVideoView.setVisibility(View.INVISIBLE);
+ activityVideoView.removeView(videoViewContainer);
+ activityNonVideoView.setVisibility(View.VISIBLE);
+ // Call back (only in API level <19, because in API level 19+ with chromium webview it crashes)
+ if (videoViewCallback != null && !videoViewCallback.getClass().getName().contains(".chromium.")) {
+ videoViewCallback.onCustomViewHidden();
+ }
+
+ // Reset video related variables
+ isVideoFullscreen = false;
+ videoViewContainer = null;
+ videoViewCallback = null;
+
+ // Notify full-screen change
+ if (toggledFullscreenCallback != null) {
+ toggledFullscreenCallback.toggledFullscreen(false);
+ }
+ }
+ }
+
+ // Video will start loading
+ @Override
+ public View getVideoLoadingProgressView() {
+ return super.getVideoLoadingProgressView();
+ }
+
+ // Video will start playing, only called in the case of android.widget.VideoView (typically API level <11)
+ @Override
+ public void onPrepared(MediaPlayer mp) {
+ if (loadingView != null) {
+ loadingView.setVisibility(View.GONE);
+ }
+ }
+
+ // Video finished playing, only called in the case of android.widget.VideoView (typically API level <11)
+ @Override
+ public void onCompletion(MediaPlayer mp) {
+ onHideCustomView();
+ }
+
+ // Error while playing video, only called in the case of android.widget.VideoView (typically API level <11)
+ @Override
+ public boolean onError(MediaPlayer mp, int what, int extra) {
+ return false; // By returning false, onCompletion() will be called
+ }
+
+ }
+
+
+}
diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/activities/WebviewConnectActivity.java b/app/src/main/java/fr/gouv/etalab/mastodon/activities/WebviewConnectActivity.java
new file mode 100644
index 000000000..21f0fdbf8
--- /dev/null
+++ b/app/src/main/java/fr/gouv/etalab/mastodon/activities/WebviewConnectActivity.java
@@ -0,0 +1,189 @@
+/* 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 . */
+
+package fr.gouv.etalab.mastodon.activities;
+
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.app.AppCompatActivity;
+import android.webkit.CookieManager;
+import android.webkit.CookieSyncManager;
+import android.webkit.WebChromeClient;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+import android.widget.ProgressBar;
+
+import com.loopj.android.http.AsyncHttpResponseHandler;
+import com.loopj.android.http.RequestParams;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import cz.msebera.android.httpclient.Header;
+import fr.gouv.etalab.mastodon.asynctasks.UpdateAccountInfoAsyncTask;
+import fr.gouv.etalab.mastodon.client.OauthClient;
+import fr.gouv.etalab.mastodon.helper.Helper;
+import mastodon.etalab.gouv.fr.mastodon.R;
+
+/**
+ * Created by Thomas on 24/04/2017.
+ * Webview to connect accounts
+ */
+public class WebviewConnectActivity extends AppCompatActivity {
+
+
+ private WebView webView;
+ private AlertDialog alert;
+ private String clientId, clientSecret;
+ private String instance;
+
+ public void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_webview);
+ Bundle b = getIntent().getExtras();
+ if(b != null)
+ instance = b.getString("instance");
+ if( instance == null)
+ finish();
+
+ SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
+ clientId = sharedpreferences.getString(Helper.CLIENT_ID, null);
+ clientSecret = sharedpreferences.getString(Helper.CLIENT_SECRET, null);
+
+ webView = (WebView) findViewById(R.id.webviewConnect);
+ clearCookies(getApplicationContext());
+ final ProgressBar pbar = (ProgressBar) findViewById(R.id.progress_bar);
+ webView.setWebChromeClient(new WebChromeClient() {
+ @Override
+ public void onProgressChanged(WebView view, int progress) {
+ if (progress < 100 && pbar.getVisibility() == ProgressBar.GONE) {
+ pbar.setVisibility(ProgressBar.VISIBLE);
+ }
+ pbar.setProgress(progress);
+ if (progress == 100) {
+ pbar.setVisibility(ProgressBar.GONE);
+ }
+ }
+ });
+
+
+ webView.setWebViewClient(new WebViewClient() {
+ @SuppressWarnings("deprecation")
+ @Override
+ public boolean shouldOverrideUrlLoading(WebView view, String url){
+ super.shouldOverrideUrlLoading(view,url);
+ if( url.contains(Helper.REDIRECT_CONTENT_WEB)){
+ String val[] = url.split("code=");
+ String code = val[1];
+
+ String action = "/oauth/token";
+ RequestParams parameters = new RequestParams();
+ parameters.add(Helper.CLIENT_ID, clientId);
+ parameters.add(Helper.CLIENT_SECRET, clientSecret);
+ parameters.add(Helper.REDIRECT_URI,Helper.REDIRECT_CONTENT_WEB);
+ parameters.add("grant_type", "authorization_code");
+ parameters.add("code",code);
+ new OauthClient(instance).post(action, parameters, new AsyncHttpResponseHandler() {
+ @Override
+ public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
+ String response = new String(responseBody);
+ JSONObject resobj;
+ try {
+ resobj = new JSONObject(response);
+ String token = resobj.get("access_token").toString();
+ SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
+ SharedPreferences.Editor editor = sharedpreferences.edit();
+ editor.putString(Helper.PREF_KEY_OAUTH_TOKEN, token);
+ editor.apply();
+ //Update the account with the token;
+ new UpdateAccountInfoAsyncTask(WebviewConnectActivity.this, token, instance).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+
+ }
+
+ @Override
+ public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
+ error.printStackTrace();
+ }
+ });
+
+
+ return true;
+ }
+ return false;
+ }
+
+ });
+ webView.loadUrl(redirectUserToAuthorizeAndLogin());
+ }
+
+
+ @Override
+ public void onBackPressed() {
+ if (webView != null && webView.canGoBack()) {
+ webView.goBack();
+ } else {
+ super.onBackPressed();
+ }
+ }
+
+
+
+ private String redirectUserToAuthorizeAndLogin() {
+
+ String queryString = Helper.CLIENT_ID + "="+ clientId;
+ queryString += "&" + Helper.REDIRECT_URI + "="+ Uri.encode(Helper.REDIRECT_CONTENT_WEB);
+ queryString += "&" + Helper.RESPONSE_TYPE +"=code";
+ queryString += "&" + Helper.SCOPE +"=" + Helper.OAUTH_SCOPES;
+ return "https://" + instance + Helper.EP_AUTHORIZE + "?" + queryString;
+ }
+
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (alert != null) {
+ alert.dismiss();
+ alert = null;
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ public static void clearCookies(Context context)
+ {
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
+ CookieManager.getInstance().removeAllCookies(null);
+ CookieManager.getInstance().flush();
+ } else {
+ CookieSyncManager cookieSyncMngr=CookieSyncManager.createInstance(context);
+ cookieSyncMngr.startSync();
+ CookieManager cookieManager=CookieManager.getInstance();
+ cookieManager.removeAllCookie();
+ cookieManager.removeSessionCookie();
+ cookieSyncMngr.stopSync();
+ cookieSyncMngr.sync();
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/fragments/SettingsFragment.java b/app/src/main/java/fr/gouv/etalab/mastodon/fragments/SettingsFragment.java
new file mode 100644
index 000000000..08ff2756f
--- /dev/null
+++ b/app/src/main/java/fr/gouv/etalab/mastodon/fragments/SettingsFragment.java
@@ -0,0 +1,166 @@
+package fr.gouv.etalab.mastodon.fragments;
+/* 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 . */
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v7.widget.SwitchCompat;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.LinearLayout;
+
+import fr.gouv.etalab.mastodon.helper.Helper;
+import mastodon.etalab.gouv.fr.mastodon.R;
+
+
+/**
+ * Created by Thomas on 24/06/2017.
+ * Fragment for settings, yes I didn't use PreferenceFragment :)
+ */
+public class SettingsFragment extends Fragment {
+
+
+ private Context context;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+
+ View rootView = inflater.inflate(R.layout.fragment_settings, container, false);
+ context = getContext();
+ final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
+
+
+ boolean show_reply = sharedpreferences.getBoolean(Helper.SET_SHOW_REPLY, false);
+ final CheckBox set_show_reply = (CheckBox) rootView.findViewById(R.id.set_show_reply);
+ set_show_reply.setChecked(show_reply);
+
+ set_show_reply.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ SharedPreferences.Editor editor = sharedpreferences.edit();
+ editor.putBoolean(Helper.SET_SHOW_REPLY, set_show_reply.isChecked());
+ editor.apply();
+ }
+ });
+
+ boolean show_error_messages = sharedpreferences.getBoolean(Helper.SET_SHOW_ERROR_MESSAGES, true);
+ final CheckBox set_show_error_messages = (CheckBox) rootView.findViewById(R.id.set_show_error_messages);
+ set_show_error_messages.setChecked(show_error_messages);
+
+ set_show_error_messages.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ SharedPreferences.Editor editor = sharedpreferences.edit();
+ editor.putBoolean(Helper.SET_SHOW_ERROR_MESSAGES, set_show_error_messages.isChecked());
+ editor.apply();
+ }
+ });
+
+
+ boolean notif_validation = sharedpreferences.getBoolean(Helper.SET_NOTIF_VALIDATION, true);
+ final CheckBox set_share_validation = (CheckBox) rootView.findViewById(R.id.set_share_validation);
+ set_share_validation.setChecked(notif_validation);
+
+ set_share_validation.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ SharedPreferences.Editor editor = sharedpreferences.edit();
+ editor.putBoolean(Helper.SET_NOTIF_VALIDATION, set_share_validation.isChecked());
+ editor.apply();
+ }
+ });
+
+
+ final CheckBox set_embedded_browser = (CheckBox) rootView.findViewById(R.id.set_embedded_browser);
+ final LinearLayout set_javascript_container = (LinearLayout) rootView.findViewById(R.id.set_javascript_container);
+ final SwitchCompat set_javascript = (SwitchCompat) rootView.findViewById(R.id.set_javascript);
+ boolean javascript = sharedpreferences.getBoolean(Helper.SET_JAVASCRIPT, true);
+ boolean embedded_browser = sharedpreferences.getBoolean(Helper.SET_EMBEDDED_BROWSER, true);
+ if( !embedded_browser){
+ set_javascript_container.setVisibility(View.GONE);
+ }else{
+ set_javascript_container.setVisibility(View.VISIBLE);
+ }
+ set_embedded_browser.setChecked(embedded_browser);
+ set_embedded_browser.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ SharedPreferences.Editor editor = sharedpreferences.edit();
+ editor.putBoolean(Helper.SET_EMBEDDED_BROWSER, set_embedded_browser.isChecked());
+ editor.apply();
+ if( !set_embedded_browser.isChecked()){
+ set_javascript_container.setVisibility(View.GONE);
+ }else{
+ set_javascript_container.setVisibility(View.VISIBLE);
+ }
+ }
+ });
+
+ set_javascript.setChecked(javascript);
+ set_javascript.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ SharedPreferences.Editor editor = sharedpreferences.edit();
+ editor.putBoolean(Helper.SET_JAVASCRIPT, isChecked);
+ editor.apply();
+ }
+ });
+
+ final LinearLayout set_cookies_container = (LinearLayout) rootView.findViewById(R.id.set_cookies_container);
+ final SwitchCompat set_cookies = (SwitchCompat) rootView.findViewById(R.id.set_cookies);
+ boolean cookies = sharedpreferences.getBoolean(Helper.SET_COOKIES, false);
+
+ set_cookies.setChecked(cookies);
+ set_cookies.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ SharedPreferences.Editor editor = sharedpreferences.edit();
+ editor.putBoolean(Helper.SET_COOKIES, isChecked);
+ editor.apply();
+ }
+ });
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ set_cookies_container.setVisibility(View.VISIBLE);
+ }else {
+ set_cookies_container.setVisibility(View.GONE);
+ }
+
+ return rootView;
+ }
+
+
+
+ @Override
+ public void onCreate(Bundle saveInstance) {
+ super.onCreate(saveInstance);
+ }
+
+
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ this.context = context;
+ }
+
+
+
+
+}
diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/fragments/SettingsNotificationsFragment.java b/app/src/main/java/fr/gouv/etalab/mastodon/fragments/SettingsNotificationsFragment.java
index 3c809a01e..a6724e2fd 100644
--- a/app/src/main/java/fr/gouv/etalab/mastodon/fragments/SettingsNotificationsFragment.java
+++ b/app/src/main/java/fr/gouv/etalab/mastodon/fragments/SettingsNotificationsFragment.java
@@ -49,10 +49,10 @@ public class SettingsNotificationsFragment extends Fragment {
boolean notif_ask = sharedpreferences.getBoolean(Helper.SET_NOTIF_ASK, true);
boolean notif_mention = sharedpreferences.getBoolean(Helper.SET_NOTIF_MENTION, true);
boolean notif_share = sharedpreferences.getBoolean(Helper.SET_NOTIF_SHARE, true);
- boolean notif_validation = sharedpreferences.getBoolean(Helper.SET_NOTIF_VALIDATION, true);
+
boolean notif_wifi = sharedpreferences.getBoolean(Helper.SET_WIFI_ONLY, false);
boolean notif_silent = sharedpreferences.getBoolean(Helper.SET_NOTIF_SILENT, false);
- boolean show_error_messages = sharedpreferences.getBoolean(Helper.SET_SHOW_ERROR_MESSAGES, true);
+
boolean notif_hometimeline = sharedpreferences.getBoolean(Helper.SET_NOTIF_HOMETIMELINE, true);
final CheckBox set_notif_follow = (CheckBox) rootView.findViewById(R.id.set_notif_follow);
@@ -60,9 +60,9 @@ public class SettingsNotificationsFragment extends Fragment {
final CheckBox set_notif_follow_ask = (CheckBox) rootView.findViewById(R.id.set_notif_follow_ask);
final CheckBox set_notif_follow_mention = (CheckBox) rootView.findViewById(R.id.set_notif_follow_mention);
final CheckBox set_notif_follow_share = (CheckBox) rootView.findViewById(R.id.set_notif_follow_share);
- final CheckBox set_share_validation = (CheckBox) rootView.findViewById(R.id.set_share_validation);
+
final CheckBox set_notif_hometimeline = (CheckBox) rootView.findViewById(R.id.set_notif_hometimeline);
- final CheckBox set_show_error_messages = (CheckBox) rootView.findViewById(R.id.set_show_error_messages);
+
final SwitchCompat switchCompatWIFI = (SwitchCompat) rootView.findViewById(R.id.set_wifi_only);
final SwitchCompat switchCompatSilent = (SwitchCompat) rootView.findViewById(R.id.set_silence);
@@ -71,9 +71,8 @@ public class SettingsNotificationsFragment extends Fragment {
set_notif_follow_ask.setChecked(notif_ask);
set_notif_follow_mention.setChecked(notif_mention);
set_notif_follow_share.setChecked(notif_share);
- set_share_validation.setChecked(notif_validation);
set_notif_hometimeline.setChecked(notif_hometimeline);
- set_show_error_messages.setChecked(show_error_messages);
+
switchCompatWIFI.setChecked(notif_wifi);
switchCompatSilent.setChecked(notif_silent);
@@ -125,22 +124,7 @@ public class SettingsNotificationsFragment extends Fragment {
editor.apply();
}
});
- set_share_validation.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- SharedPreferences.Editor editor = sharedpreferences.edit();
- editor.putBoolean(Helper.SET_NOTIF_VALIDATION, set_share_validation.isChecked());
- editor.apply();
- }
- });
- set_show_error_messages.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- SharedPreferences.Editor editor = sharedpreferences.edit();
- editor.putBoolean(Helper.SET_SHOW_ERROR_MESSAGES, set_show_error_messages.isChecked());
- editor.apply();
- }
- });
+
switchCompatWIFI.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/fragments/SettingsOptimizationFragment.java b/app/src/main/java/fr/gouv/etalab/mastodon/fragments/SettingsOptimizationFragment.java
index feb93ba16..09997b768 100644
--- a/app/src/main/java/fr/gouv/etalab/mastodon/fragments/SettingsOptimizationFragment.java
+++ b/app/src/main/java/fr/gouv/etalab/mastodon/fragments/SettingsOptimizationFragment.java
@@ -20,7 +20,6 @@ import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.CheckBox;
import android.widget.RadioGroup;
import android.widget.SeekBar;
import android.widget.TextView;
@@ -46,18 +45,6 @@ public class SettingsOptimizationFragment extends Fragment {
final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
- boolean show_reply = sharedpreferences.getBoolean(Helper.SET_SHOW_REPLY, false);
- final CheckBox set_show_reply = (CheckBox) rootView.findViewById(R.id.set_show_reply);
- set_show_reply.setChecked(show_reply);
-
- set_show_reply.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- SharedPreferences.Editor editor = sharedpreferences.edit();
- editor.putBoolean(Helper.SET_SHOW_REPLY, set_show_reply.isChecked());
- editor.apply();
- }
- });
//Status per page
SeekBar statusSeekBar = (SeekBar) rootView.findViewById(R.id.set_toots_per_page);
diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/fragments/TabLayoutSettingsFragment.java b/app/src/main/java/fr/gouv/etalab/mastodon/fragments/TabLayoutSettingsFragment.java
index 07f568433..19987d235 100644
--- a/app/src/main/java/fr/gouv/etalab/mastodon/fragments/TabLayoutSettingsFragment.java
+++ b/app/src/main/java/fr/gouv/etalab/mastodon/fragments/TabLayoutSettingsFragment.java
@@ -40,6 +40,7 @@ public class TabLayoutSettingsFragment extends Fragment {
View inflatedView = inflater.inflate(R.layout.tablayout_settings, container, false);
TabLayout tabLayout = (TabLayout) inflatedView.findViewById(R.id.tabLayout);
+ tabLayout.addTab(tabLayout.newTab().setText(getString(R.string.settings)));
tabLayout.addTab(tabLayout.newTab().setText(getString(R.string.notifications)));
tabLayout.addTab(tabLayout.newTab().setText(getString(R.string.optimization)));
tabLayout.addTab(tabLayout.newTab().setText(getString(R.string.profile)));
@@ -84,10 +85,12 @@ public class TabLayoutSettingsFragment extends Fragment {
public Fragment getItem(int position) {
switch (position) {
case 0:
- return new SettingsNotificationsFragment();
+ return new SettingsFragment();
case 1:
- return new SettingsOptimizationFragment();
+ return new SettingsNotificationsFragment();
case 2:
+ return new SettingsOptimizationFragment();
+ case 3:
return new SettingsProfileFragment();
default:
return new SettingsNotificationsFragment();
diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/helper/Helper.java b/app/src/main/java/fr/gouv/etalab/mastodon/helper/Helper.java
index c82661364..c1f8453b0 100644
--- a/app/src/main/java/fr/gouv/etalab/mastodon/helper/Helper.java
+++ b/app/src/main/java/fr/gouv/etalab/mastodon/helper/Helper.java
@@ -51,6 +51,7 @@ import android.util.Log;
import android.view.MenuItem;
import android.view.View;
import android.view.WindowManager;
+import android.webkit.WebView;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
@@ -81,6 +82,7 @@ import java.util.regex.Pattern;
import fr.gouv.etalab.mastodon.activities.HashTagActivity;
import fr.gouv.etalab.mastodon.activities.LoginActivity;
import fr.gouv.etalab.mastodon.activities.ShowAccountActivity;
+import fr.gouv.etalab.mastodon.activities.WebviewActivity;
import fr.gouv.etalab.mastodon.asynctasks.RemoveAccountAsyncTask;
import fr.gouv.etalab.mastodon.client.Entities.Account;
import fr.gouv.etalab.mastodon.client.Entities.Mention;
@@ -155,6 +157,10 @@ public class Helper {
public static final String SET_NOTIF_SILENT = "set_notif_silent";
public static final String SET_SHOW_REPLY = "set_show_reply";
public static final String SET_SHOW_ERROR_MESSAGES = "set_show_error_messages";
+ public static final String SET_EMBEDDED_BROWSER = "set_embedded_browser";
+ public static final String SET_JAVASCRIPT = "set_javascript";
+ public static final String SET_COOKIES = "set_cookies";
+
//End points
public static final String EP_AUTHORIZE = "/oauth/authorize";
@@ -179,6 +185,12 @@ public class Helper {
private static final Pattern SHORTNAME_PATTERN = Pattern.compile(":([-+\\w]+):");
+ private static final Pattern urlPattern = Pattern.compile(
+ "(?:^|[\\W])((ht|f)tp(s?):\\/\\/|www\\.)"
+ + "(([\\w\\-]+\\.){1,}?([\\w\\-.~]+\\/?)*"
+ + "[\\p{Alnum}.,%_=?\\-+()\\[\\]\\*$~@!:/{};']*)",
+ Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
+
/**
* Converts emojis in input to unicode
* @param input String
@@ -793,19 +805,40 @@ public class Helper {
* @return TextView
*/
public static TextView clickableElements(final Context context, TextView statusTV, String fullContent, List mentions, List tags) {
- //Retrieves accounts name
- Pattern sPattern = Pattern.compile("@([a-zA-Z0-9_]{1,})<\\/span>");
- Matcher m = sPattern.matcher(fullContent);
- while (m.find()) {
- fullContent = fullContent.replaceAll(m.group(0), "" + m.group(0) + "");
- }
+
SpannableString spannableString;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
spannableString = new SpannableString(Html.fromHtml(fullContent, Html.FROM_HTML_MODE_COMPACT));
else
//noinspection deprecation
spannableString = new SpannableString(Html.fromHtml(fullContent));
+ SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
+ boolean embedded_browser = sharedpreferences.getBoolean(Helper.SET_EMBEDDED_BROWSER, true);
+ if( embedded_browser){
+ Matcher matcher = urlPattern.matcher(spannableString);
+ while (matcher.find()){
+ int matchStart = matcher.start(1);
+ int matchEnd = matcher.end();
+ final String url = spannableString.toString().substring(matchStart, matchEnd);
+ spannableString.setSpan(new ClickableSpan() {
+ @Override
+ public void onClick(View textView) {
+ Intent intent = new Intent(context, WebviewActivity.class);
+ Bundle b = new Bundle();
+ b.putString("url", url);
+ intent.putExtras(b);
+ context.startActivity(intent);
+ }
+ @Override
+ public void updateDrawState(TextPaint ds) {
+ super.updateDrawState(ds);
+ }
+ },
+ matchStart, matchEnd,
+ Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+ }
+ }
//Deals with mention to make them clickable
if( mentions != null && mentions.size() > 0 ) {
//Looping through accounts which are mentioned
diff --git a/app/src/main/res/layout/activity_webview.xml b/app/src/main/res/layout/activity_webview.xml
index 8cc5e803e..d4053bb14 100644
--- a/app/src/main/res/layout/activity_webview.xml
+++ b/app/src/main/res/layout/activity_webview.xml
@@ -32,11 +32,23 @@
android:layout_height="10dp"
android:padding="2dp">
-
+ android:layout_weight="1">
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_webview_connect.xml b/app/src/main/res/layout/activity_webview_connect.xml
new file mode 100644
index 000000000..8cc5e803e
--- /dev/null
+++ b/app/src/main/res/layout/activity_webview_connect.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_settings.xml b/app/src/main/res/layout/fragment_settings.xml
new file mode 100644
index 000000000..9a5931f17
--- /dev/null
+++ b/app/src/main/res/layout/fragment_settings.xml
@@ -0,0 +1,111 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_settings_notifications.xml b/app/src/main/res/layout/fragment_settings_notifications.xml
index 62e7e4872..7e857ebce 100644
--- a/app/src/main/res/layout/fragment_settings_notifications.xml
+++ b/app/src/main/res/layout/fragment_settings_notifications.xml
@@ -93,17 +93,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content" />
-
-
-
-
diff --git a/app/src/main/res/layout/tablayout_settings.xml b/app/src/main/res/layout/tablayout_settings.xml
index 993fd97ad..f77e681bb 100644
--- a/app/src/main/res/layout/tablayout_settings.xml
+++ b/app/src/main/res/layout/tablayout_settings.xml
@@ -25,8 +25,9 @@
android:id="@+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ app:tabMaxWidth="0dp"
android:background="@android:color/white"
- app:tabMode="fixed"
+ app:tabMode="scrollable"
app:tabGravity="fill"
/>
diff --git a/app/src/main/res/layout/webview_actionbar.xml b/app/src/main/res/layout/webview_actionbar.xml
new file mode 100644
index 000000000..60a616282
--- /dev/null
+++ b/app/src/main/res/layout/webview_actionbar.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index 380e8bc83..02581bd67 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -23,4 +23,6 @@
#282c37
#009688
#F44336
+
+ #000
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 9483c568a..4f717dc5e 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -53,6 +53,7 @@
Notifications
Demandes d\'abonnements
Optimisation
+ Paramètres
Profil
Que souhaitez-vous faire ?
Supprimer un compte
@@ -242,6 +243,10 @@
Vous avez atteint les 160 caractères autorisés !
Vous avez atteint les 30 caractères autorisés !
+ Utiliser le navigateur intégré
+ Activer Javascript
+ Autoriser les cookies tiers
+
Actualités
Notifier lors de nouveaux pouets sur la page d\'accueil
Afficher les messages d\'erreur