Files
MonsterCards/Android/app/src/main/java/com/majinnaibu/monstercards/MainActivity.java

244 lines
10 KiB
Java

package com.majinnaibu.monstercards;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.OpenableColumns;
import android.view.MenuItem;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.navigation.NavController;
import androidx.navigation.NavDirections;
import androidx.navigation.fragment.NavHostFragment;
import androidx.navigation.ui.AppBarConfiguration;
import androidx.navigation.ui.NavigationUI;
import android.widget.Toast;
import com.google.android.material.bottomnavigation.BottomNavigationView;
import com.google.gson.Gson;
import com.majinnaibu.monstercards.helpers.MonsterImportHelper;
import com.majinnaibu.monstercards.helpers.StringHelper;
import com.majinnaibu.monstercards.importers.BinderImporter;
import com.majinnaibu.monstercards.importers.DnDBeyondImporter;
import com.majinnaibu.monstercards.init.AppCenterInitializer;
import com.majinnaibu.monstercards.utils.Logger;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Locale;
import java.util.Objects;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.core.Single;
import io.reactivex.rxjava3.schedulers.Schedulers;
public class MainActivity extends AppCompatActivity {
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
if (item.getItemId() == android.R.id.home) {
getOnBackPressedDispatcher().onBackPressed();
return true;
}
return super.onOptionsItemSelected(item);
}
@SuppressWarnings("ConstantConditions")
@Override
protected void onCreate(Bundle savedInstanceState) {
WindowCompat.setDecorFitsSystemWindows(getWindow(), false);
super.onCreate(savedInstanceState);
AppCenterInitializer.init(getApplication());
setContentView(R.layout.activity_main);
View container = findViewById(R.id.container);
if (container != null) {
ViewCompat.setOnApplyWindowInsetsListener(container, (v, windowInsets) -> {
Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(insets.left, insets.top, insets.right, 0);
return windowInsets;
});
}
BottomNavigationView navView = findViewById(R.id.nav_view);
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder(
R.id.navigation_search,
R.id.navigation_dashboard,
R.id.navigation_collections,
R.id.navigation_library)
.build();
NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.nav_host_fragment);
NavController navController = navHostFragment.getNavController();
NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
NavigationUI.setupWithNavController(navView, navController);
onNewIntent(getIntent());
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if (intent == null) {
return;
}
String action = intent.getAction();
Bundle extras = intent.getExtras();
if ("android.intent.action.SEND".equals(action) && extras != null) {
CharSequence sharedText = extras.getCharSequence(Intent.EXTRA_TEXT);
if (sharedText != null) {
String textStr = sharedText.toString().trim();
DnDBeyondImporter dndImporter = new DnDBeyondImporter();
if (dndImporter.canImport(textStr)) {
importMonsterFromInputAndNavigate(textStr);
return;
}
}
} else if ("android.intent.action.VIEW".equals(action)) {
Uri dataUri = intent.getData();
if (dataUri != null) {
String dataStr = dataUri.toString().trim();
DnDBeyondImporter dndImporter = new DnDBeyondImporter();
if (dndImporter.canImport(dataStr)) {
importMonsterFromInputAndNavigate(dataStr);
return;
}
}
}
String json = readMonsterJSONFromIntent(intent);
if (!StringHelper.isNullOrEmpty(json)) {
importMonsterFromInputAndNavigate(json);
}
}
public void importMonsterFromInputAndNavigate(@NonNull String input) {
BinderImporter binderImporter = new BinderImporter();
if (binderImporter.canImport(input)) {
Toast.makeText(this, R.string.toast_importing_url, Toast.LENGTH_SHORT).show();
Single.fromCallable(() -> binderImporter.parse(input))
.flatMapCompletable(binder -> ((MonsterCardsApplication) getApplication()).getMonsterRepository().importBinder(binder))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(() -> {
Toast.makeText(this, R.string.snackbar_import_binder_success, Toast.LENGTH_LONG).show();
NavHostFragment navHostFragment = Objects.requireNonNull((NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.nav_host_fragment));
NavController navController = navHostFragment.getNavController();
navController.navigate(R.id.navigation_library);
}, throwable -> {
Logger.logError("Failed to import binder from input", throwable);
Toast.makeText(this, R.string.failed_to_import_url, Toast.LENGTH_LONG).show();
});
return;
}
Toast.makeText(this, R.string.toast_importing_url, Toast.LENGTH_SHORT).show();
Single.fromCallable(() -> MonsterImportHelper.fromJSON(input))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(monster -> {
String serializedJson = new Gson().toJson(monster);
NavHostFragment navHostFragment = Objects.requireNonNull((NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.nav_host_fragment));
NavController navController = navHostFragment.getNavController();
NavDirections navAction = MobileNavigationDirections.actionGlobalMonsterImportFragment(serializedJson);
navController.navigate(navAction);
}, throwable -> {
Logger.logError("Failed to import monster from input", throwable);
Toast.makeText(this, R.string.failed_to_import_url, Toast.LENGTH_LONG).show();
});
}
@Nullable
private String readMonsterJSONFromIntent(@NonNull Intent intent) {
String action = intent.getAction();
Bundle extras = intent.getExtras();
Uri uri = null;
if ("android.intent.action.SEND".equals(action)) {
if (extras != null) {
uri = extras.getParcelable(Intent.EXTRA_STREAM);
}
} else if ("android.intent.action.VIEW".equals(action) || "android.intent.action.EDIT".equals(action)) {
uri = intent.getData();
} else {
Logger.logError(String.format("unexpected launch configuration action: %s", action));
}
if (uri == null || !isMonsterFile(uri)) {
if (uri != null) {
Logger.logError("Ignored file because extension is not supported (.monster, .card, .binder): " + uri);
}
return null;
}
String json = readContentsOfUri(uri);
if (StringHelper.isNullOrEmpty(json)) {
return null;
}
return json;
}
private boolean isMonsterFile(@NonNull Uri uri) {
String fileName = getFileNameFromUri(uri);
if (fileName == null) {
return false;
}
String lowerName = fileName.toLowerCase(Locale.ROOT);
return lowerName.endsWith(".monster") || lowerName.endsWith(".monster.txt")
|| lowerName.endsWith(".card") || lowerName.endsWith(".card.txt")
|| lowerName.endsWith(".binder") || lowerName.endsWith(".binder.txt");
}
@Nullable
private String getFileNameFromUri(@NonNull Uri uri) {
String displayName = null;
if ("content".equals(uri.getScheme())) {
try (Cursor cursor = getContentResolver().query(uri, new String[]{OpenableColumns.DISPLAY_NAME}, null, null, null)) {
if (cursor != null && cursor.moveToFirst()) {
int nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
if (nameIndex != -1) {
displayName = cursor.getString(nameIndex);
}
}
} catch (Exception e) {
Logger.logError("Error querying display name from content URI", e);
}
}
if (displayName == null) {
displayName = uri.getPath();
}
return displayName;
}
@Nullable
private String readContentsOfUri(Uri uri) {
StringBuilder builder = new StringBuilder();
try (InputStream inputStream =
getContentResolver().openInputStream(uri);
BufferedReader reader = new BufferedReader(
new InputStreamReader(Objects.requireNonNull(inputStream)))) {
String line;
while ((line = reader.readLine()) != null) {
builder.append(line);
}
} catch (IOException e) {
Logger.logError("error reading file", e);
return null;
}
return builder.toString();
}
}