Skip to content

Commit

Permalink
Merge pull request #87 from akaMrNagar/hotfix
Browse files Browse the repository at this point in the history
More accurate screen usage along with bug fixes
  • Loading branch information
akaMrNagar authored Oct 5, 2024
2 parents e1b9e35 + 0b6a6ad commit dfaae27
Show file tree
Hide file tree
Showing 13 changed files with 231 additions and 160 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -100,15 +100,6 @@ public void unBindService() {
}
}

/**
* Starts the service if it is not already running.
*/
public void startOnly(String action) {
if (!Utils.isServiceRunning(mContext, mServiceClass.getName())) {
mContext.startService(new Intent(mContext, mServiceClass).setAction(action));
}
}

/**
* Starts and binds the service if it is not already running.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ private static List<AndroidApp> fetchAppsAndUsage(@NonNull Context context) {
long screenUsageStart = screenUsageCal.getTimeInMillis();
long dataUsageStart = dataUsageCal.getTimeInMillis();

HashMap<String, Long> screenUsageOneDay = ScreenUsageHelper.fetchUsageForInterval(usageStatsManager, screenUsageStart, screenUsageStart + ms24Hours);
HashMap<String, Long> screenUsageOneDay = ScreenUsageHelper.fetchUsageForInterval(usageStatsManager, screenUsageStart, screenUsageStart + ms24Hours, null);
HashMap<Integer, Long> mobileUsageOneDay = NetworkUsageHelper.fetchMobileUsageForInterval(networkStatsManager, dataUsageStart, dataUsageStart + ms24Hours);
HashMap<Integer, Long> wifiUsageOneDay = NetworkUsageHelper.fetchWifiUsageForInterval(networkStatsManager, dataUsageStart, dataUsageStart + ms24Hours);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import java.util.Calendar;
import java.util.HashMap;
Expand All @@ -30,14 +31,17 @@ public class ScreenUsageHelper {

/**
* Fetches screen usage statistics for a specified time interval using usage events.
* If the target package is not null then this method will fetch usage for that app only
* otherwise for all device apps.
*
* @param usageStatsManager The UsageStatsManager used to query screen usage data.
* @param start The start time of the interval in milliseconds.
* @param end The end time of the interval in milliseconds.
* @param targetedPackage The package name of the app for fetching its screen usage.
* @return A map with package names as keys and their corresponding screen usage time in seconds as values.
*/
@NonNull
public static HashMap<String, Long> fetchUsageForInterval(@NonNull UsageStatsManager usageStatsManager, long start, long end) {
public static HashMap<String, Long> fetchUsageForInterval(@NonNull UsageStatsManager usageStatsManager, long start, long end, @Nullable String targetedPackage) {
HashMap<String, Long> oneDayUsageMap = new HashMap<>();
UsageEvents usageEvents = usageStatsManager.queryEvents(start, end);
Map<String, UsageEvents.Event> lastResumedEvents = new HashMap<>();
Expand All @@ -47,20 +51,30 @@ public static HashMap<String, Long> fetchUsageForInterval(@NonNull UsageStatsMan
while (usageEvents.hasNextEvent()) {
UsageEvents.Event currentEvent = new UsageEvents.Event(); // Do not move this from while loop
usageEvents.getNextEvent(currentEvent);
String packageName = currentEvent.getPackageName();
int eventType = currentEvent.getEventType();

String packageName = currentEvent.getPackageName();
/// If target package is not null
if (targetedPackage != null && !packageName.equals(targetedPackage)) continue;

String className = currentEvent.getClassName();
String eventKey = packageName + className;

if (eventType == UsageEvents.Event.ACTIVITY_RESUMED) {
lastResumedEvents.put(packageName, currentEvent);
lastResumedEvents.put(eventKey, currentEvent);
isFirstEvent = false;
} else if (eventType == UsageEvents.Event.ACTIVITY_PAUSED) {
} else if (eventType == UsageEvents.Event.ACTIVITY_STOPPED || eventType == UsageEvents.Event.ACTIVITY_PAUSED) {
Long screenTime = oneDayUsageMap.getOrDefault(packageName, 0L);
UsageEvents.Event lastResumedEvent = lastResumedEvents.get(packageName);
if (lastResumedEvent != null && lastResumedEvent.getPackageName().equals(packageName)) {
UsageEvents.Event lastResumedEvent = lastResumedEvents.get(eventKey);

if (lastResumedEvent != null
&& lastResumedEvent.getPackageName().equals(packageName)
&& lastResumedEvent.getClassName().equals(className)
) {
// Calculate usage from the last ACTIVITY_RESUMED to this ACTIVITY_PAUSED
screenTime += (currentEvent.getTimeStamp() - lastResumedEvent.getTimeStamp());
oneDayUsageMap.put(packageName, screenTime);
lastResumedEvents.remove(packageName);
lastResumedEvents.remove(eventKey);
} else if (isFirstEvent) {
// Fallback logic in case no matching ACTIVITY_RESUMED was found. May be this app was opened before START time
screenTime += (currentEvent.getTimeStamp() - start);
Expand Down Expand Up @@ -90,36 +104,9 @@ public static long fetchAppUsageTodayTillNow(@NonNull UsageStatsManager usageSta

long start = midNightCal.getTimeInMillis();
long end = System.currentTimeMillis();
UsageEvents usageEvents = usageStatsManager.queryEvents(start, end);
UsageEvents.Event lastAppResumeEvent = null;
long screenTime = 0;


while (usageEvents.hasNextEvent()) {
UsageEvents.Event currentEvent = new UsageEvents.Event(); // Do not move this from while loop
usageEvents.getNextEvent(currentEvent);

// Skip it if the event does not belong to the specified app
if (!currentEvent.getPackageName().equals(packageName)) continue;

int eventType = currentEvent.getEventType();
if (eventType == UsageEvents.Event.ACTIVITY_RESUMED) {
lastAppResumeEvent = currentEvent;
} else if (eventType == UsageEvents.Event.ACTIVITY_PAUSED) {
if (lastAppResumeEvent != null) {
screenTime += (currentEvent.getTimeStamp() - lastAppResumeEvent.getTimeStamp());
} else {
screenTime += (currentEvent.getTimeStamp() - start);
}
}
}

// If the app is still open (no PAUSED event yet), calculate time till `end`
if (lastAppResumeEvent != null && lastAppResumeEvent.getTimeStamp() < end) {
screenTime += (end - lastAppResumeEvent.getTimeStamp());
}
long screenTime = fetchUsageForInterval(usageStatsManager, start, end, packageName).getOrDefault(packageName, 0L);

Log.d("Time", "fetchAppUsageFromEvents: package: " + packageName + " screen time seconds : " + (screenTime / 1000));
return (screenTime / 1000);
Log.d("Time", "fetchAppUsageFromEvents: package: " + packageName + " screen time seconds : " + screenTime);
return screenTime;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@

package com.mindful.android.helpers;

import static com.mindful.android.services.EmergencyPauseService.DEFAULT_EMERGENCY_PASSES_COUNT;

import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
Expand All @@ -20,7 +22,6 @@

import com.mindful.android.models.BedtimeSettings;
import com.mindful.android.models.WellBeingSettings;
import com.mindful.android.utils.AppConstants;
import com.mindful.android.utils.Utils;

import org.jetbrains.annotations.Contract;
Expand Down Expand Up @@ -66,8 +67,8 @@ public static String fetchLocale(Context context) {
/**
* Stores the current locale
*
* @param context The application context.
* @param languageCode The language code for locale.
* @param context The application context.
* @param languageCode The language code for locale.
*/
public static void storeLocale(Context context, String languageCode) {
checkAndInitializePrefs(context);
Expand Down Expand Up @@ -114,7 +115,10 @@ public static void registerListener(Context context, SharedPreferences.OnSharedP
*/
public static void unregisterListener(Context context, SharedPreferences.OnSharedPreferenceChangeListener callback) {
checkAndInitializePrefs(context);
mSharedPrefs.unregisterOnSharedPreferenceChangeListener(callback);
try {
mSharedPrefs.unregisterOnSharedPreferenceChangeListener(callback);
} catch (Exception ignored) {
}
}

/**
Expand Down Expand Up @@ -235,7 +239,7 @@ public static int fetchNotificationAskCount(@NonNull Context context) {
*/
public static int fetchEmergencyPassesCount(@NonNull Context context) {
if (mSharedPrefs == null) checkAndInitializePrefs(context);
return mSharedPrefs.getInt(PREF_KEY_EMERGENCY_PASSES_COUNT, AppConstants.DEFAULT_EMERGENCY_PASSES_COUNT);
return mSharedPrefs.getInt(PREF_KEY_EMERGENCY_PASSES_COUNT, DEFAULT_EMERGENCY_PASSES_COUNT);
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
*
* *
* * * Copyright (c) 2024 Mindful (https://github.com/akaMrNagar/Mindful)
* * * Author : Pawan Nagar (https://github.com/akaMrNagar)
* * *
* * * This source code is licensed under the GPL-2.0 license license found in the
* * * LICENSE file in the root directory of this source tree.
* *
*
*/

package com.mindful.android.models;

public class AggregatedUsage {
public final int totalScreenUsageMins;
public final int totalMobileUsageMBs;
public final int totalWifiUsageMBs;

public AggregatedUsage(int totalScreenUsageMins, int totalMobileUsageMBs, int totalWifiUsageMBs) {
this.totalScreenUsageMins = totalScreenUsageMins;
this.totalMobileUsageMBs = totalMobileUsageMBs;
this.totalWifiUsageMBs = totalWifiUsageMBs;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.util.Log;

import com.mindful.android.generics.SafeServiceConnection;
import androidx.annotation.NonNull;

import com.mindful.android.helpers.AlarmTasksSchedulingHelper;
import com.mindful.android.helpers.SharedPrefsHelper;
import com.mindful.android.services.MindfulTrackerService;
Expand All @@ -44,23 +46,43 @@ public void onReceive(Context context, Intent intent) {
// Fetch bedtime settings to check if the bedtime schedule is on
boolean isBedtimeScheduleOn = SharedPrefsHelper.fetchBedtimeSettings(context).isScheduleOn;

// Start needful services
startServices(context);

// Reschedule bedtime workers if the bedtime schedule is on
if (isBedtimeScheduleOn) AlarmTasksSchedulingHelper.scheduleBedtimeStartTask(context);

// Reschedule midnight reset worker
AlarmTasksSchedulingHelper.scheduleMidnightResetTask(context, false);
}
}

private void startServices(@NonNull Context context) {
try {
// Start the MindfulTrackerService if needed
if (!SharedPrefsHelper.fetchAppTimers(context).isEmpty()) {
SafeServiceConnection<MindfulTrackerService> mTrackerServiceConn = new SafeServiceConnection<>(MindfulTrackerService.class, context);
mTrackerServiceConn.startOnly(MindfulTrackerService.ACTION_START_SERVICE_TIMER_MODE);
Intent serviceIntent = new Intent(context, MindfulTrackerService.class);
serviceIntent.setAction(MindfulTrackerService.ACTION_START_SERVICE_TIMER_MODE);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(serviceIntent);
} else {
context.startService(serviceIntent);
}
}

// Start the MindfulVpnService if needed
if (!SharedPrefsHelper.fetchBlockedApps(context).isEmpty() && MindfulVpnService.prepare(context) == null) {
SafeServiceConnection<MindfulVpnService> mVpnServiceConn = new SafeServiceConnection<>(MindfulVpnService.class, context);
mVpnServiceConn.startOnly(MindfulVpnService.ACTION_START_SERVICE_VPN);
}
Intent serviceIntent = new Intent(context, MindfulVpnService.class);
serviceIntent.setAction(MindfulVpnService.ACTION_START_SERVICE_VPN);

// Reschedule bedtime workers if the bedtime schedule is on
if (isBedtimeScheduleOn) AlarmTasksSchedulingHelper.scheduleBedtimeStartTask(context);

// Reschedule midnight reset worker
AlarmTasksSchedulingHelper.scheduleMidnightResetTask(context, false);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(serviceIntent);
} else {
context.startService(serviceIntent);
}
}
} catch (Exception ignored) {
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@

package com.mindful.android.receivers.alarm;

import static com.mindful.android.services.EmergencyPauseService.DEFAULT_EMERGENCY_PASSES_COUNT;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
Expand All @@ -23,7 +25,6 @@
import com.mindful.android.helpers.SharedPrefsHelper;
import com.mindful.android.services.MindfulAccessibilityService;
import com.mindful.android.services.MindfulTrackerService;
import com.mindful.android.utils.AppConstants;
import com.mindful.android.utils.Utils;

public class MidnightResetReceiver extends BroadcastReceiver {
Expand All @@ -36,29 +37,35 @@ public class MidnightResetReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, @NonNull Intent intent) {
if (ACTION_START_MIDNIGHT_RESET.equals(intent.getAction())) {
try {
onMidnightReset(context);
Log.d(TAG, "onReceive: Midnight reset task completed successfully");
} catch (Exception ignored) {
}

// Reset emergency passes count to default value
SharedPrefsHelper.storeEmergencyPassesCount(context, AppConstants.DEFAULT_EMERGENCY_PASSES_COUNT);
// Schedule task for next day
AlarmTasksSchedulingHelper.scheduleMidnightResetTask(context, false);
}
}

// Let tracking service know about midnight reset
if (Utils.isServiceRunning(context, MindfulTrackerService.class.getName())) {
Intent serviceIntent = new Intent(context, MindfulTrackerService.class).setAction(ACTION_MIDNIGHT_SERVICE_RESET);
context.startService(serviceIntent);
}

// Let accessibility service know about midnight reset
if (Utils.isServiceRunning(context, MindfulAccessibilityService.class.getName())) {
Intent serviceIntent = new Intent(context, MindfulAccessibilityService.class).setAction(ACTION_MIDNIGHT_SERVICE_RESET);
context.startService(serviceIntent);
} else {
// Else at least reset short content screen time
SharedPrefsHelper.storeShortsScreenTimeMs(context, 0);
}
private void onMidnightReset(@NonNull Context context) {
// Reset emergency passes count to default value
SharedPrefsHelper.storeEmergencyPassesCount(context, DEFAULT_EMERGENCY_PASSES_COUNT);

Log.d(TAG, "onReceive: Midnight reset task completed successfully");
// Let tracking service know about midnight reset
if (Utils.isServiceRunning(context, MindfulTrackerService.class.getName())) {
Intent serviceIntent = new Intent(context, MindfulTrackerService.class).setAction(ACTION_MIDNIGHT_SERVICE_RESET);
context.startService(serviceIntent);
}

// Schedule task for next day
AlarmTasksSchedulingHelper.scheduleMidnightResetTask(context, false);
// Let accessibility service know about midnight reset
if (Utils.isServiceRunning(context, MindfulAccessibilityService.class.getName())) {
Intent serviceIntent = new Intent(context, MindfulAccessibilityService.class).setAction(ACTION_MIDNIGHT_SERVICE_RESET);
context.startService(serviceIntent);
} else {
// Else at least reset short content screen time
SharedPrefsHelper.storeShortsScreenTimeMs(context, 0);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@

package com.mindful.android.services;

import static com.mindful.android.utils.AppConstants.DEFAULT_EMERGENCY_PASS_PERIOD_MS;
import static com.mindful.android.utils.AppConstants.EMERGENCY_PAUSE_SERVICE_NOTIFICATION_ID;

import android.app.Notification;
Expand All @@ -36,6 +35,8 @@
import com.mindful.android.utils.Utils;

public class EmergencyPauseService extends Service {
public static final int DEFAULT_EMERGENCY_PASSES_COUNT = 3;
private static final int DEFAULT_EMERGENCY_PASS_PERIOD_MS = 5 * 60 * 1000;
private static final String TAG = "Mindful.EmergencyPauseService";
public static final String ACTION_START_SERVICE_EMERGENCY = "com.mindful.android.EmergencyPauseService.START_SERVICE_EMERGENCY";

Expand Down
Loading

0 comments on commit dfaae27

Please sign in to comment.