diff --git a/app/build.gradle b/app/build.gradle index 6818f34..c3f3c01 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,8 +1,9 @@ apply plugin: 'com.android.application' +apply plugin: 'com.neenbedankt.android-apt' android { compileSdkVersion 23 - buildToolsVersion "23.0.1" + buildToolsVersion "23.0.2" defaultConfig { applicationId "net.kacpak.batterychargingmonitor" @@ -13,26 +14,42 @@ android { } buildTypes { release { - minifyEnabled false + minifyEnabled true + shrinkResources true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } + debug { + debuggable true + minifyEnabled true + applicationIdSuffix ".debug" + versionNameSuffix '.debug' + } } } -def supportLibrary = "23.1.1" +repositories { + maven { url "https://jitpack.io" } // To compile from GitHub when MavenCentral is outdated (circleprogress) +} + +ext { + supportLibrary_version = "23.3.0" + circleProgress_version = "a947edf" // TODO change to new release when startingPoint is merged + butterKnife_version = "8.0.1" +} dependencies { - compile fileTree(dir: 'libs', include: ['*.jar']) + compile fileTree(include: ['*.jar'], dir: 'libs') testCompile 'junit:junit:4.12' - // Support Libraries - compile "com.android.support:appcompat-v7:$supportLibrary" - compile "com.android.support:design:$supportLibrary" - compile "com.android.support:support-annotations:$supportLibrary" - + compile "com.android.support:appcompat-v7:$supportLibrary_version" + compile "com.android.support:design:$supportLibrary_version" + compile "com.android.support:support-annotations:$supportLibrary_version" + compile "com.android.support:cardview-v7:$supportLibrary_version" + compile "com.android.support:gridlayout-v7:$supportLibrary_version" + compile "com.android.support:recyclerview-v7:$supportLibrary_version" // Circle Progress - compile 'com.github.lzyzsd:circleprogress:1.1.0@aar' - + compile "com.github.lzyzsd:circleprogress:$circleProgress_version" // Butter Knife - compile 'com.jakewharton:butterknife:7.0.1' + compile "com.jakewharton:butterknife:$butterKnife_version" + apt "com.jakewharton:butterknife-compiler:$butterKnife_version" } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index fb1eace..eb50468 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,12 +2,16 @@ + + + + android:theme="@style/AppTheme"> @@ -17,6 +21,26 @@ + + + + + + + + + + + + + diff --git a/app/src/main/java/net/kacpak/batterychargingmonitor/App.java b/app/src/main/java/net/kacpak/batterychargingmonitor/App.java new file mode 100644 index 0000000..39704b4 --- /dev/null +++ b/app/src/main/java/net/kacpak/batterychargingmonitor/App.java @@ -0,0 +1,19 @@ +package net.kacpak.batterychargingmonitor; + +import android.app.Application; +import android.content.Context; + +public class App extends Application { + + private static Context mContext; + + @Override + public void onCreate() { + super.onCreate(); + mContext = this; + } + + public static Context getContext(){ + return mContext; + } +} \ No newline at end of file diff --git a/app/src/main/java/net/kacpak/batterychargingmonitor/data/BatteryDataRepository.java b/app/src/main/java/net/kacpak/batterychargingmonitor/data/BatteryDataRepository.java index 18793d2..f75f724 100644 --- a/app/src/main/java/net/kacpak/batterychargingmonitor/data/BatteryDataRepository.java +++ b/app/src/main/java/net/kacpak/batterychargingmonitor/data/BatteryDataRepository.java @@ -1,9 +1,232 @@ package net.kacpak.batterychargingmonitor.data; +import android.content.ContentValues; +import android.content.Context; +import android.content.SharedPreferences; +import android.database.Cursor; +import android.net.Uri; +import android.preference.PreferenceManager; + +import net.kacpak.batterychargingmonitor.R; +import net.kacpak.batterychargingmonitor.data.database.ChargeInformation; +import net.kacpak.batterychargingmonitor.data.database.DatabaseContract.DataEntry; + +import java.util.Arrays; +import java.util.Date; +import java.util.List; + +/** + * Zarządzanie danymi o baterii i historii ładowania + */ public class BatteryDataRepository { - static int p = 0; + private final Context mContext; + + /** + * Tworzy nowy obiekt do zarządzania danymi o baterii i historii ładowania + * @param context {@see Context} aplikacji + */ + public BatteryDataRepository(Context context) { + mContext = context; + } + + /** + * Obecny stan baterii + */ public BatteryStatus getStatus() { - return new BatteryStatus(++p); + return new BatteryStatus(); + } + + /** + * Zwraca stan ładowania baterii + * @param chargeId id z historii ładowania baterii + * @return stan ładowania baterii + */ + public ChargeInformation getChargeInformation(long chargeId) { + Uri dataUri = DataEntry.buildUri(chargeId); + Cursor entryInfo = mContext.getContentResolver().query( + dataUri, + null, + null, + null, + null + ); + + if (null == entryInfo) + return null; + + entryInfo.moveToFirst(); + return new ChargeInformation(entryInfo); + } + + /** + * Dodaje nowy wpis do historii ładowania + * @param start Data i godzina rozpoczęcia ładowania + * @param type Typ ładowarki + * @param percentage Początkowy procent naładowania baterii + * @param temperatureCelsius Temperatura w Celsjuszach + * @param voltage Początkowe napięcie na baterii + */ + public Uri add(Date start, int type, int percentage, float temperatureCelsius, int voltage) { + ContentValues values = new ContentValues(); + values.put(DataEntry.COLUMN_TYPE, type); + values.put(DataEntry.COLUMN_START, start.getTime()); + values.put(DataEntry.COLUMN_START_PERCENTAGE, percentage); + values.put(DataEntry.COLUMN_START_TEMPERATURE_C, temperatureCelsius); + values.put(DataEntry.COLUMN_START_VOLTAGE, voltage); + + return mContext.getContentResolver().insert( + DataEntry.CONTENT_URI, + values + ); + } + + /** + * Zakańcza ładowanie baterii + * @param stop Data i godzina zakończenia ładowania + * @param percentage Końcowy procent naładowania baterii + * @param temperatureCelsius Temperatura w Celsjuszach + * @param voltage Ostateczne napięcie na baterii + * @param note Notatka do ładowania + */ + public int finishCharging(Date stop, int percentage, float temperatureCelsius, int voltage, String note) { + ContentValues values = new ContentValues(); + values.put(DataEntry.COLUMN_CHARGE_FINISHED, 1); + values.put(DataEntry.COLUMN_STOP, stop.getTime()); + values.put(DataEntry.COLUMN_STOP_PERCENTAGE, percentage); + values.put(DataEntry.COLUMN_STOP_TEMPERATURE_C, temperatureCelsius); + values.put(DataEntry.COLUMN_STOP_VOLTAGE, voltage); + values.put(DataEntry.COLUMN_NOTE, note); + + return mContext.getContentResolver().update( + DataEntry.CONTENT_URI_UNFINISHED, + values, + null, + null + ); + } + + /** + * Usuwa z historii podane wpisy + * @param ids ID wpisów w bazie danych + */ + public int delete(List ids) { + String inClause = ids.toString().replace("[", "(").replace("]", ")"); + + return mContext.getContentResolver().delete( + DataEntry.CONTENT_URI, + DataEntry._ID + " IN " + inClause, + null + ); + } + + /** + * Usuwa nic nie wnoszące wpisy z historii (krótsze niż podane w preferencjach) + */ + public int deleteIrrelevant() { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext); + int minDuration = 0; + try { + String irrelevantDurationPreference = prefs.getString(mContext.getString(R.string.pref_key_irrelevant_duration), "0"); + minDuration = Integer.parseInt(irrelevantDurationPreference) * 1000; + } catch (Exception e) { + // Do nothing + } + + return mContext.getContentResolver().delete( + DataEntry.CONTENT_URI, + "CAST(ABS(" + DataEntry.COLUMN_STOP + " - " + DataEntry.COLUMN_START + ") AS INTEGER) <= " + minDuration, + null + ); + } + + /** + * Łączy podane wpisy w jeden + * @param ids ID wpisów w bazie danych + */ + public int merge(List ids) { + // Będziemy szukać tylko wpisów z id "(a, b, ...)" + String inClause = ids.toString().replace("[", "(").replace("]", ")"); + + Cursor entries = mContext.getContentResolver().query( + DataEntry.CONTENT_URI, + null, + DataEntry._ID + " IN " + inClause, + null, + DataEntry.COLUMN_START + " ASC" + ); + + // Ustalam typ wpisu dla łączenia + entries.moveToFirst(); + final int typeColumnIndex = entries.getColumnIndex(DataEntry.COLUMN_TYPE); + int type = entries.getInt(typeColumnIndex); + while (entries.moveToNext()) { + if (entries.getInt(typeColumnIndex) != type) { + type = 0; + break; + } + } + + // Wartości do wpisania + ContentValues values = new ContentValues(); + + // Sczytujemy część początkową + entries.moveToFirst(); + values.put(DataEntry.COLUMN_START, entries.getLong(entries.getColumnIndex(DataEntry.COLUMN_START))); + values.put(DataEntry.COLUMN_START_PERCENTAGE, entries.getInt(entries.getColumnIndex(DataEntry.COLUMN_START_PERCENTAGE))); + values.put(DataEntry.COLUMN_START_TEMPERATURE_C, entries.getFloat(entries.getColumnIndex(DataEntry.COLUMN_START_TEMPERATURE_C))); + values.put(DataEntry.COLUMN_START_VOLTAGE, entries.getInt(entries.getColumnIndex(DataEntry.COLUMN_START_VOLTAGE))); + values.put(DataEntry.COLUMN_TYPE, type); + + // Sczytujemy wartość końcową + entries.moveToLast(); + values.put(DataEntry.COLUMN_STOP, entries.getLong(entries.getColumnIndex(DataEntry.COLUMN_STOP))); + values.put(DataEntry.COLUMN_STOP_PERCENTAGE, entries.getInt(entries.getColumnIndex(DataEntry.COLUMN_STOP_PERCENTAGE))); + values.put(DataEntry.COLUMN_STOP_TEMPERATURE_C, entries.getFloat(entries.getColumnIndex(DataEntry.COLUMN_STOP_TEMPERATURE_C))); + values.put(DataEntry.COLUMN_STOP_VOLTAGE, entries.getInt(entries.getColumnIndex(DataEntry.COLUMN_STOP_VOLTAGE))); + + if (entries.getInt(entries.getColumnIndex(DataEntry.COLUMN_CHARGE_FINISHED)) == 1) + values.put(DataEntry.COLUMN_CHARGE_FINISHED, 1); + + entries.close(); + + + mContext.getContentResolver().insert( + DataEntry.CONTENT_URI, + values + ); + + return delete(ids); + } + + /** + * Ilość ładowań z uwzględnieniem preferencji + */ + public int getChargedCount() { + return getChargedCount(true); + } + + /** + * Ilość ładowań + * @param withPreferences true dla uwzględnienia danych od użytkownika + */ + public int getChargedCount(boolean withPreferences) { + Cursor countCursor = mContext.getContentResolver().query( + DataEntry.CONTENT_URI, + new String[] {"count(*) AS count"}, + null, + null, + null + ); + + int count = countCursor.moveToFirst() ? countCursor.getInt(0) : 0; + countCursor.close(); + + // Jeśli nie uwzględniamy preferencji, zwróć wartość + if (!withPreferences) + return count; + + // Jeśli je uwzględniamy dodaj je do wyniku + return count + new UserPreferences(mContext).getPreviousChargesCount(); } } diff --git a/app/src/main/java/net/kacpak/batterychargingmonitor/data/BatteryStatus.java b/app/src/main/java/net/kacpak/batterychargingmonitor/data/BatteryStatus.java index 4c42fdb..1b94b3d 100644 --- a/app/src/main/java/net/kacpak/batterychargingmonitor/data/BatteryStatus.java +++ b/app/src/main/java/net/kacpak/batterychargingmonitor/data/BatteryStatus.java @@ -1,13 +1,259 @@ package net.kacpak.batterychargingmonitor.data; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.BatteryManager; +import android.support.annotation.NonNull; +import android.util.Log; + +import net.kacpak.batterychargingmonitor.App; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; + +/** + * Aktualny status baterii + */ public class BatteryStatus { - private final int percentage; - public BatteryStatus(int percentage) { - this.percentage = percentage; + /** + * Battery Status Changed Intent + */ + private Intent mBatteryStatus; + + /** + * Obecne natężenie prądu + */ + private int mCurrent; + + /** + * Średnie natężenie prądu + */ + private int mCurrentAvg; + + /** + * Ścieżka do folderu systemowego zawierającego dane o stanie baterii + */ + private static final String mBatteryDataPath = "/sys/class/power_supply/battery/"; + + /** + * Tworzy obiekt do odczytu stanu baterii + */ + public BatteryStatus() { + IntentFilter mFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); + mBatteryStatus = App.getContext().registerReceiver(null, mFilter); + mCurrent = readBatteryStatus("current_now"); + mCurrentAvg = readBatteryStatus("current_avg"); + } + + /** + * Zwraca zadaną wartość z obecnego stanu baterii lub {@see defaultValue} + * @param name wybrana stała z {@see BatteryManager} + */ + private int getBatteryStatusExtra(String name) { + return getBatteryStatusExtra(name, -1); + } + + /** + * Zwraca zadaną wartość z obecnego stanu baterii lub {@see defaultValue} + * @param name wybrana stała z {@see BatteryManager} + * @param defaultValue wartoć domyślna + */ + private int getBatteryStatusExtra(String name, int defaultValue) { + return mBatteryStatus.getIntExtra(name, defaultValue); + } + + /** + * Procent naładowania baterii + */ + public int getChargePercentage() { + int level = getBatteryStatusExtra(BatteryManager.EXTRA_LEVEL); + int scale = getBatteryStatusExtra(BatteryManager.EXTRA_SCALE); + return (int)(100 * level / (float)scale); + } + + /** + * Zwraca true jeśli telefon jest w trakcie ładowania lub ukończył ładowanie + */ + public boolean isCharging() { + int status = getBatteryStatusExtra(BatteryManager.EXTRA_STATUS); + return status == BatteryManager.BATTERY_STATUS_CHARGING || status == BatteryManager.BATTERY_STATUS_FULL; + } + + /** + * Zwraca stałą BATTERY_PLUGGED z {@see BatteryManager} + * @return 1: BATTERY_PLUGGED_AC, 2: BATTERY_PLUGGED_USB, 3: BATTERY_PLUGGED_WIRELESS + */ + public int getPluggedInformation() { + return getBatteryStatusExtra(BatteryManager.EXTRA_PLUGGED); + } + + /** + * Czy bateria jest ładowana z portu USB + */ + public boolean isPluggedUSB() { + return getPluggedInformation() == BatteryManager.BATTERY_PLUGGED_USB; + } + + /** + * Czy bateria ładowana jest ładowarką przewodową + */ + public boolean isPluggedAC() { + return getPluggedInformation() == BatteryManager.BATTERY_PLUGGED_AC; + } + + /** + * Czy bateria ładowana jest bezprzewodowo + */ + public boolean isPluggedWireless() { + return getPluggedInformation() == BatteryManager.BATTERY_PLUGGED_WIRELESS; + } + + /** + * Zwraca stałą BATTERY_HEALTH z {@see BatteryManager} + * @return 1: UNKNOWN, 2: GOOD, 3: OVERHEAT, 4: DEAD, 5: OVER_VOLTAGE, 6: UNSPECIFIED_FAILURE, 7: COLD + */ + public int getHealthInformation() { + return getBatteryStatusExtra(BatteryManager.EXTRA_HEALTH); + } + + /** + * Czy stan baterii nieokreślony + */ + public boolean isHealthUnknown() { + return getHealthInformation() == 1; + } + + /** + * Czy stan baterii dobry + */ + public boolean isHealthGood() { + return getHealthInformation() == 2; + } + + /** + * Czy bateria się przegrzewa + */ + public boolean isHealthOverheat() { + return getHealthInformation() == 3; + } + + /** + * Czy bateria zużyta + */ + public boolean isHealthDead() { + return getHealthInformation() == 4; + } + + /** + * Czy bateria ładowana zbyt wysokim prądem + */ + public boolean isHealthOverVoltage() { + return getHealthInformation() == 5; + } + + /** + * Czy bateria uległa niezidentyfikowanemu uszkodzeniu + */ + public boolean isHealthUnspecifiedFailure() { + return getHealthInformation() == 6; + } + + /** + * Czy bateria pracuje przy zbyt niskiej temperaturze + */ + public boolean isHealthCold() { + return getHealthInformation() == 7; + } + + /** + * Temperatura baterii w stopniach Celsjusza + */ + public float getTemperatureInCelsius() { + return getBatteryStatusExtra(BatteryManager.EXTRA_TEMPERATURE) / (float)10; + } + + /** + * Temperatura baterii w stopniach Fahrenheit'a + */ + public float getTemperatureInFahrenheit() { + return getTemperatureInCelsius() * 9 / 5 + 32; + } + + /** + * Napięcie baterii w mV + */ + public int getVoltage() { + return getBatteryStatusExtra(BatteryManager.EXTRA_VOLTAGE); + } + + /** + * Natężenie prądu w mA + */ + public int getCurrent() { + return mCurrent; + } + + /** + * Informuje czy dane o obecnym natężeniu prądu są dostępne + */ + public boolean isCurrentAvailable() { + return isBatteryStatusAvailable("current_now"); + } + + /** + * Średnie natężenie prądu w mA + */ + public int getCurrentAverage() { + return mCurrentAvg; + } + + /** + * Informuje czy dane o średnim natężeniu prądu są dostępne + */ + public boolean isCurrentAverageAvailable() { + return isBatteryStatusAvailable("current_avg"); + } + + /** + * Zwraca wartość z danego pliku lub 0 + * @param filename + */ + private int readBatteryStatus(@NonNull String filename) { + File file = new File(mBatteryDataPath + filename); + + if (file.exists()) { + BufferedReader reader = null; + try { + reader = new BufferedReader(new FileReader(file)); + String line = reader.readLine(); + reader.close(); + + return Integer.parseInt(line) / 1000; + + } catch (Exception e) { + Log.e("Read file: " + filename, e.toString()); + + } finally { + try { + if (reader != null) reader.close(); + } catch (IOException e) { + Log.e("Close file: " + filename, e.toString()); + } + } + } + + return 0; } - public int getPercentage() { - return percentage; + /** + * Sprawdza czy dany stan baterii jest przechowywany w plikach systemowych + * @param filename nazwa pliku + */ + private boolean isBatteryStatusAvailable(@NonNull String filename) { + File file = new File(mBatteryDataPath + filename); + return file.exists(); } } diff --git a/app/src/main/java/net/kacpak/batterychargingmonitor/data/UserPreferences.java b/app/src/main/java/net/kacpak/batterychargingmonitor/data/UserPreferences.java new file mode 100644 index 0000000..2e239fd --- /dev/null +++ b/app/src/main/java/net/kacpak/batterychargingmonitor/data/UserPreferences.java @@ -0,0 +1,64 @@ +package net.kacpak.batterychargingmonitor.data; + +import android.content.Context; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; + +import net.kacpak.batterychargingmonitor.R; + +public class UserPreferences { + + private final Context mContext; + private final SharedPreferences mPreferences; + + public UserPreferences(Context context) { + mContext = context; + mPreferences = PreferenceManager.getDefaultSharedPreferences(context); + } + + /** + * Czy temperatura baterii powinna być wyświetlana w stopniach Celsjusza + */ + public boolean isTemperatureInCelsius() { + try { + return mPreferences.getBoolean( + mContext.getString(R.string.pref_key_temperature_celsius), + Boolean.parseBoolean(mContext.getString(R.string.pref_key_temperature_celsius_default)) + ); + + } catch (Exception e) { + return true; + } + } + + /** + * Ilość podanych ładowań baterii przed instalacją aplikacji + */ + public int getPreviousChargesCount() { + try { + return Integer.parseInt( + mPreferences.getString( + mContext.getString(R.string.pref_key_add_to_count), + mContext.getString(R.string.pref_key_add_to_count_default) + )); + + } catch (Exception e) { + return 0; + } + } + + /** + * Zwraca minimalny czas w sekundach, po którym wpisy nie będą automatycznie usuwane + */ + public int getIrrelevantChargeDuration() { + try { + return Integer.parseInt(mPreferences.getString( + mContext.getString(R.string.pref_key_irrelevant_duration), + mContext.getString(R.string.pref_key_irrelevant_duration_default) + )); + + } catch (Exception e) { + return 0; + } + } +} diff --git a/app/src/main/java/net/kacpak/batterychargingmonitor/data/database/ChargeInformation.java b/app/src/main/java/net/kacpak/batterychargingmonitor/data/database/ChargeInformation.java new file mode 100644 index 0000000..a42b59c --- /dev/null +++ b/app/src/main/java/net/kacpak/batterychargingmonitor/data/database/ChargeInformation.java @@ -0,0 +1,114 @@ +package net.kacpak.batterychargingmonitor.data.database; + +import android.database.Cursor; + +import java.util.Date; + +import net.kacpak.batterychargingmonitor.data.database.DatabaseContract.DataEntry; + +public class ChargeInformation { + + private int mType; + private boolean mChargeFinished; + private long mStart; + private float mStartTemperature; + private int mStartPercentage; + private int mStartVoltage; + private long mStop; + private float mStopTemperature; + private int mStopPercentage; + private int mStopVoltage; + private String mNote; + + public ChargeInformation(Cursor cursor) { + String[] columns = cursor.getColumnNames(); + + for (int i = 0; i < columns.length; i++) + switch (columns[i]) { + case DataEntry.COLUMN_TYPE: + mType = cursor.getInt(i); + break; + case DataEntry.COLUMN_START: + mStart = cursor.getLong(i); + break; + case DataEntry.COLUMN_START_TEMPERATURE_C: + mStartTemperature = cursor.getFloat(i); + break; + case DataEntry.COLUMN_START_PERCENTAGE: + mStartPercentage = cursor.getInt(i); + break; + case DataEntry.COLUMN_START_VOLTAGE: + mStartVoltage = cursor.getInt(i); + break; + case DataEntry.COLUMN_STOP: + mStop = cursor.getLong(i); + break; + case DataEntry.COLUMN_STOP_TEMPERATURE_C: + mStopTemperature = cursor.getFloat(i); + break; + case DataEntry.COLUMN_STOP_PERCENTAGE: + mStopPercentage = cursor.getInt(i); + break; + case DataEntry.COLUMN_STOP_VOLTAGE: + mStopVoltage = cursor.getInt(i); + break; + case DataEntry.COLUMN_NOTE: + mNote = cursor.getString(i); + break; + case DataEntry.COLUMN_CHARGE_FINISHED: + mChargeFinished = cursor.getInt(i) != 0; + break; + } + } + + public boolean isFinished() { + return mChargeFinished; + } + + public int getType() { + return mType; + } + + public String getNote() { + return mNote; + } + + public Date getStartDate() { + return new Date(mStart); + } + + public float getStartTemperature() { + return mStartTemperature; + } + + public int getStartPercentage() { + return mStartPercentage; + } + + public int getStartVoltage() { + return mStartVoltage; + } + + public Date getStopDate() { + return new Date(mStop); + } + + public float getStopTemperature() { + return mStopTemperature; + } + + public int getStopPercentage() { + return mStopPercentage; + } + + public int getStopVoltage() { + return mStopVoltage; + } + + public long getDuration() { + if (mStop != 0) + return mStop - mStart; + return new Date().getTime() - new Date(mStart).getTime(); + } + +} diff --git a/app/src/main/java/net/kacpak/batterychargingmonitor/data/database/DataProvider.java b/app/src/main/java/net/kacpak/batterychargingmonitor/data/database/DataProvider.java new file mode 100644 index 0000000..8ff7025 --- /dev/null +++ b/app/src/main/java/net/kacpak/batterychargingmonitor/data/database/DataProvider.java @@ -0,0 +1,238 @@ +package net.kacpak.batterychargingmonitor.data.database; + +import android.content.ContentProvider; +import android.content.ContentValues; +import android.content.UriMatcher; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; +import android.net.Uri; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.util.Log; + +import net.kacpak.batterychargingmonitor.data.database.DatabaseContract; +import net.kacpak.batterychargingmonitor.data.database.DatabaseContract.DataEntry; +import net.kacpak.batterychargingmonitor.data.database.DatabaseHelper; + +import java.util.ArrayList; + +public class DataProvider extends ContentProvider { + + private SQLiteOpenHelper mDatabase; + + private static final UriMatcher sUriMatcher = buildUriMatcher(); + private static final int DATA = 100; + private static final int DATA_ID = 101; + private static final int DATA_UNFINISHED = 102; + + private static UriMatcher buildUriMatcher() { + final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH); + final String authority = DatabaseContract.CONTENT_AUTHORITY; + + matcher.addURI(authority, DatabaseContract.DATA_PATH, DATA); + matcher.addURI(authority, DatabaseContract.DATA_PATH + "/#", DATA_ID); + matcher.addURI(authority, DatabaseContract.DATA_PATH_UNFINISHED, DATA_UNFINISHED); + + return matcher; + } + + @Override + public boolean onCreate() { + mDatabase = new DatabaseHelper(getContext()); + return true; + } + + @Nullable + @Override + public Cursor query(@NonNull Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { + Cursor returnCursor; + + switch (sUriMatcher.match(uri)) { + case DATA: { + returnCursor = mDatabase.getReadableDatabase().query( + DataEntry.TABLE_NAME, + projection, + selection, + selectionArgs, + null, + null, + sortOrder + ); + break; + } + case DATA_ID: { + long id = DataEntry.getIdFromUri(uri); + returnCursor = mDatabase.getReadableDatabase().query( + DataEntry.TABLE_NAME, + projection, + DataEntry._ID + " = " + id, + null, + null, + null, + sortOrder + ); + break; + } + case DATA_UNFINISHED: { + returnCursor = mDatabase.getReadableDatabase().query( + DataEntry.TABLE_NAME, + projection, + DataEntry.COLUMN_CHARGE_FINISHED + " = 0", + null, + null, + null, + null + ); + break; + } + default: + throw new UnsupportedOperationException("Unknown uri: " + uri); + } + + try { + returnCursor.setNotificationUri(getContext().getContentResolver(), uri); + + } catch (NullPointerException e) { + Log.e("No content resolver", e.toString()); + } + + return returnCursor; + } + + @Nullable + @Override + public String getType(@NonNull Uri uri) { + final int match = sUriMatcher.match(uri); + + switch (match) { + case DATA: + return DataEntry.CONTENT_TYPE; + case DATA_ID: + return DataEntry.CONTENT_ITEM_TYPE; + case DATA_UNFINISHED: + return DataEntry.CONTENT_ITEM_TYPE; + default: + throw new UnsupportedOperationException("Unknown uri: " + uri); + } + } + + @Nullable + @Override + public Uri insert(@NonNull Uri uri, ContentValues values) { + final SQLiteDatabase db = mDatabase.getWritableDatabase(); + final int match = sUriMatcher.match(uri); + Uri returnUri; + + switch (match) { + case DATA: { + long id = db.insert(DataEntry.TABLE_NAME, null, values); + if (id > 0) + returnUri = DataEntry.buildUri(id); + else + throw new android.database.SQLException("Failed to insert row into " + uri); + break; + } + default: + throw new UnsupportedOperationException("Unknown uri: " + uri); + } + + try { + getContext().getContentResolver().notifyChange(uri, null); + + } catch (NullPointerException e) { + Log.e("No content resolver", e.toString()); + } + + return returnUri; + } + + @Override + public int delete(@NonNull Uri uri, String selection, String[] selectionArgs) { + final SQLiteDatabase db = mDatabase.getWritableDatabase(); + final int match = sUriMatcher.match(uri); + int rowsDeleted; + + switch (match) { + case DATA: { + rowsDeleted = db.delete(DataEntry.TABLE_NAME, selection, selectionArgs); + break; + } + case DATA_ID: { + long id = DataEntry.getIdFromUri(uri); + rowsDeleted = db.delete(DataEntry.TABLE_NAME, DataEntry._ID + " = " + id, null); + break; + } + default: + throw new UnsupportedOperationException("Unknown uri: " + uri); + } + + try { + if (rowsDeleted != 0) + getContext().getContentResolver().notifyChange(uri, null); + + } catch (NullPointerException e) { + Log.e("No content resolver", e.toString()); + } + + return rowsDeleted; + } + + @Override + public int update(@NonNull Uri uri, ContentValues values, String selection, String[] selectionArgs) { + final SQLiteDatabase db = mDatabase.getWritableDatabase(); + final int match = sUriMatcher.match(uri); + int rowsUpdated; + + switch (match) { + case DATA: { + rowsUpdated = db.update(DataEntry.TABLE_NAME, values, selection, selectionArgs); + break; + } + case DATA_UNFINISHED: { + Cursor cursor = query( + DataEntry.CONTENT_URI_UNFINISHED, + new String[] { DataEntry._ID }, + null, + null, + null + ); + + ArrayList ids = new ArrayList<>(); + while (cursor.moveToNext()) + ids.add(cursor.getLong(0)); + cursor.close(); + String inClause = ids.toString().replace("[", "(").replace("]", ")"); + + rowsUpdated = db.update( + DataEntry.TABLE_NAME, + values, + DataEntry._ID + " IN " + inClause, + null + ); + break; + } + case DATA_ID: { + rowsUpdated = db.update( + DataEntry.TABLE_NAME, + values, + DataEntry._ID + " = " + DataEntry.getIdFromUri(uri), + null + ); + break; + } + default: + throw new UnsupportedOperationException("Unknown uri: " + uri); + } + + try { + if (rowsUpdated != 0) + getContext().getContentResolver().notifyChange(uri, null); + + } catch (NullPointerException e) { + Log.e("No content resolver", e.toString()); + } + + return rowsUpdated; + } +} diff --git a/app/src/main/java/net/kacpak/batterychargingmonitor/data/database/DatabaseContract.java b/app/src/main/java/net/kacpak/batterychargingmonitor/data/database/DatabaseContract.java new file mode 100644 index 0000000..ed0b5ef --- /dev/null +++ b/app/src/main/java/net/kacpak/batterychargingmonitor/data/database/DatabaseContract.java @@ -0,0 +1,51 @@ +package net.kacpak.batterychargingmonitor.data.database; + +import android.content.ContentResolver; +import android.content.ContentUris; +import android.net.Uri; +import android.provider.BaseColumns; + +public final class DatabaseContract { + + public static final String CONTENT_AUTHORITY = "net.kacpak.batterychargingmonitor"; + public static final Uri BASE_CONTENT_URI = Uri.parse("content://" + CONTENT_AUTHORITY); + + public static final String DATA_PATH = DataEntry.TABLE_NAME; + public static final String DATA_PATH_UNFINISHED = DATA_PATH + "/" + DataEntry.PATH_UNFINISHED; + + public static final class DataEntry implements BaseColumns { + public static final String TABLE_NAME = "batteryHistory"; + public static final String COLUMN_NOTE = "note"; + public static final String COLUMN_TYPE = "type"; + public static final String COLUMN_START = "start"; + public static final String COLUMN_START_PERCENTAGE = "start_percentage"; + public static final String COLUMN_START_VOLTAGE = "start_voltage"; + public static final String COLUMN_START_TEMPERATURE_C = "start_temperature_c"; + public static final String COLUMN_STOP = "stop"; + public static final String COLUMN_STOP_PERCENTAGE = "stop_percentage"; + public static final String COLUMN_STOP_VOLTAGE = "stop_voltage"; + public static final String COLUMN_STOP_TEMPERATURE_C = "stop_temperature_c"; + public static final String COLUMN_CHARGE_FINISHED = "charge_finished"; + public static final String PATH_UNFINISHED = "unfinished"; + + public static final Uri CONTENT_URI = + BASE_CONTENT_URI.buildUpon().appendPath(DATA_PATH).build(); + + public static final Uri CONTENT_URI_UNFINISHED = + CONTENT_URI.buildUpon().appendPath(PATH_UNFINISHED).build(); + + public static final String CONTENT_TYPE = + ContentResolver.CURSOR_DIR_BASE_TYPE + "/" + CONTENT_AUTHORITY + "/" + DATA_PATH; + + public static final String CONTENT_ITEM_TYPE = + ContentResolver.CURSOR_ITEM_BASE_TYPE + "/" + CONTENT_AUTHORITY + "/" + DATA_PATH; + + public static Uri buildUri(long id) { + return ContentUris.withAppendedId(CONTENT_URI, id); + } + + public static long getIdFromUri(Uri uri) { + return Long.parseLong(uri.getPathSegments().get(1)); + } + } +} diff --git a/app/src/main/java/net/kacpak/batterychargingmonitor/data/database/DatabaseHelper.java b/app/src/main/java/net/kacpak/batterychargingmonitor/data/database/DatabaseHelper.java new file mode 100644 index 0000000..24c9ec5 --- /dev/null +++ b/app/src/main/java/net/kacpak/batterychargingmonitor/data/database/DatabaseHelper.java @@ -0,0 +1,42 @@ +package net.kacpak.batterychargingmonitor.data.database; + +import android.content.Context; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; +import net.kacpak.batterychargingmonitor.data.database.DatabaseContract.DataEntry; + +public class DatabaseHelper extends SQLiteOpenHelper { + + private static final int DATABASE_VERSION = 1; + public static final String DATABASE_NAME = "battery_history.db"; + + public DatabaseHelper(Context context) { + super(context, DATABASE_NAME, null, DATABASE_VERSION); + } + + @Override + public void onCreate(SQLiteDatabase db) { + final String SQL_CREATE_DATA_TABLE = "CREATE TABLE " + DataEntry.TABLE_NAME + " (" + + DataEntry._ID + " INTEGER PRIMARY KEY, " + + DataEntry.COLUMN_CHARGE_FINISHED + " INTEGER NOT NULL DEFAULT 0, " + + DataEntry.COLUMN_TYPE + " INTEGER NOT NULL DEFAULT 0, " + + DataEntry.COLUMN_START + " INTEGER NOT NULL DEFAULT 0, " + + DataEntry.COLUMN_START_PERCENTAGE + " INTEGER NOT NULL DEFAULT 0, " + + DataEntry.COLUMN_START_VOLTAGE + " INTEGER NOT NULL DEFAULT 0, " + + DataEntry.COLUMN_START_TEMPERATURE_C + " REAL NOT NULL DEFAULT 0, " + + DataEntry.COLUMN_STOP + " INTEGER NOT NULL DEFAULT 0, " + + DataEntry.COLUMN_STOP_PERCENTAGE + " INTEGER NOT NULL DEFAULT 0, " + + DataEntry.COLUMN_STOP_VOLTAGE + " INTEGER NOT NULL DEFAULT 0, " + + DataEntry.COLUMN_STOP_TEMPERATURE_C + " REAL NOT NULL DEFAULT 0, " + + DataEntry.COLUMN_NOTE + " TEXT DEFAULT NULL " + + " );"; + + db.execSQL(SQL_CREATE_DATA_TABLE); + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + db.execSQL("DROP TABLE IF EXISTS " + DataEntry.TABLE_NAME); + onCreate(db); + } +} diff --git a/app/src/main/java/net/kacpak/batterychargingmonitor/data/receiver/PowerConnectionReceiver.java b/app/src/main/java/net/kacpak/batterychargingmonitor/data/receiver/PowerConnectionReceiver.java new file mode 100644 index 0000000..c4d6307 --- /dev/null +++ b/app/src/main/java/net/kacpak/batterychargingmonitor/data/receiver/PowerConnectionReceiver.java @@ -0,0 +1,86 @@ +package net.kacpak.batterychargingmonitor.data.receiver; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.util.Log; +import android.widget.Toast; + +import net.kacpak.batterychargingmonitor.R; +import net.kacpak.batterychargingmonitor.data.BatteryDataRepository; +import net.kacpak.batterychargingmonitor.data.BatteryStatus; + +import java.util.Date; + +public class PowerConnectionReceiver extends BroadcastReceiver { + + private Context mContext; + private BatteryStatus mBatteryStatus; + + @Override + public void onReceive(Context context, Intent intent) { + mContext = context; + mBatteryStatus = new BatteryStatus(); + showBatteryStatus(); + + if (mBatteryStatus.isCharging()) + startNewCharging(); + else + finishCharging(); + } + + public void showBatteryStatus() { + StringBuilder data = new StringBuilder(); + + data.append(String.format(mContext.getString(R.string.battery_percent), mBatteryStatus.getChargePercentage())); + data.append(" "); + + if (mBatteryStatus.isCharging()) { + if (mBatteryStatus.isPluggedAC()) data.append(mContext.getString(R.string.battery_ac_charging)); + if (mBatteryStatus.isPluggedUSB()) data.append(mContext.getString(R.string.battery_usb_charging)); + if (mBatteryStatus.isPluggedWireless()) data.append(mContext.getString(R.string.battery_usb_charging)); + } else { + data.append(mContext.getString(R.string.battery_disconnected)); + } + + data.append("\n"); + data.append(String.format(mContext.getString(R.string.battery_temperature_celsius), mBatteryStatus.getTemperatureInCelsius())); + + data.append(" "); + data.append(String.format(mContext.getString(R.string.battery_voltage), mBatteryStatus.getVoltage())); + + Toast.makeText(mContext, data, Toast.LENGTH_SHORT).show(); + } + + public void startNewCharging() { + try { + new BatteryDataRepository(mContext).add( + new Date(), + mBatteryStatus.getPluggedInformation(), + mBatteryStatus.getChargePercentage(), + mBatteryStatus.getTemperatureInCelsius(), + mBatteryStatus.getVoltage() + ); + + } catch (Exception e) { + Log.e("startNewCharging", e.toString()); + Toast.makeText(mContext, R.string.error_failed_to_start_charging, Toast.LENGTH_SHORT).show(); + } + } + + public void finishCharging() { + try { + new BatteryDataRepository(mContext).finishCharging( + new Date(), + mBatteryStatus.getChargePercentage(), + mBatteryStatus.getTemperatureInCelsius(), + mBatteryStatus.getVoltage(), + null + ); + + } catch (Exception e) { + Log.e("finishCharging", e.toString()); + Toast.makeText(mContext, R.string.error_failed_complete_charging, Toast.LENGTH_SHORT).show(); + } + } +} diff --git a/app/src/main/java/net/kacpak/batterychargingmonitor/ui/MainActivity.java b/app/src/main/java/net/kacpak/batterychargingmonitor/ui/MainActivity.java index 6ce727b..bc852ad 100644 --- a/app/src/main/java/net/kacpak/batterychargingmonitor/ui/MainActivity.java +++ b/app/src/main/java/net/kacpak/batterychargingmonitor/ui/MainActivity.java @@ -1,6 +1,7 @@ package net.kacpak.batterychargingmonitor.ui; import android.app.Fragment; +import android.content.Intent; import android.os.Bundle; import android.support.design.widget.FloatingActionButton; import android.support.design.widget.Snackbar; @@ -16,25 +17,34 @@ import net.kacpak.batterychargingmonitor.R; import net.kacpak.batterychargingmonitor.ui.history.HistoryFragment; +import net.kacpak.batterychargingmonitor.ui.settings.SettingsActivity; import net.kacpak.batterychargingmonitor.ui.summary.SummaryFragment; +import butterknife.BindView; +import butterknife.ButterKnife; + public class MainActivity extends AppCompatActivity - implements NavigationView.OnNavigationItemSelectedListener { + implements NavigationView.OnNavigationItemSelectedListener, NavigationDrawerManipulation { + + @BindView(R.id.drawer_layout) + DrawerLayout mDrawer; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity); + // ButterKnife + ButterKnife.bind(this); + // Toolbar Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); // Navigation Drawer - DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); ActionBarDrawerToggle toggle = new ActionBarDrawerToggle( - this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close); - drawer.setDrawerListener(toggle); + this, mDrawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close); + mDrawer.setDrawerListener(toggle); toggle.syncState(); NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view); @@ -51,35 +61,25 @@ protected void onCreate(Bundle savedInstanceState) { @Override public void onBackPressed() { - DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); - if (drawer.isDrawerOpen(GravityCompat.START)) - drawer.closeDrawer(GravityCompat.START); + if (mDrawer.isDrawerOpen(GravityCompat.START)) + mDrawer.closeDrawer(GravityCompat.START); else super.onBackPressed(); } @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.main, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - int id = item.getItemId(); + public boolean onNavigationItemSelected(MenuItem item) { + final int id = item.getItemId(); - if (id == R.id.action_settings) { + // Ustawienia + if (id == R.id.nav_settings) { + mDrawer.closeDrawer(GravityCompat.START); + startActivity(new Intent(this, SettingsActivity.class)); return true; } - return super.onOptionsItemSelected(item); - } - - @Override - public boolean onNavigationItemSelected(MenuItem item) { - final int id = item.getItemId(); + // Zmiana fragmentu Fragment fragment = null; - if (id == R.id.nav_summary) fragment = new SummaryFragment(); else if (id == R.id.nav_history) @@ -90,8 +90,24 @@ else if (id == R.id.nav_history) .replace(R.id.content, fragment) .commit(); - DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); - drawer.closeDrawer(GravityCompat.START); + mDrawer.closeDrawer(GravityCompat.START); + return true; + } + + @Override + public boolean disableNavigationDrawer() { + if (mDrawer == null) return false; + + mDrawer.closeDrawers(); + mDrawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED); + return true; + } + + @Override + public boolean enableNavigationDrawer() { + if (mDrawer == null) return false; + + mDrawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED); return true; } } diff --git a/app/src/main/java/net/kacpak/batterychargingmonitor/ui/NavigationDrawerManipulation.java b/app/src/main/java/net/kacpak/batterychargingmonitor/ui/NavigationDrawerManipulation.java new file mode 100644 index 0000000..f664438 --- /dev/null +++ b/app/src/main/java/net/kacpak/batterychargingmonitor/ui/NavigationDrawerManipulation.java @@ -0,0 +1,6 @@ +package net.kacpak.batterychargingmonitor.ui; + +public interface NavigationDrawerManipulation { + boolean disableNavigationDrawer(); + boolean enableNavigationDrawer(); +} diff --git a/app/src/main/java/net/kacpak/batterychargingmonitor/ui/history/HistoryAdapter.java b/app/src/main/java/net/kacpak/batterychargingmonitor/ui/history/HistoryAdapter.java new file mode 100644 index 0000000..1ae0dcb --- /dev/null +++ b/app/src/main/java/net/kacpak/batterychargingmonitor/ui/history/HistoryAdapter.java @@ -0,0 +1,85 @@ +package net.kacpak.batterychargingmonitor.ui.history; + +import android.content.Context; +import android.database.Cursor; +import android.os.BatteryManager; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CursorAdapter; +import android.widget.TextView; + +import net.kacpak.batterychargingmonitor.R; +import net.kacpak.batterychargingmonitor.data.database.ChargeInformation; + +import butterknife.BindView; +import butterknife.ButterKnife; + +public class HistoryAdapter extends CursorAdapter { + + HistoryAdapter(Context context, Cursor cursor) { + super(context, cursor, 0); + } + + @Override + public View newView(Context context, Cursor cursor, ViewGroup parent) { + View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_history, parent, false); + view.setTag(new HistoryAdapterViewHolder(view)); + return view; + } + + @Override + public void bindView(View view, Context context, Cursor cursor) { + ChargeInformation data = new ChargeInformation(cursor); + + String type; + switch (data.getType()) { + case 0: + type = context.getString(R.string.power_state_mixed); + break; + case BatteryManager.BATTERY_PLUGGED_AC: + type = context.getString(R.string.power_state_ac); + break; + case BatteryManager.BATTERY_PLUGGED_USB: + type = context.getString(R.string.power_state_usb); + break; + case BatteryManager.BATTERY_PLUGGED_WIRELESS: + type = context.getString(R.string.power_state_wireless); + break; + default: + type = "?"; + } + + long seconds = data.getDuration() / 1000 % 60; + long minutes = data.getDuration() / (60 * 1000) % 60; + long hours = data.getDuration() / (60 * 60 * 1000); + String duration = String.format(context.getString(R.string.history_list_item_duration), hours, minutes, seconds); + + String percentageIncrease = data.isFinished() + ? String.format(context.getString(R.string.history_list_item_percentage_increase), data.getStartPercentage(), data.getStopPercentage()) + : String.format(context.getString(R.string.history_list_item_percentage_while_charging), data.getStartPercentage()); + + HistoryAdapterViewHolder holder = (HistoryAdapterViewHolder) view.getTag(); + holder.type.setText(type); + holder.duration.setText(duration); + holder.percentage_increase.setText(percentageIncrease); + } + + /** + * ViewHolder dla wiersza w historii + */ + public class HistoryAdapterViewHolder { + @BindView(R.id.type) + public TextView type; + + @BindView(R.id.duration) + public TextView duration; + + @BindView(R.id.percentage_increase) + public TextView percentage_increase; + + public HistoryAdapterViewHolder(View itemView) { + ButterKnife.bind(this, itemView); + } + } +} diff --git a/app/src/main/java/net/kacpak/batterychargingmonitor/ui/history/HistoryContract.java b/app/src/main/java/net/kacpak/batterychargingmonitor/ui/history/HistoryContract.java index bf2f318..ced72b4 100644 --- a/app/src/main/java/net/kacpak/batterychargingmonitor/ui/history/HistoryContract.java +++ b/app/src/main/java/net/kacpak/batterychargingmonitor/ui/history/HistoryContract.java @@ -1,9 +1,24 @@ package net.kacpak.batterychargingmonitor.ui.history; +import android.content.Loader; +import android.database.Cursor; +import android.os.Bundle; + +import java.util.List; + public interface HistoryContract { interface View { + void swapCursor(Cursor cursor); + void showDeletedCountMessage(int count); + void showMergedCountMessage(int count); } interface UserActionsListener { + Loader onCreateLoader(int id, Bundle args); + void onLoadFinished(Cursor data); + void onLoaderReset(); + void removeIrrelevantEntries(); + void removeEntries(List entries); + void mergeEntries(List entries); } } diff --git a/app/src/main/java/net/kacpak/batterychargingmonitor/ui/history/HistoryFragment.java b/app/src/main/java/net/kacpak/batterychargingmonitor/ui/history/HistoryFragment.java index 861a94e..2d5dc3d 100644 --- a/app/src/main/java/net/kacpak/batterychargingmonitor/ui/history/HistoryFragment.java +++ b/app/src/main/java/net/kacpak/batterychargingmonitor/ui/history/HistoryFragment.java @@ -1,7 +1,244 @@ package net.kacpak.batterychargingmonitor.ui.history; +import android.app.AlertDialog; +import android.app.DialogFragment; import android.app.Fragment; +import android.app.LoaderManager; +import android.content.DialogInterface; +import android.content.Loader; +import android.database.Cursor; +import android.graphics.Color; +import android.os.Build; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.util.SparseBooleanArray; +import android.view.ActionMode; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.widget.AbsListView; +import android.widget.AdapterView; +import android.widget.ListView; +import android.widget.Toast; -public class HistoryFragment extends Fragment implements HistoryContract.View { +import net.kacpak.batterychargingmonitor.R; +import net.kacpak.batterychargingmonitor.data.BatteryStatus; +import net.kacpak.batterychargingmonitor.ui.NavigationDrawerManipulation; +import net.kacpak.batterychargingmonitor.ui.historydetail.HistoryDetailContract; +import net.kacpak.batterychargingmonitor.ui.historydetail.HistoryDetailDialog; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import butterknife.BindView; +import butterknife.ButterKnife; + +public class HistoryFragment extends Fragment implements + HistoryContract.View, LoaderManager.LoaderCallbacks, + AdapterView.OnItemClickListener, AbsListView.MultiChoiceModeListener { + + /** + * ID dla nowo-tworzonego Loadera + */ + private static final int HISTORY_LOADER = 0; + + /** + * Interaktor + */ + private HistoryContract.UserActionsListener mActionsListener; + + /** + * RecyclerView do wyświetlenia historii ładowań telefony + */ + @BindView(R.id.listview_history) + ListView mListView; + + /** + * Adapter obsługujący wyświetlanie poszczególnych wpisów z bazy + */ + private HistoryAdapter mHistoryAdapter; + + /** + * Initializer + */ + @Override + public void onActivityCreated(Bundle savedInstanceState) { + mActionsListener = new HistoryPresenter(this, getActivity()); + getActivity().setTitle(R.string.title_history); + getLoaderManager().initLoader(HISTORY_LOADER, null, this); + + super.onActivityCreated(savedInstanceState); + } + + /** + * Tworzy widok naszego Fragmentu + */ + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View root = inflater.inflate(R.layout.content_history, container, false); + ButterKnife.bind(this, root); + setHasOptionsMenu(true); + + mHistoryAdapter = new HistoryAdapter(getActivity(), null); + + mListView.setAdapter(mHistoryAdapter); + mListView.setOnItemClickListener(this); + mListView.setChoiceMode(AbsListView.CHOICE_MODE_MULTIPLE_MODAL); + mListView.setMultiChoiceModeListener(this); +// mListView.setEmptyView(null); + + return root; + } + + @Override + public Loader onCreateLoader(int id, Bundle args) { + return mActionsListener.onCreateLoader(id, args); + } + + @Override + public void onLoadFinished(Loader loader, Cursor data) { + mActionsListener.onLoadFinished(data); + } + + @Override + public void onLoaderReset(Loader loader) { + mActionsListener.onLoaderReset(); + } + + @Override + public void swapCursor(Cursor cursor) { + mHistoryAdapter.swapCursor(cursor); + } + + @Override + public void showDeletedCountMessage(int count) { + Toast.makeText(getActivity(), getString(R.string.history_list_counted_entries_deleted, count), Toast.LENGTH_SHORT).show(); + } + + @Override + public void showMergedCountMessage(int count) { + Toast.makeText(getActivity(), getString(R.string.history_list_counted_entries_merged, count), Toast.LENGTH_SHORT).show(); + } + + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + Cursor cursor = (Cursor) parent.getItemAtPosition(position); + if (cursor != null) { + Bundle args = new Bundle(); + args.putLong(HistoryDetailContract.CHARGE_ID, cursor.getLong(0)); + + DialogFragment fragment = new HistoryDetailDialog(); + fragment.setArguments(args); + fragment.show(getFragmentManager(), "dialog"); + } + } + + @Override + public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) { + // Jeśli jest zaznaczony ostatni wpis w trakcie ładowania, dezaktywuj usuwanie + mode.getMenu().findItem(R.id.action_delete).setEnabled( + !(mListView.getCheckedItemPositions().get(0) && new BatteryStatus().isCharging())); + + // Jeśli zaznaczeń są conajmniej 2, to aktywuj łączenie + mode.getMenu().findItem(R.id.action_merge).setEnabled(mListView.getCheckedItemCount() > 1); + + String title = String.format(getString(R.string.history_list_selected_count), mListView.getCheckedItemCount()); + mode.setTitle(title); + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + super.onCreateOptionsMenu(menu, inflater); + inflater.inflate(R.menu.menu_history, menu); + } + + @Override + public boolean onCreateActionMode(ActionMode mode, Menu menu) { + // Inflate menu for CAB (Contextual Action Bar) + MenuInflater inflater = mode.getMenuInflater(); + inflater.inflate(R.menu.menu_history_contextual, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) {switch (item.getItemId()) { + case R.id.action_clear_entries: + mActionsListener.removeIrrelevantEntries(); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + @Override + public boolean onPrepareActionMode(ActionMode mode, Menu menu) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){ + getActivity().getWindow().setStatusBarColor( + getResources().getColor(R.color.colorPrimaryDark) + ); + } + + ((NavigationDrawerManipulation) getActivity()).disableNavigationDrawer(); + return true; + } + + @Override + public boolean onActionItemClicked(final ActionMode mode, MenuItem item) { + final int id = item.getItemId(); + + DialogInterface.OnClickListener positiveAnswer = new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + if (id == R.id.action_delete) + mActionsListener.removeEntries(getSelectedEntriesIds()); + else + mActionsListener.mergeEntries(getSelectedEntriesIds()); + mode.finish(); + } + }; + + final int message = (id == R.id.action_delete) ? R.string.history_list_confirm_delete : R.string.history_list_confirm_merge; + + new AlertDialog.Builder(getActivity()) + .setTitle(android.R.string.dialog_alert_title) + .setMessage(message) + .setPositiveButton(android.R.string.yes, positiveAnswer) + .setNegativeButton(android.R.string.no, null) + .show(); + + return true; + } + + @Override + public void onDestroyActionMode(ActionMode mode) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){ + getActivity().getWindow().setStatusBarColor(Color.TRANSPARENT); + } + + ((NavigationDrawerManipulation) getActivity()).enableNavigationDrawer(); + } + + /** + * Zwraca id zaznaczonych elementów + * @return lista z id zaznaczonych elementów + */ + private List getSelectedEntriesIds() { + List ids = new ArrayList<>(); + SparseBooleanArray positions = mListView.getCheckedItemPositions(); + + for (int i = 0; i < positions.size(); i++) { + if (positions.valueAt(i)) { + Cursor item = (Cursor) mHistoryAdapter.getItem(positions.keyAt(i)); + ids.add(item.getLong(0)); + } + } + + return ids; + } } diff --git a/app/src/main/java/net/kacpak/batterychargingmonitor/ui/history/HistoryPresenter.java b/app/src/main/java/net/kacpak/batterychargingmonitor/ui/history/HistoryPresenter.java new file mode 100644 index 0000000..4a6f5ed --- /dev/null +++ b/app/src/main/java/net/kacpak/batterychargingmonitor/ui/history/HistoryPresenter.java @@ -0,0 +1,109 @@ +package net.kacpak.batterychargingmonitor.ui.history; + +import android.content.Context; +import android.content.CursorLoader; +import android.content.Loader; +import android.database.Cursor; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.widget.Toast; + +import net.kacpak.batterychargingmonitor.data.BatteryDataRepository; +import net.kacpak.batterychargingmonitor.data.database.DatabaseContract.DataEntry; + +import java.lang.ref.WeakReference; +import java.util.List; + + +public class HistoryPresenter implements HistoryContract.UserActionsListener { + + /** + * Kontekst aplikacji + */ + private final WeakReference mContext; + + /** + * Widok + */ + private WeakReference mView; + + /** + * Pobierane dane z bazy danych + */ + private static final String[] sProjection = { + DataEntry._ID, + DataEntry.COLUMN_START, + DataEntry.COLUMN_STOP, + DataEntry.COLUMN_START_PERCENTAGE, + DataEntry.COLUMN_STOP_PERCENTAGE, + DataEntry.COLUMN_TYPE, + DataEntry.COLUMN_CHARGE_FINISHED + }; + + /** + * Tworzy Presenter dla widoku podsumowania ({@link HistoryContract.View}) z automatyczną aktualizacją + * @param mHistoryView Widok + * @param context Kontekst widoku + */ + public HistoryPresenter(@NonNull HistoryContract.View mHistoryView, @NonNull Context context) { + mView = new WeakReference<>(mHistoryView); + mContext = new WeakReference<>(context); + } + + /** + * Tworzy Loadera do wczytania danych o historii ładowania + * @param id id Loadera + * @param args dodatkowe argumenty + */ + @Override + public Loader onCreateLoader(int id, Bundle args) { + return new CursorLoader(mContext.get(), + DataEntry.CONTENT_URI, + sProjection, + null, + null, + DataEntry.COLUMN_START + " DESC" + ); + } + + @Override + public void onLoadFinished(Cursor data) { + HistoryContract.View view = mView.get(); + if (null != view) + view.swapCursor(data); + } + + @Override + public void onLoaderReset() { + HistoryContract.View view = mView.get(); + if (null != view) + view.swapCursor(null); + } + + @Override + public void removeIrrelevantEntries() { + int deletedCount = new BatteryDataRepository(mContext.get()).deleteIrrelevant(); + + HistoryContract.View view = mView.get(); + if (null != view) + view.showDeletedCountMessage(deletedCount); + } + + @Override + public void removeEntries(List entries) { + int deletedCount = new BatteryDataRepository(mContext.get()).delete(entries); + + HistoryContract.View view = mView.get(); + if (null != view) + view.showDeletedCountMessage(deletedCount); + } + + @Override + public void mergeEntries(List entries) { + int mergedCount = new BatteryDataRepository(mContext.get()).merge(entries); + + HistoryContract.View view = mView.get(); + if (null != view) + view.showMergedCountMessage(mergedCount); + } +} diff --git a/app/src/main/java/net/kacpak/batterychargingmonitor/ui/historydetail/HistoryDetailContract.java b/app/src/main/java/net/kacpak/batterychargingmonitor/ui/historydetail/HistoryDetailContract.java new file mode 100644 index 0000000..ccf34de --- /dev/null +++ b/app/src/main/java/net/kacpak/batterychargingmonitor/ui/historydetail/HistoryDetailContract.java @@ -0,0 +1,24 @@ +package net.kacpak.batterychargingmonitor.ui.historydetail; + +import java.util.Date; + +public interface HistoryDetailContract { + + String CHARGE_ID = "id"; + + interface View { + void setChargerType(int chargerType); + void setChargingStartDate(Date chargingStartDate); + void setChargingDuration(long hours, long minutes, long seconds); + void setChargeBump(int startingPercentage); + void setChargeBump(int startingPercentage, int finishedPercentage); + void setStartingTemperature(float startingTemperature); + void setFinishedTemperature(float finishedTemperature); + void setStartingVoltage(int startingVoltage); + void setFinishedVoltage(int finishedVoltage); + } + + interface UserActionsListener { + void updateDetails(); + } +} diff --git a/app/src/main/java/net/kacpak/batterychargingmonitor/ui/historydetail/HistoryDetailDialog.java b/app/src/main/java/net/kacpak/batterychargingmonitor/ui/historydetail/HistoryDetailDialog.java new file mode 100644 index 0000000..9267294 --- /dev/null +++ b/app/src/main/java/net/kacpak/batterychargingmonitor/ui/historydetail/HistoryDetailDialog.java @@ -0,0 +1,166 @@ +package net.kacpak.batterychargingmonitor.ui.historydetail; + +import android.app.Dialog; +import android.app.DialogFragment; +import android.os.BatteryManager; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.widget.TextView; + +import net.kacpak.batterychargingmonitor.R; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; + +import butterknife.BindView; +import butterknife.ButterKnife; + +import static net.kacpak.batterychargingmonitor.ui.historydetail.HistoryDetailContract.*; + +/** + * Detale danego wpisu z historii + */ +public class HistoryDetailDialog extends DialogFragment implements HistoryDetailContract.View { + +// public static final SimpleDateFormat sDateFormat = new SimpleDateFormat("d MMM yyyy H:mm"); + public static final DateFormat sDateFormat = SimpleDateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.SHORT); + + @BindView(R.id.duration) + TextView mDurationTextView; + + @BindView(R.id.charger_type) + TextView mChargerTypeTextView; + + @BindView(R.id.start_date) + TextView mChargingStartTextView; + + @BindView(R.id.start_temp) + TextView mTemperatureStartTextView; + + @BindView(R.id.stop_temp) + TextView mTemperatureStopTextView; + + @BindView(R.id.start_voltage) + TextView mVoltageStartTextView; + + @BindView(R.id.stop_voltage) + TextView mVoltageStopTextView; + + @BindView(R.id.charge_bump) + TextView mPercentageTextView; + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + Dialog dialog = super.onCreateDialog(savedInstanceState); + dialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE); + return dialog; + } + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View root = inflater.inflate(R.layout.dialog_history_detail, container, false); + ButterKnife.bind(this, root); + + Bundle args = getArguments(); + if (null != args && args.containsKey(CHARGE_ID)) { + long id = args.getLong(CHARGE_ID); + + UserActionsListener mActionsListener = new HistoryDetailPresenter(this, getActivity(), id); + mActionsListener.updateDetails(); + } + + return root; + } + + @Override + public void setChargerType(int chargerType) { + String type; + switch (chargerType) { + case 0: + type = getString(R.string.power_state_mixed); + break; + case BatteryManager.BATTERY_PLUGGED_AC: + type = getString(R.string.power_state_ac); + break; + case BatteryManager.BATTERY_PLUGGED_USB: + type = getString(R.string.power_state_usb); + break; + case BatteryManager.BATTERY_PLUGGED_WIRELESS: + type = getString(R.string.power_state_wireless); + break; + default: + type = getString(R.string.power_state_unknown); + } + mChargerTypeTextView.setText(type); + } + + @Override + public void setChargingStartDate(Date chargingStartDate) { + mChargingStartTextView.setText( + sDateFormat.format(chargingStartDate) + ); + } + + @Override + public void setChargingDuration(long hours, long minutes, long seconds) { + mDurationTextView.setText(String.format( + getString(R.string.history_list_item_duration), + hours, minutes, seconds + )); + } + + @Override + public void setChargeBump(int startingPercentage) { + mPercentageTextView.setText(String.format( + getString(R.string.history_list_item_percentage_while_charging), + startingPercentage + )); + } + + @Override + public void setChargeBump(int startingPercentage, int finishedPercentage) { + mPercentageTextView.setText(String.format( + getString(R.string.history_list_item_percentage_increase), + startingPercentage, + finishedPercentage + )); + } + + @Override + public void setStartingTemperature(float startingTemperature) { + mTemperatureStartTextView.setText(String.format( + getString(R.string.temperature_celsius), + startingTemperature + )); + } + + @Override + public void setFinishedTemperature(float finishedTemperature) { + mTemperatureStopTextView.setText(String.format( + getString(R.string.temperature_celsius), + finishedTemperature + )); + } + + @Override + public void setStartingVoltage(int startingVoltage) { + mVoltageStartTextView.setText(String.format( + getString(R.string.voltage), + startingVoltage + )); + } + + @Override + public void setFinishedVoltage(int finishedVoltage) { + mVoltageStopTextView.setText(String.format( + getString(R.string.voltage), + finishedVoltage + )); + } +} diff --git a/app/src/main/java/net/kacpak/batterychargingmonitor/ui/historydetail/HistoryDetailPresenter.java b/app/src/main/java/net/kacpak/batterychargingmonitor/ui/historydetail/HistoryDetailPresenter.java new file mode 100644 index 0000000..4480913 --- /dev/null +++ b/app/src/main/java/net/kacpak/batterychargingmonitor/ui/historydetail/HistoryDetailPresenter.java @@ -0,0 +1,62 @@ +package net.kacpak.batterychargingmonitor.ui.historydetail; + +import android.content.Context; + +import net.kacpak.batterychargingmonitor.data.BatteryDataRepository; +import net.kacpak.batterychargingmonitor.data.database.ChargeInformation; + +import java.lang.ref.WeakReference; + +public class HistoryDetailPresenter implements HistoryDetailContract.UserActionsListener { + + private WeakReference mHistoryDetailView; + + private ChargeInformation mChargeInformation; + + public HistoryDetailPresenter(HistoryDetailContract.View historyDetailView, Context context, long chargeId) { + mHistoryDetailView = new WeakReference<>(historyDetailView); + mChargeInformation = new BatteryDataRepository(context).getChargeInformation(chargeId); + } + + @Override + public void updateDetails() { + HistoryDetailContract.View view = mHistoryDetailView.get(); + + if (null == view) + return; + + // Initial battery charge + view.setChargeBump(mChargeInformation.getStartPercentage()); + + // Charger Type + view.setChargerType(mChargeInformation.getType()); + + // Duration + long seconds = mChargeInformation.getDuration() / 1000 % 60; + long minutes = mChargeInformation.getDuration() / (60 * 1000) % 60; + long hours = mChargeInformation.getDuration() / (60 * 60 * 1000); + view.setChargingDuration(hours, minutes, seconds); + + // Starting Date + view.setChargingStartDate(mChargeInformation.getStartDate()); + + // Starting Temperature + view.setStartingTemperature(mChargeInformation.getStartTemperature()); + + // Starting Voltage + view.setStartingVoltage(mChargeInformation.getStartVoltage()); + + // If charging finished + if (!mChargeInformation.isFinished()) + return; + + // Charge Bump + view.setChargeBump(mChargeInformation.getStartPercentage(), mChargeInformation.getStopPercentage()); + + // Finished Temperature + view.setFinishedTemperature(mChargeInformation.getStopTemperature()); + + // Finished Voltage + view.setFinishedVoltage(mChargeInformation.getStopVoltage()); + } +} diff --git a/app/src/main/java/net/kacpak/batterychargingmonitor/ui/settings/SettingsActivity.java b/app/src/main/java/net/kacpak/batterychargingmonitor/ui/settings/SettingsActivity.java new file mode 100644 index 0000000..3e7b408 --- /dev/null +++ b/app/src/main/java/net/kacpak/batterychargingmonitor/ui/settings/SettingsActivity.java @@ -0,0 +1,28 @@ +package net.kacpak.batterychargingmonitor.ui.settings; + +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.Toolbar; + +import net.kacpak.batterychargingmonitor.R; + +public class SettingsActivity extends AppCompatActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_no_navigation); + + // Toolbar + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); + setSupportActionBar(toolbar); + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + + if (savedInstanceState == null) { + getFragmentManager().beginTransaction() + .add(R.id.content, new SettingsFragment()) + .commit(); + } + } + +} diff --git a/app/src/main/java/net/kacpak/batterychargingmonitor/ui/settings/SettingsFragment.java b/app/src/main/java/net/kacpak/batterychargingmonitor/ui/settings/SettingsFragment.java new file mode 100644 index 0000000..c4b6120 --- /dev/null +++ b/app/src/main/java/net/kacpak/batterychargingmonitor/ui/settings/SettingsFragment.java @@ -0,0 +1,14 @@ +package net.kacpak.batterychargingmonitor.ui.settings; + +import android.os.Bundle; +import android.preference.PreferenceFragment; + +import net.kacpak.batterychargingmonitor.R; + +public class SettingsFragment extends PreferenceFragment { + @Override + public void onCreate(final Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + addPreferencesFromResource(R.xml.settings); + } +} diff --git a/app/src/main/java/net/kacpak/batterychargingmonitor/ui/summary/SummaryContract.java b/app/src/main/java/net/kacpak/batterychargingmonitor/ui/summary/SummaryContract.java index 81ae549..a4c9f5b 100644 --- a/app/src/main/java/net/kacpak/batterychargingmonitor/ui/summary/SummaryContract.java +++ b/app/src/main/java/net/kacpak/batterychargingmonitor/ui/summary/SummaryContract.java @@ -1,20 +1,23 @@ package net.kacpak.batterychargingmonitor.ui.summary; -import android.support.annotation.IdRes; import android.support.annotation.IntRange; +import android.support.annotation.StringRes; public interface SummaryContract { interface View { void setBatteryChargeIndicator(@IntRange(from=0,to=100) int percentage); - void setBatteryHealth(@IdRes int healthId); + void setBatteryHealth(@StringRes int healthId); void setBatteryVoltage(int voltage); - void setBatteryAmperage(int amperage); - void setBatteryTemperature(double temperature); + void setBatteryCurrent(int current); + void setBatteryCurrent(int current, int currentAvg); + void setBatteryTemperatureInCelsius(double temperature); + void setBatteryTemperatureInFahrenheit(double temperature); void setBatteryChargingCounter(int counter); + void hideBatteryCurrentData(); } interface UserActionsListener { - void updateAllData(); + void updateView(); void startUpdates(); void stopUpdates(); } diff --git a/app/src/main/java/net/kacpak/batterychargingmonitor/ui/summary/SummaryFragment.java b/app/src/main/java/net/kacpak/batterychargingmonitor/ui/summary/SummaryFragment.java index 5de5656..a296f90 100644 --- a/app/src/main/java/net/kacpak/batterychargingmonitor/ui/summary/SummaryFragment.java +++ b/app/src/main/java/net/kacpak/batterychargingmonitor/ui/summary/SummaryFragment.java @@ -1,19 +1,26 @@ package net.kacpak.batterychargingmonitor.ui.summary; import android.app.Fragment; +import android.content.Intent; +import android.content.pm.ResolveInfo; import android.os.Bundle; import android.support.annotation.IdRes; import android.support.annotation.IntRange; import android.support.annotation.Nullable; +import android.support.annotation.StringRes; import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; +import android.widget.TextView; import com.github.lzyzsd.circleprogress.DonutProgress; import net.kacpak.batterychargingmonitor.R; -import butterknife.Bind; +import butterknife.BindView; import butterknife.ButterKnife; public class SummaryFragment extends Fragment implements SummaryContract.View { @@ -25,7 +32,7 @@ public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); setRetainInstance(true); - mActionsListener = new SummaryPresenter(this); + mActionsListener = new SummaryPresenter(this, getActivity()); getActivity().setTitle(R.string.title_summary); } @@ -34,9 +41,37 @@ public void onActivityCreated(Bundle savedInstanceState) { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View root = inflater.inflate(R.layout.content_summary, container, false); ButterKnife.bind(this, root); + setHasOptionsMenu(true); return root; } + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + super.onCreateOptionsMenu(menu, inflater); + inflater.inflate(R.menu.menu_summary, menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == R.id.action_battery_settings) + return showPowerUsage(); + return super.onOptionsItemSelected(item); + } + + /** + * Uruchamia domyślną aplikację monitorującą zużycie baterii + */ + private boolean showPowerUsage() { + Intent powerUsageIntent = new Intent(Intent.ACTION_POWER_USAGE_SUMMARY); + ResolveInfo resolveInfo = getActivity().getPackageManager().resolveActivity(powerUsageIntent, 0); + + // Check that the Battery app exists on this device + if (resolveInfo != null) + startActivity(powerUsageIntent); + + return true; + } + @Override public void onResume() { super.onResume(); @@ -44,12 +79,12 @@ public void onResume() { } @Override - public void onStop() { - super.onStop(); + public void onPause() { + super.onPause(); mActionsListener.stopUpdates(); } - @Bind(R.id.battery_charge_indicator) + @BindView(R.id.battery_charge_indicator) DonutProgress mBatteryChargeIndicator; @Override @@ -57,28 +92,69 @@ public void setBatteryChargeIndicator(@IntRange(from = 0, to = 100) int percenta mBatteryChargeIndicator.setProgress(percentage); } - @Override - public void setBatteryHealth(@IdRes int healthId) { + @BindView(R.id.health) + TextView mBatteryHealth; + @Override + public void setBatteryHealth(@StringRes int healthId) { + mBatteryHealth.setText(healthId); } + @BindView(R.id.voltage) + TextView mBatteryVoltage; + @Override public void setBatteryVoltage(int voltage) { + mBatteryVoltage.setText(String.format(getString(R.string.voltage), voltage)); + } + + @BindView(R.id.current) + TextView mBatteryCurrent; + @Override + public void setBatteryCurrent(int current) { + mBatteryCurrent.setText(String.format(getString(R.string.current), current)); } @Override - public void setBatteryAmperage(int amperage) { + public void setBatteryCurrent(int current, int currentAvg) { + String currentTxt = new StringBuilder() + .append(String.format(getString(R.string.current), current)) + .append(" (") + .append(String.format(getString(R.string.current), currentAvg)) + .append(')') + .toString(); + + mBatteryCurrent.setText(currentTxt); + } + + @BindView(R.id.current_heading) + TextView mBatteryCurrentHeading; + @Override + public void hideBatteryCurrentData() { + mBatteryCurrent.setVisibility(View.GONE); + mBatteryCurrentHeading.setVisibility(View.GONE); } + @BindView(R.id.temperature) + TextView mBatteryTemperature; + @Override - public void setBatteryTemperature(double temperature) { + public void setBatteryTemperatureInCelsius(double temperature) { + mBatteryTemperature.setText(String.format(getString(R.string.temperature_celsius), temperature)); + } + @Override + public void setBatteryTemperatureInFahrenheit(double temperature) { + mBatteryTemperature.setText(String.format(getString(R.string.temperature_fahrenheit), temperature)); } + @BindView(R.id.counter) + TextView mBatteryChargingCounter; + @Override public void setBatteryChargingCounter(int counter) { - + mBatteryChargingCounter.setText(String.format(getString(R.string.count_times), counter)); } } diff --git a/app/src/main/java/net/kacpak/batterychargingmonitor/ui/summary/SummaryPresenter.java b/app/src/main/java/net/kacpak/batterychargingmonitor/ui/summary/SummaryPresenter.java index 04748bc..bfa50b5 100644 --- a/app/src/main/java/net/kacpak/batterychargingmonitor/ui/summary/SummaryPresenter.java +++ b/app/src/main/java/net/kacpak/batterychargingmonitor/ui/summary/SummaryPresenter.java @@ -1,10 +1,15 @@ package net.kacpak.batterychargingmonitor.ui.summary; +import android.content.Context; import android.os.Handler; import android.support.annotation.NonNull; +import net.kacpak.batterychargingmonitor.R; import net.kacpak.batterychargingmonitor.data.BatteryDataRepository; import net.kacpak.batterychargingmonitor.data.BatteryStatus; +import net.kacpak.batterychargingmonitor.data.UserPreferences; + +import java.lang.ref.WeakReference; public class SummaryPresenter implements SummaryContract.UserActionsListener { @@ -16,7 +21,12 @@ public class SummaryPresenter implements SummaryContract.UserActionsListener { /** * Widok */ - private final SummaryContract.View mSummaryView; + private final WeakReference mView; + + /** + * Kontekst aplikacji + */ + private final Context mContext; /** * Wskazuje czy należy zaktualizować widok @@ -28,28 +38,75 @@ public class SummaryPresenter implements SummaryContract.UserActionsListener { */ private BatteryStatus mBatteryStatus; + private Handler mRunnableHandler; + private Runnable mUpdater; + /** * Tworzy Presenter dla widoku podsumowania ({@link SummaryContract.View}) z automatyczną aktualizacją - * @param mSummaryView + * @param view */ - public SummaryPresenter(@NonNull SummaryContract.View mSummaryView) { - this.mSummaryView = mSummaryView; + public SummaryPresenter(@NonNull SummaryContract.View view, Context context) { + mView = new WeakReference<>(view); + mContext = context; updateBatteryStatus(); + updateView(); } /** * Aktualizuje obecny stan baterii */ private void updateBatteryStatus() { - mBatteryStatus = new BatteryDataRepository().getStatus(); + mBatteryStatus = new BatteryDataRepository(mContext).getStatus(); } /** * Aktualizuje dane interfejsu użytkownika */ @Override - public void updateAllData() { - mSummaryView.setBatteryChargeIndicator(mBatteryStatus.getPercentage()); + public void updateView() { + SummaryContract.View view = mView.get(); + + if (null == view) + return; + + // Obecny stan naładowania baterii w procentach + view.setBatteryChargeIndicator(mBatteryStatus.getChargePercentage()); + + // Obecna temperatura baterii w wybranej jednostce + if (new UserPreferences(mContext).isTemperatureInCelsius()) + view.setBatteryTemperatureInCelsius(mBatteryStatus.getTemperatureInCelsius()); + else + view.setBatteryTemperatureInFahrenheit(mBatteryStatus.getTemperatureInFahrenheit()); + + // Napięcie na baterii + view.setBatteryVoltage(mBatteryStatus.getVoltage()); + + // Natężenie prądu + if (mBatteryStatus.isCurrentAvailable()) { + if (mBatteryStatus.isCurrentAverageAvailable()) + view.setBatteryCurrent(mBatteryStatus.getCurrent(), mBatteryStatus.getCurrentAverage()); + else + view.setBatteryCurrent(mBatteryStatus.getCurrent()); + } else + view.hideBatteryCurrentData(); + + // Stan zdrowia baterii + int healthStringId; + switch (mBatteryStatus.getHealthInformation()) { + case 2: healthStringId = R.string.health_good; break; + case 3: healthStringId = R.string.health_overheat; break; + case 4: healthStringId = R.string.health_dead; break; + case 5: healthStringId = R.string.health_over_voltage; break; + case 6: healthStringId = R.string.health_unspecified_failure; break; + case 7: healthStringId = R.string.health_cold; break; + default: healthStringId = R.string.health_unknown; + } + view.setBatteryHealth(healthStringId); + + // Licznik ładowań + view.setBatteryChargingCounter( + new BatteryDataRepository(mContext).getChargedCount() + ); } /** @@ -59,18 +116,21 @@ public void updateAllData() { public void startUpdates() { mUpdateData = true; - final Handler handler = new Handler(); - final Runnable updateTask = new Runnable() { + if (mRunnableHandler != null) + mRunnableHandler.removeCallbacks(mUpdater); + + mRunnableHandler = new Handler(); + mUpdater = new Runnable() { @Override public void run() { if (mUpdateData) { updateBatteryStatus(); - updateAllData(); - handler.postDelayed(this, DATA_UPDATE_INTERVAL); + updateView(); + mRunnableHandler.postDelayed(mUpdater, DATA_UPDATE_INTERVAL); } } }; - handler.postDelayed(updateTask, DATA_UPDATE_INTERVAL); + mRunnableHandler.postDelayed(mUpdater, DATA_UPDATE_INTERVAL); } /** @@ -79,6 +139,8 @@ public void run() { @Override public void stopUpdates() { mUpdateData = false; + if (mRunnableHandler != null) + mRunnableHandler.removeCallbacks(mUpdater); } } diff --git a/app/src/main/res/drawable-nodpi/thunder.png b/app/src/main/res/drawable-nodpi/thunder.png new file mode 100644 index 0000000..1328451 Binary files /dev/null and b/app/src/main/res/drawable-nodpi/thunder.png differ diff --git a/app/src/main/res/drawable-v21/ic_menu_gallery.xml b/app/src/main/res/drawable-v21/ic_menu_gallery.xml deleted file mode 100644 index f6872c4..0000000 --- a/app/src/main/res/drawable-v21/ic_menu_gallery.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/history_list_selector.xml b/app/src/main/res/drawable/history_list_selector.xml new file mode 100644 index 0000000..5d2fd9c --- /dev/null +++ b/app/src/main/res/drawable/history_list_selector.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_clear_all.xml b/app/src/main/res/drawable/ic_clear_all.xml new file mode 100644 index 0000000..db78320 --- /dev/null +++ b/app/src/main/res/drawable/ic_clear_all.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_delete.xml b/app/src/main/res/drawable/ic_delete.xml new file mode 100644 index 0000000..e1696ce --- /dev/null +++ b/app/src/main/res/drawable/ic_delete.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_delete_active.xml b/app/src/main/res/drawable/ic_delete_active.xml new file mode 100644 index 0000000..0e434a1 --- /dev/null +++ b/app/src/main/res/drawable/ic_delete_active.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_delete_inactive.xml b/app/src/main/res/drawable/ic_delete_inactive.xml new file mode 100644 index 0000000..ab60fe7 --- /dev/null +++ b/app/src/main/res/drawable/ic_delete_inactive.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_merge.xml b/app/src/main/res/drawable/ic_merge.xml new file mode 100644 index 0000000..80e097f --- /dev/null +++ b/app/src/main/res/drawable/ic_merge.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_merge_active.xml b/app/src/main/res/drawable/ic_merge_active.xml new file mode 100644 index 0000000..2047132 --- /dev/null +++ b/app/src/main/res/drawable/ic_merge_active.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_merge_inactive.xml b/app/src/main/res/drawable/ic_merge_inactive.xml new file mode 100644 index 0000000..c23a328 --- /dev/null +++ b/app/src/main/res/drawable/ic_merge_inactive.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_settings.xml b/app/src/main/res/drawable/ic_settings.xml new file mode 100644 index 0000000..ace746c --- /dev/null +++ b/app/src/main/res/drawable/ic_settings.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_toolbar_battery_charging_active.xml b/app/src/main/res/drawable/ic_toolbar_battery_charging_active.xml new file mode 100644 index 0000000..165e279 --- /dev/null +++ b/app/src/main/res/drawable/ic_toolbar_battery_charging_active.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/side_nav_bar.xml b/app/src/main/res/drawable/side_nav_bar.xml deleted file mode 100644 index 458b4b0..0000000 --- a/app/src/main/res/drawable/side_nav_bar.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - \ No newline at end of file diff --git a/app/src/main/res/layout-land/content_summary.xml b/app/src/main/res/layout-land/content_summary.xml new file mode 100644 index 0000000..5be8d1c --- /dev/null +++ b/app/src/main/res/layout-land/content_summary.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity.xml b/app/src/main/res/layout/activity.xml index 6ba9383..0d0ed76 100644 --- a/app/src/main/res/layout/activity.xml +++ b/app/src/main/res/layout/activity.xml @@ -21,7 +21,6 @@ android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" - android:background="?attr/colorPrimary" app:popupTheme="@style/AppTheme.PopupOverlay" /> diff --git a/app/src/main/res/layout/activity_no_navigation.xml b/app/src/main/res/layout/activity_no_navigation.xml new file mode 100644 index 0000000..85c2458 --- /dev/null +++ b/app/src/main/res/layout/activity_no_navigation.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/content_history.xml b/app/src/main/res/layout/content_history.xml new file mode 100644 index 0000000..8405d33 --- /dev/null +++ b/app/src/main/res/layout/content_history.xml @@ -0,0 +1,9 @@ + + \ No newline at end of file diff --git a/app/src/main/res/layout/content_summary.xml b/app/src/main/res/layout/content_summary.xml index 354c16a..7027e06 100644 --- a/app/src/main/res/layout/content_summary.xml +++ b/app/src/main/res/layout/content_summary.xml @@ -9,20 +9,35 @@ android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:orientation="vertical" + android:background="@color/colorWindowBackground" app:layout_behavior="@string/appbar_scrolling_view_behavior" tools:context=".ui.MainActivity"> - + - + + + + android:layout_marginBottom="24dp" + android:gravity="center"> + + + + diff --git a/app/src/main/res/layout/content_summary_card.xml b/app/src/main/res/layout/content_summary_card.xml new file mode 100644 index 0000000..e7030e0 --- /dev/null +++ b/app/src/main/res/layout/content_summary_card.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_history_detail.xml b/app/src/main/res/layout/dialog_history_detail.xml new file mode 100644 index 0000000..490598e --- /dev/null +++ b/app/src/main/res/layout/dialog_history_detail.xml @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/list_item_history.xml b/app/src/main/res/layout/list_item_history.xml new file mode 100644 index 0000000..ce1b890 --- /dev/null +++ b/app/src/main/res/layout/list_item_history.xml @@ -0,0 +1,39 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/nav_header.xml b/app/src/main/res/layout/nav_header.xml index fcc84ca..4a3ee95 100644 --- a/app/src/main/res/layout/nav_header.xml +++ b/app/src/main/res/layout/nav_header.xml @@ -2,7 +2,7 @@ + android:src="@mipmap/ic_launcher" /> - + + + + + + - + + + + + diff --git a/app/src/main/res/menu/main.xml b/app/src/main/res/menu/main.xml deleted file mode 100644 index a2411e3..0000000 --- a/app/src/main/res/menu/main.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - diff --git a/app/src/main/res/menu/menu_history.xml b/app/src/main/res/menu/menu_history.xml new file mode 100644 index 0000000..2aa429d --- /dev/null +++ b/app/src/main/res/menu/menu_history.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/menu_history_contextual.xml b/app/src/main/res/menu/menu_history_contextual.xml new file mode 100644 index 0000000..0899b7c --- /dev/null +++ b/app/src/main/res/menu/menu_history_contextual.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/menu_summary.xml b/app/src/main/res/menu/menu_summary.xml new file mode 100644 index 0000000..76778c9 --- /dev/null +++ b/app/src/main/res/menu/menu_summary.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/nav_drawer.xml b/app/src/main/res/menu/nav_drawer.xml index 1286f7c..34768c9 100644 --- a/app/src/main/res/menu/nav_drawer.xml +++ b/app/src/main/res/menu/nav_drawer.xml @@ -1,15 +1,20 @@ - + + android:title="@string/title_summary" /> + android:title="@string/title_history" /> + + + - diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png index cde69bc..3d31866 100644 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png index c133a0c..0d8436a 100644 Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png index bfa42f0..4ef8763 100644 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png index 324e72c..1ead658 100644 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png index aee44e1..97691de 100644 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index efee38f..d39ccec 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -6,4 +6,53 @@ Otwórz menu nawigacji Historia Podsumowanie + %1$d razy + Nieznane + Dobre + Przegrzanie + Martwa + Zbyt wysokie napięcie + Nieznane uszkodzenie + Zbyt zimna + Zdrowie + Temperatura + Napięcie + Natężenie + Ładowano + Zużycie Baterii + Bateria: %1$d%%. + Napięcie: %1$dmV. + Temperatura: %1$.1f°C. + Temperatura: %1$.1f°F. + Ładowano %1$d razy. + Odłączona. + Ładowanie normalne. + Ładowanie przez USB. + Ładowanie bezprzewodowe. + Nie udało sie zkończyć ładowania. + Nie udało się zapisać ładowania. + Liczba ładowań przed instalacją tej aplikacji + Poprzednie ładowania + Nieistotna długość ładowania + Minimalny czas w sekundach, po którym wpisy nie będą automatycznie usuwane + Temperatura + Temperatura w stopniach Fahrenheita + Temperatura w stopniach Celsjusza + Naładowanie + Typ Ładowarki + Czas Ładowania + Temperatura początkowa + Napięcie początkowe + Temperatura końcowa + Napięcie końcowe + Szczegóły Ładowania + Czy na pewno chcesz usunąć te wpisy? + Czy na pewno chcesz połączyć te wpisy? + Usunięto %1$d wpisów + Połączono %1$d wpisów + %1$dg %2$dm %3$ds + Usuń + Połącz + Zaznaczono: %1$d + Wyczyść zbędne wpisy \ No newline at end of file diff --git a/app/src/main/res/values-v21/styles.xml b/app/src/main/res/values-v21/styles.xml index 251fb9f..e70ac80 100644 --- a/app/src/main/res/values-v21/styles.xml +++ b/app/src/main/res/values-v21/styles.xml @@ -1,9 +1,12 @@ -> + - + + diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 3ab3e9c..8ebd2b9 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -1,6 +1,18 @@ - #3F51B5 - #303F9F - #FF4081 + #C8E6C9 + #4CAF50 + #388E3C + #009688 + + #212121 + #101010 + #727272 + #FFFFFF + + #E5E5E5 + #B6B6B6 + + #8B000000 + #41000000 diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index c2e2f02..16d36a3 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -2,9 +2,13 @@ 16dp 160dp + 16dp 16dp 16dp - 200dp + + + 240dp + 320dp diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7224cb3..5e8e399 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -8,4 +8,95 @@ Close navigation drawer Settings + + + %1$d%% + %1$dmV + %1$dmA + %1$.1f°C + %1$.1f°F + %1$d times + + @string/health_unknown + @string/health_good + @string/health_overheat + @string/health_dead + @string/health_over_voltage + @string/health_unspecified_failure + @string/health_cold + + Unknown + Good + Overheat + Dead + Over Voltage + Unspecified Failure + Cold + AC + USB + Wi + Mix + \? + + + Health + Temperature + Voltage + Current + Charged + Battery Usage + + + Battery: %1$d%%. + Voltage: %1$dmV. + Temperature: %1$.1f°C. + Temperature: %1$.1f°F. + Disconnected. + Charging with AC. + Charging with USB. + Charging with Wireless. + Charged %1$d times. + + + add_to_count + 0 + Previous Charges + Number of charges before installing this app + irrelevant_duration + 5 + Irrelevant Charging Duration + Minimum charging duration in seconds assuring entry won\'t be deleted + temperature_in_celsius + true + Temperature scale + Temperature in Celsius + Temperature in Fahrenheit + + + %1$d%% → %2$d%% + %1$d%% → --- + %1$dh %2$dm %3$ds + Selected: %1$d + Merge + Delete + Merged %1$d entries + Deleted %1$d entries + Do you really want to merge this items? + Do you really want to delete this items? + Clear Entries + + + Charging Details + Duration + Charge + Charger Type + Initial Temperature + Finished Temperature + Initial Voltage + Finished Voltage + --- + + + Failed to save this charging. + Failed to complete charging. diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 545b9c6..bf051e5 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -1,20 +1,39 @@ - - - - + + + + - + + + + diff --git a/app/src/main/res/xml/settings.xml b/app/src/main/res/xml/settings.xml new file mode 100644 index 0000000..04f25d2 --- /dev/null +++ b/app/src/main/res/xml/settings.xml @@ -0,0 +1,27 @@ + + + + + + + + \ No newline at end of file diff --git a/build.gradle b/build.gradle index e0b366a..cc19335 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,8 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:1.5.0' + classpath 'com.android.tools.build:gradle:2.1.0' + classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index f23df6e..b9e5d19 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Wed Oct 21 11:34:03 PDT 2015 +#Sat Apr 30 19:46:32 CEST 2016 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip