package com.nutiteq.advancedmap3;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.ListActivity;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MenuItem.OnMenuItemClickListener;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import com.nutiteq.datasources.PackageManagerTileDataSource;
import com.nutiteq.packagemanager.NutiteqPackageManager;
import com.nutiteq.packagemanager.PackageErrorType;
import com.nutiteq.packagemanager.PackageInfo;
import com.nutiteq.packagemanager.PackageManagerListener;
import com.nutiteq.packagemanager.PackageStatus;
import com.nutiteq.packagemanager.PackageAction;
import com.nutiteq.ui.MapView;
import com.nutiteq.wrappedcommons.PackageInfoVector;
import com.nutiteq.wrappedcommons.StringVector;
/**
* A sample demonstrating how to use offline package manager of the Nutiteq SDK.
*
* The sample downloads the latest package list from Nutiteq online service,
* displays this list and allows user to manage offline packages (download, update, delete them).
*
* Note that the sample does not include MapView, but using download packages
* is actually very similar to using other tile sources - SDK contains PackageManagerTileDataSource
* that will automatically display all imported or downloaded packages. PackageManagerTileDataSource
* needs PackageManager instance, so it is best to create a PackageManager instance at application level
* share this instance between activities.
*
*/
public class PackageManagerActivity extends ListActivity {
/**
* A full package info, containing package info and status.
*/
private static class Package {
final String packageName;
final String packageId;
final PackageInfo packageInfo;
final PackageStatus packageStatus;
Package(String packageName, PackageInfo packageInfo, PackageStatus packageStatus) {
this.packageName = packageName;
this.packageId = (packageInfo != null ? packageInfo.getPackageId() : null);
this.packageInfo = packageInfo;
this.packageStatus = packageStatus;
}
}
/**
* A holder class for packages containing views for each row in list view.
*/
private static class PackageHolder {
TextView nameView;
TextView statusView;
Button actionButton;
}
/**
* Adapter for displaying packages as a list.
*/
private class PackageAdapter extends ArrayAdapter<Package> {
Context context;
int layoutResourceId;
ArrayList<Package> packages;
public PackageAdapter(Context context, int layoutResourceId, ArrayList<Package> packages) {
super(context, layoutResourceId, packages);
this.context = context;
this.layoutResourceId = layoutResourceId;
this.packages = packages;
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
View row = convertView;
PackageHolder holder = null;
// Create new holder object or reuse existing
if (row == null) {
LayoutInflater inflater = ((Activity) context).getLayoutInflater();
row = inflater.inflate(layoutResourceId, parent, false);
holder = new PackageHolder();
holder.nameView = (TextView) row.findViewById(com.nutiteq.advancedmap3.R.id.package_name);
holder.statusView = (TextView) row.findViewById(com.nutiteq.advancedmap3.R.id.package_status);
holder.actionButton = (Button) row.findViewById(com.nutiteq.advancedmap3.R.id.package_action);
row.setTag(holder);
} else {
holder = (PackageHolder) row.getTag();
}
// Report package name and size
final Package pkg = packages.get(position);
holder.nameView.setText(pkg.packageName);
if (pkg.packageInfo != null) {
String status = "available";
if (pkg.packageInfo.getSize().longValue() < 1024 * 1024) {
status += " v." + pkg.packageInfo.getVersion()+" (<1MB)";
} else {
status += " v." + pkg.packageInfo.getVersion()+" (" + pkg.packageInfo.getSize().longValue() / 1024 / 1024 + "MB)";
}
// Check if the package is downloaded/is being downloaded (so that status is not null)
if (pkg.packageStatus != null) {
if (pkg.packageStatus.getCurrentAction() == PackageAction.PACKAGE_ACTION_READY) {
status = "ready";
holder.actionButton.setText("RM");
holder.actionButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
packageManager.startPackageRemove(pkg.packageInfo.getPackageId());
}
});
} else if (pkg.packageStatus.getCurrentAction() == PackageAction.PACKAGE_ACTION_WAITING) {
status = "queued";
holder.actionButton.setText("C");
holder.actionButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
packageManager.cancelPackageTasks(pkg.packageInfo.getPackageId());
}
});
} else {
if (pkg.packageStatus.getCurrentAction() == PackageAction.PACKAGE_ACTION_COPYING) {
status = "copying";
} else if (pkg.packageStatus.getCurrentAction() == PackageAction.PACKAGE_ACTION_DOWNLOADING) {
status = "downloading";
} else if (pkg.packageStatus.getCurrentAction() == PackageAction.PACKAGE_ACTION_REMOVING) {
status = "removing";
}
status += " " + Integer.toString((int) pkg.packageStatus.getProgress()) + "%";
if (pkg.packageStatus.isPaused()) {
status = status + " (paused)";
holder.actionButton.setText("R");
holder.actionButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
packageManager.setPackagePriority(pkg.packageInfo.getPackageId(), 0);
}
});
} else {
holder.actionButton.setText("P");
holder.actionButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
packageManager.setPackagePriority(pkg.packageInfo.getPackageId(), -1);
}
});
}
}
} else {
holder.actionButton.setText("DL");
holder.actionButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
packageManager.startPackageDownload(pkg.packageInfo.getPackageId());
}
});
}
holder.statusView.setText(status);
} else {
holder.actionButton.setText(">");
holder.actionButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
currentFolder = currentFolder + pkg.packageName + "/";
updatePackages();
}
});
holder.statusView.setText("");
}
return row;
}
}
/**
* Listener for package manager events.
*/
class PackageListener extends PackageManagerListener {
@Override
public void onPackageListUpdated() {
updatePackages();
}
@Override
public void onPackageListFailed() {
updatePackages();
displayToast("Failed to download package list");
}
@Override
public void onPackageStatusChanged(String id, int version, PackageStatus status) {
updatePackage(id);
}
@Override
public void onPackageCancelled(String id, int version) {
updatePackage(id);
}
@Override
public void onPackageUpdated(String id, int version) {
updatePackage(id);
}
@Override
public void onPackageFailed(String id, int version, PackageErrorType errorType) {
updatePackage(id);
displayToast("Failed to download package " + id + "/" + version + ": " + errorType);
}
}
private NutiteqPackageManager packageManager;
private ArrayAdapter<Package> packageAdapter;
private ArrayList<Package> packageArray = new ArrayList<Package>();
private String currentFolder = ""; // Current 'folder' of the package, for example "Asia/"
private String language = "en"; // Language for the package names. Most major languages are supported
public static PackageManagerTileDataSource dataSource;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Register license
MapView.registerLicense("XTUN3Q0ZBd2NtcmFxbUJtT1h4QnlIZ2F2ZXR0Mi9TY2JBaFJoZDNtTjUvSjJLay9aNUdSVjdnMnJwVXduQnc9PQoKcHJvZHVjdHM9c2RrLWlvcy0zLiosc2RrLWFuZHJvaWQtMy4qCnBhY2thZ2VOYW1lPWNvbS5udXRpdGVxLioKYnVuZGxlSWRlbnRpZmllcj1jb20ubnV0aXRlcS4qCndhdGVybWFyaz1ldmFsdWF0aW9uCnVzZXJLZXk9MTVjZDkxMzEwNzJkNmRmNjhiOGE1NGZlZGE1YjA0OTYK", getApplicationContext());
// Create package manager
File packageFolder = new File(getApplicationContext().getExternalFilesDir(null), "mappackages");
if (!(packageFolder.mkdirs() || packageFolder.isDirectory())) {
Log.e(Const.LOG_TAG, "Could not create package folder!");
}
packageManager = new NutiteqPackageManager("nutiteq.osm", packageFolder.getAbsolutePath());
packageManager.setPackageManagerListener(new PackageListener());
packageManager.startPackageListDownload();
// Initialize list view
setContentView(R.layout.list);
packageAdapter = new PackageAdapter(this, com.nutiteq.advancedmap3.R.layout.package_item_row, packageArray);
getListView().setAdapter(packageAdapter);
}
@Override
public void onStart() {
super.onStart();
packageManager.start();
}
@Override
public void onStop() {
packageManager.stop(false);
super.onStop();
}
@Override
public void onDestroy() {
packageManager.setPackageManagerListener(null);
super.onDestroy();
}
@Override
public void onBackPressed() {
if (currentFolder.length() == 0) {
super.onBackPressed();
} else {
currentFolder = currentFolder.substring(0, currentFolder.lastIndexOf('/', currentFolder.length() - 2) + 1);
updatePackages();
}
}
private ArrayList<Package> getPackages() {
HashMap<String, Package> pkgs = new HashMap<String, Package>();
PackageInfoVector packageInfoVector = packageManager.getServerPackages();
for (int i = 0; i < packageInfoVector.size(); i++) {
PackageInfo packageInfo = packageInfoVector.get(i);
// Get the list of names for this package. Each package may have multiple names,
// packages are grouped using '/' as a separator, so the the full name for Sweden
// is "Europe/Northern Europe/Sweden". We display packages as a tree, so we need
// to extract only relevant packages belonging to the current folder.
StringVector packageNames = packageInfo.getNames(language);
for (int j = 0; j < packageNames.size(); j++) {
String packageName = packageNames.get(j);
if (!packageName.startsWith(currentFolder)) {
continue; // belongs to a different folder, so ignore
}
packageName = packageName.substring(currentFolder.length());
int index = packageName.indexOf('/');
Package pkg;
if (index == -1) {
// This is actual package
PackageStatus packageStatus = packageManager.getLocalPackageStatus(packageInfo.getPackageId(), -1);
pkg = new Package(packageName, packageInfo, packageStatus);
} else {
// This is package group
packageName = packageName.substring(0, index);
if (pkgs.containsKey(packageName)) {
continue;
}
pkg = new Package(packageName, null, null);
}
pkgs.put(packageName, pkg);
}
}
return new ArrayList<Package>(pkgs.values());
}
private void updatePackages() {
if (packageAdapter == null) {
return;
}
runOnUiThread(new Runnable() {
@Override
public void run() {
// Simply reload all packages from the scratch
packageArray.clear();
packageArray.addAll(getPackages());
packageAdapter.notifyDataSetChanged();
}
});
}
private void updatePackage(final String packageId) {
if (packageAdapter == null) {
return;
}
runOnUiThread(new Runnable() {
@Override
public void run() {
// Try to find the package that needs to be updated
for (int i = 0; i < packageArray.size(); i++) {
Package pkg = packageArray.get(i);
if (packageId.equals(pkg.packageId)) {
PackageStatus packageStatus = packageManager.getLocalPackageStatus(packageId, -1);
pkg = new Package(pkg.packageName, pkg.packageInfo, packageStatus);
packageArray.set(i, pkg);
// TODO: it would be much better to only refresh the changed row
packageAdapter.notifyDataSetChanged();
}
}
}
});
}
private void displayToast(final String msg) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplication(), msg, Toast.LENGTH_SHORT).show();
}
});
}
@SuppressLint({ "InlinedApi", "NewApi" })
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuItem menuItem = menu.add("Map").setOnMenuItemClickListener(new OnMenuItemClickListener(){
@Override
public boolean onMenuItemClick (MenuItem item){
// Using static global variable to pass data. Avoid this in your app (memory leaks etc)!
PackageManagerActivity.dataSource = new PackageManagerTileDataSource(PackageManagerActivity.this.packageManager);
Intent myIntent = new Intent(PackageManagerActivity.this,
PackagedMapActivity.class);
PackageManagerActivity.this.startActivity(myIntent);
return true;
}
});
menuItem.setIcon(android.R.drawable.ic_dialog_map);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
}
return super.onCreateOptionsMenu(menu);
}
}