├── Journaler ├── .gitignore ├── .idea │ └── vcs.xml ├── app │ ├── .gitignore │ ├── Releasing │ │ └── keystore.jks │ ├── build.gradle │ ├── proguard-rules.pro │ └── src │ │ ├── androidTestDebug │ │ └── kotlin │ │ │ └── com │ │ │ └── journaler │ │ │ ├── MainServiceTest.kt │ │ │ ├── MainSuite.kt │ │ │ └── NoteTest.kt │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── assets │ │ │ └── fonts │ │ │ │ ├── Exo2-Black.ttf │ │ │ │ ├── Exo2-BlackItalic.ttf │ │ │ │ ├── Exo2-Bold.ttf │ │ │ │ ├── Exo2-BoldItalic.ttf │ │ │ │ ├── Exo2-ExtraBold.ttf │ │ │ │ ├── Exo2-ExtraBoldItalic.ttf │ │ │ │ ├── Exo2-ExtraLight.ttf │ │ │ │ ├── Exo2-ExtraLightItalic.ttf │ │ │ │ ├── Exo2-Italic.ttf │ │ │ │ ├── Exo2-Light.ttf │ │ │ │ ├── Exo2-LightItalic.ttf │ │ │ │ ├── Exo2-Medium.ttf │ │ │ │ ├── Exo2-MediumItalic.ttf │ │ │ │ ├── Exo2-Regular.ttf │ │ │ │ ├── Exo2-SemiBold.ttf │ │ │ │ ├── Exo2-SemiBoldItalic.ttf │ │ │ │ ├── Exo2-Thin.ttf │ │ │ │ └── Exo2-ThinItalic.ttf │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── journaler │ │ │ │ ├── Journaler.kt │ │ │ │ ├── activity │ │ │ │ ├── BaseActivity.kt │ │ │ │ ├── ItemActivity.kt │ │ │ │ ├── MainActivity.kt │ │ │ │ ├── NoteActivity.kt │ │ │ │ └── TodoActivity.kt │ │ │ │ ├── adapter │ │ │ │ └── EntryAdapter.kt │ │ │ │ ├── api │ │ │ │ ├── BackendServiceHeaderMap.kt │ │ │ │ ├── BackendServiceRetrofit.kt │ │ │ │ ├── JournalerApiToken.kt │ │ │ │ ├── JournalerBackendService.kt │ │ │ │ ├── TokenManager.kt │ │ │ │ └── UserLoginRequest.kt │ │ │ │ ├── database │ │ │ │ ├── Content.kt │ │ │ │ ├── Crud.kt │ │ │ │ ├── DbHelper.kt │ │ │ │ └── DbModel.kt │ │ │ │ ├── execution │ │ │ │ └── TaskExecutor.kt │ │ │ │ ├── extension │ │ │ │ └── animations.kt │ │ │ │ ├── fragment │ │ │ │ ├── BaseFragment.kt │ │ │ │ ├── ItemsFragment.kt │ │ │ │ └── ManualFragment.kt │ │ │ │ ├── location │ │ │ │ └── LocationProvider.kt │ │ │ │ ├── model │ │ │ │ ├── Entry.kt │ │ │ │ ├── MODE.kt │ │ │ │ ├── Note.kt │ │ │ │ └── Todo.kt │ │ │ │ ├── navigation │ │ │ │ ├── NavigationDrawerAdapter.kt │ │ │ │ └── NavigationDrawerItem.kt │ │ │ │ ├── perferences │ │ │ │ ├── PreferencesConfiguration.kt │ │ │ │ ├── PreferencesProvider.kt │ │ │ │ └── PreferencesProviderAbstract.kt │ │ │ │ ├── permission │ │ │ │ ├── PermissionCompatActivity.kt │ │ │ │ └── PermissionRequestCallback.kt │ │ │ │ ├── provider │ │ │ │ └── JournalerProvider.kt │ │ │ │ ├── receiver │ │ │ │ ├── BootReceiver.kt │ │ │ │ ├── NetworkReceiver.kt │ │ │ │ └── ShutdownReceiver.kt │ │ │ │ └── service │ │ │ │ ├── DataSynchronization.kt │ │ │ │ ├── DatabaseService.kt │ │ │ │ └── MainService.kt │ │ ├── proguard-rules.pro │ │ └── res │ │ │ ├── anim │ │ │ ├── bottom_to_top.xml │ │ │ ├── fade_in.xml │ │ │ ├── fade_out.xml │ │ │ ├── hide_to_bottom.xml │ │ │ ├── hide_to_top.xml │ │ │ └── top_to_bottom.xml │ │ │ ├── drawable │ │ │ ├── add.png │ │ │ ├── main_icon.png │ │ │ ├── rect_rounded_green.xml │ │ │ ├── rect_rounded_green_dark.xml │ │ │ ├── rect_rounded_grey_disabled.xml │ │ │ ├── selector_button_green.xml │ │ │ └── selector_button_grey.xml │ │ │ ├── layout │ │ │ ├── activity_header.xml │ │ │ ├── activity_main.xml │ │ │ ├── activity_note.xml │ │ │ ├── activity_todo.xml │ │ │ ├── adapter_entry.xml │ │ │ ├── adapter_navigation_drawer.xml │ │ │ ├── fragment_items.xml │ │ │ └── fragment_manual.xml │ │ │ ├── menu │ │ │ └── main.xml │ │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ └── values │ │ │ ├── colors.xml │ │ │ ├── dimens.xml │ │ │ ├── strings.xml │ │ │ ├── styles.xml │ │ │ └── tags.xml │ │ └── testDebug │ │ └── kotlin │ │ └── com │ │ └── journaler │ │ └── DummyTest.kt ├── build.gradle ├── content_provider_client_example │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ └── com │ │ │ └── journaler │ │ │ └── content_provider_client │ │ │ └── MainActivity.kt │ │ └── res │ │ ├── drawable │ │ └── main_icon.png │ │ ├── layout │ │ └── activity_main.xml │ │ └── values │ │ ├── strings.xml │ │ └── styles.xml └── settings.gradle ├── README.md └── notes.txt /Journaler/.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .gradle 3 | build/ 4 | gradle* 5 | !gradle-plugins* 6 | gradle-app.setting 7 | !gradle-wrapper.jar 8 | .gradletasknamecache 9 | local.properties 10 | gen 11 | 12 | *.iml -------------------------------------------------------------------------------- /Journaler/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Journaler/app/.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | .mtj.tmp/ 3 | 4 | *.jar 5 | *.war 6 | *.ear 7 | hs_err_pid* 8 | .idea/* 9 | .DS_Store 10 | .idea/shelf 11 | /android.tests.dependencies 12 | /confluence/target 13 | /dependencies 14 | /dist 15 | /gh-pages 16 | /ideaSDK 17 | /android-studio/sdk 18 | out 19 | tmp 20 | workspace.xml 21 | *.versionsBackup 22 | /idea/testData/debugger/tinyApp/classes* 23 | /jps-plugin/testData/kannotator 24 | ultimate/.DS_Store 25 | ultimate/.idea/shelf 26 | ultimate/dependencies 27 | ultimate/ideaSDK 28 | ultimate/out 29 | ultimate/tmp 30 | ultimate/workspace.xml 31 | ultimate/*.versionsBackup 32 | .idea/workspace.xml 33 | .idea/tasks.xml 34 | .idea/dataSources.ids 35 | .idea/dataSources.xml 36 | .idea/dataSources.local.xml 37 | .idea/sqlDataSources.xml 38 | .idea/dynamic.xml 39 | .idea/uiDesigner.xml 40 | .idea/gradle.xml 41 | .idea/libraries 42 | .idea/mongoSettings.xml 43 | *.iws 44 | /out/ 45 | .idea_modules/ 46 | atlassian-ide-plugin.xml 47 | com_crashlytics_export_strings.xml 48 | crashlytics.properties 49 | crashlytics-build.properties 50 | fabric.properties 51 | target/ 52 | pom.xml.tag 53 | pom.xml.releaseBackup 54 | pom.xml.versionsBackup 55 | pom.xml.next 56 | release.properties 57 | dependency-reduced-pom.xml 58 | buildNumber.properties 59 | .mvn/timing.properties 60 | !/.mvn/wrapper/maven-wrapper.jar 61 | samples/* 62 | build/* 63 | .gradle/* 64 | !libs/*.jar 65 | !Releases/*.jar 66 | 67 | credentials*.gradle 68 | gen 69 | -------------------------------------------------------------------------------- /Journaler/app/Releasing/keystore.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/-Mastering-Android-Development-with-Kotlin/c09c0cee8c7af997d05b6db5bdbaf55a6d9b00e3/Journaler/app/Releasing/keystore.jks -------------------------------------------------------------------------------- /Journaler/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: "com.android.application" 2 | apply plugin: "kotlin-android" 3 | apply plugin: "kotlin-android-extensions" 4 | 5 | repositories { 6 | maven { url "https://maven.google.com" } 7 | } 8 | 9 | android { 10 | compileSdkVersion 26 11 | buildToolsVersion "25.0.3" 12 | defaultConfig { 13 | applicationId "com.journaler" 14 | minSdkVersion 21 15 | targetSdkVersion 26 16 | versionCode 1 17 | versionName "1.0" 18 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 19 | } 20 | buildTypes { 21 | release { 22 | minifyEnabled false 23 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 24 | } 25 | } 26 | sourceSets { 27 | main.java.srcDirs += [ 28 | 'src/main/kotlin', 29 | 'src/common/kotlin', 30 | 'src/debug/kotlin', 31 | 'src/release/kotlin', 32 | 'src/staging/kotlin', 33 | 'src/preproduction/kotlin', 34 | 'src/debug/java', 35 | 'src/release/java', 36 | 'src/staging/java', 37 | 'src/preproduction/java', 38 | 'src/testDebug/java', 39 | 'src/testDebug/kotlin', 40 | 'src/androidTestDebug/java', 41 | 'src/androidTestDebug/kotlin' 42 | ] 43 | } 44 | signingConfigs { 45 | release { 46 | storeFile file("Releasing/keystore.jks") 47 | storePassword "12345678" 48 | keyAlias "key0" 49 | keyPassword "12345678" 50 | } 51 | } 52 | buildTypes { 53 | debug { 54 | applicationIdSuffix ".dev" 55 | } 56 | staging { 57 | debuggable true 58 | applicationIdSuffix ".sta" 59 | } 60 | preproduction { 61 | applicationIdSuffix ".pre" 62 | } 63 | release { 64 | debuggable false 65 | minifyEnabled false 66 | signingConfig signingConfigs.release 67 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 68 | } 69 | } 70 | productFlavors { 71 | demo { 72 | applicationIdSuffix ".demo" 73 | versionNameSuffix "-demo" 74 | } 75 | complete { 76 | applicationIdSuffix ".complete" 77 | versionNameSuffix "-complete" 78 | } 79 | special { 80 | applicationIdSuffix ".special" 81 | versionNameSuffix "-special" 82 | } 83 | } 84 | testOptions { 85 | unitTests.returnDefaultValues = true 86 | } 87 | } 88 | 89 | repositories { 90 | jcenter() 91 | mavenCentral() 92 | } 93 | 94 | dependencies { 95 | compile "org.jetbrains.kotlin:kotlin-reflect:1.1.51" 96 | compile "org.jetbrains.kotlin:kotlin-stdlib:1.1.51" 97 | compile "com.android.support:design:26.0.1" 98 | compile "com.android.support:appcompat-v7:26.0.1" 99 | 100 | compile "com.google.code.gson:gson:2.8.0" 101 | compile "com.github.salomonbrys.kotson:kotson:2.3.0" 102 | compile "com.squareup.retrofit2:retrofit:2.3.0" 103 | compile "com.squareup.retrofit2:converter-gson:2.0.2" 104 | compile "com.squareup.okhttp3:okhttp:3.9.0" 105 | compile "com.squareup.okhttp3:logging-interceptor:3.9.0" 106 | 107 | compile "junit:junit:4.12" 108 | testCompile "junit:junit:4.12" 109 | 110 | testCompile "org.jetbrains.kotlin:kotlin-reflect:1.1.51" 111 | testCompile "org.jetbrains.kotlin:kotlin-stdlib:1.1.51" 112 | 113 | compile "org.jetbrains.kotlin:kotlin-test:1.1.51" 114 | testCompile "org.jetbrains.kotlin:kotlin-test:1.1.51" 115 | 116 | compile "org.jetbrains.kotlin:kotlin-test-junit:1.1.51" 117 | testCompile "org.jetbrains.kotlin:kotlin-test-junit:1.1.51" 118 | 119 | compile 'com.android.support:support-annotations:26.0.1' 120 | androidTestCompile 'com.android.support:support-annotations:26.0.1' 121 | 122 | compile 'com.android.support.test:runner:0.5' 123 | androidTestCompile 'com.android.support.test:runner:0.5' 124 | 125 | compile 'com.android.support.test:rules:0.5' 126 | androidTestCompile 'com.android.support.test:rules:0.5' 127 | 128 | 129 | } -------------------------------------------------------------------------------- /Journaler/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/milosvasic/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /Journaler/app/src/androidTestDebug/kotlin/com/journaler/MainServiceTest.kt: -------------------------------------------------------------------------------- 1 | package com.journaler 2 | 3 | import android.content.ComponentName 4 | import android.content.Context 5 | import android.content.Intent 6 | import android.content.ServiceConnection 7 | import android.os.IBinder 8 | import android.support.test.InstrumentationRegistry 9 | import android.util.Log 10 | import com.journaler.service.MainService 11 | import org.junit.After 12 | import org.junit.Before 13 | import org.junit.Test 14 | import kotlin.test.assertNotNull 15 | 16 | class MainServiceTest { 17 | 18 | private var ctx: Context? = null 19 | private val tag = "Main service test" 20 | 21 | private val serviceConnection = object : ServiceConnection { 22 | override fun onServiceConnected(p0: ComponentName?, binder: IBinder?) { 23 | Log.v(tag, "Service connected") 24 | } 25 | 26 | override fun onServiceDisconnected(p0: ComponentName?) { 27 | Log.v(tag, "Service disconnected") 28 | } 29 | } 30 | 31 | @Before 32 | fun beforeMainServiceTest() { 33 | Log.v(tag, "Starting") 34 | ctx = InstrumentationRegistry.getInstrumentation().context 35 | } 36 | 37 | @Test 38 | fun testMainService() { 39 | Log.v(tag, "Running") 40 | assertNotNull(ctx) 41 | val serviceIntent = Intent(ctx, MainService::class.java) 42 | ctx?.startService(serviceIntent) 43 | val result = ctx?.bindService( 44 | serviceIntent, 45 | serviceConnection, 46 | android.content.Context.BIND_AUTO_CREATE 47 | ) 48 | assert(result != null && result) 49 | } 50 | 51 | @After 52 | fun afterMainServiceTest() { 53 | Log.v(tag, "Finishing") 54 | ctx?.unbindService(serviceConnection) 55 | val serviceIntent = Intent(ctx, MainService::class.java) 56 | ctx?.stopService(serviceIntent) 57 | } 58 | 59 | } -------------------------------------------------------------------------------- /Journaler/app/src/androidTestDebug/kotlin/com/journaler/MainSuite.kt: -------------------------------------------------------------------------------- 1 | package com.journaler 2 | 3 | import org.junit.runner.RunWith 4 | import org.junit.runners.Suite 5 | 6 | @RunWith(Suite::class) 7 | @Suite.SuiteClasses( 8 | MainServiceTest::class, 9 | NoteTest::class 10 | ) 11 | class MainSuite -------------------------------------------------------------------------------- /Journaler/app/src/androidTestDebug/kotlin/com/journaler/NoteTest.kt: -------------------------------------------------------------------------------- 1 | package com.journaler 2 | 3 | import android.location.Location 4 | import com.journaler.database.Content 5 | import com.journaler.model.Note 6 | import org.junit.Test 7 | 8 | class NoteTest { 9 | 10 | @Test 11 | fun noteTest() { 12 | val note = Note( 13 | "stub ${System.currentTimeMillis()}", 14 | "stub ${System.currentTimeMillis()}", 15 | Location("Stub") 16 | ) 17 | 18 | val id = Content.NOTE.insert(note) 19 | note.id = id 20 | 21 | assert(note.id > 0) 22 | } 23 | } -------------------------------------------------------------------------------- /Journaler/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 20 | 21 | 25 | 26 | 29 | 30 | 33 | 34 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 72 | 73 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /Journaler/app/src/main/assets/fonts/Exo2-Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/-Mastering-Android-Development-with-Kotlin/c09c0cee8c7af997d05b6db5bdbaf55a6d9b00e3/Journaler/app/src/main/assets/fonts/Exo2-Black.ttf -------------------------------------------------------------------------------- /Journaler/app/src/main/assets/fonts/Exo2-BlackItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/-Mastering-Android-Development-with-Kotlin/c09c0cee8c7af997d05b6db5bdbaf55a6d9b00e3/Journaler/app/src/main/assets/fonts/Exo2-BlackItalic.ttf -------------------------------------------------------------------------------- /Journaler/app/src/main/assets/fonts/Exo2-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/-Mastering-Android-Development-with-Kotlin/c09c0cee8c7af997d05b6db5bdbaf55a6d9b00e3/Journaler/app/src/main/assets/fonts/Exo2-Bold.ttf -------------------------------------------------------------------------------- /Journaler/app/src/main/assets/fonts/Exo2-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/-Mastering-Android-Development-with-Kotlin/c09c0cee8c7af997d05b6db5bdbaf55a6d9b00e3/Journaler/app/src/main/assets/fonts/Exo2-BoldItalic.ttf -------------------------------------------------------------------------------- /Journaler/app/src/main/assets/fonts/Exo2-ExtraBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/-Mastering-Android-Development-with-Kotlin/c09c0cee8c7af997d05b6db5bdbaf55a6d9b00e3/Journaler/app/src/main/assets/fonts/Exo2-ExtraBold.ttf -------------------------------------------------------------------------------- /Journaler/app/src/main/assets/fonts/Exo2-ExtraBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/-Mastering-Android-Development-with-Kotlin/c09c0cee8c7af997d05b6db5bdbaf55a6d9b00e3/Journaler/app/src/main/assets/fonts/Exo2-ExtraBoldItalic.ttf -------------------------------------------------------------------------------- /Journaler/app/src/main/assets/fonts/Exo2-ExtraLight.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/-Mastering-Android-Development-with-Kotlin/c09c0cee8c7af997d05b6db5bdbaf55a6d9b00e3/Journaler/app/src/main/assets/fonts/Exo2-ExtraLight.ttf -------------------------------------------------------------------------------- /Journaler/app/src/main/assets/fonts/Exo2-ExtraLightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/-Mastering-Android-Development-with-Kotlin/c09c0cee8c7af997d05b6db5bdbaf55a6d9b00e3/Journaler/app/src/main/assets/fonts/Exo2-ExtraLightItalic.ttf -------------------------------------------------------------------------------- /Journaler/app/src/main/assets/fonts/Exo2-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/-Mastering-Android-Development-with-Kotlin/c09c0cee8c7af997d05b6db5bdbaf55a6d9b00e3/Journaler/app/src/main/assets/fonts/Exo2-Italic.ttf -------------------------------------------------------------------------------- /Journaler/app/src/main/assets/fonts/Exo2-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/-Mastering-Android-Development-with-Kotlin/c09c0cee8c7af997d05b6db5bdbaf55a6d9b00e3/Journaler/app/src/main/assets/fonts/Exo2-Light.ttf -------------------------------------------------------------------------------- /Journaler/app/src/main/assets/fonts/Exo2-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/-Mastering-Android-Development-with-Kotlin/c09c0cee8c7af997d05b6db5bdbaf55a6d9b00e3/Journaler/app/src/main/assets/fonts/Exo2-LightItalic.ttf -------------------------------------------------------------------------------- /Journaler/app/src/main/assets/fonts/Exo2-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/-Mastering-Android-Development-with-Kotlin/c09c0cee8c7af997d05b6db5bdbaf55a6d9b00e3/Journaler/app/src/main/assets/fonts/Exo2-Medium.ttf -------------------------------------------------------------------------------- /Journaler/app/src/main/assets/fonts/Exo2-MediumItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/-Mastering-Android-Development-with-Kotlin/c09c0cee8c7af997d05b6db5bdbaf55a6d9b00e3/Journaler/app/src/main/assets/fonts/Exo2-MediumItalic.ttf -------------------------------------------------------------------------------- /Journaler/app/src/main/assets/fonts/Exo2-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/-Mastering-Android-Development-with-Kotlin/c09c0cee8c7af997d05b6db5bdbaf55a6d9b00e3/Journaler/app/src/main/assets/fonts/Exo2-Regular.ttf -------------------------------------------------------------------------------- /Journaler/app/src/main/assets/fonts/Exo2-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/-Mastering-Android-Development-with-Kotlin/c09c0cee8c7af997d05b6db5bdbaf55a6d9b00e3/Journaler/app/src/main/assets/fonts/Exo2-SemiBold.ttf -------------------------------------------------------------------------------- /Journaler/app/src/main/assets/fonts/Exo2-SemiBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/-Mastering-Android-Development-with-Kotlin/c09c0cee8c7af997d05b6db5bdbaf55a6d9b00e3/Journaler/app/src/main/assets/fonts/Exo2-SemiBoldItalic.ttf -------------------------------------------------------------------------------- /Journaler/app/src/main/assets/fonts/Exo2-Thin.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/-Mastering-Android-Development-with-Kotlin/c09c0cee8c7af997d05b6db5bdbaf55a6d9b00e3/Journaler/app/src/main/assets/fonts/Exo2-Thin.ttf -------------------------------------------------------------------------------- /Journaler/app/src/main/assets/fonts/Exo2-ThinItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/-Mastering-Android-Development-with-Kotlin/c09c0cee8c7af997d05b6db5bdbaf55a6d9b00e3/Journaler/app/src/main/assets/fonts/Exo2-ThinItalic.ttf -------------------------------------------------------------------------------- /Journaler/app/src/main/kotlin/com/journaler/Journaler.kt: -------------------------------------------------------------------------------- 1 | package com.journaler 2 | 3 | import android.app.Application 4 | import android.content.Context 5 | import android.content.Intent 6 | import android.content.IntentFilter 7 | import android.net.ConnectivityManager 8 | import android.util.Log 9 | import com.journaler.receiver.NetworkReceiver 10 | import com.journaler.service.MainService 11 | 12 | class Journaler : Application() { 13 | 14 | private val networkReceiver = NetworkReceiver() 15 | 16 | companion object { 17 | val tag = "Journaler" 18 | var ctx: Context? = null 19 | } 20 | 21 | override fun onCreate() { 22 | super.onCreate() 23 | ctx = applicationContext 24 | Log.v(tag, "[ ON CREATE ]") 25 | val filter = IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION) 26 | registerReceiver(networkReceiver, filter) 27 | } 28 | 29 | override fun onLowMemory() { 30 | super.onLowMemory() 31 | Log.w(tag, "[ ON LOW MEMORY ]") 32 | // If we get low on memory we will stop service if running. 33 | stopService() 34 | } 35 | 36 | override fun onTrimMemory(level: Int) { 37 | super.onTrimMemory(level) 38 | Log.d(tag, "[ ON TRIM MEMORY ]: $level") 39 | } 40 | 41 | private fun stopService() { 42 | val serviceIntent = Intent(this, MainService::class.java) 43 | stopService(serviceIntent) 44 | } 45 | 46 | } -------------------------------------------------------------------------------- /Journaler/app/src/main/kotlin/com/journaler/activity/BaseActivity.kt: -------------------------------------------------------------------------------- 1 | package com.journaler.activity 2 | 3 | import android.Manifest 4 | import android.content.Context 5 | import android.graphics.Typeface 6 | import android.os.Bundle 7 | import android.util.Log 8 | import android.view.Menu 9 | import android.view.View 10 | import android.view.ViewGroup 11 | import android.widget.Button 12 | import android.widget.EditText 13 | import android.widget.TextView 14 | import com.journaler.R 15 | import com.journaler.extension.getAnimation 16 | import com.journaler.permission.PermissionCompatActivity 17 | import kotlinx.android.synthetic.main.activity_main.* 18 | 19 | 20 | abstract class BaseActivity : PermissionCompatActivity() { 21 | 22 | companion object { 23 | 24 | private var fontExoBold: Typeface? = null 25 | private var fontExoRegular: Typeface? = null 26 | 27 | fun applyFonts(view: View, ctx: Context) { 28 | var vTag = "" 29 | if (view.tag is String) { 30 | vTag = view.tag as String 31 | } 32 | when (view) { 33 | is ViewGroup -> { 34 | for (x in 0..view.childCount - 1) { 35 | applyFonts(view.getChildAt(x), ctx) 36 | } 37 | } 38 | is Button -> { 39 | when (vTag) { 40 | ctx.getString(R.string.tag_font_bold) -> { 41 | view.typeface = fontExoBold 42 | } 43 | else -> { 44 | view.typeface = fontExoRegular 45 | } 46 | } 47 | } 48 | is TextView -> { 49 | when (vTag) { 50 | ctx.getString(R.string.tag_font_bold) -> { 51 | view.typeface = fontExoBold 52 | } 53 | else -> { 54 | view.typeface = fontExoRegular 55 | } 56 | } 57 | } 58 | is EditText -> { 59 | when (vTag) { 60 | ctx.getString(R.string.tag_font_bold) -> { 61 | view.typeface = fontExoBold 62 | } 63 | else -> { 64 | view.typeface = fontExoRegular 65 | } 66 | } 67 | } 68 | } 69 | } 70 | } 71 | 72 | protected abstract val tag: String 73 | 74 | protected abstract fun getLayout(): Int 75 | 76 | protected abstract fun getActivityTitle(): Int 77 | 78 | override fun onCreate(savedInstanceState: Bundle?) { 79 | super.onCreate(savedInstanceState) 80 | overridePendingTransition(R.anim.fade_in, R.anim.fade_out) 81 | setContentView(getLayout()) 82 | setSupportActionBar(toolbar) 83 | Log.v(tag, "[ ON CREATE ]") 84 | requestPermissions( 85 | Manifest.permission.ACCESS_FINE_LOCATION, 86 | Manifest.permission.ACCESS_COARSE_LOCATION 87 | ) 88 | } 89 | 90 | override fun onCreateOptionsMenu(menu: Menu): Boolean { 91 | menuInflater.inflate(R.menu.main, menu) 92 | return true 93 | } 94 | 95 | override fun onPostCreate(savedInstanceState: Bundle?) { 96 | super.onPostCreate(savedInstanceState) 97 | Log.v(tag, "[ ON POST CREATE ]") 98 | applyFonts() 99 | } 100 | 101 | override fun onRestart() { 102 | super.onRestart() 103 | Log.v(tag, "[ ON RESTART ]") 104 | } 105 | 106 | override fun onStart() { 107 | super.onStart() 108 | Log.v(tag, "[ ON START ]") 109 | } 110 | 111 | override fun onResume() { 112 | super.onResume() 113 | Log.v(tag, "[ ON RESUME ]") 114 | val animation = getAnimation(R.anim.top_to_bottom) 115 | findViewById(R.id.toolbar).startAnimation(animation) 116 | } 117 | 118 | override fun onPostResume() { 119 | super.onPostResume() 120 | Log.v(tag, "[ ON POST RESUME ]") 121 | } 122 | 123 | override fun onPause() { 124 | super.onPause() 125 | Log.v(tag, "[ ON PAUSE ]") 126 | val animation = getAnimation(R.anim.hide_to_top) 127 | findViewById(R.id.toolbar).startAnimation(animation) 128 | } 129 | 130 | override fun onStop() { 131 | super.onStop() 132 | Log.v(tag, "[ ON STOP ]") 133 | } 134 | 135 | override fun onDestroy() { 136 | super.onDestroy() 137 | Log.v(tag, "[ ON DESTROY ]") 138 | } 139 | 140 | private fun applyFonts() { 141 | initFonts() 142 | Log.v(tag, "Applying fonts [ START ]") 143 | val rootView = findViewById(android.R.id.content) 144 | applyFonts(rootView, this) 145 | Log.v(tag, "Applying fonts [ END ]") 146 | } 147 | 148 | private fun initFonts() { 149 | if (fontExoBold == null) { 150 | Log.v(tag, "Initializing font [ Exo2-Bold ]") 151 | fontExoBold = Typeface.createFromAsset(assets, "fonts/Exo2-Bold.ttf") 152 | } 153 | if (fontExoRegular == null) { 154 | Log.v(tag, "Initializing font [ Exo2-Regular ]") 155 | fontExoRegular = Typeface.createFromAsset(assets, "fonts/Exo2-Regular.ttf") 156 | } 157 | } 158 | 159 | } 160 | 161 | -------------------------------------------------------------------------------- /Journaler/app/src/main/kotlin/com/journaler/activity/ItemActivity.kt: -------------------------------------------------------------------------------- 1 | package com.journaler.activity 2 | 3 | import android.app.Activity 4 | import android.os.Bundle 5 | import android.util.Log 6 | import com.journaler.R 7 | import com.journaler.model.MODE 8 | 9 | 10 | abstract class ItemActivity : BaseActivity() { 11 | 12 | protected var mode = MODE.VIEW 13 | protected var success = Activity.RESULT_CANCELED 14 | 15 | override fun getActivityTitle() = R.string.app_name 16 | 17 | override fun onCreate(savedInstanceState: Bundle?) { 18 | super.onCreate(savedInstanceState) 19 | val data = intent.extras 20 | data?.let{ 21 | val modeToSet = data.getInt(MODE.EXTRAS_KEY, MODE.VIEW.mode) 22 | mode = MODE.getByValue(modeToSet) 23 | } 24 | Log.v(tag, "Mode [ $mode ]") 25 | } 26 | 27 | override fun onDestroy() { 28 | super.onDestroy() 29 | setResult(success) 30 | } 31 | 32 | } -------------------------------------------------------------------------------- /Journaler/app/src/main/kotlin/com/journaler/activity/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.journaler.activity 2 | 3 | import android.content.ComponentName 4 | import android.content.Context 5 | import android.content.Intent 6 | import android.content.ServiceConnection 7 | import android.os.Bundle 8 | import android.os.IBinder 9 | import android.support.v4.app.Fragment 10 | import android.support.v4.app.FragmentManager 11 | import android.support.v4.app.FragmentStatePagerAdapter 12 | import android.support.v4.view.GravityCompat 13 | import android.support.v4.view.ViewPager 14 | import android.util.Log 15 | import android.view.MenuItem 16 | import com.journaler.R 17 | import com.journaler.fragment.ItemsFragment 18 | import com.journaler.navigation.NavigationDrawerAdapter 19 | import com.journaler.navigation.NavigationDrawerItem 20 | import com.journaler.perferences.PreferencesConfiguration 21 | import com.journaler.perferences.PreferencesProvider 22 | import com.journaler.service.MainService 23 | import kotlinx.android.synthetic.main.activity_main.* 24 | 25 | 26 | class MainActivity : BaseActivity() { 27 | 28 | override val tag = "Main activity" 29 | 30 | override fun getLayout() = R.layout.activity_main 31 | 32 | override fun getActivityTitle() = R.string.app_name 33 | 34 | private var service: MainService? = null 35 | 36 | private val keyPagePosition = "keyPagePosition" 37 | 38 | private val synchronize: NavigationDrawerItem by lazy { 39 | NavigationDrawerItem( 40 | getString(R.string.synchronize), 41 | Runnable { service?.synchronize() }, 42 | false 43 | ) 44 | } 45 | 46 | private val serviceConnection = object : ServiceConnection { 47 | override fun onServiceDisconnected(p0: ComponentName?) { 48 | service = null 49 | synchronize.enabled = false 50 | } 51 | 52 | override fun onServiceConnected(p0: ComponentName?, binder: IBinder?) { 53 | if (binder is MainService.MainServiceBinder) { 54 | service = binder.getService() 55 | service?.let { 56 | synchronize.enabled = true 57 | } 58 | } 59 | } 60 | } 61 | 62 | override fun onCreate(savedInstanceState: Bundle?) { 63 | super.onCreate(savedInstanceState) 64 | 65 | val provider = PreferencesProvider() 66 | val config = PreferencesConfiguration("journaler_prefs", Context.MODE_PRIVATE) 67 | val preferences = provider.obtain(config, this) 68 | 69 | pager.adapter = ViewPagerAdapter(supportFragmentManager) 70 | pager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener { 71 | override fun onPageScrollStateChanged(state: Int) { 72 | // Ignore 73 | } 74 | 75 | override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) { 76 | // Ignore 77 | } 78 | 79 | override fun onPageSelected(position: Int) { 80 | Log.v(tag, "Page [ $position ]") 81 | preferences.edit().putInt(keyPagePosition, position).apply() 82 | } 83 | }) 84 | 85 | val pagerPosition = preferences.getInt(keyPagePosition, 0) 86 | pager.setCurrentItem(pagerPosition, true) 87 | 88 | val menuItems = mutableListOf() 89 | val today = NavigationDrawerItem( 90 | getString(R.string.today), 91 | Runnable { 92 | pager.setCurrentItem(0, true) 93 | } 94 | ) 95 | 96 | val next7Days = NavigationDrawerItem( 97 | getString(R.string.next_seven_days), 98 | Runnable { 99 | pager.setCurrentItem(1, true) 100 | } 101 | ) 102 | 103 | val todos = NavigationDrawerItem( 104 | getString(R.string.todos), 105 | Runnable { 106 | pager.setCurrentItem(2, true) 107 | } 108 | ) 109 | 110 | val notes = NavigationDrawerItem( 111 | getString(R.string.notes), 112 | Runnable { 113 | pager.setCurrentItem(3, true) 114 | } 115 | ) 116 | 117 | menuItems.add(today) 118 | menuItems.add(next7Days) 119 | menuItems.add(todos) 120 | menuItems.add(notes) 121 | menuItems.add(synchronize) 122 | 123 | val navgationDrawerAdapter = NavigationDrawerAdapter(this, menuItems) 124 | left_drawer.adapter = navgationDrawerAdapter 125 | 126 | val serviceIntent = Intent(this, MainService::class.java) 127 | startService(serviceIntent) 128 | } 129 | 130 | override fun onResume() { 131 | super.onResume() 132 | val intent = Intent(this, MainService::class.java) 133 | bindService(intent, serviceConnection, android.content.Context.BIND_AUTO_CREATE) 134 | } 135 | 136 | override fun onPause() { 137 | super.onPause() 138 | unbindService(serviceConnection) 139 | } 140 | 141 | private class ViewPagerAdapter(manager: FragmentManager) : FragmentStatePagerAdapter(manager) { 142 | override fun getItem(position: Int): Fragment { 143 | return ItemsFragment() 144 | } 145 | 146 | override fun getCount(): Int { 147 | return 4 148 | } 149 | } 150 | 151 | override fun onOptionsItemSelected(item: MenuItem): Boolean { 152 | when (item.itemId) { 153 | R.id.drawing_menu -> { 154 | drawer_layout.openDrawer(GravityCompat.START) 155 | return true 156 | } 157 | R.id.options_menu -> { 158 | Log.v(tag, "Options menu.") 159 | return true 160 | } 161 | else -> return super.onOptionsItemSelected(item) 162 | } 163 | } 164 | 165 | } -------------------------------------------------------------------------------- /Journaler/app/src/main/kotlin/com/journaler/activity/NoteActivity.kt: -------------------------------------------------------------------------------- 1 | package com.journaler.activity 2 | 3 | import android.content.BroadcastReceiver 4 | import android.content.Context 5 | import android.location.Location 6 | import android.location.LocationListener 7 | import android.os.Bundle 8 | import android.os.Handler 9 | import android.os.Looper 10 | import android.os.Message 11 | import android.support.v4.content.ContextCompat 12 | import android.text.Editable 13 | import android.text.TextUtils 14 | import android.text.TextWatcher 15 | import com.journaler.R 16 | import com.journaler.location.LocationProvider 17 | import com.journaler.model.Note 18 | import kotlinx.android.synthetic.main.activity_note.* 19 | import android.content.Intent 20 | import android.content.IntentFilter 21 | import android.util.Log 22 | import com.journaler.database.Crud 23 | import com.journaler.model.MODE 24 | import com.journaler.service.DatabaseService 25 | 26 | 27 | class NoteActivity : ItemActivity() { 28 | 29 | private var note: Note? = null 30 | override val tag = "Note activity" 31 | private var handler: Handler? = null 32 | private var location: Location? = null 33 | override fun getLayout() = R.layout.activity_note 34 | 35 | private val textWatcher = object : TextWatcher { 36 | override fun afterTextChanged(p0: Editable?) { 37 | updateNote() 38 | } 39 | 40 | override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {} 41 | 42 | override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {} 43 | } 44 | 45 | private val locationListener = object : LocationListener { 46 | override fun onLocationChanged(p0: Location?) { 47 | p0?.let { 48 | LocationProvider.unsubscribe(this) 49 | location = p0 50 | val title = getNoteTitle() 51 | val content = getNoteContent() 52 | note = Note(title, content, p0) 53 | 54 | // Switching to intent service. 55 | val dbIntent = Intent(this@NoteActivity, DatabaseService::class.java) 56 | dbIntent.putExtra(DatabaseService.EXTRA_ENTRY, note) 57 | dbIntent.putExtra(DatabaseService.EXTRA_OPERATION, MODE.CREATE.mode) 58 | startService(dbIntent) 59 | sendMessage(true) 60 | } 61 | } 62 | 63 | override fun onStatusChanged(p0: String?, p1: Int, p2: Bundle?) {} 64 | 65 | override fun onProviderEnabled(p0: String?) {} 66 | 67 | override fun onProviderDisabled(p0: String?) {} 68 | } 69 | 70 | private val crudOperationListener = object : BroadcastReceiver() { 71 | override fun onReceive(ctx: Context?, intent: Intent?) { 72 | intent?.let { 73 | val crudResultValue = intent.getLongExtra( 74 | Crud.BROADCAST_EXTRAS_KEY_CRUD_OPERATION_RESULT, 0 75 | ) 76 | if (crudResultValue > 0) { 77 | note?.id = crudResultValue 78 | sendMessage(true) 79 | } else sendMessage(false) 80 | } 81 | } 82 | } 83 | 84 | override fun onCreate(savedInstanceState: Bundle?) { 85 | super.onCreate(savedInstanceState) 86 | handler = object : Handler(Looper.getMainLooper()) { 87 | override fun handleMessage(msg: Message?) { 88 | msg?.let { 89 | var color = R.color.vermilion 90 | if (msg.arg1 > 0) { 91 | color = R.color.green 92 | } 93 | indicator.setBackgroundColor(ContextCompat.getColor( 94 | this@NoteActivity, 95 | color 96 | )) 97 | } 98 | super.handleMessage(msg) 99 | } 100 | } 101 | note_title.addTextChangedListener(textWatcher) 102 | note_content.addTextChangedListener(textWatcher) 103 | val intentFiler = IntentFilter(Crud.BROADCAST_ACTION) 104 | registerReceiver(crudOperationListener, intentFiler) 105 | } 106 | 107 | override fun onDestroy() { 108 | unregisterReceiver(crudOperationListener) 109 | super.onDestroy() 110 | } 111 | 112 | private fun updateNote() { 113 | if (note == null) { 114 | if (!TextUtils.isEmpty(getNoteTitle()) && !TextUtils.isEmpty(getNoteContent())) { 115 | LocationProvider.subscribe(locationListener) 116 | } 117 | } else { 118 | note?.title = getNoteTitle() 119 | note?.message = getNoteContent() 120 | 121 | // Switching to intent service. 122 | val dbIntent = Intent(this@NoteActivity, DatabaseService::class.java) 123 | dbIntent.putExtra(DatabaseService.EXTRA_ENTRY, note) 124 | dbIntent.putExtra(DatabaseService.EXTRA_OPERATION, MODE.EDIT.mode) 125 | startService(dbIntent) 126 | sendMessage(true) 127 | } 128 | } 129 | 130 | private fun sendMessage(result: Boolean) { 131 | Log.v(tag, "Crud operation result [ $result ]") 132 | val msg = handler?.obtainMessage() 133 | if (result) { 134 | msg?.arg1 = 1 135 | } else { 136 | msg?.arg1 = 0 137 | } 138 | handler?.sendMessage(msg) 139 | } 140 | 141 | private fun getNoteContent(): String = note_content.text.toString() 142 | 143 | private fun getNoteTitle(): String = note_title.text.toString() 144 | 145 | } -------------------------------------------------------------------------------- /Journaler/app/src/main/kotlin/com/journaler/activity/TodoActivity.kt: -------------------------------------------------------------------------------- 1 | package com.journaler.activity 2 | 3 | import android.os.Bundle 4 | import com.journaler.R 5 | import kotlinx.android.synthetic.main.activity_todo.* 6 | 7 | 8 | class TodoActivity : ItemActivity() { 9 | 10 | companion object { 11 | val EXTRA_DATE = "EXTRA_DATE" 12 | val EXTRA_TIME = "EXTRA_TIME" 13 | } 14 | 15 | override val tag = "Todo activity" 16 | 17 | override fun getLayout() = R.layout.activity_todo 18 | 19 | override fun onCreate(savedInstanceState: Bundle?) { 20 | super.onCreate(savedInstanceState) 21 | val data = intent.extras 22 | data?.let { 23 | val date = data.getString(EXTRA_DATE, "") 24 | val time = data.getString(EXTRA_TIME, "") 25 | pick_date.text = date 26 | pick_time.text = time 27 | } 28 | } 29 | 30 | } -------------------------------------------------------------------------------- /Journaler/app/src/main/kotlin/com/journaler/adapter/EntryAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.journaler.adapter 2 | 3 | import android.annotation.SuppressLint 4 | import android.content.Context 5 | import android.view.LayoutInflater 6 | import android.view.View 7 | import android.view.ViewGroup 8 | import android.widget.BaseAdapter 9 | import android.widget.TextView 10 | import com.journaler.R 11 | import com.journaler.model.Entry 12 | 13 | class EntryAdapter( 14 | private val ctx: Context, 15 | private val items: List 16 | ) : BaseAdapter() { 17 | 18 | @SuppressLint("InflateParams", "ViewHolder") 19 | override fun getView(p0: Int, p1: View?, p2: ViewGroup?): View { 20 | p1?.let { 21 | return p1 22 | } 23 | val inflater = LayoutInflater.from(ctx) 24 | val view = inflater.inflate(R.layout.adapter_entry, null) 25 | val label = view.findViewById(R.id.title) 26 | label.text = items[p0].title 27 | return view 28 | } 29 | 30 | override fun getItem(p0: Int): Entry = items[p0] 31 | 32 | override fun getItemId(p0: Int): Long = items[p0].id 33 | 34 | override fun getCount(): Int = items.size 35 | 36 | } -------------------------------------------------------------------------------- /Journaler/app/src/main/kotlin/com/journaler/api/BackendServiceHeaderMap.kt: -------------------------------------------------------------------------------- 1 | package com.journaler.api 2 | 3 | object BackendServiceHeaderMap { 4 | 5 | fun obtain(authorization: Boolean = false): Map { 6 | val map = mutableMapOf( 7 | Pair("Accept", "*/*"), 8 | Pair("Content-Type", "application/json; charset=UTF-8") 9 | ) 10 | if (authorization) { 11 | map["Authorization"] = "Bearer ${TokenManager.currentToken.token}" 12 | } 13 | return map 14 | } 15 | 16 | } -------------------------------------------------------------------------------- /Journaler/app/src/main/kotlin/com/journaler/api/BackendServiceRetrofit.kt: -------------------------------------------------------------------------------- 1 | package com.journaler.api 2 | 3 | import okhttp3.OkHttpClient 4 | import okhttp3.logging.HttpLoggingInterceptor 5 | import retrofit2.Retrofit 6 | import retrofit2.converter.gson.GsonConverterFactory 7 | import java.util.concurrent.TimeUnit 8 | 9 | object BackendServiceRetrofit { 10 | 11 | fun obtain( 12 | readTimeoutInSeconds: Long = 30, 13 | connectTimeoutInSeconds: Long = 30 14 | ): Retrofit { 15 | val loggingInterceptor = HttpLoggingInterceptor() 16 | loggingInterceptor.level = HttpLoggingInterceptor.Level.BODY 17 | return Retrofit.Builder() 18 | // .baseUrl("http://127.0.0.1/") 19 | .baseUrl("http://static.milosvasic.net/jsons/journaler/") 20 | .addConverterFactory(GsonConverterFactory.create()) 21 | .client( 22 | OkHttpClient 23 | .Builder() 24 | .addInterceptor(loggingInterceptor) 25 | .readTimeout(readTimeoutInSeconds, TimeUnit.SECONDS) 26 | .connectTimeout(connectTimeoutInSeconds, TimeUnit.SECONDS) 27 | .build() 28 | ) 29 | .build() 30 | } 31 | 32 | } -------------------------------------------------------------------------------- /Journaler/app/src/main/kotlin/com/journaler/api/JournalerApiToken.kt: -------------------------------------------------------------------------------- 1 | package com.journaler.api 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class JournalerApiToken( 6 | @SerializedName("id_token") val token: String, 7 | val expires: Long 8 | ) -------------------------------------------------------------------------------- /Journaler/app/src/main/kotlin/com/journaler/api/JournalerBackendService.kt: -------------------------------------------------------------------------------- 1 | package com.journaler.api 2 | 3 | import com.journaler.model.Note 4 | import com.journaler.model.Todo 5 | import retrofit2.Call 6 | import retrofit2.http.* 7 | 8 | interface JournalerBackendService { 9 | 10 | companion object { 11 | fun obtain(): JournalerBackendService { 12 | return BackendServiceRetrofit 13 | .obtain() 14 | .create(JournalerBackendService::class.java) 15 | } 16 | } 17 | 18 | @POST("authenticate") 19 | // @POST("user/authenticate") 20 | fun login( 21 | @HeaderMap headers: Map, 22 | @Body payload: UserLoginRequest 23 | ): Call 24 | 25 | @GET("notes") 26 | // @GET("entity/note") 27 | fun getNotes( 28 | @HeaderMap headers: Map 29 | ): Call> 30 | 31 | @GET("todos") 32 | // @GET("entity/todo") 33 | fun getTodos( 34 | @HeaderMap headers: Map 35 | ): Call> 36 | 37 | @PUT("entity/note") 38 | fun publishNotes( 39 | @HeaderMap headers: Map, 40 | @Body payload: List 41 | ): Call 42 | 43 | @PUT("entity/todo") 44 | fun publishTodos( 45 | @HeaderMap headers: Map, 46 | @Body payload: List 47 | ): Call 48 | 49 | @DELETE("entity/note") 50 | fun removeNotes( 51 | @HeaderMap headers: Map, 52 | @Body payload: List 53 | ): Call 54 | 55 | @DELETE("entity/todo") 56 | fun removeTodos( 57 | @HeaderMap headers: Map, 58 | @Body payload: List 59 | ): Call 60 | 61 | } -------------------------------------------------------------------------------- /Journaler/app/src/main/kotlin/com/journaler/api/TokenManager.kt: -------------------------------------------------------------------------------- 1 | package com.journaler.api 2 | 3 | object TokenManager { 4 | 5 | var currentToken = JournalerApiToken("", -1) 6 | 7 | } -------------------------------------------------------------------------------- /Journaler/app/src/main/kotlin/com/journaler/api/UserLoginRequest.kt: -------------------------------------------------------------------------------- 1 | package com.journaler.api 2 | 3 | data class UserLoginRequest( 4 | val username: String, 5 | val password: String 6 | ) -------------------------------------------------------------------------------- /Journaler/app/src/main/kotlin/com/journaler/database/Content.kt: -------------------------------------------------------------------------------- 1 | package com.journaler.database 2 | 3 | import android.content.ContentValues 4 | import android.location.Location 5 | import android.net.Uri 6 | import android.util.Log 7 | import com.github.salomonbrys.kotson.fromJson 8 | import com.google.gson.Gson 9 | import com.journaler.Journaler 10 | import com.journaler.model.* 11 | import com.journaler.provider.JournalerProvider 12 | 13 | object Content { 14 | 15 | private val gson = Gson() 16 | private val tag = "Content" 17 | 18 | val NOTE = object : Crud { 19 | override fun insert(what: Note): Long { 20 | val inserted = insert(listOf(what)) 21 | if (!inserted.isEmpty()) return inserted[0] 22 | return 0 23 | } 24 | 25 | override fun insert(what: Collection): List { 26 | val ids = mutableListOf() 27 | what.forEach { item -> 28 | val values = ContentValues() 29 | values.put(DbHelper.COLUMN_TITLE, item.title) 30 | values.put(DbHelper.COLUMN_MESSAGE, item.message) 31 | values.put(DbHelper.COLUMN_LOCATION, gson.toJson(item.location)) 32 | val uri = Uri.parse(JournalerProvider.URL_NOTE) 33 | val ctx = Journaler.ctx 34 | ctx?.let { 35 | val result = ctx.contentResolver.insert(uri, values) 36 | result?.let { 37 | try { 38 | ids.add(result.lastPathSegment.toLong()) 39 | } catch (e: Exception) { 40 | Log.e(tag, "Error: $e") 41 | } 42 | } 43 | } 44 | } 45 | return ids 46 | } 47 | 48 | override fun update(what: Note) = update(listOf(what)) 49 | 50 | override fun update(what: Collection): Int { 51 | var count = 0 52 | what.forEach { item -> 53 | val values = ContentValues() 54 | values.put(DbHelper.COLUMN_TITLE, item.title) 55 | values.put(DbHelper.COLUMN_MESSAGE, item.message) 56 | values.put(DbHelper.COLUMN_LOCATION, gson.toJson(item.location)) 57 | val uri = Uri.parse(JournalerProvider.URL_NOTE) 58 | val ctx = Journaler.ctx 59 | ctx?.let { 60 | count += ctx.contentResolver.update( 61 | uri, values, "_id = ?", arrayOf(item.id.toString()) 62 | ) 63 | } 64 | } 65 | return count 66 | } 67 | 68 | override fun delete(what: Note): Int = delete(listOf(what)) 69 | 70 | override fun delete(what: Collection): Int { 71 | var count = 0 72 | what.forEach { item -> 73 | val uri = Uri.parse(JournalerProvider.URL_NOTE) 74 | val ctx = Journaler.ctx 75 | ctx?.let { 76 | count += ctx.contentResolver.delete( 77 | uri, "_id = ?", arrayOf(item.id.toString()) 78 | ) 79 | } 80 | } 81 | return count 82 | } 83 | 84 | override fun select(args: Pair 85 | ): List = select(listOf(args)) 86 | 87 | override fun select(args: Collection>): List { 88 | val items = mutableListOf() 89 | val selection = StringBuilder() 90 | val selectionArgs = mutableListOf() 91 | args.forEach { arg -> 92 | selection.append("${arg.first} == ?") 93 | selectionArgs.add(arg.second) 94 | } 95 | val ctx = Journaler.ctx 96 | ctx?.let { 97 | val uri = Uri.parse(JournalerProvider.URL_NOTES) 98 | val cursor = ctx.contentResolver.query( 99 | uri, null, selection.toString(), selectionArgs.toTypedArray(), null 100 | ) 101 | while (cursor.moveToNext()) { 102 | val id = cursor.getLong(cursor.getColumnIndexOrThrow(DbHelper.ID)) 103 | val titleIdx = cursor.getColumnIndexOrThrow(DbHelper.COLUMN_TITLE) 104 | val title = cursor.getString(titleIdx) 105 | val messageIdx = cursor.getColumnIndexOrThrow(DbHelper.COLUMN_MESSAGE) 106 | val message = cursor.getString(messageIdx) 107 | val locationIdx = cursor.getColumnIndexOrThrow(DbHelper.COLUMN_LOCATION) 108 | val locationJson = cursor.getString(locationIdx) 109 | val location = gson.fromJson(locationJson) 110 | val note = Note(title, message, location) 111 | note.id = id 112 | items.add(note) 113 | } 114 | cursor.close() 115 | return items 116 | } 117 | return items 118 | } 119 | 120 | override fun selectAll(): List { 121 | val items = mutableListOf() 122 | val ctx = Journaler.ctx 123 | ctx?.let { 124 | val uri = Uri.parse(JournalerProvider.URL_NOTES) 125 | val cursor = ctx.contentResolver.query( 126 | uri, null, null, null, null 127 | ) 128 | while (cursor.moveToNext()) { 129 | val id = cursor.getLong(cursor.getColumnIndexOrThrow(DbHelper.ID)) 130 | val titleIdx = cursor.getColumnIndexOrThrow(DbHelper.COLUMN_TITLE) 131 | val title = cursor.getString(titleIdx) 132 | val messageIdx = cursor.getColumnIndexOrThrow(DbHelper.COLUMN_MESSAGE) 133 | val message = cursor.getString(messageIdx) 134 | val locationIdx = cursor.getColumnIndexOrThrow(DbHelper.COLUMN_LOCATION) 135 | val locationJson = cursor.getString(locationIdx) 136 | val location = gson.fromJson(locationJson) 137 | val note = Note(title, message, location) 138 | note.id = id 139 | items.add(note) 140 | } 141 | cursor.close() 142 | } 143 | return items 144 | } 145 | } 146 | 147 | val TODO = object : Crud { 148 | override fun insert(what: Todo): Long { 149 | val inserted = insert(listOf(what)) 150 | if (!inserted.isEmpty()) return inserted[0] 151 | return 0 152 | } 153 | 154 | override fun insert(what: Collection): List { 155 | val ids = mutableListOf() 156 | what.forEach { item -> 157 | val values = ContentValues() 158 | values.put(DbHelper.COLUMN_TITLE, item.title) 159 | values.put(DbHelper.COLUMN_MESSAGE, item.message) 160 | values.put(DbHelper.COLUMN_LOCATION, gson.toJson(item.location)) 161 | val uri = Uri.parse(JournalerProvider.URL_TODO) 162 | values.put(DbHelper.COLUMN_SCHEDULED, item.scheduledFor) 163 | val ctx = Journaler.ctx 164 | ctx?.let { 165 | val result = ctx.contentResolver.insert(uri, values) 166 | result?.let { 167 | try { 168 | ids.add(result.lastPathSegment.toLong()) 169 | } catch (e: Exception) { 170 | Log.e(tag, "Error: $e") 171 | } 172 | } 173 | } 174 | } 175 | return ids 176 | } 177 | 178 | override fun update(what: Todo) = update(listOf(what)) 179 | 180 | override fun update(what: Collection): Int { 181 | var count = 0 182 | what.forEach { item -> 183 | val values = ContentValues() 184 | values.put(DbHelper.COLUMN_TITLE, item.title) 185 | values.put(DbHelper.COLUMN_MESSAGE, item.message) 186 | values.put(DbHelper.COLUMN_LOCATION, gson.toJson(item.location)) 187 | val uri = Uri.parse(JournalerProvider.URL_TODO) 188 | values.put(DbHelper.COLUMN_SCHEDULED, item.scheduledFor) 189 | val ctx = Journaler.ctx 190 | ctx?.let { 191 | count += ctx.contentResolver.update( 192 | uri, values, "_id = ?", arrayOf(item.id.toString()) 193 | ) 194 | } 195 | } 196 | return count 197 | } 198 | 199 | override fun delete(what: Todo): Int = delete(listOf(what)) 200 | 201 | override fun delete(what: Collection): Int { 202 | var count = 0 203 | what.forEach { item -> 204 | val uri = Uri.parse(JournalerProvider.URL_TODO) 205 | val ctx = Journaler.ctx 206 | ctx?.let { 207 | count += ctx.contentResolver.delete( 208 | uri, "_id = ?", arrayOf(item.id.toString()) 209 | ) 210 | } 211 | } 212 | return count 213 | } 214 | 215 | override fun select(args: Pair): List = select(listOf(args)) 216 | 217 | override fun select(args: Collection>): List { 218 | val items = mutableListOf() 219 | val selection = StringBuilder() 220 | val selectionArgs = mutableListOf() 221 | args.forEach { arg -> 222 | selection.append("${arg.first} == ?") 223 | selectionArgs.add(arg.second) 224 | } 225 | val ctx = Journaler.ctx 226 | ctx?.let { 227 | val uri = Uri.parse(JournalerProvider.URL_TODOS) 228 | val cursor = ctx.contentResolver.query( 229 | uri, null, selection.toString(), selectionArgs.toTypedArray(), null 230 | ) 231 | while (cursor.moveToNext()) { 232 | val id = cursor.getLong(cursor.getColumnIndexOrThrow(DbHelper.ID)) 233 | val titleIdx = cursor.getColumnIndexOrThrow(DbHelper.COLUMN_TITLE) 234 | val title = cursor.getString(titleIdx) 235 | val messageIdx = cursor.getColumnIndexOrThrow(DbHelper.COLUMN_MESSAGE) 236 | val message = cursor.getString(messageIdx) 237 | val locationIdx = cursor.getColumnIndexOrThrow(DbHelper.COLUMN_LOCATION) 238 | val locationJson = cursor.getString(locationIdx) 239 | val location = gson.fromJson(locationJson) 240 | val scheduledForIdx = cursor.getColumnIndexOrThrow(DbHelper.COLUMN_SCHEDULED) 241 | val scheduledFor = cursor.getLong(scheduledForIdx) 242 | val todo = Todo(title, message, location, scheduledFor) 243 | todo.id = id 244 | items.add(todo) 245 | } 246 | cursor.close() 247 | } 248 | return items 249 | } 250 | 251 | override fun selectAll(): List { 252 | val items = mutableListOf() 253 | val ctx = Journaler.ctx 254 | ctx?.let { 255 | val uri = Uri.parse(JournalerProvider.URL_TODOS) 256 | val cursor = ctx.contentResolver.query( 257 | uri, null, null, null, null 258 | ) 259 | while (cursor.moveToNext()) { 260 | val id = cursor.getLong(cursor.getColumnIndexOrThrow(DbHelper.ID)) 261 | val titleIdx = cursor.getColumnIndexOrThrow(DbHelper.COLUMN_TITLE) 262 | val title = cursor.getString(titleIdx) 263 | val messageIdx = cursor.getColumnIndexOrThrow(DbHelper.COLUMN_MESSAGE) 264 | val message = cursor.getString(messageIdx) 265 | val locationIdx = cursor.getColumnIndexOrThrow(DbHelper.COLUMN_LOCATION) 266 | val locationJson = cursor.getString(locationIdx) 267 | val location = gson.fromJson(locationJson) 268 | val scheduledForIdx = cursor.getColumnIndexOrThrow(DbHelper.COLUMN_SCHEDULED) 269 | val scheduledFor = cursor.getLong(scheduledForIdx) 270 | val todo = Todo(title, message, location, scheduledFor) 271 | todo.id = id 272 | items.add(todo) 273 | } 274 | cursor.close() 275 | } 276 | return items 277 | } 278 | } 279 | 280 | } -------------------------------------------------------------------------------- /Journaler/app/src/main/kotlin/com/journaler/database/Crud.kt: -------------------------------------------------------------------------------- 1 | package com.journaler.database 2 | 3 | interface Crud where T : DbModel { 4 | 5 | companion object { 6 | val BROADCAST_ACTION = "com.journaler.broadcast.crud" 7 | val BROADCAST_EXTRAS_KEY_CRUD_OPERATION_RESULT = "crud_result" 8 | } 9 | 10 | fun insert(what: T): Long 11 | 12 | fun insert(what: Collection): List 13 | 14 | fun update(what: T): Int 15 | 16 | fun update(what: Collection): Int 17 | 18 | fun delete(what: T): Int 19 | 20 | fun delete(what: Collection): Int 21 | 22 | fun select(args: Pair): List 23 | 24 | fun select(args: Collection>): List 25 | 26 | fun selectAll(): List 27 | 28 | } -------------------------------------------------------------------------------- /Journaler/app/src/main/kotlin/com/journaler/database/DbHelper.kt: -------------------------------------------------------------------------------- 1 | package com.journaler.database 2 | 3 | import android.database.sqlite.SQLiteDatabase 4 | import android.database.sqlite.SQLiteOpenHelper 5 | import android.util.Log 6 | import com.journaler.Journaler 7 | 8 | 9 | class DbHelper(dbName: String, val version: Int) : SQLiteOpenHelper(Journaler.ctx, dbName, null, version) { 10 | 11 | companion object { 12 | val ID: String = "_id" 13 | val TABLE_TODOS = "todos" 14 | val TABLE_NOTES = "notes" 15 | val COLUMN_TITLE: String = "title" 16 | val COLUMN_MESSAGE: String = "message" 17 | val COLUMN_LOCATION: String = "location" 18 | val COLUMN_SCHEDULED: String = "scheduled" 19 | } 20 | 21 | private val tag = "DbHelper" 22 | 23 | private val createTableNotes = """ 24 | CREATE TABLE if not exists $TABLE_NOTES 25 | ( 26 | $ID integer PRIMARY KEY autoincrement, 27 | $COLUMN_TITLE text, 28 | $COLUMN_MESSAGE text, 29 | $COLUMN_LOCATION text 30 | ) 31 | """ 32 | 33 | private val createTableTodos = """ 34 | CREATE TABLE if not exists $TABLE_TODOS 35 | ( 36 | $ID integer PRIMARY KEY autoincrement, 37 | $COLUMN_TITLE text, 38 | $COLUMN_MESSAGE text, 39 | $COLUMN_SCHEDULED integer, 40 | $COLUMN_LOCATION text 41 | ) 42 | """ 43 | 44 | override fun onCreate(db: SQLiteDatabase) { 45 | Log.d(tag, "Database [ CREATING ]") 46 | db.execSQL(createTableNotes) 47 | db.execSQL(createTableTodos) 48 | Log.d(tag, "Database [ CREATED ]") 49 | } 50 | 51 | override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) { 52 | // Ignore for now. 53 | } 54 | 55 | } -------------------------------------------------------------------------------- /Journaler/app/src/main/kotlin/com/journaler/database/DbModel.kt: -------------------------------------------------------------------------------- 1 | package com.journaler.database 2 | 3 | abstract class DbModel { 4 | 5 | abstract var id: Long 6 | 7 | } -------------------------------------------------------------------------------- /Journaler/app/src/main/kotlin/com/journaler/execution/TaskExecutor.kt: -------------------------------------------------------------------------------- 1 | package com.journaler.execution 2 | 3 | import java.util.concurrent.BlockingQueue 4 | import java.util.concurrent.LinkedBlockingQueue 5 | import java.util.concurrent.ThreadPoolExecutor 6 | import java.util.concurrent.TimeUnit 7 | 8 | class TaskExecutor private constructor( 9 | corePoolSize: Int, 10 | maximumPoolSize: Int, 11 | workQueue: BlockingQueue? 12 | 13 | ) : ThreadPoolExecutor( 14 | corePoolSize, 15 | maximumPoolSize, 16 | 0L, 17 | TimeUnit.MILLISECONDS, 18 | workQueue 19 | ) { 20 | 21 | companion object { 22 | fun getInstance(capacity: Int): TaskExecutor { 23 | return TaskExecutor( 24 | capacity, 25 | capacity * 2, 26 | LinkedBlockingQueue() 27 | ) 28 | } 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /Journaler/app/src/main/kotlin/com/journaler/extension/animations.kt: -------------------------------------------------------------------------------- 1 | package com.journaler.extension 2 | 3 | import android.app.Activity 4 | import android.view.animation.Animation 5 | import android.view.animation.AnimationUtils 6 | 7 | 8 | fun Activity.getAnimation(animation: Int): Animation = AnimationUtils.loadAnimation(this, animation) -------------------------------------------------------------------------------- /Journaler/app/src/main/kotlin/com/journaler/fragment/BaseFragment.kt: -------------------------------------------------------------------------------- 1 | package com.journaler.fragment 2 | 3 | import android.os.Bundle 4 | import android.support.v4.app.Fragment 5 | import android.util.Log 6 | import android.view.LayoutInflater 7 | import android.view.View 8 | import android.view.ViewGroup 9 | 10 | 11 | abstract class BaseFragment : Fragment() { 12 | 13 | protected abstract val logTag : String 14 | 15 | protected abstract fun getLayout(): Int 16 | 17 | override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? { 18 | Log.d(logTag, "[ ON CREATE VIEW ]") 19 | return inflater?.inflate(getLayout(), container, false) 20 | } 21 | 22 | override fun onPause() { 23 | super.onPause() 24 | Log.v(logTag, "[ ON PAUSE ]") 25 | } 26 | 27 | override fun onResume() { 28 | super.onResume() 29 | Log.v(logTag, "[ ON RESUME ]") 30 | } 31 | 32 | override fun onDestroy() { 33 | super.onDestroy() 34 | Log.d(logTag, "[ ON DESTROY ]") 35 | } 36 | 37 | } -------------------------------------------------------------------------------- /Journaler/app/src/main/kotlin/com/journaler/fragment/ItemsFragment.kt: -------------------------------------------------------------------------------- 1 | package com.journaler.fragment 2 | 3 | import android.animation.AnimatorSet 4 | import android.animation.ObjectAnimator 5 | import android.app.Activity 6 | import android.app.AlertDialog 7 | import android.content.Intent 8 | import android.os.Bundle 9 | import android.support.design.widget.FloatingActionButton 10 | import android.util.Log 11 | import android.view.LayoutInflater 12 | import android.view.View 13 | import android.view.ViewGroup 14 | import android.view.animation.AccelerateInterpolator 15 | import android.view.animation.BounceInterpolator 16 | import android.widget.ListView 17 | import com.journaler.R 18 | import com.journaler.activity.NoteActivity 19 | import com.journaler.activity.TodoActivity 20 | import com.journaler.adapter.EntryAdapter 21 | import com.journaler.database.Content 22 | import com.journaler.execution.TaskExecutor 23 | import com.journaler.model.MODE 24 | import java.text.SimpleDateFormat 25 | import java.util.* 26 | 27 | 28 | class ItemsFragment : BaseFragment() { 29 | 30 | private val TODO_REQUEST = 1 31 | private val NOTE_REQUEST = 0 32 | private val executor = TaskExecutor.getInstance(5) 33 | 34 | override val logTag = "Items fragment" 35 | 36 | override fun getLayout() = R.layout.fragment_items 37 | 38 | override fun onCreateView( 39 | inflater: LayoutInflater?, 40 | container: ViewGroup?, 41 | savedInstanceState: Bundle? 42 | ): View? { 43 | 44 | val view = inflater?.inflate(getLayout(), container, false) 45 | val btn = view?.findViewById(R.id.new_item) 46 | 47 | btn?.setOnClickListener { 48 | 49 | animate(btn) 50 | 51 | val items = arrayOf( 52 | getString(R.string.todos), 53 | getString(R.string.notes) 54 | ) 55 | 56 | val builder = AlertDialog.Builder(this@ItemsFragment.context) 57 | .setTitle(R.string.choose_a_type) 58 | .setCancelable(true) 59 | .setOnCancelListener { 60 | animate(btn, false) 61 | } 62 | .setItems( 63 | items, 64 | { _, which -> 65 | when (which) { 66 | 0 -> { 67 | openCreateTodo() 68 | } 69 | 1 -> { 70 | openCreateNote() 71 | } 72 | else -> Log.e(logTag, "Unknown option selected [ $which ]") 73 | } 74 | } 75 | ) 76 | 77 | builder.show() 78 | } 79 | 80 | return view 81 | } 82 | 83 | override fun onResume() { 84 | super.onResume() 85 | val btn = view?.findViewById(R.id.new_item) 86 | btn?.let { 87 | animate(btn, false) 88 | } 89 | executor.execute { 90 | val notes = Content.NOTE.selectAll() 91 | val adapter = EntryAdapter(activity, notes) 92 | activity.runOnUiThread { 93 | view?.findViewById(R.id.items)?.adapter = adapter 94 | } 95 | } 96 | } 97 | 98 | override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { 99 | super.onActivityResult(requestCode, resultCode, data) 100 | when (requestCode) { 101 | TODO_REQUEST -> { 102 | if (resultCode == Activity.RESULT_OK) { 103 | Log.i(logTag, "We created new TODO.") 104 | } else { 105 | Log.w(logTag, "We didn't created new TODO.") 106 | } 107 | } 108 | NOTE_REQUEST -> { 109 | if (resultCode == Activity.RESULT_OK) { 110 | Log.i(logTag, "We created new note.") 111 | } else { 112 | Log.w(logTag, "We didn't created new note.") 113 | } 114 | } 115 | } 116 | } 117 | 118 | private fun animate(btn: FloatingActionButton, expand: Boolean = true) { 119 | val animation1 = ObjectAnimator.ofFloat(btn, "scaleX", if (expand) { 120 | 1.5f 121 | } else { 122 | 1.0f 123 | }) 124 | animation1.duration = 2000 125 | animation1.interpolator = BounceInterpolator() 126 | 127 | val animation2 = ObjectAnimator.ofFloat(btn, "scaleY", if (expand) { 128 | 1.5f 129 | } else { 130 | 1.0f 131 | }) 132 | animation2.duration = 2000 133 | animation2.interpolator = BounceInterpolator() 134 | 135 | val animation3 = ObjectAnimator.ofFloat(btn, "alpha", if (expand) { 136 | 0.3f 137 | } else { 138 | 1.0f 139 | }) 140 | animation3.duration = 500 141 | animation3.interpolator = AccelerateInterpolator() 142 | 143 | val set = AnimatorSet() 144 | set.play(animation1).with(animation2).before(animation3) 145 | set.start() 146 | } 147 | 148 | private fun openCreateNote() { 149 | val intent = Intent(context, NoteActivity::class.java) 150 | val data = Bundle() 151 | data.putInt(MODE.EXTRAS_KEY, MODE.CREATE.mode) 152 | intent.putExtras(data) 153 | startActivityForResult(intent, NOTE_REQUEST) 154 | } 155 | 156 | private fun openCreateTodo() { 157 | val date = Date(System.currentTimeMillis()) 158 | val dateFormat = SimpleDateFormat("MMM dd YYYY", Locale.ENGLISH) 159 | val timeFormat = SimpleDateFormat("MM:HH", Locale.ENGLISH) 160 | 161 | val intent = Intent(context, TodoActivity::class.java) 162 | val data = Bundle() 163 | data.putInt(MODE.EXTRAS_KEY, MODE.CREATE.mode) 164 | data.putString(TodoActivity.EXTRA_DATE, dateFormat.format(date)) 165 | data.putString(TodoActivity.EXTRA_TIME, timeFormat.format(date)) 166 | intent.putExtras(data) 167 | startActivityForResult(intent, TODO_REQUEST) 168 | } 169 | 170 | } -------------------------------------------------------------------------------- /Journaler/app/src/main/kotlin/com/journaler/fragment/ManualFragment.kt: -------------------------------------------------------------------------------- 1 | package com.journaler.fragment 2 | 3 | import com.journaler.R 4 | 5 | 6 | class ManualFragment : BaseFragment() { 7 | 8 | override val logTag = "Manual Fragment" 9 | 10 | override fun getLayout() = R.layout.fragment_manual 11 | 12 | } -------------------------------------------------------------------------------- /Journaler/app/src/main/kotlin/com/journaler/location/LocationProvider.kt: -------------------------------------------------------------------------------- 1 | package com.journaler.location 2 | 3 | import android.Manifest 4 | import android.content.Context 5 | import android.content.pm.PackageManager 6 | import android.location.Criteria 7 | import android.location.Location 8 | import android.location.LocationListener 9 | import android.location.LocationManager 10 | import android.os.Bundle 11 | import android.os.Looper 12 | import android.support.v4.app.ActivityCompat 13 | import android.util.Log 14 | import com.journaler.Journaler 15 | import java.lang.ref.WeakReference 16 | import java.util.* 17 | import java.util.concurrent.CopyOnWriteArrayList 18 | 19 | 20 | object LocationProvider { 21 | 22 | private val tag = "Location provider" 23 | private val listeners = CopyOnWriteArrayList>() 24 | 25 | private val locationListener = object : LocationListener { 26 | override fun onLocationChanged(location: Location) { 27 | Log.i( 28 | tag, 29 | String.format( 30 | Locale.ENGLISH, 31 | "Location [ lat: %s ][ long: %s ]", location.latitude, location.longitude 32 | ) 33 | ) 34 | val iterator = listeners.iterator() 35 | while (iterator.hasNext()) { 36 | val reference = iterator.next() 37 | val listener = reference.get() 38 | listener?.onLocationChanged(location) 39 | } 40 | } 41 | 42 | override fun onStatusChanged(provider: String, status: Int, extras: Bundle) { 43 | Log.d( 44 | tag, 45 | String.format(Locale.ENGLISH, "Status changed [ %s ][ %d ]", provider, status) 46 | ) 47 | val iterator = listeners.iterator() 48 | while (iterator.hasNext()) { 49 | val reference = iterator.next() 50 | val listener = reference.get() 51 | listener?.onStatusChanged(provider, status, extras) 52 | } 53 | } 54 | 55 | override fun onProviderEnabled(provider: String) { 56 | Log.i(tag, String.format("Provider [ %s ][ ENABLED ]", provider)) 57 | val iterator = listeners.iterator() 58 | while (iterator.hasNext()) { 59 | val reference = iterator.next() 60 | val listener = reference.get() 61 | listener?.onProviderEnabled(provider) 62 | } 63 | } 64 | 65 | override fun onProviderDisabled(provider: String) { 66 | Log.i(tag, String.format("Provider [ %s ][ ENABLED ]", provider)) 67 | val iterator = listeners.iterator() 68 | while (iterator.hasNext()) { 69 | val reference = iterator.next() 70 | val listener = reference.get() 71 | listener?.onProviderDisabled(provider) 72 | } 73 | } 74 | } 75 | 76 | fun subscribe(subscriber: LocationListener): Boolean { 77 | val result = doSubscribe(subscriber) 78 | turnOnLocationListening() 79 | return result 80 | } 81 | 82 | fun unsubscribe(subscriber: LocationListener): Boolean { 83 | val result = doUnsubscribe(subscriber) 84 | if (listeners.isEmpty()) { 85 | turnOffLocationListening() 86 | } 87 | return result 88 | } 89 | 90 | private fun turnOnLocationListening() { 91 | Log.v(tag, "We are about to turn on location listening.") 92 | val ctx = Journaler.ctx 93 | if (ctx != null) { 94 | Log.v(tag, "We are about to check location permissions.") 95 | val permissionsOk = ActivityCompat.checkSelfPermission(ctx, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(ctx, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED 96 | if (!permissionsOk) { 97 | throw IllegalStateException("Permissions required [ ACCESS_FINE_LOCATION ][ ACCESS_COARSE_LOCATION ]") 98 | } 99 | Log.v(tag, "Location permissions are ok. We are about to request location changes.") 100 | val locationManager = ctx.getSystemService(Context.LOCATION_SERVICE) as LocationManager 101 | 102 | val criteria = Criteria() 103 | criteria.accuracy = Criteria.ACCURACY_FINE 104 | criteria.powerRequirement = Criteria.POWER_HIGH 105 | criteria.isAltitudeRequired = false 106 | criteria.isBearingRequired = false 107 | criteria.isSpeedRequired = false 108 | criteria.isCostAllowed = true 109 | 110 | locationManager.requestLocationUpdates( 111 | 1000, 1F, criteria, locationListener, Looper.getMainLooper() 112 | ) 113 | } else { 114 | Log.e(tag, "No application context available.") 115 | } 116 | } 117 | 118 | private fun turnOffLocationListening() { 119 | Log.v(tag, "We are about to turn off location listening.") 120 | val ctx = Journaler.ctx 121 | if (ctx != null) { 122 | val locationManager = ctx.getSystemService(Context.LOCATION_SERVICE) as LocationManager 123 | locationManager.removeUpdates(locationListener) 124 | } else { 125 | Log.e(tag, "No application context available.") 126 | } 127 | } 128 | 129 | private fun doSubscribe(listener: LocationListener): Boolean { 130 | val iterator = listeners.iterator() 131 | while (iterator.hasNext()) { 132 | val reference = iterator.next() 133 | val refListener = reference.get() 134 | if (refListener != null && refListener === listener) { 135 | Log.v(tag, "Already subscribed: " + listener) 136 | return false 137 | } 138 | } 139 | listeners.add(WeakReference(listener)) 140 | Log.v(tag, "Subscribed, subscribers count: " + listeners.size) 141 | return true 142 | } 143 | 144 | private fun doUnsubscribe(listener: LocationListener): Boolean { 145 | var result = true 146 | val iterator = listeners.iterator() 147 | while (iterator.hasNext()) { 148 | val reference = iterator.next() 149 | val refListener = reference.get() 150 | if (refListener != null && refListener === listener) { 151 | val success = listeners.remove(reference) 152 | if (!success) { 153 | Log.w(tag, "Couldn't un subscribe, subscribers count: " + listeners.size) 154 | } else { 155 | Log.v(tag, "Un subscribed, subscribers count: " + listeners.size) 156 | } 157 | if (result) { 158 | result = success 159 | } 160 | } 161 | } 162 | return result 163 | } 164 | 165 | } -------------------------------------------------------------------------------- /Journaler/app/src/main/kotlin/com/journaler/model/Entry.kt: -------------------------------------------------------------------------------- 1 | package com.journaler.model 2 | 3 | import android.location.Location 4 | import com.journaler.database.DbModel 5 | 6 | 7 | abstract class Entry( 8 | var title: String, 9 | var message: String, 10 | var location: Location 11 | ) : DbModel() -------------------------------------------------------------------------------- /Journaler/app/src/main/kotlin/com/journaler/model/MODE.kt: -------------------------------------------------------------------------------- 1 | package com.journaler.model 2 | 3 | 4 | enum class MODE(val mode: Int) { 5 | CREATE(0), 6 | EDIT(1), 7 | VIEW(2); 8 | 9 | companion object { 10 | val EXTRAS_KEY = "MODE" 11 | 12 | fun getByValue(value: Int): MODE { 13 | values().forEach { 14 | item -> 15 | if (item.mode == value) { 16 | return item 17 | } 18 | } 19 | return VIEW 20 | } 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /Journaler/app/src/main/kotlin/com/journaler/model/Note.kt: -------------------------------------------------------------------------------- 1 | package com.journaler.model 2 | 3 | import android.location.Location 4 | import android.os.Parcel 5 | import android.os.Parcelable 6 | 7 | class Note( 8 | title: String, 9 | message: String, 10 | location: Location 11 | ) : Entry( 12 | title, 13 | message, 14 | location 15 | ), Parcelable { 16 | 17 | override var id = 0L 18 | 19 | constructor(parcel: Parcel) : this( 20 | parcel.readString(), 21 | parcel.readString(), 22 | parcel.readParcelable(Location::class.java.classLoader) 23 | ) { 24 | id = parcel.readLong() 25 | } 26 | 27 | override fun writeToParcel(parcel: Parcel, flags: Int) { 28 | parcel.writeString(title) 29 | parcel.writeString(message) 30 | parcel.writeParcelable(location, 0) 31 | parcel.writeLong(id) 32 | } 33 | 34 | override fun describeContents(): Int = 0 35 | 36 | companion object CREATOR : Parcelable.Creator { 37 | override fun createFromParcel(parcel: Parcel): Note = Note(parcel) 38 | 39 | override fun newArray(size: Int): Array = arrayOfNulls(size) 40 | } 41 | 42 | } -------------------------------------------------------------------------------- /Journaler/app/src/main/kotlin/com/journaler/model/Todo.kt: -------------------------------------------------------------------------------- 1 | package com.journaler.model 2 | 3 | import android.location.Location 4 | 5 | class Todo( 6 | title: String, 7 | message: String, 8 | location: Location, 9 | var scheduledFor: Long 10 | ) : Entry( 11 | title, 12 | message, 13 | location 14 | ) { 15 | 16 | override var id = 0L 17 | 18 | } -------------------------------------------------------------------------------- /Journaler/app/src/main/kotlin/com/journaler/navigation/NavigationDrawerAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.journaler.navigation 2 | 3 | import android.content.Context 4 | import android.util.Log 5 | import android.view.LayoutInflater 6 | import android.view.View 7 | import android.view.ViewGroup 8 | import android.widget.BaseAdapter 9 | import android.widget.Button 10 | import android.widget.LinearLayout 11 | import com.journaler.R 12 | 13 | 14 | class NavigationDrawerAdapter( 15 | val ctx: Context, 16 | val items: List 17 | ) : BaseAdapter() { 18 | 19 | private val tag = "Nav. drw. adptr." 20 | 21 | override fun getView(position: Int, v: View?, group: ViewGroup?): View { 22 | val inflater = LayoutInflater.from(ctx) 23 | var view = v 24 | if (view == null) { 25 | view = inflater.inflate( 26 | R.layout.adapter_navigation_drawer, null 27 | ) as LinearLayout 28 | } 29 | 30 | val item = items[position] 31 | val title = view.findViewById