I'm uploading videos to my YouTube account from my Android application using YouTube Data API v3. Uploading process works perfectly. What I need is, if my upload got interrupted (eg. because of Internet connection) I need to start upload next time where it left.
private void uploadVideo(UploadInfo videoInfo) {
Insert videoInsert = prepareUpload( new File(videoInfo.getFilePath()) );
new VideoUploadAsyncTask(videoInfo, getActivity(), videoUploadAsyncTaskInterface).executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, videoInsert);
}
public Insert prepareUpload( File videoFile ) {
try {
Video videoObjectDefiningMetadata = new Video();
VideoStatus status = new VideoStatus();
status.setPrivacyStatus("public");
videoObjectDefiningMetadata.setStatus(status);
VideoSnippet snippet = new VideoSnippet();
snippet.setTitle(videoFile.getName());
snippet.setDescription("Uploaded via AnujAroshA Android app");
List<String> tags = new ArrayList<String>();
tags.add("AnujAroshA");
snippet.setTags(tags);
videoObjectDefiningMetadata.setSnippet(snippet);
InputStreamContent mediaContent = new InputStreamContent(VIDEO_FILE_FORMAT, new BufferedInputStream(new FileInputStream(videoFile)));
mediaContent.setLength(videoFile.length());
YouTube.Videos.Insert videoInsert = youtube.videos().insert("snippet,status", videoObjectDefiningMetadata, mediaContent);
MediaHttpUploader uploader = videoInsert.getMediaHttpUploader();
uploader.setDirectUploadEnabled(false);
uploader.setChunkSize(MediaHttpUploader.MINIMUM_CHUNK_SIZE);
return videoInsert;
} catch (FileNotFoundException e) {
return null;
} catch (IOException e) {
return null;
}
}
How can I do that?
Update with VideoUploadAsyncTask class doInBackground method
#Override
protected String doInBackground( Insert... inserts ) {
try{
Insert videoInsert = inserts[0];
MediaHttpUploader uploader = videoInsert.getMediaHttpUploader();
MediaHttpUploaderProgressListener progressListener = new MediaHttpUploaderProgressListener() {
#Override
public void progressChanged(MediaHttpUploader uploader) throws IOException {
switch (uploader.getUploadState()) {
case INITIATION_STARTED:
Log.d(TAG, "# INITIATION_STARTED ");
break;
case INITIATION_COMPLETE:
Log.d(TAG, "# INITIATION_COMPLETE ");
break;
case MEDIA_IN_PROGRESS:
int progress = (int) Math.round(uploader.getProgress() * 100);
Log.d(TAG, "# MEDIA_IN_PROGRESS : progress = " + progress + "%");
publishProgress(progress);
break;
case MEDIA_COMPLETE:
Log.d(TAG, "# MEDIA_COMPLETE ");
publishProgress(100);
break;
case NOT_STARTED:
Log.d(TAG, "# NOT_STARTED ");
break;
default:
break;
}
}
};
uploader.setProgressListener(progressListener);
Video returnedVideo = videoInsert.execute();
return returnedVideo.getId();
}catch(GooglePlayServicesAvailabilityIOException gpsaioe){
}catch(UserRecoverableAuthIOException uraioe){
}catch(GoogleAuthIOException gaioe){
}catch(IOException ioe){
}
return null;
}
Have you seen this question?
The answer suggests that you should use;
youtube = new YouTube.Builder(HTTP_TRANSPORT, JSON_FACTORY, new HttpRequestInitializer() {
#Override
public void initialize(HttpRequest request) throws IOException {
credential.initialize(request);
request.setIOExceptionHandler(new HttpBackOffIOExceptionHandler(new ExponentialBackOff()));
}
});
when you initialise your YouTube object.
Related
This question already has answers here:
Android: install .apk programmatically [duplicate]
(4 answers)
Closed 5 years ago.
I've seen a few answers related to this but can't quite seem to find what I'm looking for. Say I have a self hosted app. Now say I've made some changes to that app and would like to let the user know within the app that there is an update available. I can get the app to successfully download the apk file and begin installing it. After the installation is "finished" the app closes out. When I restart the app none of the changes I've made have been applied. So it appears the installation has failed, but there was no apparent crash. However, when I install the apk that was downloaded from the Downloads manager it installs just fine and the changes I have made are applied. Any ideas? Here is the section of code I use to download and install programmatically:
String destination = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + "/";
String fileName = "TheApp.apk";
destination += fileName;
final Uri uri = Uri.parse("file://" + destination);
String url = "myapplocation";
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
request.setDescription("Downloading necessary update files.");
request.setTitle("Updating The App");
final DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
final long downloadId = manager.enqueue(request);
BroadcastReceiver onComplete = new BroadcastReceiver() {
public void onReceive(Context ctxt, Intent intent) {
Intent install = new Intent(Intent.ACTION_VIEW);
install.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
install.setDataAndType(uri,
manager.getMimeTypeForDownloadedFile(downloadId));
startActivityForResult(install, 0);
unregisterReceiver(this);
}
};
registerReceiver(onComplete, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
Get VersionName and VersionCode for current Running application.
code:
try {
PackageInfo pInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
Common.VersionName = pInfo.versionName;
Common.VersionCode = pInfo.versionCode;
Log.e("VersionCode", ">>>>>>>>>>" + Common.VersionCode + Common.VersionName);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
**Check the Version Names**
if (!Common.VersionName.equals(Common.VersionNamefromWebApi)) {
AlertDialogUpdate(MakeTransactionActivity.this, Common.AppUpdateTitle, "YokoYepi Version" + Common.VersionNamefromWebApi + " available.");
}
**Alert Dialog Box**
public void AlertDialogUpdate(Activity activity, String title, CharSequence message) {
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setCancelable(false);
if (title != null) builder.setTitle(title);
builder.setMessage(message);
builder.setPositiveButton("UPDATE", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
new DownloadNewVersion().execute();
dialog.dismiss();
}
});
builder.show();
}
**Download and Install the .apk file from URL**
class DownloadNewVersion extends AsyncTask<String, Integer, Boolean> {
#Override
protected void onPreExecute() {
super.onPreExecute();
bar = new ProgressDialog(MakeTransactionActivity.this);
bar.setCancelable(false);
bar.setMessage("Downloading...");
bar.setIndeterminate(true);
bar.setCanceledOnTouchOutside(false);
bar.show();
stoptimertask();
}
protected void onProgressUpdate(Integer... progress) {
super.onProgressUpdate(progress);
bar.setIndeterminate(false);
bar.setMax(100);
bar.setProgress(progress[0]);
String msg = "";
if (progress[0] > 99) {
msg = "Finishing... ";
} else {
msg = "Downloading... " + progress[0] + "%";
}
bar.setMessage(msg);
}
#Override
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
startTimer();
bar.dismiss();
if (result) {
Toast.makeText(getApplicationContext(), "Update Done",
Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(getApplicationContext(), "Error: Try Again",
Toast.LENGTH_SHORT).show();
}
}
#Override
protected Boolean doInBackground(String... arg0) {
Boolean flag = false;
try {
String PATH;
Boolean isSDPresent = android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED);
if (isSDPresent) {
PATH = Environment.getExternalStorageDirectory() + "/Download/";
} else {
PATH = Environment.getDataDirectory() + "/Download/";
}
File file = new File(PATH);
file.mkdirs();
File outputFile = new File(file, "yokoyepi.apk");
if (outputFile.exists()) {
outputFile.delete();
}
// Download File from url
URL u = new URL(Common.AppUpdateURL);
URLConnection conn = u.openConnection();
int contentLength = conn.getContentLength();
DataInputStream stream = new DataInputStream(u.openStream());
byte[] buffer = new byte[contentLength];
stream.readFully(buffer);
stream.close();
DataOutputStream fos = new DataOutputStream(new FileOutputStream(outputFile));
fos.write(buffer);
fos.flush();
fos.close();
// Install dowloaded Apk file from Devive----------------
OpenNewVersion(PATH);
flag = true;
} catch (MalformedURLException e) {
Log.e(TAG, "Update Error: " + e.getMessage());
flag = false;
} catch (IOException e) {
Log.e(TAG, "Update Error: " + e.getMessage());
flag = false;
} catch (Exception e) {
Log.e(TAG, "Update Error: " + e.getMessage());
flag = false;
}
return flag;
}
}
void OpenNewVersion(String location) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(new File(location + "yokoyepi.apk")),
"application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
// if your not install u should call the function in onResume().
// again it will check whether apk updated or not.
While using MediaRecorder, we don't have pause/resume for API level below 24.
So there can be a way to do this is:
On pause event stop the recorder and create the recorded file.
And on resume start recording again and create another file and keep doing so until user presses stop.
And at last merge all files.
Many people asked this question on SO, but couldn't find anyway to solve this. People talk about creating multiple media files by stopping recording on pause action and restarting on resume. So my question is How can we merge/join all media file programmatically?
Note: in my case MPEG4 container - m4a for audio and mp4 for video.
I tried using SequenceInputStream to merge multiple InputStream of respective generated recorded files. But it always results the first file only.
Code Snippet:
Enumeration<InputStream> enu = Collections.enumeration(inputStreams);
SequenceInputStream sqStream = new SequenceInputStream(enu);
while ((oneByte = sqStream.read(buffer)) != -1) {
fileOutputStream.write(buffer, 0, oneByte);
}
sqStream.close();
while (enu.hasMoreElements()) {
InputStream element = enu.nextElement();
element.close();
}
fileOutputStream.flush();
fileOutputStream.close();
I could solve this problem using mp4parser library. Thanks much to author of this library :)
Add below dependency in your gradle file:
compile 'com.googlecode.mp4parser:isoparser:1.0.2'
The solution is to stop recorder when user pause and start again on resume as already mentioned in many other answers in stackoverflow. Store all the audio/video files generated in an array and use below method to merge all media files. The example is also taken from mp4parser library and modified little bit as per my need.
public static boolean mergeMediaFiles(boolean isAudio, String sourceFiles[], String targetFile) {
try {
String mediaKey = isAudio ? "soun" : "vide";
List<Movie> listMovies = new ArrayList<>();
for (String filename : sourceFiles) {
listMovies.add(MovieCreator.build(filename));
}
List<Track> listTracks = new LinkedList<>();
for (Movie movie : listMovies) {
for (Track track : movie.getTracks()) {
if (track.getHandler().equals(mediaKey)) {
listTracks.add(track);
}
}
}
Movie outputMovie = new Movie();
if (!listTracks.isEmpty()) {
outputMovie.addTrack(new AppendTrack(listTracks.toArray(new Track[listTracks.size()])));
}
Container container = new DefaultMp4Builder().build(outputMovie);
FileChannel fileChannel = new RandomAccessFile(String.format(targetFile), "rw").getChannel();
container.writeContainer(fileChannel);
fileChannel.close();
return true;
}
catch (IOException e) {
Log.e(LOG_TAG, "Error merging media files. exception: "+e.getMessage());
return false;
}
}
Use flag isAudio as true for Audio files and false for Video files.
Another solution is merging with FFmpeg
Add this line to your app build.gradle
implementation 'com.writingminds:FFmpegAndroid:0.3.2'
And use below code to merge videos.
String textFile = "";
try {
textFile = getTextFile().getAbsolutePath();
} catch (IOException e) {
e.printStackTrace();
}
String[] cmd = new String[]{
"-y",
"-f",
"concat",
"-safe",
"0",
"-i",
textFile,
"-c",
"copy",
"-preset",
"ultrafast",
getVideoFilePath()};
mergeVideos(cmd);
getTextFile()
private File getTextFile() throws IOException {
videoFiles = new String[]{firstPath, secondPath, thirdPatch};
File file = new File(getActivity().getExternalFilesDir(null), System.currentTimeMillis() + "inputFiles.txt");
FileOutputStream out = new FileOutputStream(file, false);
PrintWriter writer = new PrintWriter(out);
StringBuilder builder = new StringBuilder();
for (String path : videoFiles) {
if (path != null) {
builder.append("file ");
builder.append("\'");
builder.append(path);
builder.append("\'\n");
}
}
builder.deleteCharAt(builder.length() - 1);
String text = builder.toString();
writer.print(text);
writer.close();
out.close();
return file;
}
getVideoFilePath()
private String getVideoFilePath() {
final File dir = getActivity().getExternalFilesDir(null);
return (dir == null ? "" : (dir.getAbsolutePath() + "/"))
+ System.currentTimeMillis() + ".mp4";
}
mergeVideos()
private void mergeVideos(String[] cmd) {
FFmpeg ffmpeg = FFmpeg.getInstance(getActivity());
try {
ffmpeg.execute(cmd, new ExecuteBinaryResponseHandler() {
#Override
public void onStart() {
startTime = System.currentTimeMillis();
}
#Override
public void onProgress(String message) {
}
#Override
public void onFailure(String message) {
Toast.makeText(getActivity(), "Failed " + message, Toast.LENGTH_SHORT).show();
}
#Override
public void onSuccess(String message) {
}
#Override
public void onFinish() {
Toast.makeText(getActivity(), "Videos are merged", Toast.LENGTH_SHORT).show();
}
});
} catch (FFmpegCommandAlreadyRunningException e) {
// Handle if FFmpeg is already running
}
}
Run this code before merging
private void checkFfmpegSupport() {
FFmpeg ffmpeg = FFmpeg.getInstance(this);
try {
ffmpeg.loadBinary(new LoadBinaryResponseHandler() {
#Override
public void onStart() {
}
#Override
public void onFailure() {
Toast.makeText(VouchActivity.this, "FFmpeg not supported on this device :(", Toast.LENGTH_SHORT).show();
}
#Override
public void onSuccess() {
}
#Override
public void onFinish() {
}
});
} catch (FFmpegNotSupportedException e) {
// Handle if FFmpeg is not supported by device
}
}
Chromcast Remote player seek to resume at seek position 0 when using nanohttpd server. Main issue getting when I seek into video player in device its working fine but on TV seek-bar set at 0 position and music stat at beginning.
When to call mRemoteMediaPlayer.seek() in onSeekChanged() getting result success but on TV seek-bar set at 0 position and music stat at beginning.
public class webserver extends NanoHTTPD {
FileInputStream fileInputStream;
public webserver(){
super(8080);
}
#Override
public Response serve(String uri, Method method, Map<String, String> header,Map<String, String> parameters, Map<String, String> files) {
String mediasend=" ";
long size=0;
FileInputStream fis = null;
try {
fis = new FileInputStream(path);
//byte[] buffer = new byte[(int) fis.getChannel().size()];
size=fis.getChannel().size();
} catch (Exception e) {
e.printStackTrace();
}
switch(mediatype){
case "photo":
mediasend="image/jpeg";
break;
case "audio":
mediasend="audio/mp3";
break;
case "video":
mediasend="video/mp4";
break;
}
return new NanoHTTPD.Response(com.castoffline.castActivity.NanoHTTPD.Response.Status.OK,mediasend,fis,size);
}
}
Cast connection code
Cast.CastApi.launchApplication(mApiClient,getString(R.string.app_id),false).setResultCallback(new ResultCallback<Cast.ApplicationConnectionResult>() {
#Override
public void onResult(ApplicationConnectionResult result) {
Status status = result.getStatus();
if (status.isSuccess()) {
ApplicationMetadata applicationMetadata = result.getApplicationMetadata();
mSessionId = result.getSessionId();
String applicationStatus = result.getApplicationStatus();
boolean wasLaunched = result.getWasLaunched();
Log.d(TAG,"application name: "+ applicationMetadata.getName()+ ", status: "+ applicationStatus+ ", sessionId: "+ mSessionId+ ", wasLaunched: "+ wasLaunched);
mApplicationStarted = true;
mRemoteMediaPlayer = new RemoteMediaPlayer();
/*
* Identify the mediatype and send the metadata details to media info
*/
switch(mediatype)
{
case "audio" : mediaMetadata = new MediaMetadata(MediaMetadata.MEDIA_TYPE_MUSIC_TRACK);
mediaMetadata.putString(MediaMetadata.KEY_TITLE, "MY MUSIC TRACK"+": "+audioTitle);
mediaMetadata.putString(MediaMetadata.KEY_ARTIST,audioArtist);
mediaMetadata.addImage(new WebImage(Uri.parse("https://www.googledrive.com/host/0B61ekPEN_94sZ21mcnQtbVU2RHM/media.png")));
mediaInfo = new MediaInfo.Builder(ipdevice).setContentType(mimetype).setStreamType(MediaInfo.STREAM_TYPE_BUFFERED).setMetadata(mediaMetadata).build();
break;
case "video" : mediaMetadata = new MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE);
mediaMetadata.addImage(new WebImage(Uri.parse("https://www.googledrive.com/host/0B61ekPEN_94sZ21mcnQtbVU2RHM/film_reel.png")));
mediaMetadata.putString(MediaMetadata.KEY_TITLE, "My MOVIE"+": "+videoTitle);
mediaInfo = new MediaInfo.Builder(ipdevice).setContentType(mimetype).setStreamType(MediaInfo.STREAM_TYPE_BUFFERED).setMetadata(mediaMetadata).build();
break;
case "photo" : mediaMetadata = new MediaMetadata(MediaMetadata.MEDIA_TYPE_PHOTO);
mediaMetadata.putString(MediaMetadata.KEY_TITLE, "My PHOTO"+": ");
mediaInfo = new MediaInfo.Builder(ipdevice).setContentType(mimetype).setStreamType(MediaInfo.STREAM_TYPE_BUFFERED).setMetadata(mediaMetadata).build();
break;
default:
}
try {
Cast.CastApi.setMessageReceivedCallbacks(mApiClient,mRemoteMediaPlayer.getNamespace(), mRemoteMediaPlayer);
} catch (IOException e) {
Log.d(TAG, "Exception while creating media channel", e);
}
try {
mRemoteMediaPlayer.load(mApiClient, mediaInfo, false,0).setResultCallback(new ResultCallback<RemoteMediaPlayer.MediaChannelResult>() {
#Override
public void onResult(MediaChannelResult result) {
if (result.getStatus().isSuccess()) {
Log.d(TAG, "Media loaded successfully");
}
}});
/*
* checks if the video is playing or if it is paused and according it will be played/paused in the receiver
*/
videoview.setPlayPauseListener(new CustomVideoView.PlayPauseListener() {
AudioManager amanager=(AudioManager)getSystemService(Context.AUDIO_SERVICE);
#Override
public void onPlay() {
playbackPaused=false; //videoView is playing
if(mSelectedDevice!=null && mApiClient != null && mRemoteMediaPlayer != null){
//volume is set to mute if media is casting in Chromecast
amanager.setStreamMute(AudioManager.STREAM_MUSIC, true);
sendMediaControl(playbackPaused,false);
}else{
amanager.setStreamVolume(AudioManager.STREAM_MUSIC, 3,1);
}
}
#Override
public void onPause(){
playbackPaused=true; //videoView is paused
if (mSelectedDevice != null && mApiClient != null && mRemoteMediaPlayer != null){
amanager.setStreamMute(AudioManager.STREAM_MUSIC, false);
sendMediaControl(playbackPaused,false);
}else{
amanager.setStreamVolume(AudioManager.STREAM_MUSIC, 3,1); }
}
/* Currently Seek function is not working for the media playback while casting
* (non-Javadoc)
* #see com.castoffline.castActivity.CustomVideoView.PlayPauseListener#onSeekChanged(int)
*/
#Override
public void onSeekChanged(int pos){
Log.d(String.valueOf(videoview.getCurrentPosition()),"seekinsie");
// seek(videoview.getCurrentPosition());
Log.d("mimetype ",mimetype);
Log.d("seek1",""+pos);
if (mSelectedDevice != null && mApiClient != null && mRemoteMediaPlayer != null){
videoview.pause();
final long position=videoview.getCurrentPosition();
Log.d("seek",""+position);
mRemoteMediaPlayer.seek(mApiClient,position,RemoteMediaPlayer.RESUME_STATE_UNCHANGED).setResultCallback(new ResultCallback<RemoteMediaPlayer.MediaChannelResult>(){
#Override
public void onResult(MediaChannelResult result) {
if (result.getStatus().isSuccess()) {
Log.d(String.valueOf("State Code "+result.getStatus().getStatusCode()),""+mRemoteMediaPlayer.getApproximateStreamPosition());
}
}
});
mRemoteMediaPlayer.setOnStatusUpdatedListener(new RemoteMediaPlayer.OnStatusUpdatedListener(){
#Override
public void onStatusUpdated() {
#SuppressWarnings("unused")
MediaStatus mediaStatus = mRemoteMediaPlayer.getMediaStatus();
Log.d("seek state update",""+mediaStatus);
}
});
}
}
});
} catch (IllegalStateException e) {
Log.d(TAG, "Problem occurred with media during loading", e);
} catch (Exception e) {
Log.d(TAG, "Problem opening media during loading", e);}
} else {
Log.e(TAG,"application could not launch");
teardown();
}
}
});
}
Remote player control code.
private void sendMediaControl(final boolean playbackPaused,final boolean change)
{
if (mApiClient != null && mRemoteMediaPlayer != null){
mRemoteMediaPlayer.requestStatus(mApiClient).setResultCallback( new ResultCallback<RemoteMediaPlayer.MediaChannelResult>() {
#Override
public void onResult(RemoteMediaPlayer.MediaChannelResult mediaChannelResult) {
if(playbackPaused ==true){
mRemoteMediaPlayer.pause(mApiClient);
}else{
mRemoteMediaPlayer.play(mApiClient);
}
}
});
}
}
There can be two things here:
I don't know if it is a limitation of nanohttpd or a configuration issue but what you are seeing is because the nanaohttpd (at least the way you have configured it) doesn't support seek. When you do a seek, your receiver will call into your http server (nanohttpd in this case) and passes a position and asks the web server to seek to that position and start streaming from there. If the web server doesn't support that, you will not be able to seek successfully. As a test, set up an apache server on your laptop, just for testing, and point to that instead of your embedded web server and see if that works or not.
There might be a mismatch between units of position; so if, say, your local player is using seconds and is reporting, say, 60 when it is a minute into the content and if you send that to the cast receiver, it will be interpreted as 60 milliseconds which is practically same as 0 seconds, so check on that too.
I have the following code. submitClick() is triggered when someone clicks the submit button on my application. In the past, submit click would be click once, and all of submitFile() would be carried out. By adding the threading addition to checkContent(), now submitClick has to be clicked twice for all of submitFile() to be completed. Is there any way to make it so that submitFile() finishes (with checkContent()) so the user only has to press my apps submit button?
Thank you in advance.
public void submitClick(View v) throws Exception{
if (!submitted) { submitFile(); }
}
public void submitFile() {
checkContent();
if (valid) {
Picasso.with(this).load(imageAddress).into(imagePreview);
saveImage();
//sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + Environment.getExternalStorageDirectory()))); //refreshes system to show saved file
submitted = true;
submit_but.setText("Encrypt");
}
}
private void checkContent() {
media = null;
if(URLUtil.isValidUrl(imageAddress)) {
Thread thread = new Thread() {
boolean img = false;
boolean youtube = false;
public void run() {
URLConnection connection = null;
try {
connection = new URL(imageAddress).openConnection();
} catch (IOException e) {
e.printStackTrace();
}
String contentType = connection.getHeaderField("Content-Type");
img = contentType.startsWith("image/");
if(img)
media = "image";
if (!img) {
// Check host of url if youtube exists
Uri uri = Uri.parse(imageAddress);
if ("www.youtube.com".equals(uri.getHost())) {
media = "youtube";
youtube = true;
}
}
valid = img || youtube;
}
};
thread.start();
}
}
The problem is that the Thread will finish after the call to if (valid) and the rest in the submitFile().
Easy fix is to include the entire submitFile() in a Thread, instead of just part of it. If the logic is bound to each other, it's better off that they're together.
A more android-esque fix is to use AsyncTask, as such:
public void submitClick(View v) throws Exception {
if (!submitted) { submitFile(); }
}
public void submitFile() {
if(URLUtil.isValidUrl(imageAddress)) {
new AsyncTask<Void, Void, Boolean>() {
protected Long doInBackground(Void... voids) {
boolean img = false;
boolean youtube = false;
URLConnection connection = null;
try {
connection = new URL(imageAddress).openConnection();
} catch (IOException e) {
e.printStackTrace();
}
String contentType = connection.getHeaderField("Content-Type");
img = contentType.startsWith("image/");
if(img)
media = "image";
if (!img) {
// Check host of url if youtube exists
Uri uri = Uri.parse(imageAddress);
if ("www.youtube.com".equals(uri.getHost())) {
media = "youtube";
youtube = true;
}
}
return img || youtube;
}
protected void onPostExecute(Boolean valid) {
if (valid) {
Picasso.with(this).load(imageAddress).into(imagePreview);
saveImage();
//sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + Environment.getExternalStorageDirectory()))); //refreshes system to show saved file
submitted = true;
submit_but.setText("Encrypt");
}
}
}.execute();
}
}
I am testing the Drive API for Android to upload a file that can show uploaded progress and be able to resume the upload if it fails (Files size > 30 MB.)
With the following questions:
Uploading Downloading of large size file to Google Drive giving error,
Upload progress listener not fired (Google drive API)
I was able to get the upload progress and they mention those are resumable uploads. However, I don't see any code that looks for an upload error and resume logic, thus if I kill the application and "resume" the upload, it simply starts from the beginning.
This is my code:
public class DriveScreen extends BaseDriveActivity {
private Drive service;
private Context context;
#Override
public void onConnected(Bundle connectionHint) {
super.onConnected(connectionHint);
context = this;
//https://stackoverflow.com/questions/17429798/usingoauth2-deprecated
GoogleAccountCredential credential = GoogleAccountCredential.usingOAuth2(this, Arrays.asList(DriveScopes.DRIVE_FILE));
credential.setSelectedAccountName("accountNameHERE");
service = new Drive.Builder(AndroidHttp.newCompatibleTransport(), new GsonFactory(), credential).build();
UploadFile();
}
public void UploadFile() {
AsyncTask<Void, Long, String> task = new AsyncTask<Void, Long, String>() {
java.io.File fileContent;
FileContent mediaContent;
com.google.api.services.drive.model.File body;
com.google.api.services.drive.model.File file;
private ProgressDialog mDialog;
long mFileLen;
#Override
protected void onPreExecute() {
// TODO Auto-generated method stub
super.onPreExecute();
mDialog = new ProgressDialog(context);
mDialog.setMax(100);
mDialog.setMessage("Uploading ");
mDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
mDialog.setProgress(0);
mDialog.setButton("Cancel", new OnClickListener() {
#Override
public void onClick(DialogInterface arg0, int arg1) {
// TODO Auto-generated method stub
}
});
mDialog.show();
}
class FileUploadProgressListener implements MediaHttpUploaderProgressListener {
#Override
public void progressChanged(MediaHttpUploader uploader) throws IOException {
Log.d("Percent ", String.valueOf(uploader.getProgress()));
switch (uploader.getUploadState()) {
case INITIATION_STARTED:
System.out.println("Initiation Started");
break;
case INITIATION_COMPLETE:
System.out.println("Initiation Completed");
break;
case MEDIA_IN_PROGRESS:
System.out.println("Upload in progress");
System.out.println("Upload percentage: " + uploader.getProgress());
mDialog.setProgress((int) (uploader.getProgress()*100));
break;
case MEDIA_COMPLETE:
System.out.println("Upload Completed!");
break;
case NOT_STARTED:
System.out.println("Upload Not Started!");
break;
}
}
}
#Override
protected String doInBackground(Void... arg0) {
try {
File folder = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES
);
File UPLOAD_FILE = new File(folder, "filePathHERE");
fileContent = new File(folder, "filePathHERE");
mFileLen = fileContent.length();
InputStreamContent mediaContent2 = new InputStreamContent("application/zip", new FileInputStream(UPLOAD_FILE));
mediaContent2.setLength(UPLOAD_FILE.length());
body = new com.google.api.services.drive.model.File();
body.setTitle(fileContent.getName());
body.setMimeType("application/zip");
//String parentId = null;
//int x = Files.List.setQ("mimeType = 'application/vnd.google-apps.folder' and title = 'ShareHim'");
//body.setParents(Arrays.asList(new ParentReference().setId(uploadFile.getFileHostFolderId())));
Files.Insert mInsert = service.files().insert(body, mediaContent2);
if(mFileLen > 5 * 1024 * 1024)
{
MediaHttpUploader uploader = mInsert.getMediaHttpUploader();
uploader.setDirectUploadEnabled(false);
uploader.setChunkSize(MediaHttpUploader.MINIMUM_CHUNK_SIZE);
uploader.setProgressListener(new FileUploadProgressListener());
file = mInsert.execute();
if (file != null) {
}
}
else {
mInsert.execute();
}
} catch (UserRecoverableAuthIOException e) {
System.out.println("login error");
Log.d("Error", "not login " + e);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
protected void onPostExecute(String result) {
mDialog.dismiss();
};
};
task.execute();
}
}
Now, it is uploading by chunks, but how can we resume the upload from the last chunk sent. My first impression after watching Google Drive talks on youtube was that the resumable upload was handled automatically, but it doesn't seem to be the case.
Am I approaching this the wrong way? Is it worth considering using a DriveSyncAdapter to upload a file as an alternative? (Sorry for the bad code, this is just a test)
It looks like you are using the Java REST-ful API. If you use the Android-specific API, this is all handled for you. Just hand over the file, and the API will do resumable upload as appropriate. See creating files.