Skip to content

Instantly share code, notes, and snippets.

@winziyuan
Forked from waylife/LauncherUtil
Created July 11, 2016 07:13

Revisions

  1. @waylife waylife created this gist Mar 14, 2015.
    81 changes: 81 additions & 0 deletions LauncherUtil
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,81 @@
    import java.util.List;

    import android.content.Context;
    import android.content.Intent;
    import android.content.pm.PackageInfo;
    import android.content.pm.PackageManager;
    import android.content.pm.ProviderInfo;
    import android.content.pm.ResolveInfo;
    import android.text.TextUtils;
    /**
    * @author rxread
    *
    */
    public class LauncherUtil {

    private static String mBufferedValue=null;
    /**get the current Launcher's Package Name*/
    public static String getCurrentLauncherPackageName(Context context) {
    Intent intent = new Intent(Intent.ACTION_MAIN);
    intent.addCategory(Intent.CATEGORY_HOME);
    ResolveInfo res = context.getPackageManager().resolveActivity(intent, 0);
    if (res==null||res.activityInfo == null) {
    // should not happen. A home is always installed, isn't it?
    return "";
    }
    if (res.activityInfo.packageName.equals("android")) {
    return "";
    } else {
    return res.activityInfo.packageName;
    }
    }

    /**
    *default permission is "com.android.launcher.permission.READ_SETTINGS"<br/>
    * {@link #getAuthorityFromPermission(Context, String)}<br/>
    * @param context
    */
    public static String getAuthorityFromPermissionDefault(Context context) {
    if(TextUtils.isEmpty(mBufferedValue))//we get value buffered
    mBufferedValue= getAuthorityFromPermission(context, "com.android.launcher.permission.READ_SETTINGS");
    return mBufferedValue;
    }

    /**
    * be cautious to use this, it will cost about 500ms 此函数为费时函数,大概占用500ms左右的时间<br/>
    * android系统桌面的基本信息由一个launcher.db的Sqlite数据库管理,里面有三张表<br/>
    * 其中一张表就是favorites。这个db文件一般放在data/data/com.android.launcher(launcher2)文件的databases下<br/>
    * 但是对于不同的rom会放在不同的地方<br/>
    * 例如MIUI放在data/data/com.miui.home/databases下面<br/>
    * htc放在data/data/com.htc.launcher/databases下面<br/
    * @param context
    * @param permission 读取设置的权限 READ_SETTINGS_PERMISSION
    * @return
    */
    public static String getAuthorityFromPermission(Context context, String permission) {
    if (TextUtils.isEmpty(permission)) {
    return "";
    }

    try {
    List<PackageInfo> packs = context.getPackageManager().getInstalledPackages(PackageManager.GET_PROVIDERS);
    if (packs == null) {
    return "";
    }
    for (PackageInfo pack : packs) {
    ProviderInfo[] providers = pack.providers;
    if (providers != null) {
    for (ProviderInfo provider : providers) {
    if (permission.equals(provider.readPermission)|| permission.equals(provider.writePermission)) {
    return provider.authority;
    }
    }
    }
    }
    } catch (Exception e) {
    e.printStackTrace();
    }
    return "";
    }

    }
    484 changes: 484 additions & 0 deletions ShortCutUtil
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,484 @@

    import android.content.ComponentName;
    import android.content.ContentResolver;
    import android.content.ContentValues;
    import android.content.Context;
    import android.content.Intent;
    import android.content.Intent.ShortcutIconResource;
    import android.content.pm.PackageManager;
    import android.content.pm.PackageManager.NameNotFoundException;
    import android.content.pm.ResolveInfo;
    import android.database.Cursor;
    import android.graphics.Bitmap;
    import android.net.Uri;
    import android.text.TextUtils;


    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.util.List;

    /**
    * @author rxread
    *
    */
    public class ShortCutUtil {
    public static final String ACTION_MY_GAME="com.sogou.appmall.intent.action.FROM_MY_GAME_SHORTCUT";
    public static final String TAG = "ShortcutUtil";
    private final static String ONE_KEY_CLEAN_NAME = "一键加速";
    private final static String MY_GAME="我的游戏";

    /** 一键加速 */
    private final static int SHORTCUT_TYPE_OKC = 1;
    /**我的游戏*/
    private final static int SHORTCUT_TYPE_MY_GAME=4;



    /**
    * 为程序创建程序桌面快捷方式<br/>
    * 之前创建过则不创建<br/>
    */
    public static void addApplicationShortcut(Context context) {
    // 此处分开主要是考虑到替换安装可能就没有后面的快捷方式了
    if (!PreferencesUtil.hasCreateShortcut()) {
    if (!isShortCutExist(context, context.getString(R.string.app_name))) {
    addShortcutByPackageName(context, context.getPackageName());
    // MLog.i("ShortCut", "app shortcut not exist");
    } else {
    // MLog.i("ShortCut", "app shortcut exists");
    }
    PreferencesUtil.setCreateShortCut(true);
    }
    }

    /**
    * 为程序创建一键清理快捷方式<br/>
    * 之前创建过则不创建<br/>
    */
    public static void addOneKeyCleanShortcut(Context context) {
    if(!ChannelUtil.isShouldGenerateShortCut(context))//部分渠道,不创建快捷方式
    return;
    // 一键清理以及
    if (!PreferencesUtil.hasCreateShortcutOneKeyClean()) {
    Intent oneKeyClenIntent = getShortCutIntent(context,SHORTCUT_TYPE_OKC);
    if (!isShortCutExist(context, ONE_KEY_CLEAN_NAME, oneKeyClenIntent)) {
    addOneKeyCleanShortCut(context, oneKeyClenIntent, false);
    }
    PreferencesUtil.setCreateShortCutOneKeyClean(true);
    }
    }


    /**
    * 增加桌面一键清理快捷方式
    *
    * @param allowRepeat
    * 是否判断重复 ,如果之前有,则不重复生成
    */
    public static void addOneKeyCleanShortCut(Context context,
    boolean allowRepeat) {
    Intent oneKeyCleanIntent = getShortCutIntent(context, SHORTCUT_TYPE_OKC);
    if (!allowRepeat) {
    if (!isShortCutExist(context, ONE_KEY_CLEAN_NAME, oneKeyCleanIntent)) {
    addOneKeyCleanShortCut(context, oneKeyCleanIntent, false);
    }
    } else {
    addOneKeyCleanShortCut(context, oneKeyCleanIntent, true);
    }
    }


    /** 增加一键清理快捷方式 */
    private static void addOneKeyCleanShortCut(Context context, Intent intent,
    boolean allowRepeat) {
    ShortcutIconResource iconRes = Intent.ShortcutIconResource.fromContext(
    context, R.drawable.shortcut_one_key_clean);
    addShortcut(context, ONE_KEY_CLEAN_NAME, intent, iconRes, allowRepeat);
    }

    /**
    * 为程序创建重磅推荐方式<br/>
    * 之前创建过则不创建<br/>
    */
    public static void addMyGameShortcut(Context context,Bitmap bitmap) {
    if(!ChannelUtil.isShouldGenerateShortCut(context))//部分渠道,不创建快捷方式
    return;
    // 一键清理以及
    if (!PreferencesUtil.hasCreateShortcutMyGame()) {
    Intent gameIntent = getShortCutIntent(context,SHORTCUT_TYPE_MY_GAME);
    addShortcut(context, MY_GAME, gameIntent, bitmap, false);
    PreferencesUtil.setCreateShortcutMyGame(true);
    }
    }

    /**更新我的游戏快捷方式<br/>
    * 异步,如果没有快捷方式不会创建新的,需要注意<br/>*/
    public static void updateMyGameShortcut(final Context context,final Bitmap bitmap){
    new Thread(new Runnable() {
    @Override
    public void run() {
    Intent gameIntent=getShortCutIntent(context, SHORTCUT_TYPE_MY_GAME);
    updateShortcutIcon(context, MY_GAME, gameIntent, bitmap);
    }
    }).start();
    }

    private static Intent getShortCutIntent(Context context, int type) {
    Intent intent;
    intent = new Intent(Intent.ACTION_MAIN);//使用MAIN,可以避免部分手机(比如华为、HTC部分机型)删除应用时无法删除快捷方式的问题
    switch (type) {
    case SHORTCUT_TYPE_OKC:
    intent.putExtra(Constants4UI.EXTRA_FROM,
    Constants4UI.EXTRA_FROM_SHORTCUT_ONE_KEY_CLEAN);
    intent.setClassName(context, ActivityOneKeyClean.class.getName());
    break;
    case SHORTCUT_TYPE_MY_GAME:
    //Note that for an IntentFilter to match an Intent, three conditions must hold: the action and category must match
    /*需要在相关的activity添加以下声明
    <intent-filter>
    <action android:name="com.sogou.appmall.intent.action.FROM_MY_GAME_SHORTCUT" />
    <action android:name="android.intent.action.VIEW" />
    <action android:name="android.intent.action.MAIN" />
    <category android:name="android.intent.category.DEFAULT" />
    <data android:scheme="luna_shortcut" />
    </intent-filter>
    */
    intent.addCategory(Intent.CATEGORY_DEFAULT);
    intent.setAction(ACTION_MY_GAME);
    intent.setPackage(Constants4UI.MY_PACKAGENAME);
    intent.putExtra(Constants4UI.EXTRA_FROM, Constants4UI.EXTRA_FROM_SHORTCUT_MY_GAME);
    intent.setData(Uri.parse("luna_shortcut://game"));
    break;
    default:
    break;
    }
    return intent;
    }



    /**
    * @param context
    * 执行者。
    * @params pkg 待添加快捷方式的应用包名,其值不可为null。
    * @return 返回true为正常执行完毕,<br/>
    * 返回false为pkg值为null或者找不到该应用或者该应用无用于Launch的MainActivity 。
    * @author sodino
    * */
    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 (NameNotFoundException e) {
    e.printStackTrace();
    }
    }
    if (pkgContext != null) {
    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;
    }

    /**
    * 新建快捷方式到桌面<br/>
    * 请在对应Intent的Activity中添加<br/>
    * <font color="red"> intent-filter action
    * android:name="android.intent.action.MAIN"</font> <br/>
    *
    * @param shortcutName
    * 快捷方式名
    * @param actionIntent
    * 快捷方式操作
    * @param icon
    * 快捷方式图标
    * @param allowRepeat
    * 是否允许重复生成
    *
    *
    */
    public static void addShortcut(Context context, String shortcutName,
    Intent actionIntent, ShortcutIconResource icon, boolean allowRepeat) {
    Intent shortcutIntent = new Intent(
    "com.android.launcher.action.INSTALL_SHORTCUT");
    shortcutIntent.putExtra("duplicate", allowRepeat);// 是否允许重复创建
    shortcutIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, shortcutName);// 快捷方式的标题
    shortcutIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, icon);// 快捷方式的图标
    shortcutIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, actionIntent);// 快捷方式的动作
    context.sendBroadcast(shortcutIntent);// 发送广播
    }

    /**
    * 新建快捷方式到桌面<br/>
    * 请在对应Intent的Activity中添加<br/>
    * <font color="red"> intent-filter action
    * android:name="android.intent.action.MAIN"</font> <br/>
    *
    * @param shortcutName
    * 快捷方式名
    * @param actionIntent
    * 快捷方式操作
    * @param icon
    * 快捷方式图标
    * @param allowRepeat
    * 是否允许重复生成
    *
    *
    */
    public static void addShortcut(Context context, String shortcutName,
    Intent actionIntent, Bitmap icon, boolean allowRepeat) {
    Intent shortcutIntent = new Intent(
    "com.android.launcher.action.INSTALL_SHORTCUT");
    shortcutIntent.putExtra("duplicate", allowRepeat);// 是否允许重复创建
    shortcutIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, shortcutName);// 快捷方式的标题
    shortcutIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON, icon);// 快捷方式的图标
    shortcutIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, actionIntent);// 快捷方式的动作
    context.sendBroadcast(shortcutIntent);// 发送广播
    }

    /**
    * 删除桌面快捷方式 <br/>
    *
    * @param shortcutName
    * 快捷方式名
    * @param actionIntent
    * 快捷方式操作
    * @param isDuplicate
    * 为true时循环删除快捷方式(即存在很多相同的快捷方式)
    *
    *
    */
    public static void deleteShortcut(Context context, String shortcutName,
    Intent actionIntent, boolean isDuplicate) {
    Intent shortcutIntent = new Intent(
    "com.android.launcher.action.UNINSTALL_SHORTCUT");
    shortcutIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, shortcutName);// 快捷方式的标题
    shortcutIntent.putExtra("duplicate", isDuplicate);// 是否循环删除,比如有很多一样的快捷方式
    shortcutIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, actionIntent);// 快捷方式的动作
    context.sendBroadcast(shortcutIntent);// 发送广播
    }

    /**
    * 检查快捷方式是否存在 <br/>
    * <font color=red>注意:</font> 有些手机无法判断是否已经创建过快捷方式<br/>
    * 因此,在创建快捷方式时,请添加<br/>
    * shortcutIntent.putExtra("duplicate", false);// 不允许重复创建<br/>
    * 最好使用{@link #isShortCutExist(Context, String, Intent)}
    * 进行判断,因为可能有些应用生成的快捷方式名称是一样的的<br/>
    * 此处需要在AndroidManifest.xml中配置相关的桌面权限信息<br/>
    * 错误信息已捕获<br/>
    */
    public static boolean isShortCutExist(Context context, String title) {
    boolean result = false;
    try {
    final ContentResolver cr = context.getContentResolver();
    StringBuilder uriStr = new StringBuilder();
    String authority = LauncherUtil.getAuthorityFromPermissionDefault(context);
    if(authority==null||authority.trim().equals("")){
    authority = LauncherUtil.getAuthorityFromPermission(context,LauncherUtil.getCurrentLauncherPackageName(context)+".permission.READ_SETTINGS");
    }
    uriStr.append("content://");
    if (TextUtils.isEmpty(authority)) {
    int sdkInt = android.os.Build.VERSION.SDK_INT;
    if (sdkInt < 8) { // Android 2.1.x(API 7)以及以下的
    uriStr.append("com.android.launcher.settings");
    } else if (sdkInt < 19) {// Android 4.4以下
    uriStr.append("com.android.launcher2.settings");
    } else {// 4.4以及以上
    uriStr.append("com.android.launcher3.settings");
    }
    } else {
    uriStr.append(authority);
    }
    uriStr.append("/favorites?notify=true");
    Uri uri = Uri.parse(uriStr.toString());
    Cursor c = cr.query(uri, new String[] { "title" },
    "title=? ",
    new String[] { title }, null);
    if (c != null && c.getCount() > 0) {
    result = true;
    }
    if (c != null && !c.isClosed()) {
    c.close();
    }
    } catch (Exception e) {
    e.printStackTrace();
    result=false;
    }
    return result;
    }

    /**
    * 不一定所有的手机都有效,因为国内大部分手机的桌面不是系统原生的<br/>
    * 更多请参考{@link #isShortCutExist(Context, String)}<br/>
    * 桌面有两种,系统桌面(ROM自带)与第三方桌面,一般只考虑系统自带<br/>
    * 第三方桌面如果没有实现系统响应的方法是无法判断的,比如GO桌面<br/>
    * 此处需要在AndroidManifest.xml中配置相关的桌面权限信息<br/>
    * 错误信息已捕获<br/>
    */
    public static boolean isShortCutExist(Context context, String title, Intent intent) {
    boolean result = false;
    try{
    final ContentResolver cr = context.getContentResolver();
    StringBuilder uriStr = new StringBuilder();
    String authority = LauncherUtil.getAuthorityFromPermissionDefault(context);
    if(authority==null||authority.trim().equals("")){
    authority = LauncherUtil.getAuthorityFromPermission(context,LauncherUtil.getCurrentLauncherPackageName(context)+".permission.READ_SETTINGS");
    }
    uriStr.append("content://");
    if (TextUtils.isEmpty(authority)) {
    int sdkInt = android.os.Build.VERSION.SDK_INT;
    if (sdkInt < 8) { // Android 2.1.x(API 7)以及以下的
    uriStr.append("com.android.launcher.settings");
    } else if (sdkInt < 19) {// Android 4.4以下
    uriStr.append("com.android.launcher2.settings");
    } else {// 4.4以及以上
    uriStr.append("com.android.launcher3.settings");
    }
    } else {
    uriStr.append(authority);
    }
    uriStr.append("/favorites?notify=true");
    Uri uri = Uri.parse(uriStr.toString());
    Cursor c = cr.query(uri, new String[] { "title", "intent" },
    "title=? and intent=?",
    new String[] { title, intent.toUri(0) }, null);
    if (c != null && c.getCount() > 0) {
    result = true;
    }
    if (c != null && !c.isClosed()) {
    c.close();
    }
    }catch(Exception ex){
    result=false;
    ex.printStackTrace();
    }
    return result;
    }

    /**
    * 更新桌面快捷方式图标,不一定所有图标都有效<br/>
    * 如果快捷方式不存在,则不更新<br/>.
    */
    public static void updateShortcutIcon(Context context, String title, Intent intent,Bitmap bitmap) {
    if(bitmap==null){
    XLog.i(TAG, "update shortcut icon,bitmap empty");
    return;
    }
    try{
    final ContentResolver cr = context.getContentResolver();
    StringBuilder uriStr = new StringBuilder();
    String urlTemp="";
    String authority = LauncherUtil.getAuthorityFromPermissionDefault(context);
    if(authority==null||authority.trim().equals("")){
    authority = LauncherUtil.getAuthorityFromPermission(context,LauncherUtil.getCurrentLauncherPackageName(context)+".permission.READ_SETTINGS");
    }
    uriStr.append("content://");
    if (TextUtils.isEmpty(authority)) {
    int sdkInt = android.os.Build.VERSION.SDK_INT;
    if (sdkInt < 8) { // Android 2.1.x(API 7)以及以下的
    uriStr.append("com.android.launcher.settings");
    } else if (sdkInt < 19) {// Android 4.4以下
    uriStr.append("com.android.launcher2.settings");
    } else {// 4.4以及以上
    uriStr.append("com.android.launcher3.settings");
    }
    } else {
    uriStr.append(authority);
    }
    urlTemp=uriStr.toString();
    uriStr.append("/favorites?notify=true");
    Uri uri = Uri.parse(uriStr.toString());
    Cursor c = cr.query(uri, new String[] {"_id", "title", "intent" },
    "title=? and intent=? ",
    new String[] { title, intent.toUri(0) }, null);
    int index=-1;
    if (c != null && c.getCount() > 0) {
    c.moveToFirst();
    index=c.getInt(0);//获得图标索引
    ContentValues cv=new ContentValues();
    cv.put("icon", flattenBitmap(bitmap));
    Uri uri2=Uri.parse(urlTemp+"/favorites/"+index+"?notify=true");
    int i=context.getContentResolver().update(uri2, cv, null,null);
    context.getContentResolver().notifyChange(uri,null);//此处不能用uri2,是个坑
    XLog.i(TAG, "update ok: affected "+i+" rows,index is"+index);
    }else{
    XLog.i(TAG, "update result failed");
    }
    if (c != null && !c.isClosed()) {
    c.close();
    }
    }catch(Exception ex){
    ex.printStackTrace();
    XLog.i(TAG, "update shortcut icon,get errors:"+ex.getMessage());
    }
    }


    private static byte[] flattenBitmap(Bitmap bitmap) {
    // Try go guesstimate how much space the icon will take when serialized
    // to avoid unnecessary allocations/copies during the write.
    int size = bitmap.getWidth() * bitmap.getHeight() * 4;
    ByteArrayOutputStream out = new ByteArrayOutputStream(size);
    try {
    bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
    out.flush();
    out.close();
    return out.toByteArray();
    } catch (IOException e) {
    XLog.w(TAG, "Could not write icon");
    return null;
    }
    }

    }