Sunday, May 26, 2019

Android PDF Write / Read using Android 9 (API level 28)

Android 9 (API level 28) introduces great new features and capabilities for users and developers. Some of the changes impacts File Write and Read. The following is a solution to the File Write / Read problem for pdf files

Code Structure 




GenericFileProvider.java 

package com.zackdawood;

import android.support.v4.content.FileProvider;

public class GenericFileProvider extends FileProvider {
}

FileDownloader.java
package com.zackdawood;

import android.util.Log;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

public class FileDownloader {

    private static final String TAG = "FileDownloader";

    private static final int MEGABYTE = 1024 * 1024;

    public static void downloadFile(String fileUrl, File directory) {
        try {
            Log.v(TAG, "downloadFile() invoked ");
            Log.v(TAG, "downloadFile() fileUrl " + fileUrl);
            Log.v(TAG, "downloadFile() directory " + directory);

            URL url = new URL(fileUrl);
            HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
            urlConnection.connect();

            InputStream inputStream = urlConnection.getInputStream();
            FileOutputStream fileOutputStream = new FileOutputStream(directory);
            int totalSize = urlConnection.getContentLength();

            byte[] buffer = new byte[MEGABYTE];
            int bufferLength = 0;
            while ((bufferLength = inputStream.read(buffer)) > 0) {
                fileOutputStream.write(buffer, 0, bufferLength);
            }
            fileOutputStream.close();
            Log.v(TAG, "downloadFile() completed ");

        } catch (FileNotFoundException e) {
            e.printStackTrace();
            Log.e(TAG, "downloadFile() error" + e.getMessage());
            Log.e(TAG, "downloadFile() error" + e.getStackTrace());
        } catch (MalformedURLException e) {
            e.printStackTrace();
            Log.e(TAG, "downloadFile() error" + e.getMessage());
            Log.e(TAG, "downloadFile() error" + e.getStackTrace());
        } catch (IOException e) {
            e.printStackTrace();
            Log.e(TAG, "downloadFile() error" + e.getMessage());
            Log.e(TAG, "downloadFile() error" + e.getStackTrace());
        }
    }
}
MainActivity.java
package com.zackdawood;

import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.FileProvider;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Toast;

import java.io.File;
import java.io.IOException;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";
    private static final String[] PERMISSIONS = {android.Manifest.permission.READ_EXTERNAL_STORAGE, android.Manifest.permission.WRITE_EXTERNAL_STORAGE};


    private static boolean hasPermissions(Context context, String... permissions) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && context != null && permissions != null) {
            for (String permission : permissions) {
                if (ActivityCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) {
                    return false;
                }
            }
        }
        return true;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.v(TAG, "onCreate() Method invoked ");

        ActivityCompat.requestPermissions(MainActivity.this, PERMISSIONS, 112);

    }

    public void request(View view) {

        ActivityCompat.requestPermissions(MainActivity.this, PERMISSIONS, 112);

    }

    public void view(View view) {
        Log.v(TAG, "view() Method invoked ");

        if (!hasPermissions(MainActivity.this, PERMISSIONS)) {

            Log.v(TAG, "download() Method DON'T HAVE PERMISSIONS ");

            Toast t = Toast.makeText(getApplicationContext(), "You don't have read access !", Toast.LENGTH_LONG);
            t.show();

        } else {
            File d = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);  // -> filename = maven.pdf
            File pdfFile = new File(d, "maven.pdf");

            Log.v(TAG, "view() Method pdfFile " + pdfFile.getAbsolutePath());

            Uri path = FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID + ".fileprovider", pdfFile);


            Log.v(TAG, "view() Method path " + path);

            Intent pdfIntent = new Intent(Intent.ACTION_VIEW);
            pdfIntent.setDataAndType(path, "application/pdf");
            pdfIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
            pdfIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

            try {
                startActivity(pdfIntent);
            } catch (ActivityNotFoundException e) {
                Toast.makeText(MainActivity.this, "No Application available to view PDF", Toast.LENGTH_SHORT).show();
            }
        }
        Log.v(TAG, "view() Method completed ");

    }

    public void download(View view) {
        Log.v(TAG, "download() Method invoked ");

        if (!hasPermissions(MainActivity.this, PERMISSIONS)) {

            Log.v(TAG, "download() Method DON'T HAVE PERMISSIONS ");

            Toast t = Toast.makeText(getApplicationContext(), "You don't have write access !", Toast.LENGTH_LONG);
            t.show();

        } else {
            Log.v(TAG, "download() Method HAVE PERMISSIONS ");

            //new DownloadFile().execute("http://maven.apache.org/maven-1.x/maven.pdf", "maven.pdf");
            new DownloadFile().execute("http://www.axmag.com/download/pdfurl-guide.pdf", "maven.pdf");

        }

        Log.v(TAG, "download() Method completed ");

    }

    private class DownloadFile extends AsyncTask<String, Void, Void> {

        @Override
        protected Void doInBackground(String... strings) {
            Log.v(TAG, "doInBackground() Method invoked ");

            String fileUrl = strings[0];   // -> http://maven.apache.org/maven-1.x/maven.pdf
            String fileName = strings[1];  // -> maven.pdf
            String extStorageDirectory = Environment.getExternalStorageDirectory().toString();
            File folder = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);

            File pdfFile = new File(folder, fileName);
            Log.v(TAG, "doInBackground() pdfFile invoked " + pdfFile.getAbsolutePath());
            Log.v(TAG, "doInBackground() pdfFile invoked " + pdfFile.getAbsoluteFile());

            try {
                pdfFile.createNewFile();
                Log.v(TAG, "doInBackground() file created" + pdfFile);

            } catch (IOException e) {
                e.printStackTrace();
                Log.e(TAG, "doInBackground() error" + e.getMessage());
                Log.e(TAG, "doInBackground() error" + e.getStackTrace());


            }
            FileDownloader.downloadFile(fileUrl, pdfFile);
            Log.v(TAG, "doInBackground() file download completed");

            return null;
        }
    }
}
AndroidManifest.xml

 <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.zackdawood">

    <uses-permission android:name="android.permission.INTERNET" />


    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"></uses-permission>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme"
        android:networkSecurityConfig="@xml/network_security_config">
        <provider
            android:name=".GenericFileProvider"
            android:authorities="${applicationId}.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/external_files"/>
        </provider>
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

</manifest>
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <Button
            android:id="@+id/button3"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="200px"
            android:onClick="download"
            android:text="@string/file_download" />


        <Button
            android:id="@+id/button4"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="200px"
            android:onClick="view"
            android:text="@string/file_view" />

        <Button
            android:id="@+id/button5"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="200px"
            android:onClick="request"
            android:text="@string/file_request" />
    </LinearLayout>


</android.support.design.widget.CoordinatorLayout>
strings.xml
<resources>
    <string name="app_name">DemoInternalDownload</string>
    <string name="action_settings">Settings</string>
    <string name="file_view">File View</string>
    <string name="file_download">File Download</string>
    <string name="file_request">Request Access Again</string>

</resources>
external_files.xml
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="external_files" path="."/>
</paths>
network_security_config.xml
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config cleartextTrafficPermitted="true">
        <domain includeSubdomains="true">maven.apache.org</domain>
        <domain includeSubdomains="true">www.axmag.com</domain>
    </domain-config>
</network-security-config>
build.gradle
apply plugin: 'com.android.application'

android {
    compileSdkVersion 28
    defaultConfig {
        applicationId "com.zackdawood"
        minSdkVersion 15
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
Prompt Screen
Output Screen

24 comments:

  1. Very Helpful Post...!!! Keep it up.....

    If you want to make custom website & application you can contact us on our Web App Development Company and Android App Development Company anytime.

    ReplyDelete
  2. i need to implement progress bar when file is downloading i tried
    many times but i failed .. can u help me .

    ReplyDelete
  3. This comment has been removed by the author.

    ReplyDelete
  4. Sofa dry cleaners in Jaipur
    Best sofa dry cleaners in Jaipur
    Best dry cleaners in Jaipur

    Sofa dry cleaners in Jaipur
    Best sofa dry cleaners in Jaipur
    Best dry cleaners in Jaipur

    ReplyDelete
  5. Dr Shri Niwash JAngir can help you to overcome anxiety attacks with his methodologies . Visit him to get best anxiety treatment

    ReplyDelete
  6. Sandeep Nunia is the best urology doctor in Jaipur. To get the best treatment for kidney stone visit Dr Sandeep Nunia

    ReplyDelete
  7. As we all know that mobile apps are the biggest requirement of firms. Protocloud Academy is a leading Android Mobile app development institute in Jaipur. Our mentors will guide you to translate software requirements into workable programming code and maintain and develop programs with the help of live project training. They will also give you in-depth concepts knowledge in Android Application Development, JAVA language, and fine use of the Latest Android Studio and deployment on Android Devices like Smart Phone or Android Tablets. After completion of the training program, our experts will give advanced training to guide students with the latest technologies. We will provide the training certificates by approved technology partners to help students crack the interview and get their dream job.
    https://www.protocloudtechnologies.com/android-app-development-course-in-jaipur/

    ReplyDelete
  8. Your work is very good and I appreciate you and hopping for some more informative posts omnichannel contact center solution

    ReplyDelete
  9. Aqurius and Gemini are considered most intelligent zodiac signs. It is said that Gemini brings intelligence to the Zodiac and have best mind of all the Zodiac.

    ReplyDelete
  10. Working as a freelancer exposes you to various work cultures and communication approaches when you deal with customers from various countries. A 9-to-5 remote java developer is cautious about taking on freelancing work because most companies do not allow their employees to do so. Eiliana is a renowned platform where you can find freelance remote java developer jobs, and this platform also assists you in maintaining secrecy.

    ReplyDelete
  11. लक्ष्मी कुबेर मंत्र कुबेर और मां लक्ष्मी का यह मंत्र जीवन की सभी प्रकार से धन को देने वाला है, कुबेर धन के अधिपति यानि धन के राजा हैं।

    ReplyDelete
  12. https://zacktutorials.blogspot.com/2019/05/android-pdf-write-read-using-android-9.html?showComment=1618972036343#c4163542603636294636

    ReplyDelete
  13. การใช้งาน singha66 สำหรับธุรกิจของคุณที่ต้องการเข้าสู่การขายสินค้าออนไลน์ การใช้งาน singha66 PG SLOT จะเป็นตัวเลือกที่ดีอย่างแน่นอนครับ ด้วยความสะดวกและคล่องตัวในการใช้งาน

    ReplyDelete