I'm having some problems using RemoteViews. Simply want to make a widget that shows battery level. I've done all other stuffs already.
Here is my class:
public class BatteryLevel extends AppWidgetProvider{
public void onUpdate( Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds ){
context.startService(new Intent(context, BatteryMonitorReceiver.class));
}
public static class BatteryMonitorReceiver extends Service{
private int currentBatteryLevel = 0;
private BroadcastReceiver batteryReceiver = new BroadcastReceiver(){
#Override
public void onReceive( Context context, Intent intent ){
currentBatteryLevel = intent.getIntExtra( "level", 0 );
}
};
#Override
public void onStart (Intent intent, int startId) {
registerReceiver( this.batteryReceiver, new IntentFilter( Intent.ACTION_BATTERY_CHANGED ) );
RemoteViews updateViews = new RemoteViews( getPackageName(), R.layout.main );
updateViews.setTextViewText( R.id.level, String.valueOf(currentBatteryLevel));
ComponentName thisWidget = new ComponentName( this, BatteryLevel.class );
AppWidgetManager manager = AppWidgetManager.getInstance( this );
manager.updateAppWidget(thisWidget, updateViews);
}
#Override
public IBinder onBind( Intent intent ){
return null;
}
}
}
When i start the app no error and btw no update at R.id.level.
What's wrong?
Note: api version 7.
Since your service is an inner class, I guess you have not declared it in your Android Manifest, which is needed.
And you shouldn't use onStart, it is deprecated. Use onStartCommand. And you forgot to unregister your receiver...
Related
Here i have made one widget and added its click listener where i'm starting one service.It works perfectly.But once user force stops application from appinfo my widget's click is not responding.I m not getting why this is happening.Help on this will be appreciated.Thank you
My app widget class code:
MyWidget.java:
public class MyWidget extends AppWidgetProvider {
static Context cont;
static SharedPreferences preferences;
static void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
int appWidgetId) {
cont = context;
Intent intent2 = new Intent();
intent2.setAction("....");
PendingIntent pendingIntent = PendingIntent.
getBroadcast(context, 0,
intent2, PendingIntent.FLAG_UPDATE_CURRENT);
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.my_widget);
views.setOnClickPendingIntent(R.id.appwidget_text, pendingIntent);
appWidgetManager.updateAppWidget(appWidgetId, views);
}
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
// There may be multiple widgets active, so update all of them
for (int appWidgetId : appWidgetIds) {
updateAppWidget(context, appWidgetManager, appWidgetId);
}
}
#Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
context.startService(new Intent(context, MyService.class));
}
#Override
public void onEnabled(Context context) {
.................. }
#Override
public void onDisabled(Context context) {
.................}
Always use an explicit Intent whenever possible. Your code would not work on Android 8.0 and higher, where implicit broadcasts are banned. And an explicit Intent can move your app out of the stopped state.
I tried lots of solutions but after weeks i have not been able to solve this issue: why "notifyappwidgetviewdatachanged" doesn't work? how can i update a listview placed on my widget? Where am i wrong?
Here are my classes.
Widget Provider:
public class Widget_Provider extends AppWidgetProvider
{
public static final String ACTION_MOSTRAORARI = "fACTION_TOAST";
public static final String EXTRA_STRING = "EXTRA_STRING";
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
// There may be multiple widgets active, so update all of them
for (int appWidgetId : appWidgetIds)
{
RemoteViews views = updateWidgetListView(context, appWidgetId);
final Intent onItemClick = new Intent(context, Widget_Provider.class);
onItemClick.setAction(ACTION_MOSTRAORARI);
onItemClick.setData(Uri.parse(onItemClick.toUri(Intent.URI_INTENT_SCHEME)));
final PendingIntent onClickPendingIntent = PendingIntent.getBroadcast(context, 0, onItemClick, PendingIntent.FLAG_UPDATE_CURRENT);
views.setPendingIntentTemplate(R.id.myStopList, onClickPendingIntent);
appWidgetManager.updateAppWidget(appWidgetId, views);
}
super.onUpdate(context, appWidgetManager, appWidgetIds);
}
public RemoteViews updateWidgetListView(Context context, int appWidgetId)
{
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget);
Intent svcIntent = new Intent(context, Widget_Service.class);
svcIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
svcIntent.setData(Uri.parse(svcIntent.toUri(Intent.URI_INTENT_SCHEME)));
remoteViews.setRemoteAdapter(R.id.myStopList, svcIntent);
return remoteViews;
}
#Override
public void onReceive(Context context, Intent intent)
{
if (intent.getAction().equals(ACTION_MOSTRAORARI)) {
if (MainUtils.isNewtworkAvailable(context))
{
String item = intent.getExtras().getString(EXTRA_STRING);
Intent intentOrari = new Intent(context, Diag_MostraOrari.class);
intentOrari.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
context.startActivity(intentOrari);
}
}
super.onReceive(context, intent);
}
#Override
public void onDeleted(Context context, int[] appWidgetIds) {}
#Override
public void onEnabled(Context context) {}
#Override
public void onDisabled(Context context) {}
}
Widget Service:
public class Widget_Service extends RemoteViewsService
{
#Override
public RemoteViewsFactory onGetViewFactory(Intent intent)
{
Map<String, ArrayList<String>> map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
/* ---
* Here i fetch data from a local db and store it on "map"
*/ ---
return (new Widget_ListProvider(this.getApplicationContext(), intent, map));
}
}
ListProvider:
public class Widget_ListProvider implements RemoteViewsFactory
{
private Map<String, ArrayList<String>> map = new HashMap<>();
private ArrayList<ListItem_Widget> listItemList = new ArrayList<>();
private Context context = null;
private int appWidgetId;
public Widget_ListProvider(Context context, Intent intent, Map<String, ArrayList<String>> map)
{
this.map = map;
this.context = context;
appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
populateListItem();
}
//This function populate the arraylist "listItemList" by the data stored on "map"
private void populateListItem() { [...] }
#Override
public int getCount() {
return listItemList.size();
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public RemoteViews getViewAt(int position)
{
final RemoteViews remoteView = new RemoteViews(context.getPackageName(), R.layout.listitem_widget);
ListItem_Widget listItem = listItemList.get(position);
remoteView.setTextViewText(R.id.heading, listItem.heading);
remoteView.setTextViewText(R.id.content, listItem.content);
final Intent fillInIntent = new Intent();
fillInIntent.setAction(Widget.ACTION_MOSTRAORARI);
final Bundle bundle = new Bundle();
bundle.putString(Widget.EXTRA_STRING, listItem.heading);
fillInIntent.putExtras(bundle);
remoteView.setOnClickFillInIntent(R.id.listrow, fillInIntent);
return remoteView;
}
#Override
public RemoteViews getLoadingView() {
return null;
}
#Override
public int getViewTypeCount() {
return 1;
}
#Override
public boolean hasStableIds() {
return true;
}
#Override
public void onCreate() {}
#Override
public void onDataSetChanged() {}
#Override
public void onDestroy() {}
}
The xml of my listview custom item contains just two textviews: "heading" and "content".
Where am i wrong? why when i call "notifyappwidgetviewdatachanged" from another activity nothing happens?
[EDIT]
That's the activity where i need to update my widget.
public class Diag_Line extends AppCompatActivity
{
//[...]
#Override
protected void onCreate(Bundle savedInstanceState)
{
//[...]
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(getApplicationContext());
ComponentName thisAppWidget = new ComponentName(getApplicationContext().getPackageName(), Widget.class.getName());
int[] appWidgetIds = appWidgetManager.getAppWidgetIds(thisAppWidget);
appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetIds, R.id.myStopList);
//"myStopList" is the id of my listview inside the widget xml.
//[...]
}
}
Well, the problem is a quite obvious: In your RemoteViewsFactory you have an empty method onDataSetChanged(). But when you're triggering notifyAppWidgetViewDataChanged(), you're getting callback in onDataSetChanged(). For better understanding check this pic.
If Diag_Line is a Configuration Activity just make something like this:
appWidgetManager.updateAppWidget(appWidgetIds, views); // views is a RemoteViews that you need to build
appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetIds, R.id.myStopList);
Also check official documentation here
UPDATE
Some hints for you. If you're working with widget, make all fetching data and fillings RemoteViews on background (IntentService is a nice approach). So just make IntentService and make logic for widget here. Don't forget to make him foreground (only for Android O), if you're triggering your updates by broadcasts. Also you can check library that makes all for you.
In order for notifyAppWidgetViewDataChanged() to work properly
we need to retreive the data in onDataSetChanged() method of RemoteViewSourceFactory
like this:
override fun onDataSetChanged() {
listOfArticles = newsDatabaseRepo.getSynchronouslySavedNews() as ArrayList<Articles>
}
as suggested in here
And have a method to refresh the widget if a change occured:
private fun refreshWidget() {
val appWidgetManager =
AppWidgetManager.getInstance(applicationContext)
val thisAppWidget = ComponentName(
applicationContext.packageName,
MyAppWidgetProvider::class.java.name
)
val appWidgetIds = appWidgetManager.getAppWidgetIds(thisAppWidget)
appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetIds, R.id.stack_view)
}
After calling the above method and once I come out of my App I am able to notice the changes in My Widget
So in order to check if onDataSetChanged is called after notifyAppWidgetViewDataChanged()
I kept a debug point and onDataSetChanged() was called and the list was updated as suggested in here here
Had a similar problem updating my widget, it was calling onDataSetChanged but not refreshing the layout. So in my case I had to set updateAppWidget to null before updating with remoteviews like this:
appWidgetManager.updateAppWidget(appWidgetId, null);
appWidgetManager.updateAppWidget(appWidgetId, remoteViews);
Looks like there is some sort of caching happening here.
I'm trying yo make a simple widget with clickable image that will change when its clicked.
I don't understand why it doesn't work
public class AppWidget extends AppWidgetProvider {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction()==null) {
context.startService(new Intent(context,
ToggleService.class));
} else {
super.onReceive(context, intent);
}
}
#Override
public void onUpdate(Context context, AppWidgetManager
appWidgetManager, int[] appWidgetIds) {
context.startService(new Intent(context,
ToggleService.class));
}
public static class ToggleService extends IntentService {
public ToggleService() {
super("AppWidget$ToggleService");
}
#Override
protected void onHandleIntent(Intent intent) {
ComponentName me=new ComponentName(this, AppWidget.class);
AppWidgetManager mgr=AppWidgetManager.getInstance(this);
mgr.updateAppWidget(me, buildUpdate(this));
}
private RemoteViews buildUpdate(Context context) {
RemoteViews updateViews=new
RemoteViews(context.getPackageName(),R.layout.widget);
int a = 1;
if(a ==
1) {
updateViews.setImageViewResource(R.id.phoneState,
R.drawable.dual_off);
} else {
updateViews.setImageViewResource(R.id.phoneState,
R.drawable.dual_on);
}
Intent i=new Intent(this, AppWidget.class);
PendingIntent pi = PendingIntent.getBroadcast(context, 0, i,0);
updateViews.setOnClickPendingIntent(R.id.phoneState,pi);
return updateViews;
}
}
}
I have tried all sort of things in for loop but it just won't change image.
I have done this from a book about android development, am new to java and android.
You would to update your widget by AppWidgetManager, after changing remoteview's source.In this snippet code I try to change layout of widget that it's AppWidgetProvider name is DictionWidgetProvider :
private void hideWidgetInstances() {
hideViews = new RemoteViews(App.getContext().getPackageName(),
R.layout.hide);
AppWidgetManager mManager = AppWidgetManager.getInstance(App
.getContext());
ComponentName cn = new ComponentName(App.getContext(),
DictionWidgetProvider.class);
mManager.updateAppWidget(cn, hideViews);
}
Here App is a class that extends Application.You can use Application Context to reach ComponentName.
I have a problem with service. I'm trying to do a simple battery level widget but the problem is that I don't know how to stop service when the widget is removed from homescreen.
Here is my service class
public class batteryService extends Service{
AppWidgetManager widgetManager;
private RemoteViews remoteViews;
private ComponentName thisWidget;
private BroadcastReceiver mBatInfoReceiver = new BroadcastReceiver()
{
#Override
public void onReceive(Context context, Intent intent)
{
widgetManager = AppWidgetManager.getInstance(context);
remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget);
thisWidget = new ComponentName(context, Widget.class);
int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1)*100;
int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
float value = (float)level/scale;
remoteViews.setTextViewText(R.id.widget_textview, String.valueOf(value));
widgetManager.updateAppWidget(thisWidget, remoteViews);
}
};
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
#Override
public void onCreate() {
this.registerReceiver(mBatInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
super.onCreate();
}
#Override
public void onDestroy() {
this.unregisterReceiver(mBatInfoReceiver);
super.onDestroy();
}
}
and here widget class
public class Widget extends AppWidgetProvider{
private AppWidgetManager widget_manager;
private RemoteViews remoteViews;
private ComponentName thisWidget;
private Intent intent;
#Override
public void onEnabled(Context context) {
if(intent == null)
{
intent = new Intent(context, batteryService.class);
context.startService(intent);
}
super.onEnabled(context);
}
#Override
public void onDisabled(Context context) {
if(intent != null)
{
context.stopService(intent);
intent = null;
}
super.onDisabled(context);
}
}
When I remove widget from screen the service is still a live. I was trying to change onDisable to onDeleted method but it didn't change anything.
Have you any tips for me ?
Make sure you have the appropriate action in your manifest for the disabled event:
<action android:name="android.appwidget.action.APPWIDGET_DISABLED" />
This would be alongside your existing APPWIDGET_ENABLED and perhaps the classic APPWIDGET_UPDATE actions, for the <receiver> element for your AppWidgetProvider.
Also, please consider not having your service run constantly, but rather updating the battery information once every few minutes using AlarmManager. The battery level simply does not change that often to make it necessary to attempt to keep a service in memory all of the time.
Try by using onDeleted(Context, int[])
I have an app widged which basically has a textview. I also have
an activity which displays a textview from string created within. Now
i have a button in Activity 1 which onClick will send data (string to
appWidget class) I tried putExtra and getExtra methods and also Shared
preferences method, im little confused to use which first!
Here are some inputs from me for more clarity.
Activity1:
final String addwidget =
((TextView)findViewById(R.id.verse)).getText().toString();
Intent widgetIntent = new Intent(MyScheduledActivity.this,
MainWidget.class);
widgetIntent.putExtra("widgetVerse", addwidget);
AppWidget:
public class MainWidget extends AppWidgetProvider {
RemoteViews views;
public static String verseFromFav;
private Bundle getVerseData;
#Override
public void onReceive(Context context, Intent intent) {
getVerseData = intent.getExtras();
verseFromFav = getVerseData.getString("widgetVerse");
super.onReceive(context, intent);
}
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
final int N = appWidgetIds.length;
for (int i=0; i<N; i++) {
views = new RemoteViews(context.getPackageName(),R.layout.widget_layout);
if(verseFromFav == null){
verseFromFav = "no verse";
}
views.setTextViewText(R.id.widgetVerse, verseFromFav);
ComponentName thisWidget = new ComponentName( context, MainWidget.class );
AppWidgetManager.getInstance( context ).updateAppWidget( thisWidget, views );
appWidgetManager.updateAppWidget(appWidgetIds, views);
}
super.onUpdate(context, appWidgetManager, appWidgetIds);
}
}
(THE APP WIDGET SHOWS "no verse" WHICH READS NULL)
Can any one help me with a basic idea on how to display the string
here. please. thanks in advance.
onUpdate will ALWAYS be called (sometimes multiple times) so utilize that to fire a utility or controller class and do all the work in that class.
Thus, in your app widget the only thing you might have, all I am demonstrating is updating a string in a TextView, is stuff in the onUpdate() method.
#Override
public void onUpdate( final Context context, final AppWidgetManager appWidgetManager, final int[] appWidgetIds ) {
// Need this here to set the initial state of the app widget
final AppWidgetController _actrlr = new AppWidgetController( context );
_actrlr.updateAppWidgetUI( this );
super.onUpdate( context, appWidgetManager, appWidgetIds );
}
Then in AppWidgetController class you might have something like this...
public final void updateAppWidgetUI( final Context context ) {
final AppWidgetManager _manager = AppWidgetManager.getInstance( context );
final ComponentName _componentName = new ComponentName( context, com.your.namespace.theprovidername.class );
final int[] appWidgetIds = _manager.getAppWidgetIds( _componentName );
for ( final int i : appWidgetIds ) {
final RemoteViews _rv = new RemoteViews( context.getPackageName(), R.layout.appwidget_xml_layout_file );
if( somethingIsTrue ) {
_rv.setTextViewText( R.id.textViewThatYouWantToUpdate, "The text that you wish to display if something is TRUE" );
} else {
_rv.setTextViewText( R.id.textViewThatYouWantToUpdate, "The text that you wish to display if something is FALSE" );
}
_manager.updateAppWidget( i, _rv );
}
}
RemoteView setTextViewText()
http://developer.android.com/reference/android/widget/RemoteViews.html#setTextViewText(int, java.lang.CharSequence)
I would advise you to use sharedpreference for this. It would do you better.
SharedPreferences settings =
getSharedPreferences("MyWigdetPreferences", MODE_WORLD_READABLE);
SharedPreferences.Editor prefEditor = Settings.edit();
prefEditor.putString("widgetVerse", addwidget);
prefEditor.commit();
Then in the mainWidget activity pull it out by..
SharedPreferences settings =
getSharedPreferences("MyWigdetPreferences", MODE_WORLD_READABLE);
String verse = settings.getString("widgetVerse");
That should do it.
EDIT:
Instead of
#Override
public void onReceive(Context context, Intent intent) {
getVerseData = intent.getExtras();
verseFromFav = getVerseData.getString("widgetVerse");
super.onReceive(context, intent);
}
Try
#Override
public void onReceive(Context context, Intent intent) {
getVerseData = getIntent.getExtras();
verseFromFav = getVerseData.getString("widgetVerse");
super.onReceive(context, intent);
}