diff --git a/app/src/fdroid/java/fr.gouv.etalab.mastodon/activities/MainActivity.java b/app/src/fdroid/java/fr.gouv.etalab.mastodon/activities/MainActivity.java index 643fe909e..4d704de69 100644 --- a/app/src/fdroid/java/fr.gouv.etalab.mastodon/activities/MainActivity.java +++ b/app/src/fdroid/java/fr.gouv.etalab.mastodon/activities/MainActivity.java @@ -90,6 +90,7 @@ import fr.gouv.etalab.mastodon.helper.Helper; import fr.gouv.etalab.mastodon.interfaces.OnRetrieveInstanceInterface; import fr.gouv.etalab.mastodon.interfaces.OnRetrieveMetaDataInterface; import fr.gouv.etalab.mastodon.interfaces.OnUpdateAccountInfoInterface; +import fr.gouv.etalab.mastodon.services.StreamingFederatedTimelineService; import fr.gouv.etalab.mastodon.services.StreamingService; import fr.gouv.etalab.mastodon.sqlite.Sqlite; import fr.gouv.etalab.mastodon.asynctasks.RetrieveAccountsAsyncTask; @@ -132,15 +133,15 @@ public class MainActivity extends AppCompatActivity private RelativeLayout main_app_container; private Stack stackBack = new Stack<>(); - private DisplayStatusFragment homeFragment; + private DisplayStatusFragment homeFragment, federatedFragment; private DisplayNotificationsFragment notificationsFragment; - private BroadcastReceiver receive_data; + private BroadcastReceiver receive_data, receive_federated_data; private boolean display_local, display_global; public static int countNewStatus = 0; public static int countNewNotifications = 0; private String userIdService; - private Intent streamingIntent; - public static boolean broadCastRegistred = false; + private Intent streamingIntent, streamingFederatedIntent; + public static boolean broadCastRegistred = false, broadCastFederatedRegistred = false; public MainActivity() { } @@ -151,6 +152,22 @@ public class MainActivity extends AppCompatActivity final SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, android.content.Context.MODE_PRIVATE); + + + receive_federated_data = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + Bundle b = intent.getExtras(); + userIdService = b.getString("userIdService", null); + String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null); + if( userIdService != null && userIdService.equals(userId)) { + Status status = b.getParcelable("data"); + if (federatedFragment != null) { + federatedFragment.refresh(status); + } + } + } + }; receive_data = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -191,10 +208,17 @@ public class MainActivity extends AppCompatActivity streamingIntent = new Intent(this, StreamingService.class); startService(streamingIntent); + streamingFederatedIntent = new Intent(this, StreamingFederatedTimelineService.class); + startService(streamingFederatedIntent); + if( !broadCastRegistred) { LocalBroadcastManager.getInstance(this).registerReceiver(receive_data, new IntentFilter(Helper.RECEIVE_DATA)); broadCastRegistred = true; } + if( !broadCastFederatedRegistred) { + LocalBroadcastManager.getInstance(this).registerReceiver(receive_federated_data, new IntentFilter(Helper.RECEIVE_FEDERATED_DATA)); + broadCastFederatedRegistred = true; + } @@ -912,8 +936,12 @@ public class MainActivity extends AppCompatActivity super.onDestroy(); if( streamingIntent != null) stopService(streamingIntent); + if( streamingFederatedIntent != null) + stopService(streamingFederatedIntent); LocalBroadcastManager.getInstance(this).unregisterReceiver(receive_data); + LocalBroadcastManager.getInstance(this).unregisterReceiver(receive_federated_data); broadCastRegistred = false; + broadCastFederatedRegistred = false; } @SuppressWarnings("StatementWithEmptyBody") @@ -1119,6 +1147,14 @@ public class MainActivity extends AppCompatActivity case 1: notificationsFragment = (DisplayNotificationsFragment) createdFragment; break; + case 2: + if ( !display_local && display_global) + federatedFragment = (DisplayStatusFragment) createdFragment; + break; + case 3: + if( display_local && display_global) + federatedFragment = (DisplayStatusFragment) createdFragment; + break; } return createdFragment; } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index bd131506c..4ee4ccc16 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -43,6 +43,15 @@ + + + + + + 3) { + int index = lv_status.getFirstVisiblePosition() + 1; + View v = lv_status.getChildAt(0); + int top = (v == null) ? 0 : v.getTop(); + status.setReplies(new ArrayList()); + statuses.add(0, status); + statusListAdapter.notifyDataSetChanged(); + lv_status.setSelectionFromTop(index, top); + + } else { + status.setReplies(new ArrayList()); + status.setNew(false); + statuses.add(0, status); + statusListAdapter.notifyDataSetChanged(); + } + if (textviewNoAction.getVisibility() == View.VISIBLE) + textviewNoAction.setVisibility(View.GONE); + } } } + @Override public void setMenuVisibility(final boolean visible) { super.setMenuVisibility(visible); 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 b76eac29e..9a1f91891 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 @@ -250,9 +250,9 @@ public class Helper { public static final String INTENT_ACTION = "intent_action"; //Receiver - public static final String SEARCH_VALIDATE_ACCOUNT = "search_validate_account"; public static final String HEADER_ACCOUNT = "header_account"; public static final String RECEIVE_DATA = "receive_data"; + public static final String RECEIVE_FEDERATED_DATA = "receive_federated_data"; public static final String RECEIVE_PICTURE = "receive_picture"; //User agent public static final String USER_AGENT = "Mastalab/"+ BuildConfig.VERSION_NAME + " Android/"+ Build.VERSION.RELEASE; diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/services/RestartFederatedServiceReceiver.java b/app/src/main/java/fr/gouv/etalab/mastodon/services/RestartFederatedServiceReceiver.java new file mode 100644 index 000000000..bf03f9440 --- /dev/null +++ b/app/src/main/java/fr/gouv/etalab/mastodon/services/RestartFederatedServiceReceiver.java @@ -0,0 +1,33 @@ +package fr.gouv.etalab.mastodon.services; +/* Copyright 2017 Thomas Schneider + * + * This file is a part of Mastalab + * + * 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. + * + * Mastalab 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 Mastalab; if not, + * see . */ +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +/** + * Created by Thomas on 26/09/2017. + * BroadcastReceiver for restarting the service for listening federated timeline + */ + +public class RestartFederatedServiceReceiver extends BroadcastReceiver { + + @Override + public void onReceive(Context context, Intent intent) { + Intent streamingServiceIntent = new Intent(context.getApplicationContext(), StreamingFederatedTimelineService.class); + context.startService(streamingServiceIntent); + } + +} \ No newline at end of file diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/services/StreamingFederatedTimelineService.java b/app/src/main/java/fr/gouv/etalab/mastodon/services/StreamingFederatedTimelineService.java new file mode 100644 index 000000000..a63d68662 --- /dev/null +++ b/app/src/main/java/fr/gouv/etalab/mastodon/services/StreamingFederatedTimelineService.java @@ -0,0 +1,153 @@ +package fr.gouv.etalab.mastodon.services; +/* Copyright 2017 Thomas Schneider + * + * This file is a part of Mastalab + * + * 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. + * + * Mastalab 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 Mastalab; if not, + * see . */ + +import android.app.IntentService; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.database.sqlite.SQLiteDatabase; +import android.os.Bundle; +import android.os.SystemClock; +import android.support.annotation.Nullable; +import android.support.v4.content.LocalBroadcastManager; +import android.util.Log; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URL; +import java.util.ArrayList; + +import javax.net.ssl.HttpsURLConnection; + +import fr.gouv.etalab.mastodon.client.API; +import fr.gouv.etalab.mastodon.client.Entities.Account; +import fr.gouv.etalab.mastodon.client.Entities.Notification; +import fr.gouv.etalab.mastodon.client.Entities.Status; +import fr.gouv.etalab.mastodon.client.TLSSocketFactory; +import fr.gouv.etalab.mastodon.helper.Helper; +import fr.gouv.etalab.mastodon.sqlite.AccountDAO; +import fr.gouv.etalab.mastodon.sqlite.Sqlite; + + +/** + * Created by Thomas on 26/09/2017. + * Manage service for streaming api for federated timeline + */ + +public class StreamingFederatedTimelineService extends IntentService { + + + /** + * Creates an IntentService. Invoked by your subclass's constructor. + * + * @param name Used to name the worker thread, important only for debugging. + */ + public StreamingFederatedTimelineService(String name) { + super(name); + } + public StreamingFederatedTimelineService() { + super("StreamingService"); + } + + private static HttpsURLConnection httpsURLConnection; + protected Account account; + + public void onCreate() { + super.onCreate(); + } + + + @Override + protected void onHandleIntent(@Nullable Intent intent) { + SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null); + InputStream inputStream; + BufferedReader reader = null; + Account accountStream = null; + if( httpsURLConnection != null) + httpsURLConnection.disconnect(); + if( userId != null) { + SQLiteDatabase db = Sqlite.getInstance(getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); + accountStream = new AccountDAO(getApplicationContext(), db).getAccountByID(userId); + } + if( accountStream != null){ + try { + + URL url = new URL("https://" + accountStream.getInstance() + "/api/v1/streaming/public"); + httpsURLConnection = (HttpsURLConnection) url.openConnection(); + httpsURLConnection.setRequestProperty("Content-Type", "application/json"); + httpsURLConnection.setRequestProperty("Authorization", "Bearer " + accountStream.getToken()); + httpsURLConnection.setRequestProperty("Connection", "Keep-Alive"); + httpsURLConnection.setRequestProperty("Keep-Alive", "header"); + httpsURLConnection.setRequestProperty("Connection", "close"); + httpsURLConnection.setSSLSocketFactory(new TLSSocketFactory()); + httpsURLConnection.setRequestMethod("GET"); + httpsURLConnection.setConnectTimeout(70000); + httpsURLConnection.setReadTimeout(70000); + inputStream = new BufferedInputStream(httpsURLConnection.getInputStream()); + reader = new BufferedReader(new InputStreamReader(inputStream)); + String event; + while((event = reader.readLine()) != null) { + if (!event.startsWith("data: ")) { + continue; + } + event = event.substring(6); + try { + JSONObject eventJson = new JSONObject(event); + onRetrieveStreaming(accountStream, eventJson); + } catch (JSONException e) { + e.printStackTrace(); + } + } + } catch (Exception e) { + e.printStackTrace(); + }finally { + if(reader != null){ + try{ + reader.close(); + }catch (IOException e){ + e.printStackTrace(); + } + } + SystemClock.sleep(1000); + sendBroadcast(new Intent("RestartStreamingFederatedService")); + } + } + } + + public void onRetrieveStreaming(Account account, JSONObject response) { + if( response == null ) + return; + Status status ; + Bundle b = new Bundle(); + status = API.parseStatuses(getApplicationContext(), response); + status.setReplies(new ArrayList()); + status.setNew(true); + b.putParcelable("data", status); + if( account != null) + b.putString("userIdService",account.getId()); + Intent intentBC = new Intent(Helper.RECEIVE_FEDERATED_DATA); + intentBC.putExtras(b); + LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intentBC); + } + +} diff --git a/app/src/safetynet/java/fr.gouv.etalab.mastodon/activities/MainActivity.java b/app/src/safetynet/java/fr.gouv.etalab.mastodon/activities/MainActivity.java index 6a273039b..58d59f6f6 100644 --- a/app/src/safetynet/java/fr.gouv.etalab.mastodon/activities/MainActivity.java +++ b/app/src/safetynet/java/fr.gouv.etalab.mastodon/activities/MainActivity.java @@ -92,6 +92,7 @@ import fr.gouv.etalab.mastodon.helper.Helper; import fr.gouv.etalab.mastodon.interfaces.OnRetrieveInstanceInterface; import fr.gouv.etalab.mastodon.interfaces.OnRetrieveMetaDataInterface; import fr.gouv.etalab.mastodon.interfaces.OnUpdateAccountInfoInterface; +import fr.gouv.etalab.mastodon.services.StreamingFederatedTimelineService; import fr.gouv.etalab.mastodon.services.StreamingService; import fr.gouv.etalab.mastodon.sqlite.Sqlite; import fr.gouv.etalab.mastodon.asynctasks.RetrieveAccountsAsyncTask; @@ -134,16 +135,16 @@ public class MainActivity extends AppCompatActivity private RelativeLayout main_app_container; private Stack stackBack = new Stack<>(); - private DisplayStatusFragment homeFragment; + private DisplayStatusFragment homeFragment, federatedFragment; private DisplayNotificationsFragment notificationsFragment; private static final int ERROR_DIALOG_REQUEST_CODE = 97; - private BroadcastReceiver receive_data; + private BroadcastReceiver receive_data, receive_federated_data; private boolean display_local, display_global; public static int countNewStatus = 0; public static int countNewNotifications = 0; private String userIdService; - private Intent streamingIntent; - public static boolean broadCastRegistred = false; + private Intent streamingIntent, streamingFederatedIntent; + public static boolean broadCastRegistred = false, broadCastFederatedRegistred = false; public MainActivity() { } @@ -154,6 +155,22 @@ public class MainActivity extends AppCompatActivity final SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, android.content.Context.MODE_PRIVATE); + + + receive_federated_data = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + Bundle b = intent.getExtras(); + userIdService = b.getString("userIdService", null); + String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null); + if( userIdService != null && userIdService.equals(userId)) { + Status status = b.getParcelable("data"); + if (federatedFragment != null) { + federatedFragment.refresh(status); + } + } + } + }; receive_data = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -194,10 +211,17 @@ public class MainActivity extends AppCompatActivity streamingIntent = new Intent(this, StreamingService.class); startService(streamingIntent); + streamingFederatedIntent = new Intent(this, StreamingFederatedTimelineService.class); + startService(streamingFederatedIntent); + if( !broadCastRegistred) { LocalBroadcastManager.getInstance(this).registerReceiver(receive_data, new IntentFilter(Helper.RECEIVE_DATA)); broadCastRegistred = true; } + if( !broadCastFederatedRegistred) { + LocalBroadcastManager.getInstance(this).registerReceiver(receive_federated_data, new IntentFilter(Helper.RECEIVE_FEDERATED_DATA)); + broadCastFederatedRegistred = true; + } ProviderInstaller.installIfNeededAsync(this, this); @@ -914,8 +938,12 @@ public class MainActivity extends AppCompatActivity super.onDestroy(); if( streamingIntent != null) stopService(streamingIntent); + if( streamingFederatedIntent != null) + stopService(streamingFederatedIntent); LocalBroadcastManager.getInstance(this).unregisterReceiver(receive_data); + LocalBroadcastManager.getInstance(this).unregisterReceiver(receive_federated_data); broadCastRegistred = false; + broadCastFederatedRegistred = false; } @SuppressWarnings("StatementWithEmptyBody") @@ -1168,6 +1196,14 @@ public class MainActivity extends AppCompatActivity case 1: notificationsFragment = (DisplayNotificationsFragment) createdFragment; break; + case 2: + if ( !display_local && display_global) + federatedFragment = (DisplayStatusFragment) createdFragment; + break; + case 3: + if( display_local && display_global) + federatedFragment = (DisplayStatusFragment) createdFragment; + break; } return createdFragment; }