Search code examples
androidpdfandroid-contentproviderandroid-assets

How to open pdf with any pdf app from assets using ContentProvider


Firstly, I know this question asked many time and you can be sure that I read and tried all of them but i couldn't succeed sadly. I need to ask again to make clear for all newbie at android developing. BTW, i am learning from udacity. I am trying to open pdf files from assets. I found this (cw-omnibus ContentProvider) and this is very helpful but it is not dynamic and i couldn't figure out how to make this dynamic for all pdfs. Basicly, whenever i want to open a pdf via button, cardview so on, it should open using with fileprovider. Simply, i can copy and paste FileProvider with another name and i can use it. But if i have 20 pdfs, i will have lots of java file. This is like a walk istead of using bus to go to another city. I just wan to learn this how to do it. I do not know java much as well. I tried many things to take a bus but still i couldn't find a bus. I want some help. At github example, there is a "test.pdf" is a static as you see. When the intent is called "test.pdf" opened. I tired to assing variable for test.pdf and tried switch case for cardview (like button) but i couldn't call the variable from FileProvider.java and i tired bla bla bla lots of things. :(( I know i can do cardview with recyclerview but it is difficult for me right know to understand and customize so that i manually created cardviews. I am sharing all my codes below.

XML

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/colorBg"
    android:fillViewport="true"
    tools:context="com.example.tk.buzebv2.Hizmetlerimiz">

    <LinearLayout xmlns:card_view="http://schemas.android.com/apk/res-auto"
        android:id="@+id/activity_kilavuzlar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin">

        <android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
            android:id="@+id/card_clicked1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_marginBottom="@dimen/card_view_bottom"
            android:clickable="true"
            android:foreground="?android:attr/selectableItemBackground"
            android:onClick="onClick"
            card_view:cardCornerRadius="4dp">

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

                <ImageView
                    android:id="@+id/imageView2"
                    android:layout_width="match_parent"
                    android:layout_height="150dp"
                    android:scaleType="centerCrop"
                    android:src="@drawable/hizmetlerimiz1" />

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:paddingLeft="16dp"
                    android:paddingTop="16dp"
                    android:text="Uzaktan Öğretim Sınıfı"
                    android:textColor="@color/colorDefaultText"
                    android:textSize="24sp" />

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:background="@android:color/white"
                    android:gravity="left"
                    android:lineSpacingMultiplier="1.3"
                    android:padding="@dimen/activity_horizontal_margin"
                    android:text="@string/hakkimizda_aciklama1"
                    android:textColor="@color/colorDefaultText"
                    android:textSize="16sp" />


            </LinearLayout>
        </android.support.v7.widget.CardView>

        <android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
            android:id="@+id/card_clicked2"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_marginBottom="@dimen/card_view_bottom"
            android:clickable="true"
            android:foreground="?android:attr/selectableItemBackground"
            android:onClick="onClick"
            card_view:cardCornerRadius="4dp">

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

                <ImageView
                    android:layout_width="match_parent"
                    android:layout_height="150dp"
                    android:scaleType="centerCrop"
                    android:src="@drawable/hizmetlerimiz2" />

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:paddingLeft="16dp"
                    android:paddingTop="16dp"
                    android:text="Uzaktan Öğretim Hizmetleri"
                    android:textColor="@color/colorDefaultText"
                    android:textSize="24sp" />

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:background="@android:color/white"
                    android:gravity="left"
                    android:lineSpacingMultiplier="1.3"
                    android:padding="@dimen/activity_horizontal_margin"
                    android:text="@string/hakkimizda_aciklama3"
                    android:textColor="@color/colorDefaultText"
                    android:textSize="16sp" />


            </LinearLayout>
        </android.support.v7.widget.CardView>

        <android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
            android:id="@+id/card_clicked3"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_marginBottom="@dimen/card_view_bottom"
            android:clickable="true"
            android:foreground="?android:attr/selectableItemBackground"
            android:onClick="onClick"
            card_view:cardCornerRadius="4dp">

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

                <ImageView
                    android:layout_width="match_parent"
                    android:layout_height="150dp"
                    android:scaleType="centerCrop"
                    android:src="@drawable/hizmetlerimiz3" />

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:paddingLeft="16dp"
                    android:paddingTop="16dp"
                    android:text="Online Sınav Hizmetleri"
                    android:textColor="@color/colorDefaultText"
                    android:textSize="24sp" />

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:background="@android:color/white"
                    android:gravity="left"
                    android:lineSpacingMultiplier="1.3"
                    android:padding="@dimen/activity_horizontal_margin"
                    android:text="@string/hakkimizda_aciklama3"
                    android:textColor="@color/colorDefaultText"
                    android:textSize="16sp" />


            </LinearLayout>
        </android.support.v7.widget.CardView>


    </LinearLayout>
</ScrollView>

JAVA

package com.example.tk.buzebv2;

import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.CardView;
import android.util.Log;
import android.view.View;


public class Hizmetlerimiz extends AppCompatActivity implements View.OnClickListener  {

    @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_hizmetlerimiz);


        CardView b1= (CardView) findViewById(R.id.card_clicked1);
        CardView b2= (CardView) findViewById(R.id.card_clicked2);
        CardView b3= (CardView) findViewById(R.id.card_clicked3);
        b1.setOnClickListener(this);
        b2.setOnClickListener(this);
        b3.setOnClickListener(this);

    }


    public void onClick(View v) {

        int i = v.getId();
        if (i == R.id.card_toast) {
            String hizmet_01 = "hizmet_01_a201.pdf";
            startActivity(new Intent(Intent.ACTION_VIEW,
                    Uri.parse(FileProvider.CONTENT_URI
                            + hizmet_01)));

        } else if (i == R.id.card_toast1) {
            String hizmet_02 = "hizmet_02_distance.pdf";
            startActivity(new Intent(Intent.ACTION_VIEW,
                    Uri.parse(FileProvider.CONTENT_URI
                            + hizmet_02)));


        } else if (i == R.id.card_toast2) {
            String hizmet_03 = "hizmet_03_online_sinav.pdf";
            startActivity(new Intent(Intent.ACTION_VIEW,
                    Uri.parse(FileProvider.CONTENT_URI
                            + hizmet_03)));


        }

    }
}

FileProvider.java

/***
  Copyright (c) 2008-2014 CommonsWare, LLC
  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.

  From _The Busy Coder's Guide to Android Development_
    https://commonsware.com/Android
 */

package com.commonsware.android.cp.files;

import android.content.res.AssetManager;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.util.Log;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;

public class FileProvider extends AbstractFileProvider {
  public static final Uri CONTENT_URI=
      Uri.parse("content://com.commonsware.android.cp.files/");

  @Override
  public boolean onCreate() {
    File f=new File(getContext().getFilesDir(), "test.pdf");

    if (!f.exists()) {
      AssetManager assets=getContext().getResources().getAssets();

      try {
        copy(assets.open("test.pdf"), f);
      }
      catch (IOException e) {
        Log.e("FileProvider", "Exception copying from assets", e);

        return(false);
      }
    }

    return(true);
  }

  @Override
  public ParcelFileDescriptor openFile(Uri uri, String mode)
    throws FileNotFoundException {
    File root=getContext().getFilesDir();
    File f=new File(root, uri.getPath()).getAbsoluteFile();

    if (!f.getPath().startsWith(root.getPath())) {
      throw new
        SecurityException("Resolved path jumped beyond root");
    }

    if (f.exists()) {
      return(ParcelFileDescriptor.open(f, parseMode(mode)));
    }

    throw new FileNotFoundException(uri.getPath());
  }

  @Override
  protected long getDataLength(Uri uri) {
    File f=new File(getContext().getFilesDir(), uri.getPath());

    return(f.length());
  }

  // following is from ParcelFileDescriptor source code
  // Copyright (C) 2006 The Android Open Source Project
  // (even though this method was added much after 2006...)

  private static int parseMode(String mode) {
    final int modeBits;
    if ("r".equals(mode)) {
      modeBits=ParcelFileDescriptor.MODE_READ_ONLY;
    }
    else if ("w".equals(mode) || "wt".equals(mode)) {
      modeBits=
          ParcelFileDescriptor.MODE_WRITE_ONLY
              | ParcelFileDescriptor.MODE_CREATE
              | ParcelFileDescriptor.MODE_TRUNCATE;
    }
    else if ("wa".equals(mode)) {
      modeBits=
          ParcelFileDescriptor.MODE_WRITE_ONLY
              | ParcelFileDescriptor.MODE_CREATE
              | ParcelFileDescriptor.MODE_APPEND;
    }
    else if ("rw".equals(mode)) {
      modeBits=
          ParcelFileDescriptor.MODE_READ_WRITE
              | ParcelFileDescriptor.MODE_CREATE;
    }
    else if ("rwt".equals(mode)) {
      modeBits=
          ParcelFileDescriptor.MODE_READ_WRITE
              | ParcelFileDescriptor.MODE_CREATE
              | ParcelFileDescriptor.MODE_TRUNCATE;
    }
    else {
      throw new IllegalArgumentException("Bad mode '" + mode + "'");
    }
    return modeBits;
  }
}

Solution

  • The simplest thing to do would be to use my StreamProvider, which handles all of this "out of the box" for you. It is based upon the FileProvider offered by the Android support libraries, but it offers serving of files out of assets directly, without the intervening copy made by the particular sample app of mine that you looked at.

    Otherwise, the sample that you have there will handle any number of PDFs. It happens to only set up one in onCreate(). You could instead have onCreate() fork a background thread and set up as many PDFs as you want. The rest of the code in the ContentProvider would not require changing, as it just looks for files in getFilesDir() based on the filename supplied on the Uri.