I have Small and Big widget, both are configured through the same WidgetConfigActivity. Excerpt from AndroidManifest:
<receiver
android:name=".SmallWidgetProvider"
android:icon="#drawable/small_widget_icon"
android:label="#string/small_widget">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="#xml/widget_small_info" />
</receiver>
<receiver
android:name=".BigWidgetProvider"
android:icon="#drawable/big_widget_icon"
android:label="#string/big_widget">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="#xml/widget_big_info" />
</receiver>
widget_small_info.xml:
<appwidget-provider
xmlns:android="http://schemas.android.com/apk/res/android"
android:initialLayout="#layout/widget_small_layout"
android:minHeight="40dp"
android:minWidth="40dp"
android:previewImage="#drawable/widget_small_preview"
android:updatePeriodMillis="1200000"
android:configure="com.a.b.WidgetConfigActivity">
</appwidget-provider>
I know how to get ID of the widget, but this does not tell me what kind of widget it is:
Bundle extras = intent.getExtras();
if (extras != null) {
mAppWidgetId = extras.getInt(
AppWidgetManager.EXTRA_APPWIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID);
}
How can I differentiate between Small and Big widget in WidgetConfigActivity? I think I should check for provider (Doc: This field corresponds to the android:name attribute in the element in the AndroidManifest.xml file.) with
AppWidgetManager.EXTRA_APPWIDGET_PROVIDER
But this is not in the extras.
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
AppWidgetProviderInfo providerInfo = appWidgetManager.getAppWidgetInfo(mAppWidgetId);
Found this from this answer.
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
AppWidgetProviderInfo appWidgetManager.getAppWidgetInfo(widgetid);
Related
I'm sure I'm missing something simple here but onGetViewFactory is not calling while attempting to populate a StackWidget. I've gone over this multiple times and cannot find my error.
The RemoteViewsService is being attached in updateWidget:
public static void updateWidget(Context context, AppWidgetManager appWidgetManager, int widgetId) {
// Create the RemoteViews to represent the widget layout.
Log.i(TAG, "updateWidget: Creating RemoteViews");
RemoteViews widgetViews = new RemoteViews(context.getPackageName(), R.layout.stack_widget_layout);
// Create an intent that points to our widget service.
Log.i(TAG, "updateWidget: Creating Intent");
Intent remoteIntent = new Intent(context, StackWidgetService.class);
// Attach that intent to the RemoteViews as our remote adapter.
Log.i(TAG, "updateWidget: Setting Adapter");
widgetViews.setRemoteAdapter(R.id.stack_view, remoteIntent);
// Set which view should be used as the empty view.
Log.i(TAG, "updateWidget: Setting empty view");
widgetViews.setEmptyView(R.id.stack_view, R.id.text_empty);
// Update the widget.
Log.i(TAG, "updateWidget: Updating widget");
appWidgetManager.updateAppWidget(widgetId, widgetViews);
}
The following should be called after calling appWidgetManager.updateAppWidget but is not:
#Override
public RemoteViewsFactory onGetViewFactory(Intent intent) {
// NOT CALLED
Log.i(TAG, "onGetViewFactory: Creating new view factory");
return new StackWidgetViewFactory(getApplicationContext());
}
As far as I can tell, my manifest is correct:
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name=".stackwidget.StackWidgetProvider"
android:exported="true">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data android:name="android.appwidget.provider"
android:resource="#xml/stack_widget_info" />
</receiver>
<service android:name=".stackwidget.StackWidgetService"
android:exported="true"
android:permission="android.permission.BIND_REMOTEVIEWS"/>
</application>
And here is my stack_widget_info.xml, for safety:
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="250dp"
android:minHeight="180dp"
android:updatePeriodMillis="1800000"
android:initialLayout="#layout/stack_widget_layout"
android:autoAdvanceViewId="#layout/stack_item"
android:widgetCategory="home_screen"
android:resizeMode="horizontal |vertical">
</appwidget-provider>
The issue was actually in my widget layout. Constraint layout was causing issues. It runs correctly when changing over to Linear layout.
I'm trying to build a hello world widget and I followed three different examples and every time my apk loads the main activity as an app and not a widget. In the last example it showed a manifest with no activity block. When I take the main activity out of the manifest and just have the receiver block Android Studio throws an exception of Default activity not found. So that is the one difference I have from the example. I have an <*activity> block in the manifest.
I'm in Android Studio 1.0.2. What could be causing this?
My current code is based on this example
http://www.vogella.com/tutorials/AndroidWidgets/article.html
http://www.sitepoint.com/how-to-code-an-android-widget/
https://looksok.wordpress.com/2012/12/15/android-complete-widget-tutorial-including-source-code/
AndroidMainfest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.me.countdown" >
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name="MyWidgetProvider" >
<intent-filter >
<action
android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="#xml/widget_info" />
</receiver>
</application>
</manifest>
MyWidgetProvider.xml
public class MyWidgetProvider extends AppWidgetProvider {
private static final String ACTION_CLICK = "ACTION_CLICK";
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
// Get all ids
ComponentName thisWidget = new ComponentName(context,
MyWidgetProvider.class);
int[] allWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget);
for (int widgetId : allWidgetIds) {
// create some random data
int number = (new Random().nextInt(100));
RemoteViews remoteViews = new RemoteViews(context.getPackageName(),
R.layout.widget_layout);
Log.w("WidgetExample", String.valueOf(number));
// Set the text
remoteViews.setTextViewText(R.id.update, String.valueOf(number));
// Register an onClickListener
Intent intent = new Intent(context, MyWidgetProvider.class);
intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context,
0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
remoteViews.setOnClickPendingIntent(R.id.update, pendingIntent);
appWidgetManager.updateAppWidget(widgetId, remoteViews);
}
}
}
widget_info.xml under res/xml
\<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider
xmlns:android="http://schemas.android.com/apk/res/android"
android:initialLayout="#layout/widget_layout"
android:minHeight="72dp"
android:minWidth="300dp"
android:updatePeriodMillis="300000" >
</appwidget-provider>
I'm not sure what those other examples are designed for, but the version of Android Studio I'm using - 1.2.2 - does it differently. I found the example below and was able to get a working shell of a widget, which is what I wanted. Even this one needed many small tweaks before it would compile and run, but I did get it.
Greg
http://www.clearcreekcode.com/create-a-widget-in-android-studio/
I'm trying to create a battery widget, but the textView that is supposed to show battery state isn't updating. It only changes if the phone is restarted. What is missing from the code?
AndroidManifest:
<uses-permission android:name="android.permission.BATTERY_STATS" />
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name=".WidgetConfigurationActivity"
android:excludeFromRecents="true"
android:launchMode="singleTop" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
</intent-filter>
</activity>
<receiver android:name=".MAppWidgetProvider" >
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="#xml/widget_info" />
</receiver>
</application>
AppWidgetProvider:
public class MAppWidgetProvider extends AppWidgetProvider
{
private String batteryLevel="init";
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds)
{
context.getApplicationContext().registerReceiver(this, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
updateView(context);
}
#Override
public void onReceive(Context context, Intent intent)
{
int rawlevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
int level = -1;
level = (rawlevel * 100) / scale;
batteryLevel = Integer.toString(level);
updateView(context);
super.onReceive(context, intent);
}
public void updateView(Context context)
{
RemoteViews thisViews = new RemoteViews(context.getApplicationContext()
.getPackageName(), R.layout.widget_layout);
thisViews.setTextViewText(R.id.textView1, batteryLevel);
ComponentName thisWidget = new ComponentName(context,
AnalogClockWidgetProvider.class);
AppWidgetManager.getInstance(context).updateAppWidget(thisWidget,
thisViews);
}
}
Info:
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="40dp"
android:minHeight="40dp"
android:initialLayout="#layout/widget_layout"
android:updatePeriodMillis="10000"
android:configure="in.wptrafficanalyzer.appwidgetclockconfiguration.WidgetConfigurationActivity"
android:resizeMode="horizontal|vertical">
</appwidget-provider>
First of all you need to use a service to update your widget because activity is in onPause state when you are outside application.
Secondly in service register an intent filter for receiving changes in battery status. And when its getting updated, notify appwidget.
You can also keep a variable in service to which widget will refer and set its level.
I'm trying to launch my MainActivity from my AppWidget with pending intent but I can't figure out why it doesn't fire the intent. The widget has a ListView with up to 3 items, which is bind to a RemoteViewsService. I want to start my MainActivity when any of the rows is clicked. Can anyone tell me what I might done wrong?
I already checked some related posts like this Launching an activity from an AppWidget, and even tried to run the sample from here https://github.com/commonsguy/cw-advandroid/tree/master/AppWidget, and while the sample works, still can't tell what I did differently.
Here are some of my code that are relevant, please let me know if I should post more, and apologize in advance if my post doesn't look right as this is my first question:
Widget layout:
<?xml version="1.0" encoding="utf-8"?>
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/stocks"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
AppWidgetProvider class:
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
Log.d(LOGTAG, "onUpdate");
Log.d(LOGTAG, "appWidgetIds.length="+appWidgetIds.length);
for (int i = 0; i < appWidgetIds.length; ++i) {
Log.d(LOGTAG, "appWidgetIds[i]="+appWidgetIds[i]);
}
// update each of the app widgets with the remote adapter
for (int i = 0; i < appWidgetIds.length; ++i) {
updateWidget(context, appWidgetManager, appWidgetIds[i]);
}
super.onUpdate(context, appWidgetManager, appWidgetIds);
}
public void updateWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId) {
Log.d(LOGTAG, "updateWidget id="+appWidgetId);
// Sets up the intent that points to the StackViewService that will
// provide the views for this collection.
Intent intent = new Intent(context, StockAppWidgetService.class);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
// When intents are compared, the extras are ignored, so we need to embed the extras
// into the data so that the extras will not be ignored.
intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.stock_appwidget);
rv.setRemoteAdapter(R.id.stocks, intent);
Intent contentIntent = new Intent(context, MainActivity.class);
//contentIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent startAppPendingIntent =
PendingIntent.getActivity(context, 0, contentIntent, 0);
rv.setPendingIntentTemplate(R.id.stocks, startAppPendingIntent);
appWidgetManager.updateAppWidget(appWidgetId, rv);
}
manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="x40241.chris.yuan.a4.app"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="14"
android:targetSdkVersion="16" />
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#android:style/Theme.Holo.Light.DarkActionBar" >
<activity
android:name=".MainActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".SearchableActivity" >
<intent-filter>
<action android:name="x40241.chris.yuan.a4.app.SEARCH" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<service android:name=".StockServiceImpl" />
<service android:name=".StockAppWidgetService"
android:permission="android.permission.BIND_REMOTEVIEWS" />
<receiver android:name=".ServiceLauncher">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<receiver android:name=".StockAppWidgetProvider" >
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data android:name="android.appwidget.provider"
android:resource="#xml/stock_appwidget_info" />
</receiver>
</application>
</manifest>
I think I found the answer, or at least a workable solution in a related post:
AdapterViewFlipper in app widget: setPendingIntentTemplate() and setOnClickFillInIntent() not working
It turns out I wasn't setting the FillInIntent properly. I should call setOnClickFillInIntent on the top level view of the list item instead of the list itself. Here's the code change that made it work in RemoteViewsFactory:
public RemoteViews getViewAt(int position) {
if (DEBUG) Log.d(LOGTAG, "StockRemoteViewsFactory getViewAt position="+position);
RemoteViews rv = new RemoteViews(mContext.getPackageName(), R.layout.widget_item);
rv.setTextViewText(R.id.text_symbol, mStockItems.get(position).getSymbol());
rv.setTextViewText(R.id.text_price, String.format("$%.2f", mStockItems.get(position).getPrice()));
Intent fillInIntent = new Intent(Intent.ACTION_MAIN);
fillInIntent.putExtra(StockAppWidgetProvider.ITEM_NUM, position);
// set on the top level view of the list item, instead of the list view itself
//rv.setOnClickFillInIntent(R.id.stocks, fillInIntent);
rv.setOnClickFillInIntent(R.id.general_layout, fillInIntent);
return rv;
}
My problem is I have an app widget which needs to be updated in every 32 mins. However, it's not updating the textview. I placed Log entry to my onUpdate method to see is it working on Logcat, and i confirmed method is working properly but just it's not changing the value of textview. Here is my code;
AppWidgetProvider:
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
// TODO Auto-generated method stub
ComponentName thisWidget=new ComponentName(context,
IzUlasimProvider.class);
int[] allWidgetIds=appWidgetManager.getAppWidgetIds(thisWidget);
for(int id:allWidgetIds){
String now=new SimpleDateFormat("HH:mm").format(new Date());
RemoteViews views=new RemoteViews(context.getPackageName(), R.layout.widget);
views.setTextViewText(R.id.tvWidget,now);
appWidgetManager.updateAppWidget(id, views);
now=null;
}
Log.w("Update","Update Finished");
}
My config file:
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:initialLayout="#layout/widget"
android:minHeight="110dp"
android:updatePeriodMillis="1920000"
android:minWidth="250dp" android:configure="com.izulasim.widget.IzWidgetConfig">
AndroidManifest part for the widget:
<receiver android:name="com.izulasim.widget.IzWidgetProvider">
<intent-filter >
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data android:name="android.appwidget.provider"
android:resource="#xml/widget_config" />
</receiver>
<activity
android:name="com.izulasim.widget.IzWidgetConfig"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
</intent-filter>
</activity>
<provider android:name=".IzUlasimProvider"
android:authorities="com.izulasim.provider.IzUlasim" />