Skip to content

Instantly share code, notes, and snippets.

@tprochazka
Created March 23, 2013 20:47
Show Gist options
  • Save tprochazka/5229293 to your computer and use it in GitHub Desktop.
Save tprochazka/5229293 to your computer and use it in GitHub Desktop.
Fixed version of APEZProvider for revision 3 See https://code.google.com/p/android/issues/detail?id=50461
package com.android.vending.expansion.zipfile;
/*
* Copyright (C) 2012 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.
*/
//To implement APEZProvider in your application, you'll want to change
//the AUTHORITY to match what you define in the manifest.
import com.android.vending.expansion.zipfile.ZipResourceFile.ZipEntryRO;
import android.content.ContentProvider;
import android.content.ContentProviderOperation;
import android.content.ContentProviderResult;
import android.content.ContentValues;
import android.content.Context;
import android.content.OperationApplicationException;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ProviderInfo;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.provider.BaseColumns;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
/**
* This content provider is an optional part of the library.
*
* <p>Most apps don't need to use this class. This defines a
* ContentProvider that marshalls the data from the ZIP files through a
* content provider Uri in order to provide file access for certain Android APIs
* that expect Uri access to media files.
*
*/
public abstract class APEZProvider extends ContentProvider {
private ZipResourceFile mAPKExtensionFile;
private boolean mInit;
public static final String FILEID = BaseColumns._ID;
public static final String FILENAME = "ZPFN";
public static final String ZIPFILE = "ZFIL";
public static final String MODIFICATION = "ZMOD";
public static final String CRC32 = "ZCRC";
public static final String COMPRESSEDLEN = "ZCOL";
public static final String UNCOMPRESSEDLEN = "ZUNL";
public static final String COMPRESSIONTYPE = "ZTYP";
public static final String[] ALL_FIELDS = {
FILEID,
FILENAME,
ZIPFILE,
MODIFICATION,
CRC32,
COMPRESSEDLEN,
UNCOMPRESSEDLEN,
COMPRESSIONTYPE
};
public static final int FILEID_IDX = 0;
public static final int FILENAME_IDX = 1;
public static final int ZIPFILE_IDX = 2;
public static final int MOD_IDX = 3;
public static final int CRC_IDX = 4;
public static final int COMPLEN_IDX = 5;
public static final int UNCOMPLEN_IDX = 6;
public static final int COMPTYPE_IDX = 7;
public static final int[] ALL_FIELDS_INT = {
FILEID_IDX,
FILENAME_IDX,
ZIPFILE_IDX,
MOD_IDX,
CRC_IDX,
COMPLEN_IDX,
UNCOMPLEN_IDX,
COMPTYPE_IDX
};
/**
* This needs to match the authority in your manifest
*/
public abstract String getAuthority();
@Override
public int delete(Uri arg0, String arg1, String[] arg2) {
// TODO Auto-generated method stub
return 0;
}
@Override
public String getType(Uri uri) {
return "vnd.android.cursor.item/asset";
}
@Override
public Uri insert(Uri uri, ContentValues values) {
// TODO Auto-generated method stub
return null;
}
static private final String NO_FILE = null;
private boolean initIfNecessary() {
if ( !mInit ) {
Context ctx = getContext();
PackageManager pm = ctx.getPackageManager();
ProviderInfo pi = pm.resolveContentProvider(getAuthority(), PackageManager.GET_META_DATA);
PackageInfo packInfo;
try {
packInfo = pm.getPackageInfo(ctx.getPackageName(), 0);
} catch (NameNotFoundException e1) {
e1.printStackTrace();
return false;
}
int patchFileVersion;
int mainFileVersion;
int appVersionCode = packInfo.versionCode;
String[] resourceFiles = null;
if ( null != pi.metaData ) {
mainFileVersion = pi.metaData.getInt("mainVersion", appVersionCode);
patchFileVersion = pi.metaData.getInt("patchVersion", appVersionCode);
String mainFileName = pi.metaData.getString("mainFilename");
if ( mainFileName != null ) {
String patchFileName = pi.metaData.getString("patchFilename");
if ( patchFileName != null ) {
resourceFiles = new String[] { mainFileName, patchFileName };
} else {
resourceFiles = new String[] { mainFileName };
}
}
} else {
mainFileVersion = patchFileVersion = appVersionCode;
}
try {
if ( null == resourceFiles ) {
mAPKExtensionFile = APKExpansionSupport.getAPKExpansionZipFile(ctx, mainFileVersion, patchFileVersion);
} else {
mAPKExtensionFile = APKExpansionSupport.getResourceZipFile(resourceFiles);
}
mInit = true;
return true;
} catch (IOException e) {
e.printStackTrace();
}
}
return false;
}
@Override
public boolean onCreate() {
return true;
}
@Override
public AssetFileDescriptor openAssetFile(Uri uri, String mode)
throws FileNotFoundException {
initIfNecessary();
String path = uri.getEncodedPath();
if ( path.startsWith("/") ) {
path = path.substring(1);
}
return mAPKExtensionFile.getAssetFileDescriptor(path);
}
@Override
public ContentProviderResult[] applyBatch(
ArrayList<ContentProviderOperation> operations)
throws OperationApplicationException {
initIfNecessary();
return super.applyBatch(operations);
}
@Override
public ParcelFileDescriptor openFile(Uri uri, String mode)
throws FileNotFoundException {
initIfNecessary();
AssetFileDescriptor af = openAssetFile(uri, mode);
if ( null != af ) {
return af.getParcelFileDescriptor();
}
return null;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
initIfNecessary();
// lists all of the items in the file that match
ZipEntryRO[] zipEntries;
if ( null == mAPKExtensionFile ) {
zipEntries = new ZipEntryRO[0];
} else {
zipEntries = mAPKExtensionFile.getAllEntries();
}
int[] intProjection;
if ( null == projection ) {
intProjection = ALL_FIELDS_INT;
projection = ALL_FIELDS;
} else {
int len = projection.length;
intProjection = new int[len];
for ( int i = 0; i < len; i++ ) {
if ( projection[i].equals(FILEID) ) {
intProjection[i] = FILEID_IDX;
} else if ( projection[i].equals(FILENAME) ) {
intProjection[i] = FILENAME_IDX;
} else if ( projection[i].equals(ZIPFILE) ) {
intProjection[i] = ZIPFILE_IDX;
} else if ( projection[i].equals(MODIFICATION) ) {
intProjection[i] = MOD_IDX;
} else if ( projection[i].equals(CRC32) ) {
intProjection[i] = CRC_IDX;
} else if ( projection[i].equals(COMPRESSEDLEN) ) {
intProjection[i] = COMPLEN_IDX;
} else if ( projection[i].equals(UNCOMPRESSEDLEN) ) {
intProjection[i] = UNCOMPLEN_IDX;
} else if ( projection[i].equals(COMPRESSIONTYPE) ) {
intProjection[i] = COMPTYPE_IDX;
} else {
throw new RuntimeException();
}
}
}
MatrixCursor mc = new MatrixCursor(projection, zipEntries.length);
int len = intProjection.length;
for ( ZipEntryRO zer : zipEntries ) {
MatrixCursor.RowBuilder rb = mc.newRow();
for ( int i = 0; i < len; i++ ) {
switch (intProjection[i]) {
case FILEID_IDX:
rb.add(i);
break;
case FILENAME_IDX:
rb.add(zer.mFileName);
break;
case ZIPFILE_IDX:
rb.add(zer.getZipFileName());
break;
case MOD_IDX:
rb.add(zer.mWhenModified);
break;
case CRC_IDX:
rb.add(zer.mCRC32);
break;
case COMPLEN_IDX:
rb.add(zer.mCompressedLength);
break;
case UNCOMPLEN_IDX:
rb.add(zer.mUncompressedLength);
break;
case COMPTYPE_IDX:
rb.add(zer.mMethod);
break;
}
}
}
return mc;
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
// TODO Auto-generated method stub
return 0;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment