SWADroid/SWADroid/src/main/java/es/ugr/swad/swadroid/modules/notifications/Notifications.java

811 lines
25 KiB
Java

/*
* This file is part of SWADroid.
*
* Copyright (C) 2010 Juan Miguel Boyero Corral <juanmi1982@gmail.com>
*
* SWADroid 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.
*
* SWADroid 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 SWADroid. If not, see <http://www.gnu.org/licenses/>.
*/
package es.ugr.swad.swadroid.modules.notifications;
import android.accounts.Account;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Build;
import android.os.Bundle;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AbsListView;
import android.widget.ExpandableListView;
import android.widget.ExpandableListView.OnChildClickListener;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import org.ksoap2.serialization.SoapObject;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
import es.ugr.swad.swadroid.Constants;
import es.ugr.swad.swadroid.R;
import es.ugr.swad.swadroid.database.DataBaseHelper;
import es.ugr.swad.swadroid.gui.AlertNotificationFactory;
import es.ugr.swad.swadroid.model.Model;
import es.ugr.swad.swadroid.model.SWADNotification;
import es.ugr.swad.swadroid.modules.Module;
import es.ugr.swad.swadroid.modules.login.Login;
import es.ugr.swad.swadroid.sync.SyncUtils;
import es.ugr.swad.swadroid.utils.DateTimeUtils;
import es.ugr.swad.swadroid.utils.Utils;
import es.ugr.swad.swadroid.webservices.SOAPClient;
/**
* Notifications module for get user's notifications
* @see <a href="https://openswad.org/ws/#getNotifications">getNotifications</a>
*
* @author Juan Miguel Boyero Corral <juanmi1982@gmail.com>
* @author Antonio Aguilera Malagon <aguilerin@gmail.com>
* @author Helena Rodriguez Gijon <hrgijon@gmail.com>
*/
public class Notifications extends Module implements
SwipeRefreshLayout.OnRefreshListener {
/**
* Unique identifier for notification alerts
*/
public static final int NOTIF_ALERT_ID = 1982;
/**
* Cursor orderby parameter
*/
private final String orderby = "eventTime DESC";
/**
* Notifications counter
*/
private int notifCount;
/**
* Error message returned by the synchronization service
*/
private String errorMessage;
/**
* Notifications tag name for Logcat
*/
private static final String TAG = Constants.APP_TAG + " Notifications";
/**
* Account type
*/
private static final String accountType = "es.ugr.swad.swadroid";
/**
* Synchronization authority
*/
private static final String authority = "es.ugr.swad.swadroid.content";
/**
* Synchronization receiver
*/
private static SyncReceiver receiver;
/**
* Synchronization account
*/
private static Account account;
/**
* Layout with "Pull to refresh" function
*/
private SwipeRefreshLayout refreshLayout;
/**
* Layout containing birthday message
*/
private LinearLayout mBirthdayLayout;
/**
* TextView containing birthday message
*/
private TextView mBirthdayTextView;
/**
* ListView container for notifications
*/
private ExpandableListView list;
/**
* Adapter container for notifications
*/
private NotificationsExpandableListAdapter adapter;
/**
* TextView for the empty notifications message
*/
private TextView emptyNotifTextView;
/**
* List of groups for ExpandableListView
*/
private ArrayList<String> groupItem;
/**
* List of childs for ExpandableListView
*/
private ArrayList<List<Model>> childItem;
/**
* Id for the not seen notifications group
*/
private int NOT_SEEN_GROUP_ID = 0;
/**
* Id for the seen notifications group
*/
private int SEEN_GROUP_ID = 1;
/**
* ListView click listener
*/
private OnChildClickListener clickListener = new OnChildClickListener() {
@Override
public boolean onChildClick(ExpandableListView parent, View v,
int groupPosition, int childPosition, long id) {
TextView notifCode = (TextView) v.findViewById(R.id.notifCode);
TextView code = (TextView) v.findViewById(R.id.eventCode);
TextView type = (TextView) v.findViewById(R.id.eventType);
TextView userPhoto = (TextView) v.findViewById(R.id.eventUserPhoto);
TextView sender = (TextView) v.findViewById(R.id.eventSender);
TextView course = (TextView) v.findViewById(R.id.eventLocation);
TextView summary = (TextView) v.findViewById(R.id.eventSummary);
TextView content = (TextView) v.findViewById(R.id.eventText);
TextView date = (TextView) v.findViewById(R.id.eventDate);
TextView time = (TextView) v.findViewById(R.id.eventTime);
TextView seenLocalText = (TextView) v.findViewById(R.id.seenLocal);
Intent activity = new Intent(getApplicationContext(),
NotificationItem.class);
activity.putExtra("notifCode", notifCode.getText().toString());
activity.putExtra("eventCode", code.getText().toString());
activity.putExtra("notificationType", type.getText().toString());
activity.putExtra("userPhoto", userPhoto.getText().toString());
activity.putExtra("sender", sender.getText().toString());
activity.putExtra("course", course.getText().toString());
activity.putExtra("summary", summary.getText().toString());
activity.putExtra("content", content.getText().toString());
activity.putExtra("date", date.getText().toString());
activity.putExtra("time", time.getText().toString());
activity.putExtra("seenLocal", seenLocalText.getText().toString());
startActivity(activity);
return true;
}
};
/**
* Refreshes data on screen
*/
private void refreshScreen() {
// Refresh data on screen
setChildGroupData();
hideSwipeProgress();
//If today is the user birthday, show birthday message
if((Login.getLoggedUser() != null) && (Login.getLoggedUser().getUserBirthday() != null)
&& DateTimeUtils.isBirthday(Login.getLoggedUser().getUserBirthday())) {
mBirthdayTextView.setText(getString(R.string.birthdayMsg).replace(
Constants.USERNAME_TEMPLATE, Login.getLoggedUser().getUserFirstname()));
mBirthdayLayout.setVisibility(View.VISIBLE);
} else {
mBirthdayLayout.setVisibility(View.GONE);
}
}
/**
* Sends to SWAD the "seen notifications" info
*/
private void sendReadNotifications() {
List<Model> markedNotificationsList;
String seenNotifCodes;
Intent activity;
int numMarkedNotificationsList;
// Construct a list of seen notifications in state
// "pending to mark as read in SWAD"
markedNotificationsList = dbHelper.getAllRows(
DataBaseHelper.DB_TABLE_NOTIFICATIONS,
"seenLocal='" + Utils.parseBoolString(true)
+ "' AND seenRemote='"
+ Utils.parseBoolString(false) + "'", null);
numMarkedNotificationsList = markedNotificationsList.size();
if (isDebuggable)
Log.d(TAG, "numMarkedNotificationsList="
+ numMarkedNotificationsList);
if (numMarkedNotificationsList > 0) {
// Creates a string of notification codes separated by commas
// from the previous list
seenNotifCodes = Utils
.getSeenNotificationCodes(markedNotificationsList);
if (isDebuggable)
Log.d(TAG, "seenNotifCodes=" + seenNotifCodes);
// Sends "seen notifications" info to the server
activity = new Intent(this, NotificationsMarkAllAsRead.class);
activity.putExtra("seenNotifCodes", seenNotifCodes);
activity.putExtra("numMarkedNotificationsList",
numMarkedNotificationsList);
startActivityForResult(activity,
Constants.NOTIFMARKALLASREAD_REQUEST_CODE);
}
}
/*
* (non-Javadoc)
*
* @see android.app.Activity#onActivityResult(int, int,
* android.content.Intent)
*/
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == Constants.NOTIFMARKALLASREAD_REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK) {
Log.i(TAG, "Notifications marked as read in SWAD");
} else {
Log.e(TAG, "Error marking notifications as read in SWAD");
}
}
}
/*
* (non-Javadoc)
*
* @see es.ugr.swad.swadroid.modules.Module#onCreate(android.os.Bundle)
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.expandablelist_items_pulltorefresh);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
this.findViewById(R.id.groupSpinner).setVisibility(View.GONE);
refreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipe_container_expandablelist);
list = (ExpandableListView) findViewById(R.id.expandablelist_pulltorefresh);
emptyNotifTextView = (TextView) findViewById(R.id.list_item_title);
mBirthdayLayout = (LinearLayout) findViewById(R.id.notify_layout);
mBirthdayTextView = (TextView) findViewById(R.id.notifyTextView);
groupItem = new ArrayList<>();
childItem = new ArrayList<>();
//Init ExpandableListView
initSwipeOptions();
//Set ExpandableListView data
setGroupData();
setChildGroupData();
/*
* If there aren't notifications to show, hide the notifications list
* and show the empty notifications message
*/
if (dbHelper.getAllRowsCount(DataBaseHelper.DB_TABLE_NOTIFICATIONS) == 0) {
Log.d(TAG, "[onCreate] Notifications table is empty");
emptyNotifTextView.setText(R.string.notificationsEmptyListMsg);
emptyNotifTextView.setVisibility(View.VISIBLE);
list.setOnChildClickListener(null);
list.setVisibility(View.GONE);
} else {
Log.d(TAG, "[onCreate] Notifications table is not empty");
emptyNotifTextView.setVisibility(View.GONE);
list.setVisibility(View.VISIBLE);
}
setMETHOD_NAME("getNotifications");
receiver = new SyncReceiver(this);
account = new Account(getString(R.string.app_name), accountType);
}
/**
* Launches an action when markAllRead button is pushed
*
*/
public void onMarkAllReadClick() {
dbHelper.updateAllNotifications("seenLocal",
Utils.parseBoolString(true));
// Sends to SWAD the "seen notifications" info
sendReadNotifications();
refreshScreen();
}
/*
* (non-Javadoc)
*
* @see es.ugr.swad.swadroid.modules.Module#onResume()
*/
@Override
protected void onResume() {
super.onResume();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(NotificationsSyncAdapterService.START_SYNC);
intentFilter.addAction(NotificationsSyncAdapterService.STOP_SYNC);
intentFilter.addAction(Intent.CATEGORY_DEFAULT);
registerReceiver(receiver, intentFilter);
Log.i(TAG, "Registered receiver for automatic synchronization");
refreshScreen();
}
/*
* (non-Javadoc)
*
* @see es.ugr.swad.swadroid.modules.Module#onPause()
*/
@Override
protected void onPause() {
super.onPause();
unregisterReceiver(receiver);
Log.i(TAG, "Unregistered receiver for automatic synchronization");
}
/*
* (non-Javadoc)
*
* @see es.ugr.swad.swadroid.modules.Module#requestService()
*/
@Override
protected void requestService() throws Exception {
int numDeletedNotif;
if (SyncUtils.isSyncAutomatically(getApplicationContext())) {
Log.i(TAG,
"Automatic synchronization is enabled. Requesting asynchronous sync operation");
// Call synchronization service
ContentResolver.requestSync(account, authority, new Bundle());
} else {
Log.i(TAG,
"Automatic synchronization is disabled. Requesting manual sync operation");
// Calculates next timestamp to be requested
Long timestamp = Long.valueOf(dbHelper
.getFieldOfLastNotification("eventTime"));
timestamp++;
// Creates webservice request, adds required params and sends
// request to webservice
createRequest(SOAPClient.CLIENT_TYPE);
addParam("wsKey", Login.getLoggedUser().getWsKey());
addParam("beginTime", timestamp);
sendRequest(SWADNotification.class, false);
if (result != null) {
dbHelper.beginTransaction();
// Stores notifications data returned by webservice response
ArrayList<?> res = new ArrayList<Object>((Vector<?>) result);
SoapObject soap = (SoapObject) res.get(1);
int numNotif = soap.getPropertyCount();
notifCount = 0;
for (int i = 0; i < numNotif; i++) {
SoapObject pii = (SoapObject) soap.getProperty(i);
Long notifCode = Long.valueOf(pii.getProperty("notifCode")
.toString());
Long eventCode = Long.valueOf(pii.getProperty(
"eventCode").toString());
String eventType = pii.getProperty("eventType").toString();
Long eventTime = Long.valueOf(pii.getProperty("eventTime")
.toString());
String userNickname = pii.getProperty("userNickname")
.toString();
String userSurname1 = pii.getProperty("userSurname1")
.toString();
String userSurname2 = pii.getProperty("userSurname2")
.toString();
String userFirstName = pii.getProperty("userFirstname")
.toString();
String userPhoto = pii.getProperty("userPhoto").toString();
String location = pii.getProperty("location").toString();
String summary = pii.getProperty("summary").toString();
Integer status = Integer.valueOf(pii.getProperty("status")
.toString());
String content = pii.getProperty("content").toString();
boolean notifReadSWAD = (status >= 4);
boolean notifCancelled = (status >= 8);
// Add not cancelled notifications only
if(!notifCancelled) {
SWADNotification n = new SWADNotification(notifCode,
eventCode, eventType, eventTime, userNickname, userSurname1,
userSurname2, userFirstName, userPhoto, location,
summary, status, content, notifReadSWAD,
notifReadSWAD);
dbHelper.insertNotification(n);
// Count unread notifications only
if (!notifReadSWAD) {
notifCount++;
}
if (isDebuggable)
Log.d(TAG, n.toString());
}
}
// Request finalized without errors
setResult(RESULT_OK);
Log.i(TAG, "Retrieved " + numNotif + " notifications ("
+ notifCount + " unread)");
// Clean old notifications to control database size
numDeletedNotif = dbHelper.cleanOldNotificationsByAge(Constants.CLEAN_NOTIFICATIONS_THRESHOLD);
Log.i(TAG, "Deleted " + numDeletedNotif + " notifications from database");
dbHelper.endTransaction(true);
}
}
}
/*
* (non-Javadoc)
*
* @see es.ugr.swad.swadroid.modules.Module#connect()
*/
@Override
protected void connect() {
//Toast.makeText(this, R.string.notificationsProgressDescription, Toast.LENGTH_SHORT).show();
startConnection();
}
/*
* (non-Javadoc)
*
* @see es.ugr.swad.swadroid.modules.Module#postConnect()
*/
@Override
protected void postConnect() {
Notification notif;
Intent notificationIntent = new Intent(this, Notifications.class);
PendingIntent pendingIntent;
notificationIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//single top to avoid creating many activity stacks queue
notificationIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
pendingIntent = PendingIntent.getActivity(getApplicationContext(),
0,
notificationIntent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE);
if (!SyncUtils.isSyncAutomatically(getApplicationContext())) {
if (notifCount > 0) {
notif = AlertNotificationFactory.createAlertNotification(getApplicationContext(),
getString(R.string.app_name),
notifCount + " "
+ getString(R.string.notificationsAlertMsg),
getString(R.string.app_name),
pendingIntent,
R.drawable.ic_launcher_swadroid_notif,
R.drawable.ic_launcher_swadroid,
true,
false,
false);
AlertNotificationFactory.showAlertNotification(getApplicationContext(), notif, NOTIF_ALERT_ID);
} else {
Toast.makeText(this, R.string.NoNotificationsMsg,
Toast.LENGTH_LONG).show();
}
// Sends to SWAD the "seen notifications" info
sendReadNotifications();
refreshScreen();
}
}
/*
* (non-Javadoc)
*
* @see es.ugr.swad.swadroid.modules.Module#onError()
*/
@Override
protected void onError() {
}
/**
* Removes all notifications from database
*
*/
public void clearNotifications() {
try {
dbHelper.emptyTable(DataBaseHelper.DB_TABLE_NOTIFICATIONS);
} catch (Exception e) {
error(e.getMessage(), e);
}
}
/**
* Synchronization callback. Is called when synchronization starts and stops
*
* @author Juan Miguel Boyero Corral <juanmi1982@gmail.com>
*/
private class SyncReceiver extends BroadcastReceiver {
public SyncReceiver(Notifications activity) {
}
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(
NotificationsSyncAdapterService.START_SYNC)) {
Log.i(TAG, "Started sync");
} else if (intent.getAction().equals(
NotificationsSyncAdapterService.STOP_SYNC)) {
Log.i(TAG, "Stopped sync");
notifCount = intent.getIntExtra("notifCount", 0);
errorMessage = intent.getStringExtra("errorMessage");
if ((errorMessage != null) && !errorMessage.equals("")) {
error(errorMessage, null);
} else if (notifCount == 0) {
Toast.makeText(context, R.string.NoNotificationsMsg,
Toast.LENGTH_LONG).show();
}
refreshScreen();
}
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.notification_activity_actions, menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_markAllRead:
dbHelper.updateAllNotifications("seenLocal",
Utils.parseBoolString(true));
sendReadNotifications();
refreshScreen();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
/*
* @Override public void setContentView(int layoutResID) { View v =
* getLayoutInflater().inflate(layoutResID, refreshLayout, false);
* setContentView(v); }
*
* @Override public void setContentView(View view) { setContentView(view,
* view.getLayoutParams()); }
*
* @Override public void setContentView(View view, ViewGroup.LayoutParams
* params) { refreshLayout.addView(view, params); initSwipeOptions(); }
*/
private void initSwipeOptions() {
refreshLayout.setOnRefreshListener(this);
setAppearance();
// disableSwipe();
list.setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView absListView, int scrollState) {
}
@Override
public void onScroll(AbsListView absListView, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
boolean enable = false;
if(list != null && list.getChildCount() > 0){
// check if the first item of the list is visible
boolean firstItemVisible = list.getFirstVisiblePosition() == 0;
// check if the top of the first item is visible
boolean topOfFirstItemVisible = list.getChildAt(0).getTop() == 0;
// enabling or disabling the refresh layout
enable = firstItemVisible && topOfFirstItemVisible;
}
refreshLayout.setEnabled(enable);
}
});
/*
* Create a ListView-specific touch listener. ListViews are given special treatment because
* by default they handle touches for their list items... i.e. they're in charge of drawing
* the pressed state (the list selector), handling list item clicks, etc.
*
* Requires Android 3.1 (HONEYCOMB_MR1) or newer
*/
/*if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) {
SwipeListViewTouchListener touchListener =
new SwipeListViewTouchListener(
list,
new SwipeListViewTouchListener.OnSwipeCallback() {
@Override
public void onSwipeLeft(ListView listView, int [] reverseSortedPositions) {
if(reverseSortedPositions.length > 0) {
swipeItem(reverseSortedPositions[0]);
}
}
@Override
public void onSwipeRight(ListView listView, int [] reverseSortedPositions) {
if(reverseSortedPositions.length > 0) {
swipeItem(reverseSortedPositions[0]);
}
}
@Override
public void onStartSwipe() {
disableSwipe();
}
@Override
public void onStopSwipe() {
enableSwipe();
}
},
true,
true);
list.setOnTouchListener(touchListener);
// Setting this scroll listener is required to ensure that during ListView scrolling,
// we don't look for swipes.
list.setOnScrollListener(touchListener.makeScrollListener(refreshLayout));
} else {
Log.w(TAG, "SwipeListViewTouchListener requires Android 3.1 (HONEYCOMB_MR1) or newer");
list.setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView absListView, int scrollState) {
}
@Override
public void onScroll(AbsListView absListView, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
boolean enable = false;
if(list != null && list.getChildCount() > 0){
// check if the first item of the list is visible
boolean firstItemVisible = list.getFirstVisiblePosition() == 0;
// check if the top of the first item is visible
boolean topOfFirstItemVisible = list.getChildAt(0).getTop() == 0;
// enabling or disabling the refresh layout
enable = firstItemVisible && topOfFirstItemVisible;
}
refreshLayout.setEnabled(enable);
}
});
}*/
}
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
private void setAppearance() {
refreshLayout.setColorSchemeResources(android.R.color.holo_blue_bright,
android.R.color.holo_green_light,
android.R.color.holo_orange_light,
android.R.color.holo_red_light);
}
/**
* It shows the SwipeRefreshLayout progress
*/
private void showSwipeProgress() {
refreshLayout.setRefreshing(true);
}
/**
* It shows the SwipeRefreshLayout progress
*/
private void hideSwipeProgress() {
refreshLayout.setRefreshing(false);
}
/**
* Enables swipe gesture
*/
private void enableSwipe() {
refreshLayout.setEnabled(true);
//Log.d(TAG, "RefreshLayout Swipe ENABLED");
}
/**
* Disables swipe gesture. It prevents manual gestures but keeps the option
* to show refreshing programatically.
*/
public void disableSwipe() {
refreshLayout.setEnabled(false);
//Log.d(TAG, "RefreshLayout Swipe DISABLED");
}
/**
* It must be overriden by parent classes if manual swipe is enabled.
*/
@Override
public void onRefresh() {
showSwipeProgress();
runConnection();
if(!isConnected) {
hideSwipeProgress();
}
}
private void setGroupData() {
groupItem.add(getString(R.string.notSeenLabel));
groupItem.add(getString(R.string.seenLabel));
}
private void setChildGroupData() {
List<Model> child;
//Clear data
childItem.clear();
//Add data for not seen notifications
child = dbHelper.getAllRows(DataBaseHelper.DB_TABLE_NOTIFICATIONS,
"seenLocal='" + Utils.parseBoolString(false) + "'", orderby);
childItem.add(child);
//Add data for seen notifications
child = dbHelper.getAllRows(DataBaseHelper.DB_TABLE_NOTIFICATIONS,
"seenLocal='" + Utils.parseBoolString(true) + "'", orderby);
childItem.add(child);
Log.d(TAG, "groups size=" + childItem.size());
Log.d(TAG, "not seen children size=" + childItem.get(NOT_SEEN_GROUP_ID).size());
Log.d(TAG, "seen children size=" + childItem.get(SEEN_GROUP_ID).size());
adapter = new NotificationsExpandableListAdapter(this, groupItem, childItem);
if(dbHelper.getAllRowsCount(DataBaseHelper.DB_TABLE_NOTIFICATIONS) > 0) {
Log.d(TAG, "[setChildGroupData] Notifications table is not empty");
emptyNotifTextView.setVisibility(View.GONE);
list.setVisibility(View.VISIBLE);
list.setAdapter(adapter);
list.setOnChildClickListener(clickListener);
//Expand the groups
list.expandGroup(NOT_SEEN_GROUP_ID);
list.expandGroup(SEEN_GROUP_ID);
} else {
Log.d(TAG, "[setChildGroupData] Notifications table is empty");
list.setVisibility(View.GONE);
emptyNotifTextView.setVisibility(View.VISIBLE);
enableSwipe();
}
}
}