AppWidgetHost tutorial

Tutorials with advanced 'difficulty' and more Lines of Code.

AppWidgetHost tutorial

Postby i4nc4mp » Fri Jan 15, 2010 4:41 am

Greetings-


I am about to dive in to learning how to implement AppWidgetHost for a lockscreen replacement customizable with widgets. I've seen this done on a few other apps out there but the process doesn't seem very well documented. I cannot seem to find any tutorial or resource besides the limited information in the developer.android.com site. I am attempting to search the stock Launcher source code to construct a tutorial so that others can learn from these efforts. Has anyone seen a reference or tutorial guide to placing appwidgethost in your own activity? It seems the only other use for this would be for devs working on custom home replacements. I also wish to emulate shortcuts & app launch icons but first I am focusing on implementing widget host.
i4nc4mp
Developer
Developer
 
Posts: 39
Joined: Sat Dec 12, 2009 6:44 pm

Top

Postby i4nc4mp » Fri Jan 15, 2010 10:52 pm

Here's what I've learned so far. This code is letting me put widgets in, they aren't nicely spaced or padded yet and I am working on adding size check so i can properly fill up a row then push the next requested widget to the next row. i also need to implement a way that the state is saved as persistent. it needs to remember what widgets have been added. then all that's left is more advanced fine tuning like repositioning & deleting or at the least a simple clear function.

Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1.  
  2. public class Lockscreen extends Activity {
  3.  
  4. //for testing we have set this up as a Launcher/Main icon
  5.  
  6. //for implementation we will make it subclass LockActivity
  7.  
  8.        
  9.  
  10.        
  11.  
  12. private AppWidgetManager mAppWidgetManager;
  13.  
  14. private AppWidgetHost mAppWidgetHost;
  15.  
  16.  
  17.  
  18. //private LayoutInflater mInflater;
  19.  
  20.  
  21.  
  22. static final int APPWIDGET_HOST_ID = 2037;
  23.  
  24. //this int identifies you. The Launcher's ID is 1024.
  25.  
  26. //If you were to implement different hosts that need to be distinguished
  27.  
  28. //then the ID is a shortcut for passing what you're doing to the correct one
  29.  
  30.  
  31.  
  32.  
  33.  
  34. private static final int REQUEST_CREATE_APPWIDGET = 5;
  35.  
  36. private static final int REQUEST_PICK_APPWIDGET = 9;
  37.  
  38.  
  39.  
  40. private AppWidgetHostView widgets[] = new AppWidgetHostView[16];
  41.  
  42. private int widgCount = 0;
  43.  
  44. //up to 16 widgets could be made, we have to have our reference be 16 items long
  45.  
  46. //we also need to know how many the actual user has added so we can reference them in the array
  47.  
  48.  
  49.  
  50.  
  51.  
  52. //the mediator service needs to actually maintain the created widget references since the activity
  53.  
  54. //is destroyed and recreated. this way they don't need to be spawned every time
  55.  
  56.  
  57.  
  58.  
  59.  
  60. @Override
  61.  
  62. protected void onCreate(Bundle savedInstanceState) {
  63.  
  64.     super.onCreate(savedInstanceState);
  65.  
  66.  
  67.  
  68.     requestWindowFeature(android.view.Window.FEATURE_NO_TITLE);
  69.  
  70.    
  71.  
  72.     //widgets = (FrameLayout)findViewById(R.id.widgets);
  73.  
  74.  
  75.  
  76.  
  77.  
  78.     updateLayout();
  79.  
  80.    
  81.  
  82.     //mInflater = getLayoutInflater();
  83.  
  84.    
  85.  
  86.     mAppWidgetManager = AppWidgetManager.getInstance(this);
  87.  
  88.  
  89.  
  90.     mAppWidgetHost = new AppWidgetHost(this, APPWIDGET_HOST_ID);
  91.  
  92.     mAppWidgetHost.startListening();
  93.  
  94.  
  95.  
  96. }
  97.  
  98.  
  99.  
  100. //from LockActivity
  101.  
  102. private void updateLayout() {
  103.  
  104.     LayoutInflater inflater = LayoutInflater.from(this);
  105.  
  106.  
  107.  
  108.     setContentView(inflateView(inflater));
  109.  
  110. }
  111.  
  112.  
  113.  
  114. //override is for when we subclass LockActivity for purpose pass the blank slate layout
  115.  
  116. //@Override
  117.  
  118. protected View inflateView(LayoutInflater inflater) {
  119.  
  120.        
  121.  
  122.  
  123.  
  124.         return inflater.inflate(R.layout.mylockscreen, null);
  125.  
  126. }
  127.  
  128.  
  129.  
  130.  
  131.  
  132. public boolean onCreateOptionsMenu(Menu menu) {
  133.  
  134.     menu.add(0, 1, 0, "Add Widget");
  135.  
  136.     return true;
  137.  
  138. }
  139.  
  140.  
  141.  
  142. /* Handles item selections */
  143.  
  144. public boolean onOptionsItemSelected(MenuItem item) {
  145.  
  146.     switch (item.getItemId()) {
  147.  
  148.     case 1:
  149.  
  150.         doWidgetPick();
  151.  
  152.         return true;
  153.  
  154.     }
  155.  
  156.     return false;
  157.  
  158. }
  159.  
  160.  
  161.  
  162. //here's where it gets really fun. we're going to launch the widget pick-list intent
  163.  
  164.  
  165.  
  166. //there is a bug with this intent/process where they have made it dependent on defining a custom extra widget.
  167.  
  168. //The "search" widget is not coded as an AppWidgetProvider for some reason
  169.  
  170. //they insert it into the list when pick intent is called, and if you don't insert a custom item
  171.  
  172. //you get a null pointer exception when trying to start
  173.  
  174. protected void doWidgetPick() {
  175.  
  176.         int appWidgetId = WidgetScreen.this.mAppWidgetHost.allocateAppWidgetId();
  177.  
  178.  
  179.  
  180.     Intent pickIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_PICK);
  181.  
  182.     pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
  183.  
  184.    
  185.  
  186.     /* custom extra that has to be there or else NPE will happen due to android bug   */
  187.  
  188.   //this is pulled from the Launcher source, I just changed a few things as it's just a dummy entry at this point
  189.  
  190.     ArrayList<AppWidgetProviderInfo> customInfo =
  191.  
  192.             new ArrayList<AppWidgetProviderInfo>();
  193.  
  194.     AppWidgetProviderInfo info = new AppWidgetProviderInfo();
  195.  
  196.     info.provider = new ComponentName(getPackageName(), "XXX.YYY");
  197.  
  198.     info.label = "i love android";
  199.  
  200.     info.icon = R.drawable.icon;
  201.  
  202.     customInfo.add(info);
  203.  
  204.     pickIntent.putParcelableArrayListExtra(
  205.  
  206.             AppWidgetManager.EXTRA_CUSTOM_INFO, customInfo);
  207.  
  208.     ArrayList<Bundle> customExtras = new ArrayList<Bundle>();
  209.  
  210.     Bundle b = new Bundle();
  211.  
  212.     b.putString("custom_widget", "search_widget");
  213.  
  214.     customExtras.add(b);
  215.  
  216.     pickIntent.putParcelableArrayListExtra(
  217.  
  218.             AppWidgetManager.EXTRA_CUSTOM_EXTRAS, customExtras);
  219.  
  220.     /* that's a lot of lines that are there for no function at all */
  221.  
  222.        
  223.  
  224.    
  225.  
  226.     // start the pick activity
  227.  
  228.     startActivityForResult(pickIntent, REQUEST_PICK_APPWIDGET);
  229.  
  230.     //because we've defined ourselves as a singleTask activity, it will allow this intent to be part of the task
  231.  
  232.  
  233.  
  234. }
  235.  
  236.  
  237.  
  238.  
  239.  
  240. @Override
  241.  
  242. protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  243.  
  244.     Log.v("result",requestCode + ", " + resultCode + ", " + data);
  245.  
  246.   //when the user completes the pick process, both these cases come back, according to log.
  247.  
  248.   //looks like this is happening because Pick does a check to see if a config needs to be launched first
  249.  
  250.   //if not it just sends the create intent
  251.  
  252.    
  253.  
  254.    
  255.  
  256.     if (resultCode == RESULT_OK) {
  257.  
  258.         switch (requestCode) {
  259.  
  260.                 case REQUEST_PICK_APPWIDGET:
  261.  
  262.                         addAppWidget(data);
  263.  
  264.                         break;
  265.  
  266.                 case REQUEST_CREATE_APPWIDGET:
  267.  
  268.                 completeAddAppWidget(data);
  269.  
  270.                 break;
  271.  
  272.         }
  273.  
  274.     }
  275.  
  276.     else if ((requestCode == REQUEST_PICK_APPWIDGET ||
  277.  
  278.             requestCode == REQUEST_CREATE_APPWIDGET) && resultCode == RESULT_CANCELED &&
  279.  
  280.             data != null) {
  281.  
  282.         // Clean up the appWidgetId if we canceled
  283.  
  284.         int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
  285.  
  286.         if (appWidgetId != -1) {
  287.  
  288.             mAppWidgetHost.deleteAppWidgetId(appWidgetId);
  289.  
  290.         }
  291.  
  292.     }
  293.  
  294. }
  295.  
  296.  
  297.  
  298.  
  299.  
  300. private void completeAddAppWidget(Intent data){
  301.  
  302.         //actually creates the view for the widget
  303.  
  304.  
  305.  
  306.     Bundle extras = data.getExtras();
  307.  
  308.     int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
  309.  
  310.  
  311.  
  312.     AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
  313.  
  314.    
  315.  
  316.  
  317.  
  318.     /* Launcher would calculate the grid spans needed to fit this widget
  319.  
  320.      * It would also do a check operation to abort if the cell user picked wasn't acceptable
  321.  
  322.      * given the size of the widget they chose
  323.  
  324.      */
  325.  
  326.    
  327.  
  328.     //What we'll do is log the info about the widget for help in letting user reposition it
  329.  
  330.    
  331.  
  332.     int width = appWidgetInfo.minWidth;
  333.  
  334.     int height = appWidgetInfo.minHeight;
  335.  
  336.    
  337.  
  338. /*
  339.  
  340. next we need to make a record of where we are adding this widget
  341.  
  342. what the launcher is doing is spawning a helper object where it saves details about the widget
  343.  
  344. it saves the number of cells wide and tall the widget is
  345.  
  346. it adds the spawned object to the array list for widgetinfos
  347.  
  348. the array list is a member of LauncherInfo helper object
  349.  
  350. the model seems to retain the references to everything that's been placed on the Launcher
  351.  
  352. */
  353.  
  354.     //we can get a reference to our main view here, and then add a relative layout to it.
  355.  
  356.     //I can probably directly reference the relative layout I want and then add widgets filling in from the top
  357.  
  358.     //just need to figure out how to determine if the widget being selected is too long to fit on existing row
  359.  
  360.     //to decide whether to place it on right of last widget or on the bottom
  361.  
  362.     RelativeLayout parent= (RelativeLayout) findViewById(R.id.mylockscreen);
  363.  
  364.    
  365.  
  366.     //Log.v("getting parent ref","the ID of the parent is " + parent.getId());
  367.  
  368.  
  369.  
  370.     //AppWidgetHostView newWidget = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
  371.  
  372.  
  373.  
  374.     //we need to store this widget in an array. the views can be recreated but we need to have a persistent ref
  375.  
  376.     //FIXME currently we aren't persistent, need to learn how to make activity save persistent state
  377.  
  378.  
  379.  
  380.     widgets[widgCount] = attachWidget(mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo), width, height);
  381.  
  382.    
  383.  
  384.    
  385.  
  386.     parent.addView(widgets[widgCount]);
  387.  
  388.    
  389.  
  390.    
  391.  
  392.     Log.v("widget was added","the ID of the widget view is " + widgets[widgCount].getId());
  393.  
  394.    
  395.  
  396.     widgCount++;
  397.  
  398.    
  399.  
  400.    
  401.  
  402.    
  403.  
  404.    
  405.  
  406.    
  407.  
  408.         //launcher is doing something to pass this view to their the workspace or the celllayout
  409.  
  410.        
  411.  
  412.         //so every single widget that gets created is one instance of the AppWidgetHostView.
  413.  
  414.         //the viewgroup we would have to maintain holds all the appwidgethostviews/
  415.  
  416. }
  417.  
  418.  
  419.  
  420.  
  421.  
  422. //the created new widget is passed raw with the data about its size, then we figure out how to position it
  423.  
  424. private AppWidgetHostView attachWidget(AppWidgetHostView widget, int w, int h){
  425.  
  426.          
  427.  
  428.     RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams
  429.  
  430.     (LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
  431.  
  432.     if (widgCount == 0) params.addRule(RelativeLayout.ALIGN_PARENT_TOP);
  433.  
  434.     //first widget goes at the top of the relative view widget area
  435.  
  436.     else params.addRule(RelativeLayout.RIGHT_OF, widgets[widgCount-1].getId());
  437.  
  438.      
  439.  
  440.     widget.setLayoutParams(params);
  441.  
  442.    
  443.  
  444.     widget.setId(100+widgCount);
  445.  
  446.     return widget;
  447.  
  448.     }
  449.  
  450.  
  451.  
  452. public AppWidgetHost getAppWidgetHost() {
  453.  
  454.     return mAppWidgetHost;
  455.  
  456. }
  457.  
  458.  
  459.  
  460.  
  461.  
  462.  
  463.  
  464.  
  465.  
  466. void addAppWidget(Intent data) {
  467.  
  468.     // TODO: catch bad widget exception when sent
  469.  
  470.     int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
  471.  
  472.  
  473.  
  474.     String customWidget = data.getStringExtra("custom_widget");
  475.  
  476.     if ("search_widget".equals(customWidget)) {//user picked the extra
  477.  
  478.         // We don't need this any more, since this isn't a real app widget.
  479.  
  480.         mAppWidgetHost.deleteAppWidgetId(appWidgetId);
  481.  
  482.         //scold user for disobedience
  483.  
  484.     } else {
  485.  
  486.         AppWidgetProviderInfo appWidget = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
  487.  
  488.  
  489.  
  490.         if (appWidget.configure != null) {
  491.  
  492.             // Launch over to configure widget, if needed
  493.  
  494.             Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE);
  495.  
  496.             intent.setComponent(appWidget.configure);
  497.  
  498.             intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
  499.  
  500.  
  501.  
  502.             startActivityForResult(intent, REQUEST_CREATE_APPWIDGET);
  503.  
  504.         } else {
  505.  
  506.             // Otherwise just add it
  507.  
  508.             onActivityResult(REQUEST_CREATE_APPWIDGET, Activity.RESULT_OK, data);
  509.  
  510.         }
  511.  
  512.     }
  513.  
  514. }
  515.  
  516.  
  517.  
  518. /**
  519.  
  520.  * Re-listen when widgets are reset.
  521.  
  522.  */
  523.  
  524. private void onAppWidgetReset() {
  525.  
  526.     mAppWidgetHost.startListening();
  527.  
  528. }
  529.  
  530.  
  531.  
  532. @Override
  533.  
  534. public void onDestroy() {
  535.  
  536.     //mDestroyed = true;
  537.  
  538.  
  539.  
  540.     super.onDestroy();
  541.  
  542.  
  543.  
  544.     try {
  545.  
  546.         mAppWidgetHost.stopListening();
  547.  
  548.     } catch (NullPointerException ex) {
  549.  
  550.         Log.w("lockscreen destroy", "problem while stopping AppWidgetHost during Lockscreen destruction", ex);
  551.  
  552.     }
  553.  
  554. }
  555.  
  556. }
  557.  
  558.  
Parsed in 0.060 seconds, using GeSHi 1.0.8.4


Syntax: [ Download ] [ Hide ]
Using xml Syntax Highlighting
  1. <?xml version="1.0" encoding="utf-8"?>
  2.  
  3. <RelativeLayout
  4.  
  5.    xmlns:android="http://schemas.android.com/apk/res/android"
  6.  
  7.    android:layout_width="fill_parent"
  8.  
  9.    android:layout_height="fill_parent"
  10.  
  11.    android:id="@+id/mylockscreen"
  12.  
  13. >
  14.  
  15. </RelativeLayout>
  16.  
  17.  
Parsed in 0.001 seconds, using GeSHi 1.0.8.4
Last edited by i4nc4mp on Thu Feb 18, 2010 8:02 am, edited 1 time in total.
i4nc4mp
Developer
Developer
 
Posts: 39
Joined: Sat Dec 12, 2009 6:44 pm

Postby i4nc4mp » Sat Jan 16, 2010 7:15 pm

removed--- new progress updated in main code
Last edited by i4nc4mp on Thu Feb 18, 2010 8:05 am, edited 1 time in total.
i4nc4mp
Developer
Developer
 
Posts: 39
Joined: Sat Dec 12, 2009 6:44 pm

Postby i4nc4mp » Sat Jan 23, 2010 3:15 am

removed--- speculation made obsolete by tested code
Last edited by i4nc4mp on Thu Feb 18, 2010 8:06 am, edited 1 time in total.
i4nc4mp
Developer
Developer
 
Posts: 39
Joined: Sat Dec 12, 2009 6:44 pm

Postby i4nc4mp » Mon Feb 15, 2010 7:43 am

so i think what i really need to make this work is a relative layout. this way, the base layout is relative, or i can place a relative somewhere inside one of my other lockscreens (for example to provide a 4x2 widget space at the bottom of lockscreen). then i can use that relative as a parent, and add the widget views along with location rules as i determine fit of the widget user is selecting.

Code that proves this is a good idea is now above in the tutorial example code. not quite finished yet. but it is much better than the proof of concept we had had so far.



----------------
For persistence, I learned that all you need to do is log in an array the actual AppWidgetManager ID that gets assigned automatically when user completes the pick activity. your code that does the add view also gets access to this ID. You can save those in a data base and then re-add the views just by supplying the ID to the create function.
So the on-create loads the database and checks if it has items, if so it runs the add for each one in the index. I'll post the code once I get it all running. Have not had time.
i4nc4mp
Developer
Developer
 
Posts: 39
Joined: Sat Dec 12, 2009 6:44 pm

Re: AppWidgetHost tutorial

Postby richardlalancette » Wed Jul 20, 2011 8:03 pm

Thanks for this article, got me started on the subject :)
Question now:
Where does this WidgetScreen come from?

int appWidgetId = WidgetScreen.this.mAppWidgetHost.allocateAppWidgetId();
richardlalancette
Freshman
Freshman
 
Posts: 2
Joined: Wed Jul 20, 2011 8:01 pm

Top

Re: AppWidgetHost tutorial

Postby richardlalancette » Mon Aug 15, 2011 7:03 pm

i4nc4mp,

Would it be possible to store the widget so it can be restored upon re-launching my app without going through the pickwidget activity?
richardlalancette
Freshman
Freshman
 
Posts: 2
Joined: Wed Jul 20, 2011 8:01 pm

Re: AppWidgetHost tutorial

Postby kaka » Fri Jan 13, 2012 9:29 am

that has photos of the actual watches that they offer.Do not put any trust in so-called "replica review websites". Most, if not all, are run by swiss replica watches watches at affordable rates from an online department store. There are numerous online stores that shop-girl Blancpain replicas, but you have to avoid chanel replica some highly developed ambit of preference. The firm might consider all of the versions at screen, speed up its hand on the amplitude supplied and swiss replica watches rebellious it is and the radiance test.If a the human race or a concubine is convenient plenty to own a Porsche pile, most suitable can afford a Discount Watches good carbon copy Breitling that were made specifically after the prototype model, and you can comprehend these representing a logical scarcely any hundred Discount Watches expertise. What is additional, it isn’t pivotal in the service of you yourselves to extension the thoughts on its safety at any stretch and any rank. Even rolex replica These watches are abnormally produced the foretoken of the bodies with their acclimatization admired annual starter Breitling models. These replica watches watches most renowned keep a weather eye open for manufacturers from around the world. These include: Rolex, Breitling, Bulgari, Cartier, Chanel, replica watches On the other hand, designer watches are watches more exciting because of their color styles in the world today. Glass and as plain as the nose on ones Replica bags if there is the for all that as expected from the genuine love. Despite the duplication of set someone back that not a unsatisfactory fraction sapphire crystal is fixed to protect the petite creature. Furthermore, the women’s watch is set with a crown with gaskets and a screw-down case-back. replica watch of bleue days in every nook the year such as festivals, birthdays and all sorts of other anniversaries. And the tradition of giving has not stopped and hermes handbags to own splendid self-indulgence timepieces but are above all restricted nigh the huge costs, they’re indeed supreme alternatives. Quality replicas are the replica watches us Since 1953, these watches deliver befit a limited share in of conventional appurtenances to go to military divers. These timepieces are the highest Discount Watches of both worlds. They do destitution to deterioration some of the first timepieces for ever crafted and in time to come they do not destitution to a score the swiss replica watches distinctive and interesting. A few of the classic natural leather band involving TechnoMarine wrist band may include any Hummer tie assortment not replica watches valve device, and the amplification of the 6, 9 and twelve o'clock metal Arabia digital display, 12 time silver convex cubic column, and rod glass beads daily life, the watches are loved by both people of all ages. Different people prefer different styles. All dressy watches, sporty watches and Discount Watches
kaka
Developer
Developer
 
Posts: 44
Joined: Wed Nov 23, 2011 9:24 am

Re: AppWidgetHost tutorial

Postby Zolbat » Fri Mar 30, 2012 11:42 pm

First of all, thank you, this is the only source of a widget setup on the internet, that my small mind could comprehend.
I am currently developing a launcher myself and I'd like to ask if I could use some of your code in there.
thank you very much!
Zolbat
Once Poster
Once Poster
 
Posts: 1
Joined: Fri Mar 30, 2012 11:36 pm

Re: AppWidgetHost tutorial

Postby anniedulce » Sat Mar 31, 2012 12:28 pm

WidgetScreen is nothing but the screen size of the software width of the coding that is depends on Device
Rizecorp Android Development, Android Mobile Apps
anniedulce
Experienced Developer
Experienced Developer
 
Posts: 84
Joined: Mon Mar 05, 2012 12:28 pm

Re: AppWidgetHost tutorial

Postby jharrisweinberg » Sat May 12, 2012 9:34 pm

Thanks a lot for this - it is very helpful! I have a couple questions that I was wondering about... first, has anyone figured out how to persist the widgets and their configurations? Second, the widget I attached in my app shows up properly, but it doesn't update its view when it is touched/clicked - any idea how to do that? I am hoping I don't need to use the BIND_APPWIDGET permission!
jharrisweinberg
Once Poster
Once Poster
 
Posts: 1
Joined: Sat May 12, 2012 9:27 pm

Top

Return to Advanced Tutorials

Who is online

Users browsing this forum: No registered users and 6 guests