Launch app from widget xamarin android

romkatzromkatz ILMember ✭✭

I want to add two functionalities to my widget. 1 - To launch application by clicking on widget. 2 - To add refresh button on order to update view on the widget which was built with custom rows and list view.

This is my widget file:

[BroadcastReceiver(Label = "@string/widget_name")]
[IntentFilter(new string[] { "android.appwidget.action.APPWIDGET_UPDATE" })]
[MetaData("android.appwidget.provider", Resource = "@xml/widget_word")]
public class WidgetProvider : AppWidgetProvider
{
    public override void OnUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
    {
        base.OnUpdate(context, appWidgetManager, appWidgetIds);

        int N = appWidgetIds.Length;

        for (int i = 0; i < N; ++i)
        {
            RemoteViews remoteViews = updateWidgetListView(context, appWidgetIds[i]);                   
            appWidgetManager.UpdateAppWidget(appWidgetIds[i], remoteViews);
        }

    }


    private RemoteViews updateWidgetListView(Context context, int appWidgetId)
    {
        RemoteViews header = new RemoteViews(context.PackageName, Resource.Layout.widget_layout);                
        RemoteViews remoteViews = new RemoteViews(context.PackageName, Resource.Layout.text_view_layout);
        remoteViews.AddView(Resource.Id.lay2, header);


        Intent svcIntent =  new Intent(context, typeof(WidgetService));
        svcIntent.SetPackage(context.PackageName);
        svcIntent.PutExtra(AppWidgetManager.ExtraAppwidgetId, appWidgetId);

        svcIntent.SetData(Android.Net.Uri.Parse(svcIntent.ToUri(Android.Content.IntentUriType.AndroidAppScheme)));
        remoteViews.SetViewPadding(Resource.Id.listViewWidget, 0, 90, 0, 0);
        remoteViews.SetRemoteAdapter(Resource.Id.listViewWidget, svcIntent);
        remoteViews.SetEmptyView(Resource.Id.listViewWidget, Resource.Id.empty_view);
        return remoteViews;
    }
}

Answers

  • D3nnisD3nnis USUniversity ✭✭✭
    edited January 2017

    I haven't touched this part in a long time, but I have it working. I have done it in this way. Change your WidgetProvider a bit and make a new class called UpdateMachine.
    The click event is based on a layout with a OnClick event in UpdateMachine (buildUpdate).

        updateViews = new RemoteViews (context.PackageName, Resource.Layout.LayoutToShow);
        updateViews.SetOnClickPendingIntent (Resource.Id.lessonRoot, configPendingIntent);
    

    WidgetProvider

        public override void OnUpdate (Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
        {
            context.StartService (new Intent (context, typeof (UpdateMachine)));
        }
    

    UpdateMachine

    [Service]
    public class UpdateMachine : Service
    {
        public static RemoteViews updateViews;
    
        public override StartCommandResult OnStartCommand (Intent intent, StartCommandFlags flags, int startId)     {
            // Build the widget update for today
            using (RemoteViews updateViews = buildUpdate (this)) {
                // Push update for this widget to the home screen
                ComponentName thisWidget = new ComponentName (this, Java.Lang.Class.FromType (typeof (WidgetProvider)).Name);
                AppWidgetManager manager = AppWidgetManager.GetInstance (this);
                manager.UpdateAppWidget (thisWidget, updateViews);
            }
    
            return StartCommandResult.Sticky;
        }
    
        public override IBinder OnBind (Intent intent)
        {
            return null;
        }
    
        public RemoteViews buildUpdate (Context context)
        {
            updateViews = new RemoteViews (context.PackageName, Resource.Layout.LayoutToShow);
    
            Intent configIntent = new Intent (context, typeof (MainActivity));
            PendingIntent configPendingIntent = PendingIntent.GetActivity (context, 0, configIntent, 0);
    
            //Click event for LinearLayout lessonRoot
            updateViews.SetOnClickPendingIntent (Resource.Id.lessonRoot, configPendingIntent);
    
            return updateViews;
        }
    }
    

    This takes care off the starting of the application through click.

    you can get data from local storage

    ISharedPreferences prefs = PreferenceManager.GetDefaultSharedPreferences(context);
    String data = prefs.GetString("data", "");
    

    You can set textViews this way:

    updateViews.SetTextViewText(Resource.Id.TextViewId, data);
    

    I haven't found a way yet to do this through web calls though.

  • romkatzromkatz ILMember ✭✭

    @D3nnis Thank you for your answer. Unfortunately your answer doesn't support list view implementation in widget. Maybe you have some examples with list view?

    This is my update service:

    [Service(Permission = "android.permission.BIND_REMOTEVIEWS", Exported = false)]
    public class WidgetService : RemoteViewsService
    {
        public override IRemoteViewsFactory OnGetViewFactory(Intent intent)
        {
    
            ListProvider lp = new ListProvider(this.ApplicationContext);
    
            return lp;
        }
    
    
    }
    

    and my List provider:

      namespace AppWidgetListView
     {
       public class ListProvider : Java.Lang.Object, RemoteViewsService.IRemoteViewsFactory
       {
           private List<ListItem> listItemList = new List<ListItem>();
           private Context context;
    
    
           public ListProvider(Context contextNew) 
    
           {
              context = contextNew;
    
               populateListItem();
          }
    
    
    
        private void populateListItem()
        {
            for (int i = 0; i < 10; i++)
            {
                ListItem listItem = new ListItem();
                listItem.heading = "Heading" + i;
                listItem.content = i + "This is the content of the app widget listview. Nice content though";
                listItemList.Add(listItem);
            }
        }
    
        public int Count { get { return listItemList.Count; } }
    
    
        public long GetItemId(int position)
        {
            return position;
        }
    
    
    
        public RemoteViews GetViewAt(int position)
        {
            RemoteViews remoteView = new RemoteViews(
             context.PackageName, Resource.Layout.list_row);
            ListItem listItem = listItemList[position];
            remoteView.SetTextViewText(Resource.Id.heading, listItem.heading);
            remoteView.SetTextViewText(Resource.Id.content, listItem.content);
    
            return remoteView;
    
        }
    
    
        public RemoteViews LoadingView { get { return null; } }
    
        public int ViewTypeCount { get { return 1; } }
    
        public bool HasStableIds { get { return true; } }
    
    
        public void OnCreate()
        {
           // throw new NotImplementedException();
        }
    
        public void OnDataSetChanged()
        {
           // throw new NotImplementedException();
        }
    
        public void OnDestroy()
        {
           // throw new NotImplementedException();
        }
    
    
    
    
    
    
    
    
    
    
    
    }
    

    }

  • D3nnisD3nnis USUniversity ✭✭✭

    Do you want to open the app in general when touching the widget, or do you want to have a different result for each object that you touch in the list.
    Can't you use something like this in your WidgetProvider:

        for (int i = 0; i < N; ++i)
        {
            RemoteViews remoteViews = updateWidgetListView(context, appWidgetIds[i]);                   
            appWidgetManager.UpdateAppWidget(appWidgetIds[i], remoteViews);
    
            //Open the right Activity when you click the Widget
            Intent configIntent = new Intent (context, typeof (MainActivity));
    
            //Add the appWidgetId in the Intent
            configIntent.PutExtra ("appWidgetId", appWidgetIds [i]);
            PendingIntent configPendingIntent = PendingIntent.GetActivity (context, 0, configIntent, 0);
            remoteViews.SetOnClickPendingIntent (Resource.Id.lessonRoot, configPendingIntent);
        }
    

    You also need to have a good OnStartCommand. If I don't have these lines, the click event doesn't work in any way.

        public override StartCommandResult OnStartCommand (Intent intent, StartCommandFlags flags, int startId)     {
            // Build the widget update for today
            using (RemoteViews updateViews = buildUpdate (this)) {
                // Push update for this widget to the home screen
                ComponentName thisWidget = new ComponentName (this, Java.Lang.Class.FromType (typeof (WidgetProvider)).Name);
                AppWidgetManager manager = AppWidgetManager.GetInstance (this);
                manager.UpdateAppWidget (thisWidget, updateViews);
            }
            return StartCommandResult.Sticky;
        }
    
  • D3nnisD3nnis USUniversity ✭✭✭

    Here is an example that uses the Hello World application and opens the app when you press the HELLO WORLD, CLICK ME! button. I tried to make it as small as possible.
    In this case it's using myButton. You can replace it with the whole ListView or with another object.

    updateViews.SetOnClickPendingIntent (Resource.Id.myButton, configPendingIntent);
    

    This is the whole code to make it open the app at a click event. Maybe you could make it work with your code.

    UpdateMachine.cs

    using Android.App;
    using Android.Appwidget;
    using Android.Content;
    using Android.OS;
    using Android.Widget;
    
    namespace widgetSimple
    {
        [Service]
        public class UpdateMachine : Service
        {
            public static RemoteViews updateViews;
    
            public override StartCommandResult OnStartCommand (Intent intent, StartCommandFlags flags, int startId)
            {
                // Build the widget update for today
                using (RemoteViews updateViews = buildUpdate (this)) {
                    // Push update for this widget to the home screen
                    ComponentName thisWidget = new ComponentName (this, Java.Lang.Class.FromType (typeof (WidgetExample)).Name);
                    AppWidgetManager manager = AppWidgetManager.GetInstance (this);
                    manager.UpdateAppWidget (thisWidget, updateViews);
                }
                return StartCommandResult.Sticky;
            }
    
            public override IBinder OnBind (Intent intent)
            {
                return null;
            }
    
            public RemoteViews buildUpdate (Context context)
            {
                updateViews = new RemoteViews (context.PackageName, Resource.Layout.Main);
    
                Intent configIntent = new Intent (context, typeof (MainActivity));
                PendingIntent configPendingIntent = PendingIntent.GetActivity (context, 0, configIntent, 0);
                updateViews.SetOnClickPendingIntent (Resource.Id.myButton, configPendingIntent);
    
                return updateViews;
            }
        }
    }
    

    WidgetExample.cs:

    using Android.App;
    using Android.Appwidget;
    using Android.Content;
    
    namespace widgetSimple
    {
        [BroadcastReceiver (Label = "WidgetExample", Enabled = true)]
        [IntentFilter (new string [] { "android.appwidget.action.APPWIDGET_UPDATE" })]
        [MetaData ("android.appwidget.provider", Resource = "@drawable/Widget")]
        public class QuoteMachine : AppWidgetProvider
        {
            public override void OnUpdate (Context context, AppWidgetManager appWidgetManager, int [] appWidgetIds)
            {
                context.StartService (new Intent (context, typeof (UpdateMachine)));
            }
        }
    }
    

    Widget.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
        android:minWidth="294dip"
        android:minHeight="72dip"
        android:updatePeriodMillis="3000000"
        android:initialLayout="@layout/Main" />
    
  • romkatzromkatz ILMember ✭✭

    Your examples don't work with RemoteViewsService and List Provider. I don't know where should I implement this code in my example:

    public override StartCommandResult OnStartCommand (Intent intent, StartCommandFlags flags, int startId)     {
         // Build the widget update for today
         using (RemoteViews updateViews = buildUpdate (this)) {
             // Push update for this widget to the home screen
             ComponentName thisWidget = new ComponentName (this, Java.Lang.Class.FromType (typeof      (WidgetProvider)).Name);
             AppWidgetManager manager = AppWidgetManager.GetInstance (this);
             manager.UpdateAppWidget (thisWidget, updateViews);
         }
         return StartCommandResult.Sticky;
     }
    

    I don't have buildUpdate function and List Provider extends Java.Lang.Class not component name.

  • D3nnisD3nnis USUniversity ✭✭✭

    I'm sorry, I can't help you further than this on this subject. I only used Service and not RemoteViewsService.
    Maybe you could use the code in OnGetViewFactory or an other override function.

Sign In or Register to comment.