Thursday 20 July 2017

Resolved Nought(7.0) Camera Issue (file:// Schema is not allowed to be attached with intent)

file:// is not allowed(Android N) to attach with Intent anymore or it will
throw FileUriExposedException which may cause your app crash immediately called.

Solution:

So if file:// is not allowed anymore, which approach should we go for? The answer is we should send the URI through content:// scheme instead which is the URI scheme for Content Provider. In this case, we would like to share an access to a file through our app so FileProvider is needed to be implemented.

Step - 1:
It is quite easy to implement FileProvider on your application. First you need to add a FileProvider <provider> tag in AndroidManifest.xml under <application> tag like below:

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    ...
    <application
        ...
        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="${applicationId}.provider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/provider_paths"/>
        </provider>
    </application>
</manifest>

Step: 2

And then create a provider_paths.xml file in xml folder under res folder. Folder may be needed to create if it doesn't exist.
The content of the file is shown below. It describes that we would like to share access to the External Storage at root folder (path=".") with the name external_files.
res/xml/provider_paths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="external_files" path="."/>
</paths>
Done! FileProvider is now declared and be ready to use.
Step:3
The final step is to change the line of code below in MainActivity.java
Uri photoURI = Uri.fromFile(createImageFile());
to
Uri photoURI = FileProvider.getUriForFile(MainActivity.this,
        BuildConfig.APPLICATION_ID + ".provider",
        createImageFile());
And .... done ! Your application should now work perfectly fine on any Android version including Android Nougat. Yah !

Why Nougat does not allow passing file:// with Intent anymore?

You may be curious why Android team decide to change this behavior. Actually there is a good reason behind.
If file path is sent to the target application (Camera app in this case), file will be fully accessed through the Camera app's process not the sender one.
But let's consider thoroughly, actually Camera is launched by our application to take a photo and save as a file on our app's behalf. So the access right to that file should be our app's not Camera's. Every operation did with the file should be done through our application not by Camera app itself.
And that's why file:// is now prohibited on targetSdkVersion 24 to force every developer to do this task in the proper way.

Tuesday 4 July 2017

How to integrate "Google Voice" into your android app

Google Voice | OK Google | Google Now

What is google voice action ?

This is separate from the search-by-voice functionality provided by Google Voice Search.
The concept is relatively simple: the user makes a request on Google Now ranging from “Take a picture” to “Play Thriller on myMusicApp”, and Android will open any app that is able to handle that specific request.

How google voice action work?
It recognizes your voice and converts it into the text or takes the appropriate action.
 Voice recognition feature can be achieved by RecognizerIntent.

Available Google Voice action are 
  • Set an alarm for 7 am (Alarm)
  • Set a timer for 5 minutes (Alarm)
  • Call Bosco (Communication)
  • Take a picture (Media)
  • Record a video (Media)
  • Play thriller on myMusicApp (Media)
  • Search for cat videos on MyApp (Search)
  • Open google.com (Web Browser)
How Google Voice Action Integrate in our App ?
  
      Step  1 : Update Gradle Build to Android M:
               android {

                            compileSdkVersion 23
                            buildToolsVersion "23.0.1"

                            defaultConfig {
                                                   minSdkVersion 23
                                                   targetSdkVersion 23
                           }
               }   

       
      Step 2 : Set up an Intent Filter in the Manifest to receive the voice intent 
                      from Google Now:For “Take a picture”:  

             <intent-filter>            <action android:name="android.media.action.STILL_IMAGE_CAMERA"/>            <category android:name="android.intent.category.DEFAULT"/>            <category android:name="android.intent.category.VOICE"/>           </intent-filter>


          For Searching

              <intent-filter>
              <action android:name="com.google.android.gms.actions.SEARCH_ACTION"/>
              <category android:name="android.intent.category.DEFAULT"/>
              <category android:name="android.intent.category.VOICE"/>
             </intent-filter>

       Step 3 : Check for voice interaction:

           if (!isVoiceInteractionRoot() || !isVoiceInteraction()) {               //Not a voice interaction, proceed normally                 } else {                            beginVoiceInteraction();                     }          

             Alternatively, if your voice interaction includes a search query:
            Here get text from voice as search query.

             String action = intent.getAction();             if (action.equals(Intent.ACTION_SEARCH)) {                String query = intent.getStringExtra(SearchManager.QUERY);                 handleVoiceQuery(query);             }


               
             

      - You can find more Here about google voice action.
     

Wednesday 28 June 2017

Image Utility


1. Crete local bitmap from uri 
2. Decode input stream of image and get bitmap
3. Gets the image bounds
4. Resize bitmap
5. Rotate image
6. Get Image orientation


/**
 * Image utilities
 */
public class ImageUtils {
    // Logging
    private static final String TAG = "ImageUtils";
    /** Minimum class memory class to use full-res photos */
    private final static long MIN_NORMAL_CLASS = 32;
    /** Minimum class memory class to use small photos */
    private final static long MIN_SMALL_CLASS = 24;
    private static final String BASE64_URI_PREFIX = "base64,";
    private static final Pattern BASE64_IMAGE_URI_PATTERN = Pattern.compile("^(?:.*;)?base64,.*");
    public static enum ImageSize {
        EXTRA_SMALL,
        SMALL,
        NORMAL,
    }
    public static final ImageSize sUseImageSize;
    static {
        // On HC and beyond, assume devices are more capable
        if (Build.VERSION.SDK_INT >= 11) {
            sUseImageSize = ImageSize.NORMAL;
        } else {
            if (PhotoViewActivity.sMemoryClass >= MIN_NORMAL_CLASS) {
                // We have plenty of memory; use full sized photos
                sUseImageSize = ImageSize.NORMAL;
            } else if (PhotoViewActivity.sMemoryClass >= MIN_SMALL_CLASS) {
                // We have slight less memory; use smaller sized photos
                sUseImageSize = ImageSize.SMALL;
            } else {
                // We have little memory; use very small sized photos
                sUseImageSize = ImageSize.EXTRA_SMALL;
            }
        }
    }
    /**
     * @return true if the MimeType type is image
     */
    public static boolean isImageMimeType(String mimeType) {
        return mimeType != null && mimeType.startsWith("image/");
    }


    /**
     * 01. Create a bitmap from a local URI
     *
     * @param resolver The ContentResolver
     * @param uri The local URI
     * @param maxSize The maximum size (either width or height)
     *
     * @return The new bitmap or null
     */
    public static Bitmap createLocalBitmap(ContentResolver resolver, Uri uri, int maxSize) {
        // TODO: make this method not download the image for both getImageBounds and decodeStream
        InputStream inputStream = null;
        try {
            final BitmapFactory.Options opts = new BitmapFactory.Options();
            final Point bounds = getImageBounds(resolver, uri);
            inputStream = openInputStream(resolver, uri);
            if (bounds == null || inputStream == null) {
                return null;
            }
            opts.inSampleSize = Math.max(bounds.x / maxSize, bounds.y / maxSize);
            final Bitmap decodedBitmap = decodeStream(inputStream, null, opts);
            // Correct thumbnail orientation as necessary
            // TODO: Fix rotation if it's actually a problem
            //return rotateBitmap(resolver, uri, decodedBitmap);
            return decodedBitmap;
        } catch (FileNotFoundException exception) {
            // Do nothing - the photo will appear to be missing
        } catch (IOException exception) {
            // Do nothing - the photo will appear to be missing
        } catch (IllegalArgumentException exception) {
            // Do nothing - the photo will appear to be missing
        } finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (IOException ignore) {
            }
        }
        return null;
    }
    /**
     * 02. Decode inputstream of image and get bitmap
     * Wrapper around {@link BitmapFactory#decodeStream(InputStream, Rect,
     * BitmapFactory.Options)} that returns {@code null} on {@link
     * OutOfMemoryError}.
     *
     * @param is The input stream that holds the raw data to be decoded into a
     *           bitmap.
     * @param outPadding If not null, return the padding rect for the bitmap if
     *                   it exists, otherwise set padding to [-1,-1,-1,-1]. If
     *                   no bitmap is returned (null) then padding is
     *                   unchanged.
     * @param opts null-ok; Options that control downsampling and whether the
     *             image should be completely decoded, or just is size returned.
     * @return The decoded bitmap, or null if the image data could not be
     *         decoded, or, if opts is non-null, if opts requested only the
     *         size be returned (in opts.outWidth and opts.outHeight)
     */
    public static Bitmap decodeStream(InputStream is, Rect outPadding, BitmapFactory.Options opts) {
        ByteArrayOutputStream out = null;
        InputStream byteStream = null;
        try {
            out = new ByteArrayOutputStream();
            final byte[] buffer = new byte[4096];
            int n = is.read(buffer);
            while (n >= 0) {
                out.write(buffer, 0, n);
                n = is.read(buffer);
            }
            final byte[] bitmapBytes = out.toByteArray();
            // Determine the orientation for this image
            final int orientation = Exif.getOrientation(bitmapBytes);
            // Create an InputStream from this byte array
            byteStream = new ByteArrayInputStream(bitmapBytes);
            final Bitmap originalBitmap = BitmapFactory.decodeStream(byteStream, outPadding, opts);
            if (originalBitmap != null && orientation != 0) {
                final Matrix matrix = new Matrix();
                matrix.postRotate(orientation);
                return Bitmap.createBitmap(originalBitmap, 0, 0, originalBitmap.getWidth(),
                        originalBitmap.getHeight(), matrix, true);
            }
            return originalBitmap;
        } catch (OutOfMemoryError oome) {
            Log.e(TAG, "ImageUtils#decodeStream(InputStream, Rect, Options) threw an OOME", oome);
            return null;
        } catch (IOException ioe) {
            Log.e(TAG, "ImageUtils#decodeStream(InputStream, Rect, Options) threw an IOE", ioe);
            return null;
        } finally {
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                    // Do nothing
                }
            }
            if (byteStream != null) {
                try {
                    byteStream.close();
                } catch (IOException e) {
                    // Do nothing
                }
            }
        }
    }
    /**
     * 03. Gets the image bounds
     *
     * @param resolver The ContentResolver
     * @param uri The uri
     *
     * @return The image bounds
     */
    private static Point getImageBounds(ContentResolver resolver, Uri uri)
            throws IOException {
        final BitmapFactory.Options opts = new BitmapFactory.Options();
        InputStream inputStream = null;
        String scheme = uri.getScheme();
        try {
            opts.inJustDecodeBounds = true;
            inputStream = openInputStream(resolver, uri);
            if (inputStream == null) {
                return null;
            }
            decodeStream(inputStream, null, opts);
            return new Point(opts.outWidth, opts.outHeight);
        } finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (IOException ignore) {
            }
        }
    }
    private static InputStream openInputStream(ContentResolver resolver, Uri uri) throws
            FileNotFoundException {
        String scheme = uri.getScheme();
        if ("http".equals(scheme) || "https".equals(scheme)) {
            try {
                return new URL(uri.toString()).openStream();
            } catch (MalformedURLException e) {
                // Fall-back to the previous behaviour, just in case
                Log.w(TAG, "Could not convert the uri to url: " + uri.toString());
                return resolver.openInputStream(uri);
            } catch (IOException e) {
                Log.w(TAG, "Could not open input stream for uri: " + uri.toString());
                return null;
            }
        } else if ("data".equals(scheme)) {
            byte[] data = parseDataUri(uri);
            if (data != null) {
                return new ByteArrayInputStream(data);
            }
        }
        return resolver.openInputStream(uri);
    }
    private static byte[] parseDataUri(Uri uri) {
        String ssp = uri.getSchemeSpecificPart();
        try {
            if (ssp.startsWith(BASE64_URI_PREFIX)) {
                String base64 = ssp.substring(BASE64_URI_PREFIX.length());
                return Base64.decode(base64, Base64.URL_SAFE);
            } else if (BASE64_IMAGE_URI_PATTERN.matcher(ssp).matches()){
                String base64 = ssp.substring(
                        ssp.indexOf(BASE64_URI_PREFIX) + BASE64_URI_PREFIX.length());
                return Base64.decode(base64, Base64.DEFAULT);
            } else {
                return null;
            }
        } catch (IllegalArgumentException ex) {
            Log.e(TAG, "Mailformed data URI: " + ex);
            return null;
        }
    }
}

========================
4. Resize bitmap

public static Bitmap resizeBitmap(Bitmap org, int destWidth, int destHeight) {


Bitmap blankResized = Bitmap.createBitmap(destWidth, destHeight, Bitmap.Config.ARGB_8888);


int xPadding = 0;
int yPadding = 0;
int scaledWidth = blankResized.getWidth();
int scaledHeight = blankResized.getHeight();
float scale = 1;
if (org.getWidth() > org.getHeight()) {

scale = (float) blankResized.getWidth() / (float) org.getWidth();
scaledHeight = (int) (org.getHeight() * scale);
yPadding = (blankResized.getHeight() - scaledHeight) / 2;
}
else {

scale = (float) blankResized.getHeight() / (float) org.getHeight();
scaledWidth = (int) (org.getWidth() * scale);
xPadding = (blankResized.getWidth() - scaledWidth) / 2;
}

Bitmap orgScale = Bitmap.createScaledBitmap(org, scaledWidth, scaledHeight, true);
Canvas canvas = new Canvas(blankResized);
canvas.drawBitmap(
orgScale,
new Rect(0, 0, scaledWidth, scaledHeight),
new Rect(xPadding, yPadding, xPadding + scaledWidth, yPadding + scaledHeight),
null);

return blankResized;
}

====================================
5. Rotate image

public static Bitmap rotateImage(final Bitmap bitmap, final File fileWithExifInfo) {
Bitmap rotatedBitmap = bitmap;
int orientation = 0;
try {
orientation = getImageOrientation(fileWithExifInfo.getAbsolutePath());
if (orientation != 0) {
final Matrix matrix = new Matrix();
matrix.postRotate(orientation);
rotatedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
bitmap.getHeight(), matrix, true);
bitmap.recycle();
}
} catch (final IOException e) {
}
return rotatedBitmap;
}

==================================
6. Get image orientation

public static int getImageOrientation(final String file) throws IOException {
final ExifInterface exif = new ExifInterface(file);
final int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1);
switch (orientation) {
case ExifInterface.ORIENTATION_NORMAL:
return 0;
case ExifInterface.ORIENTATION_ROTATE_90:
return 90;
case ExifInterface.ORIENTATION_ROTATE_180:
return 180;
case ExifInterface.ORIENTATION_ROTATE_270:
return 270;
default:
return 0;
}

}

Tuesday 13 June 2017

Network Utility

Peace of code for network connectivity.

Here is the static method for network related operation like
   

  • Check network availability
  • Get network information
  • Connected to WiFi
  • Connected to mobile
  • Check connection speed like connectivity is fast, slow or medium


You can add below static method in your own util class.


/**
* Get the network info
* @param context
* @return
*/
public static NetworkInfo getNetworkInfo(Context context){
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
return cm.getActiveNetworkInfo();
}
/**
* Check if there is any connectivity
* @param context
* @return
*/
public static boolean isConnected(Context context){
NetworkInfo info = Connectivity.getNetworkInfo(context);
return (info != null && info.isConnected());
}
/**
* Check if there is any connectivity to a Wifi network
* @param context
* @param type
* @return
*/
public static boolean isConnectedWifi(Context context){
NetworkInfo info = Connectivity.getNetworkInfo(context);
return (info != null && info.isConnected() && info.getType() == ConnectivityManager.TYPE_WIFI);
}
/**
* Check if there is any connectivity to a mobile network
* @param context
* @param type
* @return
*/
public static boolean isConnectedMobile(Context context){
NetworkInfo info = Connectivity.getNetworkInfo(context);
return (info != null && info.isConnected() && info.getType() == ConnectivityManager.TYPE_MOBILE);
}
/**
* Check if there is fast connectivity
* @param context
* @return
*/
public static boolean isConnectedFast(Context context){
NetworkInfo info = Connectivity.getNetworkInfo(context);
return (info != null && info.isConnected() && Connectivity.isConnectionFast(info.getType(),info.getSubtype()));
}
/**
* Check if the connection is fast
* @param type
* @param subType
* @return
*/
public static boolean isConnectionFast(int type, int subType){
if(type==ConnectivityManager.TYPE_WIFI){
return true;
}else if(type==ConnectivityManager.TYPE_MOBILE){
switch(subType){
case TelephonyManager.NETWORK_TYPE_1xRTT:
return false; // ~ 50-100 kbps
case TelephonyManager.NETWORK_TYPE_CDMA:
return false; // ~ 14-64 kbps
case TelephonyManager.NETWORK_TYPE_EDGE:
return false; // ~ 50-100 kbps
case TelephonyManager.NETWORK_TYPE_EVDO_0:
return true; // ~ 400-1000 kbps
case TelephonyManager.NETWORK_TYPE_EVDO_A:
return true; // ~ 600-1400 kbps
case TelephonyManager.NETWORK_TYPE_GPRS:
return false; // ~ 100 kbps
case TelephonyManager.NETWORK_TYPE_HSDPA:
return true; // ~ 2-14 Mbps
case TelephonyManager.NETWORK_TYPE_HSPA:
return true; // ~ 700-1700 kbps
case TelephonyManager.NETWORK_TYPE_HSUPA:
return true; // ~ 1-23 Mbps
case TelephonyManager.NETWORK_TYPE_UMTS:
return true; // ~ 400-7000 kbps
/*
* Above API level 7, make sure to set android:targetSdkVersion
* to appropriate level to use these
*/
case TelephonyManager.NETWORK_TYPE_EHRPD: // API level 11
return true; // ~ 1-2 Mbps
case TelephonyManager.NETWORK_TYPE_EVDO_B: // API level 9
return true; // ~ 5 Mbps
case TelephonyManager.NETWORK_TYPE_HSPAP: // API level 13
return true; // ~ 10-20 Mbps
case TelephonyManager.NETWORK_TYPE_IDEN: // API level 8
return false; // ~25 kbps
case TelephonyManager.NETWORK_TYPE_LTE: // API level 11
return true; // ~ 10+ Mbps
// Unknown
case TelephonyManager.NETWORK_TYPE_UNKNOWN:
default:
return false;
}
}else{
return false;
}
}