一、方法
View normalView = findViewById(R.id.normal_view);
// 下面两行代码的顺序不能颠倒
normalView.setFocusableInTouchMode(true);
normalView.requestFocus();
normalView.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
// 需要对action进行判断,否则会收到两次回调
if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
Log.i(TAG, "view监听到返回键");
return true;
}
return false;
}
});
二、参考别人的
三、悬浮窗的情况的侦听物理按键实例代码
package com.example.suspendedwindow;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import java.lang.reflect.Field;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.SortedMap;
import java.util.Timer;
import java.util.TimerTask;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.app.Application;
import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManager;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.Rect;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.provider.Settings;
import android.util.Log;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import java.lang.System;
import java.util.TreeMap;
/**
* 倒计时60s悬浮窗
*
* @author zy
*
*/
public class MainActivity extends Activity {
protected static final String TAG = "CountDownActivity";
private SharedPreferences alert_window_sp;
private SharedPreferences.Editor alert_window_editor;
protected static final int TIME = 1;
private Context context = MainActivity.this;
private TextView tv_time;
private ImageView twenty_four_hour ;
private static Timer countDown = null;
private int mValue = 0;
private int statusBarHeight;// 状态栏高度
WindowManager wm;
WindowManager.LayoutParams params ;
View countDownView;
private boolean viewAdded = false;// 透明窗体是否已经显示
Handler post = new Handler();
LinearLayout commonCardContainer;
LinearLayout.LayoutParams params_window;
int width = 10;
boolean lock = false ;
float remeberx = 0;
float remebery = 0;
float remebertempx = 0;
float remebertempy = 0;
float rangex = 0;
float rangey = 0;
int sencond_getx = 0;
int sencond_gety = 0;
float downtempx = 0;
float downtempy = 0;
boolean lock_temp = false ;
String LauncherPackageName;
@RequiresApi(api = Build.VERSION_CODES.Q)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
alert_window_sp = getSharedPreferences("ALERT_WINDOW_DATA", Context.MODE_PRIVATE);
alert_window_editor = alert_window_sp.edit();
Log.i(TAG, "getFrontPackageName= " + getFrontPackageName( getApplicationContext()));
String packageName = "";
if (needPermissionForBlocking(getApplicationContext())) {
//如果用户没有授权,引导用户去设置页面授权
Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
//在service中开启activity需要为intent添加FLAG_ACTIVITY_NEW_TASK的flag
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
} else {
//得到包名
packageName = getTopPackage();
}
LauncherPackageName = getLauncherPackageName(getApplicationContext());
Log.i(TAG, "top app = " + packageName+ " LauncherPackageName:"+LauncherPackageName);
Log.i(TAG, " getTopActivity():" + getTopActivity());
}
private String getTopActivity() {
String className = null;
ActivityManager mActivityManager = (ActivityManager) this.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningTaskInfo> list = mActivityManager.getRunningTasks(1);
if (!list.isEmpty() && list.get(0) != null && list.get(0).topActivity != null) {
className = list.get(0).topActivity.getClassName();
}
return className;
}
public String getLauncherPackageName(Context context)
{
final Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
final ResolveInfo res = context.getPackageManager().resolveActivity(intent, 0);
if(res.activityInfo == null)
{
return "";
}
//如果是不同桌面主题,可能会出现某些问题,这部分暂未处理
if(res.activityInfo.packageName.equals("android"))
{
return "";
}else
{
return res.activityInfo.packageName;
}
}
public static String get_test_time(String str,int day_number) throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
Date day = sdf.parse(str);
long ms = day.getTime() - day_number*24*3600*1000L+3*3600*1000L+25*60*1000L;
Date prevDay = new Date(ms);
System.out.println(sdf.format(prevDay));
return sdf.format(prevDay);
}
public static String current_time()
{
Date date = new Date();
String strDateFormat = "yyyy-MM-dd HH:mm";
SimpleDateFormat sdf = new SimpleDateFormat(strDateFormat);
System.out.println("current time:"+sdf.format(date));
return sdf.format(date);
}
/**
* 点击显示悬浮窗
*
* @param
*/
@SuppressLint("WrongConstant")
public void show(View v) {
wm = (WindowManager) getApplicationContext().getSystemService(
WINDOW_SERVICE); // 注意:这里必须是全局的context
// 判断UI控件是否存在,存在则移除,确保开启任意次应用都只有一个悬浮窗
if (countDownView != null) {
wm.removeView(countDownView);
}
/*
params = new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.TYPE_SYSTEM_ERROR,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSPARENT);
*/
params = new WindowManager.LayoutParams();
params.width = WindowManager.LayoutParams.WRAP_CONTENT;
params.height = WindowManager.LayoutParams.WRAP_CONTENT;
//params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
// 系统级别的窗口
params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
//| WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
// params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
// 居中显示
// params.gravity = Gravity.CENTER;
//params.gravity = Gravity.RIGHT|Gravity.BOTTOM; //悬浮窗开始在右下角显示
params.gravity = Gravity.LEFT | Gravity.TOP;
// 设置背景透明
params.format = PixelFormat.TRANSPARENT;
countDownView = new View(getApplicationContext()); // 不依赖activity的生命周期
countDownView = View.inflate(getApplicationContext(),
R.layout.countdown_weight, null);
tv_time = (TextView) countDownView.findViewById(R.id.tv_time);
twenty_four_hour = (ImageView) countDownView.findViewById(R.id.img);
RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) twenty_four_hour.getLayoutParams();
lp.width = alert_window_sp.getInt("window_width",500);
lp.height = alert_window_sp.getInt("window_height", 250);
twenty_four_hour.setLayoutParams(lp);
/*
try{
tv_time.setText(get_test_time(current_time(),1));
} catch (ParseException e) {
e.printStackTrace();
}
*/
wm.addView(countDownView, params);
viewAdded = true;
countDownView.setFocusableInTouchMode(true);
countDownView.requestFocus();
countDownView.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
// TODO Auto-generated method stub
Log.i(TAG, "setOnKeyListener onKey keyCode:"+keyCode);
if (keyCode == KeyEvent.KEYCODE_VOLUME_UP || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)
{
lock =(keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) ? true:false;
if(lock)
params.flags = 0 ;
else
params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
Toast.makeText(MainActivity.this, lock ? "Locked":"Unlocked", Toast.LENGTH_SHORT).show();
refresh();
return true;
}
return false;
}
public boolean onKeyDown(int keyCode, KeyEvent msg) {
Log.i(TAG, "setOnKeyListener onKeyDown");
return true;
}
});
/**
* 监听窗体移动事件
*/
countDownView.setOnTouchListener(new View.OnTouchListener() {
float[] temp = new float[] { 0f, 0f };
@RequiresApi(api = Build.VERSION_CODES.Q)
public boolean onTouch(View v, MotionEvent event) {
int PointerCount;
PointerCount = event.getPointerCount();
// Log.i(TAG, "PointerCount:"+PointerCount);
// Log.i(TAG, "temp[0]:" + temp[0]+" temp[1]:" + temp[1] + " X:" + event.getX()+
// " Y:"+event.getY() + " RawX:"+event.getRawX() + " RawY:"+event.getRawY()+" PointerCount:"+PointerCount);
int eventaction = event.getAction();
switch (eventaction & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: // 按下事件,记录按下时手指在悬浮窗的XY坐标值
temp[0] = event.getX();
temp[1] = event.getY();
remeberx = event.getRawX();
remebery = event.getRawY();
Log.i(TAG, " MotionEvent.ACTION_DOWN "+temp[0]+temp[1]+"\n");
break;
case MotionEvent.ACTION_POINTER_DOWN:
//屏幕上已经有一个点被按住了 第二个点被按下时触发该事件
Log.i(TAG, "MotionEvent.ACTION_POINTER_DOWN \n" + "downtempx:" + downtempx + " downtempy:" + downtempy);
lock_temp = true;
break;
case MotionEvent.ACTION_POINTER_UP:
Log.i(TAG, "MotionEvent.ACTION_POINTER_UP \n");
break;
case MotionEvent.ACTION_UP:
Log.i(TAG, "MotionEvent.ACTION_UP \n");
if(PointerCount == 1) {
}
lock_temp = false;
break;
case MotionEvent.ACTION_MOVE:
// Log.i(TAG, "MotionEvent.ACTION_MOVE event.getActionIndex():"+event.getActionIndex()+"\n");
// Log.i(TAG, "remeberx:"+remeberx+"remebery:"+remebery+
// " event.getRawX():"+event.getRawX()+" event.getRawY():"+event.getRawY()+"\n");
if(PointerCount == 2)
{
rangex = (float)((event.getRawX() - remeberx) * 0.3) ;
rangey = (float)((event.getRawY() - remebery) * 0.3) ;
// RelativeLayout twenty_four_hour = (RelativeLayout) countDownView.findViewById(R.id.twenty_four_hour);
RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) twenty_four_hour.getLayoutParams();
if((rangex > 1)||(rangex < -1)) {
lp.width = (int) (lp.width + rangex);
remeberx = event.getRawX();
}
if((rangey > 1)||(rangey < -1)) {
lp.height = (int) (lp.height + rangey);
remebery = event.getRawY();
}
alert_window_editor.putInt("window_width", lp.width);
alert_window_editor.putInt("window_height", lp.height);
alert_window_editor.commit();
twenty_four_hour.setLayoutParams(lp);
return false;
}
if(lock || lock_temp)
return true;
if(PointerCount == 1)
refreshView((int) (event.getRawX() - temp[0]), (int) (event.getRawY() - temp[1]));
break;
}
return true;
}
});
/*
cancle.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
Log.e(TAG, "取消倒计时");
wm.removeView(countDownView);
countDownView = null;
countDown.cancel();
mValue = 999;
}
});
*/
// 添加倒计时功能
countDown = new Timer();
countDown.schedule(new TimerTask() {
@Override
public void run() {
String FrontPackageName ="";
mValue++;
post.post(drawCount);
FrontPackageName = getFrontPackageName(getApplicationContext());
Log.i(TAG, "FrontPackageName = " + FrontPackageName);
Log.i(TAG, " getCurrentPkgName:" + getCurrentPkgName(getApplicationContext()));
if(FrontPackageName != null) {
//if(mValue > 20)
// twenty_four_hour.setVisibility(View.INVISIBLE);
if (FrontPackageName.contains(LauncherPackageName) && mValue > 100)
{
mValue = 0;
}
}
Log.i(TAG, " mValue:"+mValue);
if (mValue == 0) {
// 执行关机操作(这里可以使任意其他操作,根据自己的需求)
Log.e(TAG, "close");
wm.removeView(countDownView);
countDownView = null;
// 取消定时
countDown.cancel();
finish();
}
}
}, 0, 500);
// refreshView(295, 949);
statusBarHeight = 0 ;
refreshView(alert_window_sp.getInt("paramsx",250),alert_window_sp.getInt("paramsy",250));
finish();
}
private void refreshView(int x, int y) {
// 状态栏高度不能立即取,不然得到的值是0
if (statusBarHeight == 0) {
View rootView = countDownView.getRootView();
Rect r = new Rect();
rootView.getWindowVisibleDisplayFrame(r);
statusBarHeight = r.top;
}
params.x = x;
// y轴减去状态栏的高度,因为状态栏不是用户可以绘制的区域,不然拖动的时候会有跳动
params.y = y - statusBarHeight;// STATUS_HEIGHT;
Log.i(TAG, " x:"+x+" y:"+y+" params.x:"+params.x+" params.y:" +params.y +" statusBarHeight:"+statusBarHeight);
alert_window_editor.putInt("paramsx", params.x);
alert_window_editor.putInt("paramsy", params.y);
alert_window_editor.commit();
//Log.i(TAG, "MotionEvent.ACTION_UP SAVE ALERT WINDOW DATA params.x params.y\n");
refresh();
}
/**
* 添加悬浮窗或者更新悬浮窗 如果悬浮窗还没添加则添加 如果已经添加则更新其位置
*/
private void refresh() {
// 如果已经添加了就只更新view
if (viewAdded) {
wm.updateViewLayout(countDownView, params);
} else {
wm.addView(countDownView, params);
viewAdded = true;
}
}
/**
* 模拟其他操作
* @param view
*/
public void other(View view) {
Toast.makeText(context, "别的操作", Toast.LENGTH_SHORT).show();
ImageView twenty_four_hour = (ImageView) findViewById(R.id.img);
RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) twenty_four_hour.getLayoutParams();
lp.width = lp.width + 1;
twenty_four_hour.setLayoutParams(lp);
Log.i(TAG, "lp.width:"+lp.width+ " topMargin:"+lp.topMargin);
// startActivity(new Intent(context, NewActivity.class));
}
Runnable drawCount = new Runnable() {
@Override
public void run() {
//tv_time.setText(Integer.toString(mValue));
}
};
/**
* 获得top activity的包名
* @return
*/
public String getTopPackage(){
long ts = System.currentTimeMillis();
UsageStatsManager mUsageStatsManager = (UsageStatsManager)getSystemService(Context.USAGE_STATS_SERVICE);
List<UsageStats> usageStats = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_BEST, ts-1000, ts);
if (usageStats == null || usageStats.size() == 0) {//如果为空则返回""
return "";
}
Collections.sort(usageStats, new RecentUseComparator());//mRecentComp = new RecentUseComparator()
return usageStats.get(0).getPackageName();
}
static class RecentUseComparator implements Comparator<UsageStats> {
@Override
public int compare(UsageStats lhs, UsageStats rhs) {
return (lhs.getLastTimeUsed() > rhs.getLastTimeUsed()) ? -1 : (lhs.getLastTimeUsed() == rhs.getLastTimeUsed()) ? 0 : 1;
}
}
public static boolean needPermissionForBlocking(Context context) {
try {
PackageManager packageManager = context.getPackageManager();
ApplicationInfo applicationInfo = packageManager.getApplicationInfo(context.getPackageName(), 0);
AppOpsManager appOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
int mode = appOpsManager.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS, applicationInfo.uid, applicationInfo.packageName);
return (mode != AppOpsManager.MODE_ALLOWED);
} catch (PackageManager.NameNotFoundException e) {
return true;
}
}
public String getFrontPackageName(Context context) {
String topPackageName = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
UsageStatsManager mUsageStatsManager = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
long time = System.currentTimeMillis();
// We get usage stats for the last 10 seconds
List<UsageStats> stats = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, time - 1000 * 10, time);
// Sort the stats by the last time used
if (stats != null) {
SortedMap<Long, UsageStats> mySortedMap = new TreeMap<>();
for (UsageStats usageStats : stats) {
mySortedMap.put(usageStats.getLastTimeUsed(), usageStats);
}
if (mySortedMap != null && !mySortedMap.isEmpty()) {
topPackageName = mySortedMap.get(mySortedMap.lastKey()).getPackageName();
return topPackageName;
}
}
}
return null;
}
/**
* 注意: getRunningAppProcesses()方法在5.0开始,就只返回自身应用的进程,所以只能判断自身进程状态,
* 如果是400,返回为null,不能拿到当前栈顶Activity的包名
*
* @param context
* @return
*/
private static String getCurrentPkgName(Context context) {
// 5x系统以后利用反射获取当前栈顶activity的包名.
ActivityManager.RunningAppProcessInfo currentInfo = null;
Field field = null;
int startTaskToFront = 2;
String pkgName = null;
try {
// 通过反射获取进程状态字段.
field = ActivityManager.RunningAppProcessInfo.class.getDeclaredField("processState");
} catch (Exception e) {
e.printStackTrace();
}
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List appList = am.getRunningAppProcesses();
ActivityManager.RunningAppProcessInfo app;
for (int i = 0; i < appList.size(); i++) {
//ActivityManager.RunningAppProcessInfo app : appList
app = (ActivityManager.RunningAppProcessInfo) appList.get(i);
//表示前台运行进程.
if (app.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
Integer state = null;
try {
// 反射调用字段值的方法,获取该进程的状态.
state = field.getInt(app);
} catch (Exception e) {
e.printStackTrace();
}
// 根据这个判断条件从前台中获取当前切换的进程对象
if (state != null && state == startTaskToFront) {
currentInfo = app;
break;
}
}
}
if (currentInfo != null) {
pkgName = currentInfo.processName;
}
return pkgName;
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.e(TAG, "倒计时结束");
};
}