From 325d51187b88a6bf72f22fc651e1060033eab3d7 Mon Sep 17 00:00:00 2001 From: tom79 Date: Mon, 28 Aug 2017 13:53:07 +0200 Subject: [PATCH 1/8] New design api and events handler --- .../asynctasks/StreamingUserAsyncTask.java | 131 +++++++++ .../fr/gouv/etalab/mastodon/client/API.java | 54 ++-- .../OnRetrieveStreamingInterface.java | 28 ++ .../etalab/mastodon/jobs/SteamingSyncJob.java | 267 ++++++++++++++++++ .../activities/MainActivity.java | 3 + 5 files changed, 456 insertions(+), 27 deletions(-) create mode 100644 app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/StreamingUserAsyncTask.java create mode 100644 app/src/main/java/fr/gouv/etalab/mastodon/interfaces/OnRetrieveStreamingInterface.java create mode 100644 app/src/main/java/fr/gouv/etalab/mastodon/jobs/SteamingSyncJob.java diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/StreamingUserAsyncTask.java b/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/StreamingUserAsyncTask.java new file mode 100644 index 000000000..919f42036 --- /dev/null +++ b/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/StreamingUserAsyncTask.java @@ -0,0 +1,131 @@ +/* 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 . */ +package fr.gouv.etalab.mastodon.asynctasks; + + +import android.os.AsyncTask; +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.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; + +import fr.gouv.etalab.mastodon.helper.Helper; +import fr.gouv.etalab.mastodon.interfaces.OnRetrieveStreamingInterface; + + +/** + * Created by Thomas on 28/08/2017. + * Calls user streaming api + */ + +public class StreamingUserAsyncTask extends AsyncTask { + + private String instance, token, acct, userId; + private OnRetrieveStreamingInterface listener; + + public StreamingUserAsyncTask(String instance, String token, String acct, String userId, OnRetrieveStreamingInterface onRetrieveStreamingInterface){ + this.instance = instance; + this.token = token; + this.acct = acct; + this.userId = userId; + this.listener = onRetrieveStreamingInterface; + } + public enum EventStreaming{ + UPDATE, + NOTIFICATION, + DELETE + } + + @Override + protected Object doInBackground(Object[] params){ + try { + URL url = new URL("https://" + this.instance + "/api/v1/streaming/user"); + HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); + urlConnection.setRequestProperty("Content-Type", "application/json"); + urlConnection.setRequestProperty("Authorization", "Bearer "+this.token); + urlConnection.setRequestProperty("Connection", "Keep-Alive"); + urlConnection.setRequestProperty("Keep-Alive", "header"); + Log.v(Helper.TAG, "http response: " + urlConnection.getResponseCode()); + + //Object inputStream = urlConnection.getContent(); + InputStream inputStream = new BufferedInputStream(urlConnection.getInputStream()); + Log.v(Helper.TAG, readStrem(inputStream)+""); + } catch (MalformedURLException e) { + e.printStackTrace(); + } catch (IOException e) { + Log.v(Helper.TAG, "Error on url openConnection: "+e.getMessage()); + e.printStackTrace(); + } + + return null; + } + + private String readStrem(InputStream inputStream) { + BufferedReader reader = null; + try{ + reader = new BufferedReader(new InputStreamReader(inputStream)); + String line; + String event; + EventStreaming eventStreaming = null; + while((line = reader.readLine()) != null){ + switch (line.trim()){ + case "update": + event = reader.readLine(); + eventStreaming = EventStreaming.UPDATE; + break; + case "notification": + event = reader.readLine(); + eventStreaming = EventStreaming.NOTIFICATION; + break; + case "delete": + event = reader.readLine(); + eventStreaming = EventStreaming.DELETE; + break; + default: + event = null; + } + if( event != null){ + try { + JSONObject eventJson = new JSONObject(event); + listener.onRetrieveStreaming(eventStreaming, eventJson, acct, userId); + } catch (JSONException e) { + e.printStackTrace(); + } + } + } + }catch (IOException e){ + e.printStackTrace(); + }finally { + if(reader != null){ + try{ + reader.close(); + }catch (IOException e){ + e.printStackTrace(); + } + } + } + return null; + } + +} diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/client/API.java b/app/src/main/java/fr/gouv/etalab/mastodon/client/API.java index baa7b2d60..56822a2c3 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/client/API.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/client/API.java @@ -188,12 +188,12 @@ public class API { get("/accounts/verify_credentials", null, new JsonHttpResponseHandler() { @Override public void onSuccess(int statusCode, Header[] headers, JSONObject response) { - account = parseAccountResponse(response); + account = parseAccountResponse(context, response); } @Override public void onSuccess(int statusCode, Header[] headers, JSONArray response) { try { - account = parseAccountResponse(response.getJSONObject(0)); + account = parseAccountResponse(context, response.getJSONObject(0)); } catch (JSONException e) { e.printStackTrace(); } @@ -217,12 +217,12 @@ public class API { get(String.format("/accounts/%s",accountId), null, new JsonHttpResponseHandler() { @Override public void onSuccess(int statusCode, Header[] headers, JSONObject response) { - account = parseAccountResponse(response); + account = parseAccountResponse(context, response); } @Override public void onSuccess(int statusCode, Header[] headers, JSONArray response) { try { - account = parseAccountResponse(response.getJSONObject(0)); + account = parseAccountResponse(context, response.getJSONObject(0)); } catch (JSONException e) { e.printStackTrace(); } @@ -330,7 +330,7 @@ public class API { get(String.format("/accounts/%s/statuses", accountId), params, new JsonHttpResponseHandler() { @Override public void onSuccess(int statusCode, Header[] headers, JSONObject response) { - Status status = parseStatuses(response); + Status status = parseStatuses(context, response); statuses.add(status); apiResponse.setSince_id(findSinceId(headers)); apiResponse.setMax_id(findMaxId(headers)); @@ -365,7 +365,7 @@ public class API { public void onSuccess(int statusCode, Header[] headers, JSONObject response) { apiResponse.setSince_id(findSinceId(headers)); apiResponse.setMax_id(findMaxId(headers)); - Status status = parseStatuses(response); + Status status = parseStatuses(context, response); statuses.add(status); } @Override @@ -445,7 +445,7 @@ public class API { @Override public void onSuccess(int statusCode, Header[] headers, JSONObject response) { - Status status = parseStatuses(response); + Status status = parseStatuses(context, response); statuses.add(status); apiResponse.setSince_id(findSinceId(headers)); apiResponse.setMax_id(findMaxId(headers)); @@ -500,7 +500,7 @@ public class API { @Override public void onSuccess(int statusCode, Header[] headers, JSONObject response) { - Status status = parseStatuses(response); + Status status = parseStatuses(context, response); statuses.add(status); apiResponse.setSince_id(findSinceId(headers)); apiResponse.setMax_id(findMaxId(headers)); @@ -557,7 +557,7 @@ public class API { @Override public void onSuccess(int statusCode, Header[] headers, JSONObject response) { - Status status = parseStatuses(response); + Status status = parseStatuses(context, response); statuses.add(status); apiResponse.setSince_id(findSinceId(headers)); apiResponse.setMax_id(findMaxId(headers)); @@ -641,7 +641,7 @@ public class API { public void onSuccess(int statusCode, Header[] headers, JSONObject response) { apiResponse.setSince_id(findSinceId(headers)); apiResponse.setMax_id(findMaxId(headers)); - Account account = parseAccountResponse(response); + Account account = parseAccountResponse(context, response); accounts.add(account); } @Override @@ -692,7 +692,7 @@ public class API { public void onSuccess(int statusCode, Header[] headers, JSONObject response) { apiResponse.setSince_id(findSinceId(headers)); apiResponse.setMax_id(findMaxId(headers)); - Account account = parseAccountResponse(response); + Account account = parseAccountResponse(context, response); accounts.add(account); } @Override @@ -741,7 +741,7 @@ public class API { @Override public void onSuccess(int statusCode, Header[] headers, JSONObject response) { - Status status = parseStatuses(response); + Status status = parseStatuses(context, response); statuses.add(status); apiResponse.setSince_id(findSinceId(headers)); apiResponse.setMax_id(findMaxId(headers)); @@ -945,7 +945,7 @@ public class API { @Override public void onSuccess(int statusCode, Header[] headers, JSONObject response) { - Status statusreturned = parseStatuses(response); + Status statusreturned = parseStatuses(context, response); statuses.add(statusreturned); apiResponse.setSince_id(findSinceId(headers)); apiResponse.setMax_id(findMaxId(headers)); @@ -1041,7 +1041,7 @@ public class API { @Override public void onSuccess(int statusCode, Header[] headers, JSONObject response) { - Notification notification = parseNotificationResponse(response); + Notification notification = parseNotificationResponse(context, response); notifications.add(notification); apiResponse.setSince_id(findSinceId(headers)); apiResponse.setMax_id(findMaxId(headers)); @@ -1130,7 +1130,7 @@ public class API { @Override public void onSuccess(int statusCode, Header[] headers, JSONObject response) { accounts = new ArrayList<>(); - account = parseAccountResponse(response); + account = parseAccountResponse(context, response); accounts.add(account); apiResponse.setSince_id(findSinceId(headers)); apiResponse.setMax_id(findMaxId(headers)); @@ -1170,7 +1170,7 @@ public class API { @Override public void onSuccess(int statusCode, Header[] headers, JSONObject response) { accounts = new ArrayList<>(); - account = parseAccountResponse(response); + account = parseAccountResponse(context, response); accounts.add(account); apiResponse.setSince_id(findSinceId(headers)); apiResponse.setMax_id(findMaxId(headers)); @@ -1236,7 +1236,7 @@ public class API { while (i < jsonArray.length() ){ JSONObject resobj = jsonArray.getJSONObject(i); - Status status = parseStatuses(resobj); + Status status = parseStatuses(context, resobj); i++; statuses.add(status); } @@ -1253,7 +1253,7 @@ public class API { * @return Status */ @SuppressWarnings("InfiniteRecursion") - private Status parseStatuses(JSONObject resobj){ + private static Status parseStatuses(Context context, JSONObject resobj){ Status status = new Status(); try { status.setId(resobj.get("id").toString()); @@ -1315,14 +1315,14 @@ public class API { } status.setTags(tags); - status.setAccount(parseAccountResponse(resobj.getJSONObject("account"))); + status.setAccount(parseAccountResponse(context, resobj.getJSONObject("account"))); status.setContent(resobj.get("content").toString()); status.setFavourites_count(Integer.valueOf(resobj.get("favourites_count").toString())); status.setReblogs_count(Integer.valueOf(resobj.get("reblogs_count").toString())); status.setReblogged(Boolean.valueOf(resobj.get("reblogged").toString())); status.setFavourited(Boolean.valueOf(resobj.get("favourited").toString())); try{ - status.setReblog(parseStatuses(resobj.getJSONObject("reblog"))); + status.setReblog(parseStatuses(context, resobj.getJSONObject("reblog"))); }catch (Exception ignored){} } catch (JSONException e) { e.printStackTrace(); @@ -1355,7 +1355,7 @@ public class API { * @param resobj JSONObject * @return Account */ - private Account parseAccountResponse(JSONObject resobj){ + private static Account parseAccountResponse(Context context, JSONObject resobj){ Account account = new Account(); try { @@ -1392,7 +1392,7 @@ public class API { int i = 0; while (i < jsonArray.length() ) { JSONObject resobj = jsonArray.getJSONObject(i); - Account account = parseAccountResponse(resobj); + Account account = parseAccountResponse(context, resobj); accounts.add(account); i++; } @@ -1416,7 +1416,7 @@ public class API { Account account = null; while (i < jsonArray.length() ) { JSONObject resobj = jsonArray.getJSONObject(i); - account = parseAccountResponse(resobj); + account = parseAccountResponse(context, resobj); if( account.getAcct().contains(Helper.DEVELOPER_INSTANCE)) accounts.add(account); i++; @@ -1500,16 +1500,16 @@ public class API { * @param resobj JSONObject * @return Account */ - private Notification parseNotificationResponse(JSONObject resobj){ + public static Notification parseNotificationResponse(Context context, JSONObject resobj){ Notification notification = new Notification(); try { notification.setId(resobj.get("id").toString()); notification.setType(resobj.get("type").toString()); notification.setCreated_at(Helper.mstStringToDate(context, resobj.get("created_at").toString())); - notification.setAccount(parseAccountResponse(resobj.getJSONObject("account"))); + notification.setAccount(parseAccountResponse(context, resobj.getJSONObject("account"))); try{ - notification.setStatus(parseStatuses(resobj.getJSONObject("status"))); + notification.setStatus(parseStatuses(context, resobj.getJSONObject("status"))); }catch (Exception ignored){} notification.setCreated_at(Helper.mstStringToDate(context, resobj.get("created_at").toString())); } catch (JSONException e) { @@ -1531,7 +1531,7 @@ public class API { while (i < jsonArray.length() ) { JSONObject resobj = jsonArray.getJSONObject(i); - Notification notification = parseNotificationResponse(resobj); + Notification notification = parseNotificationResponse(context, resobj); notifications.add(notification); i++; } diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/interfaces/OnRetrieveStreamingInterface.java b/app/src/main/java/fr/gouv/etalab/mastodon/interfaces/OnRetrieveStreamingInterface.java new file mode 100644 index 000000000..43d5dd8cc --- /dev/null +++ b/app/src/main/java/fr/gouv/etalab/mastodon/interfaces/OnRetrieveStreamingInterface.java @@ -0,0 +1,28 @@ +/* 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 . */ +package fr.gouv.etalab.mastodon.interfaces; + +import org.json.JSONObject; + +import fr.gouv.etalab.mastodon.asynctasks.StreamingUserAsyncTask; + + +/** + * Created by Thomas on 28/08/2017. + * Interface when event from streaming api has been retrieved + */ +public interface OnRetrieveStreamingInterface { + void onRetrieveStreaming(StreamingUserAsyncTask.EventStreaming event, JSONObject response, String acct, String userId); +} diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/jobs/SteamingSyncJob.java b/app/src/main/java/fr/gouv/etalab/mastodon/jobs/SteamingSyncJob.java new file mode 100644 index 000000000..b450bf0eb --- /dev/null +++ b/app/src/main/java/fr/gouv/etalab/mastodon/jobs/SteamingSyncJob.java @@ -0,0 +1,267 @@ +package fr.gouv.etalab.mastodon.jobs; +/* 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.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.database.sqlite.SQLiteDatabase; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.os.AsyncTask; +import android.support.annotation.NonNull; +import android.view.View; + +import com.evernote.android.job.Job; +import com.evernote.android.job.JobManager; +import com.evernote.android.job.JobRequest; +import com.nostra13.universalimageloader.cache.disc.impl.UnlimitedDiskCache; +import com.nostra13.universalimageloader.core.DisplayImageOptions; +import com.nostra13.universalimageloader.core.ImageLoader; +import com.nostra13.universalimageloader.core.ImageLoaderConfiguration; +import com.nostra13.universalimageloader.core.assist.FailReason; +import com.nostra13.universalimageloader.core.display.SimpleBitmapDisplayer; +import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener; + +import org.json.JSONObject; + +import java.io.File; +import java.util.List; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import fr.gouv.etalab.mastodon.activities.MainActivity; +import fr.gouv.etalab.mastodon.asynctasks.RetrieveNotificationsAsyncTask; +import fr.gouv.etalab.mastodon.asynctasks.StreamingUserAsyncTask; +import fr.gouv.etalab.mastodon.client.API; +import fr.gouv.etalab.mastodon.client.APIResponse; +import fr.gouv.etalab.mastodon.client.Entities.Account; +import fr.gouv.etalab.mastodon.client.Entities.Notification; +import fr.gouv.etalab.mastodon.client.PatchBaseImageDownloader; +import fr.gouv.etalab.mastodon.helper.Helper; +import fr.gouv.etalab.mastodon.interfaces.OnRetrieveNotificationsInterface; +import fr.gouv.etalab.mastodon.interfaces.OnRetrieveStreamingInterface; +import fr.gouv.etalab.mastodon.sqlite.AccountDAO; +import fr.gouv.etalab.mastodon.sqlite.Sqlite; +import mastodon.etalab.gouv.fr.mastodon.R; + +import static fr.gouv.etalab.mastodon.helper.Helper.INTENT_ACTION; +import static fr.gouv.etalab.mastodon.helper.Helper.NOTIFICATION_INTENT; +import static fr.gouv.etalab.mastodon.helper.Helper.PREF_KEY_ID; +import static fr.gouv.etalab.mastodon.helper.Helper.canNotify; +import static fr.gouv.etalab.mastodon.helper.Helper.notify_user; + + +/** + * Created by Thomas on 29/04/2017. + * Notifications refresh job + */ + +public class SteamingSyncJob extends Job implements OnRetrieveStreamingInterface { + + static final String STREAMING = "job_streaming"; + private String message; + + @NonNull + @Override + protected Result onRunJob(Params params) { + //Code refresh here + callAsynchronousTask(); + return Result.SUCCESS; + } + + + public static int schedule(boolean updateCurrent){ + + Set jobRequests = JobManager.instance().getAllJobRequestsForTag(STREAMING); + if (!jobRequests.isEmpty() && !updateCurrent) { + return jobRequests.iterator().next().getJobId(); + } + + return new JobRequest.Builder(SteamingSyncJob.STREAMING) + .setPeriodic(TimeUnit.MINUTES.toMillis(Helper.MINUTES_BETWEEN_NOTIFICATIONS_REFRESH), TimeUnit.MINUTES.toMillis(5)) + .setPersisted(true) + .setUpdateCurrent(updateCurrent) + .setRequiredNetworkType(JobRequest.NetworkType.CONNECTED) + .setRequirementsEnforced(false) + .build() + .schedule(); + } + + + + /** + * Task in background starts here. + */ + private void callAsynchronousTask() { + + SQLiteDatabase db = Sqlite.getInstance(getContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); + //If an Internet connection and user agrees with notification refresh + final SharedPreferences sharedpreferences = getContext().getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + //Check which notifications the user wants to see + boolean notif_follow = sharedpreferences.getBoolean(Helper.SET_NOTIF_FOLLOW, true); + boolean notif_add = sharedpreferences.getBoolean(Helper.SET_NOTIF_ADD, true); + 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); + //User disagree with all notifications + if( !notif_follow && !notif_add && !notif_ask && !notif_mention && !notif_share) + return; //Nothing is done + //No account connected, the service is stopped + if(!Helper.isLoggedIn(getContext())) + return; + //If WIFI only and on WIFI OR user defined any connections to use the service. + if(!sharedpreferences.getBoolean(Helper.SET_WIFI_ONLY, false) || Helper.isOnWIFI(getContext())) { + List accounts = new AccountDAO(getContext(),db).getAllAccount(); + //It means there is no user in DB. + if( accounts == null ) + return; + //Retrieve users in db that owner has. + for (Account account: accounts) { + String max_id = sharedpreferences.getString(Helper.LAST_NOTIFICATION_MAX_ID + account.getId(), null); + new StreamingUserAsyncTask(account.getInstance(), account.getToken(), account.getAcct(), account.getId(), SteamingSyncJob.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + } + } + + + @Override + public void onRetrieveStreaming(StreamingUserAsyncTask.EventStreaming event, JSONObject response, String acct, String userId) { + if( response == null ) + return; + String new_max_id = null; + final SharedPreferences sharedpreferences = getContext().getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + boolean notif_follow = sharedpreferences.getBoolean(Helper.SET_NOTIF_FOLLOW, true); + boolean notif_add = sharedpreferences.getBoolean(Helper.SET_NOTIF_ADD, true); + boolean notif_mention = sharedpreferences.getBoolean(Helper.SET_NOTIF_MENTION, true); + boolean notif_share = sharedpreferences.getBoolean(Helper.SET_NOTIF_SHARE, true); + final String max_id = sharedpreferences.getString(Helper.LAST_NOTIFICATION_MAX_ID + userId, null); + + + //No previous notifications in cache, so no notification will be sent + boolean notify = false; + + String notificationUrl = null; + String title = null; + if( event == StreamingUserAsyncTask.EventStreaming.NOTIFICATION){ + Notification notification = API.parseNotificationResponse(getContext(), response); + new_max_id = notification.getId(); + switch (notification.getType()){ + case "mention": + if(notif_mention){ + notify = true; + notificationUrl = notification.getAccount().getAvatar(); + if( notification.getAccount().getDisplay_name() != null && notification.getAccount().getDisplay_name().length() > 0 ) + title = String.format("@%s %s", Helper.shortnameToUnicode(notification.getAccount().getDisplay_name(), true),getContext().getString(R.string.notif_mention)); + else + title = String.format("@%s %s", notification.getAccount().getUsername(),getContext().getString(R.string.notif_mention)); + } + break; + case "reblog": + if(notif_share){ + notify = true; + notificationUrl = notification.getAccount().getAvatar(); + if( notification.getAccount().getDisplay_name() != null && notification.getAccount().getDisplay_name().length() > 0 ) + title = String.format("@%s %s", Helper.shortnameToUnicode(notification.getAccount().getDisplay_name(), true),getContext().getString(R.string.notif_reblog)); + else + title = String.format("@%s %s", notification.getAccount().getUsername(),getContext().getString(R.string.notif_reblog)); + } + break; + case "favourite": + if(notif_add){ + notify = true; + notificationUrl = notification.getAccount().getAvatar(); + if( notification.getAccount().getDisplay_name() != null && notification.getAccount().getDisplay_name().length() > 0 ) + title = String.format("@%s %s", Helper.shortnameToUnicode(notification.getAccount().getDisplay_name(), true),getContext().getString(R.string.notif_favourite)); + else + title = String.format("@%s %s", notification.getAccount().getUsername(),getContext().getString(R.string.notif_favourite)); + } + break; + case "follow": + if(notif_follow){ + notify = true; + notificationUrl = notification.getAccount().getAvatar(); + if( notification.getAccount().getDisplay_name() != null && notification.getAccount().getDisplay_name().length() > 0 ) + title = String.format("@%s %s", Helper.shortnameToUnicode(notification.getAccount().getDisplay_name(), true),getContext().getString(R.string.notif_follow)); + else + title = String.format("@%s %s", notification.getAccount().getUsername(),getContext().getString(R.string.notif_follow)); + } + break; + default: + break; + } + SharedPreferences.Editor editor = sharedpreferences.edit(); + editor.putString(Helper.LAST_NOTIFICATION_MAX_ID + userId, notification.getId()); + editor.apply(); + message = notification.getStatus().getContent(); + if( message!= null) { + message = message.substring(0, 17); + message = message + "..."; + } + }else if ( event == StreamingUserAsyncTask.EventStreaming.UPDATE){ + + }else if( event == StreamingUserAsyncTask.EventStreaming.DELETE){ + + } + if( new_max_id != null){ + SharedPreferences.Editor editor = sharedpreferences.edit(); + editor.putString(Helper.LAST_NOTIFICATION_MAX_ID + userId, new_max_id); + editor.apply(); + return; + } + + //Check which user is connected and if activity is to front + + + + if( notify){ + final Intent intent = new Intent(getContext(), MainActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK ); + intent.putExtra(INTENT_ACTION, NOTIFICATION_INTENT); + intent.putExtra(PREF_KEY_ID, userId); + long notif_id = Long.parseLong(userId); + final int notificationId = ((notif_id + 1) > 2147483647) ? (int) (2147483647 - notif_id - 1) : (int) (notif_id + 1); + + if( notificationUrl != null){ + ImageLoader imageLoaderNoty = ImageLoader.getInstance(); + File cacheDir = new File(getContext().getCacheDir(), getContext().getString(R.string.app_name)); + ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getContext()) + .imageDownloader(new PatchBaseImageDownloader(getContext())) + .threadPoolSize(5) + .threadPriority(Thread.MIN_PRIORITY + 3) + .denyCacheImageMultipleSizesInMemory() + .diskCache(new UnlimitedDiskCache(cacheDir)) + .build(); + imageLoaderNoty.init(config); + DisplayImageOptions options = new DisplayImageOptions.Builder().displayer(new SimpleBitmapDisplayer()).cacheInMemory(false) + .cacheOnDisk(true).resetViewBeforeLoading(true).build(); + + final String finalTitle = title; + imageLoaderNoty.loadImage(notificationUrl, options, new SimpleImageLoadingListener(){ + @Override + public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { + super.onLoadingComplete(imageUri, view, loadedImage); + notify_user(getContext(), intent, notificationId, loadedImage, finalTitle, message); + + } + @Override + public void onLoadingFailed(String imageUri, View view, FailReason failReason){ + notify_user(getContext(), intent, notificationId, BitmapFactory.decodeResource(getContext().getResources(), + R.drawable.mastodonlogo), finalTitle, message); + }}); + } + } + } +} 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 730d2046b..fd9caf2b2 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 @@ -71,6 +71,7 @@ import java.util.Locale; import java.util.Stack; import java.util.concurrent.TimeUnit; +import fr.gouv.etalab.mastodon.asynctasks.StreamingUserAsyncTask; import fr.gouv.etalab.mastodon.asynctasks.UpdateAccountInfoByIDAsyncTask; import fr.gouv.etalab.mastodon.client.Entities.Account; import fr.gouv.etalab.mastodon.client.PatchBaseImageDownloader; @@ -448,6 +449,8 @@ public class MainActivity extends AppCompatActivity navigationView.getMenu().findItem(R.id.nav_follow_request).setVisible(false); } + + LinearLayout owner_container = (LinearLayout) headerLayout.findViewById(R.id.owner_container); owner_container.setOnClickListener(new View.OnClickListener() { @Override From d43feffca8e7125cea54caa8dfdee317d238cb33 Mon Sep 17 00:00:00 2001 From: tom79 Date: Mon, 28 Aug 2017 15:01:45 +0200 Subject: [PATCH 2/8] Implements broadcasts --- .../activities/MainActivity.java | 39 +++++--- .../asynctasks/StreamingUserAsyncTask.java | 2 +- .../fr/gouv/etalab/mastodon/client/API.java | 2 +- .../client/Entities/Notification.java | 37 ++++++- .../gouv/etalab/mastodon/helper/Helper.java | 1 + .../etalab/mastodon/jobs/SteamingSyncJob.java | 97 +++++++++++++++---- .../activities/MainActivity.java | 81 +++++++++++++--- 7 files changed, 214 insertions(+), 45 deletions(-) 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 da5a9ae3e..470b2d400 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 @@ -119,7 +119,7 @@ public class MainActivity extends AppCompatActivity private DisplayStatusFragment homeFragment; private DisplayNotificationsFragment notificationsFragment; - + private BroadcastReceiver receive_data; public MainActivity() { } @@ -127,6 +127,8 @@ public class MainActivity extends AppCompatActivity @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + + LocalBroadcastManager.getInstance(this).registerReceiver(receive_data, new IntentFilter(Helper.RECEIVE_DATA)); final SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, android.content.Context.MODE_PRIVATE); final int theme = sharedpreferences.getInt(Helper.SET_THEME, Helper.THEME_DARK); @@ -805,22 +807,22 @@ public class MainActivity extends AppCompatActivity @Override public void onResume(){ super.onResume(); - SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); - boolean bubbles = sharedpreferences.getBoolean(Helper.SET_BUBBLE_COUNTER, true); - if( bubbles){ - Handler handler = new Handler(); - handler.postDelayed(new Runnable() { - @Override - public void run() {refreshData();} - }, 1000); - } //Proceeds to update of the authenticated account if(Helper.isLoggedIn(getApplicationContext())) new UpdateAccountInfoByIDAsyncTask(getApplicationContext(), MainActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } + @Override + protected void onPause() { + super.onPause(); + MainActivity.activityPaused(); + } - + @Override + public void onDestroy(){ + super.onDestroy(); + LocalBroadcastManager.getInstance(this).unregisterReceiver(receive_data); + } @SuppressWarnings("StatementWithEmptyBody") @Override @@ -1062,6 +1064,7 @@ public class MainActivity extends AppCompatActivity } } + public void updateNotifCounter(int newNotifCount){ if(tabLayout.getTabAt(1) == null) return; @@ -1081,4 +1084,18 @@ public class MainActivity extends AppCompatActivity } } + public static boolean isActivityVisible() { + return activityVisible; + } + + private static void activityResumed() { + activityVisible = true; + } + + private static void activityPaused() { + activityVisible = false; + } + + private static boolean activityVisible; + } diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/StreamingUserAsyncTask.java b/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/StreamingUserAsyncTask.java index 919f42036..80730782f 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/StreamingUserAsyncTask.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/StreamingUserAsyncTask.java @@ -99,7 +99,7 @@ public class StreamingUserAsyncTask extends AsyncTask { eventStreaming = EventStreaming.NOTIFICATION; break; case "delete": - event = reader.readLine(); + event = "{\"id\":" + reader.readLine() + "}"; eventStreaming = EventStreaming.DELETE; break; default: diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/client/API.java b/app/src/main/java/fr/gouv/etalab/mastodon/client/API.java index 56822a2c3..312c42e4b 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/client/API.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/client/API.java @@ -1253,7 +1253,7 @@ public class API { * @return Status */ @SuppressWarnings("InfiniteRecursion") - private static Status parseStatuses(Context context, JSONObject resobj){ + public static Status parseStatuses(Context context, JSONObject resobj){ Status status = new Status(); try { status.setId(resobj.get("id").toString()); diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/client/Entities/Notification.java b/app/src/main/java/fr/gouv/etalab/mastodon/client/Entities/Notification.java index a8cc8f74d..384ede022 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/client/Entities/Notification.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/client/Entities/Notification.java @@ -15,13 +15,16 @@ package fr.gouv.etalab.mastodon.client.Entities; +import android.os.Parcel; +import android.os.Parcelable; + import java.util.Date; /** * Created by Thomas on 23/04/2017. */ -public class Notification { +public class Notification implements Parcelable { private String id; private String type; @@ -29,6 +32,25 @@ public class Notification { private Account account; private Status status; + protected Notification(Parcel in) { + id = in.readString(); + type = in.readString(); + account = in.readParcelable(Account.class.getClassLoader()); + status = in.readParcelable(Status.class.getClassLoader()); + } + + public static final Creator CREATOR = new Creator() { + @Override + public Notification createFromParcel(Parcel in) { + return new Notification(in); + } + + @Override + public Notification[] newArray(int size) { + return new Notification[size]; + } + }; + public String getId() { return id; } @@ -68,4 +90,17 @@ public class Notification { public void setStatus(Status status) { this.status = status; } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(id); + dest.writeString(type); + dest.writeParcelable(account, flags); + dest.writeParcelable(status, flags); + } } 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 97d89f078..3419b1cdd 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 @@ -251,6 +251,7 @@ public class Helper { //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"; //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/jobs/SteamingSyncJob.java b/app/src/main/java/fr/gouv/etalab/mastodon/jobs/SteamingSyncJob.java index b450bf0eb..98fed4230 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/jobs/SteamingSyncJob.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/jobs/SteamingSyncJob.java @@ -21,7 +21,9 @@ import android.database.sqlite.SQLiteDatabase; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.AsyncTask; +import android.os.Bundle; import android.support.annotation.NonNull; +import android.support.v4.content.LocalBroadcastManager; import android.view.View; import com.evernote.android.job.Job; @@ -35,6 +37,7 @@ import com.nostra13.universalimageloader.core.assist.FailReason; import com.nostra13.universalimageloader.core.display.SimpleBitmapDisplayer; import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener; +import org.json.JSONException; import org.json.JSONObject; import java.io.File; @@ -43,24 +46,22 @@ import java.util.Set; import java.util.concurrent.TimeUnit; import fr.gouv.etalab.mastodon.activities.MainActivity; -import fr.gouv.etalab.mastodon.asynctasks.RetrieveNotificationsAsyncTask; import fr.gouv.etalab.mastodon.asynctasks.StreamingUserAsyncTask; import fr.gouv.etalab.mastodon.client.API; -import fr.gouv.etalab.mastodon.client.APIResponse; 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.PatchBaseImageDownloader; import fr.gouv.etalab.mastodon.helper.Helper; -import fr.gouv.etalab.mastodon.interfaces.OnRetrieveNotificationsInterface; import fr.gouv.etalab.mastodon.interfaces.OnRetrieveStreamingInterface; import fr.gouv.etalab.mastodon.sqlite.AccountDAO; import fr.gouv.etalab.mastodon.sqlite.Sqlite; import mastodon.etalab.gouv.fr.mastodon.R; +import static fr.gouv.etalab.mastodon.helper.Helper.HOME_TIMELINE_INTENT; import static fr.gouv.etalab.mastodon.helper.Helper.INTENT_ACTION; import static fr.gouv.etalab.mastodon.helper.Helper.NOTIFICATION_INTENT; import static fr.gouv.etalab.mastodon.helper.Helper.PREF_KEY_ID; -import static fr.gouv.etalab.mastodon.helper.Helper.canNotify; import static fr.gouv.etalab.mastodon.helper.Helper.notify_user; @@ -73,6 +74,8 @@ public class SteamingSyncJob extends Job implements OnRetrieveStreamingInterface static final String STREAMING = "job_streaming"; private String message; + private int notificationId; + private Intent intent; @NonNull @Override @@ -130,7 +133,6 @@ public class SteamingSyncJob extends Job implements OnRetrieveStreamingInterface return; //Retrieve users in db that owner has. for (Account account: accounts) { - String max_id = sharedpreferences.getString(Helper.LAST_NOTIFICATION_MAX_ID + account.getId(), null); new StreamingUserAsyncTask(account.getInstance(), account.getToken(), account.getAcct(), account.getId(), SteamingSyncJob.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } } @@ -141,23 +143,24 @@ public class SteamingSyncJob extends Job implements OnRetrieveStreamingInterface public void onRetrieveStreaming(StreamingUserAsyncTask.EventStreaming event, JSONObject response, String acct, String userId) { if( response == null ) return; - String new_max_id = null; + String max_id = null; final SharedPreferences sharedpreferences = getContext().getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); boolean notif_follow = sharedpreferences.getBoolean(Helper.SET_NOTIF_FOLLOW, true); boolean notif_add = sharedpreferences.getBoolean(Helper.SET_NOTIF_ADD, true); boolean notif_mention = sharedpreferences.getBoolean(Helper.SET_NOTIF_MENTION, true); boolean notif_share = sharedpreferences.getBoolean(Helper.SET_NOTIF_SHARE, true); - final String max_id = sharedpreferences.getString(Helper.LAST_NOTIFICATION_MAX_ID + userId, null); - //No previous notifications in cache, so no notification will be sent boolean notify = false; String notificationUrl = null; String title = null; + Status status = null; + Notification notification = null; + String dataId = null; if( event == StreamingUserAsyncTask.EventStreaming.NOTIFICATION){ - Notification notification = API.parseNotificationResponse(getContext(), response); - new_max_id = notification.getId(); + notification = API.parseNotificationResponse(getContext(), response); + max_id = notification.getId(); switch (notification.getType()){ case "mention": if(notif_mention){ @@ -208,32 +211,86 @@ public class SteamingSyncJob extends Job implements OnRetrieveStreamingInterface message = notification.getStatus().getContent(); if( message!= null) { message = message.substring(0, 17); - message = message + "..."; + message = message + "…"; + }else{ + message = ""; } }else if ( event == StreamingUserAsyncTask.EventStreaming.UPDATE){ - + status = API.parseStatuses(getContext(), response); + SharedPreferences.Editor editor = sharedpreferences.edit(); + editor.putString(Helper.LAST_NOTIFICATION_MAX_ID + userId, status.getId()); + editor.apply(); + message = status.getContent(); + if( message!= null) { + message = message.substring(0, 17); + message = message + "…"; + }else{ + message = ""; + } }else if( event == StreamingUserAsyncTask.EventStreaming.DELETE){ + try { + dataId = response.getString("id"); + + } catch (JSONException e) { + e.printStackTrace(); + } } - if( new_max_id != null){ + if( max_id != null){ SharedPreferences.Editor editor = sharedpreferences.edit(); - editor.putString(Helper.LAST_NOTIFICATION_MAX_ID + userId, new_max_id); + editor.putString(Helper.LAST_NOTIFICATION_MAX_ID + userId, max_id); editor.apply(); return; } //Check which user is connected and if activity is to front - - - - if( notify){ - final Intent intent = new Intent(getContext(), MainActivity.class); + boolean activityVisible = false; + try{ + activityVisible = MainActivity.isActivityVisible(); + }catch (Exception ignored){} + String userconnected = sharedpreferences.getString(Helper.PREF_KEY_ID, null); + SQLiteDatabase db = Sqlite.getInstance(getContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); + Account account = new AccountDAO(getContext(), db).getAccountByID(userconnected); + //User receiving the notification is connected and application is to front, notification won't be pushed + //Instead, the interaction is done in the activity + if( activityVisible && account != null && account.getAcct().trim().equals(acct.trim()) && account.getId().trim().equals(userId.trim())){ + notify = false; + Intent intentBC = new Intent(Helper.RECEIVE_DATA); + intent.putExtra("eventStreaming", event); + Bundle b = new Bundle(); + if( event == StreamingUserAsyncTask.EventStreaming.UPDATE) + b.putParcelable("data", status); + else if(event == StreamingUserAsyncTask.EventStreaming.NOTIFICATION) + b.putParcelable("data", notification); + else if(event == StreamingUserAsyncTask.EventStreaming.DELETE) + b.putString("id", dataId); + intentBC.putExtras(b); + LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intentBC); + }else if(event == StreamingUserAsyncTask.EventStreaming.NOTIFICATION ){ + notify = true; + intent = new Intent(getContext(), MainActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK ); intent.putExtra(INTENT_ACTION, NOTIFICATION_INTENT); intent.putExtra(PREF_KEY_ID, userId); long notif_id = Long.parseLong(userId); - final int notificationId = ((notif_id + 1) > 2147483647) ? (int) (2147483647 - notif_id - 1) : (int) (notif_id + 1); + notificationId = ((notif_id + 1) > 2147483647) ? (int) (2147483647 - notif_id - 1) : (int) (notif_id + 1); + + }else if(event == StreamingUserAsyncTask.EventStreaming.UPDATE ){ + if(acct != null && status.getAccount().getAcct().trim().equals(acct.trim())){ + notify = false; + }else { + notify = true; + intent = new Intent(getContext(), MainActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra(INTENT_ACTION, HOME_TIMELINE_INTENT); + intent.putExtra(PREF_KEY_ID, userId); + long notif_id = Long.parseLong(userId); + notificationId = ((notif_id + 2) > 2147483647) ? (int) (2147483647 - notif_id - 2) : (int) (notif_id + 2); + } + } + + if( notify){ if( notificationUrl != null){ ImageLoader imageLoaderNoty = ImageLoader.getInstance(); File cacheDir = new File(getContext().getCacheDir(), getContext().getString(R.string.app_name)); 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 fd9caf2b2..34ba724e8 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 @@ -15,9 +15,11 @@ package fr.gouv.etalab.mastodon.activities; import android.annotation.SuppressLint; +import android.content.BroadcastReceiver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; +import android.content.IntentFilter; import android.content.SharedPreferences; import android.database.sqlite.SQLiteDatabase; import android.graphics.PorterDuff; @@ -31,6 +33,7 @@ import android.support.design.widget.TabLayout; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.content.ContextCompat; +import android.support.v4.content.LocalBroadcastManager; import android.support.v4.view.ViewPager; import android.support.v7.app.AlertDialog; import android.support.v7.widget.SearchView; @@ -74,6 +77,8 @@ import java.util.concurrent.TimeUnit; import fr.gouv.etalab.mastodon.asynctasks.StreamingUserAsyncTask; import fr.gouv.etalab.mastodon.asynctasks.UpdateAccountInfoByIDAsyncTask; 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.PatchBaseImageDownloader; import fr.gouv.etalab.mastodon.fragments.DisplayAccountsFragment; import fr.gouv.etalab.mastodon.fragments.DisplayFollowRequestSentFragment; @@ -125,6 +130,7 @@ public class MainActivity extends AppCompatActivity private DisplayStatusFragment homeFragment; private DisplayNotificationsFragment notificationsFragment; private static final int ERROR_DIALOG_REQUEST_CODE = 97; + private BroadcastReceiver receive_data; public MainActivity() { } @@ -132,6 +138,42 @@ public class MainActivity extends AppCompatActivity @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + LocalBroadcastManager.getInstance(this).registerReceiver(receive_data, new IntentFilter(Helper.RECEIVE_DATA)); + + receive_data = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + Bundle b = getIntent().getExtras(); + SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + boolean bubbles = sharedpreferences.getBoolean(Helper.SET_BUBBLE_COUNTER, true); + StreamingUserAsyncTask.EventStreaming eventStreaming = (StreamingUserAsyncTask.EventStreaming) intent.getSerializableExtra("eventStreaming"); + if( eventStreaming == StreamingUserAsyncTask.EventStreaming.NOTIFICATION){ + Notification notification = b.getParcelable("data"); + if( notificationsFragment.getUserVisibleHint() == true){ + + }else{ + + } + }else if(eventStreaming == StreamingUserAsyncTask.EventStreaming.UPDATE){ + Status status = b.getParcelable("data"); + if( notificationsFragment.getUserVisibleHint() == true){ + + }else{ + + } + }else if(eventStreaming == StreamingUserAsyncTask.EventStreaming.DELETE){ + String id = b.getString("id"); + if( notificationsFragment.getUserVisibleHint() == true){ + + }else{ + + } + } + } + }; + + + ProviderInstaller.installIfNeededAsync(this, this); final SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); @@ -813,23 +855,23 @@ public class MainActivity extends AppCompatActivity @Override public void onResume(){ super.onResume(); - SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); - boolean bubbles = sharedpreferences.getBoolean(Helper.SET_BUBBLE_COUNTER, true); - if( bubbles){ - Handler handler = new Handler(); - handler.postDelayed(new Runnable() { - @Override - public void run() {refreshData();} - }, 1000); - } - + MainActivity.activityResumed(); //Proceeds to update of the authenticated account if(Helper.isLoggedIn(getApplicationContext())) new UpdateAccountInfoByIDAsyncTask(getApplicationContext(), MainActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } + @Override + protected void onPause() { + super.onPause(); + MainActivity.activityPaused(); + } - + @Override + public void onDestroy(){ + super.onDestroy(); + LocalBroadcastManager.getInstance(this).unregisterReceiver(receive_data); + } @SuppressWarnings("StatementWithEmptyBody") @Override @@ -1136,4 +1178,21 @@ public class MainActivity extends AppCompatActivity } } + + + + + public static boolean isActivityVisible() { + return activityVisible; + } + + private static void activityResumed() { + activityVisible = true; + } + + private static void activityPaused() { + activityVisible = false; + } + + private static boolean activityVisible; } From d222ab3ff2743973084abfc101ac487eb6806f71 Mon Sep 17 00:00:00 2001 From: tom79 Date: Mon, 28 Aug 2017 15:30:15 +0200 Subject: [PATCH 3/8] Changes methods in fragment and mainactivity --- .../activities/MainActivity.java | 26 ++-- .../asynctasks/RetrieveFeedsAsyncTask.java | 27 ---- .../RetrieveNotificationsAsyncTask.java | 23 ---- .../client/Entities/Notification.java | 2 + .../DisplayNotificationsFragment.java | 107 +++------------ .../fragments/DisplayStatusFragment.java | 124 ++---------------- .../mastodon/fragments/SettingsFragment.java | 13 -- .../gouv/etalab/mastodon/helper/Helper.java | 5 - .../res/layout-sw600dp/fragment_settings.xml | 5 - app/src/main/res/layout/fragment_settings.xml | 5 - .../activities/MainActivity.java | 76 ++++------- 11 files changed, 66 insertions(+), 347 deletions(-) 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 470b2d400..f7bb0cfd6 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 @@ -120,15 +120,17 @@ public class MainActivity extends AppCompatActivity private DisplayStatusFragment homeFragment; private DisplayNotificationsFragment notificationsFragment; private BroadcastReceiver receive_data; - + private int newNotif, newHome; public MainActivity() { } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - LocalBroadcastManager.getInstance(this).registerReceiver(receive_data, new IntentFilter(Helper.RECEIVE_DATA)); + newNotif = 0; + newHome = 0; + final SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, android.content.Context.MODE_PRIVATE); final int theme = sharedpreferences.getInt(Helper.SET_THEME, Helper.THEME_DARK); @@ -205,7 +207,6 @@ public class MainActivity extends AppCompatActivity (getSupportFragmentManager(), tabLayout.getTabCount()); viewPager.setAdapter(adapter); viewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout)); - final boolean bubbles = sharedpreferences.getBoolean(Helper.SET_BUBBLE_COUNTER, true); tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() { @Override public void onTabSelected(TabLayout.Tab tab) { @@ -230,12 +231,14 @@ public class MainActivity extends AppCompatActivity fragmentTag = "HOME_TIMELINE"; if( bubbles && homeFragment != null) homeFragment.refreshData(); - updateHomeCounter(0); + newHome = 0; + updateHomeCounter(); break; case 1: fragmentTag = "NOTIFICATIONS"; item = navigationView.getMenu().findItem(R.id.nav_notification); - updateNotifCounter(0); + newNotif = 0; + updateNotifCounter(); if( bubbles && notificationsFragment != null) notificationsFragment.refreshData(); break; @@ -1043,7 +1046,7 @@ public class MainActivity extends AppCompatActivity } } - public void updateHomeCounter(int newHomeCount){ + public void updateHomeCounter(){ if( tabLayout.getTabAt(0) == null ) return; //noinspection ConstantConditions @@ -1051,8 +1054,8 @@ public class MainActivity extends AppCompatActivity if( tabHome == null) return; TextView tabCounterHome = (TextView) tabHome.findViewById(R.id.tab_counter); - tabCounterHome.setText(String.valueOf(newHomeCount)); - if( newHomeCount > 0){ + tabCounterHome.setText(String.valueOf(newHome)); + if( newHome > 0){ //New data are available //The fragment is not displayed, so the counter is displayed if( tabLayout.getSelectedTabPosition() != 0) @@ -1064,8 +1067,7 @@ public class MainActivity extends AppCompatActivity } } - - public void updateNotifCounter(int newNotifCount){ + public void updateNotifCounter(){ if(tabLayout.getTabAt(1) == null) return; //noinspection ConstantConditions @@ -1073,8 +1075,8 @@ public class MainActivity extends AppCompatActivity if( tabNotif == null) return; TextView tabCounterNotif = (TextView) tabNotif.findViewById(R.id.tab_counter); - tabCounterNotif.setText(String.valueOf(newNotifCount)); - if( newNotifCount > 0){ + tabCounterNotif.setText(String.valueOf(newNotif)); + if( newNotif > 0){ if( tabLayout.getSelectedTabPosition() != 1) tabCounterNotif.setVisibility(View.VISIBLE); else diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/RetrieveFeedsAsyncTask.java b/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/RetrieveFeedsAsyncTask.java index 4c913630e..8763a5587 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/RetrieveFeedsAsyncTask.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/RetrieveFeedsAsyncTask.java @@ -15,14 +15,9 @@ package fr.gouv.etalab.mastodon.asynctasks; import android.content.Context; -import android.content.SharedPreferences; import android.os.AsyncTask; - -import java.util.Date; - import fr.gouv.etalab.mastodon.client.API; import fr.gouv.etalab.mastodon.client.APIResponse; -import fr.gouv.etalab.mastodon.helper.Helper; import fr.gouv.etalab.mastodon.interfaces.OnRetrieveFeedsInterface; @@ -61,8 +56,6 @@ public class RetrieveFeedsAsyncTask extends AsyncTask { this.max_id = max_id; this.listener = onRetrieveFeedsInterface; this.refreshData = true; - updateTimeRefresh(); - } public RetrieveFeedsAsyncTask(Context context, Type action, String targetedID, String max_id, boolean showMediaOnly, OnRetrieveFeedsInterface onRetrieveFeedsInterface){ @@ -73,7 +66,6 @@ public class RetrieveFeedsAsyncTask extends AsyncTask { this.targetedID = targetedID; this.showMediaOnly = showMediaOnly; this.refreshData = true; - updateTimeRefresh(); } public RetrieveFeedsAsyncTask(Context context, Type action, String tag, String targetedID, String max_id, OnRetrieveFeedsInterface onRetrieveFeedsInterface){ this.context = context; @@ -83,17 +75,8 @@ public class RetrieveFeedsAsyncTask extends AsyncTask { this.targetedID = targetedID; this.tag = tag; this.refreshData = true; - updateTimeRefresh(); } - public RetrieveFeedsAsyncTask(Context context, Type action, String max_id, boolean refreshData, OnRetrieveFeedsInterface onRetrieveFeedsInterface){ - this.context = context; - this.action = action; - this.max_id = max_id; - this.listener = onRetrieveFeedsInterface; - this.refreshData = refreshData; - updateTimeRefresh(); - } @Override protected Void doInBackground(Void... params) { @@ -134,14 +117,4 @@ public class RetrieveFeedsAsyncTask extends AsyncTask { protected void onPostExecute(Void result) { listener.onRetrieveFeeds(apiResponse, refreshData); } - - private void updateTimeRefresh(){ - if( action == Type.HOME) { - final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, android.content.Context.MODE_PRIVATE); - SharedPreferences.Editor editor = sharedpreferences.edit(); - String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null); - editor.putString(Helper.LAST_BUBBLE_REFRESH_HOME + userId, Helper.dateToString(context, new Date())); - editor.apply(); - } - } } diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/RetrieveNotificationsAsyncTask.java b/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/RetrieveNotificationsAsyncTask.java index e59bb8417..6b7a8b7c6 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/RetrieveNotificationsAsyncTask.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/RetrieveNotificationsAsyncTask.java @@ -15,14 +15,9 @@ package fr.gouv.etalab.mastodon.asynctasks; import android.content.Context; -import android.content.SharedPreferences; import android.os.AsyncTask; - -import java.util.Date; - import fr.gouv.etalab.mastodon.client.API; import fr.gouv.etalab.mastodon.client.APIResponse; -import fr.gouv.etalab.mastodon.helper.Helper; import fr.gouv.etalab.mastodon.interfaces.OnRetrieveNotificationsInterface; @@ -51,20 +46,8 @@ public class RetrieveNotificationsAsyncTask extends AsyncTask this.userId = userId; this.token = token; this.refreshData = true; - updateTimeRefresh(); } - public RetrieveNotificationsAsyncTask(Context context, String instance, String token, String max_id, String acct, String userId, boolean refreshData, OnRetrieveNotificationsInterface onRetrieveNotificationsInterface){ - this.context = context; - this.max_id = max_id; - this.listener = onRetrieveNotificationsInterface; - this.acct = acct; - this.instance = instance; - this.userId = userId; - this.token = token; - this.refreshData = refreshData; - updateTimeRefresh(); - } @Override protected Void doInBackground(Void... params) { @@ -82,10 +65,4 @@ public class RetrieveNotificationsAsyncTask extends AsyncTask listener.onRetrieveNotifications(apiResponse, acct, userId, refreshData); } - private void updateTimeRefresh(){ - final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, android.content.Context.MODE_PRIVATE); - SharedPreferences.Editor editor = sharedpreferences.edit(); - editor.putString(Helper.LAST_BUBBLE_REFRESH_NOTIF+ userId,Helper.dateToString(context, new Date())); - editor.apply(); - } } diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/client/Entities/Notification.java b/app/src/main/java/fr/gouv/etalab/mastodon/client/Entities/Notification.java index 384ede022..c415088dc 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/client/Entities/Notification.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/client/Entities/Notification.java @@ -39,6 +39,8 @@ public class Notification implements Parcelable { status = in.readParcelable(Status.class.getClassLoader()); } + public Notification(){}; + public static final Creator CREATOR = new Creator() { @Override public Notification createFromParcel(Parcel in) { diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/fragments/DisplayNotificationsFragment.java b/app/src/main/java/fr/gouv/etalab/mastodon/fragments/DisplayNotificationsFragment.java index 6823f340b..d276e9b48 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/fragments/DisplayNotificationsFragment.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/fragments/DisplayNotificationsFragment.java @@ -37,6 +37,7 @@ import java.util.List; import fr.gouv.etalab.mastodon.activities.MainActivity; import fr.gouv.etalab.mastodon.client.APIResponse; import fr.gouv.etalab.mastodon.client.Entities.Account; +import fr.gouv.etalab.mastodon.client.Entities.Status; import fr.gouv.etalab.mastodon.drawers.NotificationsListAdapter; import fr.gouv.etalab.mastodon.helper.Helper; import fr.gouv.etalab.mastodon.sqlite.AccountDAO; @@ -145,13 +146,6 @@ public class DisplayNotificationsFragment extends Fragment implements OnRetrieve for(Notification notification: notificationsTmp){ notifications.add(notification); } - //The user clicked on the banner to refresh values so, the pointer is changed - if( notificationsTmp.size() > 0 ) { - SharedPreferences.Editor editor = sharedpreferences.edit(); - String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null); - editor.putString(Helper.LAST_MAX_ID_BUBBLE_NOTIF + userId, notificationsTmp.get(0).getId()); - editor.apply(); - } notificationsListAdapter = new NotificationsListAdapter(context,isOnWifi, behaviorWithAttachments, notifications); lv_notifications.setAdapter(notificationsListAdapter); if( notificationsTmp.size() > 0 && textviewNoAction.getVisibility() == View.VISIBLE) @@ -205,64 +199,11 @@ public class DisplayNotificationsFragment extends Fragment implements OnRetrieve return; } SharedPreferences.Editor editor = sharedpreferences.edit(); - editor.putString(Helper.LAST_BUBBLE_REFRESH_NOTIF+ userId,Helper.dateToString(context, new Date())); - editor.apply(); - String bubble_max_id = sharedpreferences.getString(Helper.LAST_MAX_ID_BUBBLE_NOTIF + userId, null); List notifications = apiResponse.getNotifications(); since_id = apiResponse.getSince_id(); max_id = apiResponse.getMax_id(); //The initial call comes from a classic tab refresh - if( refreshData ) { - manageNotifications(notifications, max_id, since_id); - //The current tab is displayed, so user is supposed to have seen the notifications - if( since_id != null && displayNotificationsFragment.getUserVisibleHint() && firstLoad) { - editor.putString(Helper.LAST_MAX_ID_BUBBLE_NOTIF + userId, since_id); - editor.apply(); - }else if(!displayNotificationsFragment.getUserVisibleHint()){ - //The refresh was done automatically, but the fragment was not displayed in viewpager - //So the bubble counter will be displayed - int countData = 0; - //Retrieves new notification count - if( bubble_max_id != null) { - for (Notification nt : notifications) { - if (nt.getId().trim().equals(bubble_max_id.trim())) - break; - countData++; - } - } - ((MainActivity)context).updateNotifCounter(countData); - } - }else { //Here, new values have been retrieved on the onResume call (forced mode) - int countData = 0; - if( bubble_max_id != null) { - for (Notification nt : notifications) { - if (nt.getId().trim().equals(bubble_max_id.trim())) - break; - countData++; - } - } - if( notifications != null && notifications.size() > 0 && countData > 0) { - max_id = null; - firstLoad = true; - notificationsTmp = new ArrayList<>(); - for (Notification tmpNotification : notifications) { - this.notificationsTmp.add(tmpNotification); - } - //New notifications will be counted - //The fragment is not displayed, so the bubble counter should be shown - if (!displayNotificationsFragment.getUserVisibleHint()) { - ((MainActivity) context).updateNotifCounter(countData); - } else { //The current fragment is visible, but for avoiding to populate with new values - // a message will be displayed at the bottom requiring a click to display these new values - new_data.setVisibility(View.VISIBLE); - } - } - } - } - - private void manageNotifications(List notifications, String max_id, String since_id){ flag_loading = (max_id == null ); - final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); if( !swiped && firstLoad && (notifications == null || notifications.size() == 0)) textviewNoAction.setVisibility(View.VISIBLE); else @@ -284,11 +225,10 @@ public class DisplayNotificationsFragment extends Fragment implements OnRetrieve //Store last notification id to avoid to notify for those that have been already seen if( notifications != null && notifications.size() > 0) { //acct is null as userId when used in Fragment, data need to be retrieved via shared preferences and db - String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null); + userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null); SQLiteDatabase db = Sqlite.getInstance(context, Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); Account currentAccount = new AccountDAO(context, db).getAccountByID(userId); if( currentAccount != null && firstLoad && since_id != null){ - SharedPreferences.Editor editor = sharedpreferences.edit(); editor.putString(Helper.LAST_NOTIFICATION_MAX_ID + currentAccount.getId(), since_id); editor.apply(); } @@ -296,40 +236,23 @@ public class DisplayNotificationsFragment extends Fragment implements OnRetrieve firstLoad = false; } + public void scrollToTop(){ if( lv_notifications != null) lv_notifications.setAdapter(notificationsListAdapter); } - public void update(){ - if( context != null){ - asyncTask = new RetrieveNotificationsAsyncTask(context, null, null, null, null, null, false, DisplayNotificationsFragment.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + + public void updateData(Notification notification){ + if( notificationsTmp != null && notificationsTmp.size() > 0){ + notificationsTmp.add(0,notification); + }else { + notificationsTmp = new ArrayList<>(); + for(Notification notificationTmp: this.notifications){ + notificationsTmp.add(notificationTmp); + } + notificationsTmp.add(0,notification); } - } - - public void refreshData(){ - - final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); - - if(context != null && this.notificationsTmp != null && this.notificationsTmp.size() > 0){ - boolean isOnWifi = Helper.isOnWIFI(context); - int behaviorWithAttachments = sharedpreferences.getInt(Helper.SET_ATTACHMENT_ACTION, Helper.ATTACHMENT_ALWAYS); - notifications = new ArrayList<>(); - for(Notification notification: this.notificationsTmp){ - notifications.add(notification); - } - if( textviewNoAction.getVisibility() == View.VISIBLE) - textviewNoAction.setVisibility(View.GONE); - notificationsListAdapter = new NotificationsListAdapter(context,isOnWifi, behaviorWithAttachments, notifications); - lv_notifications.setAdapter(notificationsListAdapter); - this.notificationsTmp = new ArrayList<>(); - } - if( since_id != null){ - //The user clicked on the tab to refresh values so, the pointer is changed - SharedPreferences.Editor editor = sharedpreferences.edit(); - String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null); - editor.putString(Helper.LAST_MAX_ID_BUBBLE_NOTIF + userId, since_id); - editor.apply(); - } - } + new_data.setVisibility(View.VISIBLE); + } } diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/fragments/DisplayStatusFragment.java b/app/src/main/java/fr/gouv/etalab/mastodon/fragments/DisplayStatusFragment.java index 3aa2e1e6b..f3df16cab 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/fragments/DisplayStatusFragment.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/fragments/DisplayStatusFragment.java @@ -229,13 +229,6 @@ public class DisplayStatusFragment extends Fragment implements OnRetrieveFeedsIn for(Status status: statusesTmp){ statuses.add(status); } - //The user clicked on the banner to refresh values so, the pointer is changed - if( statusesTmp.size() > 0 ) { - SharedPreferences.Editor editor = sharedpreferences.edit(); - String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null); - editor.putString(Helper.LAST_MAX_ID_BUBBLE_HOME + userId, statusesTmp.get(0).getId()); - editor.apply(); - } if( statusesTmp.size() > 0 && textviewNoAction.getVisibility() == View.VISIBLE) textviewNoAction.setVisibility(View.GONE); statusListAdapter = new StatusListAdapter(context, type, targetedId, isOnWifi, behaviorWithAttachments, positionSpinnerTrans, statuses); @@ -271,11 +264,6 @@ public class DisplayStatusFragment extends Fragment implements OnRetrieveFeedsIn for(Status status: statusesTmp){ statuses.add(status); } - //The user clicked on the tab to refresh values so, the pointer is changed - SharedPreferences.Editor editor = sharedpreferences.edit(); - String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null); - editor.putString(Helper.LAST_MAX_ID_BUBBLE_HOME + userId, statusesTmp.get(0).getId()); - editor.apply(); statusListAdapter = new StatusListAdapter(context, type, targetedId, isOnWifi, behaviorWithAttachments, positionSpinnerTrans, statuses); lv_status.setAdapter(statusListAdapter); statusesTmp = new ArrayList<>(); @@ -315,72 +303,7 @@ public class DisplayStatusFragment extends Fragment implements OnRetrieveFeedsIn List statuses = apiResponse.getStatuses(); since_id = apiResponse.getSince_id(); max_id = apiResponse.getMax_id(); - //Special case for home timeline - if( type == RetrieveFeedsAsyncTask.Type.HOME){ - //Retrieves some values - SharedPreferences.Editor editor = sharedpreferences.edit(); - String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null); - String bubble_max_id = sharedpreferences.getString(Helper.LAST_MAX_ID_BUBBLE_HOME + userId, null); - //The initial call comes from a classic tab refresh - if( refreshData ) { - - manageStatus(statuses, max_id, since_id); - //The current tab is displayed, so user is supposed to have seen status - if( since_id != null && displayStatusFragment.getUserVisibleHint() && firstLoad) { - editor.putString(Helper.LAST_MAX_ID_BUBBLE_HOME + userId, since_id); - editor.apply(); - }else if(!displayStatusFragment.getUserVisibleHint()){ - //Current fragment was loaded but not displayed to the user. - //So the bubble counter will be displayed - int countData = 0; - //Retrieves new status count - if( bubble_max_id != null) { - for (Status st : statuses) { - if (st.getId().trim().equals(bubble_max_id.trim())) - break; - countData++; - } - } - ((MainActivity)context).updateHomeCounter(countData); - } - }else { //Here, new values have been retrieved on the onResume call (forced mode) - int countData = 0; - if( bubble_max_id != null) { - for (Status st : statuses) { - if (st.getId().trim().equals(bubble_max_id.trim())) - break; - countData++; - } - } - - if( statuses != null && statuses.size() > 0 && countData > 0) { - max_id = null; - firstLoad = true; - statusesTmp = new ArrayList<>(); - for (Status tmpStatus : statuses) { - this.statusesTmp.add(tmpStatus); - } - //New status will be counted - //The fragment is not displayed, so the bubble counter should be shown - if (!displayStatusFragment.getUserVisibleHint()) { - ((MainActivity) context).updateHomeCounter(countData); - } else { - //The current fragment is visible, but for avoiding to populate with new values - //Values are put in temp and the banned is displayed - new_data.setVisibility(View.VISIBLE); - } - } - } - }else { - manageStatus(statuses, max_id, since_id); - } - - - - } - - private void manageStatus(List statuses, String max_id, String since_id){ flag_loading = (max_id == null ); if( !swiped && firstLoad && (statuses == null || statuses.size() == 0)) textviewNoAction.setVisibility(View.VISIBLE); @@ -401,7 +324,6 @@ public class DisplayStatusFragment extends Fragment implements OnRetrieveFeedsIn //Store last toot id for home timeline to avoid to notify for those that have been already seen if(statuses != null && statuses.size() > 0 && type == RetrieveFeedsAsyncTask.Type.HOME ){ - final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); //acct is null when used in Fragment, data need to be retrieved via shared preferences and db String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null); SQLiteDatabase db = Sqlite.getInstance(context, Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); @@ -416,7 +338,6 @@ public class DisplayStatusFragment extends Fragment implements OnRetrieveFeedsIn //Retrieves replies if(statuses != null && statuses.size() > 0 && type == RetrieveFeedsAsyncTask.Type.HOME ) { - final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); boolean showPreview = sharedpreferences.getBoolean(Helper.SET_PREVIEW_REPLIES, true); //Retrieves attached replies to a toot if (showPreview) { @@ -425,6 +346,19 @@ public class DisplayStatusFragment extends Fragment implements OnRetrieveFeedsIn } } + public void updateData(Status status){ + if( statusesTmp != null && statusesTmp.size() > 0){ + statusesTmp.add(0,status); + }else { + statusesTmp = new ArrayList<>(); + for(Status statusTmp: this.statuses){ + statusesTmp.add(statusTmp); + } + statusesTmp.add(0,status); + } + new_data.setVisibility(View.VISIBLE); + } + public void scrollToTop(){ if( lv_status != null) lv_status.setAdapter(statusListAdapter); @@ -444,36 +378,4 @@ public class DisplayStatusFragment extends Fragment implements OnRetrieveFeedsIn } statusListAdapter.notifyDataSetChanged(); } - public void update() { - if( context != null) { - asyncTask = new RetrieveFeedsAsyncTask(context, type, null, false, DisplayStatusFragment.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - } - - public void refreshData(){ - final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); - if(context != null && this.statusesTmp != null && this.statusesTmp.size() > 0){ - - boolean isOnWifi = Helper.isOnWIFI(context); - int behaviorWithAttachments = sharedpreferences.getInt(Helper.SET_ATTACHMENT_ACTION, Helper.ATTACHMENT_ALWAYS); - int positionSpinnerTrans = sharedpreferences.getInt(Helper.SET_TRANSLATOR, Helper.TRANS_YANDEX); - - statuses = new ArrayList<>(); - for(Status status: statusesTmp){ - statuses.add(status); - } - if( textviewNoAction.getVisibility() == View.VISIBLE) - textviewNoAction.setVisibility(View.GONE); - statusListAdapter = new StatusListAdapter(context, type, targetedId, isOnWifi, behaviorWithAttachments, positionSpinnerTrans, statuses); - lv_status.setAdapter(statusListAdapter); - statusesTmp = new ArrayList<>(); - } - if( since_id != null){ - //The user clicked on the tab to refresh values so, the pointer is changed - SharedPreferences.Editor editor = sharedpreferences.edit(); - String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null); - editor.putString(Helper.LAST_MAX_ID_BUBBLE_HOME + userId, since_id); - editor.apply(); - } - } } 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 index 2783602f9..5907cb623 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/fragments/SettingsFragment.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/fragments/SettingsFragment.java @@ -94,19 +94,6 @@ public class SettingsFragment extends Fragment { } }); - boolean bubble_counter = sharedpreferences.getBoolean(Helper.SET_BUBBLE_COUNTER, true); - - final CheckBox set_bubble_counter = (CheckBox) rootView.findViewById(R.id.set_bubble_counter); - set_bubble_counter.setChecked(bubble_counter); - set_bubble_counter.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - SharedPreferences.Editor editor = sharedpreferences.edit(); - editor.putBoolean(Helper.SET_BUBBLE_COUNTER, set_bubble_counter.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); 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 3419b1cdd..a5f171903 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 @@ -178,10 +178,6 @@ public class Helper { public static final String SHOW_BATTERY_SAVER_MESSAGE = "show_battery_saver_message"; public static final String LAST_NOTIFICATION_MAX_ID = "last_notification_max_id"; public static final String LAST_HOMETIMELINE_MAX_ID = "last_hometimeline_max_id"; - public static final String LAST_BUBBLE_REFRESH_NOTIF = "last_bubble_refresh_notif"; - public static final String LAST_BUBBLE_REFRESH_HOME = "last_bubble_refresh_home"; - public static final String LAST_MAX_ID_BUBBLE_NOTIF = "last_max_id_bubble_notif"; - public static final String LAST_MAX_ID_BUBBLE_HOME = "last_max_id_bubble_home"; public static final String CLIP_BOARD = "clipboard"; //Notifications public static final int NOTIFICATION_INTENT = 1; @@ -205,7 +201,6 @@ public class Helper { public static final String SET_ICON_SIZE = "set_icon_size"; public static final String SET_PREVIEW_REPLIES = "set_preview_replies"; public static final String SET_PREVIEW_REPLIES_PP = "set_preview_replies_pp"; - public static final String SET_BUBBLE_COUNTER = "set_bubble_counter"; public static final String SET_TRANSLATOR = "set_translator"; public static final int ATTACHMENT_ALWAYS = 1; diff --git a/app/src/main/res/layout-sw600dp/fragment_settings.xml b/app/src/main/res/layout-sw600dp/fragment_settings.xml index 73c3fd3d0..9e104537d 100644 --- a/app/src/main/res/layout-sw600dp/fragment_settings.xml +++ b/app/src/main/res/layout-sw600dp/fragment_settings.xml @@ -58,11 +58,6 @@ android:text="@string/set_auto_store_toot" android:layout_height="wrap_content" /> - - = TimeUnit.SECONDS.toMillis(60)) { - - if( notificationsFragment != null ){ - notificationsFragment.update(); - SharedPreferences.Editor editor = sharedpreferences.edit(); - editor.putString(Helper.LAST_BUBBLE_REFRESH_NOTIF+ account.getId(),Helper.dateToString(getApplicationContext(), new Date())); - editor.apply(); - } - } - - last_refresh = sharedpreferences.getString(Helper.LAST_BUBBLE_REFRESH_HOME + account.getId(), null); - last_refresh_date = Helper.stringToDate(getApplicationContext(), last_refresh); - - if (last_refresh_date == null || (new Date().getTime() - last_refresh_date.getTime()) >= TimeUnit.SECONDS.toMillis(60)) { - if( homeFragment != null ){ - homeFragment.update(); - SharedPreferences.Editor editor = sharedpreferences.edit(); - editor.putString(Helper.LAST_BUBBLE_REFRESH_HOME+ account.getId(),Helper.dateToString(getApplicationContext(), new Date())); - editor.apply(); - } - } - } - } - - public void updateHomeCounter(int newHomeCount){ + public void updateHomeCounter(){ if( tabLayout.getTabAt(0) == null ) return; //noinspection ConstantConditions @@ -1146,8 +1114,8 @@ public class MainActivity extends AppCompatActivity if( tabHome == null) return; TextView tabCounterHome = (TextView) tabHome.findViewById(R.id.tab_counter); - tabCounterHome.setText(String.valueOf(newHomeCount)); - if( newHomeCount > 0){ + tabCounterHome.setText(String.valueOf(newHome)); + if( newHome > 0){ //New data are available //The fragment is not displayed, so the counter is displayed if( tabLayout.getSelectedTabPosition() != 0) @@ -1159,7 +1127,7 @@ public class MainActivity extends AppCompatActivity } } - public void updateNotifCounter(int newNotifCount){ + public void updateNotifCounter(){ if(tabLayout.getTabAt(1) == null) return; //noinspection ConstantConditions @@ -1167,8 +1135,8 @@ public class MainActivity extends AppCompatActivity if( tabNotif == null) return; TextView tabCounterNotif = (TextView) tabNotif.findViewById(R.id.tab_counter); - tabCounterNotif.setText(String.valueOf(newNotifCount)); - if( newNotifCount > 0){ + tabCounterNotif.setText(String.valueOf(newNotif)); + if( newNotif > 0){ if( tabLayout.getSelectedTabPosition() != 1) tabCounterNotif.setVisibility(View.VISIBLE); else From 182a391b720ededf0c5d80b319522a5636e90a43 Mon Sep 17 00:00:00 2001 From: tom79 Date: Mon, 28 Aug 2017 15:33:05 +0200 Subject: [PATCH 4/8] Prepares jobs for streaming --- .../fr/gouv/etalab/mastodon/activities/MainApplication.java | 6 ++---- .../jobs/{SteamingSyncJob.java => StreamingSyncJob.java} | 6 +++--- 2 files changed, 5 insertions(+), 7 deletions(-) rename app/src/main/java/fr/gouv/etalab/mastodon/jobs/{SteamingSyncJob.java => StreamingSyncJob.java} (98%) diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/activities/MainApplication.java b/app/src/main/java/fr/gouv/etalab/mastodon/activities/MainApplication.java index 6fe4a5fbb..907bccef4 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/activities/MainApplication.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/activities/MainApplication.java @@ -17,8 +17,7 @@ import android.app.Application; import android.os.StrictMode; import com.evernote.android.job.JobManager; import fr.gouv.etalab.mastodon.jobs.ApplicationJob; -import fr.gouv.etalab.mastodon.jobs.HomeTimelineSyncJob; -import fr.gouv.etalab.mastodon.jobs.NotificationsSyncJob; +import fr.gouv.etalab.mastodon.jobs.StreamingSyncJob; /** * Created by Thomas on 29/04/2017. @@ -33,8 +32,7 @@ public class MainApplication extends Application{ super.onCreate(); JobManager.create(this).addJobCreator(new ApplicationJob()); JobManager.instance().getConfig().setVerbose(false); - NotificationsSyncJob.schedule(false); - HomeTimelineSyncJob.schedule(false); + StreamingSyncJob.schedule(false); StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder(); StrictMode.setVmPolicy(builder.build()); } diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/jobs/SteamingSyncJob.java b/app/src/main/java/fr/gouv/etalab/mastodon/jobs/StreamingSyncJob.java similarity index 98% rename from app/src/main/java/fr/gouv/etalab/mastodon/jobs/SteamingSyncJob.java rename to app/src/main/java/fr/gouv/etalab/mastodon/jobs/StreamingSyncJob.java index 98fed4230..3fa255c26 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/jobs/SteamingSyncJob.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/jobs/StreamingSyncJob.java @@ -70,7 +70,7 @@ import static fr.gouv.etalab.mastodon.helper.Helper.notify_user; * Notifications refresh job */ -public class SteamingSyncJob extends Job implements OnRetrieveStreamingInterface { +public class StreamingSyncJob extends Job implements OnRetrieveStreamingInterface { static final String STREAMING = "job_streaming"; private String message; @@ -93,7 +93,7 @@ public class SteamingSyncJob extends Job implements OnRetrieveStreamingInterface return jobRequests.iterator().next().getJobId(); } - return new JobRequest.Builder(SteamingSyncJob.STREAMING) + return new JobRequest.Builder(StreamingSyncJob.STREAMING) .setPeriodic(TimeUnit.MINUTES.toMillis(Helper.MINUTES_BETWEEN_NOTIFICATIONS_REFRESH), TimeUnit.MINUTES.toMillis(5)) .setPersisted(true) .setUpdateCurrent(updateCurrent) @@ -133,7 +133,7 @@ public class SteamingSyncJob extends Job implements OnRetrieveStreamingInterface return; //Retrieve users in db that owner has. for (Account account: accounts) { - new StreamingUserAsyncTask(account.getInstance(), account.getToken(), account.getAcct(), account.getId(), SteamingSyncJob.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + new StreamingUserAsyncTask(account.getInstance(), account.getToken(), account.getAcct(), account.getId(), StreamingSyncJob.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } } } From e11a19b86e754bbeef036735077e4ed59ad8b646 Mon Sep 17 00:00:00 2001 From: tom79 Date: Mon, 28 Aug 2017 15:35:26 +0200 Subject: [PATCH 5/8] Programs main jobs - removes useless ones. --- .../etalab/mastodon/jobs/ApplicationJob.java | 6 +- .../mastodon/jobs/HomeTimelineSyncJob.java | 204 ------------- .../mastodon/jobs/NotificationsSyncJob.java | 271 ------------------ 3 files changed, 2 insertions(+), 479 deletions(-) delete mode 100644 app/src/main/java/fr/gouv/etalab/mastodon/jobs/HomeTimelineSyncJob.java delete mode 100644 app/src/main/java/fr/gouv/etalab/mastodon/jobs/NotificationsSyncJob.java diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/jobs/ApplicationJob.java b/app/src/main/java/fr/gouv/etalab/mastodon/jobs/ApplicationJob.java index c087b520f..61b7159bf 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/jobs/ApplicationJob.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/jobs/ApplicationJob.java @@ -26,10 +26,8 @@ public class ApplicationJob implements JobCreator { @Override public Job create(String tag) { switch (tag) { - case NotificationsSyncJob.NOTIFICATION_REFRESH: - return new NotificationsSyncJob(); - case HomeTimelineSyncJob.HOME_TIMELINE: - return new HomeTimelineSyncJob(); + case StreamingSyncJob.STREAMING: + return new StreamingSyncJob(); case ScheduledTootsSyncJob.SCHEDULED_TOOT: return new ScheduledTootsSyncJob(); default: diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/jobs/HomeTimelineSyncJob.java b/app/src/main/java/fr/gouv/etalab/mastodon/jobs/HomeTimelineSyncJob.java deleted file mode 100644 index 19cd2d843..000000000 --- a/app/src/main/java/fr/gouv/etalab/mastodon/jobs/HomeTimelineSyncJob.java +++ /dev/null @@ -1,204 +0,0 @@ -package fr.gouv.etalab.mastodon.jobs; -/* 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.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.database.sqlite.SQLiteDatabase; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.os.AsyncTask; -import android.support.annotation.NonNull; -import android.view.View; - -import com.evernote.android.job.Job; -import com.evernote.android.job.JobManager; -import com.evernote.android.job.JobRequest; -import com.nostra13.universalimageloader.cache.disc.impl.UnlimitedDiskCache; -import com.nostra13.universalimageloader.core.DisplayImageOptions; -import com.nostra13.universalimageloader.core.ImageLoader; -import com.nostra13.universalimageloader.core.ImageLoaderConfiguration; -import com.nostra13.universalimageloader.core.assist.FailReason; -import com.nostra13.universalimageloader.core.display.SimpleBitmapDisplayer; -import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener; - -import java.io.File; -import java.util.List; -import java.util.Set; -import java.util.concurrent.TimeUnit; - -import fr.gouv.etalab.mastodon.activities.MainActivity; -import fr.gouv.etalab.mastodon.asynctasks.RetrieveHomeTimelineServiceAsyncTask; -import fr.gouv.etalab.mastodon.client.APIResponse; -import fr.gouv.etalab.mastodon.client.Entities.Account; -import fr.gouv.etalab.mastodon.client.Entities.Status; -import fr.gouv.etalab.mastodon.client.PatchBaseImageDownloader; -import fr.gouv.etalab.mastodon.helper.Helper; -import fr.gouv.etalab.mastodon.interfaces.OnRetrieveHomeTimelineServiceInterface; -import fr.gouv.etalab.mastodon.sqlite.AccountDAO; -import fr.gouv.etalab.mastodon.sqlite.Sqlite; -import mastodon.etalab.gouv.fr.mastodon.R; - -import static fr.gouv.etalab.mastodon.helper.Helper.HOME_TIMELINE_INTENT; -import static fr.gouv.etalab.mastodon.helper.Helper.INTENT_ACTION; -import static fr.gouv.etalab.mastodon.helper.Helper.PREF_KEY_ID; -import static fr.gouv.etalab.mastodon.helper.Helper.canNotify; -import static fr.gouv.etalab.mastodon.helper.Helper.notify_user; - - -/** - * Created by Thomas on 20/05/2017. - * Notifications for home timeline job - */ - -public class HomeTimelineSyncJob extends Job implements OnRetrieveHomeTimelineServiceInterface{ - - static final String HOME_TIMELINE = "home_timeline"; - - @NonNull - @Override - protected Result onRunJob(Params params) { - callAsynchronousTask(); - return Result.SUCCESS; - } - - - public static int schedule(boolean updateCurrent){ - - Set jobRequests = JobManager.instance().getAllJobRequestsForTag(HOME_TIMELINE); - if (!jobRequests.isEmpty() && !updateCurrent) { - return jobRequests.iterator().next().getJobId(); - } - return new JobRequest.Builder(HomeTimelineSyncJob.HOME_TIMELINE) - .setPeriodic(TimeUnit.MINUTES.toMillis(Helper.MINUTES_BETWEEN_HOME_TIMELINE), TimeUnit.MINUTES.toMillis(5)) - .setPersisted(true) - .setUpdateCurrent(updateCurrent) - .setRequiredNetworkType(JobRequest.NetworkType.CONNECTED) - .setRequirementsEnforced(false) - .build() - .schedule(); - } - - - /** - * Task in background starts here. - */ - private void callAsynchronousTask() { - - if( !canNotify(getContext())) - return; - final SharedPreferences sharedpreferences = getContext().getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); - boolean notif_hometimeline = sharedpreferences.getBoolean(Helper.SET_NOTIF_HOMETIMELINE, true); - //User disagree with home timeline refresh - if( !notif_hometimeline) - return; //Nothing is done - //No account connected, the service is stopped - if(!Helper.isLoggedIn(getContext())) - return; - SQLiteDatabase db = Sqlite.getInstance(getContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); - //If an Internet connection and user agrees with notification refresh - //If WIFI only and on WIFI OR user defined any connections to use the service. - if(!sharedpreferences.getBoolean(Helper.SET_WIFI_ONLY, false) || Helper.isOnWIFI(getContext())) { - List accounts = new AccountDAO(getContext(),db).getAllAccount(); - //It means there is no user in DB. - if( accounts == null ) - return; - //Retrieve users in db that owner has. - for (Account account: accounts) { - String since_id = sharedpreferences.getString(Helper.LAST_HOMETIMELINE_MAX_ID + account.getId(), null); - new RetrieveHomeTimelineServiceAsyncTask(getContext(), account.getInstance(), account.getToken(), since_id, account.getAcct(), account.getId(), HomeTimelineSyncJob.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - - } - } - } - - - @Override - public void onRetrieveHomeTimelineService(APIResponse apiResponse, String acct, String userId) { - List statuses = apiResponse.getStatuses(); - if( apiResponse.getError() != null || statuses == null || statuses.size() == 0) - return; - final SharedPreferences sharedpreferences = getContext().getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); - - final String max_id = sharedpreferences.getString(Helper.LAST_HOMETIMELINE_MAX_ID + userId, null); - if( max_id == null){ - SharedPreferences.Editor editor = sharedpreferences.edit(); - editor.putString(Helper.LAST_HOMETIMELINE_MAX_ID + userId, apiResponse.getSince_id()); - editor.apply(); - return; - } - //No previous notifications in cache, so no notification will be sent - String message; - SharedPreferences.Editor editor = sharedpreferences.edit(); - editor.putString(Helper.LAST_HOMETIMELINE_MAX_ID + userId, apiResponse.getSince_id()); - editor.apply(); - for(Status status: statuses){ - //The notification associated to max_id is discarded as it is supposed to have already been sent - //Also, if the toot comes from the owner, we will avoid to warn him/her... - if( (status.getId().equals(max_id)) || (acct != null && status.getAccount().getAcct().trim().equals(acct.trim()) )) - continue; - String notificationUrl = status.getAccount().getAvatar(); - - if(statuses.size() > 0 ) - message = getContext().getResources().getQuantityString(R.plurals.other_notif_hometimeline, statuses.size(), statuses.size()); - else - message = ""; - final Intent intent = new Intent(getContext(), MainActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK ); - intent.putExtra(INTENT_ACTION, HOME_TIMELINE_INTENT); - intent.putExtra(PREF_KEY_ID, userId); - long notif_id = Long.parseLong(userId); - final int notificationId = ((notif_id + 2) > 2147483647) ? (int) (2147483647 - notif_id - 2) : (int) (notif_id + 2); - - if( notificationUrl != null){ - ImageLoader imageLoaderNoty = ImageLoader.getInstance(); - File cacheDir = new File(getContext().getCacheDir(), getContext().getString(R.string.app_name)); - ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getContext()) - .imageDownloader(new PatchBaseImageDownloader(getContext())) - .threadPoolSize(5) - .threadPriority(Thread.MIN_PRIORITY + 3) - .denyCacheImageMultipleSizesInMemory() - .diskCache(new UnlimitedDiskCache(cacheDir)) - .build(); - imageLoaderNoty.init(config); - DisplayImageOptions options = new DisplayImageOptions.Builder().displayer(new SimpleBitmapDisplayer()).cacheInMemory(false) - .cacheOnDisk(true).resetViewBeforeLoading(true).build(); - final String finalMessage = message; - String title; - if( status.getAccount().getDisplay_name() != null && status.getAccount().getDisplay_name().length() > 0 ) - title = getContext().getResources().getString(R.string.notif_pouet, Helper.shortnameToUnicode(status.getAccount().getDisplay_name(), true)); - else - title = getContext().getResources().getString(R.string.notif_pouet, status.getAccount().getUsername()); - final String finalTitle = title; - - imageLoaderNoty.loadImage(notificationUrl, options, new SimpleImageLoadingListener(){ - @Override - public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { - super.onLoadingComplete(imageUri, view, loadedImage); - notify_user(getContext(), intent, notificationId, loadedImage, finalTitle, finalMessage); - } - @Override - public void onLoadingFailed(java.lang.String imageUri, android.view.View view, FailReason failReason){ - notify_user(getContext(), intent, notificationId, BitmapFactory.decodeResource(getContext().getResources(), - R.drawable.mastodonlogo), finalTitle, finalMessage); - }}); - - } - } - - } - -} diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/jobs/NotificationsSyncJob.java b/app/src/main/java/fr/gouv/etalab/mastodon/jobs/NotificationsSyncJob.java deleted file mode 100644 index 49603c1c1..000000000 --- a/app/src/main/java/fr/gouv/etalab/mastodon/jobs/NotificationsSyncJob.java +++ /dev/null @@ -1,271 +0,0 @@ -package fr.gouv.etalab.mastodon.jobs; -/* 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.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.database.sqlite.SQLiteDatabase; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.os.AsyncTask; -import android.support.annotation.NonNull; -import android.view.View; - -import com.evernote.android.job.Job; -import com.evernote.android.job.JobManager; -import com.evernote.android.job.JobRequest; -import com.nostra13.universalimageloader.cache.disc.impl.UnlimitedDiskCache; -import com.nostra13.universalimageloader.core.DisplayImageOptions; -import com.nostra13.universalimageloader.core.ImageLoader; -import com.nostra13.universalimageloader.core.ImageLoaderConfiguration; -import com.nostra13.universalimageloader.core.assist.FailReason; -import com.nostra13.universalimageloader.core.display.SimpleBitmapDisplayer; -import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener; - -import java.io.File; -import java.util.List; -import java.util.Set; -import java.util.concurrent.TimeUnit; -import fr.gouv.etalab.mastodon.activities.MainActivity; -import fr.gouv.etalab.mastodon.client.APIResponse; -import fr.gouv.etalab.mastodon.client.PatchBaseImageDownloader; -import fr.gouv.etalab.mastodon.helper.Helper; -import mastodon.etalab.gouv.fr.mastodon.R; -import fr.gouv.etalab.mastodon.asynctasks.RetrieveNotificationsAsyncTask; -import fr.gouv.etalab.mastodon.client.Entities.Account; -import fr.gouv.etalab.mastodon.client.Entities.Notification; -import fr.gouv.etalab.mastodon.interfaces.OnRetrieveNotificationsInterface; -import fr.gouv.etalab.mastodon.sqlite.AccountDAO; -import fr.gouv.etalab.mastodon.sqlite.Sqlite; - -import static fr.gouv.etalab.mastodon.helper.Helper.INTENT_ACTION; -import static fr.gouv.etalab.mastodon.helper.Helper.NOTIFICATION_INTENT; -import static fr.gouv.etalab.mastodon.helper.Helper.PREF_KEY_ID; -import static fr.gouv.etalab.mastodon.helper.Helper.canNotify; -import static fr.gouv.etalab.mastodon.helper.Helper.notify_user; - - -/** - * Created by Thomas on 29/04/2017. - * Notifications refresh job - */ - -public class NotificationsSyncJob extends Job implements OnRetrieveNotificationsInterface{ - - static final String NOTIFICATION_REFRESH = "job_notification"; - - @NonNull - @Override - protected Result onRunJob(Params params) { - //Code refresh here - callAsynchronousTask(); - return Result.SUCCESS; - } - - - public static int schedule(boolean updateCurrent){ - - Set jobRequests = JobManager.instance().getAllJobRequestsForTag(NOTIFICATION_REFRESH); - if (!jobRequests.isEmpty() && !updateCurrent) { - return jobRequests.iterator().next().getJobId(); - } - - return new JobRequest.Builder(NotificationsSyncJob.NOTIFICATION_REFRESH) - .setPeriodic(TimeUnit.MINUTES.toMillis(Helper.MINUTES_BETWEEN_NOTIFICATIONS_REFRESH), TimeUnit.MINUTES.toMillis(5)) - .setPersisted(true) - .setUpdateCurrent(updateCurrent) - .setRequiredNetworkType(JobRequest.NetworkType.CONNECTED) - .setRequirementsEnforced(false) - .build() - .schedule(); - } - - - - /** - * Task in background starts here. - */ - private void callAsynchronousTask() { - if( !canNotify(getContext())) - return; - SQLiteDatabase db = Sqlite.getInstance(getContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); - //If an Internet connection and user agrees with notification refresh - final SharedPreferences sharedpreferences = getContext().getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); - //Check which notifications the user wants to see - boolean notif_follow = sharedpreferences.getBoolean(Helper.SET_NOTIF_FOLLOW, true); - boolean notif_add = sharedpreferences.getBoolean(Helper.SET_NOTIF_ADD, true); - 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); - //User disagree with all notifications - if( !notif_follow && !notif_add && !notif_ask && !notif_mention && !notif_share) - return; //Nothing is done - //No account connected, the service is stopped - if(!Helper.isLoggedIn(getContext())) - return; - //If WIFI only and on WIFI OR user defined any connections to use the service. - if(!sharedpreferences.getBoolean(Helper.SET_WIFI_ONLY, false) || Helper.isOnWIFI(getContext())) { - List accounts = new AccountDAO(getContext(),db).getAllAccount(); - //It means there is no user in DB. - if( accounts == null ) - return; - //Retrieve users in db that owner has. - for (Account account: accounts) { - String max_id = sharedpreferences.getString(Helper.LAST_NOTIFICATION_MAX_ID + account.getId(), null); - new RetrieveNotificationsAsyncTask(getContext(), account.getInstance(), account.getToken(), max_id, account.getAcct(), account.getId(), NotificationsSyncJob.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - } - } - - - - @Override - public void onRetrieveNotifications(APIResponse apiResponse, String acct, String userId, boolean refreshData) { - List notifications = apiResponse.getNotifications(); - if( apiResponse.getError() != null || notifications == null || notifications.size() == 0) - return; - Bitmap icon_notification = null; - final SharedPreferences sharedpreferences = getContext().getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); - boolean notif_follow = sharedpreferences.getBoolean(Helper.SET_NOTIF_FOLLOW, true); - boolean notif_add = sharedpreferences.getBoolean(Helper.SET_NOTIF_ADD, true); - boolean notif_mention = sharedpreferences.getBoolean(Helper.SET_NOTIF_MENTION, true); - boolean notif_share = sharedpreferences.getBoolean(Helper.SET_NOTIF_SHARE, true); - final String max_id = sharedpreferences.getString(Helper.LAST_NOTIFICATION_MAX_ID + userId, null); - if( max_id == null){ - SharedPreferences.Editor editor = sharedpreferences.edit(); - editor.putString(Helper.LAST_NOTIFICATION_MAX_ID + userId, apiResponse.getSince_id()); - editor.apply(); - return; - } - - //No previous notifications in cache, so no notification will be sent - int newFollows = 0; - int newAdds = 0; - int newAsks = 0; - int newMentions = 0; - int newShare = 0; - String notificationUrl = null; - String title = null; - final String message; - for(Notification notification: notifications){ - //The notification associated to max_id is discarded as it is supposed to have already been sent - if( max_id != null && notification.getId().equals(max_id)) - continue; - switch (notification.getType()){ - case "mention": - if(notif_mention){ - newMentions++; - if( notificationUrl == null){ - notificationUrl = notification.getAccount().getAvatar(); - if( notification.getAccount().getDisplay_name() != null && notification.getAccount().getDisplay_name().length() > 0 ) - title = String.format("@%s %s", Helper.shortnameToUnicode(notification.getAccount().getDisplay_name(), true),getContext().getString(R.string.notif_mention)); - else - title = String.format("@%s %s", notification.getAccount().getUsername(),getContext().getString(R.string.notif_mention)); - } - } - break; - case "reblog": - if(notif_share){ - newShare++; - if( notificationUrl == null){ - notificationUrl = notification.getAccount().getAvatar(); - if( notification.getAccount().getDisplay_name() != null && notification.getAccount().getDisplay_name().length() > 0 ) - title = String.format("@%s %s", Helper.shortnameToUnicode(notification.getAccount().getDisplay_name(), true),getContext().getString(R.string.notif_reblog)); - else - title = String.format("@%s %s", notification.getAccount().getUsername(),getContext().getString(R.string.notif_reblog)); - - } - } - break; - case "favourite": - if(notif_add){ - newAdds++; - if( notificationUrl == null){ - notificationUrl = notification.getAccount().getAvatar(); - if( notification.getAccount().getDisplay_name() != null && notification.getAccount().getDisplay_name().length() > 0 ) - title = String.format("@%s %s", Helper.shortnameToUnicode(notification.getAccount().getDisplay_name(), true),getContext().getString(R.string.notif_favourite)); - else - title = String.format("@%s %s", notification.getAccount().getUsername(),getContext().getString(R.string.notif_favourite)); - } - } - break; - case "follow": - if(notif_follow){ - newFollows++; - if( notificationUrl == null){ - notificationUrl = notification.getAccount().getAvatar(); - if( notification.getAccount().getDisplay_name() != null && notification.getAccount().getDisplay_name().length() > 0 ) - title = String.format("@%s %s", Helper.shortnameToUnicode(notification.getAccount().getDisplay_name(), true),getContext().getString(R.string.notif_follow)); - else - title = String.format("@%s %s", notification.getAccount().getUsername(),getContext().getString(R.string.notif_follow)); - } - } - break; - default: - } - } - SharedPreferences.Editor editor = sharedpreferences.edit(); - editor.putString(Helper.LAST_NOTIFICATION_MAX_ID + userId, apiResponse.getSince_id()); - editor.apply(); - int allNotifCount = newFollows + newAdds + newAsks + newMentions + newShare; - if( allNotifCount > 0){ - //Some others notification - int other = allNotifCount -1; - if(other > 0 ) - message = getContext().getResources().getQuantityString(R.plurals.other_notifications, other, other); - else - message = ""; - final Intent intent = new Intent(getContext(), MainActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK ); - intent.putExtra(INTENT_ACTION, NOTIFICATION_INTENT); - intent.putExtra(PREF_KEY_ID, userId); - long notif_id = Long.parseLong(userId); - final int notificationId = ((notif_id + 1) > 2147483647) ? (int) (2147483647 - notif_id - 1) : (int) (notif_id + 1); - - if( notificationUrl != null && icon_notification == null){ - ImageLoader imageLoaderNoty = ImageLoader.getInstance(); - File cacheDir = new File(getContext().getCacheDir(), getContext().getString(R.string.app_name)); - ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getContext()) - .imageDownloader(new PatchBaseImageDownloader(getContext())) - .threadPoolSize(5) - .threadPriority(Thread.MIN_PRIORITY + 3) - .denyCacheImageMultipleSizesInMemory() - .diskCache(new UnlimitedDiskCache(cacheDir)) - .build(); - imageLoaderNoty.init(config); - DisplayImageOptions options = new DisplayImageOptions.Builder().displayer(new SimpleBitmapDisplayer()).cacheInMemory(false) - .cacheOnDisk(true).resetViewBeforeLoading(true).build(); - - final String finalTitle = title; - imageLoaderNoty.loadImage(notificationUrl, options, new SimpleImageLoadingListener(){ - @Override - public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { - super.onLoadingComplete(imageUri, view, loadedImage); - if( max_id != null) - notify_user(getContext(), intent, notificationId, loadedImage, finalTitle, message); - } - @Override - public void onLoadingFailed(java.lang.String imageUri, android.view.View view, FailReason failReason){ - if( max_id != null) - notify_user(getContext(), intent, notificationId, BitmapFactory.decodeResource(getContext().getResources(), - R.drawable.mastodonlogo), finalTitle, message); - }}); - } - - } - } - -} From 3a366526b0595eb1e8e81c6195aa8e20e8d3ad8b Mon Sep 17 00:00:00 2001 From: tom79 Date: Mon, 28 Aug 2017 19:04:13 +0200 Subject: [PATCH 6/8] catch notifications --- app/src/main/AndroidManifest.xml | 3 + .../mastodon/activities/MainApplication.java | 2 +- .../asynctasks/StreamingUserAsyncTask.java | 92 +++-- .../gouv/etalab/mastodon/helper/Helper.java | 3 +- .../mastodon/jobs/ScheduledTootsSyncJob.java | 1 - .../mastodon/jobs/StreamingSyncJob.java | 271 +-------------- .../mastodon/services/StreamingService.java | 314 ++++++++++++++++++ .../activities/MainActivity.java | 15 +- 8 files changed, 389 insertions(+), 312 deletions(-) create mode 100644 app/src/main/java/fr/gouv/etalab/mastodon/services/StreamingService.java diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 72d3b2012..5c0910447 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -34,6 +34,9 @@ android:roundIcon="@mipmap/ic_launcher" android:supportsRtl="true" android:theme="@style/AppThemeDark"> + connectionHashMap; + private EventStreaming lastEvent; + public StreamingUserAsyncTask(String instance, String token, String acct, String userId, OnRetrieveStreamingInterface onRetrieveStreamingInterface){ this.instance = instance; @@ -54,58 +57,76 @@ public class StreamingUserAsyncTask extends AsyncTask { public enum EventStreaming{ UPDATE, NOTIFICATION, - DELETE + DELETE, + NONE } + @Override protected Object doInBackground(Object[] params){ - try { - URL url = new URL("https://" + this.instance + "/api/v1/streaming/user"); - HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); - urlConnection.setRequestProperty("Content-Type", "application/json"); - urlConnection.setRequestProperty("Authorization", "Bearer "+this.token); - urlConnection.setRequestProperty("Connection", "Keep-Alive"); - urlConnection.setRequestProperty("Keep-Alive", "header"); - Log.v(Helper.TAG, "http response: " + urlConnection.getResponseCode()); + if( connectionHashMap == null) + connectionHashMap = new HashMap<>(); - //Object inputStream = urlConnection.getContent(); - InputStream inputStream = new BufferedInputStream(urlConnection.getInputStream()); - Log.v(Helper.TAG, readStrem(inputStream)+""); - } catch (MalformedURLException e) { - e.printStackTrace(); - } catch (IOException e) { - Log.v(Helper.TAG, "Error on url openConnection: "+e.getMessage()); - e.printStackTrace(); + boolean connectionAlive = false; + if( connectionHashMap.get(acct+userId) != null) { + try { + connectionAlive = (connectionHashMap.get(acct + userId).getResponseCode() == 200); + } catch (IOException e) { + connectionAlive = false; + } } + if( !connectionAlive) { + try { + URL url = new URL("https://" + this.instance + "/api/v1/streaming/user"); + HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); + urlConnection.setRequestProperty("Content-Type", "application/json"); + urlConnection.setRequestProperty("Authorization", "Bearer " + this.token); + urlConnection.setRequestProperty("Connection", "Keep-Alive"); + urlConnection.setRequestProperty("Keep-Alive", "header"); + connectionHashMap.put(acct+userId, urlConnection); + + InputStream inputStream = new BufferedInputStream(urlConnection.getInputStream()); + readStream(inputStream); + } catch (IOException e) { + e.printStackTrace(); + } + } return null; } - private String readStrem(InputStream inputStream) { + private String readStream(InputStream inputStream) { BufferedReader reader = null; try{ reader = new BufferedReader(new InputStreamReader(inputStream)); - String line; String event; EventStreaming eventStreaming = null; - while((line = reader.readLine()) != null){ - switch (line.trim()){ - case "update": - event = reader.readLine(); + while((event = reader.readLine()) != null){ + if( lastEvent == EventStreaming.NONE || lastEvent == null) { + switch (event.trim()) { + case "event: update": + lastEvent = EventStreaming.UPDATE; + break; + case "event: notification": + lastEvent = EventStreaming.NOTIFICATION; + break; + case "event: delete": + lastEvent = EventStreaming.DELETE; + break; + default: + lastEvent = EventStreaming.NONE; + } + }else{ + event = event.replace("data: ",""); + if(lastEvent == EventStreaming.UPDATE) { eventStreaming = EventStreaming.UPDATE; - break; - case "notification": - event = reader.readLine(); + }else if(lastEvent == EventStreaming.NOTIFICATION) { eventStreaming = EventStreaming.NOTIFICATION; - break; - case "delete": - event = "{\"id\":" + reader.readLine() + "}"; + }else if( lastEvent == EventStreaming.DELETE) { eventStreaming = EventStreaming.DELETE; - break; - default: - event = null; - } - if( event != null){ + event = "{id:" + event + "}"; + } + lastEvent = EventStreaming.NONE; try { JSONObject eventJson = new JSONObject(event); listener.onRetrieveStreaming(eventStreaming, eventJson, acct, userId); @@ -113,6 +134,7 @@ public class StreamingUserAsyncTask extends AsyncTask { e.printStackTrace(); } } + } }catch (IOException e){ e.printStackTrace(); 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 a5f171903..a703e0db7 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 @@ -237,8 +237,7 @@ public class Helper { //Refresh job - public static final int MINUTES_BETWEEN_NOTIFICATIONS_REFRESH = 15; - public static final int MINUTES_BETWEEN_HOME_TIMELINE = 30; + public static final int MINUTES_BETWEEN_STREAMING_CHECK_ALIVE = 15; //Intent public static final String INTENT_ACTION = "intent_action"; diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/jobs/ScheduledTootsSyncJob.java b/app/src/main/java/fr/gouv/etalab/mastodon/jobs/ScheduledTootsSyncJob.java index 5bc7eb5bf..a995d5b1e 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/jobs/ScheduledTootsSyncJob.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/jobs/ScheduledTootsSyncJob.java @@ -17,7 +17,6 @@ package fr.gouv.etalab.mastodon.jobs; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.support.annotation.NonNull; -import android.util.Log; import com.evernote.android.job.Job; import com.evernote.android.job.JobRequest; diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/jobs/StreamingSyncJob.java b/app/src/main/java/fr/gouv/etalab/mastodon/jobs/StreamingSyncJob.java index 3fa255c26..b1b093de3 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/jobs/StreamingSyncJob.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/jobs/StreamingSyncJob.java @@ -14,55 +14,15 @@ package fr.gouv.etalab.mastodon.jobs; * You should have received a copy of the GNU General Public License along with Mastalab; if not, * see . */ -import android.content.Context; import android.content.Intent; -import android.content.SharedPreferences; -import android.database.sqlite.SQLiteDatabase; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.os.AsyncTask; -import android.os.Bundle; import android.support.annotation.NonNull; -import android.support.v4.content.LocalBroadcastManager; -import android.view.View; - import com.evernote.android.job.Job; import com.evernote.android.job.JobManager; import com.evernote.android.job.JobRequest; -import com.nostra13.universalimageloader.cache.disc.impl.UnlimitedDiskCache; -import com.nostra13.universalimageloader.core.DisplayImageOptions; -import com.nostra13.universalimageloader.core.ImageLoader; -import com.nostra13.universalimageloader.core.ImageLoaderConfiguration; -import com.nostra13.universalimageloader.core.assist.FailReason; -import com.nostra13.universalimageloader.core.display.SimpleBitmapDisplayer; -import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener; - -import org.json.JSONException; -import org.json.JSONObject; - -import java.io.File; -import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; - -import fr.gouv.etalab.mastodon.activities.MainActivity; -import fr.gouv.etalab.mastodon.asynctasks.StreamingUserAsyncTask; -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.PatchBaseImageDownloader; import fr.gouv.etalab.mastodon.helper.Helper; -import fr.gouv.etalab.mastodon.interfaces.OnRetrieveStreamingInterface; -import fr.gouv.etalab.mastodon.sqlite.AccountDAO; -import fr.gouv.etalab.mastodon.sqlite.Sqlite; -import mastodon.etalab.gouv.fr.mastodon.R; - -import static fr.gouv.etalab.mastodon.helper.Helper.HOME_TIMELINE_INTENT; -import static fr.gouv.etalab.mastodon.helper.Helper.INTENT_ACTION; -import static fr.gouv.etalab.mastodon.helper.Helper.NOTIFICATION_INTENT; -import static fr.gouv.etalab.mastodon.helper.Helper.PREF_KEY_ID; -import static fr.gouv.etalab.mastodon.helper.Helper.notify_user; +import fr.gouv.etalab.mastodon.services.StreamingService; /** @@ -70,18 +30,16 @@ import static fr.gouv.etalab.mastodon.helper.Helper.notify_user; * Notifications refresh job */ -public class StreamingSyncJob extends Job implements OnRetrieveStreamingInterface { +public class StreamingSyncJob extends Job { static final String STREAMING = "job_streaming"; - private String message; - private int notificationId; - private Intent intent; + @NonNull @Override protected Result onRunJob(Params params) { //Code refresh here - callAsynchronousTask(); + getContext().startService(new Intent(getContext(), StreamingService.class)); return Result.SUCCESS; } @@ -92,9 +50,8 @@ public class StreamingSyncJob extends Job implements OnRetrieveStreamingInterfac if (!jobRequests.isEmpty() && !updateCurrent) { return jobRequests.iterator().next().getJobId(); } - return new JobRequest.Builder(StreamingSyncJob.STREAMING) - .setPeriodic(TimeUnit.MINUTES.toMillis(Helper.MINUTES_BETWEEN_NOTIFICATIONS_REFRESH), TimeUnit.MINUTES.toMillis(5)) + .setPeriodic(TimeUnit.MINUTES.toMillis(Helper.MINUTES_BETWEEN_STREAMING_CHECK_ALIVE), TimeUnit.MINUTES.toMillis(5)) .setPersisted(true) .setUpdateCurrent(updateCurrent) .setRequiredNetworkType(JobRequest.NetworkType.CONNECTED) @@ -103,222 +60,4 @@ public class StreamingSyncJob extends Job implements OnRetrieveStreamingInterfac .schedule(); } - - - /** - * Task in background starts here. - */ - private void callAsynchronousTask() { - - SQLiteDatabase db = Sqlite.getInstance(getContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); - //If an Internet connection and user agrees with notification refresh - final SharedPreferences sharedpreferences = getContext().getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); - //Check which notifications the user wants to see - boolean notif_follow = sharedpreferences.getBoolean(Helper.SET_NOTIF_FOLLOW, true); - boolean notif_add = sharedpreferences.getBoolean(Helper.SET_NOTIF_ADD, true); - 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); - //User disagree with all notifications - if( !notif_follow && !notif_add && !notif_ask && !notif_mention && !notif_share) - return; //Nothing is done - //No account connected, the service is stopped - if(!Helper.isLoggedIn(getContext())) - return; - //If WIFI only and on WIFI OR user defined any connections to use the service. - if(!sharedpreferences.getBoolean(Helper.SET_WIFI_ONLY, false) || Helper.isOnWIFI(getContext())) { - List accounts = new AccountDAO(getContext(),db).getAllAccount(); - //It means there is no user in DB. - if( accounts == null ) - return; - //Retrieve users in db that owner has. - for (Account account: accounts) { - new StreamingUserAsyncTask(account.getInstance(), account.getToken(), account.getAcct(), account.getId(), StreamingSyncJob.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - } - } - - - @Override - public void onRetrieveStreaming(StreamingUserAsyncTask.EventStreaming event, JSONObject response, String acct, String userId) { - if( response == null ) - return; - String max_id = null; - final SharedPreferences sharedpreferences = getContext().getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); - boolean notif_follow = sharedpreferences.getBoolean(Helper.SET_NOTIF_FOLLOW, true); - boolean notif_add = sharedpreferences.getBoolean(Helper.SET_NOTIF_ADD, true); - boolean notif_mention = sharedpreferences.getBoolean(Helper.SET_NOTIF_MENTION, true); - boolean notif_share = sharedpreferences.getBoolean(Helper.SET_NOTIF_SHARE, true); - - //No previous notifications in cache, so no notification will be sent - boolean notify = false; - - String notificationUrl = null; - String title = null; - Status status = null; - Notification notification = null; - String dataId = null; - if( event == StreamingUserAsyncTask.EventStreaming.NOTIFICATION){ - notification = API.parseNotificationResponse(getContext(), response); - max_id = notification.getId(); - switch (notification.getType()){ - case "mention": - if(notif_mention){ - notify = true; - notificationUrl = notification.getAccount().getAvatar(); - if( notification.getAccount().getDisplay_name() != null && notification.getAccount().getDisplay_name().length() > 0 ) - title = String.format("@%s %s", Helper.shortnameToUnicode(notification.getAccount().getDisplay_name(), true),getContext().getString(R.string.notif_mention)); - else - title = String.format("@%s %s", notification.getAccount().getUsername(),getContext().getString(R.string.notif_mention)); - } - break; - case "reblog": - if(notif_share){ - notify = true; - notificationUrl = notification.getAccount().getAvatar(); - if( notification.getAccount().getDisplay_name() != null && notification.getAccount().getDisplay_name().length() > 0 ) - title = String.format("@%s %s", Helper.shortnameToUnicode(notification.getAccount().getDisplay_name(), true),getContext().getString(R.string.notif_reblog)); - else - title = String.format("@%s %s", notification.getAccount().getUsername(),getContext().getString(R.string.notif_reblog)); - } - break; - case "favourite": - if(notif_add){ - notify = true; - notificationUrl = notification.getAccount().getAvatar(); - if( notification.getAccount().getDisplay_name() != null && notification.getAccount().getDisplay_name().length() > 0 ) - title = String.format("@%s %s", Helper.shortnameToUnicode(notification.getAccount().getDisplay_name(), true),getContext().getString(R.string.notif_favourite)); - else - title = String.format("@%s %s", notification.getAccount().getUsername(),getContext().getString(R.string.notif_favourite)); - } - break; - case "follow": - if(notif_follow){ - notify = true; - notificationUrl = notification.getAccount().getAvatar(); - if( notification.getAccount().getDisplay_name() != null && notification.getAccount().getDisplay_name().length() > 0 ) - title = String.format("@%s %s", Helper.shortnameToUnicode(notification.getAccount().getDisplay_name(), true),getContext().getString(R.string.notif_follow)); - else - title = String.format("@%s %s", notification.getAccount().getUsername(),getContext().getString(R.string.notif_follow)); - } - break; - default: - break; - } - SharedPreferences.Editor editor = sharedpreferences.edit(); - editor.putString(Helper.LAST_NOTIFICATION_MAX_ID + userId, notification.getId()); - editor.apply(); - message = notification.getStatus().getContent(); - if( message!= null) { - message = message.substring(0, 17); - message = message + "…"; - }else{ - message = ""; - } - }else if ( event == StreamingUserAsyncTask.EventStreaming.UPDATE){ - status = API.parseStatuses(getContext(), response); - SharedPreferences.Editor editor = sharedpreferences.edit(); - editor.putString(Helper.LAST_NOTIFICATION_MAX_ID + userId, status.getId()); - editor.apply(); - message = status.getContent(); - if( message!= null) { - message = message.substring(0, 17); - message = message + "…"; - }else{ - message = ""; - } - }else if( event == StreamingUserAsyncTask.EventStreaming.DELETE){ - try { - dataId = response.getString("id"); - - } catch (JSONException e) { - e.printStackTrace(); - } - - } - if( max_id != null){ - SharedPreferences.Editor editor = sharedpreferences.edit(); - editor.putString(Helper.LAST_NOTIFICATION_MAX_ID + userId, max_id); - editor.apply(); - return; - } - - //Check which user is connected and if activity is to front - boolean activityVisible = false; - try{ - activityVisible = MainActivity.isActivityVisible(); - }catch (Exception ignored){} - String userconnected = sharedpreferences.getString(Helper.PREF_KEY_ID, null); - SQLiteDatabase db = Sqlite.getInstance(getContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); - Account account = new AccountDAO(getContext(), db).getAccountByID(userconnected); - //User receiving the notification is connected and application is to front, notification won't be pushed - //Instead, the interaction is done in the activity - if( activityVisible && account != null && account.getAcct().trim().equals(acct.trim()) && account.getId().trim().equals(userId.trim())){ - notify = false; - Intent intentBC = new Intent(Helper.RECEIVE_DATA); - intent.putExtra("eventStreaming", event); - Bundle b = new Bundle(); - if( event == StreamingUserAsyncTask.EventStreaming.UPDATE) - b.putParcelable("data", status); - else if(event == StreamingUserAsyncTask.EventStreaming.NOTIFICATION) - b.putParcelable("data", notification); - else if(event == StreamingUserAsyncTask.EventStreaming.DELETE) - b.putString("id", dataId); - intentBC.putExtras(b); - LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intentBC); - }else if(event == StreamingUserAsyncTask.EventStreaming.NOTIFICATION ){ - notify = true; - intent = new Intent(getContext(), MainActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK ); - intent.putExtra(INTENT_ACTION, NOTIFICATION_INTENT); - intent.putExtra(PREF_KEY_ID, userId); - long notif_id = Long.parseLong(userId); - notificationId = ((notif_id + 1) > 2147483647) ? (int) (2147483647 - notif_id - 1) : (int) (notif_id + 1); - - - }else if(event == StreamingUserAsyncTask.EventStreaming.UPDATE ){ - if(acct != null && status.getAccount().getAcct().trim().equals(acct.trim())){ - notify = false; - }else { - notify = true; - intent = new Intent(getContext(), MainActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); - intent.putExtra(INTENT_ACTION, HOME_TIMELINE_INTENT); - intent.putExtra(PREF_KEY_ID, userId); - long notif_id = Long.parseLong(userId); - notificationId = ((notif_id + 2) > 2147483647) ? (int) (2147483647 - notif_id - 2) : (int) (notif_id + 2); - } - } - - if( notify){ - if( notificationUrl != null){ - ImageLoader imageLoaderNoty = ImageLoader.getInstance(); - File cacheDir = new File(getContext().getCacheDir(), getContext().getString(R.string.app_name)); - ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getContext()) - .imageDownloader(new PatchBaseImageDownloader(getContext())) - .threadPoolSize(5) - .threadPriority(Thread.MIN_PRIORITY + 3) - .denyCacheImageMultipleSizesInMemory() - .diskCache(new UnlimitedDiskCache(cacheDir)) - .build(); - imageLoaderNoty.init(config); - DisplayImageOptions options = new DisplayImageOptions.Builder().displayer(new SimpleBitmapDisplayer()).cacheInMemory(false) - .cacheOnDisk(true).resetViewBeforeLoading(true).build(); - - final String finalTitle = title; - imageLoaderNoty.loadImage(notificationUrl, options, new SimpleImageLoadingListener(){ - @Override - public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { - super.onLoadingComplete(imageUri, view, loadedImage); - notify_user(getContext(), intent, notificationId, loadedImage, finalTitle, message); - - } - @Override - public void onLoadingFailed(String imageUri, View view, FailReason failReason){ - notify_user(getContext(), intent, notificationId, BitmapFactory.decodeResource(getContext().getResources(), - R.drawable.mastodonlogo), finalTitle, message); - }}); - } - } - } } diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/services/StreamingService.java b/app/src/main/java/fr/gouv/etalab/mastodon/services/StreamingService.java new file mode 100644 index 000000000..7144da304 --- /dev/null +++ b/app/src/main/java/fr/gouv/etalab/mastodon/services/StreamingService.java @@ -0,0 +1,314 @@ +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.Service; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.database.sqlite.SQLiteDatabase; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.os.AsyncTask; +import android.os.Build; +import android.os.Bundle; +import android.os.IBinder; +import android.support.annotation.Nullable; +import android.support.v4.content.LocalBroadcastManager; +import android.text.Html; +import android.view.View; + +import com.nostra13.universalimageloader.cache.disc.impl.UnlimitedDiskCache; +import com.nostra13.universalimageloader.core.DisplayImageOptions; +import com.nostra13.universalimageloader.core.ImageLoader; +import com.nostra13.universalimageloader.core.ImageLoaderConfiguration; +import com.nostra13.universalimageloader.core.assist.FailReason; +import com.nostra13.universalimageloader.core.display.SimpleBitmapDisplayer; +import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.File; +import java.util.List; + +import fr.gouv.etalab.mastodon.activities.MainActivity; +import fr.gouv.etalab.mastodon.asynctasks.StreamingUserAsyncTask; +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.PatchBaseImageDownloader; +import fr.gouv.etalab.mastodon.helper.Helper; +import fr.gouv.etalab.mastodon.interfaces.OnRetrieveStreamingInterface; +import fr.gouv.etalab.mastodon.jobs.StreamingSyncJob; +import fr.gouv.etalab.mastodon.sqlite.AccountDAO; +import fr.gouv.etalab.mastodon.sqlite.Sqlite; +import mastodon.etalab.gouv.fr.mastodon.R; + +import static fr.gouv.etalab.mastodon.helper.Helper.HOME_TIMELINE_INTENT; +import static fr.gouv.etalab.mastodon.helper.Helper.INTENT_ACTION; +import static fr.gouv.etalab.mastodon.helper.Helper.NOTIFICATION_INTENT; +import static fr.gouv.etalab.mastodon.helper.Helper.PREF_KEY_ID; +import static fr.gouv.etalab.mastodon.helper.Helper.notify_user; + +/** + * Created by Thomas on 28/08/2017. + */ + +public class StreamingService extends Service implements OnRetrieveStreamingInterface { + + private String message; + private int notificationId; + private Intent intent; + + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + callAsynchronousTask(); + return START_NOT_STICKY; + } + + @Nullable + @Override + public IBinder onBind(Intent intent) { + return null; + } + + + /** + * Task in background starts here. + */ + private void callAsynchronousTask() { + SQLiteDatabase db = Sqlite.getInstance(getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); + //If an Internet connection and user agrees with notification refresh + final SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + //Check which notifications the user wants to see + boolean notif_follow = sharedpreferences.getBoolean(Helper.SET_NOTIF_FOLLOW, true); + boolean notif_add = sharedpreferences.getBoolean(Helper.SET_NOTIF_ADD, true); + 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); + //User disagree with all notifications + if( !notif_follow && !notif_add && !notif_ask && !notif_mention && !notif_share) + return; //Nothing is done + //No account connected, the service is stopped + if(!Helper.isLoggedIn(getApplicationContext())) + return; + //If WIFI only and on WIFI OR user defined any connections to use the service. + if(!sharedpreferences.getBoolean(Helper.SET_WIFI_ONLY, false) || Helper.isOnWIFI(getApplicationContext())) { + List accounts = new AccountDAO(getApplicationContext(),db).getAllAccount(); + //It means there is no user in DB. + if( accounts == null ) + return; + //Retrieve users in db that owner has. + for (Account account: accounts) { + new StreamingUserAsyncTask(account.getInstance(), account.getToken(), account.getAcct(), account.getId(), StreamingService.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + } + } + + + @Override + public void onRetrieveStreaming(StreamingUserAsyncTask.EventStreaming event, JSONObject response, String acct, String userId) { + if( response == null ) + return; + String max_id = null; + final SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + boolean notif_follow = sharedpreferences.getBoolean(Helper.SET_NOTIF_FOLLOW, true); + boolean notif_add = sharedpreferences.getBoolean(Helper.SET_NOTIF_ADD, true); + boolean notif_mention = sharedpreferences.getBoolean(Helper.SET_NOTIF_MENTION, true); + boolean notif_share = sharedpreferences.getBoolean(Helper.SET_NOTIF_SHARE, true); + + //No previous notifications in cache, so no notification will be sent + boolean notify = false; + + String notificationUrl = null; + String title = null; + Status status = null; + Notification notification = null; + String dataId = null; + if( event == StreamingUserAsyncTask.EventStreaming.NOTIFICATION){ + notification = API.parseNotificationResponse(getApplicationContext(), response); + max_id = notification.getId(); + switch (notification.getType()){ + case "mention": + if(notif_mention){ + notify = true; + notificationUrl = notification.getAccount().getAvatar(); + if( notification.getAccount().getDisplay_name() != null && notification.getAccount().getDisplay_name().length() > 0 ) + title = String.format("@%s %s", Helper.shortnameToUnicode(notification.getAccount().getDisplay_name(), true),getString(R.string.notif_mention)); + else + title = String.format("@%s %s", notification.getAccount().getUsername(),getString(R.string.notif_mention)); + } + break; + case "reblog": + if(notif_share){ + notify = true; + notificationUrl = notification.getAccount().getAvatar(); + if( notification.getAccount().getDisplay_name() != null && notification.getAccount().getDisplay_name().length() > 0 ) + title = String.format("@%s %s", Helper.shortnameToUnicode(notification.getAccount().getDisplay_name(), true),getString(R.string.notif_reblog)); + else + title = String.format("@%s %s", notification.getAccount().getUsername(),getString(R.string.notif_reblog)); + } + break; + case "favourite": + if(notif_add){ + notify = true; + notificationUrl = notification.getAccount().getAvatar(); + if( notification.getAccount().getDisplay_name() != null && notification.getAccount().getDisplay_name().length() > 0 ) + title = String.format("@%s %s", Helper.shortnameToUnicode(notification.getAccount().getDisplay_name(), true),getString(R.string.notif_favourite)); + else + title = String.format("@%s %s", notification.getAccount().getUsername(),getString(R.string.notif_favourite)); + } + break; + case "follow": + if(notif_follow){ + notify = true; + notificationUrl = notification.getAccount().getAvatar(); + if( notification.getAccount().getDisplay_name() != null && notification.getAccount().getDisplay_name().length() > 0 ) + title = String.format("@%s %s", Helper.shortnameToUnicode(notification.getAccount().getDisplay_name(), true),getString(R.string.notif_follow)); + else + title = String.format("@%s %s", notification.getAccount().getUsername(),getString(R.string.notif_follow)); + } + break; + default: + break; + } + SharedPreferences.Editor editor = sharedpreferences.edit(); + editor.putString(Helper.LAST_NOTIFICATION_MAX_ID + userId, notification.getId()); + editor.apply(); + if( notification.getStatus().getContent()!= null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) + message = Html.fromHtml(notification.getStatus().getContent(), Html.FROM_HTML_MODE_LEGACY).toString(); + else + //noinspection deprecation + message = Html.fromHtml(notification.getStatus().getContent()).toString(); + message = message.substring(0, message.length()>49?49:message.length()); + message = message + "…"; + }else{ + message = ""; + } + }else if ( event == StreamingUserAsyncTask.EventStreaming.UPDATE){ + status = API.parseStatuses(getApplicationContext(), response); + SharedPreferences.Editor editor = sharedpreferences.edit(); + editor.putString(Helper.LAST_NOTIFICATION_MAX_ID + userId, status.getId()); + editor.apply(); + if( status.getContent() != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) + message = Html.fromHtml(status.getContent(), Html.FROM_HTML_MODE_LEGACY).toString(); + else + //noinspection deprecation + message = Html.fromHtml(status.getContent()).toString(); + message = message.substring(0, message.length()>49?49:message.length()); + message = message + "…"; + }else{ + message = ""; + } + title = getString(R.string.notif_pouet, status.getAccount().getUsername()); + notificationUrl = status.getAccount().getAvatar(); + }else if( event == StreamingUserAsyncTask.EventStreaming.DELETE){ + try { + dataId = response.getString("id"); + + } catch (JSONException e) { + e.printStackTrace(); + } + + } + if( max_id != null){ + SharedPreferences.Editor editor = sharedpreferences.edit(); + editor.putString(Helper.LAST_NOTIFICATION_MAX_ID + userId, max_id); + editor.apply(); + return; + } + + //Check which user is connected and if activity is to front + boolean activityVisible = false; + try{ + activityVisible = MainActivity.isActivityVisible(); + }catch (Exception ignored){} + String userconnected = sharedpreferences.getString(Helper.PREF_KEY_ID, null); + SQLiteDatabase db = Sqlite.getInstance(getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); + Account account = new AccountDAO(getApplicationContext(), db).getAccountByID(userconnected); + //User receiving the notification is connected and application is to front, notification won't be pushed + //Instead, the interaction is done in the activity + if( activityVisible && account != null && !account.getAcct().trim().equals(acct.trim()) && !account.getId().trim().equals(userId.trim())){ + notify = false; + Intent intentBC = new Intent(Helper.RECEIVE_DATA); + intentBC.putExtra("eventStreaming", event); + Bundle b = new Bundle(); + if( event == StreamingUserAsyncTask.EventStreaming.UPDATE) + b.putParcelable("data", status); + else if(event == StreamingUserAsyncTask.EventStreaming.NOTIFICATION) + b.putParcelable("data", notification); + else if(event == StreamingUserAsyncTask.EventStreaming.DELETE) + b.putString("id", dataId); + intentBC.putExtras(b); + LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intentBC); + }else if(event == StreamingUserAsyncTask.EventStreaming.NOTIFICATION ){ + notify = true; + intent = new Intent(getApplicationContext(), MainActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK ); + intent.putExtra(INTENT_ACTION, NOTIFICATION_INTENT); + intent.putExtra(PREF_KEY_ID, userId); + long notif_id = Long.parseLong(userId); + notificationId = ((notif_id + 1) > 2147483647) ? (int) (2147483647 - notif_id - 1) : (int) (notif_id + 1); + + + }else if(event == StreamingUserAsyncTask.EventStreaming.UPDATE ){ + if(account.getAcct().trim().equals(acct.trim()) && account.getId().trim().equals(userId.trim())){ + notify = false; + }else { + notify = true; + intent = new Intent(getApplicationContext(), MainActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra(INTENT_ACTION, HOME_TIMELINE_INTENT); + intent.putExtra(PREF_KEY_ID, userId); + long notif_id = Long.parseLong(userId); + notificationId = ((notif_id + 2) > 2147483647) ? (int) (2147483647 - notif_id - 2) : (int) (notif_id + 2); + } + } + if( notify){ + if( notificationUrl != null){ + ImageLoader imageLoaderNoty = ImageLoader.getInstance(); + File cacheDir = new File(getApplicationContext().getCacheDir(), getString(R.string.app_name)); + ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getApplicationContext()) + .imageDownloader(new PatchBaseImageDownloader(getApplicationContext())) + .threadPoolSize(5) + .threadPriority(Thread.MIN_PRIORITY + 3) + .denyCacheImageMultipleSizesInMemory() + .diskCache(new UnlimitedDiskCache(cacheDir)) + .build(); + imageLoaderNoty.init(config); + DisplayImageOptions options = new DisplayImageOptions.Builder().displayer(new SimpleBitmapDisplayer()).cacheInMemory(false) + .cacheOnDisk(true).resetViewBeforeLoading(true).build(); + + final String finalTitle = title; + imageLoaderNoty.loadImage(notificationUrl, options, new SimpleImageLoadingListener(){ + @Override + public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { + super.onLoadingComplete(imageUri, view, loadedImage); + notify_user(getApplicationContext(), intent, notificationId, loadedImage, finalTitle, message); + + } + @Override + public void onLoadingFailed(String imageUri, View view, FailReason failReason){ + notify_user(getApplicationContext(), intent, notificationId, BitmapFactory.decodeResource(getApplicationContext().getResources(), + R.drawable.mastodonlogo), finalTitle, message); + }}); + } + } + } +} 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 144ee2080..79c9453b1 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 @@ -86,6 +86,7 @@ import fr.gouv.etalab.mastodon.fragments.DisplayNotificationsFragment; import fr.gouv.etalab.mastodon.fragments.DisplayScheduledTootsFragment; import fr.gouv.etalab.mastodon.helper.Helper; import fr.gouv.etalab.mastodon.interfaces.OnUpdateAccountInfoInterface; +import fr.gouv.etalab.mastodon.services.StreamingService; import fr.gouv.etalab.mastodon.sqlite.Sqlite; import fr.gouv.etalab.mastodon.asynctasks.RetrieveAccountsAsyncTask; import fr.gouv.etalab.mastodon.asynctasks.RetrieveFeedsAsyncTask; @@ -139,18 +140,18 @@ public class MainActivity extends AppCompatActivity @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - LocalBroadcastManager.getInstance(this).registerReceiver(receive_data, new IntentFilter(Helper.RECEIVE_DATA)); newNotif = 0; newHome = 0; + receive_data = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - Bundle b = getIntent().getExtras(); - SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + Bundle b = intent.getExtras(); StreamingUserAsyncTask.EventStreaming eventStreaming = (StreamingUserAsyncTask.EventStreaming) intent.getSerializableExtra("eventStreaming"); + if( eventStreaming == StreamingUserAsyncTask.EventStreaming.NOTIFICATION){ Notification notification = b.getParcelable("data"); - if(notificationsFragment.getUserVisibleHint()){ + if(notificationsFragment != null && notificationsFragment.getUserVisibleHint()){ notificationsFragment.updateData(notification); }else{ newNotif++; @@ -158,7 +159,7 @@ public class MainActivity extends AppCompatActivity } }else if(eventStreaming == StreamingUserAsyncTask.EventStreaming.UPDATE){ Status status = b.getParcelable("data"); - if(homeFragment.getUserVisibleHint()){ + if(homeFragment != null && homeFragment.getUserVisibleHint()){ homeFragment.updateData(status); }else{ newHome++; @@ -174,7 +175,7 @@ public class MainActivity extends AppCompatActivity } } }; - + LocalBroadcastManager.getInstance(this).registerReceiver(receive_data, new IntentFilter(Helper.RECEIVE_DATA)); ProviderInstaller.installIfNeededAsync(this, this); @@ -196,7 +197,7 @@ public class MainActivity extends AppCompatActivity finish(); return; } - + startService(new Intent(getApplicationContext(), StreamingService.class)); Helper.fillMapEmoji(getApplicationContext()); //Here, the user is authenticated Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); From e3b031940dfe70c2398d87f77f5f1590a4640e60 Mon Sep 17 00:00:00 2001 From: tom79 Date: Mon, 28 Aug 2017 19:12:40 +0200 Subject: [PATCH 7/8] Last changes --- .../DisplayNotificationsFragment.java | 17 +++++++++++++++++ .../fragments/DisplayStatusFragment.java | 18 ++++++++++++++++++ .../activities/MainActivity.java | 6 ++++++ 3 files changed, 41 insertions(+) diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/fragments/DisplayNotificationsFragment.java b/app/src/main/java/fr/gouv/etalab/mastodon/fragments/DisplayNotificationsFragment.java index d276e9b48..45b374f46 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/fragments/DisplayNotificationsFragment.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/fragments/DisplayNotificationsFragment.java @@ -255,4 +255,21 @@ public class DisplayNotificationsFragment extends Fragment implements OnRetrieve } new_data.setVisibility(View.VISIBLE); } + public void refresh(){ + if( notificationsTmp != null){ + boolean isOnWifi = Helper.isOnWIFI(context); + final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + int behaviorWithAttachments = sharedpreferences.getInt(Helper.SET_ATTACHMENT_ACTION, Helper.ATTACHMENT_ALWAYS); + notifications = new ArrayList<>(); + for(Notification notification: notificationsTmp){ + notifications.add(notification); + } + notificationsListAdapter = new NotificationsListAdapter(context,isOnWifi, behaviorWithAttachments, notifications); + lv_notifications.setAdapter(notificationsListAdapter); + if( notificationsTmp.size() > 0 && textviewNoAction.getVisibility() == View.VISIBLE) + textviewNoAction.setVisibility(View.GONE); + } + new_data.setVisibility(View.GONE); + notificationsTmp = new ArrayList<>(); + } } diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/fragments/DisplayStatusFragment.java b/app/src/main/java/fr/gouv/etalab/mastodon/fragments/DisplayStatusFragment.java index f3df16cab..400df36f5 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/fragments/DisplayStatusFragment.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/fragments/DisplayStatusFragment.java @@ -359,6 +359,24 @@ public class DisplayStatusFragment extends Fragment implements OnRetrieveFeedsIn new_data.setVisibility(View.VISIBLE); } + public void refresh(){ + if( statusesTmp != null){ + boolean isOnWifi = Helper.isOnWIFI(context); + final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + int behaviorWithAttachments = sharedpreferences.getInt(Helper.SET_ATTACHMENT_ACTION, Helper.ATTACHMENT_ALWAYS); + statuses = new ArrayList<>(); + for(Status status: statusesTmp){ + statuses.add(status); + } + if( statusesTmp.size() > 0 && textviewNoAction.getVisibility() == View.VISIBLE) + textviewNoAction.setVisibility(View.GONE); + statusListAdapter = new StatusListAdapter(context, type, targetedId, isOnWifi, behaviorWithAttachments, positionSpinnerTrans, statuses); + lv_status.setAdapter(statusListAdapter); + statusesTmp = new ArrayList<>(); + } + new_data.setVisibility(View.GONE); + } + public void scrollToTop(){ if( lv_status != null) lv_status.setAdapter(statusListAdapter); 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 79c9453b1..b828d14e8 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 @@ -156,6 +156,7 @@ public class MainActivity extends AppCompatActivity }else{ newNotif++; updateNotifCounter(); + notificationsFragment.updateData(notification); } }else if(eventStreaming == StreamingUserAsyncTask.EventStreaming.UPDATE){ Status status = b.getParcelable("data"); @@ -164,6 +165,7 @@ public class MainActivity extends AppCompatActivity }else{ newHome++; updateHomeCounter(); + homeFragment.updateData(status); } }else if(eventStreaming == StreamingUserAsyncTask.EventStreaming.DELETE){ String id = b.getString("id"); @@ -278,12 +280,16 @@ public class MainActivity extends AppCompatActivity item = navigationView.getMenu().findItem(R.id.nav_home); fragmentTag = "HOME_TIMELINE"; newHome = 0; + if( homeFragment != null) + homeFragment.refresh(); updateHomeCounter(); break; case 1: fragmentTag = "NOTIFICATIONS"; item = navigationView.getMenu().findItem(R.id.nav_notification); newNotif = 0; + if( notificationsFragment != null) + notificationsFragment.refresh(); updateNotifCounter(); break; case 2: From 03ba9a2b883d97487a448ad8dae6d9d652acc96a Mon Sep 17 00:00:00 2001 From: tom79 Date: Mon, 28 Aug 2017 19:25:19 +0200 Subject: [PATCH 8/8] Applies changes for fdroid product flavour --- .../activities/MainActivity.java | 86 +++++++++++-------- .../asynctasks/StreamingUserAsyncTask.java | 2 +- .../activities/MainActivity.java | 2 +- 3 files changed, 51 insertions(+), 39 deletions(-) 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 f7bb0cfd6..a31d8b211 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 @@ -15,9 +15,11 @@ package fr.gouv.etalab.mastodon.activities; import android.annotation.SuppressLint; +import android.content.BroadcastReceiver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; +import android.content.IntentFilter; import android.content.SharedPreferences; import android.database.sqlite.SQLiteDatabase; import android.graphics.PorterDuff; @@ -31,6 +33,7 @@ import android.support.design.widget.TabLayout; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.content.ContextCompat; +import android.support.v4.content.LocalBroadcastManager; import android.support.v4.view.ViewPager; import android.support.v7.app.AlertDialog; import android.support.v7.widget.SearchView; @@ -67,8 +70,11 @@ import java.util.Locale; import java.util.Stack; import java.util.concurrent.TimeUnit; +import fr.gouv.etalab.mastodon.asynctasks.StreamingUserAsyncTask; import fr.gouv.etalab.mastodon.asynctasks.UpdateAccountInfoByIDAsyncTask; 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.PatchBaseImageDownloader; import fr.gouv.etalab.mastodon.fragments.DisplayAccountsFragment; import fr.gouv.etalab.mastodon.fragments.DisplayFollowRequestSentFragment; @@ -76,6 +82,7 @@ import fr.gouv.etalab.mastodon.fragments.DisplayNotificationsFragment; import fr.gouv.etalab.mastodon.fragments.DisplayScheduledTootsFragment; import fr.gouv.etalab.mastodon.helper.Helper; import fr.gouv.etalab.mastodon.interfaces.OnUpdateAccountInfoInterface; +import fr.gouv.etalab.mastodon.services.StreamingService; import fr.gouv.etalab.mastodon.sqlite.Sqlite; import fr.gouv.etalab.mastodon.asynctasks.RetrieveAccountsAsyncTask; import fr.gouv.etalab.mastodon.asynctasks.RetrieveFeedsAsyncTask; @@ -127,7 +134,42 @@ public class MainActivity extends AppCompatActivity @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + receive_data = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + Bundle b = intent.getExtras(); + StreamingUserAsyncTask.EventStreaming eventStreaming = (StreamingUserAsyncTask.EventStreaming) intent.getSerializableExtra("eventStreaming"); + + if( eventStreaming == StreamingUserAsyncTask.EventStreaming.NOTIFICATION){ + Notification notification = b.getParcelable("data"); + if(notificationsFragment != null && notificationsFragment.getUserVisibleHint()){ + notificationsFragment.updateData(notification); + }else{ + newNotif++; + updateNotifCounter(); + notificationsFragment.updateData(notification); + } + }else if(eventStreaming == StreamingUserAsyncTask.EventStreaming.UPDATE){ + Status status = b.getParcelable("data"); + if(homeFragment != null && homeFragment.getUserVisibleHint()){ + homeFragment.updateData(status); + }else{ + newHome++; + updateHomeCounter(); + homeFragment.updateData(status); + } + }else if(eventStreaming == StreamingUserAsyncTask.EventStreaming.DELETE){ + String id = b.getString("id"); + if(notificationsFragment != null && notificationsFragment.getUserVisibleHint()){ + + }else{ + + } + } + } + }; LocalBroadcastManager.getInstance(this).registerReceiver(receive_data, new IntentFilter(Helper.RECEIVE_DATA)); + newNotif = 0; newHome = 0; @@ -149,7 +191,7 @@ public class MainActivity extends AppCompatActivity finish(); return; } - + startService(new Intent(getApplicationContext(), StreamingService.class)); Helper.fillMapEmoji(getApplicationContext()); //Here, the user is authenticated Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); @@ -229,18 +271,19 @@ public class MainActivity extends AppCompatActivity case 0: item = navigationView.getMenu().findItem(R.id.nav_home); fragmentTag = "HOME_TIMELINE"; - if( bubbles && homeFragment != null) - homeFragment.refreshData(); newHome = 0; + if( homeFragment != null) + homeFragment.refresh(); updateHomeCounter(); break; case 1: fragmentTag = "NOTIFICATIONS"; item = navigationView.getMenu().findItem(R.id.nav_notification); newNotif = 0; + + if( notificationsFragment != null) + notificationsFragment.refresh(); updateNotifCounter(); - if( bubbles && notificationsFragment != null) - notificationsFragment.refreshData(); break; case 2: fragmentTag = "LOCAL_TIMELINE"; @@ -810,6 +853,7 @@ public class MainActivity extends AppCompatActivity @Override public void onResume(){ super.onResume(); + MainActivity.activityResumed(); //Proceeds to update of the authenticated account if(Helper.isLoggedIn(getApplicationContext())) new UpdateAccountInfoByIDAsyncTask(getApplicationContext(), MainActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); @@ -1013,38 +1057,6 @@ public class MainActivity extends AppCompatActivity } } - private void refreshData(){ - final SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, android.content.Context.MODE_PRIVATE); - - String prefKeyOauthTokenT = sharedpreferences.getString(Helper.PREF_KEY_OAUTH_TOKEN, null); - SQLiteDatabase db = Sqlite.getInstance(getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); - Account account = new AccountDAO(getApplicationContext(), db).getAccountByToken(prefKeyOauthTokenT); - if( account != null){ - String last_refresh = sharedpreferences.getString(Helper.LAST_BUBBLE_REFRESH_NOTIF + account.getId(), null); - Date last_refresh_date = Helper.stringToDate(getApplicationContext(), last_refresh); - if (last_refresh_date == null || (new Date().getTime() - last_refresh_date.getTime()) >= TimeUnit.SECONDS.toMillis(60)) { - - if( notificationsFragment != null ){ - notificationsFragment.update(); - SharedPreferences.Editor editor = sharedpreferences.edit(); - editor.putString(Helper.LAST_BUBBLE_REFRESH_NOTIF+ account.getId(),Helper.dateToString(getApplicationContext(), new Date())); - editor.apply(); - } - } - - last_refresh = sharedpreferences.getString(Helper.LAST_BUBBLE_REFRESH_HOME + account.getId(), null); - last_refresh_date = Helper.stringToDate(getApplicationContext(), last_refresh); - - if (last_refresh_date == null || (new Date().getTime() - last_refresh_date.getTime()) >= TimeUnit.SECONDS.toMillis(60)) { - if( homeFragment != null ){ - homeFragment.update(); - SharedPreferences.Editor editor = sharedpreferences.edit(); - editor.putString(Helper.LAST_BUBBLE_REFRESH_HOME+ account.getId(),Helper.dateToString(getApplicationContext(), new Date())); - editor.apply(); - } - } - } - } public void updateHomeCounter(){ if( tabLayout.getTabAt(0) == null ) diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/StreamingUserAsyncTask.java b/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/StreamingUserAsyncTask.java index 6295d3e27..333ca8a8a 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/StreamingUserAsyncTask.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/StreamingUserAsyncTask.java @@ -136,7 +136,7 @@ public class StreamingUserAsyncTask extends AsyncTask { } } - }catch (IOException e){ + }catch (Exception e){ e.printStackTrace(); }finally { if(reader != null){ 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 b828d14e8..dd0292959 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 @@ -169,7 +169,7 @@ public class MainActivity extends AppCompatActivity } }else if(eventStreaming == StreamingUserAsyncTask.EventStreaming.DELETE){ String id = b.getString("id"); - if(notificationsFragment.getUserVisibleHint()){ + if(notificationsFragment != null && notificationsFragment.getUserVisibleHint()){ }else{