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();
        }
    }
}

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;
    }
}