Java tutorial
/* * Copyright 2013 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.wolff.androidhunt; import android.content.Context; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.content.res.Resources; import android.provider.Settings; import android.provider.Settings.Secure; import android.util.Log; import android.widget.ImageView; import org.json.JSONArray; import org.json.JSONObject; import java.math.BigInteger; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Random; public class Hunt { static Hunt theHunt; static HuntResourceManager hrm; public Boolean isShuffled = false; public long finishTime; public SoundManager soundManager; HashMap<String, Boolean> tagsFound; HashMap<String, AHTag> tags; HashMap<String, Clue> clues; ArrayList<Clue> clueList; ArrayList<AHTag> tagList; private boolean hasSeenIntro = false; static final String HAS_SEEN_INTRO_KEY = "HAS_SEEN_INTRO_KEY"; static final String WRONG_CLUE = "WRONG CLUE"; static final String ACK = "ACK"; static final String CLUE_COMPLETE = "CLUE COMPLETE"; static final String ALREADY_FOUND = "ALREADY FOUND"; static final String DECOY = "DECOY"; // The actual text for the DECOY clue. static final String DECOY_ID = "decoy"; /** Returns the singleton hunt object, and initializes it if it's not ready. */ public static Hunt getHunt(Resources res, Context context) { if (theHunt == null) { hrm = new HuntResourceManager(); hrm.unzipFile(res); theHunt = new Hunt(hrm.huntJSON, res, context); String android_id = Secure.getString(context.getContentResolver(), Secure.ANDROID_ID); if (android_id == null) { // Fall back on devices where ANDROID_ID is not reliable. theHunt.shuffle(Integer.parseInt(Settings.Secure.ANDROID_ID, 0)); } else { BigInteger bi = new BigInteger(android_id, 16); System.out.println(bi); theHunt.shuffle(bi.shortValue()); } } return theHunt; } /** Shuffles the clues. Note that each clue is marked with * a difficulty group, so that, say, a hard clue can't proceed * an easier clue. * @param seed The random number seed. */ public void shuffle(int seed) { if (isShuffled) { return; } // Divide into shuffle groups ArrayList<ArrayList<Clue>> groups = new ArrayList<ArrayList<Clue>>(10); for (int i = 0; i < 10; i++) { groups.add(null); } for (int i = 0; i < clueList.size(); i++) { Clue c = clueList.get(i); if (groups.get(c.shufflegroup) == null) { groups.set(c.shufflegroup, new ArrayList<Clue>()); } groups.get(c.shufflegroup).add(c); } clueList = new ArrayList<Clue>(); Random r = new Random(seed); for (int i = 0; i < 10; i++) { ArrayList<Clue> cl = groups.get(i); if (cl == null) continue; Collections.shuffle(cl, r); clueList.addAll(cl); } isShuffled = true; } /** * Saves the player's progress. * * @param res * @param context */ public void save(Resources res, Context context) { SharedPreferences sharedPref = context.getSharedPreferences(res.getString(R.string.preference_file_key), Context.MODE_PRIVATE); Editor editor = sharedPref.edit(); for (String key : tagsFound.keySet()) { editor.putBoolean(key, tagsFound.get(key)); } editor.putBoolean(HAS_SEEN_INTRO_KEY, hasSeenIntro); editor.commit(); } /** Loads player progress. */ public void restore(Resources res, Context context) { SharedPreferences sharedPref = context.getSharedPreferences(res.getString(R.string.preference_file_key), Context.MODE_PRIVATE); for (String tag : tags.keySet()) { Boolean val = sharedPref.getBoolean(tag, false); tagsFound.put(tag, val); } hasSeenIntro = sharedPref.getBoolean(HAS_SEEN_INTRO_KEY, false); } /** Generates the entire hunt structure from JSON */ Hunt(String jsonString, Resources res, Context context) { soundManager = new SoundManager(context); try { clues = new HashMap<String, Clue>(); clueList = new ArrayList<Clue>(); tags = new HashMap<String, AHTag>(); JSONObject huntObject = new JSONObject(jsonString); JSONArray clueObjList = huntObject.getJSONArray("clues"); tagList = new ArrayList<AHTag>(); int length = clueObjList.length(); for (int i = 0; i < length; i++) { JSONObject clueObj = clueObjList.getJSONObject(i); Clue clue = new Clue(clueObj.getString("id"), clueObj.getString("displayName"), clueObj.getString("displayText"), clueObj.getString("displayImage")); clue.shufflegroup = clueObj.getInt("shufflegroup"); JSONArray tagObjList = clueObj.getJSONArray("tags"); int tagLength = tagObjList.length(); clueList.add(clue); for (int j = 0; j < tagLength; j++) { JSONObject tagObj = tagObjList.getJSONObject(j); AHTag tag = new AHTag(tagObj.getString("id")); tag.clueId = clue.id; clue.addTag(tag); tags.put(tag.id, tag); tagList.add(tag); } clues.put(clue.id, clue); } } catch (Exception e) { if (e != null) Log.e("JSON Parser", "Error parsing Hunt data " + e.toString()); } reset(); restore(res, context); } /** Deletes all player progress.*/ public void reset() { tagsFound = new HashMap<String, Boolean>(); for (AHTag tag : tagList) { tagsFound.put(tag.id, false); } hasSeenIntro = false; } /** Gets active clue. * * One objection to this is that we are computing progress rather than remembering. * * This is deliberate; if we cache progress, we are likely * to get be wrong, especially during state transitions. The number of * operations here is very small and it is called infrequently, so this saves us the * trouble of maintaining and persisting progress. * * If this were a performance issue (called in an inner loop, for example), of course * we would cache this. * @return The current clue, or else null if you are finished. */ Clue getCurrentClue() { int length = clueList.size(); for (int i = 0; i < length; i++) { Clue clue = clueList.get(i); if (!isClueFinished(clue)) { return clue; } } // The hunt is complete! return null; } /** What clue have I *just* completed? */ public Clue getLastCompletedClue() { int length = clueList.size(); Clue lastBestClue = null; for (int i = 0; i < length; i++) { Clue clue = clueList.get(i); if (!isClueFinished(clue)) { return lastBestClue; } lastBestClue = clue; } // The hunt is complete. return lastBestClue; } public Boolean isTagFound(String id) { if (!tagsFound.containsKey(id)) { return false; } return tagsFound.get(id); } /** * Called when a tag is scanned. Checks the hunt * * @param tagId the short string that represents the tag found. * @return */ String findTag(String tagId) { if (tagId.equals(DECOY_ID)) { return DECOY; } // See if this tag is part of this clue Clue clue = getCurrentClue(); AHTag tag = tags.get(tagId); if (tag == null) { return WRONG_CLUE; } if (clue.id.equals(tag.clueId)) { if (isTagFound(tagId)) { return ALREADY_FOUND; } tagsFound.put(tag.id, true); if (isClueFinished(clue)) { return CLUE_COMPLETE; } return ACK; } return WRONG_CLUE; } /** Have we found all the clues?*/ Boolean isClueFinished(Clue clue) { for (AHTag tag : clue.tags) { if (!isTagFound(tag.id)) { return false; } } return true; } public int getTotalClues() { return clueList.size(); } /** Count from 1. */ public int getClueDisplayNumber(Clue clue) { return clueList.indexOf(clue) + 1; } /** Count from 0. */ public int getClueIndex(Clue clue) { return clueList.indexOf(clue); } /** Return value: Whether or not it needs to load from web. */ public void setClueImage(Resources res, ImageView imgView) { final Clue clue = getCurrentClue(); if (hrm.drawables.get(clue.displayImage) == null) { imgView.setImageDrawable(res.getDrawable(R.drawable.ab_icon)); } else { imgView.setImageDrawable(hrm.drawables.get(clue.displayImage)); } } public boolean hasSeenIntro() { return hasSeenIntro; } public void setIntroSeen(Boolean val) { hasSeenIntro = val; } public boolean isComplete() { return (getCurrentClue() == null); } }