Slide 1

Slide 1 text

Task & Document API Droidcon Paris 2015

Slide 2

Slide 2 text

@Mathieu_Calba +MathieuCalba

Slide 3

Slide 3 text

@Mathieu_Calba +MathieuCalba

Slide 4

Slide 4 text

What is a Task?

Slide 5

Slide 5 text

What is a Task? An Activity represents a screen

Slide 6

Slide 6 text

What is a Task? An application is often composed of many activities

Slide 7

Slide 7 text

What is a Task? An application is often composed of many activities

Slide 8

Slide 8 text

What is a Task? An application is often composed of A task is an ordered queue of activities called “back stack”

Slide 9

Slide 9 text

What is a Task? Tasks are presented in the overview screen

Slide 10

Slide 10 text

What is a Task? • Since 1.0: icon and name • Screenshot added in 4.3 • Icon and name customisation added in 5.0 Overview screen

Slide 11

Slide 11 text

What is a Task? Overview screen

Slide 12

Slide 12 text

How does it work?

Slide 13

Slide 13 text

How does it work? 2 main ways: • Attributes on the tag in the AndroidManifest.xml • Flags on the Intent Task API

Slide 14

Slide 14 text

How does it work? android:allowTaskReparenting android:alwaysRetainTaskState android:autoRemoveFromRecents android:clearTaskOnLaunch android:documentLaunchMode android:excludeFromRecents android:finishOnTaskLaunch Task API android:launchMode android:maxRecents android:noHistory android:relinquishTaskIdentity android:stateNotNeeded android:taskAffinity

Slide 15

Slide 15 text

How does it work? Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT Intent.FLAG_ACTIVITY_CLEAR_TASK Intent.FLAG_ACTIVITY_CLEAR_TOP Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS Intent.FLAG_ACTIVITY_FORWARD_RESULT Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY Intent.FLAG_ACTIVITY_MULTIPLE_TASK Intent.FLAG_ACTIVITY_NEW_DOCUMENT Intent.FLAG_ACTIVITY_NEW_TASK Task API Intent.FLAG_ACTIVITY_NO_ANIMATION Intent.FLAG_ACTIVITY_NO_HISTORY Intent.FLAG_ACTIVITY_NO_USER_ACTION Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP Intent.FLAG_ACTIVITY_REORDER_TO_FRONT Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS Intent.FLAG_ACTIVITY_SINGLE_TOP Intent.FLAG_ACTIVITY_TASK_ON_HOME Intent.FLAG_ACTIVITY_HELL

Slide 16

Slide 16 text

Have fun with flags!

Slide 17

Slide 17 text

Standard behaviour

Slide 18

Slide 18 text

Standard behaviour Action: android.intent.action.MAIN Category: android.intent.category.LAUNCHER Flags: • Intent.FLAG_ACTIVITY_NEW_TASK • Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED Start from launcher

Slide 19

Slide 19 text

Standard behaviour Start from launcher Action: android.intent.action.MAIN Category: android.intent.category.LAUNCHER Flags: • Intent.FLAG_ACTIVITY_NEW_TASK • Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED

Slide 20

Slide 20 text

Action: android.intent.action.MAIN Category: android.intent.category.LAUNCHER Flags: • Intent.FLAG_ACTIVITY_NEW_TASK • Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED Standard behaviour Start from launcher My awesome app

Slide 21

Slide 21 text

My awesome app Standard behaviour Start from launcher Action: android.intent.action.MAIN Category: android.intent.category.LAUNCHER Flags: • Intent.FLAG_ACTIVITY_NEW_TASK • Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED

Slide 22

Slide 22 text

My awesome app Standard behaviour Start from launcher Action: android.intent.action.MAIN Category: android.intent.category.LAUNCHER Flags: • Intent.FLAG_ACTIVITY_NEW_TASK • Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED

Slide 23

Slide 23 text

Intent.FLAG_ACTIVITY_NEW_TASK Start the activity in a new Task depending on the task’s affinity Standard behaviour Start from launcher

Slide 24

Slide 24 text

android:taskAffinity Used to determine in which task this activity will be launched when started with Intent.FLAG_ACTIVITY_NEW_TASK Application’s package name by default Standard behaviour Start from launcher

Slide 25

Slide 25 text

Action: null Category: null Flags: null My awesome app Standard behaviour Start from within the app My awesome screen

Slide 26

Slide 26 text

My awesome app My awesome screen Standard behaviour Start from within the app Action: null Category: null Flags: null

Slide 27

Slide 27 text

Standard behaviour Start from overview My awesome app My awesome screen Action: null Category: null Flags: null

Slide 28

Slide 28 text

My awesome app My awesome screen Standard behaviour Start from overview Action: null Category: null Flags: null

Slide 29

Slide 29 text

Standard behaviour Start from overview My awesome app My awesome screen Action: null Category: null Flags: null

Slide 30

Slide 30 text

Standard behaviour Start from overview My awesome app My awesome screen Action: null Category: null Flags: null

Slide 31

Slide 31 text

My awesome app Same Intent that launched this activity Action: android.intent.action.MAIN Category: android.intent.category.LAUNCHER Flags: Intent.FLAG_ACTIVITY_NEW_TASK Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED Standard behaviour Restart from back My awesome app My awesome screen

Slide 32

Slide 32 text

My awesome app Same Intent that launched this activity Action: android.intent.action.MAIN Category: android.intent.category.LAUNCHER Flags: Intent.FLAG_ACTIVITY_NEW_TASK Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED Standard behaviour Restart from back My awesome app My awesome screen

Slide 33

Slide 33 text

But there is more to it

Slide 34

Slide 34 text

But there is more to it USE ANOTHER APP TO DO A JOB MANY ENTRY POINTS INTO OUR APP MULTITASKING INSIDE AN APP

Slide 35

Slide 35 text

But there is more to it USE ANOTHER APP TO DO A JOB MANY ENTRY POINTS INTO OUR APP MULTITASKING INSIDE AN APP

Slide 36

Slide 36 text

But there is more to it USE ANOTHER APP TO DO A JOB MANY ENTRY POINTS INTO OUR APP MULTITASKING INSIDE AN APP

Slide 37

Slide 37 text

Using other applications startActivity(new Intent().setAction(Intent.ACTION_VIEW) .setData(Uri.parse("http://captaintrain.com"));
 What behaviour does occur? New task or not? Opening an Url

Slide 38

Slide 38 text

Using other applications Opening an Url It depends on the opened application! startActivity(new Intent().setAction(Intent.ACTION_VIEW) .setData(Uri.parse("http://captaintrain.com"));
 What behaviour does occur? New task or not?

Slide 39

Slide 39 text

Using other applications • With the old Android browser: new task • With Firefox: new task • With Chrome: same task Opening an Url

Slide 40

Slide 40 text

Using other applications Same task Action: android.intent.action.VIEW Category: null Flags: Intent.FLAG_ACTIVITY_FORWARD_RESULT Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP Opening an Url

Slide 41

Slide 41 text

Using other applications Opening an Url New task Action: android.intent.action.VIEW Category: null Flags: Intent.FLAG_ACTIVITY_NEW_TASK Intent.FLAG_ACTIVITY_FORWARD_RESULT Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP

Slide 42

Slide 42 text

Using other applications Opening an Url New task Action: android.intent.action.VIEW Category: null Flags: Intent.FLAG_ACTIVITY_NEW_TASK Intent.FLAG_ACTIVITY_FORWARD_RESULT Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP WHY DID IT APPEAR?

Slide 43

Slide 43 text

Using other applications In the old Android browser’s manifest: ! Opening an Url

Slide 44

Slide 44 text

Using other applications In the old Android browser’s manifest: ! Opening an Url

Slide 45

Slide 45 text

android:launchMode Determines how an activity is launched

Slide 46

Slide 46 text

android:launchMode • New instance of the activity all the time • Can be anywhere in the stack • Which task is determined by the presence of FLAG_ACTIVITY_NEW_TASK and the android:taskAffinity attribute android:launchMode=“standard”

Slide 47

Slide 47 text

android:launchMode • New instance of the activity most of the time • Can be anywhere in the stack • Which task is determined by the presence of FLAG_ACTIVITY_NEW_TASK and the android:taskAffinity attribute • If the targeted task already has this activity at the top of it with this android:launchMode, instead of creating a new instance, it just send a new Intent to this instance, which will receive it in the onNewIntent() method android:launchMode=“singleTop”

Slide 48

Slide 48 text

android:launchMode • Only one instance of the activity • Always the root activity of the task • Other activities can be in its task (only standard and singleTop activities) android:launchMode=“singleTask”

Slide 49

Slide 49 text

android:launchMode • Only one instance of the activity • Always the root activity of the task • Alone in its task • Every startActivity() behave just like if the Intent has the FLAG_ACTIVITY_NEW_TASK flag android:launchMode=“singleInstance”

Slide 50

Slide 50 text

android:launchMode android:launchMode behaviour can be overridden by the FLAG_ACTIVITY_* in the intent

Slide 51

Slide 51 text

Using other applications What is the correct behaviour? This way (app -> browser), it’s better to have them in the same task, because the browser is a utility Nothing can be done to prevent a new task to be launched here Opening an Url

Slide 52

Slide 52 text

Using other applications Taking a picture final Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
 startActivityForResult(intent, REQUEST_CODE_IMAGE_CAPTURE);

Slide 53

Slide 53 text

Using other applications Taking a picture Only works when activity started in the same task Otherwise, an Activity.RESULT_CANCELED is received directly final Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
 startActivityForResult(intent, REQUEST_CODE_IMAGE_CAPTURE);

Slide 54

Slide 54 text

Using other applications Sharing via email But it launch in the same task, which is not the expected behaviour on 5.0+ final Intent target = new Intent()
 .setAction(Intent.ACTION_SENDTO)
 .setData(Uri.fromParts(“mailto", "[email protected]", null))
 .putExtra(Intent.EXTRA_TEXT, "Hello Droidcon!");
 startActivity(Intent.createChooser(target, "My chooser"));

Slide 55

Slide 55 text

Using other applications Sharing via email Action: android.intent.action.SENDTO Category: null Data: mailto:[email protected] Flags: Intent.FLAG_ACTIVITY_FORWARD_RESULT Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP final Intent target = new Intent()
 .setAction(Intent.ACTION_SENDTO)
 .setData(Uri.fromParts(“mailto", "[email protected]", null))
 .putExtra(Intent.EXTRA_TEXT, "Hello Droidcon!");
 startActivity(Intent.createChooser(target, "My chooser"));

Slide 56

Slide 56 text

Using other applications Sharing via email Action: android.intent.action.SENDTO Category: null Data: mailto:[email protected] Flags: Intent.FLAG_ACTIVITY_FORWARD_RESULT Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP final Intent target = new Intent()
 .setAction(Intent.ACTION_SENDTO)
 .setData(Uri.fromParts(“mailto", "[email protected]", null))
 .putExtra(Intent.EXTRA_TEXT, "Hello Droidcon!");
 startActivity(Intent.createChooser(target, "My chooser"));

Slide 57

Slide 57 text

Using other applications Intent.FLAG_ACTIVITY_NEW_TASK? No, because we don’t want to be launched in the targeted application’s task Sharing via email

Slide 58

Slide 58 text

Using other applications Intent.FLAG_ACTIVITY_NEW_DOCUMENT (5.0+) • Start a new task dedicated to this job • New mail and original application are accessible via the overview screen • Restarting the original task from overview will not bring the mail activity Sharing via email

Slide 59

Slide 59 text

Using other applications Compatibility: Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET • Start a the job in the same task • Restarting the original task from overview will not bring the mail activity as it will be finished Sharing via email

Slide 60

Slide 60 text

Using other applications final Intent target = new Intent()
 .setAction(Intent.ACTION_SENDTO)
 .setData(Uri.fromParts(“mailto", "[email protected]", null))
 .putExtra(Intent.EXTRA_TEXT, "Hello Droidcon!");
 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
 target.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
 } else {
 target.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
 }
 startActivity(Intent.createChooser(target, "My chooser")); Sharing via email

Slide 61

Slide 61 text

Using other applications Sharing via email final Intent target = new Intent()
 .setAction(Intent.ACTION_SENDTO)
 .setData(Uri.fromParts(“mailto", "[email protected]", null))
 .putExtra(Intent.EXTRA_TEXT, "Hello Droidcon!");
 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
 target.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
 } else {
 target.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
 }
 startActivity(Intent.createChooser(target, "My chooser"));

Slide 62

Slide 62 text

Multiple entry points Internally: • From the notification • From a widget • From a wear app Externally: • Multiple icons in the launcher • Responding to a public Intent (URL, sharing, etc)

Slide 63

Slide 63 text

Multiple entry points Internally

Slide 64

Slide 64 text

Multiple entry points Internally final PendingIntent pendingIntent = TaskStackBuilder.create(this)
 .addNextIntentWithParentStack(DetailsActivity. newFocusOnBarcodeIntent(this, id)) .getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT); Action: null Category: null Flags: Intent.FLAG_ACTIVITY_NEW_TASK Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT

Slide 65

Slide 65 text

Multiple entry points Allow us to create a stack of activities Internally final PendingIntent pendingIntent = TaskStackBuilder.create(this)
 .addNextIntentWithParentStack(DetailsActivity. newFocusOnBarcodeIntent(this, id)) .getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);

Slide 66

Slide 66 text

Multiple entry points TaskStackBuilder.create(this)
 .addParentStack(ParentActivity.class)
 .addNextIntent(detailIntent)
 .startActivities(); Internally Add the parent’s stack as defined by android:parentActivityName in AndroidManifest.xml

Slide 67

Slide 67 text

Multiple entry points Add the targeted activity’s Intent Internally TaskStackBuilder.create(this)
 .addParentStack(ParentActivity.class)
 .addNextIntent(detailIntent)
 .startActivities();

Slide 68

Slide 68 text

Multiple entry points Internally Do both at the same time TaskStackBuilder.create(this)
 .addNextIntentWithParentTask(detailIntent)
 .startActivities();

Slide 69

Slide 69 text

Multiple entry points Internally Start the stack TaskStackBuilder.create(this)
 .addNextIntentWithParentTask(detailIntent)
 .startActivities();

Slide 70

Slide 70 text

Multiple entry points Internally Create a pending Intent to be launched TaskStackBuilder.create(this)
 .addNextIntentWithParentTask(detailIntent)
 .getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);

Slide 71

Slide 71 text

Multiple entry points Multiple icons in the app drawer Use cases very uncommon: app with multiple specific jobs Multiple activities declared with intent-filter: ! 
 
 
 Become interesting with non-standard android:launchMode Externally

Slide 72

Slide 72 text

Multiple entry points Intercepting Urls: ! 
 
 
 
 
 
 
 
 
 
 
 
 Externally

Slide 73

Slide 73 text

Multiple entry points From the old Android browser: Action: android.intent.action.VIEW Category: android.intent.category.BROWSABLE Data: https://www.captaintrain.com/search Flags: Intent.FLAG_ACTIVITY_FORWARD_RESULT Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP Externally S AM E TASK

Slide 74

Slide 74 text

Multiple entry points From the email app: Action: android.intent.action.VIEW Category: android.intent.category.BROWSABLE Data: https://www.captaintrain.com/search Flags: Intent.FLAG_ACTIVITY_NEW_TASK Intent.FLAG_ACTIVITY_FORWARD_RESULT Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET Intent.FLAG_ACTIVITY_NEW_DOCUMENT Intent.FLAG_ACTIVITY_NO_ANIMATION Externally N EW TASK

Slide 75

Slide 75 text

Multiple entry points From the email app: Action: android.intent.action.VIEW Category: android.intent.category.BROWSABLE Data: https://www.captaintrain.com/search Flags: Intent.FLAG_ACTIVITY_NEW_TASK Intent.FLAG_ACTIVITY_FORWARD_RESULT Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET Intent.FLAG_ACTIVITY_NEW_DOCUMENT Intent.FLAG_ACTIVITY_NO_ANIMATION Externally N EW TASK

Slide 76

Slide 76 text

Multiple entry points From Chrome: Action: android.intent.action.VIEW Category: android.intent.category.BROWSABLE Data: https://www.captaintrain.com/search Flags: Intent.FLAG_ACTIVITY_NEW_TASK Intent.FLAG_ACTIVITY_FORWARD_RESULT Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP Externally N EW TASK

Slide 77

Slide 77 text

Multiple entry points From Chrome: Action: android.intent.action.VIEW Category: android.intent.category.BROWSABLE Data: https://www.captaintrain.com/search Flags: Intent.FLAG_ACTIVITY_NEW_TASK Intent.FLAG_ACTIVITY_FORWARD_RESULT Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP Externally N EW TASK

Slide 78

Slide 78 text

Multiple entry points What is the good behaviour? Externally

Slide 79

Slide 79 text

Multiple entry points What is the good behaviour? Externally ! New task!

Slide 80

Slide 80 text

Multiple entry points 2 solutions: • let the current behaviour and let some user have some bad experience • force the creation of a new task for better user experience at the expense of clean code Externally

Slide 81

Slide 81 text

Multiple entry points 2 solutions: • let the current behaviour and let some user have some bad experience • force the creation of a new task for better user experience at the expense of clean code Externally Here come android:launchMode=“singleTask” to the rescue!

Slide 82

Slide 82 text

Multiple entry points Create a special activity that will catch all Urls you need to catch: ! 
 
 
 
 
 
 
 
 
 
 And then it will redirect to the correct activity and finish itself Externally

Slide 83

Slide 83 text

Multitasking inside an app Why? • multi-document opened at the same time • writing an email and looking at other emails as references • multi-tasking at the system level is quicker than at the app level

Slide 84

Slide 84 text

Introducing Document API (5.0+)

Slide 85

Slide 85 text

Introducing Document API (5.0+) Move from app-centric to document-centric system

Slide 86

Slide 86 text

Introducing Document API (5.0+) An extension of the Tasks API Move from app-centric to document-centric system

Slide 87

Slide 87 text

Multitasking inside an app Only one instance of this document, one task Same as setting FLAG_ACTIVITY_NEW_DOCUMENT alone on the Intent android:launchMode=“standard” mandatory Create new document

Slide 88

Slide 88 text

Multitasking inside an app Multiple instance of this document, multiple tasks Same as setting FLAG_ACTIVITY_NEW_DOCUMENT and FLAG_ACTIVITY_MULTIPLE_TASK on the Intent android:launchMode must be standard Create new document

Slide 89

Slide 89 text

Multitasking inside an app Create new document Default value of the attribute New task only if FLAG_ACTIVITY_NEW_TASK or specific android:launchMode

Slide 90

Slide 90 text

Multitasking inside an app Create new document Even with FLAG_ACTIVITY_NEW_DOCUMENT, no new document task

Slide 91

Slide 91 text

Multitasking inside an app Limiting the history Define the max number of recent documents for one activity Between 1 and 50, default to 16

Slide 92

Slide 92 text

Multitasking inside an app Starting in background

Slide 93

Slide 93 text

Multitasking inside an app Starting in background Force the grouping of documents with the original activity Force the behaviour of the android:documentLaunchMode=“always” final ActivityOptions options = ActivityOptions.makeTaskLaunchBehind();
 final Intent intent = new Intent(this, NewDocumentActivity.class)
 .addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
 startActivity(intent, options.toBundle());

Slide 94

Slide 94 text

Multitasking inside an app In ActivityManager: ! public List getAppTasks() Managing documents

Slide 95

Slide 95 text

Multitasking inside an app ActivityManager.AppTask Contains a lot of information on a task, you can: ! public RecentTaskInfo getTaskInfo() ! public void moveToFront() ! public void finishAndRemoveTask() Managing documents

Slide 96

Slide 96 text

Multitasking inside an app In a document activity: • finish() will terminate the activity but let the task remain in overview • finishAndRemoveTask() will terminate the activity and remove the task in overview Removing a document

Slide 97

Slide 97 text

Multitasking inside an app Use FLAG_ACTIVITY_NEW_DOCUMENT when launching an external activity on 5.0+ On pre-5.0, use FLAG_ACTIVITY_CLEAR_TASK_WHEN_RESET to get roughly the same behaviour Compatibility

Slide 98

Slide 98 text

Multitasking inside an app ActivityManager.TaskDescription Customising the overview entry of your activity: ! setTaskDescription(new ActivityManager.TaskDescription( "New label”, bitmap, getColor(R.color.red))); Overview screen customisation (5.0+)

Slide 99

Slide 99 text

Multitasking inside an app Overview screen customisation (5.0+)

Slide 100

Slide 100 text

Multitasking inside an app Persistance behaviours (5.0+) Default value Persist the activity only if it’s the root of the task

Slide 101

Slide 101 text

Multitasking inside an app Persistance behaviours (5.0+) If the activity is the root of the task, the task is not persisted

Slide 102

Slide 102 text

Multitasking inside an app Persistance behaviours (5.0+) The activity is persisted across reboot Uses a PersistableBundle in onCreate/onSavedInstanceState to restore/ persist some data across reboot

Slide 103

Slide 103 text

Choose the behaviour you want wisely

Slide 104

Slide 104 text

Choose the behaviour you want wisely New task only when there is a break in the app flow

Slide 105

Slide 105 text

API is complicated, but powerful

Slide 106

Slide 106 text

Credits ! • Nexus 5 frame - Cyril Mottier • Fonts: Lato, Menlo