Android 11 has new requirements regarding file access.

This prototype has working test code for Android 11 but there is still a lot missing like exlaination dialogues, refactoring, etc
This commit is contained in:
Gonk
2020-09-13 19:43:17 +02:00
parent 03fc984cc6
commit f67eb9ce67
14 changed files with 188 additions and 81 deletions

View File

@@ -9,7 +9,7 @@
>
<uses-sdk
android:minSdkVersion="4"
android:targetSdkVersion="28"
android:targetSdkVersion="30"
/>
<supports-screens
@@ -31,6 +31,8 @@
android:description="@string/app_description"
android:allowBackup="true"
android:theme="@style/AndorsTrailTheme_Blue"
android:hasFragileUserData="true"
android:preserveLegacyExternalStorage="true"
>
<activity
android:name=".activity.StartScreenActivity"
@@ -61,6 +63,16 @@
<activity android:name=".activity.BulkSelectionInterface" android:theme="@style/AndorsTrailDialogTheme_Blue" />
<activity android:name=".activity.SkillInfoActivity" android:theme="@style/AndorsTrailDialogTheme_Blue" />
<activity android:name=".activity.DisplayWorldMapActivity" />
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.gpl.rpg.AndorsTrail.fileprovider"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/fileprovider" />
</provider>
</application>
</manifest>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path path="." name="external" />
<external-files-path path="." name="external_files" />
<files-path path="." name="files" />
<root-path path="." name="root" />
</paths>

View File

@@ -7,6 +7,7 @@ import java.util.Locale;
import com.gpl.rpg.AndorsTrail.context.ControllerContext;
import com.gpl.rpg.AndorsTrail.context.WorldContext;
import com.gpl.rpg.AndorsTrail.controller.Constants;
import com.gpl.rpg.AndorsTrail.savegames.Savegames;
import com.gpl.rpg.AndorsTrail.util.Pair;
import android.annotation.SuppressLint;
@@ -118,9 +119,7 @@ public final class AndorsTrailApplication extends Application {
super.onCreate();
if ( DEVELOPMENT_DEBUGMESSAGES && isExternalStorageWritable() ) {
File root = Environment.getExternalStorageDirectory();
File appDirectory = new File(root, Constants.FILENAME_SAVEGAME_DIRECTORY);
File appDirectory = Savegames.getExternalDirectory(getApplicationContext(), Constants.FILENAME_SAVEGAME_DIRECTORY);
File logDirectory = new File( appDirectory, "log" );
File logFile = new File( logDirectory, "logcat" + System.currentTimeMillis() + ".txt" );

View File

@@ -46,6 +46,7 @@ import com.gpl.rpg.AndorsTrail.model.item.Inventory;
import com.gpl.rpg.AndorsTrail.model.item.ItemType;
import com.gpl.rpg.AndorsTrail.model.item.Loot;
import com.gpl.rpg.AndorsTrail.model.map.MapObject;
import com.gpl.rpg.AndorsTrail.savegames.Savegames;
import com.gpl.rpg.AndorsTrail.util.ThemeHelper;
import com.gpl.rpg.AndorsTrail.view.CustomDialogFactory;
import com.gpl.rpg.AndorsTrail.view.ItemContainerAdapter;
@@ -332,7 +333,17 @@ public final class Dialogs {
String text = currentActivity.getResources().getString(R.string.dialog_newversion_message);
if (!hasPermissions(currentActivity)) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
if (hasPermissions(currentActivity)) {
// TODO: Unterscheiden, ob Quell- und/oder
if (Savegames.getUsedSavegameSlots(currentActivity).size() == 0) {
text += "";
} else {
text += "";
}
}
}
else if (!hasPermissions(currentActivity)) {
text += currentActivity.getResources().getString(R.string.dialog_newversion_permission_information);
}

View File

@@ -1,6 +1,7 @@
package com.gpl.rpg.AndorsTrail.activity;
import java.io.File;
import java.io.FileNotFoundException;
import com.gpl.rpg.AndorsTrail.AndorsTrailApplication;
import com.gpl.rpg.AndorsTrail.R;
@@ -15,7 +16,9 @@ import com.gpl.rpg.AndorsTrail.util.ThemeHelper;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.content.FileProvider;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
@@ -85,7 +88,7 @@ public final class DisplayWorldMapActivity extends AndorsTrailBaseActivity {
Coord offsetWorldmapTo;
@SuppressLint("NewApi")
private void update() {
File worldmap = WorldMapController.getCombinedWorldMapFile(worldMapSegmentName);
File worldmap = WorldMapController.getCombinedWorldMapFile(this, worldMapSegmentName);
if (!worldmap.exists()) {
Toast.makeText(this, getResources().getString(R.string.menu_button_worldmap_failed), Toast.LENGTH_LONG).show();
@@ -104,14 +107,15 @@ public final class DisplayWorldMapActivity extends AndorsTrailBaseActivity {
PredefinedMap predefinedMap = world.maps.findPredefinedMap(map.mapName);
if (predefinedMap == null) continue;
if (!predefinedMap.visited) continue;
if (!WorldMapController.fileForMapExists(predefinedMap)) continue;
if (!WorldMapController.fileForMapExists(this, predefinedMap)) continue;
offsetWorldmapTo.x = Math.min(offsetWorldmapTo.x, map.worldPosition.x);
offsetWorldmapTo.y = Math.min(offsetWorldmapTo.y, map.worldPosition.y);
}
String url = "file://" + worldmap.getAbsolutePath() + '?'
// String url = "content://com.gpl.rpg.AndorsTrail.fileprovider/worldmap" + worldmap.getAbsolutePath() + '?'
// FileProvider.getUri
Uri uri = FileProvider.getUriForFile(this, "com.gpl.rpg.AndorsTrail.fileprovider", worldmap);
String url = uri.toString() + '?'
+ (world.model.player.position.x + map.worldPosition.x) * WorldMapController.WORLDMAP_DISPLAY_TILESIZE
+ ','
+ (world.model.player.position.y + map.worldPosition.y-1) * WorldMapController.WORLDMAP_DISPLAY_TILESIZE;

View File

@@ -70,9 +70,9 @@ public final class LoadSaveActivity extends AndorsTrailBaseActivity implements O
ViewGroup newSlotContainer = (ViewGroup) findViewById(R.id.loadsave_save_to_new_slot_container);
Button createNewSlot = (Button) findViewById(R.id.loadsave_save_to_new_slot);
addSavegameSlotButtons(slotList, params, Savegames.getUsedSavegameSlots());
addSavegameSlotButtons(slotList, params, Savegames.getUsedSavegameSlots(this));
checkAndRequestPermissions();
// checkAndRequestPermissions();
if (!isLoading) {
createNewSlot.setTag(SLOT_NUMBER_CREATE_NEW_SLOT);
@@ -137,7 +137,7 @@ public final class LoadSaveActivity extends AndorsTrailBaseActivity implements O
public void loadsave(int slot) {
if (slot == SLOT_NUMBER_CREATE_NEW_SLOT) {
List<Integer> usedSlots = Savegames.getUsedSavegameSlots();
List<Integer> usedSlots = Savegames.getUsedSavegameSlots(this);
if (usedSlots.isEmpty()) slot = SLOT_NUMBER_FIRST_SLOT;
else slot = Collections.max(usedSlots) + 1;
}
@@ -152,7 +152,7 @@ public final class LoadSaveActivity extends AndorsTrailBaseActivity implements O
private String getConfirmOverwriteQuestion(int slot) {
if (isLoading) return null;
if (slot == SLOT_NUMBER_CREATE_NEW_SLOT) return null; // if we're creating a new slot
if (!Savegames.getSlotFile(slot).exists()) return null;
if (!Savegames.getSlotFile(slot, this).exists()) return null;
if (preferences.displayOverwriteSavegame == AndorsTrailPreferences.CONFIRM_OVERWRITE_SAVEGAME_ALWAYS) {
return getString(R.string.loadsave_save_overwrite_confirmation_all);
@@ -192,7 +192,7 @@ public final class LoadSaveActivity extends AndorsTrailBaseActivity implements O
if (isLoading) {
if(!Savegames.getSlotFile(slot).exists()) {
if(!Savegames.getSlotFile(slot, this).exists()) {
showErrorLoadingEmptySlot();
} else {
final FileHeader header = Savegames.quickload(this, slot);

View File

@@ -190,7 +190,7 @@ public class StartScreenActivity_MainMenu extends Fragment {
});
}
boolean hasSavegames = !Savegames.getUsedSavegameSlots().isEmpty();
boolean hasSavegames = !Savegames.getUsedSavegameSlots(getActivity()).isEmpty();
startscreen_load.setEnabled(hasSavegames);
}
@@ -199,13 +199,29 @@ public class StartScreenActivity_MainMenu extends Fragment {
@TargetApi(23)
public static void checkAndRequestPermissions(final Activity activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (activity.getApplicationContext().checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
activity.requestPermissions(new String[] {Manifest.permission.READ_EXTERNAL_STORAGE}, READ_EXTERNAL_STORAGE_REQUEST);
}
if (activity.getApplicationContext().checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
activity.requestPermissions(new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, WRITE_EXTERNAL_STORAGE_REQUEST);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) {
String info = "";
if (activity.getApplicationContext().checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED
&& activity.getApplicationContext().checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
info = "RW permission on external folder exists";
if (Savegames.getUsedSavegameSlots(activity.getApplicationContext()).size() == 0) {
info += ". Destination folder is empty, migrating data.";
Savegames.MigrateData(activity.getApplicationContext());
} else {
info += ". Destination folder is not empty, no migration.";
}
} else {
info = "No rw permission on external folder";
}
final Dialog d = CustomDialogFactory.createDialog(activity,
"Info",
activity.getResources().getDrawable(android.R.drawable.ic_delete),
info,
null,
true);
CustomDialogFactory.addDismissButton(d, android.R.string.ok);
CustomDialogFactory.show(d);
}
}

View File

@@ -2,6 +2,7 @@ package com.gpl.rpg.AndorsTrail.context;
import java.lang.ref.WeakReference;
import android.content.Context;
import android.content.res.Resources;
import com.gpl.rpg.AndorsTrail.AndorsTrailApplication;
@@ -58,4 +59,5 @@ public final class ControllerContext {
public Resources getResources() {
return app.get().getResources();
}
public Context getContext() {return app.get().getApplicationContext(); }
}

View File

@@ -180,7 +180,7 @@ public final class MapController {
world.maps.worldMapRequiresUpdate = true;
if (!updateWorldmap) return;
WorldMapController.updateWorldMap(world, res);
WorldMapController.updateWorldMap(controllers.getContext(), world, res);
mapLayoutListeners.onMapTilesChanged(world.model.currentMaps.map, world.model.currentMaps.tileMap);
}

View File

@@ -128,7 +128,7 @@ public final class MovementController implements TimedMessageTask.Callback {
moveBlockedActors(newMap, model.currentMaps.tileMap);
refreshMonsterAggressiveness(newMap, model.player);
controllers.effectController.updateSplatters(newMap);
WorldMapController.updateWorldMap(world, res);
WorldMapController.updateWorldMap(controllers.getContext(), world, res);
}
private boolean mayMovePlayer() {

View File

@@ -30,6 +30,7 @@ import com.gpl.rpg.AndorsTrail.model.map.WorldMapSegment;
import com.gpl.rpg.AndorsTrail.model.map.WorldMapSegment.NamedWorldMapArea;
import com.gpl.rpg.AndorsTrail.model.map.WorldMapSegment.WorldMapSegmentMap;
import com.gpl.rpg.AndorsTrail.resource.tiles.TileCollection;
import com.gpl.rpg.AndorsTrail.savegames.Savegames;
import com.gpl.rpg.AndorsTrail.util.Coord;
import com.gpl.rpg.AndorsTrail.util.CoordRect;
import com.gpl.rpg.AndorsTrail.util.L;
@@ -40,12 +41,12 @@ public final class WorldMapController {
private static final int WORLDMAP_SCREENSHOT_TILESIZE = 8;
public static final int WORLDMAP_DISPLAY_TILESIZE = WORLDMAP_SCREENSHOT_TILESIZE;
public static void updateWorldMap(final WorldContext world, final Resources res) {
updateWorldMap(world, world.model.currentMaps.map, world.model.currentMaps.tileMap, world.model.currentMaps.tiles, res);
public static void updateWorldMap(Context context, final WorldContext world, final Resources res) {
updateWorldMap(context, world, world.model.currentMaps.map, world.model.currentMaps.tileMap, world.model.currentMaps.tiles, res);
}
private static void updateWorldMap(
final WorldContext world,
Context context, final WorldContext world,
final PredefinedMap map,
final LayeredTileMap mapTiles,
final TileCollection cachedTiles,
@@ -53,15 +54,15 @@ public final class WorldMapController {
final String worldMapSegmentName = world.maps.getWorldMapSegmentNameForMap(map.name);
if (worldMapSegmentName == null) return;
if (!shouldUpdateWorldMap(map, worldMapSegmentName, world.maps.worldMapRequiresUpdate)) return;
if (!shouldUpdateWorldMap(context, map, worldMapSegmentName, world.maps.worldMapRequiresUpdate)) return;
(new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... arg0) {
final MapRenderer renderer = new MapRenderer(world, map, mapTiles, cachedTiles);
try {
updateCachedBitmap(map, renderer);
updateWorldMapSegment(res, world, worldMapSegmentName);
updateCachedBitmap(context, map, renderer);
updateWorldMapSegment(context, res, world, worldMapSegmentName);
world.maps.worldMapRequiresUpdate = false;
if (AndorsTrailApplication.DEVELOPMENT_DEBUGMESSAGES) {
L.log("WorldMapController: Updated worldmap segment " + worldMapSegmentName + " for map " + map.name);
@@ -74,22 +75,22 @@ public final class WorldMapController {
}).execute();
}
private static boolean shouldUpdateWorldMap(PredefinedMap map, String worldMapSegmentName, boolean forceUpdate) {
private static boolean shouldUpdateWorldMap(Context context, PredefinedMap map, String worldMapSegmentName, boolean forceUpdate) {
if (forceUpdate) return true;
if (!map.visited) return true;
File file = getFileForMap(map, false);
File file = getFileForMap(context, map, false);
if (!file.exists()) return true;
file = getCombinedWorldMapFile(worldMapSegmentName);
file = getCombinedWorldMapFile(context, worldMapSegmentName);
if (!file.exists()) return true;
return false;
}
private static void updateCachedBitmap(PredefinedMap map, MapRenderer renderer) throws IOException {
ensureWorldmapDirectoryExists();
private static void updateCachedBitmap(Context context, PredefinedMap map, MapRenderer renderer) throws IOException {
ensureWorldmapDirectoryExists(context);
File file = getFileForMap(map, false);
File file = getFileForMap(context, map, false);
if (file.exists()) return;
Bitmap image = renderer.drawMap();
@@ -150,9 +151,8 @@ public final class WorldMapController {
}
}
private static void ensureWorldmapDirectoryExists() throws IOException {
File root = Environment.getExternalStorageDirectory();
File dir = new File(root, Constants.FILENAME_SAVEGAME_DIRECTORY);
private static void ensureWorldmapDirectoryExists(Context context) throws IOException {
File dir = Savegames.getExternalDirectory(context, Constants.FILENAME_SAVEGAME_DIRECTORY);
if (!dir.exists()) dir.mkdir();
dir = new File(dir, Constants.FILENAME_WORLDMAP_DIRECTORY);
if (!dir.exists()) dir.mkdir();
@@ -160,33 +160,32 @@ public final class WorldMapController {
File noMediaFile = new File(dir, ".nomedia");
if (!noMediaFile.exists()) noMediaFile.createNewFile();
}
public static boolean fileForMapExists(PredefinedMap map) {
public static boolean fileForMapExists(Context context, PredefinedMap map) {
if (map.lastSeenLayoutHash.length() > 0) {
return getPngFile(map.name + '.' + map.lastSeenLayoutHash).exists();
return getPngFile(context, map.name + '.' + map.lastSeenLayoutHash).exists();
}
return getPngFile(map.name).exists();
return getPngFile(context, map.name).exists();
}
private static File getFileForMap(PredefinedMap map, boolean verifyFileExists) {
private static File getFileForMap(Context context, PredefinedMap map, boolean verifyFileExists) {
if (map.lastSeenLayoutHash.length() > 0) {
File fileWithHash = getPngFile(map.name + '.' + map.lastSeenLayoutHash);
File fileWithHash = getPngFile(context, map.name + '.' + map.lastSeenLayoutHash);
if (!verifyFileExists) return fileWithHash;
else if (fileWithHash.exists()) return fileWithHash;
}
return getPngFile(map.name);
return getPngFile(context, map.name);
}
private static File getPngFile(String fileName) {
return new File(getWorldmapDirectory(), fileName + ".png");
private static File getPngFile(Context context, String fileName) {
return new File(getWorldmapDirectory(context), fileName + ".png");
}
private static File getWorldmapDirectory() {
File dir = Environment.getExternalStorageDirectory();
dir = new File(dir, Constants.FILENAME_SAVEGAME_DIRECTORY);
private static File getWorldmapDirectory(Context context) {
File dir = Savegames.getExternalDirectory(context, Constants.FILENAME_SAVEGAME_DIRECTORY);
return new File(dir, Constants.FILENAME_WORLDMAP_DIRECTORY);
}
public static File getCombinedWorldMapFile(String segmentName) {
return new File(getWorldmapDirectory(), Constants.FILENAME_WORLDMAP_HTMLFILE_PREFIX + segmentName + Constants.FILENAME_WORLDMAP_HTMLFILE_SUFFIX);
public static File getCombinedWorldMapFile(Context context, String segmentName) {
return new File(getWorldmapDirectory(context), Constants.FILENAME_WORLDMAP_HTMLFILE_PREFIX + segmentName + Constants.FILENAME_WORLDMAP_HTMLFILE_SUFFIX);
}
private static String getWorldMapSegmentAsHtml(Resources res, WorldContext world, String segmentName) {
private static String getWorldMapSegmentAsHtml(Context context, Resources res, WorldContext world, String segmentName) {
WorldMapSegment segment = world.maps.worldMapSegments.get(segmentName);
Map<String, File> displayedMapFilenamesPerMapName = new HashMap<String, File>(segment.maps.size());
@@ -195,7 +194,7 @@ public final class WorldMapController {
PredefinedMap predefinedMap = world.maps.findPredefinedMap(map.mapName);
if (predefinedMap == null) continue;
if (!predefinedMap.visited) continue;
File f = WorldMapController.getFileForMap(predefinedMap, true);
File f = WorldMapController.getFileForMap(context, predefinedMap, true);
if (!f.exists()) continue;
displayedMapFilenamesPerMapName.put(map.mapName, f);
@@ -294,9 +293,9 @@ public final class WorldMapController {
return new CoordRect(topLeft, new Size(bottomRight.x - topLeft.x, bottomRight.y - topLeft.y));
}
public static void updateWorldMapSegment(Resources res, WorldContext world, String segmentName) throws IOException {
String mapAsHtml = getWorldMapSegmentAsHtml(res, world, segmentName);
File outputFile = getCombinedWorldMapFile(segmentName);
public static void updateWorldMapSegment(Context context, Resources res, WorldContext world, String segmentName) throws IOException {
String mapAsHtml = getWorldMapSegmentAsHtml(context, res, world, segmentName);
File outputFile = getCombinedWorldMapFile(context, segmentName);
PrintWriter pw = new PrintWriter(outputFile);
pw.write(mapAsHtml);
pw.close();

View File

@@ -47,7 +47,7 @@ public class LegacySavegamesContentAdaptations {
if (segmentsCovered.contains(segment.name)) continue;
segmentsCovered.add(segment.name);
try {
WorldMapController.updateWorldMapSegment(res, world, segment.name);
WorldMapController.updateWorldMapSegment(controllers.getContext(), res, world, segment.name);
if (AndorsTrailApplication.DEVELOPMENT_DEBUGMESSAGES) {
L.log("Forcing generation of worldmap file for segment " + segment.name);
}

View File

@@ -19,8 +19,10 @@ import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import android.content.ContentProvider;
import android.content.Context;
import android.content.res.Resources;
import android.os.Build;
import android.os.Environment;
import android.os.SystemClock;
@@ -39,6 +41,59 @@ public final class Savegames {
private static long lastBackup = 0;
public static void MigrateData(Context context) {
try {
copy(new File(Environment.getExternalStorageDirectory(), Constants.CHEAT_DETECTION_FOLDER),
getExternalDirectory(context, Constants.CHEAT_DETECTION_FOLDER));
copy(new File(Environment.getExternalStorageDirectory(), Constants.FILENAME_SAVEGAME_DIRECTORY),
getExternalDirectory(context, Constants.FILENAME_SAVEGAME_DIRECTORY));
} catch (IOException e) {
L.log("Error migrating data: " + e.toString());
}
}
private static void copy(File sourceLocation, File targetLocation) throws IOException {
if (!sourceLocation.exists()) {
return;
}
if (sourceLocation.isDirectory()) {
copyDirectory(sourceLocation, targetLocation);
} else {
copyFile(sourceLocation, targetLocation);
}
}
private static void copyDirectory(File source, File target) throws IOException {
if (!target.exists()) {
target.mkdir();
}
for (String f : source.list()) {
copy(new File(source, f), new File(target, f));
}
}
private static void copyFile(File source, File target) throws IOException {
InputStream in = null;
OutputStream out = null;
try {
in = new FileInputStream(source);
out = new FileOutputStream(target);
byte[] buf = new byte[1024];
int length;
while ((length = in.read(buf)) > 0) {
out.write(buf, 0, length);
}
} finally {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
}
}
public static enum LoadSavegameResult {
success
, unknownError
@@ -71,7 +126,7 @@ public final class Savegames {
androidContext.deleteFile(Constants.FILENAME_SAVEGAME_QUICKSAVE);
writeCheatCheck(androidContext, savedVersion, id);
} else if (SystemClock.uptimeMillis() > lastBackup + 120000) {
writeBackup(savegame, id);
writeBackup(androidContext, savegame, id);
lastBackup = SystemClock.uptimeMillis();
}
}
@@ -83,9 +138,8 @@ public final class Savegames {
}
}
private static void writeBackup(byte[] savegame, String playerId) throws IOException {
File root = Environment.getExternalStorageDirectory();
File cheatDetectionFolder = new File(root, Constants.CHEAT_DETECTION_FOLDER);
private static void writeBackup(Context androidContext, byte[] savegame, String playerId) throws IOException {
File cheatDetectionFolder = getExternalDirectory(androidContext, Constants.CHEAT_DETECTION_FOLDER);
if (!cheatDetectionFolder.exists()) cheatDetectionFolder.mkdir();
File backupFile = new File(cheatDetectionFolder, playerId + "X");
FileOutputStream fileOutputStream = new FileOutputStream(backupFile);
@@ -111,7 +165,7 @@ public final class Savegames {
if (!saveWorld(world, androidContext, SLOT_QUICKSAVE)) {
return LoadSavegameResult.unknownError;
}
getSlotFile(slot).delete();
getSlotFile(slot, androidContext).delete();
writeCheatCheck(androidContext, DENY_LOADING_BECAUSE_GAME_IS_CURRENTLY_PLAYED, fh.playerId);
}
return result;
@@ -129,8 +183,7 @@ public final class Savegames {
private static boolean triedToCheat(Context androidContext, FileHeader fh) throws IOException {
long savedVersionToCheck = 0;
File root = Environment.getExternalStorageDirectory();
File cheatDetectionFolder = new File(root, Constants.CHEAT_DETECTION_FOLDER);
File cheatDetectionFolder = getExternalDirectory(androidContext, Constants.CHEAT_DETECTION_FOLDER);
if (!cheatDetectionFolder.exists()) cheatDetectionFolder.mkdir();
File cheatDetectionFile = new File(cheatDetectionFolder, fh.playerId);
if (cheatDetectionFile.exists()) {
@@ -172,31 +225,35 @@ public final class Savegames {
if (slot == SLOT_QUICKSAVE) {
return androidContext.openFileOutput(Constants.FILENAME_SAVEGAME_QUICKSAVE, Context.MODE_PRIVATE);
} else {
ensureSavegameDirectoryExists();
return new FileOutputStream(getSlotFile(slot));
ensureSavegameDirectoryExists(androidContext);
return new FileOutputStream(getSlotFile(slot, androidContext));
}
}
private static void ensureSavegameDirectoryExists() {
File root = Environment.getExternalStorageDirectory();
File dir = new File(root, Constants.FILENAME_SAVEGAME_DIRECTORY);
private static void ensureSavegameDirectoryExists(Context context) {
File dir = getExternalDirectory(context, Constants.FILENAME_SAVEGAME_DIRECTORY);
if (!dir.exists()) dir.mkdir();
}
private static FileInputStream getInputFile(Context androidContext, int slot) throws IOException {
if (slot == SLOT_QUICKSAVE) {
return androidContext.openFileInput(Constants.FILENAME_SAVEGAME_QUICKSAVE);
} else {
return new FileInputStream(getSlotFile(slot));
return new FileInputStream(getSlotFile(slot, androidContext));
}
}
public static File getSlotFile(int slot) {
File root = getSavegameDirectory();
public static File getSlotFile(int slot, Context context) {
File root = getExternalDirectory(context, Constants.FILENAME_SAVEGAME_DIRECTORY);
return new File(root, Constants.FILENAME_SAVEGAME_FILENAME_PREFIX + slot);
}
private static File getSavegameDirectory() {
File root = Environment.getExternalStorageDirectory();
return new File(root, Constants.FILENAME_SAVEGAME_DIRECTORY);
public static File getExternalDirectory(Context context, String name) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) {
return context.getExternalFilesDir(name);
}
else {
File root = Environment.getExternalStorageDirectory();
return new File(root, name);
}
}
public static void saveWorld(WorldContext world, OutputStream outStream, String displayInfo) throws IOException {
@@ -240,7 +297,7 @@ public final class Savegames {
public static FileHeader quickload(Context androidContext, int slot) {
try {
if (slot != SLOT_QUICKSAVE) {
File f = getSlotFile(slot);
File f = getSlotFile(slot, androidContext);
if (!f.exists()) return null;
}
FileInputStream fos = getInputFile(androidContext, slot);
@@ -255,8 +312,7 @@ public final class Savegames {
}
private static void writeCheatCheck(Context androidContext, long savedVersion, String playerId) throws IOException {
File root = Environment.getExternalStorageDirectory();
File cheatDetectionFolder = new File(root, Constants.CHEAT_DETECTION_FOLDER);
File cheatDetectionFolder = getExternalDirectory(androidContext, Constants.CHEAT_DETECTION_FOLDER);
if (!cheatDetectionFolder.exists()) cheatDetectionFolder.mkdir();
File cheatDetectionFile = new File(cheatDetectionFolder, playerId);
FileOutputStream fileOutputStream = new FileOutputStream(cheatDetectionFile);
@@ -273,10 +329,11 @@ public final class Savegames {
}
private static final Pattern savegameFilenamePattern = Pattern.compile(Constants.FILENAME_SAVEGAME_FILENAME_PREFIX + "(\\d+)");
public static List<Integer> getUsedSavegameSlots() {
public static List<Integer> getUsedSavegameSlots(Context context) {
try {
final List<Integer> result = new ArrayList<Integer>();
getSavegameDirectory().listFiles(new FilenameFilter() {
getExternalDirectory(context, Constants.FILENAME_SAVEGAME_DIRECTORY).listFiles(new FilenameFilter() {
@Override
public boolean accept(File f, String filename) {
Matcher m = savegameFilenamePattern.matcher(filename);