├── app
├── .gitignore
├── release
│ ├── app-release.apk
│ └── output.json
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── drawable
│ │ │ │ ├── cross.png
│ │ │ │ ├── tick.png
│ │ │ │ ├── warning.png
│ │ │ │ ├── app_logo.png
│ │ │ │ ├── splash_screen_logo.png
│ │ │ │ ├── splash_background.xml
│ │ │ │ └── ic_launcher_background.xml
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── drawable-nodpi
│ │ │ │ └── example_picture.png
│ │ │ ├── layout
│ │ │ │ ├── spinner_item.xml
│ │ │ │ ├── activity_welcome.xml
│ │ │ │ ├── checkbox.xml
│ │ │ │ ├── activity_main.xml
│ │ │ │ ├── activity_chart.xml
│ │ │ │ ├── app_list.xml
│ │ │ │ └── activity_app_info.xml
│ │ │ ├── mipmap-anydpi-v26
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ ├── values
│ │ │ │ ├── colors.xml
│ │ │ │ ├── strings.xml
│ │ │ │ └── styles.xml
│ │ │ └── drawable-v24
│ │ │ │ └── ic_launcher_foreground.xml
│ │ ├── java
│ │ │ └── tj
│ │ │ │ └── beataddiction
│ │ │ │ ├── NotificationReceiver.java
│ │ │ │ ├── ResetDataReceiver.java
│ │ │ │ ├── WelcomeActivity.java
│ │ │ │ ├── BootBroadcastReceiver.java
│ │ │ │ ├── TrackedAppInfo.java
│ │ │ │ ├── AppInfo.java
│ │ │ │ ├── Alarms.java
│ │ │ │ ├── AppInfoListAdapter.java
│ │ │ │ ├── BackgroundService.java
│ │ │ │ ├── ChartActivity.java
│ │ │ │ ├── Utils.java
│ │ │ │ ├── DatabaseHelper.java
│ │ │ │ ├── MainActivity.java
│ │ │ │ └── AppInfoActivity.java
│ │ └── AndroidManifest.xml
│ ├── test
│ │ └── java
│ │ │ └── tj
│ │ │ └── beataddiction
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── tj
│ │ └── beataddiction
│ │ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
└── build.gradle
├── settings.gradle
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .idea
├── encodings.xml
├── compiler.xml
├── vcs.xml
├── misc.xml
├── runConfigurations.xml
├── gradle.xml
└── jarRepositories.xml
├── .gitignore
├── README.md
├── LICENSE
├── gradle.properties
├── gradlew.bat
└── gradlew
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/app/release/app-release.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tanjim17/BeatAddiction/HEAD/app/release/app-release.apk
--------------------------------------------------------------------------------
/app/src/main/res/drawable/cross.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tanjim17/BeatAddiction/HEAD/app/src/main/res/drawable/cross.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/tick.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tanjim17/BeatAddiction/HEAD/app/src/main/res/drawable/tick.png
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tanjim17/BeatAddiction/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/app/src/main/res/drawable/warning.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tanjim17/BeatAddiction/HEAD/app/src/main/res/drawable/warning.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/app_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tanjim17/BeatAddiction/HEAD/app/src/main/res/drawable/app_logo.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tanjim17/BeatAddiction/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tanjim17/BeatAddiction/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tanjim17/BeatAddiction/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tanjim17/BeatAddiction/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tanjim17/BeatAddiction/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/splash_screen_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tanjim17/BeatAddiction/HEAD/app/src/main/res/drawable/splash_screen_logo.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tanjim17/BeatAddiction/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tanjim17/BeatAddiction/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-nodpi/example_picture.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tanjim17/BeatAddiction/HEAD/app/src/main/res/drawable-nodpi/example_picture.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tanjim17/BeatAddiction/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tanjim17/BeatAddiction/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tanjim17/BeatAddiction/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/release/output.json:
--------------------------------------------------------------------------------
1 | [{"outputType":{"type":"APK"},"apkData":{"type":"MAIN","splits":[],"versionCode":4,"versionName":"1.3","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}]
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Apr 17 11:09:58 BDT 2020
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
7 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/spinner_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #008577
4 | #00574B
5 | #D81B60
6 | #AA0000
7 |
8 | #66000000
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/splash_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | -
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/java/tj/beataddiction/NotificationReceiver.java:
--------------------------------------------------------------------------------
1 | package tj.beataddiction;
2 |
3 | import android.content.BroadcastReceiver;
4 | import android.content.Context;
5 | import android.content.Intent;
6 |
7 | public class NotificationReceiver extends BroadcastReceiver {
8 | @Override
9 | public void onReceive(Context context, Intent intent) {
10 | BackgroundService.enqueueWork(context, new Intent(context, BackgroundService.class));
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_welcome.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/test/java/tj/beataddiction/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package tj.beataddiction;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/checkbox.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
11 |
--------------------------------------------------------------------------------
/app/src/main/java/tj/beataddiction/ResetDataReceiver.java:
--------------------------------------------------------------------------------
1 | package tj.beataddiction;
2 |
3 | import android.content.BroadcastReceiver;
4 | import android.content.Context;
5 | import android.content.Intent;
6 |
7 | public class ResetDataReceiver extends BroadcastReceiver {
8 | @Override
9 | public void onReceive(Context context, Intent intent) {
10 | DatabaseHelper dbHelper = new DatabaseHelper(context);
11 | dbHelper.resetAllIsUsageExceeded();
12 | dbHelper.close();
13 | }
14 | }
--------------------------------------------------------------------------------
/app/src/main/java/tj/beataddiction/WelcomeActivity.java:
--------------------------------------------------------------------------------
1 | package tj.beataddiction;
2 |
3 | import androidx.appcompat.app.AppCompatActivity;
4 |
5 | import android.content.Intent;
6 | import android.os.Bundle;
7 |
8 | public class WelcomeActivity extends AppCompatActivity {
9 |
10 | @Override
11 | protected void onCreate(Bundle savedInstanceState) {
12 | super.onCreate(savedInstanceState);
13 | startActivity(new Intent(this, MainActivity.class));
14 | finish();
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Beat Addiction
3 |
4 | New message: %1$s
5 |
6 | Share
7 | Reply
8 | SAVE
9 |
10 | WelcomeActivity
11 | Dummy Button
12 | DUMMY\nCONTENT
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/java/tj/beataddiction/BootBroadcastReceiver.java:
--------------------------------------------------------------------------------
1 | package tj.beataddiction;
2 |
3 | import android.content.BroadcastReceiver;
4 | import android.content.Context;
5 | import android.content.Intent;
6 |
7 | public class BootBroadcastReceiver extends BroadcastReceiver {
8 | @Override
9 | public void onReceive(Context context, Intent intent) {
10 | if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) {
11 | Alarms.resetIsUsageExceededData(context);
12 | Alarms.scheduleNotification(context);
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/java/tj/beataddiction/TrackedAppInfo.java:
--------------------------------------------------------------------------------
1 | package tj.beataddiction;
2 |
3 | class TrackedAppInfo {
4 | private String packageName;
5 | private int timeAllowed;
6 | private int isUsageExceeded;
7 |
8 | TrackedAppInfo(String packageName, int timeAllowed, int isUsageExceeded) {
9 | this.packageName = packageName;
10 | this.timeAllowed = timeAllowed;
11 | this.isUsageExceeded = isUsageExceeded;
12 | }
13 |
14 | String getPackageName() {
15 | return packageName;
16 | }
17 |
18 | int getTimeAllowed() {
19 | return timeAllowed;
20 | }
21 |
22 | int getIsUsageExceeded() {
23 | return isUsageExceeded;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class appName to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file appName.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/tj/beataddiction/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package tj.beataddiction;
2 |
3 | import android.content.Context;
4 |
5 | import androidx.test.InstrumentationRegistry;
6 | import androidx.test.runner.AndroidJUnit4;
7 |
8 | import org.junit.Test;
9 | import org.junit.runner.RunWith;
10 |
11 | import static org.junit.Assert.*;
12 |
13 | /**
14 | * Instrumented test, which will execute on an Android device.
15 | *
16 | * @see Testing documentation
17 | */
18 | @RunWith(AndroidJUnit4.class)
19 | public class ExampleInstrumentedTest {
20 | @Test
21 | public void useAppContext() {
22 | // Context of the app under test.
23 | Context appContext = InstrumentationRegistry.getTargetContext();
24 |
25 | assertEquals("tj.beataddiction", appContext.getPackageName());
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # BeatAddiction
2 | Beat Addiction is a simple android app for tracking your daily app usage. You can see last 7 days' daily usage time of every installed app on your
3 | device in a bar chart. You can also set daily usage limit for an app and get notification when you cross the limit.
4 |
5 | ### Minimum Android version
6 | Android 7.0 Nougat
7 |
8 | ### Screenshots
9 |
10 |
11 |
12 | ### Installation
13 | You can download apk from [here](https://github.com/tanjim17/BeatAddiction/releases/download/v1.4/beat_addiction_v1.4.apk).
14 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_chart.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
12 |
13 |
20 |
24 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Tanjim Ahmed Khan
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/java/tj/beataddiction/AppInfo.java:
--------------------------------------------------------------------------------
1 | package tj.beataddiction;
2 |
3 | import android.graphics.drawable.Drawable;
4 |
5 | public class AppInfo implements Comparable{
6 | private String appName;
7 | private Drawable icon;
8 | private String packageName;
9 | private boolean isTracked;
10 | private boolean isUsageExceeded;
11 |
12 | AppInfo(String appName, Drawable icon, String packageName, boolean isTracked, boolean isUsageExceeded) {
13 | this.appName = appName;
14 | this.icon = icon;
15 | this.packageName = packageName;
16 | this.isTracked = isTracked;
17 | this.isUsageExceeded = isUsageExceeded;
18 | }
19 | String getAppName() {
20 | return appName;
21 | }
22 | Drawable getIcon() {
23 | return icon;
24 | }
25 | String getPackageName() {
26 | return packageName;
27 | }
28 | boolean getIsTracked() {
29 | return isTracked;
30 | }
31 | boolean getIsUsageExceeded() {
32 | return isUsageExceeded;
33 | }
34 |
35 | @Override
36 | public int compareTo(AppInfo appInfo) {
37 | return appName.compareTo(appInfo.getAppName());
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/.idea/jarRepositories.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 29
5 | buildToolsVersion "29.0.2"
6 | defaultConfig {
7 | applicationId "tj.beataddiction"
8 | minSdkVersion 24
9 | targetSdkVersion 29
10 | versionCode 5
11 | versionName "1.4"
12 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | compileOptions {
21 | sourceCompatibility JavaVersion.VERSION_1_8
22 | targetCompatibility JavaVersion.VERSION_1_8
23 | }
24 | }
25 |
26 | repositories {
27 | maven { url 'https://jitpack.io' }
28 | }
29 |
30 | dependencies {
31 | implementation fileTree(dir: 'libs', include: ['*.jar'])
32 | implementation 'androidx.appcompat:appcompat:1.1.0'
33 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
34 | implementation 'androidx.legacy:legacy-support-v4:1.0.0'
35 | testImplementation 'junit:junit:4.12'
36 | androidTestImplementation 'androidx.test:runner:1.2.0'
37 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
38 | implementation 'com.github.PhilJay:MPAndroidChart:v2.2.4'
39 | }
40 |
--------------------------------------------------------------------------------
/app/src/main/java/tj/beataddiction/Alarms.java:
--------------------------------------------------------------------------------
1 | package tj.beataddiction;
2 |
3 | import android.app.AlarmManager;
4 | import android.app.PendingIntent;
5 | import android.content.Context;
6 | import android.content.Intent;
7 | import android.icu.util.Calendar;
8 | import android.os.SystemClock;
9 |
10 | class Alarms {
11 |
12 | static void scheduleNotification(Context context) {
13 | Intent intent = new Intent(context, NotificationReceiver.class);
14 | PendingIntent alarmIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
15 |
16 | AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
17 | if (alarmManager != null) {
18 | alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
19 | SystemClock.elapsedRealtime(), 60*1000, alarmIntent);
20 | }
21 | }
22 |
23 | static void resetIsUsageExceededData(Context context) {
24 | Intent intent = new Intent(context, ResetDataReceiver.class);
25 | PendingIntent alarmIntent = PendingIntent.getBroadcast(context, 1, intent, 0);
26 |
27 | Calendar calendar = Calendar.getInstance();
28 | calendar.set(Calendar.HOUR_OF_DAY, 0);
29 | calendar.set(Calendar.MINUTE, 0);
30 | calendar.set(Calendar.SECOND, 0);
31 |
32 | AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
33 | if (alarmManager != null) {
34 | alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
35 | AlarmManager.INTERVAL_DAY, alarmIntent);
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/app_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
14 |
15 |
24 |
25 |
29 |
30 |
36 |
37 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
9 |
10 |
11 |
12 |
18 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/app/src/main/java/tj/beataddiction/AppInfoListAdapter.java:
--------------------------------------------------------------------------------
1 | package tj.beataddiction;
2 |
3 | import android.content.Context;
4 | import android.view.LayoutInflater;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 | import android.widget.BaseAdapter;
8 | import android.widget.ImageView;
9 | import android.widget.TextView;
10 |
11 | import java.util.List;
12 |
13 | public class AppInfoListAdapter extends BaseAdapter {
14 | private LayoutInflater layoutInflater;
15 | private List appInfoList;
16 |
17 | AppInfoListAdapter(Context context, List appInfoList) {
18 | layoutInflater =(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
19 | this.appInfoList = appInfoList;
20 | }
21 |
22 | @Override
23 | public int getCount() {
24 | return appInfoList.size();
25 | }
26 |
27 | @Override
28 | public AppInfo getItem(int position) {
29 | return appInfoList.get(position);
30 | }
31 |
32 | @Override
33 | public long getItemId(int position) {
34 | return position;
35 | }
36 |
37 | @Override
38 | public View getView(final int position, View convertView, ViewGroup parent) {
39 | ViewHolder listViewHolder;
40 | if(convertView == null) {
41 | listViewHolder = new ViewHolder();
42 | convertView = layoutInflater.inflate(R.layout.app_list, parent, false);
43 |
44 | listViewHolder.appName = convertView.findViewById(R.id.list_app_name);
45 | listViewHolder.appIcon = convertView.findViewById(R.id.list_app_icon);
46 | listViewHolder.isUsageExceeded = convertView.findViewById(R.id.isUsageExceeded);
47 | listViewHolder.isTracked = convertView.findViewById(R.id.isTracked);
48 | convertView.setTag(listViewHolder);
49 | }
50 | else {
51 | listViewHolder = (ViewHolder)convertView.getTag();
52 | }
53 | listViewHolder.appName.setText(appInfoList.get(position).getAppName());
54 | listViewHolder.appIcon.setImageDrawable(appInfoList.get(position).getIcon());
55 | if(appInfoList.get(position).getIsUsageExceeded())
56 | listViewHolder.isUsageExceeded.setVisibility(View.VISIBLE);
57 | else listViewHolder.isUsageExceeded.setVisibility(View.INVISIBLE);
58 | if(appInfoList.get(position).getIsTracked())
59 | listViewHolder.isTracked.setVisibility(View.VISIBLE);
60 | else listViewHolder.isTracked.setVisibility(View.INVISIBLE);
61 |
62 | return convertView;
63 | }
64 |
65 | class ViewHolder {
66 | TextView appName;
67 | ImageView appIcon;
68 | ImageView isUsageExceeded;
69 | TextView isTracked;
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/app/src/main/java/tj/beataddiction/BackgroundService.java:
--------------------------------------------------------------------------------
1 | package tj.beataddiction;
2 |
3 | import android.app.ActivityManager;
4 | import android.app.Notification;
5 | import android.app.NotificationManager;
6 | import android.content.Context;
7 | import android.content.Intent;
8 | import android.content.pm.PackageManager;
9 | import android.icu.util.Calendar;
10 | import android.media.RingtoneManager;
11 | import android.util.Log;
12 |
13 | import androidx.annotation.NonNull;
14 | import androidx.core.app.JobIntentService;
15 |
16 | import java.util.HashMap;
17 | import java.util.List;
18 | import java.util.stream.Collectors;
19 |
20 | public class BackgroundService extends JobIntentService {
21 | private DatabaseHelper dbHelper;
22 | private static final String TAG = "BackgroundService";
23 | private static final int JOB_ID = 1;
24 |
25 | public static void enqueueWork(Context context, Intent intent) {
26 | enqueueWork(context, BackgroundService.class, JOB_ID, intent);
27 | }
28 |
29 | @Override
30 | protected void onHandleWork(@NonNull Intent intent) {
31 | dbHelper = new DatabaseHelper(this);
32 | List trackedAppInfos = dbHelper.getAllRows();
33 |
34 | Calendar calendar = Calendar.getInstance();
35 | calendar.set(Calendar.HOUR_OF_DAY, 0);
36 | calendar.set(Calendar.MINUTE, 0);
37 | calendar.set(Calendar.SECOND, 0);
38 | long beginTime = calendar.getTimeInMillis();
39 | long endTime = beginTime + Utils.DAY_IN_MILLIS;
40 | HashMap appUsageMap = Utils.getTimeSpent(this, null, beginTime, endTime);
41 |
42 | String currentRunningPackageName = null;
43 | List list = appUsageMap.keySet().stream().filter(s -> s.startsWith("current")).collect(Collectors.toList());
44 | if(list.size() > 0) {currentRunningPackageName = list.get(0).replaceFirst("current", "");}
45 |
46 | for(int i = 0; i < trackedAppInfos.size(); i++) {
47 | TrackedAppInfo trackedAppInfo = trackedAppInfos.get(i);
48 | String packageName = trackedAppInfo.getPackageName();
49 |
50 | if(appUsageMap.containsKey(packageName)) {
51 | Integer usageTime = appUsageMap.get(packageName);
52 | if(usageTime == null) usageTime = 0;
53 | int allowedTime = trackedAppInfo.getTimeAllowed();
54 | int isUsageExceeded = trackedAppInfo.getIsUsageExceeded();
55 |
56 | if((usageTime > allowedTime && isUsageExceeded == 0) ||
57 | (isUsageExceeded == 1 && packageName.equals(currentRunningPackageName))) {
58 | try {
59 | dbHelper.setIsUsageExceeded(packageName);
60 | String appName = (String) getPackageManager()
61 | .getApplicationLabel(getPackageManager().getApplicationInfo(packageName, 0));
62 | showNotification(appName, i);
63 | } catch (PackageManager.NameNotFoundException e) {
64 | Log.e(TAG, "package name not found");
65 | }
66 | }
67 | }
68 | }
69 | }
70 |
71 | public void onDestroy() {
72 | super.onDestroy();
73 | dbHelper.close();
74 | }
75 |
76 | private void showNotification(String appName, int id) {
77 | Notification.Builder builder
78 | = new Notification.Builder(getApplicationContext())
79 | .setContentTitle(appName + " usage exceeded!")
80 | .setContentText("Close your app now!")
81 | .setSmallIcon(R.drawable.warning)
82 | .setPriority(Notification.PRIORITY_MAX)
83 | .setSound(RingtoneManager. getDefaultUri (RingtoneManager. TYPE_NOTIFICATION));
84 | NotificationManager notificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
85 | notificationManager.notify(id, builder.build());
86 | }
87 | }
88 |
89 |
90 |
--------------------------------------------------------------------------------
/app/src/main/java/tj/beataddiction/ChartActivity.java:
--------------------------------------------------------------------------------
1 | package tj.beataddiction;
2 |
3 | import androidx.appcompat.app.AppCompatActivity;
4 |
5 | import android.icu.text.SimpleDateFormat;
6 | import android.icu.util.Calendar;
7 | import android.os.Bundle;
8 | import android.widget.TextView;
9 | import android.widget.Toast;
10 |
11 | import com.github.mikephil.charting.charts.BarChart;
12 | import com.github.mikephil.charting.data.BarData;
13 | import com.github.mikephil.charting.data.BarDataSet;
14 | import com.github.mikephil.charting.data.BarEntry;
15 | import com.github.mikephil.charting.data.Entry;
16 | import com.github.mikephil.charting.formatter.ValueFormatter;
17 | import com.github.mikephil.charting.utils.ColorTemplate;
18 | import com.github.mikephil.charting.utils.ViewPortHandler;
19 |
20 | import java.util.ArrayList;
21 | import java.util.HashMap;
22 |
23 | public class ChartActivity extends AppCompatActivity {
24 | private String packageName;
25 | private String appName;
26 | private BarChart barChart;
27 | private TextView appNameView;
28 |
29 | @Override
30 | protected void onCreate(Bundle savedInstanceState) {
31 | super.onCreate(savedInstanceState);
32 | setContentView(R.layout.activity_chart);
33 |
34 | packageName = getIntent().getStringExtra("packageName");
35 | appName = getIntent().getStringExtra("appName");
36 |
37 | barChart = findViewById(R.id.barchart);
38 | appNameView = findViewById(R.id.chart_app_name);
39 | appNameView.setText(appName);
40 |
41 | ArrayList entries = new ArrayList<>();
42 | Calendar calendar = Calendar.getInstance();
43 | calendar.set(Calendar.HOUR_OF_DAY, 0);
44 | calendar.set(Calendar.MINUTE, 0);
45 | calendar.set(Calendar.SECOND, 0);
46 | long beginTime = calendar.getTimeInMillis();
47 | long endTime = beginTime + Utils.DAY_IN_MILLIS;
48 | for (int daysAgo = 0; daysAgo < 7; daysAgo++) {
49 | HashMap appUsageMap = Utils.getTimeSpent(this, packageName, beginTime, endTime);
50 | Integer usageTime = appUsageMap.get(packageName);
51 | if (usageTime == null) usageTime = 0;
52 | entries.add(new BarEntry(usageTime, 7 - daysAgo - 1));
53 | beginTime -= Utils.DAY_IN_MILLIS;
54 | endTime -= Utils.DAY_IN_MILLIS;
55 | }
56 | BarDataSet bardataset = new BarDataSet(entries, "daily usage");
57 |
58 | ArrayList labels = new ArrayList<>();
59 | SimpleDateFormat dateFormat = new SimpleDateFormat("dd MMM");
60 | calendar = Calendar.getInstance();
61 | calendar.add(Calendar.DATE, -6);
62 | for (int i = 0; i < 7; i++) {
63 | labels.add(dateFormat.format(calendar.getTime()));
64 | calendar.add(Calendar.DATE, 1);
65 | }
66 |
67 | BarData data = new BarData(labels, bardataset);
68 | barChart.setData(data);
69 | barChart.setDescription(null);
70 | barChart.getData().setValueFormatter(new TimeFormatter());
71 | barChart.getXAxis().setLabelsToSkip(0);
72 | barChart.getAxisLeft().setDrawLabels(false);
73 | barChart.getAxisRight().setDrawLabels(false);
74 | barChart.getAxisLeft().setAxisMinValue(0);
75 | barChart.animateY(500);
76 | bardataset.setColors(ColorTemplate.COLORFUL_COLORS);
77 | }
78 |
79 | private class TimeFormatter implements ValueFormatter {
80 | @Override
81 | public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) {
82 | if(value == 0) {
83 | return "0s";
84 | }
85 | int[] usageTimes = Utils.reverseProcessTime((int)value);
86 | String hour = String.valueOf(usageTimes[0]);
87 | String minute = String.valueOf(usageTimes[1]);
88 | String second = String.valueOf(usageTimes[2]);
89 | String time = "";
90 | if(usageTimes[0] > 0) time += hour + "h ";
91 | if(usageTimes[1] > 0) time += minute + "m ";
92 | if(usageTimes[2] > 0) time += second + "s";
93 | return time;
94 | }
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/app/src/main/java/tj/beataddiction/Utils.java:
--------------------------------------------------------------------------------
1 | package tj.beataddiction;
2 |
3 | import android.app.AppOpsManager;
4 | import android.app.usage.UsageEvents;
5 | import android.app.usage.UsageStatsManager;
6 | import android.content.Context;
7 | import android.content.pm.ApplicationInfo;
8 | import android.content.pm.PackageManager;
9 |
10 | import java.util.ArrayList;
11 | import java.util.HashMap;
12 | import java.util.List;
13 |
14 | import static android.content.Context.APP_OPS_SERVICE;
15 |
16 | class Utils {
17 | static long DAY_IN_MILLIS = 86400 * 1000;
18 |
19 | static int processTime(int hour, int minute, int second) {
20 | return hour*3600 + minute*60 + second;
21 | }
22 |
23 | static int[] reverseProcessTime(int time) {
24 | int[] hourMinSec = new int[3];
25 | hourMinSec[0] = time / 3600;
26 | time = time % 3600;
27 | hourMinSec[1] = time / 60;
28 | hourMinSec[2] = time % 60;
29 | return hourMinSec;
30 | }
31 |
32 | static HashMap getTimeSpent(Context context, String packageName, long beginTime, long endTime) {
33 | UsageEvents.Event currentEvent;
34 | List allEvents = new ArrayList<>();
35 | HashMap appUsageMap = new HashMap<>();
36 |
37 | UsageStatsManager usageStatsManager = (UsageStatsManager)context.getSystemService(Context.USAGE_STATS_SERVICE);
38 | UsageEvents usageEvents = usageStatsManager.queryEvents(beginTime, endTime);
39 |
40 | while (usageEvents.hasNextEvent()) {
41 | currentEvent = new UsageEvents.Event();
42 | usageEvents.getNextEvent(currentEvent);
43 | if(currentEvent.getPackageName().equals(packageName) || packageName == null) {
44 | if (currentEvent.getEventType() == UsageEvents.Event.ACTIVITY_RESUMED
45 | || currentEvent.getEventType() == UsageEvents.Event.ACTIVITY_PAUSED) {
46 | allEvents.add(currentEvent);
47 | String key = currentEvent.getPackageName();
48 | if (appUsageMap.get(key) == null)
49 | appUsageMap.put(key, 0);
50 | }
51 | }
52 | }
53 |
54 | for (int i = 0; i < allEvents.size() - 1; i++) {
55 | UsageEvents.Event E0 = allEvents.get(i);
56 | UsageEvents.Event E1 = allEvents.get(i + 1);
57 |
58 | if (E0.getEventType() == UsageEvents.Event.ACTIVITY_RESUMED
59 | && E1.getEventType() == UsageEvents.Event.ACTIVITY_PAUSED
60 | && E0.getClassName().equals(E1.getClassName())) {
61 | int diff = (int)(E1.getTimeStamp() - E0.getTimeStamp());
62 | diff /= 1000;
63 | Integer prev = appUsageMap.get(E0.getPackageName());
64 | if(prev == null) prev = 0;
65 | appUsageMap.put(E0.getPackageName(), prev + diff);
66 | }
67 | }
68 |
69 | if(allEvents.size() != 0) {
70 | UsageEvents.Event lastEvent = allEvents.get(allEvents.size() - 1);
71 | if (lastEvent.getEventType() == UsageEvents.Event.ACTIVITY_RESUMED) {
72 | String currentRunningPackageName = lastEvent.getPackageName();
73 | int diff = (int) System.currentTimeMillis() - (int) lastEvent.getTimeStamp();
74 | diff /= 1000;
75 | Integer prev = appUsageMap.get(currentRunningPackageName);
76 | if (prev == null) prev = 0;
77 | appUsageMap.put(currentRunningPackageName, prev + diff);
78 | appUsageMap.put("current" + currentRunningPackageName, -1); //for notification purpose
79 | }
80 | }
81 |
82 | return appUsageMap;
83 | }
84 |
85 | static boolean isUsageAccessAllowed(Context context) {
86 | try {
87 | PackageManager packageManager = context.getPackageManager();
88 | ApplicationInfo applicationInfo = packageManager.getApplicationInfo(context.getPackageName(), 0);
89 | AppOpsManager appOpsManager = (AppOpsManager)context.getSystemService(APP_OPS_SERVICE);
90 | int mode = 0;
91 | if (appOpsManager != null) {
92 | mode = appOpsManager.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS,
93 | applicationInfo.uid, applicationInfo.packageName);
94 | }
95 | return mode==AppOpsManager.MODE_ALLOWED;
96 | } catch (PackageManager.NameNotFoundException e) {
97 | return false;
98 | }
99 | }
100 | }
--------------------------------------------------------------------------------
/app/src/main/java/tj/beataddiction/DatabaseHelper.java:
--------------------------------------------------------------------------------
1 | package tj.beataddiction;
2 |
3 | import android.content.ContentValues;
4 | import android.content.Context;
5 | import android.database.Cursor;
6 | import android.database.sqlite.SQLiteDatabase;
7 | import android.database.sqlite.SQLiteOpenHelper;
8 |
9 | import java.util.ArrayList;
10 | import java.util.List;
11 |
12 | public class DatabaseHelper extends SQLiteOpenHelper {
13 | private static final String DATABASE_NAME = "BEAT_ADDICTION.db";
14 | private static final String TABLE_NAME = "TRACKED_APPS";
15 | private static final String PACKAGE_NAME = "PACKAGE_NAME";
16 | private static final String TIME_ALLOWED = "TIME_ALLOWED";
17 | private static final String IS_USAGE_EXCEEDED = "IS_USAGE_EXCEEDED";
18 | //when IS_USAGE_EXCEEDED changes from 0 to 1 (when TIME_SPENT
19 | //surpasses TIME_ALLOWED), a notification appears.
20 | //It is reset everyday at 12 am. The purpose of this field is to
21 | //prevent notification getting appeared constantly.
22 | private static final int VERSION = 1;
23 |
24 | DatabaseHelper(Context context) {
25 | super(context, DATABASE_NAME, null, VERSION);
26 | SQLiteDatabase db = this.getWritableDatabase();
27 | }
28 |
29 | @Override
30 | public void onCreate(SQLiteDatabase db) {
31 | db.execSQL("CREATE TABLE " + TABLE_NAME + "(PACKAGE_NAME TEXT PRIMARY KEY," +
32 | "TIME_ALLOWED INTEGER, IS_USAGE_EXCEEDED INTEGER)");
33 | }
34 |
35 | @Override
36 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
37 | db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
38 | onCreate(db);
39 | }
40 |
41 | void insert(String packageName, int timeAllowed) {
42 | SQLiteDatabase db = this.getWritableDatabase();
43 | ContentValues contentValues = new ContentValues();
44 | contentValues.put(PACKAGE_NAME, packageName);
45 | contentValues.put(TIME_ALLOWED, timeAllowed);
46 | contentValues.put(IS_USAGE_EXCEEDED, 0);
47 | long i = db.insert(TABLE_NAME, null, contentValues);
48 | db.close();
49 | }
50 |
51 | void delete(String packageName) {
52 | SQLiteDatabase db = this.getWritableDatabase();
53 | db.delete(TABLE_NAME, PACKAGE_NAME + " = ?", new String[]{packageName});
54 | db.close();
55 | }
56 |
57 | TrackedAppInfo getRow(String packageName) {
58 | TrackedAppInfo trackedAppInfo = null;
59 | SQLiteDatabase db = this.getReadableDatabase();
60 | Cursor cursor = db.rawQuery("SELECT * FROM " +TABLE_NAME+ " WHERE " + PACKAGE_NAME + " = ?", new String[]{packageName});
61 | if(cursor.moveToFirst()) {
62 | int timeAllowed = cursor.getInt(cursor.getColumnIndex(TIME_ALLOWED));
63 | int isUsageExceeded = cursor.getInt(cursor.getColumnIndex(IS_USAGE_EXCEEDED));
64 | trackedAppInfo = new TrackedAppInfo(packageName, timeAllowed, isUsageExceeded);
65 | }
66 | cursor.close();
67 | db.close();
68 | return trackedAppInfo;
69 | }
70 |
71 | List getAllRows() {
72 | List trackedAppInfos = new ArrayList<>();
73 | SQLiteDatabase db = this.getReadableDatabase();
74 | Cursor cursor = db.rawQuery("SELECT * FROM " +TABLE_NAME, null);
75 | while(cursor.moveToNext()) {
76 | String packageName = cursor.getString(cursor.getColumnIndex(PACKAGE_NAME));
77 | int timeAllowed = cursor.getInt(cursor.getColumnIndex(TIME_ALLOWED));
78 | int isUsageExceeded = cursor.getInt(cursor.getColumnIndex(IS_USAGE_EXCEEDED));
79 | trackedAppInfos.add(new TrackedAppInfo(packageName, timeAllowed, isUsageExceeded));
80 | }
81 | cursor.close();
82 | db.close();
83 | return trackedAppInfos;
84 | }
85 |
86 | void setTimeAllowed(String packageName, int timeAllowed) {
87 | SQLiteDatabase db = this.getWritableDatabase();
88 | ContentValues contentValues = new ContentValues();
89 | contentValues.put(TIME_ALLOWED, timeAllowed);
90 | db.update(TABLE_NAME, contentValues, PACKAGE_NAME + " = ?", new String[]{packageName});
91 | db.close();
92 | }
93 |
94 | void resetAllIsUsageExceeded() {
95 | SQLiteDatabase db = this.getWritableDatabase();
96 | ContentValues contentValues = new ContentValues();
97 | contentValues.put(IS_USAGE_EXCEEDED, 0);
98 | db.update(TABLE_NAME, contentValues, null, null);
99 | db.close();
100 | }
101 |
102 | void resetIsUsageExceeded(String packageName) {
103 | SQLiteDatabase db = this.getWritableDatabase();
104 | ContentValues contentValues = new ContentValues();
105 | contentValues.put(IS_USAGE_EXCEEDED, 0);
106 | db.update(TABLE_NAME, contentValues, PACKAGE_NAME + " = ?", new String[]{packageName});
107 | db.close();
108 | }
109 |
110 | void setIsUsageExceeded(String packageName) {
111 | SQLiteDatabase db = this.getWritableDatabase();
112 | ContentValues contentValues = new ContentValues();
113 | contentValues.put(IS_USAGE_EXCEEDED, 1);
114 | db.update(TABLE_NAME, contentValues, PACKAGE_NAME + " = ?", new String[]{packageName});
115 | db.close();
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/app/src/main/java/tj/beataddiction/MainActivity.java:
--------------------------------------------------------------------------------
1 | package tj.beataddiction;
2 |
3 | import androidx.appcompat.app.AlertDialog;
4 | import androidx.appcompat.app.AppCompatActivity;
5 |
6 | import android.content.Context;
7 | import android.content.DialogInterface;
8 | import android.content.Intent;
9 | import android.content.SharedPreferences;
10 | import android.content.pm.PackageInfo;
11 | import android.content.pm.PackageManager;
12 | import android.graphics.drawable.Drawable;
13 | import android.os.Bundle;
14 | import android.provider.Settings;
15 | import android.view.View;
16 | import android.widget.AdapterView;
17 | import android.widget.CheckBox;
18 | import android.widget.CompoundButton;
19 | import android.widget.ListView;
20 | import android.widget.Toast;
21 |
22 | import java.util.ArrayList;
23 | import java.util.Collections;
24 | import java.util.List;
25 |
26 | public class MainActivity extends AppCompatActivity {
27 | private DatabaseHelper dbHelper;
28 | private ListView appListView;
29 | private static int LAUNCH_SETTINGS_ACTIVITY = 1;
30 |
31 | @Override
32 | protected void onCreate(Bundle savedInstanceState) {
33 | super.onCreate(savedInstanceState);
34 | setContentView(R.layout.activity_main);
35 |
36 | dbHelper = new DatabaseHelper(this);
37 | appListView = findViewById(R.id.app_list);
38 |
39 | Alarms.resetIsUsageExceededData(getApplicationContext());
40 | startBackgroundService();
41 |
42 | openDialog();
43 | }
44 |
45 | @Override
46 | protected void onResume() {
47 | super.onResume();
48 | showAppListAndSetClickListener();
49 | }
50 |
51 | private List getAppInfoList() {
52 | PackageManager packageManager = getPackageManager();
53 | List packageInfoList = packageManager.getInstalledPackages(PackageManager.GET_ACTIVITIES);
54 | List appInfoList = new ArrayList<>();
55 |
56 | for (int i = 0; i < packageInfoList.size(); i++) {
57 | PackageInfo packageInfo = packageInfoList.get(i);
58 | if (packageManager.getLaunchIntentForPackage(packageInfo.packageName) != null) {
59 | String appName = packageInfo.applicationInfo.loadLabel(packageManager).toString();
60 | Drawable appIcon = packageInfo.applicationInfo.loadIcon(packageManager);
61 | String packageName = packageInfo.packageName;
62 |
63 | TrackedAppInfo trackedAppInfo = dbHelper.getRow(packageName);
64 | if(trackedAppInfo != null) {
65 | boolean isUsageExceeded = trackedAppInfo.getIsUsageExceeded() == 1;
66 | appInfoList.add(new AppInfo(appName, appIcon, packageName, true, isUsageExceeded));
67 | }
68 | else {
69 | appInfoList.add(new AppInfo(appName, appIcon, packageName, false, false));
70 | }
71 | }
72 | }
73 | Collections.sort(appInfoList);
74 | return appInfoList;
75 | }
76 |
77 | private void showAppListAndSetClickListener() {
78 | final ListappInfoList = getAppInfoList();
79 | AppInfoListAdapter appInfoListAdapter = new AppInfoListAdapter(MainActivity.this, appInfoList);
80 | appListView.setAdapter(appInfoListAdapter);
81 |
82 | appListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
83 | @Override
84 | public void onItemClick(AdapterView> adapterView, View view, final int i, long l) {
85 | Intent intent = new Intent(MainActivity.this, AppInfoActivity.class);
86 | intent.putExtra("packageName", appInfoList.get(i).getPackageName());
87 | intent.putExtra("appName", appInfoList.get(i).getAppName());
88 | startActivity(intent);
89 | }
90 | });
91 | }
92 |
93 | private void startBackgroundService() {
94 | if(Utils.isUsageAccessAllowed(this)) {
95 | Alarms.scheduleNotification(getApplicationContext());
96 | }
97 | }
98 |
99 | private void openDialog() {
100 | final SharedPreferences sharedPreferences = getSharedPreferences("DialogInfo", Context.MODE_PRIVATE);
101 |
102 | View checkBoxView = View.inflate(this, R.layout.checkbox, null);
103 | CheckBox checkBox = checkBoxView.findViewById(R.id.checkBox);
104 | checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
105 | @Override
106 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
107 | SharedPreferences.Editor editor = sharedPreferences.edit();
108 | editor.putBoolean("openDialog", false);
109 | editor.apply();
110 | }
111 | });
112 |
113 | AlertDialog.Builder builder = new AlertDialog.Builder(this);
114 | builder.setTitle("IMPORTANT!")
115 | .setMessage("You might not get usage notifications after reboot. To solve this issue, you can either " +
116 | "give auto start permission for this app manually from settings or launch " +
117 | "this app at least once after reboot. That's all :)")
118 | .setView(checkBoxView)
119 | .setPositiveButton("OK", new DialogInterface.OnClickListener() {
120 | @Override
121 | public void onClick(DialogInterface dialog, int which) {
122 | dialog.dismiss();
123 | }
124 | })
125 | .setCancelable(false);
126 |
127 | if(sharedPreferences.getBoolean("openDialog", true)) {
128 | builder.show();
129 | }
130 | }
131 |
132 | @Override
133 | protected void onDestroy() {
134 | super.onDestroy();
135 | dbHelper.close();
136 |
137 | }
138 | }
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:appName=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/src/main/java/tj/beataddiction/AppInfoActivity.java:
--------------------------------------------------------------------------------
1 | package tj.beataddiction;
2 |
3 | import androidx.appcompat.app.AlertDialog;
4 | import androidx.appcompat.app.AppCompatActivity;
5 |
6 | import android.content.DialogInterface;
7 | import android.content.Intent;
8 | import android.content.pm.PackageManager;
9 | import android.icu.util.Calendar;
10 | import android.os.Bundle;
11 | import android.provider.Settings;
12 | import android.util.Log;
13 | import android.view.View;
14 | import android.widget.ArrayAdapter;
15 | import android.widget.Button;
16 | import android.widget.ImageView;
17 | import android.widget.Spinner;
18 | import android.widget.TextView;
19 | import android.widget.Toast;
20 |
21 | import java.util.HashMap;
22 |
23 | public class AppInfoActivity extends AppCompatActivity {
24 | private static final String TAG = "AppInfoActivity";
25 | private static final int LAUNCH_SETTINGS_ACTIVITY = 1;
26 | private DatabaseHelper dbHelper;
27 | private String packageName;
28 | private String appName;
29 | private TextView appNameView;
30 | private ImageView appIcon;
31 | private Spinner hour1;
32 | private Spinner minute1;
33 | private TextView hour2;
34 | private TextView minute2;
35 | private TextView second2;
36 | private Button saveButton;
37 | private Button stopButton;
38 | private Button chartButton;
39 | private ImageView warning;
40 | private TrackedAppInfo trackedAppInfo;
41 | private int timeAllowed;
42 |
43 | @Override
44 | protected void onCreate(Bundle savedInstanceState) {
45 | super.onCreate(savedInstanceState);
46 | setContentView(R.layout.activity_app_info);
47 |
48 | if(!Utils.isUsageAccessAllowed(this)) {
49 | openUsageDialog();
50 | }
51 |
52 | packageName = getIntent().getStringExtra("packageName");
53 | appName = getIntent().getStringExtra("appName");
54 |
55 | dbHelper = new DatabaseHelper(this);
56 |
57 | appNameView = findViewById(R.id.chart_app_name);
58 | appIcon = findViewById(R.id.list_app_icon);
59 | hour1 = findViewById(R.id.hour1);
60 | minute1 = findViewById(R.id.minute1);
61 | hour2 = findViewById(R.id.hour2);
62 | minute2 = findViewById(R.id.minute2);
63 | second2 = findViewById(R.id.second2);
64 | saveButton = findViewById(R.id.save);
65 | stopButton = findViewById(R.id.stop);
66 | chartButton = findViewById(R.id.show_chart);
67 | warning = findViewById(R.id.isUsageExceeded);
68 |
69 | setSpinner();
70 | setAppNameAndImage();
71 |
72 | trackedAppInfo = dbHelper.getRow(packageName);
73 | if(trackedAppInfo != null) {
74 | timeAllowed = trackedAppInfo.getTimeAllowed();
75 | showTimeAllowed(timeAllowed);
76 |
77 | stopButton.setVisibility(View.VISIBLE);
78 | saveButton.setText(R.string.save);
79 | stopButton.setOnClickListener(new View.OnClickListener() {
80 | @Override
81 | public void onClick(View v) {
82 | openTrackingDialog();
83 | }
84 | });
85 | }
86 | saveButton.setOnClickListener(new View.OnClickListener() {
87 | @Override
88 | public void onClick(View v) {
89 | editTrackingInfo();
90 | }
91 | });
92 |
93 | chartButton.setOnClickListener(new View.OnClickListener() {
94 | @Override
95 | public void onClick(View v) {
96 | Intent intent = new Intent(AppInfoActivity.this, ChartActivity.class);
97 | intent.putExtra("packageName", packageName);
98 | intent.putExtra("appName", appName);
99 | startActivity(intent);
100 | }
101 | });
102 | }
103 |
104 | @Override
105 | protected void onResume() {
106 | super.onResume();
107 | int timeSpent = getTimeSpent();
108 | showTimeSpent(timeSpent);
109 | if(trackedAppInfo != null) {
110 | if(timeSpent > timeAllowed) {
111 | warning.setVisibility(ImageView.VISIBLE);
112 | hour2.setTextColor(getResources().getColor(R.color.warning, null));
113 | minute2.setTextColor(getResources().getColor(R.color.warning, null));
114 | second2.setTextColor(getResources().getColor(R.color.warning, null));
115 | }
116 | }
117 | }
118 |
119 | private void setSpinner() {
120 | Integer[] hourValues = new Integer[24];
121 | final Integer[] minuteValues = new Integer[60];
122 | for(int i=0; i<24; i++) {
123 | hourValues[i] = i;
124 | }
125 | for(int i=0; i<60; i++) {
126 | minuteValues[i] = i;
127 | }
128 | ArrayAdapter hourAdapter = new ArrayAdapter<>(this, R.layout.spinner_item, hourValues);
129 | ArrayAdapter minuteAdapter = new ArrayAdapter<>(this, R.layout.spinner_item, minuteValues);
130 | hour1.setAdapter(hourAdapter);
131 | minute1.setAdapter(minuteAdapter);
132 | }
133 |
134 | private void setAppNameAndImage() {
135 | appNameView.setText(appName);
136 | PackageManager packageManager = getPackageManager();
137 | try {
138 | appIcon.setImageDrawable(packageManager.getApplicationIcon(packageName));
139 | } catch (PackageManager.NameNotFoundException e) {
140 | Log.d(TAG, "package name not found");
141 | }
142 | }
143 |
144 | private void showTimeAllowed(int timeAllowed) {
145 | int[] timesAllowed = Utils.reverseProcessTime(timeAllowed);
146 | hour1.setSelection(timesAllowed[0]);
147 | minute1.setSelection(timesAllowed[1]);
148 | }
149 |
150 | private void showTimeSpent(int timeSpent) {
151 | int[] timesAllowed = Utils.reverseProcessTime(timeSpent);
152 | hour2.setText(String.valueOf(timesAllowed[0]));
153 | minute2.setText(String.valueOf(timesAllowed[1]));
154 | second2.setText(String.valueOf(timesAllowed[2]));
155 | }
156 |
157 | private int getTimeSpent() {
158 | Calendar calendar = Calendar.getInstance();
159 | calendar.set(Calendar.HOUR_OF_DAY, 0);
160 | calendar.set(Calendar.MINUTE, 0);
161 | calendar.set(Calendar.SECOND, 0);
162 | long beginTime = calendar.getTimeInMillis();
163 | long endTime = beginTime + Utils.DAY_IN_MILLIS;
164 | HashMap appUsageMap = Utils.getTimeSpent(this, packageName, beginTime, endTime);
165 | Integer usageTime = appUsageMap.get(packageName);
166 | if (usageTime == null) usageTime = 0;
167 | return usageTime;
168 | }
169 |
170 | private void editTrackingInfo() {
171 | Integer hour = Integer.parseInt(hour1.getSelectedItem().toString());
172 | Integer minute = Integer.parseInt(minute1.getSelectedItem().toString());
173 | int editedTimeAllowed = Utils.processTime(hour, minute, 0);
174 | if(dbHelper.getRow(packageName) == null) {
175 | dbHelper.insert(packageName, editedTimeAllowed);
176 | }
177 | else {
178 | dbHelper.setTimeAllowed(packageName, editedTimeAllowed);
179 | }
180 | if(editedTimeAllowed > getTimeSpent()) {
181 | dbHelper.resetIsUsageExceeded(packageName);
182 | }
183 | Toast.makeText(getApplicationContext(),"Changes Saved!",Toast.LENGTH_LONG).show();
184 | finish();
185 | }
186 |
187 | private void openUsageDialog() {
188 | AlertDialog.Builder builder = new AlertDialog.Builder(this);
189 | builder.setTitle("Usage Access Needed :(")
190 | .setMessage("You need to give usage access to this app to see usage data of your apps. " +
191 | "Click \"Go To Settings\" and then give the access :)")
192 | .setPositiveButton("Go To Settings", new DialogInterface.OnClickListener() {
193 | @Override
194 | public void onClick(DialogInterface dialog, int which) {
195 | Intent usageAccessIntent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
196 | startActivityForResult(usageAccessIntent, LAUNCH_SETTINGS_ACTIVITY);
197 | }
198 | })
199 | .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
200 | @Override
201 | public void onClick(DialogInterface dialog, int which) {
202 | dialog.dismiss();
203 | }
204 | })
205 | .setCancelable(false);
206 | builder.show();
207 | }
208 |
209 | private void openTrackingDialog() {
210 | AlertDialog.Builder builder = new AlertDialog.Builder(this);
211 | builder.setTitle("Do you want to stop tracking this app?")
212 | .setNegativeButton("No", new DialogInterface.OnClickListener() {
213 | @Override
214 | public void onClick(DialogInterface dialog, int which) {
215 | dialog.dismiss();
216 | }
217 | })
218 | .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
219 | @Override
220 | public void onClick(DialogInterface dialog, int which) {
221 | dbHelper.delete(packageName);
222 | dialog.dismiss();
223 | finish();
224 | }
225 | })
226 | .setCancelable(false);
227 | builder.show();
228 | }
229 |
230 | @Override
231 | protected void onActivityResult(int requestCode, int resultCode, Intent data) {
232 | super.onActivityResult(requestCode, resultCode, data);
233 |
234 | if (requestCode == LAUNCH_SETTINGS_ACTIVITY) {
235 | if(Utils.isUsageAccessAllowed(this)) {
236 | Alarms.scheduleNotification(this);
237 | }
238 | }
239 | }
240 |
241 | @Override
242 | protected void onDestroy() {
243 | super.onDestroy();
244 | dbHelper.close();
245 | }
246 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_app_info.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |
12 |
16 |
17 |
26 |
27 |
37 |
38 |
48 |
49 |
60 |
61 |
74 |
75 |
89 |
90 |
104 |
105 |
117 |
118 |
130 |
131 |
143 |
144 |
154 |
155 |
169 |
170 |
183 |
184 |
196 |
197 |
209 |
210 |
222 |
223 |
237 |
238 |
249 |
250 |
251 |
--------------------------------------------------------------------------------