Skip to content
This repository was archived by the owner on Mar 16, 2019. It is now read-only.

Commit c9a2d48

Browse files
committed
#234 Add APK expansion library
1 parent 94c30a7 commit c9a2d48

File tree

4 files changed

+816
-0
lines changed

4 files changed

+816
-0
lines changed

android/src/main/AndroidManifest.xml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,28 @@
11
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
22
package="com.RNFetchBlob">
33

4+
<!-- Required to access Google Play Licensing -->
5+
<uses-permission android:name="com.android.vending.CHECK_LICENSE" />
6+
7+
<!-- Required to download files from Google Play -->
8+
<uses-permission android:name="android.permission.INTERNET" />
9+
10+
<!-- Required to keep CPU alive while downloading files
11+
(NOT to keep screen awake) -->
12+
<uses-permission android:name="android.permission.WAKE_LOCK" />
13+
14+
<!-- Required to poll the state of the network connection
15+
and respond to changes -->
16+
<uses-permission
17+
android:name="android.permission.ACCESS_NETWORK_STATE" />
18+
19+
<!-- Required to check whether Wi-Fi is enabled -->
20+
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
21+
22+
<!-- Required to read and write the expansion files on shared storage -->
23+
<uses-permission
24+
android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
25+
426
<application
527
android:label="@string/app_name">
628

Lines changed: 287 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,287 @@
1+
package com.android.vending.expansion.zipfile;
2+
3+
/*
4+
* Copyright (C) 2012 The Android Open Source Project
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
//To implement APEZProvider in your application, you'll want to change
20+
//the AUTHORITY to match what you define in the manifest.
21+
22+
import com.android.vending.expansion.zipfile.ZipResourceFile.ZipEntryRO;
23+
24+
import android.content.ContentProvider;
25+
import android.content.ContentProviderOperation;
26+
import android.content.ContentProviderResult;
27+
import android.content.ContentValues;
28+
import android.content.Context;
29+
import android.content.OperationApplicationException;
30+
import android.content.pm.PackageInfo;
31+
import android.content.pm.PackageManager;
32+
import android.content.pm.PackageManager.NameNotFoundException;
33+
import android.content.pm.ProviderInfo;
34+
import android.content.res.AssetFileDescriptor;
35+
import android.database.Cursor;
36+
import android.database.MatrixCursor;
37+
import android.net.Uri;
38+
import android.os.ParcelFileDescriptor;
39+
import android.provider.BaseColumns;
40+
41+
import java.io.FileNotFoundException;
42+
import java.io.IOException;
43+
import java.util.ArrayList;
44+
45+
/**
46+
* This content provider is an optional part of the library.
47+
*
48+
* <p>Most apps don't need to use this class. This defines a
49+
* ContentProvider that marshalls the data from the ZIP files through a
50+
* content provider Uri in order to provide file access for certain Android APIs
51+
* that expect Uri access to media files.
52+
*
53+
*/
54+
public abstract class APEZProvider extends ContentProvider {
55+
56+
private ZipResourceFile mAPKExtensionFile;
57+
private boolean mInit;
58+
59+
public static final String FILEID = BaseColumns._ID;
60+
public static final String FILENAME = "ZPFN";
61+
public static final String ZIPFILE = "ZFIL";
62+
public static final String MODIFICATION = "ZMOD";
63+
public static final String CRC32 = "ZCRC";
64+
public static final String COMPRESSEDLEN = "ZCOL";
65+
public static final String UNCOMPRESSEDLEN = "ZUNL";
66+
public static final String COMPRESSIONTYPE = "ZTYP";
67+
68+
public static final String[] ALL_FIELDS = {
69+
FILEID,
70+
FILENAME,
71+
ZIPFILE,
72+
MODIFICATION,
73+
CRC32,
74+
COMPRESSEDLEN,
75+
UNCOMPRESSEDLEN,
76+
COMPRESSIONTYPE
77+
};
78+
79+
public static final int FILEID_IDX = 0;
80+
public static final int FILENAME_IDX = 1;
81+
public static final int ZIPFILE_IDX = 2;
82+
public static final int MOD_IDX = 3;
83+
public static final int CRC_IDX = 4;
84+
public static final int COMPLEN_IDX = 5;
85+
public static final int UNCOMPLEN_IDX = 6;
86+
public static final int COMPTYPE_IDX = 7;
87+
88+
public static final int[] ALL_FIELDS_INT = {
89+
FILEID_IDX,
90+
FILENAME_IDX,
91+
ZIPFILE_IDX,
92+
MOD_IDX,
93+
CRC_IDX,
94+
COMPLEN_IDX,
95+
UNCOMPLEN_IDX,
96+
COMPTYPE_IDX
97+
};
98+
99+
/**
100+
* This needs to match the authority in your manifest
101+
*/
102+
public abstract String getAuthority();
103+
104+
@Override
105+
public int delete(Uri arg0, String arg1, String[] arg2) {
106+
// TODO Auto-generated method stub
107+
return 0;
108+
}
109+
110+
@Override
111+
public String getType(Uri uri) {
112+
return "vnd.android.cursor.item/asset";
113+
}
114+
115+
@Override
116+
public Uri insert(Uri uri, ContentValues values) {
117+
// TODO Auto-generated method stub
118+
return null;
119+
}
120+
121+
static private final String NO_FILE = "N";
122+
123+
private boolean initIfNecessary() {
124+
if ( !mInit ) {
125+
Context ctx = getContext();
126+
PackageManager pm = ctx.getPackageManager();
127+
ProviderInfo pi = pm.resolveContentProvider(getAuthority(), PackageManager.GET_META_DATA);
128+
PackageInfo packInfo;
129+
try {
130+
packInfo = pm.getPackageInfo(ctx.getPackageName(), 0);
131+
} catch (NameNotFoundException e1) {
132+
e1.printStackTrace();
133+
return false;
134+
}
135+
int patchFileVersion;
136+
int mainFileVersion;
137+
int appVersionCode = packInfo.versionCode;
138+
String[] resourceFiles = null;
139+
if ( null != pi.metaData ) {
140+
mainFileVersion = pi.metaData.getInt("mainVersion", appVersionCode);
141+
patchFileVersion = pi.metaData.getInt("patchVersion", appVersionCode);
142+
String mainFileName = pi.metaData.getString("mainFilename", NO_FILE);
143+
if ( NO_FILE != mainFileName ) {
144+
String patchFileName = pi.metaData.getString("patchFilename", NO_FILE);
145+
if ( NO_FILE != patchFileName ) {
146+
resourceFiles = new String[] { mainFileName, patchFileName };
147+
} else {
148+
resourceFiles = new String[] { mainFileName };
149+
}
150+
}
151+
} else {
152+
mainFileVersion = patchFileVersion = appVersionCode;
153+
}
154+
try {
155+
if ( null == resourceFiles ) {
156+
mAPKExtensionFile = APKExpansionSupport.getAPKExpansionZipFile(ctx, mainFileVersion, patchFileVersion);
157+
} else {
158+
mAPKExtensionFile = APKExpansionSupport.getResourceZipFile(resourceFiles);
159+
}
160+
mInit = true;
161+
return true;
162+
} catch (IOException e) {
163+
e.printStackTrace();
164+
}
165+
}
166+
return false;
167+
}
168+
169+
@Override
170+
public boolean onCreate() {
171+
return true;
172+
}
173+
174+
@Override
175+
public AssetFileDescriptor openAssetFile(Uri uri, String mode)
176+
throws FileNotFoundException {
177+
initIfNecessary();
178+
String path = uri.getEncodedPath();
179+
if ( path.startsWith("/") ) {
180+
path = path.substring(1);
181+
}
182+
return mAPKExtensionFile.getAssetFileDescriptor(path);
183+
}
184+
185+
@Override
186+
public ContentProviderResult[] applyBatch(
187+
ArrayList<ContentProviderOperation> operations)
188+
throws OperationApplicationException {
189+
initIfNecessary();
190+
return super.applyBatch(operations);
191+
}
192+
193+
@Override
194+
public ParcelFileDescriptor openFile(Uri uri, String mode)
195+
throws FileNotFoundException {
196+
initIfNecessary();
197+
AssetFileDescriptor af = openAssetFile(uri, mode);
198+
if ( null != af ) {
199+
return af.getParcelFileDescriptor();
200+
}
201+
return null;
202+
}
203+
204+
@Override
205+
public Cursor query(Uri uri, String[] projection, String selection,
206+
String[] selectionArgs, String sortOrder) {
207+
initIfNecessary();
208+
// lists all of the items in the file that match
209+
ZipEntryRO[] zipEntries;
210+
if ( null == mAPKExtensionFile ) {
211+
zipEntries = new ZipEntryRO[0];
212+
} else {
213+
zipEntries = mAPKExtensionFile.getAllEntries();
214+
}
215+
int[] intProjection;
216+
if ( null == projection ) {
217+
intProjection = ALL_FIELDS_INT;
218+
projection = ALL_FIELDS;
219+
} else {
220+
int len = projection.length;
221+
intProjection = new int[len];
222+
for ( int i = 0; i < len; i++ ) {
223+
if ( projection[i].equals(FILEID) ) {
224+
intProjection[i] = FILEID_IDX;
225+
} else if ( projection[i].equals(FILENAME) ) {
226+
intProjection[i] = FILENAME_IDX;
227+
} else if ( projection[i].equals(ZIPFILE) ) {
228+
intProjection[i] = ZIPFILE_IDX;
229+
} else if ( projection[i].equals(MODIFICATION) ) {
230+
intProjection[i] = MOD_IDX;
231+
} else if ( projection[i].equals(CRC32) ) {
232+
intProjection[i] = CRC_IDX;
233+
} else if ( projection[i].equals(COMPRESSEDLEN) ) {
234+
intProjection[i] = COMPLEN_IDX;
235+
} else if ( projection[i].equals(UNCOMPRESSEDLEN) ) {
236+
intProjection[i] = UNCOMPLEN_IDX;
237+
} else if ( projection[i].equals(COMPRESSIONTYPE) ) {
238+
intProjection[i] = COMPTYPE_IDX;
239+
} else {
240+
throw new RuntimeException();
241+
}
242+
}
243+
}
244+
MatrixCursor mc = new MatrixCursor(projection, zipEntries.length);
245+
int len = intProjection.length;
246+
for ( ZipEntryRO zer : zipEntries ) {
247+
MatrixCursor.RowBuilder rb = mc.newRow();
248+
for ( int i = 0; i < len; i++ ) {
249+
switch (intProjection[i]) {
250+
case FILEID_IDX:
251+
rb.add(i);
252+
break;
253+
case FILENAME_IDX:
254+
rb.add(zer.mFileName);
255+
break;
256+
case ZIPFILE_IDX:
257+
rb.add(zer.getZipFileName());
258+
break;
259+
case MOD_IDX:
260+
rb.add(zer.mWhenModified);
261+
break;
262+
case CRC_IDX:
263+
rb.add(zer.mCRC32);
264+
break;
265+
case COMPLEN_IDX:
266+
rb.add(zer.mCompressedLength);
267+
break;
268+
case UNCOMPLEN_IDX:
269+
rb.add(zer.mUncompressedLength);
270+
break;
271+
case COMPTYPE_IDX:
272+
rb.add(zer.mMethod);
273+
break;
274+
}
275+
}
276+
}
277+
return mc;
278+
}
279+
280+
@Override
281+
public int update(Uri uri, ContentValues values, String selection,
282+
String[] selectionArgs) {
283+
// TODO Auto-generated method stub
284+
return 0;
285+
}
286+
287+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package com.android.vending.expansion.zipfile;
2+
/*
3+
* Copyright (C) 2012 The Android Open Source Project
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
import java.io.File;
18+
import java.io.FileFilter;
19+
import java.io.IOException;
20+
import java.util.Vector;
21+
import java.util.regex.Matcher;
22+
import java.util.regex.Pattern;
23+
24+
import android.content.Context;
25+
import android.os.Environment;
26+
import android.util.Log;
27+
28+
public class APKExpansionSupport {
29+
// The shared path to all app expansion files
30+
private final static String EXP_PATH = "/Android/obb/";
31+
32+
static String[] getAPKExpansionFiles(Context ctx, int mainVersion, int patchVersion) {
33+
String packageName = ctx.getPackageName();
34+
Vector<String> ret = new Vector<String>();
35+
if (Environment.getExternalStorageState().equals(
36+
Environment.MEDIA_MOUNTED)) {
37+
// Build the full path to the app's expansion files
38+
File root = Environment.getExternalStorageDirectory();
39+
File expPath = new File(root.toString() + EXP_PATH + packageName);
40+
41+
// Check that expansion file path exists
42+
if (expPath.exists()) {
43+
if ( mainVersion > 0 ) {
44+
String strMainPath = expPath + File.separator + "main." + mainVersion + "." + packageName + ".obb";
45+
File main = new File(strMainPath);
46+
if ( main.isFile() ) {
47+
ret.add(strMainPath);
48+
}
49+
}
50+
if ( patchVersion > 0 ) {
51+
String strPatchPath = expPath + File.separator + "patch." + patchVersion + "." + packageName + ".obb";
52+
File main = new File(strPatchPath);
53+
if ( main.isFile() ) {
54+
ret.add(strPatchPath);
55+
}
56+
}
57+
}
58+
}
59+
String[] retArray = new String[ret.size()];
60+
ret.toArray(retArray);
61+
return retArray;
62+
}
63+
64+
static public ZipResourceFile getResourceZipFile(String[] expansionFiles) throws IOException {
65+
ZipResourceFile apkExpansionFile = null;
66+
for (String expansionFilePath : expansionFiles) {
67+
if ( null == apkExpansionFile ) {
68+
apkExpansionFile = new ZipResourceFile(expansionFilePath);
69+
} else {
70+
apkExpansionFile.addPatchFile(expansionFilePath);
71+
}
72+
}
73+
return apkExpansionFile;
74+
}
75+
76+
static public ZipResourceFile getAPKExpansionZipFile(Context ctx, int mainVersion, int patchVersion) throws IOException{
77+
String[] expansionFiles = getAPKExpansionFiles(ctx, mainVersion, patchVersion);
78+
return getResourceZipFile(expansionFiles);
79+
}
80+
}

0 commit comments

Comments
 (0)