Android MVP for multiple user roles - android

I have an activity that shows some of its content based on a users role.
For example home activity shows add/delete views for manager and show add/delete/edit views for senior manager and I am currently using MVP pattern and I need best design patterns for the presentation layer in my case as I have more than 6 user types and may increase.

I just draw my login through coding. Hopefully, It may help you.
Create a sperate Java class and declare all the UI components with
public and static keyword.
public class UI {
public static TextView textView1,textView2;
public static Button add,delete,edit,commit;
}
In your MainActivity initialize the UI Components which declared in
separate java class.
public class MainActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
UI.textView1 = (TextView) findViewById(R.id.tv1);
//.. Intialize one by one there....
}
}
In your presenter Class make a method named UserType(String user)
and start comparing user and set Visibility to UI Components
according to the user.
public void UserType(String user){
if (user.contentEquals("admin")){
UI.add.setVisibility(View.VISIBLE);
UI.delete.setVisibility(View.VISIBLE);
UI.edit.setVisibility(View.VISIBLE);
UI.commit.setVisibility(View.VISIBLE);
}else if (user.contentEquals("user")){
UI.add.setVisibility(View.VISIBLE);
UI.delete.setVisibility(View.VISIBLE);
UI.edit.setVisibility(View.GONE);
UI.commit.setVisibility(View.GONE);
}
}

Related

ViewModel granularity with Activities and Fragments

This question is centered around the architecture of an Android Application. When using the LifeCycle component ViewModel, is it best to have one ViewModel per fragment or one ViewModel for the parent activity, to which the Fragments are subscribed to?
It seems unclear to me how to orient something like a Master-Detail fragment-activity relationship without some coupling. For instance, if each Fragment had it's own ViewModel, it is unclear how the Activity should know how to react without coupling (interface, direct functions calls).
As I mentioned in the comments, there is no unique way to accomplish this, but ideally, and very specifically to your Master/Detail flow concern, let's analyze the default provided example:
ItemDetialActivity handles fragment creation and display, FAB and menu actions. Note that there is nothing related to user data, only "system" handles . I, for instance, try to limit activities responsabilities to navigation, and stuff you really can't avoid like menu button handling. Now, ItemListActivity appears to be violating this principle because takes care of displaying the list (Google examples only create confusion -IMHO- between these separation of concerns), I would create a separate fragment that contains the RecyclerView and its adapter.
Now to the nitty gritty. Here is a very high-level skeleton I hope you can make use of. Check it out, implement it, and come back if there are any questions:
public interface BaseView {
LifecycleOwner lifecycleOwner();
/* perform actions that affect a basic screen status, like hide/show progress bars and errors,
animate views, etc. */
}
public class BaseRepo {
// will contain LiveData instances which will postValues()
}
public class FooRepo extends BaseRepo {
/* will contain access to database and networking functions, either by creating instance methods
or enforcing with an interface, it's up to you. */
}
public class BaseModel<P extends BasePresenter> extends ViewModel {
protected final FooRepo fooRepo; // optional, can be on concretes
<T> void subscribe(LiveData<T> liveData, Observer<T> observer) {
liveData.observe(view.lifecycleOwner(), observer);
}
<T> void unsubscribe(LiveData<T> liveData, Observer<T> observer) {
if (liveData != null) {
liveData.removeObserver(observer);
}
}
...
}
public abstract class BasePresenter<M extends BaseModel, V extends BaseView> implements LifecycleObserver {
protected V view;
protected M model;
public void setModel(M model) {
this.model = model;
}
public final void attachView(V view, Lifecycle lifecycle) {
this.view = view;
lifecycle.addObserver(this);
}
public void setPresenter(P presenter) {
this.presenter = presenter;
this.presenter.setModel(this);
}
...
}
public abstract class BaseFragment implements BaseView {
/* generics is highly encouraged here, I've seen examples of both BasePresenter<P>
and BaseView<P> */
protected P presenter;
/* You should bind layers here, or in the concrete class,
either with Dagger, reflection, or some other way */
#Override
public LifecycleOwner lifecycleOwner() {
return this;
}
...
}
Now, for every concrete screen you should create a presenter, model, and fragment that derive from the bases, and perform specifics there. I hope it helps.

Communicating between components in Android

So I have an Activity. The Activity hosts a ViewPager with tabs, each tab holding a Fragment in it. The Fragments themselves have a RecyclerView each. I need to communicate changes from the RecyclerView's adapter to the activity.
Currently, I am using the listener pattern and communicating using interface between each of the components. i.e I have an interface between the RecyclerView's adapter and the Fragment holding it. Then an interface from the Fragment to the ViewPager's FragmentStatePagerAdapter which is creating all the Fragments. And 1 more interface between the ViewPager's adapter and the Activity hosting the ViewPager. I feel that there are too many interfaces for all the components because of how they are structured.
Currently I am not facing issues as such but I think the listener pattern is acting like an anti-pattern due to all the nested components. Instead of creating independent components I think the hierarchy will make it difficult for making code changes in future.
Am I doing it correctly or is there a better way to do it? Is this a case where I should use an Event Bus or Observer Pattern (If yes can you point me to some examples where someone overcame a similar problems using it)?
NOTE : If it matters, I need it to maintain a global object in the activity, something like a shopping cart where I can add or remove items and these items are present in RecyclerView's adapter from where I can add it to the cart and also increment or decrement the count for a particular item. The ViewPager and Tabs help segregate these items in various categories.
Edit 1 : Some code trying out #LucaNicoletti's approach -
I have skipped one level that is the level with the ViewPager's FragmentStatePagerAdapter. I guess that should not matter and stripped of some other code to keep it small.
MainActivity:
public class MainActivity extends AppCompatActivity implements View.OnClickListener, FoodAdapter.OnFoodItemCountChangeListener {
#Override
public void onFoodItemDecreased(FoodItemModel foodItemModel, int count) {
Log.d("Test", "Dec");
}
#Override
public void onFoodItemIncreased(FoodItemModel foodItemModel, int count) {
Log.d("Test", "Inc");
}
// Other methods here
}
Fragment hosting the Adapter:
public class FoodCategoryListFragment extends Fragment implements FoodAdapter.OnFoodItemCountChangeListener {
// Other boring variables like recyclerview and layout managers
FoodAdapter foodAdapter;
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
// Other boring intializations for recyclerview and stuff
// I set the click listener here directly on the adapter instance
// I don't have this adapter instance in my activity
foodAdapter.setOnFoodItemClickListener(this);
rvFoodList.setAdapter(foodAdapter);
}
}
The adapter class at the lowest level:
public class FoodAdapter extends RecyclerView.Adapter<FoodAdapter.FoodViewHolder> {
private OnFoodItemCountChangeListener onFoodItemCountChangeListener;
private List<FoodItemModel> foodItems;
// The interface
public interface OnFoodItemCountChangeListener {
void onFoodItemIncreased(FoodItemModel foodItemModel, int count);
void onFoodItemDecreased(FoodItemModel foodItemModel, int count);
}
// This is called from the fragment since I don't have the adapter instance
// in my activty
public void setOnFoodItemClickListener(OnFoodItemCountChangeListener onFoodItemCountChangeListener) {
this.onFoodItemCountChangeListener = onFoodItemCountChangeListener;
}
// Other boring adapter stuff here
#Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.bMinus:
onFoodItemCountChangeListener.onFoodItemDecreased(foodItems.get(getAdapterPosition()),
Integer.parseInt(etCounter.getText().toString()));
}
break;
case R.id.bPlus:
onFoodItemCountChangeListener.onFoodItemIncreased(foodItems.get(getAdapterPosition()),
Integer.parseInt(etCounter.getText().toString()));
}
break;
}
}
}
my comments were:
what you should/could do it's to have a global data repo which holds the shopping cart and listeners associated with changes to it. Like a singleton, like ShoppingCart.getInstance().addListener(this); and ShoppingCart.getInstance().addItem(new Item(id));
and
Yes. That's what I'm suggesting. Do not forget that this Singleton can never ever holds Context or Activity because u don't want to leak memory, so always call removeListener. On my opinion it would reduce dependency as all your view controllers only interact with the data model
and I'll add some code to exemplify as a proper answer.
Below is a very crude, typed by heart code, but it should give an idea. All the UI elements are only tied to the data, and not to each other.
Similar stuff could be implemented with libraries that provide observable pattern out of the box for data-only objects.
public class ShoppingCart {
private ShoppingCart single;
private static void init(){
.. init single if not null
}
private List<Item> items = new ArrayList<>();
public int numberOfItems;
public long totalPrice;
private static void addItem(Item item){
init()
single.items.add(item);
single.numberOfItems++;
single.totalPrice+=item.price;
dispatchChange();
}
private static void removeItem(Item item){
init();
single.numberOfItems--;
single.totalPrice-=item.price;
dispatchChange();
single.items.remove(item);
}
private void dispatchChange(){
// TODO: write real loop here
for(single.listeners) listener.onCartChanged(single.cart);
}
public interface Listener {
void onCartChanged(ShoppingCart cart);
}
private List<Listener> listeners = new ArrayList<>();
// TODO: addListener and removeListener code
public static class Item {
String id;
String name;
long price;
}
}
To communicate between components (Activity, Fragment) you have to use an event bus.
In android, you could choose between:
RxJava
Otto
Green Robot EventBus
A blog to explain this.

How to pass a Presenter instance to two activities in Android using MVP pattern

I am attempting to implement the MVP pattern in a new Android application. I have two activities, and have defined two View interfaces, one for each of the activities. For example:
public class MyWindow1 implements IView1
public class MyWindow2 implements IView2
And I have a common Presenter class:
public class MainPresenter implements Serializable {
public MainPresenter() {
}
public void setView1(IView1 view1) {
this.view1 = view1;
}
public void setView2(IView2 view2) {
this.view2 = view2;
}
This is all fairly straightforward so far. Now, the activities register with the presenter when they are first created, for example for view1:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_menu);
mainPresenter = new MainPresenter();
mainPresenter.setView1(this);
}
However, I have a problem now - I need to be able to create activity 2 (i.e. view2) and register it with the Presenter. But I cannot do this as the only (it seems?) way to pass objects between activities is the incredibly impractical method of using serialization. Which doesn't work in this case because I have the references back to the views, which would get lost during serialization.
How can I llink a single presenter to two views, ideally without just declaring the presenter as a static global, which I am very close to doing.

Confused about activities and classes

I'm implementing a list which needs to be displayed by an activity and modified (add items, remove items, sort) by the user. Currently I have two different classes - the activity class and the list class which has all the operations on the list. However, the activity class is going to need access to the list in order to adapt and display it, and it seems kind of clumsy to either duplicate the list for the activity or make the list public. I get the feeling I might not be understanding what activities are correctly - should my two classes really be one single class? I assumed activities were mostly for UI, not for modifying the underlying datastructure.
Activities are the main building blocks of Android, all the different screens that you see in your Application are Activities. You should understand that not all Java Classes used in your Android App are Activities (only those Java Classes that extends Activity are Activities).
The Java Classes that are not Activity (used in your code) can be simply plain Data Models, Custom Adapters, Custom Views, Database Handlers, Services, and so on.
All these Java files are used separately from the Activity Class to provide Modularity and to avoid creating a Mess and deficiency by implementing all the Functionality in a Single Activity Class.
You use the instances of these other Java Classes (or use them statically) in your Activity Class.
For Displaying a Simple List, you can use ListView widget, and you don't really need a separate class for it's implementation. Likewise, if you are preparing to implement a ListView with functionality such as Delete, Add, Update, etc. Then Custom ListView is the alternative option that you can use.
You cannot implement a Custom List View in a single Activity Class, you will be needing a Custom Adapter Class, a Custom Data Model Class and other related classes for it's implementation.
Here is a list of some useful tutorials for implementing Simple and Custom ListViews:
Vogella's ListView Tut
The Open Tutorials
Android Example 1
Android Example 2
I hope this helps.
I'm implementing a list which needs to be displayed by an activity and modified (add items, remove items, sort) by the user.
You can do the simple operations defined by the List<T> interface like add, remove, etc. You can write a custom Comparator<T> to perform sort operations. Then use the Collections.sort() method to do your work.
Currently I have two different classes - the activity class and the list class which has all the operations on the list.
It depends. I generally prefer to make a singleton instance of the list and let my activity modify it on callbacks from ListView. There is nothing clumsy about letting your Activity handle the addition or removal from list.
However, the activity class is going to need access to the list in order to adapt and display it, and it seems kind of clumsy to either duplicate the list for the activity or make the list public.
Like I said, look up what a Singleton Instance is. Share your list across multiple activities by creating a class that has the list in it. Declare the list public. That way, you share the list and do not duplicate it. Remember: if you duplicate the data multiple times. keeping them in sync is going to be a tough nut to crack.
Think about it like that. Your oncreate is called once. If you need things done on the list, they will most probably be on your onItemClick, onItemLongClick kinda events. And when that happens, you should call a AsyncTask, coded in your same activity, so that the onPostExecute can modify its UI elements and the list. Some example below.
Note, the code below has been reduced bigtime, so excuse syntax
package com.taxeetaregistration;
public class Bookings extends ListActivity implements OnClickListener, OnItemClickListener {
private static LayoutInflater inflater = null;
private LinkedList<BookingRecord> bookingRecord;
private ListView customerList;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.bookings);
Log.d("Taxeeta", "Entered BookingExperience");
bookingRecord = new LinkedList<BookingRecord>();
customerList = (ListView) findViewById(android.R.id.list);
customerList.setAdapter(new CustomerList());
customerList.setOnItemClickListener(this);
getBookings = new GetBookings();
getBookings.execute();
}
public class CustomerList extends BaseAdapter implements OnClickListener {
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null || convertView.getTag() == null) {
convertView = inflater.inflate(R.layout.bookingresponse_row, null);
final CustomerViewHolder viewHolder = new CustomerViewHolder();
viewHolder.customerRow = (LinearLayout) convertView.findViewById(R.id.customerRow);
viewHolder.customerName = (TextView) convertView.findViewById(R.id.customerName);
viewHolder.customerPhoneNumber = (TextView) convertView
.findViewById(R.id.customerPhoneNumber);
convertView.setTag(viewHolder);
}
// Setting all values in listview
String temp = bookingRecord.get(position).customer.getFullName();
((CustomerViewHolder) (convertView.getTag())).customerName.setText(temp);
temp = bookingRecord.get(position).customer.getPhoneNumber();
((CustomerViewHolder) (convertView.getTag())).customerPhoneNumber.setText(temp);
return convertView;
}
public class GetBookings extends AsyncTask<Object, Integer, Object> {
#Override
protected Object doInBackground(Object... params) {
connectToServer();
//Do all network related work here, and update
publishProgress(j);
}
}
return null;
}
#Override
public void onPostExecute(Object result) {
super.onPostExecute(result);
if (bookingRecord != null && bookingRecord.size() > 0) {
busy.setVisibility(View.GONE);
((BaseAdapter) customerList.getAdapter()).notifyDataSetChanged();
} else {
progressBarUpper.setVisibility(View.GONE);
Log.d("Taxeeta", "No cabbies found");
}
}
#Override
protected void onProgressUpdate(Integer... i) {
super.onProgressUpdate(i);
boolean found = false;
customersFound.setText("" + totalCabbiesSubscribed);
BookingRecord newRecord = new BookingRecord();
newRecord.customerJourney = customerJourney;
newRecord.customer = customer;
bookingRecord.addLast(newRecord);
customersConfirmed.setText("" + bookingRecord.size());
}
}
private class CustomerViewHolder {
public LinearLayout customerRow;
public TextView customerName;
public TextView customerPhoneNumber;
public TextView customerFrom, customerTo;
public ListView cabbieList;
public float distanceFromCustomer = -1.0f;
}
public class BookingRecord {
public BookingRecord() {
cabbies = new ArrayList<CabbieDetails>();
}
public IJourneyDetails customerJourney;
public IUserDetails customer;
public SearchResultsConcrete cabbieList;
public ArrayList<CabbieDetails> cabbies;
}
}

Basic data storage, for relatively small amounts of info, with a single set of methods

For example, I have a class called animals. I don't think I need to subclass this into birds, mammals, etc, because I only want to have one class that contains methods and ui for all animals, e.g. show animal being born, show animal growing, show animal being butchered, show animal being eaten (aside: yes, this is just an example but you get the picture). However, I will only want to load all the birds and action them then return to the main menu, then navigate to the mammals and show them, and so on. In my real app each "animal" will have 3x int of information only. There is no video but it will have continuous audio and animation.
Since I only use 1/6 of the animal data each time, ie 10-15 animals, maybe 60-100 animals for the entire app what am I best off doing?
Comment turned into answer
I would create an interface called Animal which contains all the functions.
public interface Animal {
public void drawEating();
public void drawDeath();
}
Then create classes implementing this interface:
public class Bird implements Animal {
public void drawEating() {
// TODO: fill in
}
public void drawDeath() {
// TODO: fill in
}
}
To pass the Animal you want to draw to a new Activity you can pass it in a bundle like described here, or create a function in your new activity called registerAnimal and call that from your first activity.
public class AnimalActivity extends Activity {
Animal animal = null;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
public void registerAnimal(Animal a) {
animal = a;
}
public void someDrawFunction() {
if (animal != null)
animal.drawEating();
}
}
Using this scheme you only have one activity for all animals, thus saving lots of repetetive code.
If you like to use some basic fields of animal class i.e name, color, etc you should create an abstract class animal instead an interface.

Categories

Resources