Thursday, April 1, 2021

Android 9.0 USB Camera Implementation

There is also Android AOSP - Supporting External USB Camera this page, which defines same thing posted here, with some more details.

We have a device with an USB HUB connected to it. The external cameras are very flaky. I have tried many different varieties of cameras, and it turns out that not all are directly supported. 

Then I found this library called https://github.com/suethan/AndroidUSBCamera based on multiple other projects. I was able to get the camera working by including this project (libuvccamera) as a dependency and using it to build my app. 2/3 cameras worked well. We used mainly generic USB UVC cameras. 

Now for our purposes, we needed the camera buffer callbacks to send video data over a video call. I am not sure how that works. 

On android 8.0 however I have a completely working implementation. I am not sure of the details, as the modifications to the source were made by my Software Architect. But it works perfectly well. Just plug in the camera and we can use it. However there is quite some work involved. 

I'm working on porting those changes over to the Android Pie, however, I am not sure whether it can be done. It looks doable, but without his guidance or support (he is not available) I am not sure I can get it.

In conclusion, I have to say, yes! I was able to successfully compile and build AOSP Pie on sdm660_64, but it doesn't work as an internal camera yet. I am working on it.
You have to add couple of Kernel level configs, and also then enable some other services. Follow the guide here External USB Cameras

Rest of the details are really not that clear and would love to have someone explain what they did. (without screwing their NDAs of course :-) )
Actually I stand corrected. 

UVC Camera works fine in Android 9.0. Follow the steps and add the required things. 

I was thrown a little off track because we were facing trouble with one particular camera. Now let me tell you how we got it working, and please know this is not a one fix works for all situation. It depends on your requirement and you'll need to lose some to gain some. All cameras may never work.

First things first, install YAWCAM on your PC. Then connect your camera, open yawcam and make sure your device is selected and you can see it's preview. (Note: Yawcam won't start if you have 0 webcams connected).

Next, look under Settings > Device (Device name) > Format Control > Color Space/ Compression. You can have some options or just one option of the following
  • YUV
  • YUV2
  • MJPEG
  • UVY etc.
This helps to find out what formats are supported on your device. 

Now in your AOSP USB Camera HAL, if you check, you might notice that only MJPEG or YUV might be selected. Usually YUV is supported by all cameras. However, MJPEG is not. MJPEG also allows for better resolution. (So that's gonna be a trade off). If you are using Qualcomm HAL, and have enabled all the services and the camera is still not working, then check under ~/android/hardware/qcom/camera/usbcamcore/src/QualcommUsbCamera.cpp 

I noticed that MJPEG was enabled by default, and the 4 cameras I tried, 3 of them supported MJPEG so it worked perfectly. However, we had another camera, which was more important for us than others, and turns out it only supported YUV. So I turned this setting off like so

#define JPEG_ON_USB_CAMERA      0

Notice in this piece how it chooses 

#if JPEG_ON_USB_CAMERA
   rc = initUsbCamera(camHal, camHal->pictWidth, camHal->pictHeight,
                       V4L2_PIX_FMT_MJPEG);
#else
   rc = initUsbCamera(camHal, camHal->pictWidth, camHal->pictHeight,
                       V4L2_PIX_FMT_YUYV);

So that's it people, I hope I have solved all your issues with these damn cameras and the HALs. 

Find update for YUV support here: for interface 3.4



  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
diff --git a/camera/device/3.4/default/ExternalCameraDevice.cpp b/camera/device/3.4/default/ExternalCameraDevice.cpp
index ee7ffaa..853c0a5 100644
--- a/camera/device/3.4/default/ExternalCameraDevice.cpp
+++ b/camera/device/3.4/default/ExternalCameraDevice.cpp
@@ -38,8 +38,10 @@ namespace {
 // Other formats to consider in the future:
 // * V4L2_PIX_FMT_YVU420 (== YV12)
 // * V4L2_PIX_FMT_YVYU (YVYU: can be converted to YV12 or other YUV420_888 formats)
-const std::array<uint32_t, /*size*/1> kSupportedFourCCs {{
-    V4L2_PIX_FMT_MJPEG
+const std::array<uint32_t, /*size*/3> kSupportedFourCCs {{
+    V4L2_PIX_FMT_MJPEG,
+    V4L2_PIX_FMT_YUYV,
+    V4L2_PIX_FMT_YUV420
 }}; // double braces required in C++11
 
 constexpr int MAX_RETRY = 5; // Allow retry v4l2 open failures a few times.
@@ -766,6 +768,7 @@ void ExternalCameraDevice::trimSupportedFormats(
 
     // Remove formats that has aspect ratio not croppable from largest size
     std::vector<SupportedV4L2Format> out;
+    std::vector<SupportedV4L2Format> outFixed;
     for (const auto& fmt : sortedFmts) {
         float ar = ASPECT_RATIO(fmt);
         if (isAspectRatioClose(ar, maxSizeAr)) {
@@ -782,6 +785,14 @@ void ExternalCameraDevice::trimSupportedFormats(
         }
     }
     sortedFmts = out;
+    for (const auto& fmt : sortedFmts) {
+        if (fmt.fourcc == V4L2_PIX_FMT_MJPEG) {
+            outFixed.push_back(fmt);
+        }
+    }
+    if (outFixed.size() > 0) {
+        sortedFmts = outFixed;
+    }
 }
 
 std::vector<SupportedV4L2Format>
diff --git a/camera/device/3.4/default/ExternalCameraDeviceSession.cpp b/camera/device/3.4/default/ExternalCameraDeviceSession.cpp
index 1af3f39..37bf75f 100644
--- a/camera/device/3.4/default/ExternalCameraDeviceSession.cpp
+++ b/camera/device/3.4/default/ExternalCameraDeviceSession.cpp
@@ -1724,33 +1724,60 @@ bool ExternalCameraDeviceSession::OutputThread::threadLoop() {
         return false;
     };
 
-    if (req->frameIn->mFourcc != V4L2_PIX_FMT_MJPEG) {
-        return onDeviceError("%s: do not support V4L2 format %c%c%c%c", __FUNCTION__,
+    if (req->frameIn->mFourcc != V4L2_PIX_FMT_MJPEG && req->frameIn->mFourcc != V4L2_PIX_FMT_YUYV && req->frameIn->mFourcc != V4L2_PIX_FMT_YUV420) {
+        return onDeviceError("%s: do not support V4L2 format %c%c%c%c xxx", __FUNCTION__,
                 req->frameIn->mFourcc & 0xFF,
                 (req->frameIn->mFourcc >> 8) & 0xFF,
                 (req->frameIn->mFourcc >> 16) & 0xFF,
                 (req->frameIn->mFourcc >> 24) & 0xFF);
     }
+    ALOGV("%s: V4L2 format: %c%c%c%c", __FUNCTION__,
+                req->frameIn->mFourcc & 0xFF,
+                (req->frameIn->mFourcc >> 8) & 0xFF,
+                (req->frameIn->mFourcc >> 16) & 0xFF,
+                (req->frameIn->mFourcc >> 24) & 0xFF);
 
     std::unique_lock<std::mutex> lk(mBufferLock);
     // Convert input V4L2 frame to YU12 of the same size
     // TODO: see if we can save some computation by converting to YV12 here
     uint8_t* inData;
     size_t inDataSize;
+    int res = -1;
     req->frameIn->map(&inData, &inDataSize);
-    // TODO: in some special case maybe we can decode jpg directly to gralloc output?
-    ATRACE_BEGIN("MJPGtoI420");
-    int res = libyuv::MJPGToI420(
-            inData, inDataSize,
-            static_cast<uint8_t*>(mYu12FrameLayout.y),
-            mYu12FrameLayout.yStride,
-            static_cast<uint8_t*>(mYu12FrameLayout.cb),
-            mYu12FrameLayout.cStride,
-            static_cast<uint8_t*>(mYu12FrameLayout.cr),
-            mYu12FrameLayout.cStride,
-            mYu12Frame->mWidth, mYu12Frame->mHeight,
-            mYu12Frame->mWidth, mYu12Frame->mHeight);
-    ATRACE_END();
+    ALOGV("%s: Size: %zu, WxH: (%u, %u)", __FUNCTION__, inDataSize, mYu12Frame->mWidth, mYu12Frame->mHeight);
+    if (req->frameIn->mFourcc == V4L2_PIX_FMT_MJPEG) {
+        // TODO: in some special case maybe we can decode jpg directly to gralloc output?
+        ATRACE_BEGIN("MJPGtoI420");
+        res = libyuv::MJPGToI420(
+                inData, inDataSize,
+                static_cast<uint8_t*>(mYu12FrameLayout.y),
+                mYu12FrameLayout.yStride,
+                static_cast<uint8_t*>(mYu12FrameLayout.cb),
+                mYu12FrameLayout.cStride,
+                static_cast<uint8_t*>(mYu12FrameLayout.cr),
+                mYu12FrameLayout.cStride,
+                mYu12Frame->mWidth, mYu12Frame->mHeight,
+                mYu12Frame->mWidth, mYu12Frame->mHeight);
+        ATRACE_END();
+    } else if (req->frameIn->mFourcc == V4L2_PIX_FMT_YUYV && inDataSize != libyuv::kUnknownDataSize ) { //&& inDataSize != libyuv::kUnknownDataSize
+        ALOGV("%s: Converting to I420 | Size: %zu, WxH: (%ux%u), yStride: %u, cStride: %u", __FUNCTION__, inDataSize, mYu12Frame->mWidth, mYu12Frame->mHeight, mYu12FrameLayout.yStride, mYu12FrameLayout.cStride);
+        ATRACE_BEGIN("YUY2ToI420");
+        res = libyuv::YUY2ToI420(inData, mYu12Frame->mWidth * 2,
+                static_cast<uint8_t*>(mYu12FrameLayout.y),
+                mYu12FrameLayout.yStride,
+                static_cast<uint8_t*>(mYu12FrameLayout.cb),
+                mYu12FrameLayout.cStride,
+                static_cast<uint8_t*>(mYu12FrameLayout.cr),
+                mYu12FrameLayout.cStride,
+                mYu12Frame->mWidth, mYu12Frame->mHeight);
+        ATRACE_END();
+    } else{
+        ALOGV("%s: YUV420 format. No conversion", __FUNCTION__);
+        memcpy(static_cast<uint8_t*>(mYu12FrameLayout.y), inData, inDataSize/2);
+        memcpy(static_cast<uint8_t*>(mYu12FrameLayout.cb), (inData + inDataSize/2), inDataSize/4);
+        memcpy(static_cast<uint8_t*>(mYu12FrameLayout.cr), (inData + inDataSize/2 + inDataSize/4), inDataSize/4 );
+        res = 0;
+    }
 
     if (res != 0) {
         // For some webcam, the first few V4L2 frames might be malformed...
diff --git a/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDeviceSession.h b/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDeviceSession.h
index 0b94c11..c764809 100644
--- a/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDeviceSession.h
+++ b/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDeviceSession.h
@@ -108,7 +108,7 @@ struct ExternalCameraDeviceSession : public virtual RefBase {
 
     static const int kMaxProcessedStream = 2;
     static const int kMaxStallStream = 1;
-    static const uint32_t kMaxBytesPerPixel = 2;
+    static const uint32_t kMaxBytesPerPixel = 4;
 
 protected:
 

Tuesday, March 3, 2020

How ROBOTICs and Automation might be taking over!

The world today is changing quicker than before. Machines are making machines, which is something alarming. These machines now require minimal human intervention. By enabling such systems, we have figured out that the same can be applied to any job industry.

For example, the web industry might see a decline in jobs due to the fact that webpages are being generated perfectly by bots at this point and there might be no need for a middle-man/developer. The generated result theme will be within the given guidelines and the machine may produce a better version than the human. Else, consider the heart surgeons, who get paid so high. Machines are able to perfom such complex surguries with very little mediation.

As Kurzgesagt – In a Nutshell, describes in a video of theirs, which seems all too convincing to me, the world is changing too quickly. The video is titled The Rise of the Machines – Why Automation is Different this Time, can be found on youtubehere, which you can watch and decide.

What I feel is that nowadays the growth rate of AI is very widespread. Amazon, Google, and Facebook all have their AI programs going on. In such a scenario, they are trying to automate the redundant tasks to maximize their user productivity. However, certain drawbacks of these technologies are also coming out. Constant user activity monitoring, voice recording are some of the suspicious activity

Many people are scared, including me! The last few years after the internet was made available to us, we have advanced ourselves like never before! Cutting edge technology has become state of the art.

It might be far from perfection but nevertheless, we are on the way..

Tuesday, October 1, 2019

Showing A Custom Toast message in Android

Many a times we have to go beyond the built-in options and build our own custom interfaces. Here I will share with you the code to build your own custom Android Toast Messages.

Code Snippet


//Show custom Toast Message.
Toast t = new Toast(this);
View v = LayoutInflater.from(this).inflate(R.layout.no_data, null);
TextView messageTV = (TextView) v.findViewById(R.id.textViewNoDataMessage);
messageTV.setCompoundDrawablePadding(4);
messageTV.setCompoundDrawablesWithIntrinsicBounds(getResources().getDrawable(R.drawable.bullet_ball_glass_red_16), null, null, null);
messageTV.setText(R.string.device_unauthorized_message);
messageTV.setTextSize(25);
messageTV.setTypeface(Typeface.DEFAULT);
t.setGravity(Gravity.CENTER_VERTICAL, 0, 0);
t.setDuration(Toast.LENGTH_LONG);
t.setView(v);
t.show();



Here you go guys! Hope this helps you! Enjoy!

Saturday, January 12, 2019

Modding like a pro, Need For Speed Most Wanted (2004) Style

Modding like a Pro

Sorry for the delay in my posts, I was held up with some other work!

NOW LET'S TALK NFS
One of the most amazing games by EA, I played it over and over again for over 10 years and still do!


NFS Most Wanted has been an amazing game-changer since its release! 

Today I am bringing you a Hack/Mod to take your gaming experience to the NEXT LEVEL.

Disclaimer: This guide is for educational purposes only. Any mentioned characters or elements of the game is by coincidence only. For more details can be found on my privacy policy.


YOU'LL SEE THIS IF YOU SUCCESSFULLY FIND AND INSTALL ARUSHAN's FERRARI 360 MOD for NFS MW 2005. This is prerequisite.
Let's see what I'm talking about!

I found a MOD for the in-game Lancer. I have always loved Lancer, but it lacked a few things! 

For starters, it's not really a fast car, top speed is low at around 200mph and had only few gears like 5, unlike most supercars with 6. But what I loved about it was the handling and the acceleration, as everyone could guess!

Enter Lancer Evo X. This mod created by some amazing folks and I found on one of the sites, NFSPlanet or NFSCars, was a perfect one. However, they had given me just a stock car, upgradeable in-game as expected. After playing around for a while, I decided that I should take things to the next level. 




One day, I was just modding another car, when I stumbled upon an amazing discovery. I found the exact spot where the car's engine settings were controlled! 

I mean, lets recap on how NFS MW and some other games required MODs to be installed:
  1. Find a nice MOD for something you want to change/replace in-game
  2. Here's the link to what I believe is the same one I have, by NightRaven Mitsubishi Lancer Evo X
  3. MOD creators would include a set of instructions related to installing the mod.
  4. You would follow them to the dot, replace the necessary files from the MOD.zip and everything would work as expected.
  5. Of course there would be some troubleshooting with some mods, but that was mainly it.
  6. There's also a whole story I can tell you on creating your own mods, and using NFS VTLEdit to find the memory addresses for each car that you are modding. I found mine and used them wisely!
So what I stumbled upon was the fact that some of the MOD files for NFS were in plain text. I mean no code necessary! Since I was always interested in Auto, coding and SPEED, I decided to try to modify some of the files to see what works! 

Lo behold! I hit gold with Attributes.mwps


So cutting the story short, here's what you do

Open the Addons Folder, (Created when installing mods)


  1. Next, open the CARS_REPLACE. This is the folder structure created by Arushan, the NFS MW Mod pioneer.
  2. You might need a VTLEdit to find out which addresses map to which one, but that's another tutorial. Required if you don't want to use the links I provided for the car
  3. Next, open up the car folder and you should see this
  4. Open the folder with your favorite editor. 
  5. Then make the changes according to my file here

    #####################################################
    ########## Lancer X Attributes script      ##########
    #####################################################
    ########## Converted by NightRaven         ##########
    ########## PATCHED BY K007SAM              ##########
    ########## nechiporuk-dmitr@mail.ru        ##########
    ########## elite---developers.blogspot.com ##########
    #####################################################

    ##ENGINE
    ##Torque
    patch float bin:0x13d58 900
    patch float bin:0x13d5c 200
    patch float bin:0x13d60 900
    patch float bin:0x13d64 900
    patch float bin:0x13d68 900
    patch float bin:0x13d6c 900
    patch float bin:0x13d70 1000
    patch float bin:0x13d74 1000
    patch float bin:0x13d78 1000

    ##Transmission - Final Gear
    patch float bin:0x20300 3.610

    ##Gears
    patch float bin:0x20288 2.525
    patch float bin:0x2028c 0
    patch float bin:0x20290 2.825
    patch float bin:0x20294 1.625
    patch float bin:0x20298 1.475
    patch float bin:0x2029c 1.055
    patch float bin:0x202a0 0.755
    patch float bin:0x202a4 0.475

    ##Steering
    patch float bin:0x233b8 1.25

    ##MASS
    patch float bin:0x30d18 1250


  6. These values were found by me for each segment on VTLEdit. It was my dream to build a car which is fast, and very agile. This should give a good starting point. NFS MW can be a game you never stop playing, if you get it done right!
  7. Try it out and leave your comments below!
  8. Changing the Torque changes how fast the car accelerates.
  9. Changing the Gears changes the Gear Ratios, which means you can modify the top speed in each gear and also the Top Speed of the car. 
  10. Steering changes how much the car steers and should be set according to the Mass. Mass of course when higher, the car becomes bulkier.
You have a lot of options to try and perfect your dream car. Get going and best of luck from my side. For a demo, just checkout out my new and upcoming channel Retro NFS NFSMWCC if you already haven't. 


Friday, October 19, 2018

[JAVA][Android] Google Nearby API - NearbyManager

Google Nearby API - NearbyManager.java

One of the coolest things I have worked on is the Nearby API.

Here's a couple of resources for you to checkout before you begin!!

  1. Google Nearby - https://developers.google.com/nearby/
  2. Android KT - Nearby API - http://androidkt.com/nearby-connections-api-2-0/
  3. Best Practices - Nearby Communications - https://developer.android.com/distribute/best-practices/engage/nearby-interations
  4. Android Bluetooth Overview - https://developer.android.com/guide/topics/connectivity/bluetooth
  5. Monzo Nearby Friends - https://monzo.com/blog/2018/05/08/nearby-friends/
  6. LinkedIn Nearby (Search or open your LinkedIn on your phone to try with someone!)
Custom made GIF -> Just for you! Download and use. FREE! (Right-Click => Save as)

Nearby Animation GIF
Custom made Nearby GIF (Download FREE)
TO use this GIF, be sure to use a lib like Bumptech Glide to set it to a ImageView in android!

Approaches/Tips

  1. Start by deciding how you want to use this API!
  2. The support for Messages is both Android and IOS, though some apps are having trouble trying to get through Apple's stricter policies when requiring microphone permissions.
  3. The support for Connections API is limited to Android only at this point.
  4. Make a mental model on how you want to build your application. Like shown below.
  5. You will need to create an API key with Google Cloud Console and enable access to Nearby API specifically.
  6. Plus at first application launch, you would have to check for the required permissions as well!
  7. To generate an API key, you would need your SHA1 Android Debug Fingerprint, which is super easy to get BTW. Steps below ->
  8. Next put together all the resources such as animations, images, layouts and strings! (cheap trick for strings: After you type the text inside a layout file, wait Android Studio to highlight it, then just press Alt+Enter and choose "Extract String resource" and voila, your life is set!)
  9. Be sure to add all the dependencies like shown below.
  10. Be sure to configure and add all the API keys and Activities to your Android Manifest as shown below!
  11. And that it you'all!!!!!!!! Now you can begin building that next cool app

Android Manifest

Manifest pt 1

Manifest pt 2


Dependencies Setup

Gradle app config
It doesn't matter if some of the dependencies are not the latest version. Here I chose 4.2.0 because I wasn't able to compile it with 4.8.0 and whatever functionality I required from Glide is working just as fine!

Basic Directory Structure

Directory Structure

Getting your Fingerprint


To get your fingerprint, just run or Double-Click on the Gradle task signingReport and it should run and show you the Fingerprint right in your Console/Debugger/Run task messages window!

Gradle Task to get Fingerprint

The Glorious supporting class

I have made a supporting class to help the new comers engage and better experiment with this API, without needing to get their hands dirty coding the nitty-gritty details!

This is a prerequisite I put together to help people get started with the Nearby API! We don't have a supporting class as such and I figured this would help anyone starting outThis could also help you to find missing pieces in your puzzle! Good luck!





[Class] NearbyManager.java (Just as promised!)

1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.util.Log;
import android.widget.Toast;
import com.google.android.gms.nearby.Nearby;
import com.google.android.gms.nearby.messages.Message;
import com.google.android.gms.nearby.messages.MessageListener;
import com.google.android.gms.nearby.messages.MessagesClient;
import com.google.android.gms.nearby.messages.PublishCallback;
import com.google.android.gms.nearby.messages.PublishOptions;
import com.google.android.gms.nearby.messages.Strategy;
import com.google.android.gms.nearby.messages.SubscribeCallback;
import com.google.android.gms.nearby.messages.SubscribeOptions;
import com.google.android.gms.tasks.OnSuccessListener;

public class NearbyManager {
    private boolean isSubscribing = false;
    private boolean isPublishing = false;
    private Activity current;
    private MessagesClient mMessageClient;
    private MessageListener mMessageListener;
    private static final Strategy PUB_STRATEGY = new Strategy.Builder()
            .setTtlSeconds(Strategy.TTL_SECONDS_DEFAULT).build();
    private static final Strategy SUB_STRATEGY = new Strategy.Builder()
            .setTtlSeconds(Strategy.TTL_SECONDS_MAX).build();
    private String mPubMessage;


    public boolean isSubscribing() {
        return isSubscribing;
    }

    public boolean isPublishing() {
        return isPublishing;
    }

    public NearbyManager(Activity activity){
        current = activity;
        PermissionManager.checkPerms(current);
        mMessageClient = Nearby.getMessagesClient(current);
        mMessageListener = new MessageListener(){
            @Override
            public void onFound(Message message) {
                if(message != null){
                    //DO SOMETHING
                    }
                }
            }
            @Override
            public void onLost(Message message) {
                Toast.makeText(current, "Device is lost", Toast.LENGTH_SHORT).show();
            }
        };

    }
    public void publish(final String message){
        mPubMessage = message;
        PublishOptions options = new PublishOptions
                .Builder()
                .setStrategy(PUB_STRATEGY)
                .setCallback(new PublishCallback(){
                    @Override
                    public void onExpired() {
                        super.onExpired();
                        current.runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                isPublishing = false;
                            }
                        });
                    }
                })
                //.setStrategy(Strategy.BLE_ONLY) <-- removed this
                .build();
        mMessageClient.publish(new Message(message.getBytes()), options).addOnSuccessListener(current, new OnSuccessListener<Void>() {
            @Override
            public void onSuccess(Void aVoid) {
                Toast.makeText(current, "Publishing! Message:" + message, Toast.LENGTH_SHORT).show();
            }
        });
        isPublishing = true;
    }
    public void subscribe(){
        SubscribeOptions options = new SubscribeOptions.Builder()
                .setStrategy(SUB_STRATEGY)
                .setCallback(new SubscribeCallback(){
                    @Override
                    public void onExpired() {
                        super.onExpired();
                        Toast.makeText(current, "No longer Subscribing!", Toast.LENGTH_SHORT).show();
                        current.runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                isSubscribing = false;
                            }
                        });
                    }
                })
                //.setStrategy(Strategy.BLE_ONLY) <-- removed this
                .build();
        mMessageClient.subscribe(mMessageListener, options).addOnSuccessListener(current, new OnSuccessListener<Void>() {
            @Override
            public void onSuccess(Void aVoid) {
                Toast.makeText(current, "Subscribing!", Toast.LENGTH_SHORT).show();
            }
        });
        isSubscribing = true;
    }
    public void unsubscribe(){
        if(isSubscribing && mMessageClient != null)
            mMessageClient.unsubscribe(mMessageListener);
        isSubscribing = false;
    }
    public void unpublish(){
        if(isPublishing && mMessageClient != null)
            mMessageClient.unpublish(new Message(mPubMessage.getBytes()));
        isPublishing = false;
    }

}


So that's that guys! Until next, time, have fun coding!
Hopefully coming soon live demos..Unless NDA 😝