├── .gitignore ├── .idea ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── encodings.xml ├── gradle.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── .settings └── org.eclipse.jdt.core.prefs ├── AndroidManifest.xml ├── CONTRIBUTING.rst ├── Gfx ├── .DS_Store └── Smartphone │ └── .DS_Store ├── LICENSE ├── README.rst ├── VERSION ├── VERSIONING ├── app ├── .gitignore ├── app.iml ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── org │ │ └── aykit │ │ └── MyOwnNotes │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── ic_launcher-web.png │ ├── java │ │ └── org │ │ │ └── aykit │ │ │ └── MyOwnNotes │ │ │ ├── activities │ │ │ ├── LoginActivity.java │ │ │ ├── NoteDetailActivity.java │ │ │ └── NoteListActivity.java │ │ │ ├── adapter │ │ │ ├── DividerItemDecoration.java │ │ │ └── NotesListAdapter.java │ │ │ ├── asynctasks │ │ │ └── SyncNotesAsyncTask.java │ │ │ ├── database │ │ │ ├── NoteColumns.java │ │ │ ├── NotesDatabase.java │ │ │ ├── NotesProvider.java │ │ │ └── model │ │ │ │ └── Note.java │ │ │ ├── fragments │ │ │ ├── NoteDetailFragment.java │ │ │ └── NoteListFragment.java │ │ │ └── helpers │ │ │ ├── LegacyImporter.java │ │ │ └── Settings.java │ └── res │ │ ├── anim │ │ └── slide_in_right.xml │ │ ├── drawable-hdpi │ │ └── logo.png │ │ ├── drawable-ldpi │ │ └── logo.png │ │ ├── drawable-mdpi │ │ └── logo.png │ │ ├── drawable-xhdpi │ │ └── logo.png │ │ ├── drawable-xxhdpi │ │ └── logo.png │ │ ├── drawable-xxxhdpi │ │ └── logo.png │ │ ├── drawable │ │ ├── ic_add_24dp.xml │ │ ├── ic_cloud_upload_24dp.xml │ │ ├── ic_delete_24dp.xml │ │ └── ic_exit_to_app_24dp.xml │ │ ├── layout-sw600dp │ │ └── activity_note_list.xml │ │ ├── layout │ │ ├── activity_login.xml │ │ ├── activity_note_app_bar.xml │ │ ├── activity_note_detail.xml │ │ ├── activity_note_list.xml │ │ ├── drawer_header.xml │ │ ├── fragment_note_detail.xml │ │ ├── fragment_note_list.xml │ │ └── row_note.xml │ │ ├── menu │ │ ├── main_nav.xml │ │ └── note_detail.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-v21 │ │ └── styles.xml │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── setup.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── org │ └── aykit │ └── MyOwnNotes │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── myownnotes-android.iml ├── project.properties ├── res ├── layout │ ├── activity_note_list.xml │ ├── activity_note_single.xml │ ├── activity_note_single_textview.xml │ ├── activity_settings.xml │ └── note_listview_row.xml ├── values-de │ └── strings.xml ├── values-es │ └── strings.xml ├── values-eu │ └── strings.xml ├── values-fr │ └── strings.xml ├── values-it │ └── strings.xml ├── values-sr │ └── strings.xml ├── values-sw600dp │ └── dimens.xml ├── values-tr │ └── strings.xml └── values │ └── strings.xml ├── settings.gradle └── src └── org └── aykit └── owncloud_notes ├── NoteListActivity.java ├── NoteSingleActivity.java └── SettingsActivity.java /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/android 3 | 4 | ### Android ### 5 | # Built application files 6 | *.apk 7 | *.ap_ 8 | 9 | # Files for the ART/Dalvik VM 10 | *.dex 11 | 12 | # Java class files 13 | *.class 14 | 15 | # Generated files 16 | bin/ 17 | gen/ 18 | out/ 19 | 20 | # Gradle files 21 | .gradle/ 22 | build/ 23 | 24 | # Local configuration file (sdk path, etc) 25 | local.properties 26 | 27 | # Proguard folder generated by Eclipse 28 | proguard/ 29 | 30 | # Log Files 31 | *.log 32 | 33 | # Android Studio Navigation editor temp files 34 | .navigation/ 35 | 36 | # Android Studio captures folder 37 | captures/ 38 | 39 | # Intellij 40 | *.iml 41 | .idea/workspace.xml 42 | .idea/tasks.xml 43 | .idea/libraries 44 | 45 | # Keystore files 46 | *.jks 47 | 48 | # External native build folder generated in Android Studio 2.2 and later 49 | .externalNativeBuild 50 | 51 | ### Android Patch ### 52 | gen-external-apklibs 53 | 54 | # End of https://www.gitignore.io/api/android -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 3 | org.eclipse.jdt.core.compiler.compliance=1.6 4 | org.eclipse.jdt.core.compiler.source=1.6 5 | -------------------------------------------------------------------------------- /AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 9 | 10 | 11 | 12 | 13 | 19 | 24 | 25 | 29 | 30 | 34 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 49 | 50 | 55 | 56 | 60 | 61 | 66 | 67 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /CONTRIBUTING.rst: -------------------------------------------------------------------------------- 1 | ************ 2 | Contributing 3 | ************ 4 | 5 | If you want to contribute, you are more than welcome. There is only one thing you should keep in mind: 6 | 7 | 1. **Always write code in the development branch. The master branch is for package maintainers only. We will not accept pull requests based on the master branch.** 8 | 2. **!!! Your submissions will be GPL3. If you are not ok with this, don't submit your code !!!** 9 | 10 | That's it. If you have no idea where to help out: 11 | 12 | * check the issues list, 13 | * write a translation, 14 | * proof read code, 15 | * proof read translations 16 | 17 | Together we are be able to write a really good application for all of us. 18 | 19 | 20 | How to submit bugs / issues 21 | =========================== 22 | Bugs are nasty and we hate them as much as you do. Whenever you submit an issue, provide the following information. This is not to annoy you! Without that information, we don't know where to look: 23 | 24 | Provide all details concerning your software environment 25 | -------------------------------------------------------- 26 | * ownCloud server version: https://yourcloud/index.php/settings/admin 27 | * ownCloud encryption plugin installed and enabled and if so, the version 28 | * Android / CM / whateverdistroyouhave Version 29 | 30 | Provide all details concerning the specific issue 31 | ------------------------------------------------- 32 | * Update My Own Notes to latest version. If you are using F-Droid, that may take a day or two. Look at `our git repository`_ and browse the tags to see which version is the newest one. 33 | * Enable extensive LogCat messages under "Settings/Debug" in MyOwnNotes. 34 | * Submit the crash report on your phone. This way we can determine the problem in the google developers console (please be aware that you are sending the crash report to google). 35 | * If you don't want to send data to google: send us the Output of `logcat`_ 36 | * Or look at /data/anr/traces.txt on your device and submit the crash log 37 | 38 | 39 | How to submit feature requests 40 | ============================== 41 | New features are great, but feature requests can be annoying as hell. Here is how to write feature requests that may be fulfilled sometimes: 42 | 43 | - Ask yourself if you really need this feature. 44 | - Look into github. Has someone else submitted the same feature request? Please look at the closed issues as well! 45 | - Ask yourself why you need this feature although no one else asked for it. 46 | - Look at the app. Maybe the feature is already in there! If it's there but it took you too long to find it, please submit an issue. It means that our UX design is bad. 47 | - Are you the only person in the world who may need this feature? E.g. "I invented a new format and My Own Notes should support it." 48 | - We ignore every feature request not provided in Gherkin. See below for how to correctly submit Gherkin style feature requests. 49 | 50 | 51 | Gherkin? 52 | -------- 53 | Gerkhin is a language for application testing. When you submit your feature request in Gherkin, you make sure that your feature can be automatically tested when it arrives. Big plus for code quality! 54 | 55 | Take a look at `Gherkin documentation`_ for how-to write it. Here is an `Real World Gherkin Example`_: 56 | 57 | .. code-block:: cucumber 58 | 59 | Feature: Share with My Own Notes 60 | Text in other applications should have a Share-With My Own notes option. 61 | This creates a new note where the content of the new note is the text selected 62 | in the application. 63 | 64 | Scenario: Showing Share with My Own Notes option 65 | Given that there is text selected in any application not being My Own Notes 66 | When I press on the system wide Share-To Button 67 | Then I see the option "Share with My Own Notes" 68 | 69 | Scenario: Sharing text with My Own Notes 70 | Given that there is text selected in any application not being My Own Notes 71 | And Share-To has been pressed 72 | When I select Share With My Own Notes 73 | Then the application My Own Notes is opened 74 | And a new note is created 75 | And the selected text is the text of the new note 76 | 77 | 78 | How to submit translations 79 | ========================== 80 | 81 | New Translations 82 | ---------------- 83 | To avoid incomplete translations, new ones will be added in a seperate branch. Please submit an issue and we will add this branch for you. Be aware that all new translations should be proof read by at least one other user before being merged into the development branch. If no one complains about the translation in a week's time, the pull request is treated as being ok and will be merged. 84 | 85 | Improving existing translations 86 | ------------------------------- 87 | Just do your edits in the development branch. 88 | 89 | Proof read translations 90 | ----------------------- 91 | This is crucial. You really need to proof read your translations! 92 | 93 | Critical Translations 94 | --------------------- 95 | Translation requests (issues) blocking critical bugfixes will be marked as critical. If they are not provided 48 hours later, the release containing critical bugfixes will be distributed nevertheless. The translation request will be marked "critical, delayed" and should be treated with uttermost priority. 96 | 97 | 98 | How to submit code 99 | ================== 100 | 101 | 1. Check 102 | 2. Double Check 103 | 3. Test 104 | 4. Test with a second device 105 | 5. Beautify 106 | 6. Repeat Steps 1-5 as often as necessary 107 | 7. `Submit`_ 108 | 109 | Notes regarding the workflow 110 | ---------------------------- 111 | * New entries in values/strings.xml have to be added in all other languages as well. That way, translators will be able to find new strings easily. Additionally, the code does not break if a string is forgotten. 112 | 113 | 114 | License 115 | ======= 116 | One last note: This application is licensed under `GPL 3`_. All code submitted will be released under the same license. For more information, look at the LICENSE file. 117 | 118 | .. _Gherkin documentation: https://github.com/cucumber/cucumber/wiki/Gherkin 119 | .. _GPL 3: http://www.gnu.org/copyleft/gpl.html 120 | .. _logcat: http://wiki.cyanogenmod.org/w/Doc:_debugging_with_logcat 121 | .. _network graph: https://github.com/aykit/myownnotes-android/network 122 | .. _our git repository: https://github.com/aykit/myownnotes-android 123 | .. _Real World Gherkin Example: https://github.com/aykit/myownnotes-android/issues/89 124 | .. _Submit: https://help.github.com/categories/63/articles 125 | -------------------------------------------------------------------------------- /Gfx/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aykit/MyOwnNotes/225efdb1f483df815ef467f2351747b82b786c54/Gfx/.DS_Store -------------------------------------------------------------------------------- /Gfx/Smartphone/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aykit/MyOwnNotes/225efdb1f483df815ef467f2351747b82b786c54/Gfx/Smartphone/.DS_Store -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ============== 2 | MyOwnNotes 2 3 | ============== 4 | 5 | This project is no longer maintained 6 | ==================================== 7 | 8 | If you want to become maintainer, post here: https://github.com/aykit/MyOwnNotes/issues/163 9 | 10 | 11 | Information 12 | =========== 13 | 14 | This is Version 2, a complete rewrite of MyOwnNotes. This application does not depend on the server side owncloud notes app anymore. However, it does depend on the `ownCloud android application`_ for authentication. 15 | 16 | MyOwnNotes 2 has not been released on the official android store yet. Release will follow in the next couple of weeks. Feel free to add issues after consulting the CONTRIBUTING.rst file. 17 | 18 | 19 | The right README 20 | ================ 21 | Make sure you read the README of the appropriate branch. The one you are seeing right now might not be the one you are looking for. 22 | 23 | 24 | Contribute 25 | ========== 26 | Please take a look at CONTRIBUTING.rst for information regarding extending this application. 27 | 28 | The information there applies to **translations** and **issue submissions** as well. 29 | 30 | 31 | Install 32 | ======= 33 | to use this app you will need: 34 | 35 | + ownCloud server Version >= 9.0 see `ownCloud Docs`_ 36 | + Mobile device using Android Version >= 5.0 37 | 38 | 39 | .. _`FAQ`: 40 | 41 | FAQ 42 | === 43 | 44 | How do I get ownCloud? 45 | ---------------------- 46 | 47 | See the `ownCloud website`_ for more information. 48 | 49 | 50 | What do you think about nextcloud? 51 | ---------------------------------- 52 | 53 | We did not think much about it. That was easy, thanks to tobiasKaminsky_. For reference, a good insight into the split is probably `carla's blog entry`_. 54 | 55 | 56 | Contributors of Version 2.x 57 | =========================== 58 | 59 | Maintainer 60 | ---------- 61 | * `aykit`_ : Non-profit organisation supporting art, culture and science 62 | 63 | Developers 64 | ---------- 65 | * `kh0r`_: Main author of Revision 2.0 66 | * tobiasKaminsky_: NextCloud support 67 | * nobse_: Small changes 68 | 69 | 70 | Contributors of Version 1.x 71 | =========================== 72 | 73 | Here is a list of all contributers, including ourselves. A big thank you to all the people who help developing this application. Please be aware that all contributions are GPL3 licensed. 74 | 75 | Maintainer 76 | ---------- 77 | * `aykit`_ : Non-profit organisation supporting art, culture and science 78 | 79 | Developers 80 | ---------- 81 | * Main Developer: `steppenhahn`_ 82 | 83 | Translators 84 | ----------- 85 | * French: `flo1`_ , `gityeti`_ 86 | * Serbian: `pejakm`_ 87 | * Spanish: `tmelikoff`_ 88 | * Turkish: `wakeup`_ 89 | 90 | Testers 91 | ------- 92 | Unfortunately, we are not able to greet everyone in person. Without your feedback, we wouldn't be able to improve My Own Notes. Please keep up testing and providing valuable information regarding your issues. We promise we will keep up fixing and improving as best as we can. 93 | 94 | 95 | License 96 | ======= 97 | My Own Notes and all contributions are licensed as `GPL3`_ 98 | 99 | 100 | .. _CheapSSLsecurity: https://cheapsslsecurity.com 101 | .. _carla's blog entry: https://web.archive.org/web/20170404145931/http://carlaschroder.com/nextcloud-is-dirty-deal/ 102 | .. _contact us: mailto:z-o48hohw4l9qla@ay.vc 103 | .. _Entwicklerbier.org: https://blog.entwicklerbier.org/2014/05/securing-the-internet-of-things-how-about-securing-the-internet-first/ 104 | .. _google dev: https://code.google.com/p/android/issues/detail?id=11231#c107 105 | .. _google summer of code: https://summerofcode.withgoogle.com/organizations/6453536335331328/ 106 | .. _GPL3: https://github.com/aykit/myownnotes-android/blob/master/LICENSE 107 | .. _My Own Notes App: https://github.com/aykit/myownnotes-android 108 | .. _My Own Notes Website: https://aykit.org/sites/myownnotes.html 109 | .. _ownCloud android application: https://play.google.com/store/apps/details?id=com.owncloud.android 110 | .. _ownCloud Docs: http://doc.owncloud.org/ 111 | .. _ownCloud website: https://owncloud.org/install/ 112 | .. _SSL Labs: https://www.ssllabs.com/ssltest/ 113 | .. _StartSSL: https://startssl.com 114 | .. _Setting up owncloud on Speed: https://blog.entwicklerbier.org/2014/06/setting-up-owncloud-on-speed/ 115 | 116 | .. _aykit: https://aykit.org 117 | .. _flo1: https://github.com/flo1 118 | .. _gityeti: https://github.com/gityeti 119 | .. _pejakm: https://github.com/pejakm 120 | .. _steppenhahn: https://github.com/steppenhahn 121 | .. _tmelikoff: http://https://github.com/tmelikoff 122 | .. _wakeup: https://github.com/wakeup 123 | .. _kh0r: https://github.com/kh0r 124 | .. _tobiasKaminsky: https://github.com/tobiasKaminsky 125 | .. _nobse: https://github.com/nobse 126 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | Look at AndroidManifest.xml for version information -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/app.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'com.neenbedankt.android-apt' 3 | 4 | android { 5 | compileSdkVersion 23 6 | buildToolsVersion "23.0.2" 7 | defaultConfig { 8 | applicationId "org.aykit.MyOwnNotes" 9 | minSdkVersion 16 10 | targetSdkVersion 23 11 | versionCode 12 12 | versionName '2.1.1' 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | productFlavors { 21 | local { 22 | applicationId 'org.aykit.MyOwnNotes.local' 23 | resValue "string", "app_name", "MyOwnNotes[Local]" 24 | } 25 | store { 26 | applicationId 'org.aykit.MyOwnNotes' 27 | resValue "string", "app_name", "MyOwnNotes" 28 | } 29 | } 30 | packagingOptions { 31 | exclude 'META-INF/LICENSE.txt' 32 | exclude 'META-INF/NOTICE.txt' 33 | } 34 | } 35 | 36 | dependencies { 37 | compile fileTree(dir: 'libs', include: ['*.jar']) 38 | testCompile 'junit:junit:4.12' 39 | compile 'com.android.support:appcompat-v7:23.1.1' 40 | compile 'com.android.support:support-v4:23.1.1' 41 | compile 'com.android.support:design:23.1.1' 42 | compile 'com.android.support:recyclerview-v7:23.1.1' 43 | apt 'net.simonvt.schematic:schematic-compiler:0.6.3' 44 | compile 'net.simonvt.schematic:schematic:0.6.3' 45 | compile 'com.jakewharton:butterknife:7.0.1' 46 | compile 'com.github.owncloud:android-library:oc-android-library-0.9.8' 47 | compile 'commons-io:commons-io:2.4' 48 | 49 | compile('com.afollestad.material-dialogs:core:0.8.1.0@aar') { 50 | transitive = true 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /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/mklepp/Documents/android-sdks/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 | -------------------------------------------------------------------------------- /app/src/androidTest/java/org/aykit/MyOwnNotes/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package org.aykit.MyOwnNotes; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 14 | 15 | 18 | 19 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 33 | 36 | 37 | 38 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /app/src/main/ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aykit/MyOwnNotes/225efdb1f483df815ef467f2351747b82b786c54/app/src/main/ic_launcher-web.png -------------------------------------------------------------------------------- /app/src/main/java/org/aykit/MyOwnNotes/activities/LoginActivity.java: -------------------------------------------------------------------------------- 1 | package org.aykit.MyOwnNotes.activities; 2 | 3 | import android.Manifest; 4 | import android.accounts.Account; 5 | import android.accounts.AccountManager; 6 | import android.content.DialogInterface; 7 | import android.content.Intent; 8 | import android.content.pm.PackageManager; 9 | import android.net.Uri; 10 | import android.os.AsyncTask; 11 | import android.os.Bundle; 12 | import android.preference.PreferenceManager; 13 | import android.support.annotation.NonNull; 14 | import android.support.design.widget.CoordinatorLayout; 15 | import android.support.design.widget.FloatingActionButton; 16 | import android.support.design.widget.Snackbar; 17 | import android.support.v4.app.ActivityCompat; 18 | import android.support.v4.content.ContextCompat; 19 | import android.support.v7.app.AlertDialog; 20 | import android.support.v7.app.AppCompatActivity; 21 | import android.support.v7.widget.Toolbar; 22 | import android.text.InputType; 23 | import android.view.View; 24 | import android.widget.AdapterView; 25 | import android.widget.ArrayAdapter; 26 | import android.widget.Button; 27 | import android.widget.ListView; 28 | import android.widget.ProgressBar; 29 | 30 | import com.afollestad.materialdialogs.DialogAction; 31 | import com.afollestad.materialdialogs.MaterialDialog; 32 | import com.owncloud.android.lib.common.OwnCloudClient; 33 | import com.owncloud.android.lib.common.OwnCloudClientFactory; 34 | import com.owncloud.android.lib.common.OwnCloudCredentialsFactory; 35 | 36 | import org.aykit.MyOwnNotes.R; 37 | import org.aykit.MyOwnNotes.helpers.Settings; 38 | 39 | import java.util.ArrayList; 40 | import java.util.Arrays; 41 | import java.util.List; 42 | 43 | import butterknife.Bind; 44 | import butterknife.ButterKnife; 45 | 46 | public class LoginActivity extends AppCompatActivity implements AdapterView.OnItemClickListener { 47 | 48 | private static final int PERMISSIONS_REQUEST_GET_ACCOUNTS = 1; 49 | 50 | private static final String OWNCLOUD_PACKAGE_NAME = "com.owncloud.android"; 51 | private static final String NEXTCLOUD_PACKAGE_NAME = "com.nextcloud.client"; 52 | 53 | @Bind(R.id.coordinatorlayout) 54 | CoordinatorLayout coordinatorLayout; 55 | 56 | @Bind(R.id.toolbar) 57 | Toolbar toolbar; 58 | 59 | @Bind(android.R.id.list) 60 | ListView listView; 61 | 62 | @Bind(android.R.id.empty) 63 | Button emptyButton; 64 | 65 | @Bind(android.R.id.progress) 66 | ProgressBar progressBar; 67 | 68 | @Bind(R.id.button_add) 69 | FloatingActionButton addButton; 70 | 71 | @Override 72 | protected void onCreate(Bundle savedInstanceState) { 73 | super.onCreate(savedInstanceState); 74 | setContentView(R.layout.activity_login); 75 | ButterKnife.bind(this); 76 | 77 | toolbar.setTitle(getTitle()); 78 | 79 | listView.setEmptyView(emptyButton); 80 | listView.setOnItemClickListener(this); 81 | 82 | addButton.setOnClickListener(new View.OnClickListener() { 83 | @Override 84 | public void onClick(View v) { 85 | 86 | new MaterialDialog.Builder(LoginActivity.this) 87 | .title(R.string.dialog_add_account_title) 88 | .content(R.string.dialog_add_account_message) 89 | .positiveText(R.string.dialog_add_account_owncloud) 90 | .negativeText(R.string.dialog_add_account_nextcloud) 91 | .onPositive(new MaterialDialog.SingleButtonCallback() { 92 | @Override 93 | public void onClick(MaterialDialog materialDialog, DialogAction dialogAction) { 94 | openCloudApp(OWNCLOUD_PACKAGE_NAME); 95 | } 96 | }) 97 | .onNegative(new MaterialDialog.SingleButtonCallback() { 98 | @Override 99 | public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) { 100 | openCloudApp(NEXTCLOUD_PACKAGE_NAME); 101 | } 102 | }) 103 | .show(); 104 | 105 | } 106 | }); 107 | } 108 | 109 | @Override 110 | protected void onResume() { 111 | super.onResume(); 112 | 113 | checkGetAccountPermission(); 114 | } 115 | 116 | private void queryPassword(final Account account) { 117 | 118 | new MaterialDialog.Builder(this) 119 | .title(R.string.settings_text_password) 120 | .content(account.name) 121 | .inputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD) 122 | .input(R.string.settings_text_password_hint, 0, new MaterialDialog.InputCallback() { 123 | @Override 124 | public void onInput(MaterialDialog dialog, CharSequence input) { 125 | String password = input.toString(); 126 | PreferenceManager.getDefaultSharedPreferences(LoginActivity.this).edit().putString(Settings.PREF_ACCOUNT_PASSWORD, password).apply(); 127 | 128 | useAccount(account); 129 | } 130 | }) 131 | .negativeText(android.R.string.cancel) 132 | .onNegative(new MaterialDialog.SingleButtonCallback() { 133 | @Override 134 | public void onClick(MaterialDialog materialDialog, DialogAction dialogAction) { 135 | 136 | PreferenceManager.getDefaultSharedPreferences(LoginActivity.this).edit().remove(Settings.PREF_ACCOUNT_PASSWORD).apply(); 137 | } 138 | }) 139 | .show(); 140 | 141 | } 142 | 143 | /* 144 | * starts owncloud or nextcloud app 145 | * - if not found open in playstore 146 | * - if playstore not found open in browser 147 | */ 148 | public void openCloud(View view) { 149 | new AlertDialog.Builder(this).setTitle("Please choose a cloud!") 150 | .setPositiveButton("Nextcloud", new DialogInterface.OnClickListener() { 151 | @Override 152 | public void onClick(DialogInterface dialogInterface, int i) { 153 | openCloudApp(NEXTCLOUD_PACKAGE_NAME); 154 | } 155 | }) 156 | .setNegativeButton("ownCloud", new DialogInterface.OnClickListener() { 157 | @Override 158 | public void onClick(DialogInterface dialogInterface, int i) { 159 | openCloudApp(OWNCLOUD_PACKAGE_NAME); 160 | } 161 | }) 162 | .create().show(); 163 | } 164 | 165 | private void openCloudApp(String packageName) { 166 | PackageManager manager = getPackageManager(); 167 | try { 168 | Intent i = manager.getLaunchIntentForPackage(packageName); 169 | if (i == null) { 170 | throw new PackageManager.NameNotFoundException(); 171 | } 172 | i.addCategory(Intent.CATEGORY_LAUNCHER); 173 | startActivity(i); 174 | } catch (PackageManager.NameNotFoundException e) { 175 | try { 176 | startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=" + packageName))); 177 | } catch (android.content.ActivityNotFoundException anfe) { 178 | startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=" + packageName))); 179 | } 180 | } 181 | } 182 | 183 | private void checkStoredAccount() { 184 | String storedAccountName = PreferenceManager.getDefaultSharedPreferences(this).getString(Settings.PREF_ACCOUNT_NAME, null); 185 | 186 | Account storedAccount = null; 187 | if (storedAccountName != null) { 188 | for (Account account : getAccounts()) { 189 | if (account.name.equals(storedAccountName)) { 190 | storedAccount = account; 191 | break; 192 | } 193 | } 194 | useAccount(storedAccount); 195 | } else { 196 | showAccounts(); 197 | } 198 | } 199 | 200 | private void checkGetAccountPermission(){ 201 | // Here, thisActivity is the current activity 202 | if (ContextCompat.checkSelfPermission(this, 203 | Manifest.permission.GET_ACCOUNTS) 204 | != PackageManager.PERMISSION_GRANTED) { 205 | 206 | // Should we show an explanation? 207 | if (ActivityCompat.shouldShowRequestPermissionRationale(this, 208 | Manifest.permission.GET_ACCOUNTS)) { 209 | 210 | new MaterialDialog.Builder(this) 211 | .title(R.string.dialog_permission_title) 212 | .content(R.string.dialog_permission_content) 213 | .positiveText(android.R.string.yes) 214 | .onPositive(new MaterialDialog.SingleButtonCallback() { 215 | @Override 216 | public void onClick(MaterialDialog materialDialog, DialogAction dialogAction) { 217 | ActivityCompat.requestPermissions(LoginActivity.this, 218 | new String[]{Manifest.permission.GET_ACCOUNTS}, 219 | PERMISSIONS_REQUEST_GET_ACCOUNTS); 220 | } 221 | }) 222 | .show(); 223 | } else { 224 | ActivityCompat.requestPermissions(LoginActivity.this, 225 | new String[]{Manifest.permission.GET_ACCOUNTS}, 226 | PERMISSIONS_REQUEST_GET_ACCOUNTS); 227 | } 228 | } else { 229 | showAccounts(); 230 | } 231 | } 232 | 233 | @Override 234 | public void onRequestPermissionsResult(int requestCode, 235 | String permissions[], int[] grantResults) { 236 | switch (requestCode) { 237 | case PERMISSIONS_REQUEST_GET_ACCOUNTS: { 238 | // If request is cancelled, the result arrays are empty. 239 | if (grantResults.length > 0 240 | && grantResults[0] == PackageManager.PERMISSION_GRANTED) { 241 | showAccounts(); 242 | } else { 243 | Snackbar.make(coordinatorLayout, R.string.dialog_permission_denied, Snackbar.LENGTH_LONG).show(); 244 | } 245 | return; 246 | } 247 | } 248 | } 249 | 250 | private ArrayList getAccounts() { 251 | ArrayList accounts = new ArrayList<>(); 252 | accounts.addAll(Arrays.asList(AccountManager.get(this).getAccountsByType(getString(R.string.owncloud_account_type)))); 253 | accounts.addAll(Arrays.asList(AccountManager.get(this).getAccountsByType(getString(R.string.nextcloud_account_type)))); 254 | 255 | return accounts; 256 | } 257 | 258 | private void showAccounts() { 259 | ArrayList accounts = getAccounts(); 260 | 261 | List accountNames = new ArrayList<>(); 262 | 263 | for (Account account: accounts) { 264 | accountNames.add(account.name); 265 | } 266 | 267 | if (accounts.size() == 0) { 268 | emptyButton.animate().alpha(1); 269 | listView.animate().alpha(0); 270 | } else { 271 | listView.animate().alpha(1); 272 | listView.setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, accountNames)); 273 | } 274 | } 275 | 276 | private void useAccount(Account selectedAccount) { 277 | if (PreferenceManager.getDefaultSharedPreferences(this).contains(Settings.PREF_ACCOUNT_PASSWORD)) { 278 | new CheckAccountAsyncTask().execute(selectedAccount); 279 | } else { 280 | queryPassword(selectedAccount); 281 | } 282 | } 283 | 284 | @Override 285 | public void onItemClick(AdapterView parent, View view, int position, long id) { 286 | String accountName = listView.getAdapter().getItem(position).toString(); 287 | PreferenceManager.getDefaultSharedPreferences(LoginActivity.this).edit().putString(Settings.PREF_ACCOUNT_NAME, accountName).apply(); 288 | PreferenceManager.getDefaultSharedPreferences(LoginActivity.this).edit().remove(Settings.PREF_ACCOUNT_PASSWORD).apply(); 289 | checkStoredAccount(); 290 | } 291 | 292 | private class CheckAccountAsyncTask extends AsyncTask { 293 | protected void onPreExecute() { 294 | // Runs on the UI thread before doInBackground 295 | // Good for toggling visibility of a progress indicator 296 | progressBar.animate().alpha(1); 297 | listView.animate().alpha(0); 298 | } 299 | 300 | @Override 301 | protected Account doInBackground(Account... selectedAccounts) { 302 | 303 | Account selectedAccount = selectedAccounts[0]; 304 | Uri baseUrl = Settings.getAccountURL(selectedAccount.name); 305 | String username = Settings.getAccountUsername(selectedAccount.name); 306 | String password = PreferenceManager.getDefaultSharedPreferences(LoginActivity.this).getString(Settings.PREF_ACCOUNT_PASSWORD, null); 307 | if (password == null) { 308 | return null; 309 | } 310 | 311 | OwnCloudClient client = OwnCloudClientFactory.createOwnCloudClient(baseUrl, LoginActivity.this, false); 312 | 313 | client.setCredentials(OwnCloudCredentialsFactory.newBasicCredentials(username, password)); 314 | 315 | if (!Settings.checkRemoteAccess(client)) { 316 | return null; 317 | } 318 | 319 | return selectedAccount; 320 | } 321 | 322 | @Override 323 | protected void onPostExecute(Account result) { 324 | 325 | if (result != null) { 326 | String accountName = result.name; 327 | Snackbar.make(coordinatorLayout, accountName, Snackbar.LENGTH_LONG).show(); 328 | 329 | startActivity(new Intent(LoginActivity.this, NoteListActivity.class)); 330 | finish(); 331 | 332 | } else { 333 | PreferenceManager.getDefaultSharedPreferences(LoginActivity.this).edit().remove(Settings.PREF_ACCOUNT_PASSWORD).apply(); 334 | Snackbar.make(coordinatorLayout, R.string.toast_check_username_password, Snackbar.LENGTH_LONG).show(); 335 | showAccounts(); 336 | } 337 | progressBar.animate().alpha(0); 338 | } 339 | } 340 | } 341 | -------------------------------------------------------------------------------- /app/src/main/java/org/aykit/MyOwnNotes/activities/NoteDetailActivity.java: -------------------------------------------------------------------------------- 1 | package org.aykit.MyOwnNotes.activities; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.support.v7.widget.Toolbar; 7 | import android.view.MenuItem; 8 | 9 | import org.aykit.MyOwnNotes.R; 10 | import org.aykit.MyOwnNotes.fragments.NoteDetailFragment; 11 | 12 | /** 13 | * An activity representing a single Note detail screen. This 14 | * activity is only used on handset devices. On tablet-size devices, 15 | * item details are presented side-by-side with a list of items 16 | * in a {@link NoteListActivity}. 17 | *

18 | * This activity is mostly just a 'shell' activity containing nothing 19 | * more than a {@link NoteDetailFragment}. 20 | */ 21 | public class NoteDetailActivity extends AppCompatActivity { 22 | 23 | @Override 24 | protected void onCreate(Bundle savedInstanceState) { 25 | super.onCreate(savedInstanceState); 26 | setContentView(R.layout.activity_note_detail); 27 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 28 | setSupportActionBar(toolbar); 29 | 30 | // Show the Up button in the action bar. 31 | getSupportActionBar().setDisplayHomeAsUpEnabled(true); 32 | 33 | // savedInstanceState is non-null when there is fragment state 34 | // saved from previous configurations of this activity 35 | // (e.g. when rotating the screen from portrait to landscape). 36 | // In this case, the fragment will automatically be re-added 37 | // to its container so we don't need to manually add it. 38 | // For more information, see the Fragments API guide at: 39 | // 40 | // http://developer.android.com/guide/components/fragments.html 41 | // 42 | if (savedInstanceState == null) { 43 | // Create the detail fragment and add it to the activity 44 | // using a fragment transaction. 45 | Bundle arguments = new Bundle(); 46 | arguments.putParcelable(NoteDetailFragment.ARG_NOTE, getIntent().getParcelableExtra(NoteDetailFragment.ARG_NOTE)); 47 | NoteDetailFragment fragment = new NoteDetailFragment(); 48 | fragment.setArguments(arguments); 49 | getSupportFragmentManager().beginTransaction() 50 | .add(R.id.note_detail_container, fragment) 51 | .commit(); 52 | } 53 | } 54 | 55 | @Override 56 | public boolean onOptionsItemSelected(MenuItem item) { 57 | int id = item.getItemId(); 58 | if (id == android.R.id.home) { 59 | onBackPressed(); 60 | return true; 61 | } 62 | return super.onOptionsItemSelected(item); 63 | } 64 | 65 | @Override 66 | public void onBackPressed() { 67 | startActivity(new Intent(this, NoteListActivity.class)); 68 | overridePendingTransition(android.R.anim.fade_in, android.R.anim.slide_out_right); 69 | finish(); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /app/src/main/java/org/aykit/MyOwnNotes/activities/NoteListActivity.java: -------------------------------------------------------------------------------- 1 | package org.aykit.MyOwnNotes.activities; 2 | 3 | import android.content.BroadcastReceiver; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.content.IntentFilter; 7 | import android.database.Cursor; 8 | import android.net.Uri; 9 | import android.os.Bundle; 10 | import android.preference.PreferenceManager; 11 | import android.support.design.widget.FloatingActionButton; 12 | import android.support.design.widget.NavigationView; 13 | import android.support.v4.content.LocalBroadcastManager; 14 | import android.support.v4.widget.DrawerLayout; 15 | import android.support.v7.app.ActionBarDrawerToggle; 16 | import android.support.v7.app.AppCompatActivity; 17 | import android.support.v7.widget.Toolbar; 18 | import android.view.MenuItem; 19 | import android.view.View; 20 | import android.widget.TextView; 21 | import android.widget.Toast; 22 | 23 | import com.afollestad.materialdialogs.DialogAction; 24 | import com.afollestad.materialdialogs.MaterialDialog; 25 | 26 | import org.aykit.MyOwnNotes.BuildConfig; 27 | import org.aykit.MyOwnNotes.R; 28 | import org.aykit.MyOwnNotes.asynctasks.SyncNotesAsyncTask; 29 | import org.aykit.MyOwnNotes.database.NotesProvider; 30 | import org.aykit.MyOwnNotes.database.model.Note; 31 | import org.aykit.MyOwnNotes.fragments.NoteDetailFragment; 32 | import org.aykit.MyOwnNotes.fragments.NoteListFragment; 33 | import org.aykit.MyOwnNotes.helpers.LegacyImporter; 34 | import org.aykit.MyOwnNotes.helpers.Settings; 35 | 36 | import java.util.List; 37 | 38 | import butterknife.Bind; 39 | import butterknife.ButterKnife; 40 | 41 | 42 | /** 43 | * An activity representing a list of Notes. This activity 44 | * has different presentations for handset and tablet-size devices. On 45 | * handsets, the activity presents a list of items, which when touched, 46 | * lead to a {@link NoteDetailActivity} representing 47 | * item details. On tablets, the activity presents the list of items and 48 | * item details side-by-side using two vertical panes. 49 | *

50 | * The activity makes heavy use of fragments. The list of items is a 51 | * {@link NoteListFragment} and the item details 52 | * (if present) is a {@link NoteDetailFragment}. 53 | *

54 | * This activity also implements the required 55 | * {@link NoteListFragment.Callbacks} interface 56 | * to listen for item selections. 57 | */ 58 | public class NoteListActivity extends AppCompatActivity 59 | implements NoteListFragment.Callbacks { 60 | 61 | /** 62 | * Whether or not the activity is in two-pane mode, i.e. running on a tablet 63 | * device. 64 | */ 65 | private boolean mTwoPane; 66 | 67 | @Bind(R.id.toolbar) 68 | Toolbar toolbar; 69 | 70 | @Bind(R.id.navigation) 71 | NavigationView navigationView; 72 | 73 | @Bind(R.id.drawer_layout) 74 | DrawerLayout drawerLayout; 75 | 76 | @Bind(R.id.button_add) 77 | FloatingActionButton addButton; 78 | 79 | private BroadcastReceiver syncBroadcastReceiver = new BroadcastReceiver() { 80 | @Override 81 | public void onReceive(Context context, Intent intent) { 82 | switch (intent.getAction()) { 83 | case SyncNotesAsyncTask.SYNC_FAILED: 84 | int message = intent.getIntExtra(Intent.EXTRA_TEXT, R.string.toast_connection_error); 85 | Toast.makeText(NoteListActivity.this, message, Toast.LENGTH_LONG).show(); 86 | break; 87 | } 88 | } 89 | }; 90 | 91 | @Override 92 | protected void onCreate(Bundle savedInstanceState) { 93 | super.onCreate(savedInstanceState); 94 | setContentView(R.layout.activity_note_app_bar); 95 | ButterKnife.bind(this); 96 | 97 | checkForLegacyDatabase(); 98 | 99 | setSupportActionBar(toolbar); 100 | toolbar.setTitle(getTitle()); 101 | 102 | addButton.setOnClickListener(new View.OnClickListener() { 103 | @Override 104 | public void onClick(View v) { 105 | final Context appContext = getApplicationContext(); 106 | new Thread(new Runnable() { 107 | @Override 108 | public void run() { 109 | Note newNote = new Note(); 110 | Uri uri = appContext.getContentResolver().insert(NotesProvider.NOTES.CONTENT_URI, newNote.getContentValues()); 111 | if (uri != null) { 112 | Cursor result = appContext.getContentResolver().query(uri, null, null, null, null); 113 | if (result != null) { 114 | result.moveToFirst(); 115 | newNote = new Note(result); 116 | result.close(); 117 | onNoteSelected(newNote); 118 | } 119 | } 120 | } 121 | }).start(); 122 | } 123 | }); 124 | 125 | if (findViewById(R.id.note_detail_container) != null) { 126 | // The detail container view will be present only in the 127 | // large-screen layouts (res/values-large and 128 | // res/values-sw600dp). If this view is present, then the 129 | // activity should be in two-pane mode. 130 | mTwoPane = true; 131 | } 132 | 133 | navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() { 134 | @Override 135 | public boolean onNavigationItemSelected(MenuItem item) { 136 | int id = item.getItemId(); 137 | if (id == R.id.action_logout) { 138 | 139 | // do logout stuff 140 | new MaterialDialog.Builder(NoteListActivity.this) 141 | .title(R.string.dialog_logout_title) 142 | .positiveText(android.R.string.yes) 143 | .onPositive(new MaterialDialog.SingleButtonCallback() { 144 | @Override 145 | public void onClick(MaterialDialog materialDialog, DialogAction dialogAction) { 146 | Settings.clearApp(NoteListActivity.this); 147 | finish(); 148 | startActivity(new Intent(NoteListActivity.this, LoginActivity.class)); 149 | } 150 | }) 151 | .negativeText(android.R.string.no) 152 | .show(); 153 | } 154 | return false; 155 | } 156 | }); 157 | 158 | MenuItem logoutItem = navigationView.getMenu().getItem(0); 159 | if (logoutItem != null){ 160 | String username = PreferenceManager.getDefaultSharedPreferences(this).getString(Settings.PREF_ACCOUNT_NAME, ""); 161 | logoutItem.setTitle(username); 162 | } 163 | 164 | TextView appNameView = (TextView)navigationView.getHeaderView(0).findViewById(R.id.app_name); 165 | if (appNameView != null){ 166 | appNameView.setText(appNameView.getText()+" "+ BuildConfig.VERSION_NAME); 167 | } 168 | 169 | ActionBarDrawerToggle actionBarDrawerToggle = new ActionBarDrawerToggle(this,drawerLayout,toolbar,R.string.drawer_open, R.string.drawer_closed){ 170 | 171 | @Override 172 | public void onDrawerClosed(View drawerView) { 173 | // Code here will be triggered once the drawer closes as we dont want anything to happen so we leave this blank 174 | super.onDrawerClosed(drawerView); 175 | } 176 | 177 | @Override 178 | public void onDrawerOpened(View drawerView) { 179 | // Code here will be triggered once the drawer open as we dont want anything to happen so we leave this blank 180 | 181 | super.onDrawerOpened(drawerView); 182 | } 183 | }; 184 | 185 | //Setting the actionbarToggle to drawer layout 186 | drawerLayout.setDrawerListener(actionBarDrawerToggle); 187 | 188 | //calling sync state is necessay or else your hamburger icon wont show up 189 | actionBarDrawerToggle.syncState(); 190 | } 191 | 192 | @Override 193 | protected void onResume() { 194 | super.onResume(); 195 | 196 | IntentFilter filter = new IntentFilter(); 197 | filter.addAction(SyncNotesAsyncTask.SYNC_FAILED); 198 | 199 | LocalBroadcastManager.getInstance(this).registerReceiver(syncBroadcastReceiver, filter); 200 | 201 | // start sync after registering receiver 202 | SyncNotesAsyncTask.start(this); 203 | } 204 | 205 | @Override 206 | protected void onPause() { 207 | super.onPause(); 208 | 209 | LocalBroadcastManager.getInstance(this).unregisterReceiver(syncBroadcastReceiver); 210 | 211 | } 212 | 213 | /** 214 | * Callback method from {@link NoteListFragment.Callbacks} 215 | * indicating that the item with the given ID was selected. 216 | */ 217 | @Override 218 | public void onNoteSelected(Note note) { 219 | Bundle arguments = new Bundle(); 220 | arguments.putParcelable(NoteDetailFragment.ARG_NOTE, note); 221 | if (mTwoPane) { 222 | // In two-pane mode, show the detail view in this activity by 223 | // adding or replacing the detail fragment using a 224 | // fragment transaction. 225 | NoteDetailFragment fragment = new NoteDetailFragment(); 226 | fragment.setArguments(arguments); 227 | getSupportFragmentManager().beginTransaction() 228 | .replace(R.id.note_detail_container, fragment) 229 | .commit(); 230 | 231 | } else { 232 | // In single-pane mode, simply start the detail activity 233 | // for the selected item 234 | Intent detailIntent = new Intent(this, NoteDetailActivity.class); 235 | detailIntent.putExtras(arguments); 236 | startActivity(detailIntent); 237 | 238 | overridePendingTransition(R.anim.slide_in_right, android.R.anim.fade_out); 239 | } 240 | } 241 | 242 | @Override 243 | public void onNoteSwiped(final Note note) { 244 | new MaterialDialog.Builder(this) 245 | .title(R.string.dialog_delete_title) 246 | .content(note.title) 247 | .positiveText(android.R.string.yes) 248 | .onPositive(new MaterialDialog.SingleButtonCallback() { 249 | @Override 250 | public void onClick(MaterialDialog materialDialog, DialogAction dialogAction) { 251 | new Thread(new Runnable() { 252 | @Override 253 | public void run() { 254 | note.setDeleted(); 255 | getContentResolver().update(NotesProvider.NOTES.withId(note.id), note.getContentValues(), null, null); 256 | SyncNotesAsyncTask.start(NoteListActivity.this); 257 | } 258 | }).start(); 259 | } 260 | }) 261 | .negativeText(android.R.string.no) 262 | .show(); 263 | } 264 | 265 | private void checkForLegacyDatabase(){ 266 | LegacyImporter importer = new LegacyImporter(this); 267 | if (importer.checkForMigration()){ 268 | final List extractedNotes = importer.extractNotes(); 269 | 270 | new Thread(new Runnable() { 271 | @Override 272 | public void run() { 273 | for (Note note : extractedNotes) { 274 | getContentResolver().insert(NotesProvider.NOTES.CONTENT_URI, note.getContentValues()); 275 | } 276 | SyncNotesAsyncTask.start(NoteListActivity.this); 277 | } 278 | }).start(); 279 | } 280 | } 281 | } 282 | -------------------------------------------------------------------------------- /app/src/main/java/org/aykit/MyOwnNotes/adapter/DividerItemDecoration.java: -------------------------------------------------------------------------------- 1 | package org.aykit.MyOwnNotes.adapter; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.graphics.Canvas; 6 | import android.graphics.Rect; 7 | import android.graphics.drawable.Drawable; 8 | import android.support.v7.widget.LinearLayoutManager; 9 | import android.support.v7.widget.RecyclerView; 10 | import android.view.View; 11 | 12 | /** 13 | * Created by mklepp on 22/11/15. 14 | */ 15 | /* 16 | * Copyright (C) 2014 The Android Open Source Project 17 | * 18 | * Licensed under the Apache License, Version 2.0 (the "License"); 19 | * you may not use this file except in compliance with the License. 20 | * You may obtain a copy of the License at 21 | * 22 | * http://www.apache.org/licenses/LICENSE-2.0 23 | * 24 | * Unless required by applicable law or agreed to in writing, software 25 | * distributed under the License is distributed on an "AS IS" BASIS, 26 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 27 | * See the License for the specific language governing permissions and 28 | * limitations under the License. 29 | */ 30 | public class DividerItemDecoration extends RecyclerView.ItemDecoration { 31 | 32 | private static final int[] ATTRS = new int[]{ 33 | android.R.attr.listDivider 34 | }; 35 | 36 | public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL; 37 | 38 | public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL; 39 | 40 | private Drawable mDivider; 41 | 42 | private int mOrientation; 43 | 44 | public DividerItemDecoration(Context context, int orientation) { 45 | final TypedArray a = context.obtainStyledAttributes(ATTRS); 46 | mDivider = a.getDrawable(0); 47 | a.recycle(); 48 | setOrientation(orientation); 49 | } 50 | 51 | public void setOrientation(int orientation) { 52 | if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) { 53 | throw new IllegalArgumentException("invalid orientation"); 54 | } 55 | mOrientation = orientation; 56 | } 57 | 58 | @Override 59 | public void onDraw(Canvas c, RecyclerView parent) { 60 | if (mOrientation == VERTICAL_LIST) { 61 | drawVertical(c, parent); 62 | } else { 63 | drawHorizontal(c, parent); 64 | } 65 | } 66 | 67 | public void drawVertical(Canvas c, RecyclerView parent) { 68 | final int left = parent.getPaddingLeft(); 69 | final int right = parent.getWidth() - parent.getPaddingRight(); 70 | 71 | final int childCount = parent.getChildCount(); 72 | for (int i = 0; i < childCount; i++) { 73 | final View child = parent.getChildAt(i); 74 | final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child 75 | .getLayoutParams(); 76 | final int top = child.getBottom() + params.bottomMargin; 77 | final int bottom = top + mDivider.getIntrinsicHeight(); 78 | mDivider.setBounds(left, top, right, bottom); 79 | mDivider.draw(c); 80 | } 81 | } 82 | 83 | public void drawHorizontal(Canvas c, RecyclerView parent) { 84 | final int top = parent.getPaddingTop(); 85 | final int bottom = parent.getHeight() - parent.getPaddingBottom(); 86 | 87 | final int childCount = parent.getChildCount(); 88 | for (int i = 0; i < childCount; i++) { 89 | final View child = parent.getChildAt(i); 90 | final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child 91 | .getLayoutParams(); 92 | final int left = child.getRight() + params.rightMargin; 93 | final int right = left + mDivider.getIntrinsicHeight(); 94 | mDivider.setBounds(left, top, right, bottom); 95 | mDivider.draw(c); 96 | } 97 | } 98 | 99 | @Override 100 | public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) { 101 | if (mOrientation == VERTICAL_LIST) { 102 | outRect.set(0, 0, 0, mDivider.getIntrinsicHeight()); 103 | } else { 104 | outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0); 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /app/src/main/java/org/aykit/MyOwnNotes/adapter/NotesListAdapter.java: -------------------------------------------------------------------------------- 1 | package org.aykit.MyOwnNotes.adapter; 2 | 3 | /** 4 | * Created by mklepp on 22/11/15. 5 | */ 6 | import android.content.Context; 7 | import android.database.Cursor; 8 | import android.support.v7.widget.RecyclerView; 9 | import android.view.LayoutInflater; 10 | import android.view.View; 11 | import android.view.ViewGroup; 12 | import android.widget.AdapterView; 13 | import android.widget.TextView; 14 | 15 | import org.aykit.MyOwnNotes.R; 16 | import org.aykit.MyOwnNotes.database.NoteColumns; 17 | import org.aykit.MyOwnNotes.database.model.Note; 18 | 19 | import butterknife.Bind; 20 | import butterknife.ButterKnife; 21 | 22 | public class NotesListAdapter extends RecyclerView.Adapter { 23 | 24 | private Cursor mCursor; 25 | private AdapterView.OnItemClickListener mOnItemClickListener; 26 | 27 | public NotesListAdapter(Cursor cursor) { 28 | mCursor = cursor; 29 | } 30 | 31 | @Override 32 | public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 33 | Context context = parent.getContext(); 34 | LayoutInflater inflater = LayoutInflater.from(context); 35 | 36 | // Inflate the custom layout 37 | View contactView = inflater.inflate(R.layout.row_note, parent, false); 38 | 39 | // Return a new holder instance 40 | ViewHolder viewHolder = new ViewHolder(contactView, this); 41 | return viewHolder; 42 | } 43 | 44 | @Override 45 | public void onBindViewHolder(ViewHolder holder, int position) { 46 | mCursor.moveToPosition(position); 47 | String title = mCursor.getString(mCursor.getColumnIndex(NoteColumns.TITLE)); 48 | String status = mCursor.getString(mCursor.getColumnIndex(NoteColumns.STATUS)); 49 | 50 | int statusIcon = 0; 51 | switch (status){ 52 | case NoteColumns.STATUS_DELETE: 53 | case NoteColumns.STATUS_NEW: 54 | case NoteColumns.STATUS_UPDATE: 55 | statusIcon = R.drawable.ic_cloud_upload_24dp; 56 | break; 57 | } 58 | 59 | holder.note.setText(title); 60 | holder.note.setCompoundDrawablesWithIntrinsicBounds(0, 0, statusIcon, 0); 61 | } 62 | 63 | @Override 64 | public int getItemCount() { 65 | return mCursor!=null?mCursor.getCount():0; 66 | } 67 | 68 | public void changeCursor(Cursor cursor) { 69 | Cursor old = swapCursor(cursor); 70 | if (old != null) { 71 | old.close(); 72 | } 73 | } 74 | 75 | public Cursor swapCursor(Cursor newCursor) { 76 | if (newCursor == mCursor) { 77 | return null; 78 | } 79 | final Cursor oldCursor = mCursor; 80 | mCursor = newCursor; 81 | notifyDataSetChanged(); 82 | return oldCursor; 83 | } 84 | 85 | public Note getItem(int position) { 86 | mCursor.moveToPosition(position); 87 | return new Note(mCursor); 88 | } 89 | 90 | public void setOnItemClickListener(AdapterView.OnItemClickListener onItemClickListener) { 91 | mOnItemClickListener = onItemClickListener; 92 | } 93 | 94 | private void onItemHolderClick(RecyclerView.ViewHolder itemHolder){ 95 | if (mOnItemClickListener != null) { 96 | mOnItemClickListener.onItemClick(null, itemHolder.itemView, 97 | itemHolder.getAdapterPosition(), itemHolder.getItemId()); 98 | } 99 | } 100 | 101 | static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { 102 | @Bind(R.id.note) 103 | TextView note; 104 | 105 | private NotesListAdapter mAdapter; 106 | 107 | ViewHolder(View v, NotesListAdapter adapter) { 108 | super(v); 109 | ButterKnife.bind(this, v); 110 | v.setOnClickListener(this); 111 | mAdapter = adapter; 112 | } 113 | 114 | @Override 115 | public void onClick(View v) { 116 | mAdapter.onItemHolderClick(this); 117 | } 118 | } 119 | } -------------------------------------------------------------------------------- /app/src/main/java/org/aykit/MyOwnNotes/database/NoteColumns.java: -------------------------------------------------------------------------------- 1 | package org.aykit.MyOwnNotes.database; 2 | 3 | import net.simonvt.schematic.annotation.AutoIncrement; 4 | import net.simonvt.schematic.annotation.Check; 5 | import net.simonvt.schematic.annotation.DataType; 6 | import net.simonvt.schematic.annotation.NotNull; 7 | import net.simonvt.schematic.annotation.PrimaryKey; 8 | 9 | import static net.simonvt.schematic.annotation.DataType.Type.INTEGER; 10 | import static net.simonvt.schematic.annotation.DataType.Type.TEXT; 11 | 12 | /** 13 | * Created by mklepp on 22/11/15. 14 | */ 15 | public interface NoteColumns { 16 | 17 | String STATUS_NEW = "new"; 18 | String STATUS_DELETE = "delete"; 19 | String STATUS_UPDATE = "update"; 20 | String STATUS_DONE = "done"; 21 | 22 | @DataType(INTEGER) @PrimaryKey @AutoIncrement String _ID = "_id"; 23 | 24 | @DataType(TEXT) @NotNull String TITLE = "title"; 25 | @DataType(TEXT) String CONTENT = "content"; 26 | 27 | @DataType(TEXT) 28 | @Check(NoteColumns.STATUS + " in ('" + NoteColumns.STATUS_NEW + "', '" 29 | + NoteColumns.STATUS_UPDATE + "', '" 30 | + NoteColumns.STATUS_DELETE + "', '" 31 | + NoteColumns.STATUS_DONE + "')") 32 | String STATUS = "status"; 33 | 34 | @DataType(INTEGER) 35 | String CREATION_DATE = "creation_date"; 36 | 37 | @DataType(TEXT) 38 | String FILENAME = "filename"; 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/java/org/aykit/MyOwnNotes/database/NotesDatabase.java: -------------------------------------------------------------------------------- 1 | package org.aykit.MyOwnNotes.database; 2 | 3 | import net.simonvt.schematic.annotation.Database; 4 | import net.simonvt.schematic.annotation.Table; 5 | 6 | /** 7 | * Created by mklepp on 22/11/15. 8 | */ 9 | @Database(version = NotesDatabase.VERSION) 10 | public class NotesDatabase { 11 | public static final int VERSION = 1; 12 | 13 | @Table(NoteColumns.class) public static final String NOTES = "notes"; 14 | } 15 | -------------------------------------------------------------------------------- /app/src/main/java/org/aykit/MyOwnNotes/database/NotesProvider.java: -------------------------------------------------------------------------------- 1 | package org.aykit.MyOwnNotes.database; 2 | 3 | import android.net.Uri; 4 | 5 | import net.simonvt.schematic.annotation.ContentProvider; 6 | import net.simonvt.schematic.annotation.ContentUri; 7 | import net.simonvt.schematic.annotation.InexactContentUri; 8 | import net.simonvt.schematic.annotation.TableEndpoint; 9 | 10 | import org.aykit.MyOwnNotes.BuildConfig; 11 | 12 | /** 13 | * Created by mklepp on 22/11/15. 14 | */ 15 | @ContentProvider(authority = NotesProvider.AUTHORITY, database = NotesDatabase.class) 16 | public final class NotesProvider { 17 | 18 | public static final String AUTHORITY = BuildConfig.APPLICATION_ID+".NotesProvider"; 19 | static final Uri BASE_CONTENT_URI = Uri.parse("content://" + AUTHORITY); 20 | 21 | 22 | private static Uri buildUri(String... paths) { 23 | Uri.Builder builder = BASE_CONTENT_URI.buildUpon(); 24 | for (String path : paths) { 25 | builder.appendPath(path); 26 | } 27 | return builder.build(); 28 | } 29 | 30 | @TableEndpoint(table = NotesDatabase.NOTES) 31 | public static class NOTES { 32 | 33 | @ContentUri( 34 | path = NotesDatabase.NOTES, 35 | type = "vnd.android.cursor.dir/list", 36 | defaultSort = NoteColumns.TITLE + " ASC") 37 | public static final Uri CONTENT_URI = buildUri(NotesDatabase.NOTES); 38 | 39 | @InexactContentUri( 40 | name = "NOTE_ID", 41 | path = NotesDatabase.NOTES + "/#", 42 | type = "vnd.android.cursor.item/note", 43 | whereColumn = NoteColumns._ID, 44 | pathSegment = 1) 45 | public static Uri withId(long id) { 46 | return buildUri(NotesDatabase.NOTES, String.valueOf(id)); 47 | } 48 | } 49 | 50 | } -------------------------------------------------------------------------------- /app/src/main/java/org/aykit/MyOwnNotes/database/model/Note.java: -------------------------------------------------------------------------------- 1 | package org.aykit.MyOwnNotes.database.model; 2 | 3 | import android.content.ContentValues; 4 | import android.database.Cursor; 5 | import android.os.Parcel; 6 | import android.os.Parcelable; 7 | import android.text.TextUtils; 8 | 9 | import org.aykit.MyOwnNotes.database.NoteColumns; 10 | 11 | /** 12 | * Created by mklepp on 22/11/15. 13 | */ 14 | public class Note implements Parcelable { 15 | public long id; 16 | public String title; 17 | public String content; 18 | public int creationDate; 19 | String status; 20 | public String filename; 21 | 22 | public static final String NEW_TITLE = "new"; 23 | 24 | public Note(){ 25 | this.title = NEW_TITLE; 26 | this.status = NoteColumns.STATUS_NEW; 27 | this.creationDate = (int)(System.currentTimeMillis()/1000L); 28 | } 29 | 30 | public Note(Cursor cursor){ 31 | id = cursor.getLong(cursor.getColumnIndex(NoteColumns._ID)); 32 | title = cursor.getString(cursor.getColumnIndex(NoteColumns.TITLE)); 33 | content = cursor.getString(cursor.getColumnIndex(NoteColumns.CONTENT)); 34 | status = cursor.getString(cursor.getColumnIndex(NoteColumns.STATUS)); 35 | filename = cursor.getString(cursor.getColumnIndex(NoteColumns.FILENAME)); 36 | if (TextUtils.isEmpty(filename) || status.equals(NoteColumns.STATUS_NEW)){ 37 | // Allow ascii-only filename and remove slashes 38 | filename = title.replaceAll("[^\\x00-\\x7F]", "").replaceAll("[/\\\\]", "")+".txt"; 39 | } 40 | } 41 | 42 | protected Note(Parcel in) { 43 | id = in.readLong(); 44 | title = in.readString(); 45 | content = in.readString(); 46 | status = in.readString(); 47 | creationDate = in.readInt(); 48 | filename = in.readString(); 49 | } 50 | 51 | @Override 52 | public void writeToParcel(Parcel dest, int flags) { 53 | dest.writeLong(id); 54 | dest.writeString(title); 55 | dest.writeString(content); 56 | dest.writeString(status); 57 | dest.writeInt(creationDate); 58 | dest.writeString(filename); 59 | } 60 | 61 | public static final Creator CREATOR = new Creator() { 62 | @Override 63 | public Note createFromParcel(Parcel in) { 64 | return new Note(in); 65 | } 66 | 67 | @Override 68 | public Note[] newArray(int size) { 69 | return new Note[size]; 70 | } 71 | }; 72 | 73 | @Override 74 | public int describeContents() { 75 | return 0; 76 | } 77 | 78 | public ContentValues getContentValues() { 79 | ContentValues cv = new ContentValues(); 80 | if (id > 0) { 81 | cv.put(NoteColumns._ID, id); 82 | } 83 | cv.put(NoteColumns.STATUS, status); 84 | cv.put(NoteColumns.TITLE, title); 85 | if (!TextUtils.isEmpty(content)) { 86 | cv.put(NoteColumns.CONTENT, content); 87 | } 88 | cv.put(NoteColumns.CREATION_DATE, creationDate); 89 | cv.put(NoteColumns.FILENAME, filename); 90 | return cv; 91 | } 92 | 93 | public void setEdited(){ 94 | if (NoteColumns.STATUS_DONE.equals(status)){ 95 | status = NoteColumns.STATUS_UPDATE; 96 | } 97 | } 98 | 99 | public void setDeleted() { 100 | status = NoteColumns.STATUS_DELETE; 101 | } 102 | 103 | public void setUploaded() { 104 | status = NoteColumns.STATUS_DONE; 105 | } 106 | 107 | public boolean isEdited() { 108 | return status.equals(NoteColumns.STATUS_UPDATE); 109 | } 110 | 111 | public boolean isDone() { 112 | return status.equals(NoteColumns.STATUS_DONE); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /app/src/main/java/org/aykit/MyOwnNotes/fragments/NoteDetailFragment.java: -------------------------------------------------------------------------------- 1 | package org.aykit.MyOwnNotes.fragments; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.os.Bundle; 7 | import android.support.v4.app.Fragment; 8 | import android.support.v7.widget.Toolbar; 9 | import android.text.Editable; 10 | import android.text.TextWatcher; 11 | import android.view.LayoutInflater; 12 | import android.view.Menu; 13 | import android.view.MenuInflater; 14 | import android.view.MenuItem; 15 | import android.view.View; 16 | import android.view.ViewGroup; 17 | import android.widget.EditText; 18 | 19 | import com.afollestad.materialdialogs.DialogAction; 20 | import com.afollestad.materialdialogs.MaterialDialog; 21 | 22 | import org.aykit.MyOwnNotes.R; 23 | import org.aykit.MyOwnNotes.activities.NoteDetailActivity; 24 | import org.aykit.MyOwnNotes.activities.NoteListActivity; 25 | import org.aykit.MyOwnNotes.database.NotesProvider; 26 | import org.aykit.MyOwnNotes.database.model.Note; 27 | 28 | import butterknife.Bind; 29 | import butterknife.ButterKnife; 30 | 31 | /** 32 | * A fragment representing a single Note detail screen. 33 | * This fragment is either contained in a {@link NoteListActivity} 34 | * in two-pane mode (on tablets) or a {@link NoteDetailActivity} 35 | * on handsets. 36 | */ 37 | public class NoteDetailFragment extends Fragment implements TextWatcher { 38 | /** 39 | * The fragment argument representing the item ID that this fragment 40 | * represents. 41 | */ 42 | public static final String ARG_NOTE = "note"; 43 | 44 | private Note mNote; 45 | 46 | @Bind(R.id.title) 47 | EditText titleView; 48 | 49 | @Bind(R.id.content) 50 | EditText contentView; 51 | 52 | /** 53 | * Mandatory empty constructor for the fragment manager to instantiate the 54 | * fragment (e.g. upon screen orientation changes). 55 | */ 56 | public NoteDetailFragment() { 57 | } 58 | 59 | @Override 60 | public void onCreate(Bundle savedInstanceState) { 61 | super.onCreate(savedInstanceState); 62 | 63 | Bundle args = getArguments(); 64 | if (args.containsKey(ARG_NOTE)) { 65 | 66 | mNote = args.getParcelable(ARG_NOTE); 67 | 68 | Activity activity = this.getActivity(); 69 | Toolbar appBarLayout = (Toolbar) activity.findViewById(R.id.toolbar); 70 | if (appBarLayout != null) { 71 | appBarLayout.setTitle(mNote.title); 72 | } 73 | } 74 | 75 | setHasOptionsMenu(true); 76 | } 77 | 78 | @Override 79 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 80 | Bundle savedInstanceState) { 81 | return inflater.inflate(R.layout.fragment_note_detail, container, false); 82 | } 83 | 84 | @Override 85 | public void onViewCreated(View view, Bundle savedInstanceState) { 86 | super.onViewCreated(view, savedInstanceState); 87 | ButterKnife.bind(this, view); 88 | 89 | titleView.setText(mNote.title); 90 | if (mNote.title.equals(Note.NEW_TITLE)) { 91 | titleView.setSelection(0, mNote.title.length()); 92 | } else { 93 | titleView.setSelection(mNote.title.length()); 94 | } 95 | titleView.addTextChangedListener(this); 96 | contentView.setText(mNote.content); 97 | contentView.addTextChangedListener(this); 98 | } 99 | 100 | @Override 101 | public void onDestroyView() { 102 | ButterKnife.unbind(this); 103 | super.onDestroyView(); 104 | } 105 | 106 | @Override 107 | public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 108 | inflater.inflate(R.menu.note_detail, menu); 109 | super.onCreateOptionsMenu(menu, inflater); 110 | } 111 | 112 | @Override 113 | public boolean onOptionsItemSelected(MenuItem item) { 114 | int id = item.getItemId(); 115 | if (id == R.id.delete_note) { 116 | if (isAdded()) { 117 | new MaterialDialog.Builder(getActivity()) 118 | .title(R.string.dialog_delete_title) 119 | .content(mNote.title) 120 | .positiveText(android.R.string.yes) 121 | .onPositive(new MaterialDialog.SingleButtonCallback() { 122 | @Override 123 | public void onClick(MaterialDialog materialDialog, DialogAction dialogAction) { 124 | new Thread(new Runnable() { 125 | @Override 126 | public void run() { 127 | mNote.setDeleted(); 128 | getActivity().getContentResolver().update(NotesProvider.NOTES.withId(mNote.id), mNote.getContentValues(), null, null); 129 | } 130 | }).start(); 131 | 132 | // close detailview 133 | getActivity().navigateUpTo(new Intent(getActivity(), NoteListActivity.class)); 134 | } 135 | }) 136 | .negativeText(android.R.string.no) 137 | .show(); 138 | } 139 | 140 | return true; 141 | } 142 | return super.onOptionsItemSelected(item); 143 | } 144 | 145 | @Override 146 | public void beforeTextChanged(CharSequence s, int start, int count, int after) { 147 | 148 | } 149 | 150 | @Override 151 | public void onTextChanged(CharSequence s, int start, int before, int count) { 152 | 153 | } 154 | 155 | @Override 156 | public void afterTextChanged(Editable s) { 157 | mNote.title = titleView.getText().toString(); 158 | mNote.content = contentView.getText().toString(); 159 | final Context appContext = getActivity().getApplicationContext(); 160 | new Thread(new Runnable() { 161 | @Override 162 | public void run() { 163 | mNote.setEdited(); 164 | appContext.getContentResolver().update(NotesProvider.NOTES.withId(mNote.id), mNote.getContentValues(), null, null); 165 | } 166 | }).start(); 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /app/src/main/java/org/aykit/MyOwnNotes/fragments/NoteListFragment.java: -------------------------------------------------------------------------------- 1 | package org.aykit.MyOwnNotes.fragments; 2 | 3 | import android.app.Activity; 4 | import android.content.BroadcastReceiver; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.content.IntentFilter; 8 | import android.database.Cursor; 9 | import android.graphics.Bitmap; 10 | import android.graphics.Canvas; 11 | import android.graphics.Paint; 12 | import android.graphics.drawable.BitmapDrawable; 13 | import android.graphics.drawable.Drawable; 14 | import android.os.Bundle; 15 | import android.support.v4.app.Fragment; 16 | import android.support.v4.app.LoaderManager; 17 | import android.support.v4.content.CursorLoader; 18 | import android.support.v4.content.Loader; 19 | import android.support.v4.content.LocalBroadcastManager; 20 | import android.support.v4.widget.SwipeRefreshLayout; 21 | import android.support.v7.widget.LinearLayoutManager; 22 | import android.support.v7.widget.RecyclerView; 23 | import android.support.v7.widget.helper.ItemTouchHelper; 24 | import android.view.LayoutInflater; 25 | import android.view.View; 26 | import android.view.ViewGroup; 27 | import android.widget.AdapterView; 28 | import android.widget.TextView; 29 | 30 | import org.aykit.MyOwnNotes.R; 31 | import org.aykit.MyOwnNotes.adapter.DividerItemDecoration; 32 | import org.aykit.MyOwnNotes.adapter.NotesListAdapter; 33 | import org.aykit.MyOwnNotes.asynctasks.SyncNotesAsyncTask; 34 | import org.aykit.MyOwnNotes.database.NoteColumns; 35 | import org.aykit.MyOwnNotes.database.NotesProvider; 36 | import org.aykit.MyOwnNotes.database.model.Note; 37 | 38 | import butterknife.Bind; 39 | import butterknife.ButterKnife; 40 | 41 | /** 42 | * A list fragment representing a list of Notes. This fragment 43 | * also supports tablet devices by allowing list items to be given an 44 | * 'activated' state upon selection. This helps indicate which item is 45 | * currently being viewed in a {@link NoteDetailFragment}. 46 | *

47 | * Activities containing this fragment MUST implement the {@link Callbacks} 48 | * interface. 49 | */ 50 | public class NoteListFragment extends Fragment implements LoaderManager.LoaderCallbacks, AdapterView.OnItemClickListener, SwipeRefreshLayout.OnRefreshListener { 51 | 52 | private NotesListAdapter adapter; 53 | 54 | private static final int LOADER_NOTES = 20; 55 | 56 | @Bind(android.R.id.list) 57 | RecyclerView recyclerView; 58 | 59 | @Bind(android.R.id.empty) 60 | TextView emptyView; 61 | 62 | @Bind(R.id.swipeContainer) 63 | SwipeRefreshLayout swipeRefreshLayout; 64 | 65 | Bitmap trashIcon; 66 | 67 | private BroadcastReceiver syncBroadcastReceiver = new BroadcastReceiver() { 68 | @Override 69 | public void onReceive(Context context, Intent intent) { 70 | switch (intent.getAction()) { 71 | case SyncNotesAsyncTask.SYNC_FINISHED: 72 | swipeRefreshLayout.setRefreshing(false); 73 | break; 74 | case SyncNotesAsyncTask.SYNC_PROGRESS: 75 | break; 76 | } 77 | } 78 | }; 79 | 80 | /** 81 | * The serialization (saved instance state) Bundle key representing the 82 | * activated item position. Only used on tablets. 83 | */ 84 | private static final String STATE_ACTIVATED_POSITION = "activated_position"; 85 | 86 | /** 87 | * The fragment's current callback object, which is notified of list item 88 | * clicks. 89 | */ 90 | private Callbacks mCallbacks = sDummyCallbacks; 91 | 92 | @Override 93 | public void onRefresh() { 94 | SyncNotesAsyncTask.start(getActivity()); 95 | } 96 | 97 | /** 98 | * A callback interface that all activities containing this fragment must 99 | * implement. This mechanism allows activities to be notified of item 100 | * selections. 101 | */ 102 | public interface Callbacks { 103 | /** 104 | * Callback for when an item has been selected. 105 | */ 106 | void onNoteSelected(Note note); 107 | void onNoteSwiped(Note note); 108 | } 109 | 110 | /** 111 | * A dummy implementation of the {@link Callbacks} interface that does 112 | * nothing. Used only when this fragment is not attached to an activity. 113 | */ 114 | private static Callbacks sDummyCallbacks = new Callbacks() { 115 | 116 | @Override 117 | public void onNoteSelected(Note note) { 118 | } 119 | 120 | @Override 121 | public void onNoteSwiped(Note note) { 122 | 123 | } 124 | }; 125 | 126 | /** 127 | * Mandatory empty constructor for the fragment manager to instantiate the 128 | * fragment (e.g. upon screen orientation changes). 129 | */ 130 | public NoteListFragment() { 131 | } 132 | 133 | @Override 134 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 135 | Bundle savedInstanceState) { 136 | return inflater.inflate(R.layout.fragment_note_list, container, false); 137 | } 138 | 139 | @Override 140 | public void onViewCreated(final View view, Bundle savedInstanceState) { 141 | super.onViewCreated(view, savedInstanceState); 142 | ButterKnife.bind(this, view); 143 | 144 | trashIcon = drawableToBitmap(getResources().getDrawable(R.drawable.ic_delete_24dp)); 145 | 146 | if (adapter != null) { 147 | recyclerView.setAdapter(adapter); 148 | } 149 | recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); 150 | 151 | RecyclerView.ItemDecoration itemDecoration = new 152 | DividerItemDecoration(getContext(), DividerItemDecoration.VERTICAL_LIST); 153 | recyclerView.addItemDecoration(itemDecoration); 154 | 155 | swipeRefreshLayout.setOnRefreshListener(this); 156 | swipeRefreshLayout.setColorSchemeResources(R.color.accent, R.color.primary); 157 | 158 | ItemTouchHelper helper = new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(0, 159 | ItemTouchHelper.START) { 160 | 161 | @Override 162 | public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) { 163 | return false; 164 | } 165 | 166 | @Override 167 | public void onSwiped(final RecyclerView.ViewHolder viewHolder, int direction) { 168 | Note note = adapter.getItem(viewHolder.getAdapterPosition()); 169 | mCallbacks.onNoteSwiped(note); 170 | 171 | // reset state 172 | adapter.notifyItemChanged(viewHolder.getAdapterPosition()); 173 | } 174 | 175 | @Override 176 | public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) { 177 | if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) { 178 | // Get RecyclerView item from the ViewHolder 179 | View itemView = viewHolder.itemView; 180 | 181 | Paint p = new Paint(); 182 | 183 | p.setColor(getResources().getColor(R.color.colorAccent)); 184 | 185 | c.drawRect((float) itemView.getRight() + dX, (float) itemView.getTop(), 186 | (float) itemView.getRight(), (float) itemView.getBottom(), p); 187 | 188 | c.drawBitmap(trashIcon, 189 | Math.max((float) itemView.getRight() - itemView.getPaddingRight() - trashIcon.getWidth(), itemView.getRight() + dX), 190 | (float) itemView.getTop() + ((float) itemView.getBottom() - (float) itemView.getTop() - trashIcon.getHeight()) / 2, 191 | new Paint()); 192 | 193 | 194 | float width = (float) viewHolder.itemView.getWidth(); 195 | float alpha = 1.0f - Math.abs(dX) / width; 196 | viewHolder.itemView.setAlpha(alpha); 197 | viewHolder.itemView.setTranslationX(dX); 198 | } 199 | 200 | } 201 | }); 202 | helper.attachToRecyclerView(recyclerView); 203 | 204 | getLoaderManager().initLoader(LOADER_NOTES, null, this); 205 | } 206 | 207 | @Override 208 | public void onDestroyView() { 209 | ButterKnife.unbind(this); 210 | super.onDestroyView(); 211 | } 212 | 213 | @Override 214 | public void onAttach(Activity activity) { 215 | super.onAttach(activity); 216 | 217 | // Activities containing this fragment must implement its callbacks. 218 | if (!(activity instanceof Callbacks)) { 219 | throw new IllegalStateException("Activity must implement fragment's callbacks."); 220 | } 221 | 222 | mCallbacks = (Callbacks) activity; 223 | } 224 | 225 | @Override 226 | public void onDetach() { 227 | super.onDetach(); 228 | 229 | // Reset the active callbacks interface to the dummy implementation. 230 | mCallbacks = sDummyCallbacks; 231 | } 232 | 233 | // @Override 234 | // public void onSaveInstanceState(Bundle outState) { 235 | // super.onSaveInstanceState(outState); 236 | // if (mActivatedPosition != ListView.INVALID_POSITION) { 237 | // // Serialize and persist the activated item position. 238 | // outState.putInt(STATE_ACTIVATED_POSITION, mActivatedPosition); 239 | // } 240 | // } 241 | 242 | @Override 243 | public Loader onCreateLoader(int id, Bundle args) { 244 | String select = NoteColumns.STATUS + "<>?"; 245 | String[] selectArgs = new String[]{NoteColumns.STATUS_DELETE}; 246 | String sortOrder = NoteColumns.CREATION_DATE+" ASC"; 247 | return new CursorLoader(getActivity(), NotesProvider.NOTES.CONTENT_URI, null, select, selectArgs, sortOrder); 248 | } 249 | 250 | @Override 251 | public void onLoadFinished(Loader loader, Cursor data) { 252 | if (adapter == null) { 253 | adapter = new NotesListAdapter(data); 254 | adapter.setOnItemClickListener(this); 255 | recyclerView.setAdapter(adapter); 256 | } else { 257 | adapter.changeCursor(data); 258 | } 259 | 260 | if (data != null && data.getCount() > 0) { 261 | emptyView.animate().alpha(0); 262 | } else { 263 | emptyView.animate().alpha(1); 264 | } 265 | } 266 | 267 | @Override 268 | public void onLoaderReset(Loader loader) { 269 | if (adapter != null) { 270 | adapter.changeCursor(null); 271 | } 272 | } 273 | 274 | @Override 275 | public void onItemClick(AdapterView parent, View view, int position, long id) { 276 | Note note = adapter.getItem(position); 277 | mCallbacks.onNoteSelected(note); 278 | } 279 | 280 | @Override 281 | public void onResume() { 282 | super.onResume(); 283 | 284 | IntentFilter filter = new IntentFilter(); 285 | filter.addAction(SyncNotesAsyncTask.SYNC_PROGRESS); 286 | filter.addAction(SyncNotesAsyncTask.SYNC_FINISHED); 287 | 288 | LocalBroadcastManager.getInstance(getActivity()).registerReceiver(syncBroadcastReceiver, filter); 289 | } 290 | 291 | @Override 292 | public void onPause() { 293 | super.onPause(); 294 | 295 | LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(syncBroadcastReceiver); 296 | 297 | } 298 | 299 | public static Bitmap drawableToBitmap (Drawable drawable) { 300 | Bitmap bitmap = null; 301 | 302 | if (drawable instanceof BitmapDrawable) { 303 | BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable; 304 | if(bitmapDrawable.getBitmap() != null) { 305 | return bitmapDrawable.getBitmap(); 306 | } 307 | } 308 | 309 | if(drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) { 310 | bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); // Single color bitmap will be created of 1x1 pixel 311 | } else { 312 | bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); 313 | } 314 | 315 | Canvas canvas = new Canvas(bitmap); 316 | drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); 317 | drawable.draw(canvas); 318 | return bitmap; 319 | } 320 | } 321 | -------------------------------------------------------------------------------- /app/src/main/java/org/aykit/MyOwnNotes/helpers/LegacyImporter.java: -------------------------------------------------------------------------------- 1 | package org.aykit.MyOwnNotes.helpers; 2 | 3 | import android.content.Context; 4 | import android.database.Cursor; 5 | import android.database.sqlite.SQLiteDatabase; 6 | 7 | import org.aykit.MyOwnNotes.database.model.Note; 8 | 9 | import java.io.File; 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | /** 14 | * Created by mklepp on 21/02/16. 15 | */ 16 | public class LegacyImporter { 17 | 18 | static final String LEGACY_DB_NAME = "notes.db"; 19 | 20 | private Context mContext; 21 | 22 | public LegacyImporter(Context context) { 23 | this.mContext = context; 24 | } 25 | 26 | private File getDatabasePath(){ 27 | return mContext.getDatabasePath(LEGACY_DB_NAME); 28 | } 29 | 30 | public boolean checkForMigration(){ 31 | File oldDb = getDatabasePath(); 32 | 33 | if (oldDb.exists()){ 34 | return true; 35 | } 36 | 37 | return false; 38 | } 39 | 40 | public List extractNotes(){ 41 | 42 | List extractedNotes = new ArrayList<>(); 43 | 44 | SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(getDatabasePath(), null); 45 | 46 | Cursor cursor = db.rawQuery("SELECT * FROM notes WHERE noteStatus IS NOT NULL", null); 47 | 48 | if (cursor!=null) 49 | { 50 | while(cursor.moveToNext()){ 51 | Note note = new Note(); 52 | note.title = "Unsynchronized Note from previous version"; 53 | note.content = cursor.getString(cursor.getColumnIndex("content")); 54 | 55 | extractedNotes.add(note); 56 | } 57 | 58 | // close and delete old database 59 | db.close(); 60 | getDatabasePath().delete(); 61 | } else { 62 | db.close(); 63 | } 64 | 65 | return extractedNotes; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /app/src/main/java/org/aykit/MyOwnNotes/helpers/Settings.java: -------------------------------------------------------------------------------- 1 | package org.aykit.MyOwnNotes.helpers; 2 | 3 | import android.content.Context; 4 | import android.net.Uri; 5 | import android.preference.PreferenceManager; 6 | 7 | import com.owncloud.android.lib.common.OwnCloudClient; 8 | import com.owncloud.android.lib.common.operations.RemoteOperationResult; 9 | import com.owncloud.android.lib.resources.files.CreateRemoteFolderOperation; 10 | import com.owncloud.android.lib.resources.files.FileUtils; 11 | import com.owncloud.android.lib.resources.files.ReadRemoteFolderOperation; 12 | 13 | import org.aykit.MyOwnNotes.database.generated.NotesDatabase; 14 | 15 | /** 16 | * Created by mklepp on 26/11/15. 17 | */ 18 | public class Settings { 19 | public static final String PREF_ACCOUNT_NAME = "PREF_ACCOUNT_NAME"; 20 | public static final String PREF_ACCOUNT_PASSWORD = "PREF_ACCOUNT_PASSWORD"; 21 | 22 | public static final String NOTE_PATH_DEFAULT = "Notes"; 23 | 24 | public static Uri getAccountURL(String accountname){ 25 | String[] credentials = accountname.split("@"); 26 | if (credentials.length != 2) { 27 | return null; 28 | } 29 | return Uri.parse("https://"+credentials[1]); 30 | } 31 | 32 | public static String getAccountUsername(String accountName){ 33 | String[] credentials = accountName.split("@"); 34 | if (credentials.length != 2) { 35 | return null; 36 | } 37 | return credentials[0]; 38 | } 39 | 40 | public static boolean checkRemoteAccess(OwnCloudClient client) { 41 | ReadRemoteFolderOperation refreshOperation = new ReadRemoteFolderOperation(FileUtils.PATH_SEPARATOR); 42 | RemoteOperationResult result = refreshOperation.execute(client); 43 | 44 | return result.isSuccess(); 45 | } 46 | 47 | // if notes folder does'nt exist, create him 48 | public static boolean checkRemoteFolder(OwnCloudClient client) { 49 | 50 | ReadRemoteFolderOperation refreshOperation = new ReadRemoteFolderOperation(Settings.NOTE_PATH_DEFAULT); 51 | RemoteOperationResult result = refreshOperation.execute(client); 52 | 53 | if (!result.isSuccess()) { 54 | CreateRemoteFolderOperation createOperation = new CreateRemoteFolderOperation(Settings.NOTE_PATH_DEFAULT, true); 55 | RemoteOperationResult createResult = createOperation.execute(client); 56 | if (createResult.isSuccess()) { 57 | return true; 58 | } 59 | } else { 60 | return true; 61 | } 62 | 63 | return false; 64 | } 65 | 66 | public static void clearApp(Context context) { 67 | // delete stored preferences - username/password 68 | PreferenceManager.getDefaultSharedPreferences(context).edit().clear().apply(); 69 | 70 | // delete database 71 | clearDatabase(context); 72 | } 73 | 74 | public static void clearDatabase(Context context){ 75 | NotesDatabase db = NotesDatabase.getInstance(context); 76 | // close before deleting 77 | db.close(); 78 | // remove database file 79 | context.deleteDatabase(db.getDatabaseName()); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /app/src/main/res/anim/slide_in_right.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aykit/MyOwnNotes/225efdb1f483df815ef467f2351747b82b786c54/app/src/main/res/drawable-hdpi/logo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-ldpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aykit/MyOwnNotes/225efdb1f483df815ef467f2351747b82b786c54/app/src/main/res/drawable-ldpi/logo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aykit/MyOwnNotes/225efdb1f483df815ef467f2351747b82b786c54/app/src/main/res/drawable-mdpi/logo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aykit/MyOwnNotes/225efdb1f483df815ef467f2351747b82b786c54/app/src/main/res/drawable-xhdpi/logo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aykit/MyOwnNotes/225efdb1f483df815ef467f2351747b82b786c54/app/src/main/res/drawable-xxhdpi/logo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aykit/MyOwnNotes/225efdb1f483df815ef467f2351747b82b786c54/app/src/main/res/drawable-xxxhdpi/logo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_add_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_cloud_upload_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_delete_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_exit_to_app_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/layout-sw600dp/activity_note_list.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 13 | 14 | 17 | 18 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_login.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 16 | 17 | 22 | 23 | 24 | 25 | 30 | 31 | 39 | 40 | 44 | 45 |