Utils: Custom Toast

Snippet shows simple implementation of custom Toast that can be easily used in hole application.

Header

Key notes

  • Use Toast to show simple informative messages
  • Create custom Toast when you need to change for example text font, view color or add some image to make message more informative
  • You can not change Toast animation even if you create custom one
  • You have only two duration options Toast.LENGTH_SHORT and Toast.LENGTH_LONG
  • You can not handle click on Toast even on custom one
  • Try to reuse Toast instance instead of creating new one

Usage

// use it like native Toast object
ErrorToast.makeToast(activity, "Message", Toast.LENGTH_LONG).show();

ErrorToast toast = new ErrorToast(activity);
toast .setText("Message");
toast .setGravity(Toast.LENGTH_SHORT);
toast .show();

// cancel toast if it is showing at the moment
if(toast .isShowing()){
    toast .cancel();
}

Sources

ErrorToast class

import android.app.Activity;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;

public class ErrorToast {

    private Activity mActivity;
    private Toast mToast;

    public ErrorToast(Activity activity) {
        mActivity = activity;
        View view = mActivity.getLayoutInflater().inflate(
                R.layout.view_toast,
                (ViewGroup) activity.getWindow().getDecorView(),
                false
        );

        mToast = new Toast(mActivity.getApplicationContext());
        mToast.setView(view);
    }

    public static ErrorToast makeToast(Activity activity, String message, int duration) {
        ErrorToast result = new ErrorToast(activity);

        result.mToast.setDuration(duration);
        result.mToast.setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, 0);
        result.setText(message);

        return result;
    }

    public void show() {
        mToast.show();
    }

    public void cancel() {
        mToast.cancel();
    }

    public void setText(String message) {
        TextView txtMessage = (TextView) mToast.getView().findViewById(R.id.txtMessage);
        txtMessage.setText(message);
    }

    public void setText(int messageId) {
        setText(mActivity.getText(messageId).toString());
    }

    public boolean isShowing() {
        return mToast.getView().isShown();
    }

    public void setDuration(int duration) {
        mToast.setDuration(duration);
    }

    public void setGravity(int gravity, int xOffset, int yOffset) {
        mToast.setGravity(gravity, xOffset, yOffset);
    }
}

Resources

view_toast.xml file

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                xmlns:tools="http://schemas.android.com/tools"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:minHeight="@dimen/min_lay_height"
                android:minWidth="@dimen/min_lay_width"
                android:background="@drawable/toast_bg">

    <TextView android:id="@+id/txtMessage"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:paddingLeft="@dimen/spacing_big"
              android:paddingRight="16dp"
              android:drawableLeft="@android:drawable/ic_dialog_alert"
              android:drawableStart="@android:drawable/ic_dialog_alert"
              android:drawablePadding="@dimen/spacing_big"
              android:gravity="center_vertical"
              android:layout_centerVertical="true"
              android:textColor="@android:color/white"
              android:textSize="@dimen/text_size_medium"
              tools:text="No connection."/>
</RelativeLayout>

black_rect.xml file

<?xml version="1.0" encoding="utf-8"?>

<shape xmlns:android="http://schemas.android.com/apk/res/android"
       android:shape="rectangle">

    <solid android:color="#66000000"/>
    <corners android:radius="@dimen/rect_radius"/>

</shape>

toast_bg.xml file

<?xml version="1.0" encoding="utf-8"?>

<inset xmlns:android="http://schemas.android.com/apk/res/android"
       android:drawable="@drawable/black_rect"
       android:insetTop="@dimen/spacing_small"
       android:insetRight="@dimen/spacing_big"
       android:insetBottom="@dimen/spacing_small"
       android:insetLeft="@dimen/spacing_big" />

dimens.xml file

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <dimen name="rect_radius">2dp</dimen>
    <dimen name="ac_padding">16dp</dimen>

    <dimen name="spacing_small">4dp</dimen>
    <dimen name="spacing_big">8dp</dimen>

    <dimen name="text_size_micro">12sp</dimen>
    <dimen name="text_size_small">14sp</dimen>
    <dimen name="text_size_medium">18sp</dimen>
    <dimen name="text_size_large">22sp</dimen>

    <dimen name="min_lay_width">48dp</dimen>
    <dimen name="min_lay_height">48dp</dimen>
</resources>

Utils: Resource Fetcher

Gist source on github

Some times we need find resource ID by it String name. This class will help to do this

Usage

ResourceFetcher fetcher = new ResourceFetcher(context);

ImageView view = ...
view.setImageResource(fetcher.getDrawableId("name"));

Sources

public class ResourceFetcher {

    private Context mContext;

    public ResourceFetcher(Context context) {
        mContext = context.getApplicationContext();
    }

    public int getDrawableId(String id) {
        return mContext.getResources().getIdentifier(id, "drawable", mContext.getPackageName());
    }
}

Utils: Dialog Helper

Gist source on github

Class that helps to show/dismiss DialogFraments allowing state loss

public class DialogHelper {

    public static void showAllowStateLoss(FragmentActivity activity, String tag, DialogFragment dialog) {
        if (activity != null && !activity.isFinishing()) {
            FragmentTransaction ft = activity.getSupportFragmentManager().beginTransaction();
            ft.add(dialog,tag);
            ft.commitAllowingStateLoss();
        }
    }


    public static void dismissAllowStateLoss(FragmentActivity activity, DialogFragment dialog) {
        if (activity != null && !activity.isFinishing()) {
            dialog.dismissAllowingStateLoss();
        }
    }
}

Best Practice: Application

Header

Article sources on github

Base class for those who need to maintain global application state.

  • Application is the start point of your program
  • It’s called when the application is starting, before any activity, service, or receiver objects have been created
  • Only Content Provider is started before it

All logic are based in one Application class and few interfaces :

Application

Application is a Singleton that you can get from any Activity or Service with getApplication() method. Also you can cast your getApplicationContext() to Application.

// get inside of Activity or Service
Application app = getApplication();

// get from Contex
Application app = (Application)view.getContext().getApplicationContext();

You can create your own custom application. To do this you need :

  • Create class that extends Application
public class App extends Application {
    // your logic goes here
}
  • Initialize your custom Application in manifest (just add name tag that should matches your Application path)
<!--AndroidManifest.xml-->
<application
    android:name="yourpackage.App"
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/AppTheme">

And what it can give to us ?

// ...
Application app = getApplication();

// as Application extends ContextWrapper we can do everything the it can
// (get Assets as well)
AssetManager assets = app.getAssets();

And thats all, all we could do before API 14 without overriding. So if you are supporting old OS versions and need to maintain global application state you will need to create Custom Application

public class App extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        // your application starts from here
    }

    @Override
    public void onLowMemory() {
        super.onLowMemory();
        // This is called when the overall system is running low on memory
        // and actively running processes should trim their memory usage
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        // Called by the system when the device configuration changes while your
        // component is running. Unlike activities Application doesn't restart when
        // a configuration changes
    }


    @Override
    public void onTerminate() {
        super.onTerminate();
        // This method is for use in emulated process environments only.
        // You can simply forget about it because it will never be called on real device
    }
}

From API 14 android guys added few simple observers, so now you don’t need to create Custom Application every time

  • Added possibility to set ComponentCallbacks : onConfigurationChanged and onLowMemory methods
  • Added new ComponentCallbacks2 interface: implements ComponentCallbacks and has new onTrimMemory method. It provide us possibility to handle different memory levels change.
  • Note that onLowMemory is not called from API 14. You should only use it as a fallback for older versions, which can be treated the same as onTrimMemory with the ComponentCallbacks2.TRIM_MEMORY_COMPLETE level.
  • Added registerActivityLifecycleCallbacks which allows you to handle state change of each activity in your program
// set ComponentCallbacks with out overriding
app.registerComponentCallbacks(new ComponentCallbacks2() {
    @Override
    public void onConfigurationChanged(Configuration configuration) {
    // determinate Configuration Change
    }

    @Override
    public void onLowMemory() {
    // use it only for older API version 
    }

    @Override
    public void onTrimMemory(int level) {
    // Called when the operating system has determined that it is a good
    // time for a process to trim unneeded memory from its process
    }
});

// set ActivityLifecycleCallbacks
app.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks(){
    @Override
    public void onActivityCreated(Activity activity, Bundle bundle) {}

    @Override
    public void onActivityStarted(Activity activity) {}

    @Override
    public void onActivityResumed(Activity activity) {}

    @Override
    public void onActivityPaused(Activity activity) {}

    @Override
    public void onActivityStopped(Activity activity) {}

    @Override
    public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {}

    @Override
    public void onActivityDestroyed(Activity activity) {}
});

From API 18 we have one more observer Application.OnProvideAssistDataListener that allows to place into the bundle anything you would like to appear in the Intent.EXTRA_ASSIST_CONTEXT part of the assist Intent

app.registerOnProvideAssistDataListener(new Application.OnProvideAssistDataListener() {
    @Override
    public void onProvideAssistData(Activity activity, Bundle data) {
    // put your changes here
    }
});

Performance & Tips

  • Applications starts before any activity, service, or receiver objects have been created. Use this to initialize your model (http client, database, libraries etc.)

  • There is normally no need to use Application as your static model(hold your objects as class fields). In most situation, static singletons can provide the same functionality in a more modular way. If your singleton needs a global context (for example to register broadcast receivers), the function to retrieve it can be given a Context which internally uses Context.getApplicationContext() when first constructing the singleton.

public class App extends Application {

    @Override
    public void onCreate() {
        super.onCreate();

        // init your model before any activities starts
        // in this way you will always know where your application starts
        initModel();
    }

    private void initModel() {
        Context applicationContext = getApplicationContext();

        // initialize volley http client
        RequestManager.initializeWith(applicationContext);
        ImageManager.initializeWith(applicationContext);

        // initialize your singleton
        Model.INSTANCE.initialize(applicationContext);

        // initialize your preferences
        PreferencesManager.initializeInstance(applicationContext);
    }
}
public enum Model {
    INSTANCE;
    public void initialize(Context context){
        // save your context as field
        // or do whatever you want here..
    }
}
  • Always remember that Application runs on UI thread. Implementations of your onCreate method should be as quick as possible since the time spent in this method directly impacts the performance of starting the first activity, service, or receiver in a process

  • If you override onCreate method, be sure to call super.onCreate()

  • You can save your static fast changing global data right before your Application be killed. For example if you are collecting some GPS locations statistic is a bad practice for battery life to save it every time when new location came. Just save it when user want to stop collecting data is not enough because your Application can be killed in background when Android need some memory. To be sure that your data will be not lost you can use onLowMemory and onTrimMemory methods. Be sure that you need to do this in your Application class, every Activity and Service implements ComponentCallbacks interface. Always remember that even after this call application can be not killed or be killed with some delay.

// in Application, Activity or Service

ComponentCallbacks2 mComponentCallbacks = new ComponentCallbacks2() {
    @Override
    public void onTrimMemory(int level) {
        if(level == ComponentCallbacks2.TRIM_MEMORY_COMPLETE){
            saveGlobalData();
        }
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
    }

    @Override
    public void onLowMemory() {
        // call it here to support old operating systems
        saveGlobalData();
    }

    private void saveGlobalData() {
        // save your stats to database
    }
};
  • You can use onTrimMemory with ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN to handle when your application goes to background
@Override
    public void onTrimMemory(int level) {
    if(level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN){
        // app in background
    }
}
  • Save context in your onCreate() to have possibility to get it from any where in your project. But note that it will cause a lot of Spaghetti code. This is highly not recommended
Context context = App.getContext();
public class App extends Application {
    private static Context mContext;
    @Override
    public void onCreate() {
        super.onCreate();
        mContext = getApplicationContext();
    }
    public static Context getContext() {
        return mContext;
    }
}    
  • There are cases when we need to handle that all Activities are destroyed (App is off). For example if you need to stop Service that work with few screens when Application goes off. ActivityLifecycleCallbacks can help you to deal with it
// set ActivityLifecycleCallbacks
app.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks(){

    private int mScreensCounter;

    @Override
    public void onActivityCreated(Activity activity, Bundle bundle) {
      mScreensCounter++;
    }

    @Override
    public void onActivityDestroyed(Activity activity) {
       mScreensCounter--;

       if(mScreensCounter == 0) {
          //... Application is Off
       }

       if(mScreensCounter < 0) {
           mScreensCounter = 0;
       }
    }

    //... 
});

Volley: Cache Image Loader

Gist source on github

There are cases when you need to clean your memory. In Volley Request Manager I used fields to save cache references. There is more practical solution, just create a wrapper for ImageLoader :

Usage

CacheImageLoader loader = ... // init loader

loader.getMemoryCache().remove(url); // remove item by key
loader.getMemoryCache().evictAll(); // remove all

loader.getDiskCache().remove(url); // remove item by key
loader.getDiskCache().clear(); // remove all

Wrapper sources :

public class CacheImageLoader extends ImageLoader {

    /**
     * Constructs a new ImageLoader.
     *
     * @param queue      The RequestQueue to use for making image requests.
     * @param imageCache The cache to use as an L1 cache.
     */

    private final MemoryCache mMemoryCache;
    private final Cache mDiskCache;

    public CacheImageLoader(RequestQueue queue, MemoryCache memoryCache) {
        super(queue, memoryCache);
        mMemoryCache = memoryCache;
        mDiskCache = queue.getCache();
    }

    public MemoryCache getMemoryCache() {
        return mMemoryCache;
    }
    public Cache getDiskCache() {
        return mDiskCache;
    }
}

Utils: Keyboard

Gist source on github

Some times we need to hide/show keyboard after some actions. This class will help to do this :

public class KeyboardUtils {

    public static void showKeyboard(View theView) {
        Context context = theView.getContext();
        Object service = context.getSystemService(Context.INPUT_METHOD_SERVICE);

        InputMethodManager imm = (InputMethodManager) service;
        if (imm != null) {
            imm.toggleSoftInput(
                    InputMethodManager.SHOW_FORCED,
                    InputMethodManager.HIDE_IMPLICIT_ONLY);
        }
    }

    public static void hideKeyboard(View theView) {
        Context context = theView.getContext();
        Object service = context.getSystemService(Context.INPUT_METHOD_SERVICE);

        InputMethodManager imm = (InputMethodManager) service;
        if (imm != null) {
            imm.hideSoftInputFromWindow(theView.getWindowToken(), 0);
        }
    }
}

Volley: Authorization

Gist sources on github

Example of adding authorization header to Volley Requests:

public class AuthRequest extends JsonObjectRequest {

    public AuthRequest(int method, String url, JSONObject jsonRequest,
            Response.Listener<JSONObject> listener,
            Response.ErrorListener errorListener) {
        super(method, url, jsonRequest, listener, errorListener);
    }

    public AuthRequest(String url, JSONObject jsonRequest,
            Response.Listener<JSONObject> listener,
            Response.ErrorListener errorListener) {
        super(url, jsonRequest, listener, errorListener);
    }

    @Override
    public Map<String, String> getHeaders() throws AuthFailureError {
        return createBasicAuthHeader("user", "password");
    }

    Map<String, String> createBasicAuthHeader(String username, String password) {
        Map<String, String> headerMap = new HashMap<String, String>();

        String credentials = username + ":" + password;
        String encodedCredentials = Base64.encodeToString(credentials.getBytes(), Base64.NO_WRAP);
        headerMap.put("Authorization", "Basic " + encodedCredentials);

        return headerMap;
    }
}

Utils: Uri

Gist source on github

While working with file system we often need to get path from Uri object.

As in Android 4.4 KitKat was added new Document picker we need to use 19 or later SDK version.

In your manifest :

<uses-sdk
    android:targetSdkVersion="19" />

And here is UriUtils class :

public class UriUtils {

    public static String getPathFromUri(Context context, Uri uri) {
        String result = null;

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT &&
                DocumentsContract.isDocumentUri(context, uri)) {
            result = getPathFromDocumentURI(context, uri);
        } else if ("content".equalsIgnoreCase(uri.getScheme())) {
            result = getPathFromContentTypeUri(context, uri);
        } else if ("file".equalsIgnoreCase(uri.getScheme())) {
            result = uri.getPath();
        }

        return result;
    }

    private static String getPathFromContentTypeUri(Context context, Uri uri) {
        String result = null;
        Cursor cursor = null;

        try {
            cursor = context.getContentResolver().query(uri, null, null, null, null);
            int columnIndex = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA);
            if (cursor.moveToFirst()) {
                result = cursor.getString(columnIndex);
            }
        } catch (Exception e) {
            // Eat it
            e.printStackTrace();
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }

        return result;
    }

    private static String getPathFromDocumentURI(Context context, Uri uri) {
        String result = null;
        //Will return "image:x*"
        String wholeID = DocumentsContract.getDocumentId(uri);

        // Split at colon, use second item in the array
        String id = wholeID.split(":")[1];

        String[] column = {MediaStore.Images.Media.DATA};

        // where id is equal to
        String sel = MediaStore.Images.Media._ID + "=?";
        Cursor cursor = null;
        try {
            cursor = context.getContentResolver().query(
                    MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                    column,
                    sel,
                    new String[]{id},
                    null);

            int columnIndex = cursor.getColumnIndex(column[0]);
            if (cursor.moveToFirst()) {
                result = cursor.getString(columnIndex);
            }
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
        return result;
    }
}

Spinner: Hint

Gist source on github

There is no default API to set hint on Spinner. To add it we will need a small workaround.

List<Object> objects = new ArrayList<Object>();
objects.add(firstItem);
objects.add(secondItem);
// add hint as last item
objects.add(hint);

HintAdapter adapter = new HintAdapter(context, objects, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);

Spinner spinnerFilmType = (Spinner) findViewById(R.id.spinner);
spinner.setAdapter(adapter);

// show hint
spinner.setSelection(adapter.getCount());

Adapter source:

public class HintAdapter
        extends ArrayAdapter<Objects> {

    public HintAdapter(Context theContext, List<Object> objects) {
        super(theContext, R.id.text1, R.id.text1, objects);
    }

    public HintAdapter(Context theContext, List<Object> objects, int theLayoutResId) {
        super(theContext, theLayoutResId, R.id.text1, objects);
    }

    @Override
    public int getCount() {
        // don't display last item. It is used as hint.
        int count = super.getCount();
        return count > 0 ? count - 1 : count;
    }
}

Volley Request Manager – Lite

Header

Article source on github
Full source code on github

In my Volley Request Manager article I’ve described model that covers a lot of stuff. But in practice you need only half of it or even less. So I’ve decided to create this version, that is :

  • Reusable
  • So simple as possible
  • With possibility to include HTTP Client and Image Loader or only one of them

Description

Library consists of two simple packages: http and utils. They are independent.

http contains only two independent classes :

  • ImageManager – for image loading
  • RequestManager – for requests processing

utils contains classes that will help you to create your own Requests, Queues or ImageLoaders. You can use them if you wish

Usage

Include library or just copy component that you need from http package in your project.

// init component for request processing
RequestManager.initializeWith(getApplicationContext());

// create request
Request request = new JsonObjectRequest(
        Request.Method.GET,
        "request url here",
        null,
        mListener,
        mErrorListener);

// process request with default queue      
RequestManager.queue().add(request);
// init component for image loading
ImageManager.initializeWith(getApplicationContext());

// load image with default ImageLoader
ImageManager.loader().get(
        "http://farm6.staticflickr.com/5475/10375875123_75ce3080c6_b.jpg",
        mImageListener);

// load image with NetworkImageView
NetworkImageView view = new NetworkImageView(context);

view.setImageUrl(
        "http://farm6.staticflickr.com/5475/10375875123_75ce3080c6_b.jpg",
        ImageManager.loader()); // to use default ImageLoader

Tips

  • Create HttpUtils class to hold all your url, headers creation methods in one place
public class HttpUtils {

    public static String createTestUrl() {
        Uri.Builder uri = new Uri.Builder();
        uri.scheme("http");
        uri.authority("httpbin.org");
        uri.path("get");
        uri.appendQueryParameter("name", "Jon Doe");
        uri.appendQueryParameter("age", "21");

        return uri.build().toString();
    }
}
  • Create HttpFactory class to hold all your reusable requests
public class HttpFactory {

    public static void createTestRequest(Response.Listener<JSONObject> listener,
            Response.ErrorListener errorListener) {
        Request request = new JsonObjectRequest(
                Request.Method.GET,
                HttpUtils.createTestUrl(),
                null,
                listener,
                errorListener);
        RequestManager.queue().add(request);
    }

    public static void loadImageWithDefaultStub(String url, NetworkImageView view) {
        view.setDefaultImageResId(R.drawable.ic_launcher);
        view.setImageUrl(
                url,
                ImageManager.loader());
    }
}