Skip to content

Commit ef8d557

Browse files
authored
Merge pull request #142 from akaMrNagar/dev
Fix: Active period not working properly and Optimized app usage loading
2 parents b57d83c + b63d1ad commit ef8d557

File tree

13 files changed

+111
-178
lines changed

13 files changed

+111
-178
lines changed

android/app/src/main/java/com/mindful/android/MainActivity.java

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -136,21 +136,17 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result
136136
break;
137137
}
138138
case "getDeviceApps": {
139-
DeviceAppsHelper.getDeviceApps(this, result);
139+
DeviceAppsHelper.getDeviceApps(
140+
this,
141+
result,
142+
mTrackerServiceConn.isConnected() ? mTrackerServiceConn.getService().getAppsLaunchCount() : new HashMap<>(0)
143+
);
140144
break;
141145
}
142146
case "getShortsScreenTimeMs": {
143147
result.success(SharedPrefsHelper.getSetShortsScreenTimeMs(this, null));
144148
break;
145149
}
146-
case "getAppLaunchCounts": {
147-
result.success(
148-
mTrackerServiceConn.isConnected() ?
149-
mTrackerServiceConn.getService().getAppsLaunchCount() :
150-
new HashMap<String, Integer>(0)
151-
);
152-
break;
153-
}
154150
case "setDataResetTime": {
155151
SharedPrefsHelper.getSetDataResetTimeMins(this, call.arguments() == null ? 0 : call.arguments());
156152
result.success(true);

android/app/src/main/java/com/mindful/android/helpers/DeviceAppsHelper.java

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import android.content.pm.ApplicationInfo;
2121
import android.content.pm.PackageInfo;
2222
import android.content.pm.PackageManager;
23-
import android.os.Build;
2423
import android.os.Handler;
2524
import android.os.Looper;
2625

@@ -57,7 +56,11 @@ public class DeviceAppsHelper {
5756
* @param context The context to use for fetching app information.
5857
* @param channelResult The result channel to which the app data will be sent.
5958
*/
60-
public static void getDeviceApps(Context context, MethodChannel.Result channelResult) {
59+
public static void getDeviceApps(
60+
Context context,
61+
MethodChannel.Result channelResult,
62+
HashMap<String, Integer> appsLaunchCountMap
63+
) {
6164
SuccessCallback<List<Map<String, Object>>> callback = new SuccessCallback<List<Map<String, Object>>>() {
6265
@Override
6366
public void onSuccess(List<Map<String, Object>> result) {
@@ -68,7 +71,7 @@ public void onSuccess(List<Map<String, Object>> result) {
6871
new Thread(new Runnable() {
6972
@Override
7073
public void run() {
71-
List<AndroidApp> apps = fetchAppsAndUsage(context);
74+
List<AndroidApp> apps = fetchAppsAndUsage(context, appsLaunchCountMap);
7275
List<Map<String, Object>> resultMap = new ArrayList<>(apps.size());
7376

7477
for (AndroidApp app : apps) {
@@ -81,7 +84,7 @@ public void run() {
8184
}
8285

8386
@NonNull
84-
private static List<AndroidApp> fetchAppsAndUsage(@NonNull Context context) {
87+
private static List<AndroidApp> fetchAppsAndUsage(@NonNull Context context, @NonNull HashMap<String, Integer> appsLaunchCountMap) {
8588
PackageManager packageManager = context.getPackageManager();
8689

8790
// Fetch set of important apps like Dialer, Launcher etc.
@@ -95,12 +98,6 @@ private static List<AndroidApp> fetchAppsAndUsage(@NonNull Context context) {
9598
for (PackageInfo app : fetchedApps) {
9699
// Only include apps which are launchable
97100
if (packageManager.getLaunchIntentForPackage(app.packageName) != null) {
98-
99-
int category = -1;
100-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
101-
category = app.applicationInfo.category;
102-
}
103-
104101
// Check if the app is important or default to system like dialer and launcher
105102
boolean isSysDefault = impSystemApps.contains(app.packageName);
106103
deviceApps.add(
@@ -109,7 +106,7 @@ private static List<AndroidApp> fetchAppsAndUsage(@NonNull Context context) {
109106
app.packageName, // package name
110107
Utils.getEncodedAppIcon(packageManager.getApplicationIcon(app.applicationInfo)), // icon
111108
isSysDefault, // is default app used by system like dialer or launcher
112-
category, // category
109+
appsLaunchCountMap.getOrDefault(app.packageName, 0), // launch count for today
113110
app.applicationInfo.uid // app uid
114111
)
115112
);

android/app/src/main/java/com/mindful/android/helpers/ScreenUsageHelper.java

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121
import java.util.Calendar;
2222
import java.util.HashMap;
23-
import java.util.Map;
23+
import java.util.LinkedHashMap;
2424

2525
/**
2626
* ScreenUsageHelper provides utility methods for gathering and calculating screen usage statistics
@@ -43,7 +43,7 @@ public class ScreenUsageHelper {
4343
public static HashMap<String, Long> fetchUsageForInterval(@NonNull UsageStatsManager usageStatsManager, long start, long end, @Nullable String targetedPackage) {
4444
HashMap<String, Long> oneDayUsageMap = new HashMap<>();
4545
UsageEvents usageEvents = usageStatsManager.queryEvents(start, end);
46-
Map<String, UsageEvents.Event> lastResumedEvents = new HashMap<>();
46+
LinkedHashMap<String, UsageEvents.Event> lastResumedEvents = new LinkedHashMap<>(2);
4747
boolean isFirstEvent = true;
4848

4949

@@ -86,27 +86,6 @@ public static HashMap<String, Long> fetchUsageForInterval(@NonNull UsageStatsMan
8686
return oneDayUsageMap;
8787
}
8888

89-
/**
90-
* Fetches the screen usage time of a specific application for the current day until now using usage events.
91-
*
92-
* @param usageStatsManager The UsageStatsManager used to query screen usage data.
93-
* @param packageName The package name of the application whose usage time is to be fetched.
94-
* @return The total screen usage time of the specified application in seconds.
95-
*/
96-
public static int fetchAppUsageTodayTillNow(@NonNull UsageStatsManager usageStatsManager, String packageName) {
97-
Calendar midNightCal = Calendar.getInstance();
98-
midNightCal.set(Calendar.HOUR_OF_DAY, 0);
99-
midNightCal.set(Calendar.MINUTE, 0);
100-
midNightCal.set(Calendar.SECOND, 0);
101-
102-
long start = midNightCal.getTimeInMillis();
103-
long end = System.currentTimeMillis();
104-
long screenTime = fetchUsageForInterval(usageStatsManager, start, end, packageName).getOrDefault(packageName, 0L);
105-
106-
// Log.d("Time", "fetchAppUsageFromEvents: package: " + packageName + " screen time seconds : " + screenTime);
107-
return (int) screenTime;
108-
}
109-
11089
/**
11190
* Fetches the screen usage time of a all installed application for the current day until now using usage events.
11291
*

android/app/src/main/java/com/mindful/android/models/AndroidApp.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public class AndroidApp {
2929
public String packageName;
3030
public int appUid;
3131
public String appIcon;
32-
public int category;
32+
public int launchCount;
3333
public boolean isImpSysApp;
3434

3535
// Usage info
@@ -45,15 +45,15 @@ public class AndroidApp {
4545
* @param packageName The package name of the application.
4646
* @param appIcon The icon of the application.
4747
* @param isImpSysApp Indicates if the application is an important system app.
48-
* @param category The category of the application.
48+
* @param launchCount The launch count of the application.
4949
* @param appUid The UID of the application.
5050
*/
51-
public AndroidApp(String appName, String packageName, String appIcon, boolean isImpSysApp, int category, int appUid) {
51+
public AndroidApp(String appName, String packageName, String appIcon, boolean isImpSysApp, int launchCount, int appUid) {
5252
this.appName = appName;
5353
this.packageName = packageName;
5454
this.appIcon = appIcon;
5555
this.isImpSysApp = isImpSysApp;
56-
this.category = category;
56+
this.launchCount = launchCount;
5757
this.appUid = appUid;
5858
// Initialize usage arrays with 7 days worth of data, defaulting to 0
5959
this.screenTimeThisWeek = new ArrayList<>(Collections.nCopies(7, 0L));
@@ -85,7 +85,7 @@ public Map<String, Object> toMap() {
8585
appMap.put("packageName", packageName);
8686
appMap.put("appIcon", appIcon);
8787
appMap.put("isImpSysApp", isImpSysApp);
88-
appMap.put("category", category);
88+
appMap.put("launchCount", launchCount);
8989
appMap.put("screenTimeThisWeek", screenTimeThisWeek);
9090
appMap.put("mobileUsageThisWeek", mobileUsageThisWeek);
9191
appMap.put("wifiUsageThisWeek", wifiUsageThisWeek);
@@ -106,7 +106,7 @@ public String toString() {
106106
", packageName='" + packageName + '\'' +
107107
", appUid=" + appUid +
108108
", isImpSysApp=" + isImpSysApp +
109-
", category=" + category +
109+
", launchCount=" + launchCount +
110110
", screenTimeThisWeek=" + screenTimeThisWeek +
111111
", mobileUsageThisWeek=" + mobileUsageThisWeek +
112112
", wifiUsageThisWeek=" + wifiUsageThisWeek +

android/app/src/main/java/com/mindful/android/services/MindfulTrackerService.java

Lines changed: 43 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -250,12 +250,13 @@ private void onNewAppLaunched(String packageName) {
250250
/// Return if no restriction applied
251251
AppRestrictions appRestrictions = mAppsRestrictions.get(packageName);
252252
if (appRestrictions == null) return;
253-
long recallDelayMS = Long.MAX_VALUE;
254-
PurgedReason recallReason = null;
255-
boolean isPeriodRecall = false;
253+
PurgedReason timerReason = null;
254+
boolean isActivePeriodTimer = false;
255+
long timerDelayMS = Long.MAX_VALUE;
256256

257257
/// Check for app launch limit
258258
if (appRestrictions.launchLimit > 0 && launchCount > appRestrictions.launchLimit) {
259+
Log.d(TAG, "onNewAppLaunched: App's launch limit ran out");
259260
PurgedReason reason = new PurgedReason(getString(R.string.app_paused_dialog_info_for_launch_count_out));
260261
mPurgedApps.put(packageName, reason);
261262
showOverlayDialog(packageName, reason);
@@ -268,17 +269,17 @@ private void onNewAppLaunched(String packageName) {
268269

269270
/// Outside active period
270271
if (Utils.isTimeOutsideTODs(appRestrictions.activePeriodStart, appRestrictions.activePeriodEnd)) {
271-
mPurgedApps.put(packageName, reason);
272+
Log.d(TAG, "onNewAppLaunched: App's active period is over");
272273
showOverlayDialog(packageName, reason);
273274
return;
274275
}
275276

276277
/// Between active period so update recall delay
277278
long willOverInMs = Utils.todDifferenceFromNow(appRestrictions.activePeriodEnd);
278-
if (willOverInMs < recallDelayMS) {
279-
recallDelayMS = willOverInMs;
280-
isPeriodRecall = true;
281-
recallReason = reason;
279+
if (willOverInMs < timerDelayMS) {
280+
timerReason = reason;
281+
isActivePeriodTimer = true;
282+
timerDelayMS = willOverInMs;
282283
}
283284
}
284285

@@ -291,6 +292,7 @@ private void onNewAppLaunched(String packageName) {
291292

292293
/// App timer ran out
293294
if (appScreenTimeSec >= appRestrictions.timerSec) {
295+
Log.d(TAG, "onNewAppLaunched: App's timer is over");
294296
PurgedReason reason = new PurgedReason(getString(R.string.app_paused_dialog_info_for_app_timer_out), appRestrictions.timerSec, appScreenTimeSec);
295297
mPurgedApps.put(packageName, reason);
296298
showOverlayDialog(packageName, reason);
@@ -299,10 +301,10 @@ private void onNewAppLaunched(String packageName) {
299301

300302
/// App timer left so update recall delay
301303
long leftAppLimitMs = (appRestrictions.timerSec - appScreenTimeSec) * 1000;
302-
if (leftAppLimitMs < recallDelayMS) {
303-
recallDelayMS = leftAppLimitMs;
304-
recallReason = new PurgedReason(getString(R.string.app_paused_dialog_info_for_app_timer_left), appRestrictions.timerSec, appScreenTimeSec);
305-
isPeriodRecall = false;
304+
if (leftAppLimitMs < timerDelayMS) {
305+
timerReason = new PurgedReason(getString(R.string.app_paused_dialog_info_for_app_timer_left), appRestrictions.timerSec, appScreenTimeSec);
306+
isActivePeriodTimer = false;
307+
timerDelayMS = leftAppLimitMs;
306308
}
307309
}
308310

@@ -316,17 +318,17 @@ private void onNewAppLaunched(String packageName) {
316318
PurgedReason reason = new PurgedReason(getString(R.string.group_paused_dialog_info_for_active_period_over, associatedGroup.groupName));
317319
/// Outside active period
318320
if (Utils.isTimeOutsideTODs(associatedGroup.activePeriodStart, associatedGroup.activePeriodEnd)) {
319-
mPurgedApps.put(packageName, reason);
321+
Log.d(TAG, "onNewAppLaunched: App's associated group's active period is over");
320322
showOverlayDialog(packageName, reason);
321323
return;
322324
}
323325

324326
/// Between active period so update recall delay
325327
long willOverInMs = Utils.todDifferenceFromNow(associatedGroup.activePeriodEnd);
326-
if (willOverInMs < recallDelayMS) {
327-
recallDelayMS = willOverInMs;
328-
isPeriodRecall = true;
329-
recallReason = reason;
328+
if (willOverInMs < timerDelayMS) {
329+
timerReason = reason;
330+
isActivePeriodTimer = true;
331+
timerDelayMS = willOverInMs;
330332
}
331333
}
332334

@@ -339,6 +341,7 @@ private void onNewAppLaunched(String packageName) {
339341

340342
/// Group timer ran out
341343
if (groupScreenTimeSec >= associatedGroup.timerSec) {
344+
Log.d(TAG, "onNewAppLaunched: App's associated group's timer is over");
342345
PurgedReason reason = new PurgedReason(getString(R.string.app_paused_dialog_info_for_group_timer_out, associatedGroup.groupName), associatedGroup.timerSec, groupScreenTimeSec);
343346
mPurgedApps.put(packageName, reason);
344347
showOverlayDialog(packageName, reason);
@@ -347,25 +350,26 @@ private void onNewAppLaunched(String packageName) {
347350

348351
/// Group timer left so update recall delay
349352
long leftGroupLimitMs = (associatedGroup.timerSec - groupScreenTimeSec) * 1000;
350-
if (leftGroupLimitMs < recallDelayMS) {
351-
recallDelayMS = leftGroupLimitMs;
352-
recallReason = new PurgedReason(getString(R.string.app_paused_dialog_info_for_group_timer_left, associatedGroup.groupName), associatedGroup.timerSec, groupScreenTimeSec);
353-
isPeriodRecall = false;
353+
if (leftGroupLimitMs < timerDelayMS) {
354+
timerReason = new PurgedReason(getString(R.string.app_paused_dialog_info_for_group_timer_left, associatedGroup.groupName), associatedGroup.timerSec, groupScreenTimeSec);
355+
isActivePeriodTimer = false;
356+
timerDelayMS = leftGroupLimitMs;
354357
}
355358
}
356359
}
357360

358361

359362
// Return if delay doesn't changed
360-
if (recallDelayMS == Long.MAX_VALUE) return;
363+
if (timerDelayMS == Long.MAX_VALUE) return;
361364

362365
// schedule timer for lowest time to recall for usage recheck
363366
scheduleUsageAlertCountDownTimer(
364367
packageName,
365-
recallReason,
368+
timerReason,
366369
appRestrictions.alertInterval,
367-
appRestrictions.alertByDialog && !isPeriodRecall,
368-
recallDelayMS
370+
appRestrictions.alertByDialog,
371+
isActivePeriodTimer,
372+
timerDelayMS
369373
);
370374
}
371375

@@ -396,36 +400,38 @@ private boolean isAppAlreadyPurged(String packageName) {
396400
* Schedules a countdown timer to alert the user of remaining time for a specific app.
397401
* Provides notifications or overlay dialogs based on specified alert intervals and thresholds.
398402
*
399-
* @param packageName The package name of the app.
400-
* @param reason The reason for which to schedule timer.
401-
* @param alertByDialog True if alerts should be shown as overlay dialogs, otherwise as notifications.
402-
* @param alertIntervalSec The interval at which alerts should occur in SECONDS.
403-
* @param millisInFuture The time in Ms in future till the countdown timer will run.
403+
* @param packageName The package name of the app.
404+
* @param reason The reason for which to schedule timer.
405+
* @param alertIntervalSec The interval at which alerts should occur in SECONDS.
406+
* @param alertByDialog True if alerts should be shown as overlay dialogs, otherwise as notifications.
407+
* @param isForActivePeriod Does the timer scheduling is for app's or group's active period or not.
408+
* @param millisInFuture The time in Ms in future till the countdown timer will run.
404409
*/
405410
private void scheduleUsageAlertCountDownTimer(
406411
String packageName,
407412
PurgedReason reason,
408413
int alertIntervalSec,
409414
boolean alertByDialog,
415+
boolean isForActivePeriod,
410416
long millisInFuture
411417
) {
412418
cancelTimers();
413419
final Set<Integer> alertMinuteTicks = getAlertTickFromDuration(millisInFuture, alertIntervalSec);
414420

415-
// Schedule the countdown timer on the main thread
421+
// This method is called on a background thread so we have to schedule the timer from the main thread.
422+
// We can't schedule timer(background task) from another background thread. It will throw exception
416423
new Handler(Looper.getMainLooper()).post(new Runnable() {
417424
@Override
418425
public void run() {
426+
// Ticks every minute
419427
mOngoingAppTimer = new CountDownTimer(millisInFuture, 60 * 1000) {
420-
// Ticks every minute
421428
@Override
422429
public void onTick(long millisUntilFinished) {
423-
// Convert to minutes
424430
int minutesRemaining = (int) (millisUntilFinished / 60000);
425431

426432
// Trigger alert if remaining time in minutes matches any alert time
427433
if (alertMinuteTicks.contains(minutesRemaining)) {
428-
if (alertByDialog) {
434+
if (alertByDialog && !isForActivePeriod) {
429435
showOverlayDialog(packageName, reason);
430436
} else {
431437
pushUsageAlertNotification(packageName, minutesRemaining);
@@ -437,14 +443,15 @@ public void onTick(long millisUntilFinished) {
437443

438444
@Override
439445
public void onFinish() {
440-
mPurgedApps.put(packageName, reason);
446+
onNewAppLaunched(packageName);
447+
if (!isForActivePeriod) mPurgedApps.put(packageName, reason);
441448
showOverlayDialog(packageName, reason);
442449
Log.d(TAG, "scheduleUsageAlertCountDownTimer: Countdown finished for package: " + packageName);
443450
}
444451
};
445-
mOngoingAppTimer.start();
446452
Log.d(TAG, "scheduleUsageAlertCountDownTimer: Timer scheduled for " + packageName + " ending at: " +
447453
new Date(millisInFuture + System.currentTimeMillis()));
454+
mOngoingAppTimer.start();
448455
}
449456
});
450457
}

0 commit comments

Comments
 (0)