project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/MapFragment.java
changeset 7508 763d3961400b
child 7560 e7ab89ab86f6
equal deleted inserted replaced
7504:ed1d52c5aa94 7508:763d3961400b
       
     1 package org.hedgewars.hedgeroid;
       
     2 
       
     3 import java.io.IOException;
       
     4 import java.util.Arrays;
       
     5 import java.util.Collections;
       
     6 import java.util.List;
       
     7 import java.util.Random;
       
     8 
       
     9 import org.hedgewars.hedgeroid.R;
       
    10 import org.hedgewars.hedgeroid.Datastructures.FrontendDataUtils;
       
    11 import org.hedgewars.hedgeroid.Datastructures.MapFile;
       
    12 import org.hedgewars.hedgeroid.Datastructures.MapRecipe;
       
    13 import org.hedgewars.hedgeroid.Datastructures.Scheme;
       
    14 import org.hedgewars.hedgeroid.Datastructures.Weaponset;
       
    15 import org.hedgewars.hedgeroid.frontlib.Frontlib;
       
    16 
       
    17 import android.content.Context;
       
    18 import android.graphics.drawable.Drawable;
       
    19 import android.os.Bundle;
       
    20 import android.os.Handler;
       
    21 import android.os.Looper;
       
    22 import android.os.Message;
       
    23 import android.support.v4.app.Fragment;
       
    24 import android.view.LayoutInflater;
       
    25 import android.view.View;
       
    26 import android.view.View.OnClickListener;
       
    27 import android.view.ViewGroup;
       
    28 import android.widget.AdapterView;
       
    29 import android.widget.AdapterView.OnItemSelectedListener;
       
    30 import android.widget.ArrayAdapter;
       
    31 import android.widget.ImageView;
       
    32 import android.widget.Spinner;
       
    33 import android.widget.TableRow;
       
    34 import android.widget.Toast;
       
    35 
       
    36 public class MapFragment extends Fragment implements RoomStateManager.Observer {
       
    37 	private Spinner mapTypeSpinner, mapNameSpinner, templateSpinner, mazeSizeSpinner;
       
    38 	private TableRow nameRow, templateRow, mazeSizeRow;
       
    39 	private ImageView mapPreview;
       
    40 	private List<MapFile> mapFiles;
       
    41 	private RoomStateManager stateManager;
       
    42 	private Random random = new Random();
       
    43 	private CalmDownHandler mapPreviewHandler;
       
    44 	
       
    45 	/*
       
    46 	 * Rendering the preview can take a few seconds on Android, so we want to prevent preview
       
    47 	 * requests from queueing up if maps are changed quickly. So if there is already a preview
       
    48 	 * being generated, we store our latest request in the newPreviewRequest variable instead.
       
    49 	 * Once the current preview is finished generating it will start on that one.
       
    50 	 */
       
    51 	private boolean previewGenerationInProgress;
       
    52 	private MapRecipe newPreviewRequest;
       
    53 	
       
    54 	@Override
       
    55 	public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
       
    56 		View v = inflater.inflate(R.layout.fragment_map, container, false);
       
    57 
       
    58 		final Context appContext = getActivity().getApplicationContext();
       
    59 		mapPreviewHandler = new CalmDownHandler(getActivity().getMainLooper(), new Runnable() {
       
    60 			public void run() {
       
    61 				if(!previewGenerationInProgress) {
       
    62 					mapPreview.setImageResource(R.drawable.roomlist_preparing);
       
    63 					MapPreviewGenerator.startPreviewGeneration(appContext, stateManager.getMapRecipe(), mapPreviewListener);
       
    64 					previewGenerationInProgress = true;
       
    65 				} else {
       
    66 					newPreviewRequest = stateManager.getMapRecipe();
       
    67 				}
       
    68 			}
       
    69 		}, 250);
       
    70 		
       
    71 		nameRow = (TableRow) v.findViewById(R.id.rowMapName);
       
    72 		templateRow = (TableRow) v.findViewById(R.id.rowTemplateFilter);
       
    73 		mazeSizeRow = (TableRow) v.findViewById(R.id.rowMazeSize);
       
    74 		mapPreview = (ImageView) v.findViewById(R.id.mapPreview);
       
    75 		mapPreview.setImageDrawable(null);;
       
    76 		mapPreview.setOnClickListener(mapClickListener);
       
    77 		
       
    78 		try {
       
    79 			mapFiles = FrontendDataUtils.getMaps(getActivity());
       
    80 		} catch (IOException e) {
       
    81 			Toast.makeText(getActivity().getApplicationContext(), R.string.error_missing_sdcard_or_files, Toast.LENGTH_LONG).show();
       
    82 			getActivity().finish();
       
    83 		}
       
    84 		Collections.sort(mapFiles, MapFile.MISSIONS_FIRST_NAME_ORDER);
       
    85 		
       
    86 		List<String> mapNames = MapFile.toDisplayNameList(mapFiles, getResources());
       
    87 		mapTypeSpinner = prepareSpinner(v, R.id.spinMapType, Arrays.asList(getResources().getStringArray(R.array.map_types)), mapTypeSelectedListener);
       
    88 		mapNameSpinner = prepareSpinner(v, R.id.spinMapName, mapNames, mapNameSelectedListener);
       
    89 		templateSpinner = prepareSpinner(v, R.id.spinTemplateFilter, Arrays.asList(getResources().getStringArray(R.array.map_templates)), mapTemplateSelectedListener);
       
    90 		mazeSizeSpinner = prepareSpinner(v, R.id.spinMazeSize, Arrays.asList(getResources().getStringArray(R.array.map_maze_sizes)), mazeSizeSelectedListener);
       
    91 
       
    92 		stateManager.registerObserver(this);
       
    93 		MapRecipe map = stateManager.getMapRecipe();
       
    94 		if(map != null) {
       
    95 			updateDisplay(map);
       
    96 		}
       
    97 		setChiefState(stateManager.getChiefStatus());
       
    98 		mapPreviewHandler.activity();
       
    99 		return v;
       
   100 	}
       
   101 	
       
   102 	private static Spinner prepareSpinner(View v, int id, List<String> items, OnItemSelectedListener itemSelectedListener) {
       
   103 		Spinner spinner = (Spinner)v.findViewById(id);
       
   104 		ArrayAdapter<String> adapter = new ArrayAdapter<String>(v.getContext(), R.layout.listview_item, items);
       
   105 		adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
       
   106 		spinner.setAdapter(adapter);
       
   107 		spinner.setOnItemSelectedListener(itemSelectedListener);
       
   108 		return spinner;
       
   109 	}
       
   110 	
       
   111 	@Override
       
   112 	public void onCreate(Bundle savedInstanceState) {
       
   113 		super.onCreate(savedInstanceState);
       
   114 		try {
       
   115 			stateManager = ((RoomStateManager.Provider)getActivity()).getRoomStateManager();
       
   116 		} catch(ClassCastException e) {
       
   117 			throw new RuntimeException("Hosting activity must implement RoomStateManager.Provider.", e);
       
   118 		}
       
   119 	}
       
   120 	
       
   121 	@Override
       
   122 	public void onDestroy() {
       
   123 		super.onDestroy();
       
   124 		mapPreviewHandler.stop();
       
   125 		stateManager.unregisterObserver(this);
       
   126 	}
       
   127 	
       
   128 	private void setChiefState(boolean chiefState) {
       
   129 		mapTypeSpinner.setEnabled(chiefState);
       
   130 		mapNameSpinner.setEnabled(chiefState);
       
   131 		templateSpinner.setEnabled(chiefState);
       
   132 		mazeSizeSpinner.setEnabled(chiefState);
       
   133 		mapPreview.setEnabled(chiefState);
       
   134 		
       
   135 		if(chiefState) {
       
   136 			sendMapnameAndGenerator();
       
   137 			stateManager.changeMapTemplate(templateSpinner.getSelectedItemPosition());
       
   138 			stateManager.changeMazeSize(mazeSizeSpinner.getSelectedItemPosition());
       
   139 		}
       
   140 	}
       
   141 	
       
   142 	private void updateDisplay(MapRecipe map) {
       
   143 		nameRow.setVisibility(map.mapgen == Frontlib.MAPGEN_NAMED ? View.VISIBLE : View.GONE);
       
   144 		templateRow.setVisibility(map.mapgen == Frontlib.MAPGEN_REGULAR ? View.VISIBLE : View.GONE);
       
   145 		mazeSizeRow.setVisibility(map.mapgen == Frontlib.MAPGEN_MAZE ? View.VISIBLE : View.GONE);
       
   146 		
       
   147 		mapTypeSpinner.setSelection(map.mapgen);
       
   148 		int mapPosition = findMapPosition(mapFiles, map.name);
       
   149 		if(mapPosition >= 0) {
       
   150 			mapNameSpinner.setSelection(mapPosition);
       
   151 		}
       
   152 		templateSpinner.setSelection(map.templateFilter);
       
   153 		mazeSizeSpinner.setSelection(map.mazeSize);
       
   154 	}
       
   155 	
       
   156 	private static int findMapPosition(List<MapFile> mapFiles, String mapName) {
       
   157 		for(int i=0; i<mapFiles.size(); i++) {
       
   158 			if(mapName.equals(mapFiles.get(i).name)) {
       
   159 				return i;
       
   160 			}
       
   161 		}
       
   162 		return -1;
       
   163 	}
       
   164 	
       
   165 	private void sendMapnameAndGenerator() {
       
   166 		int mapType = mapTypeSpinner.getSelectedItemPosition();
       
   167 		String mapName = mapFiles.get(mapNameSpinner.getSelectedItemPosition()).name;
       
   168 		stateManager.changeMapNameAndGenerator(MapRecipe.mapnameForGenerator(mapType, mapName));
       
   169 	}
       
   170 	
       
   171 	private final OnItemSelectedListener mapTypeSelectedListener = new OnItemSelectedListener() {
       
   172 		public void onItemSelected(AdapterView<?> adapter, View v, int position, long arg3) {
       
   173 			sendMapnameAndGenerator();
       
   174 		}
       
   175 		public void onNothingSelected(AdapterView<?> arg0) {}
       
   176 	};
       
   177 	
       
   178 	private final OnItemSelectedListener mapNameSelectedListener = new OnItemSelectedListener() {
       
   179 		public void onItemSelected(AdapterView<?> adapter, View v, int position, long arg3) {
       
   180 			sendMapnameAndGenerator();
       
   181 		}
       
   182 		public void onNothingSelected(AdapterView<?> arg0) {}
       
   183 	};
       
   184 	
       
   185 	private final OnItemSelectedListener mapTemplateSelectedListener = new OnItemSelectedListener() {
       
   186 		public void onItemSelected(AdapterView<?> adapter, View v, int position, long arg3) {
       
   187 			stateManager.changeMapTemplate(position);
       
   188 		}
       
   189 		public void onNothingSelected(AdapterView<?> arg0) {}
       
   190 	};
       
   191 	
       
   192 	private final OnItemSelectedListener mazeSizeSelectedListener = new OnItemSelectedListener() {
       
   193 		public void onItemSelected(AdapterView<?> adapter, View v, int position, long arg3) {
       
   194 			stateManager.changeMazeSize(position);
       
   195 		}
       
   196 		public void onNothingSelected(AdapterView<?> arg0) {}
       
   197 	};
       
   198 	
       
   199 	private final OnClickListener mapClickListener = new OnClickListener() {
       
   200 		public void onClick(View v) {
       
   201 			stateManager.changeMapSeed(MapRecipe.makeRandomSeed());
       
   202 			switch(mapTypeSpinner.getSelectedItemPosition()) {
       
   203 			case Frontlib.MAPGEN_NAMED:
       
   204 				mapNameSpinner.setSelection(random.nextInt(mapNameSpinner.getCount()));
       
   205 				break;
       
   206 			case Frontlib.MAPGEN_REGULAR:
       
   207 				templateSpinner.setSelection(Frontlib.TEMPLATEFILTER_ALL);
       
   208 				break;
       
   209 			case Frontlib.MAPGEN_MAZE:
       
   210 				mazeSizeSpinner.setSelection(random.nextInt(mazeSizeSpinner.getCount()));
       
   211 				break;
       
   212 			}
       
   213 		}
       
   214 	};
       
   215 	
       
   216 	public void onChiefStatusChanged(boolean isChief) {
       
   217 		setChiefState(isChief);
       
   218 	}
       
   219 	
       
   220 	public void onMapChanged(MapRecipe recipe) {
       
   221 		updateDisplay(recipe);
       
   222 		mapPreviewHandler.activity();
       
   223 	}
       
   224 	
       
   225 	public void onGameStyleChanged(String gameStyle) { }
       
   226 	public void onSchemeChanged(Scheme scheme) { }
       
   227 	public void onWeaponsetChanged(Weaponset weaponset) { }
       
   228 	
       
   229 	private MapPreviewGenerator.Listener mapPreviewListener = new MapPreviewGenerator.Listener() {
       
   230 		public void onMapPreviewResult(Drawable preview) {
       
   231 			if(newPreviewRequest != null) {
       
   232 				MapPreviewGenerator.startPreviewGeneration(getActivity().getApplicationContext(), newPreviewRequest, mapPreviewListener);
       
   233 				newPreviewRequest = null;
       
   234 			} else {
       
   235 				if(mapPreview != null) {
       
   236 					mapPreview.setImageDrawable(preview);
       
   237 				}
       
   238 				previewGenerationInProgress = false;
       
   239 			}
       
   240 		}
       
   241 	};
       
   242 	
       
   243 	/**
       
   244 	 * This class allows you to define a runnable that is called when there has been no activity
       
   245 	 * for a set amount of time, where activity is determined by calls to the activity() method
       
   246 	 * of the handler. It is used here to update the map preview when there have been no updates
       
   247 	 * to the relevant map information for a time, to prevent triggering several updates at once
       
   248 	 * when different parts of the information change.
       
   249 	 */
       
   250 	private static final class CalmDownHandler extends Handler {
       
   251 		int runningMessagesCounter = 0;
       
   252 		final Runnable inactivityRunnable;
       
   253 		final long inactivityMs;
       
   254 		boolean stopped;
       
   255 
       
   256 		public CalmDownHandler(Looper looper, Runnable runnable, long inactivityMs) {
       
   257 			super(looper);
       
   258 			this.inactivityRunnable = runnable;
       
   259 			this.inactivityMs = inactivityMs;
       
   260 		}
       
   261 		
       
   262 		public void activity() {
       
   263 			runningMessagesCounter++;
       
   264 			sendMessageDelayed(obtainMessage(), inactivityMs);
       
   265 		}
       
   266 		
       
   267 		@Override
       
   268 		public void handleMessage(Message msg) {
       
   269 			runningMessagesCounter--;
       
   270 			if(runningMessagesCounter==0 && !stopped) {
       
   271 				inactivityRunnable.run();
       
   272 			}
       
   273 		}
       
   274 		
       
   275 		public void stop() {
       
   276 			stopped = true;
       
   277 		}
       
   278 	}
       
   279 }