1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.example.android.pdfrendererbasic;
18 
19 import android.app.Activity;
20 import android.app.Fragment;
21 import android.content.Context;
22 import android.graphics.Bitmap;
23 import android.graphics.pdf.PdfRenderer;
24 import android.os.Bundle;
25 import android.os.ParcelFileDescriptor;
26 import android.view.LayoutInflater;
27 import android.view.View;
28 import android.view.ViewGroup;
29 import android.widget.Button;
30 import android.widget.ImageView;
31 import android.widget.Toast;
32 
33 import java.io.File;
34 import java.io.FileOutputStream;
35 import java.io.IOException;
36 import java.io.InputStream;
37 
38 /**
39  * This fragment has a big {@ImageView} that shows PDF pages, and 2 {@link android.widget.Button}s to move between
40  * pages. We use a {@link android.graphics.pdf.PdfRenderer} to render PDF pages as {@link android.graphics.Bitmap}s.
41  */
42 public class PdfRendererBasicFragment extends Fragment implements View.OnClickListener {
43 
44     /**
45      * Key string for saving the state of current page index.
46      */
47     private static final String STATE_CURRENT_PAGE_INDEX = "current_page_index";
48 
49     /**
50      * The filename of the PDF.
51      */
52     private static final String FILENAME = "sample.pdf";
53 
54     /**
55      * File descriptor of the PDF.
56      */
57     private ParcelFileDescriptor mFileDescriptor;
58 
59     /**
60      * {@link android.graphics.pdf.PdfRenderer} to render the PDF.
61      */
62     private PdfRenderer mPdfRenderer;
63 
64     /**
65      * Page that is currently shown on the screen.
66      */
67     private PdfRenderer.Page mCurrentPage;
68 
69     /**
70      * {@link android.widget.ImageView} that shows a PDF page as a {@link android.graphics.Bitmap}
71      */
72     private ImageView mImageView;
73 
74     /**
75      * {@link android.widget.Button} to move to the previous page.
76      */
77     private Button mButtonPrevious;
78 
79     /**
80      * {@link android.widget.Button} to move to the next page.
81      */
82     private Button mButtonNext;
83 
PdfRendererBasicFragment()84     public PdfRendererBasicFragment() {
85     }
86 
87     @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)88     public View onCreateView(LayoutInflater inflater, ViewGroup container,
89                              Bundle savedInstanceState) {
90         return inflater.inflate(R.layout.fragment_pdf_renderer_basic, container, false);
91     }
92 
93     @Override
onViewCreated(View view, Bundle savedInstanceState)94     public void onViewCreated(View view, Bundle savedInstanceState) {
95         super.onViewCreated(view, savedInstanceState);
96         // Retain view references.
97         mImageView = (ImageView) view.findViewById(R.id.image);
98         mButtonPrevious = (Button) view.findViewById(R.id.previous);
99         mButtonNext = (Button) view.findViewById(R.id.next);
100         // Bind events.
101         mButtonPrevious.setOnClickListener(this);
102         mButtonNext.setOnClickListener(this);
103         // Show the first page by default.
104         int index = 0;
105         // If there is a savedInstanceState (screen orientations, etc.), we restore the page index.
106         if (null != savedInstanceState) {
107             index = savedInstanceState.getInt(STATE_CURRENT_PAGE_INDEX, 0);
108         }
109         showPage(index);
110     }
111 
112     @Override
onAttach(Activity activity)113     public void onAttach(Activity activity) {
114         super.onAttach(activity);
115         try {
116             openRenderer(activity);
117         } catch (IOException e) {
118             e.printStackTrace();
119             Toast.makeText(activity, "Error! " + e.getMessage(), Toast.LENGTH_SHORT).show();
120             activity.finish();
121         }
122     }
123 
124     @Override
onDetach()125     public void onDetach() {
126         try {
127             closeRenderer();
128         } catch (IOException e) {
129             e.printStackTrace();
130         }
131         super.onDetach();
132     }
133 
134     @Override
onSaveInstanceState(Bundle outState)135     public void onSaveInstanceState(Bundle outState) {
136         super.onSaveInstanceState(outState);
137         if (null != mCurrentPage) {
138             outState.putInt(STATE_CURRENT_PAGE_INDEX, mCurrentPage.getIndex());
139         }
140     }
141 
142     /**
143      * Sets up a {@link android.graphics.pdf.PdfRenderer} and related resources.
144      */
openRenderer(Context context)145     private void openRenderer(Context context) throws IOException {
146         // In this sample, we read a PDF from the assets directory.
147         File file = new File(context.getCacheDir(), FILENAME);
148         if (!file.exists()) {
149             // Since PdfRenderer cannot handle the compressed asset file directly, we copy it into
150             // the cache directory.
151             InputStream asset = context.getAssets().open(FILENAME);
152             FileOutputStream output = new FileOutputStream(file);
153             final byte[] buffer = new byte[1024];
154             int size;
155             while ((size = asset.read(buffer)) != -1) {
156                 output.write(buffer, 0, size);
157             }
158             asset.close();
159             output.close();
160         }
161         mFileDescriptor = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
162         // This is the PdfRenderer we use to render the PDF.
163         mPdfRenderer = new PdfRenderer(mFileDescriptor);
164     }
165 
166     /**
167      * Closes the {@link android.graphics.pdf.PdfRenderer} and related resources.
168      *
169      * @throws java.io.IOException When the PDF file cannot be closed.
170      */
closeRenderer()171     private void closeRenderer() throws IOException {
172         if (null != mCurrentPage) {
173             mCurrentPage.close();
174         }
175         mPdfRenderer.close();
176         mFileDescriptor.close();
177     }
178 
179     /**
180      * Shows the specified page of PDF to the screen.
181      *
182      * @param index The page index.
183      */
showPage(int index)184     private void showPage(int index) {
185         if (mPdfRenderer.getPageCount() <= index) {
186             return;
187         }
188         // Make sure to close the current page before opening another one.
189         if (null != mCurrentPage) {
190             mCurrentPage.close();
191         }
192         // Use `openPage` to open a specific page in PDF.
193         mCurrentPage = mPdfRenderer.openPage(index);
194         // Important: the destination bitmap must be ARGB (not RGB).
195         Bitmap bitmap = Bitmap.createBitmap(mCurrentPage.getWidth(), mCurrentPage.getHeight(),
196                 Bitmap.Config.ARGB_8888);
197         // Here, we render the page onto the Bitmap.
198         // To render a portion of the page, use the second and third parameter. Pass nulls to get
199         // the default result.
200         // Pass either RENDER_MODE_FOR_DISPLAY or RENDER_MODE_FOR_PRINT for the last parameter.
201         mCurrentPage.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY);
202         // We are ready to show the Bitmap to user.
203         mImageView.setImageBitmap(bitmap);
204         updateUi();
205     }
206 
207     /**
208      * Updates the state of 2 control buttons in response to the current page index.
209      */
updateUi()210     private void updateUi() {
211         int index = mCurrentPage.getIndex();
212         int pageCount = mPdfRenderer.getPageCount();
213         mButtonPrevious.setEnabled(0 != index);
214         mButtonNext.setEnabled(index + 1 < pageCount);
215         getActivity().setTitle(getString(R.string.app_name_with_index, index + 1, pageCount));
216     }
217 
218     /**
219      * Gets the number of pages in the PDF. This method is marked as public for testing.
220      *
221      * @return The number of pages.
222      */
getPageCount()223     public int getPageCount() {
224         return mPdfRenderer.getPageCount();
225     }
226 
227     @Override
onClick(View view)228     public void onClick(View view) {
229         switch (view.getId()) {
230             case R.id.previous: {
231                 // Move to the previous page
232                 showPage(mCurrentPage.getIndex() - 1);
233                 break;
234             }
235             case R.id.next: {
236                 // Move to the next page
237                 showPage(mCurrentPage.getIndex() + 1);
238                 break;
239             }
240         }
241     }
242 
243 }
244