├── .gitattributes ├── .gitignore ├── README.md ├── app ├── .gitignore ├── build-helpers.gradle ├── build.gradle ├── libs │ └── rollbar-android-0.1.2.jar ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── fi │ │ └── aalto │ │ └── legroup │ │ └── achso │ │ └── ApplicationTest.java │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── fi │ │ └── aalto │ │ └── legroup │ │ └── achso │ │ ├── app │ │ ├── App.java │ │ ├── AppAnalytics.java │ │ ├── AppBus.java │ │ ├── AppCache.java │ │ └── AppPreferences.java │ │ ├── authentication │ │ ├── AccountLoggedOutEvent.java │ │ ├── AuthenticatedHttpClient.java │ │ ├── Authenticator.java │ │ ├── AuthenticatorService.java │ │ ├── AuthorizationActivity.java │ │ ├── LoginActivity.java │ │ ├── LoginErrorEvent.java │ │ ├── LoginManager.java │ │ ├── LoginRequestEvent.java │ │ ├── LoginStateEvent.java │ │ ├── OIDCConfig.java │ │ ├── OIDCNotReadyException.java │ │ └── OIDCUtils.java │ │ ├── authoring │ │ ├── ExportDialogFragment.java │ │ ├── ExportHelper.java │ │ ├── LocationManager.java │ │ ├── QRHelper.java │ │ ├── VideoCreatorService.java │ │ ├── VideoDeletionFragment.java │ │ ├── VideoHelper.java │ │ └── VideoTrimActivity.java │ │ ├── browsing │ │ ├── BrowserActivity.java │ │ ├── BrowserFragment.java │ │ ├── DetailActivity.java │ │ ├── DetailFragment.java │ │ └── SearchActivity.java │ │ ├── entities │ │ ├── Annotation.java │ │ ├── Group.java │ │ ├── OptimizedVideo.java │ │ ├── PooledVideo.java │ │ ├── User.java │ │ ├── UserPool.java │ │ ├── Video.java │ │ ├── VideoReference.java │ │ ├── migration │ │ │ ├── LocalizedGenreToGenreMigration.java │ │ │ └── VideoMigration.java │ │ └── serialization │ │ │ ├── Serializable.java │ │ │ ├── Serializer.java │ │ │ └── json │ │ │ ├── DateTypeConverter.java │ │ │ ├── JsonSerializable.java │ │ │ ├── JsonSerializer.java │ │ │ ├── LocationTypeConverter.java │ │ │ └── UriTypeConverter.java │ │ ├── playback │ │ ├── AnnotationEditor.java │ │ ├── PlayerActivity.java │ │ ├── PlayerFragment.java │ │ ├── annotations │ │ │ ├── AnnotationRenderer.java │ │ │ ├── MarkerStrategy.java │ │ │ └── SubtitleStrategy.java │ │ └── utilities │ │ │ ├── FrameworkOrientationReader.java │ │ │ ├── Mp4ParserOrientationReader.java │ │ │ ├── VideoOrientationPatcher.java │ │ │ └── VideoOrientationReader.java │ │ ├── settings │ │ ├── SettingsActivity.java │ │ └── SettingsFragment.java │ │ ├── sharing │ │ ├── AchRailsJavascriptInterface.java │ │ └── SharingActivity.java │ │ ├── storage │ │ ├── CombinedVideoRepository.java │ │ ├── VideoInfoRepository.java │ │ ├── VideoRepository.java │ │ ├── VideoRepositoryUpdatedEvent.java │ │ └── remote │ │ │ ├── SyncRequiredEvent.java │ │ │ ├── SyncService.java │ │ │ ├── TransferErrorEvent.java │ │ │ ├── TransferStateEvent.java │ │ │ ├── VideoHost.java │ │ │ ├── download │ │ │ ├── DownloadErrorEvent.java │ │ │ ├── DownloadService.java │ │ │ └── DownloadStateEvent.java │ │ │ ├── strategies │ │ │ ├── AchRailsStrategy.java │ │ │ ├── ClViTra2Strategy.java │ │ │ ├── DumbPhpStrategy.java │ │ │ ├── DummyStrategy.java │ │ │ └── GoViTraStrategy.java │ │ │ └── upload │ │ │ ├── UploadErrorEvent.java │ │ │ ├── UploadService.java │ │ │ ├── UploadStateEvent.java │ │ │ └── uploaders │ │ │ ├── MetadataUploader.java │ │ │ ├── ThumbnailUploader.java │ │ │ └── VideoUploader.java │ │ ├── support │ │ ├── AboutDialogFragment.java │ │ ├── FeedbackDialogFragment.java │ │ ├── MobSosDialogFragment.java │ │ ├── MobSosService.java │ │ └── OsTicketService.java │ │ ├── utilities │ │ ├── AchsoFirebaseInstanceIdService.java │ │ ├── AchsoFirebaseMessagingService.java │ │ ├── BaseActivity.java │ │ ├── EmptyCallback.java │ │ ├── ProgressDialogFragment.java │ │ ├── RepeatingTask.java │ │ └── TranslationHelper.java │ │ └── views │ │ ├── MarkedSeekBar.java │ │ ├── Marker.java │ │ ├── MarkerCanvas.java │ │ ├── RecyclerItemClickListener.java │ │ ├── VideoRefreshLayout.java │ │ ├── ZoomView.java │ │ ├── adapters │ │ ├── AnnotationsListAdapter.java │ │ ├── GroupsListAdapter.java │ │ ├── VideoGridAdapter.java │ │ └── VideoTabAdapter.java │ │ └── utilities │ │ ├── DimensionUnits.java │ │ ├── ScrollDirectionListenable.java │ │ └── ThemeColors.java │ └── res │ ├── drawable-hdpi │ ├── ic_action_pause.png │ ├── ic_action_play.png │ ├── ic_add_to_photos_white_24dp.png │ ├── ic_close_white_24dp.png │ ├── ic_cloud_done_white_24dp.png │ ├── ic_cloud_download_white_24dp.png │ ├── ic_cloud_off_white_24dp.png │ ├── ic_cloud_queue_white_24dp.png │ ├── ic_cloud_upload_white_24dp.png │ ├── ic_content_cut_white_24dp.png │ ├── ic_delete_white_24dp.png │ ├── ic_done_white_24dp.png │ ├── ic_done_white_48dp.png │ ├── ic_edit_black_24dp.png │ ├── ic_error_white_24dp.png │ ├── ic_favorite_white_24dp.png │ ├── ic_file_download_white_24dp.png │ ├── ic_group_white_24dp.png │ ├── ic_info_white_24dp.png │ ├── ic_launcher.png │ ├── ic_play_circle_outline_white_48dp.png │ ├── ic_qrcode_black_24dp.png │ ├── ic_qrcode_white_24dp.png │ ├── ic_search_white_24dp.png │ ├── ic_share_variant_white_24dp.png │ ├── ic_sync_white_24dp.png │ └── ic_videocam_white_24dp.png │ ├── drawable-mdpi │ ├── ic_action_edit.png │ ├── ic_action_pause.png │ ├── ic_action_play.png │ ├── ic_add_to_photos_white_24dp.png │ ├── ic_close_white_24dp.png │ ├── ic_cloud_done_white_24dp.png │ ├── ic_cloud_download_white_24dp.png │ ├── ic_cloud_off_white_24dp.png │ ├── ic_cloud_queue_white_24dp.png │ ├── ic_cloud_upload_white_24dp.png │ ├── ic_content_cut_white_24dp.png │ ├── ic_delete_white_24dp.png │ ├── ic_done_white_24dp.png │ ├── ic_done_white_48dp.png │ ├── ic_edit_black_24dp.png │ ├── ic_error_white_24dp.png │ ├── ic_favorite_white_24dp.png │ ├── ic_file_download_white_24dp.png │ ├── ic_group_white_24dp.png │ ├── ic_info_white_24dp.png │ ├── ic_launcher.png │ ├── ic_play_circle_outline_white_48dp.png │ ├── ic_qrcode_black_24dp.png │ ├── ic_qrcode_white_24dp.png │ ├── ic_search_white_24dp.png │ ├── ic_share_variant_white_24dp.png │ ├── ic_sync_white_24dp.png │ └── ic_videocam_white_24dp.png │ ├── drawable-xhdpi │ ├── ic_action_edit.png │ ├── ic_action_pause.png │ ├── ic_action_play.png │ ├── ic_add_to_photos_white_24dp.png │ ├── ic_close_white_24dp.png │ ├── ic_cloud_done_white_24dp.png │ ├── ic_cloud_download_white_24dp.png │ ├── ic_cloud_off_white_24dp.png │ ├── ic_cloud_queue_white_24dp.png │ ├── ic_cloud_upload_white_24dp.png │ ├── ic_content_cut_white_24dp.png │ ├── ic_delete_white_24dp.png │ ├── ic_done_white_24dp.png │ ├── ic_done_white_48dp.png │ ├── ic_edit_black_24dp.png │ ├── ic_error_white_24dp.png │ ├── ic_favorite_white_24dp.png │ ├── ic_file_download_white_24dp.png │ ├── ic_group_white_24dp.png │ ├── ic_info_white_24dp.png │ ├── ic_launcher.png │ ├── ic_play_circle_outline_white_48dp.png │ ├── ic_qrcode_black_24dp.png │ ├── ic_qrcode_white_24dp.png │ ├── ic_search_white_24dp.png │ ├── ic_share_variant_white_24dp.png │ ├── ic_sync_white_24dp.png │ └── ic_videocam_white_24dp.png │ ├── drawable-xxhdpi │ ├── ic_action_edit.png │ ├── ic_action_pause.png │ ├── ic_action_play.png │ ├── ic_add_to_photos_white_24dp.png │ ├── ic_close_white_24dp.png │ ├── ic_cloud_done_white_24dp.png │ ├── ic_cloud_download_white_24dp.png │ ├── ic_cloud_off_white_24dp.png │ ├── ic_cloud_queue_white_24dp.png │ ├── ic_cloud_upload_white_24dp.png │ ├── ic_content_cut_white_24dp.png │ ├── ic_delete_white_24dp.png │ ├── ic_done_white_24dp.png │ ├── ic_done_white_48dp.png │ ├── ic_edit_black_24dp.png │ ├── ic_error_white_24dp.png │ ├── ic_favorite_white_24dp.png │ ├── ic_file_download_white_24dp.png │ ├── ic_group_white_24dp.png │ ├── ic_info_white_24dp.png │ ├── ic_launcher.png │ ├── ic_play_circle_outline_white_48dp.png │ ├── ic_qrcode_black_24dp.png │ ├── ic_qrcode_white_24dp.png │ ├── ic_search_white_24dp.png │ ├── ic_share_variant_white_24dp.png │ ├── ic_sync_white_24dp.png │ └── ic_videocam_white_24dp.png │ ├── drawable-xxxhdpi │ ├── ic_add_to_photos_white_24dp.png │ ├── ic_close_white_24dp.png │ ├── ic_cloud_done_white_24dp.png │ ├── ic_cloud_download_white_24dp.png │ ├── ic_cloud_off_white_24dp.png │ ├── ic_cloud_queue_white_24dp.png │ ├── ic_cloud_upload_white_24dp.png │ ├── ic_content_cut_white_24dp.png │ ├── ic_delete_white_24dp.png │ ├── ic_done_white_24dp.png │ ├── ic_done_white_48dp.png │ ├── ic_edit_black_24dp.png │ ├── ic_error_white_24dp.png │ ├── ic_favorite_white_24dp.png │ ├── ic_file_download_white_24dp.png │ ├── ic_group_white_24dp.png │ ├── ic_info_white_24dp.png │ ├── ic_play_circle_outline_white_48dp.png │ ├── ic_qrcode_black_24dp.png │ ├── ic_qrcode_white_24dp.png │ ├── ic_search_white_24dp.png │ ├── ic_share_variant_white_24dp.png │ ├── ic_sync_white_24dp.png │ └── ic_videocam_white_24dp.png │ ├── drawable │ ├── annotation_marker.xml │ ├── annotation_marker_inner_ring.xml │ ├── annotation_marker_inner_ring_shadow.xml │ ├── annotation_marker_middle_ring.xml │ ├── annotation_marker_outer_ring.xml │ ├── bottom_scrim.xml │ ├── floating_controls_background.xml │ ├── material_button_fallback_dark.xml │ ├── material_button_fallback_dark_normal.xml │ ├── material_button_fallback_dark_pressed.xml │ └── top_scrim.xml │ ├── layout-land │ └── activity_information.xml │ ├── layout │ ├── activity_authentication.xml │ ├── activity_browser.xml │ ├── activity_information.xml │ ├── activity_player.xml │ ├── activity_search.xml │ ├── activity_settings.xml │ ├── activity_sharing.xml │ ├── activity_video_trim.xml │ ├── dialog_about.xml │ ├── dialog_information.xml │ ├── dialog_mobsos.xml │ ├── fragment_browser.xml │ ├── fragment_export.xml │ ├── fragment_feedback.xml │ ├── fragment_information.xml │ ├── fragment_player.xml │ ├── item_browser_grid.xml │ ├── partial_annotation_controls.xml │ ├── partial_annotation_list_item.xml │ ├── partial_group_list_item.xml │ ├── partial_playback_controls.xml │ ├── partial_toolbar.xml │ ├── partial_toolbar_with_nav_button.xml │ ├── partial_toolbar_with_nav_button_transparent.xml │ ├── partial_trim_controls.xml │ ├── spinner_item.xml │ └── subtitle.xml │ ├── menu │ ├── activity_browser.xml │ ├── activity_browser_action_mode.xml │ ├── activity_player.xml │ └── activity_search.xml │ ├── values-de │ └── strings.xml │ ├── values-et │ └── strings.xml │ ├── values-fi │ └── strings.xml │ ├── values-land │ └── dimens.xml │ ├── values-sw600dp-land │ └── dimens.xml │ ├── values-sw600dp │ └── dimens.xml │ ├── values-v21 │ └── styles.xml │ ├── values-w820dp │ └── dimens.xml │ ├── values │ ├── .gitignore │ ├── .secrets.xml │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ ├── styles.xml │ └── themes.xml │ └── xml │ ├── authenticator.xml │ ├── preferences.xml │ └── searchable.xml ├── build.gradle ├── docs ├── achso_conceptual_description.txt ├── achso_server_connection.md ├── assets │ └── large_record.graffle └── i5OpenIdConnectInAchSo.md ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Rest are created by http://www.gitignore.io 2 | 3 | ### Android ### 4 | # Built application files 5 | *.apk 6 | *.ap_ 7 | 8 | # Keystores for publishing and signing app 9 | *.keystore 10 | 11 | # Files for the Dalvik VM 12 | *.dex 13 | 14 | # Java class files 15 | *.class 16 | 17 | # Generated files 18 | bin/ 19 | gen/ 20 | 21 | # Gradle files 22 | .gradle/ 23 | build/ 24 | 25 | # Local configuration file (sdk path, etc) 26 | local.properties 27 | 28 | # Proguard folder generated by Eclipse 29 | proguard/ 30 | 31 | # Log Files 32 | *.log 33 | 34 | 35 | ### OSX ### 36 | .DS_Store 37 | .AppleDouble 38 | .LSOverride 39 | 40 | # Icon must end with two \r 41 | Icon 42 | 43 | 44 | # Thumbnails 45 | ._* 46 | 47 | # Files that might appear on external disk 48 | .Spotlight-V100 49 | .Trashes 50 | 51 | # Directories potentially created on remote AFP share 52 | .AppleDB 53 | .AppleDesktop 54 | Network Trash Folder 55 | Temporary Items 56 | .apdisk 57 | 58 | 59 | ### Linux ### 60 | *~ 61 | 62 | # KDE directory preferences 63 | .directory 64 | 65 | 66 | ### Windows ### 67 | # Windows image file caches 68 | Thumbs.db 69 | ehthumbs.db 70 | 71 | # Folder config file 72 | Desktop.ini 73 | 74 | # Recycle Bin used on file shares 75 | $RECYCLE.BIN/ 76 | 77 | # Windows Installer files 78 | *.cab 79 | *.msi 80 | *.msm 81 | *.msp 82 | 83 | 84 | ### Intellij ### 85 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm 86 | 87 | ## Directory-based project format 88 | .idea/ 89 | /*.iml 90 | *.iml 91 | app/app.iml 92 | # if you remove the above rule, at least ignore user-specific stuff: 93 | # .idea/workspace.xml 94 | # .idea/tasks.xml 95 | # .idea/dictionaries 96 | # and these sensitive or high-churn files: 97 | # .idea/dataSources.ids 98 | # .idea/dataSources.xml 99 | # .idea/sqlDataSources.xml 100 | # .idea/dynamic.xml 101 | # and, if using gradle:: 102 | # .idea/gradle.xml 103 | # .idea/libraries 104 | 105 | ## File-based project format 106 | *.ipr 107 | *.iws 108 | 109 | ## Additional for IntelliJ 110 | out/ 111 | 112 | # generated by mpeltonen/sbt-idea plugin 113 | .idea_modules/ 114 | 115 | # generated by JIRA plugin 116 | atlassian-ide-plugin.xml 117 | 118 | # generated by Crashlytics plugin (for Android Studio and Intellij) 119 | com_crashlytics_export_strings.xml 120 | 121 | # generated by Android Studio when building a release APK 122 | manifest-merger-release-report.txt 123 | 124 | # Android Studio heap captures 125 | captures/ 126 | app/google-services.json 127 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build-helpers.gradle: -------------------------------------------------------------------------------- 1 | /** 2 | * Executes a process and checks that it returns successfully. 3 | * 4 | * @param processArray A list where the first item is the executable and the rest arguments. 5 | * @return The executed process. 6 | * @throws IOException If the process returns a non-zero exit code. 7 | */ 8 | Process process(List processArray) { 9 | def process = processArray.execute() 10 | def status = process.waitFor() 11 | 12 | if (status != 0) { 13 | def errors = process.err.text 14 | throw new IOException("Process exited with non-zero code ${status}.\n\n${errors}") 15 | } 16 | 17 | process 18 | } 19 | 20 | /** 21 | * Returns the latest tag, the number of commits after it, and the latest commit SHA. The SHA will 22 | * have an asterisk as the suffix if the project contains staged changes. 23 | */ 24 | String getVersionName() { 25 | def args = [ 26 | 'git', 27 | "--git-dir=${rootDir}/.git", 28 | "--work-tree=${rootDir}", 29 | 'describe', 30 | '--tags', 31 | '--long', 32 | '--always', 33 | '--dirty=*' 34 | ] 35 | 36 | def git = this.process(args) as Process 37 | 38 | def (version, build, sha) = git.text.trim().tokenize('-') 39 | 40 | // Remove the g prefix in the SHA that stands for Git 41 | sha = sha.replace('g', '') 42 | 43 | "${version} (${build} @ ${sha})" 44 | } 45 | 46 | /** 47 | * Returns a UNIX timestamp of the latest commit. 48 | */ 49 | long getVersionCode() { 50 | def args = [ 51 | 'git', 52 | "--git-dir=${rootDir}/.git", 53 | "--work-tree=${rootDir}", 54 | 'show', 55 | '--quiet', 56 | '--format=%at' 57 | ] 58 | 59 | def git = this.process(args) as Process 60 | 61 | git.text.trim() as long 62 | } 63 | 64 | ext { 65 | getGitVersions = { 66 | def versionName = this.versionName 67 | def versionCode = this.versionCode 68 | 69 | print "\n" 70 | println "~~~~~~~~~~~~~~~ VERSION DATA ~~~~~~~~~~~~~~~" 71 | println "versionName: ${versionName}" 72 | println "versionCode: ${versionCode}" 73 | println "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" 74 | print "\n" 75 | 76 | [versionName, versionCode] 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /app/libs/rollbar-android-0.1.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/learning-layers/AchSo/5067299ba8fc11371858aeb14f5dbdcbbe132b04/app/libs/rollbar-android-0.1.2.jar -------------------------------------------------------------------------------- /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 /Applications/Android Studio.app/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 | ##### 13 | # General 14 | -dontobfuscate 15 | -keepattributes Signature, *Annotation* 16 | 17 | ##### 18 | # Retrofit 19 | -dontwarn rx.** 20 | -dontwarn com.google.appengine.api.urlfetch.* 21 | 22 | -keep class retrofit.** { *; } 23 | -keepclasseswithmembers class * { 24 | @retrofit.http.* ; 25 | } 26 | 27 | ##### 28 | # Gson 29 | -keep class sun.misc.Unsafe { *; } 30 | 31 | ##### 32 | # Okio 33 | -dontwarn java.nio.file.* 34 | -dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement 35 | 36 | ##### 37 | # OkHttp 38 | -dontwarn com.squareup.okhttp.** 39 | -keepnames class com.squareup.okhttp.** { *; } 40 | -keepnames interface com.squareup.okhttp.** { *; } 41 | 42 | ##### 43 | # Otto 44 | -keepclassmembers class ** { 45 | @com.squareup.otto.Subscribe public *; 46 | @com.squareup.otto.Produce public *; 47 | } 48 | 49 | ##### 50 | # Joda-Time 51 | -dontwarn org.joda.convert.** 52 | 53 | ##### 54 | # mp4parser 55 | -keep class com.coremedia.iso.boxes.** { *; } 56 | 57 | ##### 58 | # Guava 59 | -dontwarn sun.misc.Unsafe 60 | -dontwarn com.google.common.collect.MinMaxPriorityQueue 61 | 62 | ##### 63 | # Support library v7 64 | -keep class android.support.v7.** { *; } 65 | -keep interface android.support.v7.** { *; } 66 | 67 | ##### 68 | # Support library v4 69 | -keep class android.support.v4.** { *; } 70 | -keep interface android.support.v4.** { *; } 71 | 72 | ##### 73 | # Google Play Services 74 | -keep class * extends java.util.ListResourceBundle { 75 | protected Object[][] getContents(); 76 | } 77 | 78 | -keep public class com.google.android.gms.common.internal.safeparcel.SafeParcelable { 79 | public static final *** NULL; 80 | } 81 | 82 | -keepnames @com.google.android.gms.common.annotation.KeepName class * 83 | -keepclassmembernames class * { 84 | @com.google.android.gms.common.annotation.KeepName *; 85 | } 86 | 87 | -keepnames class * implements android.os.Parcelable { 88 | public static final ** CREATOR; 89 | } 90 | 91 | # Apache HTTP client 92 | -keep class org.apache.http.** { *; } 93 | -dontwarn org.apache.http.* 94 | 95 | -keepclassmembers class fi.aalto.legroup.achso.sharing.AchRailsJavascriptInterface { 96 | public *; 97 | } 98 | 99 | # If your project uses WebView with JS, uncomment the following 100 | # and specify the fully qualified class name to the JavaScript interface 101 | # class: 102 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 103 | # public *; 104 | #} 105 | -------------------------------------------------------------------------------- /app/src/androidTest/java/fi/aalto/legroup/achso/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package fi.aalto.legroup.achso; 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 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/fi/aalto/legroup/achso/app/AppAnalytics.java: -------------------------------------------------------------------------------- 1 | package fi.aalto.legroup.achso.app; 2 | 3 | import android.content.Context; 4 | 5 | import com.google.android.gms.analytics.GoogleAnalytics; 6 | import com.google.android.gms.analytics.Logger; 7 | import com.google.android.gms.analytics.Tracker; 8 | 9 | import java.util.Map; 10 | 11 | import fi.aalto.legroup.achso.BuildConfig; 12 | import fi.aalto.legroup.achso.R; 13 | 14 | /** 15 | * Convenience methods and parameters for Google Analytics. 16 | */ 17 | public final class AppAnalytics { 18 | 19 | // Categories 20 | public static final String CATEGORY_VIDEOS = "Videos"; 21 | 22 | // Actions 23 | public static final String ACTION_CREATE = "Create"; 24 | 25 | private static Tracker tracker; 26 | 27 | private AppAnalytics() { 28 | // Static access only 29 | } 30 | 31 | public static void setup(Context context) { 32 | GoogleAnalytics analytics = GoogleAnalytics.getInstance(context); 33 | 34 | tracker = analytics.newTracker(context.getString(R.string.gaPropertyId)); 35 | 36 | // Automatically track activities 37 | tracker.enableAutoActivityTracking(true); 38 | 39 | // Don't send the last octet of the IP address 40 | tracker.setAnonymizeIp(true); 41 | 42 | // Log analytics to the console on debug builds 43 | if (BuildConfig.DEBUG) { 44 | analytics.setDryRun(true); 45 | analytics.getLogger().setLogLevel(Logger.LogLevel.VERBOSE); 46 | } 47 | } 48 | 49 | public static Tracker getTracker() { 50 | return tracker; 51 | } 52 | 53 | public static void send(Map params) { 54 | tracker.send(params); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /app/src/main/java/fi/aalto/legroup/achso/app/AppBus.java: -------------------------------------------------------------------------------- 1 | package fi.aalto.legroup.achso.app; 2 | 3 | import android.os.Handler; 4 | import android.os.Looper; 5 | 6 | import com.squareup.otto.Bus; 7 | 8 | /** 9 | * Ensures that all Otto events are posted on the main thread. 10 | * 11 | * https://github.com/square/otto/issues/38 12 | * 13 | * @author Jake Wharton 14 | * @author pommedeterresautee 15 | */ 16 | public final class AppBus extends Bus { 17 | 18 | private final Handler handler = new Handler(Looper.getMainLooper()); 19 | 20 | @Override 21 | public void post(final Object event) { 22 | if (Looper.myLooper() == Looper.getMainLooper()) { 23 | super.post(event); 24 | } else { 25 | handler.post(new Runnable() { 26 | @Override 27 | public void run() { 28 | AppBus.super.post(event); 29 | } 30 | }); 31 | } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/fi/aalto/legroup/achso/app/AppPreferences.java: -------------------------------------------------------------------------------- 1 | package fi.aalto.legroup.achso.app; 2 | 3 | /** 4 | * Holds preference keys for programmatic usage. 5 | */ 6 | public final class AppPreferences { 7 | 8 | private AppPreferences() { 9 | // Static access only 10 | } 11 | 12 | /** 13 | * The account name used for logging in automatically. 14 | */ 15 | public static final String AUTO_LOGIN_ACCOUNT = "AUTO_LOGIN_ACCOUNT"; 16 | 17 | // NOTE: 18 | // The preference keys below are user-facing. If you change them here, change them in 19 | // preferences.xml also. 20 | 21 | /** 22 | * The base duration for an annotation pause. 23 | */ 24 | public static final String ANNOTATION_PAUSE_DURATION = "ANNOTATION_PAUSE_DURATION"; 25 | 26 | /** 27 | * Layers box base URL. 28 | */ 29 | public static final String LAYERS_BOX_URL = "LAYERS_BOX_URL"; 30 | 31 | /** 32 | * Whether analytics data should be sent. 33 | */ 34 | public static final String ANALYTICS_OPT_IN = "ANALYTICS_OPT_IN"; 35 | 36 | /** 37 | * Whether to use the public Layers servers or a private Box. 38 | */ 39 | public static final String USE_PUBLIC_LAYERS_BOX = "USE_PUBLIC_LAYERS_BOX"; 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/fi/aalto/legroup/achso/authentication/AccountLoggedOutEvent.java: -------------------------------------------------------------------------------- 1 | package fi.aalto.legroup.achso.authentication; 2 | 3 | import android.accounts.Account; 4 | 5 | /** 6 | * Created by mat on 19/04/2017. 7 | */ 8 | 9 | public class AccountLoggedOutEvent { 10 | Account account; 11 | 12 | public AccountLoggedOutEvent(Account account) { 13 | this.account = account; 14 | } 15 | 16 | public Account getAccount() { 17 | return account; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/fi/aalto/legroup/achso/authentication/AuthenticatedHttpClient.java: -------------------------------------------------------------------------------- 1 | package fi.aalto.legroup.achso.authentication; 2 | 3 | import android.accounts.Account; 4 | import android.accounts.AccountManager; 5 | import android.content.Context; 6 | import android.util.Log; 7 | 8 | import com.squareup.okhttp.Callback; 9 | import com.squareup.okhttp.OkHttpClient; 10 | import com.squareup.okhttp.Request; 11 | import com.squareup.okhttp.Response; 12 | 13 | import java.io.IOException; 14 | 15 | import static java.net.HttpURLConnection.HTTP_FORBIDDEN; 16 | import static java.net.HttpURLConnection.HTTP_INTERNAL_ERROR; 17 | import static java.net.HttpURLConnection.HTTP_NOT_FOUND; 18 | import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED; 19 | 20 | /** 21 | * Utilises HttpClient to provide automatic token renewal. 22 | */ 23 | public class AuthenticatedHttpClient { 24 | 25 | private final String TAG = getClass().getSimpleName(); 26 | 27 | protected Context context; 28 | protected OkHttpClient httpClient; 29 | 30 | public AuthenticatedHttpClient(Context context, OkHttpClient httpClient) { 31 | this.context = context.getApplicationContext(); 32 | this.httpClient = httpClient; 33 | } 34 | 35 | public String getBearerToken(Account account) { 36 | AccountManager accountManager = AccountManager.get(context); 37 | String token = null; 38 | 39 | // Try retrieving an access token from the account manager 40 | try { 41 | token = accountManager.blockingGetAuthToken(account, Authenticator.TOKEN_TYPE_ACCESS, true); 42 | } catch (Exception e) { 43 | Log.e(TAG, "Could not get access token from account: " + e.getMessage()); 44 | e.printStackTrace(); 45 | } 46 | return token; 47 | } 48 | 49 | public Response execute(Request request, Account account) throws IOException { 50 | return execute(request, account, true); 51 | } 52 | 53 | public Response execute(Request request, Account account, boolean doRetry) throws IOException { 54 | AccountManager accountManager = AccountManager.get(context); 55 | 56 | String token = getBearerToken(account); 57 | 58 | request = request.newBuilder().header("Authorization", "Bearer " + token).build(); 59 | 60 | Response response = httpClient.newCall(request).execute(); 61 | 62 | // If we're being denied access on the first try, let's renew the token and retry 63 | if (accessDenied(response) && doRetry) { 64 | accountManager.invalidateAuthToken(Authenticator.ACH_SO_ACCOUNT_TYPE, token); 65 | return execute(request, account, false); 66 | } 67 | 68 | return response; 69 | } 70 | 71 | public void enqueue(Request request, Account account, Callback cb) { 72 | AccountManager accountManager = AccountManager.get(context); 73 | 74 | String token = getBearerToken(account); 75 | 76 | request = request.newBuilder().header("Authorization", "Bearer " + token).build(); 77 | 78 | httpClient.newCall(request).enqueue(cb); 79 | } 80 | 81 | public boolean accessDenied(Response response) { 82 | int code = response.code(); 83 | 84 | // FIXME: The internal error is for SSS when tokens are expired 85 | return code == HTTP_UNAUTHORIZED 86 | || code == HTTP_FORBIDDEN 87 | || code == HTTP_NOT_FOUND 88 | || code == HTTP_INTERNAL_ERROR; 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /app/src/main/java/fi/aalto/legroup/achso/authentication/AuthenticatorService.java: -------------------------------------------------------------------------------- 1 | package fi.aalto.legroup.achso.authentication; 2 | 3 | import android.app.Service; 4 | import android.content.Intent; 5 | import android.os.IBinder; 6 | 7 | /** 8 | * The service that lets Android know about the custom Authenticator. 9 | */ 10 | public final class AuthenticatorService extends Service { 11 | 12 | @Override 13 | public IBinder onBind(Intent intent) { 14 | Authenticator authenticator = new Authenticator(this); 15 | return authenticator.getIBinder(); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /app/src/main/java/fi/aalto/legroup/achso/authentication/LoginErrorEvent.java: -------------------------------------------------------------------------------- 1 | package fi.aalto.legroup.achso.authentication; 2 | 3 | public class LoginErrorEvent { 4 | 5 | private String message; 6 | 7 | public LoginErrorEvent(String message) { 8 | this.message = message; 9 | } 10 | 11 | public String getMessage() { 12 | return message; 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/java/fi/aalto/legroup/achso/authentication/LoginRequestEvent.java: -------------------------------------------------------------------------------- 1 | package fi.aalto.legroup.achso.authentication; 2 | 3 | public class LoginRequestEvent { 4 | 5 | private Type type; 6 | 7 | public LoginRequestEvent(Type type) { 8 | this.type = type; 9 | } 10 | 11 | public Type getType() { 12 | return type; 13 | } 14 | 15 | public static enum Type { 16 | LOGIN, 17 | LOGOUT, 18 | EXPLICIT_LOGOUT 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/java/fi/aalto/legroup/achso/authentication/LoginStateEvent.java: -------------------------------------------------------------------------------- 1 | package fi.aalto.legroup.achso.authentication; 2 | 3 | /** 4 | * TODO: Make this more specific. 5 | */ 6 | public class LoginStateEvent { 7 | 8 | private LoginManager.LoginState state; 9 | private boolean notifyUser; 10 | 11 | public LoginStateEvent(LoginManager.LoginState state, boolean notifyUser) { 12 | this.state = state; 13 | this.notifyUser = notifyUser; 14 | } 15 | 16 | public LoginManager.LoginState getState() { 17 | return state; 18 | } 19 | 20 | public boolean shouldNotifyUser() { 21 | return notifyUser; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/fi/aalto/legroup/achso/authentication/OIDCNotReadyException.java: -------------------------------------------------------------------------------- 1 | package fi.aalto.legroup.achso.authentication; 2 | 3 | public class OIDCNotReadyException extends Exception { 4 | public OIDCNotReadyException() { 5 | super("OpenID Connect is not ready"); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /app/src/main/java/fi/aalto/legroup/achso/authoring/ExportDialogFragment.java: -------------------------------------------------------------------------------- 1 | package fi.aalto.legroup.achso.authoring; 2 | 3 | import android.app.Activity; 4 | import android.app.Dialog; 5 | import android.app.DialogFragment; 6 | import android.os.Bundle; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.widget.TextView; 10 | 11 | import com.afollestad.materialdialogs.MaterialDialog; 12 | import com.nispok.snackbar.Snackbar; 13 | import com.nispok.snackbar.SnackbarManager; 14 | 15 | import java.util.ArrayList; 16 | 17 | import fi.aalto.legroup.achso.R; 18 | import fi.aalto.legroup.achso.entities.Video; 19 | 20 | public final class ExportDialogFragment extends DialogFragment { 21 | 22 | private static final String ARG_EMAIL = "ARG_EMAIL"; 23 | 24 | private TextView emailText; 25 | private Activity activity; 26 | 27 | private ArrayList