Показаны сообщения с ярлыком Google. Показать все сообщения
Показаны сообщения с ярлыком Google. Показать все сообщения

понедельник, 10 декабря 2012 г.

Android. Лицензирование собственного приложения

Итак, в Google Play у вас есть крутое приложение, которое купит у вас целая куча народа. И вы хотите встроить проверку лицензии на него. Кстати, если вы разместили в маркете бесплатное приложение, платным его сделать уже не получится.

1. Настраиваем консоль разработчика


Наше лицензируемое приложение уже должно лежать в Google Developer Console. С новой политикой Google каждое приложение имеет свой уникальный ключ. Щелкаем по приложению, выбираем в левом меню "Службы и API" и копируем RSA ключ:


Необходимо подготовить аккаунт к тестированию, для этого заходим через левое меню в Настройки - Сведения об аккаунте, добавляем свой e-mail в Аккаунты GMail разработчиков (соответственно этот аккаунт должен у нас быть забит в настройках Вашего устройства). Выбирая в ComboBox "Информация о лицензии" требуемый ответ, можно протестировать реакцию приложения. А вот и картинка этого окна:



2. Настраиваем Eclipse


В Eclipse заходим в Window - Android SDK Manager, секция Extras и скачиваем Google Play Licensing Library (на картинке - в красной рамке):

Теперь подключаем библиотеку к нашему проекту. Для этого жмем правой кнопкой в Package Explorer, далее Import, далее Android - Existing Android Code Into Workspace, находим нашу библиотеку в директории <путь до android sdk>/google-market_licensing. Импортируем.

3. Создаем проект Eclipse


В нашем проекте заходим в свойства проекта, секция Android, нижняя вкладка Library. Подключаем библиотеку к проекту:





Вроде всё готово.

В нашем приложении в Android Manifest.xml в корень manifest добавляем всего одну строчку:
    



Заодно исправим главный layout в res/layout/. Вот его полный код:

В главной активити приложения добавляем импорты:

import com.google.android.vending.licensing.AESObfuscator;
import com.google.android.vending.licensing.LicenseChecker;
import com.google.android.vending.licensing.LicenseCheckerCallback;
import com.google.android.vending.licensing.ServerManagedPolicy;

И следующие члены класса:

    // Поставьте сюда свой RSA ключ из Developer Console
    private static final String BASE64_PUBLIC_KEY = "MIIBIjANBgkqhkiG9w0BAQEFveIcGxnHJQfSUs6S5eoWwYr87LfNBupk9G0rN1QZ3YiVYzp/LeJ7fNkDjC1f8RHMeJF3ZA9VjLH5d4TUmQ3M4e6/vVrNFga+BXEbAmhsv6aQ1fNzt5tBWFYUdGlhHTcfsTiFPDh17ejlfm7XlhxWuYNLuxJtzXpwdiqTqiTZed0mFut1Z1khL+34SXL4qDzegbkSdxrka/zyLnuS5dSyacszmyST7x+/NjWgg/9zlu+FRETXl+XYO2STb6RuVVgLaQIDAQAB";

    // Забейте сюда какие-нибудь числа. Свои, иначе ничего не заработает!
    private static final byte[] SALT = new byte[] { -46, 65, 30, -19, -103, -5, 74, -64, 51, 88, -95, -45, 77, -117, -36, -113, -11, 32, -64, 89 };   
        
        // компоненты на лэйауте
    private TextView mStatusText;
    private Button mCheckLicenseButton;
        
        // чекер и коллбэк для проверки лицензии
    private LicenseCheckerCallback mLicenseCheckerCallback;
    private LicenseChecker mChecker; 
 
    // Хэндлер для процесса UI.
    private Handler mHandler;
 
Метод onCreate с комментариями:

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
        setContentView(R.layout.main); 
        // получаем компоненты layouta, по кнопке запускаем повторную проверку лицензии 
        mStatusText = (TextView) findViewById(R.id.status_text);
        mCheckLicenseButton = (Button) findViewById(R.id.check_license_button);
        mCheckLicenseButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
                doCheck();
            }
        });

        mHandler = new Handler();

        // Попробуйте использовать здесь другие данные для ID устройства. ANDROID_ID будет взламываться в первую очередь.
        String deviceId = Secure.getString(getContentResolver(), Secure.ANDROID_ID);

        // Коллбэк функция по окончании проверки лицензии
        mLicenseCheckerCallback = new MyLicenseCheckerCallback();
        // конструктор LicenseChecker с правами.
        mChecker = new LicenseChecker(
            this, new ServerManagedPolicy(this,
                new AESObfuscator(SALT, getPackageName(), deviceId)),
            BASE64_PUBLIC_KEY); 
  
        // запускаем проверку  
        doCheck();
    }
  
    // в методе doCheck() готовим лэйаут к отсылке запроса  
    private void doCheck() {
        mCheckLicenseButton.setEnabled(false);
        setProgressBarIndeterminateVisibility(true);
        mStatusText.setText(R.string.checking_license);
        mChecker.checkAccess(mLicenseCheckerCallback);
    } 

Описываем класс MyLicenseChecker, который служит для обработки ответов от сервера:

 
    private class MyLicenseCheckerCallback implements LicenseCheckerCallback {

     @Override
  public void allow(int reason) {
            if (isFinishing()) {
                // Don't update UI if Activity is finishing.
                return;
            }
            // Should allow user access.
            displayResult("Allow");
        }

  @Override
  public void dontAllow(int reason) {
            if (isFinishing()) {
                // Don't update UI if Activity is finishing.
                return;
            }
            displayResult("DONT ALLOW");
            // Should not allow access. In most cases, the app should assume
            // the user has access unless it encounters this. If it does,
            // the app should inform the user of their unlicensed ways
            // and then either shut down the app or limit the user to a
            // restricted set of features.
            // In this example, we show a dialog that takes the user to Market.
            showDialog(0);
        }
  
  @Override
        public void applicationError(int errorCode) {
            if (isFinishing()) {
                // Don't update UI if Activity is finishing.
                return;
            }
            // This is a polite way of saying the developer made a mistake
            // while setting up or calling the license checker library.
            // Please examine the error code and fix the error.
            String result = String.format("ERROR", errorCode);
            displayResult(result);
        }

    } 

А также несколько функций чтобы просто показать диалог:

    protected Dialog onCreateDialog(int id) {
        // We have only one dialog.
        return new AlertDialog.Builder(this)
            .setTitle("UNLICENSED")
            .setMessage("UNLICENSED DIALOG BODY")
            .setPositiveButton("BUY BUTTON", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                    Intent marketIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(
                        "http://market.android.com/details?id=" + getPackageName()));
                    startActivity(marketIntent);
                }
            })
            .setNegativeButton("QUIT BUTTON", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                    finish();
                }
            })
            .create();
    }    
    
    private void displayResult(final String result) {
        mHandler.post(new Runnable() {
            public void run() {
                mStatusText.setText(result);
                setProgressBarIndeterminateVisibility(false);
                mCheckLicenseButton.setEnabled(true);
            }
        });
    }  


Собственно и всё. Запуская приложение и изменяя требуемый ответ через Developer Console(Информация о лицензии), получаем требуемые данные
P.S.: Если в консоли видим, что типа Using cached license response, меняйте SALT.

Полный код активити:

package com.osmsoft.licensingexample;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.provider.Settings.Secure;
import android.view.View;
import android.view.Window;
import android.widget.Button;
import android.widget.TextView;

import com.android.vending.licensing.AESObfuscator;
import com.android.vending.licensing.LicenseChecker;
import com.android.vending.licensing.LicenseCheckerCallback;
import com.android.vending.licensing.ServerManagedPolicy;

public class MainActivity extends Activity {
    private static final String BASE64_PUBLIC_KEY = "REPLACE THIS WITH YOUR PUBLIC KEY";

    // Generate your own 20 random bytes, and put them here.
    private static final byte[] SALT = new byte[] {
        -46, 65, 30, -128, -103, -57, 74, -64, 51, 88, -95, -45, 77, -117, -36, -113, -11, 32, -64,
        89
    };

    private TextView mStatusText;
    private Button mCheckLicenseButton;

    private LicenseCheckerCallback mLicenseCheckerCallback;
    private LicenseChecker mChecker;
    // A handler on the UI thread.
    private Handler mHandler;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
        setContentView(R.layout.main);

        mStatusText = (TextView) findViewById(R.id.status_text);
        mCheckLicenseButton = (Button) findViewById(R.id.check_license_button);
        mCheckLicenseButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
                doCheck();
            }
        });

        mHandler = new Handler();

        // Try to use more data here. ANDROID_ID is a single point of attack.
        String deviceId = Secure.getString(getContentResolver(), Secure.ANDROID_ID);

        // Library calls this when it's done.
        mLicenseCheckerCallback = new MyLicenseCheckerCallback();
        // Construct the LicenseChecker with a policy.
        mChecker = new LicenseChecker(
            this, new ServerManagedPolicy(this,
                new AESObfuscator(SALT, getPackageName(), deviceId)),
            BASE64_PUBLIC_KEY);
        doCheck();
    }

    protected Dialog onCreateDialog(int id) {
        // We have only one dialog.
        return new AlertDialog.Builder(this)
            .setTitle("UNLICENSED")
            .setMessage("UNLICENSED DIALOG BODY")
            .setPositiveButton("BUY BUTTON", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                    Intent marketIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(
                        "http://market.android.com/details?id=" + getPackageName()));
                    startActivity(marketIntent);
                }
            })
            .setNegativeButton("QUIT BUTTON", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                    finish();
                }
            })
            .create();
    }   

    private void doCheck() {
        mCheckLicenseButton.setEnabled(false);
        setProgressBarIndeterminateVisibility(true);
        mStatusText.setText(R.string.checking_license);
        mChecker.checkAccess(mLicenseCheckerCallback);
    }

    private void displayResult(final String result) {
        mHandler.post(new Runnable() {
            public void run() {
                mStatusText.setText(result);
                setProgressBarIndeterminateVisibility(false);
                mCheckLicenseButton.setEnabled(true);
            }
        });
    }

    private class MyLicenseCheckerCallback implements LicenseCheckerCallback {

     @Override
  public void allow(int reason) {
            if (isFinishing()) {
                // Don't update UI if Activity is finishing.
                return;
            }
            // Should allow user access.
            displayResult("Allow");
        }

  @Override
  public void dontAllow(int reason) {
            if (isFinishing()) {
                // Don't update UI if Activity is finishing.
                return;
            }
            displayResult("DONT ALLOW");
            // Should not allow access. In most cases, the app should assume
            // the user has access unless it encounters this. If it does,
            // the app should inform the user of their unlicensed ways
            // and then either shut down the app or limit the user to a
            // restricted set of features.
            // In this example, we show a dialog that takes the user to Market.
            showDialog(0);
        }
  
  @Override
        public void applicationError(int errorCode) {
            if (isFinishing()) {
                // Don't update UI if Activity is finishing.
                return;
            }
            // This is a polite way of saying the developer made a mistake
            // while setting up or calling the license checker library.
            // Please examine the error code and fix the error.
            String result = String.format("ERROR", errorCode);
            displayResult(result);
        }

    }  

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mChecker.onDestroy();
    }

}

среда, 5 декабря 2012 г.

Android Google Maps Api v2. Урок 4. Строим маршрут на карте

Итак, продолжаем нашу серию спонтанных уроков, вызванных необходимостью переводить своё приложение на новый API
Сегодня мы разберемся, как получить маршрут и отобразить его на карте. Для этого обратимся к API Google Directions.

Получать маршрут мы будем грамотно, используя AsyncTask. Мы должны обратиться к Google Service Directions, передать координаты начальной и конечной точек и распарсить JSON ответ. Например вот такой: http://maps.google.com/maps/api/directions/json?origin=55.772851,37.586806&destination=55.415003,37.899904&sensor=false

При этом мы в ответе мы получаем шифрованную полилайн для каждого шага, я написал метод decodePolilyne. Можно конечно ограничиться начальными и конечными координатами шага, но полученный результат вас не устроит, проверено на себе.

Итак, пишем интерфейс для AsyncTask, сохраняем его в файл OnRouteCalcCompleted.java:

package com.example.mapexample;

import java.util.ArrayList;

import com.google.android.gms.maps.model.LatLng;

public interface OnRouteCalcCompleted{
    void onRouteCalcBegin();
    void onRouteCompleted( ArrayList route );
}


И саму AsyncTask RouteHandler.java:


package com.example.mapexample;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import com.google.android.gms.maps.model.LatLng;

import android.os.AsyncTask;
import android.util.Log;


/**
 * Класс возвращает полилинию дороги, если указан mapView, отрисовывается на нём
 * @author awacs
 *
 */
public class RouteHandler extends AsyncTask {
   
    private final static String TAG = "RouteHandler";
    

    /**
     * Для точного интерполирования марштура
     */
    public static final int FINE_ROUTE = 1;
   
    /**
     * Для грубого интерполирования маршрута
     */
    public static final int COARSE_ROUTE = 2;    

 
    private final HttpClient client = new DefaultHttpClient();
    private String content;
    private boolean error = false;
    private String error_msg = "";
   
    private ArrayList polyline;
   
    private int accuracyRoute = 1;
   
    private long distance;
   
    private OnRouteCalcCompleted listener;
   
    public RouteHandler( OnRouteCalcCompleted l ){
        this.listener = l;
    }
   
    public double getDistance(){
        Log.d(TAG, "distance = " + distance + "m");
        return distance/1000;
    }
   
    public void calculateRoute( double latStart, double lonStart, double latEnd, double lonEnd, int accuracy ){
       
        this.accuracyRoute = accuracy;
       
        StringBuilder origin = new StringBuilder();
        origin.append( Double.toString(latStart));
        origin.append(",");
        origin.append( Double.toString(lonStart));       

        StringBuilder destination = new StringBuilder();
        destination.append( Double.toString(latEnd));
        destination.append(",");
        destination.append( Double.toString(lonEnd));       
       
        List nameValuePairs = new ArrayList();
        nameValuePairs.add(new BasicNameValuePair("origin", origin.toString()));
        nameValuePairs.add(new BasicNameValuePair("destination", destination.toString() ));
        nameValuePairs.add(new BasicNameValuePair("sensor", "false"));
        String paramString = URLEncodedUtils.format(nameValuePairs, "utf-8");
        execute( "http://maps.google.com/maps/api/directions/json" + "?" + paramString );

 
    }
   

    public boolean isError(){
        return error;
    }
   
    public String getErrorMsg(){
        return error_msg;
    }
   
    @Override
    protected String doInBackground(String... urls) {
       
        Log.d(TAG, "RouteHandler::doInBackground");

        try {
            Log.v(TAG, urls[0]);
            HttpPost httppost = new HttpPost(urls[0]);
            ResponseHandler responseHandler = new BasicResponseHandler();
            content = client.execute( httppost, responseHandler );
        } catch (ClientProtocolException e) {
            Log.d(TAG, "GetRouteHandler::ClientProtocolException");
            e.printStackTrace();
            error = true;
            cancel(true);
         } catch (IOException e) {
            Log.d(TAG, "GetRouteHandler::IOException");
            e.printStackTrace();
            error = true;
            cancel(true);
         }
        return content;
    }
   
    protected void onPostExecute(String content) {
        if (error) {
            error_msg = "Offline";
        } else {
            try {
                JSONObject response = new JSONObject(content);
                String status = response.getString("status");
                Log.v(TAG, content);
                if( status.equalsIgnoreCase("OK") ){
                    polyline = new ArrayList();
               
                    JSONArray routesArray = response.getJSONArray("routes");
                    JSONObject route = routesArray.getJSONObject(0);
                    // массив с информацией об отрезке маршрута
                    JSONArray legs = route.getJSONArray("legs");
                    JSONObject leg = legs.getJSONObject(0);   
                   
                    JSONObject distanceObj = leg.getJSONObject("distance");
                    distance = distanceObj.getLong("value");
                   
                    JSONObject durationObj = leg.getJSONObject("duration");
                   
                    // содержит куб выделения информационного окна для маршрута.
                    JSONObject bounds = route.getJSONObject("bounds");
                    JSONObject bounds_southwest = bounds.getJSONObject("southwest");
                    JSONObject bounds_northeast = bounds.getJSONObject("northeast");
                   
                    double maxLat = bounds_northeast.getDouble("lat");
                    double maxLon = bounds_northeast.getDouble("lng");
                    double minLat = bounds_southwest.getDouble("lat");
                    double minLon = bounds_southwest.getDouble("lng");
           

                    JSONArray steps = leg.getJSONArray("steps");
                    for( int i=0; i
                        JSONObject step = steps.getJSONObject(i);
                        JSONObject start_location = step.getJSONObject("start_location");
                        JSONObject end_location = step.getJSONObject("end_location");

                        double latitudeStart = start_location.getDouble("lat");
                        double longitudeStart = start_location.getDouble("lng");
                        double latitudeEnd = end_location.getDouble("lat");
                        double longitudeEnd = end_location.getDouble("lng");
                        LatLng startGeoPoint = new LatLng(latitudeStart,longitudeStart);
                        LatLng endGeoPoint = new LatLng(latitudeEnd,longitudeEnd);
                        JSONObject polylineObject = step.getJSONObject("polyline");
                        if( accuracyRoute == FINE_ROUTE ){
                            List points = decodePoly(polylineObject.getString("points"));
                            Log.d(TAG, " " + points.size());
                            polyline.addAll(points);
                        } else {
                            polyline.add(startGeoPoint);
                            polyline.add(endGeoPoint);
                        }
                    }

                } else if( status.equalsIgnoreCase("NOT_FOUND")){
                    // по крайней мере для одной заданной точки (исходной точки, пункта назначения или путевой точки) геокодирование невозможно.
                } else if( status.equalsIgnoreCase("ZERO_RESULTS")){
                    // между исходной точкой и пунктом назначения не найдено ни одного маршрута.
                } else if( status.equalsIgnoreCase("MAX_WAYPOINTS_EXCEEDED")){
                    // в запросе задано слишком много waypoints. Максимальное количество waypoints равно 8 плюс исходная точка и пункт назначения. ( (Пользователи Google Maps Premier могут выполнять запросы с количеством путевых точек до 23.)
                } else if( status.equalsIgnoreCase("INVALID_REQUEST")){
                    // запрос недопустим
                }else if( status.equalsIgnoreCase("OVER_QUERY_LIMIT")){
                    // служба получила слишком много запросов от вашего приложения в разрешенный период времени.
                }else if( status.equalsIgnoreCase("REQUEST_DENIED")){
                    // служба Directions отклонила запрос вашего приложения.
                }else if( status.equalsIgnoreCase("UNKNOWN_ERROR")){
                    // обработка запроса маршрута невозможна из-за ошибки сервера. При повторной попытке запрос может быть успешно выполнен
                }
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
       
        listener.onRouteCompleted( polyline );
       
    } // end postExecute
   
    /**
     * Декодирует полилинию из переданной гуглом строки
     * @param encoded
     * @return
     */
    private List decodePoly(String encoded) {

        List poly = new ArrayList();
        int index = 0, len = encoded.length();
        int lat = 0, lng = 0;

        while (index < len) {
            int b, shift = 0, result = 0;
            do {
                b = encoded.charAt(index++) - 63;
                result |= (b & 0x1f) << shift;
                shift += 5;
            } while (b >= 0x20);
            int dlat = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
            lat += dlat;

            shift = 0;
            result = 0;
            do {
                b = encoded.charAt(index++) - 63;
                result |= (b & 0x1f) << shift;
                shift += 5;
            } while (b >= 0x20);
            int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
            lng += dlng;

            LatLng p = new LatLng( lat/1E5, lng/1E5);
            poly.add(p);
        }

        return poly;
    } // end decodePoly      
   
} // end class


Для использования этого таска в нашей активити, нужно имплементить OnRouteCalcCompleted, соответвенно в Activity добавятся методы:
    @Override
    public void onRouteCalcBegin() {
        // Тут можно добавить например вызов прогрессбара, "Ждите, строим маршрут...."
    }

а отобразить готовую полилинию можно в добавленном onRouteCompleted:


    @Override
    public void onRouteCompleted(ArrayList route) {
        mMap.addPolyline((new PolylineOptions().color(Color.BLUE).width(5)).addAll(route));
        // а заодно и удалить прогрессбар :)
    }


Да, теперь добавляем например в инициализацию mMap из урока 3 код для вызова AsyncTask и компилируем готовое приложение.

        private RouteHandler routeHandler;

    private void setUpMapIfNeeded() {
        if (mMap == null) {
            mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.mapView)).getMap();
        }
        routeHandler = new RouteHandler( this );
        routeHandler.calculateRoute(55.772935, 37.594272, 55.88459, 37.4263165, RouteHandler.FINE_ROUTE);       
    }


Код полной активити:

package com.example.mapexample;

import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.GoogleMap.OnMapLongClickListener;
import com.google.android.gms.maps.LocationSource;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.PolylineOptions;

import java.sql.Time;
import java.util.ArrayList;

import android.graphics.Color;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.util.Log;


public class MainActivity extends FragmentActivity implements LocationSource, LocationListener, OnRouteCalcCompleted{
   
    private final static String TAG = "MainActivity";

    private OnLocationChangedListener mListener;
    private LocationManager lManager;
   
    private GoogleMap mMap;
   
    private static double mLatitude;
    private static double mLongitude;   
   
    private RouteHandler routeHandler;
   
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.mapView)).getMap();
        //mMap.setTrafficEnabled(true);
        mMap.setMyLocationEnabled(true);
        mMap.setOnMapLongClickListener(this);
        lManager = (LocationManager) getSystemService(LOCATION_SERVICE);
        lManager.requestLocationUpdates( lManager.getBestProvider(new Criteria(), true), 1, 1000, this);       
        setUpMapIfNeeded();
    }
   
    @Override
    protected void onResume() {
        super.onResume();
        setUpMapIfNeeded();
        if( lManager != null ){
            lManager.requestLocationUpdates(lManager.getBestProvider(new Criteria(), true), 1, 1000, this);
        }       
    }
   
    private void setUpMapIfNeeded() {
        if (mMap == null) {
            mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.mapView)).getMap();
        }
        routeHandler = new RouteHandler( this );
        routeHandler.calculateRoute(55.772935, 37.594272, 55.88459, 37.4263165, AppSettings.FINE_ROUTE);       
    }


     protected void onPause() {
         if( lManager != null ){
             lManager.removeUpdates(this);
         }
         super.onPause();
     }   
   



    @Override
    public void onLocationChanged(Location location) {
        if( mListener != null ){
            mListener.onLocationChanged( location );
        }
    }

    @Override
    public void activate(OnLocationChangedListener listener) {
        mListener = listener;
       
    }

    @Override
    public void deactivate() {
        mListener = null;
    }

    @Override
    public void onProviderDisabled(String provider) {
        // TODO Auto-generated method stub
       
    }

    @Override
    public void onProviderEnabled(String provider) {
        // TODO Auto-generated method stub
       
    }

    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {
        // TODO Auto-generated method stub
       
    }

    @Override
    public void onRouteCalcBegin() {
        // TODO Auto-generated method stub
       
    }

    @Override
    public void onRouteCompleted(ArrayList route) {
        mMap.addPolyline((new PolylineOptions().color(Color.BLUE).width(5)).addAll(route));
    }

}



вторник, 4 декабря 2012 г.

Android Google Maps API v2. Урок 2. Отображение карты

Итак, в первом уроке мы получили ключ разработчика и сейчас хотим его использовать по назначению.

1. Скачиваем необходимые библиотеки

Первым делом необходимо скачать и установить Google Play SDK и Android Support Library, сделать это можно из Eclipse. Для этого заходим в меню Window - Android SDK Manager, внизу находим Extras - Google Play Services и Extras - Android Support Library, жмем чекбокс и кнопку Install Packages.

Пакеты устанавливаются...

Устанавливаются...

Установились.

2. Добавляем библиотеки в Eclipse.

Необходимо в Eclipse добавить эти экстрасы. Правой кнопкой в Package Explorer. Import. Android - Existing Android Code Into Workspace. Выбираем директорию, в которой был установлен Android SDK, далее в ней папку Extras, и далее:
а) android - support - v4. Finish
б) google - google_play_services - libproject - google-play-services_lib. Finish

3. Создаем новый проект в Eclipse

Как создать проект я думаю все знают. А кто не знает - найдет кучу инструкций по Android Hello World. После создания проекта подключаем к нему библиотеки. Правой кнопкой по проекту в Package Explorer. Properties. Android. Видим вкладку Library и кнопку Add. Добавляем скаченные и импортнутые библиотеки.
Заодно проверяем версию проекта, она должна быть не ниже 2.2!


4. Правим Android Manifest

Добавляем необходимые разрешения. Здесь они немного избыточные, но это нам пригодится в будущем:
    
    

    
    

   
    
    
    
    
    

    

    

    

    

    

    

    
    
 
Здесь нужно подставить название своего пакета


В секции (!!!) добавляем свой API ключ:
        



И собственно всё

5. Правим Activity
Вместо Activity в секцию extends пишем FragmentActivity

6. Правим Layout
В layout нашей активити пишем:


Собираем приложение и проверяем. Желательно на устройстве, нормальной работы интернета на эмуляторе лично я не получил.