'How to download file from Google Drive with link in Android?

I want to download file from google drive for which I am using dependency that is compile 'com.google.android.gms:play-services:8.4.0' and using this I am able to get link from meta data from below example.

mFileId = (DriveId) data.getParcelableExtra(
                        OpenFileActivityBuilder.EXTRA_RESPONSE_DRIVE_ID);


                final DriveFile file = Drive.DriveApi.getFile(mGoogleApiClient, mFileId);


                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        // DO your work here
                        DriveResource.MetadataResult mdRslt = file.getMetadata(mGoogleApiClient).await();
                        if (mdRslt != null && mdRslt.getStatus().isSuccess()) {
                           String  link = mdRslt.getMetadata().getWebContentLink();
                            String name=mdRslt.getMetadata().getTitle();
                            Log.d("LINK", link);
                            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && getApplication().checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED)
                            {
                                requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
                            }
                            else
                            {

                              new  get_download_data(link,name).execute();
                            }
                        }
                    }
                }).start();
                }

After getting the link from Google Drive, I am calling async task to download that file from link. So my problem is when I am downloading file, it's not opening. After checking and debugging, I found that my files was not downloading properly.

For example, I have file name abc.pdf and the size is 400kb. I downloaded on my sdcard but abc.pdf is 56 kb only. I am using below code for downloading. I don't know where I was doing wrong. Please help. Thanks.

 public class get_download_data extends AsyncTask<Void,Void,String>
{
    File apkStorage = null;
    File outputFile = null;
    String link1="";
    String name1="";

    public get_database_data(String link, String name) {
        this.link1=link;
        this.name1=name;
    }
    protected void onPreExecute() {
        super.onPreExecute();



    }
    @Override
    protected String doInBackground(Void... params) {

        try {

            URL url = new URL(link1);//Create Download URl

            HttpURLConnection c = (HttpURLConnection) url.openConnection();//Open Url Connection
            c.setRequestMethod("GET");//Set Request Method to "GET" since we are grtting data


            c.setDoInput(true);

            c.connect();//connect the URL Connection

            //If Connection response is not OK then show Logs
            if (c.getResponseCode() != HttpURLConnection.HTTP_OK) {
                Log.e(TAG, "Server returned HTTP " + c.getResponseCode()
                        + " " + c.getResponseMessage());

            }else{
                Log.e(TAG, "Server returned HTTP " + c.getResponseCode()
                        + " " + c.getResponseMessage());
            }


            //Get File if SD card is present
            if (new CheckForSDCard().isSDCardPresent()) {

                apkStorage = new File(
                        Environment.getExternalStorageDirectory() + "/"
                                + "checkdb");
            } else
                Toast.makeText(getApplication(), "Oops!! There is no SD Card.", Toast.LENGTH_SHORT).show();

            //If File is not present create directory
            if (!apkStorage.exists()) {
                apkStorage.mkdir();
                Log.e(TAG, "Directory Created.");
            }

            outputFile = new File(apkStorage, name1);//Create Output file in Main File

            //Create New File if not present
            if (!outputFile.exists()) {
                outputFile.createNewFile();
                Log.e(TAG, "File Created");
            }

            OutputStream fos = new FileOutputStream(outputFile);//Get OutputStream for NewFile Location

            InputStream is = c.getInputStream();//Get InputStream for connection
            BufferedInputStream inStream = new BufferedInputStream(is, 1024);

            byte[] buffer = new byte[1024];//Set buffer type
            int len1 = 0;//init length
            while ((len1 = inStream.read(buffer)) != -1) {
                fos.write(buffer, 0, len1);//Write new file
            }

            //Close all connection after doing task
            fos.flush();
            fos.close();
            is.close();

        } catch (Exception e) {

            //Read exception if something went wrong
            e.printStackTrace();
            outputFile = null;
            Log.e(TAG, "Download Error Exception " + e.getMessage());
        }
        return null;
    }

    @Override
    protected void onPostExecute(String result_1) {
        super.onPostExecute(result_1);
        String downlodepath = Environment.getExternalStorageState()+"/"+name1;
        Log.e("Sdpath",""+imagePath);
        Toast.makeText(getApplication(), "download"+downlodepath, Toast.LENGTH_SHORT).show();

    }
}

I found this link here but some how I don't have idea how to implement this. Please let me know where I was wrong. Thanks.



Solution 1:[1]

After some work around and trying so many examples, I found the answer for this. Here are the dependencies which I have added in my project:

compile 'com.google.android.gms:play-services-auth:11.8.0'
compile 'com.google.android.gms:play-services-drive:11.8.0'

Here is my manifest file:

<?xml version="1.0" encoding="utf-8"?>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

<application
    android:allowBackup="false"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">

    <activity android:name=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

    <meta-data
        android:name="com.google.android.geo.API_KEY"
        android:value="@string/google_api_key" />

    <meta-data
        android:name="com.google.android.gms.version"
        android:value="@integer/google_play_services_version" />
</application>

</manifest>

Here is my MainActivity:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

private ActivityMainBinding binding;

private static final String TAG = "Google drive";
private static final String SIGN_IN = "Sign In";
private static final String DOWNLOAD_FILE = "Download file";

private static final int REQUEST_CODE_SIGN_IN = 0;
private static final int REQUEST_CODE_OPEN_ITEM = 1;
private static final int REQUEST_WRITE_STORAGE = 112;


private GoogleSignInAccount signInAccount;
private Set<Scope> requiredScopes;
private DriveClient mDriveClient;
private DriveResourceClient mDriveResourceClient;

private OpenFileActivityOptions openOptions;
private TaskCompletionSource<DriveId> mOpenItemTaskSource;
private File storageDir;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    binding = DataBindingUtil.setContentView(this, R.layout.activity_main);

    initialize();
    requestPermission();
    signInAccount = GoogleSignIn.getLastSignedInAccount(this);

    binding.btnSubmit.setOnClickListener(this);
    if (signInAccount != null && signInAccount.getGrantedScopes().containsAll(requiredScopes)) {
        initializeDriveClient(signInAccount);
        binding.btnSubmit.setText(DOWNLOAD_FILE);
    } else {
        binding.btnSubmit.setText(SIGN_IN);
    }

}

private void showMessage(String message) {
    Toast.makeText(this, message, Toast.LENGTH_LONG).show();
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    switch (requestCode) {
        case REQUEST_CODE_SIGN_IN:
            if (resultCode == RESULT_OK) {
                Task<GoogleSignInAccount> getAccountTask = GoogleSignIn.getSignedInAccountFromIntent(data);
                if (getAccountTask.isSuccessful()) {
                    initializeDriveClient(getAccountTask.getResult());
                    showMessage("Sign in successfully.");
                    binding.btnSubmit.setText(DOWNLOAD_FILE);
                } else {
                    showMessage("Sign in failed.");
                }
            } else {
                showMessage("Sign in failed.");
            }
            break;
        case REQUEST_CODE_OPEN_ITEM:
            if (resultCode == RESULT_OK) {
                DriveId driveId = data.getParcelableExtra(OpenFileActivityOptions.EXTRA_RESPONSE_DRIVE_ID);
                mOpenItemTaskSource.setResult(driveId);
            } else {
                mOpenItemTaskSource.setException(new RuntimeException("Unable to open file"));
            }
            break;
    }
}


private void initialize() {

    requiredScopes = new HashSet<>(2);
    requiredScopes.add(Drive.SCOPE_FILE);
    requiredScopes.add(Drive.SCOPE_APPFOLDER);

    openOptions = new OpenFileActivityOptions.Builder()
            .setSelectionFilter(Filters.eq(SearchableField.MIME_TYPE, "application/pdf"))
            .setActivityTitle("Select file")
            .build();
}

private void initializeDriveClient(GoogleSignInAccount signInAccount) {
    mDriveClient = Drive.getDriveClient(getApplicationContext(), signInAccount);
    mDriveResourceClient = Drive.getDriveResourceClient(getApplicationContext(), signInAccount);
}

@Override
public void onClick(View view) {
    if (view.getId() == R.id.btnSubmit) {
        String text = (String) ((Button) view).getText();
        if (text.equals(SIGN_IN)) {
            signIn();
        } else {
            onDriveClientReady();
        }
    }
}

private void signIn() {
    GoogleSignInOptions signInOptions = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
            .requestScopes(Drive.SCOPE_FILE)
            .requestScopes(Drive.SCOPE_APPFOLDER)
            .build();
    GoogleSignInClient googleSignInClient = GoogleSignIn.getClient(this, signInOptions);
    startActivityForResult(googleSignInClient.getSignInIntent(), REQUEST_CODE_SIGN_IN);
}

private void onDriveClientReady() {
    mOpenItemTaskSource = new TaskCompletionSource<>();
    mDriveClient.newOpenFileActivityIntentSender(openOptions)
            .continueWith(new Continuation<IntentSender, Void>() {
                @Override
                public Void then(@NonNull Task<IntentSender> task) throws Exception {
                    startIntentSenderForResult(
                            task.getResult(), REQUEST_CODE_OPEN_ITEM, null, 0, 0, 0);
                    return null;
                }
            });

    Task<DriveId> tasks = mOpenItemTaskSource.getTask();
    tasks.addOnSuccessListener(this,
            new OnSuccessListener<DriveId>() {
                @Override
                public void onSuccess(DriveId driveId) {
                    retrieveContents(driveId.asDriveFile());
                }
            })
            .addOnFailureListener(this, new OnFailureListener() {
                @Override
                public void onFailure(@NonNull Exception e) {
                    showMessage("File not selected.");
                }
            });
}

private void retrieveContents(final DriveFile file) {
    // [START open_file]
    final Task<DriveContents> openFileTask = mDriveResourceClient.openFile(file, DriveFile.MODE_READ_ONLY);
    // [END open_file]
    // [START read_contents]
    openFileTask.continueWithTask(new Continuation<DriveContents, Task<Void>>() {
        @Override
        public Task<Void> then(@NonNull Task<DriveContents> task) throws Exception {
            DriveContents contents = task.getResult();

            Log.v(TAG, "File name : " + contents.toString());
            if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
                InputStream input = contents.getInputStream();

                try {
                    File file = new File(getExternalFilesDir(null), "umesh.pdf");
                    Log.v(TAG, storageDir + "");
                    OutputStream output = new FileOutputStream(file);
                    try {
                        try {
                            byte[] buffer = new byte[4 * 1024]; // or other buffer size
                            int read;

                            while ((read = input.read(buffer)) != -1) {
                                output.write(buffer, 0, read);
                            }
                            output.flush();
                        } finally {
                            output.close();
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                } finally {
                    try {
                        input.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            showMessage("Download file successfully.");
            return mDriveResourceClient.discardContents(contents);
        }
    })
            .addOnFailureListener(new OnFailureListener() {
                @Override
                public void onFailure(@NonNull Exception e) {
                    showMessage("Unable to download file.");
                }
            });
    // [END read_contents]
}

private void requestPermission() {
    String dirPath = getFilesDir().getAbsolutePath() + File.separator + "PDF";
    storageDir = new File(dirPath);
    if (!storageDir.exists())
        storageDir.mkdirs();}}

And here is the string file for the API key:

<resources>
    <string name="app_name">GoogleDriveDemo</string>

    <string name="google_api_key">"your-key"</string>
</resources>

Solution 2:[2]

The way I use webContentLink when downloading a file in Drive API is to open a new browser window in Javascript.

var webcontentlink = 'https://docs.google.com/a/google.com/uc?id='+fileId+'&export=download'
window.open( webcontentlink);

I'd suggest you do that in Android like the one mentioned in this post:

Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(webcontentlink));
startActivity(browserIntent);

Solution 3:[3]

For someone who still stuck in this problem. Somehow the download URL cannot be returned right after driveResourceClient.createFile()

driveResourceClient?.createFile(appFolder, metadataChangeSet, contents)
            }?.addOnSuccessListener(this
            ) { driverFile -> //we cannot get webContentLink here }

At this time we can only get the file name (this one may already defined)

In my case, I don't really need the URL right after upload but when user click a copy URL button

driveResourceClient?.query(Query.Builder()
                .addFilter(Filters.eq(SearchableField.TITLE, sharedFileName))
                .build())
                ?.addOnSuccessListener {
                    url = it.first().webContentLink
                }
                ?.{
                }

I've just success with this.

Solution 4:[4]

You can easily do that using chrome custom tabs just paste the url in the custom tabs and it will show the drive website and one can download the file Refer this official documentation for chrome custom tabs https://developer.chrome.com/multidevice/android/customtabs it's an really superb feature and a best alternative for webview

Solution 5:[5]

Simplest way to download a file through Google Drive URL in Android is by navigating to browser. In this way we can download any data like .apk, .mp4, .txt..

In kotlin

val driveIntent = Intent(Intent.ACTION_VIEW, Uri.parse(DRIVE_URL))//Link can have any data link .apk, .mp4 ..
val browserChooserIntent = Intent.createChooser(driveIntent, "Choose browser")
startActivity(browserChooserIntent)

enter image description here

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Pang
Solution 2 noogui
Solution 3 Ven
Solution 4 Harsh Kothari
Solution 5 Rohit S