Merge "Adding an API demo for how to implement printing in an app." into klp-dev
This commit is contained in:
@@ -996,6 +996,15 @@
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
<activity android:name=".app.PrintCustomContent"
|
||||||
|
android:label="@string/print_custom_content"
|
||||||
|
android:enabled="@bool/atLeastKitKat">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
<category android:name="android.intent.category.SAMPLE_CODE" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
|
||||||
<!-- Application Updating Samples -->
|
<!-- Application Updating Samples -->
|
||||||
|
|
||||||
<!-- BEGIN_INCLUDE(app_update_declaration) -->
|
<!-- BEGIN_INCLUDE(app_update_declaration) -->
|
||||||
|
|||||||
59
samples/ApiDemos/res/layout/motogp_stat_item.xml
Normal file
59
samples/ApiDemos/res/layout/motogp_stat_item.xml
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Copyright (C) 2013 The Android Open Source Project
|
||||||
|
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingStart="16dip"
|
||||||
|
android:paddingEnd="16dip"
|
||||||
|
android:minHeight="64dip"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/year"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:gravity="start"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||||
|
android:textIsSelectable="false">
|
||||||
|
</TextView>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/champion"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:textIsSelectable="false"
|
||||||
|
android:duplicateParentState="true">
|
||||||
|
</TextView>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/constructor"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:gravity="end"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||||
|
android:textIsSelectable="false">
|
||||||
|
</TextView>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
21
samples/ApiDemos/res/menu/print_custom_content.xml
Normal file
21
samples/ApiDemos/res/menu/print_custom_content.xml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Copyright (C) 2013 Google Inc.
|
||||||
|
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:id="@+id/menu_print"
|
||||||
|
android:title="@string/print"
|
||||||
|
android:showAsAction="never" />
|
||||||
|
</menu>
|
||||||
@@ -154,4 +154,209 @@
|
|||||||
<item>No.</item>
|
<item>No.</item>
|
||||||
<item>Mmm... cheese.</item>
|
<item>Mmm... cheese.</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
|
<!-- Used in app/Print/Custom Layout example -->
|
||||||
|
<string-array name="motogp_years">
|
||||||
|
<item>2012</item>
|
||||||
|
<item>2011</item>
|
||||||
|
<item>2010</item>
|
||||||
|
<item>2009</item>
|
||||||
|
<item>2008</item>
|
||||||
|
<item>2007</item>
|
||||||
|
<item>2006</item>
|
||||||
|
<item>2005</item>
|
||||||
|
<item>2004</item>
|
||||||
|
<item>2003</item>
|
||||||
|
<item>2002</item>
|
||||||
|
<item>2001</item>
|
||||||
|
<item>2000</item>
|
||||||
|
<item>1999</item>
|
||||||
|
<item>1998</item>
|
||||||
|
<item>1997</item>
|
||||||
|
<item>1996</item>
|
||||||
|
<item>1995</item>
|
||||||
|
<item>1994</item>
|
||||||
|
<item>1993</item>
|
||||||
|
<item>1992</item>
|
||||||
|
<item>1991</item>
|
||||||
|
<item>1990</item>
|
||||||
|
<item>1989</item>
|
||||||
|
<item>1988</item>
|
||||||
|
<item>1987</item>
|
||||||
|
<item>1986</item>
|
||||||
|
<item>1985</item>
|
||||||
|
<item>1984</item>
|
||||||
|
<item>1983</item>
|
||||||
|
<item>1982</item>
|
||||||
|
<item>1981</item>
|
||||||
|
<item>1980</item>
|
||||||
|
<item>1979</item>
|
||||||
|
<item>1978</item>
|
||||||
|
<item>1977</item>
|
||||||
|
<item>1976</item>
|
||||||
|
<item>1975</item>
|
||||||
|
<item>1974</item>
|
||||||
|
<item>1973</item>
|
||||||
|
<item>1972</item>
|
||||||
|
<item>1971</item>
|
||||||
|
<item>1970</item>
|
||||||
|
<item>1969</item>
|
||||||
|
<item>1968</item>
|
||||||
|
<item>1967</item>
|
||||||
|
<item>1966</item>
|
||||||
|
<item>1965</item>
|
||||||
|
<item>1964</item>
|
||||||
|
<item>1963</item>
|
||||||
|
<item>1962</item>
|
||||||
|
<item>1961</item>
|
||||||
|
<item>1960</item>
|
||||||
|
<item>1959</item>
|
||||||
|
<item>1958</item>
|
||||||
|
<item>1957</item>
|
||||||
|
<item>1956</item>
|
||||||
|
<item>1955</item>
|
||||||
|
<item>1954</item>
|
||||||
|
<item>1953</item>
|
||||||
|
<item>1952</item>
|
||||||
|
<item>1951</item>
|
||||||
|
<item>1950</item>
|
||||||
|
<item>1949</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
|
<!-- Used in app/Print/Custom Layout example -->
|
||||||
|
<string-array name="motogp_champions">
|
||||||
|
<item>Jorge Lorenzo</item>
|
||||||
|
<item>Casey Stoner</item>
|
||||||
|
<item>Jorge Lorenzo</item>
|
||||||
|
<item>Valentino Rossi</item>
|
||||||
|
<item>Valentino Rossi</item>
|
||||||
|
<item>Casey Stoner</item>
|
||||||
|
<item>Nicky Hayden</item>
|
||||||
|
<item>Valentino Rossi</item>
|
||||||
|
<item>Valentino Rossi</item>
|
||||||
|
<item>Valentino Rossi</item>
|
||||||
|
<item>Valentino Rossi</item>
|
||||||
|
<item>Valentino Rossi</item>
|
||||||
|
<item>Kenny Roberts, Jr.</item>
|
||||||
|
<item>Àlex Crivillé</item>
|
||||||
|
<item>Michael Doohan</item>
|
||||||
|
<item>Michael Doohan</item>
|
||||||
|
<item>Michael Doohan</item>
|
||||||
|
<item>Michael Doohan</item>
|
||||||
|
<item>Michael Doohan</item>
|
||||||
|
<item>Kevin Schwantz</item>
|
||||||
|
<item>Wayne Rainey</item>
|
||||||
|
<item>Wayne Rainey</item>
|
||||||
|
<item>Wayne Rainey</item>
|
||||||
|
<item>Eddie Lawson</item>
|
||||||
|
<item>Eddie Lawson</item>
|
||||||
|
<item>Wayne Gardner</item>
|
||||||
|
<item>Eddie Lawson</item>
|
||||||
|
<item>Freddie Spencer</item>
|
||||||
|
<item>Eddie Lawson</item>
|
||||||
|
<item>Freddie Spencer</item>
|
||||||
|
<item>Franco Uncini</item>
|
||||||
|
<item>Marco Lucchinelli</item>
|
||||||
|
<item>Kenny Roberts</item>
|
||||||
|
<item>Kenny Roberts</item>
|
||||||
|
<item>Kenny Roberts</item>
|
||||||
|
<item>Barry Sheene</item>
|
||||||
|
<item>Barry Sheene</item>
|
||||||
|
<item>Giacomo Agostini</item>
|
||||||
|
<item>Phil Read</item>
|
||||||
|
<item>Phil Read</item>
|
||||||
|
<item>Giacomo Agostini</item>
|
||||||
|
<item>Giacomo Agostini</item>
|
||||||
|
<item>Giacomo Agostini</item>
|
||||||
|
<item>Giacomo Agostini</item>
|
||||||
|
<item>Giacomo Agostini</item>
|
||||||
|
<item>Giacomo Agostini</item>
|
||||||
|
<item>Giacomo Agostini</item>
|
||||||
|
<item>Mike Hailwood</item>
|
||||||
|
<item>Mike Hailwood</item>
|
||||||
|
<item>Mike Hailwood</item>
|
||||||
|
<item>Mike Hailwood</item>
|
||||||
|
<item>Gary Hocking</item>
|
||||||
|
<item>John Surtees</item>
|
||||||
|
<item>John Surtees</item>
|
||||||
|
<item>John Surtees</item>
|
||||||
|
<item>Libero Liberati</item>
|
||||||
|
<item>John Surtees</item>
|
||||||
|
<item>Geoff Duke</item>
|
||||||
|
<item>Geoff Duke</item>
|
||||||
|
<item>Geoff Duke</item>
|
||||||
|
<item>Umberto Masetti</item>
|
||||||
|
<item>Geoff Duke</item>
|
||||||
|
<item>Umberto Masetti</item>
|
||||||
|
<item>Leslie Graham</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
|
<!-- Used in app/Print/Custom Layout example -->
|
||||||
|
<string-array name="motogp_constructors">
|
||||||
|
<item>Yamaha</item>
|
||||||
|
<item>Honda</item>
|
||||||
|
<item>Yamaha</item>
|
||||||
|
<item>Yamaha</item>
|
||||||
|
<item>Yamaha</item>
|
||||||
|
<item>Ducati</item>
|
||||||
|
<item>Honda</item>
|
||||||
|
<item>Yamaha</item>
|
||||||
|
<item>Yamaha</item>
|
||||||
|
<item>Honda</item>
|
||||||
|
<item>Honda</item>
|
||||||
|
<item>Honda</item>
|
||||||
|
<item>Suzuki</item>
|
||||||
|
<item>Honda</item>
|
||||||
|
<item>Honda</item>
|
||||||
|
<item>Honda</item>
|
||||||
|
<item>Honda</item>
|
||||||
|
<item>Honda</item>
|
||||||
|
<item>Honda</item>
|
||||||
|
<item>Suzuki</item>
|
||||||
|
<item>Yamaha</item>
|
||||||
|
<item>Yamaha</item>
|
||||||
|
<item>Yamaha</item>
|
||||||
|
<item>Honda</item>
|
||||||
|
<item>Yamaha</item>
|
||||||
|
<item>Honda</item>
|
||||||
|
<item>Yamaha</item>
|
||||||
|
<item>Honda</item>
|
||||||
|
<item>Yamaha</item>
|
||||||
|
<item>Honda</item>
|
||||||
|
<item>Suzuki</item>
|
||||||
|
<item>Suzuki</item>
|
||||||
|
<item>Yamaha</item>
|
||||||
|
<item>Yamaha</item>
|
||||||
|
<item>Yamaha</item>
|
||||||
|
<item>Suzuki</item>
|
||||||
|
<item>Suzuki</item>
|
||||||
|
<item>Giacomo Agostini</item>
|
||||||
|
<item>Phil Read</item>
|
||||||
|
<item>Phil Read</item>
|
||||||
|
<item>Yamaha</item>
|
||||||
|
<item>MV Agusta</item>
|
||||||
|
<item>MV Agusta</item>
|
||||||
|
<item>MV Agusta</item>
|
||||||
|
<item>MV Agusta</item>
|
||||||
|
<item>MV Agusta</item>
|
||||||
|
<item>MV Agusta</item>
|
||||||
|
<item>MV Agusta</item>
|
||||||
|
<item>MV Agusta</item>
|
||||||
|
<item>MV Agusta</item>
|
||||||
|
<item>MV Agusta</item>
|
||||||
|
<item>MV Agusta</item>
|
||||||
|
<item>MV Agusta</item>
|
||||||
|
<item>MV Agusta</item>
|
||||||
|
<item>MV Agusta</item>
|
||||||
|
<item>Gilera</item>
|
||||||
|
<item>MV Agusta</item>
|
||||||
|
<item>Gilera</item>
|
||||||
|
<item>Gilera</item>
|
||||||
|
<item>Gilera</item>
|
||||||
|
<item>Gilera</item>
|
||||||
|
<item>Norton</item>
|
||||||
|
<item>Gilera</item>
|
||||||
|
<item>AJS</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -871,6 +871,13 @@
|
|||||||
<string name="btn_toggle_tabs">Toggle tab mode</string>
|
<string name="btn_toggle_tabs">Toggle tab mode</string>
|
||||||
<string name="btn_remove_all_tabs">Remove all tabs</string>
|
<string name="btn_remove_all_tabs">Remove all tabs</string>
|
||||||
|
|
||||||
|
<!-- ================================= -->
|
||||||
|
<!-- app/print print examples strings -->
|
||||||
|
<!-- ================================= -->
|
||||||
|
|
||||||
|
<string name="print_custom_content">App/Print/Print Custom Layout</string>
|
||||||
|
<string name="print">Print</string>
|
||||||
|
|
||||||
<!-- ============================ -->
|
<!-- ============================ -->
|
||||||
<!-- graphics examples strings -->
|
<!-- graphics examples strings -->
|
||||||
<!-- ============================ -->
|
<!-- ============================ -->
|
||||||
|
|||||||
@@ -0,0 +1,562 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2013 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.example.android.apis.app;
|
||||||
|
|
||||||
|
import android.app.ListActivity;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.Configuration;
|
||||||
|
import android.graphics.pdf.PdfDocument.Page;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.CancellationSignal;
|
||||||
|
import android.os.CancellationSignal.OnCancelListener;
|
||||||
|
import android.os.ParcelFileDescriptor;
|
||||||
|
import android.print.PageRange;
|
||||||
|
import android.print.PrintAttributes;
|
||||||
|
import android.print.PrintDocumentAdapter;
|
||||||
|
import android.print.PrintDocumentInfo;
|
||||||
|
import android.print.PrintManager;
|
||||||
|
import android.print.pdf.PrintedPdfDocument;
|
||||||
|
import android.util.SparseIntArray;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.View.MeasureSpec;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.BaseAdapter;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.example.android.apis.R;
|
||||||
|
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class demonstrates how to implement custom printing support.
|
||||||
|
* <p>
|
||||||
|
* This activity shows the list of the MotoGP champions by year and
|
||||||
|
* brand. The print option in the overflow menu allows the user to
|
||||||
|
* print the content. The list list of items is laid out to such that
|
||||||
|
* it fits the options selected by the user from the UI such as page
|
||||||
|
* size. Hence, for different page sizes the printed content will have
|
||||||
|
* different page count.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* This sample demonstrates how to completely implement a {@link
|
||||||
|
* PrintDocumentAdapter} in which:
|
||||||
|
* <ul>
|
||||||
|
* <li>Layout based on the selected print options is performed.</li>
|
||||||
|
* <li>Layout work is performed only if print options change would change the content.</li>
|
||||||
|
* <li>Layout result is properly reported.</li>
|
||||||
|
* <li>Only requested pages are written.</li>
|
||||||
|
* <li>Write result is properly reported.</li>
|
||||||
|
* <li>Both Layout and write respond to cancellation.</li>
|
||||||
|
* <li>Layout and render of views is demonstrated.</li>
|
||||||
|
* </ul>
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @see PrintManager
|
||||||
|
* @see PrintDocumentAdapter
|
||||||
|
*/
|
||||||
|
public class PrintCustomContent extends ListActivity {
|
||||||
|
|
||||||
|
private static final int MILS_IN_INCH = 1000;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setListAdapter(new MotoGpStatAdapter(loadMotoGpStats(),
|
||||||
|
getLayoutInflater()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
|
super.onCreateOptionsMenu(menu);
|
||||||
|
getMenuInflater().inflate(R.menu.print_custom_content, menu);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
if (item.getItemId() == R.id.menu_print) {
|
||||||
|
print();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void print() {
|
||||||
|
PrintManager printManager = (PrintManager) getSystemService(
|
||||||
|
Context.PRINT_SERVICE);
|
||||||
|
|
||||||
|
printManager.print("MotoGP stats",
|
||||||
|
new PrintDocumentAdapter() {
|
||||||
|
private int mRenderPageWidth;
|
||||||
|
private int mRenderPageHeight;
|
||||||
|
|
||||||
|
private PrintAttributes mPrintAttributes;
|
||||||
|
private PrintDocumentInfo mDocumentInfo;
|
||||||
|
private Context mPrintContext;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLayout(final PrintAttributes oldAttributes,
|
||||||
|
final PrintAttributes newAttributes,
|
||||||
|
final CancellationSignal cancellationSignal,
|
||||||
|
final LayoutResultCallback callback,
|
||||||
|
final Bundle metadata) {
|
||||||
|
|
||||||
|
// If we are already cancelled, don't do any work.
|
||||||
|
if (cancellationSignal.isCanceled()) {
|
||||||
|
callback.onLayoutCancelled();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now we determined if the print attributes changed in a way that
|
||||||
|
// would change the layout and if so we will do a layout pass.
|
||||||
|
boolean layoutNeeded = false;
|
||||||
|
|
||||||
|
final int density = Math.max(newAttributes.getResolution().getHorizontalDpi(),
|
||||||
|
newAttributes.getResolution().getVerticalDpi());
|
||||||
|
|
||||||
|
// Note that we are using the PrintedPdfDocument class which creates
|
||||||
|
// a PDF generating canvas whose size is in points (1/72") not screen
|
||||||
|
// pixels. Hence, this canvas is pretty small compared to the screen.
|
||||||
|
// The recommended way is to layout the content in the desired size,
|
||||||
|
// in this case as large as the printer can do, and set a translation
|
||||||
|
// to the PDF canvas to shrink in. Note that PDF is a vector format
|
||||||
|
// and you will not lose data during the transformation.
|
||||||
|
|
||||||
|
// The content width is equal to the page width minus the margins times
|
||||||
|
// the horizontal printer density. This way we get the maximal number
|
||||||
|
// of pixels the printer can put horizontally.
|
||||||
|
final int marginLeft = (int) (density * (float) newAttributes.getMinMargins()
|
||||||
|
.getLeftMils() / MILS_IN_INCH);
|
||||||
|
final int marginRight = (int) (density * (float) newAttributes.getMinMargins()
|
||||||
|
.getRightMils() / MILS_IN_INCH);
|
||||||
|
final int contentWidth = (int) (density * (float) newAttributes.getMediaSize()
|
||||||
|
.getWidthMils() / MILS_IN_INCH) - marginLeft - marginRight;
|
||||||
|
if (mRenderPageWidth != contentWidth) {
|
||||||
|
mRenderPageWidth = contentWidth;
|
||||||
|
layoutNeeded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The content height is equal to the page height minus the margins times
|
||||||
|
// the vertical printer resolution. This way we get the maximal number
|
||||||
|
// of pixels the printer can put vertically.
|
||||||
|
final int marginTop = (int) (density * (float) newAttributes.getMinMargins()
|
||||||
|
.getTopMils() / MILS_IN_INCH);
|
||||||
|
final int marginBottom = (int) (density * (float) newAttributes.getMinMargins()
|
||||||
|
.getBottomMils() / MILS_IN_INCH);
|
||||||
|
final int contentHeight = (int) (density * (float) newAttributes.getMediaSize()
|
||||||
|
.getHeightMils() / MILS_IN_INCH) - marginTop - marginBottom;
|
||||||
|
if (mRenderPageHeight != contentHeight) {
|
||||||
|
mRenderPageHeight = contentHeight;
|
||||||
|
layoutNeeded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a context for resources at printer density. We will
|
||||||
|
// be inflating views to render them and would like them to use
|
||||||
|
// resources for a density the printer supports.
|
||||||
|
if (mPrintContext == null || mPrintContext.getResources()
|
||||||
|
.getConfiguration().densityDpi != density) {
|
||||||
|
Configuration configuration = new Configuration();
|
||||||
|
configuration.densityDpi = density;
|
||||||
|
mPrintContext = createConfigurationContext(
|
||||||
|
configuration);
|
||||||
|
mPrintContext.setTheme(android.R.style.Theme_Holo_Light);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no layout is needed that we did a layout at least once and
|
||||||
|
// the document info is not null, also the second argument is false
|
||||||
|
// to notify the system that the content did not change. This is
|
||||||
|
// important as if the system has some pages and the content didn't
|
||||||
|
// change the system will ask, the application to write them again.
|
||||||
|
if (!layoutNeeded) {
|
||||||
|
callback.onLayoutFinished(mDocumentInfo, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For demonstration purposes we will do the layout off the main
|
||||||
|
// thread but for small content sizes like this one it is OK to do
|
||||||
|
// that on the main thread.
|
||||||
|
|
||||||
|
// Store the data as we will layout off the main thread.
|
||||||
|
final List<MotoGpStatItem> items = ((MotoGpStatAdapter)
|
||||||
|
getListAdapter()).cloneItems();
|
||||||
|
|
||||||
|
new AsyncTask<Void, Void, PrintDocumentInfo>() {
|
||||||
|
@Override
|
||||||
|
protected void onPreExecute() {
|
||||||
|
// First register for cancellation requests.
|
||||||
|
cancellationSignal.setOnCancelListener(new OnCancelListener() {
|
||||||
|
@Override
|
||||||
|
public void onCancel() {
|
||||||
|
cancel(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Stash the attributes as we will need them for rendering.
|
||||||
|
mPrintAttributes = newAttributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected PrintDocumentInfo doInBackground(Void... params) {
|
||||||
|
try {
|
||||||
|
// Create an adapter with the stats and an inflater
|
||||||
|
// to load resources for the printer density.
|
||||||
|
MotoGpStatAdapter adapter = new MotoGpStatAdapter(items,
|
||||||
|
(LayoutInflater) mPrintContext.getSystemService(
|
||||||
|
Context.LAYOUT_INFLATER_SERVICE));
|
||||||
|
|
||||||
|
int currentPage = 0;
|
||||||
|
int pageContentHeight = 0;
|
||||||
|
int viewType = -1;
|
||||||
|
View view = null;
|
||||||
|
LinearLayout dummyParent = new LinearLayout(mPrintContext);
|
||||||
|
dummyParent.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
|
||||||
|
final int itemCount = adapter.getCount();
|
||||||
|
for (int i = 0; i < itemCount; i++) {
|
||||||
|
// Be nice and respond to cancellation.
|
||||||
|
if (isCancelled()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the next view.
|
||||||
|
final int nextViewType = adapter.getItemViewType(i);
|
||||||
|
if (viewType == nextViewType) {
|
||||||
|
view = adapter.getView(i, view, dummyParent);
|
||||||
|
} else {
|
||||||
|
view = adapter.getView(i, null, dummyParent);
|
||||||
|
}
|
||||||
|
viewType = nextViewType;
|
||||||
|
|
||||||
|
// Measure the next view
|
||||||
|
measureView(view);
|
||||||
|
|
||||||
|
// Add the height but if the view crosses the page
|
||||||
|
// boundary we will put it to the next page.
|
||||||
|
pageContentHeight += view.getMeasuredHeight();
|
||||||
|
if (pageContentHeight > mRenderPageHeight) {
|
||||||
|
pageContentHeight = view.getMeasuredHeight();
|
||||||
|
currentPage++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a document info describing the result.
|
||||||
|
PrintDocumentInfo info = new PrintDocumentInfo
|
||||||
|
.Builder("MotoGP_stats.pdf")
|
||||||
|
.setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
|
||||||
|
.setPageCount(currentPage + 1)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// We completed the layout as a result of print attributes
|
||||||
|
// change. Hence, if we are here the content changed for
|
||||||
|
// sure which is why we pass true as the second argument.
|
||||||
|
callback.onLayoutFinished(info, true);
|
||||||
|
return info;
|
||||||
|
} catch (Exception e) {
|
||||||
|
// An unexpected error, report that we failed and
|
||||||
|
// one may pass in a human readable localized text
|
||||||
|
// for what the error is if known.
|
||||||
|
callback.onLayoutFailed(null);
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(PrintDocumentInfo result) {
|
||||||
|
// Update the cached info to send it over if the next
|
||||||
|
// layout pass does not result in a content change.
|
||||||
|
mDocumentInfo = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCancelled(PrintDocumentInfo result) {
|
||||||
|
// Task was cancelled, report that.
|
||||||
|
callback.onLayoutCancelled();
|
||||||
|
}
|
||||||
|
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onWrite(final PageRange[] pages,
|
||||||
|
final ParcelFileDescriptor destination,
|
||||||
|
final CancellationSignal cancellationSignal,
|
||||||
|
final WriteResultCallback callback) {
|
||||||
|
|
||||||
|
// If we are already cancelled, don't do any work.
|
||||||
|
if (cancellationSignal.isCanceled()) {
|
||||||
|
callback.onWriteCancelled();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the data as we will layout off the main thread.
|
||||||
|
final List<MotoGpStatItem> items = ((MotoGpStatAdapter)
|
||||||
|
getListAdapter()).cloneItems();
|
||||||
|
|
||||||
|
new AsyncTask<Void, Void, Void>() {
|
||||||
|
private final SparseIntArray mWrittenPages = new SparseIntArray();
|
||||||
|
private final PrintedPdfDocument mPdfDocument = new PrintedPdfDocument(
|
||||||
|
PrintCustomContent.this, mPrintAttributes);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPreExecute() {
|
||||||
|
// First register for cancellation requests.
|
||||||
|
cancellationSignal.setOnCancelListener(new OnCancelListener() {
|
||||||
|
@Override
|
||||||
|
public void onCancel() {
|
||||||
|
cancel(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Void doInBackground(Void... params) {
|
||||||
|
// Go over all the pages and write only the requested ones.
|
||||||
|
// Create an adapter with the stats and an inflater
|
||||||
|
// to load resources for the printer density.
|
||||||
|
MotoGpStatAdapter adapter = new MotoGpStatAdapter(items,
|
||||||
|
(LayoutInflater) mPrintContext.getSystemService(
|
||||||
|
Context.LAYOUT_INFLATER_SERVICE));
|
||||||
|
|
||||||
|
int currentPage = -1;
|
||||||
|
int pageContentHeight = 0;
|
||||||
|
int viewType = -1;
|
||||||
|
View view = null;
|
||||||
|
Page page = null;
|
||||||
|
LinearLayout dummyParent = new LinearLayout(mPrintContext);
|
||||||
|
dummyParent.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
|
||||||
|
// The content is laid out and rendered in screen pixels with
|
||||||
|
// the width and height of the paper size times the print
|
||||||
|
// density but the PDF canvas size is in points which are 1/72",
|
||||||
|
// so we will scale down the content.
|
||||||
|
final float scale = Math.min(
|
||||||
|
(float) mPdfDocument.getPageContentRect().width()
|
||||||
|
/ mRenderPageWidth,
|
||||||
|
(float) mPdfDocument.getPageContentRect().height()
|
||||||
|
/ mRenderPageHeight);
|
||||||
|
|
||||||
|
final int itemCount = adapter.getCount();
|
||||||
|
for (int i = 0; i < itemCount; i++) {
|
||||||
|
// Be nice and respond to cancellation.
|
||||||
|
if (isCancelled()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the next view.
|
||||||
|
final int nextViewType = adapter.getItemViewType(i);
|
||||||
|
if (viewType == nextViewType) {
|
||||||
|
view = adapter.getView(i, view, dummyParent);
|
||||||
|
} else {
|
||||||
|
view = adapter.getView(i, null, dummyParent);
|
||||||
|
}
|
||||||
|
viewType = nextViewType;
|
||||||
|
|
||||||
|
// Measure the next view
|
||||||
|
measureView(view);
|
||||||
|
|
||||||
|
// Add the height but if the view crosses the page
|
||||||
|
// boundary we will put it to the next one.
|
||||||
|
pageContentHeight += view.getHeight();
|
||||||
|
if (currentPage < 0 || pageContentHeight > mRenderPageHeight) {
|
||||||
|
pageContentHeight = view.getHeight();
|
||||||
|
currentPage++;
|
||||||
|
// Done with the current page - finish it.
|
||||||
|
if (page != null) {
|
||||||
|
mPdfDocument.finishPage(page);
|
||||||
|
}
|
||||||
|
// If the page is requested, render it.
|
||||||
|
if (containsPage(pages, currentPage)) {
|
||||||
|
page = mPdfDocument.startPage(currentPage);
|
||||||
|
page.getCanvas().scale(scale, scale);
|
||||||
|
// Keep track which pages are written.
|
||||||
|
mWrittenPages.append(mWrittenPages.size(), currentPage);
|
||||||
|
} else {
|
||||||
|
page = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the current view is on a requested page, render it.
|
||||||
|
if (page != null) {
|
||||||
|
// Layout an render the content.
|
||||||
|
view.layout(0, 0, view.getMeasuredWidth(),
|
||||||
|
view.getMeasuredHeight());
|
||||||
|
view.draw(page.getCanvas());
|
||||||
|
// Move the canvas for the next view.
|
||||||
|
page.getCanvas().translate(0, view.getHeight());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Done with the last page.
|
||||||
|
if (page != null) {
|
||||||
|
mPdfDocument.finishPage(page);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the data and return success or failure.
|
||||||
|
try {
|
||||||
|
mPdfDocument.writeTo(new FileOutputStream(
|
||||||
|
destination.getFileDescriptor()));
|
||||||
|
// Compute which page ranges were written based on
|
||||||
|
// the bookkeeping we maintained.
|
||||||
|
PageRange[] pageRanges = computeWrittenPageRanges(mWrittenPages);
|
||||||
|
callback.onWriteFinished(pageRanges);
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
callback.onWriteFailed(null);
|
||||||
|
} finally {
|
||||||
|
mPdfDocument.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCancelled(Void result) {
|
||||||
|
// Task was cancelled, report that.
|
||||||
|
callback.onWriteCancelled();
|
||||||
|
mPdfDocument.close();
|
||||||
|
}
|
||||||
|
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void measureView(View view) {
|
||||||
|
final int widthMeasureSpec = ViewGroup.getChildMeasureSpec(
|
||||||
|
MeasureSpec.makeMeasureSpec(mRenderPageWidth,
|
||||||
|
MeasureSpec.EXACTLY), 0, view.getLayoutParams().width);
|
||||||
|
final int heightMeasureSpec = ViewGroup.getChildMeasureSpec(
|
||||||
|
MeasureSpec.makeMeasureSpec(mRenderPageHeight,
|
||||||
|
MeasureSpec.EXACTLY), 0, view.getLayoutParams().height);
|
||||||
|
view.measure(widthMeasureSpec, heightMeasureSpec);
|
||||||
|
}
|
||||||
|
|
||||||
|
private PageRange[] computeWrittenPageRanges(SparseIntArray writtenPages) {
|
||||||
|
List<PageRange> pageRanges = new ArrayList<PageRange>();
|
||||||
|
|
||||||
|
int start = -1;
|
||||||
|
int end = -1;
|
||||||
|
final int writtenPageCount = writtenPages.size();
|
||||||
|
for (int i = 0; i < writtenPageCount; i++) {
|
||||||
|
if (start < 0) {
|
||||||
|
start = writtenPages.valueAt(i);
|
||||||
|
}
|
||||||
|
int oldEnd = end = start;
|
||||||
|
while (i < writtenPageCount && (end - oldEnd) <= 1) {
|
||||||
|
oldEnd = end;
|
||||||
|
end = writtenPages.valueAt(i);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
PageRange pageRange = new PageRange(start, end);
|
||||||
|
pageRanges.add(pageRange);
|
||||||
|
start = end = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
PageRange[] pageRangesArray = new PageRange[pageRanges.size()];
|
||||||
|
pageRanges.toArray(pageRangesArray);
|
||||||
|
return pageRangesArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean containsPage(PageRange[] pageRanges, int page) {
|
||||||
|
final int pageRangeCount = pageRanges.length;
|
||||||
|
for (int i = 0; i < pageRangeCount; i++) {
|
||||||
|
if (pageRanges[i].getStart() <= page
|
||||||
|
&& pageRanges[i].getEnd() >= page) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<MotoGpStatItem> loadMotoGpStats() {
|
||||||
|
String[] years = getResources().getStringArray(R.array.motogp_years);
|
||||||
|
String[] champions = getResources().getStringArray(R.array.motogp_champions);
|
||||||
|
String[] constructors = getResources().getStringArray(R.array.motogp_constructors);
|
||||||
|
|
||||||
|
List<MotoGpStatItem> items = new ArrayList<MotoGpStatItem>();
|
||||||
|
|
||||||
|
final int itemCount = years.length;
|
||||||
|
for (int i = 0; i < itemCount; i++) {
|
||||||
|
MotoGpStatItem item = new MotoGpStatItem();
|
||||||
|
item.year = years[i];
|
||||||
|
item.champion = champions[i];
|
||||||
|
item.constructor = constructors[i];
|
||||||
|
items.add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class MotoGpStatItem {
|
||||||
|
String year;
|
||||||
|
String champion;
|
||||||
|
String constructor;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MotoGpStatAdapter extends BaseAdapter {
|
||||||
|
private final List<MotoGpStatItem> mItems;
|
||||||
|
private final LayoutInflater mInflater;
|
||||||
|
|
||||||
|
public MotoGpStatAdapter(List<MotoGpStatItem> items, LayoutInflater inflater) {
|
||||||
|
mItems = items;
|
||||||
|
mInflater = inflater;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<MotoGpStatItem> cloneItems() {
|
||||||
|
return new ArrayList<MotoGpStatItem>(mItems);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getCount() {
|
||||||
|
return mItems.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getItem(int position) {
|
||||||
|
return mItems.get(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getItemId(int position) {
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View getView(int position, View convertView, ViewGroup parent) {
|
||||||
|
if (convertView == null) {
|
||||||
|
convertView = mInflater.inflate(R.layout.motogp_stat_item, parent, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
MotoGpStatItem item = (MotoGpStatItem) getItem(position);
|
||||||
|
|
||||||
|
TextView yearView = (TextView) convertView.findViewById(R.id.year);
|
||||||
|
yearView.setText(item.year);
|
||||||
|
|
||||||
|
TextView championView = (TextView) convertView.findViewById(R.id.champion);
|
||||||
|
championView.setText(item.champion);
|
||||||
|
|
||||||
|
TextView constructorView = (TextView) convertView.findViewById(R.id.constructor);
|
||||||
|
constructorView.setText(item.constructor);
|
||||||
|
|
||||||
|
return convertView;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user