Why does android RemoteView class provide only setOnClickPendingIntent - android

Android's RemoteView class provides the method setOnClickPendingIntent instead of setOnClickListener. What is the reason for this ? What's the advantage of using PendingIntent in this case ?

iirc
A remote view is not running in your applications process, therefore it has to use IPC to tell your app something is clicked. This is asynchronous and so it is a "pending" click, not an instant click. The name reflects a subtle behaviour difference.
#setOnClickPendingIntent(int, android.app.PendingIntent)
vs
#setOnClickListener(android.view.View.OnClickListener)

Related

UI testing without running a test on a real device

Is there some way to test individual View components without running the UI test?
For example, can I check cases like this:
check the change of the View state. For example, when the onClick() method is called, a change in the state of the View (color changes or visibility) is called.
check that if, for example, in the RecyclerView.Adapter I give a list, then it is filled and I can, for example, check some element and check its validity relative to the passed list.
that when a method is called, an animation starts.
I am not interested in rendering in this case, only the fact of executing the methods I need with the parameters I need.
What tools and approaches are used for this? What about Robolectric? I heard not very good reviews about him, can he fulfill my requirements? And are there any alternatives?
In my opinion, you're trying to solve the wrong problem.
What is the point of testing if a view.setColor() is called if you don't care if the rest is ok? What you should be testing (and therefore using mocks if you want/can) is the logic that triggered said "set color". Who/how did you decide that certain action triggered setColor on X views?
If you say: "but that's in the activity/fragment/onresume, how do I test that?". My answer is: don't put it in the activity/fragment/onresume, put it in a proper viewModel or delegate to a component that has your business logic that you can mock and test.
When an onClick is called, an entity (viewmodel, presenter, etc. should receive the click event, make a decision, and change the "state" of the view(s) in response. NOT the click method deciding to change the view's color on its own.
Why not?
Because that logic may be wrong, may contain a bug, is hard to test as it is, etc. Whereas if it's separate in a "delegate that is tested", it's easier to find/fix/test, etc.

How can I inspect methods called with Parcel.transact

I have a problem regarding viewing & following method calls in the android source code when Parcels get involved.
I wanted to find out more about the inner workings of PendingIntents by checking out the Android source code, but just when things get interesting, Parcels pop up a few ambiguous functions are called, and the important bit is over.
I belive specifically the following lines in the send() method of IIntentSender is important:
mRemote.transact(Stub.TRANSACTION_send, _data, _reply, 0);
This is where I get lost. How can I track down the method which is called next? Trying to view the source of transact method just reveals an interface with no code!
The type of mRemote is android.os.IBinder (an interface again)
Thanks for your help in advance!
(P.S: I used grepcode.com to inspect the source code)
The Binder is just the "guts" of an inter-process function call. Providers, Intents, and Messages are actually just abstractions of the Binder protocol. There is a lot of information out there about the Binder protocol, however it is designed so that most people don't have to worry about it.
When you call functions such as getActivity() you are acquiring an intent object that will be used later, but when that time comes, it uses the binder to do its job.
This is all to say, you've gone too far into the "guts," and need to take a step back. The Binder is literally used everywhere and is just a generic way to communicate. Whatever is on the other side of that .transact call is dependent upon the means in which the Binder (or in this case, the PendingIntent) was acquired.
Try to deduce what component would handle an intent of your particular variety, and look for a .transact method in its code. This will usually take the form of a huge switch statement that calls different functions based upon .transact's first argument. Whatever the case block calls will be what is "interesting" to you.
In your case TRANSACTION_send makes me think of finding some activity with the capability of "sending" something. Well, this sounds like a job for the ActivityManager. This code can be found here. Check out the onTransact method for some potential breakpoint positions.

Android - Attach a click listener to a RemoteViews

Suppose I have created an instance of RemoteViews and it contains two Buttons. I want when user clicks these buttons a Service (or a BroadcastReceiver or something else ) handles this click. As I know so far, there is two ways for achieve this purpose :
Assign different Actions to these Buttons for example
ACTION_BTN_1_CLICKED
and
ACTION_BTN_2_CLICKED
and then retrieve the action in the Service via intent.getAction() and finally service does a appropriate task
Put some extra into the Intent object which is enclosed via PendingIntent for example:
intent.putStringExtra("which_button", "btn1"); // for Button 1
and
intent.putStringExtra("which_button", "btn2"); // for Button 2
In the other hand in Service I can distinguish this signal via intent.getExtras().getString("which_button")
Now my question is which approach is better at least in practice? Thanks
I'm not sure you can argue one being better than another, but I think the second solution might be more manageable and extendable. You could put an Integer extra, perhaps IDs that you declare in the R class. This ensures that they are unique and allows you to reference them in switch statement. It also means your routing code is in one place, so if there is a bug there it will be easier to find.

Adding widgets to a launcher page without bindAppWidgetId()

I'm trying to turn the stock ICS launcher into a standalone app. I'm nearly there - the only things not working are the search icon and dropping widgets onto the screen, which causes a crash.
The crash is because the stock launcher uses appWidgetManager.bindAppWidgetId(appWidgetId, componentName); to add widgets, which apparently only system apps have permission to do.
So my question is, what is the correct way for a non-system app to add widgets and acheive the same UI experience as the stock ICS launcher?
Timmmm,
Your issue is that you are looking to the wrong object. You can't really control the AppWidgetManager. Its not your job, its the System's. What you CAN do is control an AppWidgetHost, it just requires a few semantics. Here are the basics.
EDIT: Extra Background on the Widget Binding Process
The AppWidgetManager is a singleton object that runs when the System is started. This means that every instance of every launcher uses the same AppWidgetManager. What differentiates them is their AppWidgetHost and the RemoteViews they are currently holding. The AppWidgetManager basically keeps a list of all of the active hosts and the widgets they are holding. An AppWidgetHost is not a priveleged object. That is, any activity may have a single host. Thus, an entire application may be nothing but Widgets, if they so choose.
When you instantiate the Host, you must then add Views to it. So, basically it is a list of child Views with no mandatory parental bounds, except what your Activity gives it. First, you ask for an ID (via myHost.allocateAppWidgetId()). Then you use your Pick Widget Activity/Dialog. The Dialog returns the WidgetInfo. The View is retrieved when you ask the Host to create the View (via createView) with the WidgetInfo and the ID you asked for. It then asks the widget for its RemoteView.
Finally, you bind the widget by placing the View in your Activity as a Child. This is done via the addView() method of the ViewGroup that holds all of your Widgets.
The Process in Action (EDITED)
First, you have to make sure you have this in your android manifest:
<uses-permission android:name="android.permission.BIND_APPWIDGET" />
Next, you have to create an AppWidgetHost (I extend my own for my launcher). The key to the Host is to keep a reference to the AppWidgetManager via AppWidgetManager.getInstance();.
AppWidgetHost myHost = new AppWidgetHost(context, SOME_NUMERICAL_CONSTANT_AS_AN_ID);
Now, get your ID:
myHost.allocateAppWidgetId()
The next step is done by whatever method you use to get the widget info. Most times it is returned via an Intent through onActivityResult. Now, all you really have to do is use the appInfo and create the view. The WidgetId is normally provided by the pick widget activity result.
AppWidgetProviderInfo withWidgetInfo
= AppWidgetManager.getInstance().getAppWidgetInfo(forWidgetId);
AppWidgetHostView hostView
= myWidgetHost.createView(myContext, forWidgetId, withWidgetInfo);
hostView.setAppWidget(forWidgetId, withWidgetInfo);
Now you just bind the View as a child to whatever you want to bind it to.
myViewGroup.addView(hostView);
Of course, you always have to consider where and how to place it, etc. Also, you have to make sure that your AppWidgetHost is listening before you start adding widgets.
myHost.startListening()
To Summarize
The Widget binding process spans many methods and steps, but all occurs through the AppWidgetHost. Because Widgets are coded outside of your namespace you don't have any control except for where you put them and how you size the View. Since they are ultimately code that runs in your space but outside of your control, the AppWidgetManager acts as a neutral mediator, while the AppWidgetHost serves as the facilitator on your app's behalf. Once this is understood, your task is simple. The steps above are all the required steps for any custom launcher (including my own).
EDIT: Final Clarification
The ICS Launcher does this as well. The appWidgetManager they use is just a wrapper housing the AppWidgetHost and the calls to the AppWidgetManager. I forget that very little of this is explained on the Android Development Central website.
Hope this helps! Let me know if you need anymore details.
FuzzicalLogic
I now know the definitive answer. In Android 4.0, you can't do it. I ended up making my users pick the widget twice, which sucks, but there is no way around it.
In Android 4.1 they fixed the problem!
SDK apps can now host widgets and don't have to use the rubbish widget picker API! You can look into the Jellybean Launcher2 source code for details, but basically, when you first try to bind a widget, Android will pop up a dialog box saying "Do you want to allow this app to bind widgets", and then the user can decide to give it permission or not.
I'm not sure why they went for the modal permission-granting dialog box rather than the all-permissions-on-install model they've used for everything else, but whatever, it works!
Now we just have to wait 4 or 5 years until everyone has Android 4.1 or greater!
I just found this tutorial on how to add appwidgets to normal apps, which might help: http://coderender.blogspot.com/2012/01/hosting-android-widgets-my.html
This tutorial still uses the "AppWidget Picker" list, so it might not work for you since ICS has the widgets picker inside the app drawer itself.
Still, was worth to mention since tutorials on hosting widgets are very rare :)
Cheers,
Yuvi
Fuzzical Logic,with your code below,
AppWidgetProviderInfo withWidgetInfo
= AppWidgetManager.getInstance().getAppWidgetInfo(forWidgetId);
AppWidgetHostView hostView
= myWidgetHost.createView(myContext, forWidgetId, withWidgetInfo);
hostView.setAppWidget(forWidgetId, withWidgetInfo);
if have not the permission of bind_widget,widgethost got nothingļ¼Œcus withwidgetinfo is null,widgethost create nothing.

Android: how to get notified when a widget is tapped

I would like to get a notification when the user taps/clicks on a widget?
Is there something similar to onClick()?
You must use AppWidgetProvider and register listeners there. Since in Widgets all view are created via RemoteViews you must use methods on them - e.g. setOnClickPendingIntent().
Here is a small example: http://developer.android.com/guide/topics/appwidgets/index.html#AppWidgetProvider

Categories

Resources