├── 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 | 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 | 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 | 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 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | 29 | 30 | 34 | 35 | 39 | 40 | 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 | 11 | 20 | 22 | 23 | 24 | 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 | 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 | --------------------------------------------------------------------------------