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