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