Wi-Fi P2P를 통해 서비스 제공하기

Android 2014. 3. 4. 13:45

- Wi-Fi P2P를 통해서 NSD와 같이 서비스를 검색할 수 있도록하는 방법이다.

- 이전 "Wi-Fi P2P 연결하기"와 동일한 퍼미션을 사용한다.

- 서비스를 등록하기 위해서 아래와 같이 addLocalService()를 호출한다. 아래에서 record는 클라이언트에 제공하는 데이터이다.

     private void startRegistration() {
       
//  Create a string map containing information about your service.
       
Map record = new HashMap();
        record
.put("listenport", String.valueOf(SERVER_PORT));
        record
.put("buddyname", "John Doe" + (int) (Math.random() * 1000));
        record
.put("available", "visible");

       
// Service information.  Pass it an instance name, service type
       
// _protocol._transportlayer , and the map containing
       
// information other devices will want once they connect to this one.
       
WifiP2pDnsSdServiceInfo serviceInfo =
               
WifiP2pDnsSdServiceInfo.newInstance("_test", "_presence._tcp", record);

       
// Add the local service, sending the service info, network channel,
       
// and listener that will be used to indicate success or failure of
       
// the request.
        mManager
.addLocalService(channel, serviceInfo, new ActionListener() {
           
@Override
           
public void onSuccess() {
               
// Command successful! Code isn't necessarily needed here,
               
// Unless you want to update the UI or add logging statements.
           
}

           
@Override
           
public void onFailure(int arg0) {
               
// Command failed.  Check for P2P_UNSUPPORTED, ERROR, or BUSY
           
}
       
});
   
}

- 주변에서 제공되는 서비스 검색을 요청할 때, 아래 2개의 Listener를 등록해서 Broadcast되는 서비스 정보를 저장한다.

final HashMap<String, String> buddies = new HashMap<String, String>();
...
private void discoverService() {
   
DnsSdTxtRecordListener txtListener = new DnsSdTxtRecordListener() {
       
@Override
       
/* Callback includes:
         * fullDomain: full domain name: e.g "printer._ipp._tcp.local."
         * record: TXT record dta as a map of key/value pairs.
         * device: The device running the advertised service.
         */


       
public void onDnsSdTxtRecordAvailable(
               
String fullDomain, Map

record, WifiP2pDevice device) {
               
Log.d(TAG, "DnsSdTxtRecord available -" + record.toString());
                buddies
.put(device.deviceAddress, record.get("buddyname"));
           
}
       
};
   
...
DnsSdServiceResponseListener servListener = new DnsSdServiceResponseListener() {
       
@Override
       
public void onDnsSdServiceAvailable(String instanceName, String registrationType,
               
WifiP2pDevice resourceType) {

               
// Update the device name with the human-friendly version from
               
// the DnsTxtRecord, assuming one arrived.
                resourceType
.deviceName = buddies
                       
.containsKey(resourceType.deviceAddress) ? buddies
                       
.get(resourceType.deviceAddress) : resourceType.deviceName;

               
// Add to the custom adapter defined specifically for showing
               
// wifi devices.
               
WiFiDirectServicesList fragment = (WiFiDirectServicesList) getFragmentManager()
                       
.findFragmentById(R.id.frag_peerlist);
               
WiFiDevicesAdapter adapter = ((WiFiDevicesAdapter) fragment
                       
.getListAdapter());

                adapter
.add(resourceType);
                adapter
.notifyDataSetChanged();
               
Log.d(TAG, "onBonjourServiceAvailable " + instanceName);
       
}
   
};

    mManager
.setDnsSdResponseListeners(channel, servListener, txtListener);
   
...
}

- 검색 요청은 아래 2개 메소드를 호출하면 된다.

serviceRequest = WifiP2pDnsSdServiceRequest.newInstance();
        mManager
.addServiceRequest(channel,
                serviceRequest
,
               
new ActionListener() {
                   
@Override
                   
public void onSuccess() {
                       
// Success!
                   
}

                   
@Override
                   
public void onFailure(int code) {
                       
// Command failed.  Check for P2P_UNSUPPORTED, ERROR, or BUSY
                   
}
               
});
mManager.discoverServices(channel, new ActionListener() {

           
@Override
           
public void onSuccess() {
               
// Success!
           
}

           
@Override
           
public void onFailure(int code) {
               
// Command failed.  Check for P2P_UNSUPPORTED, ERROR, or BUSY
               
if (code == WifiP2pManager.P2P_UNSUPPORTED) {
                   
Log.d(TAG, "P2P isn't supported on this device.");
               
else if(...)
                   
...
           
}
       
});


출처 : http://developer.android.com/

설정

트랙백

댓글

Wi-Fi P2P 연결하기

Android 2014. 3. 4. 13:15

- Android 4.0(API Level 14) 부터 Wi-Fi P2P 연결 관련 API를 제공한다.

- 아래와 같은 퍼미션이 필요하다.

    <uses-permission
       
android:required="true"
       
android:name="android.permission.ACCESS_WIFI_STATE"/>
   
<uses-permission
       
android:required="true"
       
android:name="android.permission.CHANGE_WIFI_STATE"/>
   
<uses-permission
       
android:required="true"
       
android:name="android.permission.INTERNET"/>

- WifiP2pManager를 아래와 같이 초기화해준다. 생성된 WifiP2pManager.Channel를 통해 Wi-Fi P2P Framework에 접속하게 된다.

@Override

Channel mChannel;

public void onCreate(Bundle savedInstanceState) {
   
....
    mManager
= (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
    mChannel
= mManager.initialize(this, getMainLooper(), null);
}

- BroadcastReceiver에 등록할 IntentFilter,이벤트를 처리할 onReceive(), 생명주기 관련 코드는 아래와 같이 작성한다.

private final IntentFilter intentFilter = new IntentFilter();
...
@Override
public void onCreate(Bundle savedInstanceState) {
   
super.onCreate(savedInstanceState);
    setContentView
(R.layout.main);

   
//  Indicates a change in the Wi-Fi P2P status.
    intentFilter
.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);

   
// Indicates a change in the list of available peers.
    intentFilter
.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);

   
// Indicates the state of Wi-Fi P2P connectivity has changed.
    intentFilter
.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);

   
// Indicates this device's details have changed.
    intentFilter
.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);

   
...
}

    @Override
   
public void onReceive(Context context, Intent intent) {
       
String action = intent.getAction();
       
if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) {
           
// Determine if Wifi P2P mode is enabled or not, alert
           
// the Activity.
           
int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1);
           
if (state == WifiP2pManager.WIFI_P2P_STATE_ENABLED) {
                activity
.setIsWifiP2pEnabled(true);
           
} else {
                activity
.setIsWifiP2pEnabled(false);
           
}
       
} else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {

           
// The peer list has changed!  We should probably do something about
           
// that.

       
} else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {

           
// Connection state changed!  We should probably do something about
           
// that.

       
} else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)) {
           
DeviceListFragment fragment = (DeviceListFragment) activity.getFragmentManager()
                   
.findFragmentById(R.id.frag_list);
            fragment
.updateThisDevice((WifiP2pDevice) intent.getParcelableExtra(
                   
WifiP2pManager.EXTRA_WIFI_P2P_DEVICE));

       
}
   
}

    /** register the BroadcastReceiver with the intent values to be matched */
   
@Override
   
public void onResume() {
       
super.onResume();
        receiver
= new WiFiDirectBroadcastReceiver(mManager, mChannel, this);
        registerReceiver
(receiver, intentFilter);
   
}

   
@Override
   
public void onPause() {
       
super.onPause();
        unregisterReceiver
(receiver);
   
}

- 주변에 있는 Peer들을 검색하기 위해서는 discoverPeers()를 아래와 같이 호출한다. 이것은 검색 시작을 의미하며, 실제로 Peer들이 검색되었을 때는 위의 onReceive()에서 판단한다.

mManager.discoverPeers(mChannel, new WifiP2pManager.ActionListener() {

       
@Override
       
public void onSuccess() {
           
// Code for when the discovery initiation is successful goes here.
           
// No services have actually been discovered yet, so this method
           
// can often be left blank.  Code for peer discovery goes in the
           
// onReceive method, detailed below.
       
}

       
@Override
       
public void onFailure(int reasonCode) {
           
// Code for when the discovery initiation fails goes here.
           
// Alert the user that something went wrong.
       
}
});

- 실제 Peer들의 목록을 가져오기 위해서는 위 onReceive()의 WIFI_P2P_PEERS_CHANGED_ACTION에서 requestPeers()를 호출해야 한다.

public void onReceive(Context context, Intent intent) {
   
...
   
else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {

       
// Request available peers from the wifi p2p manager. This is an
       
// asynchronous call and the calling activity is notified with a
       
// callback on PeerListListener.onPeersAvailable()
       
if (mManager != null) {
            mManager
.requestPeers(mChannel, peerListListener);
       
}
       
Log.d(WiFiDirectActivity.TAG, "P2P peers changed");
   
}...
}
    private List peers = new ArrayList();
   
...

   
private PeerListListener peerListListener = new PeerListListener() {
       
@Override
       
public void onPeersAvailable(WifiP2pDeviceList peerList) {

           
// Out with the old, in with the new.
            peers
.clear();
            peers
.addAll(peerList.getDeviceList());

           
// If an AdapterView is backed by this data, notify it
           
// of the change.  For instance, if you have a ListView of available
           
// peers, trigger an update.
           
((WiFiPeerListAdapter) getListAdapter()).notifyDataSetChanged();
           
if (peers.size() == 0) {
               
Log.d(WiFiDirectActivity.TAG, "No devices found");
               
return;
           
}
       
}
   
}

- Peer에 접속하기 위해서 아래와 같이 connect()를 호출한다.

    @Override
   
public void connect() {
       
// Picking the first device found on the network.
       
WifiP2pDevice device = peers.get(0);

       
WifiP2pConfig config = new WifiP2pConfig();
        config
.deviceAddress = device.deviceAddress;
        config
.wps.setup = WpsInfo.PBC;

        mManager
.connect(mChannel, config, new ActionListener() {

           
@Override
           
public void onSuccess() {
               
// WiFiDirectBroadcastReceiver will notify us. Ignore for now.
           
}

           
@Override
           
public void onFailure(int reason) {
               
Toast.makeText(WiFiDirectActivity.this, "Connect failed. Retry.",
                       
Toast.LENGTH_SHORT).show();
           
}
       
});
   
}

- 접속이 되면 관련 정보를 얻기 위해 onReceive()에서 아래와 같이 처리한다.

        ...
       
} else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {

           
if (mManager == null) {
               
return;
           
}

           
NetworkInfo networkInfo = (NetworkInfo) intent
                   
.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);

           
if (networkInfo.isConnected()) {

               
// We are connected with the other device, request connection
               
// info to find group owner IP

                mManager
.requestConnectionInfo(mChannel, connectionListener);
           
}
           
...

- 위의 WifiP2pManager.ConnectionInfoListener는 아래와 같다. 자신이 "group owner"이면 서버 역할을 해야 한다.

    @Override
   
public void onConnectionInfoAvailable(final WifiP2pInfo info) {

       
// InetAddress from WifiP2pInfo struct.
       
InetAddress groupOwnerAddress = info.groupOwnerAddress.getHostAddress());

       
// After the group negotiation, we can determine the group owner.
       
if (info.groupFormed && info.isGroupOwner) {
           
// Do whatever tasks are specific to the group owner.
           
// One common case is creating a server thread and accepting
           
// incoming connections.
       
} else if (info.groupFormed) {
           
// The other device acts as the client. In this case,
           
// you'll want to create a client thread that connects to the group
           
// owner.
       
}
   
}

- 이후에 상대의 IP에 접속하여 통신을 하면 된다.


출처 : http://developer.android.com/

설정

트랙백

댓글

Network Service Discovery(NSD) 사용하기

Android 2014. 2. 20. 11:13

- Android 4.1(API Level 16) 부터는 동일 로컬 네트워크 상에 있는 단말간의 접속을 쉽게 할 수 있도록 Network Service Discovery(NSD) API들을 제공한다.

- 로컬 네트워크에 NSD 서비스를 제공(서버 역할)하려면, 아래와 같이 NsdServiceInfo 객체를 생성해야 한다.

public void registerService(int port) {
   
// Create the NsdServiceInfo object, and populate it.
   
NsdServiceInfo serviceInfo  = new NsdServiceInfo();

   
// The name is subject to change based on conflicts
   
// with other services advertised on the same network.
    serviceInfo
.setServiceName("NsdChat");
    serviceInfo
.setServiceType("_http._tcp.");
    serviceInfo
.setPort(port);
   
....
}

- "NsdChat"은 다른 단말들에서 보여질 서비스 이름이다. 로컬 네트워크 상에 유일해야 한다. 만약, 이름이 겹치게 되면 "NsdChat (1)"과 같이 이름이 자동으로 변경된다.

- "_http._tcp"은 제공될 서비스 타입이다. 문법은 "_<protocol>._<transportlayer>"이며, IANA의 서비스 이름/포트 목록 URL에서 찾아서 입력하면된다. (신규 등록 URL)

- port는 다른 App.과 충돌할 가능성이 있기 때문에, 하드 코딩해서 넣으면 안된다. 만약, TCP를 사용한다면 아래와 같이 사용 가능한 포트번호를 가져올 수 있다.

public void initializeServerSocket() {
   
// Initialize a server socket on the next available port.
    mServerSocket
= new ServerSocket(0);

   
// Store the chosen port.
    mLocalPort
=  mServerSocket.getLocalPort();
   
...
}

- NSD를 등록할 때 필요한 Listener는 아래와 같다.

public void initializeRegistrationListener() {
    mRegistrationListener
= new NsdManager.RegistrationListener() {

       
@Override
       
public void onServiceRegistered(NsdServiceInfo NsdServiceInfo) {
           
// Save the service name.  Android may have changed it in order to
           
// resolve a conflict, so update the name you initially requested
           
// with the name Android actually used.
            mServiceName
= NsdServiceInfo.getServiceName();
       
}

       
@Override
       
public void onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
           
// Registration failed!  Put debugging code here to determine why.
       
}

       
@Override
       
public void onServiceUnregistered(NsdServiceInfo arg0) {
           
// Service has been unregistered.  This only happens when you call
           
// NsdManager.unregisterService() and pass in this listener.
       
}

       
@Override
       
public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
           
// Unregistration failed.  Put debugging code here to determine why.
       
}
   
};
}

- 이제 아래와 같이 등록하면, 서비스 제공이 시작된다. Async하게 동작하므로 위 Listener의 onServiceRegisterd()가 호출될때가 등록이 완료된 때이다.

public void registerService(int port) {
   
NsdServiceInfo serviceInfo  = new NsdServiceInfo();
    serviceInfo
.setServiceName("NsdChat");
    serviceInfo
.setServiceType("_http._tcp.");
    serviceInfo
.setPort(port);

    mNsdManager
= Context.getSystemService(Context.NSD_SERVICE);

    mNsdManager
.registerService(
            serviceInfo
, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener);
}

- 로컬 네트워크에 제공중인 NSD 서비스를 이용(클라이언트 역할)하려면, 아래와 같은 Listener를 등록하고 discoverServices() 를 호출해주면 된다. 서비스가 찾아졌을때에는 ServiceType이 맞는지, 자기 자신이 아닌지, ServiceName이 맞는지 확인해야 한다.

public void initializeDiscoveryListener() {

   
// Instantiate a new DiscoveryListener
    mDiscoveryListener
= new NsdManager.DiscoveryListener() {

       
//  Called as soon as service discovery begins.
       
@Override
       
public void onDiscoveryStarted(String regType) {
           
Log.d(TAG, "Service discovery started");
       
}

       
@Override
       
public void onServiceFound(NsdServiceInfo service) {
           
// A service was found!  Do something with it.
           
Log.d(TAG, "Service discovery success" + service);
           
if (!service.getServiceType().equals(SERVICE_TYPE)) {
               
// Service type is the string containing the protocol and
               
// transport layer for this service.
               
Log.d(TAG, "Unknown Service Type: " + service.getServiceType());
           
} else if (service.getServiceName().equals(mServiceName)) {
               
// The name of the service tells the user what they'd be
               
// connecting to. It could be "Bob's Chat App".
               
Log.d(TAG, "Same machine: " + mServiceName);
           
} else if (service.getServiceName().contains("NsdChat")){
                mNsdManager
.resolveService(service, mResolveListener);
           
}
       
}

       
@Override
       
public void onServiceLost(NsdServiceInfo service) {
           
// When the network service is no longer available.
           
// Internal bookkeeping code goes here.
           
Log.e(TAG, "service lost" + service);
       
}

       
@Override
       
public void onDiscoveryStopped(String serviceType) {
           
Log.i(TAG, "Discovery stopped: " + serviceType);
       
}

       
@Override
       
public void onStartDiscoveryFailed(String serviceType, int errorCode) {
           
Log.e(TAG, "Discovery failed: Error code:" + errorCode);
            mNsdManager
.stopServiceDiscovery(this);
       
}

       
@Override
       
public void onStopDiscoveryFailed(String serviceType, int errorCode) {
           
Log.e(TAG, "Discovery failed: Error code:" + errorCode);
            mNsdManager
.stopServiceDiscovery(this);
       
}
   
};
}
    mNsdManager.discoverServices(
        SERVICE_TYPE
, NsdManager.PROTOCOL_DNS_SD, mDiscoveryListener);

- 위 코드의 resolveService() 는 발견된 서비스에 접속을 하는 API이다. Listener는 아래와 같이 생성하여 등록한다.

public void initializeResolveListener() {
    mResolveListener
= new NsdManager.ResolveListener() {

       
@Override
       
public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {
           
// Called when the resolve fails.  Use the error code to debug.
           
Log.e(TAG, "Resolve failed" + errorCode);
       
}

       
@Override
       
public void onServiceResolved(NsdServiceInfo serviceInfo) {
           
Log.e(TAG, "Resolve Succeeded. " + serviceInfo);

           
if (serviceInfo.getServiceName().equals(mServiceName)) {
               
Log.d(TAG, "Same IP.");
               
return;
           
}
            mService
= serviceInfo;
           
int port = mService.getPort();
           
InetAddress host = mService.getHost();
       
}
   
};
}

- NSD LifeCycle 관리는 아래와 같이 해준다.

//In your application's Activity

   
@Override
   
protected void onPause() {
       
if (mNsdHelper != null) {
            mNsdHelper
.tearDown();
       
}
       
super.onPause();
   
}

   
@Override
   
protected void onResume() {
       
super.onResume();
       
if (mNsdHelper != null) {
            mNsdHelper
.registerService(mConnection.getLocalPort());
            mNsdHelper
.discoverServices();
       
}
   
}

   
@Override
   
protected void onDestroy() {
        mNsdHelper
.tearDown();
        mConnection
.tearDown();
       
super.onDestroy();
   
}

   
// NsdHelper's tearDown method
       
public void tearDown() {
        mNsdManager
.unregisterService(mRegistrationListener);
        mNsdManager
.stopServiceDiscovery(mDiscoveryListener);
   
}


출처 : http://developer.android.com/

설정

트랙백

댓글

문서를 수동 설정하여 프린터로 인쇄하기

Android 2014. 2. 13. 13:10

 PrintManager객체를 얻어서, 인쇄하기 위해서 아래와 같이 한다.

private void doPrint() {
   
// Get a PrintManager instance
   
PrintManager printManager = (PrintManager) getActivity()
           
.getSystemService(Context.PRINT_SERVICE);

   
// Set job name, which will be displayed in the print queue
   
String jobName = getActivity().getString(R.string.app_name) + " Document";

   
// Start a print job, passing in a PrintDocumentAdapter implementation
   
// to handle the generation of a print document
    printManager
.print(jobName, new MyPrintDocumentAdapter(getActivity()),
           
null); //
}

- 인쇄할 내용에 대한 정의는 PrintDocumentAdapter에서 하게 되며, 인쇄할 때의 다양한 옵션(가로/세로 모드 등)은 print()의 마지막 인자값(위 코드의 null)인  PrintAttributes을 통해서 설정할 수 있다.

PrintDocumentAdapter는 인쇄 Lifecycle에 해당하는 아래 4개의 콜백 메소드를 가지고 있다. 이 메소드들은 Main Thread에서 돌아가므로, 오래 걸리는 작업은 AsyncTask등을 이용해서 별도 Thread로 돌려야 한다.

onStart() : 인쇄 시작할때 불린다. (필수 아님)

onLayout() : 인쇄 결과물에 영향을 주는 옵션(페이지 크기, 인쇄 방향 등)을 사용자가 변경할 때 불린다. 변경된 옵션(페이지 크기 등)에 맞는 내용을 계산해서 Framework에 관련 내용(PrintDocumentInfo, 페이지수 등의 정보를 담고 있음)을 설정한다.

onWrite() : 인쇄할 페이지 내용에 대한 랜더링이 필요할때 불린다. onLayout()이 불린 후, 1번 이상 불린다.

onFinish() : 인쇄가 끝났을때 불린다. (필수 아님)

- onLayout()은 LayoutResultCallback을 통해 3종류(설정 완료, 취소, 실패)의 결과를 반환할 수 있다. 아래와 같이 구현한다.

@Override
public void onLayout(PrintAttributes oldAttributes,
                     
PrintAttributes newAttributes,
                     
CancellationSignal cancellationSignal,
                     
LayoutResultCallback callback,
                     
Bundle metadata) {
   
// Create a new PdfDocument with the requested page attributes
    mPdfDocument
= new PrintedPdfDocument(getActivity(), newAttributes);

   
// Respond to cancellation request
   
if (cancellationSignal.isCancelled() ) {
        callback
.onLayoutCancelled();
       
return;
   
}

   
// Compute the expected number of printed pages
   
int pages = computePageCount(newAttributes);

   
if (pages > 0) {
       
// Return print information to print framework
       
PrintDocumentInfo info = new PrintDocumentInfo
               
.Builder("print_output.pdf")
               
.setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
               
.setPageCount(pages);
               
.build();
       
// Content layout reflow is complete
        callback
.onLayoutFinished(info, true);
   
} else {
       
// Otherwise report an error to the print framework
        callback
.onLayoutFailed("Page count calculation failed.");
   
}
}

onLayoutFinished()의 두번째 인자값(true)은 기존의 설정에서 변경된 것이 있는지를 나타내며, 필요없는 onWrite() 호출을 막을 수 있다. 성능 향상을 위해서는 기존 설정값을 저장/비교하여 이 인자값을 잘 보내주도록 한다.

- 위 코드의 computePageCount()는 아래와 같이 구현한다.

private int computePageCount(PrintAttributes printAttributes) {
   
int itemsPerPage = 4; // default item count for portrait mode

   
MediaSize pageSize = printAttributes.getMediaSize();
   
if (!pageSize.isPortrait()) {
       
// Six items per page in landscape orientation
        itemsPerPage
= 6;
   
}

   
// Determine number of print items
   
int printItemCount = getPrintItemCount();

   
return (int) Math.ceil(printItemCount / itemsPerPage);
}

- onWrite()는 WriteResultCallback을 통해 3종류(설정 완료, 취소, 실패)의 결과를 반환할 수 있다. 아래는 PrintedPdfDocument을 사용하여 PDF 파일을 출력하는 예제이다.

@Override
public void onWrite(final PageRange[] pageRanges,
                   
final ParcelFileDescriptor destination,
                   
final CancellationSignal cancellationSignal,
                   
final WriteResultCallback callback) {
   
// Iterate over each page of the document,
   
// check if it's in the output range.
   
for (int i = 0; i < totalPages; i++) {
       
// Check to see if this page is in the output range.
       
if (containsPage(pageRanges, i)) {
           
// If so, add it to writtenPagesArray. writtenPagesArray.size()
           
// is used to compute the next output page index.
            writtenPagesArray
.append(writtenPagesArray.size(), i);
           
PdfDocument.Page page = mPdfDocument.startPage(i);

           
// check for cancellation
           
if (cancellationSignal.isCancelled()) {
                callback
.onWriteCancelled();
                mPdfDocument
.close();
                mPdfDocument
= null;
               
return;
           
}

           
// Draw page content for printing
            drawPage
(page);

           
// Rendering is complete, so page can be finalized.
            mPdfDocument
.finishPage(page);
       
}
   
}

   
// Write PDF document to file
   
try {
        mPdfDocument
.writeTo(new FileOutputStream(
                destination
.getFileDescriptor()));
   
} catch (IOException e) {
        callback
.onWriteFailed(e.toString());
       
return;
   
} finally {
        mPdfDocument
.close();
        mPdfDocument
= null;
   
}
   
PageRange[] writtenPages = computeWrittenPages();
   
// Signal the print framework the document is complete
    callback
.onWriteFinished(writtenPages);

   
...
}

- 위 코드의 drawPage()는 추가적으로 출력하고 싶은 내용을 그리는 메소드이며, 아래와 같이 구현한 수 있다. Page의 Canvas에 그릴때 종이 가장자리에 찍히지 않는 영역이 있다는 것을 고려해야 한다.

private void drawPage(PdfDocument.Page page) {
   
Canvas canvas = page.getCanvas();

   
// units are in points (1/72 of an inch)
   
int titleBaseLine = 72;
   
int leftMargin = 54;

   
Paint paint = new Paint();
    paint
.setColor(Color.BLACK);
    paint
.setTextSize(36);
    canvas
.drawText("Test Title", leftMargin, titleBaseLine, paint);

    paint
.setTextSize(11);
    canvas
.drawText("Test paragraph", leftMargin, titleBaseLine + 25, paint);

    paint
.setColor(Color.BLUE);
    canvas
.drawRect(100, 100, 172, 172, paint);
}


출처 : http://developer.android.com/

설정

트랙백

댓글