Merged in develop (pull request #124)

Release 1.4.9-beta-7
This commit is contained in:
tom79 2017-09-30 13:44:33 +00:00
commit ba80365503
32 changed files with 1555 additions and 754 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -7,8 +7,8 @@ android {
applicationId "fr.gouv.etalab.mastodon"
minSdkVersion 15
targetSdkVersion 25
versionCode 54
versionName "1.4.9-beta-6"
versionCode 55
versionName "1.4.9-beta-7"
}
buildTypes {
release {

View File

@ -99,8 +99,6 @@ import fr.gouv.etalab.mastodon.fragments.TabLayoutSettingsFragment;
import fr.gouv.etalab.mastodon.sqlite.AccountDAO;
import mastodon.etalab.gouv.fr.mastodon.R;
import static fr.gouv.etalab.mastodon.fragments.DisplayNotificationsFragment.tempNotifications;
import static fr.gouv.etalab.mastodon.fragments.DisplayStatusFragment.tempStatuses;
import static fr.gouv.etalab.mastodon.helper.Helper.CHANGE_THEME_INTENT;
import static fr.gouv.etalab.mastodon.helper.Helper.CHANGE_USER_INTENT;
import static fr.gouv.etalab.mastodon.helper.Helper.HOME_TIMELINE_INTENT;
@ -132,15 +130,15 @@ public class MainActivity extends AppCompatActivity
private RelativeLayout main_app_container;
private Stack<Integer> stackBack = new Stack<>();
private DisplayStatusFragment homeFragment;
private DisplayStatusFragment homeFragment, federatedFragment, localFragment;
private DisplayNotificationsFragment notificationsFragment;
private BroadcastReceiver receive_data;
private BroadcastReceiver receive_data, receive_federated_data, receive_local_data;
private boolean display_local, display_global;
public static int countNewStatus = 0;
public static int countNewNotifications = 0;
private String userIdService;
private Intent streamingIntent;
public static boolean broadCastRegistred = false;
public static String lastHomeId = null, lastNotificationId = null;
public MainActivity() {
}
@ -151,50 +149,6 @@ public class MainActivity extends AppCompatActivity
final SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, android.content.Context.MODE_PRIVATE);
receive_data = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Bundle b = intent.getExtras();
StreamingService.EventStreaming eventStreaming = (StreamingService.EventStreaming) intent.getSerializableExtra("eventStreaming");
userIdService = b.getString("userIdService", null);
String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null);
if( userIdService != null && userIdService.equals(userId)) {
if (eventStreaming == StreamingService.EventStreaming.NOTIFICATION) {
Notification notification = b.getParcelable("data");
if (notificationsFragment != null) {
notificationsFragment.refresh(notification);
} else {
tempNotifications.add(notification);
}
} else if (eventStreaming == StreamingService.EventStreaming.UPDATE) {
Status status = b.getParcelable("data");
if (homeFragment != null) {
homeFragment.refresh(status);
} else {
tempStatuses.add(status);
}
} else if (eventStreaming == StreamingService.EventStreaming.DELETE) {
String id = b.getString("id");
if (notificationsFragment != null) {
if (notificationsFragment.getUserVisibleHint()) {
} else {
}
}
}
updateNotifCounter();
updateHomeCounter();
}
}
};
streamingIntent = new Intent(this, StreamingService.class);
startService(streamingIntent);
if( !broadCastRegistred) {
LocalBroadcastManager.getInstance(this).registerReceiver(receive_data, new IntentFilter(Helper.RECEIVE_DATA));
broadCastRegistred = true;
}
@ -725,7 +679,6 @@ public class MainActivity extends AppCompatActivity
* @param intent Intent - intent related to a notification in top bar
*/
private boolean mamageNewIntent(Intent intent){
if( intent == null || intent.getExtras() == null )
return false;
@ -754,6 +707,12 @@ public class MainActivity extends AppCompatActivity
matchingIntent = true;
}else if( extras.getInt(INTENT_ACTION) == CHANGE_USER_INTENT){
unCheckAllMenuItems(navigationView);
if( tabLayout.getTabAt(0) != null)
//noinspection ConstantConditions
tabLayout.getTabAt(0).select();
if( !toolbar_search.isIconified() ) {
toolbar_search.setIconified(true);
}
matchingIntent = true;
}
}else if( Intent.ACTION_SEND.equals(action) && type != null ) {
@ -896,10 +855,105 @@ public class MainActivity extends AppCompatActivity
//Proceeds to update of the authenticated account
if(Helper.isLoggedIn(getApplicationContext()))
new UpdateAccountInfoByIDAsyncTask(getApplicationContext(), MainActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
if( lastHomeId != null && homeFragment != null){
homeFragment.retrieveMissingToots(lastHomeId);
}
if( lastNotificationId != null && notificationsFragment != null){
notificationsFragment.retrieveMissingNotifications(lastNotificationId);
}
}
@Override
public void onStart(){
super.onStart();
final SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
receive_federated_data = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Bundle b = intent.getExtras();
userIdService = b.getString("userIdService", null);
String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null);
if( userIdService != null && userIdService.equals(userId)) {
Status status = b.getParcelable("data");
if (federatedFragment != null) {
federatedFragment.refresh(status);
}
}
}
};
receive_local_data = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Bundle b = intent.getExtras();
userIdService = b.getString("userIdService", null);
String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null);
if( userIdService != null && userIdService.equals(userId)) {
Status status = b.getParcelable("data");
if (localFragment != null) {
localFragment.refresh(status);
}
}
}
};
receive_data = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Bundle b = intent.getExtras();
StreamingService.EventStreaming eventStreaming = (StreamingService.EventStreaming) intent.getSerializableExtra("eventStreaming");
userIdService = b.getString("userIdService", null);
String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null);
if( userIdService != null && userIdService.equals(userId)) {
if (eventStreaming == StreamingService.EventStreaming.NOTIFICATION) {
Notification notification = b.getParcelable("data");
if (notificationsFragment != null) {
notificationsFragment.refresh(notification);
}
} else if (eventStreaming == StreamingService.EventStreaming.UPDATE) {
Status status = b.getParcelable("data");
if (homeFragment != null) {
homeFragment.refresh(status);
}
} else if (eventStreaming == StreamingService.EventStreaming.DELETE) {
String id = b.getString("id");
if (notificationsFragment != null) {
if (notificationsFragment.getUserVisibleHint()) {
} else {
}
}
}
updateNotifCounter();
updateHomeCounter();
}
}
};
streamingIntent = new Intent(this, StreamingService.class);
startService(streamingIntent);
LocalBroadcastManager.getInstance(this).registerReceiver(receive_data, new IntentFilter(Helper.RECEIVE_DATA));
LocalBroadcastManager.getInstance(this).registerReceiver(receive_federated_data, new IntentFilter(Helper.RECEIVE_FEDERATED_DATA));
LocalBroadcastManager.getInstance(this).registerReceiver(receive_local_data, new IntentFilter(Helper.RECEIVE_LOCAL_DATA));
}
@Override
public void onStop(){
super.onStop();
if( streamingIntent != null) {
SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedpreferences.edit();
String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null);
editor.putBoolean(Helper.SHOULD_CONTINUE_STREAMING_FEDERATED+userId, false);
stopService(streamingIntent);
editor.apply();
}
if( receive_data != null)
LocalBroadcastManager.getInstance(this).unregisterReceiver(receive_data);
if( receive_federated_data != null)
LocalBroadcastManager.getInstance(this).unregisterReceiver(receive_federated_data);
if( receive_local_data != null)
LocalBroadcastManager.getInstance(this).unregisterReceiver(receive_local_data);
}
@Override
protected void onPause() {
@ -910,10 +964,6 @@ public class MainActivity extends AppCompatActivity
@Override
public void onDestroy(){
super.onDestroy();
if( streamingIntent != null)
stopService(streamingIntent);
LocalBroadcastManager.getInstance(this).unregisterReceiver(receive_data);
broadCastRegistred = false;
}
@SuppressWarnings("StatementWithEmptyBody")
@ -1119,6 +1169,16 @@ public class MainActivity extends AppCompatActivity
case 1:
notificationsFragment = (DisplayNotificationsFragment) createdFragment;
break;
case 2:
if ( !display_local && display_global)
federatedFragment = (DisplayStatusFragment) createdFragment;
if( display_local)
localFragment = (DisplayStatusFragment) createdFragment;
break;
case 3:
if( display_local && display_global)
federatedFragment = (DisplayStatusFragment) createdFragment;
break;
}
return createdFragment;
}

View File

@ -43,6 +43,24 @@
<action android:name="RestartStreamingService" />
</intent-filter>
</receiver>
<service
android:name="fr.gouv.etalab.mastodon.services.StreamingFederatedTimelineService"
android:exported="false"/>
<receiver android:name="fr.gouv.etalab.mastodon.services.RestartFederatedServiceReceiver"
android:exported="false">
<intent-filter>
<action android:name="RestartStreamingFederatedService" />
</intent-filter>
</receiver>
<service
android:name="fr.gouv.etalab.mastodon.services.StreamingLocalTimelineService"
android:exported="false"/>
<receiver android:name="fr.gouv.etalab.mastodon.services.RestartLocalServiceReceiver"
android:exported="false">
<intent-filter>
<action android:name="RestartStreamingLocalService" />
</intent-filter>
</receiver>
<activity
android:name="fr.gouv.etalab.mastodon.activities.MainActivity"
android:label="@string/app_name"

View File

@ -158,7 +158,7 @@ public class HashTagActivity extends AppCompatActivity implements OnRetrieveFeed
}
@Override
public void onRetrieveFeeds(APIResponse apiResponse, boolean refreshData) {
public void onRetrieveFeeds(APIResponse apiResponse) {
mainLoader.setVisibility(View.GONE);
nextElementLoader.setVisibility(View.GONE);

View File

@ -525,7 +525,7 @@ public class ShowAccountActivity extends AppCompatActivity implements OnPostActi
}
@Override
public void onRetrieveFeeds(APIResponse apiResponse, boolean refreshData) {
public void onRetrieveFeeds(APIResponse apiResponse) {
if( apiResponse.getError() != null){
final SharedPreferences sharedpreferences = getApplicationContext().getSharedPreferences(Helper.APP_PREFS, android.content.Context.MODE_PRIVATE);
boolean show_error_messages = sharedpreferences.getBoolean(Helper.SET_SHOW_ERROR_MESSAGES, true);

View File

@ -216,7 +216,7 @@ public class ShowConversationActivity extends AppCompatActivity implements OnRet
}
@Override
public void onRetrieveFeeds(APIResponse apiResponse, boolean refreshData) {
public void onRetrieveFeeds(APIResponse apiResponse) {
if( apiResponse.getError() != null){
final SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, android.content.Context.MODE_PRIVATE);
boolean show_error_messages = sharedpreferences.getBoolean(Helper.SET_SHOW_ERROR_MESSAGES, true);

View File

@ -550,6 +550,8 @@ public class TootActivity extends AppCompatActivity implements OnRetrieveSearcAc
}else {
searchLength = 15;
}
int totalChar = toot_cw_content.length() + toot_content.length();
toot_space_left.setText(String.valueOf(totalChar));
if( currentCursorPosition- (searchLength-1) < 0 || currentCursorPosition == 0 || currentCursorPosition > s.toString().length())
return;
Matcher m;
@ -566,7 +568,7 @@ public class TootActivity extends AppCompatActivity implements OnRetrieveSearcAc
}
new RetrieveSearchAccountsAsyncTask(getApplicationContext(),search,TootActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}else{toot_content.dismissDropDown();}
int totalChar = toot_cw_content.length() + toot_content.length();
totalChar = toot_cw_content.length() + toot_content.length();
toot_space_left.setText(String.valueOf(totalChar));
}
});

View File

@ -16,11 +16,8 @@ package fr.gouv.etalab.mastodon.asynctasks;
import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;
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;
@ -40,7 +37,6 @@ public class RetrieveFeedsAsyncTask extends AsyncTask<Void, Void, Void> {
private String tag;
private boolean showMediaOnly = false;
private boolean showPinned = false;
private boolean refreshData;
public enum Type{
HOME,
@ -59,7 +55,6 @@ public class RetrieveFeedsAsyncTask extends AsyncTask<Void, Void, Void> {
this.action = action;
this.max_id = max_id;
this.listener = onRetrieveFeedsInterface;
this.refreshData = true;
}
public RetrieveFeedsAsyncTask(Context context, Type action, String targetedID, String max_id, boolean showMediaOnly, boolean showPinned, OnRetrieveFeedsInterface onRetrieveFeedsInterface){
@ -69,7 +64,6 @@ public class RetrieveFeedsAsyncTask extends AsyncTask<Void, Void, Void> {
this.listener = onRetrieveFeedsInterface;
this.targetedID = targetedID;
this.showMediaOnly = showMediaOnly;
this.refreshData = true;
this.showPinned = showPinned;
}
public RetrieveFeedsAsyncTask(Context context, Type action, String tag, String targetedID, String max_id, OnRetrieveFeedsInterface onRetrieveFeedsInterface){
@ -79,7 +73,6 @@ public class RetrieveFeedsAsyncTask extends AsyncTask<Void, Void, Void> {
this.listener = onRetrieveFeedsInterface;
this.targetedID = targetedID;
this.tag = tag;
this.refreshData = true;
}
@ -121,6 +114,6 @@ public class RetrieveFeedsAsyncTask extends AsyncTask<Void, Void, Void> {
@Override
protected void onPostExecute(Void result) {
listener.onRetrieveFeeds(apiResponse, refreshData);
listener.onRetrieveFeeds(apiResponse);
}
}

View File

@ -0,0 +1,72 @@
/* 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 <http://www.gnu.org/licenses>. */
package fr.gouv.etalab.mastodon.asynctasks;
import android.content.Context;
import android.os.AsyncTask;
import java.util.ArrayList;
import java.util.List;
import fr.gouv.etalab.mastodon.activities.MainActivity;
import fr.gouv.etalab.mastodon.client.API;
import fr.gouv.etalab.mastodon.client.APIResponse;
import fr.gouv.etalab.mastodon.interfaces.OnRetrieveMissingFeedsInterface;
/**
* Created by Thomas on 27/09/2017.
* Retrieves missing toots since last pause
*/
public class RetrieveMissingFeedsAsyncTask extends AsyncTask<Void, Void, Void> {
private Context context;
private String since_id;
private OnRetrieveMissingFeedsInterface listener;
private List<fr.gouv.etalab.mastodon.client.Entities.Status> statuses = new ArrayList<>();
public RetrieveMissingFeedsAsyncTask(Context context, String since_id, OnRetrieveMissingFeedsInterface onRetrieveMissingFeedsInterface){
this.context = context;
this.since_id = since_id;
this.listener = onRetrieveMissingFeedsInterface;
}
@Override
protected Void doInBackground(Void... params) {
int loopInc = 0;
API api = new API(context);
List<fr.gouv.etalab.mastodon.client.Entities.Status> tempStatus;
while (loopInc < 10){
APIResponse apiResponse = api.getHomeTimelineSinceId(since_id, 80);
String max_id = apiResponse.getMax_id();
since_id = apiResponse.getSince_id();
tempStatus = apiResponse.getStatuses();
if( statuses != null && tempStatus != null)
statuses.addAll(0, tempStatus);
loopInc++;
if( max_id == null || max_id.equals(since_id))
break;
}
if( statuses != null && statuses.size() > 0) {
MainActivity.lastHomeId = statuses.get(0).getId();
}
return null;
}
@Override
protected void onPostExecute(Void result) {
listener.onRetrieveMissingFeeds(statuses);
}
}

View File

@ -0,0 +1,77 @@
/* 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 <http://www.gnu.org/licenses>. */
package fr.gouv.etalab.mastodon.asynctasks;
import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
import fr.gouv.etalab.mastodon.activities.MainActivity;
import fr.gouv.etalab.mastodon.client.API;
import fr.gouv.etalab.mastodon.client.APIResponse;
import fr.gouv.etalab.mastodon.client.Entities.Notification;
import fr.gouv.etalab.mastodon.helper.Helper;
import fr.gouv.etalab.mastodon.interfaces.OnRetrieveMissingNotificationsInterface;
/**
* Created by Thomas on 27/09/2017.
* Retrieves missing notifications since last pause
*/
public class RetrieveMissingNotificationsAsyncTask extends AsyncTask<Void, Void, Void> {
private Context context;
private String since_id;
private OnRetrieveMissingNotificationsInterface listener;
private List<Notification> notifications = new ArrayList<>();
public RetrieveMissingNotificationsAsyncTask(Context context, String since_id, OnRetrieveMissingNotificationsInterface onRetrieveMissingNotifications){
this.context = context;
this.since_id = since_id;
this.listener = onRetrieveMissingNotifications;
}
@Override
protected Void doInBackground(Void... params) {
int loopInc = 0;
API api = new API(context);
List<Notification> tempNotifications;
while (loopInc < 10){
APIResponse apiResponse = api.getNotificationsSince(since_id, 40);
String max_id = apiResponse.getMax_id();
since_id = apiResponse.getSince_id();
tempNotifications = apiResponse.getNotifications();
if( notifications != null && tempNotifications != null)
notifications.addAll(0, tempNotifications);
loopInc++;
if( max_id == null || max_id.equals(since_id))
break;
}
if( notifications != null && notifications.size() > 0) {
MainActivity.lastNotificationId = notifications.get(0).getId();
}
return null;
}
@Override
protected void onPostExecute(Void result) {
listener.onRetrieveMissingNotifications(notifications);
}
}

View File

@ -271,6 +271,9 @@ public class API {
return relationship;
}
/**
* Returns a relationship between the authenticated account and an account
* @param accounts ArrayList<Account> accounts fetched
@ -468,6 +471,14 @@ public class API {
return getHomeTimeline(max_id, null, tootPerPage);
}
/**
* Retrieves home timeline for the account *synchronously*
* @return APIResponse
*/
public APIResponse getHomeTimeline( String max_id, int tootPerPage) {
return getHomeTimeline(max_id, null, tootPerPage);
}
/**
* Retrieves home timeline for the account since an Id value *synchronously*
* @return APIResponse
@ -476,6 +487,14 @@ public class API {
return getHomeTimeline(null, since_id, tootPerPage);
}
/**
* Retrieves home timeline for the account since an Id value *synchronously*
* @return APIResponse
*/
public APIResponse getHomeTimelineSinceId(String since_id, int tootPerPage) {
return getHomeTimeline(null, since_id, tootPerPage);
}
/**
* Retrieves home timeline for the account *synchronously*
* @param max_id String id max
@ -490,8 +509,8 @@ public class API {
params.put("max_id", max_id);
if (since_id != null)
params.put("since_id", since_id);
if (0 > limit || limit > 40)
limit = 40;
if (0 > limit || limit > 80)
limit = 80;
params.put("limit",String.valueOf(limit));
statuses = new ArrayList<>();
get("/timelines/home", params, new JsonHttpResponseHandler() {
@ -1070,6 +1089,15 @@ public class API {
return getNotifications(null, since_id, notificationPerPage);
}
/**
* Retrieves notifications for the authenticated account since an id*synchronously*
* @param since_id String since max
* @return APIResponse
*/
public APIResponse getNotificationsSince(String since_id, int notificationPerPage){
return getNotifications(null, since_id, notificationPerPage);
}
/**
* Retrieves notifications for the authenticated account *synchronously*
* @param max_id String id max
@ -1095,6 +1123,21 @@ public class API {
if( 0 > limit || limit > 40)
limit = 40;
params.put("limit",String.valueOf(limit));
final SharedPreferences sharedpreferences = context.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);
if( !notif_follow )
params.add("exclude_types[]", "follow");
if( !notif_add )
params.add("exclude_types[]", "favourite");
if( !notif_share )
params.add("exclude_types[]", "reblog");
if( !notif_mention )
params.add("exclude_types[]", "mention");
notifications = new ArrayList<>();
get("/notifications", params, new JsonHttpResponseHandler() {
@ -1347,8 +1390,16 @@ public class API {
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.setReblogged(Boolean.valueOf(resobj.get("reblogged").toString()));
}catch (Exception e){
status.setReblogged(false);
}
try {
status.setFavourited(Boolean.valueOf(resobj.get("favourited").toString()));
}catch (Exception e){
status.setReblogged(false);
}
try {
status.setPinned(Boolean.valueOf(resobj.get("pinned").toString()));
}catch (JSONException e){

View File

@ -29,12 +29,14 @@ import android.os.Build;
import android.os.Bundle;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.CardView;
import android.support.v7.widget.PopupMenu;
import android.text.Html;
import android.text.SpannableString;
import android.text.method.LinkMovementMethod;
import android.util.Log;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
@ -509,10 +511,166 @@ public class NotificationsListAdapter extends BaseAdapter implements OnPostActio
holder.notification_account_username.setText( String.format("@%s",notification.getAccount().getUsername()));
final View finalConvertView = convertView;
final String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null);
final View attached = holder.status_more;
holder.status_more.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
moreOptionDialog(status, finalConvertView);
holder.status_more.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
PopupMenu popup = new PopupMenu(context, attached);
final boolean isOwner = status.getAccount().getId().equals(userId);
popup.getMenuInflater()
.inflate(R.menu.option_toot, popup.getMenu());
if( status.getVisibility().equals("private") || status.getVisibility().equals("direct")){
popup.getMenu().findItem(R.id.action_mention).setVisible(false);
}
final String[] stringArrayConf;
if( isOwner) {
popup.getMenu().findItem(R.id.action_block).setVisible(false);
popup.getMenu().findItem(R.id.action_mute).setVisible(false);
popup.getMenu().findItem(R.id.action_report).setVisible(false);
stringArrayConf = context.getResources().getStringArray(R.array.more_action_owner_confirm);
}else {
popup.getMenu().findItem(R.id.action_remove).setVisible(false);
stringArrayConf = context.getResources().getStringArray(R.array.more_action_confirm);
}
popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
AlertDialog.Builder builderInner;
final API.StatusAction doAction;
switch (item.getItemId()) {
case R.id.action_remove:
builderInner = new AlertDialog.Builder(context);
builderInner.setTitle(stringArrayConf[0]);
doAction = API.StatusAction.UNSTATUS;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
builderInner.setMessage(Html.fromHtml(status.getContent(), Html.FROM_HTML_MODE_LEGACY));
else
//noinspection deprecation
builderInner.setMessage(Html.fromHtml(status.getContent()));
break;
case R.id.action_mute:
builderInner = new AlertDialog.Builder(context);
builderInner.setTitle(stringArrayConf[0]);
doAction = API.StatusAction.MUTE;
break;
case R.id.action_block:
builderInner = new AlertDialog.Builder(context);
builderInner.setTitle(stringArrayConf[1]);
doAction = API.StatusAction.BLOCK;
break;
case R.id.action_report:
builderInner = new AlertDialog.Builder(context);
builderInner.setTitle(stringArrayConf[2]);
doAction = API.StatusAction.REPORT;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
builderInner.setMessage(Html.fromHtml(status.getContent(), Html.FROM_HTML_MODE_LEGACY));
else
//noinspection deprecation
builderInner.setMessage(Html.fromHtml(status.getContent()));
break;
case R.id.action_copy:
ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
String content;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
content = Html.fromHtml(status.getContent(), Html.FROM_HTML_MODE_LEGACY).toString();
else
//noinspection deprecation
content = Html.fromHtml(status.getContent()).toString();
ClipData clip = ClipData.newPlainText(Helper.CLIP_BOARD, content);
clipboard.setPrimaryClip(clip);
Toast.makeText(context,R.string.clipboard,Toast.LENGTH_LONG).show();
return true;
case R.id.action_share:
Intent sendIntent = new Intent(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_SUBJECT, context.getString(R.string.shared_via));
sendIntent.putExtra(Intent.EXTRA_TEXT, status.getUrl());
sendIntent.setType("text/plain");
context.startActivity(Intent.createChooser(sendIntent, context.getString(R.string.share_with)));
return true;
case R.id.action_mention:
status.setTakingScreenShot(true);
notificationsListAdapter.notifyDataSetChanged();
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
Bitmap bitmap = Helper.convertTootIntoBitmap(context, finalConvertView);
status.setTakingScreenShot(false);
notificationsListAdapter.notifyDataSetChanged();
Intent intent = new Intent(context, TootActivity.class);
Bundle b = new Bundle();
String fname = "tootmention_" + status.getId() +".jpg";
File file = new File (context.getCacheDir() + "/", fname);
if (file.exists ()) //noinspection ResultOfMethodCallIgnored
file.delete ();
try {
FileOutputStream out = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, out);
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
b.putString("fileMention", fname);
b.putString("tootMention", (status.getReblog() != null)?status.getReblog().getAccount().getAcct():status.getAccount().getAcct());
b.putString("urlMention", (status.getReblog() != null)?status.getReblog().getUrl():status.getUrl());
intent.putExtras(b);
context.startActivity(intent);
}
}, 1000);
return true;
default:
return true;
}
//Text for report
EditText input = null;
if( doAction == API.StatusAction.REPORT){
input = new EditText(context);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
input.setLayoutParams(lp);
builderInner.setView(input);
}
builderInner.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog,int which) {
dialog.dismiss();
}
});
final EditText finalInput = input;
builderInner.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog,int which) {
if(doAction == API.StatusAction.UNSTATUS ){
String targetedId = status.getId();
new PostActionAsyncTask(context, doAction, targetedId, NotificationsListAdapter.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}else if(doAction == API.StatusAction.REPORT ){
String comment = null;
if( finalInput.getText() != null)
comment = finalInput.getText().toString();
new PostActionAsyncTask(context, doAction, status.getId(), status, comment, NotificationsListAdapter.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}else{
String targetedId = status.getAccount().getId();
new PostActionAsyncTask(context, doAction, targetedId, NotificationsListAdapter.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
dialog.dismiss();
}
});
builderInner.show();
return true;
}
});
popup.show();
}
});
}
});
@ -830,244 +988,4 @@ public class NotificationsListAdapter extends BaseAdapter implements OnPostActio
ImageView status_privacy;
}
/**
* More option for status (report / remove status / Mute / Block)
* @param status Status current status
*/
private void moreOptionDialog(final Status status, final View view){
SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null);
final boolean isOwner = status.getAccount().getId().equals(userId);
AlertDialog.Builder builderSingle = new AlertDialog.Builder(context);
//builderSingle.setTitle(R.string.make_a_choice);
final String[] stringArray, stringArrayConf;
final API.StatusAction[] doAction;
if( isOwner) {
if( status.getVisibility().equals("private") || status.getVisibility().equals("direct")){
String[] stringArraytmp = context.getResources().getStringArray(R.array.more_action_owner);
List<String> list = new ArrayList<>(Arrays.asList(stringArraytmp));
list.remove(3);
stringArray = list.toArray(new String[0]);
String[] stringArrayConftmp = context.getResources().getStringArray(R.array.more_action_owner_confirm);
list = new ArrayList<>(Arrays.asList(stringArrayConftmp));
list.remove(3);
stringArrayConf = list.toArray(new String[0]);
doAction = new API.StatusAction[]{API.StatusAction.UNSTATUS};
}else {
stringArray = context.getResources().getStringArray(R.array.more_action_owner);
stringArrayConf = context.getResources().getStringArray(R.array.more_action_owner_confirm);
doAction = new API.StatusAction[]{API.StatusAction.UNSTATUS};
}
}else {
if( status.getVisibility().equals("private") || status.getVisibility().equals("direct")){
String[] stringArraytmp = context.getResources().getStringArray(R.array.more_action);
List<String> list = new ArrayList<>(Arrays.asList(stringArraytmp));
list.remove(5);
stringArray = list.toArray(new String[0]);
String[] stringArrayConftmp = context.getResources().getStringArray(R.array.more_action_confirm);
list = new ArrayList<>(Arrays.asList(stringArrayConftmp));
list.remove(5);
stringArrayConf = list.toArray(new String[0]);
doAction = new API.StatusAction[]{API.StatusAction.MUTE, API.StatusAction.BLOCK, API.StatusAction.REPORT};
}else {
stringArray = context.getResources().getStringArray(R.array.more_action);
stringArrayConf = context.getResources().getStringArray(R.array.more_action_confirm);
doAction = new API.StatusAction[]{API.StatusAction.MUTE, API.StatusAction.BLOCK, API.StatusAction.REPORT};
}
}
final ArrayAdapter<String> arrayAdapter = new ArrayAdapter<>(context, android.R.layout.simple_list_item_1, stringArray);
builderSingle.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
builderSingle.setAdapter(arrayAdapter, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
AlertDialog.Builder builderInner = new AlertDialog.Builder(context);
builderInner.setTitle(stringArrayConf[which]);
if( isOwner) {
if( which == 0) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
builderInner.setMessage(Html.fromHtml(status.getContent(), Html.FROM_HTML_MODE_LEGACY));
else
//noinspection deprecation
builderInner.setMessage(Html.fromHtml(status.getContent()));
}else if( which == 1){
ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
String content;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
content = Html.fromHtml(status.getContent(), Html.FROM_HTML_MODE_LEGACY).toString();
else
//noinspection deprecation
content = Html.fromHtml(status.getContent()).toString();
ClipData clip = ClipData.newPlainText(Helper.CLIP_BOARD, content);
clipboard.setPrimaryClip(clip);
Toast.makeText(context,R.string.clipboard,Toast.LENGTH_LONG).show();
dialog.dismiss();
return;
}else if( which == 2) {
Intent sendIntent = new Intent(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_SUBJECT, context.getString(R.string.shared_via));
sendIntent.putExtra(Intent.EXTRA_TEXT, status.getUrl());
sendIntent.setType("text/plain");
context.startActivity(Intent.createChooser(sendIntent, context.getString(R.string.share_with)));
return;
}else if (which == 3){
status.setTakingScreenShot(true);
notificationsListAdapter.notifyDataSetChanged();
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
Bitmap bitmap = Helper.convertTootIntoBitmap(context, view);
status.setTakingScreenShot(false);
notificationsListAdapter.notifyDataSetChanged();
Intent intent = new Intent(context, TootActivity.class);
Bundle b = new Bundle();
String fname = "tootmention_" + status.getId() +".jpg";
File file = new File (context.getCacheDir() + "/", fname);
if (file.exists ()) //noinspection ResultOfMethodCallIgnored
file.delete ();
try {
FileOutputStream out = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, out);
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
b.putString("fileMention", fname);
b.putString("tootMention", (status.getReblog() != null)?status.getReblog().getAccount().getAcct():status.getAccount().getAcct());
b.putString("urlMention", (status.getReblog() != null)?status.getReblog().getUrl():status.getUrl());
intent.putExtras(b);
context.startActivity(intent);
}
}, 1000);
return;
}
}else {
if( which < 2 ){
builderInner.setMessage(status.getAccount().getAcct());
}else if( which == 2) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
builderInner.setMessage(Html.fromHtml(status.getContent(), Html.FROM_HTML_MODE_LEGACY));
else
//noinspection deprecation
builderInner.setMessage(Html.fromHtml(status.getContent()));
}else if( which == 3 ){
ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
String content;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
content = Html.fromHtml(status.getContent(), Html.FROM_HTML_MODE_LEGACY).toString();
else
//noinspection deprecation
content = Html.fromHtml(status.getContent()).toString();
ClipData clip = ClipData.newPlainText(Helper.CLIP_BOARD, content);
clipboard.setPrimaryClip(clip);
Toast.makeText(context,R.string.clipboard,Toast.LENGTH_LONG).show();
dialog.dismiss();
return;
}else if (which == 4){
Intent sendIntent = new Intent(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_SUBJECT, context.getString(R.string.shared_via));
sendIntent.putExtra(Intent.EXTRA_TEXT, status.getUrl());
sendIntent.setType("text/plain");
context.startActivity(Intent.createChooser(sendIntent, context.getString(R.string.share_with)));
return;
}else if (which == 5){
status.setTakingScreenShot(true);
notificationsListAdapter.notifyDataSetChanged();
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
Bitmap bitmap = Helper.convertTootIntoBitmap(context, view);
status.setTakingScreenShot(false);
notificationsListAdapter.notifyDataSetChanged();
Intent intent = new Intent(context, TootActivity.class);
Bundle b = new Bundle();
String fname = "tootmention_" + status.getId() +".jpg";
File file = new File (context.getCacheDir() + "/", fname);
if (file.exists ()) //noinspection ResultOfMethodCallIgnored
file.delete ();
try {
FileOutputStream out = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, out);
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
b.putString("fileMention", fname);
b.putString("tootMention", (status.getReblog() != null)?status.getReblog().getAccount().getAcct():status.getAccount().getAcct());
b.putString("urlMention", (status.getReblog() != null)?status.getReblog().getUrl():status.getUrl());
intent.putExtras(b);
context.startActivity(intent);
}
}, 1000);
return;
}
}
//Text for report
EditText input = null;
final int position = which;
if( doAction[which] == API.StatusAction.REPORT){
input = new EditText(context);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
input.setLayoutParams(lp);
builderInner.setView(input);
}
builderInner.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog,int which) {
dialog.dismiss();
}
});
final EditText finalInput = input;
builderInner.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog,int which) {
API.StatusAction statusAction = doAction[position];
if(statusAction == API.StatusAction.REPORT || statusAction == API.StatusAction.CREATESTATUS){
String comment = null;
if( finalInput != null && finalInput.getText() != null)
comment = finalInput.getText().toString();
new PostActionAsyncTask(context, statusAction, status.getId(), status, comment, NotificationsListAdapter.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}else{
String targetedId;
if( doAction[position] == API.StatusAction.FAVOURITE ||
doAction[position] == API.StatusAction.UNFAVOURITE ||
doAction[position] == API.StatusAction.REBLOG ||
doAction[position] == API.StatusAction.UNREBLOG ||
doAction[position] == API.StatusAction.UNSTATUS
)
targetedId = status.getId();
else
targetedId = status.getAccount().getId();
new PostActionAsyncTask(context, statusAction, targetedId, NotificationsListAdapter.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
dialog.dismiss();
}
});
builderInner.show();
}
});
builderSingle.create().requestWindowFeature(Window.FEATURE_NO_TITLE);
builderSingle.show();
}
}

View File

@ -33,6 +33,7 @@ import android.os.Bundle;
import android.os.CountDownTimer;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.CardView;
import android.support.v7.widget.PopupMenu;
import android.text.Html;
import android.text.SpannableString;
import android.text.method.LinkMovementMethod;
@ -40,6 +41,7 @@ import android.util.Log;
import android.util.Patterns;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
@ -76,6 +78,9 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import fr.gouv.etalab.mastodon.activities.LoginActivity;
import fr.gouv.etalab.mastodon.activities.MainActivity;
import fr.gouv.etalab.mastodon.activities.MediaActivity;
import fr.gouv.etalab.mastodon.activities.ShowAccountActivity;
import fr.gouv.etalab.mastodon.activities.ShowConversationActivity;
@ -235,7 +240,7 @@ public class StatusListAdapter extends BaseAdapter implements OnPostActionInterf
final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null);
final String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null);
//Display a preview for accounts that have replied *if enabled and only for home timeline*
if( type == RetrieveFeedsAsyncTask.Type.HOME ) {
@ -856,10 +861,159 @@ public class StatusListAdapter extends BaseAdapter implements OnPostActionInterf
final View finalConvertView = convertView;
final View attached = holder.status_more;
holder.status_more.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
moreOptionDialog(status, finalConvertView);
PopupMenu popup = new PopupMenu(context, attached);
final boolean isOwner = status.getAccount().getId().equals(userId);
popup.getMenuInflater()
.inflate(R.menu.option_toot, popup.getMenu());
if( status.getVisibility().equals("private") || status.getVisibility().equals("direct")){
popup.getMenu().findItem(R.id.action_mention).setVisible(false);
}
final String[] stringArrayConf;
if( isOwner) {
popup.getMenu().findItem(R.id.action_block).setVisible(false);
popup.getMenu().findItem(R.id.action_mute).setVisible(false);
popup.getMenu().findItem(R.id.action_report).setVisible(false);
stringArrayConf = context.getResources().getStringArray(R.array.more_action_owner_confirm);
}else {
popup.getMenu().findItem(R.id.action_remove).setVisible(false);
stringArrayConf = context.getResources().getStringArray(R.array.more_action_confirm);
}
popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
AlertDialog.Builder builderInner;
final API.StatusAction doAction;
switch (item.getItemId()) {
case R.id.action_remove:
builderInner = new AlertDialog.Builder(context);
builderInner.setTitle(stringArrayConf[0]);
doAction = API.StatusAction.UNSTATUS;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
builderInner.setMessage(Html.fromHtml(status.getContent(), Html.FROM_HTML_MODE_LEGACY));
else
//noinspection deprecation
builderInner.setMessage(Html.fromHtml(status.getContent()));
break;
case R.id.action_mute:
builderInner = new AlertDialog.Builder(context);
builderInner.setTitle(stringArrayConf[0]);
doAction = API.StatusAction.MUTE;
break;
case R.id.action_block:
builderInner = new AlertDialog.Builder(context);
builderInner.setTitle(stringArrayConf[1]);
doAction = API.StatusAction.BLOCK;
break;
case R.id.action_report:
builderInner = new AlertDialog.Builder(context);
builderInner.setTitle(stringArrayConf[2]);
doAction = API.StatusAction.REPORT;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
builderInner.setMessage(Html.fromHtml(status.getContent(), Html.FROM_HTML_MODE_LEGACY));
else
//noinspection deprecation
builderInner.setMessage(Html.fromHtml(status.getContent()));
break;
case R.id.action_copy:
ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
String content;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
content = Html.fromHtml(status.getContent(), Html.FROM_HTML_MODE_LEGACY).toString();
else
//noinspection deprecation
content = Html.fromHtml(status.getContent()).toString();
ClipData clip = ClipData.newPlainText(Helper.CLIP_BOARD, content);
clipboard.setPrimaryClip(clip);
Toast.makeText(context,R.string.clipboard,Toast.LENGTH_LONG).show();
return true;
case R.id.action_share:
Intent sendIntent = new Intent(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_SUBJECT, context.getString(R.string.shared_via));
sendIntent.putExtra(Intent.EXTRA_TEXT, status.getUrl());
sendIntent.setType("text/plain");
context.startActivity(Intent.createChooser(sendIntent, context.getString(R.string.share_with)));
return true;
case R.id.action_mention:
status.setTakingScreenShot(true);
statusListAdapter.notifyDataSetChanged();
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
Bitmap bitmap = Helper.convertTootIntoBitmap(context, finalConvertView);
status.setTakingScreenShot(false);
statusListAdapter.notifyDataSetChanged();
Intent intent = new Intent(context, TootActivity.class);
Bundle b = new Bundle();
String fname = "tootmention_" + status.getId() +".jpg";
File file = new File (context.getCacheDir() + "/", fname);
if (file.exists ()) //noinspection ResultOfMethodCallIgnored
file.delete ();
try {
FileOutputStream out = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, out);
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
b.putString("fileMention", fname);
b.putString("tootMention", (status.getReblog() != null)?status.getReblog().getAccount().getAcct():status.getAccount().getAcct());
b.putString("urlMention", (status.getReblog() != null)?status.getReblog().getUrl():status.getUrl());
intent.putExtras(b);
context.startActivity(intent);
}
}, 1000);
return true;
default:
return true;
}
//Text for report
EditText input = null;
if( doAction == API.StatusAction.REPORT){
input = new EditText(context);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
input.setLayoutParams(lp);
builderInner.setView(input);
}
builderInner.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog,int which) {
dialog.dismiss();
}
});
final EditText finalInput = input;
builderInner.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog,int which) {
if(doAction == API.StatusAction.UNSTATUS ){
String targetedId = status.getId();
new PostActionAsyncTask(context, doAction, targetedId, StatusListAdapter.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}else if(doAction == API.StatusAction.REPORT ){
String comment = null;
if( finalInput.getText() != null)
comment = finalInput.getText().toString();
new PostActionAsyncTask(context, doAction, status.getId(), status, comment, StatusListAdapter.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}else{
String targetedId = status.getAccount().getId();
new PostActionAsyncTask(context, doAction, targetedId, StatusListAdapter.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
dialog.dismiss();
}
});
builderInner.show();
return true;
}
});
popup.show();
}
});
@ -1027,7 +1181,7 @@ public class StatusListAdapter extends BaseAdapter implements OnPostActionInterf
@Override
public void onRetrieveFeeds(APIResponse apiResponse, boolean refreshData) {
public void onRetrieveFeeds(APIResponse apiResponse) {
if( apiResponse.getError() != null){
final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, android.content.Context.MODE_PRIVATE);
boolean show_error_messages = sharedpreferences.getBoolean(Helper.SET_SHOW_ERROR_MESSAGES, true);
@ -1330,245 +1484,4 @@ public class StatusListAdapter extends BaseAdapter implements OnPostActionInterf
})
.show();
}
/**
* More option for status (report / remove status / Mute / Block)
* @param status Status current status
*/
private void moreOptionDialog(final Status status, final View view){
SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null);
final boolean isOwner = status.getAccount().getId().equals(userId);
AlertDialog.Builder builderSingle = new AlertDialog.Builder(context);
final String[] stringArray, stringArrayConf;
final API.StatusAction[] doAction;
if( isOwner) {
if( status.getVisibility().equals("private") || status.getVisibility().equals("direct")){
String[] stringArraytmp = context.getResources().getStringArray(R.array.more_action_owner);
List<String> list = new ArrayList<>(Arrays.asList(stringArraytmp));
list.remove(3);
stringArray = list.toArray(new String[0]);
String[] stringArrayConftmp = context.getResources().getStringArray(R.array.more_action_owner_confirm);
list = new ArrayList<>(Arrays.asList(stringArrayConftmp));
list.remove(3);
stringArrayConf = list.toArray(new String[0]);
doAction = new API.StatusAction[]{API.StatusAction.UNSTATUS};
}else {
stringArray = context.getResources().getStringArray(R.array.more_action_owner);
stringArrayConf = context.getResources().getStringArray(R.array.more_action_owner_confirm);
doAction = new API.StatusAction[]{API.StatusAction.UNSTATUS};
}
}else {
if( status.getVisibility().equals("private") || status.getVisibility().equals("direct")){
String[] stringArraytmp = context.getResources().getStringArray(R.array.more_action);
List<String> list = new ArrayList<>(Arrays.asList(stringArraytmp));
list.remove(5);
stringArray = list.toArray(new String[0]);
String[] stringArrayConftmp = context.getResources().getStringArray(R.array.more_action_confirm);
list = new ArrayList<>(Arrays.asList(stringArrayConftmp));
list.remove(5);
stringArrayConf = list.toArray(new String[0]);
doAction = new API.StatusAction[]{API.StatusAction.MUTE, API.StatusAction.BLOCK, API.StatusAction.REPORT};
}else {
stringArray = context.getResources().getStringArray(R.array.more_action);
stringArrayConf = context.getResources().getStringArray(R.array.more_action_confirm);
doAction = new API.StatusAction[]{API.StatusAction.MUTE, API.StatusAction.BLOCK, API.StatusAction.REPORT};
}
}
final ArrayAdapter<String> arrayAdapter = new ArrayAdapter<>(context, android.R.layout.simple_list_item_1, stringArray);
builderSingle.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
builderSingle.setAdapter(arrayAdapter, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
AlertDialog.Builder builderInner = new AlertDialog.Builder(context);
builderInner.setTitle(stringArrayConf[which]);
if( isOwner) {
if( which == 0) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
builderInner.setMessage(Html.fromHtml(status.getContent(), Html.FROM_HTML_MODE_LEGACY));
else
//noinspection deprecation
builderInner.setMessage(Html.fromHtml(status.getContent()));
}else if( which == 1){
ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
String content;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
content = Html.fromHtml(status.getContent(), Html.FROM_HTML_MODE_LEGACY).toString();
else
//noinspection deprecation
content = Html.fromHtml(status.getContent()).toString();
ClipData clip = ClipData.newPlainText(Helper.CLIP_BOARD, content);
clipboard.setPrimaryClip(clip);
Toast.makeText(context,R.string.clipboard,Toast.LENGTH_LONG).show();
dialog.dismiss();
return;
}else if( which == 2) {
Intent sendIntent = new Intent(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_SUBJECT, context.getString(R.string.shared_via));
sendIntent.putExtra(Intent.EXTRA_TEXT, status.getUrl());
sendIntent.setType("text/plain");
context.startActivity(Intent.createChooser(sendIntent, context.getString(R.string.share_with)));
return;
}else if( which == 3) {
status.setTakingScreenShot(true);
statusListAdapter.notifyDataSetChanged();
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
Bitmap bitmap = Helper.convertTootIntoBitmap(context, view);
status.setTakingScreenShot(false);
statusListAdapter.notifyDataSetChanged();
Intent intent = new Intent(context, TootActivity.class);
Bundle b = new Bundle();
String fname = "tootmention_" + status.getId() +".jpg";
File file = new File (context.getCacheDir() + "/", fname);
if (file.exists ()) //noinspection ResultOfMethodCallIgnored
file.delete ();
try {
FileOutputStream out = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, out);
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
b.putString("fileMention", fname);
b.putString("tootMention", (status.getReblog() != null)?status.getReblog().getAccount().getAcct():status.getAccount().getAcct());
b.putString("urlMention", (status.getReblog() != null)?status.getReblog().getUrl():status.getUrl());
intent.putExtras(b);
context.startActivity(intent);
}
}, 1000);
return;
}
}else {
if( which < 2 ){
builderInner.setMessage(status.getAccount().getAcct());
}else if( which == 2) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
builderInner.setMessage(Html.fromHtml(status.getContent(), Html.FROM_HTML_MODE_LEGACY));
else
//noinspection deprecation
builderInner.setMessage(Html.fromHtml(status.getContent()));
}else if( which == 3 ){
ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
String content;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
content = Html.fromHtml(status.getContent(), Html.FROM_HTML_MODE_LEGACY).toString();
else
//noinspection deprecation
content = Html.fromHtml(status.getContent()).toString();
ClipData clip = ClipData.newPlainText(Helper.CLIP_BOARD, content);
clipboard.setPrimaryClip(clip);
Toast.makeText(context,R.string.clipboard,Toast.LENGTH_LONG).show();
dialog.dismiss();
return;
}else if( which == 4 ){
Intent sendIntent = new Intent(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_SUBJECT, context.getString(R.string.shared_via));
sendIntent.putExtra(Intent.EXTRA_TEXT, status.getUrl());
sendIntent.setType("text/plain");
context.startActivity(Intent.createChooser(sendIntent, context.getString(R.string.share_with)));
return;
}else if( which == 5 ){
status.setTakingScreenShot(true);
statusListAdapter.notifyDataSetChanged();
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
Bitmap bitmap = Helper.convertTootIntoBitmap(context, view);
status.setTakingScreenShot(false);
statusListAdapter.notifyDataSetChanged();
Intent intent = new Intent(context, TootActivity.class);
Bundle b = new Bundle();
String fname = "tootmention_" + status.getId() +".jpg";
File file = new File (context.getCacheDir() + "/", fname);
if (file.exists ()) //noinspection ResultOfMethodCallIgnored
file.delete ();
try {
FileOutputStream out = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, out);
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
b.putString("fileMention", fname);
b.putString("tootMention", (status.getReblog() != null)?status.getReblog().getAccount().getAcct():status.getAccount().getAcct());
b.putString("urlMention", (status.getReblog() != null)?status.getReblog().getUrl():status.getUrl());
intent.putExtras(b);
context.startActivity(intent);
}
}, 1000);
return;
}
}
//Text for report
EditText input = null;
final int position = which;
if( doAction[which] == API.StatusAction.REPORT){
input = new EditText(context);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
input.setLayoutParams(lp);
builderInner.setView(input);
}
builderInner.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog,int which) {
dialog.dismiss();
}
});
final EditText finalInput = input;
builderInner.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog,int which) {
API.StatusAction statusAction = doAction[position];
if(statusAction == API.StatusAction.REPORT || statusAction == API.StatusAction.CREATESTATUS){
String comment = null;
if( finalInput != null && finalInput.getText() != null)
comment = finalInput.getText().toString();
new PostActionAsyncTask(context, statusAction, status.getId(), status, comment, StatusListAdapter.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}else{
String targetedId;
if( doAction[position] == API.StatusAction.FAVOURITE ||
doAction[position] == API.StatusAction.UNFAVOURITE ||
doAction[position] == API.StatusAction.REBLOG ||
doAction[position] == API.StatusAction.UNREBLOG ||
doAction[position] == API.StatusAction.UNSTATUS
)
targetedId = status.getId();
else
targetedId = status.getAccount().getId();
new PostActionAsyncTask(context, statusAction, targetedId, StatusListAdapter.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
dialog.dismiss();
}
});
builderInner.show();
}
});
builderSingle.create().requestWindowFeature(Window.FEATURE_NO_TITLE);
builderSingle.show();
}
}

View File

@ -19,6 +19,7 @@ import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.widget.SwipeRefreshLayout;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@ -30,11 +31,11 @@ import java.util.ArrayList;
import java.util.List;
import fr.gouv.etalab.mastodon.activities.MainActivity;
import fr.gouv.etalab.mastodon.asynctasks.RetrieveFeedsAsyncTask;
import fr.gouv.etalab.mastodon.asynctasks.RetrieveMissingNotificationsAsyncTask;
import fr.gouv.etalab.mastodon.client.APIResponse;
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.interfaces.OnRetrieveMissingNotificationsInterface;
import mastodon.etalab.gouv.fr.mastodon.R;
import fr.gouv.etalab.mastodon.asynctasks.RetrieveNotificationsAsyncTask;
import fr.gouv.etalab.mastodon.client.Entities.Notification;
@ -45,7 +46,7 @@ import fr.gouv.etalab.mastodon.interfaces.OnRetrieveNotificationsInterface;
* Created by Thomas on 28/04/2017.
* Fragment to display notifications related to accounts
*/
public class DisplayNotificationsFragment extends Fragment implements OnRetrieveNotificationsInterface {
public class DisplayNotificationsFragment extends Fragment implements OnRetrieveNotificationsInterface, OnRetrieveMissingNotificationsInterface {
@ -62,7 +63,6 @@ public class DisplayNotificationsFragment extends Fragment implements OnRetrieve
private ListView lv_notifications;
private String lastReadNotifications;
private String userId;
public static ArrayList<Notification> tempNotifications = new ArrayList<>();
public DisplayNotificationsFragment(){
}
@ -155,31 +155,6 @@ public class DisplayNotificationsFragment extends Fragment implements OnRetrieve
asyncTask.cancel(true);
}
@Override
public void onResume() {
super.onResume();
ArrayList<String> knownId = new ArrayList<>();
for(Notification nt: notifications){
knownId.add(nt.getId());
}
for(Notification notification: tempNotifications){
if( !knownId.contains(notification.getId())) {
int index = lv_notifications.getFirstVisiblePosition() + 1;
View v = lv_notifications.getChildAt(0);
int top = (v == null) ? 0 : v.getTop();
notifications.add(0, notification);
notificationsListAdapter.notifyDataSetChanged();
lv_notifications.setSelectionFromTop(index, top);
if (textviewNoAction.getVisibility() == View.VISIBLE)
textviewNoAction.setVisibility(View.GONE);
}
}
((MainActivity)context).updateNotifCounter();
tempNotifications.clear();
tempNotifications = new ArrayList<>();
}
@Override
public void onRetrieveNotifications(APIResponse apiResponse, String acct, String userId, boolean refreshData) {
final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
@ -208,13 +183,18 @@ public class DisplayNotificationsFragment extends Fragment implements OnRetrieve
lv_notifications.setAdapter(notificationsListAdapter);
swiped = false;
}
if( notifications != null && notifications.size() > 0) {
for(Notification tmpNotification: notifications){
if( lastReadNotifications != null && Long.parseLong(tmpNotification.getId()) > Long.parseLong(lastReadNotifications))
if( lastReadNotifications != null && Long.parseLong(tmpNotification.getId()) > Long.parseLong(lastReadNotifications)) {
MainActivity.countNewNotifications++;
}
this.notifications.add(tmpNotification);
}
if( firstLoad) {
//Update the id of the last notification retrieved
MainActivity.lastNotificationId = notifications.get(0).getId();
SharedPreferences.Editor editor = sharedpreferences.edit();
editor.putString(Helper.LAST_NOTIFICATION_MAX_ID + this.userId, notifications.get(0).getId());
editor.apply();
@ -230,6 +210,14 @@ public class DisplayNotificationsFragment extends Fragment implements OnRetrieve
flag_loading = (max_id == null );
}
/**
* Called from main activity in onResume to retrieve missing notifications
* @param sinceId String
*/
public void retrieveMissingNotifications(String sinceId){
asyncTask = new RetrieveMissingNotificationsAsyncTask(context, sinceId, DisplayNotificationsFragment.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
@Override
public void setMenuVisibility(final boolean visible) {
super.setMenuVisibility(visible);
@ -264,6 +252,8 @@ public class DisplayNotificationsFragment extends Fragment implements OnRetrieve
if( context == null)
return;
if( notification != null){
//Update the id of the last notification retrieved
MainActivity.lastNotificationId = notification.getId();
int index = lv_notifications.getFirstVisiblePosition() + 1;
View v = lv_notifications.getChildAt(0);
int top = (v == null) ? 0 : v.getTop();
@ -275,4 +265,24 @@ public class DisplayNotificationsFragment extends Fragment implements OnRetrieve
textviewNoAction.setVisibility(View.GONE);
}
}
@Override
public void onRetrieveMissingNotifications(List<Notification> notifications) {
if( notifications != null && notifications.size() > 0) {
ArrayList<String> knownId = new ArrayList<>();
for (Notification nt : this.notifications) {
knownId.add(nt.getId());
}
for (int i = notifications.size()-1 ; i >= 0 ; i--) {
if (!knownId.contains(notifications.get(i).getId())) {
MainActivity.countNewNotifications++;
this.notifications.add(0, notifications.get(i));
}
}
notificationsListAdapter.notifyDataSetChanged();
try {
((MainActivity) context).updateNotifCounter();
}catch (Exception ignored){}
}
}
}

View File

@ -19,6 +19,7 @@ import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Parcelable;
import android.support.v4.app.Fragment;
import android.support.v4.content.LocalBroadcastManager;
@ -33,14 +34,21 @@ import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import fr.gouv.etalab.mastodon.activities.MainActivity;
import fr.gouv.etalab.mastodon.asynctasks.RetrieveMissingFeedsAsyncTask;
import fr.gouv.etalab.mastodon.asynctasks.RetrieveRepliesAsyncTask;
import fr.gouv.etalab.mastodon.client.APIResponse;
import fr.gouv.etalab.mastodon.drawers.StatusListAdapter;
import fr.gouv.etalab.mastodon.helper.Helper;
import fr.gouv.etalab.mastodon.interfaces.OnRetrieveMissingFeedsInterface;
import fr.gouv.etalab.mastodon.interfaces.OnRetrieveRepliesInterface;
import fr.gouv.etalab.mastodon.services.StreamingFederatedTimelineService;
import fr.gouv.etalab.mastodon.services.StreamingLocalTimelineService;
import mastodon.etalab.gouv.fr.mastodon.R;
import fr.gouv.etalab.mastodon.asynctasks.RetrieveFeedsAsyncTask;
import fr.gouv.etalab.mastodon.client.Entities.Status;
@ -51,7 +59,7 @@ import fr.gouv.etalab.mastodon.interfaces.OnRetrieveFeedsInterface;
* Created by Thomas on 24/04/2017.
* Fragment to display content related to status
*/
public class DisplayStatusFragment extends Fragment implements OnRetrieveFeedsInterface, OnRetrieveRepliesInterface {
public class DisplayStatusFragment extends Fragment implements OnRetrieveFeedsInterface, OnRetrieveRepliesInterface, OnRetrieveMissingFeedsInterface {
private boolean flag_loading;
@ -75,9 +83,8 @@ public class DisplayStatusFragment extends Fragment implements OnRetrieveFeedsIn
private boolean hideHeader;
private String instanceValue;
private String lastReadStatus;
private String userId;
public static ArrayList<Status> tempStatuses = new ArrayList<>();
private int lastTotalItemCount = 0;
private Intent streamingFederatedIntent, streamingLocalIntent;
private Date lastRefreshPublic, lastRefreshLocal;
public DisplayStatusFragment(){
}
@ -119,7 +126,7 @@ public class DisplayStatusFragment extends Fragment implements OnRetrieveFeedsIn
positionSpinnerTrans = sharedpreferences.getInt(Helper.SET_TRANSLATOR, Helper.TRANS_YANDEX);
swipeRefreshLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.swipeContainer);
behaviorWithAttachments = sharedpreferences.getInt(Helper.SET_ATTACHMENT_ACTION, Helper.ATTACHMENT_ALWAYS);
userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null);
String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null);
if( type == RetrieveFeedsAsyncTask.Type.HOME)
lastReadStatus = sharedpreferences.getString(Helper.LAST_HOMETIMELINE_MAX_ID + userId, null);
lv_status = (ListView) rootView.findViewById(R.id.lv_status);
@ -164,6 +171,10 @@ public class DisplayStatusFragment extends Fragment implements OnRetrieveFeedsIn
}
if(firstVisibleItem + visibleItemCount == totalItemCount ) {
if(!flag_loading ) {
if( type == RetrieveFeedsAsyncTask.Type.PUBLIC)
lastRefreshPublic = new Date();
if( type == RetrieveFeedsAsyncTask.Type.LOCAL)
lastRefreshLocal = new Date();
flag_loading = true;
if( type == RetrieveFeedsAsyncTask.Type.USER)
asyncTask = new RetrieveFeedsAsyncTask(context, type, targetedId, max_id, showMediaOnly, showPinned, DisplayStatusFragment.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
@ -190,6 +201,8 @@ public class DisplayStatusFragment extends Fragment implements OnRetrieveFeedsIn
flag_loading = true;
swiped = true;
MainActivity.countNewStatus = 0;
if( type == RetrieveFeedsAsyncTask.Type.PUBLIC)
lastRefreshPublic = new Date();
if( type == RetrieveFeedsAsyncTask.Type.USER)
asyncTask = new RetrieveFeedsAsyncTask(context, type, targetedId, max_id, showMediaOnly, showPinned, DisplayStatusFragment.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
else if( type == RetrieveFeedsAsyncTask.Type.TAG)
@ -201,13 +214,17 @@ public class DisplayStatusFragment extends Fragment implements OnRetrieveFeedsIn
swipeRefreshLayout.setColorSchemeResources(R.color.mastodonC4,
R.color.mastodonC2,
R.color.mastodonC3);
if( type == RetrieveFeedsAsyncTask.Type.PUBLIC)
lastRefreshPublic = new Date();
if( type == RetrieveFeedsAsyncTask.Type.LOCAL)
lastRefreshLocal = new Date();
if( type == RetrieveFeedsAsyncTask.Type.USER)
asyncTask = new RetrieveFeedsAsyncTask(context, type, targetedId, max_id, showMediaOnly, showPinned, DisplayStatusFragment.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
else if( type == RetrieveFeedsAsyncTask.Type.TAG)
asyncTask = new RetrieveFeedsAsyncTask(context, type, tag, targetedId, max_id, DisplayStatusFragment.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
else
else {
asyncTask = new RetrieveFeedsAsyncTask(context, type, max_id, DisplayStatusFragment.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
}else {
statusListAdapter.notifyDataSetChanged();
mainLoader.setVisibility(View.GONE);
@ -228,37 +245,6 @@ public class DisplayStatusFragment extends Fragment implements OnRetrieveFeedsIn
@Override
public void onResume() {
super.onResume();
if( type == RetrieveFeedsAsyncTask.Type.HOME && tempStatuses != null && tempStatuses.size() > 0 ){
ArrayList<String> knownId = new ArrayList<>();
for(Status st: statuses){
knownId.add(st.getId());
}
for(Status status: tempStatuses){
if( !knownId.contains(status.getId())){
int index = lv_status.getFirstVisiblePosition() + 1;
View v = lv_status.getChildAt(0);
int top = (v == null) ? 0 : v.getTop();
status.setReplies(new ArrayList<Status>());
statuses.add(0,status);
statusListAdapter.notifyDataSetChanged();
lv_status.setSelectionFromTop(index, top);
if (textviewNoAction.getVisibility() == View.VISIBLE)
textviewNoAction.setVisibility(View.GONE);
MainActivity.countNewStatus++;
}
}
if( getActivity() != null && getActivity().getClass().isInstance(MainActivity.class))
((MainActivity)context).updateHomeCounter();
//Resets value for the counter but doesn't update it
MainActivity.countNewStatus = 0;
tempStatuses.clear();
tempStatuses = new ArrayList<>();
}
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
@ -275,7 +261,7 @@ public class DisplayStatusFragment extends Fragment implements OnRetrieveFeedsIn
@Override
public void onRetrieveFeeds(APIResponse apiResponse, boolean refreshData) {
public void onRetrieveFeeds(APIResponse apiResponse) {
mainLoader.setVisibility(View.GONE);
nextElementLoader.setVisibility(View.GONE);
//Discards 404 - error which can often happen due to toots which have been deleted
@ -321,6 +307,8 @@ public class DisplayStatusFragment extends Fragment implements OnRetrieveFeedsIn
}
if( firstLoad && type == RetrieveFeedsAsyncTask.Type.HOME) {
//Update the id of the last toot retrieved
MainActivity.lastHomeId = statuses.get(0).getId();
SharedPreferences.Editor editor = sharedpreferences.edit();
String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null);
editor.putString(Helper.LAST_HOMETIMELINE_MAX_ID + userId, statuses.get(0).getId());
@ -347,12 +335,18 @@ public class DisplayStatusFragment extends Fragment implements OnRetrieveFeedsIn
}
}
/**
* Deals with new status coming from the streaming api
* @param status Status
*/
public void refresh(Status status){
//New data are available
if( type == RetrieveFeedsAsyncTask.Type.HOME ) {
if( type == RetrieveFeedsAsyncTask.Type.HOME) {
if (context == null)
return;
if (status != null) {
//Update the id of the last toot retrieved
MainActivity.lastHomeId = status.getId();
int index = lv_status.getFirstVisiblePosition() + 1;
View v = lv_status.getChildAt(0);
int top = (v == null) ? 0 : v.getTop();
@ -367,15 +361,97 @@ public class DisplayStatusFragment extends Fragment implements OnRetrieveFeedsIn
if (textviewNoAction.getVisibility() == View.VISIBLE)
textviewNoAction.setVisibility(View.GONE);
}
}else if(type == RetrieveFeedsAsyncTask.Type.PUBLIC || type == RetrieveFeedsAsyncTask.Type.LOCAL){
if (context == null)
return;
//Avoids the array to be too big...
if (status != null) {
if (lv_status.getFirstVisiblePosition() == 0) {
status.setReplies(new ArrayList<Status>());
status.setNew(false);
statuses.add(0, status);
statusListAdapter.notifyDataSetChanged();
} else {
status.setReplies(new ArrayList<Status>());
statuses.add(0, status);
}
if (textviewNoAction.getVisibility() == View.VISIBLE)
textviewNoAction.setVisibility(View.GONE);
}
}
}
@Override
public void onResume(){
super.onResume();
if( type == RetrieveFeedsAsyncTask.Type.PUBLIC){
if( getUserVisibleHint() ){
SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedpreferences.edit();
String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null);
editor.putBoolean(Helper.SHOULD_CONTINUE_STREAMING_FEDERATED+userId, true);
editor.apply();
streamingFederatedIntent = new Intent(context, StreamingFederatedTimelineService.class);
context.startService(streamingFederatedIntent);
}
Calendar date = Calendar.getInstance();
long t = date.getTimeInMillis();
Date newDate = new Date(t - TimeUnit.SECONDS.toMillis(20));
if( lastRefreshPublic.before(newDate)){
lastRefreshPublic = new Date();
max_id = null;
statuses = new ArrayList<>();
firstLoad = true;
flag_loading = true;
swiped = true;
asyncTask = new RetrieveFeedsAsyncTask(context, type, null, DisplayStatusFragment.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
}else if (type == RetrieveFeedsAsyncTask.Type.LOCAL){
if( getUserVisibleHint() ){
SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedpreferences.edit();
String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null);
editor.putBoolean(Helper.SHOULD_CONTINUE_STREAMING_LOCAL+userId, true);
editor.apply();
streamingLocalIntent = new Intent(context, StreamingLocalTimelineService.class);
context.startService(streamingLocalIntent);
}
Calendar date = Calendar.getInstance();
long t = date.getTimeInMillis();
Date newDate = new Date(t - TimeUnit.SECONDS.toMillis(20));
if( lastRefreshLocal.before(newDate)){
lastRefreshLocal = new Date();
max_id = null;
statuses = new ArrayList<>();
firstLoad = true;
flag_loading = true;
swiped = true;
asyncTask = new RetrieveFeedsAsyncTask(context, type, null, DisplayStatusFragment.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
}
}
/**
* Called from main activity in onResume to retrieve missing toots (home timeline)
* @param sinceId String
*/
public void retrieveMissingToots(String sinceId){
asyncTask = new RetrieveMissingFeedsAsyncTask(context, sinceId, DisplayStatusFragment.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
/**
* When tab comes visible, first displayed toot is defined as read
* @param visible boolean
*/
@Override
public void setMenuVisibility(final boolean visible) {
super.setMenuVisibility(visible);
if( context == null)
return;
//Store last toot id for home timeline to avoid to notify for those that have been already seen
if (type == RetrieveFeedsAsyncTask.Type.HOME && visible && statuses != null && statuses.size() > 0) {
SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
@ -385,6 +461,65 @@ public class DisplayStatusFragment extends Fragment implements OnRetrieveFeedsIn
lastReadStatus = statuses.get(0).getId();
editor.apply();
}
if( type == RetrieveFeedsAsyncTask.Type.PUBLIC ){
if (visible) {
SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedpreferences.edit();
String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null);
editor.putBoolean(Helper.SHOULD_CONTINUE_STREAMING_FEDERATED+userId, true);
editor.apply();
streamingFederatedIntent = new Intent(context, StreamingFederatedTimelineService.class);
context.startService(streamingFederatedIntent);
}else {
if( streamingFederatedIntent != null){
SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedpreferences.edit();
String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null);
editor.putBoolean(Helper.SHOULD_CONTINUE_STREAMING_FEDERATED+userId, false);
editor.apply();
context.stopService(streamingFederatedIntent);
}
}
}else if (type == RetrieveFeedsAsyncTask.Type.LOCAL){
if (visible) {
SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedpreferences.edit();
String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null);
editor.putBoolean(Helper.SHOULD_CONTINUE_STREAMING_LOCAL+userId, true);
editor.apply();
streamingLocalIntent = new Intent(context, StreamingLocalTimelineService.class);
context.startService(streamingLocalIntent);
}else {
if( streamingLocalIntent != null){
SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedpreferences.edit();
String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null);
editor.putBoolean(Helper.SHOULD_CONTINUE_STREAMING_LOCAL+userId, false);
editor.apply();
context.stopService(streamingLocalIntent);
}
}
}
}
@Override
public void onStop(){
super.onStop();
if( type == RetrieveFeedsAsyncTask.Type.PUBLIC && streamingFederatedIntent != null){
SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedpreferences.edit();
String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null);
editor.putBoolean(Helper.SHOULD_CONTINUE_STREAMING_FEDERATED+userId, false);
editor.apply();
context.stopService(streamingFederatedIntent);
}else if(type == RetrieveFeedsAsyncTask.Type.LOCAL && streamingLocalIntent != null){
SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedpreferences.edit();
String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null);
editor.putBoolean(Helper.SHOULD_CONTINUE_STREAMING_LOCAL+userId, false);
editor.apply();
context.stopService(streamingLocalIntent);
}
}
public void scrollToTop(){
@ -419,4 +554,25 @@ public class DisplayStatusFragment extends Fragment implements OnRetrieveFeedsIn
}
statusListAdapter.notifyDataSetChanged();
}
@Override
public void onRetrieveMissingFeeds(List<Status> statuses) {
if( statuses != null && statuses.size() > 0) {
ArrayList<String> knownId = new ArrayList<>();
for (Status st : this.statuses) {
knownId.add(st.getId());
}
for (int i = statuses.size()-1 ; i >= 0 ; i--) {
if (!knownId.contains(statuses.get(i).getId())) {
statuses.get(i).setNew(true);
MainActivity.countNewStatus++;
this.statuses.add(0, statuses.get(i));
}
}
statusListAdapter.notifyDataSetChanged();
try {
((MainActivity) context).updateHomeCounter();
}catch (Exception ignored){}
}
}
}

View File

@ -181,6 +181,10 @@ 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 SHOULD_CONTINUE_STREAMING = "should_continue_streaming";
public static final String SHOULD_CONTINUE_STREAMING_FEDERATED = "should_continue_streaming_federated";
public static final String SHOULD_CONTINUE_STREAMING_LOCAL = "should_continue_streaming_local";
public static final String CLIP_BOARD = "clipboard";
//Notifications
public static final int NOTIFICATION_INTENT = 1;
@ -250,9 +254,10 @@ public class Helper {
public static final String INTENT_ACTION = "intent_action";
//Receiver
public static final String SEARCH_VALIDATE_ACCOUNT = "search_validate_account";
public static final String HEADER_ACCOUNT = "header_account";
public static final String RECEIVE_DATA = "receive_data";
public static final String RECEIVE_FEDERATED_DATA = "receive_federated_data";
public static final String RECEIVE_LOCAL_DATA = "receive_local_data";
public static final String RECEIVE_PICTURE = "receive_picture";
//User agent
public static final String USER_AGENT = "Mastalab/"+ BuildConfig.VERSION_NAME + " Android/"+ Build.VERSION.RELEASE;
@ -923,6 +928,8 @@ public class Helper {
final NavigationView navigationView = (NavigationView) activity.findViewById(R.id.nav_view);
navigationView.getMenu().clear();
MainActivity.lastNotificationId = null;
MainActivity.lastHomeId = null;
navigationView.inflateMenu(R.menu.activity_main_drawer);
SQLiteDatabase db = Sqlite.getInstance(activity, Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
Account account = new AccountDAO(activity,db).getAccountByID(userID);
@ -937,17 +944,6 @@ public class Helper {
}
SharedPreferences sharedpreferences = activity.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
String oldUserId = sharedpreferences.getString(Helper.PREF_KEY_ID, null);
//User wont' be the same, temp values are cleared
if( oldUserId != null && !oldUserId.equals(userID)){
if( DisplayStatusFragment.tempStatuses != null) {
DisplayStatusFragment.tempStatuses.clear();
DisplayStatusFragment.tempStatuses = new ArrayList<>();
}
if( DisplayNotificationsFragment.tempNotifications != null) {
DisplayNotificationsFragment.tempNotifications.clear();
DisplayNotificationsFragment.tempNotifications = new ArrayList<>();
}
}
SharedPreferences.Editor editor = sharedpreferences.edit();
editor.putString(Helper.PREF_KEY_OAUTH_TOKEN, account.getToken());
editor.putString(Helper.PREF_KEY_ID, account.getId());
@ -967,9 +963,8 @@ public class Helper {
final Paint paint = new Paint();
final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
final RectF rectF = new RectF(rect);
final float roundPx = roundPixelSize;
paint.setAntiAlias(true);
canvas.drawRoundRect(rectF,roundPx,roundPx, paint);
canvas.drawRoundRect(rectF, (float) roundPixelSize, (float) roundPixelSize, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(bitmap, rect, rect, paint);
return output;

View File

@ -21,5 +21,5 @@ import fr.gouv.etalab.mastodon.client.APIResponse;
* Interface when status have been retrieved
*/
public interface OnRetrieveFeedsInterface {
void onRetrieveFeeds(APIResponse apiResponse, boolean refreshData);
void onRetrieveFeeds(APIResponse apiResponse);
}

View File

@ -0,0 +1,26 @@
/* 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 <http://www.gnu.org/licenses>. */
package fr.gouv.etalab.mastodon.interfaces;
import java.util.List;
import fr.gouv.etalab.mastodon.client.Entities.Status;
/**
* Created by Thomas on 27/09/2017.
* Interface when missing status have been retrieved
*/
public interface OnRetrieveMissingFeedsInterface {
void onRetrieveMissingFeeds(List<Status> statuses);
}

View File

@ -0,0 +1,27 @@
/* 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 <http://www.gnu.org/licenses>. */
package fr.gouv.etalab.mastodon.interfaces;
import java.util.List;
import fr.gouv.etalab.mastodon.client.Entities.Notification;
/**
* Created by Thomas on 27/09/2017.
* Interface when missing notifications have been retrieved
*/
public interface OnRetrieveMissingNotificationsInterface {
void onRetrieveMissingNotifications(List<Notification> notifications);
}

View File

@ -0,0 +1,33 @@
package fr.gouv.etalab.mastodon.services;
/* Copyright 2017 Thomas Schneider
*
* This file is a part of Mastalab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Mastalab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Mastalab; if not,
* see <http://www.gnu.org/licenses>. */
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
/**
* Created by Thomas on 26/09/2017.
* BroadcastReceiver for restarting the service for listening federated timeline
*/
public class RestartFederatedServiceReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Intent streamingServiceIntent = new Intent(context.getApplicationContext(), StreamingFederatedTimelineService.class);
context.startService(streamingServiceIntent);
}
}

View File

@ -0,0 +1,33 @@
package fr.gouv.etalab.mastodon.services;
/* Copyright 2017 Thomas Schneider
*
* This file is a part of Mastalab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Mastalab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Mastalab; if not,
* see <http://www.gnu.org/licenses>. */
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
/**
* Created by Thomas on 29/09/2017.
* BroadcastReceiver for restarting the service for listening local timeline
*/
public class RestartLocalServiceReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Intent streamingServiceIntent = new Intent(context.getApplicationContext(), StreamingLocalTimelineService.class);
context.startService(streamingServiceIntent);
}
}

View File

@ -0,0 +1,172 @@
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 <http://www.gnu.org/licenses>. */
import android.app.IntentService;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.os.SystemClock;
import android.support.annotation.Nullable;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import javax.net.ssl.HttpsURLConnection;
import fr.gouv.etalab.mastodon.client.API;
import fr.gouv.etalab.mastodon.client.Entities.Account;
import fr.gouv.etalab.mastodon.client.Entities.Notification;
import fr.gouv.etalab.mastodon.client.Entities.Status;
import fr.gouv.etalab.mastodon.client.TLSSocketFactory;
import fr.gouv.etalab.mastodon.helper.Helper;
import fr.gouv.etalab.mastodon.sqlite.AccountDAO;
import fr.gouv.etalab.mastodon.sqlite.Sqlite;
/**
* Created by Thomas on 26/09/2017.
* Manage service for streaming api for federated timeline
*/
public class StreamingFederatedTimelineService extends IntentService {
/**
* Creates an IntentService. Invoked by your subclass's constructor.
*
* @param name Used to name the worker thread, important only for debugging.
*/
public StreamingFederatedTimelineService(String name) {
super(name);
}
public StreamingFederatedTimelineService() {
super("StreamingFederatedTimelineService");
}
private static HttpsURLConnection httpsURLConnection;
protected Account account;
public void onCreate() {
super.onCreate();
SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
boolean display_global = sharedpreferences.getBoolean(Helper.SET_DISPLAY_GLOBAL, true);
if( !display_global){
stopSelf();
}
SharedPreferences.Editor editor = sharedpreferences.edit();
String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null);
editor.putBoolean(Helper.SHOULD_CONTINUE_STREAMING_FEDERATED+userId, true);
editor.apply();
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null);
InputStream inputStream;
BufferedReader reader = null;
Account accountStream = null;
if( httpsURLConnection != null)
httpsURLConnection.disconnect();
if( userId != null) {
SQLiteDatabase db = Sqlite.getInstance(getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
accountStream = new AccountDAO(getApplicationContext(), db).getAccountByID(userId);
}
if( accountStream != null){
try {
URL url = new URL("https://" + accountStream.getInstance() + "/api/v1/streaming/public");
httpsURLConnection = (HttpsURLConnection) url.openConnection();
httpsURLConnection.setRequestProperty("Content-Type", "application/json");
httpsURLConnection.setRequestProperty("Authorization", "Bearer " + accountStream.getToken());
httpsURLConnection.setRequestProperty("Connection", "Keep-Alive");
httpsURLConnection.setRequestProperty("Keep-Alive", "header");
httpsURLConnection.setRequestProperty("Connection", "close");
httpsURLConnection.setSSLSocketFactory(new TLSSocketFactory());
httpsURLConnection.setRequestMethod("GET");
httpsURLConnection.setConnectTimeout(70000);
httpsURLConnection.setReadTimeout(70000);
inputStream = new BufferedInputStream(httpsURLConnection.getInputStream());
reader = new BufferedReader(new InputStreamReader(inputStream));
String event;
while((event = reader.readLine()) != null) {
if (!event.startsWith("data: ")) {
continue;
}
if (!sharedpreferences.getBoolean(Helper.SHOULD_CONTINUE_STREAMING_FEDERATED + accountStream.getId(), true)) {
stopSelf();
return;
}
event = event.substring(6);
if( event.matches("^[0-9]{1,}$"))
continue;
try {
JSONObject eventJson = new JSONObject(event);
onRetrieveStreaming(accountStream, eventJson);
} catch (JSONException e) {
e.printStackTrace();
}
}
} catch (Exception e) {
e.printStackTrace();
}finally {
if(reader != null){
try{
reader.close();
}catch (IOException e){
e.printStackTrace();
}
}
if( sharedpreferences.getBoolean(Helper.SHOULD_CONTINUE_STREAMING_FEDERATED + accountStream.getId(), true)) {
SystemClock.sleep(1000);
sendBroadcast(new Intent("RestartStreamingFederatedService"));
}
}
}
}
public void onRetrieveStreaming(Account account, JSONObject response) {
if( response == null )
return;
Status status ;
Bundle b = new Bundle();
status = API.parseStatuses(getApplicationContext(), response);
status.setReplies(new ArrayList<Status>());
status.setNew(true);
b.putParcelable("data", status);
if( account != null)
b.putString("userIdService",account.getId());
Intent intentBC = new Intent(Helper.RECEIVE_FEDERATED_DATA);
intentBC.putExtras(b);
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intentBC);
}
}

View File

@ -0,0 +1,169 @@
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 <http://www.gnu.org/licenses>. */
import android.app.IntentService;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.os.SystemClock;
import android.support.annotation.Nullable;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import javax.net.ssl.HttpsURLConnection;
import fr.gouv.etalab.mastodon.client.API;
import fr.gouv.etalab.mastodon.client.Entities.Account;
import fr.gouv.etalab.mastodon.client.Entities.Status;
import fr.gouv.etalab.mastodon.client.TLSSocketFactory;
import fr.gouv.etalab.mastodon.helper.Helper;
import fr.gouv.etalab.mastodon.sqlite.AccountDAO;
import fr.gouv.etalab.mastodon.sqlite.Sqlite;
/**
* Created by Thomas on 29/09/2017.
* Manage service for streaming api for local timeline
*/
public class StreamingLocalTimelineService extends IntentService {
/**
* Creates an IntentService. Invoked by your subclass's constructor.
*
* @param name Used to name the worker thread, important only for debugging.
*/
public StreamingLocalTimelineService(String name) {
super(name);
}
public StreamingLocalTimelineService() {
super("StreamingLocalTimelineService");
}
private static HttpsURLConnection httpsURLConnection;
protected Account account;
public void onCreate() {
super.onCreate();
SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
boolean display_local = sharedpreferences.getBoolean(Helper.SET_DISPLAY_LOCAL, true);
if( !display_local){
stopSelf();
}
SharedPreferences.Editor editor = sharedpreferences.edit();
String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null);
editor.putBoolean(Helper.SHOULD_CONTINUE_STREAMING_LOCAL+userId, true);
editor.apply();
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null);
InputStream inputStream;
BufferedReader reader = null;
Account accountStream = null;
if( httpsURLConnection != null)
httpsURLConnection.disconnect();
if( userId != null) {
SQLiteDatabase db = Sqlite.getInstance(getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
accountStream = new AccountDAO(getApplicationContext(), db).getAccountByID(userId);
}
if( accountStream != null){
try {
URL url = new URL("https://" + accountStream.getInstance() + "/api/v1/streaming/public/local");
httpsURLConnection = (HttpsURLConnection) url.openConnection();
httpsURLConnection.setRequestProperty("Content-Type", "application/json");
httpsURLConnection.setRequestProperty("Authorization", "Bearer " + accountStream.getToken());
httpsURLConnection.setRequestProperty("Connection", "Keep-Alive");
httpsURLConnection.setRequestProperty("Keep-Alive", "header");
httpsURLConnection.setRequestProperty("Connection", "close");
httpsURLConnection.setSSLSocketFactory(new TLSSocketFactory());
httpsURLConnection.setRequestMethod("GET");
httpsURLConnection.setConnectTimeout(70000);
httpsURLConnection.setReadTimeout(70000);
inputStream = new BufferedInputStream(httpsURLConnection.getInputStream());
reader = new BufferedReader(new InputStreamReader(inputStream));
String event;
while((event = reader.readLine()) != null) {
if (!event.startsWith("data: ")) {
continue;
}
if (!sharedpreferences.getBoolean(Helper.SHOULD_CONTINUE_STREAMING_LOCAL + accountStream.getId(), true)) {
stopSelf();
return;
}
event = event.substring(6);
if( event.matches("^[0-9]{1,}$"))
continue;
try {
JSONObject eventJson = new JSONObject(event);
onRetrieveStreaming(accountStream, eventJson);
} catch (JSONException e) {
e.printStackTrace();
}
}
} catch (Exception e) {
e.printStackTrace();
}finally {
if(reader != null){
try{
reader.close();
}catch (IOException e){
e.printStackTrace();
}
}
if( sharedpreferences.getBoolean(Helper.SHOULD_CONTINUE_STREAMING_LOCAL + accountStream.getId(), true)) {
SystemClock.sleep(1000);
sendBroadcast(new Intent("RestartStreamingLocalService"));
}
}
}
}
public void onRetrieveStreaming(Account account, JSONObject response) {
if( response == null )
return;
Status status ;
Bundle b = new Bundle();
status = API.parseStatuses(getApplicationContext(), response);
status.setReplies(new ArrayList<Status>());
status.setNew(true);
b.putParcelable("data", status);
if( account != null)
b.putString("userIdService",account.getId());
Intent intentBC = new Intent(Helper.RECEIVE_LOCAL_DATA);
intentBC.putExtras(b);
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intentBC);
}
}

View File

@ -22,6 +22,7 @@ import android.os.Bundle;
import android.os.SystemClock;
import android.support.annotation.Nullable;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
import org.json.JSONException;
@ -55,7 +56,6 @@ public class StreamingService extends IntentService {
private EventStreaming lastEvent;
/**
* Creates an IntentService. Invoked by your subclass's constructor.
*
@ -79,6 +79,11 @@ public class StreamingService extends IntentService {
public void onCreate() {
super.onCreate();
SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedpreferences.edit();
String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null);
editor.putBoolean(Helper.SHOULD_CONTINUE_STREAMING+userId, true);
editor.apply();
}
@ -114,6 +119,8 @@ public class StreamingService extends IntentService {
String event;
EventStreaming eventStreaming;
while((event = reader.readLine()) != null) {
if( !sharedpreferences.getBoolean(Helper.SHOULD_CONTINUE_STREAMING + accountStream.getId(), true) )
stopSelf();
if ((lastEvent == EventStreaming.NONE || lastEvent == null) && !event.startsWith("data: ")) {
switch (event.trim()) {
case "event: update":

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_mute"
android:title="@string/more_action_1"
app:showAsAction="never" />
<item
android:id="@+id/action_block"
android:title="@string/more_action_2"
app:showAsAction="never" />
<item
android:id="@+id/action_report"
android:title="@string/more_action_3"
app:showAsAction="never" />
<item
android:id="@+id/action_remove"
android:title="@string/more_action_4"
app:showAsAction="never" />
<item
android:id="@+id/action_copy"
android:title="@string/more_action_5"
app:showAsAction="never" />
<item
android:id="@+id/action_share"
android:title="@string/more_action_6"
app:showAsAction="never" />
<item
android:id="@+id/action_mention"
android:title="@string/more_action_7"
app:showAsAction="never" />
</menu>

View File

@ -98,28 +98,23 @@
<string name="reblog_remove">Toot nicht mehr teilen?</string>
<string name="pin_add">Diesen toot anheften?</string>
<string name="pin_remove">Diesen toot nicht mehr anheften?</string>
<string-array name="more_action">
<item>Stummschalten</item>
<item>Blockieren</item>
<item>Melden</item>
<item>Kopieren</item>
<item>Teilen</item>
<item>Erwähnen</item>
</string-array>
<string-array name="more_action_owner">
<item>Entfernen</item>
<item>Kopieren</item>
<item>Teilen</item>
<item>Erwähnen</item>
</string-array>
<string name="more_action_1">Stummschalten</string>
<string name="more_action_2">Blockieren</string>
<string name="more_action_3">Melden</string>
<string name="more_action_4">Entfernen</string>
<string name="more_action_5">Kopieren</string>
<string name="more_action_6">Teilen</string>
<string name="more_action_7">Erwähnen</string>
<string-array name="more_action_confirm">
<item>Nutzer stummschalten?</item>
<item>Diesen Nutzer blockieren?</item>
<item>Diesen toot melden?</item>
<item>null</item> <!-- Ugly hack to fix confirm box-->
<item>null</item>
<item>null</item>
</string-array>
@ -130,9 +125,6 @@
<string-array name="more_action_owner_confirm">
<item>Entferne diesen toot?</item>
<item>null</item> <!-- Ugly hack to fix confirm box-->
<item>null</item>
<item>null</item>
</string-array>
<plurals name="preview_replies">

View File

@ -96,28 +96,23 @@
<string name="favourite_remove">Supprimer ce pouet des favoris ?</string>
<string name="reblog_add">Partager ce pouet ?</string>
<string name="reblog_remove">Supprimer ce pouet des partages ?</string>
<string-array name="more_action">
<item>Masquer</item>
<item>Bloquer</item>
<item>Signaler</item>
<item>Copier</item>
<item>Partager</item>
<item>Mentionner</item>
</string-array>
<string-array name="more_action_owner">
<item>Supprimer</item>
<item>Copier</item>
<item>Partager</item>
<item>Mentionner</item>
</string-array>
<string name="more_action_1">Masquer</string>
<string name="more_action_2">Bloquer</string>
<string name="more_action_3">Signaler</string>
<string name="more_action_4">Supprimer</string>
<string name="more_action_5">Copier</string>
<string name="more_action_6">Partager</string>
<string name="more_action_7">Mentionner</string>
<string-array name="more_action_confirm">
<item>Masquer ce compte ?</item>
<item>Bloquer ce compte ?</item>
<item>Signaler ce pouet ?</item>
<item>null</item> <!-- Ugly hack to fix confirm box-->
<item>null</item>
<item>null</item>
</string-array>
@ -128,9 +123,6 @@
<string-array name="more_action_owner_confirm">
<item>Supprimer ce pouet ?</item>
<item>null</item> <!-- Ugly hack to fix confirm box-->
<item>null</item>
<item>null</item>
</string-array>
<plurals name="preview_replies">

View File

@ -107,33 +107,25 @@
<item>Share</item>
<item>Mention</item>
</string-array>
<string-array name="more_action_owner">
<item>Remove</item>
<item>Copy</item>
<item>Share</item>
<item>Mention</item>
</string-array>
<string name="more_action_1">Mute</string>
<string name="more_action_2">Block</string>
<string name="more_action_3">Report</string>
<string name="more_action_4">Remove</string>
<string name="more_action_5">Copy</string>
<string name="more_action_6">Share</string>
<string name="more_action_7">Mention</string>
<string-array name="more_action_confirm">
<item>Mute this account?</item>
<item>Block this account?</item>
<item>Report this toot?</item>
<item>null</item> <!-- Ugly hack to fix confirm box-->
<item>null</item>
<item>null</item>
</string-array>
<string-array name="more_action_confirm_account">
<item>Unmute this account?</item>
<item>Unblock this account?</item>
</string-array>
<string-array name="more_action_owner_confirm">
<item>Remove this toot?</item>
<item>null</item> <!-- Ugly hack to fix confirm box-->
<item>null</item>
<item>null</item>
</string-array>
<plurals name="preview_replies">

View File

@ -101,8 +101,6 @@ import fr.gouv.etalab.mastodon.fragments.TabLayoutSettingsFragment;
import fr.gouv.etalab.mastodon.sqlite.AccountDAO;
import mastodon.etalab.gouv.fr.mastodon.R;
import static fr.gouv.etalab.mastodon.fragments.DisplayNotificationsFragment.tempNotifications;
import static fr.gouv.etalab.mastodon.fragments.DisplayStatusFragment.tempStatuses;
import static fr.gouv.etalab.mastodon.helper.Helper.CHANGE_THEME_INTENT;
import static fr.gouv.etalab.mastodon.helper.Helper.CHANGE_USER_INTENT;
import static fr.gouv.etalab.mastodon.helper.Helper.HOME_TIMELINE_INTENT;
@ -134,16 +132,16 @@ public class MainActivity extends AppCompatActivity
private RelativeLayout main_app_container;
private Stack<Integer> stackBack = new Stack<>();
private DisplayStatusFragment homeFragment;
private DisplayStatusFragment homeFragment, federatedFragment, localFragment;
private DisplayNotificationsFragment notificationsFragment;
private static final int ERROR_DIALOG_REQUEST_CODE = 97;
private BroadcastReceiver receive_data;
private BroadcastReceiver receive_data, receive_federated_data, receive_local_data;
private boolean display_local, display_global;
public static int countNewStatus = 0;
public static int countNewNotifications = 0;
private String userIdService;
private Intent streamingIntent;
public static boolean broadCastRegistred = false;
public static String lastHomeId = null, lastNotificationId = null;
public MainActivity() {
}
@ -154,53 +152,9 @@ public class MainActivity extends AppCompatActivity
final SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, android.content.Context.MODE_PRIVATE);
receive_data = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Bundle b = intent.getExtras();
StreamingService.EventStreaming eventStreaming = (StreamingService.EventStreaming) intent.getSerializableExtra("eventStreaming");
userIdService = b.getString("userIdService", null);
String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null);
if( userIdService != null && userIdService.equals(userId)) {
if (eventStreaming == StreamingService.EventStreaming.NOTIFICATION) {
Notification notification = b.getParcelable("data");
if (notificationsFragment != null) {
notificationsFragment.refresh(notification);
} else {
tempNotifications.add(notification);
}
} else if (eventStreaming == StreamingService.EventStreaming.UPDATE) {
Status status = b.getParcelable("data");
if (homeFragment != null) {
homeFragment.refresh(status);
} else {
tempStatuses.add(status);
}
} else if (eventStreaming == StreamingService.EventStreaming.DELETE) {
String id = b.getString("id");
if (notificationsFragment != null) {
if (notificationsFragment.getUserVisibleHint()) {
} else {
}
}
}
updateNotifCounter();
updateHomeCounter();
}
}
};
streamingIntent = new Intent(this, StreamingService.class);
startService(streamingIntent);
if( !broadCastRegistred) {
LocalBroadcastManager.getInstance(this).registerReceiver(receive_data, new IntentFilter(Helper.RECEIVE_DATA));
broadCastRegistred = true;
}
ProviderInstaller.installIfNeededAsync(this, this);
final int theme = sharedpreferences.getInt(Helper.SET_THEME, Helper.THEME_DARK);
if( theme == Helper.THEME_LIGHT){
setTheme(R.style.AppTheme_NoActionBar);
@ -756,6 +710,12 @@ public class MainActivity extends AppCompatActivity
matchingIntent = true;
}else if( extras.getInt(INTENT_ACTION) == CHANGE_USER_INTENT){
unCheckAllMenuItems(navigationView);
if( tabLayout.getTabAt(0) != null)
//noinspection ConstantConditions
tabLayout.getTabAt(0).select();
if( !toolbar_search.isIconified() ) {
toolbar_search.setIconified(true);
}
matchingIntent = true;
}
}else if( Intent.ACTION_SEND.equals(action) && type != null ) {
@ -898,10 +858,105 @@ public class MainActivity extends AppCompatActivity
//Proceeds to update of the authenticated account
if(Helper.isLoggedIn(getApplicationContext()))
new UpdateAccountInfoByIDAsyncTask(getApplicationContext(), MainActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
if( lastHomeId != null && homeFragment != null){
homeFragment.retrieveMissingToots(lastHomeId);
}
if( lastNotificationId != null && notificationsFragment != null){
notificationsFragment.retrieveMissingNotifications(lastNotificationId);
}
}
@Override
public void onStart(){
super.onStart();
final SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
receive_federated_data = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Bundle b = intent.getExtras();
userIdService = b.getString("userIdService", null);
String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null);
if( userIdService != null && userIdService.equals(userId)) {
Status status = b.getParcelable("data");
if (federatedFragment != null) {
federatedFragment.refresh(status);
}
}
}
};
receive_local_data = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Bundle b = intent.getExtras();
userIdService = b.getString("userIdService", null);
String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null);
if( userIdService != null && userIdService.equals(userId)) {
Status status = b.getParcelable("data");
if (localFragment != null) {
localFragment.refresh(status);
}
}
}
};
receive_data = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Bundle b = intent.getExtras();
StreamingService.EventStreaming eventStreaming = (StreamingService.EventStreaming) intent.getSerializableExtra("eventStreaming");
userIdService = b.getString("userIdService", null);
String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null);
if( userIdService != null && userIdService.equals(userId)) {
if (eventStreaming == StreamingService.EventStreaming.NOTIFICATION) {
Notification notification = b.getParcelable("data");
if (notificationsFragment != null) {
notificationsFragment.refresh(notification);
}
} else if (eventStreaming == StreamingService.EventStreaming.UPDATE) {
Status status = b.getParcelable("data");
if (homeFragment != null) {
homeFragment.refresh(status);
}
} else if (eventStreaming == StreamingService.EventStreaming.DELETE) {
String id = b.getString("id");
if (notificationsFragment != null) {
if (notificationsFragment.getUserVisibleHint()) {
} else {
}
}
}
updateNotifCounter();
updateHomeCounter();
}
}
};
streamingIntent = new Intent(this, StreamingService.class);
startService(streamingIntent);
LocalBroadcastManager.getInstance(this).registerReceiver(receive_data, new IntentFilter(Helper.RECEIVE_DATA));
LocalBroadcastManager.getInstance(this).registerReceiver(receive_federated_data, new IntentFilter(Helper.RECEIVE_FEDERATED_DATA));
LocalBroadcastManager.getInstance(this).registerReceiver(receive_local_data, new IntentFilter(Helper.RECEIVE_LOCAL_DATA));
}
@Override
public void onStop(){
super.onStop();
if( streamingIntent != null) {
SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedpreferences.edit();
String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null);
editor.putBoolean(Helper.SHOULD_CONTINUE_STREAMING_FEDERATED+userId, false);
stopService(streamingIntent);
editor.apply();
}
if( receive_data != null)
LocalBroadcastManager.getInstance(this).unregisterReceiver(receive_data);
if( receive_federated_data != null)
LocalBroadcastManager.getInstance(this).unregisterReceiver(receive_federated_data);
if( receive_local_data != null)
LocalBroadcastManager.getInstance(this).unregisterReceiver(receive_local_data);
}
@Override
protected void onPause() {
@ -912,10 +967,6 @@ public class MainActivity extends AppCompatActivity
@Override
public void onDestroy(){
super.onDestroy();
if( streamingIntent != null)
stopService(streamingIntent);
LocalBroadcastManager.getInstance(this).unregisterReceiver(receive_data);
broadCastRegistred = false;
}
@SuppressWarnings("StatementWithEmptyBody")
@ -1168,6 +1219,16 @@ public class MainActivity extends AppCompatActivity
case 1:
notificationsFragment = (DisplayNotificationsFragment) createdFragment;
break;
case 2:
if ( !display_local && display_global)
federatedFragment = (DisplayStatusFragment) createdFragment;
if( display_local)
localFragment = (DisplayStatusFragment) createdFragment;
break;
case 3:
if( display_local && display_global)
federatedFragment = (DisplayStatusFragment) createdFragment;
break;
}
return createdFragment;
}