1

Will try to keep this as concise as possible...

I would like to have a method in my MainActivty class, say updateUI().

I have another file that is an object called FBManager. That object essentially holds "static" firebase access methods.. let's say signInUser()

From the Activity I call FBManager.signInUser()... It does some logic and moves through 2 private methods and at the last private method, I would like to essentially call MainActivity.updateUI()…

This can't be done as kotlin doesn't have static methods and can't make updateUI() static. MainActivity().updateUI() compiles but is incorrect and don't want to instantiate a new MainActivity. Lastly, I thought of passing the activity into the first constructor like FBManager.signInUser(this)... But after signInUser(), it goes to an override method that can only take a bundle as an optional input (and I don't believe you can put activities into a bundle), thus I can't pass the activity reference to the final private method.....

Edit: Further elaboration

object FBManager {

fun signInUser() {
// Create intent
startActivityForResult(intent)
}

override fun onActivityResult(some intent stuff) {
//Try catch block. In try block call
firebaseAuth(Google account)
}

fun firebaseAuth(acct: GoogleSignInAccount) {
//More logic and an .onCompleteListener

// Inside .onCompeteListener, which is the problem....
MainActivity.updateUI()
}
}

Apologies for layout... Typing on my phone...

-end edit-

I hope all that makes sense as I'm new to programming and find it difficult to explain my problems (since if I truely understood them I could come up with a solution..)

So as per the title, is there any other way to do this... To call a method in MainActivity from an object FBManager?

NicCoe
  • 409
  • 4
  • 17

3 Answers3

9

what about this example? activity

class ExampleActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_example)

        FBManager.signInUser {
            updateUI()
        }
    }

    private fun updateUI() {
        // update UI
    }
}

and FBManager object

object FBManager {
    fun signInUser(callback: () -> Unit) {
        // do work and then
        callback()
    }
}
  • I really like this solution but worried it can't work due to using an onCompleteListener... I have edited the main post to try add a lot more clarification about how my object is set up. If this doesn't work, I'm think Giovanni Terlingen's solution will do the job. – NicCoe Jul 25 '18 at 23:29
  • Answer looks great and simple so I accepted it. After trying different things I started to get my process to work, however I was unable to call startActivityForResult(intent) from FBManager due to weird and unknown reasons... something about null object. So I essentially called that method in MainActivity, which nullifies my whole question. From 'override fun onActivityResult(some intent stuff)', it goes back to FBManager, authorises the sign in account, and can then updateUI() back in MainActivity using a very similar solution to yours. – NicCoe Jul 26 '18 at 11:15
0

and can't make updateUI() static.

True! You want to minimize the usage of static stuff in any of your activities.

I don't know how good you are at Java, but I use the so called observer-pattern a lot for this kind of problems. You might need to convert it to Kotlin.

If the MainActivity is running, it registers to receive events from the FBManager.

I use the following code from Telegram quite a lot:

public class NotificationCenter {

    private static int totalEvents = 1;

    public static final int updateActivity = totalEvents++;

    private final SparseArray<ArrayList<Object>> observers = new SparseArray<>();
    private final SparseArray<ArrayList<Object>> removeAfterBroadcast = new SparseArray<>();
    private final SparseArray<ArrayList<Object>> addAfterBroadcast = new SparseArray<>();

    private int broadcasting = 0;

    public interface NotificationCenterDelegate {
        void didReceivedNotification(int id, Object... args);
    }

    private static volatile NotificationCenter Instance = null;

    public static NotificationCenter getInstance() {
        NotificationCenter localInstance = Instance;
        if (localInstance == null) {
            synchronized (NotificationCenter.class) {
                localInstance = Instance;
                if (localInstance == null) {
                    Instance = localInstance = new NotificationCenter();
                }
            }
        }
        return localInstance;
    }

    public void postNotificationName(final int id, final Object... args) {
        if (Thread.currentThread() == ApplicationLoader.applicationHandler.getLooper().getThread()) {
            postNotificationNameInternal(id, args);
        } else {
            ApplicationLoader.runOnUIThread(new Runnable() {
                @Override
                public void run() {
                    postNotificationNameInternal(id, args);
                }
            });
        }
    }

    private void postNotificationNameInternal(int id, Object... args) {
        broadcasting++;
        ArrayList<Object> objects = observers.get(id);
        if (objects != null && !objects.isEmpty()) {
            for (int a = 0; a < objects.size(); a++) {
                Object obj = objects.get(a);
                ((NotificationCenterDelegate) obj).didReceivedNotification(id, args);
            }
        }
        broadcasting--;
        if (broadcasting == 0) {
            if (removeAfterBroadcast.size() != 0) {
                for (int a = 0; a < removeAfterBroadcast.size(); a++) {
                    int key = removeAfterBroadcast.keyAt(a);
                    ArrayList<Object> arrayList = removeAfterBroadcast.get(key);
                    for (int b = 0; b < arrayList.size(); b++) {
                        removeObserver(arrayList.get(b), key);
                    }
                }
                removeAfterBroadcast.clear();
            }
            if (addAfterBroadcast.size() != 0) {
                for (int a = 0; a < addAfterBroadcast.size(); a++) {
                    int key = addAfterBroadcast.keyAt(a);
                    ArrayList<Object> arrayList = addAfterBroadcast.get(key);
                    for (int b = 0; b < arrayList.size(); b++) {
                        addObserver(arrayList.get(b), key);
                    }
                }
                addAfterBroadcast.clear();
            }
        }
    }

    public void addObserver(Object observer, int id) {
        if (broadcasting != 0) {
            ArrayList<Object> arrayList = addAfterBroadcast.get(id);
            if (arrayList == null) {
                arrayList = new ArrayList<>();
                addAfterBroadcast.put(id, arrayList);
            }
            arrayList.add(observer);
            return;
        }
        ArrayList<Object> objects = observers.get(id);
        if (objects == null) {
            observers.put(id, (objects = new ArrayList<>()));
        }
        if (objects.contains(observer)) {
            return;
        }
        objects.add(observer);
    }

    public void removeObserver(Object observer, int id) {
        if (broadcasting != 0) {
            ArrayList<Object> arrayList = removeAfterBroadcast.get(id);
            if (arrayList == null) {
                arrayList = new ArrayList<>();
                removeAfterBroadcast.put(id, arrayList);
            }
            arrayList.add(observer);
            return;
        }
        ArrayList<Object> objects = observers.get(id);
        if (objects != null) {
            objects.remove(observer);
        }
    }
}

Then make your MainActivity look like this:

public class MainActivity implements NotificationCenter.NotificationCenterDelegate {

    @Override
    public void onPause() {
        NotificationCenter.getInstance().removeObserver(this, NotificationCenter.updateActivity);
        super.onPause();
    }

    @Override
    public void onResume() {
        NotificationCenter.getInstance().addObserver(this, NotificationCenter.updateActivity);
        super.onResume();
    }

    @Override
    public void didReceivedNotification(int id, Object... args) {
        if (id == NotificationCenter.updateActivity) {
            UpdateUI();
        }
    }
}

Then in your FBManager:

NotificationCenter.getInstance().postNotificationName(NotificationCenter.updateActivity, optionalData);

See the other code from this implementation here: https://github.com/gi097/MyWindesheim/tree/master/app/src/main/java/com/giovanniterlingen/windesheim

gi097
  • 7,313
  • 3
  • 27
  • 49
  • Thanks heaps for a speedy reply. I started with Java so I can at least read it and the solution sounds great. Just about to head to sleep but will try it tomorrow and post an update :) – NicCoe Jul 25 '18 at 11:58
0

You can create interface to make interaction between your FBManager and MainActivity. For example:

interface Updatable {
    void updateUi();
}

MainActivity:

class MainActivity implements Updatable {
    ...
    @Override
    void updateUi()
    ...
    FBManager.signInUser(this)
    ...
}

FBManager:

object FBManager {
    ...
    void signInUser(Updatable callback) {
        ...
        callback.updateUi()
        ...
    }
}
redlabrat
  • 507
  • 7
  • 14