Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
9ef6b1dd91
|
|||
6f7585323b
|
|||
92135e64a3
|
|||
28efeea30b
|
|||
a8a074907b
|
|||
6c29ec9820
|
|||
![]() |
8ec23144ca |
@@ -24,6 +24,11 @@ public final class CollectionHelper
|
||||
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)
|
||||
{
|
||||
return sort(input, mapper, 1);
|
||||
|
@@ -3,17 +3,21 @@ package com.blackforestbytes.simplecloudnotifier.model;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import com.blackforestbytes.simplecloudnotifier.lib.collections.CollectionHelper;
|
||||
import com.blackforestbytes.simplecloudnotifier.lib.string.Str;
|
||||
import com.blackforestbytes.simplecloudnotifier.view.MessageAdapter;
|
||||
import com.blackforestbytes.simplecloudnotifier.SCNApp;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class CMessageList
|
||||
{
|
||||
private final Object msg_lock = new Object();
|
||||
|
||||
public ArrayList<CMessage> Messages;
|
||||
public Set<String> AllAcks;
|
||||
|
||||
@@ -31,6 +35,8 @@ public class CMessageList
|
||||
}
|
||||
|
||||
private CMessageList()
|
||||
{
|
||||
synchronized (msg_lock)
|
||||
{
|
||||
Messages = new ArrayList<>();
|
||||
AllAcks = new HashSet<>();
|
||||
@@ -50,6 +56,7 @@ public class CMessageList
|
||||
|
||||
AllAcks = sharedPref.getStringSet("acks", new HashSet<>());
|
||||
}
|
||||
}
|
||||
|
||||
public CMessage add(final long scnid, final long time, final String title, final String content, final PriorityEnum pe)
|
||||
{
|
||||
@@ -60,12 +67,19 @@ public class CMessageList
|
||||
SharedPreferences sharedPref = SCNApp.getContext().getSharedPreferences("CMessageList", Context.MODE_PRIVATE);
|
||||
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));
|
||||
|
||||
while (Messages.size()>SCNSettings.inst().LocalCacheSize) Messages.remove(0);
|
||||
}
|
||||
|
||||
if (Messages.size()>1 && Messages.get(Messages.size()-2).Timestamp < msg.Timestamp)
|
||||
{
|
||||
// quick save
|
||||
|
||||
SharedPreferences.Editor e = sharedPref.edit();
|
||||
|
||||
e.putInt( "message_count", count+1);
|
||||
e.putLong( "message["+count+"].timestamp", time);
|
||||
@@ -77,22 +91,33 @@ public class CMessageList
|
||||
e.putStringSet("acks", AllAcks);
|
||||
|
||||
e.apply();
|
||||
}
|
||||
else
|
||||
{
|
||||
// full save
|
||||
|
||||
fullSave(); // does sort in here
|
||||
}
|
||||
|
||||
|
||||
for (WeakReference<MessageAdapter> ref : _listener)
|
||||
{
|
||||
MessageAdapter a = ref.get();
|
||||
if (a == null) continue;
|
||||
a.customNotifyItemInserted(count);
|
||||
a.customNotifyDataSetChanged();
|
||||
a.scrollToTop();
|
||||
}
|
||||
CleanUpListener();
|
||||
});
|
||||
|
||||
if (!run)
|
||||
{
|
||||
synchronized (msg_lock)
|
||||
{
|
||||
Messages.add(new CMessage(scnid, time, title, content, pe));
|
||||
AllAcks.add(Long.toHexString(msg.SCN_ID));
|
||||
fullSave();
|
||||
}
|
||||
fullSave(); // does sort in here
|
||||
}
|
||||
|
||||
return msg;
|
||||
@@ -114,6 +139,10 @@ public class CMessageList
|
||||
|
||||
public void fullSave()
|
||||
{
|
||||
synchronized (msg_lock)
|
||||
{
|
||||
CollectionHelper.sort_inplace(Messages, (a,b) -> Long.compare(a.Timestamp, b.Timestamp));
|
||||
|
||||
SharedPreferences sharedPref = SCNApp.getContext().getSharedPreferences("CMessageList", Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor e = sharedPref.edit();
|
||||
|
||||
@@ -134,12 +163,16 @@ public class CMessageList
|
||||
|
||||
e.apply();
|
||||
}
|
||||
}
|
||||
|
||||
public CMessage tryGet(int pos)
|
||||
{
|
||||
synchronized (msg_lock)
|
||||
{
|
||||
if (pos < 0 || pos >= Messages.size()) return null;
|
||||
return Messages.get(pos);
|
||||
}
|
||||
}
|
||||
|
||||
public CMessage tryGetFromBack(int pos)
|
||||
{
|
||||
@@ -147,9 +180,12 @@ public class CMessageList
|
||||
}
|
||||
|
||||
public int size()
|
||||
{
|
||||
synchronized (msg_lock)
|
||||
{
|
||||
return Messages.size();
|
||||
}
|
||||
}
|
||||
|
||||
public void register(MessageAdapter adp)
|
||||
{
|
||||
@@ -166,19 +202,31 @@ public class CMessageList
|
||||
}
|
||||
|
||||
public boolean isAck(long id)
|
||||
{
|
||||
synchronized (msg_lock)
|
||||
{
|
||||
return AllAcks.contains(Long.toHexString(id));
|
||||
}
|
||||
}
|
||||
|
||||
public void remove(int index)
|
||||
public CMessage removeFromBack(int pos)
|
||||
{
|
||||
Messages.remove(index);
|
||||
fullSave();
|
||||
CMessage r;
|
||||
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)
|
||||
{
|
||||
synchronized (msg_lock)
|
||||
{
|
||||
Messages.add(index, item);
|
||||
fullSave();
|
||||
}
|
||||
fullSave(); // does sort in here
|
||||
}
|
||||
}
|
||||
|
@@ -15,15 +15,20 @@ import com.google.firebase.iid.FirebaseInstanceId;
|
||||
public class SCNSettings
|
||||
{
|
||||
private final static Object _lock = new Object();
|
||||
private static SCNSettings _inst = null;
|
||||
private static volatile SCNSettings _inst = null;
|
||||
public static SCNSettings inst()
|
||||
{
|
||||
SCNSettings local = _inst;
|
||||
if (local == null)
|
||||
{
|
||||
synchronized (_lock)
|
||||
{
|
||||
if (_inst != null) return _inst;
|
||||
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 int LocalCacheSize = 500;
|
||||
public boolean EnableDeleteSwipe = true;
|
||||
|
||||
public final NotificationSettings PriorityLow = new NotificationSettings(PriorityEnum.LOW);
|
||||
public final NotificationSettings PriorityNorm = new NotificationSettings(PriorityEnum.NORMAL);
|
||||
@@ -70,6 +76,7 @@ public class SCNSettings
|
||||
|
||||
Enabled = sharedPref.getBoolean("app_enabled", Enabled);
|
||||
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.EnableSound = sharedPref.getBoolean("priority_low:enabled_sound", PriorityLow.EnableSound);
|
||||
@@ -116,6 +123,7 @@ public class SCNSettings
|
||||
|
||||
e.putBoolean("app_enabled", Enabled);
|
||||
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_sound", PriorityLow.EnableSound);
|
||||
|
@@ -171,6 +171,9 @@ public class NotificationService
|
||||
mBuilder.setShowWhen(true);
|
||||
mBuilder.setWhen(msg.Timestamp * 1000);
|
||||
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.NORMAL) mBuilder.setPriority(NotificationCompat.PRIORITY_DEFAULT);
|
||||
@@ -198,7 +201,7 @@ public class NotificationService
|
||||
|
||||
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)
|
||||
|
@@ -3,6 +3,7 @@ package com.blackforestbytes.simplecloudnotifier.util;
|
||||
import android.graphics.Canvas;
|
||||
import android.view.View;
|
||||
|
||||
import com.blackforestbytes.simplecloudnotifier.model.SCNSettings;
|
||||
import com.blackforestbytes.simplecloudnotifier.view.MessageAdapter;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -13,10 +14,21 @@ public class MessageAdapterTouchHelper extends ItemTouchHelper.SimpleCallback
|
||||
{
|
||||
private MessageAdapterTouchHelperListener listener;
|
||||
|
||||
private int dir = 0;
|
||||
|
||||
public MessageAdapterTouchHelper(int dragDirs, int swipeDirs, MessageAdapterTouchHelperListener listener)
|
||||
{
|
||||
super(dragDirs, swipeDirs);
|
||||
this.dir = swipeDirs;
|
||||
this.listener = listener;
|
||||
updateEnabled();
|
||||
}
|
||||
|
||||
public void updateEnabled()
|
||||
{
|
||||
int sdir = SCNSettings.inst().EnableDeleteSwipe ? ItemTouchHelper.LEFT : 0;
|
||||
if (dir == sdir) return;
|
||||
setDefaultSwipeDirs(dir = sdir);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -57,7 +57,6 @@ public class MessageAdapter extends RecyclerView.Adapter
|
||||
viewHolders.put(view, true);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onViewRecycled(@NonNull RecyclerView.ViewHolder holder)
|
||||
{
|
||||
@@ -87,16 +86,17 @@ public class MessageAdapter extends RecyclerView.Adapter
|
||||
manLayout.smoothScrollToPosition(viewRecycler, null, 0);
|
||||
}
|
||||
|
||||
public void removeItem(int position)
|
||||
public CMessage removeItem(int position)
|
||||
{
|
||||
CMessageList.inst().remove(position);
|
||||
notifyItemRemoved(position);
|
||||
CMessage i = CMessageList.inst().removeFromBack(position);
|
||||
notifyDataSetChanged();
|
||||
return i;
|
||||
}
|
||||
|
||||
public void restoreItem(CMessage item, int position)
|
||||
{
|
||||
CMessageList.inst().insert(position, item);
|
||||
notifyItemInserted(position);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public class MessagePresenter extends RecyclerView.ViewHolder implements View.OnClickListener
|
||||
|
@@ -9,7 +9,6 @@ import android.view.ViewGroup;
|
||||
import com.blackforestbytes.simplecloudnotifier.R;
|
||||
import com.blackforestbytes.simplecloudnotifier.SCNApp;
|
||||
import com.blackforestbytes.simplecloudnotifier.model.CMessage;
|
||||
import com.blackforestbytes.simplecloudnotifier.model.CMessageList;
|
||||
import com.blackforestbytes.simplecloudnotifier.model.SCNSettings;
|
||||
import com.blackforestbytes.simplecloudnotifier.service.IABService;
|
||||
import com.blackforestbytes.simplecloudnotifier.util.MessageAdapterTouchHelper;
|
||||
@@ -28,6 +27,8 @@ public class NotificationsFragment extends Fragment implements MessageAdapterTou
|
||||
private PublisherAdView adView;
|
||||
private MessageAdapter adpMessages;
|
||||
|
||||
public MessageAdapterTouchHelper touchHelper;
|
||||
|
||||
public NotificationsFragment()
|
||||
{
|
||||
// Required empty public constructor
|
||||
@@ -43,7 +44,7 @@ public class NotificationsFragment extends Fragment implements MessageAdapterTou
|
||||
rvMessages.setLayoutManager(lman);
|
||||
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);
|
||||
|
||||
adView = v.findViewById(R.id.adBanner);
|
||||
@@ -65,13 +66,11 @@ public class NotificationsFragment extends Fragment implements MessageAdapterTou
|
||||
{
|
||||
if (viewHolder instanceof MessageAdapter.MessagePresenter)
|
||||
{
|
||||
final CMessage deletedItem = CMessageList.inst().tryGet(viewHolder.getAdapterPosition());
|
||||
final int deletedIndex = viewHolder.getAdapterPosition();
|
||||
|
||||
final CMessage deletedItem = adpMessages.removeItem(viewHolder.getAdapterPosition());
|
||||
String name = deletedItem.Title;
|
||||
|
||||
adpMessages.removeItem(viewHolder.getAdapterPosition());
|
||||
|
||||
Snackbar snackbar = Snackbar.make(SCNApp.getMainActivity().layoutRoot, name + " removed", Snackbar.LENGTH_LONG);
|
||||
snackbar.setAction("UNDO", view -> adpMessages.restoreItem(deletedItem, deletedIndex));
|
||||
snackbar.setActionTextColor(Color.YELLOW);
|
||||
|
@@ -49,6 +49,7 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
|
||||
private Button prefUpgradeAccount;
|
||||
private TextView prefUpgradeAccount_msg;
|
||||
private TextView prefUpgradeAccount_info;
|
||||
private Switch prefEnableDeleteSwipe;
|
||||
|
||||
private Switch prefMsgLowEnableSound;
|
||||
private TextView prefMsgLowRingtone_value;
|
||||
@@ -114,6 +115,7 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
|
||||
prefUpgradeAccount = v.findViewById(R.id.prefUpgradeAccount);
|
||||
prefUpgradeAccount_msg = v.findViewById(R.id.prefUpgradeAccount2);
|
||||
prefUpgradeAccount_info = v.findViewById(R.id.prefUpgradeAccount_info);
|
||||
prefEnableDeleteSwipe = v.findViewById(R.id.prefEnableDeleteSwipe);
|
||||
|
||||
prefMsgLowEnableSound = v.findViewById(R.id.prefMsgLowEnableSound);
|
||||
prefMsgLowRingtone_value = v.findViewById(R.id.prefMsgLowRingtone_value);
|
||||
@@ -159,6 +161,7 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
|
||||
if (c == null) return;
|
||||
|
||||
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_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();
|
||||
|
||||
prefAppEnabled.setOnCheckedChangeListener((a,b) -> { s.Enabled=b; saveAndUpdate(); });
|
||||
prefEnableDeleteSwipe.setOnCheckedChangeListener((a,b) -> { s.EnableDeleteSwipe=b; saveAndUpdate(); });
|
||||
|
||||
prefLocalCacheSize.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener()
|
||||
{
|
||||
@@ -324,6 +328,7 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
|
||||
{
|
||||
SCNSettings.inst().save();
|
||||
updateUI();
|
||||
SCNApp.getMainActivity().adpTabs.tab1.touchHelper.updateEnabled();
|
||||
}
|
||||
|
||||
private void onUpgradeAccount()
|
||||
|
@@ -79,6 +79,22 @@
|
||||
|
||||
</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
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
|
@@ -29,6 +29,7 @@
|
||||
<string name="str_ledcolor">Notification light color</string>
|
||||
<string name="str_enable_vibration">Enable notification vibration</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_info">Increase your daily quota, remove the ad banner and support the developer (that\'s me)</string>
|
||||
<string name="volume_icon">Volume icon</string>
|
||||
|
@@ -1,3 +1,3 @@
|
||||
#Sat Nov 17 03:25:30 CET 2018
|
||||
VERSION_NAME=0.0.8
|
||||
VERSION_CODE=8
|
||||
#Sat Nov 17 14:27:38 CET 2018
|
||||
VERSION_NAME=0.0.9
|
||||
VERSION_CODE=9
|
||||
|
@@ -238,7 +238,8 @@ table.scode_table th:nth-child(2) {
|
||||
margin-top: 1.75rem;
|
||||
}
|
||||
|
||||
.linkcaption:hover {
|
||||
.linkcaption:hover,
|
||||
.linkcaption:focus {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
|
@@ -54,7 +54,7 @@
|
||||
|
||||
<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"><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 class="row">
|
||||
|
@@ -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($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)']);
|
||||
|
||||
//------------------------------------------------------------------
|
||||
|
Reference in New Issue
Block a user