├── app
├── .gitignore
├── release
│ ├── app-release.apk
│ ├── output.json
│ └── output-metadata.json
├── src
│ ├── main
│ │ ├── ic_launcher-playstore.png
│ │ ├── res
│ │ │ ├── drawable
│ │ │ │ ├── icon_np.png
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── icon_namoo.png
│ │ │ │ ├── icon_tistory.png
│ │ │ │ ├── icon_fingerprint.png
│ │ │ │ ├── background_splash.xml
│ │ │ │ ├── vector_size.xml
│ │ │ │ ├── background_circle_white_translucent.xml
│ │ │ │ ├── vector_home.xml
│ │ │ │ ├── vector_check.xml
│ │ │ │ ├── vector_next.xml
│ │ │ │ ├── vector_add.xml
│ │ │ │ ├── vector_apps.xml
│ │ │ │ ├── vector_draw.xml
│ │ │ │ ├── vector_delay.xml
│ │ │ │ ├── vector_theme.xml
│ │ │ │ ├── vector_random.xml
│ │ │ │ ├── vector_review.xml
│ │ │ │ ├── background_border_top.xml
│ │ │ │ ├── vector_more.xml
│ │ │ │ ├── background_border_bottom.xml
│ │ │ │ ├── vector_pattern.xml
│ │ │ │ ├── vector_visibility.xml
│ │ │ │ ├── vector_recommend.xml
│ │ │ │ ├── vector_haptic.xml
│ │ │ │ ├── vector_lock.xml
│ │ │ │ ├── vector_watch.xml
│ │ │ │ ├── vector_prevent_uninstall.xml
│ │ │ │ ├── vector_search.xml
│ │ │ │ ├── vector_remove_ad.xml
│ │ │ │ ├── vector_restore.xml
│ │ │ │ ├── vector_info.xml
│ │ │ │ ├── vector_unlock.xml
│ │ │ │ ├── vector_backup.xml
│ │ │ │ ├── vector_pin.xml
│ │ │ │ ├── vector_dark.xml
│ │ │ │ ├── vector_developer.xml
│ │ │ │ ├── vector_touch.xml
│ │ │ │ ├── vector_support.xml
│ │ │ │ ├── vector_github.xml
│ │ │ │ ├── vector_color.xml
│ │ │ │ ├── vector_license.xml
│ │ │ │ ├── foreground_border_clickable.xml
│ │ │ │ ├── vector_settings.xml
│ │ │ │ ├── vector_light.xml
│ │ │ │ └── vector_fingerprint.xml
│ │ │ ├── raw
│ │ │ │ ├── bell_watcher.mp3
│ │ │ │ └── license.xml
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── ic_launcher_round.png
│ │ │ │ └── ic_launcher_foreground.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── ic_launcher_round.png
│ │ │ │ └── ic_launcher_foreground.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── ic_launcher_round.png
│ │ │ │ └── ic_launcher_foreground.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── ic_launcher_round.png
│ │ │ │ └── ic_launcher_foreground.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── ic_launcher_round.png
│ │ │ │ └── ic_launcher_foreground.png
│ │ │ ├── xml
│ │ │ │ └── device_admin.xml
│ │ │ ├── transition
│ │ │ │ ├── fade.xml
│ │ │ │ ├── change_bounds.xml
│ │ │ │ └── slide.xml
│ │ │ ├── anim
│ │ │ │ ├── stand.xml
│ │ │ │ ├── views_translate.xml
│ │ │ │ ├── view_translate.xml
│ │ │ │ ├── activity_scale_minus_to_zero.xml
│ │ │ │ ├── activity_scale_plus_to_zero.xml
│ │ │ │ ├── activity_scale_zero_to_minus.xml
│ │ │ │ └── activity_scale_zero_to_plus.xml
│ │ │ ├── mipmap-anydpi-v26
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ ├── values
│ │ │ │ ├── attrs.xml
│ │ │ │ ├── colors.xml
│ │ │ │ ├── dimens.xml
│ │ │ │ └── styles.xml
│ │ │ └── layout
│ │ │ │ ├── view_floating_button.xml
│ │ │ │ ├── view_size_picker.xml
│ │ │ │ ├── view_check_button.xml
│ │ │ │ ├── view_app_bundle.xml
│ │ │ │ ├── fragment_theme.xml
│ │ │ │ ├── fragment_apps.xml
│ │ │ │ ├── fragment_wizard.xml
│ │ │ │ ├── z_activity_restore.xml
│ │ │ │ ├── activity_main.xml
│ │ │ │ ├── activity_pattern.xml
│ │ │ │ ├── fragment_home.xml
│ │ │ │ └── activity_pin.xml
│ │ ├── java
│ │ │ └── nm
│ │ │ │ └── security
│ │ │ │ └── namooprotector
│ │ │ │ ├── bundle
│ │ │ │ └── AppBundle.kt
│ │ │ │ ├── NamooProtector.kt
│ │ │ │ ├── receiver
│ │ │ │ ├── BootReceiver.kt
│ │ │ │ ├── UpdateReceiver.kt
│ │ │ │ └── InstallReceiver.kt
│ │ │ │ ├── util
│ │ │ │ ├── ResourceUtil.kt
│ │ │ │ ├── ServiceUtil.kt
│ │ │ │ ├── ActivityUtil.kt
│ │ │ │ ├── ConvertUtil.kt
│ │ │ │ ├── DataUtil.kt
│ │ │ │ ├── CheckUtil.kt
│ │ │ │ ├── SettingsUtil.kt
│ │ │ │ └── AnimationUtil.kt
│ │ │ │ ├── activity
│ │ │ │ ├── ZRestoreActivity.kt
│ │ │ │ ├── AddAppActivity.kt
│ │ │ │ ├── AboutActivity.kt
│ │ │ │ ├── PinActivity.kt
│ │ │ │ ├── ZBackupActivity.kt
│ │ │ │ ├── PatternActivity.kt
│ │ │ │ ├── MainActivity.kt
│ │ │ │ └── SupportActivity.kt
│ │ │ │ ├── service
│ │ │ │ ├── ProtectorServiceHelper.kt
│ │ │ │ └── ProtectorService.kt
│ │ │ │ ├── fragment
│ │ │ │ ├── HomeFragment.kt
│ │ │ │ ├── WizardFragment.kt
│ │ │ │ └── AppsFragment.kt
│ │ │ │ ├── widget
│ │ │ │ └── FloatingButton.kt
│ │ │ │ └── adapter
│ │ │ │ └── AppsAdapter.kt
│ │ └── AndroidManifest.xml
│ ├── test
│ │ └── java
│ │ │ └── nm
│ │ │ └── security
│ │ │ └── namooprotector
│ │ │ └── ExampleUnitTest.kt
│ └── androidTest
│ │ └── java
│ │ └── nm
│ │ └── security
│ │ └── namooprotector
│ │ └── ExampleInstrumentedTest.kt
├── proguard-rules.pro
└── build.gradle
├── settings.gradle
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .idea
├── codeStyles
│ ├── codeStyleConfig.xml
│ └── Project.xml
├── vcs.xml
├── misc.xml
├── runConfigurations.xml
├── gradle.xml
└── jarRepositories.xml
├── .gitignore
├── gradle.properties
├── gradlew.bat
├── README.md
└── gradlew
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/app/release/app-release.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/namooplus/NamooProtector/HEAD/app/release/app-release.apk
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/namooplus/NamooProtector/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/app/src/main/ic_launcher-playstore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/namooplus/NamooProtector/HEAD/app/src/main/ic_launcher-playstore.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/icon_np.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/namooplus/NamooProtector/HEAD/app/src/main/res/drawable/icon_np.png
--------------------------------------------------------------------------------
/app/src/main/res/raw/bell_watcher.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/namooplus/NamooProtector/HEAD/app/src/main/res/raw/bell_watcher.mp3
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/namooplus/NamooProtector/HEAD/app/src/main/res/drawable/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/icon_namoo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/namooplus/NamooProtector/HEAD/app/src/main/res/drawable/icon_namoo.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/icon_tistory.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/namooplus/NamooProtector/HEAD/app/src/main/res/drawable/icon_tistory.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/namooplus/NamooProtector/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/namooplus/NamooProtector/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/icon_fingerprint.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/namooplus/NamooProtector/HEAD/app/src/main/res/drawable/icon_fingerprint.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/namooplus/NamooProtector/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/namooplus/NamooProtector/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/namooplus/NamooProtector/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/namooplus/NamooProtector/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/namooplus/NamooProtector/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/namooplus/NamooProtector/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/namooplus/NamooProtector/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/namooplus/NamooProtector/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/namooplus/NamooProtector/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/namooplus/NamooProtector/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/namooplus/NamooProtector/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/namooplus/NamooProtector/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/namooplus/NamooProtector/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/xml/device_admin.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/transition/fade.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches/build_file_checksums.ser
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | .DS_Store
9 | /build
10 | /captures
11 | .externalNativeBuild
12 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/stand.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/release/output.json:
--------------------------------------------------------------------------------
1 | [{"outputType":{"type":"APK"},"apkInfo":{"type":"MAIN","splits":[],"versionCode":61,"versionName":"3.1","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}]
--------------------------------------------------------------------------------
/app/src/main/java/nm/security/namooprotector/bundle/AppBundle.kt:
--------------------------------------------------------------------------------
1 | package nm.security.namooprotector.bundle
2 |
3 | import android.graphics.drawable.Drawable
4 |
5 | data class AppBundle(val icon: Drawable, val label: String, val packageName: String, var state: Boolean)
--------------------------------------------------------------------------------
/app/src/main/res/anim/views_translate.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Aug 03 23:14:17 KST 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-6.1.1-all.zip
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/background_splash.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 | -
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/transition/change_bounds.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/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/transition/slide.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/vector_size.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/background_circle_white_translucent.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | -
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/vector_home.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/vector_check.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/vector_next.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/vector_add.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/vector_apps.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/vector_draw.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/vector_delay.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/vector_theme.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/vector_random.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/test/java/nm/security/namooprotector/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package nm.security.namooprotector
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/app/release/output-metadata.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 1,
3 | "artifactType": {
4 | "type": "APK",
5 | "kind": "Directory"
6 | },
7 | "applicationId": "nm.security.namooprotector",
8 | "variantName": "release",
9 | "elements": [
10 | {
11 | "type": "SINGLE",
12 | "filters": [],
13 | "properties": [],
14 | "versionCode": 63,
15 | "versionName": "4.0",
16 | "enabled": true,
17 | "outputFile": "app-release.apk"
18 | }
19 | ]
20 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/vector_review.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/view_translate.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/java/nm/security/namooprotector/NamooProtector.kt:
--------------------------------------------------------------------------------
1 | package nm.security.namooprotector
2 |
3 | import android.app.Application
4 | import android.content.Context
5 | import com.google.android.gms.ads.MobileAds
6 |
7 | class NamooProtector : Application()
8 | {
9 | init
10 | {
11 | instance = this
12 | }
13 | companion object
14 | {
15 | private var instance: NamooProtector? = null
16 |
17 | val context : Context
18 | get() = instance!!.applicationContext
19 | }
20 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/background_border_top.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | -
5 |
6 |
7 |
8 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/vector_more.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/background_border_bottom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | -
5 |
6 |
7 |
8 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/vector_pattern.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/vector_visibility.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/vector_recommend.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/vector_haptic.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/vector_lock.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/vector_watch.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/java/nm/security/namooprotector/receiver/BootReceiver.kt:
--------------------------------------------------------------------------------
1 | package nm.security.namooprotector.receiver
2 |
3 | import android.content.BroadcastReceiver
4 | import android.content.Context
5 | import android.content.Intent
6 | import nm.security.namooprotector.util.CheckUtil
7 | import nm.security.namooprotector.util.ServiceUtil
8 |
9 | class BootReceiver : BroadcastReceiver()
10 | {
11 | override fun onReceive(context: Context, intent: Intent)
12 | {
13 | if (CheckUtil.isNPValid && CheckUtil.isServiceRunning)
14 | ServiceUtil.runService(true)
15 | }
16 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/vector_prevent_uninstall.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/activity_scale_minus_to_zero.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
13 |
14 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/activity_scale_plus_to_zero.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
13 |
14 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/activity_scale_zero_to_minus.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
13 |
14 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/activity_scale_zero_to_plus.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
13 |
14 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/vector_search.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/vector_remove_ad.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/vector_restore.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/vector_info.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/vector_unlock.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/vector_backup.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/java/nm/security/namooprotector/util/ResourceUtil.kt:
--------------------------------------------------------------------------------
1 | package nm.security.namooprotector.util
2 |
3 | import androidx.core.content.ContextCompat
4 | import androidx.core.content.res.ResourcesCompat
5 | import nm.security.namooprotector.NamooProtector.Companion.context
6 |
7 | object ResourceUtil
8 | {
9 | fun getColor(resource: Int) = ResourcesCompat.getColor(context.resources, resource, null)
10 | fun getDimen(resource: Int) = context.resources.getDimension(resource)
11 | fun getDrawable(resource: Int) = ContextCompat.getDrawable(context, resource)
12 | fun getString(resource: Int) = context.resources.getString(resource)
13 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/vector_pin.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/vector_dark.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/src/main/java/nm/security/namooprotector/activity/ZRestoreActivity.kt:
--------------------------------------------------------------------------------
1 | package nm.security.namooprotector.activity
2 |
3 | import android.os.Bundle
4 | import nm.security.namooprotector.util.*
5 | import androidx.appcompat.app.AppCompatActivity
6 | import nm.security.namooprotector.R
7 |
8 | class ZRestoreActivity: AppCompatActivity()
9 | {
10 | //라이프사이클
11 | override fun onCreate(savedInstanceState: Bundle?)
12 | {
13 | super.onCreate(savedInstanceState)
14 | setContentView(R.layout.z_activity_restore)
15 |
16 | ActivityUtil.initFlag(this, true)
17 | ActivityUtil.initPreviousTitle(this)
18 | }
19 |
20 | //설정
21 |
22 | //클릭 이벤트
23 |
24 | //메소드
25 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/vector_developer.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/view_floating_button.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
19 |
20 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/nm/security/namooprotector/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package nm.security.namooprotector
2 |
3 | import android.support.test.InstrumentationRegistry
4 | import android.support.test.runner.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getTargetContext()
22 | assertEquals("nm.security.namooprotector", appContext.packageName)
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/src/main/java/nm/security/namooprotector/receiver/UpdateReceiver.kt:
--------------------------------------------------------------------------------
1 | package nm.security.namooprotector.receiver
2 |
3 | import android.content.BroadcastReceiver
4 | import android.content.Context
5 | import android.content.Intent
6 | import android.widget.Toast
7 | import nm.security.namooprotector.R
8 | import nm.security.namooprotector.util.CheckUtil
9 | import nm.security.namooprotector.util.ResourceUtil
10 | import nm.security.namooprotector.util.ServiceUtil
11 |
12 | class UpdateReceiver : BroadcastReceiver()
13 | {
14 | override fun onReceive(context: Context, intent: Intent)
15 | {
16 | if (CheckUtil.isNPValid && CheckUtil.isServiceRunning)
17 | ServiceUtil.runService(true)
18 |
19 | Toast.makeText(context, ResourceUtil.getString(R.string.alert_np_updated), Toast.LENGTH_LONG).show()
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/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 name 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 name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/vector_touch.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/java/nm/security/namooprotector/util/ServiceUtil.kt:
--------------------------------------------------------------------------------
1 | package nm.security.namooprotector.util
2 |
3 | import android.content.Intent
4 | import android.os.Build
5 | import nm.security.namooprotector.NamooProtector.Companion.context
6 | import nm.security.namooprotector.service.ProtectorService
7 |
8 | object ServiceUtil
9 | {
10 | fun runService(activate: Boolean)
11 | {
12 | if (activate)
13 | {
14 | if (Build.VERSION.SDK_INT >= 26) context.startForegroundService(Intent(context, ProtectorService::class.java))
15 | else context.startService(Intent(context, ProtectorService::class.java))
16 |
17 | CheckUtil.isServiceRunning = true
18 | }
19 | else
20 | {
21 | context.stopService(Intent(context, ProtectorService::class.java))
22 |
23 | CheckUtil.isServiceRunning = false
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/vector_support.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
12 |
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | #fafafa
6 | #fafafa
7 | #64e6b4
8 |
9 |
10 | #46AE8B
11 |
12 |
13 | #fafafa
14 | #ffffff
15 |
16 |
17 | #52cc9d
18 | #81b3e6
19 | #f48381
20 |
21 |
22 | #ffffff
23 | #e6e6e6
24 | #333333
25 | #4e4e4e
26 |
27 |
--------------------------------------------------------------------------------
/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 | # Kotlin code style for this project: "official" or "obsolete":
15 | kotlin.code.style=official
16 | android.useAndroidX=true
17 | android.enableJetifier=true
18 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 25sp
6 | 14sp
7 | 12sp
8 |
9 |
10 | 120dp
11 |
12 |
13 | 30dp
14 | 15dp
15 | 10dp
16 | 5dp
17 | 110dp
18 | 80dp
19 |
20 |
21 | 7dp
22 | 100dp
23 |
24 |
25 | 10dp
26 | 15dp
27 |
28 |
--------------------------------------------------------------------------------
/app/src/main/java/nm/security/namooprotector/activity/AddAppActivity.kt:
--------------------------------------------------------------------------------
1 | package nm.security.namooprotector.activity
2 |
3 | import android.app.Activity
4 | import android.os.Bundle
5 | import android.widget.Toast
6 | import nm.security.namooprotector.R
7 | import nm.security.namooprotector.util.*
8 | import nm.security.namooprotector.util.DataUtil.APPS
9 |
10 | class AddAppActivity: Activity()
11 | {
12 | //라이프사이클
13 | override fun onCreate(savedInstanceState: Bundle?)
14 | {
15 | super.onCreate(savedInstanceState)
16 |
17 | val packageName = intent.getStringExtra("packageName")
18 |
19 | //추가
20 | if (packageName != null)
21 | {
22 | DataUtil.put(packageName, APPS, true)
23 | Toast.makeText(this, String.format(getString(R.string.success_add_app), ConvertUtil.packageNameToAppName(packageName)), Toast.LENGTH_SHORT).show()
24 | }
25 |
26 | //종료
27 | finish()
28 | }
29 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/view_size_picker.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
18 |
19 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/vector_github.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/vector_color.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/vector_license.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/java/nm/security/namooprotector/service/ProtectorServiceHelper.kt:
--------------------------------------------------------------------------------
1 | package nm.security.namooprotector.service
2 |
3 | object ProtectorServiceHelper
4 | {
5 | //잠금 해제가 인증된 앱
6 | private val authorizedApps = mutableListOf()
7 |
8 | fun addTemporaryAuthorizedApp(packageName: String)
9 | {
10 | authorizedApps.add(AuthorizeBundle(packageName, 0L))
11 | }
12 | fun addAuthorizedApp(packageName: String, authorizedTime: Long)
13 | {
14 | if (packageName == "nm.security.namooprotector") return
15 |
16 | authorizedApps.add(AuthorizeBundle(packageName, System.currentTimeMillis() + authorizedTime * 1000))
17 | }
18 | fun clearTemporaryAuthorizedApp()
19 | {
20 | authorizedApps.removeAll { it.authorizedTime == 0L }
21 | }
22 | fun updateAuthorizedApps()
23 | {
24 | authorizedApps.removeAll { it.authorizedTime > 0L && it.authorizedTime < System.currentTimeMillis() }
25 | }
26 | fun isAuthorized(packageName: String): Boolean
27 | {
28 | return authorizedApps.find { it.packageName == packageName } != null
29 | }
30 | fun resetAuthorizedApps()
31 | {
32 | authorizedApps.clear()
33 | }
34 |
35 | data class AuthorizeBundle(val packageName: String, var authorizedTime: Long)
36 | }
--------------------------------------------------------------------------------
/app/src/main/res/raw/license.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | TedPermission
5 | https://github.com/ParkSangGwon/TedPermission
6 | Copyright 2017 Ted Park
7 | Apache Software License 2.0
8 |
9 |
10 |
11 | ColorPickerView
12 | https://github.com/skydoves/ColorPickerView
13 | Copyright 2017 skydoves
14 | Apache Software License 2.0
15 |
16 |
17 |
18 | Goldfinger
19 | https://github.com/infinum/Android-Goldfinger
20 | Copyright 2018 Infinum
21 | Apache Software License 2.0
22 |
23 |
24 |
25 | PatternLockView
26 | https://github.com/aritraroy/PatternLockView
27 | Copyright 2017 aritraroy
28 | Apache Software License 2.0
29 |
30 |
31 |
32 | LicensesDialog
33 | https://psdev.de/LicensesDialog/
34 | Copyright 2013-2016 Philip Schiffer
35 | Apache Software License 2.0
36 |
37 |
38 |
--------------------------------------------------------------------------------
/app/src/main/java/nm/security/namooprotector/util/ActivityUtil.kt:
--------------------------------------------------------------------------------
1 | package nm.security.namooprotector.util
2 |
3 | import android.app.Activity
4 | import android.app.ActivityOptions
5 | import android.content.Intent
6 | import android.util.Pair
7 | import android.view.View
8 | import android.widget.TextView
9 | import nm.security.namooprotector.R
10 |
11 | object ActivityUtil
12 | {
13 | fun initFlag(activity: Activity, darkStatusBar: Boolean)
14 | {
15 | activity.window.decorView.systemUiVisibility = if (darkStatusBar) View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR else View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
16 | }
17 | fun initPreviousTitle(activity: Activity)
18 | {
19 | activity.findViewById(R.id.previous_title_indicator).text = activity.intent.getStringExtra("title")
20 | }
21 |
22 | fun startActivityWithAnimation(from: Activity, to: Class)
23 | {
24 | val intent = Intent(from, to)
25 | .putExtra("title", from.findViewById(R.id.title_indicator).text.toString())
26 | val options = ActivityOptions.makeSceneTransitionAnimation(from,
27 | Pair.create(from.findViewById(R.id.app_indicator) as View, "appIndicator"),
28 | Pair.create(from.findViewById(R.id.title_indicator) as View, "titleIndicator"))
29 | from.startActivity(intent, options.toBundle())
30 | }
31 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/foreground_border_clickable.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | -
10 |
11 |
12 |
13 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | -
29 |
30 |
31 |
32 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | -
47 |
48 |
49 |
50 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/view_check_button.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
19 |
20 |
28 |
29 |
37 |
38 |
--------------------------------------------------------------------------------
/app/src/main/java/nm/security/namooprotector/activity/AboutActivity.kt:
--------------------------------------------------------------------------------
1 | package nm.security.namooprotector.activity
2 |
3 | import android.content.Intent
4 | import android.content.pm.PackageInfo
5 | import android.content.pm.PackageManager
6 | import android.net.Uri
7 | import android.os.Bundle
8 | import android.view.View
9 | import androidx.appcompat.app.AppCompatActivity
10 | import de.psdev.licensesdialog.LicensesDialog
11 | import kotlinx.android.synthetic.main.activity_about.*
12 | import nm.security.namooprotector.R
13 | import nm.security.namooprotector.util.ActivityUtil
14 | import nm.security.namooprotector.util.ResourceUtil
15 |
16 |
17 | class AboutActivity: AppCompatActivity()
18 | {
19 | //라이프사이클
20 | override fun onCreate(savedInstanceState: Bundle?)
21 | {
22 | super.onCreate(savedInstanceState)
23 | setContentView(R.layout.activity_about)
24 |
25 | ActivityUtil.initFlag(this, true)
26 | ActivityUtil.initPreviousTitle(this)
27 |
28 | initUI()
29 | }
30 |
31 | //설정
32 | private fun initUI()
33 | {
34 | about_app_version_indicator.text = packageManager.getPackageInfo(packageName, 0).versionName ?: ResourceUtil.getString(R.string.error_unknown)
35 | }
36 |
37 | //클릭 이벤트
38 | fun github(view: View)
39 | {
40 | startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/namooplus")))
41 | }
42 | fun tistory(view: View)
43 | {
44 | startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("https://namooplus.tistory.com")))
45 | }
46 | fun license(view: View)
47 | {
48 | LicensesDialog.Builder(this)
49 | .setNotices(R.raw.license)
50 | .build()
51 | .show()
52 | }
53 |
54 | //메소드
55 | }
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
22 |
23 |
32 |
33 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/app/src/main/java/nm/security/namooprotector/util/ConvertUtil.kt:
--------------------------------------------------------------------------------
1 | package nm.security.namooprotector.util
2 |
3 | import android.content.pm.PackageManager
4 | import android.graphics.drawable.Drawable
5 | import android.util.TypedValue
6 | import com.andrognito.patternlockview.PatternLockView
7 | import nm.security.namooprotector.NamooProtector.Companion.context
8 | import nm.security.namooprotector.R
9 |
10 | object ConvertUtil
11 | {
12 | fun packageNameToAppName(packageName: String): String
13 | {
14 | return try
15 | {
16 | context.packageManager.getApplicationLabel(context.packageManager.getApplicationInfo(packageName, 0)).toString()
17 | }
18 | catch (e: PackageManager.NameNotFoundException)
19 | {
20 | ResourceUtil.getString(R.string.error_unknown)
21 | }
22 | }
23 | fun packageNameToAppIcon(packageName: String): Drawable
24 | {
25 | return try
26 | {
27 | context.packageManager.getApplicationIcon(packageName)
28 | }
29 | catch (e: PackageManager.NameNotFoundException)
30 | {
31 | context.resources.getDrawable(R.drawable.ic_launcher, null)
32 | }
33 | }
34 |
35 | fun intColorToStringColor(color: Int): String
36 | {
37 | return String.format("#%08X", -0x1 and color)
38 | }
39 | fun dpToPx(dimen: Float): Float
40 | {
41 | return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dimen, context.resources.displayMetrics)
42 | }
43 |
44 | fun patternToString(patternView: PatternLockView, pattern: List?): String
45 | {
46 | if (pattern == null) return ""
47 |
48 | val stringBuilder = StringBuilder()
49 | for (dot in pattern) stringBuilder.append(dot.row * patternView.dotCount + dot.column + 1)
50 |
51 | return stringBuilder.toString()
52 | }
53 | }
--------------------------------------------------------------------------------
/app/src/main/java/nm/security/namooprotector/util/DataUtil.kt:
--------------------------------------------------------------------------------
1 | package nm.security.namooprotector.util
2 |
3 | import android.content.Context
4 | import nm.security.namooprotector.NamooProtector.Companion.context
5 |
6 | object DataUtil
7 | {
8 | const val NP = "NP" //광고
9 | const val LOCK = "Lock" //잠금방식
10 | const val APPS = "Apps" //잠금 앱
11 | const val SETTING = "Setting" //세부설정
12 | const val THEME = "Theme" //테마
13 |
14 | fun put(name: String, location: String, data: Any)
15 | {
16 | val sharedPreference = context.getSharedPreferences(location, Context.MODE_PRIVATE)
17 | val editor = sharedPreference.edit()
18 |
19 | when (data)
20 | {
21 | is String -> editor.putString(name, data)
22 | is Int -> editor.putInt(name, data)
23 | is Float -> editor.putFloat(name, data)
24 | is Boolean -> editor.putBoolean(name, data)
25 | }
26 |
27 | editor.apply()
28 | }
29 | fun getString(name: String, location: String, defaultValue: String = ""): String = context.getSharedPreferences(location, Context.MODE_PRIVATE).getString(name, defaultValue) ?: ""
30 | fun getInt(name: String, location: String, defaultValue: Int = 0): Int = context.getSharedPreferences(location, Context.MODE_PRIVATE).getInt(name, defaultValue)
31 | fun getFloat(name: String, location: String, defaultValue: Float = 0f): Float = context.getSharedPreferences(location, Context.MODE_PRIVATE).getFloat(name, defaultValue)
32 | fun getBoolean(name: String, location: String, defaultValue: Boolean = false): Boolean = context.getSharedPreferences(location, Context.MODE_PRIVATE).getBoolean(name, defaultValue)
33 |
34 | fun remove(location: String)
35 | {
36 | val sharedPreference = context.getSharedPreferences(location, Context.MODE_PRIVATE)
37 | val editor = sharedPreference.edit()
38 |
39 | editor.clear()
40 | editor.apply()
41 | }
42 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/vector_settings.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/java/nm/security/namooprotector/util/CheckUtil.kt:
--------------------------------------------------------------------------------
1 | package nm.security.namooprotector.util
2 |
3 | import android.app.AppOpsManager
4 | import android.content.Context
5 | import android.content.pm.PackageManager
6 | import android.hardware.camera2.CameraManager
7 | import android.net.ConnectivityManager
8 | import android.net.NetworkInfo
9 | import android.provider.Settings
10 | import co.infinum.goldfinger.Goldfinger
11 | import nm.security.namooprotector.NamooProtector.Companion.context
12 |
13 | object CheckUtil
14 | {
15 | //필수 설정 요소
16 | val isNPValid: Boolean
17 | get() = isPasswordSet && isUsageStatsPermissionGranted && isOverlayPermissionGranted
18 |
19 | val isPasswordSet: Boolean
20 | get() = SettingsUtil.lockType != null
21 |
22 | val isUsageStatsPermissionGranted: Boolean
23 | get() = (context.getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager).checkOpNoThrow("android:get_usage_stats", android.os.Process.myUid(), context.packageName) == AppOpsManager.MODE_ALLOWED
24 |
25 | val isOverlayPermissionGranted: Boolean
26 | get() = Settings.canDrawOverlays(context)
27 |
28 | //서비스
29 | var isServiceRunning: Boolean
30 | set(value) = DataUtil.put("activated", DataUtil.NP, value)
31 | get() = DataUtil.getBoolean("activated", DataUtil.NP)
32 |
33 | //광고
34 | var isAdRemoved: Boolean
35 | set(value) = DataUtil.put("removeAds", DataUtil.NP, value)
36 | get() = DataUtil.getBoolean("removeAds", DataUtil.NP)
37 |
38 | //지원
39 | fun isFingerprintAvailable(fingerprintManager: Goldfinger) =
40 | fingerprintManager.hasFingerprintHardware() && fingerprintManager.hasEnrolledFingerprint() && SettingsUtil.lockType != null
41 |
42 | val isFlashSupported: Boolean
43 | get() = context.packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH)
44 |
45 | //기타
46 | val isNetworkAvailable: Boolean
47 | get()
48 | {
49 | val activeNetwork: NetworkInfo? = (context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager).activeNetworkInfo
50 |
51 | return activeNetwork?.isConnected ?: false
52 | }
53 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/vector_light.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/vector_fingerprint.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/.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 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/view_app_bundle.xml:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
21 |
22 |
29 |
30 |
37 |
38 |
46 |
47 |
48 |
49 |
58 |
59 |
--------------------------------------------------------------------------------
/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/nm/security/namooprotector/receiver/InstallReceiver.kt:
--------------------------------------------------------------------------------
1 | package nm.security.namooprotector.receiver
2 |
3 | import android.app.NotificationChannel
4 | import android.app.NotificationManager
5 | import android.app.PendingIntent
6 | import android.content.BroadcastReceiver
7 | import android.content.Context
8 | import android.content.Intent
9 | import android.os.Build
10 | import androidx.core.app.NotificationCompat
11 | import nm.security.namooprotector.R
12 | import nm.security.namooprotector.activity.AddAppActivity
13 | import nm.security.namooprotector.util.ConvertUtil
14 | import nm.security.namooprotector.util.DataUtil
15 | import nm.security.namooprotector.util.DataUtil.APPS
16 | import nm.security.namooprotector.util.SettingsUtil
17 | import java.util.*
18 |
19 | class InstallReceiver : BroadcastReceiver()
20 | {
21 | override fun onReceive(context: Context, intent: Intent)
22 | {
23 | val packageName = intent.data!!.schemeSpecificPart
24 |
25 | if (!SettingsUtil.protectionNotification || DataUtil.getBoolean(packageName, APPS))
26 | return
27 |
28 | //패키지 전달
29 | val addAppIntent = Intent(context, AddAppActivity::class.java)
30 | addAppIntent.putExtra("packageName", packageName)
31 |
32 | //알림
33 | val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
34 |
35 | val notification = NotificationCompat.Builder(context, "namooprotector_appadd")
36 | .setSmallIcon(R.drawable.icon_np)
37 | .setContentTitle(context.getString(R.string.notification_protection_title))
38 | .setContentText(String.format(context.getString(R.string.notification_protection_message), ConvertUtil.packageNameToAppName(packageName)))
39 | .setContentIntent(PendingIntent.getActivity(context, 0, addAppIntent, PendingIntent.FLAG_UPDATE_CURRENT))
40 | .setOngoing(false)
41 | .setAutoCancel(true)
42 | .setPriority(NotificationCompat.PRIORITY_DEFAULT)
43 |
44 | if (Build.VERSION.SDK_INT >= 26)
45 | {
46 | val channel = NotificationChannel("namooprotector_appadd", context.getString(R.string.notification_protection_title), NotificationManager.IMPORTANCE_DEFAULT)
47 | channel.description = context.getString(R.string.notification_protection_description)
48 |
49 | notificationManager.createNotificationChannel(channel)
50 | }
51 |
52 | notificationManager.notify((((Date().time / 1000L) % Integer.MAX_VALUE).toInt()), notification.build())
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/app/src/main/java/nm/security/namooprotector/fragment/HomeFragment.kt:
--------------------------------------------------------------------------------
1 | package nm.security.namooprotector.fragment
2 |
3 | import android.os.Bundle
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import androidx.fragment.app.Fragment
8 | import de.psdev.licensesdialog.LicensesDialog
9 | import kotlinx.android.synthetic.main.fragment_home.*
10 | import nm.security.namooprotector.R
11 | import nm.security.namooprotector.activity.AboutActivity
12 | import nm.security.namooprotector.activity.SupportActivity
13 | import nm.security.namooprotector.util.ActivityUtil
14 | import nm.security.namooprotector.util.CheckUtil
15 | import nm.security.namooprotector.util.ResourceUtil
16 | import nm.security.namooprotector.util.ServiceUtil
17 |
18 | class HomeFragment : Fragment()
19 | {
20 | //라이프사이클
21 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View?
22 | {
23 | return inflater.inflate(R.layout.fragment_home, container, false)
24 | }
25 | override fun onViewCreated(view: View, savedInstanceState: Bundle?)
26 | {
27 | initClick()
28 | }
29 | override fun onResume()
30 | {
31 | super.onResume()
32 |
33 | initState()
34 | }
35 |
36 | //설정
37 | private fun initClick()
38 | {
39 | //활성화
40 | home_activator_background.setOnClickListener {
41 | ServiceUtil.runService(!CheckUtil.isServiceRunning)
42 | initState()
43 | }
44 |
45 | //더보기
46 | home_more_info.setOnClickListener { ActivityUtil.startActivityWithAnimation(requireActivity(), AboutActivity::class.java) }
47 | home_more_support.setOnClickListener { ActivityUtil.startActivityWithAnimation(requireActivity(), SupportActivity::class.java) }
48 | }
49 | private fun initState()
50 | {
51 | //활성화
52 | if (CheckUtil.isServiceRunning)
53 | {
54 | home_activator_background.setCardBackgroundColor(resources.getColor(R.color.positive_color, null))
55 | home_activator_state_text.text = ResourceUtil.getString(R.string.alert_service_on)
56 | home_activator_state_image.setImageResource(R.drawable.vector_lock)
57 | }
58 | else
59 | {
60 | home_activator_background.setCardBackgroundColor(resources.getColor(R.color.negative_color, null))
61 | home_activator_state_text.text = ResourceUtil.getString(R.string.alert_service_off)
62 | home_activator_state_image.setImageResource(R.drawable.vector_unlock)
63 | }
64 | }
65 | }
--------------------------------------------------------------------------------
/app/src/main/java/nm/security/namooprotector/activity/PinActivity.kt:
--------------------------------------------------------------------------------
1 | package nm.security.namooprotector.activity
2 |
3 | import android.os.Bundle
4 | import androidx.appcompat.app.AppCompatActivity
5 | import android.text.Editable
6 | import android.text.TextUtils
7 | import android.text.TextWatcher
8 | import android.view.View
9 | import kotlinx.android.synthetic.main.activity_pin.*
10 | import nm.security.namooprotector.R
11 | import nm.security.namooprotector.util.ActivityUtil
12 | import nm.security.namooprotector.util.SettingsUtil
13 |
14 | class PinActivity: AppCompatActivity()
15 | {
16 | //라이프사이클
17 | override fun onCreate(savedInstanceState: Bundle?)
18 | {
19 | super.onCreate(savedInstanceState)
20 | setContentView(R.layout.activity_pin)
21 |
22 | ActivityUtil.initFlag(this, true)
23 | ActivityUtil.initPreviousTitle(this)
24 |
25 | initListener()
26 | }
27 |
28 | //설정
29 | private fun initListener()
30 | {
31 | pin_input_1.addTextChangedListener(object : TextWatcher
32 | {
33 | override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int)
34 | {
35 | checkValid()
36 | }
37 | override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int)
38 | {
39 |
40 | }
41 | override fun afterTextChanged(s: Editable)
42 | {
43 |
44 | }
45 | })
46 | pin_input_2.addTextChangedListener(object : TextWatcher
47 | {
48 | override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int)
49 | {
50 | checkValid()
51 | }
52 | override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int)
53 | {
54 |
55 | }
56 | override fun afterTextChanged(s: Editable)
57 | {
58 |
59 | }
60 | })
61 | }
62 |
63 | //클릭 이벤트
64 | fun ok(view: View)
65 | {
66 | SettingsUtil.lockType = "pin"
67 | SettingsUtil.pin = pin_input_1.text.toString()
68 |
69 | finishAfterTransition()
70 | }
71 |
72 | //메소드
73 | private fun checkValid()
74 | {
75 | val inputPin1 = pin_input_1.text.toString()
76 | val inputPin2 = pin_input_2.text.toString()
77 |
78 | if (inputPin1.isValid() && inputPin1 == inputPin2)
79 | {
80 | pin_ok_button.isEnabled = true
81 | pin_ok_button.visibility = View.VISIBLE
82 | }
83 | else
84 | {
85 | pin_ok_button.isEnabled = false
86 | pin_ok_button.visibility = View.GONE
87 | }
88 | }
89 | private fun String.isValid(): Boolean
90 | {
91 | return this.length in 4..12 && TextUtils.isDigitsOnly(this)
92 | }
93 | }
--------------------------------------------------------------------------------
/app/src/main/java/nm/security/namooprotector/widget/FloatingButton.kt:
--------------------------------------------------------------------------------
1 | package nm.security.namooprotector.widget
2 |
3 | import android.content.Context
4 | import android.content.res.ColorStateList
5 | import android.content.res.TypedArray
6 | import android.graphics.Color
7 | import android.graphics.drawable.ColorDrawable
8 | import android.util.AttributeSet
9 | import android.util.TypedValue
10 | import android.view.LayoutInflater
11 | import android.view.View
12 | import android.widget.ImageView
13 | import android.widget.LinearLayout
14 | import android.widget.TextView
15 | import androidx.cardview.widget.CardView
16 | import androidx.core.content.ContextCompat
17 | import nm.security.namooprotector.R
18 | import nm.security.namooprotector.util.ConvertUtil
19 |
20 | class FloatingButton : CardView
21 | {
22 | private var textView: TextView? = null
23 |
24 | constructor(context: Context) : super(context)
25 | {
26 | initView()
27 | }
28 | constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
29 | {
30 | initView()
31 | getAttrs(attrs)
32 | }
33 | constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs)
34 | {
35 | initView()
36 | getAttrs(attrs, defStyle)
37 | }
38 |
39 | //설정
40 | private fun initView()
41 | {
42 | val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
43 | inflater.inflate(R.layout.view_floating_button, this)
44 |
45 | textView = findViewById(R.id.view_floating_button_text)
46 |
47 | //기본 속성
48 | val value = TypedValue()
49 | context.theme.resolveAttribute(android.R.attr.selectableItemBackground, value, true)
50 |
51 | setCardBackgroundColor(resources.getColor(R.color.highlight_color, null))
52 | foreground = ContextCompat.getDrawable(context, value.resourceId)
53 | cardElevation = resources.getDimension(R.dimen.focus_elevation)
54 | radius = ConvertUtil.dpToPx(25f)
55 | isClickable = true
56 | isFocusable = true
57 | }
58 |
59 | private fun getAttrs(attrs: AttributeSet)
60 | {
61 | val typedArray = context.obtainStyledAttributes(attrs, R.styleable.FloatingButton)
62 | updateView(typedArray)
63 | }
64 | private fun getAttrs(attrs: AttributeSet, defStyle: Int)
65 | {
66 | val typedArray = context.obtainStyledAttributes(attrs, R.styleable.FloatingButton, defStyle, 0)
67 | updateView(typedArray)
68 | }
69 | private fun updateView(typedArray: TypedArray)
70 | {
71 | textView!!.text = typedArray.getString(R.styleable.FloatingButton_floating_text)
72 | typedArray.recycle()
73 | }
74 |
75 | //메소드
76 | fun setText(text: String)
77 | {
78 | textView!!.text = text
79 | }
80 | fun setText(textResource: Int)
81 | {
82 | textView!!.setText(textResource)
83 | }
84 | }
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply plugin: 'kotlin-android'
3 | apply plugin: 'kotlin-android-extensions' //findViewById 대체
4 |
5 | def localProperties = new Properties()
6 | localProperties.load(new FileInputStream(rootProject.file("local.properties")))
7 |
8 | android {
9 | compileSdkVersion 29
10 | buildToolsVersion '29.0.2'
11 | defaultConfig {
12 | applicationId "nm.security.namooprotector"
13 | minSdkVersion 23
14 | targetSdkVersion 29
15 | versionCode 64
16 | versionName "4.0.1"
17 | vectorDrawables.useSupportLibrary = true
18 | }
19 | kotlinOptions {
20 | jvmTarget = JavaVersion.VERSION_1_8.toString()
21 | }
22 | buildTypes {
23 | release {
24 | minifyEnabled false
25 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
26 | }
27 | }
28 | defaultConfig {
29 | buildConfigField 'String', "AdmobProjectKey", localProperties['NP_ADMOB_PROJECT_KEY']
30 | buildConfigField 'String', "AdmobBannerKey", localProperties['NP_ADMOB_BANNER_KEY']
31 | buildConfigField 'String', "AdmobTestKey", localProperties['NP_ADMOB_TEST_KEY']
32 |
33 | resValue 'string', "admob_project_key", localProperties['NP_ADMOB_PROJECT_KEY']
34 | resValue 'string', "admob_banner_key", localProperties['NP_ADMOB_BANNER_KEY']
35 | resValue 'string', "admob_test_key", localProperties['NP_ADMOB_TEST_KEY']
36 | }
37 | }
38 |
39 | repositories {
40 | mavenCentral()
41 | jcenter()
42 | }
43 |
44 | dependencies {
45 | //내부 라이브러리
46 | implementation fileTree(include: ['*.jar'], dir: 'libs')
47 |
48 | //코틀린
49 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
50 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.5"
51 |
52 | //머테리얼
53 | implementation 'com.google.android.material:material:1.3.0-alpha02'
54 |
55 | //구글 서포트
56 | implementation 'androidx.core:core-ktx:1.3.1'
57 | implementation 'androidx.appcompat:appcompat:1.3.0-alpha02'
58 | implementation 'androidx.legacy:legacy-support-v4:1.0.0'
59 | implementation 'androidx.constraintlayout:constraintlayout:2.0.0'
60 | implementation 'androidx.recyclerview:recyclerview:1.2.0-alpha05'
61 | implementation 'androidx.cardview:cardview:1.0.0'
62 |
63 | //결제
64 | implementation 'com.google.android.gms:play-services-ads:19.3.0'
65 | implementation 'com.android.billingclient:billing:3.0.0'
66 | implementation 'com.android.billingclient:billing-ktx:3.0.0'
67 |
68 | //외부 라이브러리
69 | implementation 'gun0912.ted:tedpermission:2.2.2'
70 | implementation 'com.github.skydoves:colorpickerview:2.1.7'
71 | implementation 'co.infinum:goldfinger:2.0.1'
72 | implementation 'com.andrognito.patternlockview:patternlockview:1.0.0'
73 | implementation 'de.psdev.licensesdialog:licensesdialog:1.8.3'
74 | }
--------------------------------------------------------------------------------
/app/src/main/java/nm/security/namooprotector/adapter/AppsAdapter.kt:
--------------------------------------------------------------------------------
1 | package nm.security.namooprotector.adapter
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.Filter
8 | import android.widget.Filterable
9 | import android.widget.ImageView
10 | import android.widget.TextView
11 | import androidx.recyclerview.widget.RecyclerView
12 | import nm.security.namooprotector.R
13 | import nm.security.namooprotector.bundle.AppBundle
14 |
15 | class AppsAdapter(private val context: Context, private val list: ArrayList, val itemClick: (AppBundle) -> Unit) : RecyclerView.Adapter(), Filterable
16 | {
17 | var filteredList = list
18 |
19 | //라이프 사이클
20 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder
21 | {
22 | return Holder(LayoutInflater.from(context).inflate(R.layout.view_app_bundle, parent, false))
23 | }
24 | override fun onBindViewHolder(holder: Holder, position: Int)
25 | {
26 | holder.bind(filteredList[position])
27 | }
28 |
29 | //메소드
30 | override fun getItemCount(): Int
31 | {
32 | return filteredList.size
33 | }
34 | override fun getFilter(): Filter
35 | {
36 | return object : Filter()
37 | {
38 | var searchingList = ArrayList()
39 |
40 | override fun performFiltering(keyword: CharSequence): FilterResults
41 | {
42 | if (keyword.toString().isEmpty()) searchingList = list
43 |
44 | else
45 | {
46 | for (appBundle in list)
47 | {
48 | if (appBundle.label.toLowerCase().contains(keyword.toString().toLowerCase()))
49 | searchingList.add(appBundle)
50 | }
51 | }
52 |
53 | val filterResults = FilterResults()
54 | filterResults.values = searchingList
55 |
56 | return filterResults
57 | }
58 | override fun publishResults(keyword: CharSequence, results: FilterResults)
59 | {
60 | filteredList = results.values as ArrayList
61 | notifyDataSetChanged()
62 | }
63 | }
64 | }
65 |
66 | //클래스
67 | inner class Holder(itemView: View) : RecyclerView.ViewHolder(itemView)
68 | {
69 | private val icon = itemView.findViewById(R.id.view_app_bundle_icon)
70 | private val label = itemView.findViewById(R.id.view_app_bundle_label)
71 | private val packageName = itemView.findViewById(R.id.view_app_bundle_package_name)
72 | private val state = itemView.findViewById(R.id.view_app_bundle_state)
73 |
74 | fun bind (bundle: AppBundle)
75 | {
76 | icon.setImageDrawable(bundle.icon)
77 | label.text = bundle.label
78 | packageName.text = bundle.packageName
79 | state.visibility = if(bundle.state) View.VISIBLE else View.INVISIBLE
80 |
81 | itemView.setOnClickListener { itemClick(bundle) }
82 | }
83 | }
84 | }
--------------------------------------------------------------------------------
/app/src/main/java/nm/security/namooprotector/util/SettingsUtil.kt:
--------------------------------------------------------------------------------
1 | package nm.security.namooprotector.util
2 |
3 | import android.text.TextUtils
4 |
5 | object SettingsUtil
6 | {
7 | //잠금 방식
8 | var lockType: String?
9 | set(value) = DataUtil.put("type", DataUtil.LOCK, value!!)
10 | get() = DataUtil.getString("type", DataUtil.LOCK).validLockType() //신뢰 가능
11 |
12 | var pin: String?
13 | set(value) = DataUtil.put("pin", DataUtil.LOCK, value!!)
14 | get() = DataUtil.getString("pin", DataUtil.LOCK).validPin() //신뢰 가능
15 |
16 | var pattern: String?
17 | set(value) = DataUtil.put("pattern", DataUtil.LOCK, value!!)
18 | get() = DataUtil.getString("pattern", DataUtil.LOCK).validPattern() //신뢰 가능
19 |
20 | var fingerprint: Boolean
21 | set(value) = DataUtil.put("fingerprint", DataUtil.LOCK, value)
22 | get() = DataUtil.getBoolean("fingerprint", DataUtil.LOCK)
23 |
24 | //PIN 추가 설정
25 | var clickHaptic: Boolean
26 | set(value) = DataUtil.put("clickHaptic", DataUtil.SETTING, value)
27 | get() = DataUtil.getBoolean("clickHaptic", DataUtil.SETTING)
28 |
29 | var hideClick: Boolean
30 | set(value) = DataUtil.put("hideClick", DataUtil.SETTING, value)
31 | get() = DataUtil.getBoolean("hideClick", DataUtil.SETTING)
32 |
33 | var quickUnlock: Boolean
34 | set(value) = DataUtil.put("quickUnlock", DataUtil.SETTING, value)
35 | get() = DataUtil.getBoolean("quickUnlock", DataUtil.SETTING)
36 |
37 | var rearrangeKey: Boolean
38 | set(value) = DataUtil.put("rearrangeKey", DataUtil.SETTING, value)
39 | get() = DataUtil.getBoolean("rearrangeKey", DataUtil.SETTING)
40 |
41 | var lightKey: Boolean
42 | set(value) = DataUtil.put("lightKey", DataUtil.SETTING, value)
43 | get() = DataUtil.getBoolean("lightKey", DataUtil.SETTING)
44 |
45 | //패턴 추가 설정
46 | var drawHaptic: Boolean
47 | set(value) = DataUtil.put("drawHaptic", DataUtil.SETTING, value)
48 | get() = DataUtil.getBoolean("drawHaptic", DataUtil.SETTING)
49 |
50 | var hideDraw: Boolean
51 | set(value) = DataUtil.put("hideDraw", DataUtil.SETTING, value)
52 | get() = DataUtil.getBoolean("hideDraw", DataUtil.SETTING)
53 |
54 | var lightDot: Boolean
55 | set(value) = DataUtil.put("lightDot", DataUtil.SETTING, value)
56 | get() = DataUtil.getBoolean("lightDot", DataUtil.SETTING)
57 |
58 | //보안
59 | var darkLockscreen: Boolean
60 | set(value) = DataUtil.put("darkLockscreen", DataUtil.SETTING, value)
61 | get() = DataUtil.getBoolean("darkLockscreen", DataUtil.SETTING)
62 |
63 | var watchFail: Boolean
64 | set(value) = DataUtil.put("watchFail", DataUtil.SETTING, value)
65 | get() = DataUtil.getBoolean("watchFail", DataUtil.SETTING)
66 |
67 | var cover: Boolean
68 | set(value) = DataUtil.put("cover", DataUtil.SETTING, value)
69 | get() = DataUtil.getBoolean("cover", DataUtil.SETTING)
70 |
71 | //편의 기능
72 | var protectionNotification: Boolean
73 | set(value) = DataUtil.put("protectionNotification", DataUtil.SETTING, value)
74 | get() = DataUtil.getBoolean("protectionNotification", DataUtil.SETTING)
75 |
76 | var lockDelay: Int
77 | set(value) = DataUtil.put("lockDelay", DataUtil.SETTING, value)
78 | get() = DataUtil.getInt("lockDelay", DataUtil.SETTING).validLockDelay()
79 |
80 |
81 | //내부 메소드
82 | private fun String.validLockType(): String?
83 | {
84 | return if ((this == "pin" && pin != null) || (this == "pattern" && pattern != null)) this else null
85 | }
86 | private fun String.validPin(): String?
87 | {
88 | return if (this.length in 4..12 && TextUtils.isDigitsOnly(this)) this else null
89 | }
90 | private fun String.validPattern(): String?
91 | {
92 | return if (this.length in 2..9 && TextUtils.isDigitsOnly(this)) this else null
93 | }
94 | private fun Int.validLockDelay(): Int
95 | {
96 | return if (this in 1..60) this else 0
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_theme.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
19 |
20 |
22 |
23 |
24 |
25 |
40 |
41 |
55 |
56 |
64 |
65 |
75 |
76 |
86 |
87 |
97 |
98 |
99 |
100 |
101 |
102 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_apps.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
15 |
16 |
23 |
24 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
47 |
48 |
52 |
53 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
80 |
81 |
82 |
83 |
93 |
94 |
98 |
99 |
106 |
107 |
108 |
109 |
--------------------------------------------------------------------------------
/app/src/main/java/nm/security/namooprotector/fragment/WizardFragment.kt:
--------------------------------------------------------------------------------
1 | package nm.security.namooprotector.fragment
2 |
3 | import android.app.AlertDialog
4 | import android.content.ActivityNotFoundException
5 | import android.content.Intent
6 | import android.net.Uri
7 | import android.os.Bundle
8 | import android.provider.Settings
9 | import android.view.LayoutInflater
10 | import android.view.View
11 | import android.view.ViewGroup
12 | import android.widget.Toast
13 | import androidx.fragment.app.Fragment
14 | import kotlinx.android.synthetic.main.fragment_wizard.*
15 | import nm.security.namooprotector.R
16 | import nm.security.namooprotector.activity.MainActivity
17 | import nm.security.namooprotector.activity.PatternActivity
18 | import nm.security.namooprotector.activity.PinActivity
19 | import nm.security.namooprotector.util.ActivityUtil
20 | import nm.security.namooprotector.util.CheckUtil
21 | import nm.security.namooprotector.util.ResourceUtil
22 | import nm.security.namooprotector.util.ServiceUtil
23 |
24 | class WizardFragment : Fragment()
25 | {
26 | //라이프사이클
27 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View?
28 | {
29 | return inflater.inflate(R.layout.fragment_wizard, container, false)
30 | }
31 | override fun onViewCreated(view: View, savedInstanceState: Bundle?)
32 | {
33 | initClick()
34 | }
35 | override fun onResume()
36 | {
37 | super.onResume()
38 |
39 | initState()
40 | }
41 |
42 | //클릭 이벤트
43 | private fun initClick()
44 | {
45 | wizard_essential_password_button.setOnClickListener {
46 | with(AlertDialog.Builder(context))
47 | {
48 | setTitle(ResourceUtil.getString(R.string.alert_select_password_type))
49 | setItems(arrayOf(ResourceUtil.getString(R.string.name_pin), ResourceUtil.getString(R.string.name_pattern))){ _, index: Int ->
50 | when (index)
51 | {
52 | 0 -> ActivityUtil.startActivityWithAnimation(requireActivity(), PinActivity::class.java)
53 | 1 -> ActivityUtil.startActivityWithAnimation(requireActivity(), PatternActivity::class.java)
54 | }
55 | }
56 | show()
57 | }
58 | }
59 | wizard_essential_usage_stats_permission_button.setOnClickListener {
60 | try
61 | {
62 | startActivity(Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS, Uri.parse("package:" + requireActivity().packageName)))
63 | }
64 | catch (e: ActivityNotFoundException)
65 | {
66 | Toast.makeText(context, ResourceUtil.getString(R.string.alert_usage_stats_permission), Toast.LENGTH_LONG).show()
67 | startActivity(Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS))
68 | }
69 | }
70 | wizard_essential_overlay_permission_button.setOnClickListener {
71 | try
72 | {
73 | startActivity(Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + requireActivity().packageName)))
74 | }
75 | catch (e: ActivityNotFoundException)
76 | {
77 | Toast.makeText(context, ResourceUtil.getString(R.string.alert_overlay_permission), Toast.LENGTH_LONG).show()
78 | startActivity(Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION))
79 | }
80 | }
81 |
82 | wizard_start_button.setOnClickListener {
83 | ServiceUtil.runService(true)
84 | (activity as MainActivity).home(it)
85 | }
86 | }
87 |
88 | //메소드
89 | private fun initState()
90 | {
91 | wizard_essential_password_button.isChecked = CheckUtil.isPasswordSet
92 | wizard_essential_usage_stats_permission_button.isChecked = CheckUtil.isUsageStatsPermissionGranted
93 | wizard_essential_overlay_permission_button.isChecked = CheckUtil.isOverlayPermissionGranted
94 |
95 | if (CheckUtil.isNPValid)
96 | {
97 | wizard_start_button.isEnabled = true
98 | wizard_start_button.visibility = View.VISIBLE
99 | }
100 | else
101 | {
102 | wizard_start_button.isEnabled = false
103 | wizard_start_button.visibility = View.GONE
104 | }
105 | }
106 | }
--------------------------------------------------------------------------------
/app/src/main/java/nm/security/namooprotector/activity/ZBackupActivity.kt:
--------------------------------------------------------------------------------
1 | package nm.security.namooprotector.activity
2 |
3 | import android.content.Context
4 | import android.os.Bundle
5 | import android.text.Editable
6 | import android.text.TextWatcher
7 | import android.view.View
8 | import android.widget.Toast
9 | import nm.security.namooprotector.util.*
10 | import androidx.appcompat.app.AppCompatActivity
11 | import nm.security.namooprotector.R
12 | import kotlinx.android.synthetic.main.z_activity_backup.*
13 | import kotlinx.android.synthetic.main.z_activity_backup.backup_loading_layout
14 | import kotlinx.coroutines.*
15 | import kotlinx.coroutines.Dispatchers.Default
16 | import kotlinx.coroutines.Dispatchers.Main
17 | import java.io.File
18 |
19 | class ZBackupActivity: AppCompatActivity()
20 | {
21 | //라이프사이클
22 | override fun onCreate(savedInstanceState: Bundle?)
23 | {
24 | super.onCreate(savedInstanceState)
25 | setContentView(R.layout.z_activity_backup)
26 |
27 | ActivityUtil.initFlag(this, true)
28 | ActivityUtil.initPreviousTitle(this)
29 |
30 | initListener()
31 | }
32 |
33 | //설정
34 | private fun initListener()
35 | {
36 | backup_label_input.addTextChangedListener(object : TextWatcher
37 | {
38 | override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int)
39 | {
40 | checkValid()
41 | }
42 | override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int)
43 | {
44 |
45 | }
46 | override fun afterTextChanged(s: Editable)
47 | {
48 |
49 | }
50 | })
51 | }
52 |
53 | //클릭 이벤트
54 | fun rangeApps(view: View)
55 | {
56 | backup_range_apps.isChecked = !backup_range_apps.isChecked
57 | checkValid()
58 | }
59 | fun rangeSettings(view: View)
60 | {
61 | backup_range_settings.isChecked = !backup_range_settings.isChecked
62 | checkValid()
63 | }
64 | fun ok(view: View)
65 | {
66 | Toast.makeText(this, "제한된 기능", Toast.LENGTH_SHORT).show()
67 | return
68 |
69 | //초기 작업
70 | backup_ok_button.isEnabled = false
71 | backup_ok_button.visibility = View.GONE
72 | backup_loading_layout.visibility = View.VISIBLE
73 |
74 | //백업
75 | CoroutineScope(Default).launch {
76 | var content = "namupeurotekteo/"
77 |
78 | //데이터 수집 (key:value:type:location)
79 | if (backup_range_apps.isChecked)
80 | {
81 | getSharedPreferences(DataUtil.APPS, Context.MODE_PRIVATE).all.forEach { (key, value) ->
82 | content += "$key:$value:Boolean:${DataUtil.APPS}/"
83 | }
84 | }
85 | if (backup_range_settings.isChecked)
86 | {
87 | getSharedPreferences(DataUtil.SETTING, Context.MODE_PRIVATE).all.forEach { (key, value) ->
88 | content += "$key:$value:Boolean:${DataUtil.SETTING}/"
89 | }
90 | }
91 |
92 | //저장
93 | val file = File(filesDir, "${backup_label_input.text}.npb")
94 |
95 | //동일 이름 방지
96 | if (file.exists())
97 | {
98 | withContext(Main) {
99 | Toast.makeText(this@ZBackupActivity, "같은 이름의 백업 파일이 존재합니다. 이름을 다시 입력해주세요.", Toast.LENGTH_SHORT).show()
100 |
101 | backup_loading_layout.visibility = View.GONE
102 | }
103 |
104 | cancel()
105 | }
106 |
107 | file.writeText(content)
108 |
109 | withContext(Main) {
110 | Toast.makeText(this@ZBackupActivity, "백업에 성공했습니다. ${filesDir.absolutePath}", Toast.LENGTH_SHORT).show()
111 | finishAfterTransition()
112 | }
113 | }
114 | }
115 |
116 | //메소드
117 | private fun checkValid()
118 | {
119 | if (backup_label_input.text.toString().isNotEmpty() && (backup_range_apps.isChecked || backup_range_settings.isChecked))
120 | {
121 | backup_ok_button.isEnabled = true
122 | backup_ok_button.visibility = View.VISIBLE
123 | }
124 | else
125 | {
126 | backup_ok_button.isEnabled = false
127 | backup_ok_button.visibility = View.GONE
128 | }
129 | }
130 | }
--------------------------------------------------------------------------------
/app/src/main/java/nm/security/namooprotector/activity/PatternActivity.kt:
--------------------------------------------------------------------------------
1 | package nm.security.namooprotector.activity
2 |
3 | import android.os.Bundle
4 | import android.text.TextUtils
5 | import androidx.appcompat.app.AppCompatActivity
6 | import android.view.View
7 | import android.widget.Toast
8 | import com.andrognito.patternlockview.PatternLockView
9 | import com.andrognito.patternlockview.listener.PatternLockViewListener
10 | import com.andrognito.patternlockview.utils.PatternLockUtils
11 | import kotlinx.android.synthetic.main.activity_pattern.*
12 | import nm.security.namooprotector.R
13 | import nm.security.namooprotector.util.*
14 |
15 | class PatternActivity: AppCompatActivity()
16 | {
17 | var stage = 1
18 | var savedPattern = ""
19 |
20 | //라이프사이클
21 | override fun onCreate(savedInstanceState: Bundle?)
22 | {
23 | super.onCreate(savedInstanceState)
24 | setContentView(R.layout.activity_pattern)
25 |
26 | ActivityUtil.initFlag(this, true)
27 | ActivityUtil.initPreviousTitle(this)
28 |
29 | initPatternView()
30 | }
31 |
32 | //설정
33 | private fun initPatternView()
34 | {
35 | pattern_pattern_view.isTactileFeedbackEnabled = false
36 | pattern_pattern_view.isInStealthMode = false
37 | pattern_pattern_view.addPatternLockListener(object: PatternLockViewListener {
38 | override fun onStarted()
39 | {
40 | pattern_ok_button.visibility = View.GONE
41 |
42 | pattern_state_indicator.text = when (stage)
43 | {
44 | 1 -> ResourceUtil.getString(R.string.alert_draw_new_pattern)
45 | 2 -> ResourceUtil.getString(R.string.alert_confirm_new_pattern)
46 | else -> ResourceUtil.getString(R.string.error_invalid_access)
47 | }
48 | }
49 | override fun onCleared()
50 | {
51 |
52 | }
53 | override fun onProgress(progressPattern: MutableList?)
54 | {
55 |
56 | }
57 | override fun onComplete(pattern: MutableList?)
58 | {
59 | val result = ConvertUtil.patternToString(pattern_pattern_view, pattern)
60 |
61 | when (stage)
62 | {
63 | 1 ->
64 | {
65 | if (result.isValid())
66 | {
67 | savedPattern = result
68 |
69 | pattern_ok_button.setText(ResourceUtil.getString(R.string.common_next))
70 | pattern_ok_button.visibility = View.VISIBLE
71 | }
72 | else pattern_state_indicator.text = ResourceUtil.getString(R.string.error_pattern_invalid)
73 | }
74 | 2 ->
75 | {
76 | if (result == savedPattern)
77 | {
78 | pattern_ok_button.setText(ResourceUtil.getString(R.string.common_ok))
79 | pattern_ok_button.visibility = View.VISIBLE
80 | }
81 | else
82 | {
83 | stage = 1
84 | savedPattern = ""
85 |
86 | pattern_state_indicator.text = ResourceUtil.getString(R.string.error_pattern_incorrect)
87 | }
88 | }
89 | }
90 | }
91 |
92 | })
93 | }
94 |
95 | //클릭 이벤트
96 | fun ok(view: View)
97 | {
98 | when (stage)
99 | {
100 | 1 ->
101 | {
102 | stage = 2
103 |
104 | pattern_state_indicator.text = ResourceUtil.getString(R.string.alert_confirm_new_pattern)
105 | pattern_pattern_view.clearPattern()
106 | pattern_ok_button.visibility = View.GONE
107 | }
108 | 2 ->
109 | {
110 | SettingsUtil.lockType = "pattern"
111 | SettingsUtil.pattern = savedPattern
112 |
113 | finishAfterTransition()
114 | }
115 | }
116 | }
117 |
118 | //메소드
119 | private fun String.isValid(): Boolean
120 | {
121 | return this.length in 2..9 && TextUtils.isDigitsOnly(this)
122 | }
123 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # NamooProtector (English Description)
2 | **A college student's harsh development story**
3 |
4 | Are you worried if other people see your gallery stealthily?
5 | Are you worried if other people see your private messages?
6 |
7 | Keep your apps safe with Namoo Protector!
8 |
9 | ## What is 'Namoo Protector'?
10 | Namoo Protector is an application which protects your apps by locking them with PIN, pattern, or your fingerprint you set.
11 |
12 | ## Main features of NP
13 | - App lock using PIN, pattern, and fingerprint authentication
14 | - Lock Settings for your taste
15 | (Click Haptic, Rearrange Key and more..)
16 | - Lock Settings for security
17 | (Watch failure, Cover and more..)
18 | - Custom Lock Screen with a theme editor
19 |
20 | ## Virtues of NP
21 | - Since it contains only essential and useful features, it is fast and light!
22 | - It is designed user-friendly and you can control the settings intuitively!
23 | - Since it is open-sourced and requires minimum permissions, you don't need to be worried!
24 |
25 | ## Q&A
26 | Q. If it is deleted by other users, is it worth..?
27 | A. You can activate 'Prevent NP Uninstalled' in app settings and it will not be deleted!
28 |
29 | Q. Oh..no... I can't delete it..
30 | A. You might have set it as a device administrator so that NP can't be deleted. Please uncheck 'Prevent NP uninstalled' in app settings. Now you can delete it! (But why...)
31 |
32 | Q. It doesn't protect my apps :(
33 | A. If you have already added apps to lock and activated NP, but it doesn't work, please report the bug with a detail explanation via email or review.
34 |
35 | Q. Oh.. English translation is the WORST.
36 | A. Sorry, but I'm not living in an English-speaking country. If you find any inappropriate English expressions, please let me know :)
37 |
38 | Q. I think it makes a serious battery drain..
39 | A. Sorry, but App lock has no choice to have much battery because it should search apps to lock frequently. But I'm always trying to improve battery life :)
40 |
41 | ## Required permission
42 | - App usage stats (NEEDED) : It is used to search the foreground app.
43 | - Overlay (NEEDED) : It is used to show a lock screen over a locked app.
44 | - Device administrator (Optional) : It is used when you activate an option "Prevent NP uninstalled"
45 |
46 | ## License
47 | The following libraries are used in this project.
48 | - [TedPermission](https://github.com/ParkSangGwon/TedPermission)
49 | - [ColorPickerView](https://github.com/skydoves/ColorPickerView)
50 | - [GoldFinger](https://github.com/infinum/Android-Goldfinger)
51 | - [PatternLockView](https://github.com/aritraroy/PatternLockView)
52 | - [LicenseDialog](https://psdev.de/LicensesDialog/)
53 |
54 | ## Reporting bug and suggesting new ideas
55 | - Github : Create issue
56 | - Blog : http://namooplus.tistory.com/6 (Korean)
57 |
58 | ## Download
59 | [Play Store](https://play.google.com/store/apps/details?id=nm.security.namooprotector)
60 |
61 |
62 | # 나무프로텍터 (한국어 설명)
63 | **1인 대학생 개발자의 힘든 앱 개발기**
64 |
65 | 다른 사람이 자신의 갤러리를 맘대로 볼까봐 걱정되나요?
66 | 다른 사람이 사생활이 담긴 메시지와 메신저를 볼까봐 걱정되나요?
67 |
68 | 나무 프로텍터를 통해 앱을 안전하게 보호하세요!
69 |
70 | ## 나무 프로텍터란?
71 | 나무 프로텍터는 설정한 PIN, 패턴 혹은 지문인식으로 앱을 잠궈 보호하는 어플 잠금 서비스입니다.
72 |
73 | ## 나무 프로텍터 주요 기능
74 | - PIN, 패턴, 지문인식을 이용한 앱 잠금
75 | - 개인의 취향을 존중하는 잠금화면 설정
76 | (클릭 시 진동, 키 재배열 등)
77 | - 보안성을 높이는 잠금화면 설정
78 | (틀림 감지, 커버 등)
79 | - 테마 에디터를 이용해 직접 꾸미는 잠금화면
80 |
81 | ## Q&A
82 | Q. 나무프로텍터 앱이 지워지면 무용지물 아닌가요?
83 | A. 앱 설정 중 '나무프로텍터 삭제 방지'를 활성화하면 디바이스 정책에 따라 앱이 삭제되지 않습니다!
84 |
85 | Q. 앱이 지워지지 않아요 ㅠㅠ
86 | A. 앱 설정 중 '나무프로텍터 삭제 방지'가 허용되어서 그렇습니다! 삭제 방지를 해제하시면 정상적으로 삭제가 가능합니다.
87 |
88 | Q. 앱이 안 잠겨져요 :(
89 | A. 만약 앱 잠금 목록에 잠금 앱을 추가하고 나무프로텍터 서비스를 활성화한 후에도 앱이 잠기지 않는다면 리뷰에 그 상황을 자세히 설명해주세요!
90 |
91 | Q. 오타!! 마춤뻡이쐉해요
92 | A. 개발 중 오타가 발생할 수 있습니다...만약 오타나 맞춤법 이상을 찾았다면 꼭 알려주세요! ㅎㅎ
93 |
94 | Q. 이거 때문에 배터리가 빨리 다는 거 같은데 어떡해요 ㅠㅠ
95 | A. 수시로 최상단 앱을 감지해야 되는 프로텍터 특성 상 평상시보단 배터리가 많이 소모될 수 밖에 없습니다. 또한 부족한 배터리 효율은 업데이트로 계속 개선되고 있으니 이 부분은 양해해주세요 :)
96 |
97 | ## 권한 설명
98 | - 앱 사용 검색 (필수) : 최상단 앱을 찾기 위해 필요한 권한입니다.
99 | - 다른 앱 위에 띄우기 (필수) : 잠금 앱 위에 잠금화면을 띄우기 위해 필요한 권한입니다.
100 | - 디바이스 관리자 (선택) : 나무프로텍터 삭제 방지 옵션을 활성화 할 경우 필요한 권한입니다.
101 |
102 | ## 라이센스
103 | 아래의 라이브러리가 이 프로젝트에 사용되었습니다.
104 | - [TedPermission](https://github.com/ParkSangGwon/TedPermission)
105 | - [ColorPickerView](https://github.com/skydoves/ColorPickerView)
106 | - [GoldFinger](https://github.com/infinum/Android-Goldfinger)
107 | - [PatternLockView](https://github.com/aritraroy/PatternLockView)
108 | - [LicenseDialog](https://psdev.de/LicensesDialog/)
109 |
110 | ## 버그 신고 및 건의
111 | - 깃허브 : Create issue
112 | - 블로그 : http://namooplus.tistory.com/6
113 |
114 | ## 다운로드
115 | [플레이스토어](https://play.google.com/store/apps/details?id=nm.security.namooprotector)
116 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_wizard.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
12 |
15 |
16 |
24 |
25 |
26 |
27 |
31 |
32 |
39 |
40 |
51 |
52 |
60 |
61 |
70 |
71 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
100 |
101 |
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.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 |
31 |
32 | xmlns:android
33 |
34 | ^$
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | xmlns:.*
44 |
45 | ^$
46 |
47 |
48 | BY_NAME
49 |
50 |
51 |
52 |
53 |
54 |
55 | .*:id
56 |
57 | http://schemas.android.com/apk/res/android
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | .*:name
67 |
68 | http://schemas.android.com/apk/res/android
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 | name
78 |
79 | ^$
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 | style
89 |
90 | ^$
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 | .*
100 |
101 | ^$
102 |
103 |
104 | BY_NAME
105 |
106 |
107 |
108 |
109 |
110 |
111 | .*
112 |
113 | http://schemas.android.com/apk/res/android
114 |
115 |
116 | ANDROID_ATTRIBUTE_ORDER
117 |
118 |
119 |
120 |
121 |
122 |
123 | .*
124 |
125 | .*
126 |
127 |
128 | BY_NAME
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
--------------------------------------------------------------------------------
/app/src/main/java/nm/security/namooprotector/activity/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package nm.security.namooprotector.activity
2 |
3 | import android.content.Intent
4 | import android.os.Bundle
5 | import android.view.View
6 | import androidx.appcompat.app.AppCompatActivity
7 | import androidx.fragment.app.Fragment
8 | import com.google.android.gms.ads.*
9 | import kotlinx.android.synthetic.main.activity_main.*
10 | import nm.security.namooprotector.R
11 | import nm.security.namooprotector.fragment.*
12 | import nm.security.namooprotector.service.ProtectorServiceHelper
13 | import nm.security.namooprotector.util.ActivityUtil
14 | import nm.security.namooprotector.util.AnimationUtil
15 | import nm.security.namooprotector.util.AnimationUtil.alpha
16 | import nm.security.namooprotector.util.AnimationUtil.scale
17 | import nm.security.namooprotector.util.CheckUtil
18 | import nm.security.namooprotector.util.ResourceUtil
19 |
20 | class MainActivity: AppCompatActivity()
21 | {
22 | //라이프사이클
23 | override fun onCreate(savedInstanceState: Bundle?)
24 | {
25 | super.onCreate(savedInstanceState)
26 | setContentView(R.layout.activity_main)
27 |
28 | ActivityUtil.initFlag(this, true)
29 |
30 | initFragment()
31 | }
32 | override fun onResume()
33 | {
34 | super.onResume()
35 |
36 | initState()
37 | initAd()
38 |
39 | main_ad_view.resume()
40 | }
41 | override fun onPause()
42 | {
43 | main_ad_view.pause()
44 |
45 | super.onPause()
46 | }
47 | override fun onDestroy()
48 | {
49 | main_ad_view.adListener = null
50 | main_ad_view.destroy()
51 |
52 | ProtectorServiceHelper.clearTemporaryAuthorizedApp()
53 |
54 | super.onDestroy()
55 | }
56 |
57 | //설정
58 | private fun initFragment()
59 | {
60 | val title = ResourceUtil.getString(R.string.name_home)
61 |
62 | supportFragmentManager
63 | .beginTransaction()
64 | .add(R.id.main_content, HomeFragment(), title)
65 | .commit()
66 |
67 | changeTab(title, main_tab_home, main_tab_apps, main_tab_settings, main_tab_theme)
68 | }
69 | private fun initState()
70 | {
71 | if (!CheckUtil.isNPValid)
72 | {
73 | main_tab_container.visibility = View.GONE
74 |
75 | val title = ResourceUtil.getString(R.string.name_wizard)
76 |
77 | changeFragment(WizardFragment(), title)
78 | title_indicator.text = title
79 | }
80 |
81 | //잠금화면
82 | if (CheckUtil.isPasswordSet && !ProtectorServiceHelper.isAuthorized(packageName)) startActivity(Intent(this, LockScreen::class.java))
83 | }
84 | private fun initAd()
85 | {
86 | MobileAds.initialize(this) {}
87 |
88 | //광고 제거
89 | if (CheckUtil.isAdRemoved) main_ad_view.visibility = View.GONE
90 | //광고 표시
91 | else
92 | {
93 | //재로드 방지
94 | if (main_ad_view.visibility == View.VISIBLE) return
95 |
96 | main_ad_view.visibility = View.VISIBLE
97 | main_ad_view.adListener = object: AdListener()
98 | {
99 | override fun onAdFailedToLoad(errorCode: Int)
100 | {
101 | main_ad_view.visibility = View.GONE
102 | }
103 | }
104 | main_ad_view.loadAd(AdRequest.Builder().build())
105 | }
106 | }
107 |
108 | //클릭 이벤트
109 | fun home(view: View)
110 | {
111 | val title = ResourceUtil.getString(R.string.name_home)
112 |
113 | changeFragment(HomeFragment(), title)
114 | changeTab(title, main_tab_home, main_tab_apps, main_tab_settings, main_tab_theme)
115 | }
116 | fun apps(view: View)
117 | {
118 | val title = ResourceUtil.getString(R.string.name_apps)
119 |
120 | changeFragment(AppsFragment(), title)
121 | changeTab(title, main_tab_apps, main_tab_home, main_tab_settings, main_tab_theme)
122 | }
123 | fun settings(view: View)
124 | {
125 | val title = ResourceUtil.getString(R.string.name_settings)
126 |
127 | changeFragment(SettingsFragment(), title)
128 | changeTab(title, main_tab_settings, main_tab_home, main_tab_apps, main_tab_theme)
129 | }
130 | fun theme(view: View)
131 | {
132 | val title = ResourceUtil.getString(R.string.name_theme)
133 |
134 | changeFragment(ThemeFragment(), title)
135 | changeTab(title, main_tab_theme, main_tab_home, main_tab_apps, main_tab_settings)
136 | }
137 |
138 | //메소드
139 | private fun changeFragment(fragment: Fragment, tag: String)
140 | {
141 | //동일 탭 실행 방지
142 | if (supportFragmentManager.findFragmentById(R.id.main_content)!!.tag == tag) return
143 |
144 | supportFragmentManager
145 | .beginTransaction()
146 | .replace(R.id.main_content, fragment, tag)
147 | .commit()
148 | }
149 | private fun changeTab(title: String, selectedView: View, vararg unselectedViews: View)
150 | {
151 | //타이틀 변경
152 | title_indicator.text = title
153 |
154 | //탭 보이기
155 | main_tab_container.visibility = View.VISIBLE
156 |
157 | //탭 선택
158 | selectedView.alpha(1f, AnimationUtil.DEFAULT_DURATION).scale(1.1f, AnimationUtil.DEFAULT_DURATION)
159 |
160 | //탭 선택 해제
161 | unselectedViews.forEach { it.alpha(0.5f, AnimationUtil.DEFAULT_DURATION).scale(0.9f, AnimationUtil.DEFAULT_DURATION) }
162 | }
163 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/z_activity_restore.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
15 |
16 |
19 |
20 |
28 |
29 |
30 |
31 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
55 |
56 |
68 |
69 |
82 |
83 |
91 |
92 |
104 |
105 |
106 |
107 |
108 |
109 |
119 |
120 |
124 |
125 |
132 |
133 |
134 |
135 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
24 |
25 |
26 |
27 |
32 |
33 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
52 |
53 |
58 |
59 |
64 |
65 |
70 |
71 |
76 |
77 |
80 |
81 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
94 |
95 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
125 |
126 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
154 |
155 |
156 |
157 |
--------------------------------------------------------------------------------
/app/src/main/java/nm/security/namooprotector/util/AnimationUtil.kt:
--------------------------------------------------------------------------------
1 | package nm.security.namooprotector.util
2 |
3 | import android.animation.Animator
4 | import android.animation.ArgbEvaluator
5 | import android.animation.ValueAnimator
6 | import android.graphics.Rect
7 | import android.graphics.drawable.ColorDrawable
8 | import androidx.interpolator.view.animation.FastOutSlowInInterpolator
9 | import android.view.View
10 | import android.view.ViewAnimationUtils
11 | import kotlin.math.max
12 |
13 | object AnimationUtil
14 | {
15 | const val DEFAULT_DURATION = 450
16 | const val SHORT_DURATION = 300
17 | const val LONG_DURATION = 600
18 |
19 | fun View.alpha(value: Float, duration: Int, delay: Int = 0): View
20 | {
21 | if (!this.isShown)
22 | this.visibility = View.VISIBLE
23 |
24 | this.animate()
25 | .alpha(value)
26 | .setInterpolator(FastOutSlowInInterpolator())
27 | .setStartDelay(delay.toLong())
28 | .setDuration(duration.toLong())
29 | .withLayer()
30 | .withEndAction{
31 | this.alpha = value
32 |
33 | if (value == 0f)
34 | this.visibility = View.GONE
35 | }
36 |
37 | return this
38 | }
39 | fun View.scale(value: Float, duration: Int, delay: Int = 0): View
40 | {
41 | this.animate()
42 | .scaleX(value)
43 | .scaleY(value)
44 | .setInterpolator(FastOutSlowInInterpolator())
45 | .setStartDelay(delay.toLong())
46 | .setDuration(duration.toLong())
47 | .withLayer()
48 | .withEndAction {
49 | this.scaleX = value
50 | this.scaleY = value
51 | }
52 |
53 | return this
54 | }
55 | fun View.translateX(valueX: Float, duration: Int, delay: Int = 0): View
56 | {
57 | this.animate()
58 | .translationX(valueX)
59 | .setInterpolator(FastOutSlowInInterpolator())
60 | .setStartDelay(delay.toLong())
61 | .setDuration(duration.toLong())
62 | .withLayer()
63 | .withEndAction {
64 | this.translationX = valueX
65 | }
66 |
67 | return this
68 | }
69 | fun View.translateY(valueY: Float, duration: Int, delay: Int = 0): View
70 | {
71 | this.animate()
72 | .translationY(valueY)
73 | .setInterpolator(FastOutSlowInInterpolator())
74 | .setStartDelay(delay.toLong())
75 | .setDuration(duration.toLong())
76 | .withLayer()
77 | .withEndAction {
78 | this.translationY = valueY
79 | }
80 |
81 | return this
82 | }
83 | fun View.rotateX(valueX: Float, duration: Int, delay: Int = 0): View
84 | {
85 | this.animate()
86 | .rotationX(valueX)
87 | .setInterpolator(FastOutSlowInInterpolator())
88 | .setStartDelay(delay.toLong())
89 | .setDuration(duration.toLong())
90 | .withLayer()
91 | .withEndAction {
92 | this.rotationX = valueX
93 | }
94 |
95 | return this
96 | }
97 | fun View.rotateY(valueY: Float, duration: Int, delay: Int = 0): View
98 | {
99 | this.animate()
100 | .rotationY(valueY)
101 | .setInterpolator(FastOutSlowInInterpolator())
102 | .setStartDelay(delay.toLong())
103 | .setDuration(duration.toLong())
104 | .withLayer()
105 | .withEndAction {
106 | this.rotationY = valueY
107 | }
108 |
109 | return this
110 | }
111 | fun View.circularReveal(reveal: Boolean, duration: Int, delay: Int = 0): View
112 | {
113 | val rect = Rect()
114 | this.getDrawingRect(rect)
115 |
116 | val centerX = rect.centerX()
117 | val centerY = rect.centerY()
118 | val finalRadius = max(rect.width(), rect.height())
119 |
120 | val revealAnimator: Animator = if (reveal) ViewAnimationUtils.createCircularReveal(this, centerX, centerY, 0f, finalRadius.toFloat()) else ViewAnimationUtils.createCircularReveal(this, centerX, centerY, finalRadius.toFloat(), 0f)
121 | revealAnimator.interpolator = FastOutSlowInInterpolator()
122 | revealAnimator.duration = duration.toLong()
123 | revealAnimator.startDelay = delay.toLong()
124 |
125 | if (reveal)
126 | this.visibility = View.VISIBLE
127 |
128 | else
129 | {
130 | revealAnimator.addListener(object : Animator.AnimatorListener
131 | {
132 | override fun onAnimationStart(animator: Animator)
133 | {
134 |
135 | }
136 | override fun onAnimationEnd(animator: Animator)
137 | {
138 | this@circularReveal.visibility = View.GONE
139 | }
140 | override fun onAnimationCancel(animator: Animator)
141 | {
142 |
143 | }
144 | override fun onAnimationRepeat(animator: Animator)
145 | {
146 |
147 | }
148 | })
149 | }
150 |
151 | revealAnimator.start()
152 |
153 | return this
154 | }
155 | fun View.changeColor(value: Int, duration: Int, delay: Int = 0): View
156 | {
157 | val colorAnimation = ValueAnimator.ofObject(ArgbEvaluator(), (this.background as ColorDrawable).color, value)
158 |
159 | colorAnimation.duration = duration.toLong()
160 | colorAnimation.startDelay = delay.toLong()
161 | colorAnimation.addUpdateListener { animator -> this.setBackgroundColor(animator.animatedValue as Int) }
162 | colorAnimation.start()
163 |
164 | return this
165 | }
166 | }
--------------------------------------------------------------------------------
/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:name=$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/java/nm/security/namooprotector/service/ProtectorService.kt:
--------------------------------------------------------------------------------
1 | package nm.security.namooprotector.service
2 |
3 | import android.app.Service
4 | import android.content.Intent
5 | import android.os.IBinder
6 | import android.app.NotificationManager
7 | import android.app.NotificationChannel
8 | import android.os.Build
9 | import android.app.usage.UsageEvents
10 | import android.app.usage.UsageStatsManager
11 | import android.content.Context
12 | import androidx.core.app.NotificationCompat
13 | import androidx.core.content.ContextCompat
14 | import kotlinx.coroutines.*
15 | import nm.security.namooprotector.NamooProtector.Companion.context
16 | import nm.security.namooprotector.R
17 | import nm.security.namooprotector.activity.LockScreen
18 | import nm.security.namooprotector.util.CheckUtil
19 | import nm.security.namooprotector.util.DataUtil
20 | import nm.security.namooprotector.util.ResourceUtil
21 | import nm.security.namooprotector.util.SettingsUtil
22 |
23 | class ProtectorService : Service()
24 | {
25 | private val usageStatsManager by lazy { getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager }
26 |
27 | private var searchTask: Thread? = null
28 | private var search = false
29 |
30 | //라이프사이클
31 | override fun onBind(arg0: Intent): IBinder?
32 | {
33 | return null
34 | }
35 | override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int
36 | {
37 | initForeground(true)
38 | initProtector()
39 |
40 | return START_STICKY
41 | }
42 | override fun onDestroy()
43 | {
44 | CheckUtil.isServiceRunning = false
45 |
46 | initForeground(false)
47 |
48 | super.onDestroy()
49 | }
50 |
51 | //설정
52 | private fun initForeground(on: Boolean)
53 | {
54 | if (on)
55 | {
56 | //포그라운드 설정
57 | val notification = NotificationCompat.Builder(this, "namooprotector")
58 | .setSmallIcon(R.drawable.icon_np)
59 | .setContentTitle(ResourceUtil.getString(R.string.app_name))
60 | .setContentText(ResourceUtil.getString(R.string.alert_service_on))
61 | .setColor(ContextCompat.getColor(this, R.color.color_accent))
62 | .setOngoing(true)
63 | .setPriority(NotificationCompat.PRIORITY_MIN)
64 |
65 | if (Build.VERSION.SDK_INT >= 26)
66 | {
67 | val channel = NotificationChannel("namooprotector", ResourceUtil.getString(R.string.app_name), NotificationManager.IMPORTANCE_MIN)
68 | channel.description = ResourceUtil.getString(R.string.notification_service_description)
69 |
70 | (getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager).createNotificationChannel(channel)
71 | }
72 | startForeground(1, notification.build())
73 |
74 | //검색 시작
75 | search = true
76 | }
77 | else
78 | {
79 | //검색 종료
80 | search = false
81 |
82 | //포그라운드 해제
83 | stopForeground(true)
84 | }
85 | }
86 | private fun initProtector()
87 | {
88 | //중복 방지
89 | if (searchTask != null)
90 | return
91 |
92 | //검색
93 | var previousEventApp = ""
94 | var recentEventApp = ""
95 | var recentEventTime = 0L
96 | var tempRecentEventApp = ""
97 | var tempRecentEventTime = 0L
98 |
99 | searchTask = Thread(Runnable {
100 | while (search)
101 | {
102 | //현재 포그라운드 앱 검색
103 | val currentTime = System.currentTimeMillis()
104 | val usageEvents = usageStatsManager.queryEvents(currentTime - 5 * 1000, currentTime)
105 |
106 | val scannedEvent = UsageEvents.Event()
107 |
108 | while (usageEvents.hasNextEvent())
109 | {
110 | usageEvents.getNextEvent(scannedEvent)
111 |
112 | val scannedEventType = scannedEvent.eventType
113 | val scannedEventApp = scannedEvent.packageName
114 | val scannedEventTime = scannedEvent.timeStamp
115 |
116 | if (scannedEventType == UsageEvents.Event.ACTIVITY_RESUMED && scannedEventTime > recentEventTime)
117 | {
118 | tempRecentEventApp = scannedEventApp
119 | tempRecentEventTime = scannedEventTime
120 | }
121 | }
122 |
123 | if (tempRecentEventTime > recentEventTime)
124 | {
125 | //짧은 중복 인식 -> 앱 변화로 간주
126 | if (tempRecentEventApp == recentEventApp) previousEventApp = packageName
127 |
128 | recentEventApp = tempRecentEventApp
129 | recentEventTime = tempRecentEventTime
130 | }
131 |
132 | //앱 변화 감지
133 | if (recentEventApp != previousEventApp)
134 | {
135 | //잠금 해제 인증앱 업데이트
136 | ProtectorServiceHelper.updateAuthorizedApps()
137 |
138 | //잠금 딜레이
139 | if (ProtectorServiceHelper.isAuthorized(previousEventApp))
140 | {
141 | ProtectorServiceHelper.clearTemporaryAuthorizedApp()
142 | ProtectorServiceHelper.addAuthorizedApp(previousEventApp, SettingsUtil.lockDelay.toLong())
143 | }
144 |
145 | //잠금
146 | if (ProtectorServiceHelper.isAuthorized(recentEventApp))
147 | ProtectorServiceHelper.addTemporaryAuthorizedApp(recentEventApp)
148 |
149 | if (DataUtil.getBoolean(recentEventApp, DataUtil.APPS) && !ProtectorServiceHelper.isAuthorized(recentEventApp) && recentEventApp != packageName)
150 | lock(recentEventApp)
151 |
152 |
153 | previousEventApp = recentEventApp
154 | }
155 | }
156 | })
157 | searchTask!!.start()
158 | }
159 |
160 | //메소드
161 | private fun lock(packageName: String)
162 | {
163 | val lockScreen = Intent(context, LockScreen::class.java)
164 | lockScreen.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT)
165 | lockScreen.putExtra("packageName", packageName)
166 | context.startActivity(lockScreen)
167 | }
168 | }
169 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
17 |
18 |
19 |
20 |
24 |
25 |
26 |
27 |
35 |
36 |
48 |
49 |
61 |
62 |
63 |
64 |
65 |
66 |
76 |
77 |
89 |
90 |
103 |
104 |
117 |
118 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
146 |
147 |
--------------------------------------------------------------------------------
/app/src/main/java/nm/security/namooprotector/activity/SupportActivity.kt:
--------------------------------------------------------------------------------
1 | package nm.security.namooprotector.activity
2 |
3 | import android.os.Bundle
4 | import android.view.View
5 | import android.widget.Toast
6 | import nm.security.namooprotector.util.*
7 | import androidx.appcompat.app.AppCompatActivity
8 | import com.android.billingclient.api.*
9 | import kotlinx.android.synthetic.main.activity_support.*
10 | import kotlinx.coroutines.*
11 | import nm.security.namooprotector.R
12 |
13 | class SupportActivity: AppCompatActivity(), PurchasesUpdatedListener
14 | {
15 | private val billingClient by lazy { BillingClient.newBuilder(this).enablePendingPurchases().setListener(this).build() }
16 | private var skuDetailsList: List? = null
17 |
18 | private var job: Job? = null
19 |
20 | private val REMOVE_AD = "namooprotector_premium_ad_free_0107"
21 | private val COKE = "namooprotector_donate_coke"
22 | private val MEAL = "namooprotector_donate_meal"
23 | private val ECO_BAG = "namooprotector_donate_eco_bag"
24 | private val COLLECT_FOR_LAPTOP = "namooprotector_donate_collect_for_laptop"
25 |
26 | //라이프사이클
27 | override fun onCreate(savedInstanceState: Bundle?)
28 | {
29 | super.onCreate(savedInstanceState)
30 | setContentView(R.layout.activity_support)
31 |
32 | ActivityUtil.initFlag(this, true)
33 | ActivityUtil.initPreviousTitle(this)
34 |
35 | initClient()
36 | initState()
37 | }
38 | override fun onDestroy()
39 | {
40 | billingClient.endConnection()
41 |
42 | super.onDestroy()
43 |
44 | job?.cancel()
45 | }
46 |
47 | //설정
48 | private fun initClient()
49 | {
50 | billingClient.startConnection(object: BillingClientStateListener
51 | {
52 | override fun onBillingServiceDisconnected()
53 | {
54 | Toast.makeText(this@SupportActivity, getString(R.string.error_billing_get_item), Toast.LENGTH_LONG).show()
55 | }
56 | override fun onBillingSetupFinished(result: BillingResult)
57 | {
58 | if (result.responseCode == BillingClient.BillingResponseCode.OK) job = CoroutineScope(Dispatchers.Main).launch { loadSku() }
59 | else Toast.makeText(this@SupportActivity, getString(R.string.error_billing_get_item), Toast.LENGTH_LONG).show()
60 | }
61 | })
62 | }
63 | private suspend fun loadSku()
64 | {
65 | val preSkuDetails = SkuDetailsParams.newBuilder()
66 | preSkuDetails.setSkusList(listOf(REMOVE_AD, COKE, MEAL, ECO_BAG, COLLECT_FOR_LAPTOP)).setType(BillingClient.SkuType.INAPP)
67 |
68 | skuDetailsList = withContext(Dispatchers.IO) { billingClient.querySkuDetails(preSkuDetails.build()).skuDetailsList }
69 | skuDetailsList?.forEach {
70 | when (it.sku)
71 | {
72 | REMOVE_AD -> support_premium_remove_ad_button.setDescription(it.price)
73 | COKE -> support_donate_coke_button.setDescription(it.price)
74 | MEAL -> support_donate_meal_button.setDescription(it.price)
75 | ECO_BAG -> support_donate_eco_bag_button.setDescription(it.price)
76 | COLLECT_FOR_LAPTOP -> support_donate_collect_for_laptop_button.setDescription(it.price)
77 | }
78 | }
79 | }
80 | private fun initState()
81 | {
82 | support_premium_remove_ad_button.isChecked = CheckUtil.isAdRemoved
83 | }
84 |
85 | //클릭 이벤트
86 | fun purchase(view: View)
87 | {
88 | val chosenSkuDetails = skuDetailsList?.find { it.sku == view.tag }
89 |
90 | if (chosenSkuDetails != null)
91 | {
92 | val flowParams = BillingFlowParams.newBuilder()
93 | .setSkuDetails(chosenSkuDetails)
94 | .build()
95 | billingClient.launchBillingFlow(this, flowParams)
96 | }
97 | else Toast.makeText(this@SupportActivity, getString(R.string.error_standard), Toast.LENGTH_LONG).show()
98 | }
99 |
100 | //메소드
101 | private fun completePurchase(purchase: Purchase)
102 | {
103 | when
104 | {
105 | purchase.sku == REMOVE_AD -> // 광고 제거
106 | {
107 | job = CoroutineScope(Dispatchers.Main).launch {
108 | //구매 확인
109 | val preToken = AcknowledgePurchaseParams.newBuilder().setPurchaseToken(purchase.purchaseToken)
110 | withContext(Dispatchers.IO) { billingClient.acknowledgePurchase(preToken.build()) }
111 |
112 | //적용
113 | CheckUtil.isAdRemoved = true
114 | Toast.makeText(this@SupportActivity, getString(R.string.success_remove_ads), Toast.LENGTH_LONG).show()
115 |
116 | initState()
117 | }
118 | }
119 | purchase.sku.startsWith("namooprotector_donate") -> //기부
120 | {
121 | //소비
122 | val preToken = ConsumeParams.newBuilder().setPurchaseToken(purchase.purchaseToken)
123 | billingClient.consumeAsync(preToken.build()){ result, _ ->
124 | if (result.responseCode == BillingClient.BillingResponseCode.OK)
125 | Toast.makeText(this, getString(R.string.success_donate), Toast.LENGTH_LONG).show()
126 |
127 | else
128 | Toast.makeText(this, getString(R.string.error_standard), Toast.LENGTH_LONG).show()
129 | }
130 | }
131 | }
132 | }
133 |
134 | //상속
135 | override fun onPurchasesUpdated(result: BillingResult, purchases: MutableList?)
136 | {
137 | if (result.responseCode == BillingClient.BillingResponseCode.OK && purchases != null)
138 | purchases.forEach { completePurchase(it) }
139 |
140 | else if (result.responseCode == BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED) //구매 확인
141 | {
142 | CheckUtil.isAdRemoved = true
143 | Toast.makeText(this, getString(R.string.error_billing_already_purchased), Toast.LENGTH_LONG).show()
144 |
145 | initState()
146 | }
147 |
148 | else if (result.responseCode == BillingClient.BillingResponseCode.USER_CANCELED)
149 | Toast.makeText(this, getString(R.string.error_billing_user_canceled), Toast.LENGTH_SHORT).show()
150 |
151 | else
152 | Toast.makeText(this, getString(R.string.error_standard), Toast.LENGTH_SHORT).show()
153 | }
154 | }
--------------------------------------------------------------------------------
/app/src/main/java/nm/security/namooprotector/fragment/AppsFragment.kt:
--------------------------------------------------------------------------------
1 | package nm.security.namooprotector.fragment
2 |
3 | import android.app.AlertDialog
4 | import android.content.pm.PackageManager
5 | import android.os.AsyncTask
6 | import android.os.Bundle
7 | import android.text.Editable
8 | import android.text.TextWatcher
9 | import android.util.Log
10 | import androidx.fragment.app.Fragment
11 | import androidx.core.content.res.ResourcesCompat
12 | import androidx.recyclerview.widget.LinearLayoutManager
13 | import android.view.ViewGroup
14 | import android.view.LayoutInflater
15 | import android.view.View
16 | import androidx.appcompat.widget.AppCompatSeekBar
17 | import androidx.recyclerview.widget.DividerItemDecoration
18 | import androidx.recyclerview.widget.RecyclerView
19 | import kotlinx.android.synthetic.main.activity_pin.*
20 | import kotlinx.android.synthetic.main.fragment_apps.*
21 | import kotlinx.coroutines.*
22 | import nm.security.namooprotector.R
23 | import nm.security.namooprotector.adapter.AppsAdapter
24 | import nm.security.namooprotector.bundle.AppBundle
25 | import nm.security.namooprotector.util.DataUtil
26 | import java.lang.Exception
27 | import kotlin.coroutines.CoroutineContext
28 |
29 | class AppsFragment : Fragment()
30 | {
31 | private var job: Job? = null
32 |
33 | //라이프사이클
34 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View?
35 | {
36 | return inflater.inflate(R.layout.fragment_apps, container, false)
37 | }
38 | override fun onViewCreated(view: View, savedInstanceState: Bundle?)
39 | {
40 | initClick()
41 | initRecyclerView()
42 | initListener()
43 |
44 | loadApps()
45 | }
46 | override fun onDestroyView()
47 | {
48 | super.onDestroyView()
49 |
50 | job?.cancel()
51 | }
52 |
53 | //설정
54 | private fun initClick()
55 | {
56 | //메뉴
57 | apps_toggle_button.setOnClickListener{
58 | val dialog = AlertDialog.Builder(context)
59 |
60 | with(dialog)
61 | {
62 | setMessage(getString(R.string.alert_unselect_all_apps))
63 | setPositiveButton(getString(R.string.common_ok)) { _, _ ->
64 | DataUtil.remove(DataUtil.APPS)
65 | loadApps()
66 | }
67 | setNegativeButton(getString(R.string.common_cancel), null)
68 | show()
69 | }
70 |
71 | }
72 | }
73 | private fun initRecyclerView()
74 | {
75 | apps_list.layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false)
76 | apps_list.addItemDecoration(DividerItemDecoration(context, DividerItemDecoration.VERTICAL))
77 | apps_list.setHasFixedSize(true)
78 | }
79 | private fun initListener()
80 | {
81 | apps_search_input.addTextChangedListener(object : TextWatcher
82 | {
83 | override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int)
84 | {
85 | (apps_list.adapter as AppsAdapter).filter.filter(s)
86 | }
87 | override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int)
88 | {
89 |
90 | }
91 | override fun afterTextChanged(s: Editable)
92 | {
93 |
94 | }
95 | })
96 | }
97 |
98 | //메소드
99 | private fun loadApps()
100 | {
101 | job = CoroutineScope(Dispatchers.Main).launch{
102 | try
103 | {
104 | //준비
105 | apps_loading_layout.visibility = View.VISIBLE
106 | apps_search_container.visibility = View.GONE
107 | apps_toggle_button.visibility = View.GONE
108 |
109 | val appList = arrayListOf()
110 |
111 | //로드
112 | withContext(Dispatchers.Default)
113 | {
114 | //설치된 앱 불러오기
115 | val installedAppList = requireContext().packageManager.getInstalledApplications(PackageManager.GET_META_DATA)
116 | .filterNot { requireContext().packageManager.getLaunchIntentForPackage(it.packageName!!) == null
117 | || it.packageName == "nm.security.namooprotector"
118 | || it.packageName == "com.android.vending"
119 | || it.packageName == "com.android.settings"
120 | || it.packageName == "com.google.android.packageinstaller"}
121 | .sortedBy { it.loadLabel(requireContext().packageManager).toString() }
122 |
123 | //추가
124 | installedAppList.forEach { appList.add(AppBundle(it.loadIcon(requireContext().packageManager), it.loadLabel(requireContext().packageManager).toString(), it.packageName, DataUtil.getBoolean(it.packageName, DataUtil.APPS))) }
125 |
126 | //기본 앱 추가
127 | appList.add(0, AppBundle(ResourcesCompat.getDrawable(resources, R.drawable.vector_recommend, null)!!, getString(R.string.name_default_prevent_apps_uninstalled), "com.google.android.packageinstaller", DataUtil.getBoolean("com.google.android.packageinstaller", DataUtil.APPS)))
128 | appList.add(0, AppBundle(ResourcesCompat.getDrawable(resources, R.drawable.vector_recommend, null)!!, getString(R.string.name_default_play_store), "com.android.vending", DataUtil.getBoolean("com.android.vending", DataUtil.APPS)))
129 | appList.add(0, AppBundle(ResourcesCompat.getDrawable(resources, R.drawable.vector_recommend, null)!!, getString(R.string.name_default_settings), "com.android.settings", DataUtil.getBoolean("com.android.settings", DataUtil.APPS)))
130 | }
131 |
132 | //적용
133 | apps_list.adapter = AppsAdapter(requireContext(), appList)
134 | {
135 | it.state = !it.state
136 |
137 | DataUtil.put(it.packageName, DataUtil.APPS, it.state)
138 | apps_list.adapter!!.notifyDataSetChanged()
139 | }
140 |
141 | apps_loading_layout.visibility = View.GONE
142 | apps_search_container.visibility = View.VISIBLE
143 | apps_toggle_button.visibility = View.VISIBLE
144 | }
145 | catch (e: Exception)
146 | {
147 | Log.e("AppsFragment", "Process cancelled")
148 | }
149 | }
150 | }
151 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_pattern.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
15 |
16 |
24 |
25 |
26 |
27 |
35 |
36 |
42 |
43 |
51 |
52 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
81 |
82 |
83 |
84 |
95 |
96 |
108 |
109 |
122 |
123 |
131 |
132 |
144 |
145 |
146 |
147 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_home.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
20 |
33 |
34 |
37 |
38 |
49 |
50 |
62 |
63 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
94 |
95 |
102 |
103 |
108 |
109 |
116 |
117 |
118 |
119 |
120 |
121 |
133 |
134 |
141 |
142 |
147 |
148 |
155 |
156 |
157 |
158 |
159 |
160 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_pin.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
15 |
16 |
19 |
20 |
28 |
29 |
30 |
31 |
39 |
40 |
45 |
46 |
49 |
50 |
59 |
60 |
61 |
62 |
66 |
67 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
112 |
113 |
114 |
115 |
126 |
127 |
139 |
140 |
153 |
154 |
162 |
163 |
175 |
176 |
177 |
178 |
--------------------------------------------------------------------------------