package com.annimon.everlastingsummer; import java.io.InputStream; import java.util.HashMap; import java.util.Map; import com.annimon.everlastingsummer.TouchGesture.TouchGestureType; import android.annotation.SuppressLint; import android.app.Activity; 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"; private static final FadeInfo NO_FADE = new FadeInfo(false, false, 0); private static ViewActivity instance; public static ViewActivity getInstance() { return instance; } private ImageView background; private FrameLayout container; private TextView textview; private MediaPlayer musicPlayer, soundPlayer; private Map names; private Map 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(); // Маппинг <короткое_имя, имя/цвет> names = new HashMap(); // https://github.com/yakui-lover/eroge-dopil/blob/master/media.rpy#L365 names.put("me", new Person("Семён", 0xFFE1DD7D)); names.put("un", new Person("Лена", 0xFFB956FF)); // night 0xFFAA64D9, sunset: 0xFFB956FF, day: 0xFFB956FF, prolog: 0xFFB956FF names.put("dv", new Person("Алиса", 0xFFFFAA00)); // night 0xFFD28B10, sunset: 0xFFFFAA00, day: 0xFFFFAA00, prolog: 0xFFFFAA00 names.put("sl", new Person("Славя", 0xFFFFD200)); // night 0xFFD6B000, sunset: 0xFFFFD200, day: 0xFFFFD200, prolog: 0xFFFFD200 names.put("us", new Person("Ульяна", 0xFFFF3200)); // night 0xFFEA3700, sunset: 0xFFFF3200, day: 0xFFFF3200, prolog: 0xFFFF3200 names.put("mt", new Person("Ольга Дмитриевна", 0xFF00EA32)); // night 0xFF00B627, sunset: 0xFF00EA32, day: 0xFF00EA32, prolog: 0xFF00EA32 names.put("cs", new Person("Виола", 0xFFA5A5FF)); // night 0xFF8686E6, sunset: 0xFFA5A5FF, day: 0xFFA5A5FF, prolog: 0xFFA5A5FF names.put("mz", new Person("Женя", 0xFF4A86FF)); // night 0xFF5481DB, sunset: 0xFF72A0FF, day: 0xFF4A86FF, prolog: 0xFF4A86FF names.put("mi", new Person("Мику", 0xFF00DEFF)); // night 0xFF00B4CF, sunset: 0xFF00FBFF, day: 0xFF00DEFF, prolog: 0xFF00DEFF names.put("uv", new Person("Юля", 0xFF4EFF00)); // night 0xFF40D000, sunset: 0xFF4EFF00, day: 0xFF4EFF00, prolog: 0xFF4EFF00 names.put("lk", new Person("Луркмор-кун", 0xFFFF8080)); names.put("sh", new Person("Шурик", 0xFFFFF226)); // night 0xFFCDC212, sunset: 0xFFFFF226, day: 0xFFFFF226, prolog: 0xFFFFF226 names.put("el", new Person("Электроник", 0xFFFFFF00)); // night 0xFFCDCD00, sunset: 0xFFFFFF00, day: 0xFFFFFF00, prolog: 0xFFFFFF00 names.put("pi", new Person("Пионер", 0xFFE60101)); // night 0xFFE60000, sunset: 0xFFE60000, day: 0xFFE60101, prolog: 0xFFE60000 names.put("dy", new Person("Голос из динамика", 0xFFC0C0C0)); names.put("voice", new Person("Голос", 0xFFE1DD7D)); names.put("voices", new Person("Голоса", 0xFFC0C0C0)); names.put("message", new Person("Сообщение", 0xFFC0C0C0)); names.put("all", new Person("Пионеры", 0xFFED4444)); // night 0xFFE33A3A, sunset: 0xFFE33A3A, day: 0xFFED4444, prolog: 0xFFE33A3A names.put("kids", new Person("Малышня", 0xFFEB7883)); names.put("dreamgirl", new Person("...", 0xFFC0C0C0)); names.put("bush", new Person("Голос", 0xFFC0C0C0)); names.put("FIXME_voice", new Person("Голос", 0xFFC0C0C0)); names.put("odn", new Person("Одногруппник", 0xFFC0C0C0)); names.put("mt_voice", new Person("Голос", 0xFF00EA32)); // night 0xFF00B627, sunset: 0xFF00EA32, day: 0xFF00EA32, prolog: 0xFF00EA32 String scriptpath = ""; final Intent intent = getIntent(); try { InputStream stream = null; if (intent.hasExtra(EXTRA_NAME)) { scriptpath = PathResolver.script( intent.getStringExtra(EXTRA_NAME) ); stream = getAssets().open(scriptpath); } else if (intent.getData() != null) { final Uri fileUri = intent.getData(); scriptpath = fileUri.getPath(); stream = getContentResolver().openInputStream(fileUri); } else { Toast.makeText(this, "Нет данных для отображения", Toast.LENGTH_LONG).show(); finish(); } Parser.parse(Lexer.tokenize( IOUtil.readContents(stream) )); Parser.getInstance().next(); } catch (Exception ex) { final String message = "Ошибка при открытии файла " + scriptpath; Logger.log(message, ex); Toast.makeText(this, message, Toast.LENGTH_LONG).show(); finish(); } } @Override protected void onPause() { stopMusic(NO_FADE); stopSound(NO_FADE); 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; } } 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) { Logger.log("background: " + type + ", " + name, ioe); background.setImageResource(android.R.color.black); } } } public void spritesClear() { container.removeAllViews(); spriteInContainer.clear(); } public void sprite(String whoid, String params, String position) { ImageView img; if (spriteInContainer.containsKey(whoid)) { img = spriteInContainer.get(whoid); } else { img = new ImageView(this); spriteInContainer.put(whoid, 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) { Logger.log("sprite: " + path, ioe); } } public void hideSprite(String whoid) { if (!spriteInContainer.containsKey(whoid)) return; final ImageView img = spriteInContainer.get(whoid); spriteInContainer.remove(img); container.removeView(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 (!names.containsKey(whoid)) text(text); else { windowShow(); final Person person = names.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+"}", ""); html = true; } } return new SpannableString(html ? Html.fromHtml(edited) : edited); } 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) { 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) { Logger.log("sound: " + name, e); } } public void stopSound(FadeInfo fade) { if (soundPlayer == null) return; if (soundPlayer.isPlaying()) { soundPlayer.stop(); soundPlayer.release(); } soundPlayer = null; } 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); } }; private class Person { String name; int color; Person(String fullName, int color) { this.name = fullName; this.color = color; } } }