Просим доступы
Добавляем в AndroidManifest.xml запрос доступов на получение списка сохранённых аккаунтов, получение полномочий и доступ к интернет. Соответственно:
android.permission.GET_ACCOUNTS
android.permission.USE_CREDENTIALS
android.permission.INTERNET
Собственно делаем это
Так выглядит код главного Activity нашего приложения:
import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; import android.preference.PreferenceManager; import android.widget.LinearLayout; import android.widget.ScrollView; import android.widget.TextView; import android.widget.Toast; public class AuthDocActivity extends Activity { private static final String PREF_TOKEN = "token"; private GAuthHelper gah; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); gah = new GAuthHelper(this); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); String authToken = prefs.getString(PREF_TOKEN, ""); if (authToken.length()==0) { // token not found, need authorization final String[] accn = gah.getAccNames(); if (accn.length==0) { Toast.makeText(this, "Stored Google accounts not found", Toast.LENGTH_LONG).show(); } else { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle("Select a Google account"); builder.setItems(accn, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, final int which) { saveToken(accn[which]); } }).create().show(); } } else { // use stored token loadGDocs(authToken); } } private void saveToken(String accname) { gah.getAuthToken(accname, new GAuthHelper.OAuthCallbackListener() { @Override public void callback(String authToken) { if (authToken==null) { Toast.makeText(AuthDocActivity.this, "Operation cancelled", Toast.LENGTH_LONG).show(); } else { PreferenceManager.getDefaultSharedPreferences(AuthDocActivity.this).edit().putString(PREF_TOKEN, authToken).commit(); loadGDocs(authToken); } } }); } private void loadGDocs(final String token) { new DocsLoader(this){ protected void onPostExecute(String[] result) { ScrollView sw = new ScrollView(AuthDocActivity.this); LinearLayout ll = new LinearLayout(AuthDocActivity.this); ll.setOrientation(LinearLayout.VERTICAL); if (result!=null) { for (String s : result) { TextView tw = new TextView(AuthDocActivity.this); tw.setText(s); ll.addView(tw); } } else { // token expired: reload gah.invalidateToken(token); PreferenceManager.getDefaultSharedPreferences(AuthDocActivity.this).edit().putString(PREF_TOKEN, "").commit(); startActivity(new Intent(AuthDocActivity.this, AuthDocActivity.class)); finish(); } sw.addView(ll); setContentView(sw); super.onPostExecute(result); }; }.execute(token); } }
Рассмотрим его внимательнее.
В методе onCreate пытаемся получить сохранённый токен из SharedPreferences. Если он получен, выполняем метод loadGDocs, если нет - получаем список сохранённых на устройстве аккаунтов и даём возможность клиенту выбрать тот аккаунт, от имени которого следует работать дальше. Если аккаунтов нет вообще - ругаемся.
Работаем с аккаунтами
Для работы с аккаунтами у нас есть отдельный класс: GAuthHelper. Вот его код:
import android.accounts.Account; import android.accounts.AccountManager; import android.accounts.AccountManagerCallback; import android.accounts.AccountManagerFuture; import android.accounts.OperationCanceledException; import android.app.Activity; import android.os.Bundle; public class GAuthHelper { private AccountManager accountManager; private static final String ACC_TYPE = "com.google"; private static final String SCOPE = "oauth2:https://docs.google.com/feeds/"; private Activity act; public GAuthHelper(Activity activity) { accountManager = AccountManager.get(activity); act = activity; } public Account[] getAccounts() { Account[] accounts = accountManager.getAccountsByType(ACC_TYPE); return accounts; } public String[] getAccNames() { Account[] accounts = getAccounts(); String[] rez = new String[accounts.length]; for (int i = 0; i < accounts.length; i++) { rez[i] = accounts[i].name; } return rez; } private Account getAccountByName(String name) { Account[] accounts = getAccounts(); for (int i = 0; i < accounts.length; i++) { if (name.equals(accounts[i].name)) return accounts[i]; } return null; } public void invalidateToken(String token) { accountManager.invalidateAuthToken(ACC_TYPE, token); } public void getAuthToken(String accname, OAuthCallbackListener authCallbackListener) { getAuthToken(getAccountByName(accname), authCallbackListener); } public void getAuthToken(Account account, final OAuthCallbackListener authCallbackListener) { accountManager.getAuthToken(account, SCOPE, null, act, new AccountManagerCallback<Bundle>() { public void run(AccountManagerFuture<Bundle> future) { try { String token = future.getResult().getString(AccountManager.KEY_AUTHTOKEN); authCallbackListener.callback(token); } catch (OperationCanceledException e) { authCallbackListener.callback(null); } catch (Exception e) { e.printStackTrace(); } } }, null); } public static interface OAuthCallbackListener { public void callback(String authToken); } }Получив список имён аккаунтов методом getAccNames мы строим окно со списком и по выбору элемента получаем токен (методом getAuthToken) для аккаунта с выбранным именем. В этот метод мы передаём OAuthCalbackListener, реализующий интерфейс, описанный тут же, в GAuthHelper. При получении токена вызываем у listener-а calback, возвращаясь таким образом к методу loadGDocs, описанному в главном Activity. Теперь, когда мы так или иначе имеем токен пришло время разобрать-таки этот метод.
Загрузка данных из GoogleDocs
Метод loadGDocs создаёт и выполняет фоновый процесс, логика которого описана в классе DocsLoader:
import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.StringReader; import java.net.HttpURLConnection; import java.net.URL; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLSession; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; import android.app.Activity; import android.app.ProgressDialog; import android.os.AsyncTask; public class DocsLoader extends AsyncTask<String, Integer, String[]>{ private static final String DEFAULT_URL = "https://docs.google.com/feeds/default/private/full"; private final static String HEADER_PREFIX_OAUTH = "OAuth "; private ProgressDialog progressDialog; private Activity act; public DocsLoader(Activity act) { this.act = act; } @Override protected void onPreExecute() { progressDialog = new ProgressDialog(act); progressDialog.setMessage("Downloading docs list..."); progressDialog.show(); } @Override protected String[] doInBackground(String... params) { String[] rez = null; try { String resp = request(DEFAULT_URL, params[0]); DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Document doc = builder.parse(new InputSource(new StringReader(resp))); NodeList titles = doc.getElementsByTagName("title"); rez = new String[titles.getLength()]; for (int i=0; i<titles.getLength(); i++) { Node n = titles.item(i); rez[i] = n.getTextContent(); } } catch (Exception e) { e.printStackTrace(); } return rez; } @Override protected void onPostExecute(String[] result) { progressDialog.dismiss(); } private String request(String url, String token) { StringBuilder sb = new StringBuilder(); try { HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection(); if (conn instanceof HttpsURLConnection) { ((HttpsURLConnection) conn).setHostnameVerifier(new HostnameVerifier() { public boolean verify(String hostname, SSLSession session) { return true; } }); } conn.setUseCaches(false); conn.setDoOutput(true); conn.setDoInput(true); conn.setRequestProperty("GData-Version", "3.0"); conn.setRequestProperty("Authorization", HEADER_PREFIX_OAUTH + token); if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) { throw new Exception("http error "+conn.getResponseCode()); } BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream())); String nextLine = null; while ((nextLine = in.readLine()) != null) { sb.append(nextLine); } in.close(); conn.disconnect(); } catch (Exception e) { e.printStackTrace(); } return sb.toString(); } }
Этот класс расширяет AsyncTask, а значит реализует метод doInBackground. Тут мы выполняем запрос к API GoogleDocs, передавая два http-header, которые обеспечивают нам авторизацию с помощью полученного токена. Сам запрос описан в методе request. В методе onPreExecute мы запускаем прелоадер, а в onPostExecute останавливаем. Кстати, метод onPostExecute мы переопределяем в главном Activity чтобы построить список файлов по данным, полученным из API.
Собственно, на этом реализацию нашего примера можно завершить. Заметьте, что тут нам не приходиться использовать никаких специальных библиотек для работы с сервисами google, хотя их можно найти в сети достаточно много. Google Data API, которое мы тут используем, представляет собой классическое rest-API, для работы с которым достаточно отправлять обычные GET-запросы и анализировать xml-структуры, получаемые в ответ. Единственная библиотека, которую потребуется подключить к проекту, потребуется в рантайме на этапе получения токена.
Доброе утро, пробую ваш код. но на if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) {
ОтветитьУдалитьthrow new Exception("http error " + conn.getResponseCode());
}
Возвращает ошибку 403 в чем может бить пролема?
Спасибо.
Я попробовал сделать так
Удалитьint status = conn.getResponseCode();
switch (status) {
case 200:
case 201:
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
//StringBuilder sb = new StringBuilder();
String nextLine;
while ((nextLine = in.readLine()) != null) {
Log.d("VkDemoApp", "next line "+nextLine );
sb.append(nextLine);
Log.d("VkDemoApp", "sb if"+sb.toString());
}
in.close();
conn.disconnect();
Log.d("VkDemoApp", "sb exit"+sb.toString());
return sb.toString();
}
но прога крашится в mainactivity
if (result!=null) {
for (String s : result) {
TextView tw = new TextView(MainActivity.this);
tw.setText(s);
ll.addView(tw);
}
спасибо, всё работает, вот бы ещё такой же мануал для сервера ))
ОтветитьУдалить