I have multiple widgets provided by one app, and thus multiple classes that extend AppWidgetProvider. The idea is that every widget contains a Button, which starts a Service when pressed. This Service runs a bit of code and then stops. Now for the problem: for example, I have a Widget1a and a Widget1u. These widgets look like each other, but each widget does a slightly different thing. If I place a Widget1a on the homescreen and press it, a Service is started and the code that this Service should run is run, as it should be. Now I place a Widget1u on the homescreen, and suddenly Widget1a stops working and doesn't do anything when I press it. If I place a second Widget1a on the homescreen both Widget1as work again, but now Widget1u is disabled and doesn't respond when I press it. In short: it seems that at any particular time only one type's code works when it is pressed (and if there are multiple instances of the same type on the homescreen all of these instances will work, but no instances of any other widget of my app will do anything when pressed).
The code in the AppWidgetProvider of each widget is pretty basic. It merely contains an overrided onUpdate-method. It creates an Intent to start a Service that I previously created, this is then used to create a PendingIntent which is used by setOnClickPendingIntent to make sure it can be activated by a Button in the widget. Each widget makes, when its Button is pressed, a slightly different Intent so that a certain Service is executed slightly differently. So this only works for the instances of 1 widgettype at a time, namely those of which an instance was added to the homescreen most recently.
Why do all my app's other widgets stop working when I add a widget of a different type to my homescreen? Does anyone know more about widgets or how I can make sure that each widget keeps doing its job when I add a new one from the same app to the homescreen?
Here is the onUpdate method of one of my AppWidgetProviders for one of my widgets. All widgets operate similarly and I know I used a lot of copypasting which is a bad programming habit but it didn't seem to really want to work otherwise.
Code:
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
// Create an intent to launch the service
Intent serviceIntent = new Intent(context, SendService.class);
//I just put some extra data here to be used by the Service which wil eventually get this Intent and thus the data inside
serviceIntent.setData(Uri.parse("uri::somethingrandomandunique"));
serviceIntent.putExtra("Lamp", "1a");
// PendingIntent is required for the onClickPendingIntent that actually
// starts the service from a button click
PendingIntent pendingServiceIntent =
PendingIntent.getService(context, 0, serviceIntent, Intent.FLAG_ACTIVITY_NEW_TASK);
// Get the layout for the App Widget and attach a click listener to the
// button
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget1a);
views.setOnClickPendingIntent(R.id.button1a, pendingServiceIntent);
// super.onUpdate(context, appWidgetManager, appWidgetIds);
ComponentName componentName = new ComponentName(context.getPackageName(), Widget1a.class.getName());
appWidgetManager.updateAppWidget(componentName, views);
}
So what am I doing wrong?
Related
Hi.
I've been researching for many days. and i am really out of ideas.
First. I am new to all this.
Okay. my project:
I have:
- 1 Activity
- 1 Widget
So. my app is the widget itself. when you click the widget, it opens up the activity, the activity consists of a + and - button and a textview. the textview is on 0. when you click on +, you add up +1 to the textview and vica versa with the -.
So, what i want, in the activitys "onPause()", i want it to save the value and transfer it to the widget, i tried to send a broadcast, but that didnt work properly (i wanted to save the value in a hidden textview, but appwidget cant read values of TextViews -.-').
Also i tried some sql stuff.. didnt work either (appwidget doesnt support?)
Tried so save as a file on sd, didnt work (appwidget doesnt support?)
I couldnt use the broadcast thing because my app is made like this:
appwidget:
public void onUpdate(Context context,AppWidgetManager mgr,int[] appWidgetIds){
SetUp(context, mgr, appWidgetIds);
}
public void SetUp(Context c, AppWidgetManager mgr, int[] appWidgetIds){
//do stuff.....
}
public void onReceive(Context context, Intent intent) {
//If i get the number here, i cant really do anything with it, because i cant really save my value anywhere?
}
Bump. no one can help ?
I tried with the intent.putextra, but wont work.
If anyone wants to help me at msn, pm me. (eventually lightly paid)
Hi
Have you figured how to do it yet? I'm having the exact same problem and I'm unable to find any kind of solution.
Thank you!
So I'm programming a application with a widget.
On my widget I got a button that starts an activity with a PendingIntent. This activity that's getting launched isn't my main activity.
Now when I'm doing it like this I get a Strange problem:
- Launching App as normal over appdrawer -> main activity opens up
- Pressing Home or Back button
- Add widget to homescreen
- Click button on widget -> nothing happens!
If I force stop my application before adding the widget to the homescreen, a click on the button on the widget opens up the activity like it should. Now I can also launch my main activity, pause it and the widget still works. So the widget only fully works if I my application isn't running in the background while adding the widget.
Has anyone of you experienced something like that?
Thank you in advance!
it is really dependent on how your onClickListener is added to the button. ive had problems where the first widget i create doesnt have the onClickListener attached but the second one will. do you mind sharing your code that you use to attach the onClickListener?
here is mine:
Code:
/** Called when the activity is first created. */
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
Toast.makeText(context, "onUpdate()", Toast.LENGTH_SHORT).show();
//super.onUpdate(context, appWidgetManager, appWidgetIds);
//attach an onClick intent to the layout
final Intent onClick = new Intent(context, GITextCloud.class);
onClick.setAction(LAUNCH_GMAIL_GAPPS);
PendingIntent onClickPending = PendingIntent.getBroadcast(context, 0, onClick, 0);
RemoteViews rv1 = new RemoteViews(context.getPackageName(), R.layout.gitc_html);
rv1.setOnClickPendingIntent(R.id.full_widget, onClickPending);
for (int appWidgetId : appWidgetIds) {
appWidgetManager.updateAppWidget(appWidgetId, rv1);
}
}
My code is really almost the same. Just that I need PendingIntent.getActivity instead of getBroadcast.
what flags do you set for your intent? cause if your activity is already running and thats when the button wont work it could be that you need a Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT or some other flags in the PendingIntent
try setting onClick.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) or what ever your Intent is called. i think the fact that your widget and your app are going to be seperate programs means they must start in different threads. cause not all widgets need an Activity running to function
I got it now! I somehow managed to screw up my remoteviews, they didn't got updated properly.
Anyway, thanks a lot for your answers
Hi guys,
On a button click I am inflating a layout like so:
Code:
public void plusLayout(View v) {
// inflating layout here:
LinearLayout ll1 = (LinearLayout) findViewById(R.id.main_layout);
// this layout is being inflated:
View newView = getLayoutInflater().inflate(R.layout.layout_to_be_added, null);
// add layout
ll1.addView(newView);
}
But when the activity restarts, the inflated layouts are gone.
I'd like the layouts to stay there.
(The user can click a button to remove the layout by hand).
I must be missing something trivial here right?
Cheers,
Daan
Which way is it restarted?
If the complete app is restarted, a new layout will be set in the onCreate method.
nikwen said:
Which way is it restarted?
If the complete app is restarted, a new layout will be set in the onCreate method.
Click to expand...
Click to collapse
Yeah when you press back button and start the app again or completely kill it.
It also happens on orientation change as the activity get restarted then as well.
But I think you can override that in the manifest somewhere.
DaanJordaan said:
Yeah when you press back button and start the app again or completely kill it.
It also happens on orientation change as the activity get restarted then as well.
But I think you can override that in the manifest somewhere.
Click to expand...
Click to collapse
Ah ok.
The point is: If you open the app or turn your device, the onCreate method is called. There you set a completely new layout. You would need to save that the layout is inflated (you could use a SharedPreferences entry) and inflate it in the onCreate method. If you just want it to appear again after turning the device, use the onSaveInstanceState method and the onRestoreInstanceState method. That would be better practice.
Look at the activity lifecycle.
Just so I'm sure I get this right :
The user launches the app, the layouts are not inflated
He presses a button which calls your plusLayout() method, so the layouts are now inflated
The user quits the activity and restarts it, the layouts are not inflated anymore but you want them to.
Is that correct ?
If it is, 2 ways I can think of :
Overriding savedInstanceState() & onRestoreInstanceState() :
First, declare a private Boolean before the onCreate() of your activity :
Code:
private Boolean isInflated = false;
Then, set it to true in the onClick() of your button, and override savedInstanceState and onRestoreInstanceState like so :
Code:
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
// Save state changes to the savedInstanceState.
// This bundle will be passed to onCreate if th activity is
// killed and restarted.
savedInstanceState.putBoolean("inflate", isInflated);
}
Code:
@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
// Restore UI state from the savedInstanceState.
// This bundle has also been passed to onCreate.
Boolean myBoolean = savedInstanceState.getBoolean("inflate");
if (myBoolean == true)
plusLayout(myView);
}
Using the sharedPreferences
Same logic, different way to save the boolean :
Before onCreate(), declare a private boolean and a private SharedPreferences :
Code:
private Boolean isInflated = false;
private SharedPreferences prefs = getSharedPreferences("MY_PREFS");
in the onClick of your button :
Code:
isInflated = true;
Editor e = prefs.edit();
e.putBoolean("inflate", isInflated);
e.commit();
Then, in your onCreate(), retrieve the stored value and if it's true, call your plusLayout() method :
Code:
Boolean doInflate = prefs.getBoolean("inflate", false // this is the default value);
if (doInflate == true)
plusLayout(myView);
nikwen said:
Ah ok.
The point is: If you open the app or turn your device, the onCreate method is called. There you set a completely new layout. You would need to save that the layout is inflated (you could use a SharedPreferences entry) and inflate it in the onCreate method. If you just want it to appear again after turning the device, use the onSaveInstanceState method and the onRestoreInstanceState method. That would be better practice.
Look at the activity lifecycle.
Click to expand...
Click to collapse
Okay I'm working on that at the moment.
Whenever a layout is created an (int) "counter" get incremented.
I will save this "counter" in the SharedPreferences.
When the app starts layouts get created "counter" times.
Is this good practice?
It seems so strange that there isn't an easier way to save layout/activity states.
Edit:
Androguide.fr said:
Just so I'm sure I get this right :
The user launches the app, the layouts are not inflated
He presses a button which calls your plusLayout() method, so the layouts are now inflated
The user quits the activity and restarts it, the layouts are not inflated anymore but you want them to.
Is that correct ?
Click to expand...
Click to collapse
That is correct. Big thanks for the examples.
DaanJordaan said:
Okay I'm working on that at the moment.
Whenever a layout is created an (int) "counter" get incremented.
I will save this "counter" in the SharedPreferences.
When the app starts layouts get created "counter" times.
Is this good practice?
It seems so strange that there isn't an easier way to save layout/activity states.
Edit:
That is correct. Big thanks for the examples.
Click to expand...
Click to collapse
I would use his snippets. They are good (as always). Decide which one to use by what I have given above:
Just for turning:
onSaveInstanceState and onRestoreSavedInstanceState
For turning and reopening:
Shared preferences
I am using one edit text view and one OK button to input a large amount of user data during a setup function but can't figure out how to pause the thread execution unit the OK button is pressed. I don't want to have to register and use a ton of different buttons and listeners to call individual functions for each user input and so far I've found out the hard way that a while look will lock the UI thread and running the loop in a separate thread will not make the program wait. Any Ideas?
public class SetupMenuActivity extends Activity
{
private TextView setupPrompt;
boolean okButtonPressed = false;
@override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.setup_menu);
setup();
}
private OnClickListener okButtonListener = new OnClickListener()
{
@override
public void onClick(View v)
{
okButtonPressed = true;
}
};
private void setup()
{
Button okButton = (Button) findViewById(R.id.okButton);
okButton.setOnClickListener(okButtonListener);
setupPrompt = (TextView) findViewById(R.id.setupPrompt);
setupPrompt.setText("Please Enter Your Name");
// Make program wait for ok button clicked
setupPrompt.setText("Please Enter a Name for your Account");
}
}
What else could the user click/etc that you want to prevent from happening? If you want to block another button, then you can either do button.setClickable(false) or even button.setVisibility(View.GONE) until the ok button is clicked. Instead blocking the whole thread doesn't make much sense
The only two things the user can interact with is the button and the edit text box. I want to prevent the changing of the setupPrompt text view until the Ok button is pressed. The easy way to do it would be to put it into the onClickListener but there is a whole series of the prompts and waiting for user input so I'm trying to avoid creating a ton of different button listeners for each piece of user input.
TShipman1981 said:
The only two things the user can interact with is the button and the edit text box. I want to prevent the changing of the setupPrompt text view until the Ok button is pressed. The easy way to do it would be to put it into the onClickListener but there is a whole series of the prompts and waiting for user input so I'm trying to avoid creating a ton of different button listeners for each piece of user input.
Click to expand...
Click to collapse
The way you think this would work is not right, you have to think through it again, sorry . In Android, you can almost never wait for user events (because they might not happen). Instead, you have to do what you can during setup and everything that can only happen after a certain event has to be in the onEvent method (for instance onClick). What you can do to make it less complex is one method which is called only from the onClickListener. The method keeps track of how many times it has been called with an int step instance variable. That method has to execute what should happen at each step.
SimplicityApks said:
The way you think this would work is not right, you have to think through it again, sorry . In Android, you can almost never wait for user events (because they might not happen). Instead, you have to do what you can during setup and everything that can only happen after a certain event has to be in the onEvent method (for instance onClick). What you can do to make it less complex is one method which is called only from the onClickListener. The method keeps track of how many times it has been called with an int step instance variable. That method has to execute what should happen at each step.
Click to expand...
Click to collapse
Yeah Agreed with Simp. I would honestly make one method with all the info you need then get all the info and call it only when the button is clicked. If I knew a bit more of what your trying to accomplish I might be able to help you code it more efficiently.
Hey guys,
I am not getting any response on Stack Overflow so I thought I would try here.
At this point I am suspicious that it is a Samsung device specific problem. A very basic app with just a single button produces the same issue on my S4 development device.
Here is me SO question:
I have a button on one of my fragments, that sits inside a relative layout.
It's a rather large button, and when I fat finger it I get a ACTION_CANCEL motion event rather than ACTION_DOWN (it works perfectly fine with finger tips). This prevents it from registering the subsequent ACTION_UP (I assume the view's parent is taking over). I tried using the requestDisallowInterceptTouchEvent() method on the parent, to no avail.
Here is my onTouch implementation:
Code:
@Override
public boolean onTouch(View view, MotionEvent event) {
//debugging
Log.v("TOUCH EVENT", event.toString());
int action = event.getAction();
if (action == MotionEvent.ACTION_DOWN) {
mButton.getParent().requestDisallowInterceptTouchEvent(true);
//Do stuff...
return true;
} else if (action == MotionEvent.ACTION_UP) {
//Do other stuff...
return true;
} else if (action == MotionEvent.ACTION_CANCEL){
return false;
//Toast.makeText(context, "Your thumb is too fat.", Toast.LENGTH_SHORT).show();
}
return false;
}
Note that the button also uses custom background resources. I start an AsyncTask when the button is pressed and the background changes based on the progress of that task. I'm not sure if that has anything to do with the problem or not.
EDIT: I walked all the way up the View hierarchy to ViewRootImpl, and still no luck in calling requestDisallowInterceptTouchEvent() on it. Weird thing is this shows in the log when my button sticks:
Code:
08-26 11:06:15.287: D/ViewRootImpl(5428): [ViewRootImpl] action cancel - 1, s:31 s(atmel):-1.0 eccen:1.3333334
So obviously it seems that the action is either being cancelled before it even gets inside the ViewRootImpl or right after. How is this even possible?
Update: Still no progress on this... anyone?
masterjeff said:
Hey guys,
I am not getting any response on Stack Overflow so I thought I would try here.
At this point I am suspicious that it is a Samsung device specific problem. A very basic app with just a single button produces the same issue on my S4 development device.
Here is me SO question:
I have a button on one of my fragments, that sits inside a relative layout.
It's a rather large button, and when I fat finger it I get a ACTION_CANCEL motion event rather than ACTION_DOWN (it works perfectly fine with finger tips). This prevents it from registering the subsequent ACTION_UP (I assume the view's parent is taking over). I tried using the requestDisallowInterceptTouchEvent() method on the parent, to no avail.
Here is my onTouch implementation:
Code:
@Override
public boolean onTouch(View view, MotionEvent event) {
//debugging
Log.v("TOUCH EVENT", event.toString());
int action = event.getAction();
if (action == MotionEvent.ACTION_DOWN) {
mButton.getParent().requestDisallowInterceptTouchEvent(true);
//Do stuff...
return true;
} else if (action == MotionEvent.ACTION_UP) {
//Do other stuff...
return true;
} else if (action == MotionEvent.ACTION_CANCEL){
return false;
//Toast.makeText(context, "Your thumb is too fat.", Toast.LENGTH_SHORT).show();
}
return false;
}
Note that the button also uses custom background resources. I start an AsyncTask when the button is pressed and the background changes based on the progress of that task. I'm not sure if that has anything to do with the problem or not.
EDIT: I walked all the way up the View hierarchy to ViewRootImpl, and still no luck in calling requestDisallowInterceptTouchEvent() on it. Weird thing is this shows in the log when my button sticks:
Code:
08-26 11:06:15.287: D/ViewRootImpl(5428): [ViewRootImpl] action cancel - 1, s:31 s(atmel):-1.0 eccen:1.3333334
So obviously it seems that the action is either being cancelled before it even gets inside the ViewRootImpl or right after. How is this even possible?
Update: Still no progress on this... anyone?
Click to expand...
Click to collapse
Mmmh strange problem you've got there... Just an idea, maybe try to always return true in your onTouchEvent() method since you may be losing the event when an ACTION_MOVE event comes up and you return false. Other than that, could you show us your layout file? I doubt the change in background color has any effect on this, but it could be that some part of your layout is causing this.
SimplicityApks said:
Mmmh strange problem you've got there... Just an idea, maybe try to always return true in your onTouchEvent() method since you may be losing the event when an ACTION_MOVE event comes up and you return false. Other than that, could you show us your layout file? I doubt the change in background color has any effect on this, but it could be that some part of your layout is causing this.
Click to expand...
Click to collapse
Does anyone have a solution to this yet? More specifically, I think it's the Samsung's own implementation of ViewRootImpl that is causing this problem. I've been trying to figure out for a long time how to either pre-empt ViewRootImpl to intercept MotionEvents, or completely override ViewRootImpl. I found no success in either of these.
I also thought about reading from /dev/input/eventX directly, but this isn't feasible since it requires the phone to be rooted first. For myself it's ok, but if I'm writing an app for other devices that's not a solution.
Can someone from Samsung help?