Java tutorial
/* * AnyPlace: A free and open Indoor Navigation Service with superb accuracy! * * Anyplace is a first-of-a-kind indoor information service offering GPS-less * localization, navigation and search inside buildings using ordinary smartphones. * * Author(s): Timotheos Constambeys, Lambros Petrou * * Supervisor: Demetrios Zeinalipour-Yazti * * URL: http://anyplace.cs.ucy.ac.cy * Contact: anyplace@cs.ucy.ac.cy * * Copyright (c) 2015, Data Management Systems Lab (DMSL), University of Cyprus. * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in the * Software without restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, subject to the * following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * */ package com.dmsl.anyplace; import java.io.File; import java.net.URLDecoder; import java.util.ArrayList; import java.util.List; import android.app.AlertDialog; import android.app.ProgressDialog; import android.content.DialogInterface; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.support.v4.app.FragmentActivity; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.Window; import android.widget.AdapterView; import android.widget.AdapterView.OnItemSelectedListener; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.EditText; import android.widget.Spinner; import android.widget.Toast; import com.dmsl.anyplace.AnyplaceAPI; import com.dmsl.anyplace.cache.AnyplaceCache; import com.dmsl.anyplace.floor.Algo1Server; import com.dmsl.anyplace.floor.FloorSelector; import com.dmsl.anyplace.floor.FloorSelector.ErrorAnyplaceFloorListener; import com.dmsl.anyplace.floor.FloorSelector.FloorAnyplaceFloorListener; import com.dmsl.anyplace.nav.BuildingModel; import com.dmsl.anyplace.nav.FloorModel; import com.dmsl.anyplace.tasks.FetchBuildingsByBuidTask; import com.dmsl.anyplace.tasks.FetchBuildingsTask.FetchBuildingsTaskListener; import com.dmsl.anyplace.tasks.FetchFloorPlanTask; import com.dmsl.anyplace.tasks.FetchFloorsByBuidTask.FetchFloorsByBuidTaskListener; import com.dmsl.anyplace.tasks.FetchNearBuildingsTask; import com.dmsl.anyplace.tasks.FetchNearBuildingsTask.BuildingModelDistance; public class SelectBuildingActivity extends FragmentActivity implements FloorAnyplaceFloorListener, ErrorAnyplaceFloorListener { public enum Mode { NONE, NEAREST, // Automatically show nearby Building INVISIBLE // Automatically submit } private Spinner spinnerBuildings; private Spinner spinnerFloors; private Button btnRefreshWorldBuildings; private Button btnRefreshNearmeBuildings; private Button btnRefreshFloors; private String lat; private String lon; private Mode mode; private Integer selectedFloorIndex = 0; // <Floor Selector> private boolean isBuildingsLoadingFinished; private boolean isfloorSelectorJobFinished; private FloorSelector floorSelector; private String floorResult; private ProgressDialog floorSelectorDialog; // </Floor Selector> private boolean isBuildingsJobRunning = false; private boolean isFloorsJobRunning = false; private AnyplaceCache mAnyplaceCache = null; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature((int) Window.FEATURE_NO_TITLE); setContentView(R.layout.activity_select_building); // get the AnyplaceCache instance mAnyplaceCache = AnyplaceCache.getInstance(this); btnRefreshWorldBuildings = (Button) findViewById(R.id.btnWorldBuildingsRefresh); btnRefreshWorldBuildings.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (isBuildingsJobRunning) { Toast.makeText(getBaseContext(), "Another building request is in process...", Toast.LENGTH_SHORT).show(); return; } startBuildingsFetch(false, true); } }); btnRefreshNearmeBuildings = (Button) findViewById(R.id.btnNearmeBuildingsRefresh); btnRefreshNearmeBuildings.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (isBuildingsJobRunning) { Toast.makeText(getBaseContext(), "Another building request is in process...", Toast.LENGTH_SHORT).show(); return; } startBuildingsFetch(true, false); } }); spinnerFloors = (Spinner) findViewById(R.id.floors); spinnerFloors.setOnItemSelectedListener(new OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) { selectedFloorIndex = pos; } @Override public void onNothingSelected(AdapterView<?> parent) { } }); spinnerBuildings = (Spinner) findViewById(R.id.buildings); spinnerBuildings.setOnItemSelectedListener(new OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) { if (isFloorsJobRunning) { Toast.makeText(getBaseContext(), "Another request is in process...", Toast.LENGTH_SHORT).show(); return; } mAnyplaceCache.setSelectedBuildingIndex(pos); BuildingModel build = mAnyplaceCache.getSelectedBuilding(); if (build != null && build.isFloorsLoaded()) { setFloorSpinner(build.getFloors()); try { spinnerFloors.setSelection(build.getSelectedFloorIndex()); } catch (IndexOutOfBoundsException ex) { } onFloorsLoaded(build.getFloors()); } else { startFloorFetch(); } } @Override public void onNothingSelected(AdapterView<?> parent) { } }); btnRefreshFloors = (Button) findViewById(R.id.btnFloorsRefresh); btnRefreshFloors.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (isFloorsJobRunning) { Toast.makeText(getBaseContext(), "Another floor request is in process...", Toast.LENGTH_SHORT) .show(); return; } try { startFloorFetch(); } catch (IndexOutOfBoundsException ex) { Toast.makeText(getBaseContext(), "Check again the building selected...", Toast.LENGTH_SHORT) .show(); } } }); Button done = (Button) findViewById(R.id.btnDone); done.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { startFloorPlanTask(); } }); Bundle b = this.getIntent().getExtras(); if (b == null) { mode = Mode.NONE; lat = "0.0"; lon = "0.0"; } else { mode = (Mode) b.getSerializable("mode"); if (mode == null) mode = Mode.NONE; lat = b.getString("coordinates_lat"); if (lat == null) { lat = "0.0"; mode = Mode.NONE; btnRefreshNearmeBuildings.setEnabled(false); } lon = b.getString("coordinates_lon"); if (lon == null) { lon = "0.0"; mode = Mode.NONE; btnRefreshNearmeBuildings.setEnabled(false); } } // start automatically the buildings task if invisible mode if (mode != Mode.NONE) { floorSelectorDialog = new ProgressDialog(SelectBuildingActivity.this); floorSelectorDialog.setIndeterminate(true); floorSelectorDialog.setTitle("Detecting floor"); floorSelectorDialog.setMessage("Please be patient..."); floorSelectorDialog.setCancelable(true); floorSelectorDialog.setCanceledOnTouchOutside(false); floorSelector = new Algo1Server(getApplicationContext()); floorSelector.addListener((FloorSelector.FloorAnyplaceFloorListener) this); floorSelector.addListener((FloorSelector.ErrorAnyplaceFloorListener) this); isBuildingsLoadingFinished = false; isfloorSelectorJobFinished = false; floorSelector.Start(lat, lon); startBuildingsFetch(true, false); } else { List<BuildingModel> buildings = mAnyplaceCache.getSpinnerBuildings(); if (buildings.size() == 0) { startBuildingsFetch(false, false); } else { setBuildingSpinner(buildings); spinnerBuildings.setSelection(mAnyplaceCache.getSelectedBuildingIndex()); } } } @Override protected void onStart() { super.onStart(); } @Override protected void onResume() { super.onResume(); } // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override protected void onPause() { super.onPause(); } // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override protected void onStop() { super.onStop(); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override protected void onRestart() { super.onRestart(); } // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override protected void onDestroy() { super.onDestroy(); if (floorSelector != null) { floorSelector.removeListener((FloorSelector.FloorAnyplaceFloorListener) this); floorSelector.removeListener((FloorSelector.ErrorAnyplaceFloorListener) this); floorSelector.Destoy(); } System.gc(); } /** * @see android.app.Activity#onCreateOptionsMenu(android.view.Menu) */ @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.main_menu_select_building, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.main_menu_add_building: { AlertDialog.Builder alert = new AlertDialog.Builder(this); alert.setTitle("Add Building"); alert.setMessage("Private Building's ID"); // Set an EditText view to get user input final EditText editText = new EditText(this); alert.setView(editText); alert.setPositiveButton("Ok", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { try { // http://anyplace.cs.ucy.ac.cy/viewer/?buid=building_2f25420e-3cb1-4bc1-9996-3939e5530d30_1414014035379 // OR // building_2f25420e-3cb1-4bc1-9996-3939e5530d30_1414014035379 String input = editText.getText().toString().trim(); String buid = input; if (input.startsWith("http")) { Uri uri = Uri.parse(URLDecoder.decode(input, "UTF-8")); buid = uri.getQueryParameter("buid"); if (buid == null) { throw new Exception("Missing buid parameter"); } } new FetchBuildingsByBuidTask( new FetchBuildingsByBuidTask.FetchBuildingsByBuidTaskListener() { @Override public void onSuccess(String result, BuildingModel b) { ArrayList<BuildingModel> list = new ArrayList<BuildingModel>(1); list.add(b); setBuildingSpinner(list); } @Override public void onErrorOrCancel(String result) { Toast.makeText(getBaseContext(), result, Toast.LENGTH_SHORT).show(); } }, SelectBuildingActivity.this, buid).execute(); } catch (Exception e) { Toast.makeText(getBaseContext(), e.getMessage(), Toast.LENGTH_SHORT).show(); } } }); alert.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { // Canceled. } }); alert.show(); return true; } } return false; } private void startBuildingsFetch(final Boolean nearestOnly, Boolean forceReload) { final boolean btnRefreshWorldBuildingsState = btnRefreshWorldBuildings.isEnabled(); final boolean btnRefreshNearmeBuildingsState = btnRefreshNearmeBuildings.isEnabled(); btnRefreshWorldBuildings.setEnabled(false); btnRefreshNearmeBuildings.setEnabled(false); isBuildingsJobRunning = true; mAnyplaceCache.loadWorldBuildings(new FetchBuildingsTaskListener() { @Override public void onSuccess(String result, List<BuildingModel> buildings) { finishJob(); if (nearestOnly) { FetchNearBuildingsTask nearBuildings = new FetchNearBuildingsTask(); nearBuildings.run(buildings, lat, lon, 10000); // if the user should not interact with the gui and // automatically load the building if (mode == Mode.INVISIBLE && (nearBuildings.buildings.isEmpty() || nearBuildings.distances.get(0).distance > 200)) { // exit the activity since no building exists in your area Intent returnIntent = new Intent(); returnIntent.putExtra("message", "No buildings around you!"); setResult(RESULT_CANCELED, returnIntent); finish(); } else if (nearBuildings.buildings.isEmpty()) { Toast.makeText(getBaseContext(), "No buildings around you!", Toast.LENGTH_SHORT).show(); } else { setBuildingSpinner(nearBuildings.buildings, nearBuildings.distances); } } else { setBuildingSpinner(buildings); } } @Override public void onErrorOrCancel(String result) { finishJob(); Toast.makeText(getBaseContext(), result, Toast.LENGTH_SHORT).show(); } private void finishJob() { // Enable only if invisibleSelection==false btnRefreshWorldBuildings.setEnabled(btnRefreshWorldBuildingsState); btnRefreshNearmeBuildings.setEnabled(btnRefreshNearmeBuildingsState); isBuildingsJobRunning = false; } }, this, forceReload); } private void startFloorFetch() throws IndexOutOfBoundsException { BuildingModel building = mAnyplaceCache.getSelectedBuilding(); if (building != null) { spinnerBuildings.setEnabled(false); building.loadFloors(new FetchFloorsByBuidTaskListener() { @Override public void onSuccess(String result, List<FloorModel> floors) { finishJob(floors); onFloorsLoaded(floors); } @Override public void onErrorOrCancel(String result) { finishJob(new ArrayList<FloorModel>()); Toast.makeText(getBaseContext(), result, Toast.LENGTH_SHORT).show(); } private void finishJob(List<FloorModel> floors) { // UPDATE SPINNER FOR FLOORS setFloorSpinner(floors); spinnerBuildings.setEnabled(true); isFloorsJobRunning = false; } }, this, true, true); } } // Create a list for the name of each building private void setBuildingSpinner(List<BuildingModel> buildings) { setBuildingSpinner(buildings, null); } private void setBuildingSpinner(List<BuildingModel> buildings, List<BuildingModelDistance> distance) { if (!buildings.isEmpty()) { mAnyplaceCache.setSpinnerBuildings(buildings); List<String> list = new ArrayList<String>(); if (distance == null) { for (BuildingModel building : buildings) { list.add(building.name); } } else { for (int i = 0; i < buildings.size(); i++) { double value = distance.get(i).distance; if (value < 1000) { list.add(String.format("[%.0f m] %s", value, buildings.get(i).name)); } else { list.add(String.format("[%.1f km] %s", value / 1000, buildings.get(i).name)); } } } ArrayAdapter<String> spinnerBuildingsAdapter; spinnerBuildingsAdapter = new ArrayAdapter<String>(getBaseContext(), android.R.layout.simple_spinner_item, list); spinnerBuildingsAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); spinnerBuildings.setAdapter(spinnerBuildingsAdapter); } } // Create a list for the number of each floor private void setFloorSpinner(List<FloorModel> floors) { List<String> list = new ArrayList<String>(); for (FloorModel floor : floors) { list.add(floor.toString()); } ArrayAdapter<String> spinnerFloorsAdapter; spinnerFloorsAdapter = new ArrayAdapter<String>(getBaseContext(), android.R.layout.simple_spinner_item, list); spinnerFloorsAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); spinnerFloors.setAdapter(spinnerFloorsAdapter); } @Override public void onFloorError(Exception ex) { onNewFloor("0"); } private void loadFloor(String floorNumber) { BuildingModel build = mAnyplaceCache.getSelectedBuilding(); if (build != null) { selectedFloorIndex = build.checkFloorIndex(floorNumber); if (selectedFloorIndex == null) { selectedFloorIndex = 0; } try { spinnerFloors.setSelection(selectedFloorIndex); if (mode == Mode.INVISIBLE) startFloorPlanTask(); } catch (IndexOutOfBoundsException ex) { } } } @Override public void onNewFloor(String floor) { floorSelector.Stop(); isfloorSelectorJobFinished = true; if (isBuildingsLoadingFinished && floorSelectorDialog.isShowing()) { floorSelectorDialog.dismiss(); loadFloor(floor); } else { floorResult = floor; } } private void onFloorsLoaded(List<FloorModel> floors) { // if the user should not interact with the gui and // automatically // load the building if (mode != Mode.NONE) { // TODO - automatically choose the floor to be shown if // it's // only one if (floors.isEmpty()) { Toast.makeText(getBaseContext(), "No floors exist for the selected building!", Toast.LENGTH_SHORT) .show(); SelectBuildingActivity.this.finish(); } else if (floors.size() == 1) { if (mode == Mode.INVISIBLE) startFloorPlanTask(); } else { isBuildingsLoadingFinished = true; if (isfloorSelectorJobFinished) { loadFloor(floorResult); } else { if (!AnyplaceAPI.FLOOR_SELECTOR) { loadFloor("0"); } else { // Wait Floor Selector Answer floorSelectorDialog.show(); } } } } else { if (floors.isEmpty()) { Toast.makeText(getBaseContext(), "No floors exist for the selected building!", Toast.LENGTH_SHORT) .show(); } } } /* * TASKS */ private void startFloorPlanTask() { try { final BuildingModel b = mAnyplaceCache.getSelectedBuilding(); if (b == null) { Toast.makeText(getBaseContext(), "You haven't selected both building and floor...!", Toast.LENGTH_SHORT).show(); setResult(RESULT_CANCELED); finish(); return; } final FloorModel f = b.getFloors().get(selectedFloorIndex); final FetchFloorPlanTask fetchFloorPlanTask = new FetchFloorPlanTask(this, b.buid, f.floor_number); fetchFloorPlanTask.setCallbackInterface(new FetchFloorPlanTask.FetchFloorPlanTaskListener() { private ProgressDialog dialog; @Override public void onSuccess(String result, File floor_plan_file) { if (dialog != null) dialog.dismiss(); Intent returnIntent = new Intent(); returnIntent.putExtra("bmodel", mAnyplaceCache.getSelectedBuildingIndex()); returnIntent.putExtra("fmodel", selectedFloorIndex); returnIntent.putExtra("floor_plan_path", floor_plan_file.getAbsolutePath()); returnIntent.putExtra("message", result); setResult(RESULT_OK, returnIntent); finish(); } @Override public void onErrorOrCancel(String result) { if (dialog != null) dialog.dismiss(); Toast.makeText(getBaseContext(), result, Toast.LENGTH_SHORT).show(); Intent returnIntent = new Intent(); returnIntent.putExtra("message", result); setResult(RESULT_CANCELED); finish(); } @Override public void onPrepareLongExecute() { dialog = new ProgressDialog(SelectBuildingActivity.this); dialog.setIndeterminate(true); dialog.setTitle("Downloading floor plan"); dialog.setMessage("Please be patient..."); dialog.setCancelable(true); dialog.setCanceledOnTouchOutside(false); dialog.setOnCancelListener(new DialogInterface.OnCancelListener() { @Override public void onCancel(DialogInterface dialog) { fetchFloorPlanTask.cancel(true); } }); dialog.show(); } }); fetchFloorPlanTask.execute(); } catch (IndexOutOfBoundsException e) { Toast.makeText(getBaseContext(), "You haven't selected both building and floor...!", Toast.LENGTH_SHORT) .show(); setResult(RESULT_CANCELED); finish(); return; } } }