Добавляем запись на стену Вконтакте из Android-приложения

    Всем снова привет! Я вернулся.

    Уже несколько человек ждет пост о публикации записей на свою стену вконтакте. И вот эта статья. В ней будет много кода, теории вообще не будет, потому что все довольно просто и тем не менее данный пост может быть полезен тем, кто ломает голову о том как решить данную задачу.

    Поехали.


    Предистория


    Прежде всего искал доступные варианты. Их не оказалось, поэтому обратил внимание на готовые решения для твиттера и фэйсбук и на примере последнего написал несколько классов для Вконтакте.
    Идея данного примера взята из Facebook SDK, доступного по адресу: Facebook SDK

    Как это работает?


    Проект(звучит сильно конечно, ок… проектик) состоит из трех классов(ссылка на репу в конце статьи): VkSession — класс сохранения/получения параметров вконтакте(токен, user_id и т.д.), VkDialog — класс пользовательского диалога, VkApp — класс, объединяющий в себе все классы и выполняющий основную работу.

    На самом деле тут и описывать то нечего. У Вконтакте есть документация по API — Дока по API. Находим функцию wall.post — Тыц. Читаем и разбираемся.

    Важно понять принципы работы OAuth. В данном случае нужно получить идентификатор приложения(есть соответствующая форма) и далее работать с API, передавая данный ID. При работе с API Вконтакте будет посылать нам AccessToken, который нужно сохранять. Данный токен живет всего 24 часа, поэтому его необходимо проверять и сохранять время получения токена. Данные параметры я объединил в класс VkSession:

    package vkontakte;
    
    import android.content.Context;
    import android.content.SharedPreferences;
    
    public class VkSession {
        private SharedPreferences _prefs;
        private final String PREFS_NAME = "Vk:Settings";
        private Context _context;
        private SharedPreferences.Editor _editor;
        
        public VkSession(){}
        
        public VkSession(Context context){
        	_context = context;
        	_prefs = _context.getSharedPreferences(PREFS_NAME, 0);
            _editor = _prefs.edit();
        }
        
        public void saveAccessToken(String accessToken, String expires, String userId){
        	_editor.putString("VkAccessToken", accessToken);
        	_editor.putString("VkExpiresIn", expires);
        	_editor.putString("VkUserId", userId);
        	_editor.putLong("VkAccessTime", System.currentTimeMillis());
        	_editor.commit();
        }
        
        public String[] getAccessToken(){
        	String[] params = new String[4];
        	params[0] = _prefs.getString("VkAccessToken", "");
        	params[1] = _prefs.getString("VkExpiresIn", "");
        	params[2] = _prefs.getString("VkUserId", "");
        	params[3] =  String.valueOf(_prefs.getLong("VkAccessTime",0));
        	return params;
        }
        
        public void resetAccessToken(){
        	_editor.putString("VkAccessToken", "");
        	_editor.putString("VkExpiresIn", "");
        	_editor.putString("VkUserId", "");
        	_editor.putLong("VkAccessTime", 0);
        	_editor.commit();
        }   
    }
    


    Как видите, ничего сложного. Полученные настройки сохраняем в SharedPreferences. Ну и все.

    Далее, самое интересное. Класс VkApp, а точнее наиболее интересные его части с пояснениями:

    public class VkApp {
            //constants for OAUTH AUTHORIZE in Vkontakte
    	public static final String CALLBACK_URL = "http://api.vkontakte.ru/blank.html";
    	private static final String OAUTH_AUTHORIZE_URL = "http://api.vkontakte.ru/oauth/authorize?client_id=2020214&scope=8192&redirect_uri=http://api.vkontakte.ru/blank.html&display=touch&response_type=token"; 
    		 
    	private Context _context;
    	private VkDialogListener _listener;
    	private VkSession _vkSess;
    	
    	private String VK_API_URL = "https://api.vkontakte.ru/method/";
    	private String VK_POST_TO_WALL_URL = VK_API_URL + "wall.post?";
            ...
    

    Поясняю. Когда мы посылаем запрос, вконтакте отправляет ответ и ридеректит нас на адрес api.vkontakte.ru/blank.html, по суте это пустая страница(точную суть работы уже не припомню, писал код давно, так что имейте ввиду). Важно перехватывать данный адрес и реагировать на него нужным образом. Далее, идет собственно строка запроса со всеми нужными параметрами: наш идентификатор, scope(права доступа), тип дисплея, адрес ридеректа, вообщем все это есть в доке. Читайте!

    Продолжаем:
          //parse vkontakte JSON response
           private boolean parseResponse(String jsonStr){
    		boolean errorFlag = true;
    		
    		JSONObject jsonObj = null;
    		try {
    		   jsonObj = new JSONObject(jsonStr);
    		   JSONObject errorObj = null;
    		   
    		   if( jsonObj.has("error") ) {
    		       errorObj = jsonObj.getJSONObject("error");
    		       int errCode = errorObj.getInt("error_code");
    		       if( errCode == 14){
    		    	   errorFlag = false;
    		       }
    		   }
    		}
    		catch (JSONException e) {
    			e.printStackTrace();
    		}
    		
    		return errorFlag;	
    	}
    	
    	//publicate message to vk users' wall 
    	public boolean postToWall(String message) {
                    boolean errorFlag = true;
    		String[] params = _vkSess.getAccessToken();
    		
    		String accessToken = params[0];
    		String ownerId = params[2];
    		
    	        //set request uri params
    		VK_POST_TO_WALL_URL += "owner_id="+ownerId+"&message="+Uri.encode(message)+"&access_token="+accessToken;
    		
    		//send request to vkontakte api
    		HttpClient client = new DefaultHttpClient();
                    HttpGet request = new HttpGet(VK_POST_TO_WALL_URL);
            
                    try {
                         HttpResponse response = client.execute(request);
                         HttpEntity entity = response.getEntity();
    
                         String responseText = EntityUtils.toString(entity);
                
                         //parse response for error code or not
                         errorFlag = parseResponse(responseText);
                   }
                   catch(ClientProtocolException cexc){
            	   cexc.printStackTrace();
                   }
                   catch(IOException ioex){
            	    ioex.printStackTrace();
                   }
            
                   return errorFlag;
    	}
    

    В Андроид есть удобный набор классов от Apache для выполнения запросов, получения, разбор ответом и т.д. — HttpClient. В данном случае мы формируем GET-запрос и далее получаем ответ.

    Диалог состоит из WebView и для него переопределяется клиент:

    private class VkWebViewClient extends WebViewClient { 
        	@Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
            	if (url.startsWith(VkApp.CALLBACK_URL) & ( !url.contains("error") )) {
            		mListener.onComplete(url);
            		VkDialog.this.dismiss();
            		return true;
            	} 
            	else if(url.contains("error")){
            		VkDialog.this.dismiss();
            		return false;
            	}
            	else {
            	    view.loadUrl(url);
            	    return true;
            	}
            }
        	
        	@Override
            public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
                super.onReceivedError(view, errorCode, description, failingUrl);
                mListener.onError(description);
                VkDialog.this.dismiss();
            }
        	
        	@Override
            public void onPageStarted(WebView view, String url, Bitmap favicon) {
                super.onPageStarted(view, url, favicon);
                
                if( url.contains("error") ) {
                	VkDialog.this.dismiss();
                	return;
                }
                else if( url.contains("access_token")) {
                	VkDialog.this.dismiss();
                	mListener.onComplete(url);
                	return;
                }
                mSpinner.show();
            }
    
            @Override
            public void onPageFinished(WebView view, String url) {
                super.onPageFinished(view, url);      
                mSpinner.dismiss();
            }
        }
    

    В коде клиента мы ловим посылаемые адреса от вконтакте к приложению и реагируем нужным образом. Все также просто. Конечно данный код стоит доработать и не следует смело его копировать не разобравшись. Что-то поменяется на сервере и приложение будет реагировать неправильно. Кстати, в добавлении скажу, что Facebook SDK тоже сыпется, на телефонах HTC чаще всего.

    Надеюсь кому-то данная статейка будет полезной!

    Как обычно, интересные предложения, замечания и прочее пишем в личку.

    Код доступен здесь — репа
    Ads
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More

    Comments 8

      +2
      По делу — официальное приложение скорее всего не переписать все равно, а такую простую вещь учиться следовательно делать почти бестолку, если только как часть своего приложения.

      P.s. подчеркивания для членов класса — мне трудно воспринимать такой код.

        +1
        Ну, у официального приложения тоже есть свои минусы. Мне, например, хотелось бы видеть приложение, ориентированное на работу с микроблогом, аналогичное софту для твиттера. И потом, все эти вещи могут понадобиться людям, которые пишут для других систем (MeeGo, WP7, Symbian, etc)
          0
          Корпоративный стандарт. Мне тоже не нравится, но времени менять код нет. По крайней мере сейчас. Ну а люди ждали статью.
          0
          Меня всегда удивляли такие статьи, вы ведь очевидно делаете oauth-клиент для андройда (которых, кстати дофига), то казалось бы, зачем сюда приплетать контакт? Так бы статья была полезней для более широкого круга пользователей.
            +2
            Никаких клиентов я не делал и не собирался.
            Была задача добавить запись на стену из приложения. Потом несколько человек просили помочь с этой задачей — так и родилась статья. Вместо глупых комментов лучше бы статьи сами писали:)
              –1
              Я пишу, вы не беспокойтесь.
              Почему не использовали готовый oauth-компонент, тогда?
                0
                Сделал как показалось проще мне.
            0
            А как такое сделать на просто java, не android? WebView тут, кажется, нет.

            Only users with full accounts can post comments. Log in, please.