Skip to content

Commit cf6f6dd

Browse files
authored
Merge pull request #2 from akaMrNagar/dev
Fix: Popup dialog not working properly
2 parents 2f1666b + 1444743 commit cf6f6dd

File tree

19 files changed

+217
-155
lines changed

19 files changed

+217
-155
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ public static void scheduleBedtimeRoutineTasks(@NonNull Context context, @NonNul
7979
long nowInMs = System.currentTimeMillis();
8080
long alertTimeMs = Utils.todToTodayCal(bedtimeSettings.startTimeInMins - 30).getTimeInMillis();
8181
long startTimeMs = Utils.todToTodayCal(bedtimeSettings.startTimeInMins).getTimeInMillis();
82-
long endTimeMs = (startTimeMs + (bedtimeSettings.totalDurationInMins * 60000L));
82+
long endTimeMs = Utils.todToTodayCal(bedtimeSettings.startTimeInMins + bedtimeSettings.totalDurationInMins).getTimeInMillis();
8383

8484
// Bedtime is already ended then reschedule for the next day
8585
if (endTimeMs < nowInMs) {

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

Lines changed: 62 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.util.Calendar;
2222
import java.util.HashMap;
2323
import java.util.LinkedHashMap;
24+
import java.util.Map;
2425

2526
/**
2627
* ScreenUsageHelper provides utility methods for gathering and calculating screen usage statistics
@@ -33,74 +34,92 @@ public class ScreenUsageHelper {
3334
* If the target package is not null then this method will fetch usage for that app only
3435
* otherwise for all device apps.
3536
*
36-
* @param usageStatsManager The UsageStatsManager used to query screen usage data.
37-
* @param start The start time of the interval in milliseconds.
38-
* @param end The end time of the interval in milliseconds.
39-
* @param targetedPackage The package name of the app for fetching its screen usage.
37+
* @param usageStatsManager The UsageStatsManager used to query screen usage data.
38+
* @param start The start time of the interval in milliseconds.
39+
* @param end The end time of the interval in milliseconds.
40+
* @param lastActiveAppPackage The package name of the app which is active last time.
4041
* @return A map with package names as keys and their corresponding screen usage time in seconds as values.
4142
*/
4243
@NonNull
43-
public static HashMap<String, Long> fetchUsageForInterval(@NonNull UsageStatsManager usageStatsManager, long start, long end, @Nullable String targetedPackage) {
44-
HashMap<String, Long> oneDayUsageMap = new HashMap<>();
44+
public static HashMap<String, Long> fetchUsageForInterval(
45+
@NonNull UsageStatsManager usageStatsManager,
46+
long start,
47+
long end,
48+
@Nullable String lastActiveAppPackage
49+
) {
50+
HashMap<String, Long> usageMap = new HashMap<>();
4551
UsageEvents usageEvents = usageStatsManager.queryEvents(start, end);
46-
LinkedHashMap<String, UsageEvents.Event> lastResumedEvents = new LinkedHashMap<>(2);
52+
Map<String, UsageEvents.Event> lastResumedEvents = new HashMap<>();
4753
boolean isFirstEvent = true;
4854

49-
5055
while (usageEvents.hasNextEvent()) {
51-
UsageEvents.Event currentEvent = new UsageEvents.Event(); // Do not move this from while loop
52-
usageEvents.getNextEvent(currentEvent);
53-
int eventType = currentEvent.getEventType();
54-
55-
String packageName = currentEvent.getPackageName();
56-
/// If target package is not null
57-
if (targetedPackage != null && !packageName.equals(targetedPackage)) continue;
58-
59-
String className = currentEvent.getClassName();
60-
String eventKey = packageName + className;
61-
62-
if (eventType == UsageEvents.Event.ACTIVITY_RESUMED) {
63-
lastResumedEvents.put(eventKey, currentEvent);
64-
} else if (eventType == UsageEvents.Event.ACTIVITY_STOPPED || eventType == UsageEvents.Event.ACTIVITY_PAUSED) {
65-
Long screenTime = oneDayUsageMap.getOrDefault(packageName, 0L);
66-
UsageEvents.Event lastResumedEvent = lastResumedEvents.get(eventKey);
67-
68-
if (lastResumedEvent != null
69-
&& lastResumedEvent.getPackageName().equals(packageName)
70-
&& lastResumedEvent.getClassName().equals(className)
71-
) {
72-
// Calculate usage from the last ACTIVITY_RESUMED to this ACTIVITY_PAUSED
73-
screenTime += (currentEvent.getTimeStamp() - lastResumedEvent.getTimeStamp());
74-
lastResumedEvents.remove(eventKey);
75-
} else if (isFirstEvent) {
76-
// Fallback logic in case no matching ACTIVITY_RESUMED was found. May be this app was opened before START time
77-
screenTime += (currentEvent.getTimeStamp() - start);
78-
isFirstEvent = false;
79-
}
80-
oneDayUsageMap.put(packageName, screenTime);
56+
UsageEvents.Event event = new UsageEvents.Event();
57+
usageEvents.getNextEvent(event);
58+
59+
String packageName = event.getPackageName();
60+
String eventKey = packageName + event.getClassName();
61+
long timestamp = event.getTimeStamp();
62+
63+
switch (event.getEventType()) {
64+
case UsageEvents.Event.ACTIVITY_RESUMED:
65+
lastResumedEvents.put(eventKey, event);
66+
break;
67+
68+
case UsageEvents.Event.ACTIVITY_PAUSED:
69+
case UsageEvents.Event.ACTIVITY_STOPPED:
70+
Long usageTime = usageMap.getOrDefault(packageName, 0L);
71+
UsageEvents.Event lastResumedEvent = lastResumedEvents.get(eventKey);
72+
73+
if (lastResumedEvent != null) {
74+
// Normal case: App was resumed within the interval
75+
usageTime += (timestamp - lastResumedEvent.getTimeStamp());
76+
lastResumedEvents.remove(eventKey);
77+
} else if (isFirstEvent) {
78+
// Edge case: App was opened before start but stopped after start
79+
usageTime += (timestamp - start);
80+
isFirstEvent = false;
81+
}
82+
usageMap.put(packageName, usageTime);
83+
break;
84+
85+
default:
86+
break;
8187
}
8288
}
8389

90+
// Handle case where the app is still active
91+
if (lastActiveAppPackage != null) {
92+
lastResumedEvents.values().stream()
93+
.filter(event -> lastActiveAppPackage.equals(event.getPackageName()))
94+
.findFirst()
95+
.ifPresent(event -> {
96+
long usageTime = usageMap.getOrDefault(lastActiveAppPackage, 0L);
97+
usageTime += (end - event.getTimeStamp());
98+
usageMap.put(lastActiveAppPackage, usageTime);
99+
});
100+
}
101+
84102
// Convert milliseconds to seconds
85-
oneDayUsageMap.replaceAll((k, v) -> (v / 1000));
86-
return oneDayUsageMap;
103+
usageMap.replaceAll((key, value) -> value / 1000);
104+
return usageMap;
87105
}
88106

89107
/**
90108
* Fetches the screen usage time of a all installed application for the current day until now using usage events.
91109
*
92-
* @param usageStatsManager The UsageStatsManager used to query screen usage data.
110+
* @param usageStatsManager The UsageStatsManager used to query screen usage data.
111+
* @param lastActiveAppPackage The package name of the app which is active last time.
93112
* @return The total screen usage time of the specified application in seconds.
94113
*/
95114
@NonNull
96-
public static HashMap<String, Long> fetchAppUsageTodayTillNow(@NonNull UsageStatsManager usageStatsManager) {
115+
public static HashMap<String, Long> fetchAppUsageTodayTillNow(@NonNull UsageStatsManager usageStatsManager, @Nullable String lastActiveAppPackage) {
97116
Calendar midNightCal = Calendar.getInstance();
98117
midNightCal.set(Calendar.HOUR_OF_DAY, 0);
99118
midNightCal.set(Calendar.MINUTE, 0);
100119
midNightCal.set(Calendar.SECOND, 0);
101120

102121
long start = midNightCal.getTimeInMillis();
103122
long end = System.currentTimeMillis();
104-
return fetchUsageForInterval(usageStatsManager, start, end, null);
123+
return fetchUsageForInterval(usageStatsManager, start, end, lastActiveAppPackage);
105124
}
106125
}

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

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

257257
/// Check for app launch limit
@@ -265,26 +265,26 @@ private void onNewAppLaunched(String packageName) {
265265

266266
/// Check for app's active period
267267
if (appRestrictions.periodDurationInMins > 0) {
268-
PurgedReason reason = new PurgedReason(getString(R.string.app_paused_dialog_info_for_active_period_over));
268+
int periodEndTimeMinutes = appRestrictions.activePeriodStart + appRestrictions.periodDurationInMins;
269269

270270
/// Outside active period
271-
if (Utils.isTimeOutsideTODs(appRestrictions.activePeriodStart, appRestrictions.activePeriodEnd)) {
271+
if (Utils.isTimeOutsideTODs(appRestrictions.activePeriodStart, periodEndTimeMinutes)) {
272272
Log.d(TAG, "onNewAppLaunched: App's active period is over");
273+
PurgedReason reason = new PurgedReason(getString(R.string.app_paused_dialog_info_for_active_period_over));
273274
showOverlayDialog(packageName, reason);
274275
return;
275276
}
276277

277-
/// Between active period so update recall delay
278-
long willOverInMs = Utils.todDifferenceFromNow(appRestrictions.activePeriodEnd);
278+
/// Launched between active period so set timer for period ending
279+
long willOverInMs = Utils.todDifferenceFromNow(periodEndTimeMinutes);
279280
if (willOverInMs < timerDelayMS) {
280-
timerReason = reason;
281-
isActivePeriodTimer = true;
281+
timerReason = null;
282282
timerDelayMS = willOverInMs;
283283
}
284284
}
285285

286286
/// Fetch usage for all apps
287-
HashMap<String, Long> allAppsScreenUsage = ScreenUsageHelper.fetchAppUsageTodayTillNow(mUsageStatsManager);
287+
HashMap<String, Long> allAppsScreenUsage = ScreenUsageHelper.fetchAppUsageTodayTillNow(mUsageStatsManager, packageName);
288288

289289
/// Check for app timer
290290
if (appRestrictions.timerSec > 0) {
@@ -303,7 +303,6 @@ private void onNewAppLaunched(String packageName) {
303303
long leftAppLimitMs = (appRestrictions.timerSec - appScreenTimeSec) * 1000;
304304
if (leftAppLimitMs < timerDelayMS) {
305305
timerReason = new PurgedReason(getString(R.string.app_paused_dialog_info_for_app_timer_left), appRestrictions.timerSec, appScreenTimeSec);
306-
isActivePeriodTimer = false;
307306
timerDelayMS = leftAppLimitMs;
308307
}
309308
}
@@ -315,29 +314,28 @@ private void onNewAppLaunched(String packageName) {
315314

316315
/// Check for group's active period
317316
if (associatedGroup.periodDurationInMins > 0) {
318-
PurgedReason reason = new PurgedReason(getString(R.string.group_paused_dialog_info_for_active_period_over, associatedGroup.groupName));
317+
int periodEndTimeMinutes = associatedGroup.activePeriodStart + associatedGroup.periodDurationInMins;
318+
319319
/// Outside active period
320-
if (Utils.isTimeOutsideTODs(associatedGroup.activePeriodStart, associatedGroup.activePeriodEnd)) {
320+
if (Utils.isTimeOutsideTODs(associatedGroup.activePeriodStart, periodEndTimeMinutes)) {
321321
Log.d(TAG, "onNewAppLaunched: App's associated group's active period is over");
322+
PurgedReason reason = new PurgedReason(getString(R.string.group_paused_dialog_info_for_active_period_over, associatedGroup.groupName));
322323
showOverlayDialog(packageName, reason);
323324
return;
324325
}
325326

326-
/// Between active period so update recall delay
327-
long willOverInMs = Utils.todDifferenceFromNow(associatedGroup.activePeriodEnd);
327+
/// Launched between active period so set timer for period ending
328+
long willOverInMs = Utils.todDifferenceFromNow(periodEndTimeMinutes);
328329
if (willOverInMs < timerDelayMS) {
329-
timerReason = reason;
330-
isActivePeriodTimer = true;
330+
timerReason = null;
331331
timerDelayMS = willOverInMs;
332332
}
333333
}
334334

335335

336336
/// Check for associated group's timer
337337
if (associatedGroup.timerSec > 0) {
338-
long groupScreenTimeSec = associatedGroup.distractingApps.stream()
339-
.mapToLong(app -> ScreenUsageHelper.fetchAppUsageTodayTillNow(mUsageStatsManager).getOrDefault(app, 0L))
340-
.sum();
338+
long groupScreenTimeSec = associatedGroup.distractingApps.stream().mapToLong(app -> allAppsScreenUsage.getOrDefault(app, 0L)).sum();
341339

342340
/// Group timer ran out
343341
if (groupScreenTimeSec >= associatedGroup.timerSec) {
@@ -352,7 +350,6 @@ private void onNewAppLaunched(String packageName) {
352350
long leftGroupLimitMs = (associatedGroup.timerSec - groupScreenTimeSec) * 1000;
353351
if (leftGroupLimitMs < timerDelayMS) {
354352
timerReason = new PurgedReason(getString(R.string.app_paused_dialog_info_for_group_timer_left, associatedGroup.groupName), associatedGroup.timerSec, groupScreenTimeSec);
355-
isActivePeriodTimer = false;
356353
timerDelayMS = leftGroupLimitMs;
357354
}
358355
}
@@ -368,7 +365,6 @@ private void onNewAppLaunched(String packageName) {
368365
timerReason,
369366
appRestrictions.alertInterval,
370367
appRestrictions.alertByDialog,
371-
isActivePeriodTimer,
372368
timerDelayMS
373369
);
374370
}
@@ -400,19 +396,17 @@ private boolean isAppAlreadyPurged(String packageName) {
400396
* Schedules a countdown timer to alert the user of remaining time for a specific app.
401397
* Provides notifications or overlay dialogs based on specified alert intervals and thresholds.
402398
*
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.
399+
* @param packageName The package name of the app.
400+
* @param unfinishedReason The unfinishedReason for which to schedule timer.
401+
* @param alertIntervalSec The interval at which alerts should occur in SECONDS.
402+
* @param alertByDialog True if alerts should be shown as overlay dialogs, otherwise as notifications.
403+
* @param millisInFuture The time in Ms in future till the countdown timer will run.
409404
*/
410405
private void scheduleUsageAlertCountDownTimer(
411406
String packageName,
412-
PurgedReason reason,
407+
@Nullable PurgedReason unfinishedReason,
413408
int alertIntervalSec,
414409
boolean alertByDialog,
415-
boolean isForActivePeriod,
416410
long millisInFuture
417411
) {
418412
cancelTimers();
@@ -431,8 +425,8 @@ public void onTick(long millisUntilFinished) {
431425

432426
// Trigger alert if remaining time in minutes matches any alert time
433427
if (alertMinuteTicks.contains(minutesRemaining)) {
434-
if (alertByDialog && !isForActivePeriod) {
435-
showOverlayDialog(packageName, reason);
428+
if (unfinishedReason != null && alertByDialog) {
429+
showOverlayDialog(packageName, unfinishedReason);
436430
} else {
437431
pushUsageAlertNotification(packageName, minutesRemaining);
438432
}
@@ -443,10 +437,8 @@ public void onTick(long millisUntilFinished) {
443437

444438
@Override
445439
public void onFinish() {
446-
onNewAppLaunched(packageName);
447-
if (!isForActivePeriod) mPurgedApps.put(packageName, reason);
448-
showOverlayDialog(packageName, reason);
449440
Log.d(TAG, "scheduleUsageAlertCountDownTimer: Countdown finished for package: " + packageName);
441+
onNewAppLaunched(packageName);
450442
}
451443
};
452444
Log.d(TAG, "scheduleUsageAlertCountDownTimer: Timer scheduled for " + packageName + " ending at: " +

android/app/src/main/java/com/mindful/android/utils/Utils.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ public static Calendar todToTodayCal(int totalMinutes) {
225225
* Calculated the difference between time now and future tod minutes.
226226
*
227227
* @param futureTotalMinutes The total minutes from Time Of Day dart object.
228-
* @return The different in MS. If the difference is negative then return 0
228+
* @return The difference in MS. If the difference is negative then return 0
229229
*/
230230
public static long todDifferenceFromNow(int futureTotalMinutes) {
231231
long diff = todToTodayCal(futureTotalMinutes).getTimeInMillis() - System.currentTimeMillis();

android/app/src/main/res/values-ja/strings.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
<resources>
1313
<string name="app_name" comment="app_name app values">Mindful</string>
1414
<string name="accessibility_description">Mindful アプリは、ユーザー補助を使用して、ウェブサイトやアプリのショート動画をブロックします。ブロックリストに登録されたURLやコンテンツへのアクセスを制限することで、集中力を維持するのに役立ちます。 \n\n⚠️注意:Mindful はプライバシーを最優先に考えています。100%安全でオフラインで動作します。個人データの収集や保存は一切行いません。 </string>
15-
<string name="admin_description">Mindful は、ユーザーのデータを収集、保存、または送信することはありません。無料でオープンソースのソフトウェア(FOSS)なので、ソースコードを自由に確認・変更できます。管理者権限は、アプリの正常な動作に必要なシステム操作にのみ使用され、プライバシーは完全に保護されます。</string>
15+
<string name="admin_description">Mindfulはユーザーデータを収集、保存、送信しません。管理者権限は、重要なシステム操作にのみ必要であり、プライバシーを侵害することなくアプリが正しく機能することを保証します。</string>
1616
<string name="toast_enable_notification">通知を許可する</string>
1717
<string name="toast_blocked_content">コンテンツをブロックしました。前の画面に戻ります。</string>
1818
<string name="toast_redirecting">検索結果を絞り込む</string>

0 commit comments

Comments
 (0)