/**************************************************************************/
/* GodotIO.java */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
package org.godotengine.godot;
import org.godotengine.godot.input.GodotEditText;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.graphics.Point;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Display;
import android.view.DisplayCutout;
import android.view.WindowInsets;
import androidx.core.content.FileProvider;
import java.io.File;
import java.util.List;
import java.util.Locale;
// Wrapper for native library
public class GodotIO {
private static final String TAG = GodotIO.class.getSimpleName();
private final Activity activity;
private final String uniqueId;
GodotEditText edit;
final int SCREEN_LANDSCAPE = 0;
final int SCREEN_PORTRAIT = 1;
final int SCREEN_REVERSE_LANDSCAPE = 2;
final int SCREEN_REVERSE_PORTRAIT = 3;
final int SCREEN_SENSOR_LANDSCAPE = 4;
final int SCREEN_SENSOR_PORTRAIT = 5;
final int SCREEN_SENSOR = 6;
GodotIO(Activity p_activity) {
activity = p_activity;
String androidId = Settings.Secure.getString(activity.getContentResolver(),
Settings.Secure.ANDROID_ID);
if (androidId == null) {
androidId = "";
}
uniqueId = androidId;
}
/////////////////////////
// MISCELLANEOUS OS IO
/////////////////////////
public int openURI(String uriString) {
try {
Uri dataUri;
String dataType = "";
boolean grantReadUriPermission = false;
if (uriString.startsWith("/") || uriString.startsWith("file://")) {
String filePath = uriString;
// File uris needs to be provided via the FileProvider
grantReadUriPermission = true;
if (filePath.startsWith("file://")) {
filePath = filePath.replace("file://", "");
}
File targetFile = new File(filePath);
dataUri = FileProvider.getUriForFile(activity, activity.getPackageName() + ".fileprovider", targetFile);
dataType = activity.getContentResolver().getType(dataUri);
} else {
dataUri = Uri.parse(uriString);
}
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
if (TextUtils.isEmpty(dataType)) {
intent.setData(dataUri);
} else {
intent.setDataAndType(dataUri, dataType);
}
if (grantReadUriPermission) {
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
activity.startActivity(intent);
return 0;
} catch (Exception e) {
Log.e(TAG, "Unable to open uri " + uriString, e);
return 1;
}
}
public String getCacheDir() {
return activity.getCacheDir().getAbsolutePath();
}
public String getDataDir() {
return activity.getFilesDir().getAbsolutePath();
}
public String getLocale() {
return Locale.getDefault().toString();
}
public String getModel() {
return Build.MODEL;
}
public int getScreenDPI() {
return activity.getResources().getDisplayMetrics().densityDpi;
}
/**
* Returns bucketized density values.
*/
public float getScaledDensity() {
int densityDpi = activity.getResources().getDisplayMetrics().densityDpi;
float selectedScaledDensity;
if (densityDpi >= DisplayMetrics.DENSITY_XXXHIGH) {
selectedScaledDensity = 4.0f;
} else if (densityDpi >= DisplayMetrics.DENSITY_XXHIGH) {
selectedScaledDensity = 3.0f;
} else if (densityDpi >= DisplayMetrics.DENSITY_XHIGH) {
selectedScaledDensity = 2.0f;
} else if (densityDpi >= DisplayMetrics.DENSITY_HIGH) {
selectedScaledDensity = 1.5f;
} else if (densityDpi >= DisplayMetrics.DENSITY_MEDIUM) {
selectedScaledDensity = 1.0f;
} else {
selectedScaledDensity = 0.75f;
}
return selectedScaledDensity;
}
public double getScreenRefreshRate(double fallback) {
Display display = activity.getWindowManager().getDefaultDisplay();
if (display != null) {
return display.getRefreshRate();
}
return fallback;
}
public int[] getDisplaySafeArea() {
Rect rect = new Rect();
activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);
int[] result = { rect.left, rect.top, rect.right, rect.bottom };
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
WindowInsets insets = activity.getWindow().getDecorView().getRootWindowInsets();
DisplayCutout cutout = insets.getDisplayCutout();
if (cutout != null) {
int insetLeft = cutout.getSafeInsetLeft();
int insetTop = cutout.getSafeInsetTop();
result[0] = insetLeft;
result[1] = insetTop;
result[2] -= insetLeft + cutout.getSafeInsetRight();
result[3] -= insetTop + cutout.getSafeInsetBottom();
}
}
return result;
}
public int[] getDisplayCutouts() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P)
return new int[0];
DisplayCutout cutout = activity.getWindow().getDecorView().getRootWindowInsets().getDisplayCutout();
if (cutout == null)
return new int[0];
List<Rect> rects = cutout.getBoundingRects();
int cutouts = rects.size();
int[] result = new int[cutouts * 4];
int index = 0;
for (Rect rect : rects) {
result[index++] = rect.left;
result[index++] = rect.top;
result[index++] = rect.width();
result[index++] = rect.height();
}
return result;
}
public boolean hasHardwareKeyboard() {
if (edit != null) {
return edit.hasHardwareKeyboard();
} else {
return false;
}
}
public void showKeyboard(String p_existing_text, int p_type, int p_max_input_length, int p_cursor_start, int p_cursor_end) {
if (edit != null) {
edit.showKeyboard(p_existing_text, GodotEditText.VirtualKeyboardType.values()[p_type], p_max_input_length, p_cursor_start, p_cursor_end);
}
//InputMethodManager inputMgr = (InputMethodManager)activity.getSystemService(Context.INPUT_METHOD_SERVICE);
//inputMgr.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
}
public void hideKeyboard() {
if (edit != null)
edit.hideKeyboard();
}
public void setScreenOrientation(int p_orientation) {
switch (p_orientation) {
case SCREEN_LANDSCAPE: {
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
} break;
case SCREEN_PORTRAIT: {
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
} break;
case SCREEN_REVERSE_LANDSCAPE: {
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
} break;
case SCREEN_REVERSE_PORTRAIT: {
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT);
} break;
case SCREEN_SENSOR_LANDSCAPE: {
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE);
} break;
case SCREEN_SENSOR_PORTRAIT: {
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT);
} break;
case SCREEN_SENSOR: {
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_USER);
} break;
}
}
public int getScreenOrientation() {
int orientation = activity.getRequestedOrientation();
switch (orientation) {
case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE:
return SCREEN_LANDSCAPE;
case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT:
return SCREEN_PORTRAIT;
case ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE:
return SCREEN_REVERSE_LANDSCAPE;
case ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT:
return SCREEN_REVERSE_PORTRAIT;
case ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE:
case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE:
return SCREEN_SENSOR_LANDSCAPE;
case ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT:
case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT:
return SCREEN_SENSOR_PORTRAIT;
case ActivityInfo.SCREEN_ORIENTATION_SENSOR:
case ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR:
case ActivityInfo.SCREEN_ORIENTATION_FULL_USER:
return SCREEN_SENSOR;
case ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED:
case ActivityInfo.SCREEN_ORIENTATION_USER:
case ActivityInfo.SCREEN_ORIENTATION_BEHIND:
case ActivityInfo.SCREEN_ORIENTATION_NOSENSOR:
case ActivityInfo.SCREEN_ORIENTATION_LOCKED:
default:
return -1;
}
}
public void setEdit(GodotEditText _edit) {
edit = _edit;
}
public static final int SYSTEM_DIR_DESKTOP = 0;
public static final int SYSTEM_DIR_DCIM = 1;
public static final int SYSTEM_DIR_DOCUMENTS = 2;
public static final int SYSTEM_DIR_DOWNLOADS = 3;
public static final int SYSTEM_DIR_MOVIES = 4;
public static final int SYSTEM_DIR_MUSIC = 5;
public static final int SYSTEM_DIR_PICTURES = 6;
public static final int SYSTEM_DIR_RINGTONES = 7;
public String getSystemDir(int idx, boolean shared_storage) {
String what;
switch (idx) {
case SYSTEM_DIR_DESKTOP:
default: {
what = null; // This leads to the app specific external root directory.
} break;
case SYSTEM_DIR_DCIM: {
what = Environment.DIRECTORY_DCIM;
} break;
case SYSTEM_DIR_DOCUMENTS: {
what = Environment.DIRECTORY_DOCUMENTS;
} break;
case SYSTEM_DIR_DOWNLOADS: {
what = Environment.DIRECTORY_DOWNLOADS;
} break;
case SYSTEM_DIR_MOVIES: {
what = Environment.DIRECTORY_MOVIES;
} break;
case SYSTEM_DIR_MUSIC: {
what = Environment.DIRECTORY_MUSIC;
} break;
case SYSTEM_DIR_PICTURES: {
what = Environment.DIRECTORY_PICTURES;
} break;
case SYSTEM_DIR_RINGTONES: {
what = Environment.DIRECTORY_RINGTONES;
} break;
}
if (shared_storage) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
Log.w(TAG, "Shared storage access is limited on Android 10 and higher.");
}
if (TextUtils.isEmpty(what)) {
return Environment.getExternalStorageDirectory().getAbsolutePath();
} else {
return Environment.getExternalStoragePublicDirectory(what).getAbsolutePath();
}
} else {
return activity.getExternalFilesDir(what).getAbsolutePath();
}
}
public String getUniqueID() {
return uniqueId;
}
}