Repository: youtube/yt-direct-lite-android
Branch: master
Commit: 9fc42e0993a7
Files: 44
Total size: 122.9 KB
Directory structure:
gitextract_g6krwkb4/
├── .classpath
├── .gitignore
├── CONTRIBUTING.md
├── LICENSE-2.0.txt
├── README.md
├── app/
│ ├── .gitignore
│ ├── build.gradle
│ ├── libs/
│ │ └── YouTubeAndroidPlayerApi.jar
│ ├── proguard-rules.pro
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ ├── java/
│ │ └── com/
│ │ └── google/
│ │ └── ytdl/
│ │ ├── Auth.java
│ │ ├── Constants.java
│ │ ├── MainActivity.java
│ │ ├── PlayActivity.java
│ │ ├── ResumableUpload.java
│ │ ├── ReviewActivity.java
│ │ ├── UploadService.java
│ │ ├── UploadsListFragment.java
│ │ └── util/
│ │ ├── LruBitmapCache.java
│ │ ├── NetworkSingleton.java
│ │ ├── Upload.java
│ │ ├── Utils.java
│ │ └── VideoData.java
│ └── res/
│ ├── drawable/
│ │ └── list_divider_horizontal_inset.xml
│ ├── layout/
│ │ ├── activity_main.xml
│ │ ├── activity_play.xml
│ │ ├── activity_review.xml
│ │ ├── developer_setup_required.xml
│ │ ├── list_fragment.xml
│ │ └── list_item.xml
│ ├── menu/
│ │ ├── activity_main.xml
│ │ ├── play.xml
│ │ └── review.xml
│ └── values/
│ ├── dimens.xml
│ ├── strings.xml
│ └── styles.xml
├── build.gradle
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── yt-direct-lite-android.iml
================================================
FILE CONTENTS
================================================
================================================
FILE: .classpath
================================================
================================================
FILE: .gitignore
================================================
.gradle
.DS_Store
YouTubeDirectLiteforAndroid.iml
.idea
build
local.properties
================================================
FILE: CONTRIBUTING.md
================================================
# How to contribute #
We'd love to accept your patches and contributions to this project. There are
a just a few small guidelines you need to follow.
## Contributor License Agreement ##
Contributions to any Google project must be accompanied by a Contributor
License Agreement. This is not a copyright **assignment**, it simply gives
Google permission to use and redistribute your contributions as part of the
project.
* If you are an individual writing original source code and you're sure you
own the intellectual property, then you'll need to sign an [individual
CLA][].
* If you work for a company that wants to allow you to contribute your work,
then you'll need to sign a [corporate CLA][].
You generally only need to submit a CLA once, so if you've already submitted
one (even if it was for a different project), you probably don't need to do it
again.
[individual CLA]: https://developers.google.com/open-source/cla/individual
[corporate CLA]: https://developers.google.com/open-source/cla/corporate
## Submitting a patch ##
1. It's generally best to start by opening a new issue describing the bug or
feature you're intending to fix. Even if you think it's relatively minor,
it's helpful to know what people are working on. Mention in the initial
issue that you are planning to work on that bug or feature so that it can
be assigned to you.
1. Follow the normal process of [forking][] the project, and setup a new
branch to work in. It's important that each group of changes be done in
separate branches in order to ensure that a pull request only includes the
commits related to that bug or feature.
1. Go makes it very simple to ensure properly formatted code, so always run
`go fmt` on your code before committing it. You should also run
[golint][] over your code. As noted in the [golint readme][], it's not
strictly necessary that your code be completely "lint-free", but this will
help you find common style issues.
1. Any significant changes should almost always be accompanied by tests. The
project already has good test coverage, so look at some of the existing
tests if you're unsure how to go about it. [gocov][] and [gocov-html][]
are invaluable tools for seeing which parts of your code aren't being
exercised by your tests.
1. Do your best to have [well-formed commit messages][] for each change.
This provides consistency throughout the project, and ensures that commit
messages are able to be formatted properly by various git tools.
1. Finally, push the commits to your fork and submit a [pull request][].
[forking]: https://help.github.com/articles/fork-a-repo
[golint]: https://github.com/golang/lint
[golint readme]: https://github.com/golang/lint/blob/master/README
[gocov]: https://github.com/axw/gocov
[gocov-html]: https://github.com/matm/gocov-html
[well-formed commit messages]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
[squash]: http://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits
[pull request]: https://help.github.com/articles/creating-a-pull-request
## Other notes on code organization ##
Currently, everything is defined in the main `github` package, with API methods
broken into separate service objects. These services map directly to how
the [GitHub API documentation][] is organized, so use that as your guide for
where to put new methods.
Sub-service (e.g. [Repo Hooks][]) implementations are split into separate files
based on the APIs they provide. These files are named service_api.go (e.g.
repos_hooks.go) to describe the API to service mappings.
[GitHub API documentation]: http://developer.github.com/v3/
[Repo Hooks]: http://developer.github.com/v3/repos/hooks/
================================================
FILE: LICENSE-2.0.txt
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: README.md
================================================
YouTube Direct Lite for Android
===========
The 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.
For more information, you can read the [Youtube API blog post](http://apiblog.youtube.com/2013/08/heres-my-playlist-so-submit-video-maybe.html).
This 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).
To use this application,
1. In your [Google Developers Console](https://console.developers.google.com),
1. Enable the YouTube Data API v3 and Google+ API.
1. Create a client ID for Android, using your SHA1 and package name.
1. [Register your Android app](https://developers.google.com/youtube/android/player/register)
1. Plug in your Playlist Id into Constants.java and Android API Key into Auth.java




================================================
FILE: app/.gitignore
================================================
/build
app.iml
================================================
FILE: app/build.gradle
================================================
apply plugin: 'com.android.application'
android {
compileSdkVersion 23
buildToolsVersion "23.0.0"
defaultConfig {
applicationId "com.google.ytdl"
minSdkVersion 16
targetSdkVersion 23
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.google.android.gms:play-services-plus:7.8.0'
compile 'com.android.support:support-v13:23.0.0'
compile 'com.google.apis:google-api-services-youtube:v3-rev120-1.19.0'
compile 'com.google.http-client:google-http-client-android:+'
compile 'com.google.api-client:google-api-client-android:+'
compile 'com.google.api-client:google-api-client-gson:+'
compile 'com.google.code.gson:gson:2.2.4'
compile 'com.mcxiaoke.volley:library:1.0.18'
compile files('libs/YouTubeAndroidPlayerApi.jar')
}
================================================
FILE: app/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/ulukaya/android-sdks/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
================================================
FILE: app/src/main/AndroidManifest.xml
================================================
================================================
FILE: app/src/main/java/com/google/ytdl/Auth.java
================================================
/*
* Copyright (c) 2013 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.ytdl;
import com.google.android.gms.common.Scopes;
import com.google.api.services.youtube.YouTubeScopes;
public class Auth {
// Register an API key here: https://console.developers.google.com
public static final String KEY = "Replace me with your API key";
public static final String[] SCOPES = {Scopes.PROFILE, YouTubeScopes.YOUTUBE};
}
================================================
FILE: app/src/main/java/com/google/ytdl/Constants.java
================================================
/*
* Copyright (c) 2013 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.ytdl;
/**
* @author Ibrahim Ulukaya
*
* This class hold constants.
*/
public class Constants {
public static final int MAX_KEYWORD_LENGTH = 30;
public static final String DEFAULT_KEYWORD = "ytdl";
// A playlist ID is a string that begins with PL. You must replace this string with the correct
// playlist ID for the app to work
public static final String UPLOAD_PLAYLIST = "Replace me with the playlist ID you want to upload into";
public static final String APP_NAME = "ytd-android";
}
================================================
FILE: app/src/main/java/com/google/ytdl/MainActivity.java
================================================
/*
* Copyright (c) 2013 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.ytdl;
import android.accounts.AccountManager;
import android.app.Activity;
import android.app.Dialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.provider.MediaStore;
import android.support.v4.content.LocalBroadcastManager;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.ArrayAdapter;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import com.android.volley.toolbox.ImageLoader;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.api.client.extensions.android.http.AndroidHttp;
import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential;
import com.google.api.client.googleapis.extensions.android.gms.auth.GooglePlayServicesAvailabilityIOException;
import com.google.api.client.googleapis.extensions.android.gms.auth.UserRecoverableAuthIOException;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.gson.GsonFactory;
import com.google.api.client.util.ExponentialBackOff;
import com.google.api.services.youtube.YouTube;
import com.google.api.services.youtube.model.ChannelListResponse;
import com.google.api.services.youtube.model.PlaylistItem;
import com.google.api.services.youtube.model.PlaylistItemListResponse;
import com.google.api.services.youtube.model.Video;
import com.google.api.services.youtube.model.VideoListResponse;
import com.google.api.services.youtube.model.VideoSnippet;
import com.google.ytdl.util.NetworkSingleton;
import com.google.ytdl.util.Upload;
import com.google.ytdl.util.Utils;
import com.google.ytdl.util.VideoData;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* @author Ibrahim Ulukaya
*
* Main activity class which handles authorization and intents.
*/
public class MainActivity extends Activity implements
UploadsListFragment.Callbacks {
// private static final int MEDIA_TYPE_VIDEO = 7;
public static final String ACCOUNT_KEY = "accountName";
public static final String MESSAGE_KEY = "message";
public static final String YOUTUBE_ID = "youtubeId";
public static final String YOUTUBE_WATCH_URL_PREFIX = "http://www.youtube.com/watch?v=";
static final String REQUEST_AUTHORIZATION_INTENT = "com.google.example.yt.RequestAuth";
static final String REQUEST_AUTHORIZATION_INTENT_PARAM = "com.google.example.yt.RequestAuth.param";
private static final int REQUEST_GOOGLE_PLAY_SERVICES = 0;
private static final int REQUEST_GMS_ERROR_DIALOG = 1;
private static final int REQUEST_ACCOUNT_PICKER = 2;
private static final int REQUEST_AUTHORIZATION = 3;
private static final int RESULT_PICK_IMAGE_CROP = 4;
private static final int RESULT_VIDEO_CAP = 5;
private static final int REQUEST_DIRECT_TAG = 6;
private static final String TAG = "MainActivity";
final HttpTransport transport = AndroidHttp.newCompatibleTransport();
final JsonFactory jsonFactory = new GsonFactory();
GoogleAccountCredential credential;
private ImageLoader mImageLoader;
private String mChosenAccountName;
private Uri mFileURI = null;
private VideoData mVideoData;
private UploadBroadcastReceiver broadcastReceiver;
private UploadsListFragment mUploadsListFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
getWindow().requestFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
super.onCreate(savedInstanceState);
mUploadsListFragment = new UploadsListFragment(getApplicationContext());
// Check to see if the proper keys and playlist IDs have been set up
if (!isCorrectlyConfigured()) {
setContentView(R.layout.developer_setup_required);
showMissingConfigurations();
} else {
setContentView(R.layout.activity_main);
ensureLoader();
credential = GoogleAccountCredential.usingOAuth2(
getApplicationContext(), Arrays.asList(Auth.SCOPES));
// set exponential backoff policy
credential.setBackOff(new ExponentialBackOff());
if (savedInstanceState != null) {
mChosenAccountName = savedInstanceState.getString(ACCOUNT_KEY);
} else {
loadAccount();
}
credential.setSelectedAccountName(mChosenAccountName);
mUploadsListFragment = (UploadsListFragment) getFragmentManager()
.findFragmentById(R.id.list_fragment);
}
}
/**
* This method checks various internal states to figure out at startup time
* whether certain elements have been configured correctly by the developer.
* Checks that:
*
*
the API key has been configured
*
the playlist ID has been configured
*
*
* @return true if the application is correctly configured for use, false if
* not
*/
private boolean isCorrectlyConfigured() {
// This isn't going to internationalize well, but we only really need
// this for the sample app.
// Real applications will remove this section of code and ensure that
// all of these values are configured.
if (Auth.KEY.startsWith("Replace")) {
return false;
}
if (Constants.UPLOAD_PLAYLIST.startsWith("Replace")) {
return false;
}
return true;
}
/**
* This method renders the ListView explaining what the configurations the
* developer of this application has to complete. Typically, these are
* static variables defined in {@link Auth} and {@link Constants}.
*/
private void showMissingConfigurations() {
List missingConfigs = new ArrayList();
// Make sure an API key is registered
if (Auth.KEY.startsWith("Replace")) {
missingConfigs
.add(new MissingConfig(
"API key not configured",
"KEY constant in Auth.java must be configured with your Simple API key from the Google API Console"));
}
// Make sure a playlist ID is registered
if (Constants.UPLOAD_PLAYLIST.startsWith("Replace")) {
missingConfigs
.add(new MissingConfig(
"Playlist ID not configured",
"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)"));
}
// Renders a simple_list_item_2, which consists of a title and a body
// element
ListAdapter adapter = new ArrayAdapter(this,
android.R.layout.simple_list_item_2, missingConfigs) {
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View row;
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) getApplicationContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
row = inflater.inflate(android.R.layout.simple_list_item_2,
null);
} else {
row = convertView;
}
TextView titleView = (TextView) row
.findViewById(android.R.id.text1);
TextView bodyView = (TextView) row
.findViewById(android.R.id.text2);
MissingConfig config = getItem(position);
titleView.setText(config.title);
bodyView.setText(config.body);
return row;
}
};
// Wire the data adapter up to the view
ListView missingConfigList = (ListView) findViewById(R.id.missing_config_list);
missingConfigList.setAdapter(adapter);
}
@Override
protected void onResume() {
super.onResume();
if (broadcastReceiver == null)
broadcastReceiver = new UploadBroadcastReceiver();
IntentFilter intentFilter = new IntentFilter(
REQUEST_AUTHORIZATION_INTENT);
LocalBroadcastManager.getInstance(this).registerReceiver(
broadcastReceiver, intentFilter);
}
private void ensureLoader() {
if (mImageLoader == null) {
// Get the ImageLoader through your singleton class.
mImageLoader = NetworkSingleton.getInstance(this).getImageLoader();
}
}
private void loadAccount() {
SharedPreferences sp = PreferenceManager
.getDefaultSharedPreferences(this);
mChosenAccountName = sp.getString(ACCOUNT_KEY, null);
invalidateOptionsMenu();
}
private void saveAccount() {
SharedPreferences sp = PreferenceManager
.getDefaultSharedPreferences(this);
sp.edit().putString(ACCOUNT_KEY, mChosenAccountName).commit();
}
private void loadData() {
if (mChosenAccountName == null) {
return;
}
loadUploadedVideos();
}
@Override
protected void onPause() {
super.onPause();
if (broadcastReceiver != null) {
LocalBroadcastManager.getInstance(this).unregisterReceiver(
broadcastReceiver);
}
if (isFinishing()) {
// mHandler.removeCallbacksAndMessages(null);
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.menu.activity_main, menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_refresh:
loadData();
break;
case R.id.menu_accounts:
chooseAccount();
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case REQUEST_GMS_ERROR_DIALOG:
break;
case RESULT_PICK_IMAGE_CROP:
if (resultCode == RESULT_OK) {
mFileURI = data.getData();
if (mFileURI != null) {
Intent intent = new Intent(this, ReviewActivity.class);
intent.setData(mFileURI);
startActivity(intent);
}
}
break;
case RESULT_VIDEO_CAP:
if (resultCode == RESULT_OK) {
mFileURI = data.getData();
if (mFileURI != null) {
Intent intent = new Intent(this, ReviewActivity.class);
intent.setData(mFileURI);
startActivity(intent);
}
}
break;
case REQUEST_GOOGLE_PLAY_SERVICES:
if (resultCode == Activity.RESULT_OK) {
haveGooglePlayServices();
} else {
checkGooglePlayServicesAvailable();
}
break;
case REQUEST_AUTHORIZATION:
if (resultCode != Activity.RESULT_OK) {
chooseAccount();
}
break;
case REQUEST_ACCOUNT_PICKER:
if (resultCode == Activity.RESULT_OK && data != null
&& data.getExtras() != null) {
String accountName = data.getExtras().getString(
AccountManager.KEY_ACCOUNT_NAME);
if (accountName != null) {
mChosenAccountName = accountName;
credential.setSelectedAccountName(accountName);
saveAccount();
}
}
break;
case REQUEST_DIRECT_TAG:
if (resultCode == Activity.RESULT_OK && data != null
&& data.getExtras() != null) {
String youtubeId = data.getStringExtra(YOUTUBE_ID);
if (youtubeId.equals(mVideoData.getYouTubeId())) {
directTag(mVideoData);
}
}
break;
}
}
private void directTag(final VideoData video) {
final Video updateVideo = new Video();
VideoSnippet snippet = video
.addTags(Arrays.asList(
Constants.DEFAULT_KEYWORD,
Upload.generateKeywordFromPlaylistId(Constants.UPLOAD_PLAYLIST)));
updateVideo.setSnippet(snippet);
updateVideo.setId(video.getYouTubeId());
new AsyncTask() {
@Override
protected Void doInBackground(Void... voids) {
YouTube youtube = new YouTube.Builder(transport, jsonFactory,
credential).setApplicationName(Constants.APP_NAME)
.build();
try {
youtube.videos().update("snippet", updateVideo).execute();
} catch (UserRecoverableAuthIOException e) {
startActivityForResult(e.getIntent(), REQUEST_AUTHORIZATION);
} catch (IOException e) {
Log.e(TAG, e.getMessage());
}
return null;
}
}.execute((Void) null);
Toast.makeText(this,
R.string.video_submitted_to_ytdl, Toast.LENGTH_LONG)
.show();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString(ACCOUNT_KEY, mChosenAccountName);
}
private void loadUploadedVideos() {
if (mChosenAccountName == null) {
return;
}
setProgressBarIndeterminateVisibility(true);
new AsyncTask>() {
@Override
protected List doInBackground(Void... voids) {
YouTube youtube = new YouTube.Builder(transport, jsonFactory,
credential).setApplicationName(Constants.APP_NAME)
.build();
try {
/*
* Now that the user is authenticated, the app makes a
* channels list request to get the authenticated user's
* channel. Returned with that data is the playlist id for
* the uploaded videos.
* https://developers.google.com/youtube
* /v3/docs/channels/list
*/
ChannelListResponse clr = youtube.channels()
.list("contentDetails").setMine(true).execute();
// Get the user's uploads playlist's id from channel list
// response
String uploadsPlaylistId = clr.getItems().get(0)
.getContentDetails().getRelatedPlaylists()
.getUploads();
List videos = new ArrayList();
// Get videos from user's upload playlist with a playlist
// items list request
PlaylistItemListResponse pilr = youtube.playlistItems()
.list("id,contentDetails")
.setPlaylistId(uploadsPlaylistId)
.setMaxResults(20l).execute();
List videoIds = new ArrayList();
// Iterate over playlist item list response to get uploaded
// videos' ids.
for (PlaylistItem item : pilr.getItems()) {
videoIds.add(item.getContentDetails().getVideoId());
}
// Get details of uploaded videos with a videos list
// request.
VideoListResponse vlr = youtube.videos()
.list("id,snippet,status")
.setId(TextUtils.join(",", videoIds)).execute();
// Add only the public videos to the local videos list.
for (Video video : vlr.getItems()) {
if ("public".equals(video.getStatus()
.getPrivacyStatus())) {
VideoData videoData = new VideoData();
videoData.setVideo(video);
videos.add(videoData);
}
}
// Sort videos by title
Collections.sort(videos, new Comparator() {
@Override
public int compare(VideoData videoData,
VideoData videoData2) {
return videoData.getTitle().compareTo(
videoData2.getTitle());
}
});
return videos;
} catch (final GooglePlayServicesAvailabilityIOException availabilityException) {
showGooglePlayServicesAvailabilityErrorDialog(availabilityException
.getConnectionStatusCode());
} catch (UserRecoverableAuthIOException userRecoverableException) {
startActivityForResult(
userRecoverableException.getIntent(),
REQUEST_AUTHORIZATION);
} catch (IOException e) {
Utils.logAndShow(MainActivity.this, Constants.APP_NAME, e);
}
return null;
}
@Override
protected void onPostExecute(List videos) {
setProgressBarIndeterminateVisibility(false);
if (videos == null) {
return;
}
mUploadsListFragment.setVideos(videos);
}
}.execute((Void) null);
}
@Override
public void onBackPressed() {
// if (mDirectFragment.popPlayerFromBackStack()) {
// super.onBackPressed();
// }
}
@Override
public ImageLoader onGetImageLoader() {
ensureLoader();
return mImageLoader;
}
@Override
public void onVideoSelected(VideoData video) {
mVideoData = video;
Intent intent = new Intent(this, PlayActivity.class);
intent.putExtra(YOUTUBE_ID, video.getYouTubeId());
startActivityForResult(intent, REQUEST_DIRECT_TAG);
}
@Override
public void onConnected(String connectedAccountName) {
// Make API requests only when the user has successfully signed in.
loadData();
}
public void pickFile(View view) {
Intent intent = new Intent(Intent.ACTION_PICK);
intent.setType("video/*");
startActivityForResult(intent, RESULT_PICK_IMAGE_CROP);
}
public void recordVideo(View view) {
Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
// Workaround for Nexus 7 Android 4.3 Intent Returning Null problem
// create a file to save the video in specific folder (this works for
// video only)
// mFileURI = getOutputMediaFile(MEDIA_TYPE_VIDEO);
// intent.putExtra(MediaStore.EXTRA_OUTPUT, mFileURI);
// set the video image quality to high
intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1);
// start the Video Capture Intent
startActivityForResult(intent, RESULT_VIDEO_CAP);
}
public void showGooglePlayServicesAvailabilityErrorDialog(
final int connectionStatusCode) {
runOnUiThread(new Runnable() {
public void run() {
Dialog dialog = GooglePlayServicesUtil.getErrorDialog(
connectionStatusCode, MainActivity.this,
REQUEST_GOOGLE_PLAY_SERVICES);
dialog.show();
}
});
}
/**
* Check that Google Play services APK is installed and up to date.
*/
private boolean checkGooglePlayServicesAvailable() {
final int connectionStatusCode = GooglePlayServicesUtil
.isGooglePlayServicesAvailable(this);
if (GooglePlayServicesUtil.isUserRecoverableError(connectionStatusCode)) {
showGooglePlayServicesAvailabilityErrorDialog(connectionStatusCode);
return false;
}
return true;
}
private void haveGooglePlayServices() {
// check if there is already an account selected
if (credential.getSelectedAccountName() == null) {
// ask user to choose account
chooseAccount();
}
}
private void chooseAccount() {
startActivityForResult(credential.newChooseAccountIntent(),
REQUEST_ACCOUNT_PICKER);
}
/**
* Private class representing a missing configuration and what the developer
* can do to fix the issue.
*/
private class MissingConfig {
public final String title;
public final String body;
public MissingConfig(String title, String body) {
this.title = title;
this.body = body;
}
}
// public Uri getOutputMediaFile(int type)
// {
// // To be safe, you should check that the SDCard is mounted
// if(Environment.getExternalStorageState() != null) {
// // this works for Android 2.2 and above
// File mediaStorageDir = new
// File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES),
// "SMW_VIDEO");
//
// // This location works best if you want the created images to be shared
// // between applications and persist after your app has been uninstalled.
//
// // Create the storage directory if it does not exist
// if (! mediaStorageDir.exists()) {
// if (! mediaStorageDir.mkdirs()) {
// Log.d(TAG, "failed to create directory");
// return null;
// }
// }
//
// // Create a media file name
// String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss",
// Locale.getDefault()).format(new Date());
// File mediaFile;
// if(type == MEDIA_TYPE_VIDEO) {
// mediaFile = new File(mediaStorageDir.getPath() + File.separator +
// "VID_"+ timeStamp + ".mp4");
// } else {
// return null;
// }
//
// return Uri.fromFile(mediaFile);
// }
//
// return null;
// }
private class UploadBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(REQUEST_AUTHORIZATION_INTENT)) {
Log.d(TAG, "Request auth received - executing the intent");
Intent toRun = intent
.getParcelableExtra(REQUEST_AUTHORIZATION_INTENT_PARAM);
startActivityForResult(toRun, REQUEST_AUTHORIZATION);
}
}
}
}
================================================
FILE: app/src/main/java/com/google/ytdl/PlayActivity.java
================================================
package com.google.ytdl;
import android.app.Activity;
import android.app.FragmentTransaction;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.NavUtils;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import com.google.android.youtube.player.YouTubeInitializationResult;
import com.google.android.youtube.player.YouTubePlayer;
import com.google.android.youtube.player.YouTubePlayer.OnFullscreenListener;
import com.google.android.youtube.player.YouTubePlayer.PlayerStateChangeListener;
import com.google.android.youtube.player.YouTubePlayerFragment;
import com.google.api.client.extensions.android.http.AndroidHttp;
import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.gson.GsonFactory;
import com.google.ytdl.util.VideoData;
/*
* Copyright (c) 2013 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
/**
* @author Ibrahim Ulukaya
*
* Main fragment showing YouTube Direct Lite upload options and having
* YT Android Player.
*/
public class PlayActivity extends Activity implements
PlayerStateChangeListener, OnFullscreenListener {
private static final String YOUTUBE_FRAGMENT_TAG = "youtube";
final HttpTransport transport = AndroidHttp.newCompatibleTransport();
final JsonFactory jsonFactory = new GsonFactory();
GoogleAccountCredential credential;
private YouTubePlayer mYouTubePlayer;
private boolean mIsFullScreen = false;
private Intent intent;
public PlayActivity() {
}
@Override
public void onStart() {
super.onStart();
}
@Override
public void onStop() {
super.onStop();
}
public void directLite(View view) {
this.setResult(RESULT_OK, intent);
finish();
}
public void panToVideo(final String youtubeId) {
popPlayerFromBackStack();
YouTubePlayerFragment playerFragment = YouTubePlayerFragment
.newInstance();
getFragmentManager()
.beginTransaction()
.replace(R.id.detail_container, playerFragment,
YOUTUBE_FRAGMENT_TAG)
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
.addToBackStack(null).commit();
playerFragment.initialize(Auth.KEY,
new YouTubePlayer.OnInitializedListener() {
@Override
public void onInitializationSuccess(
YouTubePlayer.Provider provider,
YouTubePlayer youTubePlayer, boolean b) {
youTubePlayer.loadVideo(youtubeId);
mYouTubePlayer = youTubePlayer;
youTubePlayer
.setPlayerStateChangeListener(PlayActivity.this);
youTubePlayer
.setOnFullscreenListener(PlayActivity.this);
}
@Override
public void onInitializationFailure(
YouTubePlayer.Provider provider,
YouTubeInitializationResult result) {
showErrorToast(result.toString());
}
});
}
public boolean popPlayerFromBackStack() {
if (mIsFullScreen) {
mYouTubePlayer.setFullscreen(false);
return false;
}
if (getFragmentManager().findFragmentByTag(YOUTUBE_FRAGMENT_TAG) != null) {
getFragmentManager().popBackStack();
return false;
}
return true;
}
@Override
public void onAdStarted() {
}
@Override
public void onError(YouTubePlayer.ErrorReason errorReason) {
showErrorToast(errorReason.toString());
}
private void showErrorToast(String message) {
Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT)
.show();
}
@Override
public void onLoaded(String arg0) {
}
@Override
public void onLoading() {
}
@Override
public void onVideoEnded() {
// popPlayerFromBackStack();
}
@Override
public void onVideoStarted() {
}
@Override
public void onFullscreen(boolean fullScreen) {
mIsFullScreen = fullScreen;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getActionBar().setDisplayHomeAsUpEnabled(true);
setContentView(R.layout.activity_play);
intent = getIntent();
Button submitButton = (Button) findViewById(R.id.submit_button);
if (Intent.ACTION_VIEW.equals(intent.getAction())) {
submitButton.setVisibility(View.GONE);
setTitle(R.string.playing_uploaded_video);
}
String youtubeId = intent.getStringExtra(MainActivity.YOUTUBE_ID);
panToVideo(youtubeId);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.play, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
// Respond to the action bar's Up/Home button
case android.R.id.home:
NavUtils.navigateUpFromSameTask(this);
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public void onBackPressed() {
super.onBackPressed();
NavUtils.navigateUpFromSameTask(this);
}
public interface Callbacks {
public void onVideoSelected(VideoData video);
public void onResume();
}
}
================================================
FILE: app/src/main/java/com/google/ytdl/ResumableUpload.java
================================================
/*
* Copyright (c) 2013 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.ytdl;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.ThumbnailUtils;
import android.net.Uri;
import android.provider.MediaStore.Video.Thumbnails;
import android.support.v4.app.NotificationCompat;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
import com.google.api.client.googleapis.extensions.android.gms.auth.GooglePlayServicesAvailabilityIOException;
import com.google.api.client.googleapis.extensions.android.gms.auth.UserRecoverableAuthIOException;
import com.google.api.client.googleapis.media.MediaHttpUploader;
import com.google.api.client.googleapis.media.MediaHttpUploaderProgressListener;
import com.google.api.client.http.InputStreamContent;
import com.google.api.services.youtube.YouTube;
import com.google.api.services.youtube.model.Video;
import com.google.api.services.youtube.model.VideoListResponse;
import com.google.api.services.youtube.model.VideoSnippet;
import com.google.api.services.youtube.model.VideoStatus;
import com.google.ytdl.util.Upload;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Arrays;
import java.util.Calendar;
import java.util.List;
/**
* @author Ibrahim Ulukaya
*
* YouTube Resumable Upload controller class.
*/
public class ResumableUpload {
/**
* Assigned to the upload
*/
public static final String[] DEFAULT_KEYWORDS = {"MultiSquash", "Game"};
/**
* Indicates that the video is fully processed, see https://www.googleapis.com/discovery/v1/apis/youtube/v3/rpc
*/
private static final String SUCCEEDED = "succeeded";
private static final String TAG = "UploadingActivity";
private static int UPLOAD_NOTIFICATION_ID = 1001;
private static int PLAYBACK_NOTIFICATION_ID = 1002;
/*
* Global instance of the format used for the video being uploaded (MIME type).
*/
private static String VIDEO_FILE_FORMAT = "video/*";
/**
* Uploads user selected video in the project folder to the user's YouTube account using OAuth2
* for authentication.
*/
public static String upload(YouTube youtube, final InputStream fileInputStream,
final long fileSize, final Uri mFileUri, final String path, final Context context) {
final NotificationManager notifyManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
final NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
Intent notificationIntent = new Intent(context, ReviewActivity.class);
notificationIntent.setData(mFileUri);
notificationIntent.setAction(Intent.ACTION_VIEW);
Bitmap thumbnail = ThumbnailUtils.createVideoThumbnail(path, Thumbnails.MICRO_KIND);
PendingIntent contentIntent = PendingIntent.getActivity(context,
0, notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT);
builder.setContentTitle(context.getString(R.string.youtube_upload))
.setContentText(context.getString(R.string.youtube_upload_started))
.setSmallIcon(R.drawable.ic_stat_device_access_video).setContentIntent(contentIntent).setStyle(new NotificationCompat.BigPictureStyle().bigPicture(thumbnail));
notifyManager.notify(UPLOAD_NOTIFICATION_ID, builder.build());
String videoId = null;
try {
// Add extra information to the video before uploading.
Video videoObjectDefiningMetadata = new Video();
/*
* Set the video to public, so it is available to everyone (what most people want). This is
* actually the default, but I wanted you to see what it looked like in case you need to set
* it to "unlisted" or "private" via API.
*/
VideoStatus status = new VideoStatus();
status.setPrivacyStatus("public");
videoObjectDefiningMetadata.setStatus(status);
// We set a majority of the metadata with the VideoSnippet object.
VideoSnippet snippet = new VideoSnippet();
/*
* The Calendar instance is used to create a unique name and description for test purposes, so
* you can see multiple files being uploaded. You will want to remove this from your project
* and use your own standard names.
*/
Calendar cal = Calendar.getInstance();
snippet.setTitle("Test Upload via Java on " + cal.getTime());
snippet.setDescription("Video uploaded via YouTube Data API V3 using the Java library "
+ "on " + cal.getTime());
// Set your keywords.
snippet.setTags(Arrays.asList(Constants.DEFAULT_KEYWORD, Upload.generateKeywordFromPlaylistId(Constants.UPLOAD_PLAYLIST)));
// Set completed snippet to the video object.
videoObjectDefiningMetadata.setSnippet(snippet);
InputStreamContent mediaContent =
new InputStreamContent(VIDEO_FILE_FORMAT, new BufferedInputStream(fileInputStream));
mediaContent.setLength(fileSize);
/*
* The upload command includes: 1. Information we want returned after file is successfully
* uploaded. 2. Metadata we want associated with the uploaded video. 3. Video file itself.
*/
YouTube.Videos.Insert videoInsert =
youtube.videos().insert("snippet,statistics,status", videoObjectDefiningMetadata,
mediaContent);
// Set the upload type and add event listener.
MediaHttpUploader uploader = videoInsert.getMediaHttpUploader();
/*
* Sets whether direct media upload is enabled or disabled. True = whole media content is
* uploaded in a single request. False (default) = resumable media upload protocol to upload
* in data chunks.
*/
uploader.setDirectUploadEnabled(false);
MediaHttpUploaderProgressListener progressListener = new MediaHttpUploaderProgressListener() {
public void progressChanged(MediaHttpUploader uploader) throws IOException {
switch (uploader.getUploadState()) {
case INITIATION_STARTED:
builder.setContentText(context.getString(R.string.initiation_started)).setProgress((int) fileSize,
(int) uploader.getNumBytesUploaded(), false);
notifyManager.notify(UPLOAD_NOTIFICATION_ID, builder.build());
break;
case INITIATION_COMPLETE:
builder.setContentText(context.getString(R.string.initiation_completed)).setProgress((int) fileSize,
(int) uploader.getNumBytesUploaded(), false);
notifyManager.notify(UPLOAD_NOTIFICATION_ID, builder.build());
break;
case MEDIA_IN_PROGRESS:
builder
.setContentTitle(context.getString(R.string.youtube_upload) +
(int) (uploader.getProgress() * 100) + "%")
.setContentText(context.getString(R.string.upload_in_progress))
.setProgress((int) fileSize, (int) uploader.getNumBytesUploaded(), false);
notifyManager.notify(UPLOAD_NOTIFICATION_ID, builder.build());
break;
case MEDIA_COMPLETE:
builder.setContentTitle(context.getString(R.string.yt_upload_completed))
.setContentText(context.getString(R.string.upload_completed))
// Removes the progress bar
.setProgress(0, 0, false);
notifyManager.notify(UPLOAD_NOTIFICATION_ID, builder.build());
case NOT_STARTED:
Log.d(this.getClass().getSimpleName(), context.getString(R.string.upload_not_started));
break;
}
}
};
uploader.setProgressListener(progressListener);
// Execute upload.
Video returnedVideo = videoInsert.execute();
Log.d(TAG, "Video upload completed");
videoId = returnedVideo.getId();
Log.d(TAG, String.format("videoId = [%s]", videoId));
} catch (final GooglePlayServicesAvailabilityIOException availabilityException) {
Log.e(TAG, "GooglePlayServicesAvailabilityIOException", availabilityException);
notifyFailedUpload(context, context.getString(R.string.cant_access_play), notifyManager, builder);
} catch (UserRecoverableAuthIOException userRecoverableException) {
Log.i(TAG, String.format("UserRecoverableAuthIOException: %s",
userRecoverableException.getMessage()));
requestAuth(context, userRecoverableException);
} catch (IOException e) {
Log.e(TAG, "IOException", e);
notifyFailedUpload(context, context.getString(R.string.please_try_again), notifyManager, builder);
}
return videoId;
}
private static void requestAuth(Context context,
UserRecoverableAuthIOException userRecoverableException) {
LocalBroadcastManager manager = LocalBroadcastManager.getInstance(context);
Intent authIntent = userRecoverableException.getIntent();
Intent runReqAuthIntent = new Intent(MainActivity.REQUEST_AUTHORIZATION_INTENT);
runReqAuthIntent.putExtra(MainActivity.REQUEST_AUTHORIZATION_INTENT_PARAM, authIntent);
manager.sendBroadcast(runReqAuthIntent);
Log.d(TAG, String.format("Sent broadcast %s", MainActivity.REQUEST_AUTHORIZATION_INTENT));
}
private static void notifyFailedUpload(Context context, String message, NotificationManager notifyManager,
NotificationCompat.Builder builder) {
builder.setContentTitle(context.getString(R.string.yt_upload_failed))
.setContentText(message);
notifyManager.notify(UPLOAD_NOTIFICATION_ID, builder.build());
Log.e(ResumableUpload.class.getSimpleName(), message);
}
public static void showSelectableNotification(String videoId, Context context) {
Log.d(TAG, String.format("Posting selectable notification for video ID [%s]", videoId));
final NotificationManager notifyManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
final NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
Intent notificationIntent = new Intent(context, PlayActivity.class);
notificationIntent.putExtra(MainActivity.YOUTUBE_ID, videoId);
notificationIntent.setAction(Intent.ACTION_VIEW);
URL url;
try {
url = new URL("https://i1.ytimg.com/vi/" + videoId + "/mqdefault.jpg");
Bitmap thumbnail = BitmapFactory.decodeStream(url.openConnection().getInputStream());
PendingIntent contentIntent = PendingIntent.getActivity(context,
0, notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT);
builder.setContentTitle(context.getString(R.string.watch_your_video))
.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));
notifyManager.notify(PLAYBACK_NOTIFICATION_ID, builder.build());
Log.d(TAG, String.format("Selectable notification for video ID [%s] posted", videoId));
} catch (MalformedURLException e) {
Log.e(TAG, e.getMessage());
} catch (IOException e) {
Log.e(TAG, e.getMessage());
}
}
/**
* @return url of thumbnail if the video is fully processed
*/
public static boolean checkIfProcessed(String videoId, YouTube youtube) {
try {
YouTube.Videos.List list = youtube.videos().list("processingDetails");
list.setId(videoId);
VideoListResponse listResponse = list.execute();
List