Sunday, February 24, 2019

Google Play will require new/update App to target API level 28+ in 2019

Google Play Console will continue to require that apps target a recent API level:

  • August 2019: New apps are required to target API level 28 (Android 9) or higher.
  • November 2019: Updates to existing apps are required to target API level 28 or higher.

Existing apps that are not receiving updates are unaffected and can continue to be downloaded from the Play Store.

source: Android Developer Blog - Expanding target API level requirements in 2019


Thursday, February 21, 2019

Install JetBrains Toolbox App on Linux, and also Android Studio and IntelliJ IDEA (Java IDE)

JetBrains Toolbox App is a lightweight cross-platform companion application for JetBrains' coding tools, to manage installed tools, download new ones and open recent projects.

System requirement to install Toolbox App for Linux:
64-bit x86, glibc 2.17 (Ubuntu 14.04 or newer). JetBrains Toolbox App is packaged in AppImage and requires FUSE to run.

To check the version of your glibc, it's a simple method, using ldd command in Terminal:
$ ldd --version

It will show something like this:
ldd (Ubuntu GLIBC 2.27-3ubuntu1) 2.27

To install and set FUSE, follow the steps in AppImage Wiki.

For Ubuntu, enter the commands in Terminal:

$ sudo apt install fuse
$ sudo modprobe fuse
$ sudo groupadd fuse
$ user="$(whoami)"
$ sudo usermod -a -G fuse $user

Visit https://www.jetbrains.com/toolbox/app/ to download the, currently it's version 1.13.

This video show how to Install JetBrains Toolbox App on Linux Mint 19.1

Install Android 3.3.1 on Linux Mint 19.1 with JetBrains Toolbox, Hello World and convert Java to Kotlin.

IntelliJ IDEA (Java IDE) with JetBrains Toolbox.

Wednesday, February 20, 2019

Install OpenJDK on Linux Mint/Ubuntu

OpenJDK (https://openjdk.java.net/) provides open-source builds of the Java Development Kit, an implementation of the Java SE Platform under the GNU General Public License, version 2, with the Classpath Exception.

Commercial builds of JDK from Oracle under a non-open-source license, for a wider range of platforms, can be found at the Oracle Technology Network.

This video show how to Install OpenJDK 11.0.2 on Linux Mint 19.1. The OpenJDK will be stored in /opt/java directory, and set as default. It should be work on other Ubuntu variants also.


Oracle's OpenJDK JDK binaries for Windows, macOS, and Linux are available on release-specific pages of jdk.java.net as .tar.gz or .zip archives.

As an example, the archives for JDK 11 (currently 11.0.2) may be found on https://jdk.java.net/11/.

- Visit https://jdk.java.net/11/ to download Linux / x64 build.

- Change to the downloaded directory, it's ~/Downloads in my case.

- Extract the downloaded file, openjdk-11.0.2_linux-x64_bin.tar.gz.
$ tar -zxvf openjdk-11.0.2_linux-x64_bin.tar.gz

-  Create a folder in /opt/java where jdk will be stored.
$ sudo mkdir -p /opt/java

- Move the extracted folder to /opt/java
$ sudo mv jdk-11.0.2 /opt/java

- Set default java and javac using update-alternatives:
$ sudo update-alternatives --install /usr/bin/javac javac /opt/java/jdk-11.0.2/bin/javac 1
$ sudo update-alternatives --install /usr/bin/java java /opt/java/jdk-11.0.2/bin/java 1
$ sudo update-alternatives --config javac
$ sudo update-alternatives --config java

There are 2 choices for the alternative java, enter the selection of "1", corresponding to the number of /opt/java/jdk-11.0.2/bin/java.



may be interested:
Install JetBrains Toolbox App on Linux, and also Android Studio and IntelliJ IDEA (Java IDE)

Monday, February 11, 2019

My first exercise of using Data Binding + ObservableField

The Data Binding Library is a support library that allows you to bind UI components in your layouts to data sources in your app using a declarative format rather than programmatically. It's my first exercise of using Data Binding and ObservableField, in Java. Basically, it follow the steps in Android Developers document of Data Binding Library.


The TextViews myArchitecture, myRelease and myDensity are loaded in onCreate() once. The TextView myNumber is associated with ObservableField, and will be updated at run-time.

Prepare:

- To use the Data Binding Library in your app, you have to target devices running Android 4.0 (API level 14) or higher.

- It's recommended to use the latest Android Plugin for Gradle in your project. Currently it's 3.3.1.


Read this video how to:

- Make sure include Support Repository in the Android SDK manager.


-  Add the dataBinding element to your build.gradle file in the app module.

In Android Studio, select Project view, extend app view, select to edit build.gradle. Add the code:

    dataBinding {
        enabled = true
    }


- Enable the new data binding compiler.
Note: The new compiler in Android Plugin version 3.2 is enabled by default. (refer https://developer.android.com/topic/libraries/data-binding/start) So it's skipped here.

- layout:
layout/activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>
        <variable name="user" type="com.blogspot.android_er.myandroidinfo.MainActivity.User"/>
    </data>

    <android.support.constraint.ConstraintLayout
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <TextView
            android:id="@+id/title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="My device info"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            android:textStyle="bold"/>
        <TextView
            android:id="@+id/mylink"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="android-er.blogspot.com"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/title" />
        <TextView
            android:id="@+id/myArchitecture"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text='@{user.bArchitecture, default="Architecture"}'
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/mylink"
            android:gravity="left"
            android:textSize="30dp"/>
        <TextView
            android:id="@+id/myRelease"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text='@{user.bRelease, default="Release"}'
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/myArchitecture"
            android:gravity="left"
            android:textSize="30dp"/>
        <TextView
            android:id="@+id/myDensity"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text='@{user.bDensity, default="Density"}'
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/myRelease"
            android:gravity="left"
            android:textSize="30dp"/>

        <TextView
            android:id="@+id/myNumber"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@{user.bNum}"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/myDensity"
            android:gravity="left"
            android:textSize="50dp"
            android:textStyle="italic|bold"
            android:textColor="#0000FF"/>

    </android.support.constraint.ConstraintLayout>
</layout>

- Java code
MainActivity.java
package com.blogspot.android_er.myandroidinfo;

import android.databinding.DataBindingUtil;
import android.databinding.ObservableField;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.DisplayMetrics;

import com.blogspot.android_er.myandroidinfo.databinding.ActivityMainBinding;

public class MainActivity extends AppCompatActivity {

    public class User {
        public final String bArchitecture;
        public final String bRelease;
        public final String bDensity;
        public final ObservableField<String> bNum = new ObservableField<>();
        public User(String arch, String rel, String den) {
            this.bArchitecture = arch;
            this.bRelease = rel;
            this.bDensity = den;
        }
    }

    private class MyTask extends AsyncTask<Void, Void, Void> {

        @Override
        protected Void doInBackground(Void... voids) {

            for (int i = 0; i < 20; i++) {
                user.bNum.set(String.valueOf(i));
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return null;
        }
    }

    User user;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        String arch = System.getProperty("os.arch");
        String release = Build.VERSION.RELEASE;

        DisplayMetrics metrics = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(metrics);
        int dpi = metrics.densityDpi;

        ActivityMainBinding binding =
                DataBindingUtil.setContentView(this, R.layout.activity_main);
        user = new User(arch, release, String.valueOf(dpi));
        binding.setUser(user);

        new MyTask().execute();

    }
}


Sunday, February 10, 2019

Change Gradle plugin version in Android Studio 3.3.1

To change Gradle plugin to specified version, you can:
- the File > Project Structure > Project menu in Android Studio,


- or edit the top-level build.gradle file.

Tuesday, February 5, 2019

Get Architecture, Version and Screen DPI programmatically

This code get Architecture, Version and Screen DPI of Android device programmatically, using Java.


package com.blogspot.android_er.myandroidinfo;

import android.os.Build;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.DisplayMetrics;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView tvMyArch = findViewById(R.id.myArchitecture);
        TextView tvMyRelease = findViewById(R.id.myRelease);
        TextView tvMyDensity = findViewById(R.id.myDensity);

        String arch = System.getProperty("os.arch");
        tvMyArch.setText(arch);

        String release = Build.VERSION.RELEASE;
        tvMyRelease.setText(release);

        DisplayMetrics metrics = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(metrics);
        int dpi = metrics.densityDpi;
        tvMyDensity.setText(String.valueOf(dpi));

    }
}



<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="My device info"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:textStyle="bold"/>
    <TextView
        android:id="@+id/mylink"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="android-er.blogspot.com"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/title" />
    <TextView
        android:id="@+id/myArchitecture"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Architecture"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/mylink"
        android:gravity="left"
        android:textSize="30dp"/>
    <TextView
        android:id="@+id/myRelease"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Release"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/myArchitecture"
        android:gravity="left"
        android:textSize="30dp"/>
    <TextView
        android:id="@+id/myDensity"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Density"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/myRelease"
        android:gravity="left"
        android:textSize="30dp"/>

</android.support.constraint.ConstraintLayout>

Wednesday, January 30, 2019

Install Linux Mint 19.1 Tessa - Xfce (64-bit) on VirtuaBox 6/Windows 10

To download Linux Mint, visit https://linuxmint.com/download.php. The current release is 19.1.

This video show steps to download and install Linux Mint 19.1 Tessa - Xfce (64-bit) on Windows 10 with VirtualBox 6.

And then, you are suggested to install Guest Additions, for better performance and functions.



may be interested:
Install OpenJDK on Linux Mint/Ubuntu
Install JetBrains Toolbox App on Linux, and also Android Studio and IntelliJ IDEA (Java IDE)



Tuesday, January 22, 2019

Google + Wikipedia

Announced on 22 January 2019,
Wikimedia Foundation is partnering with Google on a set of initiatives to support a shared commitment of making information more accessible to more people around the world.

Through these partnerships, Google and Wikimedia will be working together to create new and expanded programs to empower editors to create local language content on Wikipedia, as well as developing improved translation tools. To strengthen and support Wikipedia and its mission for generations to come, Google.org will also be contributing $1.1 million to the Wikimedia Foundation and $2 million to the Wikimedia Endowment

Source: Google and Wikimedia Foundation partner to increase knowledge equity online

Monday, January 14, 2019

What is 5G?

What is 5G? | CNBC Explains



Everything You Need to Know About 5G


5G in 2019: The benefits, rollout and challenges at CES 2019

Stable release of Android Studio 3.3 released

Android Studio 3.3 released, with Navigation editor, support Instant Apps in App bundle, as well as build system updates such as lazy task configuration, better debug info when using obsolete APIs, improved incremental Java compilation when using annotation processors, and a preview of the new R8 code shrinker. Also added more granularity in the profiler options and added slow frame highlighting to help debug quicker.

You can download it today from https://developer.android.com/studio/.

Or, if you already installed previous release of Android Studio,  you can simply update to the latest version.

What’s new in Android Studio 3.3

~ Android Developers Blog post Android Studio 3.3


Tuesday, January 1, 2019

Mix using of ImageDecoder and ObjectAnimator

Last posts show how to "Display animated GIF using ImageDecoder" and "Apply PostProcess for ImageDecoder". This example I try to mix using of ImageDecoder and ObjectAnimator, to apply animation on animated GIF.



Modify MainActivity.java
package com.blogspot.android_er.androidimagedecoder;

import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
import android.app.Activity;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ImageDecoder;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.PostProcessor;
import android.graphics.drawable.AnimatedImageDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.ImageView;
import android.widget.Toast;

import java.io.IOException;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ImageView imageView1 = findViewById(R.id.gifimage1);
        ImageView imageView2 = findViewById(R.id.gifimage2);

        loadGif(imageView1);
        loadRoundGif(imageView2);

        imageView1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                prepareObjectAnimatorRotate((ImageView)v);
            }
        });

        imageView2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                prepareObjectAnimatorAlpha((ImageView)v);
            }
        });
    }

    PostProcessor myPostProcessor =
            new PostProcessor(){
                @Override
                public int onPostProcess(
                        Canvas canvas) {
                    // This will create rounded corners.
                    Path path = new Path();
                    path.setFillType(Path.FillType.INVERSE_EVEN_ODD);
                    int width = canvas.getWidth();
                    int height = canvas.getHeight();
                    path.addRoundRect(0, 0,
                            width, height, 150, 150,
                            Path.Direction.CW);
                    Paint paint = new Paint();
                    paint.setAntiAlias(true);
                    paint.setColor(Color.TRANSPARENT);
                    paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
                    canvas.drawPath(path, paint);
                    return PixelFormat.TRANSLUCENT;
                }
            };


    ImageDecoder.OnHeaderDecodedListener myOnHeaderDecodedListener =
            new ImageDecoder.OnHeaderDecodedListener(){
                @Override
                public void onHeaderDecoded
                        (ImageDecoder decoder,
                         ImageDecoder.ImageInfo info,
                         ImageDecoder.Source source) {
                    decoder.setPostProcessor(myPostProcessor);
                }
            };

    private void loadGif(ImageView iv){

        try {
            ImageDecoder.Source source =
                    ImageDecoder.createSource(getResources(), R.drawable.android_er);

            Drawable drawable = ImageDecoder.decodeDrawable(source);

            iv.setImageDrawable(drawable);

            if (drawable instanceof AnimatedImageDrawable) {
                ((AnimatedImageDrawable) drawable).start();
                Toast.makeText(getApplicationContext(),
                        "loadGif: Animation started",
                        Toast.LENGTH_LONG).show();
            }

        } catch (IOException e) {
            e.printStackTrace();
            Toast.makeText(getApplicationContext(),
                    "loadGif: IOException: \n" + e.getMessage(),
                    Toast.LENGTH_LONG).show();
        }

    }

    private void loadRoundGif(ImageView iv){

        try {
            ImageDecoder.Source source =
                    ImageDecoder.createSource(getResources(), R.drawable.android_er);

            //Drawable drawable = ImageDecoder.decodeDrawable(source);
            Drawable drawable = ImageDecoder.decodeDrawable(source,
                    myOnHeaderDecodedListener);

            iv.setImageDrawable(drawable);

            if (drawable instanceof AnimatedImageDrawable) {
                ((AnimatedImageDrawable) drawable).start();
                Toast.makeText(getApplicationContext(),
                        "loadRoundGif: Animation started",
                        Toast.LENGTH_LONG).show();
            }

        } catch (IOException e) {
            e.printStackTrace();
            Toast.makeText(getApplicationContext(),
                    "loadRoundGif: IOException: \n" + e.getMessage(),
                    Toast.LENGTH_LONG).show();
        }

    }

    private void prepareObjectAnimatorRotate(ImageView image){
        TimeInterpolator timeInterpolator =
                new AccelerateDecelerateInterpolator();
        float propertyStart = 0f;
        float propertyEnd = 360f;
        String propertyName = "rotation";
        ObjectAnimator objectAnimator
                = ObjectAnimator.ofFloat(
                        image, propertyName, propertyStart, propertyEnd);
        objectAnimator.setDuration(5000);
        objectAnimator.setRepeatCount(1);
        objectAnimator.setRepeatMode(ObjectAnimator.REVERSE);
        objectAnimator.setInterpolator(timeInterpolator);
        objectAnimator.start();
    }

    private void prepareObjectAnimatorAlpha(ImageView image){
        TimeInterpolator timeInterpolator =
                new AccelerateDecelerateInterpolator();
        float propertyStart = 1f;
        float propertyEnd = 0f;
        String propertyName = "alpha";
        ObjectAnimator objectAnimator
                = ObjectAnimator.ofFloat(
                        image, propertyName, propertyStart, propertyEnd);
        objectAnimator.setDuration(5000);
        objectAnimator.setRepeatCount(1);
        objectAnimator.setRepeatMode(ObjectAnimator.REVERSE);
        objectAnimator.setInterpolator(timeInterpolator);
        objectAnimator.start();
    }
}


Keep using the layout in last post "Apply PostProcess for ImageDecoder".

If you target to animated GIF and it cannot shown, try to disable hardwareAccelerated, refer to "Display animated GIF using ImageDecoder".


Related old post:
Interpolator effect on ObjectAnimator


Sunday, December 30, 2018

Apply PostProcess for ImageDecoder

Last post show a simple example to "Display animated GIF using ImageDecoder". This example show how to implement our own PostProcess, to apply effect on the decoded images.


Modify layout, to have two ImageView:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/background_dark"
    tools:context=".MainActivity">

    <android.support.constraint.Guideline
        android:id="@+id/guideline"
        android:layout_width="1dp"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent="0.5"/>

    <ImageView
        android:id="@+id/gifimage1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toTopOf="@+id/guideline"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

    <ImageView
        android:id="@+id/gifimage2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/guideline"/>

</android.support.constraint.ConstraintLayout>


Java code:
package com.blogspot.android_er.androidimagedecoder;

import android.app.Activity;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ImageDecoder;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.PostProcessor;
import android.graphics.drawable.AnimatedImageDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.widget.ImageView;
import android.widget.Toast;

import java.io.IOException;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ImageView imageView1 = findViewById(R.id.gifimage1);
        ImageView imageView2 = findViewById(R.id.gifimage2);

        loadGif(imageView1);
        loadRoundGif(imageView2);
    }

    PostProcessor myPostProcessor =
            new PostProcessor(){
                @Override
                public int onPostProcess(
                        Canvas canvas) {
                    // This will create rounded corners.
                    Path path = new Path();
                    path.setFillType(Path.FillType.INVERSE_EVEN_ODD);
                    int width = canvas.getWidth();
                    int height = canvas.getHeight();
                    path.addRoundRect(0, 0,
                            width, height, 150, 150,
                            Path.Direction.CW);
                    Paint paint = new Paint();
                    paint.setAntiAlias(true);
                    paint.setColor(Color.TRANSPARENT);
                    paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
                    canvas.drawPath(path, paint);
                    return PixelFormat.TRANSLUCENT;
                }
            };


    ImageDecoder.OnHeaderDecodedListener myOnHeaderDecodedListener =
            new ImageDecoder.OnHeaderDecodedListener(){
                @Override
                public void onHeaderDecoded
                        (ImageDecoder decoder,
                         ImageDecoder.ImageInfo info,
                         ImageDecoder.Source source) {
                    decoder.setPostProcessor(myPostProcessor);
                }
            };

    private void loadGif(ImageView iv){

        try {
            ImageDecoder.Source source =
                    ImageDecoder.createSource(getResources(), R.drawable.android_er);

            Drawable drawable = ImageDecoder.decodeDrawable(source);

            iv.setImageDrawable(drawable);

            if (drawable instanceof AnimatedImageDrawable) {
                ((AnimatedImageDrawable) drawable).start();
                Toast.makeText(getApplicationContext(),
                        "loadGif: Animation started",
                        Toast.LENGTH_LONG).show();
            }

        } catch (IOException e) {
            e.printStackTrace();
            Toast.makeText(getApplicationContext(),
                    "loadGif: IOException: \n" + e.getMessage(),
                    Toast.LENGTH_LONG).show();
        }

    }

    private void loadRoundGif(ImageView iv){

        try {
            ImageDecoder.Source source =
                    ImageDecoder.createSource(getResources(), R.drawable.android_er);

            //Drawable drawable = ImageDecoder.decodeDrawable(source);
            Drawable drawable = ImageDecoder.decodeDrawable(source,
                    myOnHeaderDecodedListener);

            iv.setImageDrawable(drawable);

            if (drawable instanceof AnimatedImageDrawable) {
                ((AnimatedImageDrawable) drawable).start();
                Toast.makeText(getApplicationContext(),
                        "loadRoundGif: Animation started",
                        Toast.LENGTH_LONG).show();
            }

        } catch (IOException e) {
            e.printStackTrace();
            Toast.makeText(getApplicationContext(),
                    "loadRoundGif: IOException: \n" + e.getMessage(),
                    Toast.LENGTH_LONG).show();
        }

    }
}



If you target to animated GIF and it cannot shown, try to disable hardwareAccelerated, refer to "Display animated GIF using ImageDecoder".

Next:
Mix using of ImageDecoder and ObjectAnimator


Saturday, December 29, 2018

Display animated GIF using ImageDecoder

android.graphics.ImageDecoder, introduced from API 28,  is a class for converting encoded images (like PNG, JPEG, WEBP, GIF, or HEIF) into Drawable or Bitmap objects. Before API 28, it have no official supporte for animated GIF, with ImageDecoder, we can easy display animated GIF in our Android app.

Create a new Android project with empty activity target API 28. Currently, ImageDecoder have not imcluded in Support Library, so make sure to uncheck Backwards Compatibility. May be it will be included in someday.




Copy your GIF to /res/drawable / folder


Modify layout to include a ImageView
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <ImageView
        android:id="@+id/gifimage"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

</android.support.constraint.ConstraintLayout>

Example code to display animated GIF using ImageDecoder
package com.blogspot.android_er.androidimagedecoder;

import android.app.Activity;
import android.graphics.ImageDecoder;
import android.graphics.drawable.AnimatedImageDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.widget.ImageView;
import android.widget.Toast;

import java.io.IOException;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ImageView imageView = findViewById(R.id.gifimage);

        loadGif(imageView);
    }

    private void loadGif(ImageView iv){

        try {
            ImageDecoder.Source source =
                    ImageDecoder.createSource(getResources(), R.drawable.android_er);

            Drawable drawable = ImageDecoder.decodeDrawable(source);
            iv.setImageDrawable(drawable);

            if (drawable instanceof AnimatedImageDrawable) {
                ((AnimatedImageDrawable) drawable).start();
                Toast.makeText(getApplicationContext(),
                        "Animation started",
                        Toast.LENGTH_LONG).show();
            }

        } catch (IOException e) {
            e.printStackTrace();
            Toast.makeText(getApplicationContext(),
                    "IOException: \n" + e.getMessage(),
                    Toast.LENGTH_LONG).show();
        }

    }
}


When I test on Android Emulator, I have to disable hardwareAccelerated in AndroidManifest.xml, otherwise the GIF will not shown.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.blogspot.android_er.androidimagedecoder">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity"
            android:hardwareAccelerated="false">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

This video show how it work on Android P Emulator.


Next:
Apply PostProcess for ImageDecoder
Mix using of ImageDecoder and ObjectAnimator

Wednesday, December 12, 2018

Learn Android development with Google Codelabs

Google Developers Codelabs provide a guided, tutorial, hands-on coding experience. Most codelabs will step you through the process of building a small application, or adding a new feature to an existing application. They cover a wide range of topics such as Android Wear, Google Compute Engine, Project Tango, and Google APIs on iOS.



The Advanced Android Development course provides a series of codelabs that teach you how to add advanced features to your Android apps. You'll learn how to extend the user experience, monitor app performance, use geo features, make your apps accessible and implement advanced graphics. This is a follow on course to the Android Developer Fundamentals course.



Wednesday, December 5, 2018

Flutter 1.0 announced, Google’s free and open source SDK for building native iOS and Android apps from a single codebase.

At December 4, 2018, Flutter 1.0 announced, the first stable release of Google's UI toolkit for creating beautiful, native experiences for iOS and Android from a single codebase.



Full livestream of the event Flutter Live, a global celebration of Flutter 1.0 announcement on December 4th, from the Science Museum in London.


Visit:
~ the event website Flutter Live
Flutter website

Introducing Flutter

Friday, November 16, 2018

Kotlin Conference 2018 Opening Keynote

KotlinConf 2018 - Conference Opening Keynote by Andrey Breslav


Andrey Breslav has been leading design and development of the Kotlin Programming Language at JetBrains since 2010 when the project started. He often presents as a speaker at large software conferences and contributes to the Kotlin blog.

Monday, November 12, 2018

Where is the Android SDK location installed

In case you forget the location of Android SDK was installed during the installation of Android Studio. You can find it at:

> Android Studio Menu > File > Project Structure
> It's show in Android SDK Location box





Wednesday, November 7, 2018

Android Dev Summit '18 Keynote


The Android team shares some news from Android Dev Summit, including support for a new form factor (Foldables), updates to Kotlin and Android Jetpack, a new beta release of Android Studio, as well as some new features to the Android App Bundle. Hear directly from Dave Burke, Stephanie Cuthbertson and more from the Android team.

Presented by: Dave Burke, Stephanie Cuthbertson, Jake Wharton, Matt Henderson, Romain Guy, Karen Ng & Aurash Mahbod

Android Dev Summit '18 all sessions playlist → http://bit.ly/ADS18-Sessions


Friday, October 26, 2018

Prevent WebView.loadUrl() to open system browser

It's a simple example to load my a web page in WebView.

package com.blogspot.android_er.myapplicationwebview;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.webkit.WebView;
import android.webkit.WebViewClient;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        WebView myWebView = findViewById(R.id.webview);
        myWebView.getSettings().setJavaScriptEnabled(true);
        myWebView.loadUrl("http://android-er.blogspot.com/");
    }
}


But, when the  myWebView.loadUrl("http://android-er.blogspot.com/") run, the app ask to load in system browser, not the in my WebView.

To solve it (work for me), set WebViewClient before loadUrl.

package com.blogspot.android_er.myapplicationwebview;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.webkit.WebView;
import android.webkit.WebViewClient;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        WebView myWebView = findViewById(R.id.webview);
        myWebView.getSettings().setJavaScriptEnabled(true);

        myWebView.setWebViewClient(new WebViewClient());

        myWebView.loadUrl("http://android-er.blogspot.com/");
    }
}



Layout XML
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    android:orientation="vertical">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="android-er.blogspot.com"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <WebView
        android:id="@+id/webview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />

</LinearLayout>

uses-permission of "android.permission.INTERNET" is need to load from internet.

Google will discontinue support for Nearby Notifications, on December 6th, 2018.

Due to significant increase in irrelevant and spammy notifications that were leading to a poor user experience for Android, Google have decided to discontinue support for Nearby Notifications. Nearby Notifications will be stopped on December 6th, 2018.

source: Android Developers Blog: Discontinuing support for Android Nearby Notifications