Merge branch 'pulls/1829009049/48'

This commit is contained in:
Nut.andor
2022-11-05 00:43:35 +01:00
16 changed files with 2223 additions and 1272 deletions

View File

@@ -46,14 +46,15 @@ import com.gpl.rpg.AndorsTrail.model.item.Loot;
import com.gpl.rpg.AndorsTrail.model.map.MapObject;
import com.gpl.rpg.AndorsTrail.util.ThemeHelper;
import com.gpl.rpg.AndorsTrail.view.CustomDialogFactory;
import com.gpl.rpg.AndorsTrail.view.CustomDialogFactory.CustomDialog;
import com.gpl.rpg.AndorsTrail.view.ItemContainerAdapter;
public final class Dialogs {
private static void showDialogAndPause(Dialog d, final ControllerContext context) {
private static void showDialogAndPause(CustomDialog d, final ControllerContext context) {
showDialogAndPause(d, context, null);
}
private static void showDialogAndPause(Dialog d, final ControllerContext context, final OnDismissListener onDismiss) {
private static void showDialogAndPause(CustomDialog d, final ControllerContext context, final OnDismissListener onDismiss) {
context.gameRoundController.pause();
CustomDialogFactory.setDismissListener(d, new OnDismissListener() {
@Override
@@ -195,7 +196,7 @@ public final class Dialogs {
// itemList.setPadding(20, 0, 20, 20);
itemList.setAdapter(new ItemContainerAdapter(mainActivity, world.tileManager, combinedLoot.items, world.model.player));
final Dialog d = CustomDialogFactory.createDialog(mainActivity,
final CustomDialog d = CustomDialogFactory.createDialog(mainActivity,
mainActivity.getResources().getString(title),
mainActivity.getResources().getDrawable(R.drawable.ui_icon_equipment),
msg,
@@ -249,7 +250,7 @@ public final class Dialogs {
}
public static void showHeroDied(final MainActivity mainActivity, final ControllerContext controllers) {
final Dialog d = CustomDialogFactory.createDialog(mainActivity,
final CustomDialog d = CustomDialogFactory.createDialog(mainActivity,
mainActivity.getResources().getString(R.string.dialog_game_over_title),
mainActivity.getResources().getDrawable(R.drawable.ui_icon_combat),
mainActivity.getResources().getString(R.string.dialog_game_over_text),
@@ -286,7 +287,7 @@ public final class Dialogs {
}
public static void showConfirmRest(final Activity currentActivity, final ControllerContext controllerContext, final MapObject area) {
final Dialog d = CustomDialogFactory.createDialog(currentActivity,
final CustomDialog d = CustomDialogFactory.createDialog(currentActivity,
currentActivity.getResources().getString(R.string.dialog_rest_title),
null,
currentActivity.getResources().getString(R.string.dialog_rest_confirm_message),
@@ -310,7 +311,7 @@ public final class Dialogs {
// .setMessage(R.string.dialog_rest_message)
// .setNeutralButton(android.R.string.ok, null)
// .create();
final Dialog d = CustomDialogFactory.createDialog(currentActivity,
final CustomDialog d = CustomDialogFactory.createDialog(currentActivity,
currentActivity.getResources().getString(R.string.dialog_rest_title),
null,
currentActivity.getResources().getString(R.string.dialog_rest_message),
@@ -336,7 +337,7 @@ public final class Dialogs {
text += currentActivity.getResources().getString(R.string.dialog_newversion_permission_information);
}
final Dialog d = CustomDialogFactory.createDialog(currentActivity,
final CustomDialog d = CustomDialogFactory.createDialog(currentActivity,
currentActivity.getResources().getString(R.string.dialog_newversion_title),
null,
text,
@@ -371,7 +372,7 @@ public final class Dialogs {
}
if (!world.model.statistics.hasUnlimitedSaves()) {
final Dialog d = CustomDialogFactory.createDialog(mainActivity,
final CustomDialog d = CustomDialogFactory.createDialog(mainActivity,
mainActivity.getResources().getString(R.string.menu_save_switch_character_title),
null,
mainActivity.getResources().getString(R.string.menu_save_switch_character),
@@ -460,7 +461,7 @@ public final class Dialogs {
itemList.setAdapter(new ArrayAdapter<String>(context, R.layout.combatlog_row, android.R.id.text1, combatLogMessages));
view = itemList;
final Dialog d = CustomDialogFactory.createDialog(context,
final CustomDialog d = CustomDialogFactory.createDialog(context,
context.getResources().getString(R.string.combat_log_title),
context.getResources().getDrawable(R.drawable.ui_icon_combat),
null,

View File

@@ -23,6 +23,7 @@ import com.gpl.rpg.AndorsTrail.controller.ItemController;
import com.gpl.rpg.AndorsTrail.model.item.ItemType;
import com.gpl.rpg.AndorsTrail.util.ThemeHelper;
import com.gpl.rpg.AndorsTrail.view.CustomDialogFactory;
import com.gpl.rpg.AndorsTrail.view.CustomDialogFactory.CustomDialog;
/**
* @author ejwessel
@@ -206,7 +207,7 @@ public final class BulkSelectionInterface extends AndorsTrailBaseActivity implem
// })
// .setNegativeButton(android.R.string.no, null)
// .show();
final Dialog d = CustomDialogFactory.createDialog(v.getContext(),
final CustomDialog d = CustomDialogFactory.createDialog(v.getContext(),
v.getContext().getResources().getString(R.string.bulkselection_sell_confirmation_title),
v.getContext().getResources().getDrawable(android.R.drawable.ic_dialog_info),
message,

View File

@@ -1,16 +1,29 @@
package com.gpl.rpg.AndorsTrail.activity;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import android.Manifest;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.Dialog;
import android.content.ClipData;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.RequiresApi;
import android.support.v4.provider.DocumentFile;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
@@ -22,241 +35,732 @@ import android.widget.Toast;
import com.gpl.rpg.AndorsTrail.AndorsTrailApplication;
import com.gpl.rpg.AndorsTrail.AndorsTrailPreferences;
import com.gpl.rpg.AndorsTrail.R;
import com.gpl.rpg.AndorsTrail.controller.Constants;
import com.gpl.rpg.AndorsTrail.model.ModelContainer;
import com.gpl.rpg.AndorsTrail.resource.tiles.TileManager;
import com.gpl.rpg.AndorsTrail.savegames.Savegames;
import com.gpl.rpg.AndorsTrail.savegames.Savegames.FileHeader;
import com.gpl.rpg.AndorsTrail.util.AndroidStorage;
import com.gpl.rpg.AndorsTrail.util.ThemeHelper;
import com.gpl.rpg.AndorsTrail.view.CustomDialogFactory;
import com.gpl.rpg.AndorsTrail.view.CustomDialogFactory.CustomDialog;
public final class LoadSaveActivity extends AndorsTrailBaseActivity implements OnClickListener {
private boolean isLoading = true;
private static final int SLOT_NUMBER_CREATE_NEW_SLOT = -1;
private static final int SLOT_NUMBER_FIRST_SLOT = 1;
private ModelContainer model;
private TileManager tileManager;
private AndorsTrailPreferences preferences;
private boolean isLoading = true;
//region special slot numbers
private static final int SLOT_NUMBER_CREATE_NEW_SLOT = -1;
public static final int SLOT_NUMBER_EXPORT_SAVEGAMES = -2;
public static final int SLOT_NUMBER_IMPORT_SAVEGAMES = -3;
public static final int SLOT_NUMBER_IMPORT_WORLDMAP = -4;
private static final int SLOT_NUMBER_FIRST_SLOT = 1;
//endregion
private ModelContainer model;
private TileManager tileManager;
private AndorsTrailPreferences preferences;
@Override
public void onCreate(Bundle savedInstanceState) {
setTheme(ThemeHelper.getDialogTheme());
super.onCreate(savedInstanceState);
@Override
public void onCreate(Bundle savedInstanceState) {
setTheme(ThemeHelper.getDialogTheme());
super.onCreate(savedInstanceState);
final AndorsTrailApplication app = AndorsTrailApplication.getApplicationFromActivity(this);
app.setWindowParameters(this);
this.model = app.getWorld().model;
this.preferences = app.getPreferences();
this.tileManager = app.getWorld().tileManager;
final AndorsTrailApplication app = AndorsTrailApplication.getApplicationFromActivity(this);
app.setWindowParameters(this);
this.model = app.getWorld().model;
this.preferences = app.getPreferences();
this.tileManager = app.getWorld().tileManager;
String loadsave = getIntent().getData().getLastPathSegment();
isLoading = (loadsave.equalsIgnoreCase("load"));
String loadsave = getIntent().getData().getLastPathSegment();
isLoading = (loadsave.equalsIgnoreCase("load"));
setContentView(R.layout.loadsave);
setContentView(R.layout.loadsave);
TextView tv = (TextView) findViewById(R.id.loadsave_title);
if (isLoading) {
tv.setCompoundDrawablesWithIntrinsicBounds(android.R.drawable.ic_menu_search, 0, 0, 0);
tv.setText(R.string.loadsave_title_load);
} else {
tv.setCompoundDrawablesWithIntrinsicBounds(android.R.drawable.ic_menu_save, 0, 0, 0);
tv.setText(R.string.loadsave_title_save);
}
TextView tv = (TextView) findViewById(R.id.loadsave_title);
if (isLoading) {
tv.setCompoundDrawablesWithIntrinsicBounds(android.R.drawable.ic_menu_search, 0, 0, 0);
tv.setText(R.string.loadsave_title_load);
} else {
tv.setCompoundDrawablesWithIntrinsicBounds(android.R.drawable.ic_menu_save, 0, 0, 0);
tv.setText(R.string.loadsave_title_save);
}
ViewGroup slotList = (ViewGroup) findViewById(R.id.loadsave_slot_list);
Button slotTemplateButton = (Button) findViewById(R.id.loadsave_slot_n);
LayoutParams params = slotTemplateButton.getLayoutParams();
slotList.removeView(slotTemplateButton);
ViewGroup slotList = (ViewGroup) findViewById(R.id.loadsave_slot_list);
Button slotTemplateButton = (Button) findViewById(R.id.loadsave_slot_n);
LayoutParams params = slotTemplateButton.getLayoutParams();
slotList.removeView(slotTemplateButton);
ViewGroup newSlotContainer = (ViewGroup) findViewById(R.id.loadsave_save_to_new_slot_container);
Button createNewSlot = (Button) findViewById(R.id.loadsave_save_to_new_slot);
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(this));
Button exportSaves = (Button) findViewById(R.id.loadsave_export_save);
Button importSaves = (Button) findViewById(R.id.loadsave_import_save);
Button importWorldmap = (Button) findViewById(R.id.loadsave_import_worldmap);
checkAndRequestPermissions();
if (!isLoading) {
createNewSlot.setTag(SLOT_NUMBER_CREATE_NEW_SLOT);
createNewSlot.setOnClickListener(this);
newSlotContainer.setVisibility(View.VISIBLE);
} else {
newSlotContainer.setVisibility(View.GONE);
}
}
exportSaves.setTag(SLOT_NUMBER_EXPORT_SAVEGAMES);
importSaves.setTag(SLOT_NUMBER_IMPORT_SAVEGAMES);
importWorldmap.setTag(SLOT_NUMBER_IMPORT_WORLDMAP);
private static final int READ_EXTERNAL_STORAGE_REQUEST=1;
private static final int WRITE_EXTERNAL_STORAGE_REQUEST=2;
@TargetApi(23)
private void checkAndRequestPermissions() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) {
if (getApplicationContext().checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
this.requestPermissions(new String[] {Manifest.permission.READ_EXTERNAL_STORAGE}, READ_EXTERNAL_STORAGE_REQUEST);
}
if (getApplicationContext().checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
this.requestPermissions(new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, WRITE_EXTERNAL_STORAGE_REQUEST);
}
}
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (grantResults.length > 0 && grantResults[0] != PackageManager.PERMISSION_GRANTED) {
Toast.makeText(this, R.string.storage_permissions_mandatory, Toast.LENGTH_LONG).show();
((AndorsTrailApplication)getApplication()).discardWorld();
finish();
}
}
private void addSavegameSlotButtons(ViewGroup parent, LayoutParams params, List<Integer> usedSavegameSlots) {
int unused = 1;
for (int slot : usedSavegameSlots) {
final FileHeader header = Savegames.quickload(this, slot);
if (header == null) continue;
while (unused < slot){
Button b = new Button(this);
b.setLayoutParams(params);
b.setTag(unused);
b.setOnClickListener(this);
b.setText(getString(R.string.loadsave_empty_slot, unused));
tileManager.setImageViewTileForPlayer(getResources(), b, header.iconID);
parent.addView(b, params);
unused++;
}
unused++;
Button b = new Button(this);
b.setLayoutParams(params);
b.setTag(slot);
b.setOnClickListener(this);
b.setText(slot + ". " + header.describe());
tileManager.setImageViewTileForPlayer(getResources(), b, header.iconID);
parent.addView(b, params);
}
}
public void loadsave(int slot) {
if (slot == SLOT_NUMBER_CREATE_NEW_SLOT) {
List<Integer> usedSlots = Savegames.getUsedSavegameSlots(this);
if (usedSlots.isEmpty()) slot = SLOT_NUMBER_FIRST_SLOT;
else slot = Collections.max(usedSlots) + 1;
}
if (slot < SLOT_NUMBER_FIRST_SLOT) slot = SLOT_NUMBER_FIRST_SLOT;
Intent i = new Intent();
i.putExtra("slot", slot);
setResult(Activity.RESULT_OK, i);
LoadSaveActivity.this.finish();
}
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, this).exists()) return null;
if (preferences.displayOverwriteSavegame == AndorsTrailPreferences.CONFIRM_OVERWRITE_SAVEGAME_ALWAYS) {
return getString(R.string.loadsave_save_overwrite_confirmation_all);
}
if (preferences.displayOverwriteSavegame == AndorsTrailPreferences.CONFIRM_OVERWRITE_SAVEGAME_NEVER) {
return null;
}
final String currentPlayerName = model.player.getName();
final FileHeader header = Savegames.quickload(this, slot);
if (header == null) return null;
final String savedPlayerName = header.playerName;
if (currentPlayerName.equals(savedPlayerName)) return null; // if the names match
return getString(R.string.loadsave_save_overwrite_confirmation, savedPlayerName, currentPlayerName);
}
@Override
public void onClick(View view) {
final int slot = (Integer) view.getTag();
if (!isLoading && slot != SLOT_NUMBER_CREATE_NEW_SLOT && AndorsTrailApplication.CURRENT_VERSION == AndorsTrailApplication.DEVELOPMENT_INCOMPATIBLE_SAVEGAME_VERSION) {
final FileHeader header = Savegames.quickload(this, slot);
if (header != null && header.fileversion != AndorsTrailApplication.DEVELOPMENT_INCOMPATIBLE_SAVEGAME_VERSION) {
final Dialog d = CustomDialogFactory.createDialog(this,
"Overwriting not allowed",
getResources().getDrawable(android.R.drawable.ic_dialog_alert),
"You are currently using a development version of Andor's trail. Overwriting a regular savegame is not allowed in development mode.",
null,
true);
CustomDialogFactory.addDismissButton(d, android.R.string.ok);
CustomDialogFactory.show(d);
return;
}
}
ViewGroup exportImportContainer = (ViewGroup) findViewById(R.id.loadsave_export_import_save_container);
if (isLoading) {
if(!Savegames.getSlotFile(slot, this).exists()) {
showErrorLoadingEmptySlot();
} else {
final FileHeader header = Savegames.quickload(this, slot);
if (header != null && !header.hasUnlimitedSaves) {
showSlotGetsDeletedOnLoadWarning(slot);
} else {
loadsave(slot);
}
}
} else {
final String message = getConfirmOverwriteQuestion(slot);
if (message != null) {
showConfirmoverwriteQuestion(slot, message);
} else {
loadsave(slot);
}
}
}
addSavegameSlotButtons(slotList, params, Savegames.getUsedSavegameSlots(this));
private void showErrorLoadingEmptySlot() {
final Dialog d = CustomDialogFactory.createDialog(this,
getString(R.string.startscreen_error_loading_game),
getResources().getDrawable(android.R.drawable.ic_dialog_alert),
getString(R.string.startscreen_error_loading_empty_slot),
null,
true);
CustomDialogFactory.addDismissButton(d, android.R.string.ok);
CustomDialogFactory.show(d);
}
checkAndRequestPermissions();
private void showSlotGetsDeletedOnLoadWarning(final int slot) {
final Dialog d = CustomDialogFactory.createDialog(this,
getString(R.string.startscreen_attention_slot_gets_delete_on_load),
getResources().getDrawable(android.R.drawable.ic_dialog_alert),
getString(R.string.startscreen_attention_message_slot_gets_delete_on_load),
null,
true);
CustomDialogFactory.addButton(d, android.R.string.ok, new View.OnClickListener() {
@Override
public void onClick(View v) {
loadsave(slot);
}
});
CustomDialogFactory.show(d);
}
if (!isLoading) {
createNewSlot.setTag(SLOT_NUMBER_CREATE_NEW_SLOT);
createNewSlot.setOnClickListener(this);
newSlotContainer.setVisibility(View.VISIBLE);
exportImportContainer.setVisibility(View.GONE);
} else {
newSlotContainer.setVisibility(View.GONE);
private void showConfirmoverwriteQuestion(final int slot, String message) {
final String title =
getString(R.string.loadsave_save_overwrite_confirmation_title) + ' '
+ getString(R.string.loadsave_save_overwrite_confirmation_slot, slot);
final Dialog d = CustomDialogFactory.createDialog(this,
title,
getResources().getDrawable(android.R.drawable.ic_dialog_alert),
message,
null,
true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
exportSaves.setOnClickListener(this);
importSaves.setOnClickListener(this);
importWorldmap.setOnClickListener(this);
exportImportContainer.setVisibility(View.VISIBLE);
boolean hasSavegames = !Savegames.getUsedSavegameSlots(this).isEmpty();
exportSaves.setEnabled(hasSavegames);
}
else{
exportImportContainer.setVisibility(View.GONE);
}
}
}
private static final int READ_EXTERNAL_STORAGE_REQUEST = 1;
private static final int WRITE_EXTERNAL_STORAGE_REQUEST = 2;
private void checkAndRequestPermissions() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) {
if (getApplicationContext().checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
this.requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, READ_EXTERNAL_STORAGE_REQUEST);
}
if (getApplicationContext().checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
this.requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, WRITE_EXTERNAL_STORAGE_REQUEST);
}
}
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (grantResults.length > 0 && grantResults[0] != PackageManager.PERMISSION_GRANTED) {
Toast.makeText(this, R.string.storage_permissions_mandatory, Toast.LENGTH_LONG).show();
((AndorsTrailApplication) getApplication()).discardWorld();
finish();
}
}
private void addSavegameSlotButtons(ViewGroup parent, LayoutParams params, List<Integer> usedSavegameSlots) {
int unused = 1;
for (int slot : usedSavegameSlots) {
final FileHeader header = Savegames.quickload(this, slot);
if (header == null) continue;
while (unused < slot) {
Button b = new Button(this);
b.setLayoutParams(params);
b.setTag(unused);
b.setOnClickListener(this);
b.setText(getString(R.string.loadsave_empty_slot, unused));
tileManager.setImageViewTileForPlayer(getResources(), b, header.iconID);
parent.addView(b, params);
unused++;
}
unused++;
Button b = new Button(this);
b.setLayoutParams(params);
b.setTag(slot);
b.setOnClickListener(this);
b.setText(slot + ". " + header.describe());
tileManager.setImageViewTileForPlayer(getResources(), b, header.iconID);
parent.addView(b, params);
}
}
private void cancelLoadSaveActivity(int slot){
completeLoadSaveActivity(slot, false);
}
private void completeLoadSaveActivity(int slot) {
completeLoadSaveActivity(slot, true);
}
private void completeLoadSaveActivity(int slot, boolean success) {
Intent i = new Intent();
if (slot == SLOT_NUMBER_CREATE_NEW_SLOT) {
slot = getFirstFreeSlot();
} else if (slot == SLOT_NUMBER_EXPORT_SAVEGAMES
|| slot == SLOT_NUMBER_IMPORT_SAVEGAMES
|| slot == SLOT_NUMBER_IMPORT_WORLDMAP) {
i.putExtra("import_export", true);
if(slot == SLOT_NUMBER_IMPORT_WORLDMAP){
i.putExtra("import_worldmap", true);
}
if(slot == SLOT_NUMBER_IMPORT_SAVEGAMES){
i.putExtra("import_savegames", true);
}
if(slot == SLOT_NUMBER_EXPORT_SAVEGAMES){
i.putExtra("export", true);
}
} else if (slot < SLOT_NUMBER_FIRST_SLOT)
slot = SLOT_NUMBER_FIRST_SLOT;
i.putExtra("slot", slot);
if(success) setResult(Activity.RESULT_OK, i);
else setResult(Activity.RESULT_CANCELED, i);
LoadSaveActivity.this.finish();
}
private int getFirstFreeSlot() {
int slot;
List<Integer> usedSlots = Savegames.getUsedSavegameSlots(this);
if (usedSlots.isEmpty())
slot = SLOT_NUMBER_FIRST_SLOT;
else slot = Collections.max(usedSlots) + 1;
return slot;
}
private String getConfirmOverwriteQuestion(int slot) {
if (isLoading)
return null;
return getConfirmOverwriteQuestionIgnoringLoading(slot);
}
private String getConfirmOverwriteQuestionIgnoringLoading(int slot) {
if (slot == SLOT_NUMBER_CREATE_NEW_SLOT)
return null;//creating a new savegame
if (!Savegames.getSlotFile(slot, this).exists())
return null;//nothing in slot to overwrite
if (preferences.displayOverwriteSavegame == AndorsTrailPreferences.CONFIRM_OVERWRITE_SAVEGAME_ALWAYS) {
return getString(R.string.loadsave_save_overwrite_confirmation_all);
}
if (preferences.displayOverwriteSavegame == AndorsTrailPreferences.CONFIRM_OVERWRITE_SAVEGAME_NEVER) {
return null;
}
final String currentPlayerName = model.player.getName();
final FileHeader header = Savegames.quickload(this, slot);
if (header == null) return null;
final String savedPlayerName = header.playerName;
if (currentPlayerName.equals(savedPlayerName)) return null; //if the names match
return getString(R.string.loadsave_save_overwrite_confirmation, savedPlayerName, currentPlayerName);
}
@Override
public void onClick(View view) {
final int slot = (Integer) view.getTag();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
switch (slot) {
case SLOT_NUMBER_IMPORT_WORLDMAP:
clickImportWorldmap();
return;
case SLOT_NUMBER_IMPORT_SAVEGAMES:
clickImportSaveGames();
return;
case SLOT_NUMBER_EXPORT_SAVEGAMES:
clickExportSaveGames();
return;
}
}
if (!isLoading
&& slot != SLOT_NUMBER_CREATE_NEW_SLOT
&& AndorsTrailApplication.CURRENT_VERSION == AndorsTrailApplication.DEVELOPMENT_INCOMPATIBLE_SAVEGAME_VERSION) {
if (!isOverwriteTargetInIncompatibleVersion(slot)) {
saveOrOverwriteSavegame(slot);
}
} else if (isLoading) {
loadSaveGame(slot);
} else {
saveOrOverwriteSavegame(slot);
}
}
private void saveOrOverwriteSavegame(int slot) {
final String message = getConfirmOverwriteQuestion(slot);
if (message != null) {
showConfirmoverwriteQuestion(slot, message);
} else {
completeLoadSaveActivity(slot);
}
}
private boolean isOverwriteTargetInIncompatibleVersion(int slot) {
final FileHeader header = Savegames.quickload(this, slot);
if (header != null && header.fileversion != AndorsTrailApplication.DEVELOPMENT_INCOMPATIBLE_SAVEGAME_VERSION) {
final CustomDialog d = CustomDialogFactory.createErrorDialog(this, "Overwriting not allowed", "You are currently using a development version of Andor's trail. Overwriting a regular savegame is not allowed in development mode.");
CustomDialogFactory.show(d);
return true;
}
return false;
}
//region Imports/Exports
@RequiresApi(api = Build.VERSION_CODES.P)
private void exportSaveGames(Intent data) {
Uri uri = data.getData();
Context context = getApplicationContext();
ContentResolver resolver = AndorsTrailApplication.getApplicationFromActivity(this).getContentResolver();
File storageDir = AndroidStorage.getStorageDirectory(context, Constants.FILENAME_SAVEGAME_DIRECTORY);
DocumentFile source = DocumentFile.fromFile(storageDir);
DocumentFile target = DocumentFile.fromTreeUri(context, uri);
if (target == null) {
return;
}
DocumentFile[] files = source.listFiles();
boolean hasExistingFiles = false;
for (DocumentFile file :
files) {
String fileName = file.getName();
if (fileName == null)
continue;
DocumentFile existingFile = target.findFile(fileName);
if (existingFile != null) {
hasExistingFiles = true;
break;
}
}
if (hasExistingFiles) {
showConfirmOverwriteByExportQuestion(resolver, target, files);
} else {
exportSaveGamesFolderContentToFolder(resolver, target, files);
}
}
@RequiresApi(api = Build.VERSION_CODES.P)
private void exportSaveGamesFolderContentToFolder(ContentResolver resolver, DocumentFile target, DocumentFile[] files) {
DocumentFile[] sourceFiles = new DocumentFile[files.length];
DocumentFile[] worldmapFiles = null;
for (int i = 0; i < files.length; i++) {
DocumentFile file = files[i];
if (file.isFile()) {
sourceFiles[i] = file;
} else if (file.isDirectory() && Objects.equals(file.getName(), Constants.FILENAME_WORLDMAP_DIRECTORY)) {
worldmapFiles = file.listFiles();
}
}
Context context =this;
DocumentFile[] finalWorldmapFiles = worldmapFiles;
AndroidStorage.copyDocumentFilesToDirAsync(sourceFiles,
context,
target,
(sucess) -> {
if (sucess) {
DocumentFile worldmapFolder = target.createDirectory(Constants.FILENAME_WORLDMAP_DIRECTORY);
AndroidStorage.copyDocumentFilesToDirAsync(finalWorldmapFiles,
context,
worldmapFolder,
(sucessWorldmap) -> completeLoadSaveActivity(SLOT_NUMBER_EXPORT_SAVEGAMES, sucessWorldmap));
} else {
completeLoadSaveActivity(SLOT_NUMBER_EXPORT_SAVEGAMES, false);
}
});
}
@RequiresApi(api = Build.VERSION_CODES.N)
private void importSaveGames(Intent data) {
Uri uri = data.getData();
ClipData uris = data.getClipData();
if (uri == null && uris == null) {
//no file was selected
return;
}
Context context = getApplicationContext();
ContentResolver resolver = context.getContentResolver();
File storageDir = AndroidStorage.getStorageDirectory(context, Constants.FILENAME_SAVEGAME_DIRECTORY);
DocumentFile appSavegameFolder = DocumentFile.fromFile(storageDir);
List<Uri> uriList = new ArrayList<>();
if (uri != null) {
uriList.add(uri);
} else {
for (int i = 0; i < uris.getItemCount(); i++)
uriList.add(uris.getItemAt(i).getUri());
}
importSaveGamesFromUris(context, resolver, appSavegameFolder, uriList);
}
@RequiresApi(api = Build.VERSION_CODES.N)
private void importSaveGamesFromUris(Context context, ContentResolver resolver, DocumentFile appSavegameFolder, List<Uri> uriList) {
int count = uriList.size();
ArrayList<DocumentFile> alreadyExistingFiles = new ArrayList<>();
ArrayList<DocumentFile> newFiles = new ArrayList<>();
for (int i = 0; i < count; i++) {
Uri item = uriList.get(i);
DocumentFile itemFile = DocumentFile.fromSingleUri(context, item);
boolean fileAlreadyExists = getExistsSavegameInOwnFiles(itemFile, appSavegameFolder);
if (fileAlreadyExists)
alreadyExistingFiles.add(itemFile);
else
newFiles.add(itemFile);
}
if (alreadyExistingFiles.size() > 0) {
showConfirmOverwriteByImportQuestion(resolver, appSavegameFolder, alreadyExistingFiles, newFiles);
} else {
importSaveGames(resolver, appSavegameFolder, newFiles);
}
}
private void importSaveGames(ContentResolver resolver, DocumentFile appSavegameFolder, List<DocumentFile> saveFiles) {
int size = saveFiles.size();
DocumentFile[] sources = new DocumentFile[size];
DocumentFile[] targets = new DocumentFile[size];
boolean saveAsNew = false;
for (int i = 0; i < size; i++) {
DocumentFile file = saveFiles.get(i);
if (file == null) {//null is value a marker that the next should be saved as new
saveAsNew = true;
continue;
}
int slot = getSlotFromSavegameFileName(file.getName());
if (slot == -1) {
//invalid file name
continue;
}
if (saveAsNew) {
slot = getFirstFreeSlot();
saveAsNew = false;
}
String targetName = Savegames.getSlotFileName(slot);
sources[i] = file;
targets[i] = getOrCreateDocumentFile(appSavegameFolder, targetName);
}
AndroidStorage.copyDocumentFilesFromToAsync(sources,
this,
targets,
(sucess) -> completeLoadSaveActivity(SLOT_NUMBER_IMPORT_SAVEGAMES, sucess));
}
private void completeSavegameImportAndCheckIfDone(List<Integer> importsNeedingConfirmation, int slot) {
importsNeedingConfirmation.remove((Object) slot);
if (importsNeedingConfirmation.isEmpty()) {
completeLoadSaveActivity(SLOT_NUMBER_IMPORT_SAVEGAMES);
}
}
private boolean getExistsSavegameInOwnFiles(DocumentFile savegameFile, DocumentFile appSavegameFolder) {
if (savegameFile == null)
return false;
DocumentFile foundFile = appSavegameFolder.findFile(Objects.requireNonNull(savegameFile.getName()));
return foundFile != null && foundFile.exists();
}
private int getSlotFromSavegameFileName(String fileName) {
if (fileName == null || !fileName.startsWith(Constants.FILENAME_SAVEGAME_FILENAME_PREFIX)) {
//TODO: Maybe output a message that the file didn't have the right name?
return -1;
}
String slotStr = fileName.substring(Constants.FILENAME_SAVEGAME_FILENAME_PREFIX.length());
int slot;
try {
slot = Integer.parseInt(slotStr);
return slot;
} catch (NumberFormatException e) {
//TODO: Maybe output a message that the file didn't have the right name?
return -1;
}
}
private void importSaveGameFile(ContentResolver resolver, DocumentFile appSavegameFolder, DocumentFile itemFile, int slot) {
String targetName = Savegames.getSlotFileName(slot);
DocumentFile targetFile = getOrCreateDocumentFile(appSavegameFolder, targetName);
if (targetFile == null || !targetName.equals(targetFile.getName())) {
showErrorImportingSaveGameUnknown();//TODO: maybe replace with a more specific error message
return;
}
try {
AndroidStorage.copyDocumentFile(itemFile, resolver, targetFile);
} catch (IOException e) {
showErrorImportingSaveGameUnknown();
e.printStackTrace();
}
}
private DocumentFile getOrCreateDocumentFile(DocumentFile folder, String targetName) {
DocumentFile targetFile = folder.findFile(targetName);//try finding the file
if (targetFile == null)//no file found, creating new one
targetFile = folder.createFile(Constants.NO_FILE_EXTENSION_MIME_TYPE, targetName);
return targetFile;
}
@RequiresApi(api = Build.VERSION_CODES.P)
private void importWorldmap(Intent data) {
Uri uri = data.getData();
Context context = getApplicationContext();
ContentResolver resolver = AndorsTrailApplication.getApplicationFromActivity(this).getContentResolver();
File storageDir = AndroidStorage.getStorageDirectory(context, Constants.FILENAME_SAVEGAME_DIRECTORY);
DocumentFile storageFolder = DocumentFile.fromFile(storageDir);
DocumentFile ownWorldmapFolder = storageFolder.findFile(Constants.FILENAME_WORLDMAP_DIRECTORY);
if (ownWorldmapFolder == null) {
ownWorldmapFolder = storageFolder.createDirectory(Constants.FILENAME_WORLDMAP_DIRECTORY);
}
DocumentFile chosenFolder = DocumentFile.fromTreeUri(context, uri);
if (chosenFolder == null || !chosenFolder.isDirectory()) {
showErrorImportingWorldmapWrongDirectory();
return;
}
if (!Constants.FILENAME_WORLDMAP_DIRECTORY.equals(chosenFolder.getName())) {
//user did not select the worldmap folder directly
DocumentFile file = chosenFolder.findFile(Constants.FILENAME_WORLDMAP_DIRECTORY);
if (file == null || !file.isDirectory() || !Constants.FILENAME_WORLDMAP_DIRECTORY.equals(file.getName())) {
//could not find a worldmap folder in the users selection
showErrorImportingWorldmapWrongDirectory();
return;
}
chosenFolder = file;
}
AndroidStorage.copyDocumentFilesToDirAsync(chosenFolder.listFiles(),
this,
ownWorldmapFolder,
(success) -> completeLoadSaveActivity(SLOT_NUMBER_IMPORT_WORLDMAP, success));
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private void clickExportSaveGames() {
startActivityForResult(AndroidStorage.getNewOpenDirectoryIntent(), -SLOT_NUMBER_EXPORT_SAVEGAMES);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private void clickImportSaveGames() {
startActivityForResult(AndroidStorage.getNewSelectMultipleSavegameFilesIntent(), -SLOT_NUMBER_IMPORT_SAVEGAMES);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private void clickImportWorldmap() {
startActivityForResult(AndroidStorage.getNewOpenDirectoryIntent(), -SLOT_NUMBER_IMPORT_WORLDMAP);
}
@RequiresApi(api = Build.VERSION_CODES.P)
private void showConfirmOverwriteByExportQuestion(ContentResolver resolver, DocumentFile targetFolder, DocumentFile[] files) {
final CustomDialog d = CustomDialogFactory.createDialog(this,
getString(R.string.loadsave_export_overwrite_confirmation_title),
getResources().getDrawable(android.R.drawable.ic_dialog_alert),
getString(R.string.loadsave_export_overwrite_confirmation),
null,
true);
CustomDialogFactory.addButton(d, android.R.string.yes, v -> exportSaveGamesFolderContentToFolder(resolver, targetFolder, files));
CustomDialogFactory.addDismissButton(d, android.R.string.no);
CustomDialogFactory.show(d);
}
@RequiresApi(api = Build.VERSION_CODES.N)
private void showConfirmOverwriteByImportQuestion(ContentResolver resolver,
DocumentFile appSavegameFolder,
List<DocumentFile> alreadyExistingFiles,
List<DocumentFile> newFiles) {
final String title = getString(R.string.loadsave_import_overwrite_confirmation_title);
String message = getString(R.string.loadsave_import_file_exists_question);
StringBuilder sb = new StringBuilder();
sb.append('\n');
int amount = alreadyExistingFiles.size();
Context context = AndorsTrailApplication.getApplicationFromActivity(this).getApplicationContext();
ArrayList<CustomDialog> dialogs = new ArrayList<CustomDialog>(amount) ;
for (int i = 0; i < amount ; i++) {
DocumentFile alreadyExistingFile = alreadyExistingFiles.get(i);
int slot = getSlotFromSavegameFileName(alreadyExistingFile.getName());
FileHeader existingFileHeader = Savegames.quickload(context, slot);
FileHeader importedFileHeader = null;
try (InputStream stream = resolver.openInputStream(alreadyExistingFile.getUri());
DataInputStream dataStream = new DataInputStream(stream)) {
importedFileHeader = new FileHeader(dataStream, true);
} catch (FileNotFoundException e) {
e.printStackTrace();
continue;
} catch (IOException e) {
e.printStackTrace();
continue;
}
StringBuilder messageSb = new StringBuilder();
String existingFileDescription = getString(R.string.loadsave_import_existing_description, slot, existingFileHeader.describe());
String importedFileDescription = getString(R.string.loadsave_import_imported_description, slot, importedFileHeader.describe());
messageSb.append(getString(R.string.loadsave_import_file_exists_question, existingFileDescription, importedFileDescription));
String m = messageSb.toString();
CustomDialog dialog = CustomDialogFactory.createDialog(this,
title,
getResources().getDrawable(android.R.drawable.ic_dialog_alert),
m,
null,
true,
false,
true);
CustomDialogFactory.addButton(dialog, R.string.loadsave_import_option_keep_existing, v -> {
//do nothing
GoToNextConflictOrFinish(resolver, appSavegameFolder, newFiles, dialogs);
});
CustomDialogFactory.addButton(dialog, R.string.loadsave_import_option_keep_imported, v -> {
newFiles.add(alreadyExistingFile);
GoToNextConflictOrFinish(resolver, appSavegameFolder, newFiles, dialogs);
});
CustomDialogFactory.addButton(dialog, R.string.loadsave_import_option_add_as_new, v -> {
newFiles.add(null);//add a null element as marker to know later if the next file should be imported as new or overwrite the existing one
newFiles.add(alreadyExistingFile);
GoToNextConflictOrFinish(resolver, appSavegameFolder, newFiles, dialogs);
});
CustomDialogFactory.addCancelButton(dialog, android.R.string.cancel);
CustomDialogFactory.setCancelListener(dialog, v -> {
completeLoadSaveActivity(SLOT_NUMBER_IMPORT_SAVEGAMES, false);
});
dialogs.add(dialog);
}
GoToNextConflictOrFinish(resolver, appSavegameFolder, newFiles, dialogs);
}
@RequiresApi(api = Build.VERSION_CODES.N)
private void GoToNextConflictOrFinish(ContentResolver resolver, DocumentFile appSavegameFolder, List<DocumentFile> newFiles, ArrayList<CustomDialog> dialogs) {
if(dialogs.stream().count() > 0){
CustomDialog d = dialogs.remove(0);
CustomDialogFactory.show(d);
}
else{
importSaveGames(resolver, appSavegameFolder, newFiles);
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode != Activity.RESULT_OK)
return;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
switch (-requestCode) {
case SLOT_NUMBER_EXPORT_SAVEGAMES:
exportSaveGames(data);
return;
case SLOT_NUMBER_IMPORT_SAVEGAMES:
importSaveGames(data);
return;
case SLOT_NUMBER_IMPORT_WORLDMAP:
importWorldmap(data);
return;
}
}
}
//endregion
private void loadSaveGame(int slot) {
if (!Savegames.getSlotFile(slot, this).exists()) {
showErrorLoadingEmptySlot();
} else {
final FileHeader header = Savegames.quickload(this, slot);
if (header != null && !header.hasUnlimitedSaves) {
showSlotGetsDeletedOnLoadWarning(slot);
} else {
completeLoadSaveActivity(slot);
}
}
}
//region show Dialogs
private void showErrorImportingWorldmapWrongDirectory() {
final CustomDialog d = CustomDialogFactory.createErrorDialog(this,
getString(R.string.loadsave_import_worldmap_unsuccessfull),
getString(R.string.loadsave_import_worldmap_wrong_directory));
CustomDialogFactory.show(d);
}
private void showErrorImportingSaveGameUnknown() {
final CustomDialog d = CustomDialogFactory.createErrorDialog(this,
getString(R.string.loadsave_import_save_unsuccessfull),
getString(R.string.loadsave_import_save_error_unknown));
CustomDialogFactory.show(d);
}
private void showErrorLoadingEmptySlot() {
final CustomDialog d = CustomDialogFactory.createErrorDialog(this,
getString(R.string.startscreen_error_loading_game),
getString(R.string.startscreen_error_loading_empty_slot));
CustomDialogFactory.show(d);
}
private void showSlotGetsDeletedOnLoadWarning(final int slot) {
final CustomDialog d = CustomDialogFactory.createDialog(this,
getString(R.string.startscreen_attention_slot_gets_delete_on_load),
getResources().getDrawable(android.R.drawable.ic_dialog_alert),
getString(R.string.startscreen_attention_message_slot_gets_delete_on_load),
null,
true);
CustomDialogFactory.addButton(d, android.R.string.ok, v -> completeLoadSaveActivity(slot));
CustomDialogFactory.show(d);
}
private void showConfirmoverwriteQuestion(final int slot, String message) {
final String title =
getString(R.string.loadsave_save_overwrite_confirmation_title) + ' '
+ getString(R.string.loadsave_save_overwrite_confirmation_slot, slot);
final CustomDialog d = CustomDialogFactory.createDialog(this,
title,
getResources().getDrawable(android.R.drawable.ic_dialog_alert),
message,
null,
true);
CustomDialogFactory.addButton(d, android.R.string.yes, v -> completeLoadSaveActivity(slot));
CustomDialogFactory.addDismissButton(d, android.R.string.no);
CustomDialogFactory.show(d);
}
//endregion
CustomDialogFactory.addButton(d, android.R.string.yes, new View.OnClickListener() {
@Override
public void onClick(View v) {
loadsave(slot);
}
});
CustomDialogFactory.addDismissButton(d, android.R.string.no);
CustomDialogFactory.show(d);
}
}

View File

@@ -20,11 +20,12 @@ import com.gpl.rpg.AndorsTrail.savegames.Savegames;
import com.gpl.rpg.AndorsTrail.util.ThemeHelper;
import com.gpl.rpg.AndorsTrail.view.CloudsAnimatorView;
import com.gpl.rpg.AndorsTrail.view.CustomDialogFactory;
import com.gpl.rpg.AndorsTrail.view.CustomDialogFactory.CustomDialog;
public final class LoadingActivity extends AndorsTrailBaseActivity implements OnResourcesLoadedListener, OnSceneLoadedListener {
private WorldSetup setup;
private Dialog progressDialog;
private CustomDialog progressDialog;
private CloudsAnimatorView clouds_back, clouds_mid, clouds_front;
boolean loaded = false;
@@ -165,7 +166,7 @@ public final class LoadingActivity extends AndorsTrailBaseActivity implements On
}
private void showLoadingFailedDialog(int messageResourceID) {
final Dialog d = CustomDialogFactory.createDialog(this, getResources().getString(R.string.dialog_loading_failed_title), null, getResources().getString(messageResourceID), null, true);
final CustomDialog d = CustomDialogFactory.createDialog(this, getResources().getString(R.string.dialog_loading_failed_title), null, getResources().getString(messageResourceID), null, true);
CustomDialogFactory.addDismissButton(d, android.R.string.ok);
CustomDialogFactory.setDismissListener(d, new OnDismissListener() {
@Override

View File

@@ -43,6 +43,7 @@ import com.gpl.rpg.AndorsTrail.util.Coord;
import com.gpl.rpg.AndorsTrail.util.ThemeHelper;
import com.gpl.rpg.AndorsTrail.view.CombatView;
import com.gpl.rpg.AndorsTrail.view.CustomDialogFactory;
import com.gpl.rpg.AndorsTrail.view.CustomDialogFactory.CustomDialog;
import com.gpl.rpg.AndorsTrail.view.DisplayActiveActorConditionIcons;
import com.gpl.rpg.AndorsTrail.view.ItemContainerAdapter;
import com.gpl.rpg.AndorsTrail.view.MainView;
@@ -247,7 +248,7 @@ public final class MainActivity
final ItemContainerAdapter inventoryListAdapter = new QuickslotsItemContainerAdapter(lv.getContext(), world.tileManager, world.model.player.inventory.usableItems(), world.model.player, wornTiles);
lv.setAdapter(inventoryListAdapter);
final Dialog d = CustomDialogFactory.createDialog(v.getContext(),
final CustomDialog d = CustomDialogFactory.createDialog(v.getContext(),
v.getResources().getString(R.string.inventory_assign),
v.getResources().getDrawable(R.drawable.ui_icon_equipment),
v.getResources().getString(R.string.inventory_selectitem), view, false);

View File

@@ -11,6 +11,7 @@ import com.gpl.rpg.AndorsTrail.resource.tiles.TileManager;
import com.gpl.rpg.AndorsTrail.util.ThemeHelper;
import com.gpl.rpg.AndorsTrail.view.CloudsAnimatorView;
import com.gpl.rpg.AndorsTrail.view.CustomDialogFactory;
import com.gpl.rpg.AndorsTrail.view.CustomDialogFactory.CustomDialog;
import android.app.Activity;
import android.app.Dialog;
@@ -114,7 +115,7 @@ public final class StartScreenActivity extends AndorsTrailBaseFragmentActivity i
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (grantResults.length > 0 && grantResults[0] != PackageManager.PERMISSION_GRANTED) {
final Dialog d = CustomDialogFactory.createDialog(this,
final CustomDialog d = CustomDialogFactory.createDialog(this,
getResources().getString(R.string.dialog_permission_information_title),
getResources().getDrawable(android.R.drawable.ic_dialog_info),
getResources().getString(R.string.dialog_permission_information),

View File

@@ -38,25 +38,26 @@ import com.gpl.rpg.AndorsTrail.util.AndroidStorage;
import com.gpl.rpg.AndorsTrail.util.L;
import com.gpl.rpg.AndorsTrail.util.ThemeHelper;
import com.gpl.rpg.AndorsTrail.view.CustomDialogFactory;
import com.gpl.rpg.AndorsTrail.view.CustomDialogFactory.CustomDialog;
public class StartScreenActivity_MainMenu extends Fragment {
private static final int INTENTREQUEST_PREFERENCES = 7;
public static final int INTENTREQUEST_LOADGAME = 9;
private static final int INTENTREQUEST_PREFERENCES = 7;
public static final int INTENTREQUEST_LOADGAME = 9;
private boolean hasExistingGame = false;
private Button startscreen_continue;
private Button startscreen_newgame;
private Button startscreen_load;
private ViewGroup save_preview_holder;
private ImageView save_preview_hero_icon;
private TextView save_preview_hero_desc;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
updatePreferences(false);
super.onCreateView(inflater, container, savedInstanceState);
private boolean hasExistingGame = false;
private Button startscreen_continue;
private Button startscreen_newgame;
private Button startscreen_load;
private ViewGroup save_preview_holder;
private ImageView save_preview_hero_icon;
private TextView save_preview_hero_desc;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
updatePreferences(false);
super.onCreateView(inflater, container, savedInstanceState);
if (container != null) {
@@ -64,11 +65,11 @@ public class StartScreenActivity_MainMenu extends Fragment {
}
View root = inflater.inflate(R.layout.startscreen_mainmenu, container, false);
save_preview_holder = (ViewGroup) root.findViewById(R.id.save_preview_holder);
save_preview_hero_icon = (ImageView) root.findViewById(R.id.save_preview_hero_icon);
save_preview_hero_desc = (TextView) root.findViewById(R.id.save_preview_hero_desc);
startscreen_continue = (Button) root.findViewById(R.id.startscreen_continue);
startscreen_continue.setOnClickListener(new OnClickListener() {
@@ -114,7 +115,7 @@ public class StartScreenActivity_MainMenu extends Fragment {
AndorsTrailApplication app = AndorsTrailApplication.getApplicationFromActivity(getActivity());
if (hasExistingGame && app != null && app.getWorld() != null && app.getWorld().model != null
&& app.getWorld().model.statistics != null && !app.getWorld().model.statistics.hasUnlimitedSaves()) {
final Dialog d = CustomDialogFactory.createDialog(getActivity(),
final CustomDialog d = CustomDialogFactory.createDialog(getActivity(),
getString(R.string.startscreen_load_game),
getResources().getDrawable(android.R.drawable.ic_delete),
getString(R.string.startscreen_load_game_confirm),
@@ -134,7 +135,7 @@ public class StartScreenActivity_MainMenu extends Fragment {
}
}
});
if (AndorsTrailApplication.DEVELOPMENT_FORCE_STARTNEWGAME) {
if (AndorsTrailApplication.DEVELOPMENT_DEBUGRESOURCES) {
@@ -152,14 +153,14 @@ public class StartScreenActivity_MainMenu extends Fragment {
checkAndRequestPermissions(getActivity());
migrateDataOnDemand(getActivity());
}
return root;
}
@Override
public void onResume() {
super.onResume();
String playerName;
String displayInfo = null;
int iconID = TileManager.CHAR_HERO;
@@ -189,14 +190,10 @@ public class StartScreenActivity_MainMenu extends Fragment {
setCurrentVersionForVersionCheck();
checkAndRequestPermissions(getActivity());
migrateDataOnDemand(getActivity());
boolean hasSavegames = !Savegames.getUsedSavegameSlots(getActivity()).isEmpty();
startscreen_load.setEnabled(hasSavegames);
}
});
}
boolean hasSavegames = !Savegames.getUsedSavegameSlots(getActivity()).isEmpty();
startscreen_load.setEnabled(hasSavegames);
}
@TargetApi(29)
@@ -204,23 +201,16 @@ public class StartScreenActivity_MainMenu extends Fragment {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
if (activity.getApplicationContext().checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
if (AndroidStorage.shouldMigrateToInternalStorage(activity.getApplicationContext())) {
final Dialog d = CustomDialogFactory.createDialog(activity,
final CustomDialog d = CustomDialogFactory.createDialog(activity,
getString(R.string.startscreen_migration_title),
activity.getResources().getDrawable(android.R.drawable.ic_dialog_alert),
getString(R.string.startscreen_migration_text),
null,
true);
CustomDialogFactory.addDismissButton(d, android.R.string.ok);
d.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface arg0) {
boolean hasSavegames = !Savegames.getUsedSavegameSlots(getActivity()).isEmpty();
startscreen_load.setEnabled(hasSavegames);
}
});
CustomDialogFactory.show(d);
if (!AndroidStorage.migrateToInternalStorage(activity.getApplicationContext())) {
final Dialog errorDlg = CustomDialogFactory.createDialog(activity,
final CustomDialog errorDlg = CustomDialogFactory.createDialog(activity,
getString(R.string.startscreen_migration_title),
activity.getResources().getDrawable(android.R.drawable.ic_dialog_alert),
getString(R.string.startscreen_migration_failure),
@@ -259,13 +249,13 @@ public class StartScreenActivity_MainMenu extends Fragment {
super.onAttach(activity);
listener = (OnNewGameRequestedListener) activity;
}
@Override
public void onDetach() {
super.onDetach();
listener = null;
}
private void setButtonState(final String playerName, final String displayInfo, int iconID, boolean isDead) {
startscreen_continue.setEnabled(hasExistingGame && !isDead);
startscreen_newgame.setEnabled(true);
@@ -305,9 +295,9 @@ public class StartScreenActivity_MainMenu extends Fragment {
// .create().show();
//
//
final Dialog d = CustomDialogFactory.createDialog(getActivity(),
getString(R.string.startscreen_newgame),
getResources().getDrawable(android.R.drawable.ic_delete),
final CustomDialog d = CustomDialogFactory.createDialog(getActivity(),
getString(R.string.startscreen_newgame),
getResources().getDrawable(android.R.drawable.ic_delete),
getResources().getString(R.string.startscreen_newgame_confirm),
null,
true);
@@ -318,9 +308,9 @@ public class StartScreenActivity_MainMenu extends Fragment {
}
});
CustomDialogFactory.addDismissButton(d, android.R.string.cancel);
CustomDialogFactory.show(d);
}
private static final String versionCheck = "lastversion";
@@ -337,64 +327,90 @@ public class StartScreenActivity_MainMenu extends Fragment {
e.putInt(versionCheck, AndorsTrailApplication.CURRENT_VERSION);
e.commit();
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case INTENTREQUEST_LOADGAME:
if (resultCode != Activity.RESULT_OK) break;
final int slot = data.getIntExtra("slot", 1);
continueGame(false, slot, null);
break;
case INTENTREQUEST_PREFERENCES:
updatePreferences(true);
break;
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case INTENTREQUEST_LOADGAME:
boolean unsuccessful = resultCode != Activity.RESULT_OK;
if(data == null) break;
final boolean wasImportOrExport = data.getBooleanExtra("import_export", false);
if (wasImportOrExport) {
String message = getImportExportMessage(!unsuccessful, data);
Toast.makeText(getActivity(), message, Toast.LENGTH_LONG).show();
break;
}
if (unsuccessful) break;
final int slot = data.getIntExtra("slot", 1);
continueGame(false, slot, null);
break;
case INTENTREQUEST_PREFERENCES:
updatePreferences(true);
break;
}
}
private String getImportExportMessage(boolean successful, Intent data) {
String message = "";
boolean isImportWorldmap = data.getBooleanExtra("import_worldmap", false);
boolean isImportSaves = data.getBooleanExtra("import_savegames", false);
boolean isExport = data.getBooleanExtra("export", false);
if(isImportWorldmap) {
message = getString(successful ? R.string.loadsave_import_worldmap_successfull : R.string.loadsave_import_worldmap_unsuccessfull);
} else if(isImportSaves) {
message = getString(successful ? R.string.loadsave_import_save_successfull : R.string.loadsave_import_save_unsuccessfull);
} else if(isExport) {
message = getString(successful ? R.string.loadsave_export_successfull : R.string.loadsave_export_unsuccessfull);
}
return message;
}
private void updatePreferences(boolean alreadyStartedLoadingResources) {
AndorsTrailApplication app = AndorsTrailApplication.getApplicationFromActivity(getActivity());
AndorsTrailPreferences preferences = app.getPreferences();
preferences.read(getActivity());
if (app.setLocale(getActivity())) {
if (alreadyStartedLoadingResources) {
// Changing the locale after having loaded the game requires resources to
// be re-loaded. Therefore, we just exit here.
Toast.makeText(getActivity(), R.string.change_locale_requires_restart, Toast.LENGTH_LONG).show();
doFinish();
return;
}
}
if (ThemeHelper.changeTheme(preferences.selectedTheme)) {
// Changing the theme requires a restart to re-create all activities.
Toast.makeText(getActivity(), R.string.change_theme_requires_restart, Toast.LENGTH_LONG).show();
doFinish();
return;
}
app.getWorld().tileManager.updatePreferences(preferences);
}
@SuppressLint("NewApi")
private void doFinish() {
//For Lollipop and above
((AndorsTrailApplication)getActivity().getApplication()).discardWorld();
getActivity().finish();
}
AndorsTrailApplication app = AndorsTrailApplication.getApplicationFromActivity(getActivity());
AndorsTrailPreferences preferences = app.getPreferences();
preferences.read(getActivity());
if (app.setLocale(getActivity())) {
if (alreadyStartedLoadingResources) {
// Changing the locale after having loaded the game requires resources to
// be re-loaded. Therefore, we just exit here.
Toast.makeText(getActivity(), R.string.change_locale_requires_restart, Toast.LENGTH_LONG).show();
doFinish();
return;
}
}
if (ThemeHelper.changeTheme(preferences.selectedTheme)) {
// Changing the theme requires a restart to re-create all activities.
Toast.makeText(getActivity(), R.string.change_theme_requires_restart, Toast.LENGTH_LONG).show();
doFinish();
return;
}
app.getWorld().tileManager.updatePreferences(preferences);
}
@SuppressLint("NewApi")
private void doFinish() {
//For Lollipop and above
((AndorsTrailApplication) getActivity().getApplication()).discardWorld();
getActivity().finish();
}
public interface OnNewGameRequestedListener {
public void onNewGameRequested();
}
private OnNewGameRequestedListener listener = null;
private void createNewGame() {
if (listener != null) {
listener.onNewGameRequested();
}
}
}

View File

@@ -51,6 +51,10 @@ public final class Constants {
public static final String CHEAT_DETECTION_FOLDER = "dEAGyGE3YojqXjI3x4x7";
public static final String PASSIVE_ACHIEVEMENT_CHECK_PHRASE = "passive_achievement_check";
public static final String SAVEGAME_FILE_MIME_TYPE = "application/octet-stream";
public static final String WORLDMAP_FILE_MIME_TYPE = "image/png";
public static final String NO_FILE_EXTENSION_MIME_TYPE = "application/no_file_extension_mime_type";
public static final Random rnd = new Random();
public static int rollValue(final ConstRange r) { return rollValue(r.max, r.current); }
public static int rollValue(final ConstRange r, int bias) { return rollValue((r.max + 1) * 100 -1, r.current * 100 + bias)/100; }

View File

@@ -86,7 +86,7 @@ public final class Savegames {
private static void writeBackup(Context androidContext, byte[] savegame, String playerId) throws IOException {
File cheatDetectionFolder = AndroidStorage.getStorageDirectory(androidContext, Constants.CHEAT_DETECTION_FOLDER);
if (!cheatDetectionFolder.exists()) cheatDetectionFolder.mkdir();
ensureDirExists(cheatDetectionFolder);
File backupFile = new File(cheatDetectionFolder, playerId + "X");
FileOutputStream fileOutputStream = new FileOutputStream(backupFile);
fileOutputStream.write(savegame);
@@ -127,34 +127,33 @@ public final class Savegames {
}
}
private static boolean triedToCheat(Context androidContext, FileHeader fh) throws IOException {
long savedVersionToCheck = 0;
File cheatDetectionFolder = AndroidStorage.getStorageDirectory(androidContext, Constants.CHEAT_DETECTION_FOLDER);
if (!cheatDetectionFolder.exists()) cheatDetectionFolder.mkdir();
File cheatDetectionFile = new File(cheatDetectionFolder, fh.playerId);
if (cheatDetectionFile.exists()) {
FileInputStream fileInputStream = new FileInputStream(cheatDetectionFile);
DataInputStream dataInputStream = new DataInputStream(fileInputStream);
final CheatDetection cheatDetection = new CheatDetection(dataInputStream);
savedVersionToCheck = cheatDetection.savedVersion;
dataInputStream.close();
fileInputStream.close();
}
private static boolean triedToCheat(Context androidContext, FileHeader fh) throws IOException {
long savedVersionToCheck = 0;
File cheatDetectionFolder = AndroidStorage.getStorageDirectory(androidContext, Constants.CHEAT_DETECTION_FOLDER);
ensureDirExists(cheatDetectionFolder);
File cheatDetectionFile = new File(cheatDetectionFolder, fh.playerId);
if (cheatDetectionFile.exists()) {
FileInputStream fileInputStream = new FileInputStream(cheatDetectionFile);
DataInputStream dataInputStream = new DataInputStream(fileInputStream);
final CheatDetection cheatDetection = new CheatDetection(dataInputStream);
savedVersionToCheck = cheatDetection.savedVersion;
dataInputStream.close();
fileInputStream.close();
}
if (savedVersionToCheck == DENY_LOADING_BECAUSE_GAME_IS_CURRENTLY_PLAYED) {
return true;
}
if (androidContext.getFileStreamPath(fh.playerId).exists()) {
FileInputStream fileInputStream = androidContext.openFileInput(fh.playerId);
DataInputStream dataInputStream = new DataInputStream(fileInputStream);
final CheatDetection cheatDetection = new CheatDetection(dataInputStream);
if (cheatDetection.savedVersion == DENY_LOADING_BECAUSE_GAME_IS_CURRENTLY_PLAYED) {
savedVersionToCheck = DENY_LOADING_BECAUSE_GAME_IS_CURRENTLY_PLAYED;
}
else if (cheatDetection.savedVersion > savedVersionToCheck) {
savedVersionToCheck = cheatDetection.savedVersion;
}
if (androidContext.getFileStreamPath(fh.playerId).exists()) {
FileInputStream fileInputStream = androidContext.openFileInput(fh.playerId);
DataInputStream dataInputStream = new DataInputStream(fileInputStream);
final CheatDetection cheatDetection = new CheatDetection(dataInputStream);
if (cheatDetection.savedVersion == DENY_LOADING_BECAUSE_GAME_IS_CURRENTLY_PLAYED) {
savedVersionToCheck = DENY_LOADING_BECAUSE_GAME_IS_CURRENTLY_PLAYED;
} else if (cheatDetection.savedVersion > savedVersionToCheck) {
savedVersionToCheck = cheatDetection.savedVersion;
}
if (AndorsTrailApplication.DEVELOPMENT_DEBUGMESSAGES) {
L.log("Internal cheatcheck file savedVersion: " + cheatDetection.savedVersion);
@@ -167,31 +166,48 @@ public final class Savegames {
return (savedVersionToCheck == DENY_LOADING_BECAUSE_GAME_IS_CURRENTLY_PLAYED || fh.savedVersion < savedVersionToCheck);
}
private static FileOutputStream getOutputFile(Context androidContext, int slot) throws IOException {
if (slot == SLOT_QUICKSAVE) {
return androidContext.openFileOutput(Constants.FILENAME_SAVEGAME_QUICKSAVE, Context.MODE_PRIVATE);
} else {
ensureSavegameDirectoryExists(androidContext);
return new FileOutputStream(getSlotFile(slot, androidContext));
}
}
private static void ensureSavegameDirectoryExists(Context context) {
File dir = AndroidStorage.getStorageDirectory(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, androidContext));
}
}
private static FileOutputStream getOutputFile(Context androidContext, int slot) throws IOException {
if (slot == SLOT_QUICKSAVE) {
return androidContext.openFileOutput(Constants.FILENAME_SAVEGAME_QUICKSAVE, Context.MODE_PRIVATE);
} else {
ensureSavegameDirectoryExists(androidContext);
return new FileOutputStream(getSlotFile(slot, androidContext));
}
}
public static File getSlotFile(int slot, Context context) {
File root = AndroidStorage.getStorageDirectory(context, Constants.FILENAME_SAVEGAME_DIRECTORY);
return new File(root, Constants.FILENAME_SAVEGAME_FILENAME_PREFIX + slot);
}
private static void ensureSavegameDirectoryExists(Context context) {
File dir = AndroidStorage.getStorageDirectory(context, Constants.FILENAME_SAVEGAME_DIRECTORY);
ensureDirExists(dir);
}
public static boolean ensureDirExists(File dir) {
if (!dir.exists()) {
boolean worked = dir.mkdir();
return worked;
}
return true;
}
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, androidContext));
}
}
public static File getSlotFile(int slot, Context context) {
File root = AndroidStorage.getStorageDirectory(context, Constants.FILENAME_SAVEGAME_DIRECTORY);
return getSlotFile(slot, root);
}
public static File getSlotFile(int slot, File directory) {
return new File(directory, getSlotFileName(slot));
}
public static String getSlotFileName(int slot) {
return Constants.FILENAME_SAVEGAME_FILENAME_PREFIX + slot;
}
public static void saveWorld(WorldContext world, OutputStream outStream, String displayInfo) throws IOException {
@@ -207,10 +223,11 @@ public final class Savegames {
dest.close();
}
public static LoadSavegameResult loadWorld(Resources res, WorldContext world, ControllerContext controllers, InputStream inState, FileHeader fh) throws IOException {
DataInputStream src = new DataInputStream(inState);
final FileHeader header = new FileHeader(src, fh.skipIcon);
if (header.fileversion > AndorsTrailApplication.CURRENT_VERSION) return LoadSavegameResult.savegameIsFromAFutureVersion;
public static LoadSavegameResult loadWorld(Resources res, WorldContext world, ControllerContext controllers, InputStream inState, FileHeader fh) throws IOException {
DataInputStream src = new DataInputStream(inState);
final FileHeader header = new FileHeader(src, fh.skipIcon);
if (header.fileversion > AndorsTrailApplication.CURRENT_VERSION)
return LoadSavegameResult.savegameIsFromAFutureVersion;
world.maps.readFromParcel(src, world, controllers, header.fileversion);
world.model = new ModelContainer(src, world, controllers, header.fileversion);
@@ -249,15 +266,15 @@ public final class Savegames {
}
}
private static void writeCheatCheck(Context androidContext, long savedVersion, String playerId) throws IOException {
File cheatDetectionFolder = AndroidStorage.getStorageDirectory(androidContext, Constants.CHEAT_DETECTION_FOLDER);
if (!cheatDetectionFolder.exists()) cheatDetectionFolder.mkdir();
File cheatDetectionFile = new File(cheatDetectionFolder, playerId);
FileOutputStream fileOutputStream = new FileOutputStream(cheatDetectionFile);
DataOutputStream dataOutputStream = new DataOutputStream(fileOutputStream);
CheatDetection.writeToParcel(dataOutputStream, savedVersion);
dataOutputStream.close();
fileOutputStream.close();
private static void writeCheatCheck(Context androidContext, long savedVersion, String playerId) throws IOException {
File cheatDetectionFolder = AndroidStorage.getStorageDirectory(androidContext, Constants.CHEAT_DETECTION_FOLDER);
ensureDirExists(cheatDetectionFolder);
File cheatDetectionFile = new File(cheatDetectionFolder, playerId);
FileOutputStream fileOutputStream = new FileOutputStream(cheatDetectionFile);
DataOutputStream dataOutputStream = new DataOutputStream(fileOutputStream);
CheatDetection.writeToParcel(dataOutputStream, savedVersion);
dataOutputStream.close();
fileOutputStream.close();
fileOutputStream = androidContext.openFileOutput(playerId, Context.MODE_PRIVATE);
dataOutputStream = new DataOutputStream(fileOutputStream);
@@ -268,26 +285,26 @@ public final class Savegames {
private static final Pattern savegameFilenamePattern = Pattern.compile(Constants.FILENAME_SAVEGAME_FILENAME_PREFIX + "(\\d+)");
public static List<Integer> getUsedSavegameSlots(Context context) {
try {
final List<Integer> result = new ArrayList<Integer>();
AndroidStorage.getStorageDirectory(context, Constants.FILENAME_SAVEGAME_DIRECTORY).listFiles(new FilenameFilter() {
@Override
public boolean accept(File f, String filename) {
Matcher m = savegameFilenamePattern.matcher(filename);
if (m != null && m.matches()) {
result.add(Integer.parseInt(m.group(1)));
return true;
}
return false;
}
});
Collections.sort(result);
return result;
} catch (Exception e) {
return null;
}
}
public static List<Integer> getUsedSavegameSlots(Context context) {
try {
final List<Integer> result = new ArrayList<Integer>();
AndroidStorage.getStorageDirectory(context, Constants.FILENAME_SAVEGAME_DIRECTORY).listFiles(new FilenameFilter() {
@Override
public boolean accept(File f, String filename) {
Matcher m = savegameFilenamePattern.matcher(filename);
if (m != null && m.matches()) {
result.add(Integer.parseInt(m.group(1)));
return true;
}
return false;
}
});
Collections.sort(result);
return result;
} catch (Exception e) {
return new ArrayList<Integer>();
}
}
private static final class CheatDetection {
public final int fileversion;
@@ -307,17 +324,16 @@ public final class Savegames {
}
public static final class FileHeader {
public final int fileversion;
public final String playerName;
public final String displayInfo;
public final int iconID;
public boolean skipIcon = false;
public final boolean isDead;
public final boolean hasUnlimitedSaves;
public final String playerId;
public final long savedVersion;
public static final class FileHeader {
public final int fileversion;
public final String playerName;
public final String displayInfo;
public final int iconID;
public boolean skipIcon = false;
public final boolean isDead;
public final boolean hasUnlimitedSaves;
public final String playerId;
public final long savedVersion;
public String describe() {
return (fileversion == AndorsTrailApplication.DEVELOPMENT_INCOMPATIBLE_SAVEGAME_VERSION ? "(D) " : "") + playerName + ", " + displayInfo;
@@ -326,17 +342,18 @@ public final class Savegames {
// ====== PARCELABLE ===================================================================
public FileHeader(DataInputStream src, boolean skipIcon) throws IOException {
int fileversion = src.readInt();
if (fileversion == 11) fileversion = 5; // Fileversion 5 had no version identifier, but the first byte was 11.
this.fileversion = fileversion;
if (fileversion >= 14) { // Before fileversion 14 (0.6.7), we had no file header.
this.playerName = src.readUTF();
this.displayInfo = src.readUTF();
} else {
this.playerName = null;
this.displayInfo = null;
}
public FileHeader(DataInputStream src, boolean skipIcon) throws IOException {
int fileversion = src.readInt();
if (fileversion == 11)
fileversion = 5; // Fileversion 5 had no version identifier, but the first byte was 11.
this.fileversion = fileversion;
if (fileversion >= 14) { // Before fileversion 14 (0.6.7), we had no file header.
this.playerName = src.readUTF();
this.displayInfo = src.readUTF();
} else {
this.playerName = null;
this.displayInfo = null;
}
if (fileversion >= 43) {
int id = src.readInt();

View File

@@ -1,12 +1,24 @@
package com.gpl.rpg.AndorsTrail.util;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.support.annotation.NonNull;
import android.support.annotation.RequiresApi;
import android.support.v4.content.FileProvider;
import android.support.v4.provider.DocumentFile;
import android.os.Handler;
import android.os.Looper;
import android.webkit.MimeTypeMap;
import com.gpl.rpg.AndorsTrail.R;
import com.gpl.rpg.AndorsTrail.controller.Constants;
import com.gpl.rpg.AndorsTrail.util.BackgroundWorker.BackgroundWorkerCallback;
import com.gpl.rpg.AndorsTrail.view.CustomDialogFactory;
import java.io.File;
import java.io.FileInputStream;
@@ -14,10 +26,12 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.concurrent.CancellationException;
import java.util.function.Consumer;
public final class AndroidStorage {
public static File getStorageDirectory(Context context, String name) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
return context.getExternalFilesDir(name);
}
else {
@@ -25,6 +39,7 @@ public final class AndroidStorage {
return new File(root, name);
}
}
public static boolean shouldMigrateToInternalStorage(Context context) {
boolean ret = false;
File externalSaveGameDirectory = new File(Environment.getExternalStorageDirectory(), Constants.FILENAME_SAVEGAME_DIRECTORY);
@@ -76,35 +91,236 @@ public final class AndroidStorage {
}
}
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 void copyFile(File source, File target) throws IOException {
try (InputStream in = new FileInputStream(source); OutputStream out = new FileOutputStream(target)) {
copyStream(in, out);
}
}
public static void copyStream(InputStream in, OutputStream out) throws IOException {
byte[] buf = new byte[1024];
int length;
while ((length = in.read(buf)) > 0) {
out.write(buf, 0, length);
}
}
public static void copyDocumentFileToNewOrExistingFile(DocumentFile sourceFile, ContentResolver resolver, DocumentFile targetFolder) throws IOException {
copyDocumentFileToNewOrExistingFile(sourceFile, resolver, targetFolder, Constants.NO_FILE_EXTENSION_MIME_TYPE);
}
public static void copyDocumentFileToNewOrExistingFile(DocumentFile sourceFile, ContentResolver resolver, DocumentFile targetFolder, String mimeType) throws IOException {
String fileName = sourceFile.getName();
DocumentFile file = targetFolder.findFile(fileName);
if (file == null)
file = targetFolder.createFile(mimeType, fileName);
if (file == null)
return;
AndroidStorage.copyDocumentFile(sourceFile, resolver, file);
}
public static void copyDocumentFile(DocumentFile sourceFile, ContentResolver resolver, DocumentFile targetFile) throws IOException {
try (OutputStream outputStream = resolver.openOutputStream(targetFile.getUri());
InputStream inputStream = resolver.openInputStream(sourceFile.getUri())) {
copyStream(inputStream, outputStream);
}
}
/**
* Gets the MIME-Type for a file.<p/>
* Fallback value is '* / *' (without spaces) <p/>
* Mostly copied together from: <a href="https://stackoverflow.com/q/8589645/17292289">StackOverflow</a>
*/
@NonNull
public static String getMimeType(ContentResolver resolver, Uri uri) {
String type = null;
if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
type = resolver.getType(uri);
return type;
}
final String extension = MimeTypeMap.getFileExtensionFromUrl(uri.getPath());
if (extension != null) {
type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension.toLowerCase());
}
if (type == null) {
type = "*/*"; // fallback type.
}
return type;
}
public static String getUrlForFile(Context context, File worldmap) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
String applicationId = context.getPackageName();
// Uri uri = FileProvider.getUriForFile(context, "com.gpl.rpg.AndorsTrail.fileprovider", worldmap);
Uri uri = FileProvider.getUriForFile(context, applicationId + ".fileprovider", worldmap);
return uri.toString();
} else {
return "file://" + worldmap.getAbsolutePath();
}
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public static Intent getNewOpenDirectoryIntent() {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
return intent;
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public static Intent getNewSelectMultipleSavegameFilesIntent() {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
intent.setType(Constants.SAVEGAME_FILE_MIME_TYPE);
return intent;
}
public static void copyDocumentFilesFromToAsync(DocumentFile[] sources, Context context, DocumentFile[] targets, Consumer<Boolean> callback) {
if(sources.length != targets.length)
{
throw new IllegalArgumentException("Both arrays, target & source have to have the same size");
}
BackgroundWorker worker = new BackgroundWorker();
CustomDialogFactory.CustomDialog progressDialog = getLoadingDialog(context);
progressDialog.setOnCancelListener(dialog -> worker.cancel());
ContentResolver resolver = context.getContentResolver();
Handler handler = Handler.createAsync(Looper.getMainLooper());
worker.setTask(new BackgroundWorker.worker() {
@Override
public void doWork(BackgroundWorkerCallback callback) {
try {
callback.onInitialize();
for (int i = 0; i < sources.length ; i++) {
if (worker.isCancelled()) {
callback.onFailure(new CancellationException("Cancelled"));
return;
}
DocumentFile source = sources[i];
DocumentFile target = targets[i];
if(source == null || target == null)
{
continue;
}
copyDocumentFile(source, resolver,target);
float progress = i /(float) sources.length;
callback.onProgress(progress);
}
callback.onComplete(true);
} catch (NullPointerException e) {
if (worker.isCancelled()) {
callback.onFailure(new CancellationException("Cancelled"));
return;
}
} catch (Exception e) {
callback.onFailure(e);
}
}
});
worker.setCallback(getDefaultBackgroundWorkerCallback(handler, progressDialog, callback));
worker.run();
}
@RequiresApi(api = Build.VERSION_CODES.P)
public static void copyDocumentFilesToDirAsync(DocumentFile[] files,
Context context,
DocumentFile targetDirectory,
Consumer<Boolean> callback) {
BackgroundWorker<Boolean> worker = new BackgroundWorker<>();
CustomDialogFactory.CustomDialog progressDialog = getLoadingDialog(context);
progressDialog.setOnCancelListener(dialog -> worker.cancel());
ContentResolver resolver = context.getContentResolver();
Handler handler = Handler.createAsync(Looper.getMainLooper());
worker.setTask(workerCallback -> {
try {
workerCallback.onInitialize();
for (int i = 0; i < files.length; i++) {
if (worker.isCancelled()) {
workerCallback.onFailure(new CancellationException("Cancelled"));
return;
}
DocumentFile file = files[i];
if(file == null)
continue;
copyDocumentFileToNewOrExistingFile(file, resolver, targetDirectory);
float progress = i /(float) files.length;
workerCallback.onProgress(progress);
}
workerCallback.onComplete(true);
} catch (NullPointerException e) {
if (worker.isCancelled()) {
workerCallback.onFailure(new CancellationException("Cancelled"));
}
} catch (Exception e) {
workerCallback.onFailure(e);
}
});
worker.setCallback(getDefaultBackgroundWorkerCallback(handler, progressDialog, callback));
worker.run();
}
private static BackgroundWorkerCallback<Boolean> getDefaultBackgroundWorkerCallback(Handler handler,
CustomDialogFactory.CustomDialog progressDialog,
Consumer<Boolean> callback) {
return new BackgroundWorkerCallback<Boolean>() {
private int progress = -1;
@Override
public void onInitialize() {
handler.post(() -> {
CustomDialogFactory.show(progressDialog);
});
}
@Override
public void onProgress(float progress) {
handler.post(() -> {
int intProgress = (int) (progress * 100);
if(this.progress == intProgress)
return;
this.progress = intProgress;
CustomDialogFactory.setDesc(progressDialog, intProgress + "%");
});
}
@RequiresApi(api = Build.VERSION_CODES.N)
@Override
public void onFailure(Exception e) {
handler.post(() -> {
progressDialog.dismiss();
callback.accept(false);
});
}
@RequiresApi(api = Build.VERSION_CODES.N)
@Override
public void onComplete(Boolean result) {
handler.post(() -> {
progressDialog.dismiss();
callback.accept(true);
});
}
};
}
private static CustomDialogFactory.CustomDialog getLoadingDialog(Context context) {
return CustomDialogFactory.createDialog(context,
context.getResources().getString(R.string.dialog_loading_message),
context.getResources().getDrawable(R.drawable.loading_anim),
null,
null,
false,
false);
}
}

View File

@@ -0,0 +1,46 @@
package com.gpl.rpg.AndorsTrail.util;
import java.util.concurrent.Executors;
public final class BackgroundWorker<T> {
boolean cancelled = false;
worker task;
BackgroundWorkerCallback callback;
public void setTask(worker task) {
this.task = task;
}
public void setCallback(BackgroundWorkerCallback callback) {
this.callback = callback;
}
public void cancel() {
cancelled = true;
}
interface worker<T> {
void doWork(BackgroundWorkerCallback callback);
}
interface BackgroundWorkerCallback<T> {
void onInitialize();
default void onProgress(float progress) {
}
void onFailure(Exception e);
void onComplete(T result);
}
public void run() {
Executors.newSingleThreadExecutor().execute(() -> {
task.doWork(callback);
});
}
public boolean isCancelled() {
return cancelled;
}
}

View File

@@ -1,8 +1,8 @@
package com.gpl.rpg.AndorsTrail.view;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface.OnDismissListener;
import android.content.DialogInterface.OnCancelListener;
import android.graphics.Rect;
import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.Drawable;
@@ -22,25 +22,33 @@ import com.gpl.rpg.AndorsTrail.R;
import com.gpl.rpg.AndorsTrail.util.ThemeHelper;
public class CustomDialogFactory {
public static class CustomDialog extends Dialog {
public static class CustomDialog extends android.app.Dialog {
public CustomDialog(Context context) {
super(context);
}
boolean verticalButtons = false;
}
public static CustomDialog createDialog(final Context context, String title, Drawable icon, String desc, View content, boolean hasButtons) {
public static CustomDialog createDialog(final Context context, String title, Drawable icon,
String desc, View content, boolean hasButtons, boolean canDismiss) {
return createDialog(context, title, icon, desc, content, hasButtons, canDismiss, false);
}
public static CustomDialog createDialog(final Context context, String title, Drawable icon,
String desc, View content, boolean hasButtons) {
return createDialog(context, title, icon, desc, content, hasButtons, true);
}
public static CustomDialog createDialog(final Context context, String title, Drawable icon, String desc, View content, boolean hasButtons, final boolean canDismiss) {
public static CustomDialog createDialog(final Context context, String title, Drawable icon,
String desc, View content, boolean hasButtons,
final boolean canDismiss, final boolean verticalButtons) {
final CustomDialog dialog = new CustomDialog(new ContextThemeWrapper(context, ThemeHelper.getDialogTheme())) {
@Override
public boolean onTouchEvent(MotionEvent event) {
Rect r = new Rect();
this.getWindow().getDecorView().findViewById(R.id.dialog_hitrect).getHitRect(r);
if (r.contains((int)event.getX(), (int)event.getY())) {
return super.onTouchEvent(event);
} else {
@@ -51,7 +59,7 @@ public class CustomDialogFactory {
return false;
}
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
@@ -63,7 +71,8 @@ public class CustomDialogFactory {
}
}
};
dialog.verticalButtons = verticalButtons;
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
dialog.setContentView(R.layout.custom_dialog_title_icon);
dialog.getWindow().setBackgroundDrawableResource(android.R.color.transparent);
@@ -72,23 +81,37 @@ public class CustomDialogFactory {
} else {
dialog.getWindow().setFlags(0, WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
setTitle(dialog, title, icon);
setDesc(dialog, desc);
setContent(dialog, content);
ViewGroup buttonsHolder = (ViewGroup) dialog.findViewById(R.id.dialog_button_container);
ViewGroup buttonsHolder = getButtonContainer(dialog);
ViewGroup unusedButtonsHolder = getUnusedButtonContainer(dialog);
unusedButtonsHolder.setVisibility(View.GONE);
if (hasButtons) {
buttonsHolder.setVisibility(View.VISIBLE);
} else {
buttonsHolder.setVisibility(View.GONE);
}
return dialog;
}
public static CustomDialog createErrorDialog(final Context context, String title, String description) {
final CustomDialog d = createDialog(context,
title,
context.getResources().getDrawable(android.R.drawable.ic_dialog_alert),
description,
null,
true);
CustomDialogFactory.addDismissButton(d, android.R.string.ok);
return d;
}
public static CustomDialog setTitle(final CustomDialog dialog, String title, Drawable icon) {
TextView titleView = (TextView) dialog.findViewById(R.id.dialog_title);
if (title != null || icon != null) {
@@ -100,7 +123,7 @@ public class CustomDialogFactory {
}
return dialog;
}
public static CustomDialog setDesc(final CustomDialog dialog, String desc) {
TextView descView = (TextView) dialog.findViewById(R.id.dialog_description);
ViewGroup descHolder = (ViewGroup) dialog.findViewById(R.id.dialog_description_container);
@@ -113,7 +136,7 @@ public class CustomDialogFactory {
}
return dialog;
}
public static CustomDialog setContent(final CustomDialog dialog, View content) {
ViewGroup contentHolder = (ViewGroup) dialog.findViewById(R.id.dialog_content_container);
if (content != null) {
@@ -124,54 +147,101 @@ public class CustomDialogFactory {
}
return dialog;
}
public static Dialog addButton(final Dialog dialog, int textId, final OnClickListener listener) {
Button template = (Button) dialog.findViewById(R.id.dialog_template_button);
public static CustomDialog addButton(final CustomDialog dialog, String text, final OnClickListener listener) {
return addButton(dialog, -1, text, listener);
}
public static CustomDialog addButton(final CustomDialog dialog, int textId, final OnClickListener listener) {
return addButton(dialog, textId, null, listener);
}
public static CustomDialog addButton(final CustomDialog dialog, int textId, String text, final OnClickListener listener) {
Button template = getButtonTemplate(dialog);
LayoutParams params = template.getLayoutParams();
ViewGroup buttonsHolder = (ViewGroup) dialog.findViewById(R.id.dialog_button_container);
ViewGroup buttonsHolder = getButtonContainer(dialog);
Button b = new Button(dialog.getContext());
b.setLayoutParams(params);
//Old android versions need this "reminder"
b.setBackgroundDrawable(ThemeHelper.getThemeDrawable(dialog.getContext(), R.attr.ui_theme_textbutton_drawable));
b.setTextColor(ThemeHelper.getThemeColor(dialog.getContext(), R.attr.ui_theme_dialogue_light_color));
b.setText(textId);
if(textId != -1) {
b.setText(textId);
} else {
b.setText(text);
}
b.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
listener.onClick(v);
dialog.dismiss();
}
});
buttonsHolder.addView(b, params);
return dialog;
}
public static Dialog addDismissButton(final Dialog dialog, int textId) {
public static CustomDialog addDismissButton(final CustomDialog dialog, int textId) {
return CustomDialogFactory.addButton(dialog, textId, new OnClickListener() {
@Override
public void onClick(View v) {
dialog.dismiss();
}
});
}
public static Dialog setDismissListener(Dialog dialog, OnDismissListener listener) {
public static CustomDialog addCancelButton(final CustomDialog dialog, int textId) {
return CustomDialogFactory.addButton(dialog, textId, new OnClickListener() {
@Override
public void onClick(View v) {
dialog.cancel();
}
});
}
public static CustomDialog setDismissListener(CustomDialog dialog, OnDismissListener listener) {
dialog.setOnDismissListener(listener);
return dialog;
}
public static void show(Dialog dialog) {
public static CustomDialog setCancelListener(CustomDialog dialog, OnCancelListener listener) {
dialog.setOnCancelListener(listener);
return dialog;
}
public static void show(CustomDialog dialog) {
dialog.findViewById(R.id.dialog_template_button).setVisibility(View.GONE);
dialog.findViewById(R.id.dialog_template_button_vertical).setVisibility(View.GONE);
dialog.show();
}
private static ViewGroup getUnusedButtonContainer(CustomDialog dialog) {
if (dialog.verticalButtons)
return (ViewGroup) dialog.findViewById(R.id.dialog_button_container);
else
return (ViewGroup) dialog.findViewById(R.id.dialog_button_container_vertical);
}
private static ViewGroup getButtonContainer(CustomDialog dialog) {
if (dialog.verticalButtons)
return (ViewGroup) dialog.findViewById(R.id.dialog_button_container_vertical);
else
return (ViewGroup) dialog.findViewById(R.id.dialog_button_container);
}
private static Button getButtonTemplate(CustomDialog dialog) {
if (dialog.verticalButtons)
return (Button) dialog.findViewById(R.id.dialog_template_button_vertical);
else
return (Button) dialog.findViewById(R.id.dialog_template_button);
}
}

View File

@@ -9,13 +9,15 @@ import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import com.gpl.rpg.AndorsTrail.view.CustomDialogFactory.CustomDialog;
/**
* Simply instantiate this class, implement abstract methods in an anonymous type, and tada, your Button is a Spinner!
*/
public abstract class SpinnerEmulator {
private Button spinnerButton;
private Dialog spinnerDialog = null;
private CustomDialog spinnerDialog = null;
private ListView choicesList;
private Context context;

View File

@@ -71,6 +71,29 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1" />
</LinearLayout>
<LinearLayout
android:id="@+id/dialog_button_container_vertical"
style="@style/AndorsTrail_Style_StdFrame"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="0.8"
android:gravity="center_horizontal"
android:orientation="vertical"
android:paddingLeft="5dp"
android:paddingTop="5dp"
android:paddingRight="5dp"
android:paddingBottom="5dp">
<Button
android:id="@+id/dialog_template_button_vertical"
style="@style/AndorsTrail_Style_TextButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1" />
</LinearLayout>
</LinearLayout>

View File

@@ -51,19 +51,45 @@
</ScrollView>
<LinearLayout
android:id="@+id/loadsave_save_to_new_slot_container"
android:id="@+id/loadsave_save_to_new_slot_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
style="@style/AndorsTrail_Style_StdFrame"
>
<Button
android:id="@+id/loadsave_save_to_new_slot"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
style="@style/AndorsTrail_Style_StdFrame"
>
<Button
android:id="@+id/loadsave_save_to_new_slot"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/loadsave_save_to_new_slot"
/>
</LinearLayout>
android:text="@string/loadsave_save_to_new_slot"
/>
</LinearLayout>
<LinearLayout
android:id="@+id/loadsave_export_import_save_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
style="@style/AndorsTrail_Style_StdFrame"
>
<Button
android:id="@+id/loadsave_export_save"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/loadsave_export"
/>
<Button
android:id="@+id/loadsave_import_save"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/loadsave_import_save"
/>
<Button
android:id="@+id/loadsave_import_worldmap"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/loadsave_import_worldmap"
/>
</LinearLayout>
</LinearLayout>

File diff suppressed because it is too large Load Diff