/*
* Catroid: An on-device visual programming system for Android devices
* Copyright (C) 2010-2016 The Catrobat Team
* (<http://developer.catrobat.org/credits>)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* An additional term exception under section 7 of the GNU Affero
* General Public License, version 3, is available at
* http://developer.catrobat.org/license_additional_term
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.catrobat.catroid.utils;
import android.app.Activity;
import android.app.AlertDialog.Builder;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Environment;
import android.preference.PreferenceManager;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListAdapter;
import android.widget.ListView;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.utils.GdxNativesLoader;
import com.badlogic.gdx.utils.GdxRuntimeException;
import com.facebook.AccessToken;
import com.google.common.base.Splitter;
import com.google.firebase.crash.FirebaseCrash;
import com.google.gson.Gson;
import org.catrobat.catroid.BuildConfig;
import org.catrobat.catroid.ProjectManager;
import org.catrobat.catroid.R;
import org.catrobat.catroid.common.Constants;
import org.catrobat.catroid.common.DefaultProjectHandler;
import org.catrobat.catroid.common.LookData;
import org.catrobat.catroid.common.NfcTagData;
import org.catrobat.catroid.common.ScratchProgramData;
import org.catrobat.catroid.common.SoundInfo;
import org.catrobat.catroid.content.Project;
import org.catrobat.catroid.content.Scene;
import org.catrobat.catroid.content.Sprite;
import org.catrobat.catroid.content.XmlHeader;
import org.catrobat.catroid.content.bricks.Brick;
import org.catrobat.catroid.exceptions.ProjectException;
import org.catrobat.catroid.io.StorageHandler;
import org.catrobat.catroid.transfers.LogoutTask;
import org.catrobat.catroid.ui.BaseExceptionHandler;
import org.catrobat.catroid.ui.MainMenuActivity;
import org.catrobat.catroid.ui.SettingsActivity;
import org.catrobat.catroid.ui.WebViewActivity;
import org.catrobat.catroid.ui.controller.BackPackListManager;
import org.catrobat.catroid.ui.dialogs.CustomAlertDialogBuilder;
import org.catrobat.catroid.web.ServerCalls;
import org.catrobat.catroid.web.WebconnectionException;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
public final class Utils {
private static final String TAG = Utils.class.getSimpleName();
private enum RemixUrlParsingState {
STARTING, TOKEN, BETWEEN
}
public static final int TRANSLATION_PLURAL_OTHER_INTEGER = 767676;
// Suppress default constructor for noninstantiability
private Utils() {
throw new AssertionError();
}
public static boolean externalStorageAvailable() {
String externalStorageState = Environment.getExternalStorageState();
return externalStorageState.equals(Environment.MEDIA_MOUNTED)
&& !externalStorageState.equals(Environment.MEDIA_MOUNTED_READ_ONLY);
}
public static boolean checkForExternalStorageAvailableAndDisplayErrorIfNot(final Context context) {
if (!externalStorageAvailable()) {
Builder builder = new CustomAlertDialogBuilder(context);
builder.setTitle(R.string.error);
builder.setMessage(R.string.error_no_writiable_external_storage_available);
builder.setNeutralButton(R.string.close, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
((Activity) context).moveTaskToBack(true);
}
});
builder.show();
return false;
}
return true;
}
public static boolean checkIfCrashRecoveryAndFinishActivity(final Activity context) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
if (preferences.getBoolean(BaseExceptionHandler.RECOVERED_FROM_CRASH, false)) {
if (BuildConfig.FIREBASE_CRASH_REPORT_ENABLED
&& preferences.getBoolean(SettingsActivity.SETTINGS_CRASH_REPORTS, false)) {
sendCaughtException(context);
}
if (!(context instanceof MainMenuActivity)) {
context.finish();
} else {
preferences.edit().putBoolean(BaseExceptionHandler.RECOVERED_FROM_CRASH, false).commit();
return true;
}
}
return false;
}
public static void sendCaughtException(Context context) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
Gson gson = new Gson();
String json = preferences.getString(BaseExceptionHandler.EXCEPTION_FOR_REPORT, "");
Throwable exception = gson.fromJson(json, Throwable.class);
FirebaseCrash.report(exception);
preferences.edit().remove(BaseExceptionHandler.EXCEPTION_FOR_REPORT).commit();
}
public static boolean isNetworkAvailable(Context context, boolean createDialog) {
ConnectivityManager connectivityManager = (ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
boolean isAvailable = activeNetworkInfo != null && activeNetworkInfo.isConnected();
if (!isAvailable && createDialog) {
new CustomAlertDialogBuilder(context).setTitle(R.string.no_internet)
.setMessage(R.string.error_no_internet).setPositiveButton(R.string.ok, null)
.show();
}
return isAvailable;
}
public static boolean isNetworkAvailable(Context context) {
return isNetworkAvailable(context, false);
}
public static boolean checkForNetworkError(boolean success, WebconnectionException exception) {
return !success && exception != null && exception.getStatusCode() == WebconnectionException
.ERROR_NETWORK;
}
public static boolean checkForSignInError(boolean success, WebconnectionException exception, Context context,
boolean userSignedIn) {
return (!success && exception != null) || context == null || !userSignedIn;
}
public static boolean checkForNetworkError(WebconnectionException exception) {
return exception != null && exception.getStatusCode() == WebconnectionException
.ERROR_NETWORK;
}
public static String formatDate(Date date, Locale locale) {
return DateFormat.getDateInstance(DateFormat.LONG, locale).format(date);
}
@Nullable
public static byte[] convertInputStreamToByteArray(final InputStream inputStream) {
try {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[4096];
int len;
while ((len = inputStream.read(buffer)) > -1) {
byteArrayOutputStream.write(buffer, 0, len);
}
byteArrayOutputStream.flush();
return byteArrayOutputStream.toByteArray();
} catch (IOException e) {
return null;
}
}
public static String generateRemixUrlsStringForMergedProgram(XmlHeader headerOfFirstProgram, XmlHeader headerOfSecondProgram) {
String escapedFirstProgramName = headerOfFirstProgram.getProgramName();
escapedFirstProgramName = escapedFirstProgramName.replace(Constants.REMIX_URL_PREFIX_INDICATOR,
Constants.REMIX_URL_PREFIX_REPLACE_INDICATOR);
escapedFirstProgramName = escapedFirstProgramName.replace(Constants.REMIX_URL_SUFIX_INDICATOR,
Constants.REMIX_URL_SUFIX_REPLACE_INDICATOR);
escapedFirstProgramName = escapedFirstProgramName.replace(Constants.REMIX_URL_SEPARATOR,
Constants.REMIX_URL_REPLACE_SEPARATOR);
String escapedSecondProgramName = headerOfSecondProgram.getProgramName();
escapedSecondProgramName = escapedSecondProgramName.replace(Constants.REMIX_URL_PREFIX_INDICATOR,
Constants.REMIX_URL_PREFIX_REPLACE_INDICATOR);
escapedSecondProgramName = escapedSecondProgramName.replace(Constants.REMIX_URL_SUFIX_INDICATOR,
Constants.REMIX_URL_SUFIX_REPLACE_INDICATOR);
escapedSecondProgramName = escapedSecondProgramName.replace(Constants.REMIX_URL_SEPARATOR,
Constants.REMIX_URL_REPLACE_SEPARATOR);
StringBuilder remixUrlString = new StringBuilder(escapedFirstProgramName);
if (!headerOfFirstProgram.getRemixParentsUrlString().equals("")) {
remixUrlString
.append(' ')
.append(Constants.REMIX_URL_PREFIX_INDICATOR)
.append(headerOfFirstProgram.getRemixParentsUrlString())
.append(Constants.REMIX_URL_SUFIX_INDICATOR);
}
remixUrlString
.append(Constants.REMIX_URL_SEPARATOR)
.append(' ')
.append(escapedSecondProgramName);
if (!headerOfSecondProgram.getRemixParentsUrlString().equals("")) {
remixUrlString
.append(' ')
.append(Constants.REMIX_URL_PREFIX_INDICATOR)
.append(headerOfSecondProgram.getRemixParentsUrlString())
.append(Constants.REMIX_URL_SUFIX_INDICATOR);
}
return remixUrlString.toString();
}
// based on: http://stackoverflow.com/a/27295688
public static List<String> extractRemixUrlsFromString(String text) {
RemixUrlParsingState state = RemixUrlParsingState.STARTING;
ArrayList<String> extractedUrls = new ArrayList<>();
StringBuffer temp = new StringBuffer("");
for (int index = 0; index < text.length(); index++) {
char currentCharacter = text.charAt(index);
switch (currentCharacter) {
case Constants.REMIX_URL_PREFIX_INDICATOR:
if (state == RemixUrlParsingState.STARTING) {
state = RemixUrlParsingState.BETWEEN;
} else if (state == RemixUrlParsingState.TOKEN) {
temp.delete(0, temp.length());
state = RemixUrlParsingState.BETWEEN;
}
break;
case Constants.REMIX_URL_SUFIX_INDICATOR:
if (state == RemixUrlParsingState.TOKEN) {
String extractedUrl = temp.toString().trim();
if (!extractedUrl.contains(String.valueOf(Constants.REMIX_URL_SEPARATOR))
&& extractedUrl.length() > 0) {
extractedUrls.add(extractedUrl);
}
temp.delete(0, temp.length());
state = RemixUrlParsingState.BETWEEN;
}
break;
default:
state = RemixUrlParsingState.TOKEN;
temp.append(currentCharacter);
}
}
if (extractedUrls.size() == 0 && !text.contains(String.valueOf(Constants.REMIX_URL_SEPARATOR))) {
extractedUrls.add(text);
}
return extractedUrls;
}
public static Date getScratchSecondReleasePublishedDate() {
final Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.YEAR, Constants.SCRATCH_SECOND_RELEASE_PUBLISHED_DATE_YEAR);
calendar.set(Calendar.MONTH, Constants.SCRATCH_SECOND_RELEASE_PUBLISHED_DATE_MONTH);
calendar.set(Calendar.DAY_OF_MONTH, Constants.SCRATCH_SECOND_RELEASE_PUBLISHED_DATE_DAY);
return calendar.getTime();
}
public static boolean isDeprecatedScratchProgram(final ScratchProgramData programData) {
// NOTE: ignoring old Scratch 1.x programs -> converter only supports version 2.x and later
// Scratch 1.x programs are created before May 9, 2013 (see: https://wiki.scratch.mit.edu/wiki/Scratch_2.0)
final Date releasePublishedDate = getScratchSecondReleasePublishedDate();
if (programData.getModifiedDate() != null && programData.getModifiedDate().before(releasePublishedDate)) {
return true;
} else if (programData.getCreatedDate() != null && programData.getCreatedDate().before(releasePublishedDate)) {
return true;
}
return false;
}
public static String extractParameterFromURL(final String url, final String parameterKey) {
final String query = url.split("\\?")[1];
return Splitter.on('&').trimResults().withKeyValueSeparator("=").split(query).get(parameterKey);
}
public static long extractScratchJobIDFromURL(final String url) {
if (!url.startsWith(Constants.SCRATCH_CONVERTER_BASE_URL)) {
return Constants.INVALID_SCRATCH_PROGRAM_ID;
}
final String jobIDString = extractParameterFromURL(url, "job_id");
if (jobIDString == null) {
return Constants.INVALID_SCRATCH_PROGRAM_ID;
}
final long jobID = Long.parseLong(jobIDString);
return jobID > 0 ? jobID : Constants.INVALID_SCRATCH_PROGRAM_ID;
}
public static String changeSizeOfScratchImageURL(final String url, int newHeight) {
// example: https://cdn2.scratch.mit.edu/get_image/project/10205819_480x360.png
// -> https://cdn2.scratch.mit.edu/get_image/project/10205819_240x180.png
final int width = Constants.SCRATCH_IMAGE_DEFAULT_WIDTH;
final int height = Constants.SCRATCH_IMAGE_DEFAULT_HEIGHT;
final int newWidth = Math.round(((float) width) / ((float) height) * newHeight);
return url.replace(width + "x", Integer.toString(newWidth) + "x")
.replace("x" + height, "x" + Integer.toString(newHeight));
}
public static String humanFriendlyFormattedShortNumber(final int number) {
if (number < 1_000) {
return Integer.toString(number);
} else if (number < 10_000) {
return Integer.toString(number / 1_000) + (number % 1_000 > 100 ? "."
+ Integer.toString((number % 1_000) / 100) : "") + "k";
} else if (number < 1_000_000) {
return Integer.toString(number / 1_000) + "k";
}
return Integer.toString(number / 1_000_000) + "M";
}
public static boolean setListViewHeightBasedOnItems(ListView listView) {
ListAdapter listAdapter = listView.getAdapter();
if (listAdapter != null) {
int numberOfItems = listAdapter.getCount();
// Get total height of all items.
int totalItemsHeight = 0;
for (int itemPos = 0; itemPos < numberOfItems; ++itemPos) {
View item = listAdapter.getView(itemPos, null, listView);
item.measure(0, 0);
totalItemsHeight += item.getMeasuredHeight();
}
// Get total height of all item dividers.
int totalDividersHeight = listView.getDividerHeight() * (numberOfItems - 1);
// Set list height.
ViewGroup.LayoutParams params = listView.getLayoutParams();
params.height = totalItemsHeight + totalDividersHeight;
listView.setLayoutParams(params);
listView.requestLayout();
return true;
} else {
return false;
}
}
/**
* Constructs a path out of the pathElements.
*
* @param pathElements the strings to connect. They can have "/" in them which will be de-duped in the result, if necessary.
* @return the path that was constructed.
*/
public static String buildPath(String... pathElements) {
StringBuilder result = new StringBuilder("/");
for (String pathElement : pathElements) {
result.append(pathElement).append('/');
}
String returnValue = result.toString().replaceAll("/+", "/");
if (returnValue.endsWith("/")) {
returnValue = returnValue.substring(0, returnValue.length() - 1);
}
return returnValue;
}
public static String buildProjectPath(String projectName) {
return buildPath(Constants.DEFAULT_ROOT, UtilFile.encodeSpecialCharsForFileSystem(projectName));
}
public static String buildScenePath(String projectName, String sceneName) {
return buildPath(buildProjectPath(projectName), UtilFile.encodeSpecialCharsForFileSystem(sceneName));
}
public static String buildBackpackScenePath(String sceneName) {
return buildPath(Constants.DEFAULT_ROOT, Constants.BACKPACK_DIRECTORY, Constants.SCENES_DIRECTORY,
UtilFile.encodeSpecialCharsForFileSystem(sceneName));
}
public static void showErrorDialog(Context context, int errorMessageId) {
Builder builder = new CustomAlertDialogBuilder(context);
builder.setTitle(R.string.error);
builder.setMessage(errorMessageId);
builder.setNeutralButton(R.string.close, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
Dialog errorDialog = builder.create();
errorDialog.show();
}
public static void showErrorDialog(Context context, String msg, int errorTitleId) {
Builder builder = new CustomAlertDialogBuilder(context);
builder.setTitle(errorTitleId);
builder.setMessage(msg);
builder.setNeutralButton(R.string.close, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
Dialog errorDialog = builder.create();
errorDialog.show();
}
public static void showErrorDialog(Context context, int errorTitleId, int errorMessageId) {
Builder builder = new CustomAlertDialogBuilder(context);
builder.setTitle(errorTitleId);
builder.setMessage(errorMessageId);
builder.setNeutralButton(R.string.close, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
Dialog errorDialog = builder.create();
errorDialog.show();
}
public static String md5Checksum(File file) {
if (!file.isFile()) {
Log.e(TAG, String.format("md5Checksum() Error with file %s isFile: %s isDirectory: %s exists: %s",
file.getName(),
Boolean.valueOf(file.isFile()),
Boolean.valueOf(file.isDirectory()),
Boolean.valueOf(file.exists())));
return null;
}
MessageDigest messageDigest = getMD5MessageDigest();
FileInputStream fis = null;
try {
fis = new FileInputStream(file);
byte[] buffer = new byte[Constants.BUFFER_8K];
int length;
while ((length = fis.read(buffer)) != -1) {
messageDigest.update(buffer, 0, length);
}
} catch (IOException e) {
Log.w(TAG, "IOException thrown in md5Checksum()");
} finally {
try {
if (fis != null) {
fis.close();
}
} catch (IOException e) {
Log.w(TAG, "IOException thrown in finally block of md5Checksum()");
}
}
return toHex(messageDigest.digest()).toLowerCase(Locale.US);
}
public static String md5Checksum(String string) {
MessageDigest messageDigest = getMD5MessageDigest();
messageDigest.update(string.getBytes());
return toHex(messageDigest.digest()).toLowerCase(Locale.US);
}
public static double round(double value, int precision) {
final int scale = (int) Math.pow(10, precision);
return (double) Math.round(value * scale) / scale;
}
private static String toHex(byte[] messageDigest) {
final char[] hexChars = "0123456789ABCDEF".toCharArray();
char[] hexBuffer = new char[messageDigest.length * 2];
int j = 0;
for (byte c : messageDigest) {
hexBuffer[j++] = hexChars[(c & 0xF0) >> 4];
hexBuffer[j++] = hexChars[c & 0x0F];
}
return String.valueOf(hexBuffer);
}
private static MessageDigest getMD5MessageDigest() {
MessageDigest messageDigest = null;
try {
messageDigest = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
Log.w(TAG, "NoSuchAlgorithmException thrown in getMD5MessageDigest()");
}
return messageDigest;
}
public static ArrayList<String> formatStringForBubbleBricks(String text) {
ArrayList<String> lines = new ArrayList<>();
int cursorPos = 0;
while (cursorPos + Constants.MAX_STRING_LENGTH_BUBBLES < text.length()) {
String newLine = text.substring(cursorPos, cursorPos + Constants.MAX_STRING_LENGTH_BUBBLES);
int lastWhitespace = newLine.lastIndexOf(' ');
if (lastWhitespace < 0) {
lastWhitespace = Constants.MAX_STRING_LENGTH_BUBBLES;
}
newLine = text.substring(cursorPos, cursorPos + lastWhitespace);
while (newLine.contains("\n")) {
String subLine = newLine.substring(0, newLine.indexOf('\n') + 1);
lines.add(subLine);
newLine = newLine.replace(subLine, "");
}
lines.add(newLine);
cursorPos += lastWhitespace;
}
lines.add(text.substring(cursorPos, text.length()).trim());
return lines;
}
public static String getVersionName(Context context) {
String versionName = "unknown";
try {
PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(),
PackageManager.GET_META_DATA);
versionName = packageInfo.versionName;
} catch (NameNotFoundException nameNotFoundException) {
Log.e(TAG, "Name not found", nameNotFoundException);
}
return versionName;
}
public static int getPhysicalPixels(int densityIndependentPixels, Context context) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (densityIndependentPixels * scale + 0.5f);
}
public static Activity getActivity() throws IllegalAccessException, NoSuchFieldException, ClassNotFoundException,
NoSuchMethodException, InvocationTargetException {
Class activityThreadClass = Class.forName("android.app.ActivityThread");
Object activityThread = activityThreadClass.getMethod("currentActivityThread").invoke(null);
Field activitiesField = activityThreadClass.getDeclaredField("mActivities");
activitiesField.setAccessible(true);
HashMap activities = (HashMap) activitiesField.get(activityThread);
for (Object activityRecord : activities.values()) {
Class activityRecordClass = activityRecord.getClass();
Field pausedField = activityRecordClass.getDeclaredField("paused");
pausedField.setAccessible(true);
if (!pausedField.getBoolean(activityRecord)) {
Field activityField = activityRecordClass.getDeclaredField("activity");
activityField.setAccessible(true);
Activity activity = (Activity) activityField.get(activityRecord);
return activity;
}
}
return null;
}
public static void saveToPreferences(Context context, String key, String message) {
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
Editor edit = sharedPreferences.edit();
edit.putString(key, message);
edit.commit();
}
public static void removeFromPreferences(Context context, String key) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor edit = preferences.edit();
edit.remove(key);
edit.commit();
}
public static void loadProjectIfNeeded(Context context) {
String projectName;
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
projectName = sharedPreferences.getString(Constants.PREF_PROJECTNAME_KEY, null);
if (ProjectManager.getInstance().getCurrentProject() == null) {
if (projectName == null || !StorageHandler.getInstance().projectExists(projectName)) {
projectName = context.getString(R.string.default_project_name);
}
try {
ProjectManager.getInstance().loadProject(projectName, context);
} catch (ProjectException projectException) {
Log.e(TAG, "Project cannot load", projectException);
ProjectManager.getInstance().initializeDefaultProject(context);
}
}
}
public static String getCurrentProjectName(Context context) {
if (ProjectManager.getInstance().getCurrentProject() == null) {
if (UtilFile.getProjectNames(new File(Constants.DEFAULT_ROOT)).size() == 0) {
Log.i(TAG, "Somebody deleted all projects in the file-system");
ProjectManager.getInstance().initializeDefaultProject(context);
}
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
String currentProjectName = sharedPreferences.getString(Constants.PREF_PROJECTNAME_KEY, null);
if (currentProjectName == null || !StorageHandler.getInstance().projectExists(currentProjectName)) {
currentProjectName = UtilFile.getProjectNames(new File(Constants.DEFAULT_ROOT)).get(0);
}
return currentProjectName;
}
return ProjectManager.getInstance().getCurrentProject().getName();
}
public static String deleteSpecialCharactersInString(String stringToAdapt) {
return stringToAdapt.replaceAll("[\"*/:<>?\\\\|]", "");
}
public static String getUniqueObjectName(String name) {
return searchForNonExistingObjectNameInCurrentProgram(name, 0);
}
private static String searchForNonExistingObjectNameInCurrentProgram(String name, int nextNumber) {
String newName;
if (nextNumber == 0) {
newName = name;
} else {
newName = name + "_" + nextNumber;
}
if (ProjectManager.getInstance().getCurrentScene().containsSpriteBySpriteName(newName)) {
return searchForNonExistingObjectNameInCurrentProgram(name, ++nextNumber);
}
return newName;
}
public static String getUniqueNfcTagName(String name) {
return searchForNonExistingNfcTagName(name, 0);
}
private static String searchForNonExistingNfcTagName(String name, int nextNumber) {
String newName;
List<NfcTagData> nfcTagDataList = ProjectManager.getInstance().getCurrentSprite().getNfcTagList();
if (nextNumber == 0) {
newName = name;
} else {
newName = name + "_" + nextNumber;
}
for (NfcTagData nfcTagData : nfcTagDataList) {
if (nfcTagData.getNfcTagName().equals(newName)) {
return searchForNonExistingNfcTagName(name, ++nextNumber);
}
}
return newName;
}
public static String getUniqueLookName(LookData lookData, boolean forBackPack) {
return searchForNonExistingLookName(lookData, 0, forBackPack);
}
private static String searchForNonExistingLookName(LookData originalLookData,
int nextNumber, boolean forBackPack) {
String newName;
List<LookData> lookDataList;
if (forBackPack) {
lookDataList = BackPackListManager.getInstance().getAllBackPackedLooks();
} else {
lookDataList = ProjectManager.getInstance().getCurrentSprite().getLookDataList();
}
if (nextNumber == 0) {
newName = originalLookData.getLookName();
} else {
newName = originalLookData.getLookName() + "_" + nextNumber;
}
for (LookData lookData : lookDataList) {
if (lookData.getLookName().equals(newName)) {
return searchForNonExistingLookName(originalLookData, ++nextNumber, forBackPack);
}
}
return newName;
}
public static String getUniqueSpriteName(Sprite sprite) {
return searchForNonExistingSpriteName(sprite, 0);
}
private static String searchForNonExistingSpriteName(Sprite sprite, int nextNumber) {
String newName;
List<Sprite> spriteList;
if (!sprite.isBackpackObject) {
spriteList = BackPackListManager.getInstance().getAllBackPackedSprites();
} else {
spriteList = ProjectManager.getInstance().getCurrentScene().getSpriteList();
}
if (nextNumber == 0) {
newName = sprite.getName();
} else {
newName = sprite.getName() + "_" + nextNumber;
}
for (Sprite spriteListItem : spriteList) {
if (spriteListItem.getName().equals(newName)) {
return searchForNonExistingSpriteName(sprite, ++nextNumber);
}
}
return newName;
}
public static String getUniqueSceneName(String sceneName, Project firstProject, Project secondProject) {
Project backup = ProjectManager.getInstance().getCurrentProject();
ProjectManager.getInstance().setCurrentProject(firstProject);
String result = getUniqueSceneName(sceneName, false);
ProjectManager.getInstance().setCurrentProject(secondProject);
sceneName = getUniqueSceneName(result, false);
ProjectManager.getInstance().setCurrentProject(backup);
return sceneName;
}
public static String getUniqueSceneName(String sceneName, boolean forBackPack) {
List<Scene> sceneList;
if (forBackPack) {
sceneList = BackPackListManager.getInstance().getAllBackpackedScenes();
} else {
sceneList = ProjectManager.getInstance().getCurrentProject().getSceneList();
}
String possibleNewName = sceneName;
Boolean check = true;
int nextNumber = 1;
while (check) {
check = false;
possibleNewName = sceneName + "_" + nextNumber;
for (Scene sceneListItem : sceneList) {
if (sceneListItem.getName().equals(possibleNewName)) {
check = true;
break;
}
}
nextNumber += 1;
}
return possibleNewName;
}
public static String searchForNonExistingSceneName(String sceneName, int nextNumber, boolean forBackPack) {
List<Scene> sceneList;
if (forBackPack) {
sceneList = BackPackListManager.getInstance().getAllBackpackedScenes();
} else {
sceneList = ProjectManager.getInstance().getCurrentProject().getSceneList();
}
String possibleNewName = String.format(sceneName, nextNumber);
for (Scene sceneListItem : sceneList) {
if (sceneListItem.getName().equals(possibleNewName)) {
return searchForNonExistingSceneName(sceneName, ++nextNumber, forBackPack);
}
}
return possibleNewName;
}
public static String getUniqueSoundName(SoundInfo soundInfo, boolean forBackPack) {
return searchForNonExistingSoundTitle(soundInfo, 0, forBackPack);
}
public static Project findValidProject(Context context) {
Project loadableProject = null;
List<String> projectNameList = UtilFile.getProjectNames(new File(Constants.DEFAULT_ROOT));
for (String projectName : projectNameList) {
loadableProject = StorageHandler.getInstance().loadProject(projectName, context);
if (loadableProject != null) {
break;
}
}
return loadableProject;
}
private static String searchForNonExistingSoundTitle(SoundInfo soundInfo, int nextNumber, boolean forBackPack) {
// search for sounds with the same title
String newTitle = "";
List<SoundInfo> soundInfoList;
if (forBackPack) {
soundInfoList = BackPackListManager.getInstance().getAllBackPackedSounds();
} else {
soundInfoList = ProjectManager.getInstance().getCurrentSprite().getSoundList();
}
if (nextNumber == 0) {
if (soundInfo != null) {
newTitle = soundInfo.getTitle();
}
} else {
if (soundInfo != null) {
newTitle = soundInfo.getTitle() + "_" + nextNumber;
}
}
for (SoundInfo soundInfoFromList : soundInfoList) {
if (soundInfoFromList.getTitle().equals(newTitle)) {
return searchForNonExistingSoundTitle(soundInfo, ++nextNumber, forBackPack);
}
}
return newTitle;
}
public static Pixmap getPixmapFromFile(File imageFile) {
Pixmap pixmap;
try {
GdxNativesLoader.load();
pixmap = new Pixmap(new FileHandle(imageFile));
} catch (GdxRuntimeException e) {
return null;
} catch (Exception e1) {
return null;
}
return pixmap;
}
public static void rewriteImageFileForStage(Context context, File lookFile) throws IOException {
// if pixmap cannot be created, image would throw an Exception in stage
// so has to be loaded again with other Config
Pixmap pixmap;
pixmap = Utils.getPixmapFromFile(lookFile);
if (pixmap == null) {
ImageEditing.overwriteImageFileWithNewBitmap(lookFile);
pixmap = Utils.getPixmapFromFile(lookFile);
if (pixmap == null) {
Log.e(TAG, "error_load_image rewriteImageFileForStage");
Utils.showErrorDialog(context, R.string.error_load_image);
StorageHandler.getInstance().deleteFile(lookFile.getAbsolutePath(), false);
throw new IOException("Pixmap could not be fixed");
}
}
}
public static String getUniqueProjectName() {
String projectName = "project_" + System.currentTimeMillis();
while (StorageHandler.getInstance().projectExists(projectName)) {
projectName = "project_" + System.currentTimeMillis();
}
return projectName;
}
public static boolean isStandardScene(Project project, String sceneName, Context context) {
try {
Project standardProject = DefaultProjectHandler.createAndSaveDefaultProject(getUniqueProjectName(),
context);
Scene standardScene = standardProject.getDefaultScene();
ProjectManager.getInstance().deleteCurrentProject(null);
ProjectManager.getInstance().setProject(project);
ProjectManager.getInstance().saveProject(context);
Scene sceneToCheck = ProjectManager.getInstance().getCurrentProject().getSceneByName(sceneName);
if (sceneToCheck == null) {
Log.e(TAG, "isStandardScene: scene not found");
return false;
}
boolean result = true;
for (int i = 0; i < standardScene.getSpriteList().size(); i++) {
Sprite standardSprite = standardScene.getSpriteList().get(i);
Sprite spriteToCheck = sceneToCheck.getSpriteList().get(i);
for (int t = 0; t < standardSprite.getLookDataList().size(); t++) {
LookData standardLook = standardSprite.getLookDataList().get(t);
LookData lookToCheck = spriteToCheck.getLookDataList().get(t);
result &= standardLook.equals(lookToCheck);
if (!result) {
Log.e(TAG, "isStandardScene: " + standardLook.getLookName() + " was not the same as "
+ lookToCheck.getLookName());
return false;
}
}
for (int t = 0; t < standardSprite.getSoundList().size(); t++) {
SoundInfo standardSound = standardSprite.getSoundList().get(t);
SoundInfo soundToCheck = spriteToCheck.getSoundList().get(t);
result &= standardSound.equals(soundToCheck);
if (!result) {
Log.e(TAG, "isStandardScene: " + standardSound.getTitle() + " was not the same as "
+ standardSound.getTitle());
return false;
}
}
for (int t = 0; t < standardSprite.getListWithAllBricks().size(); t++) {
Brick standardBrick = standardSprite.getListWithAllBricks().get(t);
Brick brickToCheck = spriteToCheck.getListWithAllBricks().get(t);
result &= standardBrick.getClass().toString().equals(brickToCheck.getClass().toString());
if (!result) {
Log.e(TAG, "isStandardScene: " + standardBrick.getClass().toString() + " was not the same as "
+ brickToCheck.getClass().toString());
return false;
}
}
result &= standardSprite.equals(spriteToCheck);
if (!result) {
Log.e(TAG, "isStandardScene: " + standardSprite.getName() + " was not the same as "
+ spriteToCheck.getName());
return false;
}
}
return result;
} catch (Exception e) {
Log.e(TAG, "Exception: isStandardScene: ", e);
return false;
}
}
public static boolean isStandardProject(Project projectToCheck, Context context) {
try {
Project standardProject = DefaultProjectHandler.createAndSaveDefaultProject(getUniqueProjectName(),
context);
String standardProjectXMLString = StorageHandler.getInstance().getXMLStringOfAProject(standardProject);
int start = standardProjectXMLString.indexOf("<scenes>");
int end = standardProjectXMLString.indexOf("</scenes>");
String standardProjectSpriteList = standardProjectXMLString.substring(start, end);
ProjectManager.getInstance().deleteCurrentProject(null);
ProjectManager.getInstance().setProject(projectToCheck);
ProjectManager.getInstance().saveProject(context);
String projectToCheckXMLString = StorageHandler.getInstance().getXMLStringOfAProject(projectToCheck);
start = projectToCheckXMLString.indexOf("<scenes>");
end = projectToCheckXMLString.indexOf("</scenes>");
String projectToCheckStringList = projectToCheckXMLString.substring(start, end);
return standardProjectSpriteList.contentEquals(projectToCheckStringList);
} catch (IllegalArgumentException illegalArgumentException) {
Log.e(TAG, Log.getStackTraceString(illegalArgumentException));
} catch (IOException ioException) {
Log.e(TAG, Log.getStackTraceString(ioException));
}
return true;
}
public static int convertDoubleToPluralInteger(double value) {
double absoluteValue = Math.abs(value);
if (absoluteValue > 2.5) {
return (int) Math.round(absoluteValue);
} else {
if (absoluteValue == 0.0 || absoluteValue == 1.0 || absoluteValue == 2.0) {
return (int) absoluteValue;
} else {
// Random Number to get into the "other" keyword for values like 0.99 or 2.001 seconds or degrees
// in hopefully all possible languages
return TRANSLATION_PLURAL_OTHER_INTEGER;
}
}
}
public static boolean checkIfProjectExistsOrIsDownloadingIgnoreCase(String programName) {
if (DownloadUtil.getInstance().isProgramNameInDownloadQueueIgnoreCase(programName)) {
return true;
}
File projectDirectory = new File(Utils.buildProjectPath(programName));
return projectDirectory.exists();
}
public static boolean checkIfLookExists(String name) {
for (LookData lookData : ProjectManager.getInstance().getCurrentSprite().getLookDataList()) {
if (lookData.getLookName().compareTo(name) == 0) {
return true;
}
}
return false;
}
public static boolean checkIfSoundExists(String name) {
for (SoundInfo soundInfo : ProjectManager.getInstance().getCurrentSprite().getSoundList()) {
if (soundInfo.getTitle().compareTo(name) == 0) {
return true;
}
}
return false;
}
public static void invalidateLoginTokenIfUserRestricted(Context context) {
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
if (sharedPreferences.getBoolean(Constants.RESTRICTED_USER, false)) {
logoutUser(context);
}
}
@SuppressWarnings("unused")
public static void logoutUser(Context context) {
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
String userName = sharedPreferences.getString(Constants.USERNAME, Constants.NO_USERNAME);
LogoutTask logoutTask = new LogoutTask(context, userName);
logoutTask.execute();
sharedPreferences.edit().putString(Constants.TOKEN, Constants.NO_TOKEN).commit();
sharedPreferences.edit().putString(Constants.USERNAME, Constants.NO_USERNAME).commit();
sharedPreferences.edit().putBoolean(Constants.FACEBOOK_TOKEN_REFRESH_NEEDED, false).commit();
sharedPreferences.edit().putString(Constants.FACEBOOK_EMAIL, Constants.NO_FACEBOOK_EMAIL).commit();
sharedPreferences.edit().putString(Constants.FACEBOOK_USERNAME, Constants.NO_FACEBOOK_USERNAME).commit();
sharedPreferences.edit().putString(Constants.FACEBOOK_ID, Constants.NO_FACEBOOK_ID).commit();
sharedPreferences.edit().putString(Constants.FACEBOOK_LOCALE, Constants.NO_FACEBOOK_LOCALE).commit();
AccessToken.setCurrentAccessToken(null);
sharedPreferences.edit().putString(Constants.GOOGLE_EXCHANGE_CODE, Constants.NO_GOOGLE_EXCHANGE_CODE).commit();
sharedPreferences.edit().putString(Constants.GOOGLE_EMAIL, Constants.NO_GOOGLE_EMAIL).commit();
sharedPreferences.edit().putString(Constants.GOOGLE_USERNAME, Constants.NO_GOOGLE_USERNAME).commit();
sharedPreferences.edit().putString(Constants.GOOGLE_ID, Constants.NO_GOOGLE_ID).commit();
sharedPreferences.edit().putString(Constants.GOOGLE_LOCALE, Constants.NO_GOOGLE_LOCALE).commit();
sharedPreferences.edit().putString(Constants.GOOGLE_ID_TOKEN, Constants.NO_GOOGLE_ID_TOKEN).commit();
WebViewActivity.clearCookies(context);
ToastUtil.showSuccess(context, R.string.logout_successful);
}
public static boolean isUserLoggedIn(Context context) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
String token = preferences.getString(Constants.TOKEN, Constants.NO_TOKEN);
boolean tokenValid = !(token.equals(Constants.NO_TOKEN) || token.length() != ServerCalls.TOKEN_LENGTH
|| token.equals(ServerCalls.TOKEN_CODE_INVALID));
return tokenValid;
}
public static String getNumberStringForBricks(float value) {
return (int) value == value ? "" + (int) value : "" + value;
}
public static <T> List<T> distinctListByClassOfObjects(List<T> listToDistinct) {
Map<Class, T> uniqueMap = new HashMap<>();
for (T objectInstance : listToDistinct) {
uniqueMap.put(objectInstance.getClass(), objectInstance);
}
return new ArrayList<>(uniqueMap.values());
}
public static int setBit(int number, int index, int value) {
if ((index >= 0) && (index < 32)) {
if (value == 0) {
return number & ~(1 << index);
} else {
return number | (1 << index);
}
}
return number;
}
public static int getBit(int number, int index) {
if ((index >= 0) && (index < 32)) {
return (number >> index) & 0x1;
}
return 0;
}
}