android view and stuff
This commit is contained in:
parent
5fcd33e294
commit
b6252a1c1a
@ -21,7 +21,9 @@ dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
|
||||
implementation 'com.android.support:appcompat-v7:27.1.1'
|
||||
implementation 'com.android.support:cardview-v7:27.1.1'
|
||||
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
|
||||
implementation 'com.android.support:recyclerview-v7:27.1.1'
|
||||
|
||||
implementation 'com.google.firebase:firebase-core:16.0.3'
|
||||
implementation 'com.google.firebase:firebase-messaging:17.3.2'
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
<application
|
||||
android:allowBackup="false"
|
||||
android:name="SCNApp"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
|
@ -0,0 +1,35 @@
|
||||
package com.blackforestbytes.simplecloudnotifier;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
|
||||
public class CMessage
|
||||
{
|
||||
public final long Timestamp ;
|
||||
public final String Title;
|
||||
public final String Content;
|
||||
|
||||
private static final SimpleDateFormat _format;
|
||||
static
|
||||
{
|
||||
_format = new SimpleDateFormat("yyyy'-'MM'-'dd HH':'mm':'ss", Locale.getDefault());
|
||||
_format.setTimeZone(TimeZone.getDefault());
|
||||
}
|
||||
|
||||
public CMessage(long t, String mt, String mc)
|
||||
{
|
||||
Timestamp = t;
|
||||
Title = mt;
|
||||
Content = mc;
|
||||
}
|
||||
|
||||
@SuppressLint("SimpleDateFormat")
|
||||
public String formatTimestamp()
|
||||
{
|
||||
return _format.format(new Date(Timestamp*1000));
|
||||
}
|
||||
}
|
@ -0,0 +1,122 @@
|
||||
package com.blackforestbytes.simplecloudnotifier;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class CMessageList
|
||||
{
|
||||
public ArrayList<CMessage> Messages;
|
||||
|
||||
private ArrayList<WeakReference<MessageAdapter>> _listener = new ArrayList<>();
|
||||
|
||||
private final static Object _lock = new Object();
|
||||
private static CMessageList _inst = null;
|
||||
public static CMessageList inst()
|
||||
{
|
||||
synchronized (_lock)
|
||||
{
|
||||
if (_inst != null) return _inst;
|
||||
return _inst = new CMessageList();
|
||||
}
|
||||
}
|
||||
|
||||
private CMessageList()
|
||||
{
|
||||
Messages = new ArrayList<>();
|
||||
|
||||
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", "");
|
||||
|
||||
Messages.add(new CMessage(time, title, content));
|
||||
}
|
||||
}
|
||||
|
||||
public void add(final long time, final String title, final String content)
|
||||
{
|
||||
boolean run = SCNApp.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
SharedPreferences sharedPref = SCNApp.getContext().getSharedPreferences("CMessageList", Context.MODE_PRIVATE);
|
||||
int count = sharedPref.getInt("message_count", 0);
|
||||
|
||||
SharedPreferences.Editor e = sharedPref.edit();
|
||||
|
||||
Messages.add(new CMessage(time, title, content));
|
||||
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.apply();
|
||||
|
||||
for (WeakReference<MessageAdapter> ref : _listener)
|
||||
{
|
||||
MessageAdapter a = ref.get();
|
||||
if (a == null) continue;
|
||||
a.notifyItemInserted(count);
|
||||
}
|
||||
CleanUpListener();
|
||||
}
|
||||
});
|
||||
|
||||
if (!run)
|
||||
{
|
||||
Messages.add(new CMessage(time, title, content));
|
||||
fullSave();
|
||||
}
|
||||
}
|
||||
|
||||
public void fullSave()
|
||||
{
|
||||
SharedPreferences sharedPref = SCNApp.getContext().getSharedPreferences("CMessageList", Context.MODE_PRIVATE);
|
||||
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);
|
||||
e.putString("message["+i+"].title", Messages.get(i).Title);
|
||||
e.putString("message["+i+"].content", Messages.get(i).Content);
|
||||
}
|
||||
|
||||
e.apply();
|
||||
}
|
||||
|
||||
public CMessage tryGet(int pos)
|
||||
{
|
||||
if (pos < 0 || pos >= Messages.size()) return null;
|
||||
return Messages.get(pos);
|
||||
}
|
||||
|
||||
public int size()
|
||||
{
|
||||
return Messages.size();
|
||||
}
|
||||
|
||||
public void register(MessageAdapter adp)
|
||||
{
|
||||
_listener.add(new WeakReference<>(adp));
|
||||
CleanUpListener();
|
||||
}
|
||||
|
||||
private void CleanUpListener()
|
||||
{
|
||||
for (int i=_listener.size()-1; i >= 0; i--)
|
||||
{
|
||||
if (_listener.get(i).get() == null) _listener.remove(i);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,17 +1,13 @@
|
||||
package com.blackforestbytes.simplecloudnotifier;
|
||||
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.google.firebase.messaging.FirebaseMessagingService;
|
||||
import com.google.firebase.messaging.RemoteMessage;
|
||||
|
||||
public class FBMService extends FirebaseMessagingService
|
||||
{
|
||||
/**
|
||||
* Called if InstanceID token is updated. This may occur if the security of
|
||||
* the previous token had been compromised. Note that this is called when the InstanceID token
|
||||
* is initially generated so this is where you would retrieve the token.
|
||||
*/
|
||||
@Override
|
||||
public void onNewToken(String token)
|
||||
{
|
||||
@ -19,36 +15,28 @@ public class FBMService extends FirebaseMessagingService
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessageReceived(RemoteMessage remoteMessage) {
|
||||
// [START_EXCLUDE]
|
||||
// There are two types of messages data messages and notification messages. Data messages are handled
|
||||
// here in onMessageReceived whether the app is in the foreground or background. Data messages are the type
|
||||
// traditionally used with GCM. Notification messages are only received here in onMessageReceived when the app
|
||||
// is in the foreground. When the app is in the background an automatically generated notification is displayed.
|
||||
// When the user taps on the notification they are returned to the app. Messages containing both notification
|
||||
// and data payloads are treated as notification messages. The Firebase console always sends notification
|
||||
// messages. For more see: https://firebase.google.com/docs/cloud-messaging/concept-options
|
||||
// [END_EXCLUDE]
|
||||
|
||||
Log.i("FB::MessageReceived<0>", "");
|
||||
|
||||
|
||||
// TODO(developer): Handle FCM messages here.
|
||||
// Not getting messages here? See why this may be: https://goo.gl/39bRNJ
|
||||
Log.i("FB::MessageReceived", "From: " + remoteMessage.getFrom());
|
||||
|
||||
// Check if message contains a data payload.
|
||||
if (remoteMessage.getData().size() > 0) {
|
||||
public void onMessageReceived(RemoteMessage remoteMessage)
|
||||
{
|
||||
try
|
||||
{
|
||||
Log.i("FB::MessageReceived", "From: " + remoteMessage.getFrom());
|
||||
Log.i("FB::MessageReceived", "Payload: " + remoteMessage.getData());
|
||||
}
|
||||
if (remoteMessage.getNotification() != null) Log.i("FB::MessageReceived", "Notify_Title: " + remoteMessage.getNotification().getTitle());
|
||||
if (remoteMessage.getNotification() != null) Log.i("FB::MessageReceived", "Notify_Body: " + remoteMessage.getNotification().getBody());
|
||||
|
||||
// Check if message contains a notification payload.
|
||||
if (remoteMessage.getNotification() != null) {
|
||||
Log.i("FB::MessageReceived", "Notify_Title: " + remoteMessage.getNotification().getTitle());
|
||||
Log.i("FB::MessageReceived", "Notify_Body: " + remoteMessage.getNotification().getBody());
|
||||
}
|
||||
long time = Long.parseLong(remoteMessage.getData().get("timestamp"));
|
||||
String title = remoteMessage.getData().get("title");
|
||||
String content = remoteMessage.getData().get("content");
|
||||
|
||||
// Also if you intend on generating your own notifications as a result of a received FCM
|
||||
// message, here is where that should be initiated. See sendNotification method below.
|
||||
CMessageList.inst().add(time, title, content);
|
||||
|
||||
|
||||
SCNApp.showToast(title, Toast.LENGTH_LONG);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.e("FB:Err", e.toString());
|
||||
SCNApp.showToast("Recieved invalid message from server", Toast.LENGTH_LONG);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -2,6 +2,8 @@ package com.blackforestbytes.simplecloudnotifier;
|
||||
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
@ -9,24 +11,25 @@ import com.google.android.gms.tasks.OnSuccessListener;
|
||||
import com.google.firebase.iid.FirebaseInstanceId;
|
||||
import com.google.firebase.iid.InstanceIdResult;
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
public class MainActivity extends AppCompatActivity
|
||||
{
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
FirebaseInstanceId.getInstance().getInstanceId().addOnSuccessListener(new OnSuccessListener<InstanceIdResult>() {
|
||||
@Override
|
||||
public void onSuccess(InstanceIdResult instanceIdResult) {
|
||||
Log.d("FB::ID", instanceIdResult.getId());
|
||||
Log.d("FB::TOKEN", instanceIdResult.getToken());
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
RecyclerView rvMessages = findViewById(R.id.rvMessages);
|
||||
rvMessages.setLayoutManager(new LinearLayoutManager(this, RecyclerView.VERTICAL, true));
|
||||
rvMessages.setAdapter(new MessageAdapter());
|
||||
|
||||
SCNApp.register(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop()
|
||||
{
|
||||
super.onStop();
|
||||
|
||||
CMessageList.inst().fullSave();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,72 @@
|
||||
package com.blackforestbytes.simplecloudnotifier;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
public class MessageAdapter extends RecyclerView.Adapter
|
||||
{
|
||||
public MessageAdapter()
|
||||
{
|
||||
CMessageList.inst().register(this);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType)
|
||||
{
|
||||
View myView = LayoutInflater.from(parent.getContext()).inflate(R.layout.message_card, parent, false);
|
||||
return new MessagePresenter(myView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position)
|
||||
{
|
||||
CMessage msg = CMessageList.inst().tryGet(position);
|
||||
MessagePresenter view = (MessagePresenter) holder;
|
||||
view.setMessage(msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount()
|
||||
{
|
||||
return CMessageList.inst().size();
|
||||
}
|
||||
|
||||
private class MessagePresenter extends RecyclerView.ViewHolder implements View.OnClickListener
|
||||
{
|
||||
private TextView tvTimestamp;
|
||||
private TextView tvTitle;
|
||||
private TextView tvMessage;
|
||||
|
||||
private CMessage data;
|
||||
|
||||
MessagePresenter(View itemView)
|
||||
{
|
||||
super(itemView);
|
||||
tvTimestamp = itemView.findViewById(R.id.tvTimestamp);
|
||||
tvTitle = itemView.findViewById(R.id.tvTitle);
|
||||
tvMessage = itemView.findViewById(R.id.tvMessage);
|
||||
itemView.setOnClickListener(this);
|
||||
}
|
||||
|
||||
void setMessage(CMessage msg)
|
||||
{
|
||||
tvTimestamp.setText(msg.formatTimestamp());
|
||||
tvTitle.setText(msg.Title);
|
||||
tvMessage.setText(msg.Content);
|
||||
data = msg;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onClick(View v)
|
||||
{
|
||||
SCNApp.showToast(data.Title, Toast.LENGTH_LONG);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
package com.blackforestbytes.simplecloudnotifier;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
public class SCNApp extends Application
|
||||
{
|
||||
private static SCNApp instance;
|
||||
private static WeakReference<MainActivity> mainActivity;
|
||||
|
||||
public SCNApp()
|
||||
{
|
||||
instance = this;
|
||||
}
|
||||
|
||||
public static Context getContext()
|
||||
{
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static void showToast(final String msg, final int duration)
|
||||
{
|
||||
final MainActivity a = mainActivity.get();
|
||||
if (a != null)
|
||||
{
|
||||
a.runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
Toast.makeText(a, msg, duration).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean runOnUiThread(Runnable r)
|
||||
{
|
||||
final MainActivity a = mainActivity.get();
|
||||
if (a != null) {a.runOnUiThread(r); return true;}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void register(MainActivity a)
|
||||
{
|
||||
mainActivity = new WeakReference<>(a);
|
||||
}
|
||||
}
|
@ -1,29 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".MainActivity">
|
||||
tools:showIn="@layout/activity_main">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:id="@+id/rvMessages"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Hello World!"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
android:clipToPadding="false"
|
||||
android:scrollbars="vertical" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="Get ID"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
</RelativeLayout>
|
67
android/app/src/main/res/layout/message_card.xml
Normal file
67
android/app/src/main/res/layout/message_card.xml
Normal file
@ -0,0 +1,67 @@
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:card_view="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<android.support.v7.widget.CardView
|
||||
android:id="@+id/card_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_margin="@dimen/card_margin"
|
||||
android:elevation="3dp"
|
||||
card_view:cardCornerRadius="@dimen/card_album_radius">
|
||||
|
||||
|
||||
<android.support.constraint.ConstraintLayout
|
||||
android:background="@color/cardview_light_background"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvTimestamp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="italic"
|
||||
android:text="2018-09-11 20:22:32" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvTitle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toLeftOf="@+id/tvTimestamp"
|
||||
android:layout_marginRight="4sp"
|
||||
android:layout_marginEnd="4sp"
|
||||
android:textSize="16sp"
|
||||
android:textColor="@color/colorBlack"
|
||||
android:textStyle="bold"
|
||||
android:ellipsize="none"
|
||||
android:maxLines="6"
|
||||
|
||||
android:text="Message from me"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvMessage"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toBottomOf="@+id/tvTitle"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
android:layout_margin="4sp"
|
||||
android:ellipsize="none"
|
||||
android:maxLines="32"
|
||||
android:scrollHorizontally="false"
|
||||
|
||||
android:text="asdasd asdasd asd asd a" />
|
||||
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
|
||||
</android.support.v7.widget.CardView>
|
||||
|
||||
</LinearLayout>
|
@ -3,4 +3,6 @@
|
||||
<color name="colorPrimary">#3F51B5</color>
|
||||
<color name="colorPrimaryDark">#303F9F</color>
|
||||
<color name="colorAccent">#FF4081</color>
|
||||
|
||||
<color name="colorBlack">#000</color>
|
||||
</resources>
|
||||
|
5
android/app/src/main/res/values/dimens.xml
Normal file
5
android/app/src/main/res/values/dimens.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<dimen name="card_margin">5dp</dimen>
|
||||
<dimen name="card_album_radius">0dp</dimen>
|
||||
</resources>
|
@ -9,8 +9,6 @@ buildscript {
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.1.4'
|
||||
classpath 'com.google.gms:google-services:4.0.1'
|
||||
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
}
|
||||
|
@ -44,6 +44,7 @@ $payload = json_encode(
|
||||
[
|
||||
'title' => $message,
|
||||
'body' => $content,
|
||||
'timestamp' => time(),
|
||||
]
|
||||
]);
|
||||
$header=
|
||||
@ -61,12 +62,8 @@ catch (Exception $e)
|
||||
die(json_encode(['success' => false, 'message' => 'Exception: ' . $e->getMessage()]));
|
||||
}
|
||||
|
||||
var_dump($httpresult);
|
||||
|
||||
|
||||
$stmt = $pdo->prepare('UPDATE users SET timestamp_accessed=NOW(), messages_sent=messages_sent+1 WHERE user_id = :uid');
|
||||
$stmt->execute(['uid' => $user_id]);
|
||||
|
||||
|
||||
echo (json_encode(['success' => true, 'message' => 'Message sent']));
|
||||
echo (json_encode(['success' => true, 'message' => 'Message sent', 'response' => $httpresult]));
|
||||
return 0;
|
Loading…
Reference in New Issue
Block a user