├── .gitignore ├── LICENSE.md ├── README.md ├── VideoInfoViewer └── res │ └── values │ ├── admob_values.xml │ ├── crittercism_values.xml │ └── google_analytics_values.xml ├── app ├── build.gradle ├── libs │ ├── isoviewer-1.0-RC-35.jar │ └── libGoogleAnalyticsServices.jar ├── lint.xml └── src │ └── main │ ├── AndroidManifest.xml │ ├── assets │ ├── apache_license_2.txt │ └── fonts │ │ ├── Roboto-Black.ttf │ │ ├── Roboto-BlackItalic.ttf │ │ ├── Roboto-Bold.ttf │ │ ├── Roboto-BoldCondensed.ttf │ │ ├── Roboto-BoldCondensedItalic.ttf │ │ ├── Roboto-BoldItalic.ttf │ │ ├── Roboto-Condensed.ttf │ │ ├── Roboto-CondensedItalic.ttf │ │ ├── Roboto-Italic.ttf │ │ ├── Roboto-Light.ttf │ │ ├── Roboto-LightItalic.ttf │ │ ├── Roboto-Medium.ttf │ │ ├── Roboto-MediumItalic.ttf │ │ ├── Roboto-Regular.ttf │ │ ├── Roboto-Thin.ttf │ │ └── Roboto-ThinItalic.ttf │ ├── java │ └── com │ │ └── roryhool │ │ └── videoinfoviewer │ │ ├── CreditsFragment.java │ │ ├── Extras.java │ │ ├── MainActivity.java │ │ ├── RecentVideosFragment.java │ │ ├── SearchActivity.java │ │ ├── SearchFragment.java │ │ ├── VideoActivity.java │ │ ├── VideoFragment.java │ │ ├── VideoInfoViewerApp.java │ │ ├── analytics │ │ └── Analytics.java │ │ ├── animation │ │ └── ResizeAnimation.java │ │ ├── atomfragments │ │ ├── AtomInfoFragment.java │ │ └── AtomStructureFragment.java │ │ ├── data │ │ └── Video.java │ │ ├── search │ │ └── SearchItem.java │ │ ├── utils │ │ ├── AtomHelper.java │ │ ├── BoxUtils.java │ │ ├── CursorHelper.java │ │ ├── FontManager.java │ │ ├── FormatUtils.java │ │ ├── IsoFileCache.java │ │ ├── Logg.java │ │ ├── UriHelper.java │ │ ├── VideoCache.java │ │ └── ViewUtils.java │ │ └── views │ │ ├── BoxInfoView.java │ │ ├── DisableableScrollView.java │ │ ├── RobotoTextView.java │ │ ├── ScaledTextureView.java │ │ └── VideoPlayerView.java │ └── res │ ├── drawable-hdpi │ ├── ic_add_white_36dp.png │ ├── ic_arrow_forward_black_48dp.png │ ├── ic_expand.png │ ├── ic_launcher.png │ ├── ic_media_pause2.png │ ├── ic_media_play2.png │ ├── ic_menu_add.png │ ├── ic_menu_info_details.png │ ├── ic_vidcontrol_fullscreen_off.png │ └── ic_vidcontrol_fullscreen_on.png │ ├── drawable-mdpi │ ├── ic_add_white_36dp.png │ ├── ic_arrow_forward_black_48dp.png │ ├── ic_expand.png │ ├── ic_launcher.png │ ├── ic_media_pause2.png │ ├── ic_media_play2.png │ ├── ic_menu_add.png │ ├── ic_menu_info_details.png │ ├── ic_vidcontrol_fullscreen_off.png │ └── ic_vidcontrol_fullscreen_on.png │ ├── drawable-nodpi │ └── activity_atom.xml │ ├── drawable-xhdpi │ ├── ic_add_white_36dp.png │ ├── ic_arrow_forward_black_48dp.png │ ├── ic_expand.png │ ├── ic_launcher.png │ ├── ic_media_pause2.png │ ├── ic_media_play2.png │ ├── ic_menu_add.png │ ├── ic_menu_info_details.png │ ├── ic_vidcontrol_fullscreen_off.png │ └── ic_vidcontrol_fullscreen_on.png │ ├── drawable-xxhdpi │ ├── ic_add_white_36dp.png │ ├── ic_arrow_forward_black_48dp.png │ ├── ic_launcher.png │ ├── ic_menu_add.png │ └── ic_menu_info_details.png │ ├── drawable-xxxhdpi │ ├── ic_add_white_36dp.png │ └── ic_arrow_forward_black_48dp.png │ ├── drawable │ ├── button_selector.xml │ ├── expand_background_selector.xml │ ├── expand_button_selector.xml │ ├── info_button_selector.xml │ └── video_selector.xml │ ├── layout │ ├── activity_atom.xml │ ├── activity_main.xml │ ├── activity_search.xml │ ├── activity_video.xml │ ├── atom.xml │ ├── box_info.xml │ ├── fifty_fifty_layout.xml │ ├── fragment_atom_structure.xml │ ├── fragment_box_info.xml │ ├── fragment_credits.xml │ ├── fragment_recent_videos.xml │ ├── fragment_search.xml │ ├── fragment_video.xml │ ├── matrix_item_layout.xml │ ├── matrix_layout.xml │ ├── recent_video_layout.xml │ ├── search_item.xml │ ├── string_array_layout.xml │ ├── toolbar.xml │ └── video_player.xml │ ├── menu │ ├── main.xml │ └── video.xml │ ├── values-sw600dp │ └── dimens.xml │ ├── values-sw720dp-land │ └── dimens.xml │ ├── values-v21 │ └── styles.xml │ ├── values │ ├── admob_values.xml │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml │ └── xml │ ├── global_tracker.xml │ └── searchable_video.xml ├── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── import-summary.txt └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # files for the dex VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # generated files 12 | bin/ 13 | 14 | gen/ 15 | 16 | obj/ 17 | 18 | # Android Studio 19 | .idea 20 | #.idea/workspace.xml - remove # and delete .idea if it better suit your needs. 21 | .gradle 22 | build/ 23 | .gradle/ 24 | 25 | .DS_Store 26 | 27 | **/build 28 | *.iml 29 | *.iws 30 | *.ipr 31 | *.swp 32 | *~ 33 | 34 | # Local environment setup 35 | local.properties 36 | fabric.properties -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | 2 | Copyright (c) 2016 Rory Hool 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | 13 | 14 | Apache License 15 | Version 2.0, January 2004 16 | http://www.apache.org/licenses/ 17 | 18 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 19 | 20 | 1. Definitions. 21 | 22 | "License" shall mean the terms and conditions for use, reproduction, 23 | and distribution as defined by Sections 1 through 9 of this document. 24 | 25 | "Licensor" shall mean the copyright owner or entity authorized by 26 | the copyright owner that is granting the License. 27 | 28 | "Legal Entity" shall mean the union of the acting entity and all 29 | other entities that control, are controlled by, or are under common 30 | control with that entity. For the purposes of this definition, 31 | "control" means (i) the power, direct or indirect, to cause the 32 | direction or management of such entity, whether by contract or 33 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 34 | outstanding shares, or (iii) beneficial ownership of such entity. 35 | 36 | "You" (or "Your") shall mean an individual or Legal Entity 37 | exercising permissions granted by this License. 38 | 39 | "Source" form shall mean the preferred form for making modifications, 40 | including but not limited to software source code, documentation 41 | source, and configuration files. 42 | 43 | "Object" form shall mean any form resulting from mechanical 44 | transformation or translation of a Source form, including but 45 | not limited to compiled object code, generated documentation, 46 | and conversions to other media types. 47 | 48 | "Work" shall mean the work of authorship, whether in Source or 49 | Object form, made available under the License, as indicated by a 50 | copyright notice that is included in or attached to the work 51 | (an example is provided in the Appendix below). 52 | 53 | "Derivative Works" shall mean any work, whether in Source or Object 54 | form, that is based on (or derived from) the Work and for which the 55 | editorial revisions, annotations, elaborations, or other modifications 56 | represent, as a whole, an original work of authorship. For the purposes 57 | of this License, Derivative Works shall not include works that remain 58 | separable from, or merely link (or bind by name) to the interfaces of, 59 | the Work and Derivative Works thereof. 60 | 61 | "Contribution" shall mean any work of authorship, including 62 | the original version of the Work and any modifications or additions 63 | to that Work or Derivative Works thereof, that is intentionally 64 | submitted to Licensor for inclusion in the Work by the copyright owner 65 | or by an individual or Legal Entity authorized to submit on behalf of 66 | the copyright owner. For the purposes of this definition, "submitted" 67 | means any form of electronic, verbal, or written communication sent 68 | to the Licensor or its representatives, including but not limited to 69 | communication on electronic mailing lists, source code control systems, 70 | and issue tracking systems that are managed by, or on behalf of, the 71 | Licensor for the purpose of discussing and improving the Work, but 72 | excluding communication that is conspicuously marked or otherwise 73 | designated in writing by the copyright owner as "Not a Contribution." 74 | 75 | "Contributor" shall mean Licensor and any individual or Legal Entity 76 | on behalf of whom a Contribution has been received by Licensor and 77 | subsequently incorporated within the Work. 78 | 79 | 2. Grant of Copyright License. Subject to the terms and conditions of 80 | this License, each Contributor hereby grants to You a perpetual, 81 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 82 | copyright license to reproduce, prepare Derivative Works of, 83 | publicly display, publicly perform, sublicense, and distribute the 84 | Work and such Derivative Works in Source or Object form. 85 | 86 | 3. Grant of Patent License. Subject to the terms and conditions of 87 | this License, each Contributor hereby grants to You a perpetual, 88 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 89 | (except as stated in this section) patent license to make, have made, 90 | use, offer to sell, sell, import, and otherwise transfer the Work, 91 | where such license applies only to those patent claims licensable 92 | by such Contributor that are necessarily infringed by their 93 | Contribution(s) alone or by combination of their Contribution(s) 94 | with the Work to which such Contribution(s) was submitted. If You 95 | institute patent litigation against any entity (including a 96 | cross-claim or counterclaim in a lawsuit) alleging that the Work 97 | or a Contribution incorporated within the Work constitutes direct 98 | or contributory patent infringement, then any patent licenses 99 | granted to You under this License for that Work shall terminate 100 | as of the date such litigation is filed. 101 | 102 | 4. Redistribution. You may reproduce and distribute copies of the 103 | Work or Derivative Works thereof in any medium, with or without 104 | modifications, and in Source or Object form, provided that You 105 | meet the following conditions: 106 | 107 | (a) You must give any other recipients of the Work or 108 | Derivative Works a copy of this License; and 109 | 110 | (b) You must cause any modified files to carry prominent notices 111 | stating that You changed the files; and 112 | 113 | (c) You must retain, in the Source form of any Derivative Works 114 | that You distribute, all copyright, patent, trademark, and 115 | attribution notices from the Source form of the Work, 116 | excluding those notices that do not pertain to any part of 117 | the Derivative Works; and 118 | 119 | (d) If the Work includes a "NOTICE" text file as part of its 120 | distribution, then any Derivative Works that You distribute must 121 | include a readable copy of the attribution notices contained 122 | within such NOTICE file, excluding those notices that do not 123 | pertain to any part of the Derivative Works, in at least one 124 | of the following places: within a NOTICE text file distributed 125 | as part of the Derivative Works; within the Source form or 126 | documentation, if provided along with the Derivative Works; or, 127 | within a display generated by the Derivative Works, if and 128 | wherever such third-party notices normally appear. The contents 129 | of the NOTICE file are for informational purposes only and 130 | do not modify the License. You may add Your own attribution 131 | notices within Derivative Works that You distribute, alongside 132 | or as an addendum to the NOTICE text from the Work, provided 133 | that such additional attribution notices cannot be construed 134 | as modifying the License. 135 | 136 | You may add Your own copyright statement to Your modifications and 137 | may provide additional or different license terms and conditions 138 | for use, reproduction, or distribution of Your modifications, or 139 | for any such Derivative Works as a whole, provided Your use, 140 | reproduction, and distribution of the Work otherwise complies with 141 | the conditions stated in this License. 142 | 143 | 5. Submission of Contributions. Unless You explicitly state otherwise, 144 | any Contribution intentionally submitted for inclusion in the Work 145 | by You to the Licensor shall be under the terms and conditions of 146 | this License, without any additional terms or conditions. 147 | Notwithstanding the above, nothing herein shall supersede or modify 148 | the terms of any separate license agreement you may have executed 149 | with Licensor regarding such Contributions. 150 | 151 | 6. Trademarks. This License does not grant permission to use the trade 152 | names, trademarks, service marks, or product names of the Licensor, 153 | except as required for reasonable and customary use in describing the 154 | origin of the Work and reproducing the content of the NOTICE file. 155 | 156 | 7. Disclaimer of Warranty. Unless required by applicable law or 157 | agreed to in writing, Licensor provides the Work (and each 158 | Contributor provides its Contributions) on an "AS IS" BASIS, 159 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 160 | implied, including, without limitation, any warranties or conditions 161 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 162 | PARTICULAR PURPOSE. You are solely responsible for determining the 163 | appropriateness of using or redistributing the Work and assume any 164 | risks associated with Your exercise of permissions under this License. 165 | 166 | 8. Limitation of Liability. In no event and under no legal theory, 167 | whether in tort (including negligence), contract, or otherwise, 168 | unless required by applicable law (such as deliberate and grossly 169 | negligent acts) or agreed to in writing, shall any Contributor be 170 | liable to You for damages, including any direct, indirect, special, 171 | incidental, or consequential damages of any character arising as a 172 | result of this License or out of the use or inability to use the 173 | Work (including but not limited to damages for loss of goodwill, 174 | work stoppage, computer failure or malfunction, or any and all 175 | other commercial damages or losses), even if such Contributor 176 | has been advised of the possibility of such damages. 177 | 178 | 9. Accepting Warranty or Additional Liability. While redistributing 179 | the Work or Derivative Works thereof, You may choose to offer, 180 | and charge a fee for, acceptance of support, warranty, indemnity, 181 | or other liability obligations and/or rights consistent with this 182 | License. However, in accepting such obligations, You may act only 183 | on Your own behalf and on Your sole responsibility, not on behalf 184 | of any other Contributor, and only if You agree to indemnify, 185 | defend, and hold each Contributor harmless for any liability 186 | incurred by, or claims asserted against, such Contributor by reason 187 | of your accepting any such warranty or additional liability. 188 | 189 | END OF TERMS AND CONDITIONS 190 | 191 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Video Info Viewer 2 | 3 | Video Info Viewer is an open source app that allows you to view metadata about videos on your Android devices. You can view the atom structure of the MPEG-4 file format, and inspect resolution, frame rate, and more. 4 | 5 | ## License 6 | 7 | Video Info Viewer is released under the Apache License, Version 2.0. See [LICENSE.md] (https://github.com/hoolrory/VideoInfoViewer/blob/master/LICENSE.md). 8 | 9 | In the Google Play Store: https://play.google.com/store/apps/details?id=com.roryhool.videoinfoviewer 10 | 11 | Also available for iOS: https://github.com/hoolrory/VideoInfoViewer-iOS 12 | -------------------------------------------------------------------------------- /VideoInfoViewer/res/values/admob_values.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /VideoInfoViewer/res/values/crittercism_values.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /VideoInfoViewer/res/values/google_analytics_values.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | true 12 | 13 | 14 | true 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'me.tatarka.retrolambda' 3 | apply plugin: 'io.fabric' 4 | 5 | retrolambda { 6 | // read java8home from gradle.properties or JAVA8_HOME from environment 7 | jdk project.hasProperty( 'java8home' ) ? java8home : System.getenv( "JAVA8_HOME" ) 8 | } 9 | 10 | android { 11 | compileSdkVersion 23 12 | buildToolsVersion "23.0.1" 13 | 14 | compileOptions { 15 | sourceCompatibility JavaVersion.VERSION_1_8 16 | targetCompatibility JavaVersion.VERSION_1_8 17 | } 18 | 19 | defaultConfig { 20 | applicationId "com.roryhool.videoinfoviewer" 21 | minSdkVersion 16 22 | targetSdkVersion 23 23 | } 24 | 25 | buildTypes { 26 | release { 27 | } 28 | debug { 29 | debuggable true 30 | } 31 | } 32 | } 33 | 34 | dependencies { 35 | compile 'com.android.support:appcompat-v7:23.0.1' 36 | compile "com.android.support:cardview-v7:23.0.1" 37 | compile 'com.android.support:design:23.0.1' 38 | compile 'com.android.support:recyclerview-v7:23.0.1' 39 | compile 'com.android.support:support-v4:23.0.1' 40 | compile 'com.google.android.gms:play-services-ads:7.5.0' 41 | compile 'com.google.code.gson:gson:2.2.4' 42 | compile 'com.squareup:otto:1.3.8' 43 | compile 'io.reactivex:rxandroid:0.24.0' 44 | compile 'io.reactivex:rxjava:1.0.0' 45 | compile files('libs/isoviewer-1.0-RC-35.jar') 46 | compile('com.crashlytics.sdk.android:crashlytics:2.5.5@aar') { 47 | transitive = true; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/libs/isoviewer-1.0-RC-35.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoolrory/VideoInfoViewer-Android/f4f5d0aa50be000f393a7a818155643496dae20a/app/libs/isoviewer-1.0-RC-35.jar -------------------------------------------------------------------------------- /app/libs/libGoogleAnalyticsServices.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoolrory/VideoInfoViewer-Android/f4f5d0aa50be000f393a7a818155643496dae20a/app/libs/libGoogleAnalyticsServices.jar -------------------------------------------------------------------------------- /app/lint.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 11 | 12 | 13 | 14 | 15 | 16 | 22 | 23 | 26 | 27 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 57 | 58 | 59 | 60 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 73 | 74 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /app/src/main/assets/apache_license_2.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | 3 | Version 2.0, January 2004 4 | 5 | http://www.apache.org/licenses/ 6 | 7 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 8 | 9 | 1. Definitions. 10 | 11 | "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. 16 | 17 | "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. 18 | 19 | "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. 20 | 21 | "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. 22 | 23 | "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). 24 | 25 | "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. 26 | 27 | "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." 28 | 29 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 30 | 31 | 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 32 | 33 | 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 34 | 35 | 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: 36 | 37 | You must give any other recipients of the Work or Derivative Works a copy of this License; and 38 | You must cause any modified files to carry prominent notices stating that You changed the files; and 39 | You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and 40 | If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. 41 | 42 | You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 43 | 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 44 | 45 | 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 46 | 47 | 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 48 | 49 | 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 50 | 51 | 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. 52 | 53 | END OF TERMS AND CONDITIONS -------------------------------------------------------------------------------- /app/src/main/assets/fonts/Roboto-Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoolrory/VideoInfoViewer-Android/f4f5d0aa50be000f393a7a818155643496dae20a/app/src/main/assets/fonts/Roboto-Black.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/Roboto-BlackItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoolrory/VideoInfoViewer-Android/f4f5d0aa50be000f393a7a818155643496dae20a/app/src/main/assets/fonts/Roboto-BlackItalic.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/Roboto-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoolrory/VideoInfoViewer-Android/f4f5d0aa50be000f393a7a818155643496dae20a/app/src/main/assets/fonts/Roboto-Bold.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/Roboto-BoldCondensed.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoolrory/VideoInfoViewer-Android/f4f5d0aa50be000f393a7a818155643496dae20a/app/src/main/assets/fonts/Roboto-BoldCondensed.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/Roboto-BoldCondensedItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoolrory/VideoInfoViewer-Android/f4f5d0aa50be000f393a7a818155643496dae20a/app/src/main/assets/fonts/Roboto-BoldCondensedItalic.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/Roboto-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoolrory/VideoInfoViewer-Android/f4f5d0aa50be000f393a7a818155643496dae20a/app/src/main/assets/fonts/Roboto-BoldItalic.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/Roboto-Condensed.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoolrory/VideoInfoViewer-Android/f4f5d0aa50be000f393a7a818155643496dae20a/app/src/main/assets/fonts/Roboto-Condensed.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/Roboto-CondensedItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoolrory/VideoInfoViewer-Android/f4f5d0aa50be000f393a7a818155643496dae20a/app/src/main/assets/fonts/Roboto-CondensedItalic.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/Roboto-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoolrory/VideoInfoViewer-Android/f4f5d0aa50be000f393a7a818155643496dae20a/app/src/main/assets/fonts/Roboto-Italic.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/Roboto-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoolrory/VideoInfoViewer-Android/f4f5d0aa50be000f393a7a818155643496dae20a/app/src/main/assets/fonts/Roboto-Light.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/Roboto-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoolrory/VideoInfoViewer-Android/f4f5d0aa50be000f393a7a818155643496dae20a/app/src/main/assets/fonts/Roboto-LightItalic.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/Roboto-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoolrory/VideoInfoViewer-Android/f4f5d0aa50be000f393a7a818155643496dae20a/app/src/main/assets/fonts/Roboto-Medium.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/Roboto-MediumItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoolrory/VideoInfoViewer-Android/f4f5d0aa50be000f393a7a818155643496dae20a/app/src/main/assets/fonts/Roboto-MediumItalic.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/Roboto-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoolrory/VideoInfoViewer-Android/f4f5d0aa50be000f393a7a818155643496dae20a/app/src/main/assets/fonts/Roboto-Regular.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/Roboto-Thin.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoolrory/VideoInfoViewer-Android/f4f5d0aa50be000f393a7a818155643496dae20a/app/src/main/assets/fonts/Roboto-Thin.ttf -------------------------------------------------------------------------------- /app/src/main/assets/fonts/Roboto-ThinItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoolrory/VideoInfoViewer-Android/f4f5d0aa50be000f393a7a818155643496dae20a/app/src/main/assets/fonts/Roboto-ThinItalic.ttf -------------------------------------------------------------------------------- /app/src/main/java/com/roryhool/videoinfoviewer/CreditsFragment.java: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2016 Rory Hool 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | **/ 16 | 17 | package com.roryhool.videoinfoviewer; 18 | 19 | import java.io.IOException; 20 | import java.io.InputStream; 21 | 22 | import android.os.Bundle; 23 | import android.support.v4.app.Fragment; 24 | import android.text.method.LinkMovementMethod; 25 | import android.view.LayoutInflater; 26 | import android.view.Menu; 27 | import android.view.MenuInflater; 28 | import android.view.View; 29 | import android.view.ViewGroup; 30 | import android.widget.TextView; 31 | 32 | public class CreditsFragment extends Fragment { 33 | 34 | @Override 35 | public View onCreateView( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState ) { 36 | View view = inflater.inflate( R.layout.fragment_credits, container, false ); 37 | 38 | setHasOptionsMenu( true ); 39 | 40 | try { 41 | InputStream is = view.getContext().getAssets().open( "apache_license_2.txt" ); 42 | int size = is.available(); 43 | 44 | byte[] buffer = new byte[size]; 45 | is.read( buffer ); 46 | is.close(); 47 | 48 | String str = new String( buffer ); 49 | TextView textView = (TextView) view.findViewById( R.id.apache_license ); 50 | textView.setText( str ); 51 | } catch ( IOException e ) { 52 | e.printStackTrace(); 53 | } 54 | 55 | TextView iconCreditText = (TextView) view.findViewById( R.id.icon_credit ); 56 | iconCreditText.setMovementMethod( LinkMovementMethod.getInstance() ); 57 | 58 | TextView mp4parserCreditText = (TextView) view.findViewById( R.id.mp4parser_credit ); 59 | mp4parserCreditText.setMovementMethod( LinkMovementMethod.getInstance() ); 60 | 61 | TextView gsonCreditText = (TextView) view.findViewById( R.id.gson_credit ); 62 | gsonCreditText.setMovementMethod( LinkMovementMethod.getInstance() ); 63 | 64 | TextView reactiveXCreditText = (TextView) view.findViewById( R.id.reactivex_credit ); 65 | reactiveXCreditText.setMovementMethod( LinkMovementMethod.getInstance() ); 66 | 67 | TextView reactiveXAndroidCreditText = (TextView) view.findViewById( R.id.reactivexandroid_credit ); 68 | reactiveXAndroidCreditText.setMovementMethod( LinkMovementMethod.getInstance() ); 69 | 70 | TextView ottoCreditText = (TextView) view.findViewById( R.id.otto_credit ); 71 | ottoCreditText.setMovementMethod( LinkMovementMethod.getInstance() ); 72 | 73 | TextView retrolambdaCreditText = (TextView) view.findViewById( R.id.retrolambda_credit ); 74 | retrolambdaCreditText.setMovementMethod( LinkMovementMethod.getInstance() ); 75 | 76 | TextView gradleRetrolambdaCreditText = (TextView) view.findViewById( R.id.gradle_retrolambda_credit ); 77 | gradleRetrolambdaCreditText.setMovementMethod( LinkMovementMethod.getInstance() ); 78 | 79 | return view; 80 | } 81 | 82 | @Override 83 | public void onCreateOptionsMenu( Menu menu, MenuInflater inflater ) { 84 | super.onCreateOptionsMenu( menu, inflater ); 85 | menu.clear(); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /app/src/main/java/com/roryhool/videoinfoviewer/Extras.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2016 Rory Hool 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | 17 | package com.roryhool.videoinfoviewer; 18 | 19 | public class Extras { 20 | public static final String EXTRA_VIDEO_CACHE_ID = "EXTRA_VIDEO_CACHE_ID"; 21 | public static final String EXTRA_BOX_ID = "EXTRA_BOX_ID"; 22 | } 23 | -------------------------------------------------------------------------------- /app/src/main/java/com/roryhool/videoinfoviewer/MainActivity.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2016 Rory Hool 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | 17 | package com.roryhool.videoinfoviewer; 18 | 19 | import android.os.Bundle; 20 | import android.support.v4.app.FragmentManager; 21 | import android.support.v4.app.FragmentTransaction; 22 | import android.support.v7.app.AppCompatActivity; 23 | import android.support.v7.widget.Toolbar; 24 | import android.widget.FrameLayout; 25 | 26 | import com.google.android.gms.ads.AdRequest; 27 | import com.google.android.gms.ads.AdSize; 28 | import com.google.android.gms.ads.AdView; 29 | import com.google.android.gms.analytics.HitBuilders; 30 | 31 | public class MainActivity extends AppCompatActivity { 32 | 33 | protected Toolbar mToolbar; 34 | protected FrameLayout mAdFrame; 35 | protected AdView mAdView; 36 | 37 | @Override 38 | public void onCreate( Bundle savedInstanceState ) { 39 | super.onCreate( savedInstanceState ); 40 | 41 | setContentView( R.layout.activity_main ); 42 | 43 | mAdFrame = (FrameLayout) findViewById( R.id.adFrame ); 44 | mToolbar = (Toolbar) findViewById( R.id.toolbar ); 45 | 46 | if ( savedInstanceState == null ) { 47 | FragmentManager manager = getSupportFragmentManager(); 48 | FragmentTransaction fragTransaction = manager.beginTransaction(); 49 | fragTransaction.add( R.id.fragment_frame, new RecentVideosFragment() ); 50 | fragTransaction.commit(); 51 | } 52 | 53 | setSupportActionBar( mToolbar ); 54 | } 55 | 56 | @Override 57 | public void onStart() { 58 | super.onStart(); 59 | 60 | if ( !BuildConfig.DEBUG ) { 61 | setupAds(); 62 | } 63 | 64 | VideoInfoViewerApp.getDefaultTracker().setScreenName( MainActivity.class.getSimpleName() ); 65 | VideoInfoViewerApp.getDefaultTracker().send( new HitBuilders.ScreenViewBuilder().build() ); 66 | } 67 | 68 | private void setupAds() { 69 | String admobAdUnitId = getString( R.string.main_activity_admob_ad_unit_id ); 70 | 71 | if ( admobAdUnitId != null && !admobAdUnitId.equals( ( "" ) ) ) { 72 | mAdView = new AdView( this ); 73 | mAdView.setAdSize( AdSize.BANNER ); 74 | mAdView.setAdUnitId( admobAdUnitId ); 75 | 76 | mAdFrame.addView( mAdView ); 77 | 78 | String[] testDeviceIds = getResources().getStringArray( R.array.admob_test_device_ids ); 79 | 80 | AdRequest.Builder adRequestBuilder = new AdRequest.Builder(); 81 | adRequestBuilder.addTestDevice( AdRequest.DEVICE_ID_EMULATOR ); 82 | 83 | for ( int i = 0; i < testDeviceIds.length; i++ ) { 84 | adRequestBuilder.addTestDevice( testDeviceIds[i] ); 85 | } 86 | 87 | AdRequest adRequest = adRequestBuilder.build(); 88 | mAdView.loadAd( adRequest ); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /app/src/main/java/com/roryhool/videoinfoviewer/RecentVideosFragment.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2016 Rory Hool 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | 17 | package com.roryhool.videoinfoviewer; 18 | 19 | import android.app.Activity; 20 | import android.content.Context; 21 | import android.content.Intent; 22 | import android.net.Uri; 23 | import android.os.Build; 24 | import android.os.Bundle; 25 | import android.support.design.widget.FloatingActionButton; 26 | import android.support.v4.app.Fragment; 27 | import android.support.v4.app.FragmentActivity; 28 | import android.support.v4.app.FragmentManager; 29 | import android.support.v4.app.FragmentTransaction; 30 | import android.view.LayoutInflater; 31 | import android.view.Menu; 32 | import android.view.MenuInflater; 33 | import android.view.MenuItem; 34 | import android.view.View; 35 | import android.view.ViewGroup; 36 | import android.widget.AdapterView; 37 | import android.widget.ArrayAdapter; 38 | import android.widget.ImageView; 39 | import android.widget.ListView; 40 | import android.widget.TextView; 41 | 42 | import com.roryhool.videoinfoviewer.analytics.Analytics; 43 | import com.roryhool.videoinfoviewer.data.Video; 44 | import com.roryhool.videoinfoviewer.utils.VideoCache; 45 | 46 | import java.util.List; 47 | 48 | public class RecentVideosFragment extends Fragment { 49 | 50 | protected int SELECT_VIDEO_CODE = 100; 51 | 52 | protected ListView mRecentVideosList; 53 | protected FloatingActionButton mFab; 54 | 55 | protected RecentVideosAdapter mAdapter; 56 | 57 | @Override 58 | public View onCreateView( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState ) { 59 | View view = inflater.inflate( R.layout.fragment_recent_videos, container, false ); 60 | 61 | mRecentVideosList = (ListView) view.findViewById( R.id.recentVideosList ); 62 | 63 | mFab = (FloatingActionButton) view.findViewById( R.id.fab ); 64 | mFab.setOnClickListener( this::onClickFab ); 65 | 66 | List

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | 17 | package com.roryhool.videoinfoviewer; 18 | 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | 22 | import android.app.Fragment; 23 | import android.content.Context; 24 | import android.os.AsyncTask; 25 | import android.os.Bundle; 26 | import android.view.LayoutInflater; 27 | import android.view.View; 28 | import android.view.ViewGroup; 29 | import android.widget.ArrayAdapter; 30 | import android.widget.ListView; 31 | import android.widget.SearchView.OnQueryTextListener; 32 | 33 | import com.roryhool.videoinfoviewer.data.Video; 34 | import com.roryhool.videoinfoviewer.search.SearchItem; 35 | 36 | public class SearchFragment extends Fragment implements OnQueryTextListener { 37 | 38 | protected ListView mListView; 39 | 40 | protected SearchItemAdapter mAdapter; 41 | 42 | protected Video mVideo; 43 | 44 | @Override 45 | public View onCreateView( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState ) { 46 | 47 | View view = inflater.inflate( R.layout.fragment_search, container, false ); 48 | mListView = (ListView) view.findViewById( R.id.search_items_list ); 49 | 50 | SearchItemAdapterPrepareTask task = new SearchItemAdapterPrepareTask(); 51 | task.execute( mVideo ); 52 | return view; 53 | } 54 | 55 | public void setVideo( Video video ) { 56 | mVideo = video; 57 | } 58 | 59 | @Override 60 | public boolean onQueryTextSubmit( String query ) { 61 | return false; 62 | } 63 | 64 | @Override 65 | public boolean onQueryTextChange( String newText ) { 66 | return false; 67 | } 68 | 69 | private class SearchItemAdapterPrepareTask extends AsyncTask> { 70 | 71 | @Override 72 | protected List doInBackground( Video... params ) { 73 | List items = new ArrayList<>(); 74 | 75 | return items; 76 | } 77 | 78 | @Override 79 | protected void onPostExecute( List items ) { 80 | 81 | mAdapter = new SearchItemAdapter( VideoInfoViewerApp.getContext(), 0, items ); 82 | mListView.setAdapter( mAdapter ); 83 | } 84 | } 85 | 86 | private class SearchItemAdapter extends ArrayAdapter { 87 | 88 | public SearchItemAdapter( Context context, int textViewResourceId, List objects ) { 89 | super( context, textViewResourceId, objects ); 90 | 91 | } 92 | 93 | @Override 94 | public long getItemId( int position ) { 95 | return -1; 96 | } 97 | 98 | @Override 99 | public boolean hasStableIds() { 100 | return true; 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /app/src/main/java/com/roryhool/videoinfoviewer/VideoFragment.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2016 Rory Hool 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | 17 | package com.roryhool.videoinfoviewer; 18 | 19 | import android.app.Activity; 20 | import android.content.res.Configuration; 21 | import android.os.AsyncTask; 22 | import android.os.Bundle; 23 | import android.support.v4.app.Fragment; 24 | import android.view.LayoutInflater; 25 | import android.view.View; 26 | import android.view.ViewGroup; 27 | import android.widget.Button; 28 | import android.widget.LinearLayout; 29 | 30 | import com.coremedia.iso.IsoFile; 31 | import com.roryhool.videoinfoviewer.analytics.Analytics; 32 | import com.roryhool.videoinfoviewer.atomfragments.AtomStructureFragment; 33 | import com.roryhool.videoinfoviewer.data.Video; 34 | import com.roryhool.videoinfoviewer.utils.FontManager; 35 | import com.roryhool.videoinfoviewer.utils.FormatUtils; 36 | import com.roryhool.videoinfoviewer.utils.VideoCache; 37 | import com.roryhool.videoinfoviewer.views.DisableableScrollView; 38 | import com.roryhool.videoinfoviewer.views.RobotoTextView; 39 | import com.roryhool.videoinfoviewer.views.VideoPlayerView; 40 | import com.roryhool.videoinfoviewer.views.VideoPlayerView.OnFullscreenListener; 41 | 42 | import java.io.IOException; 43 | import java.util.Locale; 44 | 45 | public class VideoFragment extends Fragment { 46 | 47 | protected DisableableScrollView mScrollView; 48 | protected VideoPlayerView mVideoPlayer; 49 | protected Button mButton; 50 | protected View mLoadingProgress; 51 | protected View mVideoPropertiesCard; 52 | protected Button mViewAtomButton; 53 | protected LinearLayout mVideoPropertiesLayout; 54 | 55 | protected Video mVideo; 56 | 57 | protected RetrieveIsoFileTask mRetrieveIsoFileTask; 58 | 59 | @Override 60 | public View onCreateView( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState ) { 61 | View view = inflater.inflate( R.layout.fragment_video, container, false ); 62 | 63 | mScrollView = (DisableableScrollView) view.findViewById( R.id.scroll_view ); 64 | mVideoPlayer = (VideoPlayerView) view.findViewById( R.id.video_player ); 65 | mButton = (Button) view.findViewById( R.id.view_atom_button ); 66 | mLoadingProgress = view.findViewById( R.id.loading_progress ); 67 | mVideoPropertiesCard = view.findViewById( R.id.video_properties_card ); 68 | mVideoPropertiesLayout = (LinearLayout) view.findViewById( R.id.video_properties_layout ); 69 | mViewAtomButton = (Button) view.findViewById( R.id.view_atom_button ); 70 | 71 | mViewAtomButton.setOnClickListener( this::onClickViewAtomButton ); 72 | 73 | mVideoPlayer.addFullscreenListener( this::onFullscreenChanged ); 74 | if ( getActivity() instanceof OnFullscreenListener ) { 75 | mVideoPlayer.addFullscreenListener( (OnFullscreenListener) getActivity() ); 76 | } 77 | 78 | Video video = null; 79 | 80 | Bundle args = getArguments(); 81 | if ( args != null && args.containsKey( Extras.EXTRA_VIDEO_CACHE_ID ) ) { 82 | int cacheId = args.getInt( Extras.EXTRA_VIDEO_CACHE_ID ); 83 | video = VideoCache.Instance().getVideoById( cacheId ); 84 | } 85 | 86 | if ( video != null ) { 87 | LoadVideo( video ); 88 | mVideoPlayer.setVideo( video ); 89 | } 90 | 91 | return view; 92 | } 93 | 94 | @Override 95 | public void onPause() { 96 | super.onPause(); 97 | onPausedOrHidden(); 98 | } 99 | 100 | @Override 101 | public void onSaveInstanceState( Bundle outState ) { 102 | super.onSaveInstanceState( outState ); 103 | outState.putAll( getArguments() ); 104 | } 105 | 106 | @Override 107 | public void setUserVisibleHint( boolean isVisibleToUser ) { 108 | super.setUserVisibleHint( isVisibleToUser ); 109 | 110 | if ( !getUserVisibleHint() ) { 111 | onPausedOrHidden(); 112 | } 113 | } 114 | 115 | protected void onPausedOrHidden() { 116 | if ( mVideoPlayer != null ) { 117 | mVideoPlayer.shutdownMediaPlayer(); 118 | } 119 | } 120 | 121 | @Override 122 | public void onConfigurationChanged( Configuration newConfig ) { 123 | super.onConfigurationChanged( newConfig ); 124 | 125 | mVideoPlayer.handleResize(); 126 | } 127 | 128 | protected void onClickViewAtomButton( View v ) { 129 | Activity activity = getActivity(); 130 | if ( activity instanceof VideoActivity ) { 131 | Bundle args = new Bundle(); 132 | args.putInt( Extras.EXTRA_VIDEO_CACHE_ID, mVideo.CacheId ); 133 | 134 | VideoActivity videoActivity = (VideoActivity) activity; 135 | videoActivity.addFragmentToVideoTab( mVideo, AtomStructureFragment.class, args ); 136 | } 137 | } 138 | 139 | protected void LoadVideo( Video video ) { 140 | mVideo = video; 141 | if ( mVideo.getIsoFile() == null && mRetrieveIsoFileTask == null ) { 142 | mRetrieveIsoFileTask = new RetrieveIsoFileTask(); 143 | mRetrieveIsoFileTask.execute( mVideo ); 144 | return; 145 | } 146 | 147 | Analytics.logEvent( "App Action", "Opened Video in VideoActivity" ); 148 | 149 | mLoadingProgress.setVisibility( View.GONE ); 150 | mVideoPropertiesCard.setVisibility( View.VISIBLE ); 151 | mViewAtomButton.setVisibility( View.VISIBLE ); 152 | 153 | addKeyValueField( R.string.key_file_name, mVideo.FileName ); 154 | addKeyValueField( R.string.key_resolution, String.format( "%dx%d", mVideo.VideoWidth, mVideo.VideoHeight ) ); 155 | addKeyValueField( R.string.key_mimetype, mVideo.MimeType ); 156 | addKeyValueField( R.string.key_frame_rate, String.format( Locale.US, "%s fps", mVideo.FrameRate ) ); 157 | // addKeyValueField( R.string.key_file_path, video.FilePath ); 158 | // addKeyValueField( R.string.key_format, video.Format ); 159 | // addKeyValueField( R.string.key_format_profile, video.FormatProfile ); 160 | // addKeyValueField( R.string.key_codec_id, video.CodecID ); 161 | 162 | String fileSizeString = "N/A"; 163 | if ( mVideo.FileSize != null ) { 164 | fileSizeString = FormatUtils.FormatFileSizeForDisplay( Float.parseFloat( mVideo.FileSize ) ); 165 | } 166 | addKeyValueField( R.string.key_file_size, fileSizeString ); 167 | 168 | String durationString = "N/A"; 169 | if ( mVideo.Duration != null ) { 170 | durationString = FormatUtils.FormatTimeForDisplay( Long.parseLong( mVideo.Duration ) ); 171 | } 172 | addKeyValueField( R.string.key_duration, durationString ); 173 | 174 | String kbps = "N/A"; 175 | if ( mVideo.BitRate != null ) { 176 | kbps = FormatUtils.FormatBpsForDisplay( Long.parseLong( mVideo.BitRate ) ); 177 | } 178 | addKeyValueField( R.string.key_bitrate, kbps ); 179 | 180 | String dateString = FormatUtils.FormatZuluDateTimeForDisplay( mVideo.Date ); 181 | addKeyValueField( R.string.key_date, dateString ); 182 | } 183 | 184 | private void addKeyValueField( int keyStringId, String value ) { 185 | Activity activity = getActivity(); 186 | if ( activity != null ) { 187 | LinearLayout keyLayout = (LinearLayout) mVideoPropertiesLayout.getChildAt( 0 ); 188 | LinearLayout valueLayout = (LinearLayout) mVideoPropertiesLayout.getChildAt( 1 ); 189 | 190 | RobotoTextView keyView = new RobotoTextView( activity ); 191 | keyView.setTextAppearance( activity, R.style.CardKey ); 192 | 193 | RobotoTextView valueView = new RobotoTextView( activity ); 194 | valueView.setTextAppearance( activity, R.style.CardValueOneLine ); 195 | FontManager.get( activity ).setRobotoLight( valueView ); 196 | 197 | keyView.setText( keyStringId ); 198 | valueView.setText( value ); 199 | 200 | keyLayout.addView( keyView ); 201 | valueLayout.addView( valueView ); 202 | } 203 | } 204 | 205 | private void onFullscreenChanged( boolean fullscreen ) { 206 | if ( fullscreen ) { 207 | mScrollView.scrollTo( 0, 0 ); 208 | mScrollView.setEnabled( false ); 209 | } else { 210 | mScrollView.setEnabled( true ); 211 | } 212 | } 213 | 214 | public class RetrieveIsoFileTask extends AsyncTask { 215 | 216 | @Override 217 | protected void onPreExecute() { 218 | } 219 | 220 | @Override 221 | protected IsoFile doInBackground( Video... videos ) { 222 | IsoFile isoFile = null; 223 | try { 224 | isoFile = new IsoFile( mVideo.FilePath ); 225 | } catch ( IOException e ) { 226 | e.printStackTrace(); 227 | } 228 | return isoFile; 229 | } 230 | 231 | @Override 232 | protected void onPostExecute( IsoFile isoFile ) { 233 | mVideo.setIsoFile( isoFile ); 234 | LoadVideo( mVideo ); 235 | } 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /app/src/main/java/com/roryhool/videoinfoviewer/VideoInfoViewerApp.java: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2016 Rory Hool 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | **/ 16 | 17 | package com.roryhool.videoinfoviewer; 18 | 19 | import android.app.Application; 20 | import android.content.Context; 21 | 22 | import com.crashlytics.android.Crashlytics; 23 | import com.google.android.gms.analytics.GoogleAnalytics; 24 | import com.google.android.gms.analytics.Tracker; 25 | import com.squareup.otto.Bus; 26 | import io.fabric.sdk.android.Fabric; 27 | 28 | public class VideoInfoViewerApp extends Application { 29 | 30 | private static Context sContext; 31 | 32 | private static Bus sBus = new Bus(); 33 | 34 | public static Context getContext() { 35 | return sContext; 36 | } 37 | 38 | public static Bus getBus() { 39 | return sBus; 40 | } 41 | 42 | @Override 43 | public void onCreate() { 44 | super.onCreate(); 45 | Fabric.with(this, new Crashlytics()); 46 | 47 | sContext = this; 48 | } 49 | 50 | private static Tracker mTracker; 51 | 52 | /** 53 | * Gets the default {@link Tracker} for this {@link Application}. 54 | * @return tracker 55 | */ 56 | synchronized public static Tracker getDefaultTracker() { 57 | if ( mTracker == null ) { 58 | GoogleAnalytics analytics = GoogleAnalytics.getInstance( getContext() ); 59 | // To enable debug logging use: adb shell setprop log.tag.GAv4 DEBUG 60 | mTracker = analytics.newTracker( R.xml.global_tracker ); 61 | } 62 | return mTracker; 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /app/src/main/java/com/roryhool/videoinfoviewer/analytics/Analytics.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2016 Rory Hool 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | 17 | package com.roryhool.videoinfoviewer.analytics; 18 | 19 | import com.google.android.gms.analytics.HitBuilders; 20 | import com.roryhool.videoinfoviewer.VideoInfoViewerApp; 21 | 22 | public class Analytics { 23 | 24 | public static void logEvent( String category, String action ) { 25 | logEvent( category, action, null, 0 ); 26 | } 27 | 28 | public static void logEvent( String category, String action, String label ) { 29 | logEvent( category, action, label, 0 ); 30 | } 31 | 32 | public static void logEvent( String category, String action, String label, long value ) { 33 | VideoInfoViewerApp.getDefaultTracker().send( 34 | new HitBuilders.EventBuilder() 35 | .setCategory( category ) 36 | .setAction( action ) 37 | .setLabel( label ) 38 | .setValue( value ) 39 | .build() ); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /app/src/main/java/com/roryhool/videoinfoviewer/animation/ResizeAnimation.java: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2016 Rory Hool 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | **/ 16 | 17 | package com.roryhool.videoinfoviewer.animation; 18 | 19 | import android.view.View; 20 | import android.view.animation.Animation; 21 | import android.view.animation.Transformation; 22 | 23 | public class ResizeAnimation extends Animation { 24 | 25 | int mOriginalHeight; 26 | int mTargetHeight; 27 | int mOffsetHeight; 28 | int mAdjacentHeightIncrement; 29 | View mView; 30 | View mAdjacentView; 31 | boolean mDown; 32 | 33 | public ResizeAnimation( View view, int originalHeight, int targetHeight, boolean down ) { 34 | mView = view; 35 | mOriginalHeight = originalHeight; 36 | mTargetHeight = targetHeight; 37 | mOffsetHeight = targetHeight - originalHeight; 38 | mDown = down; 39 | } 40 | 41 | @Override 42 | protected void applyTransformation( float interpolatedTime, Transformation t ) { 43 | int newHeight; 44 | if ( mDown ) 45 | newHeight = (int) ( mOffsetHeight * interpolatedTime ); 46 | 47 | else 48 | newHeight = (int) ( mOffsetHeight * ( 1 - interpolatedTime ) ); 49 | 50 | mView.getLayoutParams().height = newHeight + mOriginalHeight; 51 | mView.requestLayout(); 52 | 53 | if ( mAdjacentView != null ) { 54 | mAdjacentView.getLayoutParams().height = mView.getLayoutParams().height + mAdjacentHeightIncrement; 55 | mAdjacentView.requestLayout(); 56 | } 57 | } 58 | 59 | @Override 60 | public void initialize( int width, int height, int parentWidth, int parentHeight ) { 61 | super.initialize( width, height, parentWidth, parentHeight ); 62 | } 63 | 64 | @Override 65 | public boolean willChangeBounds() { 66 | return true; 67 | } 68 | 69 | public void setAdjacentView( View adjacentView ) { 70 | mAdjacentView = adjacentView; 71 | } 72 | 73 | public void setAdjacentHeightIncrement( int adjacentHeightIncrement ) { 74 | mAdjacentHeightIncrement = adjacentHeightIncrement; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /app/src/main/java/com/roryhool/videoinfoviewer/atomfragments/AtomInfoFragment.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2016 Rory Hool 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | 17 | package com.roryhool.videoinfoviewer.atomfragments; 18 | 19 | import android.app.Activity; 20 | import android.os.AsyncTask; 21 | import android.os.Bundle; 22 | import android.support.v4.app.Fragment; 23 | import android.view.LayoutInflater; 24 | import android.view.View; 25 | import android.view.ViewGroup; 26 | import android.widget.LinearLayout; 27 | import android.widget.ProgressBar; 28 | 29 | import com.coremedia.iso.boxes.Box; 30 | import com.roryhool.videoinfoviewer.Extras; 31 | import com.roryhool.videoinfoviewer.R; 32 | import com.roryhool.videoinfoviewer.analytics.Analytics; 33 | import com.roryhool.videoinfoviewer.utils.IsoFileCache; 34 | import com.roryhool.videoinfoviewer.views.BoxInfoView; 35 | 36 | public class AtomInfoFragment extends Fragment { 37 | 38 | protected boolean mLoaded = false; 39 | 40 | protected Box mBox; 41 | 42 | protected LinearLayout mRootLayout; 43 | protected ProgressBar mLoadingProgress; 44 | protected BoxInfoView mBoxInfoView; 45 | 46 | @Override 47 | public View onCreateView( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState ) { 48 | View view = inflater.inflate( R.layout.fragment_box_info, container, false ); 49 | 50 | Analytics.logEvent( "App Action", "Opened Box in AtomInfoFragment" ); 51 | mBox = getBox( getArguments() ); 52 | 53 | mRootLayout = (LinearLayout) view.findViewById( R.id.root_layout ); 54 | mLoadingProgress = (ProgressBar) view.findViewById( R.id.loading_progress ); 55 | 56 | return view; 57 | } 58 | 59 | @Override 60 | public void onStart() { 61 | super.onStart(); 62 | 63 | if ( !mLoaded ) { 64 | mLoaded = true; 65 | 66 | Activity activity = getActivity(); 67 | if ( activity != null ) { 68 | Analytics.logEvent( "Video Info", "Load Atom Info", mBox.getType() ); 69 | } 70 | new RetrieveBoxInfoTask().execute( mBox ); 71 | } 72 | } 73 | 74 | @Override 75 | public void onSaveInstanceState( Bundle outState ) { 76 | super.onSaveInstanceState( outState ); 77 | outState.putAll( getArguments() ); 78 | } 79 | 80 | @Override 81 | public void onStop() { 82 | super.onStop(); 83 | 84 | if ( mBoxInfoView != null ) { 85 | mBoxInfoView.stop(); 86 | } 87 | } 88 | 89 | protected Box getBox( Bundle bundle ) { 90 | return IsoFileCache.Instance().getBox( bundle.getInt( Extras.EXTRA_BOX_ID ) ); 91 | } 92 | 93 | protected BoxInfoView LoadBoxInfo( Box box ) { 94 | BoxInfoView view = null; 95 | 96 | Activity activity = getActivity(); 97 | if ( activity != null ) { 98 | view = new BoxInfoView( activity ); 99 | view.LoadBox( box ); 100 | } 101 | 102 | return view; 103 | } 104 | 105 | protected class RetrieveBoxInfoTask extends AsyncTask { 106 | 107 | @Override 108 | protected void onPreExecute() { 109 | } 110 | 111 | @Override 112 | protected BoxInfoView doInBackground( Box... boxes ) { 113 | return LoadBoxInfo( boxes[0] ); 114 | } 115 | 116 | @Override 117 | protected void onPostExecute( BoxInfoView view ) { 118 | mBoxInfoView = view; 119 | 120 | mLoadingProgress.setVisibility( View.GONE ); 121 | mRootLayout.addView( mBoxInfoView ); 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /app/src/main/java/com/roryhool/videoinfoviewer/data/Video.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2016 Rory Hool 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | 17 | package com.roryhool.videoinfoviewer.data; 18 | 19 | import java.io.File; 20 | import java.io.FileOutputStream; 21 | import java.io.IOException; 22 | 23 | import android.graphics.Bitmap; 24 | import android.media.MediaMetadataRetriever; 25 | import android.util.Log; 26 | 27 | import com.coremedia.iso.IsoFile; 28 | import com.coremedia.iso.boxes.Box; 29 | import com.coremedia.iso.boxes.SampleSizeBox; 30 | import com.google.gson.annotations.SerializedName; 31 | import com.roryhool.videoinfoviewer.VideoInfoViewerApp; 32 | import com.roryhool.videoinfoviewer.utils.BoxUtils; 33 | 34 | public class Video implements Comparable

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | 17 | package com.roryhool.videoinfoviewer.utils; 18 | 19 | import java.io.File; 20 | import java.util.Locale; 21 | 22 | import android.annotation.SuppressLint; 23 | import android.content.ContentUris; 24 | import android.content.Context; 25 | import android.database.Cursor; 26 | import android.net.Uri; 27 | import android.os.Build; 28 | import android.os.Environment; 29 | import android.provider.DocumentsContract; 30 | import android.provider.MediaStore; 31 | 32 | import com.roryhool.videoinfoviewer.VideoInfoViewerApp; 33 | import com.roryhool.videoinfoviewer.analytics.Analytics; 34 | 35 | public class UriHelper { 36 | 37 | private static String EXTERNAL_STORAGE_DOCUMENTS_PROVIDER = "com.android.externalstorage.documents"; 38 | private static String DOWNLOAD_DOCUMENTS_PROVIDER = "com.android.providers.downloads.documents"; 39 | private static String MEDIA_DOCUMENTS_PROVIDER = "com.android.providers.media.documents"; 40 | 41 | private static String PUBLIC_DOWNLOADS_CONTEXT_URI = "content://downloads/public_downloads"; 42 | 43 | private static String DATA_COLUMN = "_data"; 44 | 45 | private static String DOCUMENT_TYPE_AUDIO = "audio"; 46 | private static String DOCUMENT_TYPE_IMAGE = "image"; 47 | private static String DOCUMENT_TYPE_VIDEO = "video"; 48 | private static String DOCUMENT_TYPE_PRIMARY = "primary"; 49 | 50 | 51 | private static String URI_SCHEME_CONTENT = "content"; 52 | private static String URI_SCHEME_FILE = "file"; 53 | 54 | @SuppressLint( "NewApi" ) 55 | public static String ContentUriToFilePath( Context context, Uri uri ) { 56 | String authority = uri.getAuthority(); 57 | String scheme = uri.getScheme(); 58 | 59 | if ( Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && DocumentsContract.isDocumentUri( context, uri ) ) { 60 | 61 | String documentId = DocumentsContract.getDocumentId( uri ); 62 | 63 | if ( authority.equalsIgnoreCase( EXTERNAL_STORAGE_DOCUMENTS_PROVIDER ) ) { 64 | 65 | String[] documentIdPieces = documentId.split( ":" ); 66 | String documentType = documentIdPieces[0]; 67 | String documentPath = documentIdPieces[1]; 68 | 69 | if ( documentType.equalsIgnoreCase( DOCUMENT_TYPE_PRIMARY ) ) { 70 | return String.format( Locale.US, "%s/%s", Environment.getExternalStorageDirectory(), documentPath ); 71 | } 72 | 73 | } else if ( authority.equalsIgnoreCase( DOWNLOAD_DOCUMENTS_PROVIDER ) ) { 74 | 75 | Uri contentUri = ContentUris.withAppendedId( Uri.parse( PUBLIC_DOWNLOADS_CONTEXT_URI ), Long.valueOf( documentId ) ); 76 | 77 | return CursorHelper.GetColumn( context, contentUri, DATA_COLUMN, null, null ); 78 | 79 | } else if ( authority.equalsIgnoreCase( MEDIA_DOCUMENTS_PROVIDER ) ) { 80 | 81 | String[] documentIdPieces = documentId.split( ":" ); 82 | String documentType = documentIdPieces[0]; 83 | String documentPath = documentIdPieces[1]; 84 | Uri contentUri = null; 85 | 86 | if ( documentType.equalsIgnoreCase( DOCUMENT_TYPE_AUDIO ) ) { 87 | contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; 88 | } else if ( documentType.equalsIgnoreCase( DOCUMENT_TYPE_IMAGE ) ) { 89 | contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; 90 | } else if ( documentType.equalsIgnoreCase( DOCUMENT_TYPE_VIDEO ) ) { 91 | contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; 92 | } 93 | 94 | return CursorHelper.GetColumn( context, contentUri, DATA_COLUMN, "_id=?", new String[] { documentPath } ); 95 | 96 | } 97 | 98 | } else if ( scheme.equalsIgnoreCase( URI_SCHEME_CONTENT ) ) { 99 | 100 | return CursorHelper.GetColumn( context, uri, DATA_COLUMN, null, null ); 101 | 102 | } else if ( scheme.equalsIgnoreCase( URI_SCHEME_FILE ) ) { 103 | 104 | return uri.getPath(); 105 | } 106 | 107 | Analytics.logEvent( "Failure", "Failed to resolve content URI to path", uri.toString() ); 108 | 109 | return null; 110 | } 111 | 112 | 113 | public static String getFilePathFromUri( Uri uri ) { 114 | if ( uri == null ) { 115 | return null; 116 | } 117 | 118 | String filePath = null; 119 | if ( uri.getPath() != null ) { 120 | File f = new File( uri.getPath() ); 121 | if ( f.exists() ) { 122 | filePath = uri.getPath(); 123 | } 124 | } 125 | 126 | String scheme = uri.getScheme(); 127 | if ( filePath == null && scheme != null && scheme.equals( "content" ) ) { 128 | String[] projection = { MediaStore.Video.Media.DATA }; 129 | Cursor cursor = VideoInfoViewerApp.getContext().getContentResolver().query( uri, projection, null, null, null ); 130 | 131 | int dataColumn = cursor.getColumnIndex( MediaStore.Video.Media.DATA ); 132 | if ( dataColumn >= 0 && cursor.moveToFirst() ) { 133 | 134 | filePath = cursor.getString( dataColumn ); 135 | } 136 | 137 | cursor.close(); 138 | } 139 | 140 | return filePath; 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /app/src/main/java/com/roryhool/videoinfoviewer/utils/VideoCache.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2016 Rory Hool 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | 17 | package com.roryhool.videoinfoviewer.utils; 18 | 19 | import java.lang.reflect.Type; 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | 23 | import android.content.Context; 24 | import android.content.SharedPreferences; 25 | import android.util.Log; 26 | 27 | import com.google.gson.Gson; 28 | import com.google.gson.reflect.TypeToken; 29 | import com.roryhool.videoinfoviewer.VideoInfoViewerApp; 30 | import com.roryhool.videoinfoviewer.data.Video; 31 | 32 | public class VideoCache { 33 | 34 | private String RECENT_VIDEO_PREFS = "RECENT_VIDEO_PREFS"; 35 | private String RECENT_VIDEO_KEY = "RECENT_VIDEO_KEY"; 36 | 37 | private int CurrentCacheId = 0; 38 | 39 | private int MAX_VIDEOS = 5; 40 | 41 | private static VideoCache mInstance = null; 42 | 43 | public static VideoCache Instance() { 44 | if ( mInstance == null ) { 45 | mInstance = new VideoCache(); 46 | } 47 | return mInstance; 48 | } 49 | 50 | ArrayList

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | 17 | package com.roryhool.videoinfoviewer.views; 18 | 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | 22 | import android.content.Context; 23 | import android.graphics.SurfaceTexture; 24 | import android.util.AttributeSet; 25 | import android.view.TextureView; 26 | import android.view.TextureView.SurfaceTextureListener; 27 | 28 | public class ScaledTextureView extends TextureView implements SurfaceTextureListener { 29 | 30 | protected SurfaceTexture mSurfaceTexture; 31 | 32 | protected int mWidth; 33 | protected int mHeight; 34 | protected int mAspectX = 1; 35 | protected int mAspectY = 1; 36 | 37 | protected boolean mSurfaceAvailable = false; 38 | 39 | protected List mListeners = new ArrayList<>(); 40 | 41 | public ScaledTextureView( Context context ) { 42 | super( context ); 43 | init(); 44 | } 45 | 46 | public ScaledTextureView( Context context, AttributeSet attrs ) { 47 | super( context, attrs ); 48 | init(); 49 | } 50 | 51 | public ScaledTextureView( Context context, AttributeSet attrs, int defStyle ) { 52 | super( context, attrs, defStyle ); 53 | init(); 54 | } 55 | 56 | private void init() { 57 | setSurfaceTextureListener( this ); 58 | } 59 | 60 | public void addSurfaceTextureListener( SurfaceTextureListener listener ) { 61 | mListeners.add( listener ); 62 | 63 | if ( mSurfaceAvailable ) { 64 | listener.onSurfaceTextureAvailable( mSurfaceTexture, mWidth, mHeight ); 65 | } 66 | } 67 | 68 | public void setAspectRatio( int x, int y ) { 69 | mAspectX = x; 70 | mAspectY = y; 71 | requestLayout(); 72 | } 73 | 74 | @Override 75 | protected void onMeasure( int widthMeasureSpec, int heightMeasureSpec ) { 76 | int parentSpecifiedWidth = MeasureSpec.getSize( widthMeasureSpec ); 77 | int parentSpecifiedHeight = MeasureSpec.getSize( heightMeasureSpec ); 78 | 79 | double heightToWidthRatio = (double) mAspectY / (double) mAspectX; 80 | 81 | int width; 82 | int height; 83 | 84 | if ( parentSpecifiedWidth * heightToWidthRatio > parentSpecifiedHeight ) { 85 | width = (int) ( parentSpecifiedHeight / heightToWidthRatio ); 86 | height = parentSpecifiedHeight; 87 | } else { 88 | width = parentSpecifiedWidth; 89 | height = (int) ( parentSpecifiedWidth * heightToWidthRatio ); 90 | } 91 | 92 | super.onMeasure( MeasureSpec.makeMeasureSpec( width, MeasureSpec.EXACTLY ), MeasureSpec.makeMeasureSpec( height, MeasureSpec.EXACTLY ) ); 93 | } 94 | 95 | @Override 96 | public void onSurfaceTextureAvailable( SurfaceTexture surface, int width, int height ) { 97 | mSurfaceTexture = surface; 98 | 99 | mWidth = width; 100 | mHeight = height; 101 | 102 | mSurfaceAvailable = true; 103 | 104 | for ( SurfaceTextureListener listener : mListeners ) { 105 | listener.onSurfaceTextureAvailable( surface, width, height ); 106 | } 107 | } 108 | 109 | @Override 110 | public boolean onSurfaceTextureDestroyed( SurfaceTexture surface ) { 111 | for ( SurfaceTextureListener listener : mListeners ) { 112 | listener.onSurfaceTextureDestroyed( surface ); 113 | } 114 | 115 | return true; 116 | } 117 | 118 | @Override 119 | public void onSurfaceTextureSizeChanged( SurfaceTexture surface, int width, int height ) { 120 | mWidth = width; 121 | mHeight = height; 122 | 123 | for ( SurfaceTextureListener listener : mListeners ) { 124 | listener.onSurfaceTextureSizeChanged( surface, width, height ); 125 | } 126 | } 127 | 128 | @Override 129 | public void onSurfaceTextureUpdated( SurfaceTexture surface ) { 130 | for ( SurfaceTextureListener listener : mListeners ) { 131 | listener.onSurfaceTextureUpdated( surface ); 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /app/src/main/java/com/roryhool/videoinfoviewer/views/VideoPlayerView.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2016 Rory Hool 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | **/ 16 | 17 | package com.roryhool.videoinfoviewer.views; 18 | 19 | import java.io.IOException; 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | import java.util.Locale; 23 | 24 | import android.content.Context; 25 | import android.graphics.Point; 26 | import android.graphics.SurfaceTexture; 27 | import android.media.AudioManager; 28 | import android.media.MediaPlayer; 29 | import android.media.MediaPlayer.OnBufferingUpdateListener; 30 | import android.media.MediaPlayer.OnCompletionListener; 31 | import android.media.MediaPlayer.OnPreparedListener; 32 | import android.media.MediaPlayer.OnVideoSizeChangedListener; 33 | import android.net.Uri; 34 | import android.os.CountDownTimer; 35 | import android.util.AttributeSet; 36 | import android.util.Log; 37 | import android.view.Display; 38 | import android.view.Surface; 39 | import android.view.TextureView.SurfaceTextureListener; 40 | import android.view.View; 41 | import android.view.WindowManager; 42 | import android.view.animation.AlphaAnimation; 43 | import android.view.animation.TranslateAnimation; 44 | import android.widget.FrameLayout; 45 | import android.widget.ImageButton; 46 | import android.widget.ImageView; 47 | import android.widget.ProgressBar; 48 | import android.widget.RelativeLayout; 49 | import android.widget.SeekBar; 50 | import android.widget.SeekBar.OnSeekBarChangeListener; 51 | 52 | import com.roryhool.videoinfoviewer.R; 53 | import com.roryhool.videoinfoviewer.VideoActivity.CancelFullscreenEvent; 54 | import com.roryhool.videoinfoviewer.VideoInfoViewerApp; 55 | import com.roryhool.videoinfoviewer.analytics.Analytics; 56 | import com.roryhool.videoinfoviewer.animation.ResizeAnimation; 57 | import com.roryhool.videoinfoviewer.data.Video; 58 | import com.squareup.otto.Subscribe; 59 | 60 | public class VideoPlayerView extends FrameLayout implements SurfaceTextureListener, OnBufferingUpdateListener, OnCompletionListener, OnPreparedListener, OnVideoSizeChangedListener { 61 | 62 | public interface OnFullscreenListener { 63 | void onFullscreenChanged( boolean fullscreen ); 64 | } 65 | 66 | protected ScaledTextureView mVideoTextureView; 67 | protected SeekBar mSeekBar; 68 | protected ImageButton mPlayButton; 69 | protected ImageButton mFullscreenButton; 70 | protected RelativeLayout mVideoControls; 71 | protected ImageView mThumbView; 72 | protected ProgressBar mPlayableProgress; 73 | 74 | protected SurfaceTexture mSurfaceTexture; 75 | 76 | protected MediaPlayer mMediaPlayer; 77 | protected Video mVideo; 78 | 79 | protected boolean mControlsShowing; 80 | protected boolean mFullscreen; 81 | protected boolean mAlreadyLoggedPlayAction; 82 | protected boolean mStartPlayingWhenPrepared; 83 | 84 | protected List mFullscreenListeners = new ArrayList<>(); 85 | 86 | public VideoPlayerView( Context context ) { 87 | super( context ); 88 | init( context ); 89 | } 90 | 91 | public VideoPlayerView( Context context, AttributeSet attrs ) { 92 | super( context, attrs ); 93 | init( context ); 94 | } 95 | 96 | public VideoPlayerView( Context context, AttributeSet attrs, int defStyle ) { 97 | super( context, attrs, defStyle ); 98 | init( context ); 99 | } 100 | 101 | private void init( Context context ) { 102 | addView( View.inflate( context, R.layout.video_player, null ) ); 103 | 104 | mVideoTextureView = (ScaledTextureView) findViewById( R.id.video_texture_view ); 105 | 106 | mVideoTextureView.addSurfaceTextureListener( this ); 107 | 108 | mThumbView = (ImageView) findViewById( R.id.video_thumb ); 109 | 110 | mPlayableProgress = (ProgressBar) findViewById( R.id.playable_progress ); 111 | 112 | mPlayButton = (ImageButton) findViewById( R.id.play_button ); 113 | 114 | mVideoControls = (RelativeLayout) findViewById( R.id.video_controls ); 115 | mSeekBar = (SeekBar) findViewById( R.id.seek_bar ); 116 | mFullscreenButton = (ImageButton) findViewById( R.id.fullscreen_button ); 117 | 118 | mSeekBar.setOnSeekBarChangeListener( mOnSeekBarChangeListener ); 119 | mPlayButton.setOnClickListener( this::onClickPlay ); 120 | mFullscreenButton.setOnClickListener( this::onClickFullScreen ); 121 | 122 | setOnClickListener( this::onClickVideo ); 123 | 124 | VideoInfoViewerApp.getBus().register( this ); 125 | } 126 | 127 | public void setVideo( Video video ) { 128 | mVideo = video; 129 | mThumbView.setImageURI( Uri.parse( video.getThumbnailFilePath() ) ); 130 | } 131 | 132 | public void addFullscreenListener( OnFullscreenListener listener ) { 133 | mFullscreenListeners.add( listener ); 134 | } 135 | 136 | protected OnSeekBarChangeListener mOnSeekBarChangeListener = new OnSeekBarChangeListener() { 137 | 138 | boolean mResumePlaying = false; 139 | 140 | @Override 141 | public void onProgressChanged( SeekBar seekBar, int progress, boolean fromUser ) { 142 | if ( fromUser ) { 143 | if ( mMediaPlayer != null ) { 144 | mMediaPlayer.seekTo( progress ); 145 | } 146 | } 147 | } 148 | 149 | @Override 150 | public void onStartTrackingTouch( SeekBar seekBar ) { 151 | if ( mMediaPlayer != null && mMediaPlayer.isPlaying() ) { 152 | pause(); 153 | mResumePlaying = true; 154 | } 155 | } 156 | 157 | @Override 158 | public void onStopTrackingTouch( SeekBar seekBar ) { 159 | if ( mResumePlaying ) { 160 | if ( mMediaPlayer != null && !mMediaPlayer.isPlaying() ) { 161 | play(); 162 | } 163 | } 164 | mResumePlaying = false; 165 | } 166 | }; 167 | 168 | private void onClickPlay( View view ) { 169 | if ( mMediaPlayer == null ) { 170 | mStartPlayingWhenPrepared = true; 171 | mThumbView.setVisibility( View.GONE ); 172 | mControlsShowing = true; 173 | setupMediaPlayer(); 174 | } else if ( mMediaPlayer.isPlaying() ) { 175 | pause(); 176 | } else { 177 | play(); 178 | } 179 | } 180 | 181 | private void onClickVideo( View view ) { 182 | if ( mMediaPlayer != null ) { 183 | if ( !mControlsShowing ) { 184 | showControls(); 185 | } else if ( mMediaPlayer.isPlaying() ) { 186 | hideControls(); 187 | } 188 | } 189 | } 190 | 191 | private void onClickFullScreen( View view ) { 192 | toggleFullscreen(); 193 | } 194 | 195 | @Subscribe 196 | public void onCancelFullscreenEvent( CancelFullscreenEvent event ) { 197 | if ( mFullscreen ) { 198 | toggleFullscreen(); 199 | } 200 | } 201 | 202 | protected void toggleFullscreen() { 203 | Analytics.logEvent( "App Action", "Toggled Fullscreen Mode" ); 204 | if ( mMediaPlayer != null && mMediaPlayer.isPlaying() ) { 205 | resetTimer(); 206 | } 207 | 208 | int targetHeight = getContext().getResources().getDimensionPixelSize( R.dimen.video_player_size ); 209 | mFullscreen = getHeight() == targetHeight; 210 | fullscreenChanged( mFullscreen ); 211 | 212 | if ( mFullscreen ) { 213 | WindowManager wm = (WindowManager) getContext().getSystemService( Context.WINDOW_SERVICE ); 214 | Display display = wm.getDefaultDisplay(); 215 | Point size = new Point(); 216 | display.getSize( size ); 217 | 218 | targetHeight = size.y; 219 | } 220 | ResizeAnimation animation = new ResizeAnimation( VideoPlayerView.this, getHeight(), targetHeight, true ); 221 | animation.setDuration( 200 ); 222 | startAnimation( animation ); 223 | } 224 | 225 | public void shutdownMediaPlayer() { 226 | if ( mMediaPlayer != null ) { 227 | pause(); 228 | mMediaPlayer.release(); 229 | mMediaPlayer = null; 230 | 231 | slideOutVideoControls(); 232 | mThumbView.setVisibility( View.VISIBLE ); 233 | } 234 | 235 | } 236 | 237 | protected void setupMediaPlayer() { 238 | Surface s = new Surface( mSurfaceTexture ); 239 | 240 | try { 241 | mMediaPlayer = new MediaPlayer(); 242 | mMediaPlayer.setDataSource( getContext(), Uri.parse( mVideo.FilePath ) ); 243 | mMediaPlayer.setSurface( s ); 244 | mMediaPlayer.prepare(); 245 | mMediaPlayer.setOnBufferingUpdateListener( this ); 246 | mMediaPlayer.setOnCompletionListener( this ); 247 | mMediaPlayer.setOnPreparedListener( this ); 248 | mMediaPlayer.setOnVideoSizeChangedListener( this ); 249 | mMediaPlayer.setAudioStreamType( AudioManager.STREAM_MUSIC ); 250 | } catch ( IllegalArgumentException e ) { 251 | e.printStackTrace(); 252 | } catch ( SecurityException e ) { 253 | e.printStackTrace(); 254 | } catch ( IllegalStateException e ) { 255 | e.printStackTrace(); 256 | } catch ( IOException e ) { 257 | e.printStackTrace(); 258 | } 259 | } 260 | 261 | protected void fullscreenChanged( boolean fullscreen ) { 262 | for ( OnFullscreenListener listener : mFullscreenListeners ) { 263 | if ( listener != null ) { 264 | listener.onFullscreenChanged( fullscreen ); 265 | } 266 | } 267 | } 268 | 269 | public void handleResize() { 270 | int currentHeight = getHeight(); 271 | int targetHeight = getContext().getResources().getDimensionPixelSize( R.dimen.video_player_size ); 272 | 273 | if ( mFullscreen ) { 274 | WindowManager wm = (WindowManager) getContext().getSystemService( Context.WINDOW_SERVICE ); 275 | Display display = wm.getDefaultDisplay(); 276 | Point size = new Point(); 277 | display.getSize( size ); 278 | int height = size.y; 279 | 280 | targetHeight = height; 281 | 282 | Log.d( "this", String.format( Locale.US, "handleResize fullscreen, going from %d to %d", currentHeight, targetHeight ) ); 283 | ResizeAnimation animation = new ResizeAnimation( VideoPlayerView.this, currentHeight, targetHeight, true ); 284 | animation.setDuration( 200 ); 285 | startAnimation( animation ); 286 | } else { 287 | ResizeAnimation animation = new ResizeAnimation( VideoPlayerView.this, currentHeight, targetHeight, true ); 288 | animation.setDuration( 200 ); 289 | startAnimation( animation ); 290 | } 291 | } 292 | 293 | public void play() { 294 | if ( mMediaPlayer != null && !mMediaPlayer.isPlaying() ) { 295 | startTimer(); 296 | if ( mMediaPlayer.getCurrentPosition() >= mMediaPlayer.getDuration() ) { 297 | mMediaPlayer.seekTo( 0 ); 298 | } 299 | mMediaPlayer.start(); 300 | mPlayButton.setImageResource( R.drawable.ic_media_pause2 ); 301 | 302 | if ( !mAlreadyLoggedPlayAction ) { 303 | Analytics.logEvent( "App Action", "Played Video" ); 304 | mAlreadyLoggedPlayAction = true; 305 | } 306 | } 307 | } 308 | 309 | public void pause() { 310 | if ( mMediaPlayer != null && mMediaPlayer.isPlaying() ) { 311 | cancelTimer(); 312 | mMediaPlayer.pause(); 313 | mPlayButton.setImageResource( R.drawable.ic_media_play2 ); 314 | } 315 | } 316 | 317 | @Override 318 | public void onSurfaceTextureAvailable( SurfaceTexture surfaceTexture, int width, int height ) { 319 | mSurfaceTexture = surfaceTexture; 320 | 321 | mPlayableProgress.setVisibility( View.INVISIBLE ); 322 | mPlayButton.setVisibility( View.VISIBLE ); 323 | } 324 | 325 | @Override 326 | public boolean onSurfaceTextureDestroyed( SurfaceTexture surfaceTexture ) { 327 | return false; 328 | } 329 | 330 | @Override 331 | public void onSurfaceTextureSizeChanged( SurfaceTexture surfaceTexture, int width, int height ) { 332 | } 333 | 334 | @Override 335 | public void onSurfaceTextureUpdated( SurfaceTexture surfaceTexture ) { 336 | } 337 | 338 | @Override 339 | public void onVideoSizeChanged( MediaPlayer mediaPlayer, int width, int height ) { 340 | } 341 | 342 | @Override 343 | public void onPrepared( MediaPlayer mediaPlayer ) { 344 | if ( mMediaPlayer != null ) { 345 | mSeekBar.setProgress( 0 ); 346 | mSeekBar.setMax( mMediaPlayer.getDuration() ); 347 | 348 | if ( mStartPlayingWhenPrepared ) { 349 | play(); 350 | } 351 | mVideoTextureView.setAspectRatio( mMediaPlayer.getVideoWidth(), mMediaPlayer.getVideoHeight() ); 352 | slideInVideoControls(); 353 | resetTimer(); 354 | } 355 | } 356 | 357 | @Override 358 | public void onCompletion( MediaPlayer mediaPlayer ) { 359 | showControls(); 360 | cancelTimer(); 361 | mPlayButton.setImageResource( R.drawable.ic_media_play2 ); 362 | mSeekBar.setMax( mMediaPlayer.getDuration() ); 363 | } 364 | 365 | @Override 366 | public void onBufferingUpdate( MediaPlayer mp, int percent ) { 367 | } 368 | 369 | protected CountDownTimer mTimer = new CountDownTimer( 3000, 300 ) { 370 | 371 | public void onTick( long millisUntilFinished ) { 372 | updateProgress(); 373 | } 374 | 375 | public void onFinish() { 376 | hideControls(); 377 | } 378 | }; 379 | 380 | private void resetTimer() { 381 | cancelTimer(); 382 | startTimer(); 383 | } 384 | 385 | private void cancelTimer() { 386 | mTimer.cancel(); 387 | } 388 | 389 | private void startTimer() { 390 | mTimer.start(); 391 | } 392 | 393 | private void updateProgress() { 394 | if ( mMediaPlayer != null && mMediaPlayer.getDuration() > 0 ) { 395 | mSeekBar.setProgress( mMediaPlayer.getCurrentPosition() ); 396 | } 397 | } 398 | 399 | private void hideControls() { 400 | if ( !mControlsShowing ) { 401 | return; 402 | } 403 | mControlsShowing = false; 404 | 405 | AlphaAnimation alphaAnim = new AlphaAnimation( 1.0f, 0.0f ); 406 | alphaAnim.setDuration( 200 ); 407 | alphaAnim.setFillAfter( true ); 408 | mPlayButton.startAnimation( alphaAnim ); 409 | mPlayButton.setClickable( false ); 410 | 411 | slideOutVideoControls(); 412 | } 413 | 414 | private void showControls() { 415 | if ( mControlsShowing ) { 416 | return; 417 | } 418 | updateProgress(); 419 | mControlsShowing = true; 420 | 421 | fadeInPlayButton(); 422 | slideInVideoControls(); 423 | 424 | resetTimer(); 425 | } 426 | 427 | private void fadeInPlayButton() { 428 | AlphaAnimation alphaAnim = new AlphaAnimation( 0.0f, 1.0f ); 429 | alphaAnim.setDuration( 200 ); 430 | alphaAnim.setFillAfter( true ); 431 | mPlayButton.startAnimation( alphaAnim ); 432 | mPlayButton.setClickable( true ); 433 | } 434 | 435 | private void slideInVideoControls() { 436 | slideVideoControls( getResources().getDimensionPixelSize( R.dimen.min_touch ), 0 ); 437 | } 438 | 439 | private void slideOutVideoControls() { 440 | slideVideoControls( 0, getResources().getDimensionPixelSize( R.dimen.min_touch ) ); 441 | } 442 | 443 | private void slideVideoControls( int fromYDelta, int toYDelta ) { 444 | TranslateAnimation translateAnim = new TranslateAnimation( 0, 0, fromYDelta, toYDelta ); 445 | translateAnim.setDuration( 200 ); 446 | translateAnim.setFillAfter( true ); 447 | mVideoControls.startAnimation( translateAnim ); 448 | } 449 | } 450 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_add_white_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoolrory/VideoInfoViewer-Android/f4f5d0aa50be000f393a7a818155643496dae20a/app/src/main/res/drawable-hdpi/ic_add_white_36dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_arrow_forward_black_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoolrory/VideoInfoViewer-Android/f4f5d0aa50be000f393a7a818155643496dae20a/app/src/main/res/drawable-hdpi/ic_arrow_forward_black_48dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_expand.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoolrory/VideoInfoViewer-Android/f4f5d0aa50be000f393a7a818155643496dae20a/app/src/main/res/drawable-hdpi/ic_expand.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoolrory/VideoInfoViewer-Android/f4f5d0aa50be000f393a7a818155643496dae20a/app/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_media_pause2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoolrory/VideoInfoViewer-Android/f4f5d0aa50be000f393a7a818155643496dae20a/app/src/main/res/drawable-hdpi/ic_media_pause2.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_media_play2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoolrory/VideoInfoViewer-Android/f4f5d0aa50be000f393a7a818155643496dae20a/app/src/main/res/drawable-hdpi/ic_media_play2.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_menu_add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoolrory/VideoInfoViewer-Android/f4f5d0aa50be000f393a7a818155643496dae20a/app/src/main/res/drawable-hdpi/ic_menu_add.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_menu_info_details.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoolrory/VideoInfoViewer-Android/f4f5d0aa50be000f393a7a818155643496dae20a/app/src/main/res/drawable-hdpi/ic_menu_info_details.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_vidcontrol_fullscreen_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoolrory/VideoInfoViewer-Android/f4f5d0aa50be000f393a7a818155643496dae20a/app/src/main/res/drawable-hdpi/ic_vidcontrol_fullscreen_off.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_vidcontrol_fullscreen_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoolrory/VideoInfoViewer-Android/f4f5d0aa50be000f393a7a818155643496dae20a/app/src/main/res/drawable-hdpi/ic_vidcontrol_fullscreen_on.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_add_white_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoolrory/VideoInfoViewer-Android/f4f5d0aa50be000f393a7a818155643496dae20a/app/src/main/res/drawable-mdpi/ic_add_white_36dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_arrow_forward_black_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoolrory/VideoInfoViewer-Android/f4f5d0aa50be000f393a7a818155643496dae20a/app/src/main/res/drawable-mdpi/ic_arrow_forward_black_48dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_expand.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoolrory/VideoInfoViewer-Android/f4f5d0aa50be000f393a7a818155643496dae20a/app/src/main/res/drawable-mdpi/ic_expand.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoolrory/VideoInfoViewer-Android/f4f5d0aa50be000f393a7a818155643496dae20a/app/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_media_pause2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoolrory/VideoInfoViewer-Android/f4f5d0aa50be000f393a7a818155643496dae20a/app/src/main/res/drawable-mdpi/ic_media_pause2.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_media_play2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoolrory/VideoInfoViewer-Android/f4f5d0aa50be000f393a7a818155643496dae20a/app/src/main/res/drawable-mdpi/ic_media_play2.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_menu_add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoolrory/VideoInfoViewer-Android/f4f5d0aa50be000f393a7a818155643496dae20a/app/src/main/res/drawable-mdpi/ic_menu_add.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_menu_info_details.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoolrory/VideoInfoViewer-Android/f4f5d0aa50be000f393a7a818155643496dae20a/app/src/main/res/drawable-mdpi/ic_menu_info_details.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_vidcontrol_fullscreen_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoolrory/VideoInfoViewer-Android/f4f5d0aa50be000f393a7a818155643496dae20a/app/src/main/res/drawable-mdpi/ic_vidcontrol_fullscreen_off.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_vidcontrol_fullscreen_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoolrory/VideoInfoViewer-Android/f4f5d0aa50be000f393a7a818155643496dae20a/app/src/main/res/drawable-mdpi/ic_vidcontrol_fullscreen_on.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/activity_atom.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_add_white_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoolrory/VideoInfoViewer-Android/f4f5d0aa50be000f393a7a818155643496dae20a/app/src/main/res/drawable-xhdpi/ic_add_white_36dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_arrow_forward_black_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoolrory/VideoInfoViewer-Android/f4f5d0aa50be000f393a7a818155643496dae20a/app/src/main/res/drawable-xhdpi/ic_arrow_forward_black_48dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_expand.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoolrory/VideoInfoViewer-Android/f4f5d0aa50be000f393a7a818155643496dae20a/app/src/main/res/drawable-xhdpi/ic_expand.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoolrory/VideoInfoViewer-Android/f4f5d0aa50be000f393a7a818155643496dae20a/app/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_media_pause2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoolrory/VideoInfoViewer-Android/f4f5d0aa50be000f393a7a818155643496dae20a/app/src/main/res/drawable-xhdpi/ic_media_pause2.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_media_play2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoolrory/VideoInfoViewer-Android/f4f5d0aa50be000f393a7a818155643496dae20a/app/src/main/res/drawable-xhdpi/ic_media_play2.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_menu_add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoolrory/VideoInfoViewer-Android/f4f5d0aa50be000f393a7a818155643496dae20a/app/src/main/res/drawable-xhdpi/ic_menu_add.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_menu_info_details.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoolrory/VideoInfoViewer-Android/f4f5d0aa50be000f393a7a818155643496dae20a/app/src/main/res/drawable-xhdpi/ic_menu_info_details.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_vidcontrol_fullscreen_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoolrory/VideoInfoViewer-Android/f4f5d0aa50be000f393a7a818155643496dae20a/app/src/main/res/drawable-xhdpi/ic_vidcontrol_fullscreen_off.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_vidcontrol_fullscreen_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoolrory/VideoInfoViewer-Android/f4f5d0aa50be000f393a7a818155643496dae20a/app/src/main/res/drawable-xhdpi/ic_vidcontrol_fullscreen_on.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_add_white_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoolrory/VideoInfoViewer-Android/f4f5d0aa50be000f393a7a818155643496dae20a/app/src/main/res/drawable-xxhdpi/ic_add_white_36dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_arrow_forward_black_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoolrory/VideoInfoViewer-Android/f4f5d0aa50be000f393a7a818155643496dae20a/app/src/main/res/drawable-xxhdpi/ic_arrow_forward_black_48dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoolrory/VideoInfoViewer-Android/f4f5d0aa50be000f393a7a818155643496dae20a/app/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_menu_add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoolrory/VideoInfoViewer-Android/f4f5d0aa50be000f393a7a818155643496dae20a/app/src/main/res/drawable-xxhdpi/ic_menu_add.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_menu_info_details.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoolrory/VideoInfoViewer-Android/f4f5d0aa50be000f393a7a818155643496dae20a/app/src/main/res/drawable-xxhdpi/ic_menu_info_details.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_add_white_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoolrory/VideoInfoViewer-Android/f4f5d0aa50be000f393a7a818155643496dae20a/app/src/main/res/drawable-xxxhdpi/ic_add_white_36dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_arrow_forward_black_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoolrory/VideoInfoViewer-Android/f4f5d0aa50be000f393a7a818155643496dae20a/app/src/main/res/drawable-xxxhdpi/ic_arrow_forward_black_48dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/button_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | 9 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/expand_background_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | 9 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/expand_button_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 9 | 10 | 15 | 16 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/info_button_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 9 | 10 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/video_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_atom.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 12 | 13 | 19 | 20 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_search.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_video.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 13 | 14 | 20 | 21 | 27 | 28 | 33 | 34 | -------------------------------------------------------------------------------- /app/src/main/res/layout/atom.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 15 | 16 | 28 | 29 | 39 | 40 | 50 | 51 | 59 | 60 | 67 | 68 | -------------------------------------------------------------------------------- /app/src/main/res/layout/box_info.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 17 | 18 | 22 | 23 | 33 | 34 | 42 | 43 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 |