Google I/O 2016 Products: Developer’s Overview

With the 2017 Google I/O conference scheduled for less than in 2 months, I thought it would be a good idea to structure my thoughts and the experience I’ve gained with the products presented on the last year’s event. From the perspective of time it makes perfect sense to figure out whether the products did pass the test of viability.

Multi-window Display Support in Android N

Google I/O 2016 Products: Developer's Overview Google NougatAndroid N showcased the new multitasking function in form of multi-window mode, allowing to have two applications working on one screen at the same time. On a TV you can have a picture-in-picture mode with a video playing while using another app on the same screen. Window sizes can be changed by the divider manipulations between them.

There are two ways to enable multi-window mode:

  • By opening the Recents Screen and long-clicking the operation name which makes it draggable to a selected screen area and switch it to the multi-window mode.
  • By long-clicking the Recents button which signals the app to switch the current operation into the multi-window mode. The device then opens the recents screen for a user to select the second operation for simultaneous display. 

Google I/O 2016 Products: Developer's Overview Multi-window mode

To apps (settings screen and Youtube) working in multi-window mode.

To enable multi-window mode for you Android N application, apply the following changes to the AndroidManifest.xml file:

     1. In the <activity> or <application> node, insert the android:resizeableActivity attribute.

2. Provide the [“true” | “false”] attribute value to enable/disable multi-window mode.

If the attribute value is set to true, the operation can be started in split-screen and free-form mode. If the value is false, the operation does not support multiwindowing and attempting to start the operation in split-screen results in the full screen display.

The default Android N application behavior is that of true, when the attribute value is not set.

     3. Set the android:supportsPictureInPicture attribute value to true for the “picture-in-picture” mode support. Note: the resizeableActivity: attribute value has to be set as true.

    4. Run the new activity in the multiwindowing mode so that the new window is placed next to another one. Use the Intent.FLAG_ACTIVITY_LAUNCH_TO_ADJACENT flag that requests the following behavior:

  • If the device is in the multiwindowing mode, the system attempts to create a new operation window next to the window of the operation that launched it, to place the two on the same screen. The success is not guaranteed but if possible, the system switches to multiwindowing.
  • If the device is not in the split-screen mode, the flag is ignored.

Android N Drag & Drop Between Activities Support

Google I/O 2016 Products: Developer's Overview Google Nougat

From Android N on, users can drag and drop objects between multiple Activities. For this, the ClipData and DragEvent classes are used. On the sending side where the drag is initiated, the data is packed by means of the ClipData class.

The following data types are available for transferring:

  • ClipDescription.MIMETYPE_TEXT_HTML – for the HTML text transfer.
  • ClipDescription.MIMETYPE_TEXT_INTENT – for Intent transfer.
  • ClipDescription.MIMETYPE_TEXT_PLAIN – for the simple text transfer.
  • ClipDescription.MIMETYPE_TEXT_URILIST – for URI transfer.

Let’s take a closer look at dragging and dropping objects from one Activity to another, specifically, a simple text and an image. As an example, I’ll be using two activities of one application, even though the same can be done between two separate apps. To initiate object dragging, I use the View.startDragAndDrop method. On the receiving app, I install the DragEvent listener. Also, it’s mandatory to set the android:resizeableActivity attribute value to true.

        textView.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View view) {
                ClipData.Item item = new ClipData.Item(textView.getText());
                ClipData clip = new ClipData(textView.getText(),
                        new String[]{ ClipDescription.MIMETYPE_TEXT_PLAIN}, item);
                View.DragShadowBuilder dragShadowBuilder = new View.DragShadowBuilder(view);
                textView.startDragAndDrop(clip, dragShadowBuilder, null, View.DRAG_FLAG_GLOBAL);
                return true;
            }
        });
 
        imageView.setOnLongClickListener(view -> {
            String PACKAGE_NAME = getApplicationContext().getPackageName();
            Uri uri = Uri.parse("android.resource://" + PACKAGE_NAME + "/drawable/test_img");
            ClipData.Item item = new ClipData.Item(uri);
            ClipData clip = new ClipData((CharSequence) view.getTag(),
                    new String[]{ ClipDescription.MIMETYPE_TEXT_URILIST}, item);
 
            View.DragShadowBuilder dragShadowBuilder = new View.DragShadowBuilder(view);
            imageView.startDragAndDrop(clip, dragShadowBuilder, null, View.DRAG_FLAG_GLOBAL
                    | View.DRAG_FLAG_GLOBAL_URI_READ | View.DRAG_FLAG_GLOBAL_URI_WRITE);
 
            return true;
        });

The example of an Activity-transmitter source code.

The TextView and ImageView objects are supplied by the appropriate long click listeners. A long click initiates the drag and drop. The system creates the two ClipData.Item objects. With TextView, the relevant object receives the text itself and as for ImageView, it receives the image URI that is stored in the application resources.

Then, the ClipData.Item class instance is packaged into the upper-level class instance of ClipData with corresponding flags:

  • MIMETYPE_TEXT_PLAIN for text
  • MIMETYPE_TEXT_URILIST for an image.

For the shadow effect during the dragging, the DragShadowBuilder class is used.

The following flags control the dragging in the  startDragAndDrop class that initiates the dragging:

  • The View.DRAG_FLAG_GLOBAL flag allows dragging between Activities.
  • The View.DRAG_FLAG_GLOBAL_URI_READ flag allows URI reading.
  • The View.DRAG_FLAG_GLOBAL_URI_WRITE flag allows URI writing.

On the Activity-receiving end, similar View components are installed to accept the dragged objects. The OnDragListener transition listener is attached to them to track the object movement.

The following events are tracked:

  • ACTION_DROP – the object has been dropped into the receiving area.
  • ACTION_DRAG_ENTERED – the object is within the boundaries of the receiving area.
  • ACTION_DRAG_EXITED – the object has left the receiving area.

Also, it’s necessary to make sure the ImageView object has been dropped into its receiving ImageView, and likewise, the TextView object. To check, run the following:

  • event.getClipDescription().getMimeType(0).equals(MIMETYPE_TEXT_URILIST)-for URI and specifically for the image in this example.
  • event.getClipDescription().getMimeType(0).equals(MIMETYPE_TEXT_PLAIN)- for text.
        textView.setOnDragListener(new View.OnDragListener() {
            @Override
            public boolean onDrag(View v, DragEvent event) {
 
                switch (event.getAction()) {
                    case DragEvent.ACTION_DROP:
                        requestDragAndDropPermissions(event);
                        ClipData.Item item = event.getClipData().getItemAt(0);
                        if (event.getClipDescription().getMimeType(0).equals(MIMETYPE_TEXT_PLAIN)) {
                            CharSequence text = item.getText();
                            textView.setText(text);
                        }
                }
 
                return true;
            }
        });
 
        imageView.setOnDragListener((v, event) -> {
 
            switch (event.getAction()) {
                case DragEvent.ACTION_DRAG_ENTERED:
                    if (event.getClipDescription().getMimeType(0).equals(MIMETYPE_TEXT_URILIST)) {
                        imageView.setBackgroundResource(R.color.colorBgDrag);
                    }
                    break;
                case DragEvent.ACTION_DRAG_EXITED:
                    if (event.getClipDescription().getMimeType(0).equals(MIMETYPE_TEXT_URILIST)) {
                        imageView.setBackgroundResource(R.color.colorBg);
                    }
                    break;
                case DragEvent.ACTION_DROP:
                    requestDragAndDropPermissions(event);
                    ClipData.Item item = event.getClipData().getItemAt(0);
 
                    if (event.getClipDescription().getMimeType(0).equals(MIMETYPE_TEXT_URILIST)) {
                        Uri uri = item.getUri();
                        imageView.setImageURI(uri);
                    }
            }
 
            return true;
        });

The example of an Activity-receiver source code.

This is what a testing app with objects transmitting capabilities looks like:

Google I/O 2016 Products: Developer's Overview | Shakuro

The Java 8 Capabilities in Android N

Google I/O 2016 Products: Developer's Overview Google NougatAndroid N introduced the Java 8 language support including the following capabilities:

 

 

  • Static and default methods in UIs.
  • Lambda-expressions.
  • Repeating annotations.

Java 8 requires the Jack toolchain with the help of which Android compiles the Java source code into a readable Android Dalvik Executable (dex) bytecode.

Gradle settings:

android {
    compileSdkVersion 25
    buildToolsVersion "25.0.2"
    defaultConfig {
        applicationId "example.test.com.testproject"
        minSdkVersion 19
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
 
        jackOptions {
            enabled true
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

The example of Java 8 settings in Gradle.

From the Google I/O 2016 presentation:

    @TargetApi(Build.VERSION_CODES.N)
    public interface Renderable {
        void draw(Canvas c);
        default boolean isHardwareAccelerated() {
            return false;
        }
 
        static int getMaxTextureSize() {
            return 2048;
        }
    }
 
    @TargetApi(Build.VERSION_CODES.N)
    public interface FrameListener {
        default void onFrameStarted() {}
        default void onFrameEnded() {}
    }

The example of static and default methods in UI.

    @Retention(RetentionPolicy.RUNTIME)
    public @interface Figures {
        Shape[] value() default {};
    }
 
    @TargetApi(Build.VERSION_CODES.N)
    @Repeatable(value = Figures.class)
    public @interface Shape {
        String type();
    }
 
    @Shape(type = "rectangle")
    @Shape(type = "cube")
    @Shape(type = "circle")
    @Shape(type = "oval")
    public class MyShape {
 
    }

The example of repeating annotations.

        imageView.setOnClickListener(v -> {
            List<Integer> list = getArrayList();
            Collections.sort(list, (o1, o2) -> {
                return o1.compareTo(o2);
            });
        });

The example of Lambda-expressions.

Support Library Innovations in Android N

Google I/O 2016 Products: Developer's Overview Google Nougat

The Google I/O 2016 conference introduced some support library new features like:

 

  • The improved life cycle of fragments – a synchronous commit through the commitNow() method.
  • The new visual style of notifications.
  • UI customizations:
    • Custom colors.
    • New toolbar and bottom bar actions.
    • New enter/exit transitions.
  • The support of vector images in Android versions below 5.0.
  • RecyclerView AutoMeasure feature allows RecyclerView to resize itself depending on its content size. This means that the previously unavailable scenarios like WRAP-CONTENT feature usage for sizing RecyclerView are now possible. All the LayoutManagers built-in components now support automatic measuring. Because of that measurement, the layout parameters (like MATCH_PARENT in the scrolling direction) that had been ignored before, are now going to be taken into account.
  • The improved RecyclerView item change animation.

AppCompat

Google I/O 2016 Products: Developer's Overview Google Nougat1. The Day/Night Mode was introduced by the new DayNight theme family, allowing automatic switching between the light and the dark themes depending on the time of the day.

Theme change declaration:

Theme.AppCompat.DayNight enables the switching between the dark Theme.AppCompat and the light Theme.AppCompatLight based on the day of the time. tHis function with the API version 14 and later, the older versions have the light theme by default.

To enable the function in the app:

Invoke the static AppCompatDelegate.setDefaultNightMode() method that adopts one of the four values:

  • MODE_NIGHT_NO – always uses the light theme.
  • MODE_NIGHT_YES – always uses the dark theme.
  • MODE_NIGHT_AUTO – automatic theme switch depending on the time of the day.
  • MODE_NIGHT_FOLLOW_SYSTEM – default system parameter of MODE_NIGHT_NO.

Also in a separate activity, you can redefine the theme parameter values by default with the help of the setLocalNightMode() method that is invoked in the Activity by executing the following:

getDelegate().setLocalNightMode(@NightMode int mode);

This method is also good for theme check so that you don’t have to wait for certain time of the day.

2. ColorStateList.

The example of ColorStateList with theme attributes added:

  1. <?xml version=“1.0” encoding=“utf-8”?>
  2. <selector xmlns:android=“http://schemas.android.com/apk/res/android”>
  3. <item android:color=“?attr/colorControlNormal”
  4. android:alpha=“?android:disabledAlpha”
  5. android:state_enabled=“false” />
  6. <item android:color=“?attr/colorControlNormal”/>
  7. </selector>

Res/colors/tint_normal.xml

        ColorStateList colorStateList = AppCompatResources.getColorStateList(this, R.color.tint_normal);
        Button button = (Button) findViewById(R.id.test_button);
        button.setBackgroundTintList(colorStateList);

Applying color for a text button.

Design in Android N

Bottom sheets

Google I/O 2016 Products: Developer's Overview Google NougatBottom sheets is an additional content screen that you can pull up from the bottom of the device screen. The Design Support Library allows to fulfill this functionality on the earlier Android versions.

 

To implement this tool, add the following dependencies into the gradle file:

compile 'com.android.support:appcompat-v7:XX.X.X'

compile ‘com.android.support:design:XX.X.X’

There are two types of bottom sheets:

  • Persistent Bottom Sheets that are integrated with the app to display the content.
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/activity_main3"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="example.test.com.testproject.Main3Activity">
 
    <!-- main content layout-->
    <LinearLayout
        android:id="@+id/bg_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal">
 
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="main content layout"/>
 
    </LinearLayout>
 
    <!-- bottom sheet layout -->
    <LinearLayout
        android:id="@+id/bottom_sheet"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="android.support.design.widget.BottomSheetBehavior"
        android:orientation="vertical"
        app:behavior_peekHeight="80dp"
        android:elevation="6dp"
        android:background="@color/colorBgDrag">
 
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Bottom content"
            android:gravity="center"/>
 
        <Button
            android:id="@+id/switch_bg_button"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="200dp"
            android:text="Change background color"/>
 
    </LinearLayout>
 
</android.support.design.widget.CoordinatorLayout>

Persistent bottom sheet layout.

behavior_peekHeight defines the height of the visible part.

Behavior_hideable defines whether the bottom screen can be hidden by a swipe down.

The app:layout_behavior="android.support.design.widget.BottomSheetBehavior": line means the layout belongs to the bottom sheet.
You can also bind a callback to the bottom sheet to help you find out the state of the appearing screen:

        View bottomSheet = findViewById(R.id.bottom_sheet);
        BottomSheetBehavior behavior = BottomSheetBehavior.from(bottomSheet);
        behavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
            @Override
            public void onStateChanged(@NonNull View bottomSheet, int newState) {
                Log.v("BOTTOM_SHEET_STATE", String.valueOf(newState));
            }
 
            @Override
            public void onSlide(@NonNull View bottomSheet, float slideOffset) {
 
            }
        });

Callback bottom sheet state.

There are five possible states:

1. STATE_COLLAPSED is a collapsed by default state that only displays the layout partially at the bottom. The height can be managed by the app:behavior_peekHeight attribute with 0 as default.

2. STATE_DRAGGING is an interim state during the swipe itself.

3. STATE_SETTLING is a short moment between the view is released and falls into the final position.

4. STATE_EXPANDED is a state of the bottom sheet component being entirely unfolded and visible (if its height is lower than in the CoordinatorLayout or filling the entire CoordinatorLayout is filled).

5. STATE_HIDDEN is a disabled by default state (by the app:behavior_hideable attribute). Enabling it means a user is capable of hiding the bottom sheets component by a swipe down.

Persistent bottom sheet performance example:

Google I/O 2016 Products: Developer's Overview | Shakuro

On a Change background color button click, the activity window background is changed.

findViewById(R.id.switch_bg_button).setOnClickListener(v -> {
findViewById(R.id.bg_view).setBackgroundColor(Color.CYAN):
}):
  • Modal Bottom Sheets is a sliding-up dialog which is not part of the main view hierarchy. Basically it’s an alternative to a menu or a simple dialog. Also it can deliver the deeply connected content from other applications.

The BottomSheetDialogFragment fragment serves as a modal window with all the content. A swipe down shuts the window.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingLeft="@dimen/activity_vertical_margin"
    android:paddingRight="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    app:behavior_peekHeight="280dp"
    android:paddingTop="@dimen/activity_vertical_margin">
 
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:gravity="center">
 
        <ImageView
            android:id="@+id/ic_image"
            android:src="@mipmap/ic_launcher"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
 
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello bottom sheet"/>
 
    </LinearLayout>
 
   <TextView
       android:id="@+id/textview"
       android:layout_width="match_parent"
       android:layout_height="wrap_content" />
 
    <Button
        android:id="@+id/switch_bg_button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Change background color"/>
 
</LinearLayout>

Modal bottom sheet layout.

public class MyModalFragment extends BottomSheetDialogFragment {
 
    private OnFragmentInteractionListener mListener;
 
    public static MyModalFragment getInstance() {
        return new MyModalFragment();
    }
 
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.layout_custom_bottom_sheet, container, false);
        ((TextView) view.findViewById(R.id.textview)).setText(LoremIpsum.LOREM_IPSUM);
        view.findViewById(R.id.switch_bg_button).setOnClickListener(v -> {
            mListener.onFragmentInteraction();
        });
        return view;
    }
 
    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        if (context instanceof OnFragmentInteractionListener) {
            mListener = (OnFragmentInteractionListener) context;
        } else {
            throw new RuntimeException(context.toString()
                    + " must implement OnFragmentInteractionListener");
        }
    }
 
    @Override
    public void onDetach() {
        super.onDetach();
        mListener = null;
    }
 
    public interface OnFragmentInteractionListener {
        void onFragmentInteraction();
    }
}

Modal fragment

public class ModalActivity extends AppCompatActivity implements MyModalFragment.OnFragmentInteractionListener {
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_modal);
 
        MyModalFragment bottomSheetDialog = MyModalFragment.getInstance();
        bottomSheetDialog.show(getSupportFragmentManager(), "Custom Bottom Sheet");
    }
 
    @Override
    public void onFragmentInteraction() {
        findViewById(R.id.activity_modal).setBackgroundColor(Color.MAGENTA);
    }
}

Modal fragment initialization.

Modal bottom sheet performance example:

Google I/O 2016 Products: Developer's Overview | Shakuro

Google I/O 2016 Outtakes

Google I/O 2016 Products: Developer's Overview Google NougatSome of the features introduced at Google I/O 2016 have proven to be a breakthrough in the industry and in that sense having a hand on the pulse of the new technologies and features might give you an edge on the rest of them in application development.

There is clearly no reason why should neglect any of the newly introduced ideas and features as their viability is often unpredictable and dictated by the market in a spontaneous way. That’s why exploring as many hot technologies as there is available is vital for your competitive development skills. Let’s keep a close eye on what’s coming in 2017.