Android how to detect/register language/locale change listener/receiver

The easy way is to register a BroadcastReceiver for Intent.ACTION_LOCALE_CHANGED.

Example: change your ViewModel data when language/locale changed.

public class QASViewModel extends ViewModel {
    @SuppressLint("StaticFieldLeak")
    private final FragmentActivity activity;

    public QASViewModel(@NonNull FragmentActivity activity) {
        this.activity = activity;
        setLangReceiver();
    }

    private void setLangReceiver() {
        final QASViewModel qasViewModel = this;
        final BroadcastReceiver langReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                // do action when language change
            }
        };
        activity.getApplicationContext().registerReceiver(langReceiver,
                new IntentFilter(Intent.ACTION_LOCALE_CHANGED));
    }
}

Android ViewModel with ArgsConstructor via Custom ViewModelFactory

Android ViewModel is very useful.
However ViewModel has no args constructor by default.

Typical usage of ViewModel looks like:

public class UserModel extends ViewModel {
} 
final UserModel viewModel = ViewModelProviders.of(this).get(UserModel.class);

Let's look the definition of ViewModelProviders.of method.

    /**
     * Creates a {@link ViewModelProvider}, which retains ViewModels while a scope of given Activity
     * is alive. More detailed explanation is in {@link ViewModel}.
     * 

* It uses the given {@link Factory} to instantiate new ViewModels. * * @param activity an activity, in whose scope ViewModels should be retained * @param factory a {@code Factory} to instantiate new ViewModels * @return a ViewModelProvider instance */ @NonNull @MainThread public static ViewModelProvider of(@NonNull FragmentActivity activity, @Nullable Factory factory) {

Have you found it? We can pass a Factory to create the ViewModel.
Implement your own Factory then you can have args constructor for your ViewModel.

Here we set up a ViewModel with 1 argument construcgtor.
Example code:

    public QASViewModel(@NonNull FragmentActivity activity) {
        this.activity = activity;
    }
    
QASViewModel qasViewModel = ViewModelProviders.of(getActivity(),
                new QASViewModelFactory(getActivity())).get(QASViewModel.class);
import androidx.annotation.NonNull;
import androidx.fragment.app.FragmentActivity;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;

import java.lang.reflect.InvocationTargetException;

public class QASViewModelFactory extends ViewModelProvider.NewInstanceFactory {

    private final FragmentActivity activity;

    /**
     * Creates a {@code AndroidViewModelFactory}
     *
     * @param activity an AssetManager to pass in {@link QASViewModel}
     */
    public QASViewModelFactory(@NonNull FragmentActivity activity) {
        this.activity = activity;
    }

    @NonNull
    @Override
    public  T create(@NonNull Class modelClass) {
        if (QASViewModel.class.isAssignableFrom(modelClass)) {
            try {
                return modelClass.getConstructor(FragmentActivity.class).newInstance(activity);
            } catch (NoSuchMethodException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            } catch (IllegalAccessException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            } catch (InstantiationException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            } catch (InvocationTargetException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            }
        }
        return super.create(modelClass);
    }
}

How to convert UTC Date Time String to Java object and compare it ?

Use java.time.Instant;

import java.time.Instant;

Instant instantStart = Instant.parse("20200229T12:00:00Z");
Instant instantEnd = Instant.parse("20200329T12:00:00Z");
Instant instantNow = Instant.now();

return instantNow.isAfter(instantStart) &&  
       instantNow.isBefore(instantEnd);

Favourite VIM plugin, Mark : Highlight several words in different colours simultaneously.

my favourite best best best vim plugin

mark

install

vim mark *.vmb.gz :so % 

dependence

ingo

install

vim ingo-library*.vmb.gz :so % 

Usage

\m

How to convert json array to java Object

Use com.amazonaws.util.json.Jackson
import com.amazonaws.util.json.Jackson;
import java.util.ArrayList;
import lombok.Data;

@Data
class DeviceList {
  private ArrayList<String> devices;
}

DeviceList deviceList = Jackson.fromJsonString(
    "{\"devices\":[\"Mobile\", \"Desktop\"]}", 
    DeviceList.class);

Java Spring Bean constructor how to get call stack, backtrace

Easy wasy to print Java call stack or backtrace:
Implement a function and throw an Exception.
Call the function in the place you want to know its backtrace or callstack
And Catch the Exception then
Call Exception.getStackTrace
class MyService {

    public static void f() throws Exception {
        throw new Exception();
    }

    void forTest() {
        try {
            f();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Via this solution, we can get Bean constructor call stack easily.

[tomcat:launchProperties]       at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
[tomcat:launchProperties]       at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
[tomcat:launchProperties]       at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
[tomcat:launchProperties]       at java.lang.reflect.Method.invoke(Method.java:498)
[tomcat:launchProperties]       at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:162)
[tomcat:launchProperties]       at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:588)
[tomcat:launchProperties]       at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1178)
[tomcat:launchProperties]       at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1072)
[tomcat:launchProperties]       at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:511)
[tomcat:launchProperties]       at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:481)
[tomcat:launchProperties]       at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:312)
[tomcat:launchProperties]       at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
[tomcat:launchProperties]       at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:308)
[tomcat:launchProperties]       at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
[tomcat:launchProperties]       at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:208)
[tomcat:launchProperties]       at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1136)
[tomcat:launchProperties]       at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1064)
[tomcat:launchProperties]       at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:583)
[tomcat:launchProperties]       at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)
[tomcat:launchProperties]       at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:364)
[tomcat:launchProperties]       at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1269)
[tomcat:launchProperties]       at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:551)
[tomcat:launchProperties]       at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:481)
[tomcat:launchProperties]       at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:312)
[tomcat:launchProperties]       at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
[tomcat:launchProperties]       at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:308)
[tomcat:launchProperties]       at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
[tomcat:launchProperties]       at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:761)
[tomcat:launchProperties]       at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867)
[tomcat:launchProperties]       at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:543)
[tomcat:launchProperties]       at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:443)
[tomcat:launchProperties]       at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:325)
[tomcat:launchProperties]       at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:107)
[tomcat:launchProperties]       at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4851)
[tomcat:launchProperties]       at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5314)
[tomcat:launchProperties]       at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:145)
[tomcat:launchProperties]       at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1408)
[tomcat:launchProperties]       at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1398)
[tomcat:launchProperties]       at java.util.concurrent.FutureTask.run(FutureTask.java:266)
[tomcat:launchProperties]       at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
[tomcat:launchProperties]       at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
[tomcat:launchProperties]       at java.lang.Thread.run(Thread.java:748)

How to use @Mock @InjectMocks

class MyService {
    private UserDao userDao;
}

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.runners.MockitoJUnitRunner;

public class MyServiceTest {

    @InjectMocks
    private MyService myService;

    @Mock
    private UserDao userDao;
    
    @Before
    public void setUp() {
        myService = new MyService();
    }
}

Cannot mock/spy class java.lang.String Mockito cannot mock/spy following: - final classes - anonymous classes - primitive types

org.mockito.exceptions.base.MockitoException:
Cannot mock/spy class java.lang.String
Mockito cannot mock/spy following:
- final classes
- anonymous classes
- primitive types
at org.mockito.internal.runners.JUnit45AndHigherRunnerImpl$1.withBefores(JUnit45AndHigherRunnerImpl.java:27)
at org.mockito.internal.runners.JUnit45AndHigherRunnerImpl.run(JUnit45AndHigherRunnerImpl.java:37)
at org.mockito.runners.MockitoJUnitRunner.run(MockitoJUnitRunner.java:62)

In case you have to mock String, perhaps you can add a function only visible for testing via
import com.google.common.annotations.VisibleForTesting;
And in this function, mock the String type's value.
For Example:
class MyService {
    
    private String name = "myservice";

    @VisibleForTesting
    void forTest(String mockName) {
        this.name = mockName;
    }
}

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.runners.MockitoJUnitRunner;

public class MyServiceTest {

    private MyService myService;

    @Before
    public void setUp() {
        myService = new MyService();
        myService.forTest("mockName");
    }
}

fixed: embedded-redis: Unable to run on macOS Sonoma

Issue you might see below error while trying to run embedded-redis for your testing on your macOS after you upgrade to Sonoma. java.la...