455 lines
16 KiB
Java
455 lines
16 KiB
Java
package com.annimon.everlastingsummer;
|
|
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import com.annimon.everlastingsummer.TouchGesture.TouchGestureType;
|
|
import com.annimon.everlastingsummer.ast.Variables;
|
|
import android.annotation.SuppressLint;
|
|
import android.app.Activity;
|
|
import android.content.ContentResolver;
|
|
import android.content.DialogInterface;
|
|
import android.content.Intent;
|
|
import android.media.MediaPlayer;
|
|
import android.net.Uri;
|
|
import android.os.Bundle;
|
|
import android.text.*;
|
|
import android.text.style.ForegroundColorSpan;
|
|
import android.util.DisplayMetrics;
|
|
import android.view.Gravity;
|
|
import android.view.KeyEvent;
|
|
import android.view.View;
|
|
import android.view.WindowManager;
|
|
import android.widget.FrameLayout;
|
|
import android.widget.ImageView;
|
|
import android.widget.TextView;
|
|
import android.widget.Toast;
|
|
|
|
/**
|
|
* Экран воспроизведения rpy-сценария.
|
|
* @author aNNiMON
|
|
*/
|
|
public final class ViewActivity extends Activity implements TouchGesture.OnTouchGestureListener {
|
|
|
|
public static final String EXTRA_NAME = "name";
|
|
public static final String EXTRA_SAVE = "save";
|
|
|
|
private static final FadeInfo NO_FADE = new FadeInfo(false, false, 0);
|
|
private static ViewActivity instance;
|
|
|
|
public static ViewActivity getInstance() {
|
|
return instance;
|
|
}
|
|
|
|
private String scriptPath;
|
|
|
|
private ImageView background;
|
|
private FrameLayout container;
|
|
private TextView textview;
|
|
private MediaPlayer musicPlayer, soundPlayer;
|
|
|
|
private Characters characters;
|
|
private Map<String, ImageView> spriteInContainer;
|
|
private DisplayMetrics displayMetrics;
|
|
|
|
@Override
|
|
protected void onCreate(Bundle savedInstanceState) {
|
|
super.onCreate(savedInstanceState);
|
|
|
|
// Постоянная подсветка.
|
|
getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON,
|
|
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
|
getWindow().setBackgroundDrawable(null);
|
|
// Размер экрана.
|
|
displayMetrics = new DisplayMetrics();
|
|
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
|
|
|
|
setContentView(R.layout.main);
|
|
instance = this;
|
|
|
|
background = (ImageView) findViewById(R.id.background);
|
|
container = (FrameLayout) findViewById(R.id.container);
|
|
textview = (TextView) findViewById(R.id.text);
|
|
|
|
background.setOnTouchListener(new TouchGesture(this, this));
|
|
|
|
spriteInContainer = new HashMap<String, ImageView>();
|
|
|
|
characters = new CharactersES();
|
|
characters.makeNamesKnown();
|
|
|
|
final Intent intent = getIntent();
|
|
final SaveInfo save = intent.getParcelableExtra(EXTRA_SAVE);
|
|
try {
|
|
InputStream stream = null;
|
|
if (save != null) {
|
|
stream = fromSave(save);
|
|
Toast.makeText(this, "Загружено", Toast.LENGTH_SHORT).show();
|
|
} else if (intent.hasExtra(EXTRA_NAME)) {
|
|
stream = fromAssets( PathResolver.script(intent.getStringExtra(EXTRA_NAME)) );
|
|
} else if (intent.getData() != null) {
|
|
stream = fromFile(intent.getData());
|
|
} else {
|
|
Toast.makeText(this, "Нет данных для отображения", Toast.LENGTH_LONG).show();
|
|
finish();
|
|
}
|
|
|
|
Parser.parse(Lexer.tokenize( IOUtil.readContents(stream) ), save);
|
|
Parser.getInstance().next();
|
|
stream = null;
|
|
} catch (Exception ex) {
|
|
final String message = "Ошибка при открытии файла " + scriptPath;
|
|
if (Logger.DEBUG) Logger.log(message, ex);
|
|
Toast.makeText(this, message, Toast.LENGTH_LONG).show();
|
|
finish();
|
|
}
|
|
}
|
|
|
|
private InputStream fromAssets(String path) throws IOException {
|
|
scriptPath = path;
|
|
return getAssets().open(scriptPath);
|
|
}
|
|
|
|
private InputStream fromFile(Uri fileUri) throws IOException {
|
|
scriptPath = fileUri.toString();
|
|
return getContentResolver().openInputStream(fileUri);
|
|
}
|
|
|
|
private InputStream fromSave(SaveInfo save) throws IOException {
|
|
if (save.getPath().startsWith(ContentResolver.SCHEME_FILE)) {
|
|
return fromFile(Uri.parse(save.getPath()));
|
|
}
|
|
return fromAssets(save.getPath());
|
|
}
|
|
|
|
@Override
|
|
protected void onPause() {
|
|
stopMusic(NO_FADE);
|
|
stopSound(NO_FADE);
|
|
safeClearImageView(background);
|
|
spritesClear();
|
|
Parser.release();
|
|
super.onPause();
|
|
}
|
|
|
|
@Override
|
|
public void onTouchGesture(TouchGestureType type) {
|
|
switch (type) {
|
|
case SINGLE_TAP:
|
|
Parser.getInstance().next();
|
|
break;
|
|
case SWIPE:
|
|
Dialogs.with(this).showMenu(R.array.menu_items, menu);
|
|
break;
|
|
}
|
|
}
|
|
|
|
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
|
if (keyCode == KeyEvent.KEYCODE_MENU) {
|
|
Dialogs.with(this).showMenu(R.array.menu_items, menu);
|
|
return true;
|
|
}
|
|
return super.onKeyUp(keyCode, event);
|
|
}
|
|
|
|
private void onMenuItemSelected(int item) {
|
|
switch (item) {
|
|
case 0:
|
|
Parser.getInstance().prevScene();
|
|
break;
|
|
case 1:
|
|
Parser.getInstance().nextScene();
|
|
break;
|
|
case 2:
|
|
Dialogs.with(getInstance()).showNavigate();
|
|
break;
|
|
case 3:
|
|
saveState();
|
|
break;
|
|
case 4:
|
|
showLoadStateDialog();
|
|
break;
|
|
}
|
|
}
|
|
|
|
public void windowShow() {
|
|
if (textview.getVisibility() != View.VISIBLE)
|
|
textview.setVisibility(View.VISIBLE);
|
|
}
|
|
|
|
public void windowHide() {
|
|
if (textview.getVisibility() != View.INVISIBLE)
|
|
textview.setVisibility(View.INVISIBLE);
|
|
}
|
|
|
|
public void background(String type, String name, String effect) {
|
|
spritesClear();
|
|
if (name.equalsIgnoreCase("black")) background.setImageResource(android.R.color.black);
|
|
else if (name.equalsIgnoreCase("white")) background.setImageResource(android.R.color.white);
|
|
else {
|
|
try {
|
|
background.setImageBitmap(IOUtil.readBitmap(PathResolver.background(type, name)));
|
|
} catch (Exception ioe) {
|
|
if (Logger.DEBUG) Logger.log("background: " + type + ", " + name, ioe);
|
|
background.setImageResource(android.R.color.black);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void spritesClear() {
|
|
container.removeAllViews();
|
|
for (ImageView iv : spriteInContainer.values()) {
|
|
safeClearImageView(iv);
|
|
}
|
|
spriteInContainer.clear();
|
|
}
|
|
|
|
public void sprite(String whoid, String params, String position, String alias) {
|
|
ImageView img;
|
|
final String key = TextUtils.isEmpty(alias) ? whoid : alias;
|
|
if (spriteInContainer.containsKey(key)) {
|
|
img = spriteInContainer.get(key);
|
|
} else {
|
|
img = new ImageView(this);
|
|
spriteInContainer.put(key, img);
|
|
}
|
|
final FrameLayout.LayoutParams flp = new FrameLayout.LayoutParams(
|
|
FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.MATCH_PARENT);
|
|
setSpritePosition(img, flp, position);
|
|
final String path = PathResolver.sprite(whoid, params);
|
|
try {
|
|
img.setImageBitmap(IOUtil.readBitmap(path));
|
|
if (container != img.getParent())
|
|
container.addView(img, flp);
|
|
} catch (Exception ioe) {
|
|
if (Logger.DEBUG) Logger.log("sprite: " + path, ioe);
|
|
}
|
|
}
|
|
|
|
public void hideSprite(String whoid) {
|
|
if (!spriteInContainer.containsKey(whoid)) return;
|
|
final ImageView img = spriteInContainer.get(whoid);
|
|
container.removeView(img);
|
|
spriteInContainer.remove(img);
|
|
safeClearImageView(img);
|
|
}
|
|
|
|
@SuppressLint("RtlHardcoded")
|
|
private void setSpritePosition(ImageView img, FrameLayout.LayoutParams params, String position) {
|
|
// Позиционирование спрайта. fleft, left, cleft, center, cright, right, fright
|
|
final int width = displayMetrics.widthPixels;
|
|
if (TextUtils.isEmpty(position) || position.equals("center")) {
|
|
params.setMargins(0, 0, 0, 0);
|
|
params.gravity = Gravity.CENTER_HORIZONTAL;
|
|
img.setScaleType(ImageView.ScaleType.FIT_CENTER);
|
|
} else if (position.equals("left")) {
|
|
params.setMargins(0, 0, width/2, 0);
|
|
params.gravity = Gravity.LEFT;
|
|
img.setScaleType(ImageView.ScaleType.FIT_CENTER);
|
|
} else if (position.equals("cleft")) {
|
|
params.setMargins(0, 0, width/3, 0);
|
|
params.gravity = Gravity.LEFT;
|
|
img.setScaleType(ImageView.ScaleType.FIT_CENTER);
|
|
} else if (position.equals("right")) {
|
|
params.setMargins(width/2, 0, 0, 0);
|
|
params.gravity = Gravity.RIGHT;
|
|
img.setScaleType(ImageView.ScaleType.FIT_CENTER);
|
|
} else if (position.equals("cright")) {
|
|
params.setMargins(width/3, 0, 0, 0);
|
|
params.gravity = Gravity.RIGHT;
|
|
img.setScaleType(ImageView.ScaleType.FIT_CENTER);
|
|
} else if (position.equals("fleft")) {
|
|
params.setMargins(0, 0, 0, 0);
|
|
params.gravity = Gravity.LEFT;
|
|
img.setScaleType(ImageView.ScaleType.FIT_START);
|
|
} else if (position.equals("fright")) {
|
|
params.setMargins(0, 0, 0, 0);
|
|
params.gravity = Gravity.RIGHT;
|
|
img.setScaleType(ImageView.ScaleType.FIT_END);
|
|
}
|
|
}
|
|
|
|
public void pause(final long duration) {
|
|
text("");
|
|
new Thread(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
try {
|
|
Thread.sleep(duration);
|
|
} catch (InterruptedException e) { }
|
|
runOnUiThread(nextCommandRunnable);
|
|
}
|
|
}).start();
|
|
}
|
|
|
|
public void text(String text) {
|
|
if (TextUtils.isEmpty(text)) windowHide();
|
|
else {
|
|
windowShow();
|
|
textview.setText(formatString(text));
|
|
}
|
|
}
|
|
|
|
public void text(String whoid, String text) {
|
|
if (whoid.equalsIgnoreCase("th")) text("~ " + text + " ~");
|
|
else if (!characters.contains(whoid)) text(text);
|
|
else {
|
|
windowShow();
|
|
final Characters.NameInfo person = characters.get(whoid);
|
|
final String who = person.name;
|
|
Spannable spannable = formatString(who + "\n" + text);
|
|
spannable.setSpan(new ForegroundColorSpan(person.color), 0, who.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
|
textview.setText(spannable);
|
|
}
|
|
}
|
|
|
|
private Spannable formatString(String text) {
|
|
String edited = text.replaceAll("\\{w.*?\\}", "");
|
|
final char[] codes = {'b','i','s','u'};
|
|
boolean html = false;
|
|
for (int i = 0; i < codes.length; i++) {
|
|
final char ch = codes[i];
|
|
if (edited.contains("{"+ch+"}")) {
|
|
edited = edited.replace("{"+ch+"}", "<"+ch+">");
|
|
edited = edited.replace("{/"+ch+"}", "</"+ch+">");
|
|
html = true;
|
|
}
|
|
}
|
|
if (html) edited = edited.replace("\n", "<br/>");
|
|
return new SpannableString(html ? Html.fromHtml(edited) : edited);
|
|
}
|
|
|
|
public void makeNamesKnown() {
|
|
characters.makeNamesKnown();
|
|
}
|
|
|
|
public void makeNamesUnknown() {
|
|
characters.makeNamesUnknown();
|
|
}
|
|
|
|
public void meet(String whoid, String name) {
|
|
characters.setName(whoid, name);
|
|
}
|
|
|
|
public void menu(final Menu menu) {
|
|
Dialogs.with(this).showGameMenu(menu, new DialogInterface.OnClickListener() {
|
|
@Override
|
|
public void onClick(DialogInterface dialog, int item) {
|
|
dialog.dismiss();
|
|
Parser.getInstance().setPosition(menu.getPosition(item));
|
|
}
|
|
});
|
|
}
|
|
|
|
public void music(String name, FadeInfo fade) {
|
|
try {
|
|
stopMusic(fade);
|
|
musicPlayer = new MediaPlayer();
|
|
musicPlayer.setDataSource( IOUtil.getFD(PathResolver.music(name)) );
|
|
musicPlayer.prepare();
|
|
musicPlayer.setVolume(1f, 1f);
|
|
musicPlayer.setLooping(true);
|
|
musicPlayer.start();
|
|
} catch (Exception e) {
|
|
if (Logger.DEBUG) Logger.log("music: " + name, e);
|
|
}
|
|
}
|
|
|
|
public void stopMusic(FadeInfo fade) {
|
|
if (musicPlayer == null) return;
|
|
if (musicPlayer.isPlaying()) {
|
|
musicPlayer.stop();
|
|
musicPlayer.release();
|
|
}
|
|
musicPlayer = null;
|
|
}
|
|
|
|
public void sound(String name, boolean loop, FadeInfo fade) {
|
|
try {
|
|
stopSound(fade);
|
|
soundPlayer = new MediaPlayer();
|
|
soundPlayer.setDataSource( IOUtil.getFD(PathResolver.sound(name)) );
|
|
soundPlayer.prepare();
|
|
soundPlayer.setVolume(1f, 1f);
|
|
soundPlayer.setLooping(loop);
|
|
soundPlayer.start();
|
|
} catch (Exception e) {
|
|
if (Logger.DEBUG) Logger.log("sound: " + name, e);
|
|
}
|
|
}
|
|
|
|
public void stopSound(FadeInfo fade) {
|
|
if (soundPlayer == null) return;
|
|
if (soundPlayer.isPlaying()) {
|
|
soundPlayer.stop();
|
|
soundPlayer.release();
|
|
}
|
|
soundPlayer = null;
|
|
}
|
|
|
|
private static void safeClearImageView(ImageView iv) {
|
|
if (iv == null) return;
|
|
iv.setImageDrawable(null);
|
|
iv = null;
|
|
}
|
|
|
|
private void showLoadStateDialog() {
|
|
final List<SaveInfo> saves = IOUtil.listSaves(getApplicationContext());
|
|
if (saves == null || saves.isEmpty()) {
|
|
Toast.makeText(this, "Нет доступных сохранений", Toast.LENGTH_SHORT).show();
|
|
return;
|
|
}
|
|
Dialogs.with(this).showSaves(saves, new DialogInterface.OnClickListener() {
|
|
@Override
|
|
public void onClick(DialogInterface dialog, int which) {
|
|
final SaveInfo save = saves.get(which);
|
|
if (save.getPath().equals(scriptPath)) {
|
|
// Восстанавливаем сохранение текущего сценария.
|
|
Variables.setVariables(save.getVariables());
|
|
Parser.getInstance().setPosition(save.getPosition());
|
|
Toast.makeText(ViewActivity.this, "Загружено", Toast.LENGTH_SHORT).show();
|
|
} else {
|
|
// Пересоздаём активити
|
|
final Intent intent = new Intent(ViewActivity.this, ViewActivity.class);
|
|
intent.putExtra(ViewActivity.EXTRA_SAVE, save);
|
|
startActivity(intent);
|
|
finish();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
private void saveState() {
|
|
final long timestamp = System.currentTimeMillis();
|
|
final SaveInfo info = new SaveInfo();
|
|
info.setPath(scriptPath);
|
|
info.setPosition(Parser.getInstance().getPosition());
|
|
info.setTime(timestamp);
|
|
info.setVariables(Variables.getVariables());
|
|
try {
|
|
final String filename = Long.toString(timestamp);
|
|
IOUtil.writeSaveInfo(getApplicationContext(), filename, info);
|
|
Toast.makeText(this, "Сохранено", Toast.LENGTH_SHORT).show();
|
|
} catch (IOException ioe) {
|
|
if (Logger.DEBUG) Logger.log("Ошибка сохранения", ioe);
|
|
}
|
|
}
|
|
|
|
private final Runnable nextCommandRunnable = new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
Parser.getInstance().next();
|
|
}
|
|
};
|
|
|
|
private final DialogInterface.OnClickListener menu = new DialogInterface.OnClickListener() {
|
|
|
|
@Override
|
|
public void onClick(DialogInterface dialog, int item) {
|
|
onMenuItemSelected(item);
|
|
}
|
|
};
|
|
}
|