1
\$\begingroup\$

This is a pretty basic app I made to collect data necessary for a separate project. However it's also my first go at doing any Android programming, so I know there's definitely issues with it (the code itself is functional and does what I want it to do, I mean issues as in inefficiency, syntax, things like that). What can I do to improve the code and make it more efficient?

These are the four source files of the project:

MainActivity.java:

package com.test.batterytemperaturelogger;

import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;

import java.io.File;

public class MainActivity extends AppCompatActivity {

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

        /* Obtains necessary permissions. */
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            requestPermissions(new String[]{android.Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
            requestPermissions(new String[]{android.Manifest.permission.READ_EXTERNAL_STORAGE}, 1);
        }

        /* Implements functionality for the data logging buttons. */
        OnClickListener listenerLoggerButtons = new OnClickListener(){
            @Override
            public void onClick(View view){
                Intent intent = new Intent(MainActivity.this, TemperatureLoggerService.class);
                switch (view.getId()){
                    case R.id.startLogger:
                        // Start service
                        startService(intent);
                        break;
                    case R.id.stopLogger:
                        stopService(intent);
                        break;
                }
            }
        };
        findViewById(R.id.startLogger).setOnClickListener(listenerLoggerButtons);
        findViewById(R.id.stopLogger).setOnClickListener(listenerLoggerButtons);

        /* Implements functionality for the email data button. */
        OnClickListener listenerEmail = new OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent emailIntent = new Intent(Intent.ACTION_SEND);
                emailIntent.setType("text/plain");
                emailIntent.putExtra(Intent.EXTRA_EMAIL, new String[] {"test1234@gmail.com"});
                emailIntent.putExtra(Intent.EXTRA_SUBJECT, "Battery Temperature Logger Data");
                emailIntent.putExtra(Intent.EXTRA_TEXT, "This email contains the battery temperature database attached.");
                File root = Environment.getExternalStorageDirectory();
                String pathToAttachedFile = TemperatureDBHelper.DATABASE_NAME;
                File file = new File(root, pathToAttachedFile);
                if (!file.exists()){
                    return;
                }
                Uri uri = Uri.fromFile(file);
                emailIntent.putExtra(Intent.EXTRA_STREAM, uri);
                startActivity(Intent.createChooser(emailIntent, "Pick an email provider."));
            }
        };
        findViewById(R.id.emailDB).setOnClickListener(listenerEmail);
    }
}

TemperatureLoggerService.java

package com.test.batterytemperaturelogger;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.BatteryManager;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.support.annotation.RequiresApi;
import android.util.Log;
import android.widget.Toast;

import java.io.IOException;
import java.io.RandomAccessFile;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

public class TemperatureLoggerService extends Service{
    private TemperatureDBHelper temperatureDatabase;
    private Context context;
    public static final String TAG = "TempLoggerService";
    private boolean isRunning = false;
    private TemperatureLoggerServiceHandler TemperatureLoggerServiceHandler;
    DecimalFormat decimalFormat = new DecimalFormat("0.00");
    DecimalFormat decimalFormat2 = new DecimalFormat("0.0000");
    @SuppressLint("SimpleDateFormat")
    DateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");

    @Override
    public void onCreate(){
        HandlerThread handlerThread = new HandlerThread("TemperatureLoggerThread", Process.THREAD_PRIORITY_BACKGROUND);
        handlerThread.start();
        Looper looper = handlerThread.getLooper();
        TemperatureLoggerServiceHandler = new TemperatureLoggerServiceHandler(looper);
        isRunning = true;
        context = getApplicationContext();
        this.temperatureDatabase = new TemperatureDBHelper(context);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId){
        Message message = TemperatureLoggerServiceHandler.obtainMessage();
        message.arg1 = startId;
        TemperatureLoggerServiceHandler.sendMessage(message);
        Toast.makeText(this, "Temperature logging started.", Toast.LENGTH_SHORT).show();

        // If service is killed while starting, it restarts
        return START_STICKY;
    }

    @Override
    public IBinder onBind(Intent intent){
        return null;
    }

    @Override
    public void onDestroy(){
        isRunning = false;
        Toast.makeText(this, "Temperature logging stopped.", Toast.LENGTH_SHORT).show();
        temperatureDatabase.exportDB();
        temperatureDatabase.clearDB();
    }

    /* Calculates and returns battery temperature. */
    public double getBatteryTemperature(){
        Intent intent = context.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
        assert intent != null;
        double celsius = ((double)intent.getIntExtra(BatteryManager.EXTRA_TEMPERATURE,0)) / 10;
        return Double.valueOf(decimalFormat.format(((celsius * 9) / 5) + 32));
    }

    /* Returns battery level. */
    private double getBatteryLevel(){
        Intent intent = context.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
        assert intent != null;
        return (double)intent.getIntExtra(BatteryManager.EXTRA_LEVEL,0);
    }

    /* Returns battery voltage. */
    private double getBatteryVoltage(){
        Intent intent = context.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
        assert intent != null;
        double batteryVoltage = (double)intent.getIntExtra(BatteryManager.EXTRA_VOLTAGE,0);
        batteryVoltage = Double.valueOf(decimalFormat2.format(batteryVoltage / 1000));     /* Convert from millivolts to volts. */
        return batteryVoltage;
    }

    /* Returns instantaneous battery current. */
    private double getBatteryCurrent(){
        BatteryManager batteryManager = (BatteryManager) getSystemService(BATTERY_SERVICE);
        assert batteryManager != null;
        double batteryCurrent = (double)batteryManager.getLongProperty(BatteryManager.BATTERY_PROPERTY_CURRENT_NOW);
        batteryCurrent = Double.valueOf(decimalFormat2.format(batteryCurrent*Math.pow(10, -6)));   /* Convert from microamps to amps. */
        return batteryCurrent;
    }

    /* Returns available memory as a percent value. */
    private double getAvailMemory(){
        ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
        ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
        assert activityManager != null;
        activityManager.getMemoryInfo(memoryInfo);
        double availPercent;
        availPercent = Double.valueOf(decimalFormat2.format(memoryInfo.availMem / (double)memoryInfo.totalMem * 100.0));
        return availPercent;
    }

    /* Calculates and returns the CPU usage. */
    private float getCPULoad() {
        try {
            RandomAccessFile reader = new RandomAccessFile("/proc/stat", "r");
            String load = reader.readLine();

            String[] toks = load.split(" +");  // Split on one or more spaces

            long idle1 = Long.parseLong(toks[4]);
            long cpu1 = Long.parseLong(toks[2]) + Long.parseLong(toks[3]) + Long.parseLong(toks[5])
                    + Long.parseLong(toks[6]) + Long.parseLong(toks[7]) + Long.parseLong(toks[8]);

            try {
                Thread.sleep(400);
            } catch (Exception e) {
                e.printStackTrace();
            }

            reader.seek(0);
            load = reader.readLine();
            reader.close();

            toks = load.split(" +");

            long idle2 = Long.parseLong(toks[4]);
            long cpu2 = Long.parseLong(toks[2]) + Long.parseLong(toks[3]) + Long.parseLong(toks[5])
                    + Long.parseLong(toks[6]) + Long.parseLong(toks[7]) + Long.parseLong(toks[8]);

            float raw_load = (float)(Math.abs(cpu2 - cpu1)) / Math.abs((cpu2 + idle2) - (cpu1 + idle1));
            if (Float.isNaN(raw_load)){
                raw_load = (float)0.0;
            }
            return Float.valueOf(decimalFormat2.format(raw_load*100));

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

        return 0;
    }

    private final class TemperatureLoggerServiceHandler extends Handler{
        TemperatureLoggerServiceHandler(Looper looper){
            super(looper);
        }

        @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
        @Override
        public void handleMessage(Message message){
            synchronized (this){
                while(isRunning){
                    try {
                        Date currentTime = Calendar.getInstance().getTime();
                        String currentTimeString = dateFormat.format(currentTime);
                        double battery_temp = getBatteryTemperature();
                        double battery_level = getBatteryLevel();
                        double battery_voltage = getBatteryVoltage();
                        double battery_current = getBatteryCurrent();
                        double avail_memory = getAvailMemory();
                        float cpu_load = getCPULoad();
                        temperatureDatabase.insertEntry(currentTimeString, battery_temp, battery_level, battery_voltage, battery_current, avail_memory, cpu_load);
                        Log.i(TAG, "TemperatureLoggerService running! " +currentTimeString+ " " +battery_temp+ " " +battery_level+ " "
                                +battery_voltage+ " " +battery_current+ " " +avail_memory+ " " +cpu_load);
                        Thread.sleep(500);
                    }
                    catch(Exception e){
                        Log.i(TAG, e.getMessage());
                    }
                }
            }
            // Stops the service for the start Id
            stopSelfResult(message.arg1);
        }
    }
}

TemperatureDBHelper.java

package com.test.batterytemperaturelogger;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Environment;
import android.util.Log;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;

public class TemperatureDBHelper extends SQLiteOpenHelper{
    private static final int DATABASE_VERSION = 1;
    static final String DATABASE_NAME = "BatteryTemperatures.db";
    private static final String SQL_CREATE_ENTRIES =
        "CREATE TABLE " + TemperatureContract.TemperatureEntry.TABLE_NAME + " (" +
        TemperatureContract.TemperatureEntry._ID + " INTEGER PRIMARY KEY," +
        TemperatureContract.TemperatureEntry.COLUMN_TIME + " TEXT," +
        TemperatureContract.TemperatureEntry.COLUMN_TEMPERATURE + " REAL," +
        TemperatureContract.TemperatureEntry.COLUMN_LEVEL + " REAL," +
        TemperatureContract.TemperatureEntry.COLUMN_VOLTAGE + " REAL," +
        TemperatureContract.TemperatureEntry.COLUMN_CURRENT + " REAL," +
        TemperatureContract.TemperatureEntry.COLUMN_MEMORY + " REAL," +
        TemperatureContract.TemperatureEntry.COLUMN_CPU + " REAL)";

    private static final String SQL_DELETE_ENTRIES =
        "DROP TABLE IF EXISTS " + TemperatureContract.TemperatureEntry.TABLE_NAME;

    TemperatureDBHelper(Context context){
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
        Log.d("Database operations", "Database created!");
    }

    public void onCreate(SQLiteDatabase db){
        db.execSQL(SQL_CREATE_ENTRIES);
        Log.d("Database operations", "Database created!");
    }

    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion){
        // For now the upgrade process just deletes the old database and creates
        // a new one
        db.execSQL(SQL_DELETE_ENTRIES);
        onCreate(db);
        Log.d("Database operations", "Database upgraded!");
    }

    long insertEntry(String time, double temperature, double level, double voltage, double current, double memory, float cpu_load){
        SQLiteDatabase database = this.getWritableDatabase();

        ContentValues values = new ContentValues();
        values.put(TemperatureContract.TemperatureEntry.COLUMN_TIME, time);
        values.put(TemperatureContract.TemperatureEntry.COLUMN_TEMPERATURE, temperature);
        values.put(TemperatureContract.TemperatureEntry.COLUMN_LEVEL, level);
        values.put(TemperatureContract.TemperatureEntry.COLUMN_VOLTAGE, voltage);
        values.put(TemperatureContract.TemperatureEntry.COLUMN_CURRENT, current);
        values.put(TemperatureContract.TemperatureEntry.COLUMN_MEMORY, memory);
        values.put(TemperatureContract.TemperatureEntry.COLUMN_CPU, cpu_load);
        long newRowId = database.insert(TemperatureContract.TemperatureEntry.TABLE_NAME, null, values);

        Log.d("Database operations", "Row inserted!");
        return newRowId;
    }

    public Cursor getEntry(long id){
        SQLiteDatabase database = this.getReadableDatabase();
        Cursor res = database.rawQuery( " SELECT * FROM " + TemperatureContract.TemperatureEntry.TABLE_NAME + " WHERE " +
            TemperatureContract.TemperatureEntry._ID + "=?", new String[]{Long.toString(id)});
        Log.d("Database operations", "Row returned!");
        return res;
    }

    void clearDB(){
        SQLiteDatabase database = this.getWritableDatabase();
        database.delete(TemperatureContract.TemperatureEntry.TABLE_NAME, null, null);
        database.close();
    }

    void exportDB(){
        File sd = Environment.getExternalStorageDirectory();
        File data = Environment.getDataDirectory();
        FileChannel source;
        FileChannel destination;
        String currentDBPath = "/data/" + "com.test.batterytemperaturelogger" + "/databases/" + DATABASE_NAME;
        File currentDB = new File(data, currentDBPath);
        File backupDB = new File(sd, DATABASE_NAME);
        try{
            source = new FileInputStream(currentDB).getChannel();
            destination = new FileOutputStream(backupDB).getChannel();
            destination.transferFrom(source, 0, source.size());
            source.close();
            destination.close();
        } catch(IOException e){
            e.printStackTrace();
        }

    }
}

TemperatureContract.java

package com.test.batterytemperaturelogger;

import android.provider.BaseColumns;

final class TemperatureContract {
    // Prevent someone from accidentally instantiating the contract class; make
    // constructor private.
    private TemperatureContract(){
    }

    /* Inner class that defines the table contents */
    static class TemperatureEntry implements BaseColumns{
        static final String TABLE_NAME = "temperature_database";
        static final String COLUMN_TIME = "time";
        static final String COLUMN_TEMPERATURE = "battery_temperature";
        static final String COLUMN_LEVEL = "battery_level";
        static final String COLUMN_VOLTAGE = "battery_voltage";
        static final String COLUMN_CURRENT = "battery_current";
        static final String COLUMN_MEMORY = "avail_memory";
        static final String COLUMN_CPU = "cpu_load";
    }
}
\$\endgroup\$
  • 2
    \$\begingroup\$ Which "issues" are you talking about? Does it work? \$\endgroup\$ – πάντα ῥεῖ Mar 11 '18 at 17:25
  • \$\begingroup\$ Is the code effective (accomplishes what it is written for)? \$\endgroup\$ – greybeard Mar 11 '18 at 17:35
  • \$\begingroup\$ @πάνταῥεῖ It sounds very normal for beginners to have some self-doubt. There is no specific problem identified, so this review request should be welcomed. \$\endgroup\$ – 200_success Mar 11 '18 at 19:22
  • \$\begingroup\$ Yes, the code works. The notes for posting said that I didn't need to specify whether the code worked or not because it would already be assumed that the code's functional. Sorry about that! Basically I'm just looking for ways to improve my code; how it's written, if there's inefficiencies I should fix, things like that. \$\endgroup\$ – Yuerno Mar 11 '18 at 20:31

Your Answer

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

Browse other questions tagged or ask your own question.