Android 在主屏上生成快捷方式

任务

我最近在做一个项目,客户要求我们的系统自带他们指定的apk,并且是可以卸载的。这没有什么难得。
还有一个要求就是要在主屏幕上有他们apk的快捷图标。

apk安装问题

首先我们要处理安装的问题,要可以卸载肯定不能放到 /system/app/ 目录下。我们可以把某个脚本作为一个服务启动。那就需要在 xxx.rc 增加服务。这样开机就会跑这个脚本。注意pm的使用。开机就跑这个脚本。那时候pm可能还没有准备好。注意检测pm的返回值。如果返回值是空的。要用while等待。
我们使用的是mtk的方案,mtk改造过android ,在系统启动时会安装 /data/app/ 目录下的apk文件。如果你在打包系统的时候就把apk文件放到这个目录,没有问题。如果你在我上面说的那个启动脚本中cp。那就要注意cp之后要 pkill system_server 这个进程被杀后会自动重启,进而安转apk。

主屏上的快捷图标

创建快捷方式,本来就是有android 标准接口的。就是发送广播 com.android.launcher.action.INSTALL_SHORTCUT ,我这里给出一个示例代码。

/** 
  * 为PackageName的App添加快捷方式 
  * 
  * @param context context 
  * @param pkg 待添加快捷方式的应用包名
  * @return 返回true为正常执行完毕 
  */ 
       public static boolean addShortcutByPackageName(Context context, String pkg) { 
               // 快捷方式名 
               String title = "unknown"; 
               // MainActivity完整名 
               String mainAct = null; 
               // 应用图标标识 
               int iconIdentifier = 0; 
               // 根据包名寻找MainActivity 
               PackageManager pkgMag = context.getPackageManager(); 
               Intent queryIntent = new Intent(Intent.ACTION_MAIN, null); 
               queryIntent.addCategory(Intent.CATEGORY_LAUNCHER);
               // 重要,添加后可以进入直接已经打开的页面 
               queryIntent.setFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); 
               queryIntent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY); 
               List<ResolveInfo> list = pkgMag.queryIntentActivities(queryIntent, PackageManager.GET_ACTIVITIES); 
               for (int i = 0; i < list.size(); i++) { 
                       ResolveInfo info = list.get(i); 
                       if (info.activityInfo.packageName.equals(pkg)) { 
                               title = info.loadLabel(pkgMag).toString(); 
                               mainAct = info.activityInfo.name;
                               iconIdentifier = info.activityInfo.applicationInfo.icon; 
                               break; 
                       } 
               } if (mainAct == null) { 
                       // 没有启动类 
                       return false; }
               Intent shortcut = new Intent( "com.android.launcher.action.INSTALL_SHORTCUT"); 
               // 快捷方式的名称 
               shortcut.putExtra(Intent.EXTRA_SHORTCUT_NAME, title); 
               // 不允许重复创建 
               shortcut.putExtra("duplicate", false); 
               ComponentName comp = new ComponentName(pkg, mainAct); 
               shortcut.putExtra(Intent.EXTRA_SHORTCUT_INTENT, queryIntent.setComponent(comp)); 
               // 快捷方式的图标 
               Context pkgContext = null; 
               if (context.getPackageName().equals(pkg)) { 
                       pkgContext = context; 
               } else { 
                       // 创建第三方应用的上下文环境,为的是能够根据该应用的图标标识符寻找到图标文件。 
                       try { 
                               pkgContext = context.createPackageContext(pkg, Context.CONTEXT_IGNORE_SECURITY | Context.CONTEXT_INCLUDE_CODE); 
                       } catch (PackageManager.NameNotFoundException e) { 
                               e.printStackTrace(); 
                       } 
               } if (pkgContext != null) { 
                       Intent.ShortcutIconResource iconRes = Intent.ShortcutIconResource .fromContext(pkgContext, iconIdentifier); 
                       shortcut.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, iconRes); 
               } 
               // 发送广播,让接收者创建快捷方式 
               // 需权限<uses-permission
               // android:name="com.android.launcher.permission.INSTALL_SHORTCUT" /> 
               context.sendBroadcast(shortcut); 
               return true; 
       }

由于我们是在脚本中安装的apk,安装完成后就在脚本中发送一个广播。广播接收机放在launcher中。
脚本:

while [ ! -f /data/data/com.android.launcher3/databases/launcher.db  ]; do sleep 1; done
am broadcast -a subingxi.shieke.launcher

第一行是用于确保launcher已经启动。然后发广播。我把广播接收机放在
packages/apps/Launcher3/src/com/android/launcher3/Launcher.java

IntentFilter filterShike = new IntentFilter("routon.shieke.launcher");
               registerReceiver(new BroadcastReceiver() {
                       @Override
                       public void onReceive(Context context, Intent intent) {
                               Log.d(TAG,"subingxi to create shike laucher");
                               boolean pkg_installed = false;
                               int i = 10;
                               for (;i > 0;i--) {
                                       try {
                                               PackageInfo pi = getPackageManager().getPackageInfo(“subingxi.aaa", 0);
                                               PackageInfo pi1 = getPackageManager().getPackageInfo("subingxi.bbb", 0);
                                               PackageInfo pi2 = getPackageManager().getPackageInfo("subingxi.ccc", 0);
                                               PackageInfo pi3 = getPackageManager().getPackageInfo("subingxi.ddd", 0);
                                              if(pi != null && pi1 != null && pi2 != null && pi3 != null) {
                                                   Log.d(TAG,"subingxi to create shike laucher ok ");
                                                    pkg_installed = true;
                                                }

                                        } catch (NameNotFoundException ex) {
                                                Log.d(TAG,"subingxi to create shike laucher no ");
                                             pkg_installed = false;
                           }
                            if(pkg_installed)
                            break;

                           try {
                                  Thread.sleep(1000);
                                   }catch (Exception e) {
                                   }
                }

                               addShortcutByPackageName(Launcher.this,"subingxi.aaa");
                               addShortcutByPackageName(Launcher.this,"subingxi.bbb");
                               addShortcutByPackageName(Launcher.this,"subingxi.ccc");
                               addShortcutByPackageName(Launcher.this,"subingxi.ddd");

                       }

               },filterShike);

这里先检查是否安装完成,没有安装完成就等待。

这就完了吗?你会发现创建的快捷方式不是在主屏上,而是在主屏的右一屏上(也叫首屏)。这是因为android 提供的接口(广播 com.android.launcher.action.INSTALL_SHORTCUT )就是这样的。那怎么处理一下呢。

把快捷图标放在主屏上

要想把快捷方式放到主屏上,就必须分析系统处理广播 com.android.launcher.action.INSTALL_SHORTCUT 的过程。具体分析过程较为复杂,这里只记录最终结果。基于android6.0
packages/apps/Launcher3/src/com/android/launcher3/LauncherModel.java

    private static boolean findNextAvailableIconSpaceInScreen(ArrayList<ItemInfo> occupiedPos,
              int[] xy, int spanX, int spanY) {
          LauncherAppState app = LauncherAppState.getInstance();
          InvariantDeviceProfile profile = app.getInvariantDeviceProfile();
          final int xCount = (int) profile.numColumns;
          final int yCount = (int) profile.numRows;
          boolean[][] occupied = new boolean[xCount][yCount];
          if (occupiedPos != null) {
              for (ItemInfo r : occupiedPos) {
                  int right = r.cellX + r.spanX;
                  int bottom = r.cellY + r.spanY;
                  for (int x = r.cellX; 0 <= x && x < right && x < xCount; x++) {
                      for (int y = r.cellY; 0 <= y && y < bottom && y < yCount; y++) {
                          occupied[x][y] = true;
                      }
                  }
              }
          }
          return Utilities.findVacantCell(xy, spanX, spanY, xCount, yCount, occupied);
      }
  
      /**
       * Find a position on the screen for the given size or adds a new screen.
       * @return screenId and the coordinates for the item.
       */
      @Thunk Pair<Long, int[]> findSpaceForItem(
              Context context,
              ArrayList<Long> workspaceScreens,
              ArrayList<Long> addedWorkspaceScreensFinal,
              int spanX, int spanY) {
          LongSparseArray<ArrayList<ItemInfo>> screenItems = new LongSparseArray<>();
  
          // Use sBgItemsIdMap as all the items are already loaded.
          assertWorkspaceLoaded();
          synchronized (sBgLock) {
              for (ItemInfo info : sBgItemsIdMap) {
                  if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
                      ArrayList<ItemInfo> items = screenItems.get(info.screenId);
                      if (items == null) {
                          items = new ArrayList<>();
                          screenItems.put(info.screenId, items);
                      }
                      items.add(info);
                  }
              }
         }
  
          // Find appropriate space for the item.
          long screenId = 0;
          int[] cordinates = new int[2];
          boolean found = false;
  
          int screenCount = workspaceScreens.size();
          // First check the preferred screen.
                  // subingxi 
          //int preferredScreenIndex = workspaceScreens.isEmpty() ? 0 : 1;
                  int preferredScreenIndex = 0;
                  //subingxi end
                  Log.e(TAG,"subingxi_1 the preferredScreenIndex :"+preferredScreenIndex);
          if (preferredScreenIndex < screenCount) {
              screenId = workspaceScreens.get(preferredScreenIndex);
              found = findNextAvailableIconSpaceInScreen(
                      screenItems.get(screenId), cordinates, spanX, spanY);
          }
  
          if (!found) {
              // Search on any of the screens starting from the first screen.
              for (int screen = 1; screen < screenCount; screen++) {
                  screenId = workspaceScreens.get(screen);
                  if (findNextAvailableIconSpaceInScreen(
                          screenItems.get(screenId), cordinates, spanX, spanY)) {
                      // We found a space for it
                      found = true;
                      break;
                  }
              }
          }
 
          if (!found) {
              // Still no position found. Add a new screen to the end.
              screenId = LauncherAppState.getLauncherProvider().generateNewScreenId();
  
              // Save the screen id for binding in the workspace
              workspaceScreens.add(screenId);
              addedWorkspaceScreensFinal.add(screenId);
  
              // If we still can't find an empty space, then God help us all!!!
              if (!findNextAvailableIconSpaceInScreen(
                      screenItems.get(screenId), cordinates, spanX, spanY)) {
                  throw new RuntimeException("Can't find space to add the item");
              }
          }
          return Pair.create(screenId, cordinates);
     }

这个函数是用于查找空缺位置用于放快捷图标,注意56行被我换成了57行。这样就会先从主屏开始查找空缺。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值