RpyPlayer/src/com/annimon/everlastingsummer/ViewActivity.java

641 lines
22 KiB
Java
Raw Normal View History

2015-04-04 15:09:43 +03:00
package com.annimon.everlastingsummer;
import java.io.IOException;
import java.io.InputStream;
2015-04-04 15:09:43 +03:00
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
2015-04-04 15:09:43 +03:00
import java.util.Map;
import com.annimon.everlastingsummer.TouchGesture.TouchGestureType;
import com.annimon.everlastingsummer.ast.Variables;
import android.annotation.SuppressLint;
2015-04-04 15:09:43 +03:00
import android.app.Activity;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
2015-04-04 15:09:43 +03:00
import android.media.MediaPlayer;
import android.net.Uri;
2015-04-04 15:09:43 +03:00
import android.os.Bundle;
import android.text.*;
import android.text.style.ForegroundColorSpan;
import android.util.DisplayMetrics;
import android.view.Gravity;
import android.view.KeyEvent;
2015-04-04 15:09:43 +03:00
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 {
2015-04-04 15:09:43 +03:00
public static final String EXTRA_NAME = "name";
public static final String EXTRA_SAVE = "save";
private static final String STATE_SAVEINFO = "saveinfo";
private static final int
ITEM_PREV_SCENE = 0,
ITEM_NEXT_SCENE = 1,
ITEM_NAVIGATE = 2,
ITEM_SAVE = 3,
ITEM_LOAD = 4;
2015-04-04 15:09:43 +03:00
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 String backgroundName, backgroundType;
2015-04-04 15:09:43 +03:00
private ImageView background;
private FrameLayout container;
private TextView textview;
private MediaPlayer musicPlayer, soundPlayer;
private LinkedList<String> musicQueue, soundQueue;
2015-04-04 15:09:43 +03:00
private MapPlaces places;
private Characters characters;
2015-04-04 15:09:43 +03:00
private Map<String, ImageView> spriteInContainer;
private DisplayMetrics displayMetrics;
private boolean blockTap;
2015-04-04 15:09:43 +03:00
@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);
2015-04-04 15:09:43 +03:00
setContentView(R.layout.main);
instance = this;
background = (ImageView) findViewById(R.id.background);
container = (FrameLayout) findViewById(R.id.container);
textview = (TextView) findViewById(R.id.text);
musicQueue = new LinkedList<String>();
soundQueue = new LinkedList<String>();
background.setOnTouchListener(new TouchGesture(this, this));
2015-04-04 15:09:43 +03:00
spriteInContainer = new HashMap<String, ImageView>();
characters = new CharactersES();
characters.makeNamesKnown();
2015-04-04 15:09:43 +03:00
places = new MapPlacesES();
final Intent intent = getIntent();
final SaveInfo save;
if (savedInstanceState != null) {
save = savedInstanceState.getParcelable(STATE_SAVEINFO);
} else {
save = intent.getParcelableExtra(EXTRA_SAVE);
}
2015-04-04 15:09:43 +03:00
try {
InputStream stream = null;
if (save != null) {
stream = fromSave(save);
} 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, R.string.no_data, Toast.LENGTH_LONG).show();
finish();
}
Parser.parse(Lexer.tokenize( IOUtil.readContents(stream) ), save);
2015-04-14 19:14:54 +03:00
stream = null;
2015-04-04 15:09:43 +03:00
} catch (Exception ex) {
final String message = getString(R.string.error_open_file, scriptPath);
if (Logger.DEBUG) Logger.log(message, ex);
Toast.makeText(this, message, Toast.LENGTH_LONG).show();
2015-04-04 15:09:43 +03:00
finish();
}
if (save != null) {
backgroundType = save.getBackgroundType();
backgroundName = save.getBackgroundName();
Toast.makeText(this, R.string.loaded, Toast.LENGTH_SHORT).show();
}
if (backgroundName != null) {
background(backgroundType, backgroundName, "");
}
Parser.getInstance().next();
2015-04-04 15:09:43 +03:00
}
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());
}
2015-04-04 15:09:43 +03:00
@Override
protected void onPause() {
stopMusic(NO_FADE);
stopSound(NO_FADE);
super.onPause();
}
@Override
protected void onDestroy() {
2015-04-14 19:14:54 +03:00
safeClearImageView(background);
spritesClear();
Parser.release();
super.onDestroy();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
final SaveInfo info = createSave();
outState.putParcelable(STATE_SAVEINFO, info);
super.onSaveInstanceState(outState);
2015-04-04 15:09:43 +03:00
}
@Override
public void onTouchGesture(TouchGestureType type) {
switch (type) {
case SINGLE_TAP:
if (!blockTap) Parser.getInstance().next();
break;
case SWIPE:
Dialogs.with(this).showMenu(R.array.menu_items, menu);
break;
}
}
@Override
public void onBackPressed() {
Dialogs.with(this).showMenu(R.array.exit_items, exitMenu);
}
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (event.getRepeatCount() != 0) {
return super.onKeyUp(keyCode, event);
}
switch (keyCode) {
case KeyEvent.KEYCODE_SPACE:
case KeyEvent.KEYCODE_BUTTON_A:
// Далее
onTouchGesture(TouchGestureType.SINGLE_TAP);
return true;
case KeyEvent.KEYCODE_MENU:
case KeyEvent.KEYCODE_ENTER:
case KeyEvent.KEYCODE_BUTTON_START:
// Меню
onTouchGesture(TouchGestureType.SWIPE);
return true;
case KeyEvent.KEYCODE_Q:
case KeyEvent.KEYCODE_BUTTON_SELECT:
// Меню выхода
onBackPressed();
return true;
case KeyEvent.KEYCODE_BUTTON_R1:
case KeyEvent.KEYCODE_S:
onMenuItemSelected(ITEM_SAVE);
return true;
case KeyEvent.KEYCODE_BUTTON_L1:
case KeyEvent.KEYCODE_L:
onMenuItemSelected(ITEM_LOAD);
return true;
case KeyEvent.KEYCODE_BUTTON_B:
onMenuItemSelected(ITEM_NAVIGATE);
return true;
case KeyEvent.KEYCODE_BUTTON_X:
onMenuItemSelected(ITEM_PREV_SCENE);
return true;
case KeyEvent.KEYCODE_BUTTON_Y:
case KeyEvent.KEYCODE_DEL:
windowSwitchVisibility();
return true;
}
return super.onKeyUp(keyCode, event);
}
private void onMenuItemSelected(int item) {
switch (item) {
case ITEM_PREV_SCENE:
Parser.getInstance().prevScene();
break;
case ITEM_NEXT_SCENE:
Parser.getInstance().nextScene();
break;
case ITEM_NAVIGATE:
Dialogs.with(getInstance()).showNavigate();
break;
case ITEM_SAVE:
saveState();
break;
case ITEM_LOAD:
showLoadStateDialog();
break;
}
}
2015-04-04 15:09:43 +03:00
public void windowShow() {
if (textview.getVisibility() != View.VISIBLE)
textview.setVisibility(View.VISIBLE);
}
public void windowHide() {
if (textview.getVisibility() != View.INVISIBLE)
textview.setVisibility(View.INVISIBLE);
}
private void windowSwitchVisibility() {
final boolean isVisible = textview.getVisibility() == View.VISIBLE;
textview.setVisibility(isVisible ? View.INVISIBLE : View.VISIBLE);
}
2015-04-04 15:09:43 +03:00
public void background(String type, String name, String effect) {
if (TextUtils.isEmpty(name)) return;
backgroundType = type;
backgroundName = name;
text("");
2015-04-04 15:09:43 +03:00
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);
2015-04-04 15:09:43 +03:00
background.setImageResource(android.R.color.black);
}
}
}
public void spritesClear() {
container.removeAllViews();
2015-04-14 19:14:54 +03:00
for (ImageView iv : spriteInContainer.values()) {
safeClearImageView(iv);
}
2015-04-04 15:09:43 +03:00
spriteInContainer.clear();
}
public void sprite(String whoid, String params, String position, String alias) {
2015-04-04 15:09:43 +03:00
ImageView img;
final String key = TextUtils.isEmpty(alias) ? whoid : alias;
if (spriteInContainer.containsKey(key)) {
img = spriteInContainer.get(key);
2015-04-04 15:09:43 +03:00
} else {
img = new ImageView(this);
spriteInContainer.put(key, img);
2015-04-04 15:09:43 +03:00
}
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);
2015-04-04 15:09:43 +03:00
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);
}
2015-04-04 15:09:43 +03:00
}
public void hideSprite(String whoid) {
if (!spriteInContainer.containsKey(whoid)) return;
final ImageView img = spriteInContainer.get(whoid);
container.removeView(img);
2015-04-14 19:14:54 +03:00
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, final boolean hard) {
blockTap = hard;
2015-04-04 15:09:43 +03:00
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);
2015-04-04 15:09:43 +03:00
else {
windowShow();
final Characters.NameInfo person = characters.get(whoid);
2015-04-04 15:09:43 +03:00
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 String[] codes = {"b","i","s","u","big","small"};
2015-04-04 15:09:43 +03:00
boolean html = false;
for (int i = 0; i < codes.length; i++) {
final String ch = codes[i];
2015-04-04 15:09:43 +03:00
if (edited.contains("{"+ch+"}")) {
edited = edited.replace("{"+ch+"}", "<"+ch+">");
edited = edited.replace("{/"+ch+"}", "</"+ch+">");
html = true;
}
}
if (html) edited = edited.replace("\n", "<br/>");
2015-04-04 15:09:43 +03:00
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 disableAllZones() {
places.disableAllZones();
}
public void disableCurrentZone() {
places.disableCurrentZone();
}
public void resetZone(String zone) {
places.resetZone(zone);
}
public void setZone(String name, String label) {
places.setZone(name, label);
}
public void showMap() {
places.showMap(this);
}
2015-04-12 00:20:02 +03:00
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 addMusicToQueue(String name) {
if ( (musicPlayer != null) && (musicPlayer.isPlaying()) ) {
// Если музыка играет, просто добавляем в очередь
musicQueue.addLast(name);
} else {
// Воспроизводим
music(name, NO_FADE);
}
}
public void addSoundToQueue(String name) {
if ( (soundPlayer != null) && (soundPlayer.isPlaying()) ) {
soundQueue.addLast(name);
} else {
sound(name, false, NO_FADE);
}
}
2015-04-04 15:09:43 +03:00
public void music(String name, FadeInfo fade) {
try {
stopMusic(fade);
musicPlayer = new MediaPlayer();
musicPlayer.setOnCompletionListener(musicCompleteListener);
2015-04-04 15:09:43 +03:00
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);
2015-04-04 15:09:43 +03:00
}
}
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.setOnCompletionListener(soundCompleteListener);
2015-04-04 15:09:43 +03:00
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);
2015-04-04 15:09:43 +03:00
}
}
public void stopSound(FadeInfo fade) {
if (soundPlayer == null) return;
if (soundPlayer.isPlaying()) {
soundPlayer.stop();
soundPlayer.release();
}
soundPlayer = null;
}
2015-04-14 19:14:54 +03:00
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, R.string.no_saves, 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());
if (!TextUtils.isEmpty(save.getBackgroundName())) {
backgroundType = save.getBackgroundType();
backgroundName = save.getBackgroundName();
ViewActivity.getInstance().background(backgroundType, backgroundName, "");
}
Parser.getInstance().setPosition(save.getPosition());
Toast.makeText(ViewActivity.this, R.string.loaded, 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 SaveInfo info = createSave();
try {
final String filename = Long.toString(info.getTime());
IOUtil.writeSaveInfo(getApplicationContext(), filename, info);
Toast.makeText(this, R.string.saved, Toast.LENGTH_SHORT).show();
} catch (IOException ioe) {
if (Logger.DEBUG) Logger.log("Ошибка сохранения", ioe);
}
}
private SaveInfo createSave() {
final SaveInfo info = new SaveInfo();
info.setPath(scriptPath);
info.setPosition(Parser.getInstance().getLastPosition());
info.setTime(System.currentTimeMillis());
info.setVariables(Variables.getVariables());
info.setBackgroundType(backgroundType);
info.setBackgroundName(backgroundName);
return info;
}
2015-04-04 15:09:43 +03:00
private final Runnable nextCommandRunnable = new Runnable() {
@Override
public void run() {
blockTap = false;
2015-04-04 15:09:43 +03:00
Parser.getInstance().next();
}
};
private final DialogInterface.OnClickListener menu = new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int item) {
onMenuItemSelected(item);
}
};
private final DialogInterface.OnClickListener exitMenu = new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int item) {
switch (item) {
case 0:
saveState();
break;
case 1:
finish();
break;
}
}
};
private final MediaPlayer.OnCompletionListener musicCompleteListener = new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
if (musicQueue.size() > 0) {
music(musicQueue.removeFirst(), NO_FADE);
}
}
};
private final MediaPlayer.OnCompletionListener soundCompleteListener = new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
if (soundQueue.size() > 0) {
sound(soundQueue.removeFirst(), false, NO_FADE);
}
}
};
2015-04-04 15:09:43 +03:00
}