3
\$\begingroup\$

Wanted to make a schedule for myself that was (hopefully) light-weight. Right now it just has the bare essential function of saving text for a certain day.

Looking for advice on literally anything that could improve the code ie. more efficient/better style/adhering to standards/etc.

Here is a link to the github repository for easier reading: https://github.com/kalenpw/Kalendar

And the three main classes:

MainActivity.java

/*
    kalenpw
    kalenpwilliams@gmail.com

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    For a copy of the GNU General Public License see
    http://www.gnu.org/licenses/
 */

package com.example.kalenpw.kalendar;

import android.content.Context;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.CalendarView;
import android.widget.EditText;
import java.util.ArrayList;

public class MainActivity extends AppCompatActivity implements View.OnClickListener{
    CalendarView _Calendar = null;
    Button _ButtonSave = null;
    EditText _EditText = null;

    Day  _SelectedDay = null;

    ArrayList<Day> _Schedule = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //Load previous entries
        FileHandler fh = new FileHandler();
        _Schedule = fh.deserializeObject();

        //Setup widgets for easier access
        _Calendar = (CalendarView) this.findViewById(R.id.calendarView);
        _ButtonSave = (Button) this.findViewById(R.id.button8);
        _EditText = (EditText) this.findViewById(R.id.editText);

        //TODO update _SelectedDay so app doesn't crash if a choice is made before changing
        //the date

        //Set up button
        _ButtonSave.setText(R.string.btn_edit);
        updateEditText();

        _ButtonSave.setOnClickListener(this);
        _Calendar.setOnDateChangeListener(new CalendarView.OnDateChangeListener() {
            @Override
            public void onSelectedDayChange(CalendarView view, int year, int month, int dayOfMonth) {
                selectedDateChanged(view, year, month, dayOfMonth);
            }
        });

    }

    /**
     * Method called when the Save/Edit button is clicked
     * @param view
     */
    public void onClick(View view){
        String buttonText = _ButtonSave.getText().toString();

        if(buttonText.equals("Edit")){
            handleEdit();
        }
        else if(buttonText.equals("Save")){
            handleSave(view);
        }

    }

    /**
     * Handles editting of schedule
     */
    private void handleEdit(){
        _ButtonSave.setText(R.string.btn_save);
        updateEditText();
    }

    /**
     * Handles saving schedule
     * @param View view - current view
     */
    private void handleSave(View view){
        _ButtonSave.setText(R.string.btn_edit);
        updateEditText();
        closeOnScreenKeyboard(view);
        if(dayIsAlreadyInList(_SelectedDay)){
            int indexOfDay = getIndexOfSpecificDay(_SelectedDay);
            _SelectedDay.setMessage(_EditText.getText().toString());
            _Schedule.remove(indexOfDay);
            _Schedule.add(indexOfDay, _SelectedDay);
        }
        else{
            _SelectedDay.setMessage(_EditText.getText().toString());
            _Schedule.add(_SelectedDay);
        }

        //Save serialized object
        FileHandler fh = new FileHandler();
        fh.serializeObject(_Schedule);
    }

    /**
     * Method called when CalendarView selected date changes
     * @param view - current view
     * @param year - year selected
     * @param month - month selected
     * @param dayOfMonth - day selected
     */
    private void selectedDateChanged(CalendarView view, int year, int month, int dayOfMonth){
        //Switch back to view mode
        _ButtonSave.setText(R.string.btn_edit);
        _EditText.setText("");
        updateEditText();

        closeOnScreenKeyboard(view);

        Day selectedDay = new Day(year, month, dayOfMonth);
        _SelectedDay = selectedDay;
        if(dayIsAlreadyInList(selectedDay)){
            int indexOfFoundDay = getIndexOfSpecificDay(selectedDay);
            Day day = _Schedule.get(indexOfFoundDay);
            _EditText.setText(day.getMessage());
        }
    }

    /**
     * Returns the index of a specific day from the _Schedule ArrayList
     * Note: this method does no checking that the day exists so ensure
     * dayIsAlreadyInList() is called prior to calling this method
     * @param toFind - Day that needs found
     * @return int - index of given day
     */
    private int getIndexOfSpecificDay(Day toFind){
        for(Day day : _Schedule){
            if(daysAreEqual(toFind, day)){
                return _Schedule.indexOf(day);
            }
        }
        return -1;
    }

    /**
     * Closes the on screenkeyboard
     * @param view - current view
     */
    private void closeOnScreenKeyboard(View view){
        if (view != null) {
            InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
            imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
        }
    }

    /**
     * Checks if two given days are equal ie same day, month, and year
     * @param dayOne - first Day to check
     * @param dayTwo - second Day to check
     * @return boolean - true if they are the same day, otherwise false
     */
    private boolean daysAreEqual(Day dayOne, Day dayTwo){
        boolean areEqual = true;
        if(dayOne.getDay() != dayTwo.getDay() ||
                dayOne.getMonth() != dayTwo.getMonth() ||
                dayOne.getYear() != dayTwo.getYear()){
            areEqual = false;
        }
        return areEqual;
    }

    /**
     * Checks if a given day is already in the _Schedule list
     * @param dayToCheck - Day to check if it is alredy part of list
     * @return boolean - true if the Day is already in list, otherwise false
     */
    private boolean dayIsAlreadyInList(Day dayToCheck){
        boolean alreadyContains = false;
        for(Day day : _Schedule){
            if(daysAreEqual(day, dayToCheck)){
                alreadyContains = true;
                return alreadyContains;
            }
        }
        return alreadyContains;
    }

    /**
     * Updates the status of EditText widget so it can be interacted with properly
     */
    private void updateEditText(){
        String btnText = _ButtonSave.getText().toString();
        if(btnText.equals("Save")){
            _EditText.setEnabled(true);
            _EditText.setFocusable(true);
            _EditText.setFocusableInTouchMode(true);
            _EditText.setCursorVisible(true);
        }
        else if(btnText.equals("Edit")){
            _EditText.setEnabled(true);
            _EditText.setFocusable(false);
            _EditText.setFocusableInTouchMode(false);
            _EditText.setCursorVisible(false);
        }
    }

//
}

Day.java

//Represents a day on calendar with associated message

package com.example.kalenpw.kalendar;

import java.io.Serializable;

public class Day implements Serializable {
    //Fields
    private int _Year = -1;
    private int _Month = -1;
    private int _Day = -1;

    private String _Message;

    //Constructor
    public Day(int newYear, int newMonth, int newDay){
        _Year = newYear;
        _Month = newMonth;
        _Day = newDay;
    }

    //Getters & Setters
    public int getYear(){
        return _Year;
    }
    public int getMonth(){
        return _Month;
    }
    public int getDay(){
        return _Day;
    }
    public void setMessage(String newMessage){
        _Message = newMessage;
    }
    public String getMessage(){
        return _Message;
    }

}

FileHandler.java

//Offloads various file actions to a sepaprate class

package com.example.kalenpw.kalendar;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;

import android.os.Environment;

public class FileHandler {
    private String _completePath;

    //Constructor
    public FileHandler(){
        File sdCard = Environment.getExternalStorageDirectory();
        File directory = new File(sdCard + "/kalendar");
        directory.mkdirs();
        File file =  new File(directory, "Schedule.ser");
        String path = file.getAbsolutePath();
        _completePath = path;
    }

    /**
     * Deserializes an object
     * @return ArrayList<Day> the previously saved entries
     */
    public ArrayList<Day> deserializeObject(){
        ArrayList<Day> list = new ArrayList<>();

        try {
            ObjectInputStream objectIn = new ObjectInputStream(new FileInputStream(_completePath));
            list = (ArrayList<Day>) objectIn.readObject();
            objectIn.close();

        }
        catch(Exception e){
            e.printStackTrace();
        }

        return list;
    }

    /**
     * Serializes an object
     * @param objToSerialize - object to be serialized
     */
    public void serializeObject(Object objToSerialize){
        try {
            ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream(_completePath));
            outputStream.writeObject(objToSerialize);
            outputStream.close();

            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            outputStream = new ObjectOutputStream(bos);
            outputStream.writeObject(objToSerialize);
            outputStream.close();
        }
        catch(IOException e){
            e.printStackTrace();
        }
    }
//
}
\$\endgroup\$
  • \$\begingroup\$ You may already know this but on Android 6.0 and up apps that use "dangerous permissions" need to ask for permission at runtime. As far as I know writing to the sd card needs WRITE_EXTERNAL_STORAGE which is a dangerous permission developer.android.com/guide/topics/permissions/… \$\endgroup\$ – Bethan Mar 4 '17 at 13:11
  • \$\begingroup\$ @Bethan I actually didn't know that. Thanks for pointing that out however, I believe it does that automatically because when the app is launched for the first time it asks you to enable some permissions for that \$\endgroup\$ – kalenpw Mar 4 '17 at 19:31
2
\$\begingroup\$

Please have a look at Java naming convention. "Except for variables, all instance, class, and class constants are in mixed case with a lowercase first letter. Internal words start with capital letters. Variable names should not start with underscore _ or dollar sign $ characters, even though both are allowed."

I also would reccomend not to do heavy operations in main thread, as for example, in your code in onCreate() method

    _Schedule = fh.deserializeObject();

It may cause ANR - Application Not Responding

\$\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.