getAPKExpansionZipFile() returns null - android

Since my game has 100Mb I need to use expansion file. I put below code in the MainActivity in function onCreate()
ZipResourceFile expansionFile = null;
try {
expansionFile = APKExpansionSupport.getAPKExpansionZipFile(getApplicationContext(),2,0);
} catch (IOException e) {
e.printStackTrace();
}
I don't use patch expansion file, just main, so I guess putting 0 as a version is correct. When I log to check if expansionFile was successfully loaded I get null value.
if(expansionFile == null)
Gdx.app.log("EXPANSION_FILE", "NULL");
else
Gdx.app.log("EXPANSION_FILE", "NOT NULL");
I followed instructions on https://developer.android.com/google/play/expansion-files.html
and I put manually main.2.com.my.game.obb file in folder on my Nexus device:
Internal storage\Android\obb\com.my.game\
There are other obb files in Android\obb folder from other games, so this has to be a correct place. Any clues of why it doesn't mount the obb expansion file?
UPDATE
I implemented DownloaderActivity and it downloads expansion file without any problems. It was giving me "Not a zip archive" log message. I found a way to avoid it. I zipped OBB file and uploaded it to Developer Console. It automatically converted into OBB anyway but this time when I downloaded it on my Nexus it no longer gives me null value for ZipResourceFile and it doesn't give me "Not a zip archive" message anymore. But still I can't access files from it since I'm using AssetManager from Libgdx.
I found another way, which I'm testing now.

Solution to the problem where getAPKExpansionZipFile() returns null is to not use JOBB tool, but zip all the content and upload it. Google Play will convert it automatically to the OBB.
Complete solution to load images from expansion file:
I created a static variable in my own Game class which is an instance of ZipResourceFile . I made it static, because I wanted to load the expansion file in MainActivity class and use it in my FileHandle class to get images from it. If you find a different way it's up to you. This is how I did it.
public static ZipResourceFile expansionFile;
Then in your MainActivity class load expansion file. I did it in OnCreate() function.
try {
GameInfo.expansionFile = APKExpansionSupport.getAPKExpansionZipFile(getBaseContext(),22,0);
} catch (IOException e) {
e.printStackTrace();
}
Passed values to the function are these:
getAPKExpansionZipFile(Context ctx, int mainVersion, int patchVersion)
mainVersion needs to be exactly the same as declared in Manifest file :
android:versionCode="22"
Also your expansion file needs to have the same number in its name:
main.mainVersion.com.name.title
Create your own FileHandle class. This is where it looks for your specific file for example "image.png" in the expansion file.
public class CustomFileHandle extends FileHandle
{
public CustomFileHandle (String fileName) {
super(fileName);
}
#Override
public InputStream read()
{
InputStream input = null;
try {
input = GameInfo.expansionFile.getInputStream(file.getPath().replace('\\', '/'));
} catch (IOException e) {
e.printStackTrace();
}
return input;
}
}
Create your own FileHandleResolver class. This is where you use your custom FileHandle.
public class CustomFileHandleResolver implements FileHandleResolver
{
#Override
public FileHandle resolve(String fileName) {
return new CustomFileHandle(fileName);
}
}
Create instance of AssetManager and pass your FileHandleResolver to constructor.
assetManager = new AssetManager(new CustomFileHandleResolver());
If I did not omit any step then this is what you need to do to load your files from expansion file. As I mentioned in the begining I found the problem with using JOBB Tool to pack the files into OBB file. It makes getAPKExpansionZipFile() to return null. So I just zipped all the files instead. Look here for more detailed explanation : https://developer.android.com/google/play/expansion-files.html

For me it was the expansion file size in bytes. I rezipped and never changed the file size in code. Hence I was getting null value. I was off by only few bytes and that just took my entire day at workplace. Didn't realize it was that important :(
private static final XAPKFile[] xAPKS = {
new XAPKFile(true, Constants.MAIN_OBB_VERSION, Constants.EXPANSION_FILE_SIZE) // right here expansion file size must match exactly.
};
//in Constants class
public static final int EXPANSION_FILE_SIZE = 285516838; //expansion file size in bytes

Related

Can you create a .dat file in Android

I am trying to write my first app for Android. I knew Java formerly but it has been a year or two since I used it.
I want to create a simple file in internal storage - I understand I do not have to set any permissions to create such a file?
Is it a .dat file I need if I want to save an ArrayList? Does Android require me to use file extension when creating it?
Even with just trying the basic file creation - checking for existence of file and then creating it if it does not exist - does not work. Can anyone tell me what I am doing wrong?
(I have commented out the attempt to read the ArrayList, as I cannot even create the file. Just trying the basic file creation.)
(Also, I have tried the code with "Shares.dat" instead of just "Shares" as filename, that didn't work either. I don't even know whether Android recognises .dat files and to be honest I am not 100% sure that is the file I need.)
(If by any chance anyone can help, I may not be able to test any solution until next weekend......)
As for the last but one line, originally it read 'context.getFileDir()' but my class extends ActionBarActivity and I found on internet a suggestion to change to this.getFileDir(). I got a null pointer warning when I used context.getFileDir()
file = new File("Shares");
if (file.exists()){
url.setText("File Exists");
/*try{
is = openFileInput("Shares");
oi = new ObjectInputStream(is);
details = (ArrayList<Action>)oi.readObject();//warning
oi.close();//need finally??
}
catch(Exception e){url.setText((e.getMessage()));}
url.setText(details.get(0).getAddresse());*/
}
else
{
try
{
**file = new File(this.getFilesDir(), "Shares");**
}
catch(Exception e){url.setText((e.getMessage()));}
}
If you want a reference to a file that's created in private storage, you'd want to use getFileStreamPath("shares.dat") instead of creating a new File object. File extension shouldn't matter, but it's a good practice to add a file extension to keep track for yourself what those files are for.
For example:
private boolean fileExists(Context _context, String _filename) {
File temp = _context.getFileStreamPath(_filename);
if(temp == null || !temp.exists()) {
return false;
}
return true;
}
Then, if you wanted to write to a file named "shares.dat" then you'd use openFileOutput("shares.dat", Context.MODE_PRIVATE). If you wanted to read in from that file, you'd use openFileInput("shares.dat").
// Read in from file
if(fileExists(this, "shares.dat")) {
FileInputStream fis = this.openFileInput("shares.dat");
ObjectInputStream ois = new ObjectInputStream(fis);
ArrayList<Action> actions = (ArrayList<Action>)ois.readObject();
ois.close();
}
// Write out to file
FileOutputStream fos = this.openFileOutput("shares.dat", Context.MODE_PRIVATE);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(actions);
oos.close();
All stream operations shown above have the ability to throw an IOException, so be sure to wrap that code in a try/catch block as needed.

Compare one image with other images stored in the sdcard

How to compare one image token with camera with all the other images stored in the sd card and display the result?
public class SearchForFaces extends Activity {
Bitmap bitmapOriginale;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle b1 = getIntent().getExtras();
String cin= b1.getString("cin");
//getting the image
File sdCard = Environment.getExternalStorageDirectory();
File directory = new File (sdCard.getAbsolutePath() + "/Student");
File file = new File(directory, cin+"jpg");
try {
FileInputStream streamIn = new FileInputStream(file);
bitmapOriginale = BitmapFactory.decodeStream(streamIn);
streamIn.close();
} catch (IOException e) {
Log.d("SearchForFaces Exception", e.getMessage());
}
if(bitmapOriginale.sameAs(//images from sdcard))
{
//display founded image
}
}
}
i think, its not necessary read all images..if you want compare images from camera, you can "easy" take the images in DCMI folder. But ok, user will move some files in another folder. So in that case i will advice just open first folder, read files in there and check the format, after that open another folder, read files in there and again check the formats and save the paths to the jpg files.
So in this case just easy some for, foreach, while cykl or you can do it with recursion.
You will have some ArrayList (linkedList, whatever) and in this list you can put the paths. Then just call your sameAs method.
On this you can use Environment.getExternalStorageDirectory().listFiles();
But..i am not sure if your "algorithm" will work..image recognition is really hard part of computer science..and if you dont know how to get the files on SDcard..the algorithm wouldnt probably work..
But if you want to check the similarity with byte by byte comparsion, then its ok..
Check also this blog http://mihaifonoage.blogspot.com/2009/09/displaying-images-from-sd-card-in.html It shows how to read an images from sd card. Once you read them you can use your sameAs() written method to compare them.

How can I load a class from a jar file packaged in my .apk file?

I am trying to load a plugin implementation of an interface from a jar file which is in the /assets directory of my .apk file. The only way I've been able to get this to work is by extracting the jar file to private external storage and then passing that file to the DexClassLoader.
That works, but why should the jar have to exist in two places (the .apk and private external storage)? The DexClassLoader has to have a file path as its argument.
Is there a way to give it a direct path to the file that is in the /assets folder so that I don't have to use up external storage for an extra copy of what's already present?
Here are the relevant code snippets:
// somewhere in my main Activity ...
final File aExtractedDexFile = new File(getDir("dex", Context.MODE_PRIVATE),
LIBRARY_DEX_JAR);
extractDexTo(aExtractedDexFile);
loadLibraryProvider(aExtractedDexFile);
and
/** Extract the jar file that contains the implementation class.dex and place in private storage */
private void extractDexTo(File tJarInternalStoragePath) {
BufferedInputStream aJarInputStream = null;
OutputStream aDexOutputStream = null;
try {
aJarInputStream = new BufferedInputStream(getAssets().open(LIBRARY_DEX_JAR));
aJarOutputStream = new BufferedOutputStream(new FileOutputStream(tJarInternalStoragePath));
byte[] buf = new byte[BUF_SIZE];
int len;
while ((len = aJarInputStream.read(buf, 0, BUF_SIZE)) > 0)
{
aJarOutputStream.write(buf, 0, len);
}
aJarOutputStream.close();
aJarInputStream.close();
} catch (IOException e) {
if (aDexOutputStream != null) {
try {
aJarOutputStream.close();
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
if (aJarInputStream != null) {
try {
aJarInputStream.close();
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
}
}
and
/** Use DexClassLoader to load the classes from LibraryProvider */
private void loadLibraryProvider(File tFile) {
// Internal storage where the DexClassLoader writes the optimized dex file to.
final File aOptimizedDexOutputPath = getDir("outdex", Context.MODE_PRIVATE);
// Initialize the class loader with the secondary dex file.
DexClassLoader cl = new DexClassLoader(tFile.getAbsolutePath(),
aOptimizedDexOutputPath.getAbsolutePath(),
null,
getClassLoader());
Class<?> aLibProviderClazz = null;
try {
// Load the library class from the class loader.
aLibProviderClazz = cl.loadClass(LIBRARY_PROVIDER_CLASS);
sLibraryProvider = (LibraryInterface) aLibProviderClazz.newInstance();
} catch (Exception exception) {
// Handle exception gracefully here.
exception.printStackTrace();
}
}
Is there a way to give it a direct path to the file that is in the /assets folder so that I don't have to use up external storage for an extra copy of what's already present?
The answer is No. I suppose you follow this blog posted by official source implementing your code. if there is a better way of doing things, the bloger should recommend it in his blog.
Reason why you need optimizedDirectory is explained in the API:
This class loader requires an application-private, writable directory to cache optimized classes.
Also note that assets directory is not writable in apk, so it can't be done with purely assets directory.
Reason why you need copy jar file is a little bit subtle, mentioned in the blog:
First, it has to be copied to a storage location whose path can be supplied to the class loader.
Everything (folders/files) embedded within apk archive is not exposable (or interpretable) to the underlying file system at runtime. In another word, dexPath required in both DexClassLoader and PathClassLoader's constructor need a solid path string like /data/data/com.example/dex/common-lib.jar that represents the file in file system.

Default dir to store a file that my app makes

A question, I'm making a app that will store a textfile (.txt) with highscore data. I've made a file with this line from my highscores activity:
File highscoreList = new File("highscores.txt");
Where will it be placed? In the same dir as the highscores.java file? Or, how should I specify a dir that will put the textfile in the same folder as highscores.java?
You should access your files using the Context methods openFileInput and openFileOutput. You can determine where they are actually stored using getFileStreamPath. (The directory they go in can be obtained with getFilesDir.) The advantage of using this method is that the files will be private to your application and will be removed automatically if your app is uninstalled.
In your activity, you can create your File with:
File highscoreList = getFileStreamPath("highscores.txt");
If all you want to do is write to it:
FileOutputStream output = null;
try {
output = openFileOutput("highscores.txt", MODE_PRIVATE);
// write to file
} finally {
if (output != null) {
try { output.close(); }
catch (IOException e) {
Log.w(LOG_TAG, "Error closing file!", e);
}
}
}
Similarly, for reading you can use:
FileInputStream input = openFileInput("highscores.txt");
If you are trying to access your file from outside an Activity subclass, you'll need a Context. (In a View, for instance, you can use getContext(). For a helper class, you'll need to pass in your Activity instance or some other Context object.)

How to get access to raw resources that I put in res folder?

In J2ME, I've do this like that:
getClass().getResourceAsStream("/raw_resources.dat");
But in android, I always get null on this, why?
For raw files, you should consider creating a raw folder inside res directory and then call getResources().openRawResource(resourceName) from your activity.
InputStream raw = context.getAssets().open("filename.ext");
Reader is = new BufferedReader(new InputStreamReader(raw, "UTF8"));
In some situations we have to get image from drawable or raw folder using image name instead if generated id
// Image View Object
mIv = (ImageView) findViewById(R.id.xidIma);
// create context Object for to Fetch image from resourse
Context mContext=getApplicationContext();
// getResources().getIdentifier("image_name","res_folder_name", package_name);
// find out below example
int i = mContext.getResources().getIdentifier("ic_launcher","raw", mContext.getPackageName());
// now we will get contsant id for that image
mIv.setBackgroundResource(i);
Android access to raw resources
An advance approach is using Kotlin Extension function
fun Context.getRawInput(#RawRes resourceId: Int): InputStream {
return resources.openRawResource(resourceId)
}
One more interesting thing is extension function use that is defined in Closeable scope
For example you can work with input stream in elegant way without handling Exceptions and memory managing
fun Context.readRaw(#RawRes resourceId: Int): String {
return resources.openRawResource(resourceId).bufferedReader(Charsets.UTF_8).use { it.readText() }
}
TextView txtvw = (TextView)findViewById(R.id.TextView01);
txtvw.setText(readTxt());
private String readTxt()
{
InputStream raw = getResources().openRawResource(R.raw.hello);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
int i;
try
{
i = raw.read();
while (i != -1)
{
byteArrayOutputStream.write(i);
i = raw.read();
}
raw.close();
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
return byteArrayOutputStream.toString();
}
TextView01:: txtview in linearlayout
hello:: .txt file in res/raw folder (u can access ny othr folder as wel)
Ist 2 lines are 2 written in onCreate() method
rest is to be written in class extending Activity!!
getClass().getResourcesAsStream() works fine on Android. Just make sure the file you are trying to open is correctly embedded in your APK (open the APK as ZIP).
Normally on Android you put such files in the assets directory. So if you put the raw_resources.dat in the assets subdirectory of your project, it will end up in the assets directory in the APK and you can use:
getClass().getResourcesAsStream("/assets/raw_resources.dat");
It is also possible to customize the build process so that the file doesn't land in the assets directory in the APK.
InputStream in = getResources().openRawResource(resourceName);
This will work correctly. Before that you have to create the xml file / text file in raw resource. Then it will be accessible.
Edit Some times com.andriod.R will be imported if there is any error in layout file or image names. So You have to import package correctly, then only the raw file will be accessible.
This worked for for me: getResources().openRawResource(R.raw.certificate)

Categories

Resources