[
  {
    "path": ".classpath",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<classpath>\n\t<classpathentry kind=\"src\" path=\"src\"/>\n\t<classpathentry kind=\"src\" path=\"gen\"/>\n\t<classpathentry kind=\"con\" path=\"com.android.ide.eclipse.adt.ANDROID_FRAMEWORK\"/>\n\t<classpathentry exported=\"true\" kind=\"con\" path=\"com.android.ide.eclipse.adt.LIBRARIES\"/>\n\t<classpathentry exported=\"true\" kind=\"con\" path=\"com.android.ide.eclipse.adt.DEPENDENCIES\"/>\n\t<classpathentry kind=\"output\" path=\"bin/classes\"/>\n</classpath>\n"
  },
  {
    "path": ".gitignore",
    "content": ".gradle\n.DS_Store\nYouTubeDirectLiteforAndroid.iml\n.idea\nbuild\nlocal.properties"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# How to contribute #\n\nWe'd love to accept your patches and contributions to this project.  There are\na just a few small guidelines you need to follow.\n\n\n## Contributor License Agreement ##\n\nContributions to any Google project must be accompanied by a Contributor\nLicense Agreement.  This is not a copyright **assignment**, it simply gives\nGoogle permission to use and redistribute your contributions as part of the\nproject.\n\n  * If you are an individual writing original source code and you're sure you\n    own the intellectual property, then you'll need to sign an [individual\n    CLA][].\n\n  * If you work for a company that wants to allow you to contribute your work,\n    then you'll need to sign a [corporate CLA][].\n\nYou generally only need to submit a CLA once, so if you've already submitted\none (even if it was for a different project), you probably don't need to do it\nagain.\n\n[individual CLA]: https://developers.google.com/open-source/cla/individual\n[corporate CLA]: https://developers.google.com/open-source/cla/corporate\n\n\n## Submitting a patch ##\n\n  1. It's generally best to start by opening a new issue describing the bug or\n     feature you're intending to fix.  Even if you think it's relatively minor,\n     it's helpful to know what people are working on.  Mention in the initial\n     issue that you are planning to work on that bug or feature so that it can\n     be assigned to you.\n\n  1. Follow the normal process of [forking][] the project, and setup a new\n     branch to work in.  It's important that each group of changes be done in\n     separate branches in order to ensure that a pull request only includes the\n     commits related to that bug or feature.\n\n  1. Go makes it very simple to ensure properly formatted code, so always run\n     `go fmt` on your code before committing it.  You should also run\n     [golint][] over your code.  As noted in the [golint readme][], it's not\n     strictly necessary that your code be completely \"lint-free\", but this will\n     help you find common style issues.\n\n  1. Any significant changes should almost always be accompanied by tests.  The\n     project already has good test coverage, so look at some of the existing\n     tests if you're unsure how to go about it.  [gocov][] and [gocov-html][]\n     are invaluable tools for seeing which parts of your code aren't being\n     exercised by your tests.\n\n  1. Do your best to have [well-formed commit messages][] for each change.\n     This provides consistency throughout the project, and ensures that commit\n     messages are able to be formatted properly by various git tools.\n\n  1. Finally, push the commits to your fork and submit a [pull request][].\n\n[forking]: https://help.github.com/articles/fork-a-repo\n[golint]: https://github.com/golang/lint\n[golint readme]: https://github.com/golang/lint/blob/master/README\n[gocov]: https://github.com/axw/gocov\n[gocov-html]: https://github.com/matm/gocov-html\n[well-formed commit messages]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html\n[squash]: http://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits\n[pull request]: https://help.github.com/articles/creating-a-pull-request\n\n\n## Other notes on code organization ##\n\nCurrently, everything is defined in the main `github` package, with API methods\nbroken into separate service objects.  These services map directly to how\nthe [GitHub API documentation][] is organized, so use that as your guide for\nwhere to put new methods.\n\nSub-service (e.g. [Repo Hooks][]) implementations are split into separate files\nbased on the APIs they provide. These files are named service_api.go (e.g.\nrepos_hooks.go) to describe the API to service mappings.\n\n[GitHub API documentation]: http://developer.github.com/v3/\n[Repo Hooks]: http://developer.github.com/v3/repos/hooks/\n"
  },
  {
    "path": "LICENSE-2.0.txt",
    "content": "\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "YouTube Direct Lite for Android\n===========\n\nThe code is a reference implementation for an Android OS application that captures video, uploads it to YouTube, and submits the video to a [YouTube Direct Lite](http://code.google.com/p/youtube-direct-lite/) instance.\n\nFor more information, you can read the [Youtube API blog post](http://apiblog.youtube.com/2013/08/heres-my-playlist-so-submit-video-maybe.html).\n\nThis application utilizes [YouTube Data API v3](https://developers.google.com/youtube/v3/) , [YouTube Android Player API](https://developers.google.com/youtube/android/player/), [YouTube Resumable Uploads](https://developers.google.com/youtube/v3/guides/using_resumable_upload_protocol?hl=en), [Google Play Services](https://developer.android.com/google/play-services/index.html) and [Plus API](https://developers.google.com/+/mobile/android/Google).\n\nTo use this application,\n\n1. In your [Google Developers Console](https://console.developers.google.com),\n 1. Enable the YouTube Data API v3 and Google+ API.\n 1. Create a client ID for Android, using your SHA1 and package name.\n1. [Register your Android app](https://developers.google.com/youtube/android/player/register)\n1. Plug in your Playlist Id into Constants.java and Android API Key into Auth.java\n\n![alt tag](https://ytd-android.googlecode.com/files/YTDL.png)\n\n![alt tag](https://ytd-android.googlecode.com/files/YTDL-review.png)\n\n![alt tag](https://ytd-android.googlecode.com/files/YTDL-upload.png)\n\n![alt tag](https://ytd-android.googlecode.com/files/YTDL-watch.png)\n"
  },
  {
    "path": "app/.gitignore",
    "content": "/build\napp.iml"
  },
  {
    "path": "app/build.gradle",
    "content": "apply plugin: 'com.android.application'\n\nandroid {\n    compileSdkVersion 23\n    buildToolsVersion \"23.0.0\"\n\n    defaultConfig {\n        applicationId \"com.google.ytdl\"\n        minSdkVersion 16\n        targetSdkVersion 23\n        versionCode 1\n        versionName \"1.0\"\n    }\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n}\n\ndependencies {\n    compile fileTree(dir: 'libs', include: ['*.jar'])\n    compile 'com.google.android.gms:play-services-plus:7.8.0'\n    compile 'com.android.support:support-v13:23.0.0'\n    compile 'com.google.apis:google-api-services-youtube:v3-rev120-1.19.0'\n    compile 'com.google.http-client:google-http-client-android:+'\n    compile 'com.google.api-client:google-api-client-android:+'\n    compile 'com.google.api-client:google-api-client-gson:+'\n    compile 'com.google.code.gson:gson:2.2.4'\n    compile 'com.mcxiaoke.volley:library:1.0.18'\n    compile files('libs/YouTubeAndroidPlayerApi.jar')\n}\n"
  },
  {
    "path": "app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /Users/ulukaya/android-sdks/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the proguardFiles\n# directive in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n"
  },
  {
    "path": "app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.google.ytdl\"\n    android:versionCode=\"1\"\n    android:versionName=\"1.0\">\n\n    <uses-sdk\n        android:minSdkVersion=\"16\"\n        android:targetSdkVersion=\"23\" />\n\n    <uses-permission android:name=\"android.permission.INTERNET\" />\n    <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" />\n    <uses-permission android:name=\"android.permission.GET_ACCOUNTS\" />\n\n    <application\n        android:allowBackup=\"true\"\n        android:icon=\"@drawable/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:theme=\"@style/AppTheme\">\n        <activity\n            android:name=\"com.google.ytdl.MainActivity\"\n            android:configChanges=\"orientation|screenSize\"\n            android:label=\"@string/app_name\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n\n        <service\n            android:name=\"com.google.ytdl.UploadService\"\n            android:exported=\"true\" />\n\n        <activity\n            android:name=\"com.google.ytdl.PlayActivity\"\n            android:label=\"@string/title_activity_play\"\n            android:parentActivityName=\"com.google.ytdl.MainActivity\"></activity>\n        <activity\n            android:name=\"com.google.ytdl.ReviewActivity\"\n            android:label=\"@string/title_activity_review\"\n            android:parentActivityName=\"com.google.ytdl.MainActivity\"></activity>\n    </application>\n\n</manifest>"
  },
  {
    "path": "app/src/main/java/com/google/ytdl/Auth.java",
    "content": "/*\n * Copyright (c) 2013 Google Inc.\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n * \n * http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\n\npackage com.google.ytdl;\n\nimport com.google.android.gms.common.Scopes;\nimport com.google.api.services.youtube.YouTubeScopes;\n\npublic class Auth {\n    // Register an API key here: https://console.developers.google.com\n    public static final String KEY = \"Replace me with your API key\";\n\n    public static final String[] SCOPES = {Scopes.PROFILE, YouTubeScopes.YOUTUBE};\n}\n"
  },
  {
    "path": "app/src/main/java/com/google/ytdl/Constants.java",
    "content": "/*\n * Copyright (c) 2013 Google Inc.\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n * \n * http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\n\npackage com.google.ytdl;\n\n/**\n * @author Ibrahim Ulukaya <ulukaya@google.com>\n *         <p/>\n *         This class hold constants.\n */\npublic class Constants {\n    public static final int MAX_KEYWORD_LENGTH = 30;\n    public static final String DEFAULT_KEYWORD = \"ytdl\";\n    // A playlist ID is a string that begins with PL. You must replace this string with the correct\n    // playlist ID for the app to work\n    public static final String UPLOAD_PLAYLIST = \"Replace me with the playlist ID you want to upload into\";\n    public static final String APP_NAME = \"ytd-android\";\n}\n"
  },
  {
    "path": "app/src/main/java/com/google/ytdl/MainActivity.java",
    "content": "/*\n * Copyright (c) 2013 Google Inc.\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n * \n * http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\n\npackage com.google.ytdl;\n\nimport android.accounts.AccountManager;\nimport android.app.Activity;\nimport android.app.Dialog;\nimport android.content.BroadcastReceiver;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.IntentFilter;\nimport android.content.SharedPreferences;\nimport android.net.Uri;\nimport android.os.AsyncTask;\nimport android.os.Bundle;\nimport android.preference.PreferenceManager;\nimport android.provider.MediaStore;\nimport android.support.v4.content.LocalBroadcastManager;\nimport android.text.TextUtils;\nimport android.util.Log;\nimport android.view.LayoutInflater;\nimport android.view.Menu;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.Window;\nimport android.widget.ArrayAdapter;\nimport android.widget.ListAdapter;\nimport android.widget.ListView;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport com.android.volley.toolbox.ImageLoader;\nimport com.google.android.gms.common.GooglePlayServicesUtil;\nimport com.google.api.client.extensions.android.http.AndroidHttp;\nimport com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential;\nimport com.google.api.client.googleapis.extensions.android.gms.auth.GooglePlayServicesAvailabilityIOException;\nimport com.google.api.client.googleapis.extensions.android.gms.auth.UserRecoverableAuthIOException;\nimport com.google.api.client.http.HttpTransport;\nimport com.google.api.client.json.JsonFactory;\nimport com.google.api.client.json.gson.GsonFactory;\nimport com.google.api.client.util.ExponentialBackOff;\nimport com.google.api.services.youtube.YouTube;\nimport com.google.api.services.youtube.model.ChannelListResponse;\nimport com.google.api.services.youtube.model.PlaylistItem;\nimport com.google.api.services.youtube.model.PlaylistItemListResponse;\nimport com.google.api.services.youtube.model.Video;\nimport com.google.api.services.youtube.model.VideoListResponse;\nimport com.google.api.services.youtube.model.VideoSnippet;\nimport com.google.ytdl.util.NetworkSingleton;\nimport com.google.ytdl.util.Upload;\nimport com.google.ytdl.util.Utils;\nimport com.google.ytdl.util.VideoData;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.List;\n\n/**\n * @author Ibrahim Ulukaya <ulukaya@google.com>\n *         <p/>\n *         Main activity class which handles authorization and intents.\n */\npublic class MainActivity extends Activity implements\n        UploadsListFragment.Callbacks {\n    // private static final int MEDIA_TYPE_VIDEO = 7;\n    public static final String ACCOUNT_KEY = \"accountName\";\n    public static final String MESSAGE_KEY = \"message\";\n    public static final String YOUTUBE_ID = \"youtubeId\";\n    public static final String YOUTUBE_WATCH_URL_PREFIX = \"http://www.youtube.com/watch?v=\";\n    static final String REQUEST_AUTHORIZATION_INTENT = \"com.google.example.yt.RequestAuth\";\n    static final String REQUEST_AUTHORIZATION_INTENT_PARAM = \"com.google.example.yt.RequestAuth.param\";\n    private static final int REQUEST_GOOGLE_PLAY_SERVICES = 0;\n    private static final int REQUEST_GMS_ERROR_DIALOG = 1;\n    private static final int REQUEST_ACCOUNT_PICKER = 2;\n    private static final int REQUEST_AUTHORIZATION = 3;\n    private static final int RESULT_PICK_IMAGE_CROP = 4;\n    private static final int RESULT_VIDEO_CAP = 5;\n    private static final int REQUEST_DIRECT_TAG = 6;\n    private static final String TAG = \"MainActivity\";\n    final HttpTransport transport = AndroidHttp.newCompatibleTransport();\n    final JsonFactory jsonFactory = new GsonFactory();\n    GoogleAccountCredential credential;\n    private ImageLoader mImageLoader;\n    private String mChosenAccountName;\n    private Uri mFileURI = null;\n    private VideoData mVideoData;\n    private UploadBroadcastReceiver broadcastReceiver;\n    private UploadsListFragment mUploadsListFragment;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        getWindow().requestFeature(Window.FEATURE_INDETERMINATE_PROGRESS);\n        super.onCreate(savedInstanceState);\n\n        mUploadsListFragment = new UploadsListFragment(getApplicationContext());\n\n        // Check to see if the proper keys and playlist IDs have been set up\n        if (!isCorrectlyConfigured()) {\n            setContentView(R.layout.developer_setup_required);\n            showMissingConfigurations();\n        } else {\n            setContentView(R.layout.activity_main);\n\n            ensureLoader();\n\n            credential = GoogleAccountCredential.usingOAuth2(\n                    getApplicationContext(), Arrays.asList(Auth.SCOPES));\n            // set exponential backoff policy\n            credential.setBackOff(new ExponentialBackOff());\n\n            if (savedInstanceState != null) {\n                mChosenAccountName = savedInstanceState.getString(ACCOUNT_KEY);\n            } else {\n                loadAccount();\n            }\n\n            credential.setSelectedAccountName(mChosenAccountName);\n\n            mUploadsListFragment = (UploadsListFragment) getFragmentManager()\n                    .findFragmentById(R.id.list_fragment);\n\n        }\n    }\n\n    /**\n     * This method checks various internal states to figure out at startup time\n     * whether certain elements have been configured correctly by the developer.\n     * Checks that:\n     * <ul>\n     * <li>the API key has been configured</li>\n     * <li>the playlist ID has been configured</li>\n     * </ul>\n     *\n     * @return true if the application is correctly configured for use, false if\n     * not\n     */\n    private boolean isCorrectlyConfigured() {\n        // This isn't going to internationalize well, but we only really need\n        // this for the sample app.\n        // Real applications will remove this section of code and ensure that\n        // all of these values are configured.\n        if (Auth.KEY.startsWith(\"Replace\")) {\n            return false;\n        }\n        if (Constants.UPLOAD_PLAYLIST.startsWith(\"Replace\")) {\n            return false;\n        }\n        return true;\n    }\n\n    /**\n     * This method renders the ListView explaining what the configurations the\n     * developer of this application has to complete. Typically, these are\n     * static variables defined in {@link Auth} and {@link Constants}.\n     */\n    private void showMissingConfigurations() {\n        List<MissingConfig> missingConfigs = new ArrayList<MissingConfig>();\n\n        // Make sure an API key is registered\n        if (Auth.KEY.startsWith(\"Replace\")) {\n            missingConfigs\n                    .add(new MissingConfig(\n                            \"API key not configured\",\n                            \"KEY constant in Auth.java must be configured with your Simple API key from the Google API Console\"));\n        }\n\n        // Make sure a playlist ID is registered\n        if (Constants.UPLOAD_PLAYLIST.startsWith(\"Replace\")) {\n            missingConfigs\n                    .add(new MissingConfig(\n                            \"Playlist ID not configured\",\n                            \"UPLOAD_PLAYLIST constant in Constants.java must be configured with a Playlist ID to submit to. (The playlist ID typically has a prexix of PL)\"));\n        }\n\n        // Renders a simple_list_item_2, which consists of a title and a body\n        // element\n        ListAdapter adapter = new ArrayAdapter<MissingConfig>(this,\n                android.R.layout.simple_list_item_2, missingConfigs) {\n            @Override\n            public View getView(int position, View convertView, ViewGroup parent) {\n                View row;\n                if (convertView == null) {\n                    LayoutInflater inflater = (LayoutInflater) getApplicationContext()\n                            .getSystemService(Context.LAYOUT_INFLATER_SERVICE);\n                    row = inflater.inflate(android.R.layout.simple_list_item_2,\n                            null);\n                } else {\n                    row = convertView;\n                }\n\n                TextView titleView = (TextView) row\n                        .findViewById(android.R.id.text1);\n                TextView bodyView = (TextView) row\n                        .findViewById(android.R.id.text2);\n                MissingConfig config = getItem(position);\n                titleView.setText(config.title);\n                bodyView.setText(config.body);\n                return row;\n            }\n        };\n\n        // Wire the data adapter up to the view\n        ListView missingConfigList = (ListView) findViewById(R.id.missing_config_list);\n        missingConfigList.setAdapter(adapter);\n    }\n\n    @Override\n    protected void onResume() {\n        super.onResume();\n        if (broadcastReceiver == null)\n            broadcastReceiver = new UploadBroadcastReceiver();\n        IntentFilter intentFilter = new IntentFilter(\n                REQUEST_AUTHORIZATION_INTENT);\n        LocalBroadcastManager.getInstance(this).registerReceiver(\n                broadcastReceiver, intentFilter);\n    }\n\n    private void ensureLoader() {\n        if (mImageLoader == null) {\n            // Get the ImageLoader through your singleton class.\n            mImageLoader = NetworkSingleton.getInstance(this).getImageLoader();\n        }\n    }\n\n    private void loadAccount() {\n        SharedPreferences sp = PreferenceManager\n                .getDefaultSharedPreferences(this);\n        mChosenAccountName = sp.getString(ACCOUNT_KEY, null);\n        invalidateOptionsMenu();\n    }\n\n    private void saveAccount() {\n        SharedPreferences sp = PreferenceManager\n                .getDefaultSharedPreferences(this);\n        sp.edit().putString(ACCOUNT_KEY, mChosenAccountName).commit();\n    }\n\n    private void loadData() {\n        if (mChosenAccountName == null) {\n            return;\n        }\n\n        loadUploadedVideos();\n    }\n\n    @Override\n    protected void onPause() {\n        super.onPause();\n        if (broadcastReceiver != null) {\n            LocalBroadcastManager.getInstance(this).unregisterReceiver(\n                    broadcastReceiver);\n        }\n        if (isFinishing()) {\n            // mHandler.removeCallbacksAndMessages(null);\n        }\n    }\n\n    @Override\n    public boolean onCreateOptionsMenu(Menu menu) {\n        super.onCreateOptionsMenu(menu);\n        getMenuInflater().inflate(R.menu.activity_main, menu);\n        return super.onCreateOptionsMenu(menu);\n    }\n\n    @Override\n    public boolean onOptionsItemSelected(MenuItem item) {\n        switch (item.getItemId()) {\n            case R.id.menu_refresh:\n                loadData();\n                break;\n            case R.id.menu_accounts:\n                chooseAccount();\n                return true;\n        }\n        return super.onOptionsItemSelected(item);\n    }\n\n    @Override\n    protected void onActivityResult(int requestCode, int resultCode, Intent data) {\n        super.onActivityResult(requestCode, resultCode, data);\n        switch (requestCode) {\n            case REQUEST_GMS_ERROR_DIALOG:\n                break;\n            case RESULT_PICK_IMAGE_CROP:\n                if (resultCode == RESULT_OK) {\n                    mFileURI = data.getData();\n                    if (mFileURI != null) {\n                        Intent intent = new Intent(this, ReviewActivity.class);\n                        intent.setData(mFileURI);\n                        startActivity(intent);\n                    }\n                }\n                break;\n\n            case RESULT_VIDEO_CAP:\n                if (resultCode == RESULT_OK) {\n                    mFileURI = data.getData();\n                    if (mFileURI != null) {\n                        Intent intent = new Intent(this, ReviewActivity.class);\n                        intent.setData(mFileURI);\n                        startActivity(intent);\n                    }\n                }\n                break;\n            case REQUEST_GOOGLE_PLAY_SERVICES:\n                if (resultCode == Activity.RESULT_OK) {\n                    haveGooglePlayServices();\n                } else {\n                    checkGooglePlayServicesAvailable();\n                }\n                break;\n            case REQUEST_AUTHORIZATION:\n                if (resultCode != Activity.RESULT_OK) {\n                    chooseAccount();\n                }\n                break;\n            case REQUEST_ACCOUNT_PICKER:\n                if (resultCode == Activity.RESULT_OK && data != null\n                        && data.getExtras() != null) {\n                    String accountName = data.getExtras().getString(\n                            AccountManager.KEY_ACCOUNT_NAME);\n                    if (accountName != null) {\n                        mChosenAccountName = accountName;\n                        credential.setSelectedAccountName(accountName);\n                        saveAccount();\n                    }\n                }\n                break;\n            case REQUEST_DIRECT_TAG:\n                if (resultCode == Activity.RESULT_OK && data != null\n                        && data.getExtras() != null) {\n                    String youtubeId = data.getStringExtra(YOUTUBE_ID);\n                    if (youtubeId.equals(mVideoData.getYouTubeId())) {\n                        directTag(mVideoData);\n                    }\n                }\n                break;\n        }\n    }\n\n    private void directTag(final VideoData video) {\n        final Video updateVideo = new Video();\n        VideoSnippet snippet = video\n                .addTags(Arrays.asList(\n                        Constants.DEFAULT_KEYWORD,\n                        Upload.generateKeywordFromPlaylistId(Constants.UPLOAD_PLAYLIST)));\n        updateVideo.setSnippet(snippet);\n        updateVideo.setId(video.getYouTubeId());\n\n        new AsyncTask<Void, Void, Void>() {\n            @Override\n            protected Void doInBackground(Void... voids) {\n\n                YouTube youtube = new YouTube.Builder(transport, jsonFactory,\n                        credential).setApplicationName(Constants.APP_NAME)\n                        .build();\n                try {\n                    youtube.videos().update(\"snippet\", updateVideo).execute();\n                } catch (UserRecoverableAuthIOException e) {\n                    startActivityForResult(e.getIntent(), REQUEST_AUTHORIZATION);\n                } catch (IOException e) {\n                    Log.e(TAG, e.getMessage());\n                }\n                return null;\n            }\n\n        }.execute((Void) null);\n        Toast.makeText(this,\n                R.string.video_submitted_to_ytdl, Toast.LENGTH_LONG)\n                .show();\n    }\n\n    @Override\n    protected void onSaveInstanceState(Bundle outState) {\n        super.onSaveInstanceState(outState);\n        outState.putString(ACCOUNT_KEY, mChosenAccountName);\n    }\n\n    private void loadUploadedVideos() {\n        if (mChosenAccountName == null) {\n            return;\n        }\n\n        setProgressBarIndeterminateVisibility(true);\n        new AsyncTask<Void, Void, List<VideoData>>() {\n            @Override\n            protected List<VideoData> doInBackground(Void... voids) {\n\n                YouTube youtube = new YouTube.Builder(transport, jsonFactory,\n                        credential).setApplicationName(Constants.APP_NAME)\n                        .build();\n\n                try {\n                    /*\n                     * Now that the user is authenticated, the app makes a\n\t\t\t\t\t * channels list request to get the authenticated user's\n\t\t\t\t\t * channel. Returned with that data is the playlist id for\n\t\t\t\t\t * the uploaded videos.\n\t\t\t\t\t * https://developers.google.com/youtube\n\t\t\t\t\t * /v3/docs/channels/list\n\t\t\t\t\t */\n                    ChannelListResponse clr = youtube.channels()\n                            .list(\"contentDetails\").setMine(true).execute();\n\n                    // Get the user's uploads playlist's id from channel list\n                    // response\n                    String uploadsPlaylistId = clr.getItems().get(0)\n                            .getContentDetails().getRelatedPlaylists()\n                            .getUploads();\n\n                    List<VideoData> videos = new ArrayList<VideoData>();\n\n                    // Get videos from user's upload playlist with a playlist\n                    // items list request\n                    PlaylistItemListResponse pilr = youtube.playlistItems()\n                            .list(\"id,contentDetails\")\n                            .setPlaylistId(uploadsPlaylistId)\n                            .setMaxResults(20l).execute();\n                    List<String> videoIds = new ArrayList<String>();\n\n                    // Iterate over playlist item list response to get uploaded\n                    // videos' ids.\n                    for (PlaylistItem item : pilr.getItems()) {\n                        videoIds.add(item.getContentDetails().getVideoId());\n                    }\n\n                    // Get details of uploaded videos with a videos list\n                    // request.\n                    VideoListResponse vlr = youtube.videos()\n                            .list(\"id,snippet,status\")\n                            .setId(TextUtils.join(\",\", videoIds)).execute();\n\n                    // Add only the public videos to the local videos list.\n                    for (Video video : vlr.getItems()) {\n                        if (\"public\".equals(video.getStatus()\n                                .getPrivacyStatus())) {\n                            VideoData videoData = new VideoData();\n                            videoData.setVideo(video);\n                            videos.add(videoData);\n                        }\n                    }\n\n                    // Sort videos by title\n                    Collections.sort(videos, new Comparator<VideoData>() {\n                        @Override\n                        public int compare(VideoData videoData,\n                                           VideoData videoData2) {\n                            return videoData.getTitle().compareTo(\n                                    videoData2.getTitle());\n                        }\n                    });\n\n                    return videos;\n\n                } catch (final GooglePlayServicesAvailabilityIOException availabilityException) {\n                    showGooglePlayServicesAvailabilityErrorDialog(availabilityException\n                            .getConnectionStatusCode());\n                } catch (UserRecoverableAuthIOException userRecoverableException) {\n                    startActivityForResult(\n                            userRecoverableException.getIntent(),\n                            REQUEST_AUTHORIZATION);\n                } catch (IOException e) {\n                    Utils.logAndShow(MainActivity.this, Constants.APP_NAME, e);\n                }\n                return null;\n            }\n\n            @Override\n            protected void onPostExecute(List<VideoData> videos) {\n                setProgressBarIndeterminateVisibility(false);\n\n                if (videos == null) {\n                    return;\n                }\n\n                mUploadsListFragment.setVideos(videos);\n            }\n\n        }.execute((Void) null);\n    }\n\n    @Override\n    public void onBackPressed() {\n        // if (mDirectFragment.popPlayerFromBackStack()) {\n        // super.onBackPressed();\n        // }\n    }\n\n    @Override\n    public ImageLoader onGetImageLoader() {\n        ensureLoader();\n        return mImageLoader;\n    }\n\n    @Override\n    public void onVideoSelected(VideoData video) {\n        mVideoData = video;\n        Intent intent = new Intent(this, PlayActivity.class);\n        intent.putExtra(YOUTUBE_ID, video.getYouTubeId());\n        startActivityForResult(intent, REQUEST_DIRECT_TAG);\n    }\n\n    @Override\n    public void onConnected(String connectedAccountName) {\n        // Make API requests only when the user has successfully signed in.\n        loadData();\n    }\n\n    public void pickFile(View view) {\n        Intent intent = new Intent(Intent.ACTION_PICK);\n        intent.setType(\"video/*\");\n        startActivityForResult(intent, RESULT_PICK_IMAGE_CROP);\n    }\n\n    public void recordVideo(View view) {\n        Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);\n\n        // Workaround for Nexus 7 Android 4.3 Intent Returning Null problem\n        // create a file to save the video in specific folder (this works for\n        // video only)\n        // mFileURI = getOutputMediaFile(MEDIA_TYPE_VIDEO);\n        // intent.putExtra(MediaStore.EXTRA_OUTPUT, mFileURI);\n\n        // set the video image quality to high\n        intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1);\n\n        // start the Video Capture Intent\n        startActivityForResult(intent, RESULT_VIDEO_CAP);\n    }\n\n    public void showGooglePlayServicesAvailabilityErrorDialog(\n            final int connectionStatusCode) {\n        runOnUiThread(new Runnable() {\n            public void run() {\n                Dialog dialog = GooglePlayServicesUtil.getErrorDialog(\n                        connectionStatusCode, MainActivity.this,\n                        REQUEST_GOOGLE_PLAY_SERVICES);\n                dialog.show();\n            }\n        });\n    }\n\n    /**\n     * Check that Google Play services APK is installed and up to date.\n     */\n    private boolean checkGooglePlayServicesAvailable() {\n        final int connectionStatusCode = GooglePlayServicesUtil\n                .isGooglePlayServicesAvailable(this);\n        if (GooglePlayServicesUtil.isUserRecoverableError(connectionStatusCode)) {\n            showGooglePlayServicesAvailabilityErrorDialog(connectionStatusCode);\n            return false;\n        }\n        return true;\n    }\n\n    private void haveGooglePlayServices() {\n        // check if there is already an account selected\n        if (credential.getSelectedAccountName() == null) {\n            // ask user to choose account\n            chooseAccount();\n        }\n    }\n\n    private void chooseAccount() {\n        startActivityForResult(credential.newChooseAccountIntent(),\n                REQUEST_ACCOUNT_PICKER);\n    }\n\n    /**\n     * Private class representing a missing configuration and what the developer\n     * can do to fix the issue.\n     */\n    private class MissingConfig {\n\n        public final String title;\n        public final String body;\n\n        public MissingConfig(String title, String body) {\n            this.title = title;\n            this.body = body;\n        }\n    }\n\n    // public Uri getOutputMediaFile(int type)\n    // {\n    // // To be safe, you should check that the SDCard is mounted\n    // if(Environment.getExternalStorageState() != null) {\n    // // this works for Android 2.2 and above\n    // File mediaStorageDir = new\n    // File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES),\n    // \"SMW_VIDEO\");\n    //\n    // // This location works best if you want the created images to be shared\n    // // between applications and persist after your app has been uninstalled.\n    //\n    // // Create the storage directory if it does not exist\n    // if (! mediaStorageDir.exists()) {\n    // if (! mediaStorageDir.mkdirs()) {\n    // Log.d(TAG, \"failed to create directory\");\n    // return null;\n    // }\n    // }\n    //\n    // // Create a media file name\n    // String timeStamp = new SimpleDateFormat(\"yyyyMMdd_HHmmss\",\n    // Locale.getDefault()).format(new Date());\n    // File mediaFile;\n    // if(type == MEDIA_TYPE_VIDEO) {\n    // mediaFile = new File(mediaStorageDir.getPath() + File.separator +\n    // \"VID_\"+ timeStamp + \".mp4\");\n    // } else {\n    // return null;\n    // }\n    //\n    // return Uri.fromFile(mediaFile);\n    // }\n    //\n    // return null;\n    // }\n\n    private class UploadBroadcastReceiver extends BroadcastReceiver {\n        @Override\n        public void onReceive(Context context, Intent intent) {\n            if (intent.getAction().equals(REQUEST_AUTHORIZATION_INTENT)) {\n                Log.d(TAG, \"Request auth received - executing the intent\");\n                Intent toRun = intent\n                        .getParcelableExtra(REQUEST_AUTHORIZATION_INTENT_PARAM);\n                startActivityForResult(toRun, REQUEST_AUTHORIZATION);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/google/ytdl/PlayActivity.java",
    "content": "package com.google.ytdl;\n\nimport android.app.Activity;\nimport android.app.FragmentTransaction;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.support.v4.app.NavUtils;\nimport android.view.Menu;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.widget.Button;\nimport android.widget.Toast;\n\nimport com.google.android.youtube.player.YouTubeInitializationResult;\nimport com.google.android.youtube.player.YouTubePlayer;\nimport com.google.android.youtube.player.YouTubePlayer.OnFullscreenListener;\nimport com.google.android.youtube.player.YouTubePlayer.PlayerStateChangeListener;\nimport com.google.android.youtube.player.YouTubePlayerFragment;\nimport com.google.api.client.extensions.android.http.AndroidHttp;\nimport com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential;\nimport com.google.api.client.http.HttpTransport;\nimport com.google.api.client.json.JsonFactory;\nimport com.google.api.client.json.gson.GsonFactory;\nimport com.google.ytdl.util.VideoData;\n\n/*\n * Copyright (c) 2013 Google Inc.\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n * \n * http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\n\n/**\n * @author Ibrahim Ulukaya <ulukaya@google.com>\n *         <p/>\n *         Main fragment showing YouTube Direct Lite upload options and having\n *         YT Android Player.\n */\npublic class PlayActivity extends Activity implements\n        PlayerStateChangeListener, OnFullscreenListener {\n\n    private static final String YOUTUBE_FRAGMENT_TAG = \"youtube\";\n    final HttpTransport transport = AndroidHttp.newCompatibleTransport();\n    final JsonFactory jsonFactory = new GsonFactory();\n    GoogleAccountCredential credential;\n    private YouTubePlayer mYouTubePlayer;\n    private boolean mIsFullScreen = false;\n    private Intent intent;\n\n    public PlayActivity() {\n    }\n\n    @Override\n    public void onStart() {\n        super.onStart();\n\n    }\n\n    @Override\n    public void onStop() {\n        super.onStop();\n    }\n\n    public void directLite(View view) {\n        this.setResult(RESULT_OK, intent);\n        finish();\n    }\n\n    public void panToVideo(final String youtubeId) {\n        popPlayerFromBackStack();\n        YouTubePlayerFragment playerFragment = YouTubePlayerFragment\n                .newInstance();\n        getFragmentManager()\n                .beginTransaction()\n                .replace(R.id.detail_container, playerFragment,\n                        YOUTUBE_FRAGMENT_TAG)\n                .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)\n                .addToBackStack(null).commit();\n        playerFragment.initialize(Auth.KEY,\n                new YouTubePlayer.OnInitializedListener() {\n                    @Override\n                    public void onInitializationSuccess(\n                            YouTubePlayer.Provider provider,\n                            YouTubePlayer youTubePlayer, boolean b) {\n                        youTubePlayer.loadVideo(youtubeId);\n                        mYouTubePlayer = youTubePlayer;\n                        youTubePlayer\n                                .setPlayerStateChangeListener(PlayActivity.this);\n                        youTubePlayer\n                                .setOnFullscreenListener(PlayActivity.this);\n                    }\n\n                    @Override\n                    public void onInitializationFailure(\n                            YouTubePlayer.Provider provider,\n                            YouTubeInitializationResult result) {\n                        showErrorToast(result.toString());\n                    }\n                });\n    }\n\n    public boolean popPlayerFromBackStack() {\n        if (mIsFullScreen) {\n            mYouTubePlayer.setFullscreen(false);\n            return false;\n        }\n        if (getFragmentManager().findFragmentByTag(YOUTUBE_FRAGMENT_TAG) != null) {\n            getFragmentManager().popBackStack();\n            return false;\n        }\n        return true;\n    }\n\n    @Override\n    public void onAdStarted() {\n    }\n\n    @Override\n    public void onError(YouTubePlayer.ErrorReason errorReason) {\n        showErrorToast(errorReason.toString());\n    }\n\n    private void showErrorToast(String message) {\n        Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT)\n                .show();\n    }\n\n    @Override\n    public void onLoaded(String arg0) {\n    }\n\n    @Override\n    public void onLoading() {\n    }\n\n    @Override\n    public void onVideoEnded() {\n        // popPlayerFromBackStack();\n    }\n\n    @Override\n    public void onVideoStarted() {\n    }\n\n    @Override\n    public void onFullscreen(boolean fullScreen) {\n        mIsFullScreen = fullScreen;\n    }\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        getActionBar().setDisplayHomeAsUpEnabled(true);\n        setContentView(R.layout.activity_play);\n        intent = getIntent();\n        Button submitButton = (Button) findViewById(R.id.submit_button);\n        if (Intent.ACTION_VIEW.equals(intent.getAction())) {\n            submitButton.setVisibility(View.GONE);\n            setTitle(R.string.playing_uploaded_video);\n        }\n        String youtubeId = intent.getStringExtra(MainActivity.YOUTUBE_ID);\n        panToVideo(youtubeId);\n    }\n\n    @Override\n    public boolean onCreateOptionsMenu(Menu menu) {\n        // Inflate the menu; this adds items to the action bar if it is present.\n        getMenuInflater().inflate(R.menu.play, menu);\n        return true;\n    }\n\n    @Override\n    public boolean onOptionsItemSelected(MenuItem item) {\n        switch (item.getItemId()) {\n            // Respond to the action bar's Up/Home button\n            case android.R.id.home:\n                NavUtils.navigateUpFromSameTask(this);\n                return true;\n        }\n        return super.onOptionsItemSelected(item);\n    }\n\n    @Override\n    public void onBackPressed() {\n        super.onBackPressed();\n        NavUtils.navigateUpFromSameTask(this);\n    }\n\n    public interface Callbacks {\n\n        public void onVideoSelected(VideoData video);\n\n        public void onResume();\n\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/google/ytdl/ResumableUpload.java",
    "content": "/*\n * Copyright (c) 2013 Google Inc.\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n * \n * http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\n\npackage com.google.ytdl;\n\nimport android.app.NotificationManager;\nimport android.app.PendingIntent;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.graphics.Bitmap;\nimport android.graphics.BitmapFactory;\nimport android.media.ThumbnailUtils;\nimport android.net.Uri;\nimport android.provider.MediaStore.Video.Thumbnails;\nimport android.support.v4.app.NotificationCompat;\nimport android.support.v4.content.LocalBroadcastManager;\nimport android.util.Log;\n\nimport com.google.api.client.googleapis.extensions.android.gms.auth.GooglePlayServicesAvailabilityIOException;\nimport com.google.api.client.googleapis.extensions.android.gms.auth.UserRecoverableAuthIOException;\nimport com.google.api.client.googleapis.media.MediaHttpUploader;\nimport com.google.api.client.googleapis.media.MediaHttpUploaderProgressListener;\nimport com.google.api.client.http.InputStreamContent;\nimport com.google.api.services.youtube.YouTube;\nimport com.google.api.services.youtube.model.Video;\nimport com.google.api.services.youtube.model.VideoListResponse;\nimport com.google.api.services.youtube.model.VideoSnippet;\nimport com.google.api.services.youtube.model.VideoStatus;\nimport com.google.ytdl.util.Upload;\n\nimport java.io.BufferedInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.util.Arrays;\nimport java.util.Calendar;\nimport java.util.List;\n\n\n/**\n * @author Ibrahim Ulukaya <ulukaya@google.com>\n *         <p/>\n *         YouTube Resumable Upload controller class.\n */\npublic class ResumableUpload {\n    /**\n     * Assigned to the upload\n     */\n    public static final String[] DEFAULT_KEYWORDS = {\"MultiSquash\", \"Game\"};\n    /**\n     * Indicates that the video is fully processed, see https://www.googleapis.com/discovery/v1/apis/youtube/v3/rpc\n     */\n    private static final String SUCCEEDED = \"succeeded\";\n    private static final String TAG = \"UploadingActivity\";\n    private static int UPLOAD_NOTIFICATION_ID = 1001;\n    private static int PLAYBACK_NOTIFICATION_ID = 1002;\n    /*\n     * Global instance of the format used for the video being uploaded (MIME type).\n     */\n    private static String VIDEO_FILE_FORMAT = \"video/*\";\n\n    /**\n     * Uploads user selected video in the project folder to the user's YouTube account using OAuth2\n     * for authentication.\n     */\n\n    public static String upload(YouTube youtube, final InputStream fileInputStream,\n                                final long fileSize, final Uri mFileUri, final String path, final Context context) {\n        final NotificationManager notifyManager =\n                (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);\n        final NotificationCompat.Builder builder = new NotificationCompat.Builder(context);\n\n        Intent notificationIntent = new Intent(context, ReviewActivity.class);\n        notificationIntent.setData(mFileUri);\n        notificationIntent.setAction(Intent.ACTION_VIEW);\n        Bitmap thumbnail = ThumbnailUtils.createVideoThumbnail(path, Thumbnails.MICRO_KIND);\n        PendingIntent contentIntent = PendingIntent.getActivity(context,\n                0, notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT);\n        builder.setContentTitle(context.getString(R.string.youtube_upload))\n                .setContentText(context.getString(R.string.youtube_upload_started))\n                .setSmallIcon(R.drawable.ic_stat_device_access_video).setContentIntent(contentIntent).setStyle(new NotificationCompat.BigPictureStyle().bigPicture(thumbnail));\n        notifyManager.notify(UPLOAD_NOTIFICATION_ID, builder.build());\n\n        String videoId = null;\n        try {\n            // Add extra information to the video before uploading.\n            Video videoObjectDefiningMetadata = new Video();\n\n      /*\n       * Set the video to public, so it is available to everyone (what most people want). This is\n       * actually the default, but I wanted you to see what it looked like in case you need to set\n       * it to \"unlisted\" or \"private\" via API.\n       */\n            VideoStatus status = new VideoStatus();\n            status.setPrivacyStatus(\"public\");\n            videoObjectDefiningMetadata.setStatus(status);\n\n            // We set a majority of the metadata with the VideoSnippet object.\n            VideoSnippet snippet = new VideoSnippet();\n\n      /*\n       * The Calendar instance is used to create a unique name and description for test purposes, so\n       * you can see multiple files being uploaded. You will want to remove this from your project\n       * and use your own standard names.\n       */\n            Calendar cal = Calendar.getInstance();\n            snippet.setTitle(\"Test Upload via Java on \" + cal.getTime());\n            snippet.setDescription(\"Video uploaded via YouTube Data API V3 using the Java library \"\n                    + \"on \" + cal.getTime());\n\n            // Set your keywords.\n            snippet.setTags(Arrays.asList(Constants.DEFAULT_KEYWORD, Upload.generateKeywordFromPlaylistId(Constants.UPLOAD_PLAYLIST)));\n\n            // Set completed snippet to the video object.\n            videoObjectDefiningMetadata.setSnippet(snippet);\n\n            InputStreamContent mediaContent =\n                    new InputStreamContent(VIDEO_FILE_FORMAT, new BufferedInputStream(fileInputStream));\n            mediaContent.setLength(fileSize);\n\n      /*\n       * The upload command includes: 1. Information we want returned after file is successfully\n       * uploaded. 2. Metadata we want associated with the uploaded video. 3. Video file itself.\n       */\n            YouTube.Videos.Insert videoInsert =\n                    youtube.videos().insert(\"snippet,statistics,status\", videoObjectDefiningMetadata,\n                            mediaContent);\n\n            // Set the upload type and add event listener.\n            MediaHttpUploader uploader = videoInsert.getMediaHttpUploader();\n\n      /*\n       * Sets whether direct media upload is enabled or disabled. True = whole media content is\n       * uploaded in a single request. False (default) = resumable media upload protocol to upload\n       * in data chunks.\n       */\n            uploader.setDirectUploadEnabled(false);\n\n            MediaHttpUploaderProgressListener progressListener = new MediaHttpUploaderProgressListener() {\n                public void progressChanged(MediaHttpUploader uploader) throws IOException {\n                    switch (uploader.getUploadState()) {\n                        case INITIATION_STARTED:\n                            builder.setContentText(context.getString(R.string.initiation_started)).setProgress((int) fileSize,\n                                    (int) uploader.getNumBytesUploaded(), false);\n                            notifyManager.notify(UPLOAD_NOTIFICATION_ID, builder.build());\n                            break;\n                        case INITIATION_COMPLETE:\n                            builder.setContentText(context.getString(R.string.initiation_completed)).setProgress((int) fileSize,\n                                    (int) uploader.getNumBytesUploaded(), false);\n                            notifyManager.notify(UPLOAD_NOTIFICATION_ID, builder.build());\n                            break;\n                        case MEDIA_IN_PROGRESS:\n                            builder\n                                    .setContentTitle(context.getString(R.string.youtube_upload) +\n                                            (int) (uploader.getProgress() * 100) + \"%\")\n                                    .setContentText(context.getString(R.string.upload_in_progress))\n                                    .setProgress((int) fileSize, (int) uploader.getNumBytesUploaded(), false);\n                            notifyManager.notify(UPLOAD_NOTIFICATION_ID, builder.build());\n                            break;\n                        case MEDIA_COMPLETE:\n                            builder.setContentTitle(context.getString(R.string.yt_upload_completed))\n                                    .setContentText(context.getString(R.string.upload_completed))\n                                            // Removes the progress bar\n                                    .setProgress(0, 0, false);\n                            notifyManager.notify(UPLOAD_NOTIFICATION_ID, builder.build());\n                        case NOT_STARTED:\n                            Log.d(this.getClass().getSimpleName(), context.getString(R.string.upload_not_started));\n                            break;\n                    }\n                }\n            };\n            uploader.setProgressListener(progressListener);\n\n            // Execute upload.\n            Video returnedVideo = videoInsert.execute();\n            Log.d(TAG, \"Video upload completed\");\n            videoId = returnedVideo.getId();\n            Log.d(TAG, String.format(\"videoId = [%s]\", videoId));\n        } catch (final GooglePlayServicesAvailabilityIOException availabilityException) {\n            Log.e(TAG, \"GooglePlayServicesAvailabilityIOException\", availabilityException);\n            notifyFailedUpload(context, context.getString(R.string.cant_access_play), notifyManager, builder);\n        } catch (UserRecoverableAuthIOException userRecoverableException) {\n            Log.i(TAG, String.format(\"UserRecoverableAuthIOException: %s\",\n                    userRecoverableException.getMessage()));\n            requestAuth(context, userRecoverableException);\n        } catch (IOException e) {\n            Log.e(TAG, \"IOException\", e);\n            notifyFailedUpload(context, context.getString(R.string.please_try_again), notifyManager, builder);\n        }\n        return videoId;\n    }\n\n    private static void requestAuth(Context context,\n                                    UserRecoverableAuthIOException userRecoverableException) {\n        LocalBroadcastManager manager = LocalBroadcastManager.getInstance(context);\n        Intent authIntent = userRecoverableException.getIntent();\n        Intent runReqAuthIntent = new Intent(MainActivity.REQUEST_AUTHORIZATION_INTENT);\n        runReqAuthIntent.putExtra(MainActivity.REQUEST_AUTHORIZATION_INTENT_PARAM, authIntent);\n        manager.sendBroadcast(runReqAuthIntent);\n        Log.d(TAG, String.format(\"Sent broadcast %s\", MainActivity.REQUEST_AUTHORIZATION_INTENT));\n    }\n\n    private static void notifyFailedUpload(Context context, String message, NotificationManager notifyManager,\n                                           NotificationCompat.Builder builder) {\n        builder.setContentTitle(context.getString(R.string.yt_upload_failed))\n                .setContentText(message);\n        notifyManager.notify(UPLOAD_NOTIFICATION_ID, builder.build());\n        Log.e(ResumableUpload.class.getSimpleName(), message);\n    }\n\n    public static void showSelectableNotification(String videoId, Context context) {\n        Log.d(TAG, String.format(\"Posting selectable notification for video ID [%s]\", videoId));\n        final NotificationManager notifyManager =\n                (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);\n        final NotificationCompat.Builder builder = new NotificationCompat.Builder(context);\n        Intent notificationIntent = new Intent(context, PlayActivity.class);\n        notificationIntent.putExtra(MainActivity.YOUTUBE_ID, videoId);\n        notificationIntent.setAction(Intent.ACTION_VIEW);\n\n        URL url;\n        try {\n            url = new URL(\"https://i1.ytimg.com/vi/\" + videoId + \"/mqdefault.jpg\");\n            Bitmap thumbnail = BitmapFactory.decodeStream(url.openConnection().getInputStream());\n            PendingIntent contentIntent = PendingIntent.getActivity(context,\n                    0, notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT);\n            builder.setContentTitle(context.getString(R.string.watch_your_video))\n                    .setContentText(context.getString(R.string.see_the_newly_uploaded_video)).setContentIntent(contentIntent).setSmallIcon(R.drawable.ic_stat_device_access_video).setStyle(new NotificationCompat.BigPictureStyle().bigPicture(thumbnail));\n            notifyManager.notify(PLAYBACK_NOTIFICATION_ID, builder.build());\n            Log.d(TAG, String.format(\"Selectable notification for video ID [%s] posted\", videoId));\n        } catch (MalformedURLException e) {\n            Log.e(TAG, e.getMessage());\n        } catch (IOException e) {\n            Log.e(TAG, e.getMessage());\n        }\n    }\n\n\n    /**\n     * @return url of thumbnail if the video is fully processed\n     */\n    public static boolean checkIfProcessed(String videoId, YouTube youtube) {\n        try {\n            YouTube.Videos.List list = youtube.videos().list(\"processingDetails\");\n            list.setId(videoId);\n            VideoListResponse listResponse = list.execute();\n            List<Video> videos = listResponse.getItems();\n            if (videos.size() == 1) {\n                Video video = videos.get(0);\n                String status = video.getProcessingDetails().getProcessingStatus();\n                Log.e(TAG, String.format(\"Processing status of [%s] is [%s]\", videoId, status));\n                if (status.equals(SUCCEEDED)) {\n                    return true;\n                }\n            } else {\n                // can't find the video\n                Log.e(TAG, String.format(\"Can't find video with ID [%s]\", videoId));\n                return false;\n            }\n        } catch (IOException e) {\n            Log.e(TAG, \"Error fetching video metadata\", e);\n        }\n        return false;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/google/ytdl/ReviewActivity.java",
    "content": "package com.google.ytdl;\n\nimport android.app.Activity;\nimport android.content.Intent;\nimport android.content.SharedPreferences;\nimport android.net.Uri;\nimport android.os.Bundle;\nimport android.preference.PreferenceManager;\nimport android.support.v4.app.NavUtils;\nimport android.util.Log;\nimport android.view.Menu;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.widget.Button;\nimport android.widget.MediaController;\nimport android.widget.Toast;\nimport android.widget.VideoView;\n\npublic class ReviewActivity extends Activity {\n    VideoView mVideoView;\n    MediaController mc;\n    private String mChosenAccountName;\n    private Uri mFileUri;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        getActionBar().setDisplayHomeAsUpEnabled(true);\n        setContentView(R.layout.activity_review);\n        Button uploadButton = (Button) findViewById(R.id.upload_button);\n        Intent intent = getIntent();\n        if (Intent.ACTION_VIEW.equals(intent.getAction())) {\n            uploadButton.setVisibility(View.GONE);\n            setTitle(R.string.playing_the_video_in_upload_progress);\n        }\n        mFileUri = intent.getData();\n        loadAccount();\n\n        reviewVideo(mFileUri);\n    }\n\n    private void reviewVideo(Uri mFileUri) {\n        try {\n            mVideoView = (VideoView) findViewById(R.id.videoView);\n            mc = new MediaController(this);\n            mVideoView.setMediaController(mc);\n            mVideoView.setVideoURI(mFileUri);\n            mc.show();\n            mVideoView.start();\n        } catch (Exception e) {\n            Log.e(this.getLocalClassName(), e.toString());\n        }\n    }\n\n    private void loadAccount() {\n        SharedPreferences sp = PreferenceManager\n                .getDefaultSharedPreferences(this);\n        mChosenAccountName = sp.getString(MainActivity.ACCOUNT_KEY, null);\n        invalidateOptionsMenu();\n    }\n\n    @Override\n    public boolean onCreateOptionsMenu(Menu menu) {\n        // Inflate the menu; this adds items to the action bar if it is present.\n        getMenuInflater().inflate(R.menu.review, menu);\n        return true;\n    }\n\n    public void uploadVideo(View view) {\n        if (mChosenAccountName == null) {\n            return;\n        }\n        // if a video is picked or recorded.\n        if (mFileUri != null) {\n            Intent uploadIntent = new Intent(this, UploadService.class);\n            uploadIntent.setData(mFileUri);\n            uploadIntent.putExtra(MainActivity.ACCOUNT_KEY, mChosenAccountName);\n            startService(uploadIntent);\n            Toast.makeText(this, R.string.youtube_upload_started,\n                    Toast.LENGTH_LONG).show();\n            // Go back to MainActivity after upload\n            finish();\n        }\n    }\n\n    @Override\n    public boolean onOptionsItemSelected(MenuItem item) {\n        switch (item.getItemId()) {\n            // Respond to the action bar's Up/Home button\n            case android.R.id.home:\n                NavUtils.navigateUpFromSameTask(this);\n                return true;\n        }\n        return super.onOptionsItemSelected(item);\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/google/ytdl/UploadService.java",
    "content": "/*\n * Copyright (c) 2013 Google Inc.\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n * \n * http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\n\npackage com.google.ytdl;\n\nimport android.app.IntentService;\nimport android.content.Intent;\nimport android.database.Cursor;\nimport android.net.Uri;\nimport android.provider.MediaStore;\nimport android.util.Log;\n\nimport com.google.api.client.extensions.android.http.AndroidHttp;\nimport com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential;\nimport com.google.api.client.http.HttpTransport;\nimport com.google.api.client.json.JsonFactory;\nimport com.google.api.client.json.gson.GsonFactory;\nimport com.google.api.client.util.ExponentialBackOff;\nimport com.google.api.services.youtube.YouTube;\nimport com.google.common.collect.Lists;\n\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.io.InputStream;\n\n/**\n * @author Ibrahim Ulukaya <ulukaya@google.com>\n *         <p/>\n *         Intent service to handle uploads.\n */\npublic class UploadService extends IntentService {\n\n    /**\n     * defines how long we'll wait for a video to finish processing\n     */\n    private static final int PROCESSING_TIMEOUT_SEC = 60 * 20; // 20 minutes\n\n    /**\n     * controls how often to poll for video processing status\n     */\n    private static final int PROCESSING_POLL_INTERVAL_SEC = 60;\n    /**\n     * how long to wait before re-trying the upload\n     */\n    private static final int UPLOAD_REATTEMPT_DELAY_SEC = 60;\n    /**\n     * max number of retry attempts\n     */\n    private static final int MAX_RETRY = 3;\n    private static final String TAG = \"UploadService\";\n    /**\n     * processing start time\n     */\n    private static long mStartTime;\n    final HttpTransport transport = AndroidHttp.newCompatibleTransport();\n    final JsonFactory jsonFactory = new GsonFactory();\n    GoogleAccountCredential credential;\n    /**\n     * tracks the number of upload attempts\n     */\n    private int mUploadAttemptCount;\n\n    public UploadService() {\n        super(\"YTUploadService\");\n    }\n\n    private static void zzz(int duration) throws InterruptedException {\n        Log.d(TAG, String.format(\"Sleeping for [%d] ms ...\", duration));\n        Thread.sleep(duration);\n        Log.d(TAG, String.format(\"Sleeping for [%d] ms ... done\", duration));\n    }\n\n    private static boolean timeoutExpired(long startTime, int timeoutSeconds) {\n        long currTime = System.currentTimeMillis();\n        long elapsed = currTime - startTime;\n        if (elapsed >= timeoutSeconds * 1000) {\n            return true;\n        } else {\n            return false;\n        }\n    }\n\n    @Override\n    protected void onHandleIntent(Intent intent) {\n        Uri fileUri = intent.getData();\n        String chosenAccountName = intent.getStringExtra(MainActivity.ACCOUNT_KEY);\n\n        credential =\n                GoogleAccountCredential.usingOAuth2(getApplicationContext(), Lists.newArrayList(Auth.SCOPES));\n        credential.setSelectedAccountName(chosenAccountName);\n        credential.setBackOff(new ExponentialBackOff());\n\n        String appName = getResources().getString(R.string.app_name);\n        final YouTube youtube =\n                new YouTube.Builder(transport, jsonFactory, credential).setApplicationName(\n                        appName).build();\n\n\n        try {\n            tryUploadAndShowSelectableNotification(fileUri, youtube);\n        } catch (InterruptedException e) {\n            // ignore\n        }\n    }\n\n    private void tryUploadAndShowSelectableNotification(final Uri fileUri, final YouTube youtube) throws InterruptedException {\n        while (true) {\n            Log.i(TAG, String.format(\"Uploading [%s] to YouTube\", fileUri.toString()));\n            String videoId = tryUpload(fileUri, youtube);\n            if (videoId != null) {\n                Log.i(TAG, String.format(\"Uploaded video with ID: %s\", videoId));\n                tryShowSelectableNotification(videoId, youtube);\n                return;\n            } else {\n                Log.e(TAG, String.format(\"Failed to upload %s\", fileUri.toString()));\n                if (mUploadAttemptCount++ < MAX_RETRY) {\n                    Log.i(TAG, String.format(\"Will retry to upload the video ([%d] out of [%d] reattempts)\",\n                            mUploadAttemptCount, MAX_RETRY));\n                    zzz(UPLOAD_REATTEMPT_DELAY_SEC * 1000);\n                } else {\n                    Log.e(TAG, String.format(\"Giving up on trying to upload %s after %d attempts\",\n                            fileUri.toString(), mUploadAttemptCount));\n                    return;\n                }\n            }\n        }\n    }\n\n    private void tryShowSelectableNotification(final String videoId, final YouTube youtube)\n            throws InterruptedException {\n        mStartTime = System.currentTimeMillis();\n        boolean processed = false;\n        while (!processed) {\n            processed = ResumableUpload.checkIfProcessed(videoId, youtube);\n            if (!processed) {\n                // wait a while\n                Log.d(TAG, String.format(\"Video [%s] is not processed yet, will retry after [%d] seconds\",\n                        videoId, PROCESSING_POLL_INTERVAL_SEC));\n                if (!timeoutExpired(mStartTime, PROCESSING_TIMEOUT_SEC)) {\n                    zzz(PROCESSING_POLL_INTERVAL_SEC * 1000);\n                } else {\n                    Log.d(TAG, String.format(\"Bailing out polling for processing status after [%d] seconds\",\n                            PROCESSING_TIMEOUT_SEC));\n                    return;\n                }\n            } else {\n                ResumableUpload.showSelectableNotification(videoId, getApplicationContext());\n                return;\n            }\n        }\n    }\n\n    private String tryUpload(Uri mFileUri, YouTube youtube) {\n        long fileSize;\n        InputStream fileInputStream = null;\n        String videoId = null;\n        try {\n            fileSize = getContentResolver().openFileDescriptor(mFileUri, \"r\").getStatSize();\n            fileInputStream = getContentResolver().openInputStream(mFileUri);\n            String[] proj = {MediaStore.Images.Media.DATA};\n            Cursor cursor = getContentResolver().query(mFileUri, proj, null, null, null);\n            int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);\n            cursor.moveToFirst();\n\n            videoId = ResumableUpload.upload(youtube, fileInputStream, fileSize, mFileUri, cursor.getString(column_index), getApplicationContext());\n\n\n        } catch (FileNotFoundException e) {\n            Log.e(getApplicationContext().toString(), e.getMessage());\n        } finally {\n            try {\n                fileInputStream.close();\n            } catch (IOException e) {\n                // ignore\n            }\n        }\n        return videoId;\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/google/ytdl/UploadsListFragment.java",
    "content": "/*\n * Copyright (c) 2013 Google Inc.\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n * \n * http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\n\npackage com.google.ytdl;\n\nimport android.annotation.SuppressLint;\nimport android.app.Activity;\nimport android.app.Fragment;\nimport android.content.Context;\nimport android.content.IntentSender;\nimport android.os.Bundle;\nimport android.util.Log;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.BaseAdapter;\nimport android.widget.GridView;\nimport android.widget.ImageView;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport com.android.volley.toolbox.ImageLoader;\nimport com.android.volley.toolbox.NetworkImageView;\nimport com.google.android.gms.common.ConnectionResult;\nimport com.google.android.gms.common.api.GoogleApiClient;\nimport com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks;\nimport com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener;\nimport com.google.android.gms.plus.Plus;\nimport com.google.android.gms.plus.PlusOneButton;\nimport com.google.android.gms.plus.model.people.Person;\nimport com.google.ytdl.util.VideoData;\n\nimport java.util.List;\n\n/**\n * @author Ibrahim Ulukaya <ulukaya@google.com>\n *         <p/>\n *         Left side fragment showing user's uploaded YouTube videos.\n */\npublic class UploadsListFragment extends Fragment implements ConnectionCallbacks,\n        OnConnectionFailedListener {\n\n    private static final String TAG = UploadsListFragment.class.getName();\n    private static Context mContext;\n    private Callbacks mCallbacks;\n    private GoogleApiClient mGoogleApiClient;\n    private GridView mGridView;\n    private ImageLoader mImageLoader;\n\n    public UploadsListFragment() {\n    }\n\n    @SuppressLint(\"ValidFragment\")\n    public UploadsListFragment(Context context) {\n        mContext = context;\n    }\n\n\n    @Override\n    public void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n\n        mGoogleApiClient = new GoogleApiClient.Builder(mContext)\n                .addConnectionCallbacks(this)\n                .addOnConnectionFailedListener(this)\n                .addApi(Plus.API)\n                .addScope(Plus.SCOPE_PLUS_PROFILE)\n                .build();\n\n    }\n\n    @Override\n    public View onCreateView(LayoutInflater inflater, ViewGroup container,\n                             Bundle savedInstanceState) {\n        View listView = inflater.inflate(R.layout.list_fragment, container, false);\n        mGridView = (GridView) listView.findViewById(R.id.grid_view);\n        TextView emptyView = (TextView) listView.findViewById(android.R.id.empty);\n        mGridView.setEmptyView(emptyView);\n        return listView;\n    }\n\n    @Override\n    public void onViewCreated(View view, Bundle savedInstanceState) {\n        super.onViewCreated(view, savedInstanceState);\n        setProfileInfo();\n    }\n\n    public void setVideos(List<VideoData> videos) {\n        if (!isAdded()) {\n            return;\n        }\n\n        mGridView.setAdapter(new UploadedVideoAdapter(videos));\n    }\n\n    public void setProfileInfo() {\n        //not sure if mGoogleapiClient.isConnect is appropriate...\n        if (!mGoogleApiClient.isConnected() || Plus.PeopleApi.getCurrentPerson(mGoogleApiClient) == null) {\n            ((ImageView) getView().findViewById(R.id.avatar))\n                    .setImageDrawable(null);\n            ((TextView) getView().findViewById(R.id.display_name))\n                    .setText(R.string.not_signed_in);\n        } else {\n            Person currentPerson = Plus.PeopleApi.getCurrentPerson(mGoogleApiClient);\n            if (currentPerson.hasImage()) {\n                // Set the URL of the image that should be loaded into this view, and\n                // specify the ImageLoader that will be used to make the request.\n                ((NetworkImageView) getView().findViewById(R.id.avatar)).setImageUrl(currentPerson.getImage().getUrl(), mImageLoader);\n            }\n            if (currentPerson.hasDisplayName()) {\n                ((TextView) getView().findViewById(R.id.display_name))\n                        .setText(currentPerson.getDisplayName());\n            }\n        }\n    }\n\n    @Override\n    public void onResume() {\n        super.onResume();\n        mGoogleApiClient.connect();\n    }\n\n    @Override\n    public void onPause() {\n        super.onPause();\n        mGoogleApiClient.disconnect();\n    }\n\n    @Override\n    public void onConnected(Bundle bundle) {\n        if (mGridView.getAdapter() != null) {\n            ((UploadedVideoAdapter) mGridView.getAdapter()).notifyDataSetChanged();\n        }\n\n        setProfileInfo();\n        mCallbacks.onConnected(Plus.AccountApi.getAccountName(mGoogleApiClient));\n    }\n\n    @Override\n    public void onConnectionSuspended(int i) {\n\n    }\n\n    @Override\n    public void onConnectionFailed(ConnectionResult connectionResult) {\n        if (connectionResult.hasResolution()) {\n            Toast.makeText(getActivity(),\n                    R.string.connection_to_google_play_failed, Toast.LENGTH_SHORT)\n                    .show();\n\n            Log.e(TAG,\n                    String.format(\n                            \"Connection to Play Services Failed, error: %d, reason: %s\",\n                            connectionResult.getErrorCode(),\n                            connectionResult.toString()));\n            try {\n                connectionResult.startResolutionForResult(getActivity(), 0);\n            } catch (IntentSender.SendIntentException e) {\n                Log.e(TAG, e.toString(), e);\n            }\n        }\n    }\n\n    @Override\n    public void onAttach(Activity activity) {\n        super.onAttach(activity);\n        if (!(activity instanceof Callbacks)) {\n            throw new ClassCastException(\"Activity must implement callbacks.\");\n        }\n\n        mCallbacks = (Callbacks) activity;\n        mImageLoader = mCallbacks.onGetImageLoader();\n    }\n\n    @Override\n    public void onDetach() {\n        super.onDetach();\n        mCallbacks = null;\n        mImageLoader = null;\n    }\n\n    public interface Callbacks {\n        public ImageLoader onGetImageLoader();\n\n        public void onVideoSelected(VideoData video);\n\n        public void onConnected(String connectedAccountName);\n    }\n\n    private class UploadedVideoAdapter extends BaseAdapter {\n        private List<VideoData> mVideos;\n\n        private UploadedVideoAdapter(List<VideoData> videos) {\n            mVideos = videos;\n        }\n\n        @Override\n        public int getCount() {\n            return mVideos.size();\n        }\n\n        @Override\n        public Object getItem(int i) {\n            return mVideos.get(i);\n        }\n\n        @Override\n        public long getItemId(int i) {\n            return mVideos.get(i).getYouTubeId().hashCode();\n        }\n\n        @Override\n        public View getView(final int position, View convertView,\n                            ViewGroup container) {\n            if (convertView == null) {\n                convertView = LayoutInflater.from(getActivity()).inflate(\n                        R.layout.list_item, container, false);\n            }\n\n            VideoData video = mVideos.get(position);\n            ((TextView) convertView.findViewById(android.R.id.text1))\n                    .setText(video.getTitle());\n            ((NetworkImageView) convertView.findViewById(R.id.thumbnail)).setImageUrl(video.getThumbUri(), mImageLoader);\n            if (mGoogleApiClient.isConnected()) {\n                ((PlusOneButton) convertView.findViewById(R.id.plus_button))\n                        .initialize(video.getWatchUri(), null);\n            }\n            convertView.findViewById(R.id.main_target).setOnClickListener(\n                    new View.OnClickListener() {\n                        @Override\n                        public void onClick(View view) {\n                            mCallbacks.onVideoSelected(mVideos.get(position));\n                        }\n                    });\n            return convertView;\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/google/ytdl/util/LruBitmapCache.java",
    "content": "/*\n * Copyright (c) 2015 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\n\npackage com.google.ytdl.util;\n\nimport android.content.Context;\nimport android.graphics.Bitmap;\nimport android.support.v4.util.LruCache;\nimport android.util.DisplayMetrics;\n\nimport com.android.volley.toolbox.ImageLoader.ImageCache;\n\npublic class LruBitmapCache extends LruCache<String, Bitmap>\n        implements ImageCache {\n\n    public LruBitmapCache(int maxSize) {\n        super(maxSize);\n    }\n\n    public LruBitmapCache(Context ctx) {\n        this(getCacheSize(ctx));\n    }\n\n    // Returns a cache size equal to approximately three screens worth of images.\n    public static int getCacheSize(Context ctx) {\n        final DisplayMetrics displayMetrics = ctx.getResources().\n                getDisplayMetrics();\n        final int screenWidth = displayMetrics.widthPixels;\n        final int screenHeight = displayMetrics.heightPixels;\n        // 4 bytes per pixel\n        final int screenBytes = screenWidth * screenHeight * 4;\n\n        return screenBytes * 3;\n    }\n\n    @Override\n    protected int sizeOf(String key, Bitmap value) {\n        return value.getRowBytes() * value.getHeight();\n    }\n\n    @Override\n    public Bitmap getBitmap(String url) {\n        return get(url);\n    }\n\n    @Override\n    public void putBitmap(String url, Bitmap bitmap) {\n        put(url, bitmap);\n    }\n}"
  },
  {
    "path": "app/src/main/java/com/google/ytdl/util/NetworkSingleton.java",
    "content": "/*\n * Copyright (c) 2015 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\n\npackage com.google.ytdl.util;\n\nimport android.content.Context;\n\nimport com.android.volley.Request;\nimport com.android.volley.RequestQueue;\nimport com.android.volley.toolbox.ImageLoader;\nimport com.android.volley.toolbox.Volley;\n\npublic class NetworkSingleton {\n    private static NetworkSingleton mInstance;\n    private static Context mCtx;\n    private RequestQueue mRequestQueue;\n    private ImageLoader mImageLoader;\n\n    private NetworkSingleton(Context context) {\n        mCtx = context;\n        mRequestQueue = getRequestQueue();\n\n        mImageLoader = new ImageLoader(mRequestQueue,\n                new LruBitmapCache(context));\n    }\n\n    public static synchronized NetworkSingleton getInstance(Context context) {\n        if (mInstance == null) {\n            mInstance = new NetworkSingleton(context);\n        }\n        return mInstance;\n    }\n\n    public RequestQueue getRequestQueue() {\n        if (mRequestQueue == null) {\n            // getApplicationContext() is key, it keeps you from leaking the\n            // Activity or BroadcastReceiver if someone passes one in.\n            mRequestQueue = Volley.newRequestQueue(mCtx.getApplicationContext());\n        }\n        return mRequestQueue;\n    }\n\n    public <T> void addToRequestQueue(Request<T> req) {\n        getRequestQueue().add(req);\n    }\n\n    public ImageLoader getImageLoader() {\n        return mImageLoader;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/google/ytdl/util/Upload.java",
    "content": "/*\n * Copyright (c) 2013 Google Inc.\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n * \n * http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\n\npackage com.google.ytdl.util;\n\nimport com.google.ytdl.Constants;\n\npublic class Upload {\n    public static String generateKeywordFromPlaylistId(String playlistId) {\n        if (playlistId == null) playlistId = \"\";\n        if (playlistId.indexOf(\"PL\") == 0) {\n            playlistId = playlistId.substring(2);\n        }\n        playlistId = playlistId.replaceAll(\"\\\\W\", \"\");\n        String keyword = Constants.DEFAULT_KEYWORD.concat(playlistId);\n        if (keyword.length() > Constants.MAX_KEYWORD_LENGTH) {\n            keyword = keyword.substring(0, Constants.MAX_KEYWORD_LENGTH);\n        }\n        return keyword;\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/google/ytdl/util/Utils.java",
    "content": "/* Copyright (c) 2013 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.ytdl.util;\n\nimport android.app.Activity;\nimport android.content.res.Resources;\nimport android.os.Build;\nimport android.util.Log;\nimport android.widget.Toast;\n\nimport com.google.android.gms.auth.GoogleAuthException;\nimport com.google.api.client.googleapis.json.GoogleJsonError;\nimport com.google.api.client.googleapis.json.GoogleJsonResponseException;\nimport com.google.ytdl.R;\n\n/**\n * Class containing some static utility methods.\n */\npublic class Utils {\n    private Utils() {\n    }\n\n    public static boolean hasFroyo() {\n        // Can use static final constants like FROYO, declared in later versions\n        // of the OS since they are inlined at compile time. This is guaranteed behavior.\n        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO;\n    }\n\n    public static boolean hasGingerbread() {\n        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD;\n    }\n\n    public static boolean hasHoneycomb() {\n        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB;\n    }\n\n    public static boolean hasHoneycombMR1() {\n        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1;\n    }\n\n    public static boolean hasJellyBean() {\n        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;\n    }\n\n    /**\n     * Logs the given throwable and shows an error alert dialog with its message.\n     *\n     * @param activity activity\n     * @param tag      log tag to use\n     * @param t        throwable to log and show\n     */\n    public static void logAndShow(Activity activity, String tag, Throwable t) {\n        Log.e(tag, \"Error\", t);\n        String message = t.getMessage();\n        if (t instanceof GoogleJsonResponseException) {\n            GoogleJsonError details = ((GoogleJsonResponseException) t).getDetails();\n            if (details != null) {\n                message = details.getMessage();\n            }\n        } else if (t.getCause() instanceof GoogleAuthException) {\n            message = ((GoogleAuthException) t.getCause()).getMessage();\n        }\n        showError(activity, message);\n    }\n\n    /**\n     * Logs the given message and shows an error alert dialog with it.\n     *\n     * @param activity activity\n     * @param tag      log tag to use\n     * @param message  message to log and show or {@code null} for none\n     */\n    public static void logAndShowError(Activity activity, String tag, String message) {\n        String errorMessage = getErrorMessage(activity, message);\n        Log.e(tag, errorMessage);\n        showErrorInternal(activity, errorMessage);\n    }\n\n    /**\n     * Shows an error alert dialog with the given message.\n     *\n     * @param activity activity\n     * @param message  message to show or {@code null} for none\n     */\n    public static void showError(Activity activity, String message) {\n        String errorMessage = getErrorMessage(activity, message);\n        showErrorInternal(activity, errorMessage);\n    }\n\n    private static void showErrorInternal(final Activity activity, final String errorMessage) {\n        activity.runOnUiThread(new Runnable() {\n            public void run() {\n                Toast.makeText(activity, errorMessage, Toast.LENGTH_LONG).show();\n            }\n        });\n    }\n\n    private static String getErrorMessage(Activity activity, String message) {\n        Resources resources = activity.getResources();\n        if (message == null) {\n            return resources.getString(R.string.error);\n        }\n        return resources.getString(R.string.error_format, message);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/google/ytdl/util/VideoData.java",
    "content": "/*\n * Copyright (c) 2013 Google Inc.\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n * \n * http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\n\npackage com.google.ytdl.util;\n\nimport com.google.api.services.youtube.model.Video;\nimport com.google.api.services.youtube.model.VideoSnippet;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * @author Ibrahim Ulukaya <ulukaya@google.com>\n *         <p/>\n *         Helper class to handle YouTube videos.\n */\npublic class VideoData {\n    private Video mVideo;\n\n    public Video getVideo() {\n        return mVideo;\n    }\n\n    public void setVideo(Video video) {\n        mVideo = video;\n    }\n\n    public String getYouTubeId() {\n        return mVideo.getId();\n    }\n\n    public String getTitle() {\n        return mVideo.getSnippet().getTitle();\n    }\n\n    public VideoSnippet addTags(Collection<? extends String> tags) {\n        VideoSnippet mSnippet = mVideo.getSnippet();\n        List<String> mTags = mSnippet.getTags();\n        if (mTags == null) {\n            mTags = new ArrayList<String>(2);\n        }\n        mTags.addAll(tags);\n        return mSnippet;\n    }\n\n    public String getThumbUri() {\n        return mVideo.getSnippet().getThumbnails().getDefault().getUrl();\n    }\n\n    public String getWatchUri() {\n        return \"http://www.youtube.com/watch?v=\" + getYouTubeId();\n    }\n}\n"
  },
  {
    "path": "app/src/main/res/drawable/list_divider_horizontal_inset.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  Copyright 2013 The Android Open Source Project\n  \n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n  \n      http://www.apache.org/licenses/LICENSE-2.0\n  \n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n\n<inset xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:drawable=\"@drawable/list_divider_holo_dark\"\n    android:insetLeft=\"16dp\"\n    android:insetRight=\"16dp\" />\n"
  },
  {
    "path": "app/src/main/res/layout/activity_main.xml",
    "content": "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:divider=\"?android:dividerHorizontal\"\n    android:horizontalSpacing=\"10dp\"\n    android:orientation=\"vertical\"\n    android:showDividers=\"middle\"\n    android:verticalSpacing=\"10dp\">\n\n    <fragment\n        android:id=\"@+id/list_fragment\"\n        android:name=\"com.google.ytdl.UploadsListFragment\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        android:layout_weight=\"1\"\n        tools:layout=\"@layout/list_fragment\" />\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"#eee\"\n        android:divider=\"?android:dividerVertical\"\n        android:showDividers=\"middle\">\n\n        <Button\n            android:id=\"@+id/pick_button\"\n            style=\"?android:attr/borderlessButtonStyle\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:drawableTop=\"@drawable/ic_content_picture\"\n            android:ellipsize=\"end\"\n            android:fontFamily=\"sans-serif-condensed\"\n            android:hint=\"@string/button_pick\"\n            android:maxLines=\"2\"\n            android:onClick=\"pickFile\"\n            android:padding=\"8dp\"\n            android:textAppearance=\"?android:textAppearanceLarge\"\n            android:textStyle=\"bold\" />\n\n        <Button\n            android:id=\"@+id/record_button\"\n            style=\"?android:attr/borderlessButtonStyle\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"right|bottom\"\n            android:layout_weight=\"1\"\n            android:drawableTop=\"@drawable/ic_device_access_video\"\n            android:ellipsize=\"end\"\n            android:fontFamily=\"sans-serif-condensed\"\n            android:hint=\"@string/button_record\"\n            android:maxLines=\"2\"\n            android:onClick=\"recordVideo\"\n            android:padding=\"8dp\"\n            android:textAppearance=\"?android:textAppearanceLarge\"\n            android:textColorHint=\"#c00\"\n            android:textStyle=\"bold\" />\n    </LinearLayout>\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/activity_play.xml",
    "content": "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:divider=\"?android:dividerHorizontal\"\n    android:horizontalSpacing=\"10dp\"\n    android:orientation=\"vertical\"\n    android:showDividers=\"middle\"\n    android:verticalSpacing=\"10dp\"\n    tools:context=\".PlayActivity\">\n\n    <FrameLayout\n        android:id=\"@+id/container\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        android:layout_weight=\"1\">\n\n        <FrameLayout\n            android:id=\"@+id/detail_container\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"center\" />\n    </FrameLayout>\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"#eee\">\n\n        <Button\n            android:id=\"@+id/submit_button\"\n            style=\"?android:attr/borderlessButtonStyle\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:drawableTop=\"@drawable/ic_av_upload\"\n            android:ellipsize=\"end\"\n            android:fontFamily=\"sans-serif-condensed\"\n            android:hint=\"@string/button_submit\"\n            android:maxLines=\"2\"\n            android:onClick=\"directLite\"\n            android:padding=\"8dp\"\n            android:textAppearance=\"?android:textAppearanceLarge\"\n            android:textColorHint=\"#c00\"\n            android:textStyle=\"bold\" />\n    </LinearLayout>\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/activity_review.xml",
    "content": "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:divider=\"?android:dividerHorizontal\"\n    android:horizontalSpacing=\"10dp\"\n    android:orientation=\"vertical\"\n    android:showDividers=\"middle\"\n    android:verticalSpacing=\"10dp\"\n    tools:context=\".ReviewActivity\">\n\n    <FrameLayout\n        android:id=\"@+id/detail_container\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        android:layout_weight=\"1\">\n\n        <VideoView\n            android:id=\"@+id/videoView\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"center\" />\n    </FrameLayout>\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"#eee\">\n\n        <Button\n            android:id=\"@+id/upload_button\"\n            style=\"?android:attr/borderlessButtonStyle\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:drawableTop=\"@drawable/ic_av_upload\"\n            android:ellipsize=\"end\"\n            android:fontFamily=\"sans-serif-condensed\"\n            android:hint=\"@string/button_upload\"\n            android:maxLines=\"2\"\n\n            android:onClick=\"uploadVideo\"\n            android:padding=\"8dp\"\n            android:textAppearance=\"?android:textAppearanceLarge\"\n            android:textColorHint=\"#c00\"\n            android:textStyle=\"bold\" />\n\n    </LinearLayout>\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/developer_setup_required.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\">\n\n    <TextView\n        android:id=\"@+id/textView\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"106dp\"\n        android:layout_margin=\"6dp\"\n        android:text=\"@string/dev_setup_explanation\"\n        android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n\n    <ListView\n        android:id=\"@+id/missing_config_list\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center\"\n        android:layout_marginLeft=\"32dp\"\n        android:layout_marginRight=\"32dp\" />\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/list_fragment.xml",
    "content": "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:divider=\"?android:dividerHorizontal\"\n    android:dividerPadding=\"16dp\"\n    android:orientation=\"vertical\"\n    android:showDividers=\"middle\">\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:gravity=\"center_vertical\"\n        android:orientation=\"horizontal\"\n        android:padding=\"16dp\">\n\n        <com.android.volley.toolbox.NetworkImageView\n            android:id=\"@+id/avatar\"\n            android:layout_width=\"32dp\"\n            android:layout_height=\"32dp\"\n            android:background=\"#1000\"\n            android:contentDescription=\"@string/avatar\"\n            android:scaleType=\"centerCrop\" />\n\n        <TextView\n            android:id=\"@+id/display_name\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginLeft=\"8dp\"\n            android:fontFamily=\"sans-serif-light\"\n            android:textAppearance=\"?android:textAppearanceLarge\" />\n    </LinearLayout>\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:gravity=\"center_vertical\"\n        android:orientation=\"horizontal\"\n        android:padding=\"8dp\">\n\n        <TextView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginLeft=\"4dp\"\n            android:fontFamily=\"sans-serif-condensed\"\n            android:text=\"@string/pick_one_of_your_videos_to_submit\"\n            android:textAppearance=\"?android:textAppearanceMedium\"></TextView>\n    </LinearLayout>\n\n    <FrameLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        android:layout_weight=\"1\">\n\n        <GridView\n            android:id=\"@+id/grid_view\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:divider=\"@drawable/list_divider_horizontal_inset\"\n            android:fastScrollEnabled=\"true\"\n            android:numColumns=\"auto_fit\" />\n\n        <TextView\n            android:id=\"@android:id/empty\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:gravity=\"center\"\n            android:padding=\"16dp\"\n            android:text=\"@string/no_videos_to_show_here_\" />\n    </FrameLayout>\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:gravity=\"center_vertical\"\n        android:orientation=\"horizontal\"\n        android:padding=\"8dp\">\n\n        <TextView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginLeft=\"4dp\"\n            android:fontFamily=\"sans-serif-condensed\"\n            android:text=\"@string/pick_or_record_a_video_to_upload\"\n            android:textAppearance=\"?android:textAppearanceMedium\"></TextView>\n    </LinearLayout>\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/list_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:plus=\"http://schemas.android.com/apk/lib/com.google.android.gms.plus\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:gravity=\"center_vertical\"\n    android:minHeight=\"?android:listPreferredItemHeightSmall\"\n    android:orientation=\"vertical\"\n    android:paddingBottom=\"16dp\">\n\n    <LinearLayout\n        android:id=\"@+id/main_target\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:background=\"?android:selectableItemBackground\"\n        android:clickable=\"true\"\n        android:focusable=\"true\"\n        android:orientation=\"vertical\"\n        android:paddingLeft=\"16dp\"\n        android:paddingRight=\"16dp\"\n        android:paddingTop=\"16dp\">\n\n        <com.android.volley.toolbox.NetworkImageView\n            android:id=\"@+id/thumbnail\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"160dp\"\n            android:layout_marginBottom=\"8dp\"\n            android:background=\"#1000\"\n            android:scaleType=\"centerCrop\" />\n\n        <TextView\n            android:id=\"@android:id/text1\"\n            style=\"?android:textAppearanceMedium\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginBottom=\"4dp\"\n            android:ellipsize=\"end\"\n            android:fontFamily=\"sans-serif-condensed\"\n            android:maxLines=\"2\" />\n\n    </LinearLayout>\n\n    <com.google.android.gms.plus.PlusOneButton\n        android:id=\"@+id/plus_button\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"4dp\"\n        android:paddingLeft=\"16dp\"\n        android:paddingRight=\"16dp\"\n        plus:annotation=\"inline\"\n        plus:size=\"standard\" />\n\n</LinearLayout>\n"
  },
  {
    "path": "app/src/main/res/menu/activity_main.xml",
    "content": "<menu xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item\n        android:id=\"@+id/menu_accounts\"\n        android:icon=\"@drawable/ic_mailboxes_accounts\"\n        android:title=\"@string/accounts\" />\n    <item\n        android:id=\"@+id/menu_refresh\"\n        android:icon=\"@drawable/ic_menu_refresh\"\n        android:title=\"@string/refresh\" />\n</menu>\n"
  },
  {
    "path": "app/src/main/res/menu/play.xml",
    "content": "<menu xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <item\n        android:id=\"@+id/action_settings\"\n        android:orderInCategory=\"100\"\n        android:showAsAction=\"never\"\n        android:title=\"@string/action_settings\" />\n\n</menu>\n"
  },
  {
    "path": "app/src/main/res/menu/review.xml",
    "content": "<menu xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <item\n        android:id=\"@+id/action_settings\"\n        android:orderInCategory=\"100\"\n        android:showAsAction=\"never\"\n        android:title=\"@string/action_settings\" />\n\n</menu>\n"
  },
  {
    "path": "app/src/main/res/values/dimens.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <dimen name=\"thumbnail_width\">80dp</dimen>\n    <dimen name=\"thumbnail_height\">48dp</dimen>\n    <dimen name=\"photo_height\">200dp</dimen>\n    <dimen name=\"list_item_vert_margin\">8dp</dimen>\n    <dimen name=\"detail_horiz_margin\">16dp</dimen>\n    <dimen name=\"activity_horizontal_margin\">16dp</dimen>\n    <dimen name=\"activity_vertical_margin\">16dp</dimen>\n\n</resources>"
  },
  {
    "path": "app/src/main/res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <string name=\"app_name\">YouTube Direct Lite</string>\n    <string name=\"button_authorize\">Authorize</string>\n    <string name=\"button_record\">RECORD</string>\n    <string name=\"button_pick\">GALLERY</string>\n    <string name=\"button_upload\">UPLOAD TO YTDL</string>\n    <string name=\"button_submit\">SUBMIT TO YTDL</string>\n    <string name=\"menu_settings\">Settings</string>\n    <string name=\"title_activity_main\">MainActivity</string>\n    <string name=\"title_activity_display_message\">My Message</string>\n    <string name=\"google_play_not_available\">Google Play Services not available</string>\n    <string name=\"youtube_upload_started\">YouTube Direct Lite upload started</string>\n    <string name=\"connection_to_google_play_failed\">Connection to Play Services failed.</string>\n    <string name=\"youtube_upload\">YouTube upload</string>\n    <string name=\"not_signed_in\">Not signed in</string>\n    <string name=\"dev_setup_explanation\">If you\\'re seeing this screen, congratulations! You\\'ve set up the dependencies for YouTube Direct Lite correctly. You just need to set up the following for the app to work correctly:</string>\n    <string name=\"error\">Error</string>\n    <string name=\"error_format\">[Error] %s</string>\n    <string name=\"refresh\">Refresh</string>\n    <string name=\"accounts\">Accounts</string>\n    <string name=\"initiation_started\">Initiation Started</string>\n    <string name=\"initiation_completed\">Initiation Completed</string>\n    <string name=\"upload_in_progress\">Direct Lite upload in progress</string>\n    <string name=\"yt_upload_completed\">YouTube Upload Completed</string>\n    <string name=\"upload_completed\">Upload completed</string>\n    <string name=\"upload_not_started\">Upload Not Started!</string>\n    <string name=\"yt_upload_failed\">YouTube Upload Failed</string>\n    <string name=\"reauth_required\">Re-login required</string>\n    <string name=\"please_try_again\">Please try again</string>\n    <string name=\"cant_access_play\">Can\\'t access to Google Play services</string>\n    <string name=\"title_activity_play\">Click below to submit</string>\n    <string name=\"action_settings\">Settings</string>\n    <string name=\"send\">Click send</string>\n    <string name=\"title_activity_review\">Click below to upload</string>\n    <string name=\"hello_world\">Hello world!</string>\n    <string name=\"pick_one_of_your_videos_to_submit\">Submit an existing YouTube video or upload a new video using the options below</string>\n    <string name=\"no_videos_to_show_here_\">No videos to show here.</string>\n    <string name=\"avatar\">avatar</string>\n    <string name=\"pick_or_record_a_video_to_upload\">Upload an existing video from your device or record a new one</string>\n    <string name=\"see_the_newly_uploaded_video\">See the newly uploaded video</string>\n    <string name=\"watch_your_video\">Watch your video</string>\n    <string name=\"video_submitted_to_ytdl\">Video submitted to YTDL</string>\n    <string name=\"playing_uploaded_video\">Playing uploaded video</string>\n    <string name=\"playing_the_video_in_upload_progress\">Playing the video in upload progress</string>\n\n</resources>"
  },
  {
    "path": "app/src/main/res/values/styles.xml",
    "content": "<resources>\n\n    <!--\n        Base application theme, dependent on API level. This theme is replaced\n        by AppBaseTheme from res/values-vXX/styles.xml on newer devices.\n    -->\n    <style name=\"AppBaseTheme\" parent=\"android:Theme.Holo.Light\">\n        <!--\n            Theme customizations available in newer API levels can go in\n            res/values-vXX/styles.xml, while customizations related to\n            backward-compatibility can go here.\n        -->\n    </style>\n\n    <!-- Application theme. -->\n    <style name=\"AppTheme\" parent=\"AppBaseTheme\">\n        <!-- All customizations that are NOT specific to a particular API-level can go here. -->\n    </style>\n\n</resources>"
  },
  {
    "path": "build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n    repositories {\n        jcenter()\n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:1.3.0'\n\n        // NOTE: Do not place your application dependencies here; they belong\n        // in the individual module build.gradle files\n    }\n}\n\nallprojects {\n    repositories {\n        jcenter()\n    }\n}\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "#Thu Aug 20 10:24:27 EDT 2015\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-2.4-all.zip\n"
  },
  {
    "path": "gradle.properties",
    "content": "# Project-wide Gradle settings.\n\n# IDE (e.g. Android Studio) users:\n# Settings specified in this file will override any Gradle settings\n# configured through the IDE.\n\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\n# Default value: -Xmx10248m -XX:MaxPermSize=256m\n# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8\n\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true"
  },
  {
    "path": "gradlew",
    "content": "#!/usr/bin/env bash\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn ( ) {\n    echo \"$*\"\n}\n\ndie ( ) {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\nesac\n\n# For Cygwin, ensure paths are in UNIX format before anything is touched.\nif $cygwin ; then\n    [ -n \"$JAVA_HOME\" ] && JAVA_HOME=`cygpath --unix \"$JAVA_HOME\"`\nfi\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >&-\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >&-\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules\nfunction splitJvmOpts() {\n    JVM_OPTS=(\"$@\")\n}\neval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\nJVM_OPTS[${#JVM_OPTS[*]}]=\"-Dorg.gradle.appname=$APP_BASE_NAME\"\n\nexec \"$JAVACMD\" \"${JVM_OPTS[@]}\" -classpath \"$CLASSPATH\" org.gradle.wrapper.GradleWrapperMain \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem  Gradle startup script for Windows\n@rem\n@rem ##########################################################################\n\n@rem Set local scope for the variables with windows NT shell\nif \"%OS%\"==\"Windows_NT\" setlocal\n\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nset DEFAULT_JVM_OPTS=\n\nset DIRNAME=%~dp0\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\nset APP_BASE_NAME=%~n0\nset APP_HOME=%DIRNAME%\n\n@rem Find java.exe\nif defined JAVA_HOME goto findJavaFromJavaHome\n\nset JAVA_EXE=java.exe\n%JAVA_EXE% -version >NUL 2>&1\nif \"%ERRORLEVEL%\" == \"0\" goto init\n\necho.\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:findJavaFromJavaHome\nset JAVA_HOME=%JAVA_HOME:\"=%\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\n\nif exist \"%JAVA_EXE%\" goto init\n\necho.\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:init\n@rem Get command-line arguments, handling Windowz variants\n\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\nif \"%@eval[2+2]\" == \"4\" goto 4NT_args\n\n:win9xME_args\n@rem Slurp the command line arguments.\nset CMD_LINE_ARGS=\nset _SKIP=2\n\n:win9xME_args_slurp\nif \"x%~1\" == \"x\" goto execute\n\nset CMD_LINE_ARGS=%*\ngoto execute\n\n:4NT_args\n@rem Get arguments from the 4NT Shell from JP Software\nset CMD_LINE_ARGS=%$\n\n:execute\n@rem Setup the command line\n\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\n\n@rem Execute Gradle\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\n\n:end\n@rem End local scope for the variables with windows NT shell\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\n\n:fail\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\nrem the _cmd.exe /c_ return code!\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\nexit /b 1\n\n:mainEnd\nif \"%OS%\"==\"Windows_NT\" endlocal\n\n:omega\n"
  },
  {
    "path": "settings.gradle",
    "content": "include ':app'\n"
  },
  {
    "path": "yt-direct-lite-android.iml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<module external.linked.project.id=\"yt-direct-lite-android\" external.linked.project.path=\"$MODULE_DIR$\" external.root.project.path=\"$MODULE_DIR$\" external.system.id=\"GRADLE\" external.system.module.group=\"\" external.system.module.version=\"unspecified\" type=\"JAVA_MODULE\" version=\"4\">\n  <component name=\"FacetManager\">\n    <facet type=\"java-gradle\" name=\"Java-Gradle\">\n      <configuration>\n        <option name=\"BUILD_FOLDER_PATH\" value=\"$MODULE_DIR$/build\" />\n        <option name=\"BUILDABLE\" value=\"false\" />\n      </configuration>\n    </facet>\n  </component>\n  <component name=\"NewModuleRootManager\" LANGUAGE_LEVEL=\"JDK_1_7\" inherit-compiler-output=\"true\">\n    <exclude-output />\n    <content url=\"file://$MODULE_DIR$\">\n      <excludeFolder url=\"file://$MODULE_DIR$/.gradle\" />\n    </content>\n    <orderEntry type=\"inheritedJdk\" />\n    <orderEntry type=\"sourceFolder\" forTests=\"false\" />\n  </component>\n</module>"
  }
]