Thursday, May 15, 2014

How to detect if application has been updated or installed

In some cases there is a need to know if user has installed or updated your app. For example to put different texts or something else. To do this you may use code below:
static public boolean isUpdated(Context ctx) {
    PackageInfo packageInfo = null;
    try {
            packageInfo = ctx.getPackageManager().getPackageInfo(ctx.getPackageName(),
                PackageManager.GET_ACTIVITIES);
        } catch (PackageManager.NameNotFoundException e) {
            Log.e(TAG, "Can't get package info");
        }
    return (packageInfo.firstInstallTime != packageInfo.lastUpdateTime);
}

Wednesday, May 14, 2014

Settings up AndEngine in Android studio


When i decided to use AndEngine in my project I discovered that it doesn't support Android studio and there is not any passable tutorial about how to make it work. So I spent some time and set it up by myself.

Let's assume that you already have created a project and it has default structure. First create folder named third_party in the root directory of the project. Then in third_party directory create subdirectories called andengine and andenginebox2d. I assume that you already downloaded or cloned AndEngine and Box2d extension for it. Put AndEngine and AndEngineBox2d in andengine and andenginebox2d directories respectively. Create file named build.gradle in andengine directory and andenginebox2d directory. Build.gradle files is a file that tells gradle how to build your project.
Next you have to put content in your build.gradle files. In file located in andengine folder put:

apply plugin: 'android-library'

android {
    compileSdkVersion 17
    buildToolsVersion "19.0.3"

    defaultConfig {
        minSdkVersion 14
        targetSdkVersion 19
    }

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_7
        targetCompatibility JavaVersion.VERSION_1_7
    }

    buildTypes {
        release {
            runProguard false
            proguardFiles getDefaultProguardFile('proguard-project.txt')
        }
    }

    sourceSets {
        main {
            manifest.srcFile 'AndroidManifest.xml'
            java.srcDirs = ['src']
            resources.srcDirs = ['src']
            res.srcDirs = ['res']
            assets.srcDirs = ['assets']
        }

        instrumentTest.setRoot('tests')
    }
}
By doing so you are telling gradle that minSdkVersion is 14 and targetSdk is 19. Above all I'm using Java 7 in my project so there is
compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_7
        targetCompatibility JavaVersion.VERSION_1_7
    }
block.

In file located in andenginebox2d folder put such content:
apply plugin: 'android-library'

android {
    compileSdkVersion 17
    buildToolsVersion "19.0.3"

    defaultConfig {
        minSdkVersion 14
        targetSdkVersion 19
    }

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_7
        targetCompatibility JavaVersion.VERSION_1_7
    }

    buildTypes {
        release {
            runProguard false
            proguardFiles getDefaultProguardFile('proguard-project.txt')
        }
    }

    sourceSets {
        main {
            manifest.srcFile 'AndroidManifest.xml'
            java.srcDirs = ['src']
            resources.srcDirs = ['src']
            res.srcDirs = ['res']
            assets.srcDirs = ['assets']
        }

        instrumentTest.setRoot('tests')
    }
}

dependencies {
    compile project(':third_party:andengine')
}
As you can see it is almost identical to previous except dependencies block. That block tells gradle that andenginebox2d depends on andengine and gradle should include it as a library project.

We are almost done. Next step is to tell gradle that you want it to compile AndEngine with your project.
Open settings.gradle that is located in your project's root directory and add two lines to it:
include ':third_party:andengine'
include ':third_party:andenginebox2d'
Next open build.gradle which locates in app directory and add
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile project(':third_party:andenginebox2d')
    compile project(':third_party:andengine')
to dependencies block. You already know that it means that your application is dependent of andengine project.

The final step is to open AndroidManifest.xml in andegine and andenginebox2d directories and make them look like this:


    
<application /> block is needed because of bug in manifest merging tool.
That's it! Now clean your project and press run. Everything should work just fine.

Tuesday, February 12, 2013

Использование assets в Android

    В целом использование Assets в Android не представляет из себя ничего сложного: знай себе читай из InputStream. Но тут есть некоторые подводные камни. В частности производительность, которая при использовании метода list() падает ниже плинтуса. Связано это, по видимому, с тем, что ассеты хранятся в сжатом виде и при каждом считывании приходиться производить манипуляции с архивом. О том как с этим боролся я ниже.

Wednesday, January 30, 2013

Как читать из InputStream

    Предположим, что у нас есть объект класса наследника InputStream и мы хотим прочитать его полностью. Тогда неплохим решением будет сделать так:

public byte[] getAsByteArray(InputStream in){
    byte[] buffer = new byte[8196];
    int read = 0;
    ByteArrayOutputStream result = new ByteArrayOutputStream();
    while ((read = in.read(buffer)) != -1) {
        result.write(buffer, 0, read);
    }
    result.flush();
    result.close();
    return result.toByteArray();
}

Работа с Drive API. Часть 2

    Первоначально предполагалось во второй части дать обзор примера от Google, но потом стало понятно, что разбирать там особо нечего, да и это скучно довольно. Поэтому здесь будет приведен обзор основных моментов и подводных камней.
    Итак первое с чем придется столкнуться во время работы с Drive API - это получение объекта Drive. Делается это так:
Drive drive = new Drive.Builder(AndroidHttp.newCompatibleTransport(), new GsonFactory(), credential)
        .build();
credentials можно получить так - 
credential = GoogleAccountCredential.usingOAuth2(this, DriveScopes.DRIVE);
После этого у вас на руках  рабочий объект Drive. За исключением того, что вы еще не авторизованы пользователем для работы с его диском. Это разрешение получают примерно так:
try{
    drive.files().touch(fileId).execute();
}catch(UserRecoverableAuthIOException e){
    startActivityForResult(e.getIntent,REQUEST_AUTHORIZATION)
}
Затем в методе onActivityResult можно будет отловить ваш REQUEST_AUTHORIZATION. Теперь авторизация получена.
Стоит добавить, что работу с Drive API следует проводить не в UI Thread, так как в противном случае будет брошен Exception. В конце небольшой класс для работы с драйвом.
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import android.content.Context;

import com.google.api.client.extensions.android.http.AndroidHttp;
import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential;
import com.google.api.client.http.FileContent;
import com.google.api.client.http.GenericUrl;
import com.google.api.client.http.HttpResponse;
import com.google.api.client.json.gson.GsonFactory;
import com.google.api.services.drive.Drive;
import com.google.api.services.drive.DriveScopes;
import com.google.api.services.drive.model.File;
import com.google.api.services.drive.model.FileList;
import com.google.api.services.drive.model.ParentList;
import com.google.api.services.drive.model.ParentReference;

public class DriveService {

 static final String folderMIME = "application/vnd.google-apps.folder";
 public static final int REQUEST_AUTHORIZATION = 1990;

 private GoogleAccountCredential credential;
 private Drive driveService;

 public DriveService(Context context, String accountName) {

  credential = GoogleAccountCredential.usingOAuth2(context,
    DriveScopes.DRIVE);
  credential.setSelectedAccountName(accountName);
  driveService = getDriveService(credential);

 }

 private Drive getDriveService(GoogleAccountCredential credential) {
  return new Drive.Builder(AndroidHttp.newCompatibleTransport(),
    new GsonFactory(), credential).build();
 }
 
 public File getFile(String id) throws IOException{
  return driveService.files().get(id).execute();
 }
 
 public ParentList getParents(String id) throws IOException{
  return driveService.parents().list(id).execute();
 }

 public List getFileList(String q) throws IOException {

  List result = new ArrayList();
  com.google.api.services.drive.Drive.Files.List listRequest = driveService
    .files().list();
  if( q!= null && q.length() > 0)
   listRequest.setQ(q);

  do {

   FileList fList = listRequest.execute();
   result.addAll(fList.getItems());
   listRequest.setPageToken(fList.getNextPageToken());

  } while (listRequest.getPageToken() != null
    && listRequest.getPageToken().length() > 0);

  return result;

 }

 public void deleteFile(String fileId) throws IOException {

  driveService.files().delete(fileId).execute();

 }
 
 public InputStream downloadFile(File file) throws IOException {
         HttpResponse resp =
             driveService.getRequestFactory().buildGetRequest(new GenericUrl(file.getDownloadUrl()))
                 .execute();
         return resp.getContent();
   }

 public File uploadFile(String title, String desc, String parentId,
   String mime, String fName) throws IOException {

  File body = new File();
  body.setTitle(title);
  body.setDescription(desc);
  body.setMimeType(mime);

  if (parentId != null && parentId.length() > 0) {

   ParentReference parent = new ParentReference();
   parent.setId(parentId);
   body.setParents(Arrays.asList(parent));

  }

  java.io.File content = new java.io.File(fName);
  FileContent fContent = new FileContent(mime, content);

  return driveService.files().insert(body, fContent).execute();

 }

 public File createFolder(String parentId, String folderName) throws IOException {
  File body = new File();
  body.setTitle(folderName);
  body.setMimeType(folderMIME);

  if (parentId != null && parentId.length() > 0) {

   ParentReference parent = new ParentReference();
   parent.setId(parentId);
   body.setParents(Arrays.asList(parent));
  }

  return driveService.files().insert(body).execute();
 }

 public File updateFile(String fileId, String newTitle, String newDesc,
   String newMime, String newFileName) throws IOException {

  File oldFile = driveService.files().get(fileId).execute();
  oldFile.setTitle(newTitle);
  oldFile.setDescription(newDesc);
  oldFile.setMimeType(newMime);

  java.io.File newFile = new java.io.File(newFileName);
  FileContent newContent = new FileContent(newMime, newFile);

  return driveService.files().update(fileId, oldFile, newContent).execute();

 }

}

Saturday, January 26, 2013

Работа с Drive Api. Часть 1

    По долгу службы пришлось разобраться с тем, как работать с Drive Api. В целом ничего особо сложного в этом нет, но имеются некоторые особенности. Да и нормального туториала на русском языке я не нашел, что меня немного расстроило. Спешу исправить этот недочет. Итак - часть первая: