当前位置:   article > 正文

AppWidget启动流程部分 Launcher分析_startappwidgetconfigureactivityforresult怎么使用

startappwidgetconfigureactivityforresult怎么使用

基于源代码: Android2.2. Launcher.java.


操作流程:在Launcher界面长按,选择“窗口小部件”,弹出小部件选择框,选择自己的小部件,出现Configure Activity,点击OK,显示Widget.










启动流程:Laucher界面 

-------->Launcher.onLongClick() { ....           showAddDialog(cellInfo)               .......}  

Launcher.showAddDialog(){ .........  showDialog(DIALOG_CREATE_SHORTCUT); .........}  

 ----->Activity.showDialog(,){ .............     createDialog() ;   onPrepareDialog()    ........................}

------->Launcher.onCreateDialog(){ .....................    new CreateShortcut().createDialog();  .........................}   //显示 "添加到主屏幕" ; 

Launcher.onPrepareDialog(){ ....                                    .............} 

-------> Laucher.CreateShortcut.onClick() { ..........    

 case AddAdapter.ITEM_APPWIDGET: {
                    int appWidgetId = Launcher.this.mAppWidgetHost.allocateAppWidgetId(); //分配WIdgetId,不能自己设置

 Intent pickIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_PICK);
                    pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
                    // start the pick activity
                    startActivityForResult(pickIntent, REQUEST_PICK_APPWIDGET);//加载"选择窗口小部件"activity
                     break;   }

             ......}


       ---------> "选择窗口小部件"activity,选择了某一个AppWidget, onClick(),AppWidgetManager.bindAppWidgetId(),AppWidgetService.bindAppWidgetId()

appWidgetService.bindAppWidgetId(){  ...........

//发送Enable BroadCast,then AppWidgetProvider get broadcast ,执行onEnale();

//发送Update BroadCast,then AppWidgetProvider get broadcast ,执行onUpdate();

............

}


  ---------->Launcher.onActivityResult(){       .............     


  case REQUEST_PICK_APPWIDGET:
                    addAppWidget(data);
                    break;

      ..................  }


-------->Launcher.addAppWidget(){ ....................

//当需要Configure时,打开ConfigureActivity,同时将AppWidgetId传入

 if (appWidget.configure != null) {
            // Launch over to configure widget, if needed
            Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE);
            intent.setComponent(appWidget.configure);
            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
            startActivityForResultSafely(intent, REQUEST_CREATE_APPWIDGET);
    //

}else{

 // Otherwise just add it
            onActivityResult(REQUEST_CREATE_APPWIDGET, Activity.RESULT_OK, data);

}

.....................}


主要流程:

1.Createshortcut Dialog  [添加到主屏幕]  

2.AppWidgetPickActivity [ 选择小部件 ]

3.发送Broadcast

A> AppWidgetManager.sService 发送widgetID的ACTION_APPWIDGET_ENABLED【Sent when an instance of an AppWidget is added to a host for the first time】,widgetID的OnEnabled被called;

B>AppWidgetManager.sService 发送widgetID的ACTION_APPWIDGET_UPDATE,widgetID的OnUpdated被called;

C>返回Launcher, 发送ACTION_APPWIDGET_CONFIGURE,configureActivity将收到此广播,并打开自己的activity,对widget的参数进行配置【配置的参数采用SharedPreferences方式,widget跟host可以共享访问

4.添加Widget


另外: 

D>删除插件时,即在插件上长按,在拖动到垃圾桶里,会调用ACTION_APPWIDGET_DELETED【Sent when an instance of an AppWidget is deleted from its host】,这里可以做一些清除工作,比如清除配置信息,停止alarm等操作;

E>如果app只绑定了一个插件的话,则appwidgetmanager.sService会发送ACTION_APPWIDGET_DISABLED【Sent when an instance of an AppWidget is removed from the last host】,同样可以做一些清理的工作。

(2)下一次手机开机

由于插件已经绑定过了,并且configure过了,此时启动后,appwidgetmanager会执行A> B>,不会执行C>,所以第一次安装过程中,最好是对参数做一个合理的配置。不过一般来讲,widget上还有配置的选项,在运行后可以再次设置



源代码:

1.Launcher.OnLongClick

  1. public boolean onLongClick(View v) {
  2. switch (v.getId()) {
  3. case R.id.previous_screen:
  4. if (!isAllAppsVisible()) {
  5. mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
  6. HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
  7. showPreviews(v);
  8. }
  9. return true;
  10. case R.id.next_screen:
  11. if (!isAllAppsVisible()) {
  12. mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
  13. HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
  14. showPreviews(v);
  15. }
  16. return true;
  17. case R.id.all_apps_button:
  18. if (!isAllAppsVisible()) {
  19. mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
  20. HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
  21. showPreviews(v);
  22. }
  23. return true;
  24. }
  25. if (isWorkspaceLocked()) {
  26. return false;
  27. }
  28. if (!(v instanceof CellLayout)) {
  29. v = (View) v.getParent();
  30. }
  31. CellLayout.CellInfo cellInfo = (CellLayout.CellInfo) v.getTag();
  32. // This happens when long clicking an item with the dpad/trackball
  33. if (cellInfo == null) {
  34. return true;
  35. }
  36. if (mWorkspace.allowLongPress()) {
  37. if (cellInfo.cell == null) {
  38. if (cellInfo.valid) {
  39. // User long pressed on empty space
  40. mWorkspace.setAllowLongPress(false);
  41. mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
  42. HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
  43. showAddDialog(cellInfo);
  44. }
  45. } else {
  46. if (!(cellInfo.cell instanceof Folder)) {
  47. // User long pressed on an item
  48. mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
  49. HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
  50. mWorkspace.startDrag(cellInfo);
  51. }
  52. }
  53. }
  54. return true;
  55. }



2. Activity.showDialog();


  1. /**
  2. * Simple version of {@link #showDialog(int, Bundle)} that does not
  3. * take any arguments. Simply calls {@link #showDialog(int, Bundle)}
  4. * with null arguments.
  5. */
  6. public final void showDialog(int id) {
  7. showDialog(id, null);
  8. }
  9. /**
  10. * Show a dialog managed by this activity. A call to {@link #onCreateDialog(int, Bundle)}
  11. * will be made with the same id the first time this is called for a given
  12. * id. From thereafter, the dialog will be automatically saved and restored.
  13. *
  14. * <p>Each time a dialog is shown, {@link #onPrepareDialog(int, Dialog, Bundle)} will
  15. * be made to provide an opportunity to do any timely preparation.
  16. *
  17. * @param id The id of the managed dialog.
  18. * @param args Arguments to pass through to the dialog. These will be saved
  19. * and restored for you. Note that if the dialog is already created,
  20. * {@link #onCreateDialog(int, Bundle)} will not be called with the new
  21. * arguments but {@link #onPrepareDialog(int, Dialog, Bundle)} will be.
  22. * If you need to rebuild the dialog, call {@link #removeDialog(int)} first.
  23. * @return Returns true if the Dialog was created; false is returned if
  24. * it is not created because {@link #onCreateDialog(int, Bundle)} returns false.
  25. *
  26. * @see Dialog
  27. * @see #onCreateDialog(int, Bundle)
  28. * @see #onPrepareDialog(int, Dialog, Bundle)
  29. * @see #dismissDialog(int)
  30. * @see #removeDialog(int)
  31. */
  32. public final boolean showDialog(int id, Bundle args) {
  33. if (mManagedDialogs == null) {
  34. mManagedDialogs = new SparseArray<ManagedDialog>();
  35. }
  36. ManagedDialog md = mManagedDialogs.get(id);
  37. if (md == null) {
  38. md = new ManagedDialog();
  39. md.mDialog = createDialog(id, null, args);
  40. if (md.mDialog == null) {
  41. return false;
  42. }
  43. mManagedDialogs.put(id, md);
  44. }
  45. md.mArgs = args;
  46. onPrepareDialog(id, md.mDialog, args);
  47. md.mDialog.show();
  48. return true;
  49. }


3.CreateShortcut Dialog.其内容有mAddAdapter,就是"添加到主屏幕"的Dialog,点击"窗口小部件",将启动“选择窗口小部件”的  AppWidgetPickActivity。
  1. private class CreateShortcut implements DialogInterface.OnClickListener,
  2. DialogInterface.OnCancelListener, DialogInterface.OnDismissListener,
  3. DialogInterface.OnShowListener {
  4. private AddAdapter mAdapter;
  5. Dialog createDialog() {
  6. mAdapter = new AddAdapter(Launcher.this);
  7. final AlertDialog.Builder builder = new AlertDialog.Builder(Launcher.this);
  8. builder.setTitle(getString(R.string.menu_item_add_item));
  9. builder.setAdapter(mAdapter, this);
  10. builder.setInverseBackgroundForced(true);
  11. AlertDialog dialog = builder.create();
  12. dialog.setOnCancelListener(this);
  13. dialog.setOnDismissListener(this);
  14. dialog.setOnShowListener(this);
  15. return dialog;
  16. }
  17. public void onCancel(DialogInterface dialog) {
  18. mWaitingForResult = false;
  19. cleanup();
  20. }
  21. public void onDismiss(DialogInterface dialog) {
  22. }
  23. private void cleanup() {
  24. try {
  25. dismissDialog(DIALOG_CREATE_SHORTCUT);
  26. } catch (Exception e) {
  27. // An exception is thrown if the dialog is not visible, which is fine
  28. }
  29. }
  30. /**
  31. * Handle the action clicked in the "Add to home" dialog.
  32. */
  33. public void onClick(DialogInterface dialog, int which) {
  34. Resources res = getResources();
  35. cleanup();
  36. switch (which) {
  37. case AddAdapter.ITEM_SHORTCUT: {
  38. // Insert extra item to handle picking application
  39. pickShortcut();
  40. break;
  41. }
  42. case AddAdapter.ITEM_APPWIDGET: {
  43. int appWidgetId = Launcher.this.mAppWidgetHost.allocateAppWidgetId();
  44. Intent pickIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_PICK);
  45. pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
  46. // start the pick activity
  47. startActivityForResult(pickIntent, REQUEST_PICK_APPWIDGET);
  48. break;
  49. }
  50. case AddAdapter.ITEM_LIVE_FOLDER: {
  51. // Insert extra item to handle inserting folder
  52. Bundle bundle = new Bundle();
  53. ArrayList<String> shortcutNames = new ArrayList<String>();
  54. shortcutNames.add(res.getString(R.string.group_folder));
  55. bundle.putStringArrayList(Intent.EXTRA_SHORTCUT_NAME, shortcutNames);
  56. ArrayList<ShortcutIconResource> shortcutIcons =
  57. new ArrayList<ShortcutIconResource>();
  58. shortcutIcons.add(ShortcutIconResource.fromContext(Launcher.this,
  59. R.drawable.ic_launcher_folder));
  60. bundle.putParcelableArrayList(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, shortcutIcons);
  61. Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY);
  62. pickIntent.putExtra(Intent.EXTRA_INTENT,
  63. new Intent(LiveFolders.ACTION_CREATE_LIVE_FOLDER));
  64. pickIntent.putExtra(Intent.EXTRA_TITLE,
  65. getText(R.string.title_select_live_folder));
  66. pickIntent.putExtras(bundle);
  67. startActivityForResult(pickIntent, REQUEST_PICK_LIVE_FOLDER);
  68. break;
  69. }
  70. case AddAdapter.ITEM_WALLPAPER: {
  71. startWallpaper();
  72. break;
  73. }
  74. }
  75. }
  76. public void onShow(DialogInterface dialog) {
  77. mWaitingForResult = true;
  78. }
  79. }


4.在AppWidgetPickerActivity中,内容为List<PickAdapter.Item> items,存放的是所有的widget。点击onClick,intent.getExtras() == null的,执行

mAppWidgetManager.bindAppWidgetId(mAppWidgetId, intent.getComponent());


启动AppWidgetService,并sService.bindAppWidgetId(){. .. ..},并发送Enable broadcast, Update broadcast; 

 AppWidgetProvider onReceive() Broadcast,执行 onEnable(),onUpdate()


  1. public void onClick(DialogInterface dialog, int which) {
  2. Intent intent = getIntentForPosition(which);
  3. int result;
  4. if (intent.getExtras() != null) {
  5. // If there are any extras, it's because this entry is custom.
  6. // Don't try to bind it, just pass it back to the app.
  7. setResultData(RESULT_OK, intent);
  8. } else {
  9. try {
  10. mAppWidgetManager.bindAppWidgetId(mAppWidgetId, intent.getComponent());
  11. result = RESULT_OK;
  12. } catch (IllegalArgumentException e) {
  13. // This is thrown if they're already bound, or otherwise somehow
  14. // bogus. Set the result to canceled, and exit. The app *should*
  15. // clean up at this point. We could pass the error along, but
  16. // it's not clear that that's useful -- the widget will simply not
  17. // appear.
  18. result = RESULT_CANCELED;
  19. }
  20. setResultData(result, null);
  21. }
  22. finish();
  23. }

AppWidgetService.bindAppWidgetId

  1. public void bindAppWidgetId(int appWidgetId, ComponentName provider) {
  2. mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET,
  3. "bindGagetId appWidgetId=" + appWidgetId + " provider=" + provider);
  4. synchronized (mAppWidgetIds) {
  5. AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
  6. if (id == null) {
  7. throw new IllegalArgumentException("bad appWidgetId");
  8. }
  9. if (id.provider != null) {
  10. throw new IllegalArgumentException("appWidgetId " + appWidgetId + " already bound to "
  11. + id.provider.info.provider);
  12. }
  13. Provider p = lookupProviderLocked(provider);
  14. if (p == null) {
  15. throw new IllegalArgumentException("not a appwidget provider: " + provider);
  16. }
  17. if (p.zombie) {
  18. throw new IllegalArgumentException("can't bind to a 3rd party provider in"
  19. + " safe mode: " + provider);
  20. }
  21. id.provider = p;
  22. p.instances.add(id);
  23. int instancesSize = p.instances.size();
  24. if (instancesSize == 1) {
  25. // tell the provider that it's ready
  26. sendEnableIntentLocked(p);
  27. }
  28. // send an update now -- We need this update now, and just for this appWidgetId.
  29. // It's less critical when the next one happens, so when we schdule the next one,
  30. // we add updatePeriodMillis to its start time. That time will have some slop,
  31. // but that's okay.
  32. sendUpdateIntentLocked(p, new int[] { appWidgetId });
  33. // schedule the future updates
  34. registerForBroadcastsLocked(p, getAppWidgetIds(p));
  35. saveStateLocked();
  36. }
  37. }


5. 从"选择窗口小部件"Activity中返回选择的小部件REQUEST_PICK_APPWIDGET


  1. @Override
  2. protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  3. mWaitingForResult = false;
  4. // The pattern used here is that a user PICKs a specific application,
  5. // which, depending on the target, might need to CREATE the actual target.
  6. // For example, the user would PICK_SHORTCUT for "Music playlist", and we
  7. // launch over to the Music app to actually CREATE_SHORTCUT.
  8. if (resultCode == RESULT_OK && mAddItemCellInfo != null) {
  9. switch (requestCode) {
  10. case REQUEST_PICK_APPLICATION:
  11. completeAddApplication(this, data, mAddItemCellInfo);
  12. break;
  13. case REQUEST_PICK_SHORTCUT:
  14. processShortcut(data);
  15. break;
  16. case REQUEST_CREATE_SHORTCUT:
  17. completeAddShortcut(data, mAddItemCellInfo);
  18. break;
  19. case REQUEST_PICK_LIVE_FOLDER:
  20. addLiveFolder(data);
  21. break;
  22. case REQUEST_CREATE_LIVE_FOLDER:
  23. completeAddLiveFolder(data, mAddItemCellInfo);
  24. break;
  25. case REQUEST_PICK_APPWIDGET:
  26. addAppWidget(data);
  27. break;
  28. case REQUEST_CREATE_APPWIDGET:
  29. completeAddAppWidget(data, mAddItemCellInfo);
  30. break;
  31. case REQUEST_PICK_WALLPAPER:
  32. // We just wanted the activity result here so we can clear mWaitingForResult
  33. break;
  34. }
  35. } else if ((requestCode == REQUEST_PICK_APPWIDGET ||
  36. requestCode == REQUEST_CREATE_APPWIDGET) && resultCode == RESULT_CANCELED &&
  37. data != null) {
  38. // Clean up the appWidgetId if we canceled
  39. int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
  40. if (appWidgetId != -1) {
  41. mAppWidgetHost.deleteAppWidgetId(appWidgetId);
  42. }
  43. }
  44. }


6.添加Widget,当有ConfugureActivity的时候,就启动ConfigureActivity,否则直接加载AppWidget
  1. void addAppWidget(Intent data) {
  2. // TODO: catch bad widget exception when sent
  3. int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
  4. AppWidgetProviderInfo appWidget = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
  5. if (appWidget.configure != null) {
  6. // Launch over to configure widget, if needed
  7. Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE);
  8. intent.setComponent(appWidget.configure);
  9. intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
  10. startActivityForResultSafely(intent, REQUEST_CREATE_APPWIDGET);
  11. } else {
  12. // Otherwise just add it
  13. onActivityResult(REQUEST_CREATE_APPWIDGET, Activity.RESULT_OK, data);
  14. }
  15. }
7.当发送REQUEST_CREATE_APPWIDGET时,completeAddAppWidget()
  1. /**
  2. * Add a widget to the workspace.
  3. *
  4. * @param data The intent describing the appWidgetId.
  5. * @param cellInfo The position on screen where to create the widget.
  6. */
  7. private void completeAddAppWidget(Intent data, CellLayout.CellInfo cellInfo) {
  8. Bundle extras = data.getExtras();
  9. int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
  10. if (LOGD) Log.d(TAG, "dumping extras content=" + extras.toString());
  11. AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
  12. // Calculate the grid spans needed to fit this widget
  13. CellLayout layout = (CellLayout) mWorkspace.getChildAt(cellInfo.screen);
  14. int[] spans = layout.rectToCell(appWidgetInfo.minWidth, appWidgetInfo.minHeight);
  15. // Try finding open space on Launcher screen
  16. final int[] xy = mCellCoordinates;
  17. if (!findSlot(cellInfo, xy, spans[0], spans[1])) {
  18. if (appWidgetId != -1) mAppWidgetHost.deleteAppWidgetId(appWidgetId);
  19. return;
  20. }
  21. // Build Launcher-specific widget info and save to database
  22. LauncherAppWidgetInfo launcherInfo = new LauncherAppWidgetInfo(appWidgetId);
  23. launcherInfo.spanX = spans[0];
  24. launcherInfo.spanY = spans[1];
  25. LauncherModel.addItemToDatabase(this, launcherInfo,
  26. LauncherSettings.Favorites.CONTAINER_DESKTOP,
  27. mWorkspace.getCurrentScreen(), xy[0], xy[1], false);
  28. if (!mRestoring) {
  29. mDesktopItems.add(launcherInfo);
  30. // Perform actual inflation because we're live
  31. launcherInfo.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
  32. launcherInfo.hostView.setAppWidget(appWidgetId, appWidgetInfo);
  33. launcherInfo.hostView.setTag(launcherInfo);
  34. mWorkspace.addInCurrentScreen(launcherInfo.hostView, xy[0], xy[1],
  35. launcherInfo.spanX, launcherInfo.spanY, isWorkspaceLocked());
  36. }
  37. }



声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/313040
推荐阅读
相关标签
  

闽ICP备14008679号