├── settings.gradle
├── app
├── src
│ └── main
│ │ ├── .gitignore
│ │ ├── res
│ │ ├── drawable
│ │ │ ├── c.png
│ │ │ ├── del.png
│ │ │ ├── di.png
│ │ │ ├── run.png
│ │ │ ├── use.png
│ │ │ ├── backup.png
│ │ │ ├── chat.png
│ │ │ ├── image0.png
│ │ │ ├── image1.png
│ │ │ ├── image2.png
│ │ │ ├── image3.png
│ │ │ ├── image4.png
│ │ │ ├── image5.png
│ │ │ ├── image6.png
│ │ │ ├── image7.png
│ │ │ ├── image8.png
│ │ │ ├── image9.png
│ │ │ ├── jump.png
│ │ │ ├── load.png
│ │ │ ├── openmw.png
│ │ │ ├── pause.png
│ │ │ ├── save.png
│ │ │ ├── wait.png
│ │ │ ├── crossbow.png
│ │ │ ├── joystick.png
│ │ │ ├── keyboard.png
│ │ │ ├── ontarget.png
│ │ │ ├── broadsword1.png
│ │ │ ├── file_icon.png
│ │ │ ├── inventory.png
│ │ │ ├── joystick_bg.png
│ │ │ ├── directory_up.png
│ │ │ ├── pointer_arrow.png
│ │ │ ├── directory_icon.png
│ │ │ ├── starsattelites.png
│ │ │ ├── divider.xml
│ │ │ ├── toolbar_dropshadow.xml
│ │ │ └── card_backround.xml
│ │ ├── values
│ │ │ ├── themes.xml
│ │ │ ├── styles.xml
│ │ │ ├── attrs.xml
│ │ │ └── strings.xml
│ │ ├── anim
│ │ │ ├── down_from_top.xml
│ │ │ └── up_from_bottom.xml
│ │ ├── values-v21
│ │ │ └── styles.xml
│ │ ├── xml
│ │ │ ├── shortcuts.xml
│ │ │ └── settings.xml
│ │ ├── menu
│ │ │ ├── menu_browser.xml
│ │ │ ├── menu_settings.xml
│ │ │ └── drawer.xml
│ │ ├── layout
│ │ │ ├── browser.xml
│ │ │ ├── listview.xml
│ │ │ ├── file_view.xml
│ │ │ ├── activity_filechoser.xml
│ │ │ ├── toolbar.xml
│ │ │ ├── main.xml
│ │ │ ├── rowlistview.xml
│ │ │ ├── configure_controls.xml
│ │ │ ├── settings.xml
│ │ │ └── screencontrols.xml
│ │ └── values-v14
│ │ │ └── styles.xml
│ │ ├── java
│ │ ├── ui
│ │ │ ├── game
│ │ │ │ └── GameState.java
│ │ │ ├── controls
│ │ │ │ ├── GamepadEmulator.java
│ │ │ │ ├── JoystickLeft.java
│ │ │ │ ├── JoystickRight.java
│ │ │ │ ├── ButtonTouchListener.java
│ │ │ │ ├── Joystick.java
│ │ │ │ ├── Osk.kt
│ │ │ │ └── Osc.kt
│ │ │ ├── screen
│ │ │ │ └── ScreenScaler.java
│ │ │ ├── activity
│ │ │ │ ├── TextListener.java
│ │ │ │ ├── GlExtensionsActivity.java
│ │ │ │ ├── ConfigureControls.kt
│ │ │ │ ├── GameActivity.java
│ │ │ │ └── MainActivity.java
│ │ │ └── fragments
│ │ │ │ ├── FragmentSettings.java
│ │ │ │ ├── FragmentControls.java
│ │ │ │ └── FragmentBrowser.java
│ │ ├── parser
│ │ │ └── CommandlineParser.java
│ │ ├── utils
│ │ │ ├── Server.java
│ │ │ ├── Utils.java
│ │ │ └── CustomAdapter.java
│ │ ├── constants
│ │ │ └── Constants.java
│ │ ├── org
│ │ │ └── libsdl
│ │ │ │ └── app
│ │ │ │ ├── SDL.java
│ │ │ │ └── SDLAudioManager.java
│ │ ├── permission
│ │ │ └── PermissionHelper.java
│ │ ├── file
│ │ │ ├── Writer.java
│ │ │ ├── utils
│ │ │ │ └── CopyFilesFromAssets.java
│ │ │ └── ConfigsFileStorageHelper.java
│ │ ├── prefs
│ │ │ └── PreferencesHelper.java
│ │ └── cursor
│ │ │ └── MouseCursor.java
│ │ ├── gdb.sh
│ │ └── AndroidManifest.xml
├── version
├── lint.xml
├── settings-base.cfg
└── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── CI
└── travis.sh
├── .gitignore
├── .travis.yml
├── upload.sh
├── gradlew.bat
├── README.md
└── gradlew
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/app/src/main/.gitignore:
--------------------------------------------------------------------------------
1 | gdb.exec
2 | jni
3 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx16384m
2 |
--------------------------------------------------------------------------------
/app/version:
--------------------------------------------------------------------------------
1 | 0.44.0
2 | 292536439eeda58becdb7e441fe2e61ebb74529e
3 | 5fd9079b26a60d3a8a52299d0ea8146b85323339
4 |
--------------------------------------------------------------------------------
/app/lint.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/c.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terabyte25/tes3mp-android/HEAD/app/src/main/res/drawable/c.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/del.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terabyte25/tes3mp-android/HEAD/app/src/main/res/drawable/del.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/di.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terabyte25/tes3mp-android/HEAD/app/src/main/res/drawable/di.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/run.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terabyte25/tes3mp-android/HEAD/app/src/main/res/drawable/run.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/use.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terabyte25/tes3mp-android/HEAD/app/src/main/res/drawable/use.png
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terabyte25/tes3mp-android/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/app/src/main/res/drawable/backup.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terabyte25/tes3mp-android/HEAD/app/src/main/res/drawable/backup.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/chat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terabyte25/tes3mp-android/HEAD/app/src/main/res/drawable/chat.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/image0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terabyte25/tes3mp-android/HEAD/app/src/main/res/drawable/image0.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/image1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terabyte25/tes3mp-android/HEAD/app/src/main/res/drawable/image1.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/image2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terabyte25/tes3mp-android/HEAD/app/src/main/res/drawable/image2.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/image3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terabyte25/tes3mp-android/HEAD/app/src/main/res/drawable/image3.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/image4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terabyte25/tes3mp-android/HEAD/app/src/main/res/drawable/image4.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/image5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terabyte25/tes3mp-android/HEAD/app/src/main/res/drawable/image5.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/image6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terabyte25/tes3mp-android/HEAD/app/src/main/res/drawable/image6.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/image7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terabyte25/tes3mp-android/HEAD/app/src/main/res/drawable/image7.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/image8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terabyte25/tes3mp-android/HEAD/app/src/main/res/drawable/image8.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/image9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terabyte25/tes3mp-android/HEAD/app/src/main/res/drawable/image9.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/jump.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terabyte25/tes3mp-android/HEAD/app/src/main/res/drawable/jump.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/load.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terabyte25/tes3mp-android/HEAD/app/src/main/res/drawable/load.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/openmw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terabyte25/tes3mp-android/HEAD/app/src/main/res/drawable/openmw.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/pause.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terabyte25/tes3mp-android/HEAD/app/src/main/res/drawable/pause.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/save.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terabyte25/tes3mp-android/HEAD/app/src/main/res/drawable/save.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/wait.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terabyte25/tes3mp-android/HEAD/app/src/main/res/drawable/wait.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/crossbow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terabyte25/tes3mp-android/HEAD/app/src/main/res/drawable/crossbow.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/joystick.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terabyte25/tes3mp-android/HEAD/app/src/main/res/drawable/joystick.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/keyboard.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terabyte25/tes3mp-android/HEAD/app/src/main/res/drawable/keyboard.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ontarget.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terabyte25/tes3mp-android/HEAD/app/src/main/res/drawable/ontarget.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/broadsword1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terabyte25/tes3mp-android/HEAD/app/src/main/res/drawable/broadsword1.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/file_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terabyte25/tes3mp-android/HEAD/app/src/main/res/drawable/file_icon.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/inventory.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terabyte25/tes3mp-android/HEAD/app/src/main/res/drawable/inventory.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/joystick_bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terabyte25/tes3mp-android/HEAD/app/src/main/res/drawable/joystick_bg.png
--------------------------------------------------------------------------------
/CI/travis.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -e
4 |
5 | cd buildscripts
6 | ./full-build.sh
7 |
8 | cd ..
9 |
10 | ./gradlew assembleDebug
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/directory_up.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terabyte25/tes3mp-android/HEAD/app/src/main/res/drawable/directory_up.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/pointer_arrow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terabyte25/tes3mp-android/HEAD/app/src/main/res/drawable/pointer_arrow.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/directory_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terabyte25/tes3mp-android/HEAD/app/src/main/res/drawable/directory_icon.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/starsattelites.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terabyte25/tes3mp-android/HEAD/app/src/main/res/drawable/starsattelites.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle
2 | build
3 | jniLibs
4 | .idea
5 | local.properties
6 | *.iml
7 | obj
8 | assets
9 | .gdb_history
10 | app/wrap
11 | fabric.properties
12 | captures
13 | mainbuild.sh
14 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | zipStoreBase=GRADLE_USER_HOME
4 | zipStorePath=wrapper/dists
5 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/divider.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: android
2 | sudo: false
3 | notifications:
4 | email: false
5 |
6 | script: ./CI/travis.sh
7 |
8 | android:
9 | components:
10 | - tools
11 | - build-tools-26.0.2
12 | - platform-tools
13 | - tools
14 | - android-27
15 |
--------------------------------------------------------------------------------
/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/toolbar_dropshadow.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/down_from_top.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/up_from_bottom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
8 |
--------------------------------------------------------------------------------
/app/src/main/java/ui/game/GameState.java:
--------------------------------------------------------------------------------
1 | package ui.game;
2 |
3 | /**
4 | * Created by sandstranger on 14.10.16.
5 | */
6 |
7 | public class GameState {
8 | private static boolean isGameStarted = false;
9 |
10 | public static boolean getGameState() {
11 | return isGameStarted;
12 | }
13 |
14 | public static void setGameState(boolean isGameStarted) {
15 | GameState.isGameStarted = isGameStarted;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/app/src/main/res/values-v21/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/shortcuts.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
13 |
14 |
--------------------------------------------------------------------------------
/app/settings-base.cfg:
--------------------------------------------------------------------------------
1 | [Video]
2 | resolution x = 800
3 | resolution y = 600
4 |
5 | [Input]
6 | camera sensitivity = 0.4
7 | toggle sneak = true
8 |
9 | [GUI]
10 | scaling factor = 1.0
11 |
12 | [Game]
13 | allow capsule shape = true
14 |
15 | [Cells]
16 | preload enabled = false
17 |
18 | [Camera]
19 | viewing distance = 2000
20 |
21 | small feature culling = true
22 | small feature culling pixel size = 16.0
23 |
24 | [Navigator]
25 | enable = false
26 |
27 | [Map]
28 | local map resolution = 128
29 |
30 | [Terrain]
31 | distant terrain = false
32 |
33 | vertex lod mod = -1
34 |
35 | lod factor = 0.25
36 |
37 | composite map level = -3
38 | composite map resolution = 128
39 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/card_backround.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | -
5 |
6 |
7 |
8 |
9 |
10 |
11 | -
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/java/parser/CommandlineParser.java:
--------------------------------------------------------------------------------
1 | package parser;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Collections;
5 |
6 | public class CommandlineParser {
7 | private ArrayList args = new ArrayList<>();
8 | private String[] argv;
9 |
10 | public CommandlineParser(String data) {
11 | args.clear();
12 | args.add("openmw");
13 | if (data.contains("--")) {
14 | Collections.addAll(args, data.split(" "));
15 | }
16 | argv = args.toArray(new String[args.size()]);
17 | }
18 |
19 | public int getArgc() {
20 | return args.size();
21 | }
22 |
23 | public String[] getArgv() {
24 | return argv;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_browser.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/src/main/gdb.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -e
4 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
5 | cd $DIR
6 |
7 | # first argument: arch of the executable used (you have to figure it out yourself!)
8 | ABI=${1:-armeabi-v7a}
9 | source ../../../buildscripts/include/version.sh
10 |
11 | # set up fake "jni" so that ndk-gdb can find a "valid" Android.mk
12 | rm -rf jni && mkdir jni
13 | echo "APP_ABI := $ABI" > jni/Android.mk
14 |
15 | rm -f gdb.exec
16 | echo "shell rm -rf jni" >> gdb.exec
17 | echo "set solib-search-path ../../../buildscripts/symbols/$ABI/" >> gdb.exec
18 | echo "set history save on" >> gdb.exec
19 | echo "set breakpoint pending on" >> gdb.exec
20 |
21 | ../../../buildscripts/toolchain/ndk/ndk-gdb --launch --nowait -x "gdb.exec"
22 |
--------------------------------------------------------------------------------
/app/src/main/java/utils/Server.java:
--------------------------------------------------------------------------------
1 | package utils;
2 |
3 | public class Server {
4 |
5 | private boolean isPassworded;
6 | private String ip;
7 | private String serverName;
8 | private int playerCount;
9 |
10 | public Server(boolean isPassworded, String ip, String serverName, int playerCount) {
11 | this.isPassworded = isPassworded;
12 | this.ip = ip;
13 | this.serverName = serverName;
14 | this.playerCount = playerCount;
15 | }
16 |
17 | public boolean getPassworded() {
18 | return isPassworded;
19 | }
20 |
21 | public String getip() {
22 | return ip;
23 | }
24 |
25 | public String getserverName() {
26 | return serverName;
27 | }
28 |
29 | public int getplayerCount() {
30 | return playerCount;
31 | }
32 | }
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_settings.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/src/main/java/ui/controls/GamepadEmulator.java:
--------------------------------------------------------------------------------
1 | package ui.controls;
2 |
3 | import org.libsdl.app.SDLControllerManager;
4 |
5 | class GamepadEmulator {
6 |
7 | private static Boolean registered = false;
8 |
9 | static void updateStick(int stickId, float x, float y) {
10 | // random device ID to make sure it doesn't conflict with anything
11 | int deviceId = 1384510555;
12 |
13 | if (!registered) {
14 | registered = true;
15 | SDLControllerManager.nativeAddJoystick(deviceId, "Virtual", "Virtual",
16 | 0xbad, 0xf00d,
17 | false, 0xFFFFFFFF,
18 | 4, 0, 0);
19 | }
20 |
21 | SDLControllerManager.onNativeJoy(deviceId, stickId * 2 , x);
22 | SDLControllerManager.onNativeJoy(deviceId, stickId * 2 + 1, y);
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/app/src/main/java/constants/Constants.java:
--------------------------------------------------------------------------------
1 | package constants;
2 |
3 | public class Constants {
4 |
5 | public static String textureCompressionMode ="";
6 | public static final String APP_PREFERENCES = "settings";
7 | public static final String HIDE_CONTROLS = "hidecontrols";
8 |
9 | public static final String DATA_PATH = "data_path";
10 | public static final String LANGUAGE = "encoding";
11 | public static final String SUBTITLES = "subtitiles";
12 | public static final String MIPMAPPING = "mipmapping";
13 | public static final String COMMAND_LINE = "commandline";
14 | public static final String TOUCH_SENSITIVITY= "camera sensitivity";
15 | public static final String CAMERA_MULTIPLISER = "camera y multiplier";
16 | public static final String MOUSE_TRANSPARENCY = "mouse transparency";
17 | public static String commandLineData = "";
18 |
19 | public static String APPLICATION_DATA_STORAGE_PATH ="";
20 | }
21 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/browser.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
14 |
15 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/drawer.xml:
--------------------------------------------------------------------------------
1 |
2 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/listview.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/app/src/main/java/utils/Utils.java:
--------------------------------------------------------------------------------
1 | package utils;
2 |
3 | import android.app.Activity;
4 | import android.view.View;
5 |
6 | import java.io.File;
7 |
8 | /**
9 | * Created by sandstranger on 15.01.17.
10 | */
11 |
12 | public class Utils {
13 | public static void deleteFile(String path) {
14 | File file = new File(path);
15 | if (file.exists()) {
16 | file.delete();
17 | }
18 | }
19 |
20 | public static boolean fileExists(String path) {
21 | return new File(path).exists();
22 | }
23 |
24 | public static void hideAndroidControls(Activity activity) {
25 | activity.getWindow().getDecorView().setSystemUiVisibility(
26 | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
27 | | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
28 | | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
29 | | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
30 | | View.SYSTEM_UI_FLAG_FULLSCREEN
31 | | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/app/src/main/java/org/libsdl/app/SDL.java:
--------------------------------------------------------------------------------
1 | package org.libsdl.app;
2 |
3 | import android.content.Context;
4 |
5 | /**
6 | SDL library initialization
7 | */
8 | public class SDL {
9 |
10 | // This function should be called first and sets up the native code
11 | // so it can call into the Java classes
12 | public static void setupJNI() {
13 | SDLActivity.nativeSetupJNI();
14 | SDLAudioManager.nativeSetupJNI();
15 | SDLControllerManager.nativeSetupJNI();
16 | }
17 |
18 | // This function should be called each time the activity is started
19 | public static void initialize() {
20 | setContext(null);
21 |
22 | SDLActivity.initialize();
23 | SDLAudioManager.initialize();
24 | SDLControllerManager.initialize();
25 | }
26 |
27 | // This function stores the current activity (SDL or not)
28 | public static void setContext(Context context) {
29 | mContext = context;
30 | }
31 |
32 | public static Context getContext() {
33 | return mContext;
34 | }
35 |
36 | protected static Context mContext;
37 | }
38 |
--------------------------------------------------------------------------------
/app/src/main/java/permission/PermissionHelper.java:
--------------------------------------------------------------------------------
1 | package permission;
2 |
3 | import android.Manifest;
4 | import android.app.Activity;
5 | import android.content.pm.PackageManager;
6 | import android.os.Build;
7 | import android.support.v4.app.ActivityCompat;
8 | import android.support.v4.content.ContextCompat;
9 |
10 | /**
11 | * Created by sandstranger on 05.09.16.
12 | */
13 | public class PermissionHelper {
14 | public static void getWriteExternalStoragePermission(Activity activity) {
15 | if (Build.VERSION.SDK_INT >= 23) {
16 | if (ContextCompat.checkSelfPermission(activity,
17 | Manifest.permission.WRITE_EXTERNAL_STORAGE)
18 | != PackageManager.PERMISSION_GRANTED) {
19 | if (ActivityCompat.shouldShowRequestPermissionRationale(activity,
20 | Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
21 | } else {
22 | ActivityCompat.requestPermissions(activity,
23 | new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 23
24 | );
25 | }
26 | }
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/app/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/file_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
9 |
10 |
11 |
20 |
21 |
22 |
23 |
30 |
31 |
32 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_filechoser.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
10 |
11 |
14 |
15 |
19 |
20 |
25 |
26 |
33 |
34 |
35 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/app/src/main/java/ui/controls/JoystickLeft.java:
--------------------------------------------------------------------------------
1 | package ui.controls;
2 |
3 | import android.content.Context;
4 | import android.support.v4.math.MathUtils;
5 | import android.util.AttributeSet;
6 |
7 | public class JoystickLeft extends Joystick {
8 |
9 | public JoystickLeft(Context context) {
10 | super(context);
11 | }
12 |
13 | public JoystickLeft(Context context, AttributeSet attrs) {
14 | super(context, attrs);
15 | }
16 |
17 | public JoystickLeft(Context context, AttributeSet attrs, int defStyle) {
18 | super(context, attrs, defStyle);
19 | }
20 |
21 | @Override protected void updateStick() {
22 | if (down) {
23 | // GamepadEmulator takes values on a scale [-1; 1] so convert our values
24 | float w = getWidth() / 3;
25 | float diffX = currentX - initialX;
26 | float diffY = currentY - initialY;
27 |
28 | float bias = 0.3f;
29 |
30 | if (Math.abs(diffX) > Math.abs(diffY)) {
31 | diffY = Math.signum(diffY) * (Math.max(0, Math.abs(diffY) - bias * Math.abs(diffX)));
32 | } else {
33 | diffX = Math.signum(diffX) * (Math.max(0, Math.abs(diffX) - bias * Math.abs(diffY)));
34 | }
35 |
36 | float dx = MathUtils.clamp(diffX / w + 0.2f * Math.signum(diffX), -1, 1);
37 | float dy = MathUtils.clamp(diffY / w + 0.2f * Math.signum(diffY), -1, 1);
38 | GamepadEmulator.updateStick(stickId, dx, dy);
39 | } else {
40 | GamepadEmulator.updateStick(stickId, 0, 0);
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/app/src/main/java/ui/controls/JoystickRight.java:
--------------------------------------------------------------------------------
1 | package ui.controls;
2 |
3 | import android.content.Context;
4 | import android.os.Handler;
5 | import android.util.AttributeSet;
6 | import android.util.Log;
7 | import android.view.MotionEvent;
8 |
9 | import org.libsdl.app.SDLActivity;
10 |
11 | public class JoystickRight extends Joystick {
12 |
13 | private float curX, curY;
14 |
15 | public JoystickRight(Context context) {
16 | super(context);
17 | }
18 |
19 | public JoystickRight(Context context, AttributeSet attrs) {
20 | super(context, attrs);
21 | }
22 |
23 | public JoystickRight(Context context, AttributeSet attrs, int defStyle) {
24 | super(context, attrs, defStyle);
25 | }
26 |
27 | @Override public boolean onTouchEvent(MotionEvent event) {
28 | switch (event.getActionMasked()) {
29 | case MotionEvent.ACTION_DOWN:
30 | curX = event.getX();
31 | curY = event.getY();
32 |
33 | break;
34 | case MotionEvent.ACTION_MOVE:
35 | float newX = event.getX();
36 | float newY = event.getY();
37 |
38 | float mouseScalingFactor = 2.0f; // TODO: make configurable
39 |
40 | float movementX = (newX - curX) * mouseScalingFactor;
41 | float movementY = (newY - curY) * mouseScalingFactor;
42 |
43 | SDLActivity.sendRelativeMouseMotion(Math.round(movementX), Math.round(movementY));
44 |
45 | curX = newX;
46 | curY = newY;
47 | break;
48 | }
49 |
50 | return super.onTouchEvent(event);
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/app/src/main/java/file/Writer.java:
--------------------------------------------------------------------------------
1 | package file;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.File;
5 | import java.io.FileInputStream;
6 | import java.io.FileWriter;
7 | import java.io.IOException;
8 | import java.io.InputStreamReader;
9 |
10 | /**
11 | * Created by sandstranger on 13.10.16.
12 | */
13 |
14 | public class Writer {
15 |
16 | // TODO: refactor so that the order of arguments is (path, key, value)
17 | public static void write(String data, String path, String value)
18 | throws IOException {
19 | // Create a new empty file if it doesn't already exist
20 | File fin = new File(path);
21 | //noinspection ResultOfMethodCallIgnored
22 | fin.createNewFile();
23 |
24 | FileInputStream file = new FileInputStream(path);
25 | BufferedReader reader = new BufferedReader(new InputStreamReader(file));
26 | String line = reader.readLine();
27 | StringBuilder builder = new StringBuilder();
28 | boolean contains = false;
29 | while (line != null) {
30 | if (line.startsWith(value) && !contains) {
31 | builder.append(value + "=" + data);
32 | contains = true;
33 | } else
34 | builder.append(line);
35 | builder.append("\n");
36 | line = reader.readLine();
37 | }
38 | if (!contains)
39 | builder.append(value + "=" + data);
40 |
41 | reader.close();
42 | FileWriter writer = new FileWriter(path);
43 | writer.write(builder.toString());
44 | writer.flush();
45 | writer.close();
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/toolbar.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
17 |
27 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/upload.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | #
3 | # Author: Stefan Buck
4 | # License: MIT
5 | # https://gist.github.com/stefanbuck/ce788fee19ab6eb0b4447a85fc99f447
6 | #
7 | #
8 | # This script accepts the following parameters:
9 | #
10 | # * owner
11 | # * repo
12 | # * tag
13 | # * filename
14 | # * github_api_token
15 | #
16 | # Script to upload a release asset using the GitHub API v3.
17 | #
18 | # Example:
19 | #
20 | # upload-github-release-asset.sh github_api_token=TOKEN owner=stefanbuck repo=playground tag=v0.1.0 filename=./build.zip
21 | #
22 |
23 | # Check dependencies.
24 | set -e
25 | xargs=$(which gxargs || which xargs)
26 |
27 | # Validate settings.
28 | [ "$TRACE" ] && set -x
29 |
30 | CONFIG=$@
31 |
32 | for line in $CONFIG; do
33 | eval "$line"
34 | done
35 |
36 | # Define variables.
37 | GH_API="https://api.github.com"
38 | GH_REPO="$GH_API/repos/$owner/$repo"
39 | GH_TAGS="$GH_REPO/releases/tags/$tag"
40 | AUTH="Authorization: token $github_api_token"
41 | WGET_ARGS="--content-disposition --auth-no-challenge --no-cookie"
42 | CURL_ARGS="-LJO#"
43 |
44 | if [[ "$tag" == 'LATEST' ]]; then
45 | GH_TAGS="$GH_REPO/releases/latest"
46 | fi
47 |
48 | # Validate token.
49 | curl -o /dev/null -sH "$AUTH" $GH_REPO || { echo "Error: Invalid repo, token or network issue!"; exit 1; }
50 |
51 | # Read asset tags.
52 | response=$(curl -sH "$AUTH" $GH_TAGS)
53 |
54 | # Get ID of the asset based on given filename.
55 | eval $(echo "$response" | grep -m 1 "id.:" | grep -w id | tr : = | tr -cd '[[:alnum:]]=')
56 | [ "$id" ] || { echo "Error: Failed to get release id for tag: $tag"; echo "$response" | awk 'length($0)<100' >&2; exit 1; }
57 |
58 | # Upload asset
59 | echo "Uploading asset... "
60 |
61 | # Construct url
62 | GH_ASSET="https://uploads.github.com/repos/$owner/$repo/releases/$id/assets?name=$(basename $filename)"
63 |
64 | curl "$GITHUB_OAUTH_BASIC" --data-binary @"$filename" -H "Authorization: token $github_api_token" -H "Content-Type: application/octet-stream" $GH_ASSET
--------------------------------------------------------------------------------
/app/src/main/java/file/utils/CopyFilesFromAssets.java:
--------------------------------------------------------------------------------
1 | package file.utils;
2 |
3 | import java.io.File;
4 | import java.io.FileOutputStream;
5 | import java.io.IOException;
6 | import java.io.InputStream;
7 | import java.io.OutputStream;
8 |
9 | import android.content.Context;
10 | import android.content.res.AssetManager;
11 | import android.util.Log;
12 |
13 | public class CopyFilesFromAssets {
14 |
15 | private Context context;
16 | private String configsPath;
17 | public CopyFilesFromAssets(Context context,String configsPath) {
18 | this.context=context;
19 | this.configsPath=configsPath;
20 | }
21 | public void copyFileOrDir(String path) {
22 |
23 | AssetManager assetManager = context.getAssets();
24 | String assets[] = null;
25 | try {
26 | assets = assetManager.list(path);
27 | if (assets.length == 0) {
28 | copyFile(path);
29 | } else {
30 | String fullPath = configsPath;
31 | File dir = new File(fullPath);
32 | if (!dir.exists())
33 | dir.mkdirs();
34 | for (int i = 0; i < assets.length; ++i) {
35 | copyFileOrDir(path + "/" + assets[i]);
36 | }
37 | }
38 | } catch (IOException ex) {
39 |
40 | }
41 |
42 | }
43 |
44 | private void copyFile(String filename) {
45 |
46 | AssetManager assetManager = context.getAssets();
47 |
48 | InputStream in = null;
49 | OutputStream out = null;
50 | try {
51 | in = assetManager.open(filename);
52 | filename = filename.replace("libopenmw", "");
53 | String newFileName = configsPath + filename;
54 | File tmp = new File(newFileName);
55 | String dirPath = newFileName.replace(tmp.getName(), "");
56 | File dir = new File(dirPath);
57 | if (!dir.exists())
58 | dir.mkdirs();
59 | out = new FileOutputStream(newFileName);
60 |
61 | byte[] buffer = new byte[1024];
62 | int read;
63 | while ((read = in.read(buffer)) != -1) {
64 | out.write(buffer, 0, read);
65 | }
66 | in.close();
67 | in = null;
68 | out.flush();
69 | out.close();
70 | out = null;
71 | } catch (Exception e) {
72 |
73 | }
74 |
75 | }
76 |
77 | }
78 |
--------------------------------------------------------------------------------
/app/src/main/java/ui/screen/ScreenScaler.java:
--------------------------------------------------------------------------------
1 | package ui.screen;
2 |
3 | import android.util.TypedValue;
4 | import android.view.View;
5 | import android.widget.TextView;
6 |
7 | public class ScreenScaler {
8 |
9 | public static int height;
10 | public static int width;
11 | private static ScreenScaler _instance = null;
12 | private final int STANDARD_WIDTH = 1024;
13 | private final int STANDARD_HEIGHT = 768;
14 | private float scaleRatio_y = 0;
15 | private float scaleRatio_x = 0;
16 | private int marginX = 0;
17 | private int marginY = 0;
18 |
19 | private ScreenScaler() {
20 |
21 | float x = (float) width / STANDARD_WIDTH;
22 | float y = (float) height / STANDARD_HEIGHT;
23 | scaleRatio_x = Math.max(x, y);
24 | scaleRatio_y = Math.min(x, y);
25 | marginX = (width - (int) ((float) STANDARD_WIDTH * scaleRatio_x)) / 2;
26 | marginY = (height - (int) ((float) STANDARD_HEIGHT * scaleRatio_y)) / 2;
27 | }
28 |
29 | public static void textScaler(View v, float size) {
30 | float text_height =(float) v.getHeight() / size;
31 | ((TextView) v).setTextSize(TypedValue.COMPLEX_UNIT_PX, text_height);
32 | }
33 |
34 | public static void changeTextSize(final View v, final float size) {
35 | v.post(new Runnable() {
36 | @Override
37 | public void run() {
38 | textScaler(v, size);
39 |
40 | }
41 |
42 | });
43 | }
44 |
45 | public static ScreenScaler getInstance() {
46 | if (_instance == null)
47 | _instance = new ScreenScaler();
48 | return _instance;
49 | }
50 |
51 | public int getScaledFontSize(int coordinate) {
52 | int result = (int) ((float) coordinate * scaleRatio_y);
53 | return result;
54 | }
55 |
56 | public int getScaledCoordinateX(int coordinate) {
57 | float scaled = (float) coordinate * scaleRatio_x;
58 | int result = marginX + (int) (scaled < 1 ? coordinate : scaled);
59 | return result;
60 | }
61 |
62 | public int getScaledCoordinateY(int coordinate) {
63 | float scaled = (float) coordinate * scaleRatio_y;
64 | int result = marginY + (int) (scaled < 1 ? coordinate : scaled);
65 | return result;
66 | }
67 |
68 | }
69 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | maven { url 'https://maven.fabric.io/public' }
4 | }
5 |
6 | dependencies {
7 | classpath 'io.fabric.tools:gradle:1.+'
8 | }
9 | }
10 | apply plugin: 'com.android.application'
11 | apply plugin: 'io.fabric'
12 | apply plugin: 'kotlin-android'
13 |
14 | ext {
15 | versionFile = new File(project.rootDir, 'app/src/main/assets/libopenmw/resources/version')
16 | calculateVersion = {
17 | stream = new FileInputStream(versionFile)
18 | return stream.readLines()[0]
19 | }
20 | }
21 |
22 | android {
23 | compileSdkVersion 27
24 |
25 | sourceSets {
26 | main {
27 | resources {
28 | srcDir {
29 | // To include the wrap.sh script required by ASAN
30 | "wrap/res"
31 | }
32 | }
33 | }
34 | }
35 |
36 | defaultConfig {
37 | applicationId "com.libopenmw.openmw"
38 | minSdkVersion 21
39 | targetSdkVersion 27
40 | versionName calculateVersion()
41 | }
42 |
43 | lintOptions {
44 | checkReleaseBuilds false
45 | }
46 |
47 | compileOptions {
48 | targetCompatibility 1.8
49 | sourceCompatibility 1.8
50 | }
51 | }
52 | repositories {
53 | jcenter()
54 | maven {
55 | url "https://jitpack.io"
56 | }
57 | maven { url 'https://maven.fabric.io/public' }
58 | }
59 |
60 | dependencies {
61 | implementation 'com.afollestad.material-dialogs:core:0.9.6.0'
62 | implementation 'com.android.support:appcompat-v7:27.1.1'
63 | implementation 'com.android.support:design:27.1.1'
64 | implementation 'com.github.machinarius:preferencefragment:0.1.1'
65 | implementation 'com.melnykov:floatingactionbutton:1.3.0'
66 |
67 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
68 | implementation 'com.crashlytics.sdk.android:crashlytics:2.9.9'
69 | implementation 'com.crashlytics.sdk.android:crashlytics-ndk:2.0.5'
70 | }
71 |
72 | crashlytics {
73 | enableNdk true
74 | androidNdkOut '../buildscripts/symbols'
75 | androidNdkLibsOut 'src/main/jniLibs'
76 | }
77 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/main.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
12 |
13 |
16 |
17 |
20 |
21 |
28 |
29 |
39 |
40 |
44 |
45 |
46 |
47 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/rowlistview.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
14 |
15 |
16 |
23 |
24 |
28 |
29 |
36 |
37 |
38 |
46 |
47 |
48 |
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/app/src/main/java/ui/activity/TextListener.java:
--------------------------------------------------------------------------------
1 | package ui.activity;
2 |
3 | import file.ConfigsFileStorageHelper;
4 | import prefs.PreferencesHelper;
5 |
6 | import android.app.Activity;
7 | import android.content.SharedPreferences;
8 | import android.content.SharedPreferences.Editor;
9 | import android.text.Editable;
10 | import android.text.TextWatcher;
11 |
12 | public class TextListener implements TextWatcher {
13 |
14 | private SharedPreferences Settings;
15 | private String data;
16 |
17 | private String value;
18 | private String sharedprefValue;
19 | private String gameValue;
20 | private Activity a;
21 | private String mode = "";
22 |
23 | public TextListener(Activity a, String data, String value,
24 | String sharedprefValue, String gameValue,
25 | SharedPreferences Settings, String mode) {
26 | this.data = data;
27 |
28 | this.value = value;
29 | this.sharedprefValue = sharedprefValue;
30 | this.gameValue = gameValue;
31 | this.Settings = Settings;
32 | this.a = a;
33 | this.mode = mode;
34 | }
35 |
36 | @Override
37 | public void afterTextChanged(final Editable s) {
38 | saveData(s.toString());
39 |
40 | }
41 |
42 | @Override
43 | public void beforeTextChanged(CharSequence s, int start, int count,
44 | int after) {
45 |
46 | }
47 |
48 | @Override
49 | public void onTextChanged(CharSequence s, int start, int before, int count) {
50 | saveToSharedPreferences(sharedprefValue, s.toString());
51 | gameValue = s.toString();
52 | PreferencesHelper.getPrefValues(a);
53 | }
54 |
55 | private void saveData(final String s) {
56 |
57 | try {
58 | switch (mode) {
59 | case "configs":
60 | file.Writer.write(s + data, s + "/config/openmw/openmw.cfg",
61 | value);
62 |
63 | break;
64 | case "data":
65 | file.Writer.write(s + data, ConfigsFileStorageHelper.CONFIGS_FILES_STORAGE_PATH
66 | + "/config/openmw/openmw.cfg", value);
67 |
68 | break;
69 |
70 | default:
71 | break;
72 | }
73 |
74 | } catch (Exception e) {
75 |
76 | }
77 |
78 |
79 |
80 | }
81 |
82 | private void saveToSharedPreferences(String value, String buffer) {
83 | Editor editor = Settings.edit();
84 | editor.putString(value, buffer);
85 | editor.commit();
86 | }
87 |
88 | }
--------------------------------------------------------------------------------
/app/src/main/java/ui/fragments/FragmentSettings.java:
--------------------------------------------------------------------------------
1 | package ui.fragments;
2 |
3 | import android.content.SharedPreferences;
4 | import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
5 | import android.os.Bundle;
6 | import android.preference.EditTextPreference;
7 | import android.preference.Preference;
8 | import android.preference.PreferenceGroup;
9 |
10 | import com.github.machinarius.preferencefragment.PreferenceFragment;
11 | import com.libopenmw.openmw.R;
12 |
13 | public class FragmentSettings extends PreferenceFragment implements OnSharedPreferenceChangeListener {
14 |
15 | @Override
16 | public void onCreate(final Bundle savedInstanceState) {
17 | super.onCreate(savedInstanceState);
18 |
19 | addPreferencesFromResource(R.xml.settings);
20 | getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
21 | }
22 |
23 | @Override
24 | public void onResume() {
25 | super.onResume();
26 | for (int i = 0; i < getPreferenceScreen().getPreferenceCount(); ++i) {
27 | Preference preference = getPreferenceScreen().getPreference(i);
28 | if (preference instanceof PreferenceGroup) {
29 | PreferenceGroup preferenceGroup = (PreferenceGroup) preference;
30 | for (int j = 0; j < preferenceGroup.getPreferenceCount(); ++j) {
31 | Preference singlePref = preferenceGroup.getPreference(j);
32 | updatePreference(singlePref, singlePref.getKey());
33 | }
34 | } else {
35 | updatePreference(preference, preference.getKey());
36 | }
37 | }
38 | }
39 |
40 | @Override
41 | public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
42 | updatePreference(findPreference(key), key);
43 | }
44 |
45 | private void updatePreference(Preference preference, String key) {
46 | if (preference == null)
47 | return;
48 | if (preference instanceof EditTextPreference) {
49 | EditTextPreference editTextPreference = (EditTextPreference) preference;
50 | editTextPreference.setSummary(editTextPreference.getText());
51 | }
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/app/src/main/java/ui/controls/ButtonTouchListener.java:
--------------------------------------------------------------------------------
1 | package ui.controls;
2 |
3 | import android.view.MotionEvent;
4 | import android.view.View;
5 | import android.view.View.OnTouchListener;
6 |
7 | import org.libsdl.app.SDLActivity;
8 |
9 | public class ButtonTouchListener implements OnTouchListener {
10 |
11 | private int keyCode;
12 | boolean needEmulateMouse = false;
13 |
14 | private enum Movement {
15 | KEY_DOWN,
16 | KEY_UP,
17 | MOUSE_DOWN,
18 | MOUSE_UP
19 | }
20 |
21 | public ButtonTouchListener(int keyCode, boolean needEmulateMouse) {
22 | this.keyCode = keyCode;
23 | this.needEmulateMouse = needEmulateMouse;
24 | SDLActivity.mSeparateMouseAndTouch = needEmulateMouse;
25 | }
26 |
27 | @Override
28 | public boolean onTouch(final View v, MotionEvent event) {
29 | switch (event.getAction()) {
30 | case MotionEvent.ACTION_DOWN:
31 | onTouchDown(v);
32 | return true;
33 | case MotionEvent.ACTION_UP:
34 | onTouchUp(v);
35 | return true;
36 |
37 | case MotionEvent.ACTION_CANCEL:
38 | onTouchUp(v);
39 | return true;
40 | }
41 | return false;
42 | }
43 |
44 | private void onTouchDown(View v) {
45 | if (!needEmulateMouse) {
46 | eventMovement(Movement.KEY_DOWN);
47 | } else {
48 | eventMovement(Movement.MOUSE_DOWN);
49 | }
50 | }
51 |
52 | private void onTouchUp(View v) {
53 | if (!needEmulateMouse) {
54 | eventMovement(Movement.KEY_UP);
55 | } else {
56 | eventMovement(Movement.MOUSE_UP);
57 | }
58 | }
59 |
60 | protected void eventMovement(Movement event) {
61 | switch (event) {
62 | case KEY_DOWN:
63 | SDLActivity.onNativeKeyDown(keyCode);
64 | break;
65 | case KEY_UP:
66 | SDLActivity.onNativeKeyUp(keyCode);
67 | break;
68 | case MOUSE_DOWN:
69 | SDLActivity.sendMouseButton(1, keyCode);
70 | break;
71 | case MOUSE_UP:
72 | SDLActivity.sendMouseButton(0, keyCode);
73 | break;
74 | }
75 | }
76 | }
--------------------------------------------------------------------------------
/app/src/main/java/prefs/PreferencesHelper.java:
--------------------------------------------------------------------------------
1 | package prefs;
2 |
3 | import constants.Constants;
4 |
5 | import android.app.Activity;
6 | import android.content.Context;
7 | import android.content.SharedPreferences;
8 | import android.content.SharedPreferences.Editor;
9 | import android.os.Environment;
10 |
11 | public class PreferencesHelper {
12 |
13 | public static void getPrefValues(Activity a) {
14 | SharedPreferences Settings;
15 | Settings = a.getSharedPreferences(Constants.APP_PREFERENCES,
16 | Context.MODE_PRIVATE);
17 | Constants.APPLICATION_DATA_STORAGE_PATH = Settings.getString(Constants.DATA_PATH,
18 | Environment.getExternalStorageDirectory() + "/libopenmw/data");
19 | Constants.commandLineData = Settings.getString(Constants.COMMAND_LINE,
20 | "");
21 |
22 | }
23 |
24 | public static String getPreferences(String prefKey, Activity a, String defaultValue) {
25 | SharedPreferences settings;
26 | settings = a.getSharedPreferences(Constants.APP_PREFERENCES,
27 | Context.MODE_PRIVATE);
28 | return settings.getString(prefKey,
29 | defaultValue);
30 |
31 | }
32 |
33 | public static boolean getPreferences(String prefKey, Activity a) {
34 | SharedPreferences settings;
35 | settings = a.getSharedPreferences(Constants.APP_PREFERENCES,
36 | Context.MODE_PRIVATE);
37 | return settings.getBoolean(prefKey,
38 | false);
39 | }
40 |
41 | public static void setPreferences(String prefKey, Activity a,boolean value) {
42 | SharedPreferences Settings;
43 | Settings = a.getSharedPreferences(Constants.APP_PREFERENCES,
44 | Context.MODE_PRIVATE);
45 | Editor editor = Settings.edit();
46 | editor.putBoolean(prefKey, value);
47 | editor.commit();
48 | }
49 |
50 | public static void setPreferences(String prefKey, int value, Activity a) {
51 | SharedPreferences Settings;
52 | Settings = a.getSharedPreferences(Constants.APP_PREFERENCES,
53 | Context.MODE_PRIVATE);
54 | Editor editor = Settings.edit();
55 | editor.putInt(prefKey, value);
56 | editor.commit();
57 | }
58 |
59 | public static void setPreferences(String prefKey, String value, Activity a) {
60 | SharedPreferences Settings;
61 | Settings = a.getSharedPreferences(Constants.APP_PREFERENCES,
62 | Context.MODE_PRIVATE);
63 | Editor editor = Settings.edit();
64 | editor.putString(prefKey, value);
65 | editor.commit();
66 |
67 | }
68 |
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/configure_controls.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
13 |
14 |
15 |
19 |
20 |
27 |
28 |
35 |
36 |
37 |
38 |
42 |
43 |
50 |
51 |
58 |
59 |
60 |
61 |
67 |
68 |
74 |
75 |
76 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
21 |
26 |
27 |
28 |
29 |
30 |
33 |
34 |
35 |
40 |
41 |
42 |
47 |
52 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/app/src/main/java/cursor/MouseCursor.java:
--------------------------------------------------------------------------------
1 | package cursor;
2 |
3 | import android.content.Context;
4 | import android.content.res.Resources;
5 | import android.content.SharedPreferences;
6 | import android.preference.PreferenceManager;
7 | import android.util.TypedValue;
8 | import android.view.Choreographer;
9 | import android.view.View;
10 | import android.widget.ImageView;
11 | import android.widget.RelativeLayout;
12 |
13 | import com.libopenmw.openmw.R;
14 |
15 | import constants.Constants;
16 |
17 | import org.libsdl.app.SDLActivity;
18 |
19 | import ui.activity.GameActivity;
20 | import ui.activity.MainActivity;
21 |
22 | public class MouseCursor implements Choreographer.FrameCallback {
23 |
24 | private Choreographer choreographer;
25 | private ImageView cursor;
26 |
27 | private SharedPreferences Settings;
28 |
29 | private boolean touchcontrol = false;
30 |
31 | public MouseCursor(GameActivity activity) {
32 | Settings = activity.getSharedPreferences(
33 | Constants.APP_PREFERENCES, Context.MODE_PRIVATE);
34 |
35 | touchcontrol = PreferenceManager.getDefaultSharedPreferences(activity).getBoolean("touchControl", false);
36 |
37 | cursor = new ImageView(activity);
38 | if (!touchcontrol)
39 | cursor.setImageResource(R.drawable.pointer_arrow);
40 |
41 | Resources r = activity.getResources();
42 | int px = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 30, r.getDisplayMetrics());
43 | cursor.setLayoutParams(new RelativeLayout.LayoutParams((int) Math.round(px / 1.5), px));
44 |
45 | RelativeLayout layout = activity.getLayout();
46 | layout.addView(cursor);
47 |
48 | choreographer = Choreographer.getInstance();
49 | choreographer.postFrameCallback(this);
50 |
51 | float alpha = Settings.getFloat(Constants.MOUSE_TRANSPARENCY, 100.0f);
52 |
53 | cursor.setAlpha((alpha / 100.0f));
54 |
55 | }
56 |
57 | @Override
58 | public void doFrame(long frameTimeNanos) {
59 | if (SDLActivity.isMouseShown() == 0) {
60 | if (!touchcontrol)
61 | cursor.setVisibility(View.GONE);
62 | else
63 | GameActivity.osc.disableElements(false);
64 | } else {
65 | if (!touchcontrol)
66 | cursor.setVisibility(View.VISIBLE);
67 | else
68 | GameActivity.osc.disableElements(true);
69 |
70 | View surface = SDLActivity.getSurface();
71 |
72 | float translateX = 1.0f * surface.getWidth() / MainActivity.resolutionX;
73 | float translateY = 1.0f * surface.getHeight() / MainActivity.resolutionY;
74 |
75 | int mouseX = SDLActivity.getMouseX();
76 | int mouseY = SDLActivity.getMouseY();
77 |
78 | cursor.setX(mouseX * translateX + surface.getLeft());
79 | cursor.setY(mouseY * translateY + surface.getTop());
80 | }
81 |
82 | choreographer.postFrameCallback(this);
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/app/src/main/res/values-v14/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 |
10 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/app/src/main/java/ui/controls/Joystick.java:
--------------------------------------------------------------------------------
1 | package ui.controls;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.Color;
6 | import android.graphics.Paint;
7 | import android.support.v4.math.MathUtils;
8 | import android.util.AttributeSet;
9 | import android.view.MotionEvent;
10 | import android.view.View;
11 |
12 | import org.libsdl.app.SDLActivity;
13 |
14 | public class Joystick extends View {
15 |
16 | // Initial touch position
17 | protected float initialX, initialY;
18 | // Current touch position
19 | protected float currentX = -1, currentY = -1;
20 | // Whether the finger is down
21 | protected Boolean down = false;
22 | // left or right stick
23 | protected int stickId = 0;
24 |
25 | private Paint paint = new Paint();
26 |
27 | public Joystick(Context context) {
28 | super(context);
29 | }
30 |
31 | public Joystick(Context context, AttributeSet attrs) {
32 | super(context, attrs);
33 | }
34 |
35 | public Joystick(Context context, AttributeSet attrs, int defStyle) {
36 | super(context, attrs, defStyle);
37 | }
38 |
39 | public void setStick(int id) {
40 | stickId = id;
41 | }
42 |
43 | @Override
44 | public void onDraw(Canvas canvas) {
45 | super.onDraw(canvas);
46 |
47 | paint.setStyle(Paint.Style.STROKE);
48 | paint.setColor(Color.RED);
49 | canvas.drawRect(0, 0, getWidth() - 1, getHeight() - 1, paint);
50 | paint.setColor(Color.GREEN);
51 |
52 | // Draw circle for initial touch
53 | if (down) {
54 | canvas.drawCircle(initialX, initialY, getWidth() / 10, paint);
55 | }
56 |
57 | if (currentX < 0 || currentY < 0) {
58 | currentX = currentY = getWidth() / 2;
59 | }
60 |
61 | // Draw circle for current stick position
62 | canvas.drawCircle(currentX, currentY, getWidth() / 5, paint);
63 | }
64 |
65 | @Override
66 | public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
67 | setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
68 | }
69 |
70 | @Override
71 | public boolean onTouchEvent(MotionEvent event) {
72 | int action = event.getActionMasked();
73 |
74 | switch (action) {
75 | case MotionEvent.ACTION_DOWN: {
76 | initialX = event.getX();
77 | initialY = event.getY();
78 | down = true;
79 | }
80 | case MotionEvent.ACTION_MOVE: {
81 | currentX = event.getX();
82 | currentY = event.getY();
83 | break;
84 | }
85 | case MotionEvent.ACTION_UP: {
86 | down = false;
87 | currentX = currentY = -1;
88 | break;
89 | }
90 | }
91 |
92 | updateStick();
93 | invalidate();
94 | return true;
95 | }
96 |
97 | protected void updateStick() {
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/app/src/main/java/ui/activity/GlExtensionsActivity.java:
--------------------------------------------------------------------------------
1 | package ui.activity;
2 |
3 | import android.app.Activity;
4 | import android.app.ActivityManager;
5 | import android.content.Context;
6 | import android.content.Intent;
7 | import android.content.pm.ConfigurationInfo;
8 | import android.opengl.GLSurfaceView;
9 | import android.os.Bundle;
10 | import android.util.Log;
11 | import android.widget.FrameLayout;
12 |
13 | import javax.microedition.khronos.egl.EGLConfig;
14 | import javax.microedition.khronos.opengles.GL10;
15 |
16 | import constants.Constants;
17 |
18 | /**
19 | * Created by sylar on 15.07.15.
20 | */
21 | public class GlExtensionsActivity extends Activity {
22 |
23 | private String GLExtensions = "";
24 |
25 | @Override
26 | protected void onCreate(Bundle savedInstanceState) {
27 | super.onCreate(savedInstanceState);
28 | getGlExtencions();
29 | }
30 |
31 | private void getGlExtencions() {
32 | if (isDeviceSupportGles3()) {
33 | Constants.textureCompressionMode = "ETC2";
34 | startMainActivity();
35 | } else {
36 | GLSurfaceView surfaceView = new GLSurfaceView(this);
37 | surfaceView.setRenderer(renderer);
38 |
39 | setContentView(surfaceView,
40 | new FrameLayout.LayoutParams(
41 | FrameLayout.LayoutParams.WRAP_CONTENT,
42 | FrameLayout.LayoutParams.WRAP_CONTENT));
43 | }
44 |
45 | }
46 |
47 | GLSurfaceView.Renderer renderer = new GLSurfaceView.Renderer() {
48 | @Override
49 | public void onSurfaceCreated(GL10 gl, EGLConfig config) {
50 |
51 | GLExtensions = gl.glGetString(GL10.GL_EXTENSIONS);
52 | computeTextureCompressionMode();
53 | startMainActivity();
54 | }
55 |
56 | @Override
57 | public void onSurfaceChanged(GL10 gl, int width, int height) {
58 |
59 | }
60 |
61 | @Override
62 | public void onDrawFrame(GL10 gl) {
63 |
64 | }
65 | };
66 |
67 | private void startMainActivity() {
68 | GlExtensionsActivity.this.startActivity(new Intent(GlExtensionsActivity.this, MainActivity.class));
69 | GlExtensionsActivity.this.finish();
70 |
71 | }
72 |
73 | public void computeTextureCompressionMode() {
74 | if (GLExtensions.contains("GL_IMG_texture_compression_pvrtc")) {
75 | Constants.textureCompressionMode = "PVR";
76 | } else if (GLExtensions.contains("GL_OES_texture_compression_S3TC") ||
77 | GLExtensions.contains("GL_EXT_texture_compression_s3tc")) {
78 | Constants.textureCompressionMode = "DXT";
79 | } else if (GLExtensions.contains("ETC1")) {
80 | Constants.textureCompressionMode = "ETC1";
81 | } else {
82 | Constants.textureCompressionMode = "UNKNOWN";
83 | }
84 |
85 | }
86 |
87 |
88 | protected boolean isDeviceSupportGles3() {
89 | final ActivityManager activityManager =
90 | (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
91 | final ConfigurationInfo configurationInfo =
92 | activityManager.getDeviceConfigurationInfo();
93 | return configurationInfo.reqGlEsVersion >= 0x30000;
94 |
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/app/src/main/java/ui/activity/ConfigureControls.kt:
--------------------------------------------------------------------------------
1 | package ui.activity
2 |
3 | import com.libopenmw.openmw.R
4 |
5 | import android.app.Activity
6 | import android.graphics.Color
7 | import android.os.Bundle
8 | import android.preference.PreferenceManager
9 | import android.view.MotionEvent
10 | import android.view.View
11 | import android.widget.RelativeLayout
12 |
13 | import ui.controls.Osc
14 | import ui.controls.OscElement
15 | import ui.controls.VIRTUAL_SCREEN_HEIGHT
16 | import ui.controls.VIRTUAL_SCREEN_WIDTH
17 | import utils.Utils.hideAndroidControls
18 |
19 | class ConfigureCallback(activity: Activity) : View.OnTouchListener {
20 |
21 | var currentView: View? = null
22 | private var layout: RelativeLayout = activity.findViewById(R.id.controlsContainer)
23 | private var origX: Float = 0.0f
24 | private var origY: Float = 0.0f
25 | private var startX: Float = 0.0f
26 | private var startY: Float = 0.0f
27 |
28 | override fun onTouch(v: View, event: MotionEvent): Boolean {
29 | when (event.actionMasked) {
30 | MotionEvent.ACTION_DOWN -> {
31 | currentView?.setBackgroundColor(Color.TRANSPARENT)
32 | currentView = v
33 | v.setBackgroundColor(Color.RED)
34 | origX = v.x
35 | origY = v.y
36 | startX = event.rawX
37 | startY = event.rawY
38 | }
39 | MotionEvent.ACTION_MOVE -> if (currentView != null) {
40 | val view = currentView!!
41 | val x = ((event.rawX - startX) + origX).toInt()
42 | val y = ((event.rawY - startY) + origY).toInt()
43 |
44 | val el = view.tag as OscElement
45 | el.changePosition(x * VIRTUAL_SCREEN_WIDTH / layout.width, y * VIRTUAL_SCREEN_HEIGHT / layout.height)
46 | el.updateView()
47 | }
48 | }
49 |
50 | return true
51 | }
52 |
53 | }
54 |
55 | class ConfigureControls : Activity() {
56 |
57 | private var callback: ConfigureCallback? = null
58 | private var osc = Osc(false) // bandaid fix, the input parameter needs to be PreferenceManager.getDefaultSharedPreferences(getContext()).getBoolean("multiplayer", false), but I can't get a proper context
59 |
60 | public override fun onCreate(savedInstanceState: Bundle?) {
61 | super.onCreate(savedInstanceState)
62 |
63 | setContentView(R.layout.configure_controls)
64 |
65 | val cb = ConfigureCallback(this)
66 | callback = cb
67 |
68 | val container: RelativeLayout = findViewById(R.id.controlsContainer)
69 | osc.placeConfigurableElements(container, cb)
70 | }
71 |
72 | override fun onWindowFocusChanged(hasFocus: Boolean) {
73 | if (hasFocus) {
74 | hideAndroidControls(this)
75 | }
76 | }
77 |
78 | private fun changeOpacity(delta: Float) {
79 | val view = callback?.currentView ?: return
80 | val el = view.tag as OscElement
81 | el.changeOpacity(delta)
82 | el.updateView()
83 | }
84 |
85 | private fun changeSize(delta: Int) {
86 | val view = callback?.currentView ?: return
87 | val el = view.tag as OscElement
88 | el.changeSize(delta)
89 | el.updateView()
90 | }
91 |
92 | fun clickOpacityPlus(v: View) {
93 | changeOpacity(0.1f)
94 | }
95 |
96 | fun clickOpacityMinus(v: View) {
97 | changeOpacity(-0.1f)
98 | }
99 |
100 | fun clickSizePlus(v: View) {
101 | changeSize(5)
102 | }
103 |
104 | fun clickSizeMinus(v: View) {
105 | changeSize(-5)
106 | }
107 |
108 | fun clickResetControls(v: View) {
109 | osc.resetElements(applicationContext)
110 | }
111 |
112 | fun clickBack(v: View) {
113 | finish()
114 | }
115 |
116 | }
117 |
--------------------------------------------------------------------------------
/app/src/main/java/file/ConfigsFileStorageHelper.java:
--------------------------------------------------------------------------------
1 | package file;
2 |
3 | import android.app.Activity;
4 | import android.content.SharedPreferences;
5 | import android.os.Environment;
6 | import android.preference.PreferenceManager;
7 | import android.widget.Toast;
8 |
9 | import com.afollestad.materialdialogs.MaterialDialog;
10 | import com.libopenmw.openmw.BuildConfig;
11 |
12 | import constants.Constants;
13 | import file.utils.CopyFilesFromAssets;
14 |
15 | /**
16 | * Created by sandstranger on 07.01.16.
17 | */
18 | public class ConfigsFileStorageHelper {
19 |
20 | // Base path: [/sdcard]/Android/data/[com.libopenmw.openmw]/
21 | // * /sdcard - in theory, can be different, haven't seen any on modern android though
22 | // * com.libopenmw.openmw - our application id
23 | //
24 | // $base/share - savedata, shouldn't touch this
25 | // $base/resources - resource files from openmw, ok to overwrite
26 | // $base/openmw - default settings, ok to overwrite
27 | // $base/config - user settings
28 |
29 | public static final String CONFIGS_FILES_STORAGE_PATH = Environment.getExternalStorageDirectory() + "/Android/data/" + BuildConfig.APPLICATION_ID;
30 | public static final String SETTINGS_CFG = CONFIGS_FILES_STORAGE_PATH + "/config/openmw/settings.cfg";
31 | public static final String OPENMW_CFG = CONFIGS_FILES_STORAGE_PATH + "/config/openmw/openmw.cfg";
32 | private MaterialDialog dialog;
33 | private Activity activity;
34 | private SharedPreferences Settings;
35 | private final String FIRST_TIME_RUN_KEY = "first_run";
36 |
37 | public ConfigsFileStorageHelper(Activity activity, SharedPreferences Settings) {
38 | this.activity = activity;
39 | this.Settings = Settings;
40 | }
41 |
42 | public void checkAppFirstTimeRun() {
43 | if (Settings.getBoolean(FIRST_TIME_RUN_KEY, true)) {
44 | Settings.edit().putBoolean(FIRST_TIME_RUN_KEY, false).commit();
45 | }
46 | }
47 |
48 | private void hideDialog() {
49 | dialog.dismiss();
50 | Toast toast = Toast.makeText(activity,
51 | "files copied to " +CONFIGS_FILES_STORAGE_PATH, Toast.LENGTH_LONG);
52 | toast.show();
53 | }
54 |
55 | public void copyFiles() {
56 | showCopyDialog();
57 | Thread th = new Thread(new Runnable() {
58 |
59 | @Override
60 | public void run() {
61 |
62 | CopyFilesFromAssets copyFiles = new CopyFilesFromAssets(
63 | activity,
64 | CONFIGS_FILES_STORAGE_PATH);
65 | copyFiles.copyFileOrDir("libopenmw");
66 |
67 | try {
68 |
69 | file.Writer.write(
70 | CONFIGS_FILES_STORAGE_PATH + (PreferenceManager.getDefaultSharedPreferences(activity).getBoolean("multiplayer", false) ? "/tes3mp-resources" : "/resources"),
71 | OPENMW_CFG,
72 | "libopenmw/resources");
73 | file.Writer.write(Constants.APPLICATION_DATA_STORAGE_PATH, OPENMW_CFG, "data");
74 |
75 | file.Writer.write(
76 | PreferenceManager.getDefaultSharedPreferences(activity).getString(Constants.LANGUAGE, "win1250"),
77 | OPENMW_CFG,
78 | "encoding");
79 |
80 |
81 | file.Writer.write(PreferenceManager.getDefaultSharedPreferences(activity).getString(Constants.MIPMAPPING, "none"),
82 | SETTINGS_CFG,
83 | "texture filtering");
84 |
85 | file.Writer.write(String.valueOf(PreferenceManager.getDefaultSharedPreferences(activity).getBoolean(Constants.SUBTITLES, false)), SETTINGS_CFG, "subtitles");
86 |
87 | file.Writer.write("" + Settings.getFloat(Constants.CAMERA_MULTIPLISER, 2.0f), SETTINGS_CFG, Constants.CAMERA_MULTIPLISER);
88 | file.Writer.write("" + Settings.getFloat(Constants.TOUCH_SENSITIVITY, 0.01f), SETTINGS_CFG, Constants.TOUCH_SENSITIVITY);
89 |
90 |
91 | } catch (Exception e) {
92 | }
93 |
94 | activity.runOnUiThread(new Runnable() {
95 | @Override
96 | public void run() {
97 | hideDialog();
98 | }
99 | });
100 | }
101 | });
102 | th.start();
103 | }
104 |
105 | private void showCopyDialog() {
106 | dialog = new MaterialDialog.Builder(activity)
107 | .title("Copying config files")
108 | .content("Please wait")
109 | .progress(true, 0)
110 | .show();
111 | }
112 |
113 |
114 | }
115 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # tes3mp for Android
2 |
3 |
4 | ## Notes from fork maintainer
5 | This is forked from xyzz/openmw-android, and implements support for tes3mp. All credits go to the tes3mp team and OpenMW team for making this possible, as well as Schnibbsel for helping me test it. If there are any questions please feel free to ask in the issues tab. Also feel free to report any abnormalities in the Issues tab as well. Credits to Texafornian for the icon used currently.
6 |
7 | **THIS IS NOT THE WORK OF ANY OFFICIAL OPENMW OR TES3MP MEMBERS**
8 |
9 | ### Setup guide
10 | You must first install one of the packages (obviously). Then you will have to somehow get the data files for which you want to run, presumably vanilla Morrowind, in which case you will transfer the files over to your device. After transferring these files, you must open the application which was just installed, and in the 'Path to game data files' field, you will have to input the exact path of the data files which you just transfered to your device. You can get the exact path from mostly any file browser app. After this, you must enable the `Multiplayer` option which is in the menu. After toggling this, you can join any server from 2 different methods.
11 | The first, and most useful, is the browser menu, which you can open in the menu navigation bar, in the top left. In order to refresh the browser, you must pull down the tab. To sort through the server entries, you can press on the settings tool bar on the top right, which presents 2 options, which are to sort by number of players, and sort alphabetically.
12 | The second method is used to connect to servers which are not displayed on the browser, or in the event that the master server is down. You must first enter to the settings menu, and press on the `Custom command line arguments` field. Then, you must add the following field(s): `--connect` followed by a space and `[ip address of server]:[port number of server]`, where the fields are replaced by their respecitive values. In the case that there is a required password to said server, use the `--password` field, followed by a spacebar and the password.
13 |
14 | If you want to go back to Singleplayer, simply untoggle the Multiplayer setting in the settings menu, and you can just start the game into Singleplayer by pressing the play icon.
15 |
16 | ## Building
17 |
18 | There are two steps for building tes3mp for Android. The first step is building C/C++ libraries. The second step is building the Java launcher.
19 |
20 | ### Prerequisites
21 |
22 | You will need some standard tools installed that you probably already have (bash, gcc, g++, sha256sum, unzip).
23 |
24 | CMake 3.6.0 or newer is **required**, you can download the latest version [here](https://cmake.org/download/) (and place in your `PATH`) if your distro ships with an outdated version.
25 |
26 | Additionally, to build the launcher you will need Android SDK installed, it is suggested that you use Android Studio which can set it up for you (see step 2).
27 |
28 | ### Step 1: Build the libraries
29 |
30 | Go into the `buildscripts` directory and run `./build.sh`. The script will automatically download the Android native toolchain and all dependencies, and will compile and install them.
31 |
32 | ### Step 2: Build the Java launcher
33 |
34 | To get an APK file you can install, open the `android-port` directory in Android Studio and run the project.
35 |
36 | Alternatively, if you do not have Android Studio installed or would rather not use it, run `./gradlew assembleDebug` from the root directory of this repository. The resulting APK, located at `./app/build/outputs/apk/debug/app-debug.apk`, can be transferred to the device and installed.
37 |
38 | ## Notes for developers
39 |
40 | ### Debugging native code
41 |
42 | You can debug native code with `ndk-gdb`. To use it, once you've built both libraries and the apk and installed the apk, run the application and let it stay on the main menu. Then `cd` to `app/src/main` and run `./gdb.sh [arch]`. The `arch` variable has to match the library your device will be using (one of `arm`, `arm64`, `x86_64`, `x86`; `arm` is the default).
43 |
44 | This also automatically enables gdb to use unstripped libraries, so you get proper symbols, source code references, etc.
45 |
46 | ### Running Address Sanitizer
47 |
48 | To compile everything with ASAN:
49 |
50 | ```
51 | # Clean previous build
52 | ./clean.sh
53 | # Build with ASAN enabled & debug symbols
54 | ./build.sh --ccache --asan --debug
55 | # Or: ./build.sh --ccache --asan --debug --arch arm64
56 | ```
57 |
58 | Then open Android Studio and compile and install the project.
59 |
60 | To get symbolized output:
61 |
62 | ```
63 | adb logcat | ./tool/asan_symbolize.py --demangle -s ./build/arm/symbols/
64 | # Or: adb logcat | ./tool/asan_symbolize.py --demangle -s ./build/arm64/symbols/
65 | ```
66 |
67 | ## Credits
68 |
69 | Original Java code written by sandstranger. Build scripts originally written by sandstranger and bwhaines.
70 |
--------------------------------------------------------------------------------
/app/src/main/java/ui/fragments/FragmentControls.java:
--------------------------------------------------------------------------------
1 | package ui.fragments;
2 |
3 | import android.content.Context;
4 | import android.content.SharedPreferences;
5 | import android.content.SharedPreferences.Editor;
6 | import android.os.Bundle;
7 | import android.support.v4.app.Fragment;
8 | import android.view.LayoutInflater;
9 | import android.view.View;
10 | import android.view.ViewGroup;
11 | import android.widget.SeekBar;
12 | import android.widget.TextView;
13 |
14 | import com.libopenmw.openmw.R;
15 |
16 | import constants.Constants;
17 | import ui.screen.ScreenScaler;
18 | import file.ConfigsFileStorageHelper;
19 | import prefs.PreferencesHelper;
20 |
21 | public class FragmentControls extends Fragment {
22 |
23 | private SharedPreferences Settings;
24 |
25 |
26 | @Override
27 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
28 | Bundle savedInstanceState) {
29 |
30 | super.onCreate(savedInstanceState);
31 |
32 | Settings = this.getActivity().getSharedPreferences(
33 | Constants.APP_PREFERENCES, Context.MODE_PRIVATE);
34 | View rootView = inflater.inflate(R.layout.settings, container, false);
35 |
36 | PreferencesHelper.getPrefValues(this.getActivity());
37 | changeTextViewSizes(rootView);
38 | int cameraStartPos = (int) (Settings.getFloat(Constants.CAMERA_MULTIPLISER, 2.0f) );
39 | int touchStartPos = (int) (Settings.getFloat(Constants.TOUCH_SENSITIVITY, 0.01f) * 1000f);
40 | int mouseStartPos = (int) (Settings.getFloat(Constants.MOUSE_TRANSPARENCY, 100.0f) );
41 |
42 | findViews(rootView, cameraStartPos, touchStartPos, mouseStartPos, Constants.TOUCH_SENSITIVITY, Constants.CAMERA_MULTIPLISER, Constants.MOUSE_TRANSPARENCY);
43 |
44 | return rootView;
45 | }
46 |
47 |
48 | private void changeTextViewSizes(View rootView) {
49 | ScreenScaler
50 | .changeTextSize(rootView.findViewById(R.id.cameraLabel), 3f);
51 | ScreenScaler
52 | .changeTextSize(rootView.findViewById(R.id.cameraValue), 3f);
53 | ScreenScaler
54 | .changeTextSize(rootView.findViewById(R.id.touchLabel), 3f);
55 | ScreenScaler
56 | .changeTextSize(rootView.findViewById(R.id.touchValue), 3f);
57 |
58 |
59 | }
60 |
61 | private void findViews(View rootView, int cameraStartPos, int touchStartPos, int mouseStartPos, String touchPrefKey, String cameraPrefKey, String mousePrefKey) {
62 | SeekBar touchSeekBar = (SeekBar) rootView.findViewById(R.id.touchBar);
63 | TextView touchProgress = (TextView) rootView.findViewById(R.id.touchValue);
64 | addSeekBarListener(touchSeekBar, touchStartPos, 100, 1000, touchProgress, touchPrefKey);
65 |
66 | SeekBar cameraSeekBar = (SeekBar) rootView.findViewById(R.id.cameraBar);
67 | TextView cameraProgress = (TextView) rootView.findViewById(R.id.cameraValue);
68 | addSeekBarListener(cameraSeekBar, cameraStartPos, 10, 1, cameraProgress, cameraPrefKey);
69 |
70 | SeekBar mouseSeekBar = (SeekBar) rootView.findViewById(R.id.mouseBar);
71 | TextView mouseProgress = (TextView) rootView.findViewById(R.id.mouseValue);
72 | addSeekBarListener(mouseSeekBar, mouseStartPos, 100, 1, mouseProgress, mousePrefKey);
73 |
74 | }
75 |
76 |
77 | private void addSeekBarListener(SeekBar seekBar, int startProgress, int maxValue, final int step, final TextView seekBarValue, final String prefKey) {
78 | seekBar.setProgress(startProgress);
79 | seekBar.setMax(maxValue);
80 | final float[] finalProgress = {(float) startProgress / (float) step};
81 | seekBarValue.setText(String.valueOf(finalProgress[0]));
82 | seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
83 |
84 | @Override
85 | public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
86 | finalProgress[0] = (float) progress / (float) step;
87 | seekBarValue.setText(String.valueOf(finalProgress[0]));
88 | }
89 |
90 | @Override
91 | public void onStartTrackingTouch(SeekBar seekBar) {
92 |
93 | }
94 |
95 | @Override
96 | public void onStopTrackingTouch(SeekBar seekBar) {
97 | setPreferences(prefKey, finalProgress[0]);
98 | saveSeekBarProgress(String.valueOf(finalProgress[0]), prefKey);
99 |
100 | }
101 | });
102 | }
103 |
104 | private void saveSeekBarProgress(final String progress, final String key) {
105 | new Thread(new Runnable() {
106 | public void run() {
107 |
108 | try {
109 | file.Writer.write(progress, ConfigsFileStorageHelper.SETTINGS_CFG, key);
110 |
111 | } catch (Exception e)
112 |
113 | {
114 | }
115 |
116 |
117 | }
118 | }
119 |
120 | ).start();
121 |
122 | }
123 |
124 |
125 | private void setPreferences(String prefValue, float value) {
126 | Editor editor = Settings.edit();
127 | editor.putFloat(prefValue, value);
128 | editor.commit();
129 |
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | OpenMW
4 |
5 |
6 | - Central and Eastern European (win1250)
7 | - Cyrillic (win1251)
8 | - Latin (win1252)
9 |
10 |
11 |
12 | - win1250
13 | - win1251
14 | - win1252
15 |
16 |
17 |
18 | - trilinear
19 | - bilinear
20 |
21 |
22 |
23 | - Normal (1x)
24 | - Double (2x)
25 | - Half (0.5x)
26 |
27 |
28 |
29 |
30 | - normalResolution
31 | - doubleResolution
32 | - halfResolution
33 |
34 |
35 | Start game
36 | Navigation View
37 | Navigation View Closed
38 | Path to game data files
39 |
40 |
41 | Graphics library
42 | gles1
43 | OpenSceneGraph version
44 | mainline
45 | Advanced
46 | Custom command line arguments
47 | User interface
48 | GUI scaling factor
49 | Virtual touchpad sensitivity
50 | Viewing distance
51 | Reset configuration files
52 | Reset on-screen controls
53 | Configure on-screen controls
54 | Configuration was reset to default values
55 | Language settings
56 | Game language
57 | win1252
58 | Alpha+
59 | Alpha-
60 | Size+
61 | Size-
62 | Reset to defaults
63 | Physics FPS
64 |
65 |
66 | - GLESv1
67 | - GLESv2
68 |
69 |
70 |
71 | - gles1
72 | - gles2
73 |
74 |
75 |
76 | - Mainline
77 | - OpenMW Fork
78 |
79 |
80 |
81 | - mainline
82 | - fork
83 |
84 |
85 |
86 | - 15
87 | - 30
88 | - 60
89 |
90 |
91 | 60
92 |
93 | Force a simple collision box for actors
94 |
95 |
96 |
97 | - Yes
98 | - No
99 |
100 |
101 |
102 | - false
103 | - true
104 |
105 |
106 | true
107 |
108 | Preloading
109 |
110 | - Enabled
111 | - Disabled
112 |
113 |
114 | - true
115 | - false
116 |
117 | false
118 |
119 | Distant Terrain
120 |
121 | - Enabled
122 | - Disabled
123 |
124 |
125 | - true
126 | - false
127 |
128 | false
129 |
130 | 2000.0
131 |
132 | Back
133 | Custom resolution
134 |
135 |
136 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/app/src/main/java/ui/fragments/FragmentBrowser.java:
--------------------------------------------------------------------------------
1 | package ui.fragments;
2 |
3 | import android.app.AlertDialog;
4 | import android.content.DialogInterface;
5 | import android.os.Bundle;
6 | import android.support.v4.widget.SwipeRefreshLayout;
7 | import android.text.InputType;
8 | import android.view.LayoutInflater;
9 | import android.view.View;
10 | import android.support.v4.app.Fragment;
11 |
12 | import android.view.ViewGroup;
13 | import android.widget.AdapterView;
14 | import android.widget.EditText;
15 | import android.widget.ListView;
16 | import android.preference.PreferenceManager;
17 | import android.widget.Toast;
18 |
19 | import java.util.ArrayList;
20 | import java.util.Comparator;
21 | import java.util.Collections;
22 |
23 | import com.libopenmw.openmw.R;
24 |
25 | import ui.activity.MainActivity;
26 | import utils.CustomAdapter;
27 | import utils.Server;
28 | // https://www.journaldev.com/10416/android-listview-with-custom-adapter-example-tutorial
29 | public class FragmentBrowser extends Fragment {
30 | public static ArrayList Servers = new ArrayList<>();
31 | public static CustomAdapter adapter;
32 | public static boolean sortPlayersFilter = false;
33 | public static boolean sortAlphabetFilter = false;
34 | private static SwipeRefreshLayout pullToRefresh;
35 | ListView listView;
36 | public static String CommandArgs = "";
37 | private static MainActivity activity;
38 | private String password = "";
39 | public FragmentBrowser(MainActivity mainActivity) {
40 | this.activity = mainActivity;
41 | }
42 |
43 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
44 | Bundle savedInstanceState) {
45 | super.onCreate(savedInstanceState);
46 | View rootView = inflater.inflate(R.layout.browser, container, false);
47 | listView = (ListView) rootView.findViewById(R.id.list);
48 | pullToRefresh = (SwipeRefreshLayout) rootView.findViewById(R.id.pullToRefresh);
49 | adapter = new CustomAdapter(Servers, this.getActivity());
50 | listView.setAdapter(adapter);
51 | listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
52 | @Override
53 | public void onItemClick(AdapterView> parent, View view, int position, long id) {
54 |
55 | Server Server = Servers.get(position);
56 |
57 |
58 | CommandArgs = ("--connect " + Server.getip());
59 | if (!PreferenceManager.getDefaultSharedPreferences(activity).getBoolean("multiplayer", false)) {
60 | Toast toast = Toast.makeText(activity,
61 | "You must enable multiplayer in the settings menu in order to join a server.", Toast.LENGTH_LONG);
62 | toast.show();
63 | } else if (Server.getPassworded())
64 | displayInput();
65 | else
66 | activity.startGame();
67 | }
68 | });
69 | // https://javapapers.com/android/android-swipe-down-to-refresh-a-listview/
70 | pullToRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
71 | @Override
72 | public void onRefresh() {
73 | adapter.refreshData();
74 | pullToRefresh.setRefreshing(false);
75 | }
76 | });
77 |
78 | return rootView;
79 |
80 | }
81 |
82 | public static String getArgv() {
83 | return CommandArgs;
84 | }
85 |
86 | // https://stackoverflow.com/questions/10903754/input-text-dialog-android
87 | public void displayInput() {
88 | AlertDialog.Builder builder;
89 | builder = new AlertDialog.Builder(getActivity());
90 | builder.setTitle("Enter password");
91 |
92 | // Set up the input
93 | final EditText input = new EditText(getActivity());
94 | // Specify the type of input expected; this, for example, sets the input as a password, and will mask the text
95 | input.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
96 | builder.setView(input);
97 |
98 | // Set up the buttons
99 | builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
100 | @Override
101 | public void onClick(DialogInterface dialog, int which) {
102 | password = input.getText().toString();
103 | CommandArgs += (" --password " + password);
104 | activity.startGame();
105 | }
106 | });
107 | builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
108 | @Override
109 | public void onClick(DialogInterface dialog, int which) {
110 | dialog.cancel();
111 | }
112 | });
113 |
114 | builder.show();
115 | }
116 |
117 | public static void sortPlayers() {
118 | // https://stackoverflow.com/questions/31470385/sort-an-arraylist-based-on-integer
119 | adapter.sort(new Comparator(){
120 | public int compare(Server m1, Server m2) {
121 | return m2.getplayerCount() - m1.getplayerCount(); // sort order
122 | }
123 | });
124 | adapter.notifyDataSetChanged();
125 | sortPlayersFilter = true;
126 | sortAlphabetFilter = false;
127 | }
128 |
129 | public static void sortAlphabet() {
130 | adapter.sort(new Comparator(){
131 | public int compare(Server m1, Server m2) {
132 | return m1.getserverName().compareTo(m2.getserverName());
133 | }
134 | });
135 | adapter.notifyDataSetChanged();
136 | sortPlayersFilter = false;
137 | sortAlphabetFilter = true;
138 | }
139 |
140 | }
141 |
142 |
143 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
18 |
19 |
23 |
24 |
30 |
31 |
36 |
37 |
45 |
46 |
53 |
54 |
60 |
61 |
62 |
63 |
68 |
69 |
77 |
78 |
85 |
86 |
92 |
93 |
94 |
95 |
100 |
101 |
109 |
110 |
117 |
118 |
124 |
125 |
126 |
127 |
128 |
132 |
133 |
134 |
135 |
136 |
137 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
7 |
8 |
12 |
13 |
17 |
18 |
21 |
22 |
26 |
27 |
31 |
32 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
51 |
52 |
53 |
54 |
55 |
56 |
62 |
63 |
69 |
70 |
76 |
77 |
78 |
79 |
88 |
89 |
98 |
99 |
108 |
109 |
118 |
119 |
128 |
129 |
136 |
137 |
146 |
147 |
148 |
149 |
150 |
--------------------------------------------------------------------------------
/app/src/main/java/utils/CustomAdapter.java:
--------------------------------------------------------------------------------
1 | package utils;
2 |
3 | import android.content.Context;
4 | import android.os.AsyncTask;
5 | import android.util.Log;
6 | import android.view.LayoutInflater;
7 | import android.view.View;
8 | import android.view.ViewGroup;
9 | import android.view.animation.Animation;
10 | import android.view.animation.AnimationUtils;
11 | import android.widget.ArrayAdapter;
12 | import android.widget.TextView;
13 |
14 | import com.libopenmw.openmw.R;
15 |
16 | import org.json.JSONException;
17 | import org.json.JSONObject;
18 |
19 | import java.io.BufferedInputStream;
20 | import java.io.BufferedReader;
21 | import java.io.IOException;
22 | import java.io.InputStream;
23 | import java.io.InputStreamReader;
24 | import java.net.HttpURLConnection;
25 | import java.net.MalformedURLException;
26 | import java.net.URL;
27 | import java.util.ArrayList;
28 | import java.util.Iterator;
29 |
30 | import ui.fragments.FragmentBrowser;
31 |
32 |
33 | /**
34 | * Created by anupamchugh on 09/02/16.
35 | */
36 | public class CustomAdapter extends ArrayAdapter implements View.OnClickListener{
37 |
38 | private ArrayList dataSet;
39 | Context mContext;
40 |
41 | // View lookup cache
42 | private static class ViewHolder {
43 | TextView serverName;
44 | TextView ip;
45 | TextView playerCount;
46 | TextView passworded;
47 | }
48 |
49 |
50 |
51 | public CustomAdapter(ArrayList data, Context context) {
52 | super(context, R.layout.rowlistview, data);
53 | this.dataSet = data;
54 | this.mContext=context;
55 | new GetData().execute();
56 | }
57 |
58 | public void refreshData() {
59 | new GetData().execute();
60 | }
61 |
62 |
63 | @Override
64 | public void onClick(View v) {
65 |
66 | }
67 |
68 | private int lastPosition = -1;
69 |
70 | @Override
71 | public View getView(int position, View convertView, ViewGroup parent) {
72 | // Get the data item for this position
73 | Server Server = getItem(position);
74 | // Check if an existing view is being reused, otherwise inflate the view
75 | ViewHolder viewHolder; // view lookup cache stored in tag
76 |
77 | final View result;
78 |
79 | if (convertView == null) {
80 |
81 |
82 | viewHolder = new ViewHolder();
83 | LayoutInflater inflater = LayoutInflater.from(getContext());
84 | convertView = inflater.inflate(R.layout.rowlistview, parent, false);
85 | viewHolder.serverName = (TextView) convertView.findViewById(R.id.name);
86 | viewHolder.ip = (TextView) convertView.findViewById(R.id.ip);
87 | viewHolder.playerCount = (TextView) convertView.findViewById(R.id.playerCount);
88 | viewHolder.passworded = (TextView) convertView.findViewById(R.id.isPassworded);
89 | // viewHolder.info = (ImageView) convertView.findViewById(R.id.item_info);
90 |
91 | result=convertView;
92 |
93 | convertView.setTag(viewHolder);
94 | } else {
95 | viewHolder = (ViewHolder) convertView.getTag();
96 | result=convertView;
97 | }
98 |
99 | Animation animation = AnimationUtils.loadAnimation(mContext, (position > lastPosition) ? R.anim.up_from_bottom : R.anim.down_from_top);
100 | result.startAnimation(animation);
101 | lastPosition = position;
102 |
103 |
104 | viewHolder.serverName.setText(Server.getserverName());
105 | viewHolder.ip.setText(Server.getip());
106 | viewHolder.playerCount.setText(String.valueOf(Server.getplayerCount()));
107 | viewHolder.passworded.setText(Server.getPassworded() ? "Passworded" : "");
108 | // Return the completed view to render on screen
109 | return convertView;
110 | }
111 | // https://pastebin.com/a253vGJM from discordpeter
112 | class GetData extends AsyncTask {
113 | @Override
114 | protected String doInBackground(String... params) {
115 | HttpURLConnection urlConnection = null;
116 | String result = "";
117 | Log.e("App", "Fail");
118 | try {
119 | URL url = new URL("http://master.tes3mp.com:8081/api/servers");
120 | urlConnection = (HttpURLConnection) url.openConnection();
121 |
122 | int code = urlConnection.getResponseCode();
123 |
124 | if (code == 200) {
125 | InputStream in = new BufferedInputStream(urlConnection.getInputStream());
126 | if (in != null) {
127 | BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(in));
128 | String line = "";
129 |
130 | while ((line = bufferedReader.readLine()) != null)
131 | result += line;
132 | }
133 | in.close();
134 | }
135 |
136 | return result;
137 | } catch (MalformedURLException e) {
138 | Log.e("App", "Success: ");
139 | } catch (IOException e) {
140 | } finally {
141 | urlConnection.disconnect();
142 | }
143 | return result;
144 |
145 | }
146 |
147 | @Override
148 | protected void onPostExecute(String result) {
149 | FragmentBrowser.Servers.clear();
150 | try {
151 |
152 | JSONObject obj = new JSONObject(result);
153 |
154 |
155 | JSONObject obj2 = obj.getJSONObject("list servers");
156 |
157 | Iterator iterator = obj2.keys();
158 |
159 |
160 | while (iterator.hasNext()) {
161 |
162 | String key = iterator.next();
163 |
164 | try {
165 |
166 | JSONObject anotherobject = obj2.getJSONObject(key);
167 |
168 | String hostname = anotherobject.getString("hostname");
169 | int playercount = anotherobject.getInt("players");
170 | boolean passworded = anotherobject.getBoolean("passw");
171 |
172 | FragmentBrowser.Servers.add(new Server(passworded, key, hostname, playercount));
173 |
174 | } catch (JSONException e) {
175 | }
176 | }
177 |
178 | } catch (Throwable t) {
179 | }
180 | // https://stackoverflow.com/questions/16441298/android-call-notifydatasetchanged-from-asynctask
181 | if (FragmentBrowser.sortPlayersFilter)
182 | FragmentBrowser.sortPlayers();
183 | else if (FragmentBrowser.sortAlphabetFilter)
184 | FragmentBrowser.sortAlphabet();
185 |
186 | CustomAdapter.this.notifyDataSetChanged();
187 |
188 | super.onPostExecute("yes");
189 |
190 | }
191 | }
192 | }
193 |
--------------------------------------------------------------------------------
/app/src/main/java/org/libsdl/app/SDLAudioManager.java:
--------------------------------------------------------------------------------
1 | package org.libsdl.app;
2 |
3 | import android.media.*;
4 | import android.util.Log;
5 |
6 | public class SDLAudioManager
7 | {
8 | protected static final String TAG = "SDLAudio";
9 |
10 | protected static AudioTrack mAudioTrack;
11 | protected static AudioRecord mAudioRecord;
12 |
13 | public static void initialize() {
14 | mAudioTrack = null;
15 | mAudioRecord = null;
16 | }
17 |
18 | // Audio
19 |
20 | /**
21 | * This method is called by SDL using JNI.
22 | */
23 | public static int audioOpen(int sampleRate, boolean is16Bit, boolean isStereo, int desiredFrames) {
24 | int channelConfig = isStereo ? AudioFormat.CHANNEL_CONFIGURATION_STEREO : AudioFormat.CHANNEL_CONFIGURATION_MONO;
25 | int audioFormat = is16Bit ? AudioFormat.ENCODING_PCM_16BIT : AudioFormat.ENCODING_PCM_8BIT;
26 | int frameSize = (isStereo ? 2 : 1) * (is16Bit ? 2 : 1);
27 |
28 | Log.v(TAG, "SDL audio: wanted " + (isStereo ? "stereo" : "mono") + " " + (is16Bit ? "16-bit" : "8-bit") + " " + (sampleRate / 1000f) + "kHz, " + desiredFrames + " frames buffer");
29 |
30 | // Let the user pick a larger buffer if they really want -- but ye
31 | // gods they probably shouldn't, the minimums are horrifyingly high
32 | // latency already
33 | desiredFrames = Math.max(desiredFrames, (AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat) + frameSize - 1) / frameSize);
34 |
35 | if (mAudioTrack == null) {
36 | mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate,
37 | channelConfig, audioFormat, desiredFrames * frameSize, AudioTrack.MODE_STREAM);
38 |
39 | // Instantiating AudioTrack can "succeed" without an exception and the track may still be invalid
40 | // Ref: https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/media/java/android/media/AudioTrack.java
41 | // Ref: http://developer.android.com/reference/android/media/AudioTrack.html#getState()
42 |
43 | if (mAudioTrack.getState() != AudioTrack.STATE_INITIALIZED) {
44 | Log.e(TAG, "Failed during initialization of Audio Track");
45 | mAudioTrack = null;
46 | return -1;
47 | }
48 |
49 | mAudioTrack.play();
50 | }
51 |
52 | Log.v(TAG, "SDL audio: got " + ((mAudioTrack.getChannelCount() >= 2) ? "stereo" : "mono") + " " + ((mAudioTrack.getAudioFormat() == AudioFormat.ENCODING_PCM_16BIT) ? "16-bit" : "8-bit") + " " + (mAudioTrack.getSampleRate() / 1000f) + "kHz, " + desiredFrames + " frames buffer");
53 |
54 | return 0;
55 | }
56 |
57 | /**
58 | * This method is called by SDL using JNI.
59 | */
60 | public static void audioWriteShortBuffer(short[] buffer) {
61 | if (mAudioTrack == null) {
62 | Log.e(TAG, "Attempted to make audio call with uninitialized audio!");
63 | return;
64 | }
65 |
66 | for (int i = 0; i < buffer.length; ) {
67 | int result = mAudioTrack.write(buffer, i, buffer.length - i);
68 | if (result > 0) {
69 | i += result;
70 | } else if (result == 0) {
71 | try {
72 | Thread.sleep(1);
73 | } catch(InterruptedException e) {
74 | // Nom nom
75 | }
76 | } else {
77 | Log.w(TAG, "SDL audio: error return from write(short)");
78 | return;
79 | }
80 | }
81 | }
82 |
83 | /**
84 | * This method is called by SDL using JNI.
85 | */
86 | public static void audioWriteByteBuffer(byte[] buffer) {
87 | if (mAudioTrack == null) {
88 | Log.e(TAG, "Attempted to make audio call with uninitialized audio!");
89 | return;
90 | }
91 |
92 | for (int i = 0; i < buffer.length; ) {
93 | int result = mAudioTrack.write(buffer, i, buffer.length - i);
94 | if (result > 0) {
95 | i += result;
96 | } else if (result == 0) {
97 | try {
98 | Thread.sleep(1);
99 | } catch(InterruptedException e) {
100 | // Nom nom
101 | }
102 | } else {
103 | Log.w(TAG, "SDL audio: error return from write(byte)");
104 | return;
105 | }
106 | }
107 | }
108 |
109 | /**
110 | * This method is called by SDL using JNI.
111 | */
112 | public static int captureOpen(int sampleRate, boolean is16Bit, boolean isStereo, int desiredFrames) {
113 | int channelConfig = isStereo ? AudioFormat.CHANNEL_CONFIGURATION_STEREO : AudioFormat.CHANNEL_CONFIGURATION_MONO;
114 | int audioFormat = is16Bit ? AudioFormat.ENCODING_PCM_16BIT : AudioFormat.ENCODING_PCM_8BIT;
115 | int frameSize = (isStereo ? 2 : 1) * (is16Bit ? 2 : 1);
116 |
117 | Log.v(TAG, "SDL capture: wanted " + (isStereo ? "stereo" : "mono") + " " + (is16Bit ? "16-bit" : "8-bit") + " " + (sampleRate / 1000f) + "kHz, " + desiredFrames + " frames buffer");
118 |
119 | // Let the user pick a larger buffer if they really want -- but ye
120 | // gods they probably shouldn't, the minimums are horrifyingly high
121 | // latency already
122 | desiredFrames = Math.max(desiredFrames, (AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat) + frameSize - 1) / frameSize);
123 |
124 | if (mAudioRecord == null) {
125 | mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, sampleRate,
126 | channelConfig, audioFormat, desiredFrames * frameSize);
127 |
128 | // see notes about AudioTrack state in audioOpen(), above. Probably also applies here.
129 | if (mAudioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
130 | Log.e(TAG, "Failed during initialization of AudioRecord");
131 | mAudioRecord.release();
132 | mAudioRecord = null;
133 | return -1;
134 | }
135 |
136 | mAudioRecord.startRecording();
137 | }
138 |
139 | Log.v(TAG, "SDL capture: got " + ((mAudioRecord.getChannelCount() >= 2) ? "stereo" : "mono") + " " + ((mAudioRecord.getAudioFormat() == AudioFormat.ENCODING_PCM_16BIT) ? "16-bit" : "8-bit") + " " + (mAudioRecord.getSampleRate() / 1000f) + "kHz, " + desiredFrames + " frames buffer");
140 |
141 | return 0;
142 | }
143 |
144 | /** This method is called by SDL using JNI. */
145 | public static int captureReadShortBuffer(short[] buffer, boolean blocking) {
146 | // !!! FIXME: this is available in API Level 23. Until then, we always block. :(
147 | //return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
148 | return mAudioRecord.read(buffer, 0, buffer.length);
149 | }
150 |
151 | /** This method is called by SDL using JNI. */
152 | public static int captureReadByteBuffer(byte[] buffer, boolean blocking) {
153 | // !!! FIXME: this is available in API Level 23. Until then, we always block. :(
154 | //return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
155 | return mAudioRecord.read(buffer, 0, buffer.length);
156 | }
157 |
158 |
159 | /** This method is called by SDL using JNI. */
160 | public static void audioClose() {
161 | if (mAudioTrack != null) {
162 | mAudioTrack.stop();
163 | mAudioTrack.release();
164 | mAudioTrack = null;
165 | }
166 | }
167 |
168 | /** This method is called by SDL using JNI. */
169 | public static void captureClose() {
170 | if (mAudioRecord != null) {
171 | mAudioRecord.stop();
172 | mAudioRecord.release();
173 | mAudioRecord = null;
174 | }
175 | }
176 |
177 | public static native int nativeSetupJNI();
178 | }
179 |
--------------------------------------------------------------------------------
/app/src/main/java/ui/activity/GameActivity.java:
--------------------------------------------------------------------------------
1 |
2 | package ui.activity;
3 |
4 | import android.content.SharedPreferences;
5 | import android.os.Bundle;
6 | import android.os.Handler;
7 | import android.os.Process;
8 | import android.preference.PreferenceManager;
9 | import android.support.v7.app.AlertDialog;
10 | import android.system.ErrnoException;
11 | import android.system.Os;
12 | import android.util.DisplayMetrics;
13 | import android.util.Log;
14 | import android.view.MotionEvent;
15 | import android.view.View;
16 | import android.view.WindowManager;
17 | import android.widget.EditText;
18 | import android.widget.RelativeLayout;
19 |
20 | import org.libsdl.app.SDLActivity;
21 |
22 | import constants.Constants;
23 | import cursor.MouseCursor;
24 | import parser.CommandlineParser;
25 | import ui.controls.Osc;
26 | import ui.game.GameState;
27 | import file.ConfigsFileStorageHelper;
28 | import ui.fragments.FragmentBrowser;
29 |
30 | import static utils.Utils.hideAndroidControls;
31 |
32 | public class GameActivity extends SDLActivity {
33 |
34 | private int numPointersDown;
35 | private int maxPointersDown;
36 | private int mouseDeadzone;
37 | private float startX;
38 | private float startY;
39 | private boolean isMoving;
40 | private double mouseScalingFactor;
41 |
42 | public static native void getPathToJni(String path);
43 |
44 | public static native void commandLine(int argc, String[] argv);
45 |
46 | public static Osc osc;
47 |
48 | private boolean hideControls = false;
49 | private boolean touchControls = false;
50 |
51 | private MouseCursor cursor;
52 | private SharedPreferences prefs;
53 |
54 | String getOpenmwLibName() {
55 | return (PreferenceManager.getDefaultSharedPreferences(this).getBoolean("multiplayer", false) ? "tes3mp" : "openmw") + "_osg_" + prefs.getString("pref_osg", "");
56 | }
57 |
58 | @Override
59 | public void loadLibraries() {
60 | prefs = PreferenceManager.getDefaultSharedPreferences(this);
61 | String graphicsLibrary = prefs.getString("pref_graphicsLibrary", "");
62 | String physicsFPS = prefs.getString("pref_physicsFPS", "");
63 | if (!physicsFPS.isEmpty()) {
64 | try {
65 | Os.setenv("OPENMW_PHYSICS_FPS", physicsFPS, true);
66 | Os.setenv("OSG_TEXT_SHADER_TECHNIQUE", "NO_TEXT_SHADER", true);
67 | } catch (ErrnoException e) {
68 | Log.e("OpenMW", "Failed setting environment variables.");
69 | e.printStackTrace();
70 | }
71 | }
72 |
73 | System.loadLibrary("c++_shared");
74 | System.loadLibrary("openal");
75 | System.loadLibrary("SDL2");
76 | if (graphicsLibrary.equals("gles2")) {
77 | try {
78 | Os.setenv("OPENMW_GLES_VERSION", "2", true);
79 | Os.setenv("LIBGL_ES", "2", true);
80 | } catch (ErrnoException e) {
81 | Log.e("OpenMW", "Failed setting environment variables.");
82 | e.printStackTrace();
83 | }
84 | }
85 | System.loadLibrary("GL");
86 | System.loadLibrary(getOpenmwLibName());
87 | }
88 |
89 | protected String getMainSharedObject() {
90 | return "lib" + getOpenmwLibName() + ".so";
91 | }
92 |
93 | public RelativeLayout getLayout() {
94 | return (RelativeLayout) mLayout;
95 | }
96 |
97 | @Override
98 | public void onCreate(Bundle savedInstanceState) {
99 | super.onCreate(savedInstanceState);
100 | GameState.setGameState(true);
101 | // NativeListener.initJavaVm();
102 | KeepScreenOn();
103 | getPathToJni(ConfigsFileStorageHelper.CONFIGS_FILES_STORAGE_PATH);
104 | showControls();
105 |
106 | DisplayMetrics dm = getContext().getResources().getDisplayMetrics();
107 | int minRes = Math.min(dm.widthPixels, dm.heightPixels);
108 | mouseDeadzone = minRes / 40; // fairly arbitrary
109 |
110 | touchControls = PreferenceManager.getDefaultSharedPreferences(this).getBoolean("touchControl", false);
111 |
112 | try {
113 | mouseScalingFactor = Float.parseFloat(prefs.getString("pref_touchpadSensitivity", "1.8"));
114 | } catch (NumberFormatException e) {
115 | mouseScalingFactor = 1.8;
116 | }
117 | }
118 |
119 | private void showControls() {
120 | hideControls = PreferenceManager.getDefaultSharedPreferences(this).getBoolean(Constants.HIDE_CONTROLS, false);
121 | if (!hideControls) {
122 | RelativeLayout layout = getLayout();
123 | osc = new Osc(PreferenceManager.getDefaultSharedPreferences(getContext()).getBoolean("multiplayer", false));
124 | osc.placeElements(layout);
125 | }
126 | cursor = new MouseCursor(this);
127 | }
128 |
129 | private void KeepScreenOn() {
130 | boolean needKeepScreenOn = PreferenceManager.getDefaultSharedPreferences(this).getBoolean("screen_keeper", false);
131 | if (needKeepScreenOn) {
132 | getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
133 | }
134 | }
135 |
136 | @Override
137 | public void onDestroy() {
138 | finish();
139 | Process.killProcess(Process.myPid());
140 | super.onDestroy();
141 | }
142 |
143 |
144 | @Override
145 | public void onWindowFocusChanged(boolean hasFocus) {
146 | if (hasFocus) {
147 | hideAndroidControls(this);
148 | }
149 | }
150 |
151 |
152 | // Touch events
153 | @Override
154 | public boolean onTouchEvent(MotionEvent event) {
155 | if (SDLActivity.isMouseShown() != 0 && touchControls)
156 | return false;
157 | else {
158 | switch (event.getActionMasked()) {
159 | case MotionEvent.ACTION_DOWN:
160 | case MotionEvent.ACTION_POINTER_DOWN:
161 | if (numPointersDown == 0) {
162 | startX = event.getX();
163 | startY = event.getY();
164 | }
165 | ++numPointersDown;
166 | maxPointersDown = Math.max(numPointersDown, maxPointersDown);
167 | break;
168 | case MotionEvent.ACTION_UP:
169 | case MotionEvent.ACTION_POINTER_UP:
170 | numPointersDown = Math.max(0, numPointersDown - 1);
171 | if (numPointersDown == 0) {
172 | // everything's up, do the action
173 | if (!isMoving && SDLActivity.isMouseShown() != 0) {
174 | // only send clicks if we didn't move
175 | int mouseX = SDLActivity.getMouseX();
176 | int mouseY = SDLActivity.getMouseY();
177 | int mouseButton = 0;
178 |
179 | if (maxPointersDown == 1)
180 | mouseButton = 1;
181 | else if (maxPointersDown == 2)
182 | mouseButton = 2;
183 |
184 | if (mouseButton != 0) {
185 | SDLActivity.onNativeMouse(mouseButton, MotionEvent.ACTION_DOWN, mouseX, mouseY);
186 | final Handler handler = new Handler();
187 | handler.postDelayed(() -> SDLActivity.onNativeMouse(0, MotionEvent.ACTION_UP, mouseX, mouseY), 100);
188 | }
189 | }
190 |
191 | maxPointersDown = 0;
192 | isMoving = false;
193 | }
194 | break;
195 | case MotionEvent.ACTION_MOVE:
196 | if (maxPointersDown == 1) {
197 | float diffX = event.getX() - startX;
198 | float diffY = event.getY() - startY;
199 | double distance = Math.sqrt(diffX * diffX + diffY * diffY);
200 |
201 | if (distance > mouseDeadzone) {
202 | isMoving = true;
203 | startX = event.getX();
204 | startY = event.getY();
205 | } else if (isMoving) {
206 | int mouseX = SDLActivity.getMouseX();
207 | int mouseY = SDLActivity.getMouseY();
208 |
209 | long newMouseX = Math.round(mouseX + diffX * mouseScalingFactor);
210 | long newMouseY = Math.round(mouseY + diffY * mouseScalingFactor);
211 |
212 | if (SDLActivity.isMouseShown() != 0)
213 | SDLActivity.onNativeMouse(0, MotionEvent.ACTION_MOVE, newMouseX, newMouseY);
214 |
215 | startX = event.getX();
216 | startY = event.getY();
217 | }
218 | }
219 | break;
220 | }
221 | }
222 | return true;
223 | }
224 |
225 | protected String[] getArguments() {
226 | String browserarg = FragmentBrowser.getArgv();
227 | if(browserarg == "") {
228 | String cmd = PreferenceManager.getDefaultSharedPreferences(this).getString("commandLine", "");
229 | CommandlineParser commandlineParser = new CommandlineParser(cmd);
230 | return commandlineParser.getArgv();
231 | } else {
232 | CommandlineParser commandlineParser = new CommandlineParser(browserarg);
233 | return commandlineParser.getArgv();
234 | }
235 | }
236 |
237 | }
238 |
--------------------------------------------------------------------------------
/app/src/main/java/ui/controls/Osk.kt:
--------------------------------------------------------------------------------
1 | package ui.controls
2 |
3 | import android.view.KeyEvent
4 | import android.view.MotionEvent
5 | import android.view.View
6 | import android.widget.Button
7 | import android.widget.RelativeLayout
8 | import org.libsdl.app.SDLActivity
9 |
10 | class OskTouchListener(val btn: OskButton): View.OnTouchListener {
11 |
12 | override fun onTouch(v: View, event: MotionEvent): Boolean {
13 | when (event.action) {
14 | MotionEvent.ACTION_DOWN -> btn.pressed()
15 | MotionEvent.ACTION_UP -> btn.released()
16 | }
17 | return true
18 | }
19 |
20 | }
21 |
22 | /**
23 | * Base class for keyboard buttons.
24 | *
25 | * @param text text displayed on the button
26 | * @param positionX X position of the button
27 | * @param positionY Y position of the button
28 | * @param sizeW width of the button
29 | * @param sizeH height of the button
30 | */
31 | abstract class OskButton(
32 | val text: String,
33 | private val positionX: Int,
34 | private val positionY: Int,
35 | private val sizeW: Int,
36 | private val sizeH: Int
37 | ) {
38 |
39 | var view: Button? = null
40 |
41 | /**
42 | * Show the keyboard button.
43 | */
44 | fun show() {
45 | view?.visibility = View.VISIBLE
46 | }
47 |
48 | /**
49 | * Hide the keyboard button.
50 | */
51 | fun hide() {
52 | view?.visibility = View.GONE
53 | }
54 |
55 | /**
56 | * Place this button into a RelativeLayout.
57 | * This also allocates an underlying View object.
58 | */
59 | fun place(target: RelativeLayout) {
60 | val v = Button(target.context)
61 | v.transformationMethod = null
62 | v.text = text
63 | v.tag = this
64 | v.alpha = 0.5f
65 | v.visibility = View.GONE
66 |
67 | // TODO: this doesn't take soft keys into account
68 | val realScreenWidth = v.context.resources.displayMetrics.widthPixels
69 | val realScreenHeight = v.context.resources.displayMetrics.heightPixels
70 | val realX = positionX * realScreenWidth / VIRTUAL_SCREEN_WIDTH
71 | val realY = positionY * realScreenHeight / VIRTUAL_SCREEN_HEIGHT
72 |
73 | val realW = sizeW * realScreenWidth / VIRTUAL_SCREEN_WIDTH
74 | val realH = sizeH * realScreenHeight / VIRTUAL_SCREEN_HEIGHT
75 | val params = RelativeLayout.LayoutParams(realW, realH)
76 |
77 | params.leftMargin = realX
78 | params.topMargin = realY
79 |
80 | v.layoutParams = params
81 |
82 | v.setOnTouchListener(OskTouchListener(this))
83 |
84 | target.addView(v)
85 | view = v
86 | }
87 |
88 | /**
89 | * Remove current button from RelativeLayout.
90 | */
91 | fun remove(target: RelativeLayout) {
92 | target.removeView(view);
93 | }
94 | /**
95 | * This is called when the button is pressed down.
96 | * Optionally, children can override this method.
97 | */
98 | open fun pressed() {
99 | }
100 |
101 | /**
102 | * This is called when the button is released.
103 | * Every child probably should override this method.
104 | */
105 | open fun released() {
106 | }
107 | }
108 |
109 | /**
110 | * A simple keyboard button which has two states: normal and shift pressed.
111 | * This uses nativeCommitText to send the text events.
112 | *
113 | * @param key key sent when in normal state
114 | * @param shiftKey key sent when shift is pressed
115 | */
116 | class OskSimpleButton(val key: Char, val shiftKey: Char, positionX: Int, positionY: Int, sizeW: Int, sizeH: Int):
117 | OskButton(key.toString(), positionX, positionY, sizeW, sizeH) {
118 |
119 | private val keyStr = key.toString()
120 | private val shiftKeyStr = shiftKey.toString()
121 | private var curKeyStr = keyStr
122 |
123 | override fun released() {
124 | SDLActivity.nativeCommitText(curKeyStr, 0)
125 | }
126 |
127 | fun shift(on: Boolean) {
128 | curKeyStr = if (on) shiftKeyStr else keyStr
129 | view?.text = curKeyStr
130 | }
131 | }
132 |
133 | class OskRawButton(text: String, private val keyCode: Int, positionX: Int, positionY: Int, sizeW: Int, sizeH: Int):
134 | OskButton(text, positionX, positionY, sizeW, sizeH) {
135 |
136 | override fun pressed() {
137 | SDLActivity.onNativeKeyDown(keyCode)
138 | }
139 |
140 | override fun released() {
141 | SDLActivity.onNativeKeyUp(keyCode)
142 | }
143 | }
144 |
145 | class OskShift(val buttons: ArrayList, positionX: Int, positionY: Int, sizeW: Int, sizeH: Int):
146 | OskButton("Shift", positionX, positionY, sizeW, sizeH) {
147 |
148 | override fun pressed() {
149 | for (btn in buttons)
150 | btn.shift(true)
151 | }
152 |
153 | override fun released() {
154 | for (btn in buttons)
155 | btn.shift(false)
156 | }
157 | }
158 |
159 | class OskCaps(val buttons: ArrayList, positionX: Int, positionY: Int, sizeW: Int, sizeH: Int):
160 | OskButton("Caps Lock", positionX, positionY, sizeW, sizeH) {
161 |
162 | private var state = false
163 |
164 | override fun pressed() {
165 | state = !state
166 | for (btn in buttons)
167 | btn.shift(state)
168 | }
169 |
170 | }
171 |
172 | class OskLanguage(val reference: Osk, positionX: Int, positionY: Int, sizeW: Int, sizeH: Int):
173 | OskButton("Lang", positionX, positionY, sizeW, sizeH) {
174 |
175 | override fun pressed() {
176 | reference.changeLanguage()
177 | }
178 | }
179 |
180 | class Osk {
181 | /* Every key is defined by two characters, first is input normally, second is input when shift is active
182 | * Note: This is only the "middle" part of the virtual keyboard
183 | */
184 |
185 | private var elements = ArrayList()
186 |
187 | private var visible = false
188 |
189 | private lateinit var relativelayouttarget: RelativeLayout
190 |
191 | private var russian = false
192 |
193 | init {
194 | placeKeyboard(russian)
195 | }
196 |
197 | fun placeKeyboard(isRussian: Boolean) {
198 | var keyboardLayout: ArrayList
199 |
200 | val buttonWidth = 60
201 | val buttonHeight = 100
202 | val buttonMarginX = 3
203 | val buttonMarginY = 1
204 | val offsetX: Int
205 |
206 | if( isRussian )
207 | offsetX = 0
208 | else
209 | offsetX = 30
210 |
211 | val offsetY = 110
212 |
213 | var curX: Int
214 | var curY = offsetY
215 |
216 | if( isRussian ) {
217 | keyboardLayout = arrayListOf(
218 | "1!2@3#4$5%6^7&8*9(0)-_=+",
219 | "йЙцЦуУкКеЕнНгГшШщЩзЗхХ[{]}\\|",
220 | "фФыЫвВаАпПрРоОлЛдДжЖэЭ;:'\"",
221 | "яЯчЧсСмМиИтТьЬбБюЮ,<.>/?"
222 | )
223 | } else {
224 | keyboardLayout = arrayListOf(
225 | "1!2@3#4$5%6^7&8*9(0)-_=+",
226 | "qQwWeErRtTyYuUiIoOpP[{]}\\|",
227 | "aAsSdDfFgGhHjJkKlL;:'\"",
228 | "zZxXcCvVbBnNmM,<.>/?"
229 | )
230 | }
231 |
232 | val lineOffset = arrayOf(
233 | (offsetX + buttonWidth * 1.0 + buttonMarginX).toInt(),
234 | (offsetX + buttonWidth * 0.5 + buttonMarginX).toInt(),
235 | (offsetX + buttonWidth * 1.25 + buttonMarginX).toInt(),
236 | (offsetX + buttonWidth * 1.5 + buttonMarginX).toInt()
237 | )
238 |
239 | val simpleButtons = ArrayList()
240 | keyboardLayout.forEachIndexed{ i, line ->
241 | curX = lineOffset[i]
242 |
243 | for (j in 0..(line.length - 1) step 2) {
244 | simpleButtons.add(OskSimpleButton(line[j], line[j + 1], curX, curY, buttonWidth, buttonHeight))
245 | curX += buttonWidth + buttonMarginX
246 | }
247 | curY += buttonHeight + buttonMarginY
248 | }
249 | elements.addAll(simpleButtons)
250 |
251 | // Shift
252 | elements.add(OskShift(simpleButtons, offsetX, offsetY + 3 * (buttonHeight + buttonMarginY), (buttonWidth * 1.5).toInt(), buttonHeight))
253 |
254 | // Capslock
255 | elements.add(OskCaps(simpleButtons, offsetX, offsetY + 2 * (buttonHeight + buttonMarginY), (buttonWidth * 1.25).toInt(), buttonHeight))
256 |
257 | // Backspace
258 | elements.add(OskRawButton(
259 | "⌫",
260 | KeyEvent.KEYCODE_DEL,
261 | lineOffset[0] + (buttonWidth + buttonMarginX) * keyboardLayout[0].length / 2,
262 | offsetY,
263 | buttonWidth * 2,
264 | buttonHeight
265 | ))
266 |
267 | // Enter
268 | elements.add(OskRawButton(
269 | "⏎",
270 | KeyEvent.KEYCODE_ENTER,
271 | lineOffset[2] + (buttonWidth + buttonMarginX) * keyboardLayout[2].length / 2,
272 | offsetY + (buttonHeight + buttonMarginY) * 2,
273 | buttonWidth * 3,
274 | buttonHeight
275 | ))
276 |
277 | // Language
278 | elements.add(OskLanguage(this, offsetX, curY, (buttonWidth * 1.5).toInt(), buttonHeight))
279 |
280 | // Spacebar
281 | elements.add(OskSimpleButton(' ', ' ', offsetX + buttonWidth * 3, curY, buttonWidth * 7, buttonHeight))
282 |
283 | // Arrows
284 | var arrowsCurX = lineOffset[3] + (buttonWidth + buttonMarginX) * keyboardLayout[3].length / 2 + buttonWidth
285 | if (isRussian)
286 | arrowsCurX -= 15
287 | var arrowsCurY = offsetY + (buttonHeight + buttonMarginY) * 3
288 | elements.add(OskRawButton("↑", KeyEvent.KEYCODE_DPAD_UP, arrowsCurX, arrowsCurY, buttonWidth, buttonHeight))
289 | arrowsCurX -= buttonWidth + buttonMarginX
290 | arrowsCurY += buttonHeight + buttonMarginY
291 | elements.add(OskRawButton("←", KeyEvent.KEYCODE_DPAD_LEFT, arrowsCurX, arrowsCurY, buttonWidth, buttonHeight))
292 | arrowsCurX += buttonWidth + buttonMarginX
293 | elements.add(OskRawButton("↓", KeyEvent.KEYCODE_DPAD_DOWN, arrowsCurX, arrowsCurY, buttonWidth, buttonHeight))
294 | arrowsCurX += buttonWidth + buttonMarginX
295 | elements.add(OskRawButton("→", KeyEvent.KEYCODE_DPAD_RIGHT, arrowsCurX, arrowsCurY, buttonWidth, buttonHeight))
296 |
297 | // Tilde. It's kinda crappy that it's separate just for console but oh well
298 | elements.add(OskRawButton("~", 68, offsetX, offsetY, buttonWidth, buttonHeight))
299 | }
300 |
301 | fun placeElements(target: RelativeLayout) {
302 | for (element in elements) {
303 | element.place(target)
304 | }
305 | relativelayouttarget = target
306 | }
307 |
308 | fun removeElements(target: RelativeLayout) {
309 | for (element in elements) {
310 | element.remove(target)
311 | }
312 | }
313 |
314 | fun changeLanguage() {
315 | removeElements(relativelayouttarget)
316 | elements = ArrayList()
317 |
318 | russian = !russian
319 | placeKeyboard(russian)
320 | placeElements(relativelayouttarget)
321 | // hack, make visible into false so it can do the keyboard again
322 | visible = false
323 | toggle()
324 | }
325 |
326 | fun toggle() {
327 | visible = !visible
328 | for (element in elements) {
329 | if (visible)
330 | element.show()
331 | else
332 | element.hide()
333 | }
334 | }
335 | }
336 |
--------------------------------------------------------------------------------
/app/src/main/java/ui/controls/Osc.kt:
--------------------------------------------------------------------------------
1 | package ui.controls
2 |
3 | import android.content.Context
4 | import android.graphics.Color
5 | import android.preference.PreferenceManager
6 | import android.util.Log
7 | import android.util.TypedValue
8 | import android.view.KeyEvent
9 | import android.view.MotionEvent
10 | import android.view.View
11 | import android.widget.Button
12 | import android.widget.ImageView
13 | import android.widget.RelativeLayout
14 | import com.libopenmw.openmw.R
15 |
16 | const val VIRTUAL_SCREEN_WIDTH = 1024
17 | const val VIRTUAL_SCREEN_HEIGHT = 768
18 |
19 | /**
20 | * Class to hold on-screen control elements such as buttons or joysticks.
21 | * The position, opacity and size can be customized by the user.
22 | *
23 | * @param uniqueId a string identifying the element, used to store customized positions
24 | * @param defaultX: default X position of the element
25 | * @param defaultY: default Y position of the element
26 | * @param defaultSize: default size (both width and height) of the element
27 | * @param defaultOpacity: default opacity of the element
28 | */
29 | open class OscElement(
30 | public val uniqueId: String,
31 | private val defaultX: Int,
32 | private val defaultY: Int,
33 | private val defaultSize: Int = 50,
34 | private val defaultOpacity: Float = 0.5f
35 | ) {
36 |
37 | private var opacity = defaultOpacity
38 | var size = defaultSize
39 | var x = defaultX
40 | var y = defaultY
41 |
42 | var view: View? = null
43 |
44 | /**
45 | * Creates a View object for this element.
46 | * The object should have a custom OnTouchListener which performs the desired action.
47 | *
48 | * @param ctx Android Context, comes from layout.
49 | */
50 | open fun makeView(ctx: Context) {
51 | val v = ImageView(ctx)
52 | v.setBackgroundColor(Color.RED)
53 | v.tag = this
54 | view = v
55 | }
56 |
57 | /**
58 | * Creates and places this element into a RelativeLayout.
59 | *
60 | * @param target RelativeLayout to put the new element into.
61 | */
62 | fun place(target: RelativeLayout) {
63 | makeView(target.context)
64 | val v = view ?: return
65 |
66 | target.addView(v)
67 | updateView()
68 | }
69 |
70 | fun placeConfigurable(target: RelativeLayout, listener: View.OnTouchListener) {
71 | place(target)
72 | view?.setOnTouchListener(listener)
73 | view?.visibility = View.VISIBLE
74 | }
75 |
76 | fun changeOpacity(delta: Float) {
77 | opacity = Math.max(0f, Math.min(opacity + delta, 1.0f))
78 | savePrefs()
79 | }
80 |
81 | fun changeSize(delta: Int) {
82 | size = Math.max(0, size + delta)
83 | savePrefs()
84 | }
85 |
86 | fun changePosition(virtualX: Int, virtualY: Int) {
87 | x = virtualX
88 | y = virtualY
89 | savePrefs()
90 | }
91 |
92 | fun updateView() {
93 | val v = view ?: return
94 |
95 | // Convert display pixels units into real pixels
96 | val px: Float = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, size.toFloat(), v.context.resources.displayMetrics)
97 | val params = RelativeLayout.LayoutParams(px.toInt(), px.toInt())
98 |
99 | val realScreenWidth = (v.parent as View).width
100 | val realScreenHeight = (v.parent as View).height
101 | val realX = x * realScreenWidth / VIRTUAL_SCREEN_WIDTH
102 | val realY = y * realScreenHeight / VIRTUAL_SCREEN_HEIGHT
103 |
104 | params.leftMargin = realX
105 | params.topMargin = realY
106 |
107 | v.layoutParams = params
108 |
109 | v.alpha = opacity
110 | }
111 |
112 | private fun savePrefs() {
113 | val v = view ?: return
114 | val prefs = PreferenceManager.getDefaultSharedPreferences(v.context)
115 | with (prefs.edit()) {
116 | putFloat("osc:$uniqueId:opacity", opacity)
117 | putInt("osc:$uniqueId:size", size)
118 | putInt("osc:$uniqueId:x", x)
119 | putInt("osc:$uniqueId:y", y)
120 |
121 | commit()
122 | }
123 | }
124 |
125 | fun loadPrefs(ctx: Context) {
126 | val prefs = PreferenceManager.getDefaultSharedPreferences(ctx)
127 |
128 | opacity = prefs.getFloat("osc:$uniqueId:opacity", defaultOpacity)
129 | size = prefs.getInt("osc:$uniqueId:size", defaultSize)
130 | x = prefs.getInt("osc:$uniqueId:x", defaultX)
131 | y = prefs.getInt("osc:$uniqueId:y", defaultY)
132 |
133 | updateView()
134 | }
135 |
136 | fun resetPrefs(ctx: Context) {
137 | val prefs = PreferenceManager.getDefaultSharedPreferences(ctx)
138 |
139 | with (prefs.edit()) {
140 | remove("osc:$uniqueId:opacity")
141 | remove("osc:$uniqueId:size")
142 | remove("osc:$uniqueId:x")
143 | remove("osc:$uniqueId:y")
144 |
145 | commit()
146 | }
147 |
148 | loadPrefs(ctx)
149 | }
150 | }
151 |
152 | class OscImageButton(
153 | uniqueId: String,
154 | private val imageSrc: Int,
155 | defaultX: Int,
156 | defaultY: Int,
157 | private val keyCode: Int,
158 | private val needMouse: Boolean = false,
159 | defaultSize: Int = 50
160 | ) : OscElement(uniqueId, defaultX, defaultY, defaultSize) {
161 |
162 | override fun makeView(ctx: Context) {
163 | val v = ImageView(ctx)
164 | v.setImageResource(imageSrc)
165 | v.setOnTouchListener(ButtonTouchListener(keyCode, needMouse))
166 | v.tag = this
167 |
168 | view = v
169 | }
170 |
171 | }
172 |
173 | class OscKeyboardButton(
174 | uniqueId: String,
175 | private val imageSrc: Int,
176 | defaultX: Int,
177 | defaultY: Int,
178 | private val osc: Osc
179 | ) : OscElement(uniqueId, defaultX, defaultY) {
180 |
181 | override fun makeView(ctx: Context) {
182 | val v = ImageView(ctx)
183 | v.setImageResource(imageSrc)
184 | v.setOnTouchListener(View.OnTouchListener { _, motionEvent ->
185 | if (motionEvent.action == MotionEvent.ACTION_UP) {
186 | osc.toggleKeyboard()
187 | }
188 | return@OnTouchListener true
189 | })
190 | v.tag = this
191 |
192 | view = v
193 | }
194 |
195 | }
196 |
197 | class OscJoystickLeft(
198 | uniqueId: String,
199 | defaultX: Int,
200 | defaultY: Int,
201 | defaultSize: Int,
202 | private val stick: Int
203 | ) : OscElement(uniqueId, defaultX, defaultY, defaultSize) {
204 |
205 | override fun makeView(ctx: Context) {
206 | val v = JoystickLeft(ctx)
207 | v.setStick(stick)
208 | v.tag = this
209 |
210 | view = v
211 | }
212 |
213 | }
214 |
215 | class OscJoystickRight(
216 | uniqueId: String,
217 | defaultX: Int,
218 | defaultY: Int,
219 | defaultSize: Int,
220 | private val stick: Int
221 | ) : OscElement(uniqueId, defaultX, defaultY, defaultSize) {
222 |
223 | override fun makeView(ctx: Context) {
224 | val v = JoystickRight(ctx)
225 | v.setStick(stick)
226 | v.tag = this
227 |
228 | view = v
229 | }
230 |
231 | }
232 |
233 | open class OscHiddenButton(
234 | uniqueId: String,
235 | defaultX: Int,
236 | defaultY: Int,
237 | private val title: String,
238 | private val keyCode: Int
239 | ) : OscElement(uniqueId, defaultX, defaultY) {
240 |
241 | override fun makeView(ctx: Context) {
242 | val v = Button(ctx)
243 | v.tag = this
244 | v.setOnTouchListener(ButtonTouchListener(keyCode, false))
245 | v.text = title
246 | v.visibility = View.GONE
247 |
248 | view = v
249 | }
250 |
251 | }
252 |
253 |
254 | class ToggleTouchListener(private val buttons: ArrayList): View.OnTouchListener {
255 |
256 | private var shown = false
257 |
258 | override fun onTouch(v: View?, event: MotionEvent): Boolean {
259 | if (event.action != MotionEvent.ACTION_UP)
260 | return false
261 |
262 | for (button in buttons)
263 | button.view?.visibility = if (shown) View.GONE else View.VISIBLE
264 |
265 | shown = !shown
266 |
267 | return true
268 | }
269 |
270 | }
271 |
272 | class OscHiddenToggle(
273 | uniqueId: String,
274 | defaultX: Int,
275 | defaultY: Int,
276 | title: String,
277 | private val buttons: ArrayList
278 | ) : OscHiddenButton(uniqueId, defaultX, defaultY, title, 0) {
279 |
280 | override fun makeView(ctx: Context) {
281 | super.makeView(ctx)
282 | view?.setOnTouchListener(ToggleTouchListener(buttons))
283 | view?.visibility = View.VISIBLE
284 | }
285 |
286 | }
287 |
288 | class Osc(
289 | multiplayer: Boolean
290 | ) {
291 | private var osk = Osk()
292 | private var keyboardVisible = false
293 | private var keyboardButton = OscKeyboardButton("keyboard", R.drawable.keyboard, 586, 0, this)
294 |
295 | private var elements = arrayListOf(
296 | OscImageButton("run", R.drawable.run, 65, 330, 115),
297 | OscImageButton("inventory", R.drawable.inventory, 950, 95, 3, true),
298 | OscImageButton("changePerson", R.drawable.backup, 212, 0, KeyEvent.KEYCODE_TAB),
299 | OscImageButton("wait", R.drawable.wait, 274, 0, KeyEvent.KEYCODE_T),
300 | OscImageButton("pause", R.drawable.pause, 950, 0, KeyEvent.KEYCODE_ESCAPE),
301 | // TODO: replace load/save icons with more intuitive
302 | OscImageButton("weapon", R.drawable.broadsword1, 880, 95, KeyEvent.KEYCODE_F),
303 | OscImageButton("jump", R.drawable.jump, 920, 195, KeyEvent.KEYCODE_E),
304 | OscImageButton("fire", R.drawable.crossbow, 720, 300, 1, true, 90),
305 | OscImageButton("magic", R.drawable.starsattelites, 940, 480, KeyEvent.KEYCODE_R),
306 | OscImageButton("crouch", R.drawable.c, 940, 670, 113),
307 | OscImageButton("diary", R.drawable.di, 414, 0, KeyEvent.KEYCODE_J),
308 | keyboardButton,
309 | OscImageButton("use", R.drawable.use, 940, 368, KeyEvent.KEYCODE_SPACE),
310 |
311 | OscImageButton("shift", R.drawable.del, 140, 0, KeyEvent.KEYCODE_SHIFT_LEFT),
312 |
313 | OscJoystickLeft("joystickLeft", 75, 400, 170, 0),
314 | OscJoystickRight("joystickRight", 650, 400, 170, 1)
315 | )
316 |
317 | init {
318 | // add buttons we didn't do earlier
319 | if (multiplayer) {
320 | elements.add(OscImageButton("chat", R.drawable.chat, 780, 0, KeyEvent.KEYCODE_Y))
321 | // tes3mp doesn't allow quickload and quicksave
322 | } else {
323 | elements.add(OscImageButton("quickLoad", R.drawable.load, 860, 0, 139))
324 | elements.add(OscImageButton("quickSave", R.drawable.save, 780, 0, 135))
325 | }
326 | val fnButtons = ArrayList()
327 |
328 | // Fn buttons: F1, F2, F3, F4, F10, F11 are the only ones we care about
329 | arrayOf(1, 2, 3, 4, 10, 11).forEachIndexed{ i, el ->
330 | val code = 130 + el
331 | fnButtons.add(OscHiddenButton("f$el", 70, 70 * (i + 1), "F$el", code))
332 | }
333 | val fn = OscHiddenToggle("fn", 70, 0, "FN", fnButtons)
334 |
335 | // Quick buttons: 0 to 9
336 | val quickButtons = ArrayList()
337 | for (i in 0..9) {
338 | val code = KeyEvent.KEYCODE_0 + i
339 | quickButtons.add(OscHiddenButton("qp$i", 0, 70 * (i + 1), "$i", code))
340 | }
341 | val qp = OscHiddenToggle("qp", 0, 0, "QP", quickButtons)
342 |
343 | elements.addAll(fnButtons)
344 | elements.add(fn)
345 | elements.addAll(quickButtons)
346 | elements.add(qp)
347 | }
348 |
349 | fun placeElements(target: RelativeLayout) {
350 | for (element in elements) {
351 | element.place(target)
352 | element.loadPrefs(target.context)
353 | }
354 | osk.placeElements(target)
355 |
356 | target.addOnLayoutChangeListener { v, l, t, r, b, ol, ot, or, ob -> relayout(l, t, r, b, ol, ot, or, ob) }
357 | }
358 |
359 | fun toggleKeyboard() {
360 | osk.toggle()
361 | keyboardVisible = !keyboardVisible
362 | for (element in elements) {
363 | if (element == keyboardButton || element.uniqueId == "chat")
364 | continue
365 | // TODO: this kinda screws up the state for hidden toggles (i.e. open toggle => click keyboard twice)
366 | if (!keyboardVisible && element is OscHiddenButton && element !is OscHiddenToggle)
367 | continue
368 | element.view?.visibility = if (keyboardVisible) View.GONE else View.VISIBLE
369 | }
370 | }
371 |
372 | fun disableElements(disable: Boolean) {
373 | if (keyboardVisible)
374 | return
375 |
376 | for (element in elements) {
377 | if (element == keyboardButton || element.uniqueId == "chat" || element.uniqueId == "pause" || element.uniqueId == "inventory" || element.uniqueId == "shift")
378 | continue
379 | if (!disable && element is OscHiddenButton && element !is OscHiddenToggle)
380 | continue
381 | element.view?.visibility = if (disable) View.GONE else View.VISIBLE
382 | }
383 | }
384 |
385 | fun placeConfigurableElements(target: RelativeLayout, listener: View.OnTouchListener) {
386 | for (element in elements) {
387 | element.placeConfigurable(target, listener)
388 | element.loadPrefs(target.context)
389 | }
390 |
391 | target.addOnLayoutChangeListener { v, l, t, r, b, ol, ot, or, ob -> relayout(l, t, r, b, ol, ot, or, ob) }
392 | }
393 |
394 | fun resetElements(ctx: Context) {
395 | for (element in elements) {
396 | element.resetPrefs(ctx)
397 | }
398 | }
399 |
400 | private fun relayout(l: Int, t: Int, r: Int, b: Int, ol: Int, ot: Int, or: Int, ob: Int) {
401 | // don't do anything if layout didn't change
402 | if (l == ol && t == ot && r == or && b == ob)
403 | return
404 | for (element in elements) {
405 | element.updateView()
406 | }
407 | }
408 |
409 | }
410 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/screencontrols.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
9 |
10 |
16 |
17 |
18 |
21 |
22 |
31 |
32 |
41 |
42 |
50 |
51 |
59 |
60 |
69 |
70 |
79 |
80 |
89 |
90 |
99 |
100 |
109 |
110 |
119 |
120 |
129 |
130 |
139 |
140 |
149 |
150 |
159 |
160 |
169 |
170 |
179 |
180 |
189 |
190 |
199 |
200 |
209 |
210 |
219 |
220 |
229 |
230 |
239 |
240 |
249 |
250 |
259 |
260 |
269 |
270 |
279 |
280 |
289 |
290 |
299 |
300 |
309 |
310 |
319 |
320 |
329 |
330 |
339 |
340 |
349 |
350 |
359 |
360 |
369 |
370 |
379 |
380 |
381 |
382 |
--------------------------------------------------------------------------------
/app/src/main/java/ui/activity/MainActivity.java:
--------------------------------------------------------------------------------
1 | package ui.activity;
2 |
3 | import android.app.Activity;
4 | import android.app.ProgressDialog;
5 | import android.content.Context;
6 | import android.content.Intent;
7 | import android.content.SharedPreferences;
8 | import android.os.Bundle;
9 | import android.preference.PreferenceManager;
10 | import android.support.design.widget.NavigationView;
11 | import android.support.v4.widget.DrawerLayout;
12 | import android.support.v7.app.ActionBarDrawerToggle;
13 | import android.support.v7.app.AppCompatActivity;
14 | import android.support.v7.widget.Toolbar;
15 | import android.util.Log;
16 | import android.view.Menu;
17 | import android.view.MenuInflater;
18 | import android.view.MenuItem;
19 | import android.view.View;
20 | import android.widget.Button;
21 | import android.widget.EditText;
22 | import android.widget.LinearLayout;
23 | import android.widget.TextView;
24 | import android.widget.Toast;
25 |
26 | import com.crashlytics.android.Crashlytics;
27 | import com.crashlytics.android.ndk.CrashlyticsNdk;
28 | import com.libopenmw.openmw.R;
29 | import com.melnykov.fab.FloatingActionButton;
30 |
31 | import io.fabric.sdk.android.Fabric;
32 |
33 | import java.io.BufferedReader;
34 | import java.io.File;
35 | import java.io.FileInputStream;
36 | import java.io.IOException;
37 | import java.io.InputStreamReader;
38 |
39 | import constants.Constants;
40 | import file.utils.CopyFilesFromAssets;
41 | import ui.fragments.FragmentBrowser;
42 | import ui.game.GameState;
43 | import ui.fragments.FragmentControls;
44 | import ui.fragments.FragmentSettings;
45 | import permission.PermissionHelper;
46 | import ui.screen.ScreenScaler;
47 | import file.ConfigsFileStorageHelper;
48 | import prefs.PreferencesHelper;
49 |
50 | import static file.ConfigsFileStorageHelper.CONFIGS_FILES_STORAGE_PATH;
51 | import static file.ConfigsFileStorageHelper.OPENMW_CFG;
52 | import static file.ConfigsFileStorageHelper.SETTINGS_CFG;
53 | import static utils.Utils.hideAndroidControls;
54 |
55 | public class MainActivity extends AppCompatActivity {
56 |
57 | private static final String TAG = "OpenMW-Launcher";
58 | private NavigationView navigationView;
59 | private DrawerLayout drawerLayout;
60 | public TextView path;
61 | public Button browseButton;
62 | private Menu menu;
63 | private boolean isSettingsEnabled = true;
64 | private static final int REQUEST_PATH = 1;
65 | private SharedPreferences prefs;
66 | private SharedPreferences Settings;
67 | private TextListener listener;
68 | private enum TEXT_MODE {DATA_PATH, COMMAND_LINE}
69 | private static TEXT_MODE editTextMode;
70 | private ConfigsFileStorageHelper configsFileStorageHelper;
71 |
72 | public static int resolutionX = 0;
73 | public static int resolutionY = 0;
74 |
75 | @Override
76 | protected void onCreate(Bundle savedInstanceState) {
77 | GameState.setGameState(false);
78 | super.onCreate(savedInstanceState);
79 | Fabric.with(this, new Crashlytics(), new CrashlyticsNdk());
80 | PermissionHelper.getWriteExternalStoragePermission(MainActivity.this);
81 | isSettingsEnabled = true;
82 | setContentView(R.layout.main);
83 | PreferencesHelper.getPrefValues(this);
84 | prefs = PreferenceManager.getDefaultSharedPreferences(this);
85 | Settings = this.getSharedPreferences(
86 | Constants.APP_PREFERENCES, Context.MODE_PRIVATE);
87 | configsFileStorageHelper= new ConfigsFileStorageHelper(this,Settings);
88 | configsFileStorageHelper.checkAppFirstTimeRun();
89 | LinearLayout layout = (LinearLayout) findViewById(R.id.toolbarLayout);
90 | layout.setVisibility(LinearLayout.VISIBLE);
91 | path = (TextView) findViewById(R.id.path);
92 | browseButton = (Button) findViewById(R.id.buttonBrowse);
93 |
94 | disableToolBarViews();
95 |
96 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
97 |
98 | setSupportActionBar(toolbar);
99 | getSupportActionBar().setDisplayHomeAsUpEnabled(true);
100 |
101 |
102 | getSupportFragmentManager().beginTransaction()
103 | .replace(R.id.content_frame, new FragmentSettings()).commit();
104 |
105 | initializeNavigationView(toolbar);
106 | FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
107 | fab.setOnClickListener(new View.OnClickListener() {
108 | public void onClick(View v) {
109 | GameState.setGameState(true);
110 | startGame();
111 | }
112 |
113 | });
114 | }
115 |
116 |
117 | private void initializeNavigationView(Toolbar toolbar) {
118 | navigationView = (NavigationView) findViewById(R.id.navigation_drawer);
119 |
120 | navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
121 |
122 | @Override
123 | public boolean onNavigationItemSelected(MenuItem menuItem) {
124 |
125 | if (menuItem.isChecked()) menuItem.setChecked(false);
126 | else menuItem.setChecked(true);
127 |
128 | drawerLayout.closeDrawers();
129 |
130 | switch (menuItem.getItemId()) {
131 |
132 | case R.id.start_game:
133 | startGame();
134 | return true;
135 |
136 | // case R.id.plugins:
137 | // showOverflowMenu(true);
138 | // isSettingsEnabled = false;
139 | // disableToolBarViews();
140 | // MainActivity.this.getSupportFragmentManager().beginTransaction().replace(R.id.content_frame, new FragmentPlugins()).commit();
141 | // return true;
142 | case R.id.controls:
143 | showOverflowMenu(false);
144 | disableToolBarViews();
145 | MainActivity.this.getSupportFragmentManager().beginTransaction().replace(R.id.content_frame, new FragmentControls()).commit();
146 |
147 | return true;
148 | case R.id.settings:
149 | disableToolBarViews();
150 | showOverflowMenu(true);
151 | isSettingsEnabled = true;
152 | MainActivity.this.getSupportFragmentManager().beginTransaction().replace(R.id.content_frame, new FragmentSettings()).commit();
153 |
154 | return true;
155 | case R.id.browser:
156 | showOverflowMenu(true);
157 | isSettingsEnabled = false;
158 | disableToolBarViews();
159 | MainActivity.this.getSupportFragmentManager().beginTransaction().replace(R.id.content_frame, new FragmentBrowser(MainActivity.this)).commit();
160 |
161 | return true;
162 | /* case R.id.textureDecoder:
163 | showOverflowMenu(false);
164 | disableToolBarViews();
165 | MainActivity.this.getSupportFragmentManager().beginTransaction().replace(R.id.content_frame, new DDSDecoderFragment()).commit();
166 | return true;*/
167 |
168 | default:
169 |
170 | return true;
171 |
172 | }
173 | }
174 | });
175 |
176 | drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
177 | ActionBarDrawerToggle actionBarDrawerToggle = new ActionBarDrawerToggle(this, drawerLayout, toolbar, R.string.openDrawer, R.string.closeDrawer) {
178 |
179 | @Override
180 | public void onDrawerClosed(View drawerView) {
181 | super.onDrawerClosed(drawerView);
182 | }
183 |
184 | @Override
185 | public void onDrawerOpened(View drawerView) {
186 | super.onDrawerOpened(drawerView);
187 | }
188 | };
189 |
190 | drawerLayout.setDrawerListener(actionBarDrawerToggle);
191 |
192 | actionBarDrawerToggle.syncState();
193 |
194 | }
195 |
196 | @Override
197 | public void onActivityResult(int requestCode, int resultCode, Intent data) {
198 | if (requestCode == REQUEST_PATH) {
199 | if (resultCode == Activity.RESULT_OK) {
200 | String curDir = data.getStringExtra("GetDir");
201 |
202 | switch (editTextMode) {
203 | case DATA_PATH:
204 | Constants.APPLICATION_DATA_STORAGE_PATH = curDir;
205 | break;
206 | }
207 | setTexWatcher();
208 | path.setVisibility(EditText.VISIBLE);
209 | browseButton.setVisibility(Button.VISIBLE);
210 | path.setText(curDir);
211 |
212 | }
213 | }
214 | super.onActivityResult(requestCode, resultCode, data);
215 |
216 | }
217 |
218 | private void deleteRecursive(File fileOrDirectory) {
219 | if (fileOrDirectory.isDirectory())
220 | for (File child : fileOrDirectory.listFiles())
221 | deleteRecursive(child);
222 |
223 | fileOrDirectory.delete();
224 | }
225 |
226 | private void logConfig() {
227 | try {
228 | File openmwCfg = new File(OPENMW_CFG);
229 | if (openmwCfg.exists()) {
230 | BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(openmwCfg)));
231 | String line;
232 | Crashlytics.log("openmw.cfg");
233 | Crashlytics.log("--------------------------------------------------------------------------------");
234 | while ((line = reader.readLine()) != null) {
235 | // Don't log fallback lines, they are mostly useless
236 | if (!line.contains("fallback="))
237 | Crashlytics.log(line);
238 | }
239 | Crashlytics.log("--------------------------------------------------------------------------------");
240 | }
241 | } catch (Exception e) {
242 | // not a big deal if we can't log the contents
243 | }
244 | }
245 |
246 | private void runGame() {
247 | logConfig();
248 | Intent intent = new Intent(MainActivity.this,
249 | GameActivity.class);
250 | finish();
251 |
252 | MainActivity.this.startActivity(intent);
253 | }
254 |
255 | /**
256 | * Resets $base/config to default values. This contains user-modifiable openmw.cfg and settings.cfg
257 | * (and we also write some values to both on startup such as screen res or some options)
258 | */
259 | private void resetUserConfig() {
260 | // Wipe out the old version
261 | deleteRecursive(new File(CONFIGS_FILES_STORAGE_PATH + "/config"));
262 | // and copy in the default values
263 | CopyFilesFromAssets copyFiles = new CopyFilesFromAssets(this, CONFIGS_FILES_STORAGE_PATH);
264 | copyFiles.copyFileOrDir("libopenmw/config");
265 | }
266 | private void obtainScreenResolution() {
267 | View v = getWindow().getDecorView();
268 | resolutionX = v.getWidth();
269 | resolutionY = v.getHeight();
270 |
271 | // Split resolution e.g 640x480 to width/height
272 | String customResolution = prefs.getString("pref_customResolution", "");
273 | int sep = customResolution.indexOf("x");
274 | if (sep > 0) {
275 | try {
276 | int x = Integer.parseInt(customResolution.substring(0, sep));
277 | int y = Integer.parseInt(customResolution.substring(sep + 1));
278 |
279 | resolutionX = x;
280 | resolutionY = y;
281 | } catch (NumberFormatException e) {
282 | // pass
283 | }
284 | }
285 |
286 | try {
287 | file.Writer.write(String.valueOf(resolutionX), ConfigsFileStorageHelper.SETTINGS_CFG, "resolution x");
288 | file.Writer.write(String.valueOf(resolutionY), ConfigsFileStorageHelper.SETTINGS_CFG, "resolution y");
289 | } catch (IOException e) {
290 | // TODO
291 | }
292 | }
293 |
294 | public void startGame() {
295 | ProgressDialog dialog = ProgressDialog.show(
296 | this, "", "Preparing for launch...", true);
297 |
298 | Activity activity = this;
299 |
300 | // hide the controls so that ScreenResolutionHelper can get the right resolution
301 | hideAndroidControls(this);
302 |
303 | Thread th = new Thread(() -> {
304 | try {
305 | File openmwCfg = new File(OPENMW_CFG);
306 | File settingsCfg = new File(SETTINGS_CFG);
307 | if (!openmwCfg.exists() || !settingsCfg.exists()) {
308 | Log.i(TAG, "Config files don't exist, re-creating them.");
309 | resetUserConfig();
310 | }
311 |
312 | // wipe old "wipeable" (see ConfigsFileStorageHelper) config files just to be safe
313 | deleteRecursive(new File(CONFIGS_FILES_STORAGE_PATH + "/openmw"));
314 | deleteRecursive(new File(CONFIGS_FILES_STORAGE_PATH + "/resources"));
315 | deleteRecursive(new File(CONFIGS_FILES_STORAGE_PATH + "/tes3mp-resources"));
316 |
317 | // copy all assets
318 | CopyFilesFromAssets copyFiles = new CopyFilesFromAssets(activity, CONFIGS_FILES_STORAGE_PATH);
319 | copyFiles.copyFileOrDir("libopenmw/openmw");
320 | copyFiles.copyFileOrDir("libopenmw/resources");
321 | copyFiles.copyFileOrDir("libopenmw/tes3mp-resources");
322 |
323 | // openmw.cfg: data, resources
324 | // TODO: probably should just reuse ConfigsFileStorageHelper
325 | file.Writer.write(
326 | CONFIGS_FILES_STORAGE_PATH + (PreferenceManager.getDefaultSharedPreferences(this).getBoolean("multiplayer", false) ? "/tes3mp-resources" : "/resources"),
327 | OPENMW_CFG,
328 | "resources");
329 | // TODO: it will crash if there's no value/invalid value provided
330 | file.Writer.write(prefs.getString("data_files", ""), OPENMW_CFG, "data");
331 |
332 | file.Writer.write(prefs.getString("pref_encoding", "win1252"), OPENMW_CFG, "encoding");
333 |
334 | file.Writer.write(prefs.getString("pref_uiScaling", "1.0"), SETTINGS_CFG, "scaling factor");
335 |
336 | file.Writer.write(prefs.getString("pref_allowCapsuleShape", "true"), SETTINGS_CFG, "allow capsule shape");
337 |
338 | file.Writer.write(prefs.getString("pref_preload", "false"), SETTINGS_CFG, "preload enabled");
339 |
340 | file.Writer.write(prefs.getString("pref_viewingDistance", "2000"), SETTINGS_CFG, "viewing distance");
341 |
342 | file.Writer.write(prefs.getString("pref_distantTerrain", "false"), SETTINGS_CFG, "distant terrain");
343 |
344 | runOnUiThread(() -> {
345 | obtainScreenResolution();
346 | dialog.hide();
347 | runGame();
348 | });
349 | } catch (IOException e) {
350 | Log.e(TAG, "Failed to write config files.", e);
351 | Crashlytics.logException(e);
352 | }
353 | });
354 | th.start();
355 | }
356 |
357 | private void setTexWatcher() {
358 | switch (editTextMode) {
359 | case COMMAND_LINE:
360 | removeTextlistener();
361 | listener = new TextListener(this
362 | , "", "", Constants.COMMAND_LINE,
363 | Constants.commandLineData, Settings, "commandLine");
364 | path.addTextChangedListener(listener);
365 | break;
366 | case DATA_PATH:
367 | removeTextlistener();
368 | listener = new TextListener(this,
369 | "", "data", Constants.DATA_PATH, Constants.APPLICATION_DATA_STORAGE_PATH, Settings,
370 | "data");
371 | path.addTextChangedListener(listener);
372 | break;
373 | default:
374 | break;
375 | }
376 | }
377 |
378 |
379 | public void showOverflowMenu(boolean showMenu) {
380 | if (menu == null)
381 | return;
382 | menu.setGroupVisible(R.id.main_menu_group, showMenu);
383 | }
384 |
385 | @Override
386 | public boolean onPrepareOptionsMenu(Menu menu) {
387 | this.menu = menu;
388 | menu.clear();
389 | MenuInflater inflater = getMenuInflater();
390 | if (isSettingsEnabled)
391 | inflater.inflate(R.menu.menu_settings, menu);
392 | else
393 | inflater.inflate(R.menu.menu_browser, menu);
394 | return super.onPrepareOptionsMenu(menu);
395 | }
396 |
397 | @Override
398 | public boolean onOptionsItemSelected(MenuItem item) {
399 | int id = item.getItemId();
400 | if (!isSettingsEnabled)
401 | switch (id) {
402 | case R.id.action_sortPlayers:
403 | FragmentBrowser.sortPlayers();
404 | break;
405 | case R.id.action_sortAlphabet:
406 | FragmentBrowser.sortAlphabet();
407 | break;
408 | default:
409 | break;
410 | }
411 | else
412 | switch (id) {
413 | case R.id.action_show_screen_controls:
414 | startControlsActivity();
415 | break;
416 |
417 | case R.id.action_reset_config:
418 | resetUserConfig();
419 | Toast.makeText(this, getString(R.string.config_was_reset), Toast.LENGTH_SHORT).show();
420 | break;
421 |
422 | default:
423 | break;
424 | }
425 |
426 | return super.onOptionsItemSelected(item);
427 | }
428 |
429 | private void startControlsActivity() {
430 | Intent intent = new Intent(this,
431 | ConfigureControls.class);
432 | this.startActivity(intent);
433 |
434 | }
435 |
436 | private void enableToolbarViews() {
437 | path.setVisibility(EditText.VISIBLE);
438 | browseButton.setVisibility(Button.VISIBLE);
439 | setTexWatcher();
440 | }
441 |
442 | private void disableToolBarViews() {
443 | browseButton.setVisibility(Button.GONE);
444 | path.setVisibility(EditText.GONE);
445 | }
446 |
447 | @Override
448 | public void onWindowFocusChanged(boolean hasFocus) {
449 | ScreenScaler.textScaler(path, 3f);
450 | ScreenScaler.textScaler(browseButton, 4.8f);
451 | }
452 |
453 | private void removeTextlistener() {
454 | if (listener != null)
455 | path.removeTextChangedListener(listener);
456 |
457 |
458 | }
459 |
460 | }
461 |
462 |
463 |
464 |
--------------------------------------------------------------------------------