Android Open Source - 101AndroidApps Wiki Game Activity






From Project

Back to project page 101AndroidApps.

License

The source code is released under:

Licenced under the Creative Commons Attribution 4.0 licence. For full text see http://creativecommons.org/licenses/by/4.0/

If you think the Android project 101AndroidApps 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 com.hulzenga.ioi.android.app_007;
/* w w w. j a  v a  2  s  .c o  m*/
import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.media.SoundPool;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.Html;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

import com.hulzenga.ioi.android.AppActivity;
import com.hulzenga.ioi.android.R;
import com.hulzenga.ioi.android.util.Constrain;
import com.hulzenga.ioi.android.util.DeveloperTools;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;

import java.io.IOException;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Random;

public class WikiGameActivity extends AppActivity implements ButtonsFragment.OptionSelectionListener,
                                                              StatusFragment.TimeOutListener {
  public static final  String SAVED_AND_PLAYED_WIKIS     = "saved_and_played";
  public static final  String BUNDLE_DIFFICULTY          = "difficulty";
  private static final String TAG                        = "YET_ANOTHER_WIKIPEDIA_GAME";
  private static final String SAVED_NOT_PLAYED_WIKIS     = "saved_not_played";
  private static final int    DESIRED_GAME_OPTION_BUFFER = 6;
  private static final int    MAX_PARALLEL_DOWNLOADS     = 3;
  private static final int    MIN_LINK_LENGTH            = 4;
  private static final int    MAX_NUMBER_OF_LINKS        = 6;
  private static final int    PLAYED_WIKIS_LENGTH        = 100;
  private static final int    MAX_DOWNLOAD_RETRIES       = 3;
  private static       int    mRetriesLeft               = MAX_DOWNLOAD_RETRIES;
  private TextView        mLinkText;
  private TextView        mProgressBarTextView;
  private ButtonsFragment mButtonsFragment;
  private LinksFragment   mLinksFragment;
  private StatusFragment  mStatusFragment;
  private List<Button> mButtons = new ArrayList<Button>();
  private ProgressBar mProgressBar;
  // handy to keep around
  private Random             mRandom             = new Random();
  private List<Wiki>         mWikisInBuffer      = new LinkedList<Wiki>();
  private List<Wiki>         mWikisPlayed        = new LinkedList<Wiki>();
  @SuppressLint("UseSparseArrays") //code works out a lot nicer with a real map
  private Map<Integer, Wiki> mWikisInPlay        = new HashMap<Integer, Wiki>();
  private boolean            mPlayWhenReady      = true;
  private int                mCorrectChoice      = -1;
  private int                mFetchWikiTaskCount = 0;
  private Difficulty        mDifficulty;
  private TextView          mDifficultyLabelTextView;
  private SoundPool         mSoundPool;
  private int               mSoundWrong;
  private int               mSoundCorrect;
  private SharedPreferences mSharedPreferences;
  private AudioManager      mAudioManager;
  private int               mShortAnimationLength;
  private int               mMediumAnimationLength;
  private int               mLongAnimationLength;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.app_007_activity_wikipedia_game);

    mSharedPreferences = getSharedPreferences("com.hulzenga.ioi.android.app_007", Context.MODE_PRIVATE);

    mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
    mShortAnimationLength = getResources().getInteger(android.R.integer.config_shortAnimTime);
    mMediumAnimationLength = getResources().getInteger(android.R.integer.config_mediumAnimTime);
    mLongAnimationLength = getResources().getInteger(android.R.integer.config_longAnimTime);

    mButtonsFragment = (ButtonsFragment) getFragmentManager().findFragmentById(R.id.app_007_buttonsFragment);
    mLinksFragment = (LinksFragment) getFragmentManager().findFragmentById(R.id.app_007_linksFragment);
    mStatusFragment = (StatusFragment) getFragmentManager().findFragmentById(R.id.app_007_statusFragment);

    mLinkText = (TextView) findViewById(R.id.app_007_linkListText);
    mProgressBar = (ProgressBar) findViewById(R.id.app_007_downloadProgressBar);
    mProgressBarTextView = (TextView) findViewById(R.id.app_007_downloadProgressText);

    mDifficultyLabelTextView = (TextView) findViewById(R.id.app_007_difficultyTextView);

    int difficulty = getIntent().getExtras().getInt(BUNDLE_DIFFICULTY);
    switch (difficulty) {
      case 0:
        setDifficulty(Difficulty.EASY);
        break;
      case 1:
        setDifficulty(Difficulty.NORMAL);
        break;
      case 2:
        setDifficulty(Difficulty.HARD);
        break;
      default:
        Log.e(TAG, "Unknown difficulty bundle settings: " + difficulty);
        break;
    }
  }

  private void setDifficulty(Difficulty gameDifficulty) {
    mDifficulty = gameDifficulty;

    mDifficultyLabelTextView.setText(getResources().getString(mDifficulty.label));
    mProgressBar.setMax(mDifficulty.numberOfOptions);
    mButtons = mButtonsFragment.setNumberOfButtons(mDifficulty.numberOfOptions);
  }

  @Override
  protected void onResume() {
    super.onResume();

    // setup the SoundPool
    mSoundPool = new SoundPool(2, AudioManager.STREAM_MUSIC, 0);
    mSoundCorrect = mSoundPool.load(this, R.raw.app_007_correct, 1);
    mSoundWrong = mSoundPool.load(this, R.raw.app_007_wrong, 1);

    List<Wiki> savedWikis = Wiki.loadWikis(this, SAVED_NOT_PLAYED_WIKIS, true);
    if (savedWikis.size() > 0) {
      mWikisInBuffer = savedWikis;
    }

    nextQuestion();
  }

  @Override
  protected void onPause() {
    super.onPause();

    // release SoundPoolresources
    mSoundPool.release();

    // release the timer
    mStatusFragment.stopTimer();

    // save unplayed wikis
    bringWikisBackToBuffer();
    Wiki.saveWikis(this, mWikisInBuffer, SAVED_NOT_PLAYED_WIKIS);

    // load the previously saved played Wikis
    List<Wiki> savedAndPlayedWikis = Wiki.loadWikis(this, SAVED_AND_PLAYED_WIKIS, true);

    if (savedAndPlayedWikis.size() > 0) {
      // calculate how much of the previously saved played Wikis to add to
      // the list
      int lengthOfPadding = Math.min(PLAYED_WIKIS_LENGTH - mWikisPlayed.size(), savedAndPlayedWikis.size());
      mWikisPlayed.addAll(savedAndPlayedWikis.subList(0, lengthOfPadding));
    }

    Wiki.saveWikis(this, mWikisPlayed, SAVED_AND_PLAYED_WIKIS);
  }

  private void bringWikisBackToBuffer() {

    for (Integer key : mWikisInPlay.keySet()) {
      mWikisInBuffer.add(mWikisInPlay.get(key));
    }
  }

  private void nextQuestion() {

    // calculate the difference between (the desired buffer size) and
    // (all currently allocated buffers plus running fetch tasks)
    int diff = bufferSpaceRemaining();

    // spawn new FetchWikiTask until either max parallel download count or
    // desired future buffer size is reached
    while (diff > 0 && mFetchWikiTaskCount < MAX_PARALLEL_DOWNLOADS) {
      new FetchWikiTask().execute();
      diff--;
    }

    if (!isBufferBigEnough()) {
      // Not enough Wikis in the buffers, so lock up the UI and download
      // more
      lockButtons();
      clearText();
      mPlayWhenReady = true;
      publishProgress();
      showProgressBar();

    } else {
      ShowNextQuestion();
    }
  }

  private int bufferSpaceRemaining() {
    return DESIRED_GAME_OPTION_BUFFER - (mWikisInBuffer.size() + mWikisInPlay.size() + mFetchWikiTaskCount);
  }

  private boolean isBufferBigEnough() {
    return mWikisInBuffer.size() + mWikisInPlay.size() >= mDifficulty.numberOfOptions;
  }

  private void lockButtons() {
    for (Button button : mButtons) {
      button.setClickable(false);
    }
  }

  private void clearText() {
    mLinkText.setText("");
  }

  /**
   * show the download progress by setting the Progressbar and ProgressbarText
   * accordingly
   */
  private void publishProgress() {
    int i = mWikisInBuffer.size();

    mProgressBar.setProgress(i);
    mLinkText.setText(getResources().getString(R.string.app_007_downloadProgress) + " ("
        + Constrain.lowerBound(0, i) + "/" + mDifficulty.numberOfOptions + ")");
  }

  /**
   * Make the ProgressBar and ProgressBarTextView visible
   */
  private void showProgressBar() {
    mProgressBar.animate().alpha(1f).setDuration(mShortAnimationLength);
    mProgressBarTextView.animate().alpha(1f).setDuration(mShortAnimationLength);
  }

  private void ShowNextQuestion() {
    mPlayWhenReady = false;

    List<Animator> animations = new ArrayList<Animator>();

    if (!mStatusFragment.isRunning()) {
      mStatusFragment.startTimer();
    }

    for (int i = 0; i < mDifficulty.numberOfOptions; i++) {
      if (!mWikisInPlay.containsKey(i)) {
        mWikisInPlay.put(i, mWikisInBuffer.remove(0));
        mButtons.get(i).setText(mWikisInPlay.get(i).getName());
        animations.add(ObjectAnimator.ofFloat(mButtons.get(i), View.ALPHA, 0.0f, 1.0f));
      }
    }

    AnimatorSet set = new AnimatorSet();
    set.playTogether(animations);
    set.setDuration(mShortAnimationLength);
    set.addListener(new AnimatorListener() {

      @Override
      public void onAnimationStart(Animator animation) {
        // do nothing
      }

      @Override
      public void onAnimationRepeat(Animator animation) {
        // do nothing
      }

      @Override
      public void onAnimationEnd(Animator animation) {
        releaseButtons();
      }

      @Override
      public void onAnimationCancel(Animator animation) {
        // do nothing
      }
    });
    set.start();

    mCorrectChoice = mRandom.nextInt(mDifficulty.numberOfOptions);
    showWikiLinks(mWikisInPlay.get(mCorrectChoice));
  }

  private void releaseButtons() {
    for (Button button : mButtons) {
      button.setClickable(true);
    }
  }

  private void showWikiLinks(Wiki wiki) {
    StringBuilder sb = new StringBuilder();

    sb.append("<ol>");
    for (String link : wiki.getLinks()) {
      sb.append("&#8226;").append(link).append("<br/>");
    }
    sb.append("</ol>");

    mLinkText.setText(Html.fromHtml(sb.toString()));
  }

  public void onTimeOut(int score) {
    lockButtons();
  }

  public void selectWiki(int selection) {
    // lock buttons to avoid simultaneous input
    lockButtons();

    float volume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);

    AnimatorSet set = new AnimatorSet();
    List<Animator> animations = new ArrayList<Animator>();

    final Button correctButton = mButtons.get(mCorrectChoice);
    final Drawable correctBg = correctButton.getBackground().mutate();
    final Button wrongButton = mButtons.get(selection);
    final Drawable wrongBg = wrongButton.getBackground();

    DeveloperTools.makeBackgroundColored(correctButton, Color.GREEN);

    if (selection == mCorrectChoice) {
      mStatusFragment.addPoint();

      // set the wiki to correct
      mWikisInPlay.get(mCorrectChoice).setCorrect(true);

      // correct guess sound
      mSoundPool.play(mSoundCorrect, volume, volume, 0, 0, 1);

      // fade out corect button
      animations.add(ObjectAnimator.ofFloat(mButtons.get(mCorrectChoice), View.ALPHA, 1.0f, 0.0f));
      set.setDuration(mShortAnimationLength);
    } else {
      mStatusFragment.penaltyPoints(mDifficulty.penalyPoints);

      // wrong guess sound
      mSoundPool.play(mSoundWrong, volume, volume, 0, 0, 1);

      DeveloperTools.makeBackgroundColored(wrongButton, Color.RED);
      animations.add(ObjectAnimator.ofFloat(mButtons.get(mCorrectChoice), View.ALPHA, 0.7f, 1.0f, 0.7f, 1.0f));

      // rotate the correct button to draw attention to it
      //animations.add(ObjectAnimator.ofFloat(mButtons.get(mCorrectChoice), View.SCALE_X, 1.0f, 1.15f, 1.0f));
      //animations.add(ObjectAnimator.ofFloat(mButtons.get(mCorrectChoice), View.SCALE_Y, 1.0f, 1.15f, 1.0f));
      set.setDuration(mLongAnimationLength);
    }

    set.playTogether(animations);
    set.addListener(new AnimatorListener() {

      @Override
      public void onAnimationStart(Animator animation) {
        // do nothing
      }

      @Override
      public void onAnimationRepeat(Animator animation) {
        // do nothing
      }

      @Override
      public void onAnimationEnd(Animator animation) {

        // return the buttons background to their original state
        correctButton.setBackgroundDrawable(correctBg);
        wrongButton.setBackgroundDrawable(wrongBg);

                /*
                 * remove the correct choice from the list of Wikis in play and
                 * move it to the played list. Strip links to save space.
                 */
        mWikisPlayed.add(0, mWikisInPlay.remove(mCorrectChoice).stripLinks());

        // make sure the played list does not become too big
        if (mWikisPlayed.size() > PLAYED_WIKIS_LENGTH) {
          mWikisPlayed.remove(mWikisPlayed.size() - 1);
        }

        nextQuestion();
      }

      @Override
      public void onAnimationCancel(Animator animation) {
        // do nothing
      }
    });

    set.start();
  }

  private void addWikiToBuffer(Wiki option) {
    mWikisInBuffer.add(option);
    if (mPlayWhenReady) {
      publishProgress();

      if (isBufferBigEnough()) {
        hideProgressBar();
        ShowNextQuestion();
      }
    }

    if (bufferSpaceRemaining() > 0) {
      // launch a new FetchWikiPage task
      new FetchWikiTask().execute();
    }
  }

  /**
   * Hide the ProgressBar and ProgressBarTextView by making them invisible
   */
  private void hideProgressBar() {
    mProgressBar.animate().alpha(0f).setDuration(mShortAnimationLength);
    mProgressBarTextView.animate().alpha(0f).setDuration(mShortAnimationLength);

  }

  public enum Difficulty {

    EASY(R.string.app_007_easy, 3, 10, 0),
    NORMAL(R.string.app_007_normal, 3, 6, 1),
    HARD(R.string.app_007_hard, 4, 5, 2);

    public final int label;
    public final int numberOfOptions;
    public final int numberOfLinks;
    public final int penalyPoints;

    private Difficulty(int label, int numberOfOptions, int numberOfLinks, int penaltyPoints) {
      this.label = label;
      this.numberOfOptions = numberOfOptions;
      this.numberOfLinks = numberOfLinks;
      this.penalyPoints = penaltyPoints;
    }
  }

  private class FetchWikiTask extends AsyncTask<ProgressBar, Integer, Wiki> {

    private static final String RANDOM_WIKI_PAGE_URL = "http://en.m.wikipedia.org/wiki/Special:Random";

    @Override
    protected Wiki doInBackground(ProgressBar... params) {

      String name = null;
      String adress = null;
      List<String> links = new ArrayList<String>(MAX_NUMBER_OF_LINKS);

      try {

        Document doc = Jsoup.connect(RANDOM_WIKI_PAGE_URL).timeout(10000).get();

        String fullname = doc.title();
        name = fullname.substring(0, fullname.length() - 35);
        adress = doc.location();

        // select the main content div
        Element content = doc.select("div#content > div").first();

        // remove open sections (see also, references etc.)
        content.select("div.section_heading openSection").remove();

        List<String> linkPool = new LinkedList<String>();
        String link;
        for (Element element : content.select("a[href]:not([href^=#])")) {
          link = element.text();

                    /*
                     * do not include any non descriptive, blank links (such as
                     * image links) or links whose text is a hyperlink
                     */
          if (link.length() >= MIN_LINK_LENGTH && !link.contains("http://")) {
            linkPool.add(link);
          }
        }

        while (links.size() < MAX_NUMBER_OF_LINKS && linkPool.size() > 0) {
          int r = mRandom.nextInt(linkPool.size());

          links.add(linkPool.get(r));
          linkPool.remove(r);
        }
      } catch (MalformedURLException e) {
        Log.e(TAG, "malformed URL, this should not be possible");
      } catch (IOException e) {
        Log.e(TAG, "IO Exception: " + e.getMessage());
      } catch (Exception e) {
        Log.e(TAG, "Exception while trying to download/parse random wiki page: " + e.getMessage());
      }

      return new Wiki(name, adress, links);
    }

    @Override
    protected void onPreExecute() {
      super.onPreExecute();
      mFetchWikiTaskCount++;
    }

    @Override
    protected void onPostExecute(Wiki result) {
      mFetchWikiTaskCount--;

      if (result.getName() != null && result.getLinks().size() > 0) {
        mRetriesLeft = MAX_DOWNLOAD_RETRIES;
        addWikiToBuffer(result);
      } else {
        mRetriesLeft--;

        // if there are no retries left and there are not enough Wikis
        // in the buffer to play a new round the app finishes
        if (mRetriesLeft <= 0 && !isBufferBigEnough()) {
          Toast.makeText(WikiGameActivity.this, getResources().getString(R.string.app_007_downloadFailure),
              Toast.LENGTH_LONG).show();
          finish();
        } else {
          new FetchWikiTask().execute();
        }
      }
    }
  }

}




Java Source Code List

com.hulzenga.ioi.android.AppActivity.java
com.hulzenga.ioi.android.AppDetailsDialog.java
com.hulzenga.ioi.android.App.java
com.hulzenga.ioi.android.TableOfContentsActivity.java
com.hulzenga.ioi.android.app_001.HelloWorldActivity.java
com.hulzenga.ioi.android.app_002.Ball.java
com.hulzenga.ioi.android.app_002.BouncyBallsActivity.java
com.hulzenga.ioi.android.app_002.BouncyBallsView.java
com.hulzenga.ioi.android.app_003.MonsterDatabaseActivity.java
com.hulzenga.ioi.android.app_003.MonsterEditDialog.java
com.hulzenga.ioi.android.app_003.MonsterGenerator.java
com.hulzenga.ioi.android.app_003.MonsterListAdapter.java
com.hulzenga.ioi.android.app_003.database.MonsterContract.java
com.hulzenga.ioi.android.app_003.database.MonsterDatabaseHelper.java
com.hulzenga.ioi.android.app_003.database.MonsterProvider.java
com.hulzenga.ioi.android.app_004.BouncyBall3dActivity.java
com.hulzenga.ioi.android.app_004.BouncyBall3dGLSurfaceView.java
com.hulzenga.ioi.android.app_004.BouncyBall3dRenderer.java
com.hulzenga.ioi.android.app_004.BouncyBallGame.java
com.hulzenga.ioi.android.app_005.ElementActivity.java
com.hulzenga.ioi.android.app_005.ElementAdapter.java
com.hulzenga.ioi.android.app_005.ElementAnimator.java
com.hulzenga.ioi.android.app_005.ElementSnakeView.java
com.hulzenga.ioi.android.app_005.ElementView.java
com.hulzenga.ioi.android.app_005.Element.java
com.hulzenga.ioi.android.app_006.EmptyFragment.java
com.hulzenga.ioi.android.app_006.ExposureFragment.java
com.hulzenga.ioi.android.app_006.SettingChangeListener.java
com.hulzenga.ioi.android.app_006.SettingIconFragment.java
com.hulzenga.ioi.android.app_006.SettingMenuFragment.java
com.hulzenga.ioi.android.app_006.SettingMenuLvl2Fragment.java
com.hulzenga.ioi.android.app_006.Setting.java
com.hulzenga.ioi.android.app_006.SimpleCameraActivity.java
com.hulzenga.ioi.android.app_006.SimpleCameraPreview.java
com.hulzenga.ioi.android.app_007.ButtonsFragment.java
com.hulzenga.ioi.android.app_007.HighScores.java
com.hulzenga.ioi.android.app_007.LinksFragment.java
com.hulzenga.ioi.android.app_007.Menu.java
com.hulzenga.ioi.android.app_007.Review.java
com.hulzenga.ioi.android.app_007.Score.java
com.hulzenga.ioi.android.app_007.StatusFragment.java
com.hulzenga.ioi.android.app_007.WikiGameActivity.java
com.hulzenga.ioi.android.app_007.Wiki.java
com.hulzenga.ioi.android.app_008.OverheidActivity.java
com.hulzenga.ioi.android.app_008.OverheidFeedAdapter.java
com.hulzenga.ioi.android.app_008.OverheidFeedFragment.java
com.hulzenga.ioi.android.app_008.OverheidFeedItem.java
com.hulzenga.ioi.android.app_008.OverheidFeed.java
com.hulzenga.ioi.android.app_008.OverheidPagerAdapter.java
com.hulzenga.ioi.android.app_009.ThumbsUpActivity.java
com.hulzenga.ioi.android.app_009.ThumbsUpServer.java
com.hulzenga.ioi.android.app_009.ThumbsUpService.java
com.hulzenga.ioi.android.app_010.CompassActivity.java
com.hulzenga.ioi.android.app_010.CompassDetailsView.java
com.hulzenga.ioi.android.app_010.CompassView.java
com.hulzenga.ioi.android.util.Constrain.java
com.hulzenga.ioi.android.util.DeveloperTools.java
com.hulzenga.ioi.android.util.FileManager.java
com.hulzenga.ioi.android.util.ScreenMetrics.java
com.hulzenga.ioi.android.util.open_gl.ColorFunctionFactory.java
com.hulzenga.ioi.android.util.open_gl.ColorFunction.java
com.hulzenga.ioi.android.util.open_gl.ShaderTools.java
com.hulzenga.ioi.android.util.open_gl.engine.NodeController.java
com.hulzenga.ioi.android.util.open_gl.engine.SceneGraph.java
com.hulzenga.ioi.android.util.open_gl.engine.SceneNode.java
com.hulzenga.ioi.android.util.open_gl.geometry.Box.java
com.hulzenga.ioi.android.util.open_gl.geometry.Cylinder.java
com.hulzenga.ioi.android.util.open_gl.geometry.Geometry.java
com.hulzenga.ioi.android.util.open_gl.geometry.Grid.java
com.hulzenga.ioi.android.util.open_gl.geometry.Sphere.java
com.hulzenga.ioi.android.util.open_gl.geometry.Transform.java
com.hulzenga.ioi.android.util.open_gl.vector.Vec3.java
com.hulzenga.ioi.android.util.open_gl.vector.Vec4.java
fi.iki.elonen.NanoHTTPD.java