Sunday, March 28, 2021

SparceBooleanArrays

 SparseBooleanArrays map integers to booleans which basically means that it's like a map with Integer as a key and a boolean as value (Map).

However it's more efficient to use in this particular case It is intended to be more efficient than using a HashMap to map Integers to Booleans

Java NIO FileChannel

 A Java NIO FileChannel is a channel that is connected to a file. Using a file channel you can read data from a file, and write data to a file. The Java NIO FileChannel class is NIO's an alternative to reading files with the standard Java IO API.

FileChannel cannot be set into non-blocking mode. It always runs in blocking mode.

Opening a FileChannel

Before you can use a FileChannel you must open it. You cannot open a FileChannel directly. You need to obtain a FileChannel via an InputStream, OutputStream, or a RandomAccessFile. Here is how you open a FileChannel via a RandomAccessFile:

RandomAccessFile aFile     = new RandomAccessFile("data/nio-data.txt", "rw");
FileChannel      inChannel = aFile.getChannel();

Reading Data from a FileChannel

To read data from a FileChannel you call one of the read() methods. Here is an example:

ByteBuffer buf = ByteBuffer.allocate(48);

int bytesRead = inChannel.read(buf);

First a Buffer is allocated. The data read from the FileChannel is read into the Buffer.

Second the FileChannel.read() method is called. This method reads data from the FileChannel into the Buffer. The int returned by the read() method tells how many bytes were written into the Buffer. If -1 is returned, the end-of-file is reached.

Writing Data to a FileChannel

Writing data to a FileChannel is done using the FileChannel.write() method, which takes a Buffer as parameter. Here is an example:

String newData = "New String to write to file..." + System.currentTimeMillis();

ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();
buf.put(newData.getBytes());

buf.flip();

while(buf.hasRemaining()) {
    channel.write(buf);
}

Notice how the FileChannel.write() method is called inside a while-loop. There is no guarantee of how many bytes the write() method writes to the FileChannel. Therefore we repeat the write() call until the Buffer has no further bytes to write.

Closing a FileChannel

When you are done using a FileChannel you must close it. Here is how that is done:

channel.close();    

FileChannel Position

When reading or writing to a FileChannel you do so at a specific position. You can obtain the current position of the FileChannel object by calling the position() method.

You can also set the position of the FileChannel by calling the position(long pos) method.

Here are two examples:

long pos channel.position();

channel.position(pos +123);

If you set the position after the end of the file, and try to read from the channel, you will get -1 - the end-of-file marker.

If you set the position after the end of the file, and write to the channel, the file will be expanded to fit the position and written data. This may result in a "file hole", where the physical file on the disk has gaps in the written data.

FileChannel Size

The size() method of the FileChannel object returns the file size of the file the channel is connected to. Here is a simple example:

long fileSize = channel.size();    

FileChannel Truncate

You can truncate a file by calling the FileChannel.truncate() method. When you truncate a file, you cut it off at a given length. Here is an example:

channel.truncate(1024);

This example truncates the file at 1024 bytes in length.

FileChannel Force

The FileChannel.force() method flushes all unwritten data from the channel to the disk. An operating system may cache data in memory for performance reasons, so you are not guaranteed that data written to the channel is actually written to disk, until you call the force() method.

The force() method takes a boolean as parameter, telling whether the file meta data (permission etc.) should be flushed too.

Here is an example which flushes both data and meta data:

channel.force(true);

Adding Files to Android’s Media Library Using the MediaScanner

 When you add files to Android's filesystem these files are not picked up by the MedaScanner automatically. But often they should be.

As far as I can tell from a cursory search through Android's codebase, Android runs a full media scan only on reboot and when (re)mounting the sd card. This might sound bad at first - but think about it. A full scan is taking quite some time and you do not want this to happen arbitrarily, maybe even while the user is using his device heavily.

This means, any file, that must be available in the media library instantaneously, has to be added by you. You have the responsibility to get this right. Even more so, as new devices might support MTP, which means that all files - not only media files - have to made known. Read more about this in the section "Not only relevant for media files".

If you want your files to be added to the media library, you can do so either by using the MediaStore content provider, or by using the MediaScanner. In this post I deal with the MediaScanner only. In a later post I will cover how to use the MediaStore provider for making your files known.

Adding the files using a broadcast

The simplest way to do so is by sending a broadcast:

Intent intent = 
      new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
intent.setData(Uri.fromFile(file));
sendBroadcast(intent);

This works perfectly well and you should be fine with this solution most of the time.

But if you want to be more in control of what is happening, you could use one of the following two methods.

Using the static scanFile() method

If you simply need to know when the files have been added, you could use MediaScannerConnection's static method scanFile() together with a MediaScannerConnection.OnScanCompletedListener.

The static method scanFile() is badly named, as it actually takes an array of paths and thus can be used to add multiple files at once and not just one - but it nevertheless does what we want 🙂

Here's how to use this method:

MediaScannerConnection.scanFile(
      getApplicationContext(), 
      new String[]{file.getAbsolutePath()}, 
      null, 
      new OnScanCompletedListener() {
         @Override
         public void onScanCompleted(String path, Uri uri) {
            Log.v("grokkingandroid", 
                  "file " + path + " was scanned seccessfully: " + uri);
         }
      });
Parameters for the static scanFile() method
ParameterUse
contextThe application context
pathsString array containing the paths of the files you want to add
mimeTypesString array containing the mime types of the files
callbackMediaScannerConnection.OnScanCompletedListener to be notified when the scan is completed

The OnScanCompletedListener itself must implement the onScanCompleted() method. This method gets the filename and the URI for the MediaStore.Files provider passed in as parameters.

Creating an instance of MediaScannerConnection

This is the most complex way to make your files known. But it gives you even more control. You need to implement the MediaScannerConnection.MediaScannerConnectionClient for callbacks.

Your MediaScannerConnectionClient's implementation is not only called whenever a scan has been completed, but also as soon as the connection has been established. Since this might take a little while, as described in the next section, you might be interested in this callback. The way the API is constructed, you actually need to use this callback method to start the scan.

The following snippet shows you a sample implementation of the MediaScannerConnectionClient interface.

final class MyMediaScannerConnectionClient 
      implements MediaScannerConnectionClient {

   private String mFilename;
   private String mMimetype;
   private MediaScannerConnection mConn;
   
   public MyMediaScannerConnectionClient
         (Context ctx, File file, String mimetype) {
      this.mFilename = file.getAbsolutePath();
      mConn = new MediaScannerConnection(ctx, this);
      mConn.connect();
   }
   @Override
   public void onMediaScannerConnected() {
      mConn.scanFile(mFilename, mMimetype);
   }
   
   @Override
   public void onScanCompleted(String path, Uri uri) {
      mConn.disconnect();
   }    	
}

In this implementation I create the MediaScannerConnection within the constructor and here I also call its connect() method.

Note also that I start the scan within the onMediaScannerConnected() callback method.

This way the usage of this interface is as simple as it can get:

MediaScannerConnectionClient client = 
      new MyMediaScannerConnectionClient(
            getApplicationContext(), file, null);

Establishing a connection might take a while

Be aware that the connection is not established right away. That's why the following snippet would cause trouble (that's the first time I noticed problematic code in Mark Murphy's otherwise highly recommended book):

/////// Do not do this ! ///////
MediaScannerConnection c = 
      new MediaScannerConnection(
            getApplicationContext(), null);
c.connect();
c.scanFile(file.getAbsolutePath(), null);
c.disconnect();
/////// Do not do this ! ///////

This snippet would fail with an IllegalStateException:

java.lang.IllegalStateException: not connected to MediaScannerService

When doing some tests it took on my Optimus One (with an sd card) and on my Galaxy Nexus (with an extra partition) about 20 to 40 milliseconds to establish the connection - on my Archos tablet (also extra partition) about 100 milliseconds.

The scan itself takes even longer - which took me by surprise. On my Archos and Optimus One about 200 milliseconds - on my Nexus even up to 900 (!) milliseconds on its worst run.

Not only relevant for media files

With devices that use MTP instead of the traditional USB storage protocol, the MediaScanner is also used to make arbitrary files accessible via MTP. Thus any files and folders that you do not add to the MediaScanner are invisible between full scans if you plug in your device to your computer!

With the introduction of Honeycomb Google has started to push for MTP. Even if not all handset makers follow Google's decision, some might - and Google's own Nexus line definitely does so.

This means that you should use the MediaScanner also for any files that the user might want to download to his computer. It could be anything, e.g. CSV backup files, PDF files and so on. If the user might want to use them on a traditional computer, you have to make these files known using the methods described above.

Happy coding!

https://www.grokkingandroid.com/adding-files-to-androids-media-library-using-the-mediascanner/

Thursday, March 18, 2021

Android Coordinator Layout-2

 Up until now we’ve used android CoordinatorLayout in plenty of our tutorials. Yet we haven’t gone into it’s details. In this tutorial we’ll discuss and customise the CoordinatorLayout in android app.


Android CoordinatorLayout is a super-powered FrameLayout. It has a lot more to offer than it seems. It has additional level of control over it’s child views. It coordinates the animations and transitions of child views with one another.Android CoordinatorLayout

Let’s create a new Android Studio project and choose the Basic Activity template that has CoordinatorLayout by default. The layout consists of a Floating Action Button. Clicking it displays a SnackBar as shown below.

android coordinatorlayout default behaviour

Did you notice that the Floating Action Button animates up to make way for the SnackBar and comes back when the SnackBar disappear?

This is no magic. It’s how the Floating Action Button behaves inside a CoordinatorLayout.

Note : CoordinatorLayout can also expand the ToolBar to show more content or collapse it while scrolling, something that is commonly seen when you scroll a WhatsApp User’s profile screen. Don’t worry, we’ll look into this in a later tutorial.

A question that would be popping up in our heads now – How does the CoordinatorLayout know what to do with the child view? The answer lies in the next section.

CoordinatorLayout Behaviors

The FAB within a CoordinatorLayout has been specified a default Behavior that causes it to animate accordingly when another view interacts with it.

Do a Ctrl/CMD+ Click on the FloatingActionButton in the layout/activity and you shall see a Behavior has been defined on the class with an annotation.
It should look like this:


@CoordinatorLayout.DefaultBehavior(FloatingActionButton.Behavior.class)

FloatingActionButton.Behavior is the default Behavior class used on the FAB.
We can define our own Behaviors by extending the class CoordinatorLayout.Behavior.

Here T is the class whose Behavior we wish to define. In the above case it is CoordinatorLayout.Behavior.

  • The Behaviors only work on the direct child of the CoordinatorLayout.
  • It’s necessary for the CoordinatorLayout to be the root layout of the activity

Now let’s add a Button widget at the bottom of our screen.
activity_main.xml


<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:app="https://schemas.android.com/apk/res-auto"
    xmlns:tools="https://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:context="com.journaldev.coordinatorlayoutbehaviours.MainActivity">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay" />

    </android.support.design.widget.AppBarLayout>

    <include layout="@layout/content_main" />

    <!--<android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_margin="@dimen/fab_margin"
        android:src="@android:drawable/ic_dialog_email" />-->

    <android.support.v7.widget.AppCompatButton
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|start"
        android:text="CLICK ME"
        android:id="@+id/button"
        android:layout_margin="@dimen/fab_margin"
        android:layout_width="match_parent"/>

</android.support.design.widget.CoordinatorLayout>

We’ve commented out the FAB from the above layout. Now replace the FloatingActionButton listener with the AppCompatButton in the MainActivity.java as shown below.


AppCompatButton fab = (AppCompatButton) findViewById(R.id.button);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Snackbar.make(view, "Hey Button. Define a Custom Behaviour. Else I'll take your space", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });

This is how the application looks now. Any guesses?
android coordinator layout button default behaviour

To define the custom Behavior we need to be aware of two important elements:

  • child : It’s the view on which the behavior would be performed.
  • dependency : It’s the view which will trigger the behavior on the child

In the above case the AppCompatButton is the child and the SnackBar is the dependency.

Note: For the FloatingActionButton default behavior, the dependency is not just the SnackBar. There are other View elements too that trigger a behavior on the FloatingActionButton.

Let’s start by creating our own custom Behavior class that moves up the AppCompatButton. Let’s name it CustomMoveUpBehavior.java.

public class CustomMoveUpBehavior extends CoordinatorLayout.Behavior {

  public CustomMoveUpBehavior(Context context, AttributeSet attrs) {
      super(context, attrs);
  }

}

The two neccessary methods that should be overridden in the above class are layoutDependsOn and onDependentViewChanged.
Let’s add override them in our class.


package com.journaldev.coordinatorlayoutbehaviours;

import android.os.Build;
import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.Snackbar;
import android.view.View;

public class CustomMoveUpBehavior extends CoordinatorLayout.Behavior {

    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
        return dependency instanceof Snackbar.SnackbarLayout;
    }

    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
        float translationY = Math.min(0, dependency.getTranslationY() - dependency.getHeight());
        child.setTranslationY(translationY);
        return true;
    }

}

The layoutDependsOn checks whether the dependency that’ll trigger the behavior is an instanceof SnackBar.
The onDependentViewChanged is used to move up the child view(AppCompatbutton) based on a basic math calculation.

Attaching the Behavior to android CoordinatorLayout

To attach the CustomMoveUpBehavior.java we’ll create a Custom AppCompatButton and add the annotations as shown below.


package com.journaldev.coordinatorlayoutbehaviours;

import android.content.Context;
import android.support.design.widget.CoordinatorLayout;
import android.support.v7.widget.AppCompatButton;
import android.util.AttributeSet;

@CoordinatorLayout.DefaultBehavior(CustomMoveUpBehavior.class)
public class CustomButton extends AppCompatButton {
    public CustomButton(Context context) {
        super(context);
    }

    public CustomButton(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public CustomButton(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
}

Do the following changes in the activity_main.xml and MainActivity.java

Replace the AppCompatButton xml tag with the following one.


<com.journaldev.coordinatorlayoutbehaviours.CustomButton
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|start"
        android:text="CLICK ME"
        android:id="@+id/button"
        android:layout_margin="@dimen/fab_margin"
        android:layout_width="match_parent"/>

Replace the respective Button onClickListener in the MainActivity.java.


CustomButton fab = (CustomButton) findViewById(R.id.button);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Snackbar.make(view, "Hey Button. Define a Custom Behaviour. Else I'll take your space", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });

The application when run now should look like this:

android coordinatorlayout

Wasn’t it cool? Now let’s try to implement a Custom Behavior for the FAB. We’ll trigger it to rotate and move up when the SnackBar is displayed.
We’ve implemented a CustomRotateBehavior.java class. It’s given below.


public class CustomRotateBehavior extends CoordinatorLayout.Behavior {

  public CustomRotateBehavior(Context context, AttributeSet attrs) {
      super(context, attrs);
  }

@Override
    public boolean layoutDependsOn(CoordinatorLayout parent, FloatingActionButton child, View dependency) {
        return dependency instanceof Snackbar.SnackbarLayout;
    }

    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, FloatingActionButton child, View dependency) {
        float translationY = getFabTranslationYForSnackbar(parent, child);
        float percentComplete = -translationY / dependency.getHeight();
        child.setRotation(180 * percentComplete);
        child.setTranslationY(translationY);
        return false;
    }

    private float getFabTranslationYForSnackbar(CoordinatorLayout parent,
                                                FloatingActionButton fab) {
        float minOffset = 0;
        final List dependencies = parent.getDependencies(fab);
        for (int i = 0, z = dependencies.size(); i < z; i++) {
            final View view = dependencies.get(i);
            if (view instanceof Snackbar.SnackbarLayout && parent.doViewsOverlap(fab, view)) {
                minOffset = Math.min(minOffset,
                        ViewCompat.getTranslationY(view) - view.getHeight());
            }
        }

        return minOffset;
    }

}

The method getFabTranslationYForSnackbar(parent,child) calculates how far up the screen should the SnackBar come up for the FAB to start changing. Before we do the relevant changes, let’s browse through our project structure.

Android CoordinatorLayout Example Project Structure

android coordinatorlayout example

Android CoordinatorLayout Example Code

Now instead of extending the FloatingActionButton we can just define the app:layout_behavior in the FloatingActionButton view and point it to our subclass.

This is how our activity_main.xml looks now.


<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:app="https://schemas.android.com/apk/res-auto"
    xmlns:tools="https://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:context="com.journaldev.coordinatorlayoutbehaviours.MainActivity">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay" />

    </android.support.design.widget.AppBarLayout>

    <include layout="@layout/content_main" />

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_margin="@dimen/fab_margin"
        app:layout_behavior="com.journaldev.coordinatorlayoutbehaviours.CustomRotateBehavior"
        android:src="@android:drawable/arrow_down_float" />

    <!--<com.journaldev.coordinatorlayoutbehaviours.CustomButton
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|start"
        android:text="CLICK ME"
        android:id="@+id/button"
        android:layout_margin="@dimen/fab_margin"
        android:layout_width="match_parent"/>-->

</android.support.design.widget.CoordinatorLayout>

The MainActivity.java looks like this now.


package com.journaldev.coordinatorlayoutbehaviours;

import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.AppCompatButton;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        /*CustomButton fab = (CustomButton) findViewById(R.id.button);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Snackbar.make(view, "You've added the CustomMoveUpBehavior. Now I'll let you move", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });*/

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Snackbar.make(view, "Hey FAB. Please Rotate 180 degrees when I'm up.", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}

Let’s run our application one last time to see the new Behavior.

android coordinatorlayout example

This brings an end to Android CoordinatorLayout Example. We started with browsing through the default Behavior of the FAB widget and have ended up with overriding it with our own Rotation Behavior.

Not to miss out the Behavior on a Button too. It’s a long way. You can download the Android CoordinatorLayoutBehaviours Project from the below link.

Inapp update

  Inapp update https://desk.zoho.com/portal/vegabirdtech/en/kb/articles/how-to-use-burp-suite-with-android-mobile