I need to implement functionality to display application news/announcements for all users.
I will do it rarely so push notification is not for me.
Are there any library or sample, how to implement this fast and easy? Or better will be to implement it in next way:
When application starts - read html file on server, if file was updated after the last attempt - display content of file in popup.
All you need is to load an html, or better, a simple text file containing the notification index like:
in yourdomain.com/notificationnumber.txt
contains
"4"
Check in your applications, probably simply using SharedPreferences for the last displayed version. Then load all notification, for example:
yourdomain.com/notification3.html
yourdomain.com/notification4.html
Display them and store the index number to know which notifcations have been shown to the user. There are no special libraries needed for such a task.
If the announcements are not very frequent, then checking the html file on the server each time the application is launched is good enough.
I feel like it depends on the styling options, which your notifications require:
if no rich styling is needed, you could simply check an XML-file, Text-file or similar for some notification information (Texts, notification coded) as bjornson proposed and then just show an AlertDialog with the specific information.
If you require rich styling, you may need to load the content from a server by utilizing a WebView. So for example your app's frontpage could partly consist of a WebView that in specific cases shows the news, which you intend to present to the user. Please note, that the WebView-technique is/was used by Facebook to update certain parts of their apps without the troubles of releasing a new version in the various App Stores.
I have used an rss-feed from a wordpress blog for this, if there is a new entry, a webview is opened and the new entry is displayed. Instead of displaying the entry, one could just parse the data and display ist (using jdom or other libraries to read the html document) We use IntentService to check once every hour, but you could just do it once on strartup.
So the Service is build like this:
public class UpdateService extends IntentService
{
//INtent Services have a constructer where you have to pass a String to the super class.
public UpdateService()
{
super("UpdateService");
}
#Override
public IBinder onBind(Intent intent)
{
return null;
}
#Override
public void onCreate()
{
super.onCreate();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
super.onStartCommand(intent, flags, startId);
return Service.START_NOT_STICKY;
}
#Override
public void onDestroy()
{
super.onDestroy();
}
//This method downloads the file from a url and checks if its well formed. It has its own thread because android does not allow access to the internet in the same thread
private void checkfornews()
{
final SAXBuilder builder = new SAXBuilder();
new Thread(new Runnable()
{
public void run()
{
// command line should offer URIs or file names
try
{
Document doc = builder.build(url);
Element root = doc.getRootElement();
readnews(root);
// If there are no well-formedness errors,
// then no exception is thrown
}
// indicates a well-formedness error
catch (JDOMException e)
{
}
catch (IOException e)
{
}
}
}).start();
}
}
//checking for the lateste link, if its a new one we have a new link to show
private void readnews(Element root)
{
List<Element> children = root.getChildren();
Iterator<Element> childrenIterator = children.iterator();
while(childrenIterator.hasNext())
{
Element child = childrenIterator.next();
List<Element> grandchildren = child.getChildren();
Iterator<Element> grandchildrenIterator = grandchildren.iterator();
int counter=0;
while(grandchildrenIterator.hasNext())
{
Element grandchild = grandchildrenIterator.next();
String name = grandchild.getName();
if(name.equals("item") && counter ==0)
{
String title="";
String link="";
String category="";
List<Element> ggc = grandchild.getChildren();
Iterator<Element> ggci= ggc.iterator();
while(ggci.hasNext())
{
Element ggch = ggci.next();
if((ggch.getName()).equals("title"))
{
title=ggch.getText();
}
if(ggch.getName().equals("link"))
{
link=ggch.getText();
}
if(ggch.getName().equals("category"))
{
category=ggch.getText();
}
}
if(category.equals("SoundOfTheCity"))
{
counter++;
Logging.i(TAG, "found some news");
String latestnews = prefs.getString("SOTCNews", "");
if(!(latestnews.equals(link)))
{
Logging.i(TAG, "and its new");
Editor edit = prefs.edit();
edit.putString("SOTCNews", link);
edit.commit();
showNotification(title, link);
}
}
}
}
}
}
private void showNotification(String title, String link)
{
//do what needs to be done with the new data
}
#Override
protected void onHandleIntent(Intent intent)
{
checkfornews();
scheduleNext();
}
private void scheduleNext()
{
int checkingDelay = UPDATE_DELAY;
Calendar cal = Calendar.getInstance();
cal.add(Calendar.HOUR, checkingDelay);
Intent intent = new Intent(this, UpdateService.class);
PendingIntent pendingIntent = PendingIntent.getService(this, UPDATEALARM, intent, 0);
AlarmManager am = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
am.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), pendingIntent);
}
}
depending on the form of the html file you have to redo the readnews method and then the showNotification method.
If you don't want to have a backend like wordpress (or use a blog-like feature) you could just store the content of a static webpage instead of the url and if this content changes, use the showNotification method. This would probably do the trick and is a lot less work.
Related
I am building an SDK.
The Scenario
From the app, the developer passes the JsonObject and URL in a method
and inside the SDK.
I add those values in SQLite database and start a JobScheduler.
The Jobscheduler takes the request at 0 indexes out of the database
executes it.
When I get the response, I delete that request from the database and now the request at 1 index comes to the 0 index and again I execute the same code where 0th index request is fired.
The Problem
When I get the response from a server inside the SDK, I need to send it to the developer using a callback.
I can take the callback as an argument when I take the JSON and URL from user, but I don't know how to proceed further because I cannot store it into the database
Suppose I have 5 requests in the database and scheduler executes it one by one, I don't know how to send the response back to the developer. I can not pass context in the jobscheduler. The only way to do that is I get the corresponding context for each row (request) in the database.
What I tried
I tried using a LocalBroadcastManager, but I can not create a generic class and get it's onreceive() method implemented, also the context passing was a problem
Tried using Realm as a database so that I can add Context type and use my model, but it is not working as Context is not supported.
Storing Activity name in the database with other details and then driving out the class from it and activity from class. And then typecasting the activity into my callback. but it throws ClassCastException as the class it tries to derive from the Name is on the developer's app and not in my SDK
..
The App's MainActivity Code
sdkClass.queueRequest(jo.toString(),"url1",12,"MainActivity");
sdkClass.queueRequest(jo2.toString(),"url2",13,"MainActivity");
sdkClass.queueRequest(jo.toString(),"url3",14,"MainActivity");
sdkClass.executeQueue();
The SDK class which adds and executes code
public void queueRequest(String payload, String URL, int requestId, String identifier){
RequestsDatabase database = new RequestsDatabase(context);
database.insertItem(TxnType,payload,url, String.valueOf(requestId), identifier);
}
public JobScheduler getQueueInstance(Context context){
if(jobScheduler==null){
jobScheduler = (JobScheduler)context.getSystemService(JOB_SCHEDULER_SERVICE);
}
return jobScheduler;
}
public void executeQueue(){
getQueueInstance(context);
ComponentName jobService = new ComponentName(context.getPackageName(), MyJobService.class.getName());
JobInfo jobInfo = new JobInfo.Builder(1,jobService).setPersisted(true).setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY). build();
jobScheduler.schedule(jobInfo);
}
MyJobServiceCode
#Override
public boolean onStartJob(JobParameters params) {
// UtilityMethods.showToast(this,params.getExtras().getString("json"));
Toast.makeText(this,"test", Toast.LENGTH_SHORT).show();
database = RequestsDatabase.getInstance(this);
ALlRequests = database.getAllItemId();
executeCall();
return false;
}
public void executeCall(){
ALlRequests = database.getAllItemId();
if(ALlRequests.size()>0) {
requestModel = ALlRequests.get(0);
try {
String payload = getPayload(this, requestModel.getTxnType(), requestModel.getPayload());
SilverLineRequest req = new SilverLineRequest(this, requestModel.getUrl(), payload, Integer.parseInt(requestModel.getRequestId()), this);
req.executeToServer();
Toast.makeText(getApplicationContext(), "requested", Toast.LENGTH_LONG).show();
} catch (JSONException e) {
e.printStackTrace();
}
}
else{
Toast.makeText(this, "Queue empty", Toast.LENGTH_LONG).show();
}
}
#Override
public void onSuccess(String response, int requestId) {
if(requestId ==Integer.parseInt(requestModel.getRequestId())){
Toast.makeText(this,"responded", Toast.LENGTH_SHORT).show();
database.deleteItem(requestModel.getTxnType());
// HERE I WANT TO SEND THE USER RESPONSE LIKE
// listener.onTransactionSuccess(response)
// BUT I DON'T HAVE 'listener' SO IT SHOWS NULLPOINTER
SDKClass sl = new SDKClass(this);
sl.executeQueue();
}
}
Any help will be blessed right now.
You store requests in database, so it can has unique id (autoincrement).
Then you can store callbacks in memory with relation request id -> callback.
On response you can call it.
If you want, return id to developers. Then they can bind and unbind and get results when they need, even the response had got yesterday.
Take a look on TransferUtility from Amazon AWS SDK for example.
I have a project where a user can have multiple logins across multiple devices.
Now the user can subscribe to a particular topic on any device and the need is that the rest of the device logins should also do the same. Similar case is when one device unsubscribes, the rest should also follow suite.
In order to do this, I have made a Node under each user where all the subscriptions are maintained in the firebase database. I have a START_STICKY service which attaches a Firebase listener to this location and subs/unsubs from the topics when the changes occur. The code for the service is attached under the description.
In regular usage from observation, the service that i have does re-spawn due to the start sticky in case the system kills it. It will also explicitly respawn in case the user tampers with it using the developer options. The only cases which will cause it to completely cease are :
signout
data cleared
force stop
My questions are
how badly will keeping the listener attached affect the battery life. AFAIK Firebase has an exponential backoff when the web socket disconnects to prevent constant battery drain
Can the firebase listener just give up reconnecting if the connection is off for quite some time? If so, when is the backoff limit reached.
Is there a better way to ensure that a topic is subscribed and unsubscribed across multiple devices?
Is the service a good way to do this? can the following service be optimised? And yes it does need to run constantly.
Code
public class SubscriptionListenerService extends Service {
DatabaseReference userNodeSubscriptionRef;
ChildEventListener subscribedTopicsListener;
SharedPreferences sessionPref,subscribedTopicsPreference;
SharedPreferences.Editor subscribedtopicsprefeditor;
String userid;
boolean stoppedInternally = false;
SharedPreferences.OnSharedPreferenceChangeListener sessionPrefChangeListener;
#Nullable
#Override
public IBinder onBind(Intent intent) {
//do not need a binder over here
return null;
}
#Override
public void onCreate(){
super.onCreate();
Log.d("FragmentCreate","onCreate called inside service");
sessionPref = getSharedPreferences("SessionPref",0);
subscribedTopicsPreference=getSharedPreferences("subscribedTopicsPreference",0);
subscribedtopicsprefeditor=subscribedTopicsPreference.edit();
userid = sessionPref.getString("userid",null);
sessionPrefChangeListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
#Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
Log.d("FragmentCreate","The shared preference changed "+key);
stoppedInternally=true;
sessionPref.unregisterOnSharedPreferenceChangeListener(this);
if(userNodeSubscriptionRef!=null && subscribedTopicsListener!=null){
userNodeSubscriptionRef.removeEventListener(subscribedTopicsListener);
}
stopSelf();
}
};
sessionPref.registerOnSharedPreferenceChangeListener(sessionPrefChangeListener);
subscribedTopicsListener = new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
if(!(dataSnapshot.getValue() instanceof Boolean)){
Log.d("FragmentCreate","Please test subscriptions with a boolean value");
}else {
if ((Boolean) dataSnapshot.getValue()) {
//here we subscribe to the topic as the topic has a true value
Log.d("FragmentCreate", "Subscribing to topic " + dataSnapshot.getKey());
subscribedtopicsprefeditor.putBoolean(dataSnapshot.getKey(), true);
FirebaseMessaging.getInstance().subscribeToTopic(dataSnapshot.getKey());
} else {
//here we unsubscribed from the topic as the topic has a false value
Log.d("FragmentCreate", "Unsubscribing from topic " + dataSnapshot.getKey());
subscribedtopicsprefeditor.remove(dataSnapshot.getKey());
FirebaseMessaging.getInstance().unsubscribeFromTopic(dataSnapshot.getKey());
}
subscribedtopicsprefeditor.commit();
}
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
//either an unsubscription will trigger this, or a re-subscription after an unsubscription
if(!(dataSnapshot.getValue() instanceof Boolean)){
Log.d("FragmentCreate","Please test subscriptions with a boolean value");
}else{
if((Boolean)dataSnapshot.getValue()){
Log.d("FragmentCreate","Subscribing to topic "+dataSnapshot.getKey());
subscribedtopicsprefeditor.putBoolean(dataSnapshot.getKey(),true);
FirebaseMessaging.getInstance().subscribeToTopic(dataSnapshot.getKey());
}else{
Log.d("FragmentCreate","Unsubscribing from topic "+dataSnapshot.getKey());
subscribedtopicsprefeditor.remove(dataSnapshot.getKey());
FirebaseMessaging.getInstance().unsubscribeFromTopic(dataSnapshot.getKey());
}
subscribedtopicsprefeditor.commit();
}
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
//Log.d("FragmentCreate","Unubscribing from topic "+dataSnapshot.getKey());
//FirebaseMessaging.getInstance().unsubscribeFromTopic(dataSnapshot.getKey());
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
//do nothing, this won't happen --- rather this isnt important
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.d("FragmentCreate","Failed to listen to subscriptions node");
}
};
if(userid!=null){
Log.d("FragmentCreate","Found user id in service "+userid);
userNodeSubscriptionRef = FirebaseDatabase.getInstance().getReference().child("Users").child(userid).child("subscriptions");
userNodeSubscriptionRef.addChildEventListener(subscribedTopicsListener);
userNodeSubscriptionRef.keepSynced(true);
}else{
Log.d("FragmentCreate","Couldn't find user id");
stoppedInternally=true;
stopSelf();
}
}
#Override
public int onStartCommand(Intent intent,int flags,int startId){
//don't need anything done over here
//The intent can have the following extras
//If the intent was started by the alarm manager ..... it will contain android.intent.extra.ALARM_COUNT
//If the intent was sent by the broadcast receiver listening for boot/update ... it will contain wakelockid
//If it was started from within the app .... it will contain no extras in the intent
//The following will not throw an exception if the intent does not have an wakelockid in extra
//As per android doc... the following method releases the wakelock if any specified inside the extra and returns true
//If no wakelockid is specified, it will return false;
if(intent!=null){
if(BootEventReceiver.completeWakefulIntent(intent)){
Log.d("FragmentCreate","Wakelock released");
}else{
Log.d("FragmentCreate","Wakelock not acquired in the first place");
}
}else{
Log.d("FragmentCreate","Intent started by regular app usage");
}
return START_STICKY;
}
#Override
public void onDestroy(){
if(userNodeSubscriptionRef!=null){
userNodeSubscriptionRef.keepSynced(false);
}
userNodeSubscriptionRef = null;
subscribedTopicsListener = null;
sessionPref = null;
subscribedTopicsPreference = null;
subscribedtopicsprefeditor = null;
userid = null;
sessionPrefChangeListener = null;
if(stoppedInternally){
Log.d("FragmentCreate","Service getting stopped due to no userid or due to logout or data clearance...do not restart auto.. it will launch when user logs in or signs up");
}else{
Log.d("FragmentCreate","Service getting killed by user explicitly from running services or by force stop ... attempt restart");
//well basically restart the service using an alarm manager ... restart after one minute
AlarmManager alarmManager = (AlarmManager) this.getSystemService(ALARM_SERVICE);
Intent restartServiceIntent = new Intent(this,SubscriptionListenerService.class);
restartServiceIntent.setPackage(this.getPackageName());
//context , uniqueid to identify the intent , actual intent , type of pending intent
PendingIntent pendingIntentToBeFired = PendingIntent.getService(this,1,restartServiceIntent,PendingIntent.FLAG_ONE_SHOT);
if(Build.VERSION.SDK_INT>=23){
alarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime()+600000,pendingIntentToBeFired);
}else{
alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime()+600000,pendingIntentToBeFired);
}
}
super.onDestroy();
}
}
A service is not really necessary for what you're trying to do. There's no advantage to having a service, except that it may keep your app's process alive longer than it would without the service started. If you don't need to actually take advantage of the special properties of a Service, there's no point in using one (or you haven't really made a compelling case why it does need to be started all the time). Just register the listener when the app process starts, and let it go until the app process is killed for whatever reason. I highly doubt that your users will be upset about not having subscription updates if the app just isn't running (they certainly aren't using it!).
The power drain on an open socket that does no I/O is minimal. Also, an open socket will not necessarily keep the device's cell radio on at full power, either. So if the listen location isn't generating new values, then your listener is never invoked, and there is no network I/O. If the value being listened to is changing a lot, you might want reconsider just how necessary it is to keep the user's device busy with those updates.
The listener itself isn't "polling" or "retrying". The entire Firebase socket connection is doing this. The listener has no clue what's going on behind the scenes. It's either receiving updates, or not. It doesn't know or care about the state of the underlying websocket. The fact that a location is of interest to the client is actually managed on the server - that is what's ultimately responsible for noticing a change and propagating that to listening clients.
I'm creating a notification management app and I want to get the contents of notifications which other apps show. currently I use codes like this :
statusBarNotification.getNotification().extras.getString(Notification.EXTRA_TITLE);
and this :
statusBarNotification.getNotification().extras.getString(Notification.EXTRA_TEXT);
to read the title and text of notifications. but after a few hours I couldn't find a way to get the image which comes along with the notification's text. for example a profile picture which is showed in Whatsapp's notification. I know it's not the Small or Large icons, I checked a few times.
So if anyone could help in any way, it would be much appreciated
I assume you use NotificationListenerService to listen to notification from other app.
In you NotificationService class, extract icon ressource id in extra Notification.EXTRA_SMALL_ICON and access the other app package ressources to get the Drawable.
Notification.EXTRA_PICTURE contains the large image sent in the notification :
public class NotificationService extends NotificationListenerService {
Context context;
#Override
public void onCreate() {
super.onCreate();
context = getApplicationContext();
}
#Override
public void onNotificationPosted(StatusBarNotification statusBarNotification) {
// a notification is posted
String pack = statusBarNotification.getPackageName();
Bundle extras = statusBarNotification.getNotification().extras;
int iconId = extras.getInt(Notification.EXTRA_SMALL_ICON);
try {
PackageManager manager = getPackageManager();
Resources resources = manager.getResourcesForApplication(pack);
Drawable icon = resources.getDrawable(iconId);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
if (extras.containsKey(Notification.EXTRA_PICTURE)) {
// this bitmap contain the picture attachment
Bitmap bmp = (Bitmap) extras.get(Notification.EXTRA_PICTURE);
}
}
#Override
public void onNotificationRemoved(StatusBarNotification statusBarNotification) {
//call when notification is removed
}
}
I got stuck in to the problem where I need to show my first application in to some area of second application's screen. Both codes are under my control. Can any one suggest me where should I proceed as I am not getting any clue about the situation.
if some one help me for the issue, it would be a great help for me.
Or
If I can open both of my applications using the multiscreen option available in S3.
Write a service on either of your application or a individual application. Have AIDL(Android Interface Definition Language) defined as IRemoteService.aidl, the below is my pseudo code or sample implementation. Using this approach you can start activity and handle events of another application through your application.
// IRemoteService.aidl
// Declare any non-default types here with import statements
/** Example service interface */
interface IAccountService {
String getLoggedInUserInfo(String appId);
void userLogin(String appId,ILoginCallback cb);
void signout(String appId);
}
interface ILoginCallback {
void loginSuccess(String userId);
void loginFailed();
}
In your service have some RemoteCallbacks
#Override
public IBinder onBind(Intent intent) {
final RemoteCallbackList<ILoginCallback> mCallbacks = new RemoteCallbackList<ILoginCallback>();
if(mCallbacks!=null){
int i = mCallbacks.beginBroadcast();
while(i>0){
i--;
try {
Log.e(TAG, "Callback ...");
mCallbacks.getBroadcastItem(i).loginSuccess(newUserId);
} catch (RemoteException e) {
// The RemoteCallbackList will take care of removing
// the dead object for us.
}
}
mCallbacks.finishBroadcast();
}
}
private final IAccountService.Stub mBinder = new IAccountService.Stub() {
#Override
public void userLogin(String appId,ILoginCallback cb) throws RemoteException {
String userId = Settings.getSettings().getUserId();
if(userId ==null||userId.length()==0){
mCallbacks.register(cb);
Intent intent = new Intent(getApplicationContext(), AccountLoginActivity.class);
intent.putExtra("deviceId", Settings.getSettings().getDeviceUniqueId());
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
}
}
You can find detailed AIDL examples in the below links.
http://owenhuangtw.pixnet.net/blog/post/23760257-android-aidl-(android-interface-definition-language)
http://www.app-solut.com/blog/2011/04/using-the-android-interface-definition-language-aidl-to-make-a-remote-procedure-call-rpc-in-android/
https://github.com/afollestad/aidl-example
I would like to know the purpose of foloowing two files:
frameworks/base/core/java/android/app/IActivityWatcher.aidl
[description: Callback interface to watch the user's traversal through activities.]
frameworks/base/core/java/android/app/IProcessObserver.aidl
[no description]
I am trying to build an app wherein user can decide which apps can be run during particular period of time (say, from 10am till 4pm).
Is there any way where my app will get notified if one the apps specified by the user starts? This way my app can send kill command (I am assuming that root access is available.)
It seems that IActivityWatcher has been removed beginning with JellyBean, in order to monitor which Activity is running foreground, you can use IProcessObserver as following:
mActivityManagerNative = ActivityManagerNative.getDefault();
if (mActivityManagerNative != null) {
try {
mActivityManagerNative.registerProcessObserver(mProcessObserver);
} catch (RemoteException e) {
Log.e("TAG", "onCreate() RemoteException!");
}
}
private IProcessObserver.Stub mProcessObserver = new IProcessObserver.Stub() {
#Override
public void onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities) {
doWhatUWantHere();
}
#Override
public void onImportanceChanged(int pid, int uid, int importance) {
}
#Override
public void onProcessDied(int pid, int uid) {
}
};
P.S.
You can use following code snippets to get the package name of foreground running Activity:
private String getForegroundPackage() {
ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
List<RecentTaskInfo> taskInfo = am.getRecentTasks(1,
ActivityManager.RECENT_IGNORE_UNAVAILABLE);
return taskInfo.isEmpty()
? null : taskInfo.get(0).baseIntent.getComponent().getPackageName();
}