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

Commit a9f799a

Browse files
nullkillerAndrii Danylchenko
authored and
Andrii Danylchenko
committed
Allow loading VCMI data from zip to internal directory using file picker intent
1 parent 5d0e381 commit a9f799a

File tree

16 files changed

+196
-30
lines changed

16 files changed

+196
-30
lines changed

.github/workflows/github.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,12 @@ jobs:
1919

2020
steps:
2121
- uses: actions/checkout@v2
22+
with:
23+
submodules: recursive
2224

2325
- name: Update VCMI Sources
2426
run: |
2527
git submodule update --init --recursive ${{github.workspace}}/ext/vcmi
26-
git submodule update --init --recursive ${{github.workspace}}/ext/iconv
2728
2829
- name: Dependencies
2930
run: |

ext/oneTBB

ext/vcmi

Submodule vcmi updated 1082 files

project/vcmi-app/src/main/AndroidManifest.xml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
33
package="eu.vcmi.vcmi">
44

5-
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
6-
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
5+
<uses-permission android:name="android.permission.INTERNET" />
6+
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
7+
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
78

89
<application
910
android:extractNativeLibs="true"
@@ -44,7 +45,9 @@
4445

4546
<service
4647
android:name=".ServerService"
47-
android:process="eu.vcmi.vcmi.srv" />
48+
android:process="eu.vcmi.vcmi.srv"
49+
android:description="@string/server_name"
50+
android:exported="false"/>
4851
</application>
4952

5053
</manifest>
Binary file not shown.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
e94d9d3b4538ad80b5ef0ad163c0a3a8
1+
337452bc70fe7b22b869c4e38095f3f0

project/vcmi-app/src/main/java/eu/vcmi/vcmi/ActivityLauncher.java

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@
33
import android.app.Activity;
44
import android.content.Intent;
55
import android.content.pm.PackageManager;
6+
import android.net.Uri;
7+
import android.os.Build;
68
import android.os.Bundle;
9+
import android.os.Environment;
710
import androidx.annotation.NonNull;
811
import android.view.Menu;
912
import android.view.MenuItem;
@@ -14,9 +17,16 @@
1417
import org.json.JSONObject;
1518
import org.libsdl.app.SDLActivity;
1619

20+
import java.io.BufferedReader;
1721
import java.io.File;
22+
import java.io.IOException;
23+
import java.io.InputStream;
24+
import java.io.InputStreamReader;
25+
import java.net.InetAddress;
26+
import java.net.ServerSocket;
1827
import java.util.ArrayList;
1928
import java.util.List;
29+
import java.util.Objects;
2030

2131
import eu.vcmi.vcmi.content.AsyncLauncherInitialization;
2232
import eu.vcmi.vcmi.settings.CodepageSettingController;
@@ -29,6 +39,7 @@
2939
import eu.vcmi.vcmi.settings.ScreenResSettingController;
3040
import eu.vcmi.vcmi.settings.SoundSettingController;
3141
import eu.vcmi.vcmi.settings.StartGameController;
42+
import eu.vcmi.vcmi.settings.UpdateVcmiFilesController;
3243
import eu.vcmi.vcmi.util.FileUtil;
3344
import eu.vcmi.vcmi.util.Log;
3445
import eu.vcmi.vcmi.util.SharedPrefs;
@@ -50,6 +61,7 @@ public class ActivityLauncher extends ActivityWithToolbar
5061
private LauncherSettingController<Float, SharedPrefs> mCtrlPointerMulti;
5162
private LauncherSettingController<Integer, Config> mCtrlSoundVol;
5263
private LauncherSettingController<Integer, Config> mCtrlMusicVol;
64+
private LauncherSettingController<Void, Void> mCtrlUpdateFiles;
5365
private final AsyncLauncherInitialization.ILauncherCallbacks mInitCallbacks = new AsyncLauncherInitialization.ILauncherCallbacks()
5466
{
5567
@Override
@@ -140,6 +152,7 @@ private void initSettingsGui()
140152
mCtrlPointerMulti = new PointerMultiplierSettingController(this).init(R.id.launcher_btn_pointer_multi, mPrefs);
141153
mCtrlSoundVol = new SoundSettingController(this).init(R.id.launcher_btn_volume_sound, mConfig);
142154
mCtrlMusicVol = new MusicSettingController(this).init(R.id.launcher_btn_volume_music, mConfig);
155+
mCtrlUpdateFiles = new UpdateVcmiFilesController(this, v -> onVcmiDataImportRequested()).init(R.id.launcher_btn_update_zip);
143156

144157
mActualSettings.clear();
145158
mActualSettings.add(mCtrlCodepage);
@@ -152,6 +165,45 @@ private void initSettingsGui()
152165
mCtrlStart.hide(); // start is initially hidden, until we confirm that everything is okay via AsyncLauncherInitialization
153166
}
154167

168+
private static final int PICK_VCMI_ZIP_FILE = 2;
169+
170+
private void onVcmiDataImportRequested()
171+
{
172+
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
173+
intent.addCategory(Intent.CATEGORY_OPENABLE);
174+
intent.setType("application/zip");
175+
176+
startActivityForResult(intent, PICK_VCMI_ZIP_FILE);
177+
}
178+
179+
@Override
180+
public void onActivityResult(int requestCode, int resultCode, Intent resultData)
181+
{
182+
if (requestCode == PICK_VCMI_ZIP_FILE && resultCode == Activity.RESULT_OK)
183+
{
184+
// The result data contains a URI for the document or directory that
185+
// the user selected.
186+
Uri uri = null;
187+
if (resultData != null) {
188+
uri = resultData.getData();
189+
try (
190+
InputStream inputStream = getContentResolver().openInputStream(uri)
191+
) {
192+
if (FileUtil.unpackZipFile(inputStream, Const.getVcmiDataDir(this))) {
193+
return;
194+
}
195+
}
196+
catch(Exception e) {
197+
Log.e("Can not parse zip file", e);
198+
}
199+
}
200+
201+
return;
202+
}
203+
204+
super.onActivityResult(requestCode, resultCode, resultData);
205+
}
206+
155207
private void onLaunchGameBtnPressed()
156208
{
157209
saveConfig();
@@ -167,7 +219,7 @@ private void saveConfig()
167219

168220
try
169221
{
170-
mConfig.save(new File(FileUtil.configFileLocation()));
222+
mConfig.save(new File(FileUtil.configFileLocation(Const.getVcmiDataDir(this))));
171223
}
172224
catch (final Exception e)
173225
{
@@ -181,7 +233,7 @@ private void loadConfigFile()
181233
try
182234
{
183235
final String settingsFileContent =
184-
FileUtil.read(new File(FileUtil.configFileLocation()));
236+
FileUtil.read(new File(FileUtil.configFileLocation(Const.getVcmiDataDir(this))));
185237
mConfig = Config.load(new JSONObject(settingsFileContent));
186238
}
187239
catch (final Exception e)

project/vcmi-app/src/main/java/eu/vcmi/vcmi/ActivityMods.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ protected void onCreate(@Nullable final Bundle savedInstanceState)
7575

7676
private void loadLocalModData() throws IOException, JSONException
7777
{
78-
final String dataRoot = Environment.getExternalStorageDirectory() + "/" + Const.VCMI_DATA_ROOT_FOLDER_NAME;
78+
final String dataRoot = getDataDir() + "/" + Const.VCMI_DATA_ROOT_FOLDER_NAME;
7979
final String internalDataRoot = getFilesDir() + "/" + Const.VCMI_DATA_ROOT_FOLDER_NAME;
8080

8181
final File modsRoot = new File(dataRoot + "/Mods");
@@ -150,7 +150,7 @@ private void handleNoData()
150150

151151
private void saveModSettingsToFile()
152152
{
153-
mModContainer.saveToFile(new File(Environment.getExternalStorageDirectory(), Const.VCMI_DATA_ROOT_FOLDER_NAME + "/config/modSettings.json"));
153+
mModContainer.saveToFile(new File(getDataDir(), Const.VCMI_DATA_ROOT_FOLDER_NAME + "/config/modSettings.json"));
154154
}
155155

156156
private class OnModsRepoInitialized implements VCMIModsRepo.IOnModsRepoDownloaded

project/vcmi-app/src/main/java/eu/vcmi/vcmi/Const.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
package eu.vcmi.vcmi;
22

3+
import android.content.Context;
34
import android.os.Build;
5+
import android.os.Environment;
6+
7+
import java.io.File;
48

59
/**
610
* @author F
@@ -12,4 +16,19 @@ public class Const
1216
public static final int SUPPRESS_TRY_WITH_RESOURCES_WARNING = Build.VERSION_CODES.KITKAT;
1317

1418
public static final String VCMI_DATA_ROOT_FOLDER_NAME = "vcmi-data";
19+
20+
public static final String VCMI_DATA_ZIP_FILE_NAME = "vcmi-data.zip";
21+
22+
public static File getVcmiDataDir(Context context)
23+
{
24+
File root;
25+
26+
if (Build.VERSION.SDK_INT >= 24) {
27+
root = context.getDataDir();
28+
} else {
29+
root = Environment.getExternalStorageDirectory();
30+
}
31+
32+
return new File(root, Const.VCMI_DATA_ROOT_FOLDER_NAME);
33+
}
1534
}

project/vcmi-app/src/main/java/eu/vcmi/vcmi/NativeMethods.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,9 @@ public static void setupMsg(final Messenger msg)
5151
@SuppressWarnings(Const.JNI_METHOD_SUPPRESS)
5252
public static String dataRoot()
5353
{
54-
String root = new File(Environment.getExternalStorageDirectory(), Const.VCMI_DATA_ROOT_FOLDER_NAME).getAbsolutePath();
54+
Context ctx = requireContext();
55+
String root = Const.getVcmiDataDir(ctx).getAbsolutePath();
56+
5557
Log.i("Accessing data root: " + root);
5658
return root;
5759
}

project/vcmi-app/src/main/java/eu/vcmi/vcmi/content/AsyncLauncherInitialization.java

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import android.Manifest;
44
import android.app.Activity;
55
import android.content.Context;
6+
import android.content.Intent;
67
import android.content.pm.PackageManager;
78
import android.os.AsyncTask;
89
import android.os.Build;
@@ -36,13 +37,17 @@ public AsyncLauncherInitialization(final ILauncherCallbacks callback)
3637

3738
private InitResult init()
3839
{
39-
Log.d(this, "Starting init checks");
40-
InitResult initResult = handlePermissions();
41-
if (!initResult.mSuccess)
40+
InitResult initResult;
41+
42+
if(Build.VERSION.SDK_INT < 24)
4243
{
43-
return initResult;
44+
Log.d(this, "Starting init checks");
45+
initResult = handlePermissions();
46+
if (!initResult.mSuccess) {
47+
return initResult;
48+
}
49+
Log.d(this, "Permissions check passed");
4450
}
45-
Log.d(this, "Permissions check passed");
4651

4752
initResult = handleDataFoldersInitialization();
4853
if (!initResult.mSuccess)
@@ -62,11 +67,13 @@ private InitResult handleDataFoldersInitialization()
6267
return new InitResult(false, "Internal error");
6368
}
6469
final Context ctx = callbacks.ctx();
65-
final File baseDir = Environment.getExternalStorageDirectory();
70+
71+
final File vcmiDir = Const.getVcmiDataDir(ctx);
72+
6673
final File internalDir = ctx.getFilesDir();
67-
final File vcmiDir = new File(baseDir, Const.VCMI_DATA_ROOT_FOLDER_NAME);
6874
final File vcmiInternalDir = new File(internalDir, Const.VCMI_DATA_ROOT_FOLDER_NAME);
6975
Log.i(this, "Using " + vcmiDir.getAbsolutePath() + " as root vcmi dir");
76+
7077
if (!vcmiDir.exists()) // we don't have root folder == new install (or deleted)
7178
{
7279
boolean allCreated = vcmiDir.mkdir();
@@ -104,10 +111,10 @@ private InitResult handleDataFoldersInitialization()
104111

105112
final File testVcmiData = new File(vcmiInternalDir, "Mods/vcmi/mod.json");
106113
final boolean internalVcmiDataExisted = testVcmiData.exists();
107-
if (!internalVcmiDataExisted && !FileUtil.unpackVcmiDataToInternalDir(vcmiInternalDir, ctx.getAssets()))
108-
{
109-
// unpacking internal data normally shouldn't fail, probably only sensible reason is no space left on device
110-
return new InitResult(false, ctx.getString(R.string.launcher_error_vcmi_data_internal_missing));
114+
if (!internalVcmiDataExisted && !FileUtil.unpackVcmiDataToInternalDir(vcmiInternalDir, ctx.getAssets())) {
115+
// no h3 data present -> instruct user where to put it
116+
new InitResult(false,
117+
ctx.getString(R.string.launcher_error_h3_data_missing, vcmiDir.getAbsolutePath(), Const.VCMI_DATA_ROOT_FOLDER_NAME));
111118
}
112119

113120
final String previousInternalDataHash = callbacks.prefs().load(SharedPrefs.KEY_CURRENT_INTERNAL_ASSET_HASH, null);
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package eu.vcmi.vcmi.settings;
2+
3+
import androidx.appcompat.app.AppCompatActivity;
4+
import android.view.View;
5+
6+
import eu.vcmi.vcmi.R;
7+
import eu.vcmi.vcmi.util.GeneratedVersion;
8+
9+
/**
10+
* @author nullkiller
11+
*/
12+
public class UpdateVcmiFilesController extends LauncherSettingController<Void, Void>
13+
{
14+
private View.OnClickListener mOnSelectedAction;
15+
16+
public UpdateVcmiFilesController(
17+
final AppCompatActivity act,
18+
final View.OnClickListener onSelectedAction)
19+
{
20+
super(act);
21+
mOnSelectedAction = onSelectedAction;
22+
}
23+
24+
@Override
25+
protected String mainText()
26+
{
27+
return mActivity.getString(R.string.launcher_btn_import_zip);
28+
}
29+
30+
@Override
31+
protected String subText()
32+
{
33+
return mActivity.getString(R.string.launcher_btn_import_zip_description);
34+
}
35+
36+
@Override
37+
public void onClick(final View v)
38+
{
39+
mOnSelectedAction.onClick(v);
40+
}
41+
}

0 commit comments

Comments
 (0)