Android Open Source - Freebloks-Android Freebloks Activity






From Project

Back to project page Freebloks-Android.

License

The source code is released under:

GNU General Public License

If you think the Android project Freebloks-Android listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package de.saschahlusiak.freebloks.game;
//from  ww w.jav  a2 s.c om
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.Serializable;
import java.lang.Thread.State;
import java.util.ArrayList;

import com.google.example.games.basegameutils.BaseGameActivity;

import de.saschahlusiak.freebloks.BuildConfig;
import de.saschahlusiak.freebloks.Global;
import de.saschahlusiak.freebloks.R;
import de.saschahlusiak.freebloks.controller.GameMode;
import de.saschahlusiak.freebloks.controller.JNIServer;
import de.saschahlusiak.freebloks.controller.PlayerData;
import de.saschahlusiak.freebloks.controller.SpielClient;
import de.saschahlusiak.freebloks.controller.SpielClientInterface;
import de.saschahlusiak.freebloks.controller.Spielleiter;
import de.saschahlusiak.freebloks.donate.DonateActivity;
import de.saschahlusiak.freebloks.lobby.ChatEntry;
import de.saschahlusiak.freebloks.lobby.LobbyDialog;
import de.saschahlusiak.freebloks.model.Player;
import de.saschahlusiak.freebloks.model.Spiel;
import de.saschahlusiak.freebloks.model.Stone;
import de.saschahlusiak.freebloks.model.Turn;
import de.saschahlusiak.freebloks.network.NET_CHAT;
import de.saschahlusiak.freebloks.network.NET_SERVER_STATUS;
import de.saschahlusiak.freebloks.network.NET_SET_STONE;
import de.saschahlusiak.freebloks.network.Network;
import de.saschahlusiak.freebloks.preferences.FreebloksPreferences;
import de.saschahlusiak.freebloks.view.Freebloks3DView;
import de.saschahlusiak.freebloks.view.effects.BoardStoneGlowEffect;
import de.saschahlusiak.freebloks.view.effects.Effect;
import de.saschahlusiak.freebloks.view.effects.EffectSet;
import de.saschahlusiak.freebloks.view.effects.StoneFadeEffect;
import de.saschahlusiak.freebloks.view.effects.StoneRollEffect;
import de.saschahlusiak.freebloks.view.model.Intro;
import de.saschahlusiak.freebloks.view.model.Sounds;
import de.saschahlusiak.freebloks.view.model.Theme;
import de.saschahlusiak.freebloks.view.model.ViewModel;
import de.saschahlusiak.freebloks.view.model.Intro.OnIntroCompleteListener;
import android.annotation.TargetApi;
import android.app.ActionBar;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.GradientDrawable.Orientation;
import android.media.AudioManager;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Parcel;
import android.os.StrictMode;
import android.os.Vibrator;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnLongClickListener;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.Window;
import android.view.View.OnClickListener;
import android.view.WindowManager;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.CycleInterpolator;
import android.view.animation.TranslateAnimation;
import android.widget.ImageButton;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;

public class FreebloksActivity extends BaseGameActivity implements ActivityInterface, SpielClientInterface, OnIntroCompleteListener {
  static final String tag = FreebloksActivity.class.getSimpleName();

  static final int DIALOG_GAME_MENU = 1;
  static final int DIALOG_LOBBY = 2;
  static final int DIALOG_QUIT = 3;
  static final int DIALOG_RATE_ME = 4;
  static final int DIALOG_JOIN = 5;
  static final int DIALOG_HOST = 9;
  static final int DIALOG_CUSTOM_GAME = 7;
  static final int DIALOG_NEW_GAME_CONFIRMATION = 8;

  static final int REQUEST_FINISH_GAME = 1;

  static final int NOTIFICATION_GAME_ID = 1;

  public static final String GAME_STATE_FILE = "gamestate.bin";


  Freebloks3DView view;
  SpielClient client = null;
  SpielClientThread spielthread = null;
  Vibrator vibrator;
  boolean vibrate_on_move;
  boolean show_notifications;
  boolean undo_with_back;
  boolean hasActionBar;
  NET_SERVER_STATUS lastStatus;
  Menu optionsMenu;
  ViewGroup statusView;
  NotificationManager notificationManager;
  Notification multiplayerNotification;

  ConnectTask connectTask;

  String clientName;
  int difficulty;
  GameMode gamemode;
  int fieldsize;

  ImageButton chatButton;
  ArrayList<ChatEntry> chatEntries;

  class ConnectTask extends AsyncTask<String,Void,String> {
    ProgressDialog progress;
    SpielClient myclient = null;
    boolean show_lobby;
    Runnable connectedRunnable;

    ConnectTask(SpielClient client, boolean show_lobby, Runnable connectedRunnable) {
      this.myclient = client;
      spielthread = new SpielClientThread(myclient);
      this.show_lobby = show_lobby;
      this.connectedRunnable = connectedRunnable;
    }

    @Override
    protected void onPreExecute() {
      lastStatus = null;
      view.setSpiel(null, null);
      chatButton.setVisibility(View.INVISIBLE);
      chatEntries.clear();
      progress = new ProgressDialog(FreebloksActivity.this);
      progress.setMessage(getString(R.string.connecting));
      progress.setIndeterminate(true);
      progress.setProgressStyle(ProgressDialog.STYLE_SPINNER);
      progress.setCancelable(true);
      progress.show();
      super.onPreExecute();
    }

    @Override
    protected String doInBackground(String... params) {
      try {
        this.myclient.connect(FreebloksActivity.this, params[0], Network.DEFAULT_PORT);
      } catch (Exception e) {
        e.printStackTrace();
        return e.getMessage();
      }
      if (connectedRunnable != null)
        connectedRunnable.run();
      view.setSpiel(myclient, myclient.spiel);
      return null;
    }

    @Override
    protected void onCancelled() {
      connectTask = null;
      super.onCancelled();
    }

    @Override
    protected void onPostExecute(String result) {
      connectTask = null;
      FreebloksActivity.this.client = this.myclient;
      progress.dismiss();
      if (result != null) {
        AlertDialog.Builder builder = new AlertDialog.Builder(FreebloksActivity.this);
        builder.setTitle(android.R.string.dialog_alert_title);
        builder.setMessage(result);
        builder.setIcon(android.R.drawable.ic_dialog_alert);
        builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
          @Override
          public void onClick(DialogInterface dialog, int which) {
            dialog.dismiss();
            showDialog(DIALOG_GAME_MENU);
          }
        });
        builder.setOnCancelListener(new OnCancelListener() {

          @Override
          public void onCancel(DialogInterface dialog) {
            showDialog(DIALOG_GAME_MENU);
          }
        });
        builder.create().show();
      } else {
        if (show_lobby)
          showDialog(DIALOG_LOBBY);

        myclient.addClientInterface(FreebloksActivity.this);
        newCurrentPlayer(myclient.spiel.current_player());
        /* TODO: there is probably a race condition, when the device is
         * rotated, the task finishes and a new task is started while
         * the thread is running.
         */
        if (spielthread.getState() == State.NEW)
          spielthread.start();
      }
      super.onPostExecute(result);
    }
  }

  SharedPreferences prefs;

  @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
  @Override
  public void onCreate(Bundle savedInstanceState) {
    Log.d(tag, "onCreate");

    if (Build.VERSION.SDK_INT >= 11 && BuildConfig.DEBUG) {
           StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
                .detectCustomSlowCalls()
                   .detectDiskReads()
                   .detectDiskWrites()
                   .detectNetwork()   // or .detectAll() for all detectable problems
                   .penaltyLog()
                   .build());
           StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
                   .detectLeakedSqlLiteObjects()
                   .detectLeakedClosableObjects()
                   .detectAll()
                   .penaltyLog()
                   .build());
      }

    hasActionBar = false;
    /* by default, don't show title bar */
    if (Build.VERSION.SDK_INT >= 11) {
      /* all honeycomb tablets had no menu button; leave action bar visible */
      hasActionBar = true;
      if (Build.VERSION.SDK_INT >= 14) {
        /* tablets/phone with ICS may or may not have physical buttons. Show action bar if mising */
        ViewConfiguration viewConfig = ViewConfiguration.get(this);
        /* we need the action bar if we don't have a menu key */
        hasActionBar = !viewConfig.hasPermanentMenuKey();
      }
    }

    if (!hasActionBar)
      requestWindowFeature(Window.FEATURE_NO_TITLE);
    else
      requestWindowFeature(Window.FEATURE_ACTION_BAR_OVERLAY);

    getGameHelper().setMaxAutoSignInAttempts(0);
    super.onCreate(savedInstanceState);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
          Window w = getWindow();
          
          w.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
//          w.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION, WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
      }

    if (hasActionBar) {
      if (Build.VERSION.SDK_INT >= 14) {
        getActionBar().setHomeButtonEnabled(true);
        getActionBar().setIcon(android.R.drawable.ic_dialog_dialer);
      } else
        getActionBar().setDisplayOptions(0, ActionBar.DISPLAY_SHOW_HOME);
        
//      getActionBar().setDisplayUseLogoEnabled(false);
//      getActionBar().setBackgroundDrawable(new ColorDrawable(Color.argb(104, 0, 0, 0)));
      getActionBar().setBackgroundDrawable(new ColorDrawable(0));
      getActionBar().setDisplayShowTitleEnabled(false);
    }

    setContentView(R.layout.main_3d);

    prefs = PreferenceManager.getDefaultSharedPreferences(FreebloksActivity.this);
    notificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);

    view = (Freebloks3DView)findViewById(R.id.board);
    view.setActivity(this);
    
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && prefs.getBoolean("immersive_mode", true)) {
      view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
    }


    statusView = (ViewGroup)findViewById(R.id.currentPlayerLayout);
    vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);
    setVolumeControlStream(AudioManager.STREAM_MUSIC);
    chatButton = (ImageButton)findViewById(R.id.chatButton);
    chatButton.setVisibility(View.INVISIBLE);
    chatButton.setOnClickListener(new OnClickListener() {
      @Override
      public void onClick(View v) {
        chatButton.clearAnimation();
        showDialog(DIALOG_LOBBY);
      }
    });
    if (savedInstanceState != null)
      chatEntries = (ArrayList<ChatEntry>)savedInstanceState.getSerializable("chatEntries");
    else
      chatEntries = new ArrayList<ChatEntry>();

    newCurrentPlayer(-1);

    RetainedConfig config = (RetainedConfig)getLastNonConfigurationInstance();
    if (config != null) {
      spielthread = config.clientThread;
      lastStatus = config.lastStatus;
      view.model.soundPool = config.soundPool;
      view.model.intro = config.intro;
      if (view.model.intro != null)
        view.model.intro.setModel(view.model, this);
      canresume = true;
      chatButton.setVisibility((lastStatus != null && lastStatus.clients > 1) ? View.VISIBLE : View.INVISIBLE);
    }
    if (savedInstanceState != null) {
      view.setScale(savedInstanceState.getFloat("view_scale", 1.0f));
      showRateDialog = savedInstanceState.getBoolean("showRateDialog", false);
    } else {
      view.setScale(prefs.getFloat("view_scale", 1.0f));
      showRateDialog = RateAppDialog.checkShowRateDialog(this);
      if (prefs.getLong("rate_number_of_starts", 0) == Global.DONATE_STARTS) {
        Intent intent = new Intent(this, DonateActivity.class);
        startActivity(intent);
      }
    }
    if (view.model.soundPool == null)
      view.model.soundPool = new Sounds(this);

    clientName = prefs.getString("player_name", null);
    difficulty = prefs.getInt("difficulty", 10);  /* TODO: generalize the value */
    gamemode = GameMode.from(prefs.getInt("gamemode", GameMode.GAMEMODE_4_COLORS_4_PLAYERS.ordinal()));
    fieldsize = prefs.getInt("fieldsize", Spiel.DEFAULT_FIELD_SIZE_X);

    if (spielthread != null) {
      /* we just rotated and got *hot* objects */
      client = spielthread.client;
      client.addClientInterface(this);
      view.setSpiel(client, client.spiel);
      newCurrentPlayer(client.spiel.current_player());
    } else if (savedInstanceState != null) {
      /* this can happen, when there is no game running and we rotate the device */
      if (!readStateFromBundle(savedInstanceState)) {
        canresume = false;
        newCurrentPlayer(-1);
      }
    } else {
      if (prefs.getBoolean("show_animations", true) && ! prefs.getBoolean("skip_intro", false)) {
        view.model.intro = new Intro(view.model, this);
        newCurrentPlayer(-1);
      } else
        OnIntroCompleted();
    }

    statusView.setOnClickListener(new OnClickListener() {
      @Override
      public void onClick(View v) {
        if (view.model.intro != null)
          view.model.intro.cancel();
      }
    });

    findViewById(R.id.myLocation).setOnClickListener(new OnClickListener() {
      @Override
      public void onClick(View v) {
        view.model.board.resetRotation();
      }
    });


    final Animation a = new TranslateAnimation(0, 8, 0, 0);
    a.setInterpolator(new CycleInterpolator(2));
    a.setDuration(500);
    Runnable r = new Runnable() {
      @Override
      public void run() {
        boolean local = false;
        View t = findViewById(R.id.currentPlayer);
        t.postDelayed(this, 5000);

        if (client != null && client.spiel != null)
          local = client.spiel.is_local_player();
        if (!local)
          return;

        t.startAnimation(a);
      }
    };
    findViewById(R.id.currentPlayer).postDelayed(r, 1000);
  }

  boolean canresume = false;
  boolean showRateDialog = false;

  @Override
  public void OnIntroCompleted() {
    newCurrentPlayer(-1);
    try {
      if (restoreOldGame()) {
        canresume = true;
      } else {
        canresume = false;
      }
    } catch (Exception e) {
      canresume = false;
      Toast.makeText(FreebloksActivity.this, R.string.could_not_restore_game, Toast.LENGTH_LONG).show();
    }

    if (!canresume || ! prefs.getBoolean("auto_resume", false))
      showDialog(DIALOG_GAME_MENU);

    if (showRateDialog)
      showDialog(DIALOG_RATE_ME);
  }

  @Override
  protected void onDestroy() {
    Log.d(tag, "onDestroy");
    notificationManager.cancelAll();
    notificationManager.cancel(NOTIFICATION_GAME_ID);

    if (connectTask != null) try {
      connectTask.cancel(true);
      connectTask.get();
      connectTask = null;
    } catch (Exception e) {
      e.printStackTrace();
    }
    if (spielthread != null) try {
      spielthread.client.disconnect();
      spielthread.goDown();
      spielthread.join();
      spielthread = null;
    } catch (Exception e) {
      e.printStackTrace();
    }
    if (client != null) {
      client.removeClientInterface(this);
      /* TODO: make attach/detach of view symmetric */
      client.removeClientInterface(view);
    }
    if (view.model.soundPool != null)
      view.model.soundPool.release();
    view.model.soundPool = null;
    super.onDestroy();
  }

  @Override
  protected void onPause() {
    Log.d(tag, "onPause");
    if (client != null && client.spiel.isStarted() && !client.spiel.isFinished())
      saveGameState(GAME_STATE_FILE);
    super.onPause();
  }

  @Override
  protected void onResume() {
    Log.d(tag, "onResume");
    super.onResume();
  }

  @Override
  protected void onStop() {
    if (client != null && client.isConnected()) {
      if ((lastStatus != null && lastStatus.clients > 1) ||
        (!client.spiel.isStarted()))
        updateMultiplayerNotification(true, null);
    }
    view.onPause();
    Editor editor = prefs.edit();
    editor.putFloat("view_scale", view.getScale());
    editor.commit();
    Log.d(tag, "onStop");
    super.onStop();
  }

  @Override
  protected void onStart() {
    Log.d(tag, "onStart");
    super.onStart();
    view.onResume();

    notificationManager.cancel(NOTIFICATION_GAME_ID);
    multiplayerNotification = null;

    vibrate_on_move = prefs.getBoolean("vibrate", true);
    view.model.soundPool.setEnabled(prefs.getBoolean("sounds", true));
    show_notifications = prefs.getBoolean("notifications", true);
    view.model.showSeeds = prefs.getBoolean("show_seeds", true);
    view.model.showOpponents = prefs.getBoolean("show_opponents", true);
    view.model.showAnimations = Integer.parseInt(prefs.getString("animations", String.format("%d", ViewModel.ANIMATIONS_FULL)));
    view.model.snapAid = prefs.getBoolean("snap_aid", true);
    view.model.immersiveMode = prefs.getBoolean("immersive_mode", true);
    undo_with_back = prefs.getBoolean("back_undo", false);
    clientName = prefs.getString("player_name", null);
    if (clientName != null && clientName.equals(""))
      clientName = null;
    Theme t = Theme.get(prefs.getString("theme", "texture_wood"), false);
    view.setTheme(t);

    updateSoundMenuEntry();
    /* update wheel in case showOpponents has changed */
    view.model.wheel.update(view.model.board.getShowWheelPlayer());
  }

  @Override
  public Object onRetainNonConfigurationInstance() {
    Log.d(tag, "onRetainNonConfigurationInstance");
    RetainedConfig config = new RetainedConfig();
    config.clientThread = spielthread;
    config.lastStatus = lastStatus;
    config.soundPool = view.model.soundPool;
    config.intro = view.model.intro;
    view.model.soundPool = null;
    spielthread = null;
    return config;
  }

  @Override
  protected void onSaveInstanceState(Bundle outState) {
    Log.d(tag, "onSaveInstanceState");
    super.onSaveInstanceState(outState);
    outState.putFloat("view_scale", view.getScale());
    outState.putBoolean("showRateDialog", showRateDialog);
    outState.putSerializable("chatEntries", chatEntries);
    writeStateToBundle(outState);
  }

  private void writeStateToBundle(Bundle outState) {
    if (client == null)
      return;
    if (client.spiel == null)
      return;
    synchronized (client) {
      Spielleiter l = client.spiel;
      outState.putSerializable("game", l);
    }
  }

  private boolean readStateFromBundle(Bundle in) {
    try {
      Spielleiter spiel1 = (Spielleiter)in.getSerializable("game");

      JNIServer.runServer(spiel1, spiel1.m_gamemode.ordinal(), spiel1.m_field_size_x, difficulty);

      /* this will start a new SpielClient, which needs to be restored
       * from saved gamestate first */
      SpielClient client = new SpielClient(spiel1, difficulty, null, spiel1.m_field_size_x);
      client.spiel.setStarted(true);
      connectTask = new ConnectTask(client, false, null);
      connectTask.execute((String)null);

      return true;
    }
    catch(Exception e) {
      e.printStackTrace();
      return false;
    }
  }

  long gameStartTime = 0;

  public void startNewGame() {
    boolean players[] = null;

    /* when starting a new game from the options menu, keep previous
     * selected settings / players, etc.
     */
    if (client != null && client.spiel != null) {
      players = client.getLastPlayers();
    }
    startNewGame(null, false, players);
  }

  public void startNewGame(final String server, final boolean show_lobby, final boolean[] request_player) {
    newCurrentPlayer(-1);
    if (server == null) {
      JNIServer.runServer(null, gamemode.ordinal(), fieldsize, difficulty);
    }

    if (spielthread != null)
      spielthread.client.disconnect();

    view.model.clearEffects();
    Spielleiter spiel = new Spielleiter(fieldsize, fieldsize);
    final SpielClient client = new SpielClient(spiel, difficulty, request_player, fieldsize);
    spiel.start_new_game(gamemode);
    spiel.set_stone_numbers(0, 0, 0, 0, 0);

    connectTask = new ConnectTask(client, show_lobby, new Runnable() {
      @Override
      public void run() {
        if (request_player == null)
          client.request_player(-1, clientName);
        else {
          for (int i = 0; i < 4; i++)
            if (request_player[i])
              client.request_player(i, clientName);
        }
        if (! show_lobby)
          client.request_start();
      }
    });
    connectTask.execute(server);
  }

  boolean restoreOldGame() throws Exception {
    try {
      FileInputStream fis = openFileInput(FreebloksActivity.GAME_STATE_FILE);
      ByteArrayOutputStream bos = new ByteArrayOutputStream();
      Parcel p = Parcel.obtain();
      byte[] b = new byte[1024];
      int bytesRead;
      while ((bytesRead = fis.read(b)) != -1) {
         bos.write(b, 0, bytesRead);
      }
      fis.close();
      fis = null;

      byte[] bytes = bos.toByteArray();
      bos.close();
      bos = null;

      Bundle bundle;
      p.unmarshall(bytes, 0, bytes.length);
      p.setDataPosition(0);
      bundle = p.readBundle(FreebloksActivity.class.getClassLoader());
      p.recycle();

      deleteFile(GAME_STATE_FILE);

      if (readStateFromBundle(bundle)) {
        return true;
      } else {
        return false;
      }
    } catch (FileNotFoundException fe) {
      /* signal non-failure if game state file is missing */
      return false;
    } catch (Exception e) {
      e.printStackTrace();
      throw e;
    }
  }

  private void saveGameState(final String filename) {
    final Parcel p = Parcel.obtain();
    Bundle b = new Bundle();
    writeStateToBundle(b);
    p.writeBundle(b);
    new Thread() {
      public void run() {
        try {
          FileOutputStream fos;
          fos = openFileOutput(filename, Context.MODE_PRIVATE);
          fos.write(p.marshall());
          fos.flush();
          fos.close();
          p.recycle();
        } catch (Exception e) {
          e.printStackTrace();
        }
      };
    }.start();
  }

  @Override
  public boolean onCreateOptionsMenu(Menu menu) {
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.game_optionsmenu, menu);
    optionsMenu = menu;

    return true;
  }

  @Override
  public boolean onPrepareOptionsMenu(Menu menu) {
    boolean local = false;
    if (client != null && client.spiel != null)
      local = client.spiel.is_local_player();

    menu.findItem(R.id.undo).setEnabled(local && lastStatus != null && lastStatus.clients <= 1);
    menu.findItem(R.id.hint).setEnabled(local);
    menu.findItem(R.id.sound_toggle_button).setVisible(hasActionBar);
    updateSoundMenuEntry();

    return super.onPrepareOptionsMenu(menu);
  }

  void updateSoundMenuEntry() {
    boolean on = true;
    if (optionsMenu == null)
      return;
    if (view != null && view.model != null && view.model.soundPool != null)
      on = view.model.soundPool.isEnabled();
    optionsMenu.findItem(R.id.sound_toggle_button).setTitle(on ? R.string.sound_on : R.string.sound_off);
    optionsMenu.findItem(R.id.sound_toggle_button).setIcon(on ? android.R.drawable.ic_lock_silent_mode_off : android.R.drawable.ic_lock_silent_mode);
  }

  @Override
  protected Dialog onCreateDialog(int id) {
    switch (id) {
    case DIALOG_LOBBY:
      if (client == null)
        return null;
      return new LobbyDialog(this, new DialogInterface.OnCancelListener() {
        @Override
        public void onCancel(DialogInterface arg0) {
          client.disconnect();
        }
      }, chatEntries);

    case DIALOG_QUIT:
      AlertDialog.Builder builder = new AlertDialog.Builder(this);
      builder.setMessage(R.string.do_you_want_to_leave_current_game);
      builder.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface arg0, int arg1) {
          canresume = true;
          showDialog(DIALOG_GAME_MENU);
        }
      });
      builder.setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface arg0, int arg1) {
          arg0.dismiss();
        }
      });
      return builder.create();

    case DIALOG_NEW_GAME_CONFIRMATION:
      builder = new AlertDialog.Builder(this);
      builder.setMessage(R.string.do_you_want_to_leave_current_game);
      builder.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface arg0, int arg1) {
          startNewGame();
        }
      });
      builder.setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface arg0, int arg1) {
          arg0.dismiss();
        }
      });
      return builder.create();

    case DIALOG_RATE_ME:
      return new RateAppDialog(this);

    case DIALOG_GAME_MENU:
      return new GameMenu(this);

    case DIALOG_CUSTOM_GAME:
      return new CustomGameDialog(this, new CustomGameDialog.OnStartCustomGameListener() {
        @Override
        public boolean OnStart(CustomGameDialog dialog) {
          difficulty = dialog.getDifficulty();
          gamemode = dialog.getGameMode();
          fieldsize = dialog.getFieldSize();
          startNewGame(
              null,
              false,
              dialog.getPlayers());
          dismissDialog(DIALOG_CUSTOM_GAME);
          dismissDialog(DIALOG_GAME_MENU);
          return true;
        }
      });

    case DIALOG_JOIN:
      return new CustomGameDialog(this, new CustomGameDialog.OnStartCustomGameListener() {
        @Override
        public boolean OnStart(CustomGameDialog dialog) {
          clientName = dialog.getName();
          gamemode = dialog.getGameMode();
          startNewGame(
              dialog.getServer(),
              true,
              dialog.getPlayers());
          dismissDialog(DIALOG_GAME_MENU);
          return true;
        }
      });

    case DIALOG_HOST:
      return new CustomGameDialog(this, new CustomGameDialog.OnStartCustomGameListener() {
        @Override
        public boolean OnStart(CustomGameDialog dialog) {
          clientName = dialog.getName();
          gamemode = dialog.getGameMode();
          fieldsize = dialog.getFieldSize();
          /* TODO: host should have configurable difficulty */
          startNewGame(
              null,
              true,
              dialog.getPlayers());
          dismissDialog(DIALOG_GAME_MENU);
          return true;
        }
      });

    default:
      return super.onCreateDialog(id);
    }
  }

  @Override
  protected void onPrepareDialog(int id, final Dialog dialog, Bundle args) {
    switch (id) {
    case DIALOG_LOBBY:
      if (client != null) {
        ((LobbyDialog)dialog).setSpiel(client);
        if (lastStatus != null)
          ((LobbyDialog)dialog).serverStatus(lastStatus);
        dialog.setOnCancelListener(new OnCancelListener() {
          @Override
          public void onCancel(DialogInterface arg0) {
            if (!client.spiel.isStarted() && !client.spiel.isFinished()) {
              canresume = false;
              client.disconnect();
              showDialog(DIALOG_GAME_MENU);
            }
          }
        });
      } else {
        /* this can happen when the app is saved but purged from memory
         * upon resume, the open dialog is reopened but the client connection
         * has to be disconnected. just close the lobby since there is no
         * connection
         */
        dialog.dismiss();
        canresume = false;
        showDialog(DIALOG_GAME_MENU);
      }
      break;

    case DIALOG_GAME_MENU:
      dialog.findViewById(R.id.new_game).setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
          dialog.dismiss();
          /* starting new game from dialog creates game with previous settings */
          startNewGame(
              null,
              false,
              null);
        }
      });
      dialog.findViewById(R.id.resume_game).setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
          dialog.dismiss();
        }
      });
      dialog.findViewById(R.id.star).setVisibility(showRateDialog ? View.VISIBLE : View.GONE);
      dialog.findViewById(R.id.star).setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
          showDialog(DIALOG_RATE_ME);
        }
      });
//      dialog.findViewById(R.id.sound_toggle_button).setVisibility(hasActionBar ? View.GONE : View.VISIBLE);
      dialog.findViewById(R.id.resume_game).setEnabled(canresume);
      dialog.setCanceledOnTouchOutside(canresume);
      dialog.findViewById(R.id.preferences).setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
          Intent intent = new Intent(FreebloksActivity.this, FreebloksPreferences.class);
          startActivity(intent);
        }
      });
      dialog.findViewById(R.id.join_game).setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
          showDialog(DIALOG_JOIN);
        }
      });
      dialog.findViewById(R.id.host_game).setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
          showDialog(DIALOG_HOST);
        }
      });
      dialog.findViewById(R.id.new_game_custom).setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
          showDialog(DIALOG_CUSTOM_GAME);
        }
      });
      dialog.findViewById(R.id.new_game).setOnLongClickListener(new OnLongClickListener() {
        @Override
        public boolean onLongClick(View v) {
          showDialog(DIALOG_CUSTOM_GAME);
          return true;
        }
      });
      break;

    case DIALOG_JOIN:
      ((CustomGameDialog)dialog).prepareJoinDialog(clientName, difficulty, GameMode.GAMEMODE_4_COLORS_4_PLAYERS, fieldsize);
      break;

    case DIALOG_HOST:
      ((CustomGameDialog)dialog).prepareHostDialog(clientName, difficulty, gamemode, fieldsize);
      break;

    case DIALOG_CUSTOM_GAME:
      ((CustomGameDialog)dialog).prepareCustomGameDialog(clientName, difficulty, gamemode, fieldsize);
      break;
    }
    super.onPrepareDialog(id, dialog, args);
  }

  @Override
  public boolean onOptionsItemSelected(MenuItem item) {
    Intent intent;
    switch (item.getItemId()) {
    case android.R.id.home:
      if (client != null && client.isConnected())
        canresume = true;
      else
        canresume = false;

      showDialog(DIALOG_GAME_MENU);
      if (view.model.intro != null)
        view.model.intro.cancel();
      return true;

    case R.id.new_game:
      if (view.model.intro != null)
        view.model.intro.cancel();
      else {
        if (client == null || (client.spiel != null && client.spiel.isFinished()))
          startNewGame();
        else
          showDialog(DIALOG_NEW_GAME_CONFIRMATION);
      }
      return true;

    case R.id.preferences:
      intent = new Intent(this, FreebloksPreferences.class);
      startActivity(intent);
      return true;

    case R.id.sound_toggle_button:
      Editor editor = prefs.edit();
      view.model.soundPool.toggle();
      editor.putBoolean("sounds", view.model.soundPool.isEnabled());
      editor.commit();
      updateSoundMenuEntry();
      Toast.makeText(this, getString(view.model.soundPool.isEnabled() ? R.string.sound_on : R.string.sound_off), Toast.LENGTH_SHORT).show();
      return true;

    case R.id.hint:
      if (client == null)
        return true;
      findViewById(R.id.progressBar).setVisibility(View.VISIBLE);
      findViewById(R.id.movesLeft).setVisibility(View.INVISIBLE);
      view.model.currentStone.startDragging(null, null, 0);
      spielthread.post(new Runnable() {
        @Override
        public void run() {
          client.request_hint(client.spiel.current_player());
        }
      });
      return true;

    case R.id.undo:
      if (client == null)
        return true;
      view.model.clearEffects();
      spielthread.post(new Runnable() {
        @Override
        public void run() {
          client.request_undo();
        }
      });
      view.model.soundPool.play(view.model.soundPool.SOUND_UNDO, 1.0f, 1.0f);
      return true;

    case R.id.show_main_menu:
      if (client != null && client.spiel.isStarted() && lastStatus != null && lastStatus.clients > 1)
        showDialog(DIALOG_QUIT);
      else {
        if (client != null && client.isConnected())
          canresume = true;
        else
          canresume = false;

        showDialog(DIALOG_GAME_MENU);
        if (view.model.intro != null)
          view.model.intro.cancel();
      }
      return true;

    default:
      return super.onOptionsItemSelected(item);
    }
  }

  @Override
  protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    switch (requestCode) {
    case REQUEST_FINISH_GAME:
      if (resultCode == GameFinishActivity.RESULT_NEW_GAME) {
        if (client == null) {
          /* TODO: find out why client can be null here */
          startNewGame(null, false, null);
        } else {
          startNewGame(
            client.getLastHost(),
            client.getLastHost() != null,
            client.getLastPlayers());
        }
      }
      if (resultCode == GameFinishActivity.RESULT_SHOW_MENU) {
        showDialog(DIALOG_GAME_MENU);
      }
      break;

    }
    super.onActivityResult(requestCode, resultCode, data);
  }

  @Override
  public void newCurrentPlayer(final int player) {
    runOnUiThread(new Runnable() {
      @Override
      public void run() {
        if (multiplayerNotification != null)
          updateMultiplayerNotification(false, null);
        boolean local = false;
        int showPlayer = view.model.board.getShowDetailsPlayer();
        if (client != null && client.spiel != null)
          local = client.spiel.is_local_player(player);
        else
          showPlayer = player;

        if (optionsMenu != null) {
          optionsMenu.findItem(R.id.hint).setEnabled(local);
          optionsMenu.findItem(R.id.undo).setEnabled(local && lastStatus != null && lastStatus.clients <= 1);
        }

        findViewById(R.id.progressBar).setVisibility((local || player < 0) ? View.GONE : View.VISIBLE);

        TextView movesLeft, points, status;
        movesLeft = (TextView)findViewById(R.id.movesLeft);
        movesLeft.setVisibility(View.INVISIBLE);
        points = (TextView)findViewById(R.id.points);
        points.setVisibility(View.INVISIBLE);

        status = (TextView)findViewById(R.id.currentPlayer);
        status.clearAnimation();
        findViewById(R.id.myLocation).setVisibility((showPlayer >= 0) ? View.VISIBLE : View.INVISIBLE);
        if (player < 0)
          statusView.setBackgroundColor(Color.rgb(64, 64, 80));
        if (view.model.intro != null)
          status.setText(R.string.touch_to_skip);
        else if (client == null || !client.isConnected())
          status.setText(R.string.not_connected);
        else if (client.spiel.isFinished()) {
          int pl = view.model.board.getShowWheelPlayer();
          Player p = client.spiel.get_player(pl);
          status.setText("[" + getPlayerName(pl) + "]");
          statusView.setBackgroundColor(Global.PLAYER_BACKGROUND_COLOR[view.model.getPlayerColor(pl)]);
          points.setVisibility(View.VISIBLE);
          points.setText(getResources().getQuantityString(R.plurals.number_of_points, p.m_stone_points, p.m_stone_points));
          movesLeft.setVisibility(View.VISIBLE);
          movesLeft.setText(getResources().getQuantityString(R.plurals.number_of_stones_left, p.m_stone_count, p.m_stone_count));
        } else if (player >= 0 || showPlayer >= 0) {
          if (showPlayer < 0) {
            statusView.setBackgroundColor(Global.PLAYER_BACKGROUND_COLOR[view.model.getPlayerColor(player)]);
            Player p = client.spiel.get_player(player);
            points.setVisibility(View.VISIBLE);
            points.setText(getResources().getQuantityString(R.plurals.number_of_points, p.m_stone_points, p.m_stone_points));
            if (!local)
              status.setText(getString(R.string.waiting_for_color, getPlayerName(player)));
            else {
              status.setText(getString(R.string.your_turn, getPlayerName(player)));

              movesLeft.setVisibility(View.VISIBLE);
              movesLeft.setText(getString(R.string.player_status_moves, p.m_number_of_possible_turns));
            }
          } else {
            statusView.setBackgroundColor(Global.PLAYER_BACKGROUND_COLOR[view.model.getPlayerColor(showPlayer)]);
            Player p = client.spiel.get_player(showPlayer);
            points.setVisibility(View.VISIBLE);
            points.setText(getResources().getQuantityString(R.plurals.number_of_points, p.m_stone_points, p.m_stone_points));

            if (p.m_number_of_possible_turns <= 0)
              status.setText("[" + getString(R.string.color_is_out_of_moves, getPlayerName(showPlayer)) + "]");
            else {
              status.setText(getPlayerName(showPlayer));

              movesLeft.setVisibility((local || player < 0) ? View.VISIBLE : View.INVISIBLE);
              movesLeft.setText(getString(R.string.player_status_moves, p.m_number_of_possible_turns));
            }
          }

        } else
          status.setText(R.string.no_player);
      }
    });
  }

  /* we have to store the number of possible turns before and after a stone has been set
   * to detect blocking of other players */
  private int number_of_possible_turns[] = new int[4];

  @Override
  public void stoneWillBeSet(NET_SET_STONE s) {
    for (int i = 0; i < 4; i++)
      number_of_possible_turns[i] = client.spiel.get_player(i).m_number_of_possible_turns;
  }

  @Override
  public void stoneHasBeenSet(final NET_SET_STONE s) {
    if (client == null)
      return;
    if (view == null)
      return;
    runOnUiThread(new Runnable() {

      @Override
      public void run() {
        if (!client.spiel.is_local_player(s.player)) {
          if (view == null)
            return;
          if (view.model.soundPool == null)
            return;
          view.model.soundPool.play(view.model.soundPool.SOUND_CLICK1, 1.0f, 0.9f + (float)Math.random() * 0.2f);
          vibrate(Global.VIBRATE_SET_STONE);
        }
      }
    });

    for (int i = 0; i < 4; i++) {
      final Player p = client.spiel.get_player(i);
      if (p.m_number_of_possible_turns <= 0 && number_of_possible_turns[i] > 0) {
        runOnUiThread(new Runnable() {
          @Override
          public void run() {
            Toast.makeText(FreebloksActivity.this, getString(R.string.color_is_out_of_moves, getPlayerName(p.getPlayerNumber())), Toast.LENGTH_SHORT).show();
            if (view != null) {
              if (view.model.soundPool != null)
                view.model.soundPool.play(view.model.soundPool.SOUND_PLAYER_OUT, 0.8f, 1.0f);
              if (view.model.hasAnimations()) {
                int sx, sy;
                sx = client.spiel.get_player_start_x(p.getPlayerNumber());
                sy = client.spiel.get_player_start_y(p.getPlayerNumber());
                for (int x = 0; x < client.spiel.m_field_size_x; x++)
                  for (int y = 0; y < client.spiel.m_field_size_y; y++)
                    if (client.spiel.get_game_field(y, x) == p.getPlayerNumber()) {
                      boolean effected = false;
                      synchronized (view.model.effects) {
                        for (int j = 0; j < view.model.effects.size(); j++)
                          if (view.model.effects.get(j).isEffected(x, y)) {
                            effected = true;
                            break;
                          }
                      }
                      if (!effected) {
                        final float distance = (float)Math.sqrt((x - sx)*(x - sx) + (y - sy)*(y - sy));
                        Effect effect = new BoardStoneGlowEffect(
                            view.model,
                            view.model.getPlayerColor(p.getPlayerNumber()),
                            x,
                            y,
                            distance);
                        view.model.addEffect(effect);
                      }
                    }
              }
            }
          }
        });
      }
    }
  }

  @Override
  public void hintReceived(NET_SET_STONE s) {
    runOnUiThread(new Runnable() {
      @Override
      public void run() {
        findViewById(R.id.progressBar).setVisibility(View.INVISIBLE);
        findViewById(R.id.movesLeft).setVisibility(View.VISIBLE);
      }
    });
  }

  @Override
  public void gameFinished() {
    deleteFile(GAME_STATE_FILE);
    /* TODO: play sound on game finish */
    runOnUiThread(new Runnable() {
      @Override
      public void run() {
        updateMultiplayerNotification(false, null);
        PlayerData[] data = client.spiel.getResultData();
        new AddScoreTask(getApplicationContext(), client.spiel.m_gamemode).execute(data);

        Intent intent = new Intent(FreebloksActivity.this, GameFinishActivity.class);
        intent.putExtra("game", (Serializable)client.spiel);
        intent.putExtra("lastStatus", (Serializable)lastStatus);
        intent.putExtra("clientName", clientName);
        startActivityForResult(intent, REQUEST_FINISH_GAME);
      }
    });
  }

  @Override
  public void chatReceived(final NET_CHAT c) {
    String name = null;
    int player = -1;
    if (lastStatus != null && c.client >= 0) {
      if (lastStatus.isVersion(2))
        for (int i = 0; i < lastStatus.spieler.length; i++)
          if (lastStatus.spieler[i] == c.client) {
            player = i;
            break;
          }
      name = lastStatus.getClientName(getResources(), c.client);
    } else {
      /* if we have advanced status, ignore all server messages (c == -1) */
      /* server messages are generated in serverStatus */
      if (lastStatus != null && lastStatus.isVersion(2))
        return;
      name = getString(R.string.client_d, c.client + 1);
    }

    final ChatEntry e = new ChatEntry(c.client, c.text, name);
    e.setPlayer(player);

    if (!client.spiel.is_local_player(player) &&
      (client.spiel.isStarted() || multiplayerNotification != null))
      updateMultiplayerNotification(true, e.toString());

    runOnUiThread(new Runnable() {
      @Override
      public void run() {
        chatEntries.add(e);
        if (c.client == -1)
          Toast.makeText(FreebloksActivity.this, "* " + c.text,
              Toast.LENGTH_LONG).show();
        else if (hasWindowFocus()) {
          /* only animate chatButton, if no dialog has focus */
          /* TODO: animate if activity is stopped or paused? */

          Animation a = new AlphaAnimation(0.4f, 1.0f);
          a.setDuration(350);
          a.setRepeatCount(Animation.INFINITE);
          a.setRepeatMode(Animation.REVERSE);
          chatButton.startAnimation(a);
        }
      }
    });
  }

  @Override
  public void gameStarted() {
    gameStartTime = System.currentTimeMillis();

    Log.d(tag, "Game started");
    for (int i = 0; i < Spiel.PLAYER_MAX; i++)
      if (client.spiel.is_local_player(i))
        Log.d(tag, "Local player: " + i);
  }

  @Override
  public void stoneUndone(Stone s, Turn t) {

  }

  @Override
  public void serverStatus(NET_SERVER_STATUS status) {
    if (lastStatus != null && status != null && lastStatus.isVersion(2)) {
      /* generate server chat messages, aka "joined" and "left" */

      for (int i = 0; i < lastStatus.spieler.length; i++) {
        NET_SERVER_STATUS s;
        final int tid;
        if (lastStatus.spieler[i] < 0 && status.spieler[i] >= 0) {
          /* joined */
          s = status;
          tid = R.string.player_joined_color;
        } else if (lastStatus.spieler[i] >= 0 && status.spieler[i] < 0) {
          /* left */
          s = lastStatus;
          tid = R.string.player_left_color;
        } else continue;
        String name;
        name = s.getClientName(getResources(), s.spieler[i]);

        final String text = getString(tid, name, getResources().getStringArray(R.array.color_names)[view.model.getPlayerColor(i)]);
        final ChatEntry e = new ChatEntry(-1, text, name);
        e.setPlayer(i);
        
        if (view == null)
          return;
        if (view.model == null)
          return;
        if (view.model.spiel == null)
          return;

        if (!view.model.spiel.is_local_player(i))
          updateMultiplayerNotification(tid == R.string.player_left_color && client.spiel.isStarted(), text);

        runOnUiThread(new Runnable() {
          @Override
          public void run() {
            chatEntries.add(e);
          }
        });
      }
    }
    lastStatus = status;
    if (lastStatus.clients > 1) {
      chatButton.post(new Runnable() {
        @Override
        public void run() {
          chatButton.setVisibility(View.VISIBLE);
        }
      });
    }
  }

  @Override
  public void onConnected(Spiel spiel) {

  }

  @Override
  public void onDisconnected(Spiel spiel) {
    Log.w(tag, "onDisconnected()");
    final Exception error = spielthread == null ? null : spielthread.getError();

    runOnUiThread(new Runnable() {
      @Override
      public void run() {
        newCurrentPlayer(-1);

        if (error != null) {
          /* TODO: add sound on disconnect on error */
          saveGameState(GAME_STATE_FILE);

          AlertDialog.Builder builder = new AlertDialog.Builder(FreebloksActivity.this);
          builder.setTitle(android.R.string.dialog_alert_title);
          builder.setMessage(getString(R.string.disconnect_error, error.getMessage()));
          builder.setIcon(android.R.drawable.ic_dialog_alert);
          builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
              dialog.dismiss();

              try {
                canresume = restoreOldGame();
              } catch (Exception e) {
                e.printStackTrace();
              }
            }
          });
          builder.create().show();
        }
      }
    });
  }

  @Override
  public boolean commitCurrentStone(final Stone stone, final int x, final int y) {
    if (client == null)
      return false;
    
    if (!client.spiel.is_local_player())
      return false;
    if (client.spiel.is_valid_turn(stone, client.spiel.current_player(), y, x) != Stone.FIELD_ALLOWED)
      return false;

    if (view.model.hasAnimations()) {
      Stone st = new Stone();
      int player = client.spiel.current_player();
      st.copyFrom(stone);
      StoneRollEffect e = new StoneRollEffect(view.model, st, view.model.getPlayerColor(player), x, y, view.model.currentStone.hover_height_high, -15.0f);

      EffectSet set = new EffectSet();
      set.add(e);
      set.add(new StoneFadeEffect(view.model, st, view.model.getPlayerColor(player), x, y, 1.0f));
      view.model.addEffect(set);
    }

    view.model.soundPool.play(view.model.soundPool.SOUND_CLICK1, 1.0f, 0.9f + (float)Math.random() * 0.2f);
    vibrate(Global.VIBRATE_SET_STONE);

    spielthread.post(new Runnable() {
      @Override
      public void run() {
        client.set_stone(stone, y, x);
      }
    });
    return true;
  }

  @Override
  public void vibrate(int ms) {
    if (vibrate_on_move)
      vibrator.vibrate(ms);
  }

  @Override
  public void onBackPressed() {
    if (undo_with_back && client != null && client.isConnected()) {
      view.model.clearEffects();

      spielthread.post(new Runnable() {
        @Override
        public void run() {
          client.request_undo();
        }
      });
      view.model.soundPool.play(view.model.soundPool.SOUND_UNDO, 1.0f, 1.0f);
      return;
    }
    if (client != null && client.spiel.isStarted() && !client.spiel.isFinished() && lastStatus != null && lastStatus.clients > 1)
      showDialog(DIALOG_QUIT);
    else {
      if (view.model.intro != null) {
        view.model.intro.cancel();
      }
      else {
        if (client != null && client.isConnected())
          canresume = true;
        else
          canresume = false;
        showDialog(DIALOG_GAME_MENU);
      }
    }
  }

  @Override
  public void showPlayer(int player) {
    if (client == null)
      return;
    if (client.spiel == null)
      return;
    newCurrentPlayer(client.spiel.current_player());
  }

  String getPlayerName(int player) {
    String color_name = getResources().getStringArray(R.array.color_names)[view.model.getPlayerColor(player)];
    /* this will ensure that always the local name is used, even though the server
     * might still have stored an old or no name at all
     *
     * When resuming a game, the name is lost and never set again. This is a non issue now.
     */
    if (clientName != null && clientName.length() > 0 && client != null && client.spiel != null && client.spiel.is_local_player(player))
      return clientName;
    if (lastStatus == null)
      return color_name;
    return lastStatus.getPlayerName(getResources(), player, view.model.getPlayerColor(player));
  }

  void updateMultiplayerNotification(boolean forceShow, String chat) {
    if (client == null || client.spiel == null)
      return;
    if (!client.isConnected())
      return;
    if (multiplayerNotification == null && !forceShow)
      return;
    if (!show_notifications)
      return;

    Notification n = new Notification();

    Intent intent = new Intent(this, FreebloksActivity.class);
    intent.setAction(Intent.ACTION_MAIN);
    intent.addCategory(Intent.CATEGORY_LAUNCHER);
    if (chat != null)
      intent.putExtra("showChat", true);
    PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 1, intent, PendingIntent.FLAG_UPDATE_CURRENT);

    n.flags = Notification.FLAG_AUTO_CANCEL;
    if (forceShow && chat == null)
      n.flags |= Notification.FLAG_ONGOING_EVENT;
    if (multiplayerNotification != null)
      n.flags |= Notification.FLAG_ONGOING_EVENT;

    if (!client.spiel.isStarted()) {
      n.icon = R.drawable.notification_waiting;
      n.setLatestEventInfo(this,
          getString(R.string.app_name),
          getString(R.string.lobby_waiting_for_players),
          pendingIntent);
    } else if (client.spiel.isFinished()) {
      n.icon = R.drawable.notification_main;
      n.setLatestEventInfo(this,
          getString(R.string.app_name),
          getString(R.string.game_finished),
          pendingIntent);
    } else {
      if (client.spiel.current_player() < 0)
        return;
      if (client.spiel.is_local_player()) {
        n.icon = R.drawable.notification_your_turn;
        n.setLatestEventInfo(this,
            getString(R.string.app_name),
            getString(R.string.your_turn, getPlayerName(client.spiel.current_player())),
            pendingIntent);
        if (!forceShow) {
          n.tickerText = getString(R.string.your_turn, getPlayerName(client.spiel.current_player()));
          n.defaults = Notification.DEFAULT_VIBRATE;
        }
      } else {
        n.icon = R.drawable.notification_waiting;
        n.setLatestEventInfo(this,
            getString(R.string.app_name),
            getString(R.string.waiting_for_color, getPlayerName(client.spiel.current_player())),
            pendingIntent);
      }
    }
    if (chat != null) {
      n.icon = R.drawable.notification_chat;
      n.setLatestEventInfo(this,
          getString(R.string.app_name),
          chat,
          pendingIntent);
      n.tickerText = chat;
      n.defaults = Notification.DEFAULT_VIBRATE;
      n.sound = Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.chat);
    } else
      multiplayerNotification = n;

    notificationManager.notify(NOTIFICATION_GAME_ID, n);
  }

  @Override
  protected void onNewIntent(Intent intent) {
    if (intent.hasExtra("showChat") && client != null && client.spiel.isStarted())
      showDialog(DIALOG_LOBBY);
    super.onNewIntent(intent);
  }

  @Override
  public void onSignInFailed() {

  }

  @Override
  public void onSignInSucceeded() {

  }
}




Java Source Code List

com.google.example.games.basegameutils.BaseGameActivity.java
com.google.example.games.basegameutils.GameHelperUtils.java
com.google.example.games.basegameutils.GameHelper.java
de.saschahlusiak.freebloks.AboutActivity.java
de.saschahlusiak.freebloks.Global.java
de.saschahlusiak.freebloks.backup.FreebloksBackupAgent.java
de.saschahlusiak.freebloks.controller.GameMode.java
de.saschahlusiak.freebloks.controller.JNIServer.java
de.saschahlusiak.freebloks.controller.PlayerData.java
de.saschahlusiak.freebloks.controller.SpielClientInterface.java
de.saschahlusiak.freebloks.controller.SpielClient.java
de.saschahlusiak.freebloks.controller.Spielleiter.java
de.saschahlusiak.freebloks.database.FreebloksDBOpenHandler.java
de.saschahlusiak.freebloks.database.FreebloksDB.java
de.saschahlusiak.freebloks.database.HighscoreDB.java
de.saschahlusiak.freebloks.donate.DonateActivity.java
de.saschahlusiak.freebloks.game.ActivityInterface.java
de.saschahlusiak.freebloks.game.AddScoreTask.java
de.saschahlusiak.freebloks.game.CustomGameDialog.java
de.saschahlusiak.freebloks.game.FreebloksActivity.java
de.saschahlusiak.freebloks.game.GameFinishActivity.java
de.saschahlusiak.freebloks.game.GameMenu.java
de.saschahlusiak.freebloks.game.RateAppDialog.java
de.saschahlusiak.freebloks.game.RetainedConfig.java
de.saschahlusiak.freebloks.game.SpielClientThread.java
de.saschahlusiak.freebloks.lobby.ChatEntry.java
de.saschahlusiak.freebloks.lobby.ChatListAdapter.java
de.saschahlusiak.freebloks.lobby.ColorAdapter.java
de.saschahlusiak.freebloks.lobby.LobbyDialog.java
de.saschahlusiak.freebloks.model.Player.java
de.saschahlusiak.freebloks.model.Spiel.java
de.saschahlusiak.freebloks.model.Stone.java
de.saschahlusiak.freebloks.model.Turn.java
de.saschahlusiak.freebloks.model.Turnpool.java
de.saschahlusiak.freebloks.network.NET_CHAT.java
de.saschahlusiak.freebloks.network.NET_CURRENT_PLAYER.java
de.saschahlusiak.freebloks.network.NET_GAME_FINISH.java
de.saschahlusiak.freebloks.network.NET_GRANT_PLAYER.java
de.saschahlusiak.freebloks.network.NET_HEADER.java
de.saschahlusiak.freebloks.network.NET_REQUEST_GAME_MODE.java
de.saschahlusiak.freebloks.network.NET_REQUEST_HINT.java
de.saschahlusiak.freebloks.network.NET_REQUEST_PLAYER.java
de.saschahlusiak.freebloks.network.NET_REQUEST_UNDO.java
de.saschahlusiak.freebloks.network.NET_REVOKE_PLAYER.java
de.saschahlusiak.freebloks.network.NET_SERVER_STATUS.java
de.saschahlusiak.freebloks.network.NET_SET_STONE.java
de.saschahlusiak.freebloks.network.NET_START_GAME.java
de.saschahlusiak.freebloks.network.NET_UNDO_STONE.java
de.saschahlusiak.freebloks.network.Network.java
de.saschahlusiak.freebloks.preferences.AboutFragment.java
de.saschahlusiak.freebloks.preferences.DisplayFragment.java
de.saschahlusiak.freebloks.preferences.FreebloksPreferences.java
de.saschahlusiak.freebloks.preferences.InterfaceFragment.java
de.saschahlusiak.freebloks.preferences.MiscFragment.java
de.saschahlusiak.freebloks.preferences.StatisticsFragment.java
de.saschahlusiak.freebloks.preferences.ThemePreference.java
de.saschahlusiak.freebloks.stats.StatisticsActivity.java
de.saschahlusiak.freebloks.stats.StatisticsAdapter.java
de.saschahlusiak.freebloks.view.BackgroundRenderer.java
de.saschahlusiak.freebloks.view.BoardRenderer.java
de.saschahlusiak.freebloks.view.Freebloks3DView.java
de.saschahlusiak.freebloks.view.FreebloksRenderer.java
de.saschahlusiak.freebloks.view.SimpleModel.java
de.saschahlusiak.freebloks.view.effects.AbsEffect.java
de.saschahlusiak.freebloks.view.effects.AbsStoneEffect.java
de.saschahlusiak.freebloks.view.effects.BoardStoneGlowEffect.java
de.saschahlusiak.freebloks.view.effects.EffectSet.java
de.saschahlusiak.freebloks.view.effects.Effect.java
de.saschahlusiak.freebloks.view.effects.PhysicalStoneEffect.java
de.saschahlusiak.freebloks.view.effects.StoneFadeEffect.java
de.saschahlusiak.freebloks.view.effects.StoneRollEffect.java
de.saschahlusiak.freebloks.view.effects.StoneUndoEffect.java
de.saschahlusiak.freebloks.view.model.Board.java
de.saschahlusiak.freebloks.view.model.CurrentStone.java
de.saschahlusiak.freebloks.view.model.Intro.java
de.saschahlusiak.freebloks.view.model.Sounds.java
de.saschahlusiak.freebloks.view.model.Theme.java
de.saschahlusiak.freebloks.view.model.ViewElement.java
de.saschahlusiak.freebloks.view.model.ViewModel.java
de.saschahlusiak.freebloks.view.model.Wheel.java