사용자가 현재 어떠한 앱을 사용하는지 확인하는 방법을 찾던 중 UsageStatsManage를 찾게 되었다.
UsageStatsManage의 구현 방법은 버튼을 누르고 난 후에 백그라운드에서 현재 어떠한 앱을 사용하는 지 체크 후 로그에 띄우는 방식으로 구현해 보았다.
우선 mainfests -> AndroidManifest.xml에서 다른 앱의 정보를 얻기 위한 권한을 설정한다.
<!-- UsageStatsManager - 다른 앱의 정보를 얻기 위해서 필요한 권환-->
<!-- 오류가 나는 경우 alt+enter 또는 xmlns:tools="http://schemas.android.com/tools" 을 추가 -->
<uses-permission
android:name="android.permission.PACKAGE_USAGE_STATS"
tools:ignore="ProtectedPermissions" />
activity_main.xml에서는 간단하게 시작 버튼과 종료 버튼만을 만들어 구현한다.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/start_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.499" />
<Button
android:id="@+id/end_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="49dp"
android:layout_marginEnd="1dp"
android:text="Button"
app:layout_constraintEnd_toEndOf="@+id/start_button"
app:layout_constraintTop_toBottomOf="@+id/start_button" />
</androidx.constraintlayout.widget.ConstraintLayout>
다음은 MainActivity 코드이다. 주석을 잘 확인하면서 코드를 적으면 좋을 것 같다.
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import android.app.AppOpsManager;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStatsManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.util.LongSparseArray;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
CheckPackageNameThread checkPackageNameThread;
boolean operation = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 버튼 정의
Button start_button = findViewById(R.id.start_button);
Button end_button = findViewById(R.id.end_button);
// 시작 버튼 이벤트
start_button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// 권환 허용이 안되어 있으면 권환 설정창으로 이동
if(!checkPermission()) {
Intent PermissionIntent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS, Uri.parse("package:" + getPackageName()));
startActivity(PermissionIntent);
}
// 권환 허용 되어 있으면 현재 포그라운드 앱 패키지 로그로 띄운다.
else{
operation = true;
checkPackageNameThread = new CheckPackageNameThread();
checkPackageNameThread.start();
}
}
});
// 종료 버튼 이벤트
end_button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
operation = false;
}
});
}
// 현재 포그라운드 앱 패키지 로그로 띄우는 함수
private class CheckPackageNameThread extends Thread{
public void run(){
// operation == true 일때만 실행
while(operation){
if(!checkPermission())
continue;
// 현재 포그라운드 앱 패키지 이름 가져오기
System.out.println(getPackageName(getApplicationContext()));
try {
// 2초마다 패키치 이름을 로그창에 출력
sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
// 권환 체크
private boolean checkPermission(){
boolean granted = false;
AppOpsManager appOps = (AppOpsManager) getApplicationContext()
.getSystemService(Context.APP_OPS_SERVICE);
int mode = appOps.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS,
android.os.Process.myUid(), getApplicationContext().getPackageName());
if (mode == AppOpsManager.MODE_DEFAULT) {
granted = (getApplicationContext().checkCallingOrSelfPermission(
android.Manifest.permission.PACKAGE_USAGE_STATS) == PackageManager.PERMISSION_GRANTED);
}
else {
granted = (mode == AppOpsManager.MODE_ALLOWED);
}
return granted;
}
// 자신의 앱의 최소 타겟을 롤리팝 이전으로 설정
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
// 현재 포그라운드 앱 패키지를 가져오는 함수
public static String getPackageName(@NonNull Context context) {
// UsageStatsManager 선언
UsageStatsManager usageStatsManager = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
// 마지막 실행 앱 타임스탬프
long lastRunAppTimeStamp = 0L;
// 얼마만큼의 시간동안 수집한 앱의 이름을 가져오는지 정하기 (begin ~ end 까지의 앱 이름을 수집한다)
final long INTERVAL = 1000 * 60 * 5;
final long end = System.currentTimeMillis();
final long begin = end - INTERVAL; // 5분전
LongSparseArray packageNameMap = new LongSparseArray<>();
// 수집한 이벤트들을 담기 위한 UsageEvents
final UsageEvents usageEvents = usageStatsManager.queryEvents(begin, end);
// 이벤트가 여러개 있을 경우 (최소 존재는 해야 hasNextEvent가 null이 아니니까)
while (usageEvents.hasNextEvent()) {
// 현재 이벤트를 가져오기
UsageEvents.Event event = new UsageEvents.Event();
usageEvents.getNextEvent(event);
// 현재 이벤트가 포그라운드 상태라면(현재 화면에 보이는 앱이라면)
if(isForeGroundEvent(event)) {
// 해당 앱 이름을 packageNameMap에 넣는다.
packageNameMap.put(event.getTimeStamp(), event.getPackageName());
// 가장 최근에 실행 된 이벤트에 대한 타임스탬프를 업데이트 해준다.
if(event.getTimeStamp() > lastRunAppTimeStamp) {
lastRunAppTimeStamp = event.getTimeStamp();
}
}
}
// 가장 마지막까지 있는 앱의 이름을 리턴해준다.
return packageNameMap.get(lastRunAppTimeStamp, "").toString();
}
// 앱이 포그라운드 상태인지 체크
private static boolean isForeGroundEvent(UsageEvents.Event event) {
// 이벤트가 없으면 false 반환
if(event == null)
return false;
// 이벤트가 포그라운드 상태라면 true 반환
if(BuildConfig.VERSION_CODE >= 29)
return event.getEventType() == UsageEvents.Event.ACTIVITY_RESUMED;
return event.getEventType() == UsageEvents.Event.MOVE_TO_FOREGROUND;
}
}
코드 흐름을 보게되면 권한을 체크하고 권한 설정이 안되어 있으면 권한 설정 창을 띄어 권한을 허용할건지 체크하고 권한이 허용되어 있으면 현재 포그라운드 앱 패키지 이름을 로그로 띄운다.
위 코드를 다 적고 실행시키면 다음 사진과 같이 2초 간격으로 사용자가 들어간 앱 패키지 이름을 로그로 나오는 것을 확인할 수 있다.
참고
github
'Develop > Java' 카테고리의 다른 글
[Java] 자바 Android 권한 요청 한번에 처리하기 (4) | 2021.08.07 |
---|---|
[Java] 자바 Android 액티비티나 서비스에서 다른 서비스 종료시키기 (0) | 2021.08.05 |
[AWS] 아마존 웹 서비스 Android + Amazon Cognito 에러 메시지 (0) | 2021.07.21 |
[Java] 자바 Android 다이얼로그(Dialog) 구현 (0) | 2021.07.19 |
[AWS] 아마존 웹 서비스 Android + Amazon Cognito 비빌번호 재설정 (0) | 2021.07.18 |