/**
* Copyright (C) 2013 Gundog Studios LLC.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.godsandtowers;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.restlet.resource.ClientResource;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.WindowManager;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;
import com.godsandtowers.core.GameEngine;
import com.godsandtowers.core.GameInfo;
import com.godsandtowers.core.PlayerSaver;
import com.godsandtowers.core.PlayerStats;
import com.godsandtowers.core.networking.ApplicationInfo;
import com.godsandtowers.core.networking.GetAppInfoResource;
import com.godsandtowers.graphics.game.BitmapCache;
import com.godsandtowers.graphics.menu.MenuLayoutManager;
import com.godsandtowers.messaging.ApplicationHandler;
import com.godsandtowers.messaging.ApplicationMessageProcessor;
import com.godsandtowers.util.ADS;
import com.godsandtowers.util.AppRater;
import com.godsandtowers.util.Constants;
import com.godsandtowers.util.ResourceUtilities;
import com.godsandtowers.util.TDWAndroidPreferences;
import com.godsandtowers.util.TDWPreferences;
import com.gundogstudios.gl.ModelUtils;
import com.gundogstudios.modules.AndroidAssets;
import com.gundogstudios.modules.AndroidAudio;
import com.gundogstudios.modules.AndroidGLES11;
import com.gundogstudios.modules.AndroidLogger;
import com.gundogstudios.modules.AndroidReporter;
import com.gundogstudios.modules.AssetModule;
import com.gundogstudios.modules.Modules;
import com.gundogstudios.modules.PurchaseModule;
import com.gundogstudios.modules.basic.BasicMessageModule;
import com.gundogstudios.modules.basic.BasicNetworkModule;
import com.gundogstudios.modules.basic.BasicProfilerModule;
import com.gundogstudios.modules.basic.EmptyLogger;
import com.gundogstudios.modules.basic.EmptyProfilerModule;
import com.gundogstudios.util.FastMath;
public abstract class MainActivity extends Activity {
protected abstract void initPurchaser();
private static final String TAG = "MainActivity";
public static final String GAME_RESULTS = "GameResults";
public static final String PLAYER_STATS = "PlayerStats";
public static final String SAVE_STATS_FILENAME = "stats.gat";
public static final String SAVE_GAME_FILENAME = "game.gat";
private PlayerStats stats;
private GameStateManager game;
private MenuLayoutManager menu;
private boolean firstLoad = true;
public void clearGL() {
game.clearGL();
}
public void displayGame() {
if (Modules.PREFERENCES.get(TDWPreferences.SOUND, true)) {
int song = FastMath.round(FastMath.random(1, 10));
Modules.AUDIO.play("" + song, true);
}
game.resume();
RelativeLayout gameLayout = (RelativeLayout) super.findViewById(R.id.gameLayout);
gameLayout.setVisibility(View.VISIBLE);
LinearLayout menuLayout = (LinearLayout) super.findViewById(R.id.menuLayout);
menuLayout.setVisibility(View.GONE);
// This code is key to ensure that the normal Views receive the button commands, not the GLView.
// Without this the back button and the menu buttons will not work
gameLayout.setFocusable(true);
gameLayout.setFocusableInTouchMode(true);
gameLayout.requestFocus();
}
public void displayMenu() {
if (Modules.PREFERENCES.get(TDWPreferences.SOUND, true)) {
Modules.AUDIO.play("theme", true);
}
menu.reset();
game.pause();
displayResume();
RelativeLayout gameLayout = (RelativeLayout) super.findViewById(R.id.gameLayout);
gameLayout.setVisibility(View.GONE);
LinearLayout menuLayout = (LinearLayout) super.findViewById(R.id.menuLayout);
menuLayout.setVisibility(View.VISIBLE);
// This code is key to ensure that the normal Views receive the button commands, not the GLView.
// Without this the back button and the menu buttons will not work
menuLayout.setFocusable(true);
menuLayout.setFocusableInTouchMode(true);
menuLayout.requestFocus();
displayLogo();
}
private void displayLogo() {
final ScrollView scrollableLeftLayout = (ScrollView) super.findViewById(R.id.scrollableLeftLayout);
final ScrollView scrollableRightLayout = (ScrollView) super.findViewById(R.id.scrollableRightLayout);
final LinearLayout logoLayout = (LinearLayout) super.findViewById(R.id.logo_layout);
if (firstLoad) {
scrollableLeftLayout.setVisibility(View.GONE);
scrollableRightLayout.setVisibility(View.GONE);
logoLayout.setVisibility(View.VISIBLE);
BitmapDrawable drawable = new BitmapDrawable(this.getResources(),
BitmapCache.getBitmap(R.drawable.gundog_studios_logo));
logoLayout.setBackgroundDrawable(drawable);
AlphaAnimation animation = new AlphaAnimation(0f, 1f);
animation.setDuration(5000);
final Activity activity = this;
animation.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
scrollableLeftLayout.setVisibility(View.VISIBLE);
scrollableRightLayout.setVisibility(View.VISIBLE);
logoLayout.setVisibility(View.GONE);
firstLoad = false;
LinearLayout main = (LinearLayout) activity.findViewById(R.id.menuLayout);
BitmapDrawable drawable = new BitmapDrawable(activity.getResources(), BitmapCache
.getBitmap(R.drawable.menu_background));
main.setBackgroundDrawable(drawable);
}
});
logoLayout.startAnimation(animation);
logoLayout.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
scrollableLeftLayout.setVisibility(View.VISIBLE);
scrollableRightLayout.setVisibility(View.VISIBLE);
logoLayout.setVisibility(View.GONE);
logoLayout.setAnimation(null);
firstLoad = false;
LinearLayout main = (LinearLayout) activity.findViewById(R.id.menuLayout);
BitmapDrawable drawable = new BitmapDrawable(activity.getResources(), BitmapCache
.getBitmap(R.drawable.menu_background));
main.setBackgroundDrawable(drawable);
}
});
} else {
scrollableLeftLayout.setVisibility(View.VISIBLE);
scrollableRightLayout.setVisibility(View.VISIBLE);
logoLayout.setVisibility(View.GONE);
}
}
public void newGame(GameEngine engine) {
game.shutdown();
game.init(engine);
displayGame();
}
public void gameError(Exception e) {
e.printStackTrace();
Modules.LOG.error(TAG, "error in game engine " + e.toString());
game.shutdown();
displayMenu();
}
public void gameFinished(GameInfo gameInfo) {
Modules.LOG.info(TAG, "Game Finished " + gameInfo.getLength());
game.shutdown();
gameInfo.setNoXP(super.getPackageName().equals("com.godsandtowers.free"));
stats.update(gameInfo);
save();
RelativeLayout gameLayout = (RelativeLayout) super.findViewById(R.id.gameLayout);
gameLayout.setVisibility(View.GONE);
if (GameInfo.TUTORIAL != gameInfo.getGameType()) {
Intent intent = new Intent(MainActivity.this, ResultsActivity.class);
gameInfo.removePlayers();
intent.putExtra(GAME_RESULTS, gameInfo);
super.startActivity(intent);
} else {
menu.reset();
displayMenu();
}
}
public void reset() {
AlertDialog a = new AlertDialog(this) {
};
a.setTitle(R.string.options_resetPlayer);
final MainActivity activity = this;
DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (which == AlertDialog.BUTTON_POSITIVE) {
game.shutdown();
AssetModule assetModule = Modules.ASSETS;
Modules.LOG.info(TAG,
"Deleting previous game file " + assetModule.delete("", MainActivity.SAVE_STATS_FILENAME));
Modules.LOG.info(TAG,
"Deleting previous stats file " + assetModule.delete("", MainActivity.SAVE_GAME_FILENAME));
Toast toast = Toast.makeText(activity, activity.getText(R.string.manager_playerReset),
Toast.LENGTH_LONG);
toast.show();
stats.reset();
menu.reset();
Modules.PURCHASER.restoreTransactions();
displayMenu();
}
}
};
a.setButton(AlertDialog.BUTTON_POSITIVE, this.getText(R.string.options_reset), listener);
a.setButton(AlertDialog.BUTTON_NEGATIVE, this.getText(R.string.options_cancel), listener);
a.show();
}
public void clearAssets() {
AlertDialog a = new AlertDialog(this) {
};
a.setTitle(R.string.options_assetClear);
final MainActivity activity = this;
DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (which == AlertDialog.BUTTON_POSITIVE) {
// ((AndroidAssets) Modules.ASSETS).deleteAllAssets();
Toast toast = Toast.makeText(activity, R.string.options_clearing, Toast.LENGTH_LONG);
toast.show();
activity.finish();
}
}
};
a.setButton(AlertDialog.BUTTON_POSITIVE, this.getText(R.string.options_clear), listener);
a.setButton(AlertDialog.BUTTON_NEGATIVE, this.getText(R.string.options_cancel), listener);
a.show();
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
Modules.LOG.info(TAG, "onConfigurationChanged");
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (KeyEvent.KEYCODE_BACK == keyCode) {
onBackPressed();
return true;
}
if (game.isRunning()) {
return game.onKeyUp(keyCode, event);
}
return true;
}
public void onBackPressed() {
// super.onBackPressed();
Modules.LOG.info(TAG, "On Back Pressed");
RelativeLayout gameLayout = (RelativeLayout) super.findViewById(R.id.gameLayout);
if (gameLayout.isShown()) {
game.pause();
displayMenu();
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
System.setProperty("java.net.preferIPv6Addresses", "false");
super.onCreate(savedInstanceState);
super.setContentView(R.layout.mainlayout);
super.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
ADS.setActivity(this);
ResourceUtilities.init(super.getResources());
BitmapCache.init(super.getResources());
Modules.LOG = Constants.TESTING ? new AndroidLogger() : new EmptyLogger();
Modules.AUDIO = new AndroidAudio(this.getAssets());
Modules.ASSETS = new AndroidAssets(this.getAssets());
initPurchaser();
Modules.GL = new AndroidGLES11();
Modules.PROFILER = Constants.TESTING ? new BasicProfilerModule() : new EmptyProfilerModule();
Modules.MESSENGER = new BasicMessageModule();
Modules.PREFERENCES = new TDWAndroidPreferences(this);
Modules.REPORTER = new AndroidReporter();
Modules.NETWORKING = new BasicNetworkModule();
Modules.LOG.info(TAG, "onCreate " + super.getPackageName());
Modules.MESSENGER.register(ApplicationMessageProcessor.ID, new ApplicationHandler(this));
if (savedInstanceState != null) {
Modules.LOG.info(TAG, "Trying to load from savedInstanceState");
stats = (PlayerStats) savedInstanceState.getSerializable(PLAYER_STATS);
}
if (stats == null) {
Modules.LOG.info(TAG, "Trying to load from new file");
AssetModule assetModule = Modules.ASSETS;
try {
InputStream fin = assetModule.openInput("", SAVE_STATS_FILENAME);
stats = PlayerSaver.load(fin);
fin.close();
} catch (Exception e) {
Modules.LOG.info(TAG,
"Failed to load previous save, deleting file " + assetModule.delete("", SAVE_STATS_FILENAME));
}
}
if (stats == null) {
Modules.LOG.info(TAG, "Unable to find previous player stats, creating new player");
stats = new PlayerStats(0);
}
menu = new MenuLayoutManager(this, stats);
checkAppInfo();
game = new GameStateManager(this);
try {
game.tryLoad(this, savedInstanceState);
} catch (Exception e) {
Modules.REPORTER.report(e);
Modules.LOG.info(TAG, "Failed to load game engine due to " + e.getLocalizedMessage());
}
String URI = this.getResources().getText(R.string.app_uri).toString();
AppRater.launchRating(this, URI);
}
private void checkAppInfo() {
final Activity activity = this;
new Thread(new Runnable() {
@Override
public void run() {
try {
ClientResource prepareGameClient = new ClientResource("http://" + Constants.RESTLET_HOSTNAME + ":"
+ Constants.RESTLET_PORT + Constants.RESTLET_GETINFO);
prepareGameClient.setRequestEntityBuffering(true);
GetAppInfoResource prepareGameResource = prepareGameClient.wrap(GetAppInfoResource.class);
Modules.LOG.info(TAG, "sending getAppInfo requests");
final ApplicationInfo appInfo = prepareGameResource.getLatestInfo();
if (appInfo == null) {
Modules.LOG.error(TAG, "getLatestInfo returned null, but no error");
} else {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
menu.setNews(appInfo.getNews());
try {
int currentVersion = Integer.parseInt(Constants.APP_VERSION.replace(".", ""));
int serverVersion = Integer.parseInt(appInfo.getAppVersion().replace(".", ""));
menu.setOnlinePlay(currentVersion >= serverVersion);
} catch (Exception e) {
Modules.LOG.error(TAG, "error setting online play");
}
}
});
}
} catch (Exception e) {
Modules.LOG.error(TAG, "Failed to retrieve latest application info due to " + e.getMessage());
}
}
}).start();
}
public void lowerGraphics() {
final Activity activity = this;
if (game.isRunning())
game.shutdownNow();
super.runOnUiThread(new Runnable() {
Dialog dialog;
@Override
public void run() {
ScrollView scrollView = new ScrollView(activity);
scrollView.setScrollbarFadingEnabled(false);
LinearLayout outsideLayout = new LinearLayout(activity);
outsideLayout.setOrientation(LinearLayout.VERTICAL);
TextView text = new TextView(activity);
text.setText(R.string.main_graphics);
outsideLayout.addView(text);
scrollView.addView(outsideLayout);
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setView(scrollView);
builder.setCancelable(false);
builder.setNeutralButton(R.string.tutorial_continue, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
ModelUtils.decreaseTextureQuality();
long free = Runtime.getRuntime().freeMemory() / 1024;
long total = Runtime.getRuntime().totalMemory() / 1024;
long max = Runtime.getRuntime().maxMemory() / 1048576;
Modules.REPORTER.report(new RuntimeException("Application quit due to OOM: Ram: f" + free
+ "KB t" + total + "KB m" + max + "MB"));
activity.finish();
}
});
dialog = builder.create();
dialog.show();
}
});
}
@Override
public void onResume() {
super.onResume();
Modules.LOG.info(TAG, "onResume");
PurchaseModule purchaseModule = Modules.PURCHASER;
purchaseModule.init(this, stats);
purchaseModule.restoreTransactions();
displayResume();
displayMenu();
}
private void displayResume() {
if (game.canRun()) {
menu.showResume();
} else {
menu.hideResume();
}
menu.attachNews();
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
Modules.LOG.info(TAG, "onSaveInstanceState");
outState.putSerializable(PLAYER_STATS, stats);
game.saveInstanceState(outState);
}
@Override
public void onPause() {
super.onPause();
Modules.LOG.info(TAG, "onPause");
Modules.AUDIO.shutdown();
save();
}
private void save() {
try {
AssetModule assetModule = Modules.ASSETS;
OutputStream fout = assetModule.openOutput("", SAVE_STATS_FILENAME);
PlayerSaver.write(stats, fout);
fout.close();
} catch (IOException e) {
Modules.LOG.info(TAG, "Failed to save game due to ioexception\n" + e.toString());
}
}
@Override
public void onDestroy() {
super.onDestroy();
Modules.LOG.info(TAG, "onDestroy");
Modules.NETWORKING.disconnect();
Modules.PURCHASER.destroy();
game.save();
game.shutdown();
}
@Override
public CharSequence onCreateDescription() {
return getText(R.string.app_description);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Modules.LOG.info(TAG, "Activity returned with resultCode " + resultCode);
displayMenu();
}
}