Logical Flow of all Intents (PendingIntents) in this app widget snippet - java

I am learning how to build home screen widget in Android. This code is for an app widget to toggle RingerMode from NORMAL to SILENT and vice-versa.
This works fine but I need a full in-sight of logical flow (i.e which gets initiated when, goes where, does what, dies when) of all the intents in this.
Please help me understand this topic clearer.
package com.dummies.android.silentmodetoggle;
import android.app.Activity;
import android.app.IntentService;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.media.AudioManager;
import android.util.Log;
import android.widget.RemoteViews;
public class AppWidget extends AppWidgetProvider {
public static String tag ="SilentModeToggleWidget";
#Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
Log.d(tag, "onReceive() first line");
if(intent.getAction()==null)
{
//Do Something
Log.d(tag, "before startService()");
context.startService(new Intent(context, ToggleService.class));
}
else{
super.onReceive(context, intent);
}
}
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
// Do something in specified intervals
context.startService(new Intent(context, ToggleService.class));
}
public static class ToggleService extends IntentService{
public ToggleService(){
super("AppWidget$ToggleService");
Log.d(tag, "In ToggleService construcor");
}
#Override
protected void onHandleIntent(Intent arg0) {
Log.d(tag, "In ToggleService > onHandleIntent");
// TODO Auto-generated method stub
ComponentName cn = new ComponentName(this, AppWidget.class);
AppWidgetManager mgr = AppWidgetManager.getInstance(this);
mgr.updateAppWidget(cn, buildUpdate(this));
}
private RemoteViews buildUpdate(Context context){
RemoteViews updateViews = new RemoteViews(context.getPackageName(), R.layout.widget);
AudioManager audioManager = (AudioManager)context.getSystemService(Activity.AUDIO_SERVICE);
if(audioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT){
updateViews.setImageViewResource(R.id.phoneState, R.drawable.phone_on);
audioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
}
else{
updateViews.setImageViewResource(R.id.phoneState, R.drawable.phone_silent);
audioManager.setRingerMode(AudioManager.RINGER_MODE_SILENT);
}
Intent i = new Intent(this, AppWidget.class);
PendingIntent pi = PendingIntent.getBroadcast(context, 0, i, 0);
updateViews.setOnClickPendingIntent(R.id.phoneState, pi);
return updateViews;
}
}
}

still not sure on what are u asking, but I'll try to answer, if not what u want, please explain in a different way.
Intent are usually started immediatly. You create an Intent can start it by calling startService or startActivity, so for example:
context.startService(new Intent(context, ToggleService.class));
on both times you wrote the above code, the service ToggleService is immediatly started.
PendingIntent on the other hand are saved to be started at later time, so for example
updateViews.setOnClickPendingIntent(R.id.phoneState, pi);
on the above line, the AppWidget Broadcast is started when the user clicks on R.id.phoneState in your HomeScreen AppWidget.
Each PendingIntent is stored in the system with a certain ID, this ID is the requestCode parameter (you used zero on your code)... that means, that if you create a different PendingIntent with the same ID it will override it, meaning a different action will be started when the user clicks on R.id.phoneState

Related

How to change the text of a textView after a button is clicked?

I tried to setup a pendingIntent which evokes the method "shrinkTheNumber()" of the class "shrinkNumber.java" after the button is clicked.
The Problem is that at the point of envoking that method i get the following error:
Cannot resolve constructor 'Intent(android.content.Context, void)
com.example.myapplication.shrinkNumber
public static void shrinkTheNumber(#NonNull Context context)
I can't figure out what the problem is.
The goal of the whole program is that the text of the textView gets changed when the button is clicked.
class shrinkNumber:
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.widget.RemoteViews;
public class shrinkNumber {
public static void shrinkTheNumber(Context context)
{
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
remoteViews.setTextViewText(R.id.textView, "hello");
updatewidgetnow(context, remoteViews);
}
public static void updatewidgetnow (Context context, RemoteViews remoteViews){
ComponentName widgetComponent = new ComponentName(context, WidgetProvider.class);
AppWidgetManager.getInstance(context).updateAppWidget(widgetComponent, remoteViews);
}
}
Here is the part with the pendingIntent:
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
Intent shrinkIntent = new Intent(context, shrinkNumber.shrinkTheNumber(context));
PendingIntent shrinkPendingIntent = PendingIntent.getActivity(context,0,shrinkIntent,0);
remoteViews.setOnClickPendingIntent(R.id.button2, shrinkPendingIntent);
appWidgetManager.updateAppWidget(appWidgetIds, remoteViews);
super.onUpdate(context, appWidgetManager, appWidgetIds);
}
Intent is Android's native way to change from one activity to another. These do not change anything in the target/starting point class. There is no direct way to pass the context to the target activity using Intent. Similarly you can't call a particular method from intent. But there is a workaround for this, you can do something like this :
Intent intent = new Intent(this, shrinkNumber.class);
intent.putExtra("methodName","shrinkTheNumber");
and then in the called activity , you can take the intent and decide which method to call like this:
if(intent.getStringExtra("methodName").equals("shrinkTheNumber")){
shrinkTheNumber();
}
Hope it helps

Daily Notification in Android not working

I wanted my Application to send notification everyday to the user, following this link and doing everything as described. The app doesn't send notifications. The buildLocalNotification function is being triggered every 60 seconds, but no Notification is displayed. I kept an Interval of 2000 ms but android changed it to 60 Seconds.
Logcat Message: Suspiciously short interval 2000 millis; expanding to 60 seconds Every 60 seconds the notification function is triggered.
This is my
NotfBroadcastReceiver.class
import android.app.Notification;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import androidx.core.app.NotificationCompat;
#SuppressWarnings("deprecation")
public class NotfBroadCastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
//Get notification manager to manage/send notifications
//Intent to invoke app when click on notification.
//In this sample, we want to start/launch this sample app when user clicks on notification
Intent intentToRepeat = new Intent(context, SplashScreen.class);
//set flag to restart/relaunch the app
intentToRepeat.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
//Pending intent to handle launch of Activity in intent above
PendingIntent pendingIntent =
PendingIntent.getActivity(context, NotificationHelper.ALARM_TYPE_ELAPSED, intentToRepeat, PendingIntent.FLAG_UPDATE_CURRENT);
//Build notification
Notification repeatedNotification = buildLocalNotification(context, pendingIntent).build();
//Send local notification
NotificationHelper.getNotificationManager(context).notify(NotificationHelper.ALARM_TYPE_ELAPSED, repeatedNotification);
}
public NotificationCompat.Builder buildLocalNotification(Context context, PendingIntent pendingIntent) {
NotificationCompat.Builder builder =
(NotificationCompat.Builder) new NotificationCompat.Builder(context)
.setContentIntent(pendingIntent)
.setSmallIcon(android.R.drawable.arrow_up_float)
.setContentTitle("Workout Reminder")
.setAutoCancel(true);
Log.d("DEBUG", "buildLocalNotification: "+"Notification");
return builder;
}
This is my
AlarmBootReceiver.class
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class AlarmBootReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) {
//only enabling one type of notifications for demo purposes
NotificationHelper.scheduleRepeatingElapsedNotification(context);
}
}
}
This is the code that turn notification on and off:
swnotf.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b)
{
if(b)
{
Log.d("DEBUG", "onCheckedChanged: "+ "ON");
CommonFunctions.setSwitchState(getContext(),"ON");
NotificationHelper.scheduleRepeatingElapsedNotification(getContext());
NotificationHelper.enableBootReceiver(getContext());
}
else
{
Log.d("DEBUG", "onCheckedChanged: "+"OFF");
CommonFunctions.setSwitchState(getContext(),"OFF");
NotificationHelper.cancelAlarmElapsed();
NotificationHelper.disableBootReceiver(getContext());
}
}
});
Please I have already spent THREE days trying other solutions and fixing this. I don't want to spend more time on this. Any help would be Appreciated.
Thank you.

notify() method not been called form when calling it from another class with synchronized()

I'm trying to fire a notification when the battery level is less the %10.
here is my notification class located at MainActivity.java
public void notify(View view) {
String title = "Warning! low battery";
String text = "Please plug your mobile device to a charger";
Intent intent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,0,intent,0);
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_qs_battery_saver)
.setContentTitle(title)
.setContentText(text)
.setContentIntent(pendingIntent)
.build();
notificationManager.notify(notificationID++, notification);
}
I'm trying to fire it from the BatteryReceiver class:
package com.michal.ex2;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.BatteryManager;
import android.util.Log;
import android.view.View;
public class BatteryReceiver extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
String action = intent.getAction();
if(intent.getAction().equals(Intent.ACTION_BATTERY_CHANGED)) {
IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent batteryStatus = context.registerReceiver(null, ifilter);
int level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
int scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, 100);
int percent = (level * 100) / scale;
int plugged = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
boolean isPlugged;
isPlugged = plugged == BatteryManager.BATTERY_PLUGGED_AC || plugged == BatteryManager.BATTERY_PLUGGED_USB;
if ((percent == 10) && !isPlugged) {
Log.d("mylog", "low battery");
//notify();
synchronized(mActivity){
mActivity.notify();
}
}
}
}
First i tried calling calling notify from MainActivity, but the app crash with the error:
java.lang.IllegalMonitorStateException: object not locked by thread before notify()
I've searched for a solution and found that I need to lock the notify, so I tried calling it with synchronized():
synchronized(mActivity){
mActivity.notify();
but nothing happened. The battery receiver works alright(I'm getting the right log message) but notify() is not been called.
Maybe the function you'are calling is not the MainActivity.notify(View view) but the Object.notify(). Therefore nothing happens. Give an appropriate function argument.
mActivity.notify(null);

AlarmReceiver & Notification Method causing crash?

I am currently trying to set up the date picker and time picker to fire out a notification when the time is reached. I have created a method in MainActivity and this is being called from AlarmReceiver. Every time the timer reaches the set amount, the application is crashing and no errors are being shown in logcat.
I know it is something to do with this method being called from AlarmReceiver, i just don't know what the problems is. This method is also currently linked to a button which is working when pressed (buttonStopAlarm) fires a notification when pressed as wanted) so overall the method does work, it's just not working when being called from another class.
Any help would be greatly appreciated! Thank you!
AlarmReceiver
package servicealarmdemo.test2;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
public class AlarmReceiver extends BroadcastReceiver {
MainActivity main = new MainActivity();
#Override
public void onReceive(Context arg0, Intent arg1) {
Toast.makeText(arg0, "Alarm received!", Toast.LENGTH_LONG).show();
main.addNotification();
}
}
By doing this MainActivity main = new MainActivity(); you are just creating an instance of MainActivity but it will not have it's context mean this which is basically provided when Activity is started by the OS
so move you Notification code in your Receiver and use arg0 as context
public class AlarmReceiver extends BroadcastReceiver {
//MainActivity main = new MainActivity();
Context cxt;
#Override
public void onReceive(Context arg0, Intent arg1) {
Toast.makeText(arg0, "Alarm received!", Toast.LENGTH_LONG).show();
cxt = arg0;
//main.addNotification();
addNotification();
}
public void addNotification() {
NotificationCompat.Builder builder =
new NotificationCompat.Builder(cxt)
.setSmallIcon(R.drawable.icon_transperent)
.setContentTitle("Achieve Alert!")
.setContentText("This is a reminder for your deadline!");
Intent notificationIntent = new Intent(cxt, MainActivity.class);
PendingIntent contentIntent = PendingIntent.getActivity(cxt, 0, notificationIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
builder.setContentIntent(contentIntent);
// Add as notification
NotificationManager manager = (NotificationManager)cxt.getSystemService(Context.NOTIFICATION_SERVICE);
builder.setVibrate(new long[] { 0, 1000, 1000, 1000, 1000 });
manager.notify(0, builder.build());
}
}

Android App Widget broadcast doesn't work

I have a widget with 1 image view and 1 text view.
I want to change image on click. Consider following code doesn't catch touch event
package com.sigrlami.rixvpn.widget;
import java.util.Random;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.widget.RemoteViews;
import com.sigrlami.rixvpn.R;
public class VpnWidgetProvider extends AppWidgetProvider {
private boolean currentStatus = false;
// log tag
private static final String LOG = "com.sigrlami.rixvpn.widget.VpnWidgetProvider";
//public static final String CLICK = "com.sigrlami.rixvpn.CLICK";
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
// Get all ids
ComponentName thisWidget = new ComponentName(context, VpnWidgetProvider.class);
int[] allWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget);
for (int widgetId : allWidgetIds) {
// Create some random data
int number = (new Random().nextInt(100));
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.vpn_widget);
Log.w("WidgetExample", String.valueOf(number));
// Set the text
remoteViews.setTextViewText(R.id.layout_vpn_widget_tv_Check, String.valueOf(number));
// Register an onClickListener
Intent intent = new Intent(context, VpnWidgetProvider.class);
intent.setAction(appWidgetManager.ACTION_APPWIDGET_UPDATE);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
remoteViews.setOnClickPendingIntent(R.id.layout_vpn_widget_iv_Check, pendingIntent);
if (currentStatus = false) {
remoteViews.setImageViewResource(R.id.layout_vpn_widget_iv_Check, R.drawable.on);
} else {
remoteViews.setImageViewResource(R.id.layout_vpn_widget_iv_Check, R.drawable.off);
}
appWidgetManager.updateAppWidget(widgetId, remoteViews);
}
}
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {
if (currentStatus == false) {
currentStatus = true;
} else {
currentStatus = false;
}
}
super.onReceive(context, intent);
}
}
Looking to this part through debugger shows nothing strange, but when I change to getActivity() it shows that I'm trying to start a activity that does not exist. I see that something is going on, but this is useless to me.
// Register an onClickListener
Intent intent = new Intent(context, VpnWidgetProvider.class);
intent.setAction(appWidgetManager.ACTION_APPWIDGET_UPDATE);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
remoteViews.setOnClickPendingIntent(R.id.layout_vpn_widget_iv_Check, pendingIntent);
But it works 1 time when app deployed to device.
Any suggestions what I have done wrong?
For handling click in AppWidgetProvider you need to use custom broadcast reciver as:
<receiver android:name="VpnWidgetProvider"
android:label="#string/widget_title" android:exported="false" android:icon="#drawable/volume">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<action android:name="com.xxxx.xxxx.VpnWidgetProvider.ACTION_WIDGET_REFRESH"/>
<action android:name="android.appwidget.action.APPWIDGET_DELETED"/>
<action android:name="android.media.RINGER_MODE_CHANGED"/>
</intent-filter>
<meta-data android:name="android.appwidget.provider"
android:resource="#xml/widget_info" />
and in AppWidgetProvider class:
public class VpnWidgetProvider extends AppWidgetProvider {
public static String ACTION_WIDGET_REFRESH = "ActionReceiverRefresh";
//your code here...
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
//you code here...
Intent intent = new Intent(context, VpnWidgetProvider.class);
intent.setAction(appWidgetManager.ACTION_WIDGET_REFRESH);//get action here
//you code here...
see this full working example for handling onclick on home Screen AppWidget:
Silenttoggle Home Screen Widget

Categories