4
\$\begingroup\$

I'm developing an android app that is basically a text messaging app that allows you to interact with AI.

What should I do about staying organized while starting my first large project? I fear that my code is starting to get messy, and I don't want to practice bad habits or do something that I will regret later. For example, I'm just creating methods when I need them, but I don't know if this is the correct way to write code. Should I keep learning and practicing, or is this experience necessary to get better?

import android.app.Activity;
import android.content.Intent;
import android.content.res.Resources;
import android.os.Handler;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.view.ContextThemeWrapper;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.view.View.OnClickListener;
import android.widget.TextView;
import android.widget.Toast;

public class Activity extends AppCompatActivity implements OnClickListener {

    // This object is used to simulate reading and responding to a text. it can create threads.
    private Handler mHandler = new Handler();

    // initiate variable globally because i need to setClickable throughout the program
    TextView sendButton;

    // I initiate this globally because it's used in multiple methods.
    TextView typingActionBarTextview;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        setContentView(R.layout.activity);
        super.onCreate(savedInstanceState);

        // set up on click listener to start interface
        LinearLayout letsChatActionBar = (LinearLayout) findViewById(R.id.lets_chat_action_bar);
        letsChatActionBar.setOnClickListener(this);

        // set up on click listener to start send_button
        sendButton = (TextView) findViewById(R.id.send_button);
        sendButton.setOnClickListener(this);
        //so the user can't send random messages
        sendButton.setClickable(false);

        // I define in onCreate to use elsewhere. 
        typingActionBarTextview = (TextView) findViewById(R.id.typing_action_bar_textview);

    }

    // performs anything based on what the user clicks on
    @Override
    public void onClick(View view) {
        switch(view.getId()){

            case R.id.lets_chat_action_bar:
                Intent intent = new Intent(this, LetsChatActivity.class);
                startActivityForResult(intent, 1);
                break;

            case R.id.send_button:
                messageSent();
                break;

        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {

        // these two if statements check to make sure the return activity was successful.
        if (requestCode == 1) {
            if(resultCode == Activity.RESULT_OK){

                if(data.getStringExtra("result").equals("hey how are you")) {
                    //howAreYouInterface();
                    typingActionBarTextview.setText("kamusta ka?");

                    sendButton.setClickable(true);
                }
                else {
                    //These toast messages help debug and figure out where I'm at. I'll delete them later.
                    Toast.makeText(PatChatActivity.this, "unit not created", Toast.LENGTH_LONG).show();

                }
            }
            if (resultCode == Activity.RESULT_CANCELED) {
                //Write your code if there's no result
                String errorMessage = "onActivityResult debuggin time.";
                Toast.makeText(PatChatActivity.this, errorMessage, Toast.LENGTH_LONG).show();
            }
        }
    }

    private void messageSent() {
        EditText messageSending = (EditText) findViewById(R.id.message_sending);
        String sentMessage = messageSending.getText().toString();
        hideKeyboard(messageSending);

        //clears edit text
        messageSending.setText("");


        if (sentMessage.equals("kamusta ka?")) {


            // TODO : clean this the fuck up. 
            //hideKeyboard(messageSending);

            final LinearLayout messagesLayout = (LinearLayout) findViewById(R.id.messages_layout);
            TextView sentOne = new TextView(new ContextThemeWrapper(this, R.style.SentTextMessage), null, 0);
            sentOne.setText(sentMessage);

            makeSentMessage(sentOne, messagesLayout);

            final TextView replyOne = new TextView(new ContextThemeWrapper(this, R.style.ContactTextMessage), null, 0);
            replyOne.setText("mabuti naman! ikaw?");


            //TODO: change to reading dots
            Toast.makeText(PatChatActivity.this, "...", Toast.LENGTH_SHORT).show();

            // this pause is to simulate someone reading and replying.
            mHandler.postDelayed(new Runnable() {
                public void run() {
                    makeContactMessage(replyOne, messagesLayout);
                }
            }, 5000);

        } 

        else if (sentMessage.equals("mabuti")) {
            // TODO : clean this the fuck up. 
            //hideKeyboard(messageSending);



            final LinearLayout messagesLayout = (LinearLayout) findViewById(R.id.messages_layout);
            TextView sentOne = new TextView(new ContextThemeWrapper(this, R.style.SentTextMessage), null, 0);
            sentOne.setText(sentMessage);

            makeSentMessage(sentOne, messagesLayout);

            final TextView replyOne = new TextView(new ContextThemeWrapper(this, R.style.ContactTextMessage), null, 0);
            replyOne.setText("ah sige :)");


            Toast.makeText(PatChatActivity.this, "...", Toast.LENGTH_SHORT).show();
            mHandler.postDelayed(new Runnable() {
                public void run() {
                    makeContactMessage(replyOne, messagesLayout);
                }
            }, 5000);
        }

        // if something goes wrong, this is for debugging.
        else {
            Toast.makeText(this, "incorrect typing", Toast.LENGTH_LONG).show();
        }
    }

    private void makeSentMessage(TextView tv, LinearLayout layout) {
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.WRAP_CONTENT,
                LinearLayout.LayoutParams.WRAP_CONTENT
        );
        Resources r = this.getResources();
        int onetwoeightdp = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 128, r.getDisplayMetrics());
        int sixteendp = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 16, r.getDisplayMetrics());

        tv.setTextColor(ContextCompat.getColor(this, R.color.colorTextDefault));

        // .setMargins(left, top, right, bottom)
        params.gravity = Gravity.RIGHT;
        params.setMargins(onetwoeightdp, sixteendp, sixteendp, 0);
        tv.setLayoutParams(params);
        layout.addView(tv);
    }

    private void makeContactMessage(TextView tv, LinearLayout layout) {
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.WRAP_CONTENT,
                LinearLayout.LayoutParams.WRAP_CONTENT
        );
        Resources r = this.getResources();
        int onetwoeightdp = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 128, r.getDisplayMetrics());
        int sixteendp = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 16, r.getDisplayMetrics());

        tv.setTextColor(ContextCompat.getColor(this, R.color.colorTextDefault));

        // .setMargins(left, top, right, bottom)
        params.gravity = Gravity.LEFT;
        params.setMargins(sixteendp, sixteendp, onetwoeightdp, 0);
        tv.setLayoutParams(params);
        layout.addView(tv);

        //sets next textview for what to reply next
        typingActionBarTextview.setText("mabuti");
    }

    private void hideKeyboard(EditText et) {
        InputMethodManager inputMethodManager =(InputMethodManager)getSystemService(Activity.INPUT_METHOD_SERVICE);
        inputMethodManager.hideSoftInputFromWindow(et.getWindowToken(), 0);
    }

}
\$\endgroup\$
3
+50
\$\begingroup\$

Be modular, apply best practices

So far, this is reasonably good, minus the numerous consecutive empty lines. You have related code in understandable chunks so that's one positive that really lends to future maintenance.

Your logic is broken up into methods which is good. It could stand to be even better if we apply some best practices and further modularization to aid in decomposition / flexibility e.g in the messageSent method. I would:

  1. Evaluate sent message once and use a switch statement in lieu of ifs.
  2. Implement string resources rather than raw text to provide flexibility in language setting.
  3. Extract all the inner logic in the conditional blocks to their own methods.

On comments & Javadoc

I see you have some reminder comments, TODOs and maybe some leftovers from copypasta, I would urge you to at least consider multi-line comments for each method. It should state when it would apply, expected inputs, outputs and exceptions. javadoc style is very handy for that and helps you easily produce documentation with a one line command. Check out the offical reference for more detail. However, even prior to that I would remove all commented code. Commented code is dead code and serves no purpose but to distract. If you have something you need to remember later, it should be in a separate reference or TODO text file.

Break up functions further.

This is a partial reiteration of the first point but stands on its own and merits repeating. Try to identify code blocks that you will or can anticipate to reusing. Stratify kindred logic in activities that make sense from auxiliary / necessary functions that aren't necessarily bound to that activity. The latter can be in separate files altogether.

It may not currently seem evident since there isn't much code yet, but in the future having Util & Helper class can come in handy. For example, in any of my Android projects that use a database, I have a dedicated DatabaseHelper class that handles connecting, queries, and exceptions with external calls made at class level using just one method. Similarly, your hideKeyboard method isn't necessarily bound to the Activity itself, it can be apart of a helper class that contains a myriad of defined functions that have project-wide reuse potential in not just this project but future applications as well.

\$\endgroup\$
4
\$\begingroup\$

In terms of staying organized, one thing you could keep in your mind is something called the Single Responsibility Principle. You may have heard about this before, but if not, it basically says that a class or method should do one thing only, and it should do it well.

For instance, in your Activity class, I see it implements an OnClickListener. So, to me, that says that your Activity class should handle click events. I also see several methods relating to messages. These should likely be at least two different classes - a click listener, and a message handler - one class who's responsibility is to handle click events, and another who's responsibility is to handle message processing. The click listener can use the message handler by calling various methods it has publicly available, but it doesn't need to know how the message handler does its work. You could even break things down farther than that, if you wanted to.

Just starting by separating these two concerns should help you organize your code in a cleaner way, and thinking about the single responsibility principle should help you keep things clean as your project grows.

You mention that you just keep creating new methods when you need them. On the surface, there is nothing wrong with that. BUT, as you are doing so, think about whether you are introducing more responsibilities to the class you are working with. For example, I talked about separating your click handling and your message handling. What happens if, in the future, you need to add a different kind of message handling? That would be a new responsibility, probably best suited to a new kind of message handler.

if you're looking for credible sources for information on stuff like this, I recommend Clean Code, by Robert C. Martin (also known as "Uncle Bob"). If you are looking for advice on how to structure and organize your code, and keep things clean and easy to change or extend, I can think of no better source.

Good luck!

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.