13 Commits

Author SHA1 Message Date
9ef6b1dd91 swipe-to-delete setting 2018-11-17 17:18:15 +01:00
6f7585323b fixed notification being overwritten 2018-11-17 17:06:34 +01:00
92135e64a3 css 2018-11-17 17:00:14 +01:00
28efeea30b message delete problems 2018-11-17 16:45:42 +01:00
a8a074907b fixed update when in message-list 2018-11-17 16:02:31 +01:00
6c29ec9820 max-size = 2048 chars (FCM restrictions) 2018-11-17 15:44:44 +01:00
jenkins
8ec23144ca [Jenkins] Increment version 2018-11-17 14:28:04 +01:00
b703853b28 send.bash example 2018-11-17 14:26:29 +01:00
6f7529fc9b fixed duplicate-recieve 2018-11-17 14:01:51 +01:00
27b45098f0 ups 2018-11-17 13:46:23 +01:00
21f66d99cd fixed timestamp format in requery 2018-11-17 13:44:42 +01:00
1745051e6e fixed info.php return value 2018-11-17 12:51:20 +01:00
jenkins
b395e054e6 [Jenkins] Increment version 2018-11-17 03:25:54 +01:00
20 changed files with 361 additions and 103 deletions

View File

@@ -24,6 +24,11 @@ public final class CollectionHelper
return output; return output;
} }
public static <T> void sort_inplace(List<T> input, Comparator<T> comparator)
{
Collections.sort(input, comparator);
}
public static <T, U extends Comparable<U>> List<T> sort(List<T> input, Func1to1<T, U> mapper) public static <T, U extends Comparable<U>> List<T> sort(List<T> input, Func1to1<T, U> mapper)
{ {
return sort(input, mapper, 1); return sort(input, mapper, 1);

View File

@@ -3,17 +3,21 @@ package com.blackforestbytes.simplecloudnotifier.model;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import com.blackforestbytes.simplecloudnotifier.lib.collections.CollectionHelper;
import com.blackforestbytes.simplecloudnotifier.lib.string.Str; import com.blackforestbytes.simplecloudnotifier.lib.string.Str;
import com.blackforestbytes.simplecloudnotifier.view.MessageAdapter; import com.blackforestbytes.simplecloudnotifier.view.MessageAdapter;
import com.blackforestbytes.simplecloudnotifier.SCNApp; import com.blackforestbytes.simplecloudnotifier.SCNApp;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
public class CMessageList public class CMessageList
{ {
private final Object msg_lock = new Object();
public ArrayList<CMessage> Messages; public ArrayList<CMessage> Messages;
public Set<String> AllAcks; public Set<String> AllAcks;
@@ -32,23 +36,26 @@ public class CMessageList
private CMessageList() private CMessageList()
{ {
Messages = new ArrayList<>(); synchronized (msg_lock)
AllAcks = new HashSet<>();
SharedPreferences sharedPref = SCNApp.getContext().getSharedPreferences("CMessageList", Context.MODE_PRIVATE);
int count = sharedPref.getInt("message_count", 0);
for (int i=0; i < count; i++)
{ {
long time = sharedPref.getLong("message["+i+"].timestamp", 0); Messages = new ArrayList<>();
String title = sharedPref.getString("message["+i+"].title", ""); AllAcks = new HashSet<>();
String content = sharedPref.getString("message["+i+"].content", "");
PriorityEnum prio = PriorityEnum.parseAPI(sharedPref.getInt("message["+i+"].priority", 1));
long scnid = sharedPref.getLong("message["+i+"].scnid", 0);
Messages.add(new CMessage(scnid, time, title, content, prio)); SharedPreferences sharedPref = SCNApp.getContext().getSharedPreferences("CMessageList", Context.MODE_PRIVATE);
int count = sharedPref.getInt("message_count", 0);
for (int i=0; i < count; i++)
{
long time = sharedPref.getLong("message["+i+"].timestamp", 0);
String title = sharedPref.getString("message["+i+"].title", "");
String content = sharedPref.getString("message["+i+"].content", "");
PriorityEnum prio = PriorityEnum.parseAPI(sharedPref.getInt("message["+i+"].priority", 1));
long scnid = sharedPref.getLong("message["+i+"].scnid", 0);
Messages.add(new CMessage(scnid, time, title, content, prio));
}
AllAcks = sharedPref.getStringSet("acks", new HashSet<>());
} }
AllAcks = sharedPref.getStringSet("acks", new HashSet<>());
} }
public CMessage add(final long scnid, final long time, final String title, final String content, final PriorityEnum pe) public CMessage add(final long scnid, final long time, final String title, final String content, final PriorityEnum pe)
@@ -60,29 +67,44 @@ public class CMessageList
SharedPreferences sharedPref = SCNApp.getContext().getSharedPreferences("CMessageList", Context.MODE_PRIVATE); SharedPreferences sharedPref = SCNApp.getContext().getSharedPreferences("CMessageList", Context.MODE_PRIVATE);
int count = sharedPref.getInt("message_count", 0); int count = sharedPref.getInt("message_count", 0);
SharedPreferences.Editor e = sharedPref.edit(); synchronized (msg_lock)
{
Messages.add(msg);
AllAcks.add(Long.toHexString(msg.SCN_ID));
Messages.add(msg); while (Messages.size()>SCNSettings.inst().LocalCacheSize) Messages.remove(0);
AllAcks.add(Long.toHexString(msg.SCN_ID)); }
while (Messages.size()>SCNSettings.inst().LocalCacheSize) Messages.remove(0); if (Messages.size()>1 && Messages.get(Messages.size()-2).Timestamp < msg.Timestamp)
{
// quick save
e.putInt( "message_count", count+1); SharedPreferences.Editor e = sharedPref.edit();
e.putLong( "message["+count+"].timestamp", time);
e.putString("message["+count+"].title", title);
e.putString("message["+count+"].content", content);
e.putInt( "message["+count+"].priority", pe.ID);
e.putLong( "message["+count+"].scnid", scnid);
e.putStringSet("acks", AllAcks); e.putInt( "message_count", count+1);
e.putLong( "message["+count+"].timestamp", time);
e.putString("message["+count+"].title", title);
e.putString("message["+count+"].content", content);
e.putInt( "message["+count+"].priority", pe.ID);
e.putLong( "message["+count+"].scnid", scnid);
e.putStringSet("acks", AllAcks);
e.apply();
}
else
{
// full save
fullSave(); // does sort in here
}
e.apply();
for (WeakReference<MessageAdapter> ref : _listener) for (WeakReference<MessageAdapter> ref : _listener)
{ {
MessageAdapter a = ref.get(); MessageAdapter a = ref.get();
if (a == null) continue; if (a == null) continue;
a.customNotifyItemInserted(count); a.customNotifyDataSetChanged();
a.scrollToTop(); a.scrollToTop();
} }
CleanUpListener(); CleanUpListener();
@@ -90,9 +112,12 @@ public class CMessageList
if (!run) if (!run)
{ {
Messages.add(new CMessage(scnid, time, title, content, pe)); synchronized (msg_lock)
AllAcks.add(Long.toHexString(msg.SCN_ID)); {
fullSave(); Messages.add(new CMessage(scnid, time, title, content, pe));
AllAcks.add(Long.toHexString(msg.SCN_ID));
}
fullSave(); // does sort in here
} }
return msg; return msg;
@@ -114,31 +139,39 @@ public class CMessageList
public void fullSave() public void fullSave()
{ {
SharedPreferences sharedPref = SCNApp.getContext().getSharedPreferences("CMessageList", Context.MODE_PRIVATE); synchronized (msg_lock)
SharedPreferences.Editor e = sharedPref.edit();
e.clear();
e.putInt("message_count", Messages.size());
for (int i = 0; i < Messages.size(); i++)
{ {
e.putLong( "message["+i+"].timestamp", Messages.get(i).Timestamp); CollectionHelper.sort_inplace(Messages, (a,b) -> Long.compare(a.Timestamp, b.Timestamp));
e.putString("message["+i+"].title", Messages.get(i).Title);
e.putString("message["+i+"].content", Messages.get(i).Content); SharedPreferences sharedPref = SCNApp.getContext().getSharedPreferences("CMessageList", Context.MODE_PRIVATE);
e.putInt( "message["+i+"].priority", Messages.get(i).Priority.ID); SharedPreferences.Editor e = sharedPref.edit();
e.putLong( "message["+i+"].scnid", Messages.get(i).SCN_ID);
e.clear();
e.putInt("message_count", Messages.size());
for (int i = 0; i < Messages.size(); i++)
{
e.putLong( "message["+i+"].timestamp", Messages.get(i).Timestamp);
e.putString("message["+i+"].title", Messages.get(i).Title);
e.putString("message["+i+"].content", Messages.get(i).Content);
e.putInt( "message["+i+"].priority", Messages.get(i).Priority.ID);
e.putLong( "message["+i+"].scnid", Messages.get(i).SCN_ID);
}
e.putStringSet("acks", AllAcks);
e.apply();
} }
e.putStringSet("acks", AllAcks);
e.apply();
} }
public CMessage tryGet(int pos) public CMessage tryGet(int pos)
{ {
if (pos < 0 || pos >= Messages.size()) return null; synchronized (msg_lock)
return Messages.get(pos); {
if (pos < 0 || pos >= Messages.size()) return null;
return Messages.get(pos);
}
} }
public CMessage tryGetFromBack(int pos) public CMessage tryGetFromBack(int pos)
@@ -148,7 +181,10 @@ public class CMessageList
public int size() public int size()
{ {
return Messages.size(); synchronized (msg_lock)
{
return Messages.size();
}
} }
public void register(MessageAdapter adp) public void register(MessageAdapter adp)
@@ -167,18 +203,30 @@ public class CMessageList
public boolean isAck(long id) public boolean isAck(long id)
{ {
return AllAcks.contains(Long.toHexString(id)); synchronized (msg_lock)
{
return AllAcks.contains(Long.toHexString(id));
}
} }
public void remove(int index) public CMessage removeFromBack(int pos)
{ {
Messages.remove(index); CMessage r;
fullSave(); synchronized (msg_lock)
{
int index = Messages.size() - pos - 1;
r = Messages.remove(index);
}
fullSave(); // does sort in here
return r;
} }
public void insert(int index, CMessage item) public void insert(int index, CMessage item)
{ {
Messages.add(index, item); synchronized (msg_lock)
fullSave(); {
Messages.add(index, item);
}
fullSave(); // does sort in here
} }
} }

View File

@@ -15,14 +15,19 @@ import com.google.firebase.iid.FirebaseInstanceId;
public class SCNSettings public class SCNSettings
{ {
private final static Object _lock = new Object(); private final static Object _lock = new Object();
private static SCNSettings _inst = null; private static volatile SCNSettings _inst = null;
public static SCNSettings inst() public static SCNSettings inst()
{ {
synchronized (_lock) SCNSettings local = _inst;
if (local == null)
{ {
if (_inst != null) return _inst; synchronized (_lock)
return _inst = new SCNSettings(); {
local = _inst;
if (local == null) _inst = local = new SCNSettings();
}
} }
return local;
} }
// ------------------------------------------------------------ // ------------------------------------------------------------
@@ -47,6 +52,7 @@ public class SCNSettings
public boolean Enabled = true; public boolean Enabled = true;
public int LocalCacheSize = 500; public int LocalCacheSize = 500;
public boolean EnableDeleteSwipe = true;
public final NotificationSettings PriorityLow = new NotificationSettings(PriorityEnum.LOW); public final NotificationSettings PriorityLow = new NotificationSettings(PriorityEnum.LOW);
public final NotificationSettings PriorityNorm = new NotificationSettings(PriorityEnum.NORMAL); public final NotificationSettings PriorityNorm = new NotificationSettings(PriorityEnum.NORMAL);
@@ -70,6 +76,7 @@ public class SCNSettings
Enabled = sharedPref.getBoolean("app_enabled", Enabled); Enabled = sharedPref.getBoolean("app_enabled", Enabled);
LocalCacheSize = sharedPref.getInt("local_cache_size", LocalCacheSize); LocalCacheSize = sharedPref.getInt("local_cache_size", LocalCacheSize);
EnableDeleteSwipe = sharedPref.getBoolean("do_del_swipe", EnableDeleteSwipe);
PriorityLow.EnableLED = sharedPref.getBoolean("priority_low:enabled_led", PriorityLow.EnableLED); PriorityLow.EnableLED = sharedPref.getBoolean("priority_low:enabled_led", PriorityLow.EnableLED);
PriorityLow.EnableSound = sharedPref.getBoolean("priority_low:enabled_sound", PriorityLow.EnableSound); PriorityLow.EnableSound = sharedPref.getBoolean("priority_low:enabled_sound", PriorityLow.EnableSound);
@@ -116,6 +123,7 @@ public class SCNSettings
e.putBoolean("app_enabled", Enabled); e.putBoolean("app_enabled", Enabled);
e.putInt( "local_cache_size", LocalCacheSize); e.putInt( "local_cache_size", LocalCacheSize);
e.putBoolean("do_del_swipe", EnableDeleteSwipe);
e.putBoolean("priority_low:enabled_led", PriorityLow.EnableLED); e.putBoolean("priority_low:enabled_led", PriorityLow.EnableLED);
e.putBoolean("priority_low:enabled_sound", PriorityLow.EnableSound); e.putBoolean("priority_low:enabled_sound", PriorityLow.EnableSound);

View File

@@ -283,10 +283,7 @@ public class ServerCommunication
SCNApp.refreshAccountTab(); SCNApp.refreshAccountTab();
if (json_int(json, "unack_count")>0) if (json_int(json, "unack_count")>0) ServerCommunication.requery(id, key, loader);
{
ServerCommunication.requery(id, key, loader);
}
} catch (Exception e) { } catch (Exception e) {
Log.e("SC:info", e.toString()); Log.e("SC:info", e.toString());
@@ -357,20 +354,6 @@ public class ServerCommunication
FBMService.recieveData(time, title, content, prio, scn_id, true); FBMService.recieveData(time, title, content, prio, scn_id, true);
} }
SCNSettings.inst().user_id = json_int(json, "user_id");
SCNSettings.inst().quota_curr = json_int(json, "quota");
SCNSettings.inst().quota_max = json_int(json, "quota_max");
SCNSettings.inst().promode_server = json_bool(json, "is_pro");
if (!json_bool(json, "fcm_token_set")) SCNSettings.inst().fcm_token_server = "";
SCNSettings.inst().save();
SCNApp.refreshAccountTab();
if (json_int(json, "unack_count")>0)
{
ServerCommunication.requery(id, key, loader);
}
} catch (Exception e) { } catch (Exception e) {
Log.e("SC:info", e.toString()); Log.e("SC:info", e.toString());
SCNApp.showToast("Communication with server failed", 4000); SCNApp.showToast("Communication with server failed", 4000);
@@ -446,12 +429,12 @@ public class ServerCommunication
} }
} }
public static void ack(int id, String key, CMessage msg) public static void ack(int id, String key, long msg_scn_id)
{ {
try try
{ {
Request request = new Request.Builder() Request request = new Request.Builder()
.url(BASE_URL + "ack.php?user_id=" + id + "&user_key=" + key + "&scn_msg_id=" + msg.SCN_ID) .url(BASE_URL + "ack.php?user_id=" + id + "&user_key=" + key + "&scn_msg_id=" + msg_scn_id)
.build(); .build();
client.newCall(request).enqueue(new Callback() { client.newCall(request).enqueue(new Callback() {

View File

@@ -50,15 +50,15 @@ public class FBMService extends FirebaseMessagingService
public static void recieveData(long time, String title, String content, PriorityEnum prio, long scn_id, boolean alwaysAck) public static void recieveData(long time, String title, String content, PriorityEnum prio, long scn_id, boolean alwaysAck)
{ {
CMessage msg = CMessageList.inst().add(scn_id, time, title, content, prio);
if (CMessageList.inst().isAck(scn_id)) if (CMessageList.inst().isAck(scn_id))
{ {
Log.w("FB::MessageReceived", "Recieved ack-ed message: " + scn_id); Log.w("FB::MessageReceived", "Recieved ack-ed message: " + scn_id);
if (alwaysAck) ServerCommunication.ack(SCNSettings.inst().user_id, SCNSettings.inst().user_key, msg); if (alwaysAck) ServerCommunication.ack(SCNSettings.inst().user_id, SCNSettings.inst().user_key, scn_id);
return; return;
} }
CMessage msg = CMessageList.inst().add(scn_id, time, title, content, prio);
if (SCNApp.isBackground()) if (SCNApp.isBackground())
{ {
NotificationService.inst().showBackground(msg); NotificationService.inst().showBackground(msg);
@@ -68,6 +68,6 @@ public class FBMService extends FirebaseMessagingService
NotificationService.inst().showForeground(msg); NotificationService.inst().showForeground(msg);
} }
ServerCommunication.ack(SCNSettings.inst().user_id, SCNSettings.inst().user_key, msg); ServerCommunication.ack(SCNSettings.inst().user_id, SCNSettings.inst().user_key, scn_id);
} }
} }

View File

@@ -171,6 +171,9 @@ public class NotificationService
mBuilder.setShowWhen(true); mBuilder.setShowWhen(true);
mBuilder.setWhen(msg.Timestamp * 1000); mBuilder.setWhen(msg.Timestamp * 1000);
mBuilder.setAutoCancel(true); mBuilder.setAutoCancel(true);
mBuilder.setCategory(Notification.CATEGORY_MESSAGE);
mBuilder.setStyle(new NotificationCompat.InboxStyle());
mBuilder.setGroup(prio.toString());
if (msg.Priority == PriorityEnum.LOW) mBuilder.setPriority(NotificationCompat.PRIORITY_LOW); if (msg.Priority == PriorityEnum.LOW) mBuilder.setPriority(NotificationCompat.PRIORITY_LOW);
if (msg.Priority == PriorityEnum.NORMAL) mBuilder.setPriority(NotificationCompat.PRIORITY_DEFAULT); if (msg.Priority == PriorityEnum.NORMAL) mBuilder.setPriority(NotificationCompat.PRIORITY_DEFAULT);
@@ -198,7 +201,7 @@ public class NotificationService
Notification n = mBuilder.build(); Notification n = mBuilder.build();
if (mNotificationManager != null) mNotificationManager.notify(0, n); if (mNotificationManager != null) mNotificationManager.notify((int)msg.SCN_ID, n);
} }
@RequiresApi(api = Build.VERSION_CODES.O) @RequiresApi(api = Build.VERSION_CODES.O)

View File

@@ -3,6 +3,7 @@ package com.blackforestbytes.simplecloudnotifier.util;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.view.View; import android.view.View;
import com.blackforestbytes.simplecloudnotifier.model.SCNSettings;
import com.blackforestbytes.simplecloudnotifier.view.MessageAdapter; import com.blackforestbytes.simplecloudnotifier.view.MessageAdapter;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@@ -13,10 +14,21 @@ public class MessageAdapterTouchHelper extends ItemTouchHelper.SimpleCallback
{ {
private MessageAdapterTouchHelperListener listener; private MessageAdapterTouchHelperListener listener;
private int dir = 0;
public MessageAdapterTouchHelper(int dragDirs, int swipeDirs, MessageAdapterTouchHelperListener listener) public MessageAdapterTouchHelper(int dragDirs, int swipeDirs, MessageAdapterTouchHelperListener listener)
{ {
super(dragDirs, swipeDirs); super(dragDirs, swipeDirs);
this.dir = swipeDirs;
this.listener = listener; this.listener = listener;
updateEnabled();
}
public void updateEnabled()
{
int sdir = SCNSettings.inst().EnableDeleteSwipe ? ItemTouchHelper.LEFT : 0;
if (dir == sdir) return;
setDefaultSwipeDirs(dir = sdir);
} }
@Override @Override

View File

@@ -57,7 +57,6 @@ public class MessageAdapter extends RecyclerView.Adapter
viewHolders.put(view, true); viewHolders.put(view, true);
} }
@Override @Override
public void onViewRecycled(@NonNull RecyclerView.ViewHolder holder) public void onViewRecycled(@NonNull RecyclerView.ViewHolder holder)
{ {
@@ -87,16 +86,17 @@ public class MessageAdapter extends RecyclerView.Adapter
manLayout.smoothScrollToPosition(viewRecycler, null, 0); manLayout.smoothScrollToPosition(viewRecycler, null, 0);
} }
public void removeItem(int position) public CMessage removeItem(int position)
{ {
CMessageList.inst().remove(position); CMessage i = CMessageList.inst().removeFromBack(position);
notifyItemRemoved(position); notifyDataSetChanged();
return i;
} }
public void restoreItem(CMessage item, int position) public void restoreItem(CMessage item, int position)
{ {
CMessageList.inst().insert(position, item); CMessageList.inst().insert(position, item);
notifyItemInserted(position); notifyDataSetChanged();
} }
public class MessagePresenter extends RecyclerView.ViewHolder implements View.OnClickListener public class MessagePresenter extends RecyclerView.ViewHolder implements View.OnClickListener

View File

@@ -9,7 +9,6 @@ import android.view.ViewGroup;
import com.blackforestbytes.simplecloudnotifier.R; import com.blackforestbytes.simplecloudnotifier.R;
import com.blackforestbytes.simplecloudnotifier.SCNApp; import com.blackforestbytes.simplecloudnotifier.SCNApp;
import com.blackforestbytes.simplecloudnotifier.model.CMessage; import com.blackforestbytes.simplecloudnotifier.model.CMessage;
import com.blackforestbytes.simplecloudnotifier.model.CMessageList;
import com.blackforestbytes.simplecloudnotifier.model.SCNSettings; import com.blackforestbytes.simplecloudnotifier.model.SCNSettings;
import com.blackforestbytes.simplecloudnotifier.service.IABService; import com.blackforestbytes.simplecloudnotifier.service.IABService;
import com.blackforestbytes.simplecloudnotifier.util.MessageAdapterTouchHelper; import com.blackforestbytes.simplecloudnotifier.util.MessageAdapterTouchHelper;
@@ -28,6 +27,8 @@ public class NotificationsFragment extends Fragment implements MessageAdapterTou
private PublisherAdView adView; private PublisherAdView adView;
private MessageAdapter adpMessages; private MessageAdapter adpMessages;
public MessageAdapterTouchHelper touchHelper;
public NotificationsFragment() public NotificationsFragment()
{ {
// Required empty public constructor // Required empty public constructor
@@ -43,7 +44,7 @@ public class NotificationsFragment extends Fragment implements MessageAdapterTou
rvMessages.setLayoutManager(lman); rvMessages.setLayoutManager(lman);
rvMessages.setAdapter(adpMessages = new MessageAdapter(v.findViewById(R.id.tvNoElements), lman, rvMessages)); rvMessages.setAdapter(adpMessages = new MessageAdapter(v.findViewById(R.id.tvNoElements), lman, rvMessages));
ItemTouchHelper.SimpleCallback itemTouchHelperCallback = new MessageAdapterTouchHelper(0, ItemTouchHelper.LEFT, this); ItemTouchHelper.SimpleCallback itemTouchHelperCallback = touchHelper = new MessageAdapterTouchHelper(0, ItemTouchHelper.LEFT, this);
new ItemTouchHelper(itemTouchHelperCallback).attachToRecyclerView(rvMessages); new ItemTouchHelper(itemTouchHelperCallback).attachToRecyclerView(rvMessages);
adView = v.findViewById(R.id.adBanner); adView = v.findViewById(R.id.adBanner);
@@ -65,13 +66,11 @@ public class NotificationsFragment extends Fragment implements MessageAdapterTou
{ {
if (viewHolder instanceof MessageAdapter.MessagePresenter) if (viewHolder instanceof MessageAdapter.MessagePresenter)
{ {
final CMessage deletedItem = CMessageList.inst().tryGet(viewHolder.getAdapterPosition());
final int deletedIndex = viewHolder.getAdapterPosition(); final int deletedIndex = viewHolder.getAdapterPosition();
final CMessage deletedItem = adpMessages.removeItem(viewHolder.getAdapterPosition());
String name = deletedItem.Title; String name = deletedItem.Title;
adpMessages.removeItem(viewHolder.getAdapterPosition());
Snackbar snackbar = Snackbar.make(SCNApp.getMainActivity().layoutRoot, name + " removed", Snackbar.LENGTH_LONG); Snackbar snackbar = Snackbar.make(SCNApp.getMainActivity().layoutRoot, name + " removed", Snackbar.LENGTH_LONG);
snackbar.setAction("UNDO", view -> adpMessages.restoreItem(deletedItem, deletedIndex)); snackbar.setAction("UNDO", view -> adpMessages.restoreItem(deletedItem, deletedIndex));
snackbar.setActionTextColor(Color.YELLOW); snackbar.setActionTextColor(Color.YELLOW);

View File

@@ -49,6 +49,7 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
private Button prefUpgradeAccount; private Button prefUpgradeAccount;
private TextView prefUpgradeAccount_msg; private TextView prefUpgradeAccount_msg;
private TextView prefUpgradeAccount_info; private TextView prefUpgradeAccount_info;
private Switch prefEnableDeleteSwipe;
private Switch prefMsgLowEnableSound; private Switch prefMsgLowEnableSound;
private TextView prefMsgLowRingtone_value; private TextView prefMsgLowRingtone_value;
@@ -114,6 +115,7 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
prefUpgradeAccount = v.findViewById(R.id.prefUpgradeAccount); prefUpgradeAccount = v.findViewById(R.id.prefUpgradeAccount);
prefUpgradeAccount_msg = v.findViewById(R.id.prefUpgradeAccount2); prefUpgradeAccount_msg = v.findViewById(R.id.prefUpgradeAccount2);
prefUpgradeAccount_info = v.findViewById(R.id.prefUpgradeAccount_info); prefUpgradeAccount_info = v.findViewById(R.id.prefUpgradeAccount_info);
prefEnableDeleteSwipe = v.findViewById(R.id.prefEnableDeleteSwipe);
prefMsgLowEnableSound = v.findViewById(R.id.prefMsgLowEnableSound); prefMsgLowEnableSound = v.findViewById(R.id.prefMsgLowEnableSound);
prefMsgLowRingtone_value = v.findViewById(R.id.prefMsgLowRingtone_value); prefMsgLowRingtone_value = v.findViewById(R.id.prefMsgLowRingtone_value);
@@ -159,6 +161,7 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
if (c == null) return; if (c == null) return;
if (prefAppEnabled.isChecked() != s.Enabled) prefAppEnabled.setChecked(s.Enabled); if (prefAppEnabled.isChecked() != s.Enabled) prefAppEnabled.setChecked(s.Enabled);
if (prefEnableDeleteSwipe.isChecked() != s.EnableDeleteSwipe) prefEnableDeleteSwipe.setChecked(s.EnableDeleteSwipe);
prefUpgradeAccount.setVisibility( SCNSettings.inst().promode_local ? View.GONE : View.VISIBLE); prefUpgradeAccount.setVisibility( SCNSettings.inst().promode_local ? View.GONE : View.VISIBLE);
prefUpgradeAccount_info.setVisibility(SCNSettings.inst().promode_local ? View.GONE : View.VISIBLE); prefUpgradeAccount_info.setVisibility(SCNSettings.inst().promode_local ? View.GONE : View.VISIBLE);
@@ -214,6 +217,7 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
SCNSettings s = SCNSettings.inst(); SCNSettings s = SCNSettings.inst();
prefAppEnabled.setOnCheckedChangeListener((a,b) -> { s.Enabled=b; saveAndUpdate(); }); prefAppEnabled.setOnCheckedChangeListener((a,b) -> { s.Enabled=b; saveAndUpdate(); });
prefEnableDeleteSwipe.setOnCheckedChangeListener((a,b) -> { s.EnableDeleteSwipe=b; saveAndUpdate(); });
prefLocalCacheSize.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() prefLocalCacheSize.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener()
{ {
@@ -324,6 +328,7 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
{ {
SCNSettings.inst().save(); SCNSettings.inst().save();
updateUI(); updateUI();
SCNApp.getMainActivity().adpTabs.tab1.touchHelper.updateEnabled();
} }
private void onUpgradeAccount() private void onUpgradeAccount()

View File

@@ -79,6 +79,22 @@
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginBottom="2dp"
android:layout_marginTop="2dp"
android:background="#c0c0c0"/>
<Switch
android:layout_marginStart="4dp"
android:layout_marginEnd="4dp"
android:id="@+id/prefEnableDeleteSwipe"
android:text="@string/str_deleteswipe"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="48dp" />
<View <View
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="1dp" android:layout_height="1dp"

View File

@@ -29,6 +29,7 @@
<string name="str_ledcolor">Notification light color</string> <string name="str_ledcolor">Notification light color</string>
<string name="str_enable_vibration">Enable notification vibration</string> <string name="str_enable_vibration">Enable notification vibration</string>
<string name="str_upgrade_account">Upgrade account</string> <string name="str_upgrade_account">Upgrade account</string>
<string name="str_deleteswipe">Delete messages by swiping left</string>
<string name="str_promode">Thank you for supporting the app and using the pro mode</string> <string name="str_promode">Thank you for supporting the app and using the pro mode</string>
<string name="str_promode_info">Increase your daily quota, remove the ad banner and support the developer (that\'s me)</string> <string name="str_promode_info">Increase your daily quota, remove the ad banner and support the developer (that\'s me)</string>
<string name="volume_icon">Volume icon</string> <string name="volume_icon">Volume icon</string>

View File

@@ -1,3 +1,3 @@
#Mon Nov 12 18:26:41 CET 2018 #Sat Nov 17 14:27:38 CET 2018
VERSION_NAME=0.0.7 VERSION_NAME=0.0.9
VERSION_CODE=7 VERSION_CODE=9

77
examples/scn_send.sh Normal file
View File

@@ -0,0 +1,77 @@
#!/usr/bin/env bash
#
# Call with `scn_send title`
# or `scn_send title content`
# or `scn_send title content priority`
#
#
if [ "$#" -lt 1 ]; then
echo "no title supplied via parameter"
exit 1
fi
################################################################################
# INSERT YOUR DATA HERE #
################################################################################
user_id=999
user_key="????????????????????????????????????????????????????????????????"
################################################################################
title=$1
content=""
if [ "$#" -gt 1 ]; then
content=$2
fi
priority=1
if [ "$#" -gt 2 ]; then
priority=$3
fi
usr_msg_id=$(uuidgen)
while true ; do
curlresp=$(curl -s -o /dev/null -w "%{http_code}" \
-d "user_id=$user_id" -d "user_key=$user_key" -d "title=$title" \
-d "content=$content" -d "priority=$priority" -d "msg_id=$usr_msg_id" \
https://scn.blackforestbytes.com/send.php)
if [ "$curlresp" == 200 ] ; then
echo "Successfully send"
exit 0
fi
if [ "$curlresp" == 400 ] ; then
echo "Bad request - something went wrong"
exit 1
fi
if [ "$curlresp" == 401 ] ; then
echo "Unauthorized - wrong userid/userkey"
exit 1
fi
if [ "$curlresp" == 403 ] ; then
echo "Quota exceeded - wait one hour before re-try"
sleep 3600
fi
if [ "$curlresp" == 412 ] ; then
echo "Precondition Failed - No device linked"
exit 1
fi
if [ "$curlresp" == 500 ] ; then
echo "Internal server error - waiting for better times"
sleep 60
fi
# if none of the above matched we probably hav no network ...
echo "Send failed (response code $curlresp) ... try again in 5s"
sleep 5
done

View File

@@ -30,7 +30,7 @@ if ($data['user_key'] !== $user_key) die(json_encode(['success' => false, 'errid
$stmt = $pdo->prepare('SELECT COUNT(*) FROM messages WHERE ack=0 AND sender_user_id=:uid'); $stmt = $pdo->prepare('SELECT COUNT(*) FROM messages WHERE ack=0 AND sender_user_id=:uid');
$stmt->execute(['uid' => $user_id]); $stmt->execute(['uid' => $user_id]);
$nack_count = $stmt->fetch(PDO::FETCH_NUM); $nack_count = $stmt->fetch(PDO::FETCH_NUM)[0];
$quota = $data['quota_today']; $quota = $data['quota_today'];

View File

@@ -41,7 +41,7 @@ foreach ($nonacks_sql as $nack)
'title' => $nack['title'], 'title' => $nack['title'],
'body' => $nack['content'], 'body' => $nack['content'],
'priority' => $nack['priority'], 'priority' => $nack['priority'],
'timestamp' => $nack['timestamp'], 'timestamp' => strtotime($nack['timestamp']),
'usr_msg_id' => $nack['usr_message_id'], 'usr_msg_id' => $nack['usr_message_id'],
'scn_msg_id' => $nack['scn_message_id'], 'scn_msg_id' => $nack['scn_message_id'],
]; ];

View File

@@ -187,6 +187,95 @@
Be aware that the server only saves send messages for a short amount of time. Because of that you can only use this to prevent duplicates in a short time-frame, older messages with the same ID are probably already deleted and the message will be send again. Be aware that the server only saves send messages for a short amount of time. Because of that you can only use this to prevent duplicates in a short time-frame, older messages with the same ID are probably already deleted and the message will be send again.
</p> </p>
</div> </div>
<h2>Bash script example</h2>
<div class="section">
<p>
Depending on your use case it can be useful to create a bash script that handles things like resending messages if you have connection problems or waiting if there is no quota left.<br/>
Here is an example how such a scrippt could look like, you can put it into <code>/usr/local/sbin</code> and call it with <code>scn_send "title" "content"</code>
</p>
<pre style="color:#000000;" class="yellow-code"><span style="color:#3f7f59; font-weight:bold;">#!/usr/bin/env bash</span>
<span style="color:#3f7f59; ">#</span>
<span style="color:#3f7f59; "># Call with `scn_send title`</span>
<span style="color:#3f7f59; "># or `scn_send title content`</span>
<span style="color:#3f7f59; "># or `scn_send title content priority`</span>
<span style="color:#3f7f59; ">#</span>
<span style="color:#3f7f59; ">#</span>
<span style="color:#7f0055; font-weight:bold; ">if</span> [ <span style="color:#2a00ff; ">"$#"</span> -lt 1 ]; <span style="color:#7f0055; font-weight:bold; ">then</span>
<span style="color:#7f0055; font-weight:bold; ">echo</span> <span style="color:#2a00ff; ">"no title supplied via parameter"</span>
<span style="color:#7f0055; font-weight:bold; ">exit</span> 1
<span style="color:#7f0055; font-weight:bold; ">fi</span>
<span style="color:#3f7f59; ">################################################################################</span>
<span style="color:#3f7f59; "># INSERT YOUR DATA HERE #</span>
<span style="color:#3f7f59; ">################################################################################</span>
user_id=999
user_key=<span style="color:#2a00ff; ">"????????????????????????????????????????????????????????????????"</span>
<span style="color:#3f7f59; ">################################################################################</span>
title=$1
content=<span style="color:#2a00ff; ">""</span>
<span style="color:#7f0055; font-weight:bold; ">if</span> [ <span style="color:#2a00ff; ">"$#"</span> -gt 1 ]; <span style="color:#7f0055; font-weight:bold; ">then</span>
content=$2
<span style="color:#7f0055; font-weight:bold; ">fi</span>
priority=1
<span style="color:#7f0055; font-weight:bold; ">if</span> [ <span style="color:#2a00ff; ">"$#"</span> -gt 2 ]; <span style="color:#7f0055; font-weight:bold; ">then</span>
priority=$3
<span style="color:#7f0055; font-weight:bold; ">fi</span>
usr_msg_id=$(uuidgen)
<span style="color:#7f0055; font-weight:bold; ">while</span> true ; <span style="color:#7f0055; font-weight:bold; ">do</span>
curlresp=$(curl -s -o <span style="color:#3f3fbf; ">/dev/null</span> -w <span style="color:#2a00ff; ">"%{http_code}"</span> <span style="color:#2a00ff; ">\</span>
-d <span style="color:#2a00ff; ">"</span><span style="color:#2a00ff; ">user_id</span><span style="color:#2a00ff; ">=</span><span style="color:#2a00ff; ">$user_id</span><span style="color:#2a00ff; ">"</span> -d <span style="color:#2a00ff; ">"</span><span style="color:#2a00ff; ">user_key</span><span style="color:#2a00ff; ">=</span><span style="color:#2a00ff; ">$user_key</span><span style="color:#2a00ff; ">"</span> -d <span style="color:#2a00ff; ">"</span><span style="color:#2a00ff; ">title</span><span style="color:#2a00ff; ">=</span><span style="color:#2a00ff; ">$title</span><span style="color:#2a00ff; ">"</span> <span style="color:#2a00ff; ">\</span>
-d <span style="color:#2a00ff; ">"</span><span style="color:#2a00ff; ">content</span><span style="color:#2a00ff; ">=</span><span style="color:#2a00ff; ">$content</span><span style="color:#2a00ff; ">"</span> -d <span style="color:#2a00ff; ">"</span><span style="color:#2a00ff; ">priority</span><span style="color:#2a00ff; ">=</span><span style="color:#2a00ff; ">$priority</span><span style="color:#2a00ff; ">"</span> -d <span style="color:#2a00ff; ">"</span><span style="color:#2a00ff; ">msg_id</span><span style="color:#2a00ff; ">=</span><span style="color:#2a00ff; ">$usr_msg_id</span><span style="color:#2a00ff; ">"</span> <span style="color:#2a00ff; ">\</span>
https:<span style="color:#3f3fbf; ">/</span><span style="color:#3f3fbf; ">/scn.blackforestbytes.com/send.php</span>)
<span style="color:#7f0055; font-weight:bold; ">if</span> [ <span style="color:#2a00ff; ">"</span><span style="color:#2a00ff; ">$curlresp</span><span style="color:#2a00ff; ">"</span> == 200 ] ; <span style="color:#7f0055; font-weight:bold; ">then</span>
<span style="color:#7f0055; font-weight:bold; ">echo</span> <span style="color:#2a00ff; ">"Successfully send"</span>
<span style="color:#7f0055; font-weight:bold; ">exit</span> 0
<span style="color:#7f0055; font-weight:bold; ">fi</span>
<span style="color:#7f0055; font-weight:bold; ">if</span> [ <span style="color:#2a00ff; ">"</span><span style="color:#2a00ff; ">$curlresp</span><span style="color:#2a00ff; ">"</span> == 400 ] ; <span style="color:#7f0055; font-weight:bold; ">then</span>
<span style="color:#7f0055; font-weight:bold; ">echo</span> <span style="color:#2a00ff; ">"Bad request - something went wrong"</span>
<span style="color:#7f0055; font-weight:bold; ">exit</span> 1
<span style="color:#7f0055; font-weight:bold; ">fi</span>
<span style="color:#7f0055; font-weight:bold; ">if</span> [ <span style="color:#2a00ff; ">"</span><span style="color:#2a00ff; ">$curlresp</span><span style="color:#2a00ff; ">"</span> == 401 ] ; <span style="color:#7f0055; font-weight:bold; ">then</span>
<span style="color:#7f0055; font-weight:bold; ">echo</span> <span style="color:#2a00ff; ">"Unauthorized - wrong </span><span style="color:#3f3fbf; ">userid/userkey</span><span style="color:#2a00ff; ">"</span>
<span style="color:#7f0055; font-weight:bold; ">exit</span> 1
<span style="color:#7f0055; font-weight:bold; ">fi</span>
<span style="color:#7f0055; font-weight:bold; ">if</span> [ <span style="color:#2a00ff; ">"</span><span style="color:#2a00ff; ">$curlresp</span><span style="color:#2a00ff; ">"</span> == 403 ] ; <span style="color:#7f0055; font-weight:bold; ">then</span>
<span style="color:#7f0055; font-weight:bold; ">echo</span> <span style="color:#2a00ff; ">"Quota exceeded - wait one hour before re-try"</span>
sleep 3600
<span style="color:#7f0055; font-weight:bold; ">fi</span>
<span style="color:#7f0055; font-weight:bold; ">if</span> [ <span style="color:#2a00ff; ">"</span><span style="color:#2a00ff; ">$curlresp</span><span style="color:#2a00ff; ">"</span> == 412 ] ; <span style="color:#7f0055; font-weight:bold; ">then</span>
<span style="color:#7f0055; font-weight:bold; ">echo</span> <span style="color:#2a00ff; ">"Precondition Failed - No device linked"</span>
<span style="color:#7f0055; font-weight:bold; ">exit</span> 1
<span style="color:#7f0055; font-weight:bold; ">fi</span>
<span style="color:#7f0055; font-weight:bold; ">if</span> [ <span style="color:#2a00ff; ">"</span><span style="color:#2a00ff; ">$curlresp</span><span style="color:#2a00ff; ">"</span> == 500 ] ; <span style="color:#7f0055; font-weight:bold; ">then</span>
<span style="color:#7f0055; font-weight:bold; ">echo</span> <span style="color:#2a00ff; ">"Internal server error - waiting for better times"</span>
sleep 60
<span style="color:#7f0055; font-weight:bold; ">fi</span>
<span style="color:#3f7f59; "># if none of the above matched we probably hav no network ...</span>
<span style="color:#7f0055; font-weight:bold; ">echo</span> <span style="color:#2a00ff; ">"Send failed (response code </span><span style="color:#2a00ff; ">$curlresp</span><span style="color:#2a00ff; ">) ... try again in 5s"</span>
sleep 5
<span style="color:#7f0055; font-weight:bold; ">done</span>
</pre>
<p>
Be aware that the server only saves send messages for a short amount of time. Because of that you can only use this to prevent duplicates in a short time-frame, older messages with the same ID are probably already deleted and the message will be send again.
</p>
</div>
</div> </div>
</body> </body>

View File

@@ -42,6 +42,11 @@ body
border-left: .25rem solid #E53935; border-left: .25rem solid #E53935;
} }
.yellow-code
{
border-left: .25rem solid #FFCB05;
}
#mainpnl input, #mainpnl input,
#mainpnl textarea #mainpnl textarea
{ {
@@ -233,6 +238,13 @@ table.scode_table th:nth-child(2) {
margin-top: 1.75rem; margin-top: 1.75rem;
} }
.linkcaption:hover { .linkcaption:hover,
.linkcaption:focus {
text-decoration: none; text-decoration: none;
}
pre, pre span
{
font-family: Menlo, Consolas, monospace;
background: #F9F9F9;;
} }

View File

@@ -54,7 +54,7 @@
<div class="row responsive-label"> <div class="row responsive-label">
<div class="col-sm-12 col-md-3"><label for="txt" class="doc">Message Content</label></div> <div class="col-sm-12 col-md-3"><label for="txt" class="doc">Message Content</label></div>
<div class="col-sm-12 col-md"><textarea id="txt" class="doc" <?php echo (isset($_GET['preset_content']) ? (' value="'.$_GET['preset_content'].'" '):(''));?> rows="8"></textarea></div> <div class="col-sm-12 col-md"><textarea id="txt" class="doc" <?php echo (isset($_GET['preset_content']) ? (' value="'.$_GET['preset_content'].'" '):(''));?> rows="8" maxlength="2048"></textarea></div>
</div> </div>
<div class="row"> <div class="row">

View File

@@ -33,7 +33,7 @@ try
if (strlen(trim($message)) == 0) api_return(400, ['success' => false, 'error' => ERR::NO_TITLE, 'errhighlight' => 103, 'message' => 'No title specified']); if (strlen(trim($message)) == 0) api_return(400, ['success' => false, 'error' => ERR::NO_TITLE, 'errhighlight' => 103, 'message' => 'No title specified']);
if (strlen($message) > 120) api_return(400, ['success' => false, 'error' => ERR::TITLE_TOO_LONG, 'errhighlight' => 103, 'message' => 'Title too long (120 characters)']); if (strlen($message) > 120) api_return(400, ['success' => false, 'error' => ERR::TITLE_TOO_LONG, 'errhighlight' => 103, 'message' => 'Title too long (120 characters)']);
if (strlen($content) > 10000) api_return(400, ['success' => false, 'error' => ERR::CONTENT_TOO_LONG, 'errhighlight' => 104, 'message' => 'Content too long (10000 characters)']); if (strlen($content) > 2048) api_return(400, ['success' => false, 'error' => ERR::CONTENT_TOO_LONG, 'errhighlight' => 104, 'message' => 'Content too long (10000 characters)']);
if ($usrmsgid != null && strlen($usrmsgid) > 64) api_return(400, ['success' => false, 'error' => ERR::USR_MSG_ID_TOO_LONG, 'errhighlight' => -1, 'message' => 'MessageID too long (64 characters)']); if ($usrmsgid != null && strlen($usrmsgid) > 64) api_return(400, ['success' => false, 'error' => ERR::USR_MSG_ID_TOO_LONG, 'errhighlight' => -1, 'message' => 'MessageID too long (64 characters)']);
//------------------------------------------------------------------ //------------------------------------------------------------------