diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index 7dea86961..4cc1c0016 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -24,10 +24,10 @@ jobs: runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v2 - - name: Set up JDK 1.8 + - name: Set up JDK 11 uses: actions/setup-java@v1 with: - java-version: 1.8 + java-version: '11' - name: Install GNU gettext run: sudo apt install gettext - name: Build with Gradle @@ -35,10 +35,10 @@ jobs: - name: Save armeabi artifact uses: actions/upload-artifact@v2 with: - name: Minetest-armeabi-v7a.apk + name: MultiCraft-armeabi-v7a.apk path: build/android/app/build/outputs/apk/release/app-armeabi-v7a-release-unsigned.apk - name: Save arm64 artifact uses: actions/upload-artifact@v2 with: - name: Minetest-arm64-v8a.apk + name: MultiCraft-arm64-v8a.apk path: build/android/app/build/outputs/apk/release/app-arm64-v8a-release-unsigned.apk diff --git a/build/android/app/build.gradle b/build/android/app/build.gradle index 6f0ba6e21..1c4b83e07 100644 --- a/build/android/app/build.gradle +++ b/build/android/app/build.gradle @@ -1,16 +1,16 @@ apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' + android { - compileSdkVersion 29 - buildToolsVersion '30.0.3' - ndkVersion '22.0.7026061' + compileSdkVersion 31 + buildToolsVersion '31.0.0' + ndkVersion '23.1.7779620' defaultConfig { applicationId 'com.multicraft.game' - minSdkVersion 16 - //noinspection OldTargetApi - targetSdkVersion 29 + minSdkVersion 19 + targetSdkVersion 31 versionName "${versionMajor}.${versionMinor}.${versionPatch}" versionCode project.versionCode - multiDexEnabled true } // load properties @@ -55,6 +55,10 @@ android { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } + + buildFeatures { + viewBinding true + } } import com.android.build.OutputFile @@ -131,19 +135,9 @@ dependencies { implementation project(':native') /* Third-party libraries */ - implementation 'androidx.multidex:multidex:2.0.1' - implementation 'androidx.preference:preference:1.1.1' - implementation 'com.google.android.play:core:1.9.1' - implementation 'io.reactivex.rxjava2:rxjava:2.2.20' - implementation 'io.reactivex.rxjava2:rxandroid:2.1.1' - //noinspection GradleDependency - implementation 'com.squareup.okhttp3:okhttp:3.12.12' - //noinspection GradleDependency - implementation 'commons-io:commons-io:2.5' - implementation 'gun0912.ted:tedpermission-rx2:2.2.3' - implementation 'com.google.code.gson:gson:2.8.6' - - /* Analytics libraries */ - //noinspection GradleDynamicVersion - /*implementation 'com.bugsnag:bugsnag-android-core:5.+'*/ + implementation 'androidx.appcompat:appcompat:1.4.0' + implementation 'androidx.appcompat:appcompat-resources:1.4.0' + implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.0' + implementation 'androidx.work:work-runtime-ktx:2.7.1' + implementation 'com.google.android.material:material:1.4.0' } diff --git a/build/android/app/src/main/AndroidManifest.xml b/build/android/app/src/main/AndroidManifest.xml index 8b5e52a65..f10ffb170 100644 --- a/build/android/app/src/main/AndroidManifest.xml +++ b/build/android/app/src/main/AndroidManifest.xml @@ -4,12 +4,10 @@ package="com.multicraft.game" android:installLocation="auto"> - + - + @@ -59,6 +56,7 @@ - - diff --git a/build/android/app/src/main/java/com/bugsnag/android/Bugsnag.java b/build/android/app/src/main/java/com/bugsnag/android/Bugsnag.java deleted file mode 100644 index 698041ff3..000000000 --- a/build/android/app/src/main/java/com/bugsnag/android/Bugsnag.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.bugsnag.android; - -import android.app.Application; -import android.util.Log; - -public class Bugsnag { - public static void notify(Throwable e) { - Log.getStackTraceString(e); - } - - public static void leaveBreadcrumb(String s) { - Log.d("Bugsnag", s); - } - - public static void start(Application application) { - Log.d("Bugsnag", "Bugsnag initialized"); - } -} diff --git a/build/android/app/src/main/java/com/multicraft/game/CustomEditText.java b/build/android/app/src/main/java/com/multicraft/game/CustomEditText.kt similarity index 50% rename from build/android/app/src/main/java/com/multicraft/game/CustomEditText.java rename to build/android/app/src/main/java/com/multicraft/game/CustomEditText.kt index c32d68325..a1d2c2729 100644 --- a/build/android/app/src/main/java/com/multicraft/game/CustomEditText.java +++ b/build/android/app/src/main/java/com/multicraft/game/CustomEditText.kt @@ -1,7 +1,7 @@ /* MultiCraft -Copyright (C) 2014-2020 MoNTE48, Maksim Gamarnik -Copyright (C) 2014-2020 ubulem, Bektur Mambetov +Copyright (C) 2014-2021 MoNTE48, Maksim Gamarnik +Copyright (C) 2014-2021 ubulem, Bektur Mambetov This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -18,28 +18,20 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -package com.multicraft.game; +package com.multicraft.game -import android.content.Context; -import android.view.KeyEvent; -import android.view.inputmethod.InputMethodManager; +import android.content.Context +import android.util.AttributeSet +import android.view.KeyEvent +import android.view.inputmethod.InputMethodManager -import androidx.appcompat.widget.AppCompatEditText; - -import java.util.Objects; - -public class CustomEditText extends AppCompatEditText { - public CustomEditText(Context context) { - super(context); - } - - @Override - public boolean onKeyPreIme(int keyCode, KeyEvent event) { +class CustomEditText constructor(context: Context, attrs: AttributeSet) : + com.google.android.material.textfield.TextInputEditText(context, attrs) { + override fun onKeyPreIme(keyCode: Int, event: KeyEvent): Boolean { if (keyCode == KeyEvent.KEYCODE_BACK) { - InputMethodManager mgr = (InputMethodManager) - getContext().getSystemService(Context.INPUT_METHOD_SERVICE); - Objects.requireNonNull(mgr).hideSoftInputFromWindow(this.getWindowToken(), 0); + val mgr = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager + mgr.hideSoftInputFromWindow(this.windowToken, 0) } - return false; + return false } } diff --git a/build/android/app/src/main/java/com/multicraft/game/GameActivity.java b/build/android/app/src/main/java/com/multicraft/game/GameActivity.java index 923cd6214..01c4b0b56 100644 --- a/build/android/app/src/main/java/com/multicraft/game/GameActivity.java +++ b/build/android/app/src/main/java/com/multicraft/game/GameActivity.java @@ -1,7 +1,7 @@ /* MultiCraft -Copyright (C) 2014-2020 MoNTE48, Maksim Gamarnik -Copyright (C) 2014-2020 ubulem, Bektur Mambetov +Copyright (C) 2014-2021 MoNTE48, Maksim Gamarnik +Copyright (C) 2014-2021 ubulem, Bektur Mambetov This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -20,72 +20,43 @@ with this program; if not, write to the Free Software Foundation, Inc., package com.multicraft.game; -import android.app.ActivityManager; +import static android.content.res.Configuration.HARDKEYBOARDHIDDEN_NO; +import static android.text.InputType.TYPE_CLASS_TEXT; +import static android.text.InputType.TYPE_TEXT_FLAG_MULTI_LINE; +import static android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD; +import static com.multicraft.game.helpers.Utilities.finishApp; +import static com.multicraft.game.helpers.Utilities.getTotalMem; +import static com.multicraft.game.helpers.Utilities.makeFullScreen; + import android.app.NativeActivity; -import android.content.Context; +import android.content.Intent; import android.content.res.Configuration; -import android.content.res.Resources; +import android.net.Uri; import android.os.Bundle; -import android.text.InputType; -import android.text.method.LinkMovementMethod; -import android.view.Gravity; import android.view.KeyEvent; import android.view.WindowManager.LayoutParams; +import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; +import android.widget.Button; import android.widget.EditText; -import android.widget.TextView; import androidx.appcompat.app.AlertDialog; -import com.bugsnag.android.Bugsnag; -import com.multicraft.game.helpers.PreferencesHelper; -import com.multicraft.game.helpers.RateMeHelper; -import com.multicraft.game.helpers.Utilities; - -import org.json.JSONObject; -import org.json.JSONTokener; - -import io.reactivex.Completable; -import io.reactivex.Observable; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.disposables.Disposable; -import io.reactivex.schedulers.Schedulers; - -import static android.content.res.Configuration.KEYBOARD_QWERTY; -import static com.multicraft.game.helpers.AdManager.initAd; -import static com.multicraft.game.helpers.AdManager.setAdsCallback; -import static com.multicraft.game.helpers.AdManager.startAd; -import static com.multicraft.game.helpers.AdManager.stopAd; -import static com.multicraft.game.helpers.Constants.versionCode; -import static com.multicraft.game.helpers.PreferencesHelper.IS_ASK_CONSENT; -import static com.multicraft.game.helpers.PreferencesHelper.TAG_EXIT_GAME_COUNT; -import static com.multicraft.game.helpers.PreferencesHelper.TAG_LAST_RATE_VERSION_CODE; -import static com.multicraft.game.helpers.PreferencesHelper.getInstance; -import static com.multicraft.game.helpers.Utilities.getIcon; -import static com.multicraft.game.helpers.Utilities.makeFullScreen; +import com.multicraft.game.databinding.InputTextBinding; public class GameActivity extends NativeActivity { + public static boolean isMultiPlayer; + static { try { System.loadLibrary("MultiCraft"); - } catch (UnsatisfiedLinkError | OutOfMemoryError e) { - Bugsnag.notify(e); - System.exit(0); - } catch (IllegalArgumentException i) { - Bugsnag.notify(i); - System.exit(0); - } catch (Error | Exception e) { - Bugsnag.notify(e); + } catch (UnsatisfiedLinkError e) { System.exit(0); } } private int messageReturnCode = -1; private String messageReturnValue = ""; - private int height, width; - private boolean consent, isMultiPlayer; - private PreferencesHelper pf; - private Disposable adInitSub, gdprSub; private boolean hasKeyboard; public static native void pauseGame(); @@ -95,118 +66,20 @@ public class GameActivity extends NativeActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - Bundle bundle = getIntent().getExtras(); - Resources resources = getResources(); - height = bundle != null ? bundle.getInt("height", 0) : resources.getDisplayMetrics().heightPixels; - width = bundle != null ? bundle.getInt("width", 0) : resources.getDisplayMetrics().widthPixels; getWindow().addFlags(LayoutParams.FLAG_KEEP_SCREEN_ON); - hasKeyboard = !(resources.getConfiguration().hardKeyboardHidden == KEYBOARD_QWERTY); + hasKeyboard = getResources().getConfiguration().hardKeyboardHidden == HARDKEYBOARDHIDDEN_NO; keyboardEvent(hasKeyboard); - pf = getInstance(this); - RateMeHelper.onStart(this); - askGdpr(); - } - - private void subscribeAds() { - if (pf.isAdsEnable()) { - adInitSub = Completable.fromAction(() -> initAd(this, consent)) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(() -> setAdsCallback(this)); - } - } - - // GDPR check - private void askGdpr() { - if (pf.isAskConsent()) - isGdprSubject(); - else { - consent = true; - subscribeAds(); - } - } - - private void isGdprSubject() { - gdprSub = Observable.fromCallable(() -> Utilities.getJson("http://adservice.google.com/getconfig/pubvendors")) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(result -> { - JSONObject json = new JSONObject(new JSONTokener(result)); - if (json.getBoolean("is_request_in_eea_or_unknown")) - showGdprDialog(); - else { - consent = true; - subscribeAds(); - } - }, - throwable -> { - consent = true; - subscribeAds(); - }); - } - - private void showGdprDialog() { - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setIcon(getIcon(this)); - builder.setTitle(getString(R.string.app_name)); - TextView tv = new TextView(this); - tv.setText(R.string.gdpr_main_text); - tv.setTypeface(null); - tv.setPadding(20, 0, 20, 0); - tv.setGravity(Gravity.CENTER); - tv.setMovementMethod(LinkMovementMethod.getInstance()); - builder.setView(tv); - builder.setPositiveButton(getString(R.string.gdpr_agree), (dialogInterface, i) -> { - dialogInterface.dismiss(); - pf.saveSettings(IS_ASK_CONSENT, false); - consent = true; - subscribeAds(); - }); - builder.setNegativeButton(getString(R.string.gdpr_disagree), (dialogInterface, i) -> { - dialogInterface.dismiss(); - pf.saveSettings(IS_ASK_CONSENT, false); - consent = false; - subscribeAds(); - }); - builder.setCancelable(false); - final AlertDialog dialog = builder.create(); - if (!isFinishing()) - dialog.show(); - } - - private void checkRateDialog() { - if (RateMeHelper.shouldShowRateDialog()) { - pf.saveSettings(TAG_LAST_RATE_VERSION_CODE, versionCode); - runOnUiThread(() -> RateMeHelper.showRateDialog(this)); - } } @Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); - if (hasFocus) - makeFullScreen(this); - } - - @Override - protected void onResume() { - super.onResume(); - makeFullScreen(this); + if (hasFocus) makeFullScreen(getWindow()); } @Override public void onBackPressed() { - } - - @Override - protected void onDestroy() { - super.onDestroy(); - if (adInitSub != null) adInitSub.dispose(); - if (gdprSub != null) gdprSub.dispose(); - } - - public void showDialog(String acceptButton, String hint, String current, int editType) { - runOnUiThread(() -> showDialogUI(hint, current, editType)); + // Ignore the back press so MultiCraft can handle it } @Override @@ -215,38 +88,51 @@ public class GameActivity extends NativeActivity { pauseGame(); } + @Override + protected void onResume() { + super.onResume(); + makeFullScreen(getWindow()); + } + @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); - boolean statusKeyboard = !(getResources().getConfiguration().hardKeyboardHidden == KEYBOARD_QWERTY); + boolean statusKeyboard = getResources().getConfiguration().hardKeyboardHidden == HARDKEYBOARDHIDDEN_NO; if (hasKeyboard != statusKeyboard) { hasKeyboard = statusKeyboard; keyboardEvent(hasKeyboard); } } + @SuppressWarnings("unused") + public void showDialog(String s, String hint, String current, int editType) { + runOnUiThread(() -> showDialogUI(hint, current, editType)); + } + private void showDialogUI(String hint, String current, int editType) { final AlertDialog.Builder builder = new AlertDialog.Builder(this); - EditText editText = new CustomEditText(this); - builder.setView(editText); + if (editType == 1) builder.setPositiveButton(R.string.done, null); + InputTextBinding binding = InputTextBinding.inflate(getLayoutInflater()); + String hintText = (!hint.isEmpty()) ? hint : getResources().getString( + (editType == 3) ? R.string.input_password : R.string.input_text); + binding.inputLayout.setHint(hintText); + builder.setView(binding.getRoot()); AlertDialog alertDialog = builder.create(); + EditText editText = binding.editText; editText.requestFocus(); - editText.setHint(hint); editText.setText(current); + if (editType != 1) editText.setImeOptions(EditorInfo.IME_FLAG_NO_FULLSCREEN); final InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE); - imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, - InputMethodManager.HIDE_IMPLICIT_ONLY); - if (editType == 1) - editText.setInputType(InputType.TYPE_CLASS_TEXT | - InputType.TYPE_TEXT_FLAG_MULTI_LINE); - else if (editType == 3) - editText.setInputType(InputType.TYPE_CLASS_TEXT | - InputType.TYPE_TEXT_VARIATION_PASSWORD); - else - editText.setInputType(InputType.TYPE_CLASS_TEXT); + int inputType = TYPE_CLASS_TEXT; + if (editType == 1) { + inputType = inputType | TYPE_TEXT_FLAG_MULTI_LINE; + editText.setMaxLines(8); + } else if (editType == 3) + inputType = inputType | TYPE_TEXT_VARIATION_PASSWORD; + editText.setInputType(inputType); editText.setSelection(editText.getText().length()); - editText.setOnKeyListener((view, KeyCode, event) -> { - if (KeyCode == KeyEvent.KEYCODE_ENTER) { + editText.setOnEditorActionListener((view, KeyCode, event) -> { + if (KeyCode == KeyEvent.KEYCODE_ENTER || KeyCode == KeyEvent.KEYCODE_ENDCALL) { imm.hideSoftInputFromWindow(editText.getWindowToken(), 0); messageReturnCode = 0; messageReturnValue = editText.getText().toString(); @@ -255,7 +141,18 @@ public class GameActivity extends NativeActivity { } return false; }); + // should be above `show()` + alertDialog.getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_STATE_VISIBLE); alertDialog.show(); + Button button = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE); + if (button != null) { + button.setOnClickListener(view -> { + imm.hideSoftInputFromWindow(editText.getWindowToken(), 0); + messageReturnCode = 0; + messageReturnValue = editText.getText().toString(); + alertDialog.dismiss(); + }); + } alertDialog.setOnCancelListener(dialog -> { getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); messageReturnValue = current; @@ -267,6 +164,7 @@ public class GameActivity extends NativeActivity { return messageReturnCode; } + @SuppressWarnings("unused") public String getDialogValue() { messageReturnCode = -1; return messageReturnValue; @@ -276,40 +174,32 @@ public class GameActivity extends NativeActivity { return getResources().getDisplayMetrics().density; } - public int getDisplayHeight() { - return height; - } - - public int getDisplayWidth() { - return width; - } - public float getMemoryMax() { - ActivityManager actManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); - ActivityManager.MemoryInfo memInfo = new ActivityManager.MemoryInfo(); - float memory = 1.0f; - if (actManager != null) { - actManager.getMemoryInfo(memInfo); - memory = memInfo.totalMem * 1.0f / (1024 * 1024 * 1024); - memory = Math.round(memory * 100) / 100.0f; - } - return memory; + return getTotalMem(this); } public void notifyServerConnect(boolean multiplayer) { isMultiPlayer = multiplayer; - if (isMultiPlayer) - stopAd(); } public void notifyExitGame() { - pf.saveSettings(TAG_EXIT_GAME_COUNT, pf.getExitGameCount() + 1); - if (!isFinishing()) { - if (isMultiPlayer) { - if (pf.isAdsEnable()) - startAd(this, false, true); - } else - checkRateDialog(); + } + + @SuppressWarnings("unused") + public void openURI(String uri) { + try { + Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(uri)); + startActivity(browserIntent); + } catch (Exception ignored) { } } + + @SuppressWarnings("unused") + public void finishGame(String exc) { + finishApp(true, this); + } + + @SuppressWarnings("unused") + public void handleError(String exc) { + } } diff --git a/build/android/app/src/main/java/com/multicraft/game/JsonSettings.java b/build/android/app/src/main/java/com/multicraft/game/JsonSettings.java deleted file mode 100644 index 6f407f0ef..000000000 --- a/build/android/app/src/main/java/com/multicraft/game/JsonSettings.java +++ /dev/null @@ -1,88 +0,0 @@ -/* -MultiCraft -Copyright (C) 2014-2020 MoNTE48, Maksim Gamarnik -Copyright (C) 2014-2020 ubulem, Bektur Mambetov - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation; either version 3.0 of the License, or -(at your option) any later version. - -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 Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ - -package com.multicraft.game; - -import com.google.gson.annotations.SerializedName; - -import java.util.List; - -public class JsonSettings { - @SerializedName(value = "version_code") - private int versionCode; - @SerializedName(value = "version_code_bad") - private List badVersionCodes; - @SerializedName(value = "rate_min_version_code") - private int rateMinVersionCode; - @SerializedName(value = "package") - private String packageName; - @SerializedName(value = "content_ru") - private String contentRus; - @SerializedName(value = "content_en") - private String contentEng; - @SerializedName(value = "ads_delay") - private int adsDelay; - @SerializedName(value = "ads_repeat") - private int adsRepeat; - @SerializedName(value = "ads_enable") - private boolean adsEnabled; - @SerializedName(value = "review_enable") - private boolean reviewEnabled; - - public int getVersionCode() { - return versionCode; - } - - public List getBadVersionCodes() { - return badVersionCodes; - } - - public int getRateMinVersionCode() { - return rateMinVersionCode; - } - - public String getPackageName() { - return packageName; - } - - public int getAdsDelay() { - return adsDelay; - } - - public int getAdsRepeat() { - return adsRepeat; - } - - public boolean isAdsEnabled() { - return adsEnabled; - } - - public boolean isReviewEnabled() { - return reviewEnabled; - } - - public String getContentRus() { - return contentRus; - } - - public String getContentEng() { - return contentEng; - } -} diff --git a/build/android/app/src/main/java/com/multicraft/game/MainActivity.java b/build/android/app/src/main/java/com/multicraft/game/MainActivity.java deleted file mode 100644 index 7c958e9eb..000000000 --- a/build/android/app/src/main/java/com/multicraft/game/MainActivity.java +++ /dev/null @@ -1,466 +0,0 @@ -/* -MultiCraft -Copyright (C) 2014-2020 MoNTE48, Maksim Gamarnik -Copyright (C) 2014-2020 ubulem, Bektur Mambetov - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation; either version 3.0 of the License, or -(at your option) any later version. - -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 Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ - -package com.multicraft.game; - -import android.app.AlarmManager; -import android.app.PendingIntent; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.IntentSender; -import android.graphics.BlendMode; -import android.graphics.BlendModeColorFilter; -import android.graphics.Color; -import android.graphics.Point; -import android.graphics.PorterDuff; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.LayerDrawable; -import android.os.Bundle; -import android.text.Html; -import android.view.Display; -import android.view.View; -import android.view.WindowManager.LayoutParams; -import android.widget.ProgressBar; -import android.widget.TextView; -import android.widget.Toast; - -import androidx.appcompat.app.AlertDialog; -import androidx.appcompat.app.AppCompatActivity; - -import com.bugsnag.android.Bugsnag; -import com.google.android.play.core.appupdate.AppUpdateInfo; -import com.google.android.play.core.appupdate.AppUpdateManager; -import com.google.android.play.core.appupdate.AppUpdateManagerFactory; -import com.google.android.play.core.install.InstallStateUpdatedListener; -import com.google.android.play.core.install.model.AppUpdateType; -import com.google.android.play.core.install.model.InstallStatus; -import com.google.android.play.core.install.model.UpdateAvailability; -import com.google.android.play.core.tasks.Task; -import com.multicraft.game.callbacks.CallBackListener; -import com.multicraft.game.helpers.PermissionHelper; -import com.multicraft.game.helpers.PreferencesHelper; -import com.multicraft.game.helpers.Utilities; -import com.multicraft.game.helpers.VersionManagerHelper; - -import org.apache.commons.io.FileUtils; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import io.reactivex.Completable; -import io.reactivex.Observable; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.disposables.Disposable; -import io.reactivex.schedulers.Schedulers; - -import static android.provider.Settings.ACTION_WIFI_SETTINGS; -import static android.provider.Settings.ACTION_WIRELESS_SETTINGS; -import static com.multicraft.game.UnzipService.ACTION_FAILURE; -import static com.multicraft.game.UnzipService.UNZIP_FAILURE; -import static com.multicraft.game.UnzipService.UNZIP_SUCCESS; -import static com.multicraft.game.helpers.ApiLevelHelper.isAndroidQ; -import static com.multicraft.game.helpers.ApiLevelHelper.isJellyBeanMR1; -import static com.multicraft.game.helpers.ApiLevelHelper.isLollipop; -import static com.multicraft.game.helpers.ApiLevelHelper.isOreo; -import static com.multicraft.game.helpers.Constants.FILES; -import static com.multicraft.game.helpers.Constants.GAMES; -import static com.multicraft.game.helpers.Constants.NO_SPACE_LEFT; -import static com.multicraft.game.helpers.Constants.REQUEST_CONNECTION; -import static com.multicraft.game.helpers.Constants.REQUEST_UPDATE; -import static com.multicraft.game.helpers.Constants.UPDATE_LINK; -import static com.multicraft.game.helpers.Constants.versionName; -import static com.multicraft.game.helpers.PreferencesHelper.TAG_BUILD_NUMBER; -import static com.multicraft.game.helpers.PreferencesHelper.TAG_LAUNCH_TIMES; -import static com.multicraft.game.helpers.Utilities.addShortcut; -import static com.multicraft.game.helpers.Utilities.deleteFiles; -import static com.multicraft.game.helpers.Utilities.getIcon; -import static com.multicraft.game.helpers.Utilities.getZipsFromAssets; -import static com.multicraft.game.helpers.Utilities.makeFullScreen; - -public class MainActivity extends AppCompatActivity implements CallBackListener { - private ArrayList zips; - private int height, width; - private ProgressBar mProgressBar, mProgressBarIndeterminate; - private TextView mLoading; - private VersionManagerHelper versionManagerHelper = null; - private PreferencesHelper pf; - private AppUpdateManager appUpdateManager; - final InstallStateUpdatedListener listener = state -> { - if (state.installStatus() == InstallStatus.DOWNLOADING) { - if (mProgressBar != null) { - int progress = (int) (state.bytesDownloaded() * 100 / state.totalBytesToDownload()); - showProgress(R.string.downloading, R.string.downloadingp, progress); - } - } else if (state.installStatus() == InstallStatus.DOWNLOADED) { - appUpdateManager.completeUpdate(); - } - }; - private Disposable connectionSub, versionManagerSub, cleanSub, copySub; - private final BroadcastReceiver myReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - int progress = 0; - if (intent != null) - progress = intent.getIntExtra(UnzipService.ACTION_PROGRESS, 0); - if (progress >= 0) { - if (mProgressBar != null) { - showProgress(R.string.loading, R.string.loadingp, progress); - } - } else if (progress == UNZIP_FAILURE) { - Toast.makeText(MainActivity.this, intent.getStringExtra(ACTION_FAILURE), Toast.LENGTH_LONG).show(); - showRestartDialog(""); - } else if (progress == UNZIP_SUCCESS) { - deleteFiles(Arrays.asList(FILES, GAMES), getCacheDir().toString()); - runGame(); - } - } - }; - private Task appUpdateInfoTask; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().addFlags(LayoutParams.FLAG_KEEP_SCREEN_ON); - setContentView(R.layout.activity_main); - pf = PreferencesHelper.getInstance(this); - appUpdateManager = AppUpdateManagerFactory.create(this); - appUpdateInfoTask = appUpdateManager.getAppUpdateInfo(); - IntentFilter filter = new IntentFilter(UnzipService.ACTION_UPDATE); - registerReceiver(myReceiver, filter); - if (!isTaskRoot()) { - finish(); - return; - } - addLaunchTimes(); - PermissionHelper permission = new PermissionHelper(this); - permission.setListener(this); - permission.askPermissions(); - } - - @Override - protected void onResume() { - super.onResume(); - makeFullScreen(this); - appUpdateInfoTask.addOnSuccessListener(appUpdateInfo -> { - if (appUpdateInfo.installStatus() == InstallStatus.DOWNLOADED) - appUpdateManager.completeUpdate(); - }); - } - - @Override - public void onBackPressed() { - // Prevent abrupt interruption when copy game files from assets - } - - @Override - protected void onDestroy() { - super.onDestroy(); - if (connectionSub != null) connectionSub.dispose(); - if (versionManagerSub != null) versionManagerSub.dispose(); - if (cleanSub != null) cleanSub.dispose(); - if (copySub != null) copySub.dispose(); - appUpdateManager.unregisterListener(listener); - unregisterReceiver(myReceiver); - } - - private void addLaunchTimes() { - pf.saveSettings(TAG_LAUNCH_TIMES, pf.getLaunchTimes() + 1); - } - - // interface - private void showProgress(int textMessage, int progressMessage, int progress) { - if (mProgressBar.getVisibility() == View.GONE) { - updateViews(textMessage, View.VISIBLE, View.GONE, View.VISIBLE); - mProgressBar.setProgress(0); - } else if (progress > 0) { - mLoading.setText(String.format(getResources().getString(progressMessage), progress)); - mProgressBar.setProgress(progress); - // colorize the progress bar - Drawable progressDrawable = ((LayerDrawable) - mProgressBar.getProgressDrawable()).getDrawable(1); - int color = Color.rgb(255 - progress * 2, progress * 2, 25); - if (isAndroidQ()) - progressDrawable.setColorFilter(new BlendModeColorFilter(color, BlendMode.SRC_IN)); - else - progressDrawable.setColorFilter(color, PorterDuff.Mode.SRC_IN); - } - } - - // real screen resolution - private void getDefaultResolution() { - Display display = getWindowManager().getDefaultDisplay(); - Point size = new Point(); - if (isJellyBeanMR1()) - display.getRealSize(size); - else - display.getSize(size); - height = Math.min(size.x, size.y); - width = Math.max(size.x, size.y); - } - - @Override - public void onWindowFocusChanged(boolean hasFocus) { - super.onWindowFocusChanged(hasFocus); - if (hasFocus) - makeFullScreen(this); - } - - private void init() { - mProgressBar = findViewById(R.id.PB1); - mProgressBarIndeterminate = findViewById(R.id.PB2); - mLoading = findViewById(R.id.tv_progress); - if (!pf.isCreateShortcut() && !isOreo()) - addShortcut(this); - checkAppVersion(); - } - - void showUpdateDialog() { - updateViews(R.string.loading, View.VISIBLE, View.VISIBLE, View.GONE); - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setIcon(getIcon(this)) - .setTitle(R.string.available) - .setMessage(Html.fromHtml( - versionManagerHelper.getMessage(), null, versionManagerHelper.getCustomTagHandler())) - .setPositiveButton(R.string.update, (dialogInterface, i) -> { - versionManagerHelper.updateNow(versionManagerHelper.getUpdateUrl()); - finish(); - }) - .setNeutralButton(R.string.later, (dialogInterface, i) -> { - versionManagerHelper.remindMeLater(); - startNative(); - }); - builder.setCancelable(false); - final AlertDialog dialog = builder.create(); - if (!isFinishing()) - dialog.show(); - } - - public void startUpdate() { - appUpdateInfoTask.addOnSuccessListener(appUpdateInfo -> { - if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE - && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)) { - try { - appUpdateManager.startUpdateFlowForResult( - appUpdateInfo, AppUpdateType.FLEXIBLE, this, REQUEST_UPDATE); - } catch (IntentSender.SendIntentException e) { - //Bugsnag.notify(e); - showUpdateDialog(); - } - } else { - showUpdateDialog(); - } - }); - appUpdateInfoTask.addOnFailureListener(e -> { - //Bugsnag.notify(e); - showUpdateDialog(); - }); - } - - private void checkUrlVersion() { - versionManagerHelper = new VersionManagerHelper(this); - if (versionManagerHelper.isCheckVersion()) - versionManagerSub = Observable.fromCallable(() -> Utilities.getJson(UPDATE_LINK)) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .timeout(3000, TimeUnit.MILLISECONDS) - .subscribe(result -> isShowDialog(versionManagerHelper.isShow(result)), - throwable -> runOnUiThread(() -> isShowDialog(false))); - else isShowDialog(false); - } - - private void runGame() { - pf.saveSettings(TAG_BUILD_NUMBER, versionName); - connectionSub = checkConnection(); - } - - private void startNative() { - getDefaultResolution(); - Intent intent = new Intent(this, GameActivity.class); - intent.putExtra("height", height); - intent.putExtra("width", width); - intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK); - startActivity(intent); - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - if (requestCode == REQUEST_CONNECTION) { - checkUrlVersion(); - } else if (requestCode == REQUEST_UPDATE) { - if (resultCode == RESULT_OK) - appUpdateManager.registerListener(listener); - else - startNative(); - } - } - - private void cleanUpOldFiles(boolean isAll) { - updateViews(R.string.preparing, View.VISIBLE, View.VISIBLE, View.GONE); - Completable delObs; - File externalStorage = getExternalFilesDir(null); - if (isAll) - delObs = Completable.fromAction(() -> deleteFiles(Collections.singletonList(externalStorage))); - else { - List filesList = Arrays.asList(new File(externalStorage, "cache"), - new File(getFilesDir(), "builtin"), - new File(getFilesDir(), "games"), - new File(externalStorage, "debug.txt")); - delObs = Completable.fromAction(() -> deleteFiles(filesList)); - } - cleanSub = delObs.subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(() -> startCopy(isAll)); - } - - private void checkAppVersion() { - String prefVersion; - try { - prefVersion = pf.getBuildNumber(); - } catch (ClassCastException e) { - prefVersion = "1"; - } - if (prefVersion.equals(versionName)) { - mProgressBarIndeterminate.setVisibility(View.VISIBLE); - runGame(); - } else { - cleanUpOldFiles(prefVersion.equals("0")); - } - } - - public void updateViews(int text, int textVisib, int progressIndetermVisib, int progressVisib) { - mLoading.setText(text); - mLoading.setVisibility(textVisib); - mProgressBarIndeterminate.setVisibility(progressIndetermVisib); - mProgressBar.setVisibility(progressVisib); - } - - public void isShowDialog(boolean flag) { - if (flag) { - if (isLollipop()) - startUpdate(); - else - showUpdateDialog(); - } else - startNative(); - } - - private Disposable checkConnection() { - return Observable.fromCallable(Utilities::isReachable) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .timeout(4000, TimeUnit.MILLISECONDS) - .subscribe(result -> { - if (result) checkUrlVersion(); - else showConnectionDialog(); - }, - throwable -> runOnUiThread(this::showConnectionDialog)); - } - - private void startCopy(boolean isAll) { - zips = getZipsFromAssets(this); - copySub = Completable.fromAction(() -> runOnUiThread(() -> copyAssets(zips))) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(() -> startUnzipService(zips)); - } - - private void copyAssets(ArrayList zips) { - for (String zipName : zips) { - try (InputStream in = getAssets().open("data/" + zipName)) { - FileUtils.copyInputStreamToFile(in, new File(getCacheDir(), zipName)); - } catch (IOException e) { - Bugsnag.leaveBreadcrumb("Failed to copy " + zipName); - if (e.getLocalizedMessage().contains(NO_SPACE_LEFT)) - showRestartDialog(NO_SPACE_LEFT); - else { - showRestartDialog(""); - Bugsnag.notify(e); - } - } - } - } - - private void startUnzipService(ArrayList file) { - Intent intent = new Intent(this, UnzipService.class); - intent.putStringArrayListExtra(UnzipService.EXTRA_KEY_IN_FILE, file); - startService(intent); - } - - private void showRestartDialog(final String source) { - String message; - if (NO_SPACE_LEFT.equals(source)) - message = getString(R.string.no_space); - else - message = getString(R.string.restart); - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setMessage(message) - .setPositiveButton(R.string.ok, (dialogInterface, i) -> restartApp()); - builder.setCancelable(false); - final AlertDialog dialog = builder.create(); - if (!isFinishing()) - dialog.show(); - } - - private void restartApp() { - Intent intent = new Intent(getApplicationContext(), MainActivity.class); - int mPendingIntentId = 1337; - AlarmManager mgr = (AlarmManager) getApplicationContext().getSystemService(Context.ALARM_SERVICE); - if (mgr != null) - mgr.set(AlarmManager.RTC, System.currentTimeMillis(), PendingIntent.getActivity( - getApplicationContext(), mPendingIntentId, intent, PendingIntent.FLAG_CANCEL_CURRENT)); - System.exit(0); - } - - @Override - public void onEvent(boolean isContinue) { - if (isFinishing()) return; - if (isContinue) init(); - else finish(); - } - - private void showConnectionDialog() { - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setMessage(R.string.conn_message) - .setPositiveButton(R.string.conn_wifi, (dialogInterface, i) -> startHandledActivity(new Intent(ACTION_WIFI_SETTINGS))) - .setNegativeButton(R.string.conn_mobile, (dialogInterface, i) -> startHandledActivity(new Intent(ACTION_WIRELESS_SETTINGS))) - .setNeutralButton(R.string.ignore, (dialogInterface, i) -> startNative()); - builder.setCancelable(false); - final AlertDialog dialog = builder.create(); - if (!isFinishing()) - dialog.show(); - } - - private void startHandledActivity(Intent intent) { - try { - startActivityForResult(intent, REQUEST_CONNECTION); - } catch (Exception e) { - startNative(); - } - } -} diff --git a/build/android/app/src/main/java/com/multicraft/game/MainActivity.kt b/build/android/app/src/main/java/com/multicraft/game/MainActivity.kt new file mode 100644 index 000000000..e45018b74 --- /dev/null +++ b/build/android/app/src/main/java/com/multicraft/game/MainActivity.kt @@ -0,0 +1,266 @@ +/* +MultiCraft +Copyright (C) 2014-2021 MoNTE48, Maksim Gamarnik +Copyright (C) 2014-2021 ubulem, Bektur Mambetov + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 3.0 of the License, or +(at your option) any later version. + +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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +package com.multicraft.game + +import android.content.DialogInterface +import android.content.Intent +import android.content.SharedPreferences +import android.graphics.Color +import android.graphics.drawable.LayerDrawable +import android.os.Bundle +import android.provider.Settings.ACTION_WIFI_SETTINGS +import android.provider.Settings.ACTION_WIRELESS_SETTINGS +import android.view.View +import android.view.WindowManager +import androidx.appcompat.app.AlertDialog +import androidx.appcompat.app.AppCompatActivity +import androidx.core.graphics.BlendModeColorFilterCompat +import androidx.core.graphics.BlendModeCompat +import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.lifecycleScope +import androidx.work.WorkInfo +import com.multicraft.game.databinding.ActivityMainBinding +import com.multicraft.game.helpers.Constants.NO_SPACE_LEFT +import com.multicraft.game.helpers.Constants.REQUEST_CONNECTION +import com.multicraft.game.helpers.PreferenceHelper +import com.multicraft.game.helpers.PreferenceHelper.TAG_BUILD_VER +import com.multicraft.game.helpers.PreferenceHelper.TAG_LAUNCH_TIMES +import com.multicraft.game.helpers.PreferenceHelper.TAG_SHORTCUT_EXIST +import com.multicraft.game.helpers.PreferenceHelper.getBoolValue +import com.multicraft.game.helpers.PreferenceHelper.getIntValue +import com.multicraft.game.helpers.PreferenceHelper.getStringValue +import com.multicraft.game.helpers.PreferenceHelper.set +import com.multicraft.game.helpers.Utilities.addShortcut +import com.multicraft.game.helpers.Utilities.copyInputStreamToFile +import com.multicraft.game.helpers.Utilities.finishApp +import com.multicraft.game.helpers.Utilities.getIcon +import com.multicraft.game.helpers.Utilities.isConnected +import com.multicraft.game.helpers.Utilities.makeFullScreen +import com.multicraft.game.workmanager.UnzipWorker.Companion.PROGRESS +import com.multicraft.game.workmanager.WorkerViewModel +import com.multicraft.game.workmanager.WorkerViewModelFactory +import kotlinx.coroutines.launch +import java.io.File +import java.io.IOException + +class MainActivity : AppCompatActivity() { + private lateinit var binding: ActivityMainBinding + private var externalStorage: File? = null + private val sep = File.separator + private lateinit var prefs: SharedPreferences + private val versionName = BuildConfig.VERSION_NAME + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) + binding = ActivityMainBinding.inflate(layoutInflater) + setContentView(binding.root) + prefs = PreferenceHelper.init(this) + var storageUnavailable = false + try { + externalStorage = getExternalFilesDir(null) + if (filesDir == null || cacheDir == null || externalStorage == null) + throw IOException("Bad disk space state") + } catch (e: IOException) { + storageUnavailable = true + showRestartDialog(e.message!!.contains(NO_SPACE_LEFT)) + } + if (storageUnavailable) return + lateInit() + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + @Suppress("DEPRECATION") + super.onActivityResult(requestCode, resultCode, data) + if (requestCode == REQUEST_CONNECTION) + checkAppVersion() + + } + + override fun onBackPressed() { + // Prevent abrupt interruption when copy game files from assets + } + + override fun onResume() { + super.onResume() + makeFullScreen(window) + } + + override fun onWindowFocusChanged(hasFocus: Boolean) { + super.onWindowFocusChanged(hasFocus) + if (hasFocus) makeFullScreen(window) + } + + private fun addLaunchTimes() { + val launchTimes = prefs.getIntValue(TAG_LAUNCH_TIMES) + 1 + prefs[TAG_LAUNCH_TIMES] = launchTimes + } + + // interface + private fun showProgress(textMessage: Int, progressMessage: Int, progress: Int) { + if (binding.progressBar.visibility == View.GONE) { + binding.tvProgress.setText(textMessage) + binding.progressCircle.visibility = View.GONE + binding.progressBar.visibility = View.VISIBLE + binding.progressBar.progress = 0 + } else if (progress > 0) { + binding.tvProgress.text = String.format(getString(progressMessage), progress) + binding.progressBar.progress = progress + // colorize the progress bar + val progressDrawable = + (binding.progressBar.progressDrawable as LayerDrawable).getDrawable(1) + val color = Color.rgb(255 - progress * 2, progress * 2, 25) + progressDrawable.colorFilter = + BlendModeColorFilterCompat.createBlendModeColorFilterCompat( + color, BlendModeCompat.SRC_IN + ) + } + } + + private fun lateInit() { + addLaunchTimes() + if (!prefs.getBoolValue(TAG_SHORTCUT_EXIST)) try { + addShortcut(this) + } catch (ignored: Exception) { + } + checkConnection() + } + + private fun startNative() { + val initLua = File(filesDir, "builtin${sep}mainmenu${sep}init.lua") + if (initLua.exists() && initLua.canRead()) { + val intent = Intent(this, GameActivity::class.java) + intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_CLEAR_TASK + startActivity(intent) + } else { + prefs[TAG_BUILD_VER] = "0" + showRestartDialog(false) + } + } + + private fun prepareToRun() { + binding.tvProgress.setText(R.string.preparing) + binding.progressCircle.visibility = View.VISIBLE + binding.progressBar.visibility = View.GONE + val filesList = listOf( + File(externalStorage, "builtin"), + File(externalStorage, "cache"), // ToDo: remove me! + File(externalStorage, "games${sep}default"), + File(externalStorage, "textures${sep}base"), + File(externalStorage, "debug.txt"), + File(filesDir, "builtin"), + File(filesDir, "games"), + File(filesDir, "textures${sep}base") + ) + val zips = assets.list("data")!!.toList() + lifecycleScope.launch { + filesList.forEach { it.deleteRecursively() } + zips.forEach { + try { + assets.open("data$sep$it").use { input -> + File(cacheDir, it).copyInputStreamToFile(input) + } + } catch (e: IOException) { + runOnUiThread { showRestartDialog(e.message!!.contains(NO_SPACE_LEFT)) } + return@forEach + } + } + startUnzipWorker(zips) + } + } + + private fun checkAppVersion() { + val prefVersion = prefs.getStringValue(TAG_BUILD_VER) + if (prefVersion == versionName) + startNative() + else + prepareToRun() + } + + // check connection available + private fun checkConnection() = lifecycleScope.launch { + val result = isConnected(this@MainActivity) + if (result) checkAppVersion() + else try { + showConnectionDialog() + } catch (e: Exception) { + checkAppVersion() + } + } + + private fun startUnzipWorker(file: List) { + val viewModelFactory = WorkerViewModelFactory(application, file.toTypedArray()) + val viewModel = ViewModelProvider(this, viewModelFactory).get(WorkerViewModel::class.java) + viewModel.unzippingWorkObserver + .observe(this, Observer { workInfo -> + if (workInfo == null) + return@Observer + val progress = workInfo.progress.getInt(PROGRESS, 0) + showProgress(R.string.loading, R.string.loadingp, progress) + + if (workInfo.state.isFinished) { + if (workInfo.state == WorkInfo.State.FAILED) { + showRestartDialog(false) + } else if (workInfo.state == WorkInfo.State.SUCCEEDED) { + prefs[TAG_BUILD_VER] = versionName + startNative() + } + } + }) + viewModel.startOneTimeWorkRequest() + } + + private fun showRestartDialog(space: Boolean) { + val message = if (space) getString(R.string.no_space) else getString(R.string.restart) + val builder = AlertDialog.Builder(this) + builder.setMessage(message) + .setPositiveButton(R.string.ok) { _, _ -> finishApp(!space, this) } + .setCancelable(false) + val dialog = builder.create() + makeFullScreen(dialog.window!!) + if (!isFinishing) dialog.show() + } + + // connection dialog + private fun showConnectionDialog() { + val builder = AlertDialog.Builder(this) + builder.setIcon(getIcon(this)) + .setTitle(R.string.conn_title) + .setMessage(R.string.conn_message) + .setPositiveButton(R.string.conn_wifi) { _, _ -> + @Suppress("DEPRECATION") + startActivityForResult(Intent(ACTION_WIFI_SETTINGS), REQUEST_CONNECTION) + } + .setNegativeButton(R.string.conn_mobile) { _, _ -> + @Suppress("DEPRECATION") + startActivityForResult(Intent(ACTION_WIRELESS_SETTINGS), REQUEST_CONNECTION) + } + .setNeutralButton(R.string.ignore) { _, _ -> checkAppVersion() } + .setCancelable(false) + val dialog = builder.create() + makeFullScreen(dialog.window!!) + if (!isFinishing) { + dialog.show() + dialog.getButton(DialogInterface.BUTTON_NEUTRAL)?.setTextColor(Color.RED) + } + } +} diff --git a/build/android/app/src/main/java/com/multicraft/game/UnzipService.java b/build/android/app/src/main/java/com/multicraft/game/UnzipService.java deleted file mode 100644 index 9362e68b5..000000000 --- a/build/android/app/src/main/java/com/multicraft/game/UnzipService.java +++ /dev/null @@ -1,173 +0,0 @@ -/* -MultiCraft -Copyright (C) 2014-2020 MoNTE48, Maksim Gamarnik -Copyright (C) 2014-2020 ubulem, Bektur Mambetov - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation; either version 3.0 of the License, or -(at your option) any later version. - -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 Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ - -package com.multicraft.game; - -import android.app.IntentService; -import android.app.Notification; -import android.app.NotificationChannel; -import android.app.NotificationManager; -import android.content.Context; -import android.content.Intent; - -import com.bugsnag.android.Bugsnag; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; -import java.util.zip.ZipInputStream; - -import static com.multicraft.game.helpers.ApiLevelHelper.isOreo; -import static com.multicraft.game.helpers.Constants.NO_SPACE_LEFT; -import static com.multicraft.game.helpers.Utilities.getLocationByZip; -import static com.multicraft.game.helpers.Utilities.getZipsFromAssets; - -public class UnzipService extends IntentService { - public static final String ACTION_UPDATE = "com.multicraft.game.UPDATE"; - public static final String EXTRA_KEY_IN_FILE = "com.multicraft.game.file"; - public static final String ACTION_PROGRESS = "com.multicraft.game.progress"; - public static final String ACTION_FAILURE = "com.multicraft.game.failure"; - public static final int UNZIP_SUCCESS = -1; - public static final int UNZIP_FAILURE = -2; - private final int id = 1; - private NotificationManager mNotifyManager; - private String failureMessage; - private boolean isSuccess = true; - - public UnzipService() { - super("com.multicraft.game.UnzipService"); - } - - private void isDir(String dir, String unzipLocation) { - File f = new File(unzipLocation, dir); - if (!f.mkdirs() && !f.isDirectory()) - Bugsnag.leaveBreadcrumb(f + " (destination) folder was not created"); - } - - @Override - protected void onHandleIntent(Intent intent) { - createNotification(); - unzip(intent); - } - - private void createNotification() { - // There are hardcoding only for show it's just strings - String name = "com.multicraft.game"; - String channelId = "MultiCraft channel"; // The user-visible name of the channel. - String description = "notifications from MultiCraft"; // The user-visible description of the channel. - Notification.Builder builder; - if (mNotifyManager == null) - mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - if (isOreo()) { - int importance = NotificationManager.IMPORTANCE_LOW; - NotificationChannel mChannel = null; - if (mNotifyManager != null) - mChannel = mNotifyManager.getNotificationChannel(channelId); - if (mChannel == null) { - mChannel = new NotificationChannel(channelId, name, importance); - mChannel.setDescription(description); - // Configure the notification channel, NO SOUND - mChannel.setSound(null, null); - mChannel.enableLights(false); - mChannel.enableVibration(false); - mNotifyManager.createNotificationChannel(mChannel); - } - builder = new Notification.Builder(this, channelId); - } else - builder = new Notification.Builder(this); - builder.setContentTitle(getString(R.string.notification_title)) - .setContentText(getString(R.string.notification_description)) - .setSmallIcon(R.drawable.update); - mNotifyManager.notify(id, builder.build()); - } - - private void unzip(Intent intent) { - ArrayList zips; - if (intent != null) - zips = intent.getStringArrayListExtra(EXTRA_KEY_IN_FILE); - else - zips = getZipsFromAssets(this); - String cacheDir = getCacheDir().toString(); - int per = 0; - int size = getSummarySize(zips, cacheDir); - byte[] readBuffer = new byte[8192]; - for (String zip : zips) { - String location = getLocationByZip(this, zip); - File zipFile = new File(cacheDir, zip); - ZipEntry ze; - int readLen; - try (FileInputStream fileInputStream = new FileInputStream(zipFile); - ZipInputStream zipInputStream = new ZipInputStream(fileInputStream)) { - while ((ze = zipInputStream.getNextEntry()) != null) { - String fileName = ze.getName(); - if (ze.isDirectory()) { - ++per; - isDir(fileName, location); - } else { - File extractedFile = new File(location, fileName); - publishProgress(100 * ++per / size); - try (OutputStream outputStream = new FileOutputStream(extractedFile)) { - while ((readLen = zipInputStream.read(readBuffer)) != -1) { - outputStream.write(readBuffer, 0, readLen); - } - } - } - } - } catch (IOException e) { - if (!e.getLocalizedMessage().contains(NO_SPACE_LEFT)) - Bugsnag.notify(e); - failureMessage = e.getLocalizedMessage(); - isSuccess = false; - } - } - } - - private void publishProgress(int progress) { - Intent intentUpdate = new Intent(ACTION_UPDATE); - intentUpdate.putExtra(ACTION_PROGRESS, progress); - if (!isSuccess) intentUpdate.putExtra(ACTION_FAILURE, failureMessage); - sendBroadcast(intentUpdate); - } - - private int getSummarySize(ArrayList zips, String path) { - int size = 0; - for (String z : zips) { - try { - ZipFile zipFile = new ZipFile(new File(path, z)); - size += zipFile.size(); - } catch (IOException e) { - Bugsnag.notify(e); - } - } - return size; - } - - @Override - public void onDestroy() { - super.onDestroy(); - mNotifyManager.cancel(id); - publishProgress(isSuccess ? UNZIP_SUCCESS : UNZIP_FAILURE); - } -} diff --git a/build/android/app/src/main/java/com/multicraft/game/helpers/ApiLevelHelper.java b/build/android/app/src/main/java/com/multicraft/game/helpers/ApiLevelHelper.java deleted file mode 100644 index e97ff4da0..000000000 --- a/build/android/app/src/main/java/com/multicraft/game/helpers/ApiLevelHelper.java +++ /dev/null @@ -1,54 +0,0 @@ -/* -MultiCraft -Copyright (C) 2014-2020 MoNTE48, Maksim Gamarnik -Copyright (C) 2014-2020 ubulem, Bektur Mambetov - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation; either version 3.0 of the License, or -(at your option) any later version. - -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 Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ - -package com.multicraft.game.helpers; - -import static android.os.Build.VERSION.SDK_INT; -import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1; -import static android.os.Build.VERSION_CODES.KITKAT; -import static android.os.Build.VERSION_CODES.LOLLIPOP; -import static android.os.Build.VERSION_CODES.O; -import static android.os.Build.VERSION_CODES.Q; - -public class ApiLevelHelper { - public static boolean isGreaterOrEqual(int versionCode) { - return SDK_INT >= versionCode; - } - - public static boolean isJellyBeanMR1() { - return isGreaterOrEqual(JELLY_BEAN_MR1); - } - - public static boolean isKitkat() { - return isGreaterOrEqual(KITKAT); - } - - public static boolean isLollipop() { - return isGreaterOrEqual(LOLLIPOP); - } - - public static boolean isOreo() { - return isGreaterOrEqual(O); - } - - public static boolean isAndroidQ() { - return isGreaterOrEqual(Q); - } -} diff --git a/build/android/app/src/main/java/com/multicraft/game/MyApplication.java b/build/android/app/src/main/java/com/multicraft/game/helpers/ApiLevelHelper.kt similarity index 61% rename from build/android/app/src/main/java/com/multicraft/game/MyApplication.java rename to build/android/app/src/main/java/com/multicraft/game/helpers/ApiLevelHelper.kt index 3972f78da..468932ee8 100644 --- a/build/android/app/src/main/java/com/multicraft/game/MyApplication.java +++ b/build/android/app/src/main/java/com/multicraft/game/helpers/ApiLevelHelper.kt @@ -1,7 +1,7 @@ /* MultiCraft -Copyright (C) 2014-2020 MoNTE48, Maksim Gamarnik -Copyright (C) 2014-2020 ubulem, Bektur Mambetov +Copyright (C) 2014-2021 MoNTE48, Maksim Gamarnik +Copyright (C) 2014-2021 ubulem, Bektur Mambetov This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -18,16 +18,17 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -package com.multicraft.game; +package com.multicraft.game.helpers -import androidx.multidex.MultiDexApplication; +import android.os.Build.VERSION.SDK_INT +import android.os.Build.VERSION_CODES.* -import com.bugsnag.android.Bugsnag; +object ApiLevelHelper { + private fun isGreaterOrEqual(versionCode: Int) = SDK_INT >= versionCode -public class MyApplication extends MultiDexApplication { - @Override - public void onCreate() { - super.onCreate(); - Bugsnag.start(this); - } + fun isLollipop() = isGreaterOrEqual(LOLLIPOP) + + fun isMarshmallow() = isGreaterOrEqual(M) + + fun isOreo() = isGreaterOrEqual(O) } diff --git a/build/android/app/src/main/java/com/multicraft/game/helpers/Constants.java b/build/android/app/src/main/java/com/multicraft/game/helpers/Constants.java deleted file mode 100644 index 688b4a0a6..000000000 --- a/build/android/app/src/main/java/com/multicraft/game/helpers/Constants.java +++ /dev/null @@ -1,36 +0,0 @@ -/* -MultiCraft -Copyright (C) 2014-2020 MoNTE48, Maksim Gamarnik -Copyright (C) 2014-2020 ubulem, Bektur Mambetov - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation; either version 3.0 of the License, or -(at your option) any later version. - -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 Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ - -package com.multicraft.game.helpers; - -import com.multicraft.game.BuildConfig; - -public class Constants { - public static final int REQUEST_CONNECTION = 104; - public static final int REQUEST_UPDATE = 102; - public static final int SESSION_COUNT = 5; - public static final String NO_SPACE_LEFT = "ENOSPC"; - public static final String FILES = "Files.zip"; - public static final String GAMES = "games.zip"; - public static final int versionCode = BuildConfig.VERSION_CODE; - public static final String versionName = BuildConfig.VERSION_NAME; - public static final String appPackage = BuildConfig.APPLICATION_ID; - public static final String UPDATE_LINK = "http://updates.multicraft.world/Android.json"; -} diff --git a/build/android/app/src/main/java/com/multicraft/game/callbacks/CallBackListener.java b/build/android/app/src/main/java/com/multicraft/game/helpers/Constants.kt similarity index 74% rename from build/android/app/src/main/java/com/multicraft/game/callbacks/CallBackListener.java rename to build/android/app/src/main/java/com/multicraft/game/helpers/Constants.kt index 1a7bd889a..ac918cf50 100644 --- a/build/android/app/src/main/java/com/multicraft/game/callbacks/CallBackListener.java +++ b/build/android/app/src/main/java/com/multicraft/game/helpers/Constants.kt @@ -1,7 +1,7 @@ /* MultiCraft -Copyright (C) 2014-2020 MoNTE48, Maksim Gamarnik -Copyright (C) 2014-2020 ubulem, Bektur Mambetov +Copyright (C) 2014-2021 MoNTE48, Maksim Gamarnik +Copyright (C) 2014-2021 ubulem, Bektur Mambetov This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -18,8 +18,9 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -package com.multicraft.game.callbacks; +package com.multicraft.game.helpers -public interface CallBackListener { - void onEvent(boolean isContinue); +object Constants { + const val REQUEST_CONNECTION = 104 + const val NO_SPACE_LEFT = "ENOSPC" } diff --git a/build/android/app/src/main/java/com/multicraft/game/helpers/PermissionHelper.java b/build/android/app/src/main/java/com/multicraft/game/helpers/PermissionHelper.java deleted file mode 100644 index e850294f3..000000000 --- a/build/android/app/src/main/java/com/multicraft/game/helpers/PermissionHelper.java +++ /dev/null @@ -1,136 +0,0 @@ -/* -MultiCraft -Copyright (C) 2014-2020 MoNTE48, Maksim Gamarnik -Copyright (C) 2014-2020 ubulem, Bektur Mambetov - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation; either version 3.0 of the License, or -(at your option) any later version. - -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 Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ - -package com.multicraft.game.helpers; - -import android.annotation.SuppressLint; - -import androidx.appcompat.app.AppCompatActivity; - -import com.multicraft.game.R; -import com.multicraft.game.callbacks.CallBackListener; -import com.tedpark.tedpermission.rx2.TedRx2Permission; - -import static android.Manifest.permission.ACCESS_FINE_LOCATION; -import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; - -public class PermissionHelper { - private final AppCompatActivity activity; - private CallBackListener listener; - private PreferencesHelper pf; - - public PermissionHelper(AppCompatActivity activity) { - this.activity = activity; - } - - public void setListener(CallBackListener listener) { - this.listener = listener; - } - - public void askPermissions() { - pf = PreferencesHelper.getInstance(activity); - askStoragePermissions(); - } - - // permission block - @SuppressLint("CheckResult") - private void askStoragePermissions() { - TedRx2Permission.with(activity) - .setPermissions(WRITE_EXTERNAL_STORAGE) - .request() - .subscribe(tedPermissionResult -> { - if (tedPermissionResult.isGranted()) { - if (pf.getLaunchTimes() % 3 == 1) - askLocationPermissions(); - else listener.onEvent(true); - } else { - if (TedRx2Permission.canRequestPermission(activity, WRITE_EXTERNAL_STORAGE)) - askStorageRationalePermissions(); - else askStorageWhenDoNotShow(); - } - }); - } - - // storage permissions block - @SuppressLint("CheckResult") - private void askStorageRationalePermissions() { - TedRx2Permission.with(activity) - .setRationaleMessage(R.string.explain) - .setDeniedMessage(R.string.denied) - .setDeniedCloseButtonText(R.string.close_game) - .setGotoSettingButtonText(R.string.settings) - .setPermissions(WRITE_EXTERNAL_STORAGE) - .request() - .subscribe(tedPermissionResult -> { - if (tedPermissionResult.isGranted()) { - if (pf.getLaunchTimes() % 3 == 1) - askLocationPermissions(); - else listener.onEvent(true); - } else { - listener.onEvent(false); - } - }); - } - - @SuppressLint("CheckResult") - private void askStorageWhenDoNotShow() { - TedRx2Permission.with(activity) - .setDeniedMessage(R.string.denied) - .setDeniedCloseButtonText(R.string.close_game) - .setGotoSettingButtonText(R.string.settings) - .setPermissions(WRITE_EXTERNAL_STORAGE) - .request() - .subscribe(tedPermissionResult -> { - if (tedPermissionResult.isGranted()) { - if (pf.getLaunchTimes() % 3 == 1) - askLocationPermissions(); - else listener.onEvent(true); - } else { - listener.onEvent(false); - } - }); - } - - // location permissions block - @SuppressLint("CheckResult") - private void askLocationPermissions() { - TedRx2Permission.with(activity) - .setPermissions(ACCESS_FINE_LOCATION) - .request() - .subscribe(tedPermissionResult -> { - if (tedPermissionResult.isGranted()) { - listener.onEvent(true); - } else { - if (TedRx2Permission.canRequestPermission(activity, ACCESS_FINE_LOCATION)) - askLocationRationalePermissions(); - else listener.onEvent(true); - } - }); - } - - @SuppressLint("CheckResult") - private void askLocationRationalePermissions() { - TedRx2Permission.with(activity) - .setRationaleMessage(R.string.location) - .setPermissions(ACCESS_FINE_LOCATION) - .request() - .subscribe(tedPermissionResult -> listener.onEvent(true)); - } -} diff --git a/build/android/app/src/main/java/com/multicraft/game/helpers/PreferenceHelper.kt b/build/android/app/src/main/java/com/multicraft/game/helpers/PreferenceHelper.kt new file mode 100644 index 000000000..8857b9a47 --- /dev/null +++ b/build/android/app/src/main/java/com/multicraft/game/helpers/PreferenceHelper.kt @@ -0,0 +1,63 @@ +/* +MultiCraft +Copyright (C) 2014-2021 MoNTE48, Maksim Gamarnik +Copyright (C) 2014-2021 ubulem, Bektur Mambetov + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 3.0 of the License, or +(at your option) any later version. + +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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +package com.multicraft.game.helpers + +import android.content.Context +import android.content.SharedPreferences + +object PreferenceHelper { + const val TAG_SHORTCUT_EXIST = "createShortcut" + const val TAG_BUILD_VER = "buildVer" + const val TAG_LAUNCH_TIMES = "launchTimes" + + fun init(context: Context): SharedPreferences = + context.getSharedPreferences("MultiCraftSettings", Context.MODE_PRIVATE) + + private inline fun SharedPreferences.edit(operation: (SharedPreferences.Editor) -> Unit) { + val editor = this.edit() + operation(editor) + editor.apply() + } + + operator fun SharedPreferences.set(key: String, value: Any?) = when (value) { + is String? -> edit { it.putString(key, value) } + is Int -> edit { it.putInt(key, value) } + is Boolean -> edit { it.putBoolean(key, value) } + is Float -> edit { it.putFloat(key, value) } + is Long -> edit { it.putLong(key, value) } + else -> throw UnsupportedOperationException("Not yet implemented") + } + + fun SharedPreferences.getBoolValue(key: String): Boolean = when (key) { + TAG_SHORTCUT_EXIST -> getBoolean(key, false) + else -> throw UnsupportedOperationException("Not yet implemented") + } + + fun SharedPreferences.getIntValue(key: String) = when (key) { + TAG_LAUNCH_TIMES -> getInt(key, 0) + else -> throw UnsupportedOperationException("Not yet implemented") + } + + fun SharedPreferences.getStringValue(key: String) = when (key) { + TAG_BUILD_VER -> getString(key, "0") as String + else -> throw UnsupportedOperationException("Not yet implemented") + } +} diff --git a/build/android/app/src/main/java/com/multicraft/game/helpers/PreferencesHelper.java b/build/android/app/src/main/java/com/multicraft/game/helpers/PreferencesHelper.java deleted file mode 100644 index 4313eae5d..000000000 --- a/build/android/app/src/main/java/com/multicraft/game/helpers/PreferencesHelper.java +++ /dev/null @@ -1,132 +0,0 @@ -/* -MultiCraft -Copyright (C) 2014-2020 MoNTE48, Maksim Gamarnik -Copyright (C) 2014-2020 ubulem, Bektur Mambetov - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation; either version 3.0 of the License, or -(at your option) any later version. - -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 Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ - -package com.multicraft.game.helpers; - -import android.content.Context; -import android.content.SharedPreferences; - -public class PreferencesHelper { - public static final String TAG_SHORTCUT_EXIST = "createShortcut"; - public static final String TAG_BUILD_NUMBER = "buildNumber"; - public static final String TAG_LAUNCH_TIMES = "launchTimes"; - public static final String TAG_COPY_OLD_WORLDS = "copyOldWorlds"; - public static final String TAG_LAST_RATE_VERSION_CODE = "lastRateVersionCode"; - public static final String TAG_RATE_MIN_VERSION_CODE = "rateMinVersionCode"; - public static final String TAG_EXIT_GAME_COUNT = "exitGameCount"; - public static final String TAG_REVIEW_ENABLE = "reviewEnable"; - public static final String TAG_OPT_OUT = "optOut"; - public static final String IS_ASK_CONSENT = "isAskConsent"; - public static final String IS_LOADED = "interstitialLoaded"; - public static final String RV_LOADED = "rewardedVideoLoaded"; - public static final String ADS_DELAY = "adsDelay"; - public static final String ADS_REPEAT = "adsRepeat"; - public static final String ADS_ENABLE = "adsEnable"; - private static final String SETTINGS = "MultiCraftSettings"; - - private static PreferencesHelper instance; - private static SharedPreferences sharedPreferences; - - private PreferencesHelper(Context context) { - sharedPreferences = context.getSharedPreferences(SETTINGS, Context.MODE_PRIVATE); - } - - public static PreferencesHelper getInstance(Context context) { - if (instance == null) { - synchronized (PreferencesHelper.class) { - if (instance == null) - instance = new PreferencesHelper(context.getApplicationContext()); - } - } - return instance; - } - - public boolean isCreateShortcut() { - return sharedPreferences.getBoolean(TAG_SHORTCUT_EXIST, false); - } - - public boolean isInterstitialLoaded() { - return sharedPreferences.getBoolean(IS_LOADED, false); - } - - public boolean isVideoLoaded() { - return sharedPreferences.getBoolean(RV_LOADED, false); - } - - public boolean isAskConsent() { - return sharedPreferences.getBoolean(IS_ASK_CONSENT, true); - } - - public boolean isWorldsCopied() { - return sharedPreferences.getBoolean(TAG_COPY_OLD_WORLDS, false); - } - - public String getBuildNumber() { - return sharedPreferences.getString(TAG_BUILD_NUMBER, "0"); - } - - public int getLaunchTimes() { - return sharedPreferences.getInt(TAG_LAUNCH_TIMES, 0); - } - - public int getLastRateVersionCode() { - return sharedPreferences.getInt(TAG_LAST_RATE_VERSION_CODE, 0); - } - - public int getRateMinVersionCode() { - return sharedPreferences.getInt(TAG_RATE_MIN_VERSION_CODE, 0); - } - - public int getExitGameCount() { - return sharedPreferences.getInt(TAG_EXIT_GAME_COUNT, 0); - } - - public int getAdsDelay() { - return sharedPreferences.getInt(ADS_DELAY, 600); - } - - public int getAdsRepeat() { - return sharedPreferences.getInt(ADS_REPEAT, 900); - } - - public boolean isAdsEnable() { - return sharedPreferences.getBoolean(ADS_ENABLE, true); - } - - public boolean isReviewEnable() { - return sharedPreferences.getBoolean(TAG_REVIEW_ENABLE, true); - } - - public boolean isOptOut() { - return sharedPreferences.getBoolean(TAG_OPT_OUT, false); - } - - public void saveSettings(String tag, boolean bool) { - sharedPreferences.edit().putBoolean(tag, bool).apply(); - } - - public void saveSettings(String tag, String value) { - sharedPreferences.edit().putString(tag, value).apply(); - } - - public void saveSettings(String tag, int value) { - sharedPreferences.edit().putInt(tag, value).apply(); - } -} diff --git a/build/android/app/src/main/java/com/multicraft/game/helpers/RateMeHelper.java b/build/android/app/src/main/java/com/multicraft/game/helpers/RateMeHelper.java deleted file mode 100644 index 9dc014ca7..000000000 --- a/build/android/app/src/main/java/com/multicraft/game/helpers/RateMeHelper.java +++ /dev/null @@ -1,96 +0,0 @@ -/* -MultiCraft -Copyright (C) 2014-2020 MoNTE48, Maksim Gamarnik -Copyright (C) 2014-2020 ubulem, Bektur Mambetov - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation; either version 3.0 of the License, or -(at your option) any later version. - -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 Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ - -package com.multicraft.game.helpers; - -import android.app.Dialog; -import android.app.NativeActivity; -import android.content.Intent; -import android.net.Uri; -import android.view.View; -import android.widget.RatingBar; -import android.widget.Toast; - -import com.multicraft.game.R; - -import static com.multicraft.game.helpers.ApiLevelHelper.isKitkat; -import static com.multicraft.game.helpers.Constants.SESSION_COUNT; -import static com.multicraft.game.helpers.PreferencesHelper.TAG_OPT_OUT; -import static com.multicraft.game.helpers.Utilities.getStoreUrl; - -public class RateMeHelper { - private static PreferencesHelper pf; - - public static void onStart(NativeActivity activity) { - pf = PreferencesHelper.getInstance(activity); - } - - private static boolean isEnoughTimePassed() { - return (pf.getLastRateVersionCode() == 0) && pf.getLaunchTimes() >= SESSION_COUNT - && pf.getExitGameCount() >= SESSION_COUNT; - } - - private static boolean isWorthToReview() { - return (pf.getLastRateVersionCode() != 0) - && pf.getRateMinVersionCode() > pf.getLastRateVersionCode(); - } - - public static boolean shouldAskForReview() { - return pf.isReviewEnable() && (isEnoughTimePassed() || isWorthToReview()); - } - - public static boolean shouldShowRateDialog() { - if (pf.isOptOut()) - return false; - else { - return shouldAskForReview(); - } - } - - public static void showRateDialog(NativeActivity activity) { - final Dialog dialog = new Dialog(activity, R.style.RateMe); - dialog.setCancelable(false); - if (isKitkat()) - dialog.getWindow().getDecorView().setSystemUiVisibility( - View.SYSTEM_UI_FLAG_HIDE_NAVIGATION - | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY - | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); - dialog.setContentView(R.layout.rate_dialog); - RatingBar ratingBar = dialog.findViewById(R.id.ratingBar); - ratingBar.setOnRatingBarChangeListener((ratingBar1, rating, fromUser) -> { - if (rating >= 4) { - try { - Toast.makeText(activity, R.string.thank, Toast.LENGTH_LONG).show(); - Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(getStoreUrl())); - activity.startActivity(intent); - } catch (Exception e) { - // nothing - } - dialog.dismiss(); - pf.saveSettings(TAG_OPT_OUT, true); - } else { - dialog.dismiss(); - Toast.makeText(activity, R.string.sad, Toast.LENGTH_LONG).show(); - } - }); - if (!activity.isFinishing()) - dialog.show(); - } -} diff --git a/build/android/app/src/main/java/com/multicraft/game/helpers/Utilities.java b/build/android/app/src/main/java/com/multicraft/game/helpers/Utilities.java deleted file mode 100644 index cca528ff9..000000000 --- a/build/android/app/src/main/java/com/multicraft/game/helpers/Utilities.java +++ /dev/null @@ -1,172 +0,0 @@ -/* -MultiCraft -Copyright (C) 2014-2020 MoNTE48, Maksim Gamarnik -Copyright (C) 2014-2020 ubulem, Bektur Mambetov - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation; either version 3.0 of the License, or -(at your option) any later version. - -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 Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ - -package com.multicraft.game.helpers; - -import android.app.Activity; -import android.app.ActivityManager; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.graphics.Bitmap; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -import android.view.View; - -import com.bugsnag.android.Bugsnag; -import com.multicraft.game.MainActivity; -import com.multicraft.game.R; - -import org.apache.commons.io.FileUtils; - -import java.io.File; -import java.io.IOException; -import java.net.HttpURLConnection; -import java.net.URL; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; - -import static android.os.Environment.getExternalStorageDirectory; -import static com.multicraft.game.helpers.ApiLevelHelper.isKitkat; -import static com.multicraft.game.helpers.Constants.FILES; -import static com.multicraft.game.helpers.Constants.GAMES; -import static com.multicraft.game.helpers.Constants.appPackage; -import static com.multicraft.game.helpers.PreferencesHelper.TAG_SHORTCUT_EXIST; - -public class Utilities { - private static boolean isInternetAvailable(String url) { - try { - HttpURLConnection urlc = - (HttpURLConnection) new URL(url).openConnection(); - urlc.setRequestProperty("Connection", "close"); - urlc.setConnectTimeout(2000); - urlc.connect(); - int ResponseCode = urlc.getResponseCode(); - return ResponseCode == HttpURLConnection.HTTP_NO_CONTENT || - ResponseCode == HttpURLConnection.HTTP_OK; - } catch (IOException e) { - return false; - } - } - - public static boolean isReachable() { - return isInternetAvailable("http://clients3.google.com/generate_204") || - isInternetAvailable("http://servers.multicraft.world"); - } - - public static void deleteFiles(List files, String path) { - for (String f : files) { - File file = new File(path, f); - if (file.exists()) - FileUtils.deleteQuietly(file); - } - } - - public static void deleteFiles(List files) { - for (File file : files) { - if (file != null && file.exists()) - FileUtils.deleteQuietly(file); - } - } - - public static String getStoreUrl() { - return "market://details?id=" + appPackage; - } - - public static void makeFullScreen(Activity activity) { - if (isKitkat()) - activity.getWindow().getDecorView().setSystemUiVisibility( - View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | - View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | - View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); - } - - public static Drawable getIcon(Activity activity) { - try { - return activity.getPackageManager().getApplicationIcon(activity.getPackageName()); - } catch (PackageManager.NameNotFoundException e) { - Bugsnag.notify(e); - return activity.getResources().getDrawable(R.mipmap.ic_launcher); - } - } - - public static void addShortcut(Activity activity) { - ActivityManager activityManager = - (ActivityManager) activity.getSystemService(Context.ACTIVITY_SERVICE); - int size = 0; - if (activityManager != null) - size = activityManager.getLauncherLargeIconSize(); - Bitmap shortcutIconBitmap = ((BitmapDrawable) getIcon(activity)).getBitmap(); - if (shortcutIconBitmap.getWidth() != size || shortcutIconBitmap.getHeight() != size) - shortcutIconBitmap = Bitmap.createScaledBitmap(shortcutIconBitmap, size, size, true); - PreferencesHelper.getInstance(activity).saveSettings(TAG_SHORTCUT_EXIST, true); - Intent shortcutIntent = new Intent(activity, MainActivity.class); - shortcutIntent.setAction(Intent.ACTION_MAIN); - Intent addIntent = new Intent(); - addIntent.putExtra("duplicate", false); - addIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent); - addIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, activity.getResources().getString(R.string.app_name)); - addIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON, shortcutIconBitmap); - addIntent.setAction("com.android.launcher.action.INSTALL_SHORTCUT"); - activity.getApplicationContext().sendBroadcast(addIntent); - } - - public static String getJson(String url) { - OkHttpClient client = new OkHttpClient.Builder() - .callTimeout(3000, TimeUnit.MILLISECONDS) - .build(); - Request request = new Request.Builder() - .url(url) - .build(); - try { - Response response = client.newCall(request).execute(); - return response.body().string(); - } catch (IOException | NullPointerException e) { - return "{}"; - } - } - - public static String getLocationByZip(Context context, String zipName) { - String path; - switch (zipName) { - case FILES: - case GAMES: - path = context.getFilesDir().toString(); - break; - default: - throw new IllegalArgumentException("No such zip name"); - } - return path; - } - - public static ArrayList getZipsFromAssets(Context context) { - try { - return new ArrayList<>(Arrays.asList(context.getAssets().list("data"))); - } catch (IOException e) { - return new ArrayList<>(Arrays.asList(FILES, GAMES)); - } - } -} diff --git a/build/android/app/src/main/java/com/multicraft/game/helpers/Utilities.kt b/build/android/app/src/main/java/com/multicraft/game/helpers/Utilities.kt new file mode 100644 index 000000000..b213044e5 --- /dev/null +++ b/build/android/app/src/main/java/com/multicraft/game/helpers/Utilities.kt @@ -0,0 +1,140 @@ +/* +MultiCraft +Copyright (C) 2014-2021 MoNTE48, Maksim Gamarnik +Copyright (C) 2014-2021 ubulem, Bektur Mambetov + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 3.0 of the License, or +(at your option) any later version. + +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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +package com.multicraft.game.helpers + +import android.annotation.SuppressLint +import android.app.Activity +import android.app.ActivityManager +import android.app.AlarmManager +import android.app.PendingIntent +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.graphics.Bitmap +import android.graphics.drawable.BitmapDrawable +import android.net.ConnectivityManager +import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED +import android.view.View +import android.view.Window +import androidx.appcompat.app.AppCompatActivity +import androidx.core.content.ContextCompat +import androidx.core.view.WindowCompat +import androidx.core.view.WindowInsetsCompat.Type.navigationBars +import androidx.core.view.WindowInsetsCompat.Type.statusBars +import androidx.core.view.WindowInsetsControllerCompat +import androidx.core.view.WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE +import com.multicraft.game.MainActivity +import com.multicraft.game.R +import com.multicraft.game.helpers.ApiLevelHelper.isLollipop +import com.multicraft.game.helpers.ApiLevelHelper.isMarshmallow +import com.multicraft.game.helpers.ApiLevelHelper.isOreo +import com.multicraft.game.helpers.PreferenceHelper.TAG_SHORTCUT_EXIST +import com.multicraft.game.helpers.PreferenceHelper.set +import java.io.File +import java.io.InputStream +import kotlin.math.roundToInt +import kotlin.system.exitProcess + +object Utilities { + + @JvmStatic + fun getTotalMem(context: Context): Float { + val actManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager + val memInfo = ActivityManager.MemoryInfo() + actManager.getMemoryInfo(memInfo) + var memory = memInfo.totalMem * 1.0f / (1024 * 1024 * 1024) + memory = (memory * 100).roundToInt() / 100.0f + return memory + } + + @JvmStatic + fun makeFullScreen(window: Window) { + if (isLollipop()) { + WindowCompat.setDecorFitsSystemWindows(window, false) + WindowInsetsControllerCompat(window, window.decorView).let { + it.hide(statusBars() or navigationBars()) + it.systemBarsBehavior = BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE + } + } else @Suppress("DEPRECATION") { + val decor = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or + View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY or + View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + window.decorView.systemUiVisibility = decor + } + } + + fun getIcon(activity: Activity) = try { + activity.packageManager.getApplicationIcon(activity.packageName) + } catch (e: PackageManager.NameNotFoundException) { + ContextCompat.getDrawable(activity, R.mipmap.ic_launcher) + } + + @Suppress("DEPRECATION") + fun addShortcut(activity: AppCompatActivity) { + if (isOreo()) return + val activityManager = activity.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager + val size = activityManager.launcherLargeIconSize + var shortcutIconBitmap = (getIcon(activity) as BitmapDrawable).bitmap + if (shortcutIconBitmap.width != size || shortcutIconBitmap.height != size) + shortcutIconBitmap = Bitmap.createScaledBitmap(shortcutIconBitmap, size, size, true) + val shortcutIntent = Intent(activity, MainActivity::class.java) + shortcutIntent.action = Intent.ACTION_MAIN + val addIntent = Intent() + addIntent.putExtra("duplicate", false) + addIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent) + addIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, R.string.app_name) + addIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON, shortcutIconBitmap) + addIntent.action = "com.android.launcher.action.INSTALL_SHORTCUT" + activity.applicationContext.sendBroadcast(addIntent) + // save preference + PreferenceHelper.init(activity)[TAG_SHORTCUT_EXIST] = true + } + + @JvmStatic + fun finishApp(restart: Boolean, activity: Activity) { + if (restart) @SuppressLint("UnspecifiedImmutableFlag") { + val intent = Intent(activity, activity::class.java) + val mPendingIntentId = 1337 + val mgr = activity.getSystemService(Context.ALARM_SERVICE) as AlarmManager + mgr.set( + AlarmManager.RTC, System.currentTimeMillis(), PendingIntent.getActivity( + activity, mPendingIntentId, intent, PendingIntent.FLAG_CANCEL_CURRENT + ) + ) + } + exitProcess(0) + } + + fun File.copyInputStreamToFile(inputStream: InputStream) = + this.outputStream().use { fileOut -> inputStream.copyTo(fileOut, 8192) } + + fun isConnected(context: Context): Boolean { + val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager + if (isMarshmallow()) { + val activeNetwork = cm.activeNetwork ?: return false + val capabilities = cm.getNetworkCapabilities(activeNetwork) ?: return false + return capabilities.hasCapability(NET_CAPABILITY_VALIDATED) + } else @Suppress("DEPRECATION") { + val activeNetworkInfo = cm.activeNetworkInfo ?: return false + return activeNetworkInfo.isConnected + } + } +} diff --git a/build/android/app/src/main/java/com/multicraft/game/helpers/VersionManagerHelper.java b/build/android/app/src/main/java/com/multicraft/game/helpers/VersionManagerHelper.java deleted file mode 100644 index e2bbfc491..000000000 --- a/build/android/app/src/main/java/com/multicraft/game/helpers/VersionManagerHelper.java +++ /dev/null @@ -1,157 +0,0 @@ -/* -MultiCraft -Copyright (C) 2014-2020 MoNTE48, Maksim Gamarnik -Copyright (C) 2014-2020 ubulem, Bektur Mambetov - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation; either version 3.0 of the License, or -(at your option) any later version. - -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 Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ - -package com.multicraft.game.helpers; - -import android.content.Intent; -import android.net.Uri; -import android.text.Editable; -import android.text.Html; - -import androidx.appcompat.app.AppCompatActivity; -import androidx.preference.PreferenceManager; - -import com.google.gson.Gson; -import com.multicraft.game.JsonSettings; - -import org.xml.sax.XMLReader; - -import java.util.Calendar; -import java.util.List; -import java.util.Locale; - -import static com.multicraft.game.helpers.Constants.versionCode; -import static com.multicraft.game.helpers.PreferencesHelper.ADS_DELAY; -import static com.multicraft.game.helpers.PreferencesHelper.ADS_ENABLE; -import static com.multicraft.game.helpers.PreferencesHelper.ADS_REPEAT; -import static com.multicraft.game.helpers.PreferencesHelper.TAG_RATE_MIN_VERSION_CODE; -import static com.multicraft.game.helpers.PreferencesHelper.TAG_REVIEW_ENABLE; -import static com.multicraft.game.helpers.Utilities.getStoreUrl; - -public class VersionManagerHelper { - private final CustomTagHandler customTagHandler; - private final String PREF_REMINDER_TIME = "w.reminder.time"; - private final AppCompatActivity activity; - private String message, updateUrl; - - public VersionManagerHelper(AppCompatActivity act) { - this.activity = act; - this.customTagHandler = new CustomTagHandler(); - } - - public boolean isCheckVersion() { - long currentTimeStamp = Calendar.getInstance().getTimeInMillis(); - long reminderTimeStamp = getReminderTime(); - return currentTimeStamp > reminderTimeStamp; - } - - private boolean isBadVersion(List badVersions) { - return badVersions.contains(versionCode); - } - - private JsonSettings parseJson(String result) { - JsonSettings settings = new Gson().fromJson(result, JsonSettings.class); - String lang = Locale.getDefault().getLanguage(); - String content = lang.equals("ru") ? settings.getContentRus() : settings.getContentEng(); - setMessage(content); - setUpdateUrl("market://details?id=" + settings.getPackageName()); - savePrefSettings(settings); - return settings; - } - - private void savePrefSettings(JsonSettings settings) { - PreferencesHelper pf = PreferencesHelper.getInstance(activity); - pf.saveSettings(TAG_RATE_MIN_VERSION_CODE, settings.getRateMinVersionCode()); - pf.saveSettings(TAG_REVIEW_ENABLE, settings.isReviewEnabled()); - pf.saveSettings(ADS_DELAY, settings.getAdsDelay()); - pf.saveSettings(ADS_REPEAT, settings.getAdsRepeat()); - pf.saveSettings(ADS_ENABLE, settings.isAdsEnabled()); - } - - public boolean isShow(String result) { - if (result.equals("{}")) return false; - JsonSettings jsonSettings = parseJson(result); - return (versionCode < jsonSettings.getVersionCode()) || - isBadVersion(jsonSettings.getBadVersionCodes()); - } - - public String getMessage() { - String defaultMessage = "What's new?"; - return message != null ? message : defaultMessage; - } - - private void setMessage(String message) { - this.message = message; - } - - public String getUpdateUrl() { - return updateUrl != null ? updateUrl : getStoreUrl(); - } - - private void setUpdateUrl(String updateUrl) { - this.updateUrl = updateUrl; - } - - public void updateNow(String url) { - if (url != null) { - try { - Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); - activity.startActivity(intent); - } catch (Exception e) { - // nothing - } - } - } - - public void remindMeLater() { - Calendar c = Calendar.getInstance(); - c.add(Calendar.MINUTE, 1); - long reminderTimeStamp = c.getTimeInMillis(); - setReminderTime(reminderTimeStamp); - } - - private long getReminderTime() { - return PreferenceManager.getDefaultSharedPreferences(activity).getLong( - PREF_REMINDER_TIME, 0); - } - - private void setReminderTime(long reminderTimeStamp) { - PreferenceManager.getDefaultSharedPreferences(activity).edit().putLong( - PREF_REMINDER_TIME, reminderTimeStamp).apply(); - } - - public CustomTagHandler getCustomTagHandler() { - return customTagHandler; - } - - private static class CustomTagHandler implements Html.TagHandler { - @Override - public void handleTag(boolean opening, String tag, Editable output, - XMLReader xmlReader) { - // you may add more tag handler which are not supported by android here - if ("li".equals(tag)) { - if (opening) - output.append(" \u2022 "); - else - output.append("\n"); - } - } - } -} diff --git a/build/android/app/src/main/java/com/multicraft/game/workmanager/UnzipWorker.kt b/build/android/app/src/main/java/com/multicraft/game/workmanager/UnzipWorker.kt new file mode 100644 index 000000000..8a7edcb64 --- /dev/null +++ b/build/android/app/src/main/java/com/multicraft/game/workmanager/UnzipWorker.kt @@ -0,0 +1,103 @@ +/* +MultiCraft +Copyright (C) 2014-2021 MoNTE48, Maksim Gamarnik +Copyright (C) 2014-2021 ubulem, Bektur Mambetov + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 3.0 of the License, or +(at your option) any later version. + +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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +package com.multicraft.game.workmanager + +import android.app.NotificationChannel +import android.app.NotificationManager +import android.content.Context +import android.content.Context.NOTIFICATION_SERVICE +import androidx.core.app.NotificationCompat +import androidx.work.CoroutineWorker +import androidx.work.ForegroundInfo +import androidx.work.WorkerParameters +import androidx.work.workDataOf +import com.multicraft.game.R +import com.multicraft.game.helpers.ApiLevelHelper.isOreo +import com.multicraft.game.helpers.Utilities.copyInputStreamToFile +import java.io.File +import java.io.IOException +import java.util.zip.ZipFile + +class UnzipWorker(private val appContext: Context, workerParams: WorkerParameters) : + CoroutineWorker(appContext, workerParams) { + + private fun createForegroundInfo(): ForegroundInfo { + if (isOreo()) { + val importance = NotificationManager.IMPORTANCE_DEFAULT + val mChannel = NotificationChannel(CHANNEL_ID, CHANNEL_NAME, importance) + mChannel.setSound(null, null) + mChannel.description = CHANNEL_DESC + val notificationManager = + appContext.getSystemService(NOTIFICATION_SERVICE) as NotificationManager + notificationManager.createNotificationChannel(mChannel) + } + + val notification = NotificationCompat.Builder(applicationContext, CHANNEL_ID) + .setContentTitle(appContext.getString(R.string.notification_title)) + .setContentText(appContext.getString(R.string.notification_description)) + .setSmallIcon(R.drawable.update) + .build() + return ForegroundInfo(NOTIFICATION_ID, notification) + } + + override suspend fun doWork(): Result { + val zips = inputData.getStringArray(EXTRA_KEY_IN_FILE)!! + setForeground(createForegroundInfo()) + var previousProgress = 0 + return try { + var per = 0 + val zipList = zips.map { ZipFile(File(appContext.cacheDir, it)) } + val size = zipList.sumOf { it.size() } + zipList.forEach { zip -> + zip.use { + it.entries().asSequence().forEach { entry -> + zip.getInputStream(entry).use { input -> + val filePath = File(appContext.filesDir, entry.name) + if (entry.isDirectory) + filePath.mkdirs() + else + filePath.copyInputStreamToFile(input) + val currentProgress = 100 * ++per / size + if (currentProgress > previousProgress) { + previousProgress = currentProgress + setProgress(workDataOf(PROGRESS to currentProgress)) + } + } + } + } + } + Result.success() + } catch (e: IOException) { + Result.failure() + } finally { + zips.forEach { File(appContext.cacheDir, it).delete() } + } + } + + companion object { + const val PROGRESS = "progress" + const val NOTIFICATION_ID = 1 + const val CHANNEL_ID = "MultiCraft channel" + const val CHANNEL_NAME = "com.multicraft.game" + const val CHANNEL_DESC = "Notifications from MultiCraft" + const val EXTRA_KEY_IN_FILE = "com.multicraft.game.file" + } +} diff --git a/build/android/app/src/main/java/com/multicraft/game/workmanager/WorkerViewModel.kt b/build/android/app/src/main/java/com/multicraft/game/workmanager/WorkerViewModel.kt new file mode 100644 index 000000000..35724059f --- /dev/null +++ b/build/android/app/src/main/java/com/multicraft/game/workmanager/WorkerViewModel.kt @@ -0,0 +1,55 @@ +/* +MultiCraft +Copyright (C) 2014-2021 MoNTE48, Maksim Gamarnik +Copyright (C) 2014-2021 ubulem, Bektur Mambetov + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 3.0 of the License, or +(at your option) any later version. + +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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +package com.multicraft.game.workmanager + +import android.app.Application +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.LiveData +import androidx.work.OneTimeWorkRequest +import androidx.work.WorkInfo +import androidx.work.WorkManager +import androidx.work.workDataOf +import com.multicraft.game.workmanager.UnzipWorker.Companion.EXTRA_KEY_IN_FILE + +class WorkerViewModel( + application: Application, + private val zips: Array +) : + AndroidViewModel(application) { + + private val mWorkManager: WorkManager by lazy { WorkManager.getInstance(application) } + + val unzippingWorkObserver: LiveData by lazy { + mWorkManager.getWorkInfoByIdLiveData( + unzippingWorkReq.id + ) + } + + private val unzippingWorkReq: OneTimeWorkRequest by lazy { + OneTimeWorkRequest.Builder(UnzipWorker::class.java) + .setInputData(workDataOf(EXTRA_KEY_IN_FILE to zips)) + .build() + } + + fun startOneTimeWorkRequest() { + mWorkManager.enqueue(unzippingWorkReq) + } +} diff --git a/build/android/app/src/main/java/com/multicraft/game/helpers/AdManager.java b/build/android/app/src/main/java/com/multicraft/game/workmanager/WorkerViewModelFactory.kt similarity index 51% rename from build/android/app/src/main/java/com/multicraft/game/helpers/AdManager.java rename to build/android/app/src/main/java/com/multicraft/game/workmanager/WorkerViewModelFactory.kt index 0eb087543..1547c6b27 100644 --- a/build/android/app/src/main/java/com/multicraft/game/helpers/AdManager.java +++ b/build/android/app/src/main/java/com/multicraft/game/workmanager/WorkerViewModelFactory.kt @@ -1,7 +1,7 @@ /* MultiCraft -Copyright (C) 2014-2020 MoNTE48, Maksim Gamarnik -Copyright (C) 2014-2020 ubulem, Bektur Mambetov +Copyright (C) 2014-2021 MoNTE48, Maksim Gamarnik +Copyright (C) 2014-2021 ubulem, Bektur Mambetov This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -18,24 +18,23 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -package com.multicraft.game.helpers; +package com.multicraft.game.workmanager -import android.app.Activity; +import android.app.Application +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider -public class AdManager { - public static void initAd(final Activity activity, boolean consent) { - // NDA code here - } - public static void setAdsCallback(final Activity activity) { - // NDA code here - } - - public static void startAd(final Activity activity, boolean isFirstTime, boolean isShowNow) { - // NDA code here - } - - public static void stopAd() { - // NDA code here +class WorkerViewModelFactory( + private val application: Application, + private val zips: Array +) : + ViewModelProvider.Factory { + override fun create(modelClass: Class): T { + if (modelClass.isAssignableFrom(WorkerViewModel::class.java)) { + @Suppress("UNCHECKED_CAST") + return WorkerViewModel(application, zips) as T + } + throw IllegalArgumentException("Unknown ViewModel class") } } diff --git a/build/android/app/src/main/res/drawable/background.png b/build/android/app/src/main/res/drawable/background.png index 55afbb6f4..84f1fd4c6 100644 Binary files a/build/android/app/src/main/res/drawable/background.png and b/build/android/app/src/main/res/drawable/background.png differ diff --git a/build/android/app/src/main/res/drawable/custom_dialog_rounded.xml b/build/android/app/src/main/res/drawable/custom_dialog_rounded.xml deleted file mode 100644 index f5e0ff00c..000000000 --- a/build/android/app/src/main/res/drawable/custom_dialog_rounded.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - diff --git a/build/android/app/src/main/res/drawable/custom_edittext_rounded.xml b/build/android/app/src/main/res/drawable/custom_edittext_rounded.xml deleted file mode 100644 index d72c57859..000000000 --- a/build/android/app/src/main/res/drawable/custom_edittext_rounded.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/build/android/app/src/main/res/drawable/custom_progress_bar.xml b/build/android/app/src/main/res/drawable/custom_progress_bar.xml index da5ed98b9..bc8e70538 100644 --- a/build/android/app/src/main/res/drawable/custom_progress_bar.xml +++ b/build/android/app/src/main/res/drawable/custom_progress_bar.xml @@ -1,6 +1,6 @@ - + - + diff --git a/build/android/app/src/main/res/drawable/ic_launcher_background.xml b/build/android/app/src/main/res/drawable/ic_launcher_background.xml deleted file mode 100644 index fe5102ec9..000000000 --- a/build/android/app/src/main/res/drawable/ic_launcher_background.xml +++ /dev/null @@ -1,170 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/build/android/app/src/main/res/font/retron2000.ttf b/build/android/app/src/main/res/font/retron2000.ttf new file mode 100644 index 000000000..f30c7e1b7 Binary files /dev/null and b/build/android/app/src/main/res/font/retron2000.ttf differ diff --git a/build/android/app/src/main/res/layout/activity_main.xml b/build/android/app/src/main/res/layout/activity_main.xml index 4778e3ec2..2b88a76a4 100644 --- a/build/android/app/src/main/res/layout/activity_main.xml +++ b/build/android/app/src/main/res/layout/activity_main.xml @@ -1,12 +1,10 @@ + android:indeterminate="true" /> + android:textColor="@color/not_white" + android:textSize="14sp" /> diff --git a/build/android/app/src/main/res/layout/input_text.xml b/build/android/app/src/main/res/layout/input_text.xml new file mode 100644 index 000000000..a27bf6e29 --- /dev/null +++ b/build/android/app/src/main/res/layout/input_text.xml @@ -0,0 +1,13 @@ + + + + diff --git a/build/android/app/src/main/res/layout/rate_dialog.xml b/build/android/app/src/main/res/layout/rate_dialog.xml deleted file mode 100644 index e0515207d..000000000 --- a/build/android/app/src/main/res/layout/rate_dialog.xml +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - - - -