Created
March 23, 2013 20:47
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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