0

I created a command prompt widget for android, it's my first time working with Xamarin, and Android development, and it's been rough. Everything works as intended except the widget stops working after awhile when left on the home screen; this also happens occasionally when I update the code in my IDE, and install a new debug version on my emulator/physical device.

You're able to see the widget, but none of the buttons work and the ListView is not populated with anything. I set the update period to 24 hours, and I build everything in a RemoteViews function. I'm thinking maybe that the pending intents get reset somehow, but I'm not certain the mechanism in how that works. The project is open source in a git-hub, and I will link it down below. Appreciate any help on this, and any advice in general on how to improve this widget.

https://github.com/chrisz99/CommAndroid

[BroadcastReceiver(Label = "CommAndroid", Exported = false, Enabled = true, Name = "com.company.terminalapp.WidgetProvider",Icon ="@mipmap/terminalappicon")]
[IntentFilter(new string[] { "android.appwidget.action.APPWIDGET_UPDATE" })]
[MetaData("android.appwidget.provider", Resource = "@xml/my_widget")]


//Class to inflate and show the widget
public class WidgetProvider : AppWidgetProvider
{
    //Initialize Global Vars for Class
    public static ListViewFactory listViewFactory;
    private Handler handler;


    //Update method for a widget
    public override void OnUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
    {
        base.OnUpdate(context, appWidgetManager, appWidgetIds);

        foreach (int appwidgetId in appWidgetIds)
        {
            // Build the remote views for the widget
            var widgetView = BuildRemoteViews(context, appwidgetId);

            


            // Update all instances of the widget with the new remote views
            appWidgetManager.UpdateAppWidget(appwidgetId, widgetView);

        }



    }


    //Build the widget view, returns to the update method
    public RemoteViews BuildRemoteViews(Context context, int appWidgetIds)
    {
        //Initialize our widget view, basically pointing to our main widget layout xml
        var widgetView = new RemoteViews(context.PackageName, Resource.Layout.widget_layout);
        var listView = new RemoteViews(context.PackageName, Resource.Layout.listview_layout);

        

        //Initializing an intent of our WidgetRemoteViewService, to handle the creation of our list view factory
        //Instructing Widget to use our WidgetRemoteViewService with our ListView
        Intent listViewIntent = new Intent(context, typeof(WidgetRemoteViewService));
        widgetView.SetRemoteAdapter(Resource.Id.listView1, listViewIntent);


        //Initializing an intent for the main widget button click
        var clickIntent = new Intent(context, typeof(WidgetProvider));
        clickIntent.SetAction("com.company.terminalapp.WIDGET_BUTTON_CLICK");

        // Initializing a new Intent for the delete button click action
        var deleteIntent = new Intent(context, typeof(WidgetProvider));
        deleteIntent.SetAction("com.company.terminalapp.DELETE_BUTTON_CLICK");


        // Initializing pending intents for the button click actions
        var pendingIntent = PendingIntent.GetBroadcast(context, 5555533, clickIntent, PendingIntentFlags.Immutable);
        var pendingDeleteIntent = PendingIntent.GetBroadcast(context, 55522111, deleteIntent, PendingIntentFlags.Immutable);

        // Associating the pending intents with the respective buttons in the widget layout
        widgetView.SetOnClickPendingIntent(Resource.Id.delete_button, pendingDeleteIntent);
        widgetView.SetOnClickPendingIntent(Resource.Id.widget_button, pendingIntent);




        // ...

        return widgetView;
    }

    //On Deleted
    public override void OnDeleted(Context context, int[] appWidgetIds)
    {
        base.OnDeleted(context, appWidgetIds);

    }

    //Method to update the listview in widget
    private async void updateListView(Context context, RemoteViews views, bool isDelete, string command)
    {
        //Create a new list factory and view for our widget
        //Create an AppWidgetManager, reference component name, and get appWidgetIds
        listViewFactory = new ListViewFactory(context);
        var widgetView = new RemoteViews(context.PackageName, Resource.Layout.widget_layout);
        AppWidgetManager appWidgetManager = AppWidgetManager.GetInstance(context);
        ComponentName componentName = new ComponentName(context, Java.Lang.Class.FromType(typeof(WidgetProvider)).Name);
        int[] appWidgetIds = appWidgetManager.GetAppWidgetIds(componentName);

        //Checks bool parameter to see if user wants to clear list
        if (isDelete != true)
        {
            //Initialize string result that is returned from querying the command through class TerminalCommands
            //Add the command itself, and the results to the list
            string result = await TerminalCommands.queryCommand(command, context, appWidgetManager, appWidgetIds);
            listViewFactory.addCommand("CMD: " + command, result);

            //Create Handler to facilitate Self Scrolling of Listview
            //Set Scroll position, partially update app widget
            //Don't know why this works instead of just setting scroll position, but hey it's android development
            handler = new Handler(Looper.MainLooper);
            handler.PostDelayed(async() =>
            {
                   
                widgetView.SetScrollPosition(Resource.Id.listView1, ListViewFactory.items.Count - 1);
                appWidgetManager.PartiallyUpdateAppWidget(appWidgetIds, widgetView);

            }, 500);
      


        }
        //Bool parameter for user clearing list
        else
        {
            listViewFactory.clearList();
            widgetView.SetEmptyView(Resource.Id.listView1, Resource.Id.empty_view);

        }

        //Update the widget view
        appWidgetManager.NotifyAppWidgetViewDataChanged(appWidgetIds, Resource.Id.listView1);
        appWidgetManager.UpdateAppWidget(componentName, views);


    }



    //Method for receiving broadcasts from broadcast receiver
    public override void OnReceive(Context context, Intent intent)
    {
        //Initialize view for our widget layout
        base.OnReceive(context, intent);
        var widgetView = new RemoteViews(context.PackageName, Resource.Layout.widget_layout);

        //Command Click
        if (intent.Action == "com.company.terminalapp.WIDGET_BUTTON_CLICK")
        {
            //Create a new intent of activity InputProvider
            //Start the input provider activity
            Intent inputIntent = new Intent(Application.Context, typeof(InputProvider));
            inputIntent.AddFlags(ActivityFlags.NewTask);
            context.StartActivity(inputIntent);


        }
        //Clear Click
        else if (intent.Action == "com.company.terminalapp.DELETE_BUTTON_CLICK")
        {
            updateListView(context, widgetView, true, "");
        }
        //User input
        else if (intent.Action == "com.company.terminalapp.USER_INPUT_SUBMITTED")
        {
            string command = intent.GetStringExtra("user_input");
            updateListView(context, widgetView, false, command);
        }



    }

}

}

Lions L
  • 11
  • 3

0 Answers0