I have made a simple widget with stretchable layout, and want to allow users to put widgets with different sizes using the same layout and the same widget provider class.
I have found, that I need to create 4 widget_provider_x_y.xml files (x and y are values 2,2 2,3 3,2 3,3) that is very similar except width and height.
Also I have found, that I need to create multiple copies of WidgetProvider classes that is exactly the same (except names). If I try to use single WidgetProvider class - I will see only one widget in widgets list.
I have a question:
- why do I need to copy java class? It is the same, because layout is the same, and I do not want to have 4 more files in projects that I need to update simultaneously.
Here is a part of my androidManifest.xml:
<receiver android:name=".SimpleNoteWidgetProvider_3_2" android:label="#string/app_widget_3_2">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
<action android:name="ACTION_WIDGET_UPDATE_FROM_ACTIVITY"/>
<action android:name="ACTION_WIDGET_UPDATE_FROM_WIDGET"/>
</intent-filter>
<meta-data android:name="android.appwidget.provider" android:resource="#xml/widget_provider_3_2"/>
</receiver>
<receiver android:name=".SimpleNoteWidgetProvider_3_3" android:label="#string/app_widget_3_3">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
<action android:name="ACTION_WIDGET_UPDATE_FROM_ACTIVITY"/>
<action android:name="ACTION_WIDGET_UPDATE_FROM_WIDGET"/>
</intent-filter>
<meta-data android:name="android.appwidget.provider" android:resource="#xml/widget_provider_3_3"/>
</receiver>
You don't have to copy your WidgetProvider class n times to get n widget sizes. Just inherit from the main WidgetProvider class like this:
public class SimpleNoteWidgetProvider_3_2 extends SimpleNoteWidgetProvider {
}
and
public class SimpleNoteWidgetProvider_3_3 extends SimpleNoteWidgetProvider {
}
Related
I'm supporting different variants of the same basic widget (dark scheme, light scheme, etc.) and find that I can't use the same provider class in more than one receiver declaration in the manifest, such as:
<receiver android:name=".otd.OtdWidgetProvider" android:label="#string/otd_widget_label">
<meta-data android:name="android.appwidget.provider" android:resource="#xml/otd_appwidget_provider"/>
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
</intent-filter>
</receiver>
<receiver android:name=".otd.OtdWidgetProvider" android:label="#string/otd_widget_label">
<meta-data android:name="android.appwidget.provider" android:resource="#xml/otd_appwidget_light_provider"/>
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
</intent-filter>
</receiver>
Note that each receiver does declare a different XML file, which is where the different layouts are specified. I have worked around this by creating identical provider classes with different names (e.g., "otd.OtdWidgetProviderLight", etc.) but it would be more efficient to use the same provider class (internally, the provider can easily differentiate the variants by looking up the widget info via AppWidgetManager). Is there some way that I've missed to get around this limitation?
How do I organise my code? I have tried to put everything in a different package but then my notifications with GCM doesn't work anymore. if i put everything in the same root package no problem.
this are my two tests, left gives problems on gcm right doesn't:
Is there any way to easy organise your code like I did in the right one? or do all the classes, activities, services needs to be in the same package?
EDIT 1 = SOLUTION
You need to create your own class that extends GCMBroadcastReceiver like this:
import android.content.Context;
import com.google.android.gcm.GCMBroadcastReceiver;
public class GCMReceiver extends GCMBroadcastReceiver{
#Override
protected String getGCMIntentServiceClassName(Context context) {
return "yourAppPackage.yourNewAddedPackage.GCMIntentService";
}
}
Then you need to change the default receiver in your manifest to the package where this class is located. for example:
<receiver android:name="yourPackage.PackageOfTheGCMReceiver.GCMReceiver"
android:permission="com.google.android.c2dm.permission.SEND" >
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="be.vanlooverenkoen.piautomation" />
</intent-filter>
</receiver>
If you want to organize your code, you have patterns options like: MVC, There is a great debate about whether Android uses default MVC.
But I see your picture in your structure you want a recommendation for .java files.
I give the following recommendation that I have seen in different files, books and github repositories (The packages are in alphabetical order):
activities
adapters
authenticator
data
data.migrations
fragments
helpers
interfaces
models
preferences
sync
You need to create your own class that extends GCMBroadcastReceiver like this:
import android.content.Context;
import com.google.android.gcm.GCMBroadcastReceiver;
public class GCMReceiver extends GCMBroadcastReceiver{
#Override
protected String getGCMIntentServiceClassName(Context context) {
return "yourAppPackage.yourNewAddedPackage.GCMIntentService";
}
}
Then you need to change the default receiver in your manifest to the package where this class is located. for example:
<receiver android:name="yourPackage.PackageOfTheGCMReceiver.GCMReceiver"
android:permission="com.google.android.c2dm.permission.SEND" >
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="be.vanlooverenkoen.piautomation" />
</intent-filter>
</receiver>
I would like to put the GCMIntentService in a directory other than my package root.
The GCM documentation states that
By default, it must be named .GCMIntentService, unless the
application uses a custom BroadcastReceiver that redefines its name.
My question is - how do I create this "custom BroadcastReceiver" thay talk about?
Try this -- Rename or change package of GCMIntentService class
The basic docs have you add the following to your manifest:
<receiver android:name="com.google.android.gcm.GCMBroadcastReceiver" android:permission="com.google.android.c2dm.permission.SEND" >
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="my_app_package" />
</intent-filter>
</receiver>
This points to a GCM-supplied BroadcastReceiver that will route events to your .GCMIntentService. If you wish to have your service reside in some other package, you will need to supply your own BroadcastReceiver. This may be as simple as creating one that subclasses GCMBroadcastReceiver and overrides getGCMIntentServiceClassName() to return the fully-qualified class name of the service to use.
I have searched and searched and cannot seem to find the answer to this specific question about a custom intent.
I have an application with 4 activities, 1 is the main that sets things up and the other 3 represent the different screens I present to the user. I am trying to use custom intents to start the different activities.
Here is my AndroidManifext.xml:
<?xml version="1.0" encoding="utf-8"?>
<application android:icon="#drawable/icon" android:label="#string/app_name">
<activity android:name=".Activities.REDB_main" android:label="#string/app_name"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".Activities.ChooseCards" android:launchMode="singleTop">
<intent-filter>
<action android:name="#string/ACTION_VIEW" />
<action android:name="#string/ACTION_REFRESH" />
<category android:name="#string/CATEGORY_SHUFFLE" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity android:name=".Activities.SelectSets" android:launchMode="singleTop">
<intent-filter>
<action android:name="#string/ACTION_VIEW" />
<action android:name="#string/ACTION_REFRESH" />
<category android:name="#string/CATEGORY_SELECT_SETS" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity android:name=".Activities.SelectCards" android:launchMode="singleTop"
android:permission="android.permission.ACCESS_CHECKIN_PROPERTIES">
<intent-filter>
<action android:name="#string/ACTION_VIEW" />
<action android:name="#string/ACTION_REFRESH" />
<category android:name="#string/CATEGORY_SELECT_CARDS" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</application>
I create a category for each application and then the two kinds of actions I want it to handle. I know that I could use explicit intents, but since I want to have different actions I figured that making the implicit intents would work better.
I call the first of my real activities from within my main with this:
Intent intent = new Intent().setAction(getString(R.string.ACTION_VIEW));
intent.addCategory(getString(R.string.CATEGORY_SHUFFLE));
startActivity(intent);
Of course, the reason I am here is because the above can never find the activity that matches the intent. The error messages states the action and category correctly and unless I'm wrong, the above manifest creates the intent-filters correctly.
Searching around online, I always seem to find examples with data also being used. I messed around with adding data just to see if it was necessary but it did not seem to matter.
On a slightly different note, is there a different way I should be controlling the flow of my program besides intents? The reason I have two actions is because 1 switches the view while the other is there to just refresh the data so that when the user switches to the screen later, they don't see it quickly refresh the data but instead just the new stuff.
I know that I could use explicit intents, but since I want to have different actions I figured that making the implicit intents would work better.
I rather doubt that. Mostly you use <intent-filter> when you want things other than your own app to start the component (e.g., third party apps).
Also, I doubt you want android:launchMode="singleTop" on all of those. And I am very certain that you do not want android:permission="android.permission.ACCESS_CHECKIN_PROPERTIES" on the last one, as you won't be able to launch your own activity then, most likely.
The error messages states the action and category correctly and unless I'm wrong, the above manifest creates the intent-filters correctly.
I have never seen an Android application use a string resource for a <category> element. Perhaps that is contributing to your difficulty. Also, since the <category> elements are not doing you any good that I can see (except your LAUNCHER one), I am unclear why you put them there.
I suggest that you just drop the <intent-filter> elements and use explicit Intents.
Don't use resource strings to make intents actions, use android.intent.action.VIEW instead
I've just finished my Android widget. Now I need to have different sizes of this widget for the user to choose from.
For example, I need a medium, small and large size widget, so when the user installs the app and hold the home screen then choose widget, in the widget menu I want him to see three widgets with the same app name but with the size. Something like this:
helloSmall
helloMedium
helloLarge
I have the medium one ready, but how can I add the small and the large in the same app? Knowing that all three sizes contain the same exact data and actions, just the size and the background are different.
You need a receiver definition for each type in your manifest file like:
<receiver android:name=".MyWidget" android:label="#string/medium_widget_name">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data android:name="android.appwidget.provider"
android:resource="#xml/medium_widget_provider" />
</receiver>
<receiver android:name=".MyWidget" android:label="#string/large_widget_name">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data android:name="android.appwidget.provider"
android:resource="#xml/large_widget_provider" />
</receiver>
This would allow you to have the same AppWidgetProvider class be used for multiple widgets, with different widget names and different sizes defined in the <appwidget-provider> XML.
Now if you need more differences in your widgets than what is in the <appwidget-provider> XML I would create a base widget class that implements all the common behavoir between the different types:
public abstract class MyBaseWidget extends AppWidgetProvider
And then each of your concrete implementations could extend MyBaseWidget. Then in your manifest file you would have a receiver definition for each of your concrete implementations like:
<receiver android:name=".MyMediumWidget" android:label="#string/medium_widget_name">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data android:name="android.appwidget.provider"
android:resource="#xml/medium_widget_provider" />
</receiver>
<receiver android:name=".MyLargeWidget" android:label="#string/large_widget_name">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data android:name="android.appwidget.provider"
android:resource="#xml/large_widget_provider" />
</receiver>
Actually, android:name for each widget have to be different. If you will do this as in example, only one widget will be visible in widgets list.
Guys, I had the same problem.
You need to actually add a second widget provider aswell;
<receiver android:name=**".MyWidget**" android:label="#string/medium_widget_name">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data android:name="android.appwidget.provider"
android:resource="#xml/medium_widget_provider" />
</receiver>
<receiver android:name=**".MyWidget2"** android:label="#string/large_widget_name">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data android:name="android.appwidget.provider"
android:resource="#xml/large_widget_provider" />
</receiver>
Enjoy
Ok so basically you will need:
layout file fore each widget. ex: main_small.xml, main_medium.xml ...
in the xml directory add a provider for each widget. ex: small_provider.xml, medium_provider.xml ... and so on (note if you don't have an xml directory add it under the drawable directory).
now what!
define a receiver in the manifest for each widget. (just like the example in the main answer)
you can use the same layout or deferent layout. basically this is up to you.
in your provider you should have something like this:
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="146dip"
android:minHeight="138dip"
android:updatePeriodMillis="10000"
android:initialLayout="#layout/main"
/>
make sure, for each provider to specify the target layout file you want to use. in this code I'm asking for the file main.xml in the layout directory. for my medium widget for example i'll have another provider with the same exact code but i'll change the last line
> android:initialLayout="#layout/medium".
I hope this helps if not let me know and I can upload a working example on my website and you can take a closer look at it. please let me know how it goes.
best of luck.
Some extra info to the other answers...
If you are duplicating the files mentioned, and if your widget uses a Service to provide some functionality, you might have to duplicate your service.
If you duplicate your Service, remember to update your manifest with the new service, otherwise the new service won't run...
This wasted some time for me.
If you use any BroadcastReceiver to send Intents to your duplicate Services... don't forget to update that code too:
you must now send intents to each of the services.