App with multiple .action.MAIN - android

I have an app ready and working well, and now I want to add a widget to it. The thing is, Im not sure, if I am doing this thing right, because:
my widget would have 4 buttons, each starting a different activity from the original app
Im aware of the additional neccessary initializations, this thing would cause, since 3 buttons would start 3 activities, that would skip the main activity
I did add the .action.MAIN tag to my manifest, to the corresponding activities, thus I can access them
Is this the correct way to do this? Or is there any other, recommended way? The tag that I added to the manifest:
<action android:name="android.intent.action.MAIN" />
EDIT: forgot to mention in the original post, that the widget is a completely separate app, you would have to download it separetely from the market.

I did add the .action.MAIN tag to my manifest, to the corresponding activities, thus I can access them
That's not a good idea, as nothing can then distinguish between them (such as your buttons in your app widget). Either use unique actions (good) or hard-wire in class names to the activities in your app widget code (not so good).

Related

Can there be two instances of the same activity within an app?

I noticed that one of my colleagues is declaring all the member variables of an activity as static. His point is that there will never be two instances of the activity, and static member variables are more efficient than non-static. Of course, when the activity is created, he will initialize all the members variables as one would do.
I feel this is wrong. I don't think the gain is even justifiable. But I wanted to run this by you guys. Whether or not it is a good programming practice is probably beyond the scope of stackoverflow's mandate. So my specific question is if using static member variables for an activity ever break anything (besides coder's error of not initializing them properly in onCreate).
Thank you in advance for your help.
Yes, android can create 2 instances of the same activity, so your friend is wrong.
To have only one instance, you must declare the following in manifest
<activity android:name=".YourActivity"
android:launchMode="singleTask"
android:label="#string/app_name" />
Yes, you can have multiple instances on the same activity and you often want to.
For example, if I have 3 views that simply compose of a ListView. You can create 3 activities that had the same code duplicated and small changes, or one activity that handles the loading of the layout and getting a reference to the ListView and extend it to add functionality.
You can limit an activity to one instance by declaring it that way in the manifest file.

android acitivity without registering in androidmanifest.xml

I came across a problem in a job interview that whether I can dynamically add a new activity to an Android application without releasing a new version of the app. And he told me that there exists certain mechanism that we could dynamically change the Activity to a new one, without registering in the AndroidManifest.xml file. I searched some documents, but did not found possible way to do this.
Can I start an Activity without registering in the AndroidManifest.xml file? And is it possible to dynamically modify the existing Activity?
Can I start an Activity without registering in the AndroidManifest.xml file?
No. I don't think you can start an activity that is not registered in the AndroidManifest.xml file. The manifest file keeps track of the activities that the app can use upon compiling/building the application. Any attempt to open an unregistered activity will result in an application crash.
And is it possible to dynamically modify the existing Activity?
As Bette Devine said, you can change the layout of the existing activity by calling setContentView(R.layout.new_layout); based on some user action (like a button press). However, calling setContentView more than once in your activity is a bad practice that people generally avoid doing. It is not recommended since you'll have to write code that would manage user interaction for the second layout. Imagine writing two activity codes in one java file. That would result in unnecessary clutter of code when you can just write them separately.
Yes it is possible to dynamically modify an existing one.
Here modification does not mean that you are changing the name of an activity but means that you are changing the content.
Just call the setContentView method of activity to give a new layout to the activiy and you whole activity now will be hosting a different content.
setContentView(R.layout.new_layout);

Dynamic activity registration

I've got this question regarding Android activities and AndroidManifest.xml. The question arised when I was working with Java servlets, and wanted to create a "module-based" or something similar to plugin based server. This is working fine, and I can dynamically load the servlets I want, using a code approach similar to this: Dynamically add a servlet to the servletConfig. The servlets are dynamically found and mapped during the server startup.
The question here is not how to find the proper activities, or how to create a new instance of them, or how to start them, that part I have figured out. I can iterate over my packages and find the appropriate Activity-classes, create new instances of them and add them to a list.
I've been using a interface which all the Activities must implement in order to be a valid activity. That way, I can create new parts, extentions or new features for my application, and everything just works. In my application I have a list of button that users can click on, and the button list is generated by adding them to my view using a ListView.
private void displayLoadedContent() {
View v = inflateLayout(R.layout.buttonlayout);
ListView view = (ListView) v.findViewById(R.id.list);
view.setAdapter(new ListButtonAdapter(this, content));
}
where content is declared as
List<MyActivityInterface> content;
and ListButtonAdapter extends BaseAdapter.
So no problem there. The problem is that I have to declare each and every single one of my activites in AndroidManifest.xml. That file is like a big list of possible acitivies to display, and kind of messy, so I'm not going to display the code here.
I realize that I'll have to add one activity to the manifest, but I was hoping that the one activity should be enough. My approach can in many ways be looked as a Front Controller pattern, where each of the activities is responsible for loading and displaying the acitivity they need.
I'm simply asking if it's even possible to dynamically registrer activities in the manifest (or allow them to run in another way), and if no, is there any other valid solution?
Android requires that each activity to be registered in your AndroidManifest.xml file ahead of time. When deployed, your app will live on a device as an .apk file, which can't be modified (aside from being updated/replaced with a new APK).
If I understand the question correctly, you simply don't want to have to list a large number of activities in your manifest file? Fragments don't need to be registered in the manifest file, so if your goal is to dynamically swap out UI components, that would be the answer. Fragments are available back to 1.6 via the Support library.

widgets disappear after updating app, using library

I have 2 versions of an app, free and paid, but have been maintaining the code separately. I've finally moved the code into a library referenced by both to make maintaining the code easier.
I found that changing the AppwidgetProvider caused the launcher to delete any existing widgets, so I moved those classes back out of the library to keep the provider the same so users don't have to recreate their widgets. The launcher no longer deletes the widgets, but instead, they simply don't appear after updating.
If I call AppWidgetManager.getAppWidgetIds for the componentname, as it's always been, the appwidgetid is still there. The appwidgetprovider and service still get called to update the widget, and /data/system/appwidgets.xml still shows the widget, but the launcher never displays it.
It's not that it's invisible, as long pressing in the widget location brings up the wallpaper chooser. I can create new widgets just fine, but I don't want to frustrate users by asking them to recreate their widgets. The logs don't show any errors thrown by the launcher or AppwidgetService.
Any ideas why the widget stops rendering after updating? It's somehow related to moving most of the code into a separate library. Thanks!
Edit: I'm testing on an emulator, api level 15, stock launcher
OK, I found a solution, but I feel sick for what it is...
After reading about how classes declared in the manifest should never change, I went and creating each class in the manifest as a class in the app, extending the corresponding class in the library. Then, I had to change every Intent to include the correct class, using forName. So, an example of this scenario is:
app package: com.sample.package
activity: MyActivity
library: com.sample.package.core
activity: MyActivity
There's a MyActivity in the app, and a MyActivity in the library. The MyActivity in the app simply extends com.sample.package.core.MyActivity.
Then, any occurrence of...
new Intent(context, MyActivity.class)
...in the library must become...
new Intent(context, Class.forName(context.getPackageName() + "." + MyActivity.class.getSimpleName());
If there's a way to do this through the manifest, please let me know!

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.

Categories

Resources