Notification builder


#1

I built an android app that gets a message from a server and pass it to the notification … every thing work wonderful … when i close the app (shutdown) … i stop getting the notification …
how can i make my app working at the background like “whatsApp” when it closed she still getting messages
i read and try sketch’s from the net … but no success …

help me … plzzzzzzzzzzzzz

thanx a lot


#2

@basher24 You could create a foreground service from your sketch.

I quickly adapted some code from a larger project:

service_demo.pde

void setup() {
  fullScreen();
  initService();
}

void draw() {
  background(180);

  if (instance != null) {
    println("Service is running:", instance.running);
  }
}

void initService() {
  Intent startIntent = new Intent(getActivity(), DummyService.class);
  getActivity().startService(startIntent);
}

void stop() {
  if (instance != null) {
    instance.stop();
  }
}

DummyService.pde

import android.app.Service;
import android.os.Handler;
import android.os.IBinder;
import android.content.Intent;
import android.app.Notification;
import android.app.PendingIntent;

DummyService instance;

class DummyService extends Service {
  static final String TAG = "DummyService";
  static final int FOREGROUND_NOTIFICATION_ID = 1;

  boolean running = false;
  Handler handler = new Handler();
  Runnable runnable;
  
  int WAIT_SECONDS = 30;

  @Override
  void onCreate() {
    super.onCreate();
    start();
    instance = this;
  }

  @Override
  void onDestroy() {
    stop();
    super.onDestroy();
  }

  @Override
  IBinder onBind(Intent intent) {
    // Used only in case of bound services.
    return null;
  }

  void start() {
    goForeground();
    workLoop();
    running = true;
  }
  
  void stop() {
    running = false;
  }
  
  void goForeground() {
    Intent notificationIntent = new Intent(this, getActivity().getClass());
    PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
    Notification n = new Notification.Builder(this)
            .setContentTitle("Dummy Service")
            .setContentText("Testing foreground services from Processing.")
            .setContentIntent(pendingIntent)
            .build();
    startForeground(FOREGROUND_NOTIFICATION_ID, n);    
  }
  
  void workLoop() {
    runnable = new Runnable() {
      @Override
      public void run() {
        // Do something
      }
    };
    if (running) {
      handler.postDelayed(runnable, WAIT_SECONDS * 1000);
    }
  }
}

I hope this can get you going!


#3

thanks andres

i try to lunch the program but i still get null

for the variable “instance”

basher


#4

Ok… I missed a couple of important things. Firs of all, the service class needs to be declared in the manifest file… and also think the source file for DummyService should be a .java instead of .pde, so…

service_demo.pde

import android.content.Intent;

void setup() {
  fullScreen();
  initService();
}

void draw() {
  background(180);

  if (DummyService.instance != null) {
    println("Service is running:", DummyService.instance.running);
  }
}

void initService() {
  DummyService.activity = getActivity();
  Intent startIntent = new Intent(getActivity(), DummyService.class);
  getActivity().startService(startIntent);
}

void stop() {
  if (DummyService.instance != null) {
    DummyService.instance.stop();
  }
}

DummyService.java

import android.app.Activity;
import android.app.Service;
import android.os.Handler;
import android.os.IBinder;
import android.content.Intent;
import android.app.Notification;
import android.app.PendingIntent;

class DummyService extends Service {
  static final String TAG = "DummyService";
  static final int FOREGROUND_NOTIFICATION_ID = 1;

  static Activity activity;
  static DummyService instance;

  boolean running = false;
  Handler handler = new Handler();
  Runnable runnable;
  
  int WAIT_SECONDS = 30;

  @Override
  public void onCreate() {
    System.out.println("Created service");
    super.onCreate();
    start();
    instance = this;
  }

  @Override
  public void onDestroy() {
    stop();
    super.onDestroy();
  }

  @Override
  public IBinder onBind(Intent intent) {
    // Used only in case of bound services.
    return null;
  }

  void start() {
    goForeground();
    workLoop();
    running = true;
  }
  
  void stop() {
    running = false;
  }
  
  void goForeground() {
    Intent notificationIntent = new Intent(this, activity.getClass());
    PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
    Notification n = new Notification.Builder(this)
            .setContentTitle("Dummy Service")
            .setContentText("Testing foreground services from Processing.")
            .setContentIntent(pendingIntent)
            .build();
    startForeground(FOREGROUND_NOTIFICATION_ID, n);    
  }
  
  void workLoop() {
    runnable = new Runnable() {
      @Override
      public void run() {
        // Do something
      }
    };
    if (running) {
      handler.postDelayed(runnable, WAIT_SECONDS * 1000);
    }
  }
}

and the manifest file:

<?xml version="1.0" encoding="UTF-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="">
    <uses-sdk android:minSdkVersion="17" android:targetSdkVersion="26"/>
    <application android:icon="@drawable/icon" android:label="">
        <activity android:name=".MainActivity" android:theme="@style/Theme.AppCompat.Light.NoActionBar.FullScreen">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <service android:enabled="true" android:exported="false" android:name=".DummyService"/>
    </application>
</manifest>

#5

Now I have this error :slight_smile:

FATAL EXCEPTION: main
Process: processing.test.service_demo, PID: 2514
java.lang.RuntimeException: Unable to instantiate service processing.test.service_demo.DummyService: java.lang.IllegalAccessException: java.lang.Class<processing.test.service_demo.DummyService> is not accessible from java.lang.Class<android.app.ActivityThread>
at android.app.ActivityThread.handleCreateService(ActivityThread.java:3446)
at android.app.ActivityThread.-wrap6(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1725)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6692)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1468)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1358)
Caused by: java.lang.IllegalAccessException: java.lang.Class<processing.test.service_demo.DummyService> is not accessible from java.lang.Class<android.app.ActivityThread>
at java.lang.Class.newInstance(Native Method)
at android.app.ActivityThread.handleCreateService(ActivityThread.java:3443)
… 8 more


#6

Declare the DummyService class as public, i.e.:

public class DummyService extends Service {

Just noticed another error in my example, the runnable should post itself at the end of the run() method to keep the process going in the background:

  void workLoop() {
    System.out.println("launching...");
    runnable = new Runnable() {
      @Override
      public void run() {
        System.out.println("working...");
        if (running) {
          handler.postDelayed(runnable, WAIT_SECONDS * 1000);
        }        
      }
    };
    handler.postDelayed(runnable, WAIT_SECONDS * 1000);    
  }

With these changes, you should see the message “working…” every two seconds even if the app is in the background.


#7

thank you very much :blush::blush::blush::blush:
its work very fine …

I insert whats inside setup() to my setup() program .
and add the functions below draw() to my sketch …

my sketch name is “AnnexSafe” … which reports building worker accidents or hazard from the field to an office server (excel sheet) … and receive notification from the server If the issue has not been addressed .

so , i didnt access my App when i tap the notification … i try some codes from internet … no access

I respect your help
thanx
basher


#8

hi andres ,

basher again … short question … how can i ran a function from my (processing sketch) inside run() at the DummyService.java ?

the function (listed below) is checking\trying connection between server and client …

public void MyConnection()
{

client = new MuKCastClient(this, ServerIP, int(ServerPort));

                try {

                     client.connect();     
                     ConnectStatus = "Connected";

                } catch(IOException e) {
                                   //   e.printStackTrace();   
                                        ConnectStatus = "disConnected";
                                        }

}

thanx ;
basher


#9

Hi, the tricky part of using services is that the code in the java file does not know anything about the sketch, you need to explicitly send a reference to the sketch object so the service can access all your functions. A not-very elegant way to do this, but which seems to work, is as follows (only listing the changes):

AndroidService.pde

...
void draw() {
  background(180);

  if (DummyService.instance != null) {
    DummyService.instance.setSketch(this);
  }
}

void hi() {
  println("hi");
}
...
public class DummyService extends Service {
  ...
  AndroidService sketch;
  ...
  void setSketch(AndroidService sketch) {
    this.sketch = sketch;
  }
  ...
  void workLoop() {
    System.out.println("launching...");
    runnable = new Runnable() {
      @Override
      public void run() {
        System.out.println("working...");
        if (sketch != null) {
          sketch.hi();
        }
        if (running) {
          handler.postDelayed(runnable, WAIT_SECONDS * 1000);
        }        
      }
    };
    handler.postDelayed(runnable, WAIT_SECONDS * 1000);    
  }

#10

hi,
thanx …
i have this Error !
working …
FATAL EXCEPTION: main
Process: processing.test.annexsafe, PID: 9475
android.os.NetworkOnMainThreadException
at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1303)
at java.net.Inet6AddressImpl.lookupHostByName(Inet6AddressImpl.java:86)
at java.net.Inet6AddressImpl.lookupAllHostAddr(Inet6AddressImpl.java:74)
at java.net.InetAddress.getByName(InetAddress.java:708)
at processing.test.annexsafe.AnnexSafe.isConnectingToInternet(AnnexSafe.java:2288)
at processing.test.annexsafe.DummyService$1.run(DummyService.java:91)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6692)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1468)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1358)


#11

This link looks promising related to your last error.

Kf


#12

I still having the problem when the App goes to the background (when i close all the applications) the loop function “void workLoop()” stopped of working (i think there as an error witch stopped the loop function)

Here is my function i used at processing sketch :

public void isConnectingToInternet(){
 
  ConnectivityManager connectivityManager = (ConnectivityManager) act.getSystemService(Context.CONNECTIVITY_SERVICE);
  NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
                                 
  if (activeNetworkInfo != null) { 
    if( activeNetworkInfo.isConnected()) { 
     try{
         address = InetAddress.getByName("www.google.com");                                  
         String hostIP = address.getHostAddress() ;
         if(hostIP.equals(currentIP) == false ) {
                    ConnectStatus = "disConnected" ;
                    currentIP = hostIP ;                    
         }                 
         if (ConnectStatus.equals("disConnected")) {             
             MyConnection();
          } 
      } catch(UnknownHostException e ){ ConnectStatus = "disConnected" ;}
  }  else { ConnectStatus = "disConnected" ; }
 
}else { ConnectStatus = "disConnected" ; }

}

public void MyConnection()
{

client = new MuKCastClient(this, ServerIP, int(ServerPort));


                    try {

                         client.connect();     
                         ConnectStatus = "Connected";
    
                    } catch(IOException e) {
                                       //   e.printStackTrace();   
                                            ConnectStatus = "disConnected";
                                            }
                                
}

here the code i used in  java file : mu pde file called AnnexSafe

import android.app.Activity;
import android.app.Service;
import android.os.Handler;
import android.os.IBinder;
import android.content.Intent;
import android.app.Notification;
import android.app.PendingIntent;
import processing.core.PApplet;
import android.os.StrictMode;


public class DummyService extends Service {
  
   
  public AnnexSafe sketch;
   
  static final String TAG = "DummyService";
  static final int FOREGROUND_NOTIFICATION_ID = 1;

  static Activity activity;
  static DummyService instance;
  
  boolean running = false;
  Handler handler = new Handler();
  Runnable runnable;
  
  int WAIT_SECONDS = 5;


// public DummyService(PApplet sketch) {
  
 // this.sketch = sketch;
//}


  @Override
  public void onCreate() {
    System.out.println("Created service");
    super.onCreate();
    StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
    StrictMode.setThreadPolicy(policy);
    start();
    instance = this;
  }

  @Override
  public void onDestroy() {
    stop();
    super.onDestroy();
  }

  @Override
  public IBinder onBind(Intent intent) {
    // Used only in case of bound services.
    return null;
  }

  void start() {
    goForeground();
    workLoop();
    running = true;
  }
  
  void stop() {
    running = false;
  }
  
  void goForeground() {
    Intent notificationIntent = new Intent(this, activity.getClass());
    PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
    Notification m = new Notification.Builder(this)
            .setContentTitle("Dummy Service")
            .setContentText("Testing foreground services from Processing.")
            .setContentIntent(pendingIntent)
            .build();
    startForeground(FOREGROUND_NOTIFICATION_ID, m);
        
  }
 
  void setSketch(AnnexSafe sketch) {
    this.sketch = sketch;
  }
  
  
  void workLoop() {
    System.out.println("launching ....");
  //  runnable = new Runnable() {
  Thread thread = new Thread(runnable = new Runnable(){  
      @Override
      public void run() {
          System.out.println("working ....");
          if (sketch != null) {
            System.out.println("hi ....");
            sketch.isConnectingToInternet();
        }
        
         
        if (running) {
           handler.postDelayed(runnable, WAIT_SECONDS * 1000);
           }
      }
    });
    thread.start();
handler.postDelayed(runnable, WAIT_SECONDS * 1000);
  }
  
}

#13

thanx … the problem solved …

I respect your helps
thanx
basher