Tag: Android

  • Snippet to determine a size of control in Android

    It is a common task to get a control size. You may need it to create image assets to customize that control.

    First we get a control object, for example ToggleButton. Then we get a pixel density of the device. After that we can convert px units to dp units.

    ToggleButton toggleButton = (ToggleButton) getView().findViewById(R.id.toggleHideFromAgency);
    float density = getActivity().getResources().getDisplayMetrics().density;
    float width = toggleButton.getWidth();
    float height = toggleButton.getHeight();
    //float px = someDpValue * density;
    float dpWidth = width / density;
    float dpHeight = height / density;
    Log.v(Helper.TAG, "ToggleButton width = " + Float.toString(dpWidth));
    Log.v(Helper.TAG, "ToggleButton height = " + Float.toString(dpHeight));
    
  • Android Java Serialization Using Generics

    I have created a class – SerializationManager, it can serialize any objects and write them to files. It can be used to save and restore form states in Android. It is based on Java Generics. What is achieved by this is that you do not need to duplicate serialization code in every class.

    images

    Object must implement Serialized interface. Here form is an object instance of a class. It can be named as just obj.

    package com.alwawee.util;
    
    import android.content.Context;
    
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    
    public class SerializationManager {
    
        //================================================================================
        // Constructor
        //================================================================================
        public SerializationManager(){
    
        }
    
    
        //================================================================================
        // Other
        //================================================================================
        // Reads saved object from a file. Object must implement Serialized interface.
        public T readObjectFromFile(Context context, String className) {
            T form = null;
            try {
                String fileName = className + ".ser";
                FileInputStream fileInputStream = context.openFileInput(fileName);
                ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
                form = (T) objectInputStream.readObject();
                objectInputStream.close();
                fileInputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            return form;
        }
    
    
        // Serializes object and saves it to a file. File name is created using object type (class name). 
        public void saveObjectToFile(Context context, T form) {
            try {
                String fileName = form.getClass().getSimpleName() + ".ser";
                FileOutputStream fileOutputStream = context.openFileOutput(fileName, Context.MODE_PRIVATE);
                ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
                objectOutputStream.writeObject(form);
                objectOutputStream.close();
                fileOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    This is how you can use it. First you declare a SerializationManager in your class and you tell it which class you are going to serialize:

    private SerializationManager manager;
    

    Then in onCreate method you create SerializationManager instance:

    @Override
        protected void onCreate(Bundle savedInstanceState) {
            manager = new SerializationManager();
    

    Writing object to a file:

    // Saving to a file
    manager.saveObjectToFile(this, form);
    

    Reading objects from saved file:

    // Reading form object from a file
    form = manager.readObjectFromFile(this, "CreateResumeForm");
    

    A little joke for reading to the end:
    java generics

  • Serialize an object, save it to a file and read it from a file in Android

    Android interface Serializable is not sufficient to save your objects to the file, it only creates streams.

    This is a complete code with error handling and handling closes of streams. Add it to your class that you want to be able to serialize and deserialize.

    In my case the class name is CreateResumeForm. You should change it to your own class name.

    // Constant with a file name
    public static String fileName = "createResumeForm.ser";
    
    // Serializes an object and saves it to a file
    public void saveToFile(Context context) {
        try {
            FileOutputStream fileOutputStream = context.openFileOutput(fileName, Context.MODE_PRIVATE);
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
            objectOutputStream.writeObject(this);
            objectOutputStream.close();
            fileOutputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    
    // Creates an object by reading it from a file
    public static CreateResumeForm readFromFile(Context context) {
        CreateResumeForm createResumeForm = null;
        try {
            FileInputStream fileInputStream = context.openFileInput(fileName);
            ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
            createResumeForm = (CreateResumeForm) objectInputStream.readObject();
            objectInputStream.close();
            fileInputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return createResumeForm;
    }
    

    Use it like this in your Activity:

    form = CreateResumeForm.readFromFile(this);
    
  • On TextView Hints and StringUtils.isBlank

    I had a problem caused by setting empty or blank strings to the TextView and AutoCompleteTextView – hints were invisible after that. So I decided to check strings, that are set, if they were not only empty, but also not blank. I used StringUtils.isBlank instead of StringUtils.isEmpty: it works much better now, and hints are always visible. If you don’t still use StringUtils, I highly recommend you to do. This is a Gradle import for a library that contains them:

    dependencies {
        compile 'org.apache.commons:commons-lang3:3.0.+'
    }
    
    String keywordsForTextView = searchHistoryItem.getKeywords();
    if ( StringUtils.isBlank(keywordsForTextView) ) {
          Resources res = context.getResources();
          keywordsForTextView = res.getString(R.string.no_keywords);
    }
    

    And another example with AutoCompleteTextView:

    public void setKeywords(String keywords) {
       this.keywords = keywords;
       if (StringUtils.isNotBlank(keywords)) {
          Log.v(Helper.TAG, "Setting keywords to the keywordAutoCompleteTextView: " + keywords);
          keywordAutoCompleteTextView.setText(keywords);
       }
    }
    

    Here is some information on how isNotEmpty and isNotBlank are working.

    Title of this article: On Android TextView and AutoCompleteTextView Hint and StringUtils.isBlank and .isEmpty

  • Gradle build file for a project with Android Annotations

    There is a sample Gradle build file for a project with Android Annotations. This is pretty complicated stuff, that was achieved with a lot of my blood, so I decided to post it here, so that you can use it to try to find your mistakes. Android Annotations library is loaded with apt. The position of every setting is important. There is also a trick to avoid Lint errors and a custom task to run app via command line.

    I used Gradle 1.12 and Android Studio 0.8.4 with Android SDK 20, Android Build Tools 20.0 and Android Plugin 0.12.+.

    buildscript {
        repositories {
            mavenCentral()
        }
    
        dependencies {
            classpath 'com.android.tools.build:gradle:0.12.+'
            classpath 'com.neenbedankt.gradle.plugins:android-apt:1.3'
        }
    }
    
    repositories {
        mavenCentral()
        mavenLocal()
    }
    
    apply plugin: 'com.android.application'
    apply plugin: 'android-apt'
    def AAVersion = '3.0.1'
    
    dependencies {
        apt "org.androidannotations:androidannotations:$AAVersion"
        compile "org.androidannotations:androidannotations-api:$AAVersion"
        compile "com.android.support:support-v4:18.0.+"
        compile "com.android.support:appcompat-v7:18.0.+"
        compile 'org.apache.commons:commons-lang3:3.0.+'
        compile fileTree(dir: 'libs', include: '*.jar')
    }
    
    apt {
        arguments {
            androidManifestFile variant.processResources.manifestFile
            resourcePackageName "ru.rabota.app2"
        }
    }
    
    android {
        compileSdkVersion 20
        buildToolsVersion '20.0.0'
    
        defaultConfig {
            minSdkVersion 10
            targetSdkVersion 20
            versionCode 17
            versionName "2.4.2"
        }
    
        sourceSets {
            main {
                manifest.srcFile 'AndroidManifest.xml'
                java.srcDirs = ['src']
                resources.srcDirs = ['src']
                aidl.srcDirs = ['src']
                renderscript.srcDirs = ['src']
                res.srcDirs = ['res']
                assets.srcDirs = ['assets']
            }
    
            androidTest.setRoot('tests')
        }
    
        lintOptions {
            abortOnError false
        }
    }
    
    task appStart(type: Exec, dependsOn: ':installDebug') {
        // linux 
        commandLine 'adb', 'shell', 'am', 'start', '-n', 'ru.rabota.app2/ru.rabota.intro.IntroActivity'
    
        // windows
        // commandLine 'cmd', '/c', 'adb', 'shell', 'am', 'start', '-n', 'com.alwawee.multiverse/com.alwawee.main.MainActivity'      
    }
    
  • Running Android app on device using Gradle

    Here I will show you, how I managed to build and run an Android app via command line (Terminal, since I use Mac) using Gradle and how I start a resulting app on the device (Google Nexus 5 with Android 4.4.2) automatically. I had to solve two major problems to achieve this.

    The first problem is with Gradle version: last version of Gradle is not compatible with the last version of Android Plugin.
    The second problem is with custom task to run app on device: you have to put the correct package and activity names.

    The final result looks like this:

    ~/Documents/Multiverse: $ gradle appStart
    

    Where Multiverse is the root folder of my project and the name of the project, appStart – custom Gradle task that builds and runs an app on device.
    Gradle loads all dependencies, builds app, makes a lint check and starts and app on your device automatically. Yahoo! This is how it looks on Terminal:

    	pkg: /data/local/tmp/Multiverse-debug.apk
    Success
    :appStart
    Starting: Intent { cmp=com.alwawee.multiverse/com.alwawee.main.MainActivity }
    
    BUILD SUCCESSFUL
    
    Total time: 13.475 secs
    

    However, I should notice, that this works slower than just running via Eclipse: as you see it takes at least 13 seconds for a little project on Macbook Pro Retina Display Late 2012.

    First of all you need to install a correct version of Gradle. If you just download the latest version, your Android plugin will not work. You have to get a version, for instance, 1.10, not 2.0. Also you need to setup PATH variable, so that you will have bin folder of Gradle and android sdks tools there:

    ~/Documents/Multiverse: $ vim ~/.bash_profile
    
    export PATH=/Users/Denis/gradle-1.10/bin:$PATH
    export PATH=/Users/Denis/android-sdks/platform-tools:$PATH
    export PATH=/Users/Denis/android-sdks/tools:$PATH
    
    ~/Documents/Multiverse: $ source ~/.bash_profile
    

    Then I created a gradle build file:

    buildscript {
        repositories {
            mavenCentral()
        }
    
        dependencies {
            classpath 'com.android.tools.build:gradle:0.11.1'
        }
    }
    
    apply plugin: 'android'
    
    dependencies {
        compile files('libs/android-support-v4.jar')
        compile files('libs/androidannotations-api-3.0.1.jar')
        compile files('libs/commons-lang3-3.1.jar')
        compile files('libs/flurryAnalytics_3.4.0.jar')
        compile files('libs/libGoogleAnalyticsServices.jar')
    }
    
    android {
        compileSdkVersion 19
        buildToolsVersion "19.1"
    }
    
    android {
        sourceSets {
            main {
                manifest.srcFile 'AndroidManifest.xml'
                java.srcDirs = ['src']
                resources.srcDirs = ['src']
                aidl.srcDirs = ['src']
                renderscript.srcDirs = ['src']
                res.srcDirs = ['res']
                assets.srcDirs = ['assets']
            }
    
            androidTest.setRoot('tests')
        }
    }
    
    /*android {
          lintOptions {
              abortOnError false
          }
    }*/
    
    task appStart(type: Exec, dependsOn: ':installDebug') {
        // linux, mac 
        commandLine 'adb', 'shell', 'am', 'start', '-n', 'com.alwawee.multiverse/com.alwawee.main.MainActivity'
    
        // windows
        // commandLine 'cmd', '/c', 'adb', 'shell', 'am', 'start', '-n', 'com.alwawee.multiverse/com.alwawee.main.MainActivity'      
    }
    

    Here com.alwawee.multiverse is the package name of the application.
    And com.alwawee.main.MainActivity is the full Activity name, that the application starts from. This is how my project tree looks like:
    Снимок экрана 2014-07-28 в 1.51.29

    This is a part of the Manifest file:

    
    
        
        ...
            
            
        ...
    

    At the end there is a custom task appStart. It is important to correctly write the package name before class name. I thought that the package name is the package name for MainActivity class. I was wrong. It’s a name for a whole app’s package name. You can find it at the root node of your Manifest file.

    Notes on commented code: use abortOnError false if you have any serious problems with lint and don’t want to solve them. Uncomment the line about Windows, if you use Windows.

    This is a StackOverflow question, I asked to reach this goal.

  • How to add shortcut to Home screen in Android programmatically

    I improved a little bit a solution that was found in StackOverflow. Now program saves in preferences whether a shortcut was already added and doesn’t add it in new launches of an app if user deleted it. This also saves a little bit time, since the code to add an existing shortcut doesn’t run anymore.

    final static public String PREFS_NAME = "PREFS_NAME";
    final static private String PREF_KEY_SHORTCUT_ADDED = "PREF_KEY_SHORTCUT_ADDED";
    
    
    // Creates shortcut on Android widget screen
    private void createShortcutIcon(){
    
        // Checking if ShortCut was already added
        SharedPreferences sharedPreferences = getPreferences(MODE_PRIVATE);
        boolean shortCutWasAlreadyAdded = sharedPreferences.getBoolean(PREF_KEY_SHORTCUT_ADDED, false);
        if (shortCutWasAlreadyAdded) return;
    
        Intent shortcutIntent = new Intent(getApplicationContext(), IntroActivity.class);
        shortcutIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        shortcutIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
    
        Intent addIntent = new Intent();
        addIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
        addIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, "YourAppName");
        addIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, Intent.ShortcutIconResource.fromContext(getApplicationContext(), R.drawable.ic_launcher));
        addIntent.setAction("com.android.launcher.action.INSTALL_SHORTCUT");
        getApplicationContext().sendBroadcast(addIntent);
    
        // Remembering that ShortCut was already added
        SharedPreferences.Editor editor = sharedPreferences.edit();
        editor.putBoolean(PREF_KEY_SHORTCUT_ADDED, true);
        editor.commit();
    }
    

    In Android Manifest:

    
    
  • Open-Source Expandable Selection Tree for Android

    Today I have published a new open source project on GitHub, called DKExpandableSelectionTree. It is a better expandable list view for Android that allows user to select multiple items from a tree of objects. You can clear all selections, you can select and deselect multiple items by selecting a whole group. It also displays a number of selected items inside a group in parentheses after the name of a group.

    This is how it will look in a sample project, when you clone the repository and run it:
    Screenshot_2014-05-27-19-22-47

    This is how it looks in my project:
    Screenshot_2014-05-27-19-16-17