Downloader Library 的使用範例如下

Declaring user permissions

AndroidManifest.xml 宣告如下:

<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.test.playapkexpansiontest"
android:versionCode="2"
android:versionName="1.1" >

<uses-sdk
android:minSdkVersion="16"
android:targetSdkVersion="20" />

<!-- Required to access Google Play Licensing -->
<uses-permission android:name="com.android.vending.CHECK_LICENSE" />

<!-- Required to download files from Google Play -->
<uses-permission android:name="android.permission.INTERNET" />

<!-- Required to keep CPU alive while downloading files (NOT to keep screen awake) -->
<uses-permission android:name="android.permission.WAKE_LOCK" />

<!-- Required to poll the state of the network connection and respond to changes -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

<!-- Required to check whether Wi-Fi is enabled -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>

<!-- Required to read and write the expansion files on shared storage -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity android:name=".MainActivity"
    android:label="@string/app_name" >
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>
<service android:name=".SampleDownloaderService" />
<receiver android:name=".SampleAlarmReceiver" />
</application>

</manifest>

 

Implementing the downloader service

你必須實作一個 Service 去繼承 DownloaderService,並覆寫以下三個 method:

  1. getPublicKey():回傳一組 Base64-encoded RSA public key,可以從你的 Google Play Developer Console Publisher 帳號中取得。
    未命名    
  2. getSALT():回傳一組 rondom bytes array,供 licensing Policy 使用。
  3. getAlarmReceiverClassName():回傳自定義的 BoradcastReceiver 的 Class Name,用以處理異常情形發生時作錯誤處理。

SampleDownloaderService.java 內容如下:

package com.test.playapkexpansiontest;

import com.google.android.vending.expansion.downloader.impl.DownloaderService;

public class SampleDownloaderService extends DownloaderService {

    // stuff for LVL -- MODIFY FOR YOUR APPLICATION!
    public static final String BASE64_PUBLIC_KEY = "Your_LVL_Key";

    // used by the preference obfuscater
    public static final byte[] SALT = new byte[] {
        1, 43, -12, -1, 54, 98, -100, -12, 43, 2, -8,
        -4, 9, 5, -106, -108, -33, 45, -1, 84
    };

    @Override
    public String getPublicKey() {
        return BASE64_PUBLIC_KEY;
    }


    @Override
    public byte[] getSALT() {
        return SALT;
    }

    @Override
    public String getAlarmReceiverClassName() {
        return SampleAlarmReceiver.class.getName();
    }

}

 

Implementing the alarm receiver

為了去監控 downloader process 與必要時重啟 download process,DownloaderService 會去排程 RTC_WAKEUP alarm 發出 intent 通知你的程式,因此你必須實作一個 BroadcastReceiver ,並覆寫 onReceive() 去呼叫 Downloader Library 的 DownloaderClientMarshaller.startDownloadServiceIfRequired() API。

SampleAlarmReceiver.java 內容如下:

package com.test.playapkexpansiontest;

import com.google.android.vending.expansion.downloader.DownloaderClientMarshaller;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager.NameNotFoundException;


public class SampleAlarmReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        try {
            DownloaderClientMarshaller.startDownloadServiceIfRequired(
                    context, intent, SampleDownloaderService.class);
        } catch (NameNotFoundException e) {
            e.printStackTrace();
        }
    }

}

 

Starting the download

你的 Main Activity 必須去驗證是否 expansion file 有在 device 上,如果沒有則啟動 download 程序。

啟動 download 程序的步驟如下:

  1. 檢查 expansion file 是否有正確被下載
    使用 Downloader Library 的 Helper class 所提供的 function
        - getExpansionAPKFileName(Context, c, boolean mainFile, int versionCode)
        - doesFileExist(Context c, String fileName, long fileSize)
  2. 若檢查後發現 expansion file 下載失敗,則啟動 download process
    呼叫 DownloaderClientMarshaller.startDownloadServiceIfRequired(Context c, PendingIntent notificationClient, Class<?> serviceClass),傳入參數如下:
        - context:你程式的 Context。
        - notificationClient:一個可啟動你程式的 PendingIntent 。
        - serviceClass:你實作 DownloaderService 的 class object。
    呼叫此 function 後會回傳是否 download 需要被啟動,可能的回傳值如下:
        - NO_DOWNLOAD_REQUIRED:expansion file 已經存在,或是下載程序正在執行中。
        - LVL_CHECK_REQUIRED:進行 LVL 驗證以取得 expansion file 的 URL。
        - DOWNLOAD_REQUIRED:URL 已經取得,但還沒開始下載 expansion file。
  3. 若 startDownloadServiceIfRequired 傳回的不是 NO_DOWNLOAD_REQUIRED,則 Downloader Library 會開始進行下載程序,你必須更新你的 UI 已顯示目前下載狀況。
    呼叫 DownloaderClientMarshaller.CreateStub(IDownloaderClient client, Class<?> downloaderService) 去建立一個 IStub,已取得 Downloader Library 的 cllbacks。
    建議在 Main Thread 的 onCreate() 中在下載程序開始之後馬上呼叫 CreateStub(),並在 onResume() 呼叫 connect(),最後在 onStop() 呼叫 disconnect()。

 

Receiveing download process

你必須實作 Downloader Library 提供的 IDownloaderClient interface 去取得 download process 的更新或與之互動。需要的 interface method 如下:

  1. onServiceConnected(Messenger m):當與 download process 連結建立之後,此 function 會被呼叫,此時可以從參數中得到 DownloaderService 的 instance。
    你可以透過該 instance 呼叫以下 function 以控制下載程序:
        - requestPauseDownload():暫停下載程序。
        - requestContinueDownload():恢復下載程序。
        - setDownloadFlags(int flags):設定那些 Network Type 可以允許下載檔案,預設 user 只能透過 Wi-Fi 下載檔案,若你欲讓使用者也能透過手機網路下載,須加上 FLAGS_DOWNLOAD_OVER_CELLULAR 這個 FLAG。
  2. onDownloadStateChanged(int newState):當 download state 改變時,此 function 會被呼叫。
  3. onDownloadProgress(DownloadProgressInfo progress):download process 呼叫此 function,將 DownloadProgressInfo 傳遞出來,你可藉著裡面的資訊更新 UI。

 activity_main.xml 內容如下:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

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

        <TextView android:id="@+id/statusText"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="10dp"
            android:layout_marginLeft="5dp"
            android:layout_marginTop="10dp"
            android:textStyle="bold" />

        <LinearLayout android:id="@+id/downloaderDashboard"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_below="@id/statusText"
            android:orientation="vertical" >

            <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_weight="1" >

                <TextView android:id="@+id/progressAsFraction"
                    style="@android:style/TextAppearance.Small"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_alignParentLeft="true"
                    android:layout_marginLeft="5dp"
                    android:text="0MB / 0MB" />

                <TextView
                    android:id="@+id/progressAsPercentage"
                    style="@android:style/TextAppearance.Small"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_alignRight="@+id/progressBar"
                    android:text="0%" />

                <ProgressBar android:id="@+id/progressBar"
                    style="?android:attr/progressBarStyleHorizontal"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_below="@+id/progressAsFraction"
                    android:layout_marginBottom="10dp"
                    android:layout_marginLeft="10dp"
                    android:layout_marginRight="10dp"
                    android:layout_marginTop="10dp"
                    android:layout_weight="1" />

                <TextView android:id="@+id/progressAverageSpeed"
                    style="@android:style/TextAppearance.Small"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_alignParentLeft="true"
                    android:layout_below="@+id/progressBar"
                    android:layout_marginLeft="5dp" />

                <TextView android:id="@+id/progressTimeRemaining"
                    style="@android:style/TextAppearance.Small"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_alignRight="@+id/progressBar"
                    android:layout_below="@+id/progressBar" />                

            </RelativeLayout>

        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal" >

            <Button android:id="@+id/pauseButton"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_vertical"
                android:layout_marginBottom="10dp"
                android:layout_marginLeft="10dp"
                android:layout_marginRight="5dp"
                android:layout_marginTop="10dp"
                android:layout_weight="0"
                android:minHeight="40dp"
                android:minWidth="94dp"
                android:text="@string/text_button_pause" />

        </LinearLayout>
    </LinearLayout>
</LinearLayout>

 

MainActivity.java 內容如下:


package com.tset.playapkexpansiontest;

import com.android.vending.expansion.zipfile.ZipResourceFile;
import com.android.vending.expansion.zipfile.ZipResourceFile.ZipEntryRO;
import com.google.android.vending.expansion.downloader.Constants;
import com.google.android.vending.expansion.downloader.DownloadProgressInfo;
import com.google.android.vending.expansion.downloader.DownloaderClientMarshaller;
import com.google.android.vending.expansion.downloader.DownloaderServiceMarshaller;
import com.google.android.vending.expansion.downloader.Helpers;
import com.google.android.vending.expansion.downloader.IDownloaderClient;
import com.google.android.vending.expansion.downloader.IDownloaderService;
import com.google.android.vending.expansion.downloader.IStub;

import android.app.Activity;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Messenger;
import android.os.SystemClock;
import android.provider.Settings;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;

import java.io.DataInputStream;
import java.io.IOException;
import java.util.zip.CRC32;

public class MainActivity extends Activity implements IDownloaderClient {
    private static final String LOG_TAG = "LVLDownloader";
    private ProgressBar mPB;

    private TextView mStatusText;
    private TextView mProgressFraction;
    private TextView mProgressPercent;
    private TextView mAverageSpeed;
    private TextView mTimeRemaining;

    private View mDashboard;
    private View mCellMessage;

    private Button mPauseButton;
    private Button mWiFiSettingsButton;

    private boolean mStatePaused;
    private int mState;

    private IDownloaderService mRemoteService;

    private IStub mDownloaderClientStub;

    static private final float SMOOTHING_FACTOR = 0.005f;

    private boolean mCancelValidation;

    private void setState(int newState) {
        if (mState != newState) {
            mState = newState;
            mStatusText.setText(Helpers.getDownloaderStringResourceIDFromState(newState));
        }
    }

    private void setButtonPausedState(boolean paused) {
        mStatePaused = paused;
        int stringResourceID = paused ? R.string.text_button_resume :
        R.string.text_button_pause;
        mPauseButton.setText(stringResourceID);
    }

    private static class XAPKFile {
        public final boolean mIsMain;
        public final int mFileVersion;
        public final long mFileSize;

        XAPKFile(boolean isMain, int fileVersion, long fileSize) {
            mIsMain = isMain;
            mFileVersion = fileVersion;
            mFileSize = fileSize;
        }
    }

    private static final XAPKFile[] xAPKS = {
        new XAPKFile(
            true, // true signifies a main file
            2, // the version of the APK that the file was uploaded against
            31017440L // the length of the file in bytes
        )
    };


    boolean expansionFilesDelivered() {
        for (XAPKFile xf : xAPKS) {
            String fileName = Helpers.getExpansionAPKFileName(this, xf.mIsMain, xf.mFileVersion);
            if (!Helpers.doesFileExist(this, fileName, xf.mFileSize, false))
                return false;
            }
        return true;
    }

    void validateXAPKZipFiles() {
        AsyncTask<Object, DownloadProgressInfo, Boolean> validationTask =
                new AsyncTask<Object, DownloadProgressInfo, Boolean>() {

            @Override
            protected void onPreExecute() {
                mDashboard.setVisibility(View.VISIBLE);
                mCellMessage.setVisibility(View.GONE);
                mStatusText.setText(R.string.text_verifying_download);
                mPauseButton.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        mCancelValidation = true;
                    }
                 });

                mPauseButton.setText(R.string.text_button_cancel_verify);
                 super.onPreExecute();
             }

            @Override
            protected Boolean doInBackground(Object... params) {
                for (XAPKFile xf : xAPKS) {
                    String fileName = Helpers.getExpansionAPKFileName(
                            MainActivity.this, xf.mIsMain, xf.mFileVersion);

                    if (!Helpers.doesFileExist(MainActivity.this, fileName, xf.mFileSize, false))
                        return false;

                    fileName = Helpers.generateSaveFileName(MainActivity.this, fileName);
                    ZipResourceFile zrf;
                    byte[] buf = new byte[1024 * 256];

                    try {
                        zrf = new ZipResourceFile(fileName);
                        ZipEntryRO[] entries = zrf.getAllEntries();
                        long totalCompressedLength = 0;

                        for (ZipEntryRO entry : entries) {
                            totalCompressedLength += entry.mCompressedLength;
                        }

                        float averageVerifySpeed = 0;
                        long totalBytesRemaining = totalCompressedLength;
                        long timeRemaining;

                       for (ZipEntryRO entry : entries) {
                           if (-1 != entry.mCRC32) {
                               long length = entry.mUncompressedLength;
                               CRC32 crc = new CRC32();
                               DataInputStream dis = null;

                               try {
                                   dis = new DataInputStream(
                                   zrf.getInputStream(entry.mFileName));

                                   long startTime = SystemClock.uptimeMillis();

                                    while (length > 0) {
                                        int seek = (int) (length > buf.length ? buf.length : length);
                                        dis.readFully(buf, 0, seek);
                                        crc.update(buf, 0, seek);
                                        length -= seek;
                                        long currentTime = SystemClock.uptimeMillis();
                                        long timePassed = currentTime - startTime;

                                        if (timePassed > 0) {
                                            float currentSpeedSample = (float) seek / (float) timePassed;

                                            if (0 != averageVerifySpeed) {
                                                averageVerifySpeed = SMOOTHING_FACTOR
                                                * currentSpeedSample
                                                + (1 - SMOOTHING_FACTOR)
                                                * averageVerifySpeed;
                                            } else {
                                                averageVerifySpeed = currentSpeedSample;
                                            }

                                            totalBytesRemaining -= seek;
                                            timeRemaining = (long) (totalBytesRemaining / averageVerifySpeed);

                                            this.publishProgress(
                                                new DownloadProgressInfo(
                                                totalCompressedLength,
                                                totalCompressedLength - totalBytesRemaining,
                                                timeRemaining,
                                                averageVerifySpeed)
                                            );
                                        }

                                        startTime = currentTime;

                                        if (mCancelValidation)
                                            return true;
                                    }

                                    if (crc.getValue() != entry.mCRC32) {
                                        Log.e(Constants.TAG,
                                        "CRC does not match for entry: " + entry.mFileName);
                                        Log.e(Constants.TAG,
                                        "In file: " + entry.getZipFileName());
                                        return false;
                                    }
                                } finally {
                                    if (null != dis) {
                                        dis.close();
                                    }
                                 }
                             }
                         }
                     } catch (IOException e) {
                         e.printStackTrace();
                         return false;
                     }
                 }

                 return true;
            }

            @Override
            protected void onProgressUpdate(DownloadProgressInfo... values) {
                onDownloadProgress(values[0]);
                super.onProgressUpdate(values);
            }

            @Override
            protected void onPostExecute(Boolean result) {
                if (result) {
                    mDashboard.setVisibility(View.VISIBLE);
                    mCellMessage.setVisibility(View.GONE);
                    mStatusText.setText(R.string.text_validation_complete);
                    mPauseButton.setOnClickListener(new View.OnClickListener() {

                        @Override
                        public void onClick(View view) {
                            finish();
                        }
                    });
                    mPauseButton.setText(android.R.string.ok);
                } else {
                    mDashboard.setVisibility(View.VISIBLE);
                    mCellMessage.setVisibility(View.GONE);
                    mStatusText.setText(R.string.text_validation_failed);
                    mPauseButton.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View view) {
                            finish();
                        }
                    });
                    mPauseButton.setText(android.R.string.cancel);
                }
                super.onPostExecute(result);
           }

        };

        validationTask.execute(new Object());
    }

    private void initializeDownloadUI() {
        mDownloaderClientStub = DownloaderClientMarshaller.CreateStub(this,
                SampleDownloaderService.class);
        setContentView(R.layout.activity_main);

        mPB = (ProgressBar) findViewById(R.id.progressBar);
        mStatusText = (TextView) findViewById(R.id.statusText);
        mProgressFraction = (TextView) findViewById(R.id.progressAsFraction);
        mProgressPercent = (TextView) findViewById(R.id.progressAsPercentage);
        mAverageSpeed = (TextView) findViewById(R.id.progressAverageSpeed);
        mTimeRemaining = (TextView) findViewById(R.id.progressTimeRemaining);
        mDashboard = findViewById(R.id.downloaderDashboard);
        mCellMessage = findViewById(R.id.approveCellular);
        mPauseButton = (Button) findViewById(R.id.pauseButton);
        mWiFiSettingsButton = (Button) findViewById(R.id.wifiSettingsButton);

        mPauseButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    if (mStatePaused) {
                        mRemoteService.requestContinueDownload();
                    } else {
                        mRemoteService.requestPauseDownload();
                    }
                    setButtonPausedState(!mStatePaused);
                }
        });

        mWiFiSettingsButton.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                startActivity(new Intent(Settings.ACTION_WIFI_SETTINGS));
            }
        });

        Button resumeOnCell = (Button) findViewById(R.id.resumeOverCellular);
        resumeOnCell.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View view) {
                mRemoteService.setDownloadFlags(
                        IDownloaderService.FLAGS_DOWNLOAD_OVER_CELLULAR);
                mRemoteService.requestContinueDownload();
                mCellMessage.setVisibility(View.GONE);
            }
        });

    }

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

        initializeDownloadUI();

        if (!expansionFilesDelivered()) {

            try {
                Intent launchIntent = MainActivity.this.getIntent();
                Intent intentToLaunchThisActivityFromNotification = new Intent(
                    MainActivity.this, MainActivity.this.getClass());
                intentToLaunchThisActivityFromNotification.setFlags(
                    Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
                intentToLaunchThisActivityFromNotification.setAction(launchIntent.getAction());

                if (launchIntent.getCategories() != null) {
                    for (String category : launchIntent.getCategories()) {
                        intentToLaunchThisActivityFromNotification.addCategory(category);
                    }
                }

                PendingIntent pendingIntent = PendingIntent.getActivity(
                    MainActivity.this,
                    0, intentToLaunchThisActivityFromNotification,
                    PendingIntent.FLAG_UPDATE_CURRENT);

                int startResult = DownloaderClientMarshaller.startDownloadServiceIfRequired(this,
                    pendingIntent, SampleDownloaderService.class);

                if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) {
                    initializeDownloadUI();
                    return;
                }
            } catch (NameNotFoundException e) {
                Log.e(LOG_TAG, "Cannot find own package! MAYDAY!");
                e.printStackTrace();
            }

        } else {
            validateXAPKZipFiles();
        }

    }    

    @Override
    public void onServiceConnected(Messenger m) {
        mRemoteService = DownloaderServiceMarshaller.CreateProxy(m);
        mRemoteService.onClientUpdated(mDownloaderClientStub.getMessenger());
    }

    @Override
    public void onDownloadStateChanged(int newState) {
        setState(newState);
        boolean showDashboard = true;
        boolean showCellMessage = false;
        boolean paused;
        boolean indeterminate;

        switch (newState) {
        case IDownloaderClient.STATE_IDLE:
            paused = false;
            indeterminate = true;
            break;
        case IDownloaderClient.STATE_CONNECTING:
        case IDownloaderClient.STATE_FETCHING_URL:
            showDashboard = true;
            paused = false;
            indeterminate = true;
            break;
        case IDownloaderClient.STATE_DOWNLOADING:
            paused = false;
            showDashboard = true;
            indeterminate = false;
            break;

        case IDownloaderClient.STATE_FAILED_CANCELED:
        case IDownloaderClient.STATE_FAILED:
        case IDownloaderClient.STATE_FAILED_FETCHING_URL:
        case IDownloaderClient.STATE_FAILED_UNLICENSED:
            paused = true;
            showDashboard = false;
            indeterminate = false;
            break;
        case IDownloaderClient.STATE_PAUSED_NEED_CELLULAR_PERMISSION:
        case IDownloaderClient.STATE_PAUSED_WIFI_DISABLED_NEED_CELLULAR_PERMISSION:
            showDashboard = false;
            paused = true;
            indeterminate = false;
            showCellMessage = true;
            break;

        case IDownloaderClient.STATE_PAUSED_BY_REQUEST:
            paused = true;
            indeterminate = false;
            break;
        case IDownloaderClient.STATE_PAUSED_ROAMING:
        case IDownloaderClient.STATE_PAUSED_SDCARD_UNAVAILABLE:
            paused = true;
            indeterminate = false;
                break;
        case IDownloaderClient.STATE_COMPLETED:
            showDashboard = false;
            paused = false;
            indeterminate = false;
            validateXAPKZipFiles();
            return;
        default:
            paused = true;
            indeterminate = true;
            showDashboard = true;
        }

        int newDashboardVisibility = showDashboard ? View.VISIBLE : View.GONE;

        if (mDashboard.getVisibility() != newDashboardVisibility) {
            mDashboard.setVisibility(newDashboardVisibility);
        }

        int cellMessageVisibility = showCellMessage ? View.VISIBLE : View.GONE;

        if (mCellMessage.getVisibility() != cellMessageVisibility) {
            mCellMessage.setVisibility(cellMessageVisibility);
        }

        mPB.setIndeterminate(indeterminate);
        setButtonPausedState(paused);
    }

    @Override
    public void onDownloadProgress(DownloadProgressInfo progress) {
        mAverageSpeed.setText(getString(R.string.kilobytes_per_second,
        Helpers.getSpeedString(progress.mCurrentSpeed)));
        mTimeRemaining.setText(getString(R.string.time_remaining,
        Helpers.getTimeRemaining(progress.mTimeRemaining)));

        progress.mOverallTotal = progress.mOverallTotal;
        mPB.setMax((int) (progress.mOverallTotal >> 8));
        mPB.setProgress((int) (progress.mOverallProgress >> 8));
        mProgressPercent.setText(Long.toString(progress.mOverallProgress
            * 100 /
            progress.mOverallTotal) + "%");
        mProgressFraction.setText(Helpers.getDownloadProgressString
            (progress.mOverallProgress,
            progress.mOverallTotal));
    }

    @Override
    protected void onDestroy() {
        this.mCancelValidation = true;
        super.onDestroy();
    }
}

arrow
arrow
    全站熱搜

    擒猿小舖 發表在 痞客邦 留言(0) 人氣()