【Demo记录】悬浮窗—通过服务显示栈顶app包名到悬浮窗

1、 悬浮窗的基本操作

  1. 1) 创建悬浮窗
WindowManager windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);

if (floatView == null) {
    //设置悬浮窗的ui 
View floatView = LayoutInflater.from(this).inflate(R.layout.float_window_text, null);
    //设置悬浮窗的参数
    params = new WindowManager.LayoutParams();
    //使用 TYPE_SYSTEM_ALERT 需要有用户选择app 进行权限申请
    //使用Toast方式,不需要权限
    params.type = WindowManager.LayoutParams.TYPE_TOAST;
    params.format = PixelFormat.RGBA_8888;
    params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
            | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
    params.gravity = Gravity.LEFT | Gravity.TOP;
    params.width = WindowManager.LayoutParams.WRAP_CONTENT;
    params.height = WindowManager.LayoutParams.WRAP_CONTENT;

    windowManager.addView(floatView,params);//创建悬浮窗
}
  1. 2) 更新悬浮窗

修改View 或者参数之后进行更新

windowManager.updateViewLayout(floatView, params);
  1. 3) 移除悬浮窗
windowManager.removeView(floatView);

2、 悬浮窗与Activity

在Activity中对悬浮窗进行操作,遇到了一些列问题。
:通过下列界面来控制悬浮窗

这里写图片描述

模拟器(版本号17)运行效果
在开启悬浮窗时销毁activity,悬浮窗一起销毁了。再开启activity进行操作,就是重新开始。

乐视2(android 6.0)运行效果
在开启悬浮窗时销毁activity,悬浮窗没有一起销毁,再开启activity进行操作,不能关闭原来的悬浮窗,activity 重新开始,不能对原来的悬浮窗进行操作。

悬浮窗显示效果分析:在activity中创建悬浮窗,悬浮窗没有被销毁,当重新进入activity的时候,activity获取到的悬浮窗对象并不是之前的悬浮窗对象,对之前的悬浮窗已经失去了可控性。而想要保持对悬浮窗的可控性,就需要将悬浮窗与activity的生命周期绑定,悬浮窗和创建它的这个activity共存亡。

3、 悬浮窗与服务

场景:在activity中开启服务,使用悬浮窗显示手机栈顶app所在的应用包名。
如何获取栈顶app所在的应用包名—另一篇demo中记录

Service代码:

/**
 * 自定义服务  时刻记录手机的栈顶activity 所在包  
* 并将包名显示到悬浮窗 服务停止的时候关闭悬浮窗
 */
public class TopAppService extends Service {
    private Timer timer;
    private WindowManager windowManager;
    private View floatView;
    private WindowManager.LayoutParams params;
    private boolean isFirst;
    private TextView tvFloatWindow;
    Handler handler = new Handler() {
    };

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        initFloatWindow();
        isFirst = true;//默认是第一次
        if (timer == null) {
            timer = new Timer();
            timer.scheduleAtFixedRate(new FloatWindowTimer(), 0, 5000);//每隔5s 执行一次
        }
        return super.onStartCommand(intent, flags, startId);
    }

    /**
     * 获取栈顶app的应用包名,显示到悬浮窗
     *
     * @param topApp 栈顶app的应用包名
     */
    private void uploadFloatWindow(final String topApp) {
        //第一次执行时,创建悬浮窗,后面的操作,只对悬浮窗进行更新
        if (isFirst) {
            //创建悬浮窗
            handler.post(new Runnable() {
                @Override
                public void run() {
                    tvFloatWindow.setText(topApp);
                    windowManager.addView(floatView, params);
                }
            });
            isFirst = false;
        } else {
            //更新悬浮窗
            handler.post(new Runnable() {
                @Override
                public void run() {
                    tvFloatWindow.setText(topApp);
                    windowManager.updateViewLayout(floatView, params);
                }
            });
        }
    }

    /**
     * 初始化FloatWindow
     */
    private void initFloatWindow() {
        windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);

        if (floatView == null) {
            floatView = LayoutInflater.from(this).inflate(R.layout.float_window_text, null);
            params = new WindowManager.LayoutParams();
            //使用 TYPE_SYSTEM_ALERT 需要有用户选择app 进行权限申请
            //使用Toast方式,不需要权限
            params.type = WindowManager.LayoutParams.TYPE_TOAST;
            params.format = PixelFormat.RGBA_8888;
            params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                    | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
            params.gravity = Gravity.LEFT | Gravity.TOP;
            params.width = WindowManager.LayoutParams.WRAP_CONTENT;
            params.height = WindowManager.LayoutParams.WRAP_CONTENT;
        }
        tvFloatWindow = (TextView) floatView.findViewById(R.id.tv_float_window);
    }

    /**
     * 判断  用户查看历史记录的权利是否给予app(获取栈顶app的权限)
     *
     * @return
     */
    private boolean isUseGranted() {
        Context appContext = MyApplication.getAppContext();
        AppOpsManager appOps = (AppOpsManager) appContext
                .getSystemService(Context.APP_OPS_SERVICE);
        int mode = -1;
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) {
            mode = appOps.checkOpNoThrow("android:get_usage_stats",
                    android.os.Process.myUid(), appContext.getPackageName());
        }
        boolean granted = mode == AppOpsManager.MODE_ALLOWED;
        return granted;
    }

    /**
     * 高版本:获取顶层的activity的包名
     *
     * @return
     */
    private String getHigherPackageName() {
        String topPackageName = "";
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {

            UsageStatsManager mUsageStatsManager = (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE);

            long time = System.currentTimeMillis();
            // We get usage stats for the last 10 seconds
            //time - 1000 * 1000, time 开始时间和结束时间的设置,在这个时间范围内 获取栈顶Activity 有效
            List<UsageStats> stats = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, time - 1000 * 1000, time);
            // Sort the stats by the last time used
            if (stats != null) {
                SortedMap<Long, UsageStats> mySortedMap = new TreeMap<Long, UsageStats>();
                for (UsageStats usageStats : stats) {
                    mySortedMap.put(usageStats.getLastTimeUsed(), usageStats);
                }
                if (mySortedMap != null && !mySortedMap.isEmpty()) {
                    topPackageName = mySortedMap.get(mySortedMap.lastKey()).getPackageName();
                    Log.e("TopPackage Name", topPackageName);
                }
            }
        }
        return topPackageName;
    }

    /**
     * 低版本:获取栈顶app的包名
     *
     * @return
     */
    private String getLowerVersionPackageName() {
        String topPackageName;//低版本  直接获取getRunningTasks
        ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
        ComponentName topActivity = activityManager.getRunningTasks(1).get(0).topActivity;
        topPackageName = topActivity.getPackageName();
        return topPackageName;
    }

    /**
     * 计时器:获取栈顶app的应用包名显示到悬浮窗
     */
    class FloatWindowTimer extends TimerTask {
        public void run() {
            if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) {
                boolean useGranted = isUseGranted();
                Log.e("TopAppService", "查看历史记录权限 是否允许授权=" + useGranted);
                if (useGranted) {
                    String topApp = getHigherPackageName();
                    uploadFloatWindow(topApp);
                } else {
                    //开启应用授权界面
                    Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    startActivity(intent);
                }
            } else {
                String topApp = getLowerVersionPackageName();
                uploadFloatWindow(topApp);
            }
        }
    }
    @Override
    public void onDestroy() {
           super.onDestroy();
          //如果悬浮窗是显示的  需要移除悬浮窗
          timer.cancel();
          timer = null;
          windowManager.removeView(floatView);
    }
}

模拟器(版本号17)效果:

这里写图片描述

乐视2 (android 6.0)效果:

这里写图片描述

4、 Demo下载地址

http://download.csdn.net/detail/u012391876/9716855

猜你喜欢

转载自blog.csdn.net/u012391876/article/details/53766470