AppUtils.java
package net.zer0bandwidth.android.lib.app;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.res.Configuration;
import android.os.Build;
import android.support.v4.text.TextUtilsCompat;
import android.support.v4.view.ViewCompat;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import net.zer0bandwidth.android.lib.R;
import java.util.Locale;
/**
* Provides utilities for dealing with common Android app tasks.
* @since zer0bandwidth-net/android 0.0.1 (#5), moved in 0.0.4 (#13)
*/
@SuppressWarnings("unused") // This is a library.
public class AppUtils
{
/** A logging tag. */
public static final String LOG_TAG = AppUtils.class.getSimpleName() ;
/**
* Replacement text to be used if the app's version name can't be found.
* In version 0.2.1 of this library, the value changed from an
* empty string to {@code "(unknown)"}.
* @see #getAppVersion
* @see #getAppNameAndVersion
*/
public static final String APP_VERSION_NOT_FOUND = "(unknown)" ;
/**
* Returns the string containing the app's current version.
* @param ctx a context in which to fetch a package manager
* @return the app version as a string, or an empty string if not found
*/
public static String getAppVersion( Context ctx )
{
try
{
PackageInfo info = ctx.getPackageManager().getPackageInfo(
ctx.getPackageName(), 0 ) ;
return ( info.versionName == null ?
APP_VERSION_NOT_FOUND : info.versionName ) ;
}
catch( Exception x )
{
Log.w( LOG_TAG, "Can't discover app version.", x ) ;
return APP_VERSION_NOT_FOUND ;
}
}
/**
* Returns a string containing the app's name and version (if found).
* @param ctx a context in which to fetch resources
* @return the app's name and version, formatted as specified by the default
* string resource
* @see R.string#app_name_and_version_format
*/
public static String getAppNameAndVersion( Context ctx )
{
return AppUtils.getAppNameAndVersion( ctx,
R.string.app_name_and_version_format ) ;
}
/**
* Returns a string containing the app's name and version (if found).
* @param ctx a context in which to fetch resources
* @param resFormat a custom string resource specifying the format in which
* the string should be rendered; must contain two string
* variables (one for the name, one for the version)
* @return the app's name and version, formatted as specified by the
* supplied string resource
*/
public static String getAppNameAndVersion( Context ctx, int resFormat )
{
final String sAppName = ctx.getString( R.string.app_name ) ;
final String sAppVersion = AppUtils.getAppVersion(ctx) ;
if( ! sAppVersion.equals( APP_VERSION_NOT_FOUND ) )
return ctx.getString( resFormat, sAppName, sAppVersion ) ;
else
return sAppName ;
}
/**
* Initializes a back button for the activity.
*
* Call this method in your activity's {@link Activity#onCreate onCreate}
* method. Then, in the activity's
* {@link Activity#onOptionsItemSelected onOptionsItemSelected} method,
* remember to set a handler for this button.
*
* <pre>
* public boolean onOptionsItemSelected( MenuItem item )
* {
* switch( item.getItemId() )
* {
* case android.R.id.home:
* this.onBackPressed() ;
* break ;
* // (other items...)
* }
* }
* </pre>
*
* @param act an activity.
* @see <a href="http://stackoverflow.com/a/33041114">StackOverflow answer #33041114</a>
*/
@SuppressWarnings("ConstantConditions") // Exception is caught.
public static void initBackButtonForActivity( Activity act )
{
try { act.getActionBar().setDisplayHomeAsUpEnabled(true) ; }
catch( Exception x )
{ Log.d( LOG_TAG, "Could not initialize back button.", x ) ; }
}
/**
* Initializes a back button for the activity.
*
* Call this method in your activity's
* {@link AppCompatActivity#onCreate onCreate} method. Then, in the activity's
* {@link AppCompatActivity#onOptionsItemSelected onOptionsItemSelected}
* method, remember to set a handler for this button.
*
* <pre>
* public boolean onOptionsItemSelected( MenuItem item )
* {
* switch( item.getItemId() )
* {
* case android.R.id.home:
* this.onBackPressed() ;
* break ;
* // (other items...)
* }
* }
* </pre>
*
* @param act an activity.
* @see <a href="http://stackoverflow.com/a/33041114">StackOverflow answer #33041114</a>
*/
@SuppressWarnings("ConstantConditions") // Exception is caught.
public static void initBackButtonForActivity( AppCompatActivity act )
{
try { act.getSupportActionBar().setDisplayHomeAsUpEnabled(true) ; }
catch( Exception x )
{ Log.d( LOG_TAG, "Could not initialize back button.", x ) ; }
}
/**
* (in AppCompat context) Determines whether the current text layout is
* right-to-left.
* @param ctx the context in which to evaluate text directionality
* @return {@code true} if text layout is right-to-left
* @see TextUtilsCompat#getLayoutDirectionFromLocale
* @see <a href="http://stackoverflow.com/a/14389640">Stack Overflow answer #14389640</a>
* @see <a href="http://stackoverflow.com/a/23203698">Stack Overflow answer #23203698</a>
* @since zer0bandwidth-net/android 0.0.2 (#8)
*/
public static boolean isTextCompatRTL( Context ctx )
{
Configuration cfg = ctx.getResources().getConfiguration() ;
if( Build.VERSION.SDK_INT < 17 )
return legacyIsTextRTL(cfg) ;
Locale loc ;
if( Build.VERSION.SDK_INT < 24 )
{ // The `locale` property was deprecated in API 24.
//noinspection deprecation
loc = cfg.locale ;
}
else
loc = cfg.getLocales().get(0) ;
int nLayoutDirection =
TextUtilsCompat.getLayoutDirectionFromLocale(loc) ;
return ( nLayoutDirection != ViewCompat.LAYOUT_DIRECTION_LTR ) ;
}
/**
* (in non-compat context) Determines whether the current text layout is
* right-to-left.
* @param ctx the context in which to evaluate text directionality
* @return {@code true} if text layout is right-to-left
* @see TextUtils#getLayoutDirectionFromLocale
* @see <a href="http://stackoverflow.com/a/14389640">Stack Overflow answer #14389640</a>
* @see <a href="http://stackoverflow.com/a/23203698">Stack Overflow answer #23203698</a>
* @since zer0bandwidth-net/android 0.0.2 (#8)
*/
public static boolean isTextRTL( Context ctx )
{
Configuration cfg = ctx.getResources().getConfiguration() ;
if( Build.VERSION.SDK_INT < 17 )
return legacyIsTextRTL(cfg) ;
Locale loc ;
if( Build.VERSION.SDK_INT < 24 )
{ // The `locale` property was deprecated in API 24.
//noinspection deprecation
loc = cfg.locale ;
}
else
loc = cfg.getLocales().get(0) ;
int nLayoutDirection = TextUtils.getLayoutDirectionFromLocale(loc) ;
return ( nLayoutDirection != View.LAYOUT_DIRECTION_LTR ) ;
}
/**
* (prior to API 17) Determines whether the current text layout is
* right-to-left.
* Consumed by {@link #isTextRTL} and {@link #isTextCompatRTL}.
* @param cfg the current context's configuration
* @return {@code true} if text layout is right-to-left
* @see <a href="http://stackoverflow.com/a/23203698">Stack Overflow answer #23203698</a>
* @since zer0bandwidth-net/android 0.1.5 (#32)
*/
@SuppressWarnings( "deprecation" ) // locale deprecated only after API 24
protected static boolean legacyIsTextRTL( Configuration cfg )
{
int nDir = Character.getDirectionality(
cfg.locale.getDisplayName().charAt(0) ) ;
return( nDir == Character.DIRECTIONALITY_RIGHT_TO_LEFT
|| nDir == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC ) ;
}
}