Reputation: 53
What is the proper method of calling a function in my application's Activity
from a widget located on the home screen? For my example, I have a background thread that is initialized in my Activity class, and would like to be able to press the widget from the home screen to call specific functions to start/stop the thread without having to enter the application.
Below is a stripped and barebone version of the Activity I am working with:
public class MainActivity extends AppCompatActivity {
private Thread thread;
private Runnable runner;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startThread();
}
private void startThread() {
//start/restart the thread
}
public void stopThread() {
//interupt the thread
}
}
Below is the barebone code for the home screen widget:
static void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId) {
Intent intent = new Intent(context, MainActivity.class);
intent.putExtra("Goal","Stop");
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.speed_widget);
views.setOnClickPendingIntent(R.id.widgetButton, pendingIntent);
appWidgetManager.updateAppWidget(appWidgetId, views);
}
Here is what my current best solution is: From the research I've done so far, it seems like I need to continue to use putExtra()
and then getExtra()
with the Intent that is sent from the widget, and add in a string to say whether to call startThread()
or stopThread()
based on the status of the thread. The current goal is to use it like this:
String intentGoal = getIntent().getExtras().getString("Goal");
if(intentGoal.compareTo("Stop")==0){
stopThread();
}else if(intentGoal.compareTo("Start")==0){
startThread();
}
And it would be most likely ran in an overloaded version of onNewIntent()
. However, the intent sent from the widget results in a call to onCreate()
as well, which I don't want to have happen since it would restart the thread and various other initialization code that I have will be re-ran as well. I tried setting the launchMode to singleTask, but it would still call to onCreate()
rather than only onNewIntent()
. To recap, I would like to be able to press the home screen widget, and have that start or stop the thread, or more generally call a specific function in an existing `MainActivity'.
Upvotes: 3
Views: 2137
Reputation: 53
Between Michael Spitsin's answer and the answer here, I was able to figure out a way to have specific methods execute when home screen widget buttons are clicked. As Michael said, there is a difference between getBroadcast
and getActivity
. For this case, I was able to use getBroadcast
to broadcast an intent, but instead of having the intent be to open the MainActivity
, I used this.class
, where "this" is the class that the function updateAppWidget is in.
What this does is broadcast an intent to itself, and is caught in the onReceive(Context,Intent)
in the same class. Make sure to use addAction
on the Intent you send to give your intent a way to distinguish itself from other Intents that are caught by onReceive
You simply need to overload the method with your own implementation similar to how the other answer I linked to does it. Below is the stripped down bare-bone version of what I got to get the widget working:
public class MyWidget extends AppWidgetProvider {
public static String YOUR_ACTION = "YourAction";
static void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId) {
Intent intent = new Intent(context, SpeedWidget.class);
intent.setAction(YOUR_ACTION);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.speed_widget);
views.setOnClickPendingIntent(R.id.widgetButton, pendingIntent);
appWidgetManager.updateAppWidget(appWidgetId, views);
}
@Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
if (intent.getAction().equals(YOUR_ACTION)) {
//THIS IS WHERE YOUR CODE WILL BE EXECUTED
}
}
}
}
Upvotes: 0
Reputation: 2608
I'm not an expert in Widget-building-process, but I assume you need to change:
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
To:
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
Since you need to provide some interaction with application, you don't need to attach your widget to specific activity. Instead of that you need to use so to say messaging mechanism. In Android it's BroadcastReciever
. For example, you use it to interact with your "start" Service. You can read here about simple example.
Also, according to this answer we came to solution, that you need to register reciever in manifest:
Then in onRecieve
method you can parse your intent and do whatever you want. In your case, I would recomend to transport working with background from Activity
to Service
, since Activity can be closed (okey, Service
too, but it can be restored and run without screen). And in MyWidgetReciever.onReceive
you need to start service with passing "stop" intent.
I'm not sure, but, probably you can catch broadcast in service directly, not in external reciever, but it could be situation when service will be killed, so it will not handle your broadcast.
Upvotes: 1