QML: Get Android signal strength level


Second post regarding how to get system info from Android to QML. This post will analyze the way to install an Android listener to be advised when phone signal strength level change.




Full project example can be found here

At first, to get signal strength data, you need to add the following permission to your android app:

android.permission.READ_PHONE_STATE

Since we need to use native Android listener to get the info we need, in consequence, to use a small java code to interface through JNI to our C++ code. There is nothing difficult here, just use the code in the example for your app. The code is quite simple since it export a function called for install the listener and a callback function for allow the change event to advise the C++/QML code on top.

public class SignalStrengthListener
{
    private static Activity m_ActivityInstance;

    public static void Init(Activity ActivityInstance)
    {
        m_ActivityInstance = ActivityInstance;
    }

    public static native void SignalStrengthChanged(int SignalStrength);

    public static void InstallSignalStrengthListener()
    {
        final TelephonyManager TelephonyMngr = (TelephonyManager)m_ActivityInstance.getSystemService(Context.TELEPHONY_SERVICE);

        m_ActivityInstance.runOnUiThread(new Runnable()
        {
            @Override
            public void run()
            {
                TelephonyMngr.listen(new SignalStateListener(), PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
            }
        });
    }

    private static class SignalStateListener extends PhoneStateListener
    {
        @Override
        public void onSignalStrengthsChanged(SignalStrength signalStrength)
        {
            super.onSignalStrengthsChanged(signalStrength);

            SignalStrengthChanged(signalStrength.getGsmSignalStrength());
        }
    }
}

C++ side will call the function to install the listener at startup and register the callback function to be invoked by java code when the signal strength level change.

QObject *pQmlRootObject = NULL;

void AndroidSignalStrengthChanged(JNIEnv *env, jobject thiz, jint signalStrength)
{
    Q_UNUSED(env)
    Q_UNUSED(thiz)

    if(pQmlRootObject != NULL)
    {
        QMetaObject::invokeMethod(pQmlRootObject,
                                  "androidSignalStrengthChanged",
                                  Qt::AutoConnection,
                                  Q_ARG(QVariant, signalStrength)
                                  );
    }
}

void InitializeForAndroid()
{
    JNINativeMethod methods[] = {
        {
            "SignalStrengthChanged",
            "(I)V",
            reinterpret_cast(AndroidSignalStrengthChanged)
        }
    };

    QAndroidJniObject javaClass("com/falsinsoft/example/signalstrength/SignalStrengthListener");
    QAndroidJniEnvironment env;

    jclass objectClass = env->GetObjectClass(javaClass.object());

    env->RegisterNatives(objectClass,
        methods,
        sizeof(methods) / sizeof(methods[0]));
    env->DeleteLocalRef(objectClass);

    QAndroidJniObject::callStaticMethod("com/falsinsoft/example/signalstrength/SignalStrengthListener", "InstallSignalStrengthListener");
}

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;
    engine.load(QUrl(QLatin1String("qrc:/main.qml")));
    if (engine.rootObjects().isEmpty())
        return -1;

    pQmlRootObject = engine.rootObjects().at(0);
    InitializeForAndroid();

    return app.exec();
}

The value returned is an integer reporting the current signal level. There are not some "official" range since it depends by the symbol you use in your app for show the level. In the example I used the range labels as reported into Android emulator as follow:

if(signalStrength >= 30)
    signalStatus = "Great";
else if(signalStrength >= 20)
    signalStatus = "Good";
else if(signalStrength >= 12)
    signalStatus = "Moderate";
else if(signalStrength >= 5)
    signalStatus = "Poor";
else
    signalStatus = "None";

After physical changed in signal level happened the call to the listener is not immediate but take some seconds, nothing to worry about though.



Comments

Popular posts from this blog

Access GPIO from Linux user space

Launch an app from Android shell terminal