Sunday, April 5, 2015

How to create document centric apps with concurrent tasks on Android Lollipop

http://xmodulo.com/document-centric-apps-concurrent-tasks-android.html

Android L introduced a great new feature: the ability for a single app to have multiple tasks available on the 'Recent Apps' list. This is called Document Centric apps (Concurrent Tasks). In fact, this feature is a rather significant change in multitasking model in Android L, where focus has been shifted from traditional app-centric multitasking to document-centric multitasking.

This easily makes sense for some apps. A prime example is web browsers with tabs. Each tab can be a task, and the user switches tabs the same way he switches apps. In addition, apps where the user has to switch back to a central activity (such as shopping apps) can benefit from this. The checkout activity can be a task of its own, or the user can open multiple shopping items and switch between them till finally making a choice.

In this tutorial, we discuss how to do this correctly with the Android L APIs. Note that the techniques discussed here apply to Android Lollipop (API 21) devices only.

Preparation

With Android Lollipop the default software buttons are now back, home and recent tasks. Obviously, this is not just an aesthetic design choice. With the new ability of a single app to spurn multiple tasks, the Recent Apps list should be more frequently used.

Another new, but highly overlooked, Lollipop feature is the ability for apps to be persisted across device reboots. Previously, after a reboot, all apps are cleared from the Recent Apps list. But with Lollipop, after a reboot, all the apps that were previously opened remain opened, and available via the Recent Apps list. Android L also gives app developers power to determine if and how their apps (and concurrent tasks spurned) should be available on reboot.

Note that if your app targets older versions, be sure to add the standard check for device version around all of the code in this tutorial as follows.
1
2
3
4
5
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP){
    // Lollipop and above devices
} else{
    // Devices before Lollipop
}

Implementation

Taking advantage of concurrent tasks is actually pretty straightforward. You add an API 21 flag (Intent.FLAG_ACTIVITY_NEW_DOCUMENT) to the new Activity Intent. Calling startActivity() on an Intent with this flag starts the target activity in a task of its own.

1
2
3
Intent intent = new Intent(this, ActivityB.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
startActivity(intent);

With the above code, ActivityB will be started in a task of its own, available via the Recent Apps list. You can switch between ActivityA and ActivityB by tapping the Recent Apps button. The issue with the above code is that there can only be one instance of ActivityB. Triggering the startActivity() function again will re-open the same running ActivityB instance.



To run multiple instances of ActivityB is not much difficult though. We simply add another API 21 flag (Intent.FLAG_ACTIVITY_MULTIPLE_TASK)

1
2
3
4
Intent intent = new Intent(this, ActivityB.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
startActivity(intent);

Voila! Multiple ActivityB instances can now run at the same time. This then introduces a new issue. How do you keep track of the tasks opened? How do you know how many ActivityB instances are running? And if ActivityA also opens an ActivityC with both NEW_DOCUMENT and MULTIPLE_TASK flags, how do you manage them all?

A method of keeping track of opened tasks and activities is to use Intent's putExtra() method which puts an identifier in each opened task.
1
2
3
4
5
Intent intent = new Intent(this, ActivityB.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
intent.putExtra(KEY_EXTRA_NEW_DOCUMENT_COUNTER, ++NEW_DOCUMENT_COUNTER);
startActivity(intent);

To get a list of tasks, we get the System's ActivityManager instance, and query getAppTasks(). This returns a list of all tasks associated with the current activity. In the snippet below, we get our app's task list, query each task for its base Intent, and identify the Activity using the getIntExtra() with KEY_EXTRA_NEW_DOCUMENT_COUNTER.

1
2
3
4
5
6
7
8
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
List myTasks = manager.getAppTasks();
 
int counter = 1;
for(ActivityManager.AppTask task : myTasks) {
    Intent baseIntent = task.getTaskInfo().baseIntent;
    Log.e("TASK " + counter++, baseIntent.getIntExtra(KEY_EXTRA_NEW_DOCUMENT_COUNTER, -1) + "");
}

Since your app tasks are now scattered among all tasks from all other apps, it would be nice, for users, if they can close other tasks from the root task, and jump back to the root task with a single tap.

Thankfully, the APIs contain methods for that.

For example, to finish a particular task, given that we track the tasks with KEY_EXTRA_NEW_DOCUMENT_COUNTER:

1
2
3
4
5
6
7
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
List myTasks = manager.getAppTasks();
for(ActivityManager.AppTask task : myTasks) {
    Intent baseIntent = task.getTaskInfo().baseIntent;
    if(baseIntent.getIntExtra(KEY_EXTRA_NEW_DOCUMENT_COUNTER, -1) == 3)
          task.finishAndRemoveTask();
}

The preferred and correct way to end a task is by calling finishAndRemoveTask(). Activity also has a similarly named finishAndRemoveTask() method (introduced in API 21), so, the correct way to close an Activity in a new task is to call finishAndRemoveTask(), rather than just finish().

To jump back to the root activity, make sure to add the flag (Intent.FLAG_ACTIVITY_NEW_DOCUMENT) before the startActivity() method. Recall that doing this simply switches to an already opened/running task for that activity.

Persist on Reboot

To persist your tasks across reboots, add this to your activity in the AndroidManifest.xml file:

1
android:persistableMode="persistAcrossReboots"

The persistableMode attribute was added in API 21 (http://developer.android.com/reference/android/R.attr.html#persistableMode). This attribute allows three possible states:
  • persistRootOnly - The default, and it persists only the root activity/task. The root activity/task will not be passed a PersistableBundle to store its state.
  • persistNever - This state indicates that the task/activity should not be persisted.
  • persistAcrossReboots - Activities with this attribute will be provided a PersistableBundle in the new, overloaded, onSaveInstanceState() method. This PersistableBundle will then be provided back to the Activity in its onPostCreate() method.
To save state using a PersistableBundle, override the following method.

1
2
3
4
5
@Override
    public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
        // Save data to PersistableBundle
        super.onSaveInstanceState(outState, outPersistentState);
    }

Whereas, to restore state after a reboot, override onPostCreate()

1
2
3
4
5
6
7
8
@Override
    public void onPostCreate(Bundle savedInstanceState, PersistableBundle persistentState) {
        super.onPostCreate(savedInstanceState, persistentState);
        // Restore state from PersistableBundle
        if (persistentState != null) {
            // Read relevant data
        }
    }

Conclusion

While these new APIs provide some possibly exciting uses, there is the potential for misuse, especially with Recent Apps persisting across reboots. Bear in mind that having your app dominating a user's Recent Apps list is possibly going to be annoying. Consider implementing an obvious way for the user to see all tasks for your app, and to selectively close some. Also consider implementing a one touch method to switch to your root task from all other tasks.

As usual, all tips discussed above are implemented in a sample app, and the entire source code is available on Github. Feel free to use and modify as required.

Wednesday, April 1, 2015

How to enable User and Group Disk Quota on CentOS 7 & RHEL 7

http://www.linuxtechi.com/enable-user-group-disk-quota-on-centos-7-rhel-7

As a Linux system admin we generally face low disk space issues. By implementing the user and group disk quota on the file system we can resolve the space issue.
Quota restricts the users to use only allowed disk and inodes on the particular file system. In this post we will discuss how to enable user & group disk quota on /home File system on CentOS 7 & RHEL 7
Step:1 Add usrquota & grpquota option on /home in /etc/fstab file.
[root@linuxtechi ~]# vi /etc/fstab
home-quota
Save & exit the file.
In this example i have add user and group quota options on /home
Step:2 Remount  /home file system via  mount command
[root@linuxtechi ~]# mount -o remount /home
Now recheck the /home file system whether Quota is enable or not.
[root@linuxtechi ~]# mount | grep /home
/dev/mapper/centos-home on /home type ext4 (rw,relatime,seclabel,quota,usrquota,grpquota,data=ordered)
Step:3 Create Quota Database Files using quotacheck
[root@linuxtechi home]# quotacheck -cugv /home
Whereas :
-c : create quota file and don’t use the existing file
-v : verbose ouput
-u : user disk quota
-g : group disk quota
Above Command will create aquota.user & aquota.group files under /home
Turn on quota on /home using below command :
[root@linuxtechi ~]# quotaon /home/
Step:4 Assign user & group disk quota via edquota commands
Syntax # edquota -u
# edquota -g
[root@linuxtechi ~]# edquota -u jack
edquota
[root@linuxtechi ~]# edquota -g sys_admin
group-quota
As shown above we have two kind of Disk quota limits :
soft : It will warn the users if the soft limit of disk quota reached ( size is in KB), in above example for jack user soft limit is 5500 KB ( approx 5.5MB )
hard : It will not allow the users to create new files once the hard limit is reached. ( Size in KB ), in above example hard limit for jack user is 6000 KB ( approx 6 MB )
Note : We can also set the Quota on the basis of the inodes ( i.e numbers of files that the user can create on particular file system)
Let’s take an example , login as jack user and try to create a file of 8MB.
[root@linuxtechi ~]# su - jack

[jack@linuxtechi ~]$ dd if=/dev/zero of=bgfile bs=1M count=8
dm-2: warning, user block quota exceeded.
dm-2: write failed, user block limit reached.
dd: error writing ‘bgfile’: Disk quota exceeded
6+0 records in
5+0 records out
6144000 bytes (6.1 MB) copied, 0.00711317 s, 864 MB/s
As we see above soft & hard limit is exceeded for jack user. Now onwards user jack can’t create new files.
Step:5 Display Quota report for Users in human readable
[root@linuxtechi ~]# repquota -as
repquota
Step:6 Configure Grace Period for Soft Limit
Grace period is the amount of time during which soft limit can can be exceeded, once the grace period reached then soft limit will become the hard limit.
Use the edquota command to set Grace period .
[root@linuxtechi ~]# edquota -t
Soft-limit-grace-period
Please don’t hesitate to share your feedback and comments on this post :)

How to Use the Linux Command Line: Basics of CLI

http://www.linux.com/learn/tutorials/819973-how-to-use-the-linux-command-line-basics-of-cli

terminal window
If you are using a desktop environment then you need a terminal emulator to emulate the terminal within that interface. Different distros come with their own terminal emulators: KDE comes with Konsole and Gnome comes with Gnome Terminal.
One shell to rule them all, one shell to find them, one shell to bring them all and in the same distro bind them.
Command line is one of the many strengths of Linux based systems. Why is it a strength? There is no one answer; there are many answers. I agree that the graphical user interface (GUI) makes it easier for a user to interact with their system and that's what new users may need to get started with Linux; that's what I needed when I was starting off with Linux back in 2005. But as I matured as a user I found CLI (command line interface) was more efficient than fiddling with the buttons of a tool.
CLI also allows users to be independent of distros. Just look at the derivates of Ubuntu, even if they use the same code-base they have different tools to do the same job. Different desktop environments on the same distro need different ways to perform the same task. A user has to un-learn and then re-learn the process of doing the same thing while they hop between distros. Furthermore if we move between Fedora, openSUSE and Arch, it becomes even more complicated.
But once you understand that in Debian-based systems apt-get or dpkg are the commands that you need to manage software, life becomes easy. Then it desn't matter whether you are on Ubuntu or Lubuntu.
When I was dependent on a GUI, I used to get worried whether that particular distro has that feature or not - it was all about certain features being exposed or hidden through the GUI. One simple example is that Gnome's Nautilus doesn't allow batch rename of files where as KDE's Dolphin does. As a result the user of x distro or DE hesitates in trying out other projects fearing they won't find the same tools. A Gnome user doesn't have to sacrifice such a useful function, thanks to the command line.
But that's not all command line does. It also saves system resources which are consumed by GUIs. So if you are on a slower system, you are better off with the command line than GUI.
People tend to think command line is difficult; it's not. It's more or less like SMSing to your PC, telling it what to do.
So without further ado let's learn some basics of command line.

Get the shell

Shell is basically a program that turns the 'text' that you type into commands/orders for your computer to perform. As such there is a set structure of commands; different OSes may use a different structure to perform the same task.
There are many Shells available for Linux, but the most popular is Bash (Bourne-Again shell) which was written by the GNU Project. Another more modern shell with more features is 'zsh' which you can install for your distribution (we will talk about shells in a later article).
If you are using a desktop environment then you need a terminal emulator to emulate the terminal within that interface. Different distros come with their own terminal emulators: KDE comes with Konsole and Gnome comes with Gnome Terminal.

Basics Commands

When you open a terminal emulator, by default you are in the home directory of the logged in user. You will see the name of the logged in user followed by the hostname. $ means you are logged in as a regular user, whereas # means you are logged in as root.
Unless you are performing administrative tasks or working inside root directories never work as root as it will change the permissions of all directories and files you worked on, making root the user of those directories and their content.
You can list all directories and files inside the current directory by using the ls command.
[swapnil@swaparch ~]$ ls
Desktop Documents Downloads Music Pictures Public Templates Videos

Moving around

To change to any directory, use the cd command. You can also use the 'Tab' key which will auto completes the path. Use forward slash to enter directories. So if I want to change directory to 'Downloads' which is inside my home folder, we run cd and then give the path. In this case 'swapnil' is the username. You need to type your username:
Documents/ Downloads/
[swapnil@swaparch ~]$ cd /home/swapnil/Downloads/
[swapnil@swaparch Downloads]$

As you can see in the third line, 'Downloads' directory has moved inside the square brackets, which denotes that currently we are inside this directory. I can see all subdirectories and files inside Downloads directory by running the ls command.
You don't have to give the complete path if you want to move inside the sub-directory of the current directory. Let's say we want to move inside the 'Test' directory within the current 'Downloads' directory. Just type cd and the directory name, in this case it's 'Test', without any slash.
[swapnil@swaparch Downloads]$ cd Test
If you want to change to another directory just follow the same pattern: cd PATH_OF_DIRECTORY . If you want to move one step back in the directory then use cd . . /. To go back two directories use cd . . /. . /and so on.
But if you want to get out of the current directory and go back to home, simply type cd.

Seeing is believing

You don't have to change directory to see its content. You can use the ls command in following manner:
ls /PATH_OF_DIRECTORY
Example:
[swapnil@swaparch ~]$ ls /home/swapnil/Downloads/Test/

There is no place to hide

To see hidden directories and files use -a option with the ls command.
[swapnil@swaparch ~]$ ls -a /home/swapnil/Downloads/Test/

Size does matter

In order to see the size of directories and files you can use -l option with the ls command. It will also tell the permissions of the files and directories, their owners and the time/date of modification:
[swapnil@swaparch ~]$ ls -l /home/swapnil/Downloads/Test/
total 4
drwxr-xr-x 2 swapnil users 4096 Mar 26 11:55 Test_2

The command gave us the file size in a form hard to understand. If you want to get the file size in human readable format then use ls -lh command:
[swapnil@swaparch ~]$ ls -lh /home/swapnil/Downloads/Test/
total 4.0K
drwxr-xr-x 2 swapnil users 4.0K Mar 26 11:55 Test_2

If you want to get a simple list of all the directories and files inside a location, without extra info such as file size, etc., use ls -R command. This command will give a very long output (depending on how many files are there) as directory trees.

Let's create some directories

If you want to create new directories the command is mkdir. By default the directory will be created in the current directory. So give the complete path of the location where you want the directory to be created:
mkdir /path-of-the-parent-directory/name-of-the-new-directory
So if I want to create a directory 'distros' inside the 'Downloads' directory, then this is the command I will run:
[swapnil@swaparch ~]$ mkdir /home/swapnil/Downloads/distros
If you want to create a sub-directory inside a new directory then use '-p' option with 'mkdir'. I am going to create a directory called 'distro' along with a sub-directory called 'opensuse' inside it. If I run the mkdir command with '/distro/opensuse' as the path, it will throw an error that the directory 'distro' doesn't exist. That's when the option 'p' comes at play and creates all the directories in the given path:
mkdir -p /home/swapnil/Downloads/distros/opensuse
This command will create new directory 'distros' and sub-directory 'opensuse' inside it.

And now let's delete them

If you want to delete any file or directory the command is 'rm' (for files) and 'rm -r' (for directories). You need to be very careful with this command because if you fail to give the correct path of the file or directory then it will remove everything from the current directory and you may lose precious data. The command is simple:
rm /path-of-the-directory-or-file
If I want to remove the opensuse directory, the command would be:
rm -r /home/swapnil/Downloads/distros/opensuse/
However, if you want to delete all the content of a directory without deleting the directory itself use the '*' wildcard with a slash. Let's say I want to delete all the content of opensuse directory:
rm /home/swapnil/Downloads/distros/opensuse/*
If there are sub-directories inside, for example, opensuse directory then you will need that '-r' option to also delete the sub-directories:
rm -r /home/swapnil/Downloads/distros/opensuse/*
That's all for today. This article will make you pretty comfortable with the command line. In the next article we will take you to the next level of managing your system via CLI.
Till then, cd bye