diff --git a/.gitignore b/.gitignore index c2d7006..f32f0c5 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,5 @@ local.properties build/ *~ -*.swp \ No newline at end of file +*.swp +*.class diff --git a/.travis.yml b/.travis.yml index c1692c3..2eb9dca 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,5 +3,5 @@ android: components: - platform-tools - tools - - build-tools-23.0.3 - - android-23 + - build-tools-29.0.2 + - android-30 diff --git a/README.md b/README.md index cf58a79..d1117eb 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Small Android application to use with mete, the Matekasse of Chaosdorf. ### Where * [Chaosdorf Wiki](https://wiki.chaosdorf.de/Meteroid) -* [Google Play](https://play.google.com/store/apps/details?id=de.chaosdorf.meteroid) +* [Google Play](https://play.google.com/store/apps/details?id=de.chaosdorf.meteroid2) * [F-Droid](https://f-droid.org/repository/browse/?fdid=de.chaosdorf.meteroid) * [apk](https://github.com/chaosdorf/meteroid/releases/) diff --git a/gfx/button_barcode.eps b/gfx/button_barcode.eps new file mode 100644 index 0000000..eb6a67a --- /dev/null +++ b/gfx/button_barcode.eps @@ -0,0 +1,41 @@ +%!PS-Adobe-2.0 +%%Creator: "barcode", libbarcode sample frontend +%%DocumentPaperSizes: a4 +%%EndComments +%%EndProlog + +%%Page: 1 1 + +% Printing barcode for "MeTe", scaled 1.00, encoded using "code 128-B" +% The space/bar succession is represented by the following widths (space first): +% 02112141131231122142133111122143111412331112 +[ +% height xpos ypos width height xpos ypos width + [75.00 11.00 15.00 1.85] [75.00 13.50 15.00 0.85] + [75.00 16.50 15.00 0.85] [70.00 21.50 20.00 0.85] + [70.00 24.50 20.00 2.85] [70.00 28.00 20.00 1.85] + [70.00 32.50 20.00 0.85] [70.00 35.00 20.00 1.85] + [70.00 38.50 20.00 0.85] [70.00 44.00 20.00 1.85] + [70.00 47.50 20.00 2.85] [70.00 52.50 20.00 0.85] + [70.00 54.50 20.00 0.85] [70.00 57.00 20.00 1.85] + [70.00 60.50 20.00 0.85] [70.00 66.50 20.00 2.85] + [70.00 69.50 20.00 0.85] [70.00 73.00 20.00 3.85] + [75.00 77.00 15.00 1.85] [75.00 82.50 15.00 2.85] + [75.00 85.50 15.00 0.85] [75.00 88.00 15.00 1.85] + +] { {} forall setlinewidth moveto 0 exch rlineto stroke} bind forall +[ +% char xpos ypos fontsize + [(M) 21.00 10.00 12.00] + [(e) 32.00 10.00 0.00] + [(T) 43.00 10.00 0.00] + [(e) 54.00 10.00 0.00] +] { {} forall dup 0.00 ne { + /Helvetica findfont exch scalefont setfont + } {pop} ifelse + moveto show} bind forall +% End barcode for "MeTe" + +showpage +%%Trailer + diff --git a/gfx/button_barcode.png b/gfx/button_barcode.png new file mode 100644 index 0000000..928d351 Binary files /dev/null and b/gfx/button_barcode.png differ diff --git a/gfx/button_barcode.xcf b/gfx/button_barcode.xcf new file mode 100644 index 0000000..baf9e0b Binary files /dev/null and b/gfx/button_barcode.xcf differ diff --git a/gfx/empty_glass.png b/gfx/empty_glass.png new file mode 100644 index 0000000..a8e5096 Binary files /dev/null and b/gfx/empty_glass.png differ diff --git a/meteroid/src/main/ic_launcher-web.png b/gfx/ic_launcher-web.png similarity index 100% rename from meteroid/src/main/ic_launcher-web.png rename to gfx/ic_launcher-web.png diff --git a/gradle.properties b/gradle.properties index b3c7a03..e69de29 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +0,0 @@ -org.gradle.jvmargs=-Xmx2048M diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 3391a4c..e708b1c 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 2deee66..442d913 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Fri Jun 17 17:57:55 CEST 2016 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-bin.zip diff --git a/gradlew b/gradlew index 9d82f78..4f906e0 100755 --- a/gradlew +++ b/gradlew @@ -1,4 +1,20 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ############################################################################## ## @@ -6,20 +22,38 @@ ## ############################################################################## -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" -warn ( ) { +warn () { echo "$*" } -die ( ) { +die () { echo echo "$*" echo @@ -30,6 +64,7 @@ die ( ) { cygwin=false msys=false darwin=false +nonstop=false case "`uname`" in CYGWIN* ) cygwin=true @@ -40,28 +75,14 @@ case "`uname`" in MINGW* ) msys=true ;; + NONSTOP* ) + nonstop=true + ;; esac -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then @@ -85,7 +106,7 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then @@ -105,10 +126,11 @@ if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath @@ -134,27 +156,30 @@ if $cygwin ; then else eval `echo args$i`="\"$arg\"" fi - i=$((i+1)) + i=`expr $i + 1` done case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " } -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index aec9973..ac1b06f 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,3 +1,19 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @@ -8,20 +24,23 @@ @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if "%ERRORLEVEL%" == "0" goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -35,7 +54,7 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% @@ -45,34 +64,14 @@ echo location of your Java installation. goto fail -:init -@rem Get command-line arguments, handling Windowz variants - -if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ - :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell diff --git a/meteroid/build.gradle b/meteroid/build.gradle index 97947ea..3c1a541 100644 --- a/meteroid/build.gradle +++ b/meteroid/build.gradle @@ -24,36 +24,76 @@ buildscript { repositories { - maven { url 'http://repo1.maven.org/maven2' } + mavenCentral() + maven { + url "https://plugins.gradle.org/m2/" + } + google() } dependencies { - classpath 'com.android.tools.build:gradle:2.1.0' + classpath 'com.android.tools.build:gradle:4.0.1' + classpath 'com.github.spotbugs:spotbugs-gradle-plugin:3.0.0' } } apply plugin: 'com.android.application' +apply plugin: "com.github.spotbugs" dependencies { repositories { - maven { url 'http://repo1.maven.org/maven2' } + mavenCentral() + google() + jcenter() } - compile 'org.jetbrains:annotations:13.0' - compile 'com.squareup.okhttp3:okhttp:3.3.1' + implementation 'org.jetbrains:annotations:13.0' + // Retrofit >= 2.7 needs Android 5+ + implementation 'com.squareup.retrofit2:retrofit:2.6.4' + implementation 'com.squareup.retrofit2:converter-gson:2.6.4' + implementation 'com.google.zxing:android-integration:3.3.0' + implementation 'com.shamanland:fab:0.0.8' + implementation 'com.android.support:support-core-ui:27.1.0' + implementation 'com.squareup.picasso:picasso:2.71828' } //see https://stackoverflow.com/a/22183825/2192464 gradle.projectsEvaluated { tasks.withType(JavaCompile) { options.compilerArgs << "-Xlint:deprecation" + options.compilerArgs << "-Xlint:unchecked" } } android { - compileSdkVersion 23 - buildToolsVersion '23.0.3' + compileSdkVersion 30 + buildToolsVersion '29.0.2' signingConfigs {} lintOptions { abortOnError false - //ActionBar on Gingerbread - disable 'UnusedAttribute' } + buildFeatures { + dataBinding = true + viewBinding = true + } + defaultConfig { + targetSdkVersion 30 + minSdkVersion 14 + } +} + + +// see https://stackoverflow.com/a/52718914/2192464 +sourceSets { + main { + java.srcDirs = [] + } +} +tasks.withType(com.github.spotbugs.SpotBugsTask) { + dependsOn 'assembleDebug' + classes = files("$projectDir.absolutePath/build/intermediates/javac/debug/classes/") + source = fileTree('src/main/java') +} +spotbugs { + toolVersion = "4.0.1" + ignoreFailures = true + effort = "max" + reportLevel = "high" } diff --git a/meteroid/src/main/AndroidManifest.xml b/meteroid/src/main/AndroidManifest.xml index 0569ca1..a6f4bb0 100644 --- a/meteroid/src/main/AndroidManifest.xml +++ b/meteroid/src/main/AndroidManifest.xml @@ -25,19 +25,17 @@ - - + android:versionCode="40" + android:versionName="2.8.0"> + + + diff --git a/meteroid/src/main/java/de/chaosdorf/meteroid/About.java b/meteroid/src/main/java/de/chaosdorf/meteroid/About.java new file mode 100644 index 0000000..5707331 --- /dev/null +++ b/meteroid/src/main/java/de/chaosdorf/meteroid/About.java @@ -0,0 +1,110 @@ +/******************************************************************************* + + * The MIT License (MIT) + * + * Copyright (c) 2013-2016 Chaosdorf e.V. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +package de.chaosdorf.meteroid; + +import android.app.ActionBar; +import android.app.Activity; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager.NameNotFoundException; +import android.databinding.DataBindingUtil; +import android.databinding.ObservableBoolean; +import android.os.Build; +import android.os.Bundle; +import android.os.VibrationEffect; +import android.os.Vibrator; +import android.view.MenuItem; +import android.view.View; + + +import de.chaosdorf.meteroid.databinding.ActivityAboutBinding; + +public class About extends Activity +{ + private ActivityAboutBinding binding; + private Vibrator vibrator; + private final ObservableBoolean glassEmpty = new ObservableBoolean(false); + private static final String FALLBACK_VERSION_NAME = "UNKNOWN"; + + @Override + protected void onCreate(final Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + binding = DataBindingUtil.setContentView(this, R.layout.activity_about); + binding.setGlassEmpty(glassEmpty); + + vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE); + + String versionName = FALLBACK_VERSION_NAME; + try + { + PackageInfo info = getPackageManager().getPackageInfo(getPackageName(), 0); + versionName = info.versionName; + } + catch(NameNotFoundException ignored) {} + binding.setVersionName(versionName); + + ActionBar actionBar = getActionBar(); + if(actionBar != null) + { + actionBar.setDisplayHomeAsUpEnabled(true); + } + + binding.appIcon.setOnLongClickListener(new View.OnLongClickListener() + { + private static final int VIBRATOR_DURATION = 500; // ms + + @Override + public boolean onLongClick(View view) + { + if(vibrator != null && vibrator.hasVibrator()) + { + if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) + { + vibrator.vibrate(VibrationEffect.createOneShot(VIBRATOR_DURATION, VibrationEffect.DEFAULT_AMPLITUDE)); + } + else + { + vibrator.vibrate(VIBRATOR_DURATION); + } + } + glassEmpty.set(!glassEmpty.get()); + return true; + } + }); + } + + @Override + public boolean onOptionsItemSelected(final MenuItem item) + { + switch (item.getItemId()) + { + case android.R.id.home: + finish(); + break; + } + return super.onOptionsItemSelected(item); + } +} diff --git a/meteroid/src/main/java/de/chaosdorf/meteroid/BuyDrink.java b/meteroid/src/main/java/de/chaosdorf/meteroid/BuyDrink.java index cb643f0..7339b38 100644 --- a/meteroid/src/main/java/de/chaosdorf/meteroid/BuyDrink.java +++ b/meteroid/src/main/java/de/chaosdorf/meteroid/BuyDrink.java @@ -24,13 +24,17 @@ package de.chaosdorf.meteroid; +import android.annotation.TargetApi; import android.app.ActionBar; -import android.app.Activity; import android.content.Context; -import android.content.SharedPreferences; +import android.content.Intent; +import android.content.pm.ShortcutInfo; +import android.content.pm.ShortcutManager; +import android.databinding.DataBindingUtil; +import android.graphics.Color; +import android.graphics.drawable.Icon; import android.os.Bundle; import android.os.Build; -import android.preference.PreferenceManager; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.Menu; @@ -39,27 +43,29 @@ import android.view.ViewGroup; import android.view.Window; import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.GridView; -import android.widget.ImageButton; import android.widget.ImageView; -import android.widget.ListView; -import android.widget.ProgressBar; import android.widget.TextView; +import com.shamanland.fab.FloatingActionButton; +import com.shamanland.fab.ShowHideOnScroll; + import org.jetbrains.annotations.NotNull; -import java.text.DecimalFormat; +import java.util.Arrays; +import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; +import com.google.zxing.integration.android.IntentIntegrator; +import com.google.zxing.integration.android.IntentResult; -import de.chaosdorf.meteroid.controller.DrinkController; +import de.chaosdorf.meteroid.controller.MeteroidAdapter; import de.chaosdorf.meteroid.controller.MoneyController; -import de.chaosdorf.meteroid.controller.UserController; -import de.chaosdorf.meteroid.longrunningio.LongRunningIOGet; +import de.chaosdorf.meteroid.databinding.ActivityBuyDrinkBinding; +import de.chaosdorf.meteroid.longrunningio.LongRunningIOCallback; +import de.chaosdorf.meteroid.longrunningio.LongRunningIORequest; import de.chaosdorf.meteroid.longrunningio.LongRunningIOTask; import de.chaosdorf.meteroid.model.BuyableItem; import de.chaosdorf.meteroid.model.User; @@ -68,90 +74,274 @@ import de.chaosdorf.meteroid.util.Utility; import de.chaosdorf.meteroid.MeteroidNetworkActivity; -public class BuyDrink extends MeteroidNetworkActivity implements AdapterView.OnItemClickListener +public class BuyDrink extends MeteroidNetworkActivity implements AdapterView.OnItemClickListener, LongRunningIOCallback { - private final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("0.00 '\u20AC'"); - private final AtomicBoolean isBuying = new AtomicBoolean(true); + private final AtomicBoolean intentHandled = new AtomicBoolean(false); private final AtomicReference buyingItem = new AtomicReference(null); - private Activity activity = null; - private ProgressBar progressBar = null; - private GridView gridView = null; - private ListView listView = null; - private String hostname = null; - - private int userID = 0; private User user; - - private boolean useGridView; - private boolean multiUserMode; + private ActivityBuyDrinkBinding binding; + private ShortcutManager shortcutManager; + private IntentIntegrator barcodeIntegrator; + + private static final String ACTION_BUY = "de.chaosdorf.meteroid.ACTION_BUY"; + private static final String ACTION_SCAN = "de.chaosdorf.meteroid.ACTION_SCAN"; + private static final String EXTRA_BUYABLE_ITEM_IS_DRINK = "de.chaosdorf.meteroid.EXTRA_BUYABLE_ITEM_IS_DRINK"; + private static final String EXTRA_BUYABLE_ITEM_ID = "de.chaosdorf.meteroid.EXTRA_BUYABLE_ITEM_ID"; + private static final String EXTRA_BUYABLE_ITEM_PRICE = "de.chaosdorf.meteroid.EXTRA_BUYABLE_ITEM_PRICE"; @Override protected void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); - activity = this; requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); - setContentView(R.layout.activity_buy_drink); + binding = DataBindingUtil.setContentView(this, R.layout.activity_buy_drink); + binding.setUser(user); + binding.setDECIMALFORMAT(DECIMAL_FORMAT); - progressBar = (ProgressBar) findViewById(R.id.progress_bar); - gridView = (GridView) findViewById(R.id.grid_view); - listView = (ListView) findViewById(R.id.list_view); + barcodeIntegrator = new IntentIntegrator(this); - final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); - hostname = prefs.getString("hostname", null); - userID = prefs.getInt("userid", 0); - useGridView = prefs.getBoolean("use_grid_view", false); - multiUserMode = prefs.getBoolean("multi_user_mode", false); - - final ImageButton backButton = (ImageButton) findViewById(R.id.button_back); - backButton.setOnClickListener(new View.OnClickListener() + ActionBar actionBar = getActionBar(); + actionBar.setDisplayHomeAsUpEnabled(true); + + if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) + { + binding.fab.setOnClickListener(new View.OnClickListener() + { + public void onClick(View view) + { + scanBarcode(); + } + }); + binding.fab.setVisibility(View.VISIBLE); + } + + if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) { - public void onClick(View view) + if(!config.multiUserMode) { - Utility.resetUsername(activity); - Utility.startActivity(activity, PickUsername.class); + shortcutManager = getSystemService(ShortcutManager.class); + // make sure all pinned shortcuts are enabled as we have a current user + List shortcuts = shortcutManager.getPinnedShortcuts(); + // if this were Java 8 I could use a stream :( + List shortcutIDs = new ArrayList<>(); + for(ShortcutInfo shortcut : shortcuts) { + shortcutIDs.add(shortcut.getId()); + } + shortcutManager.enableShortcuts(shortcutIDs); } - }); - - final ImageButton reloadButton = (ImageButton) findViewById(R.id.button_reload); - reloadButton.setOnClickListener(new View.OnClickListener() + } + + reload(); + } + + private void handleIntent(List buyableItemList) + { + if (intentHandled.compareAndSet(false, true)) { - public void onClick(View view) + Intent intent = getIntent(); + if(intent != null) { - Utility.startActivity(activity, BuyDrink.class); + String action = intent.getAction(); + if(action != null) + { + if(action.equals(ACTION_BUY)) // shortcut + { + handleBuyIntent(buyableItemList, intent); + } + else if(action.equals(ACTION_SCAN)) + { + scanBarcode(); + } + } } - }); - - final ImageButton editButton = (ImageButton) findViewById(R.id.button_edit); - editButton.setOnClickListener(new View.OnClickListener() + } + } + + private void handleBuyIntent(List buyableItemList, Intent intent) + { + BuyableItem itemSelected = null; + if(intent.getBooleanExtra(EXTRA_BUYABLE_ITEM_IS_DRINK, false)) { - public void onClick(View view) + int id = intent.getIntExtra(EXTRA_BUYABLE_ITEM_ID, -1); + if(id == -1) { - Utility.startActivity(activity, UserSettings.class); + Utility.displayToastMessage(this, getResources().getString(R.string.buy_drink_invalid_intent)); + return; } - }); - - if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) + for(BuyableItem item: buyableItemList) + { + if(item.isDrink()) + { + if(((Drink)item).getId() == id) { + itemSelected = item; + break; + } + } + } + } + else { - ActionBar actionBar = getActionBar(); - actionBar.setDisplayHomeAsUpEnabled(true); - reloadButton.setVisibility(View.GONE); - backButton.setVisibility(View.GONE); - editButton.setVisibility(View.GONE); + double price = intent.getDoubleExtra(EXTRA_BUYABLE_ITEM_PRICE, 0.0); + if(price == 0.0) + { + Utility.displayToastMessage(this, getResources().getString(R.string.buy_drink_invalid_intent)); + return; + } + for(BuyableItem item: buyableItemList) + { + if(!item.isDrink()) + { + if(item.getPrice() == price) + { + itemSelected = item; + break; + } + } + } + } + if(itemSelected == null) + { + Utility.displayToastMessage(this, getResources().getString(R.string.buy_drink_invalid_intent)); + return; + } + buy(itemSelected); + } + + private void buy(BuyableItem buyableItem) + { + buyingItem.set(buyableItem); + setProgressBarIndeterminateVisibility(true); + if(buyableItem.isDrink()) + { + new LongRunningIORequest(this, LongRunningIOTask.BUY_DRINK, connection.getAPI().buy(config.userID, ((Drink)buyableItem).getId())); + } + else + { + new LongRunningIORequest(this, LongRunningIOTask.ADD_MONEY, connection.getAPI().deposit(config.userID, -buyableItem.getPrice())); + } + + if(shortcutManager != null) + { + ShortcutInfo shortcut = shortcutForItem(buyableItem); + updateShortcuts(shortcut); + shortcutManager.reportShortcutUsed(shortcut.getId()); } - - new LongRunningIOGet(this, LongRunningIOTask.GET_USER, hostname + "users/" + userID + ".json"); - new LongRunningIOGet(this, LongRunningIOTask.GET_DRINKS, hostname + "drinks.json"); + } + + private void scanBarcode() + { + if(shortcutManager != null) + { + Intent intent = new Intent(this, this.getClass()); + intent.setAction(ACTION_SCAN); + ShortcutInfo shortcut = new ShortcutInfo.Builder(this, "barcode") + .setShortLabel(getResources().getString(R.string.shortcut_scan)) + .setIcon(Icon.createWithResource(this, R.drawable.button_barcode)) + .setIntent(intent) + .build(); + updateShortcuts(shortcut); + shortcutManager.reportShortcutUsed(shortcut.getId()); + } + barcodeIntegrator.initiateScan(); + } + + private ShortcutInfo shortcutForItem(BuyableItem item) + { + String id = null; + Intent intent = new Intent(this, this.getClass()); + intent.setAction(ACTION_BUY); + intent.putExtra(EXTRA_BUYABLE_ITEM_IS_DRINK, item.isDrink()); + if(item.isDrink()) + { + Drink drink = (Drink)item; + id = "d" + drink.getId(); + intent.putExtra(EXTRA_BUYABLE_ITEM_ID, drink.getId()); + } + else + { + id = "m" + item.getLogoUrl(null); + intent.putExtra(EXTRA_BUYABLE_ITEM_PRICE, item.getPrice()); + } + return new ShortcutInfo.Builder(this, id) + .setShortLabel(item.getName()) + .setIcon(Icon.createWithResource(this, R.drawable.default_drink)) // TODO + .setIntent(intent) + .build(); + } + + /** + * Updates the dynamic shortcuts. + * If a shortcut with this ID does not yet exist, it is added and some other one is removed. + * If a shortcut with this ID does already exist, nothing happens. + */ + @TargetApi(Build.VERSION_CODES.N_MR1) + private void updateShortcuts(ShortcutInfo shortcut) { + assert shortcutManager != null; + List shortcuts = shortcutManager.getDynamicShortcuts(); + for(ShortcutInfo current : shortcuts) { + if(current.getId().equals(shortcut.getId())) { + // nothing to do here + return; + } + } + + // Do we have enough space to add another one? + // getMaxShortcutCountPerActivity gives us the upper bound of how many shortcuts we can set. + // But not every launcher can display as much. WTF. + // At least the AOSP launcher can display 4. Let's hope that others can do the same. + int maxShortcuts = Math.min(shortcutManager.getMaxShortcutCountPerActivity(), 4); + if(shortcuts.size() >= maxShortcuts) { + // if not, we have to remove one first + // pick the last one per default - it's the one we put there the first, I think + String idToRemove = shortcuts.get(shortcuts.size() - 1).getId(); + shortcutManager.removeDynamicShortcuts(Arrays.asList(idToRemove)); + } + // then add our new one + shortcutManager.addDynamicShortcuts(Arrays.asList(shortcut)); + } + + public void reload() + { + binding.gridView.setVisibility(View.GONE); + binding.listView.setVisibility(View.GONE); + binding.buyDrinkError.setVisibility(View.GONE); + binding.progressBar.setVisibility(View.VISIBLE); + new LongRunningIORequest(this, LongRunningIOTask.GET_USER, connection.getAPI().getUser(config.userID)); + new LongRunningIORequest>(this, LongRunningIOTask.GET_DRINKS, connection.getAPI().listDrinks()); + } + + private void pickUsername() + { + config.userID = config.NO_USER_ID; + config.save(); + if(shortcutManager != null) { + // if we have no user, we can't buy drinks + // removing dynamic shortcuts is easy + shortcutManager.removeAllDynamicShortcuts(); + // but if the user pinned them, that's going to be a bit more difficult + List shortcuts = shortcutManager.getPinnedShortcuts(); + // if this were Java 8 I could use a stream :( + List shortcutIDs = new ArrayList<>(); + for(ShortcutInfo shortcut : shortcuts) { + shortcutIDs.add(shortcut.getId()); + } + shortcutManager.disableShortcuts(shortcutIDs); + } + if(!config.multiUserMode) + { + Utility.startActivity(this, PickUsername.class, Intent.FLAG_ACTIVITY_CLEAR_TOP); + } + finish(); } @Override public boolean onCreateOptionsMenu(final Menu menu) { getMenuInflater().inflate(R.menu.buydrink, menu); - MenuUtility.setChecked(menu, R.id.use_grid_view, useGridView); - MenuUtility.setChecked(menu, R.id.multi_user_mode, multiUserMode); + MenuUtility.setChecked(menu, R.id.use_grid_view, config.useGridView); + MenuUtility.setChecked(menu, R.id.multi_user_mode, config.multiUserMode); return true; } @@ -161,60 +351,46 @@ public boolean onOptionsItemSelected(final MenuItem item) switch (item.getItemId()) { case android.R.id.home: - Utility.resetUsername(this); - Utility.startActivity(this, PickUsername.class); + pickUsername(); break; case R.id.action_reload: - Utility.startActivity(this, BuyDrink.class); + reload(); break; case R.id.action_edit: Utility.startActivity(this, UserSettings.class); break; + case R.id.action_barcode: + scanBarcode(); + break; case R.id.edit_hostname: Utility.startActivity(this, SetHostname.class); break; case R.id.reset_username: - Utility.resetUsername(this); - Utility.startActivity(this, PickUsername.class); + pickUsername(); break; case R.id.use_grid_view: - useGridView = Utility.toggleUseGridView(this); - item.setChecked(useGridView); - Utility.startActivity(this, BuyDrink.class); + Utility.toggleUseGridView(this); + item.setChecked(config.useGridView); + recreate(); break; - case R.id.multi_user_mode: - multiUserMode = MenuUtility.onClickMultiUserMode(this, item); + case R.id.about: + Utility.startActivity(this, About.class); break; } return super.onOptionsItemSelected(item); } - @Override - public boolean onKeyDown(final int keyCode, @NotNull final KeyEvent event) - { - if (keyCode == KeyEvent.KEYCODE_BACK) - { - if (multiUserMode) - { - Utility.resetUsername(this); - Utility.startActivity(this, MainActivity.class); - return true; - } - } - return super.onKeyDown(keyCode, event); - } - @Override public void onDestroy() { buyingItem.set(null); - if (gridView != null) + if (binding.gridView != null) { - gridView.setAdapter(null); + binding.gridView.setAdapter(null); } - if (listView != null) + if (binding.listView != null) { - listView.setAdapter(null); + binding.listView.setAdapter(null); } super.onDestroy(); } @@ -231,116 +407,116 @@ public void displayErrorMessage(final LongRunningIOTask task, final String messa { Utility.displayToastMessage(this, message); } - final TextView textView = (TextView) findViewById(R.id.buy_drink_error); - textView.setVisibility(View.VISIBLE); - gridView.setVisibility(View.GONE); - listView.setVisibility(View.GONE); - progressBar.setVisibility(View.GONE); + binding.buyDrinkError.setVisibility(View.VISIBLE); + binding.gridView.setVisibility(View.GONE); + binding.listView.setVisibility(View.GONE); + binding.progressBar.setVisibility(View.GONE); } @Override - public void processIOResult(final LongRunningIOTask task, final String json) + public void processIOResult(final LongRunningIOTask task, final Object result) { - if (json != null) + switch (task) { - switch (task) + // Parse user data + case GET_USER: + case UPDATE_USER: { - // Parse user data - case GET_USER: - case UPDATE_USER: + user = (User)result; + binding.setUser(user); + ((BuyDrink)activity).user = user; + if (task == LongRunningIOTask.GET_USER) { - user = UserController.parseUserFromJSON(json); - if (task == LongRunningIOTask.GET_USER) - { - final TextView label = (TextView) findViewById(R.id.username); - final ImageView icon = (ImageView) findViewById(R.id.icon); - label.setText(user.getName()); - Utility.loadGravatarImage(this, icon, user); - } - final TextView balance = (TextView) findViewById(R.id.balance); - balance.setText(DECIMAL_FORMAT.format(user.getBalance())); - isBuying.set(false); - setProgressBarIndeterminateVisibility(false); - break; + Utility.loadUserImage(this, binding.icon, user); } + isBuying.set(false); + setProgressBarIndeterminateVisibility(false); + break; + } + + // Parse drinks + case GET_DRINKS: + { + final List buyableItemList = (List)result; + MoneyController.addMoney(buyableItemList); - // Parse drinks - case GET_DRINKS: + final BuyableItemAdapter buyableItemAdapter = new BuyableItemAdapter(buyableItemList); + if (config.useGridView) { - final List buyableItemList = DrinkController.parseAllDrinksFromJSON(json, hostname); - MoneyController.addMoney(buyableItemList); - Collections.sort(buyableItemList, new BuyableComparator()); - - final BuyableItemAdapter buyableItemAdapter = new BuyableItemAdapter(buyableItemList); - if (useGridView) + binding.gridView.setAdapter(buyableItemAdapter); + binding.gridView.setOnItemClickListener(this); + binding.gridView.setVisibility(View.VISIBLE); + } + else + { + binding.listView.setAdapter(buyableItemAdapter); + binding.listView.setOnItemClickListener(this); + binding.listView.setVisibility(View.VISIBLE); + } + binding.progressBar.setVisibility(View.GONE); + if(binding.fab != null) + { + (config.useGridView? binding.gridView : binding.listView) + .setOnTouchListener(new ShowHideOnScroll(binding.fab)); + } + handleIntent(buyableItemList); + break; + } + + // Bought drink + case BUY_DRINK: + { + final BuyableItem buyableItem = buyingItem.get(); + if (buyableItem != null) + { + buyingItem.set(null); + Utility.displayToastMessage(this, + String.format( + getResources().getString(R.string.buy_drink_bought_drink), + buyableItem.getName(), + DECIMAL_FORMAT.format(buyableItem.getPrice()) + ) + ); + // Adjust the displayed balance to give an immediate user feedback + if (user != null) { - gridView.setAdapter(buyableItemAdapter); - gridView.setOnItemClickListener(this); - gridView.setVisibility(View.VISIBLE); + user.setBalance(user.getBalance() - buyableItem.getPrice()); } - else + if (config.multiUserMode && user.getRedirect()) { - listView.setAdapter(buyableItemAdapter); - listView.setOnItemClickListener(this); - listView.setVisibility(View.VISIBLE); + pickUsername(); + break; } - progressBar.setVisibility(View.GONE); - break; - } - - // Bought drink - case BUY_DRINK: - { - final BuyableItem buyableItem = buyingItem.get(); - if (buyableItem != null) + if(!buyableItem.getActive()) { - buyingItem.set(null); - Utility.displayToastMessage(this, - String.format( - getResources().getString(R.string.buy_drink_bought_drink), - buyableItem.getName(), - DECIMAL_FORMAT.format(buyableItem.getDonationRecommendation()) - ) - ); - // Adjust the displayed balance to give an immediate user feedback - if (user != null) - { - final TextView balance = (TextView) findViewById(R.id.balance); - balance.setText(DECIMAL_FORMAT.format(user.getBalance() - buyableItem.getDonationRecommendation())); - } - if (multiUserMode) - { - Utility.startActivity(this, PickUsername.class); - break; - } + new LongRunningIORequest>(this, LongRunningIOTask.GET_DRINKS, connection.getAPI().listDrinks()); } - new LongRunningIOGet(this, LongRunningIOTask.UPDATE_USER, hostname + "users/" + userID + ".json"); - break; } - - // Added money - case ADD_MONEY: + new LongRunningIORequest(this, LongRunningIOTask.UPDATE_USER, connection.getAPI().getUser(config.userID)); + break; + } + + // Added money + case ADD_MONEY: + { + final BuyableItem buyableItem = buyingItem.get(); + if (buyableItem != null) { - final BuyableItem buyableItem = buyingItem.get(); - if (buyableItem != null) + buyingItem.set(null); + Utility.displayToastMessage(this, + String.format( + getResources().getString(R.string.buy_drink_added_money), + DECIMAL_FORMAT.format(-buyableItem.getPrice()) + ) + ); + // Adjust the displayed balance to give an immediate user feedback + if (user != null) { - buyingItem.set(null); - Utility.displayToastMessage(this, - String.format( - getResources().getString(R.string.buy_drink_added_money), - DECIMAL_FORMAT.format(-buyableItem.getDonationRecommendation()) - ) - ); - // Adjust the displayed balance to give an immediate user feedback - if (user != null) - { - final TextView balance = (TextView) findViewById(R.id.balance); - balance.setText(DECIMAL_FORMAT.format(user.getBalance() - buyableItem.getDonationRecommendation())); - } + user.setBalance(user.getBalance() - buyableItem.getPrice()); } - new LongRunningIOGet(this, LongRunningIOTask.UPDATE_USER, hostname + "users/" + userID + ".json"); - break; } + new LongRunningIORequest(this, LongRunningIOTask.UPDATE_USER, connection.getAPI().getUser(config.userID)); + break; } } } @@ -355,24 +531,18 @@ public void onItemClick(final AdapterView adapterView, final View view, final } if (isBuying.compareAndSet(false, true)) { - final BuyableItem buyableItem = (BuyableItem) (useGridView ? gridView.getItemAtPosition(index) : listView.getAdapter().getItem(index)); + final BuyableItem buyableItem = (BuyableItem) (config.useGridView ? binding.gridView.getItemAtPosition(index) : binding.listView.getAdapter().getItem(index)); if (buyableItem != null) { - buyingItem.set(buyableItem); - setProgressBarIndeterminateVisibility(true); - if(buyableItem.isDrink()) - { - new LongRunningIOGet(this, LongRunningIOTask.BUY_DRINK, hostname + "users/" + userID + "/buy.json?drink=" + ((Drink)buyableItem).getId()); - } - else - { - new LongRunningIOGet(this, LongRunningIOTask.ADD_MONEY, hostname + "users/" + userID + "/deposit.json?amount=" + (-buyableItem.getDonationRecommendation())); - } + buy(buyableItem); + } else { + isBuying.set(false); + System.err.println("Touched item was null, ignoring."); } } } - private class BuyableItemAdapter extends ArrayAdapter + private class BuyableItemAdapter extends MeteroidAdapter { private final List drinkList; private final LayoutInflater inflater; @@ -381,7 +551,7 @@ private class BuyableItemAdapter extends ArrayAdapter { super(activity, R.layout.activity_buy_drink, drinkList); this.drinkList = drinkList; - this.inflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + this.inflater = activity.getLayoutInflater(); } public View getView(final int position, final View convertView, final ViewGroup parent) @@ -389,7 +559,7 @@ public View getView(final int position, final View convertView, final ViewGroup View view = convertView; if (view == null) { - view = inflater.inflate(useGridView ? R.layout.activity_buy_drink_item_gridview : R.layout.activity_buy_drink_item, parent, false); + view = inflater.inflate(config.useGridView ? R.layout.activity_buy_drink_item_gridview : R.layout.activity_buy_drink_item, parent, false); } if (view == null) { @@ -399,10 +569,10 @@ public View getView(final int position, final View convertView, final ViewGroup final BuyableItem buyableItem = drinkList.get(position); final ImageView icon = (ImageView) view.findViewById(R.id.icon); - Utility.loadBuyableItemImage(activity, icon, buyableItem); + Utility.loadBuyableItemImage(activity, icon, buyableItem, config.hostname); final TextView label = (TextView) view.findViewById(R.id.label); - label.setText(createLabel(buyableItem, useGridView)); + label.setText(createLabel(buyableItem, config.useGridView)); return view; } @@ -414,7 +584,7 @@ private String createLabel(final BuyableItem buyableItem, final boolean useGridV { label.append("+"); } - label.append(DECIMAL_FORMAT.format(-buyableItem.getDonationRecommendation())); + label.append(DECIMAL_FORMAT.format(-buyableItem.getPrice())); if (buyableItem.isDrink()) { if (useGridView) @@ -426,13 +596,19 @@ private String createLabel(final BuyableItem buyableItem, final boolean useGridV return label.toString(); } } - - private class BuyableComparator implements Comparator + + // the barcode scan result + public void onActivityResult(int requestCode, int resultCode, Intent intent) { - @Override - public int compare(final BuyableItem buyableItem, final BuyableItem buyableItem2) + IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, intent); + if(scanResult != null) { - return (int) Math.round(buyableItem2.getDonationRecommendation() * 100 - buyableItem.getDonationRecommendation() * 100); + if(scanResult.getContents() != null) + { + System.err.println("Scanned barcode: " + scanResult.toString()); + new LongRunningIORequest(this, LongRunningIOTask.BUY_DRINK, connection.getAPI().buy_barcode(config.userID, scanResult.getContents())); + } } } + } diff --git a/meteroid/src/main/java/de/chaosdorf/meteroid/MainActivity.java b/meteroid/src/main/java/de/chaosdorf/meteroid/MainActivity.java index 40fd2e2..06c21a7 100644 --- a/meteroid/src/main/java/de/chaosdorf/meteroid/MainActivity.java +++ b/meteroid/src/main/java/de/chaosdorf/meteroid/MainActivity.java @@ -25,10 +25,9 @@ package de.chaosdorf.meteroid; import android.app.Activity; -import android.content.SharedPreferences; import android.os.Bundle; -import android.preference.PreferenceManager; +import de.chaosdorf.meteroid.util.Config; import de.chaosdorf.meteroid.util.Utility; public class MainActivity extends Activity @@ -38,16 +37,14 @@ protected void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); - final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); - final String hostname = prefs.getString("hostname", null); - final int userID = prefs.getInt("userid", 0); + final Config config = Config.getInstance(getApplicationContext()); - if (hostname == null) + if (config.hostname == null) { // Set hostname if not done yet Utility.startActivity(this, SetHostname.class); } - else if (userID == 0) + else if (config.multiUserMode || config.userID == config.NO_USER_ID) { // Pick username if not done yet Utility.startActivity(this, PickUsername.class); @@ -57,5 +54,6 @@ else if (userID == 0) // Ready to buy some drinks Utility.startActivity(this, BuyDrink.class); } + finish(); } } diff --git a/meteroid/src/main/java/de/chaosdorf/meteroid/MeteroidNetworkActivity.java b/meteroid/src/main/java/de/chaosdorf/meteroid/MeteroidNetworkActivity.java index 5fd6988..10d31c3 100644 --- a/meteroid/src/main/java/de/chaosdorf/meteroid/MeteroidNetworkActivity.java +++ b/meteroid/src/main/java/de/chaosdorf/meteroid/MeteroidNetworkActivity.java @@ -25,9 +25,29 @@ package de.chaosdorf.meteroid; import android.app.Activity; +import android.os.Bundle; -import de.chaosdorf.meteroid.longrunningio.LongRunningIOCallback; +import java.text.DecimalFormat; +import de.chaosdorf.meteroid.util.Config; +import de.chaosdorf.meteroid.util.Connection; +import de.chaosdorf.meteroid.util.Utility; -public abstract class MeteroidNetworkActivity extends Activity implements LongRunningIOCallback -{} + +public abstract class MeteroidNetworkActivity extends Activity +{ + protected DecimalFormat DECIMAL_FORMAT = new DecimalFormat("0.00 '\u20AC'"); + + protected Activity activity; + protected Config config; + protected Connection connection; + + @Override + protected void onCreate(final Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + activity = this; + config = Config.getInstance(getApplicationContext()); + connection = Connection.getInstance(config); + } +} diff --git a/meteroid/src/main/java/de/chaosdorf/meteroid/PickUsername.java b/meteroid/src/main/java/de/chaosdorf/meteroid/PickUsername.java index 4ca81d8..55d940b 100644 --- a/meteroid/src/main/java/de/chaosdorf/meteroid/PickUsername.java +++ b/meteroid/src/main/java/de/chaosdorf/meteroid/PickUsername.java @@ -25,12 +25,10 @@ package de.chaosdorf.meteroid; import android.app.ActionBar; -import android.app.Activity; +import android.databinding.DataBindingUtil; import android.content.Context; -import android.content.SharedPreferences; import android.os.Bundle; import android.os.Build; -import android.preference.PreferenceManager; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.Menu; @@ -38,85 +36,87 @@ import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.GridView; import android.widget.ImageButton; import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.ProgressBar; import android.widget.TextView; +import android.support.v4.widget.SwipeRefreshLayout; + +import com.shamanland.fab.FloatingActionButton; +import com.shamanland.fab.ShowHideOnScroll; + import org.jetbrains.annotations.NotNull; import java.util.Date; import java.util.List; -import de.chaosdorf.meteroid.controller.UserController; -import de.chaosdorf.meteroid.longrunningio.LongRunningIOGet; +import de.chaosdorf.meteroid.controller.MeteroidAdapter; +import de.chaosdorf.meteroid.databinding.ActivityPickUsernameBinding; +import de.chaosdorf.meteroid.databinding.ActivityPickUsernameItemBinding; +import de.chaosdorf.meteroid.longrunningio.LongRunningIOCallback; +import de.chaosdorf.meteroid.longrunningio.LongRunningIORequest; import de.chaosdorf.meteroid.longrunningio.LongRunningIOTask; import de.chaosdorf.meteroid.model.User; import de.chaosdorf.meteroid.util.MenuUtility; import de.chaosdorf.meteroid.util.Utility; import de.chaosdorf.meteroid.MeteroidNetworkActivity; -public class PickUsername extends MeteroidNetworkActivity implements AdapterView.OnItemClickListener +public class PickUsername extends MeteroidNetworkActivity implements AdapterView.OnItemClickListener, LongRunningIOCallback> { - private static final int NEW_USER_ID = -1; - - private Activity activity = null; - private ProgressBar progressBar = null; - private GridView gridView = null; - private boolean multiUserMode = false; private boolean editHostnameOnBackButton = false; + private ActivityPickUsernameBinding binding; @Override protected void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); - activity = this; - setContentView(R.layout.activity_pick_username); - - progressBar = (ProgressBar) findViewById(R.id.progress_bar); - gridView = (GridView) findViewById(R.id.grid_view); - - final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); - final String hostname = prefs.getString("hostname", null); - multiUserMode = prefs.getBoolean("multi_user_mode", false); + binding = DataBindingUtil.setContentView(this, R.layout.activity_pick_username); - final ImageButton backButton = (ImageButton) findViewById(R.id.button_back); - backButton.setOnClickListener(new View.OnClickListener() + ActionBar actionBar = getActionBar(); + actionBar.setDisplayHomeAsUpEnabled(true); + + if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - public void onClick(View view) + binding.swiperefresh.setEnabled(true); + binding.swiperefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { - Utility.startActivity(activity, SetHostname.class); - } - }); - - final ImageButton reloadButton = (ImageButton) findViewById(R.id.button_reload); - reloadButton.setOnClickListener(new View.OnClickListener() - { - public void onClick(View view) + @Override + public void onRefresh() + { + reload(); + } + }); + binding.gridView.setOnTouchListener(new ShowHideOnScroll(binding.fab)); + binding.fab.setOnClickListener(new View.OnClickListener() { - Utility.startActivity(activity, PickUsername.class); - } - }); + public void onClick(View view) + { + Utility.startActivity(activity, UserSettings.class); + } + }); + binding.fab.setVisibility(View.VISIBLE); + } - if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) + reload(); + } + + public void reload() + { + binding.pickUsernameError.setVisibility(View.GONE); + binding.gridView.setVisibility(View.GONE); + binding.progressBar.setVisibility(View.VISIBLE); + if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - ActionBar actionBar = getActionBar(); - actionBar.setDisplayHomeAsUpEnabled(true); - reloadButton.setVisibility(View.GONE); - backButton.setVisibility(View.GONE); + binding.swiperefresh.setRefreshing(true); } - - new LongRunningIOGet(this, LongRunningIOTask.GET_USERS, hostname + "users.json"); + new LongRunningIORequest>(this, LongRunningIOTask.GET_USERS, connection.getAPI().listUsers()); } @Override public boolean onCreateOptionsMenu(final Menu menu) { getMenuInflater().inflate(R.menu.pickusername, menu); - MenuUtility.setChecked(menu, R.id.multi_user_mode, multiUserMode); + MenuUtility.setChecked(menu, R.id.multi_user_mode, config.multiUserMode); return true; } @@ -125,20 +125,22 @@ public boolean onOptionsItemSelected(final MenuItem item) { switch (item.getItemId()) { - case android.R.id.home: - Utility.startActivity(this, SetHostname.class); - break; case R.id.action_reload: - Utility.startActivity(this, PickUsername.class); + reload(); break; case R.id.action_add: Utility.startActivity(this, UserSettings.class); break; + case android.R.id.home: case R.id.edit_hostname: Utility.startActivity(this, SetHostname.class); break; case R.id.multi_user_mode: - multiUserMode = MenuUtility.onClickMultiUserMode(this, item); + Utility.toggleMultiUserMode(this); + item.setChecked(config.multiUserMode); + break; + case R.id.about: + Utility.startActivity(this, About.class); break; } return super.onOptionsItemSelected(item); @@ -161,9 +163,9 @@ public boolean onKeyDown(final int keyCode, @NotNull final KeyEvent event) @Override public void onDestroy() { - if (gridView != null) + if (binding.gridView != null) { - gridView.setAdapter(null); + binding.gridView.setAdapter(null); } super.onDestroy(); } @@ -172,53 +174,49 @@ public void onDestroy() public void displayErrorMessage(final LongRunningIOTask task, final String message) { Utility.displayToastMessage(this, message); - final LinearLayout linearLayout = (LinearLayout) findViewById(R.id.pick_username_error); - linearLayout.setVisibility(View.VISIBLE); - gridView.setVisibility(View.GONE); + binding.pickUsernameError.setVisibility(View.VISIBLE); + binding.gridView.setVisibility(View.GONE); editHostnameOnBackButton = true; - progressBar.setVisibility(View.GONE); + binding.progressBar.setVisibility(View.GONE); + if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) + { + binding.swiperefresh.setRefreshing(false); + } } @Override - public void processIOResult(final LongRunningIOTask task, final String json) + public void processIOResult(final LongRunningIOTask task, final List result) { - final PickUsername pickusername = this; - if (task == LongRunningIOTask.GET_USERS && json != null) + if (task == LongRunningIOTask.GET_USERS) { - final List itemList = UserController.parseAllUsersFromJSON(json); - if(Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) - { - itemList.add(new User(NEW_USER_ID, getResources().getString(R.string.pick_username_new_user), "", 0, new Date(), new Date())); - } + final List itemList = result; + editHostnameOnBackButton = false; final UserAdapter userAdapter = new UserAdapter(itemList); - gridView.setAdapter(userAdapter); - gridView.setOnItemClickListener(pickusername); - progressBar.setVisibility(View.GONE); - gridView.setVisibility(View.VISIBLE); + binding.gridView.setAdapter(userAdapter); + binding.gridView.setOnItemClickListener(this); + binding.progressBar.setVisibility(View.GONE); + binding.gridView.setVisibility(View.VISIBLE); + if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) + { + binding.swiperefresh.setRefreshing(false); + } } } @Override public void onItemClick(final AdapterView adapterView, final View view, final int index, final long l) { - final User user = (User) gridView.getItemAtPosition(index); + final User user = (User) binding.gridView.getItemAtPosition(index); if (user != null && user.getName() != null) { - if (user.getId() == NEW_USER_ID) - { - Utility.startActivity(this, UserSettings.class); - } - else - { - final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); - prefs.edit().putInt("userid", user.getId()).apply(); - Utility.startActivity(this, BuyDrink.class); - } + config.userID = user.getId(); + config.save(); + Utility.startActivity(this, BuyDrink.class); } } - public class UserAdapter extends ArrayAdapter + public class UserAdapter extends MeteroidAdapter { private final List userList; private final LayoutInflater inflater; @@ -227,33 +225,35 @@ public class UserAdapter extends ArrayAdapter { super(activity, R.layout.activity_pick_username, userList); this.userList = userList; - this.inflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + this.inflater = activity.getLayoutInflater(); } + public View getView(final int position, final View convertView, final ViewGroup parent) { View view = convertView; + ActivityPickUsernameItemBinding itemBinding; if (view == null) { - view = inflater.inflate(R.layout.activity_pick_username_item, parent, false); + itemBinding = ActivityPickUsernameItemBinding.inflate( + inflater, parent, false + ); + view = itemBinding.getRoot(); } - if (view == null) + else + { + itemBinding = ActivityPickUsernameItemBinding.bind(view); + } + if (view == null | itemBinding == null) { return null; } final User user = userList.get(position); - final ImageView icon = (ImageView) view.findViewById(R.id.icon); - final TextView label = (TextView) view.findViewById(R.id.label); - - Utility.loadGravatarImage(activity, icon, user); - icon.setContentDescription(user.getName()); - label.setText(user.getName()); - if (user.getId() == NEW_USER_ID) - { - icon.setImageDrawable(getResources().getDrawable(R.drawable.add_user)); - } + Utility.loadUserImage(activity, itemBinding.icon, user); + itemBinding.icon.setContentDescription(user.getName()); + itemBinding.label.setText(user.getName()); return view; } diff --git a/meteroid/src/main/java/de/chaosdorf/meteroid/SetHostname.java b/meteroid/src/main/java/de/chaosdorf/meteroid/SetHostname.java index d4ef3e5..58ef857 100644 --- a/meteroid/src/main/java/de/chaosdorf/meteroid/SetHostname.java +++ b/meteroid/src/main/java/de/chaosdorf/meteroid/SetHostname.java @@ -26,54 +26,53 @@ import android.app.Activity; import android.app.ActionBar; -import android.content.SharedPreferences; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.databinding.DataBindingUtil; import android.os.Bundle; import android.os.Build; -import android.preference.PreferenceManager; import android.text.Editable; import android.text.Selection; import android.view.View; import android.view.Menu; import android.view.MenuItem; import android.webkit.URLUtil; -import android.widget.Button; -import android.widget.EditText; +import de.chaosdorf.meteroid.databinding.ActivitySetHostnameBinding; +import de.chaosdorf.meteroid.util.Config; +import de.chaosdorf.meteroid.util.Connection; import de.chaosdorf.meteroid.util.Utility; public class SetHostname extends Activity { private Activity activity = null; - private SharedPreferences prefs; - private EditText editText; - private Button saveButton; + private Config config; + private Connection connection; + private ActivitySetHostnameBinding binding; @Override protected void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); activity = this; - setContentView(R.layout.activity_set_hostname); + binding = DataBindingUtil.setContentView(this, R.layout.activity_set_hostname); - prefs = PreferenceManager.getDefaultSharedPreferences(this); - final String hostname = prefs.getString("hostname", null); + config = Config.getInstance(getApplicationContext()); - editText = (EditText) findViewById(R.id.hostname); - if (editText != null) + if (binding.hostname != null) { - if (hostname != null) + if (config.hostname != null) { - editText.setText(hostname); + binding.hostname.setText(config.hostname); } - final Editable editTextHostname = editText.getText(); + final Editable editTextHostname = binding.hostname.getText(); if (editTextHostname != null) { Selection.setSelection(editTextHostname, editTextHostname.length()); } } - saveButton = (Button) findViewById(R.id.button_save); - saveButton.setOnClickListener(new View.OnClickListener() + binding.buttonSave.setOnClickListener(new View.OnClickListener() { public void onClick(View view) { @@ -81,13 +80,10 @@ public void onClick(View view) } }); - if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) + ActionBar actionBar = getActionBar(); + if(actionBar != null) { - ActionBar actionBar = getActionBar(); - if(actionBar != null) - { - saveButton.setVisibility(View.GONE); - } + binding.buttonSave.setVisibility(View.GONE); } } @@ -107,24 +103,28 @@ public boolean onOptionsItemSelected(final MenuItem item) public boolean onCreateOptionsMenu(final Menu menu) { getMenuInflater().inflate(R.menu.settings, menu); + + // the delete item doesn't make sense here + final MenuItem deleteItem = menu.findItem(R.id.action_delete); + if(deleteItem != null) + { + deleteItem.setVisible(false); + deleteItem.setEnabled(false); + } + return true; } public void saveHostname() { - if (editText == null) - { - Utility.displayToastMessage(activity, getResources().getString(R.string.set_hostname_empty)); - return; - } - final Editable editTextHostname = editText.getText(); + final Editable editTextHostname = binding.hostname.getText(); if (editTextHostname == null) { Utility.displayToastMessage(activity, getResources().getString(R.string.set_hostname_empty)); return; } String newHostname = editTextHostname.toString(); - if (newHostname.equals("http://")) + if (newHostname.equals("http://") || newHostname.equals("https://")) { Utility.displayToastMessage(activity, getResources().getString(R.string.set_hostname_empty)); return; @@ -138,7 +138,32 @@ public void saveHostname() Utility.displayToastMessage(activity, getResources().getString(R.string.set_hostname_invalid)); return; } - prefs.edit().putString("hostname", newHostname).apply(); + // TODO: Do this properly. + final String url = newHostname; + if(URLUtil.isHttpUrl(url)) { + new AlertDialog.Builder(this) + .setMessage(R.string.set_hostname_continue_http) + .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() + { + @Override + public void onClick(DialogInterface dialog, int id) + { + saveAndExit(url); + } + }) + .setNegativeButton(android.R.string.cancel, null) // Do nothing on click. + .create().show(); + } else { + saveAndExit(url); + } + } + + private void saveAndExit(String newHostname) { + config.hostname = newHostname; + config.apiVersion = Utility.guessApiVersion(newHostname); + config.save(); + connection = Connection.getInstance(config); + connection.reset(); Utility.startActivity(activity, PickUsername.class); } } diff --git a/meteroid/src/main/java/de/chaosdorf/meteroid/UserSettings.java b/meteroid/src/main/java/de/chaosdorf/meteroid/UserSettings.java index c5f0812..840a495 100644 --- a/meteroid/src/main/java/de/chaosdorf/meteroid/UserSettings.java +++ b/meteroid/src/main/java/de/chaosdorf/meteroid/UserSettings.java @@ -25,30 +25,27 @@ package de.chaosdorf.meteroid; import android.app.ActionBar; -import android.app.Activity; +import android.app.AlertDialog; +import android.databinding.DataBindingUtil; +import android.databinding.ObservableBoolean; +import android.content.DialogInterface; import android.content.Intent; -import android.content.SharedPreferences; import android.os.Bundle; import android.os.Build; -import android.preference.PreferenceManager; import android.view.KeyEvent; import android.view.View; import android.view.Menu; import android.view.MenuItem; import android.view.Window; -import android.widget.ImageButton; -import android.widget.TextView; import org.jetbrains.annotations.NotNull; -import java.text.DecimalFormat; import java.text.ParseException; import java.util.Date; -import de.chaosdorf.meteroid.controller.UserController; -import de.chaosdorf.meteroid.longrunningio.LongRunningIOPost; -import de.chaosdorf.meteroid.longrunningio.LongRunningIOPatch; -import de.chaosdorf.meteroid.longrunningio.LongRunningIOGet; +import de.chaosdorf.meteroid.databinding.ActivityUserSettingsBinding; +import de.chaosdorf.meteroid.longrunningio.LongRunningIOCallback; +import de.chaosdorf.meteroid.longrunningio.LongRunningIORequest; import de.chaosdorf.meteroid.longrunningio.LongRunningIOTask; import de.chaosdorf.meteroid.model.User; import de.chaosdorf.meteroid.util.Utility; @@ -56,67 +53,72 @@ public class UserSettings extends MeteroidNetworkActivity { - private final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("0.00"); - - private Activity activity = null; - private TextView usernameText; - private TextView emailText; - private TextView balanceText; - private SharedPreferences prefs; - private int userID; - private String hostname = null; + private User user; + private ActivityUserSettingsBinding binding; + private final ObservableBoolean writable = new ObservableBoolean(false); @Override protected void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); - activity = this; requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); - setContentView(R.layout.activity_user_settings); - - usernameText = (TextView) findViewById(R.id.username); - emailText = (TextView) findViewById(R.id.email); - balanceText = (TextView) findViewById(R.id.balance); - prefs = PreferenceManager.getDefaultSharedPreferences(this); - userID = prefs.getInt("userid", 0); - hostname = prefs.getString("hostname", null); + binding = DataBindingUtil.setContentView(this, R.layout.activity_user_settings); + binding.setUser(user); + binding.setDECIMALFORMAT(DECIMAL_FORMAT); + binding.setWritable(writable); - final ImageButton backButton = (ImageButton) findViewById(R.id.button_back); - backButton.setOnClickListener(new View.OnClickListener() + binding.buttonBack.setOnClickListener(new View.OnClickListener() { public void onClick(View view) { - goBack(); + finish(); } }); - - final ImageButton saveButton = (ImageButton) findViewById(R.id.button_save); - saveButton.setOnClickListener(new View.OnClickListener() + + binding.buttonDelete.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { - saveUser(); + deleteUser(); } }); - if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) + binding.buttonSave.setOnClickListener(new View.OnClickListener() { - ActionBar actionBar = getActionBar(); - if(actionBar != null) + @Override + public void onClick(View view) { - actionBar.setDisplayHomeAsUpEnabled(true); - backButton.setVisibility(View.GONE); - saveButton.setVisibility(View.GONE); + saveUser(); } - } + }); - if(userID != 0) //existing user + ActionBar actionBar = getActionBar(); + if(actionBar != null) { - makeReadOnly(); - new LongRunningIOGet(this, LongRunningIOTask.GET_USER, hostname + "users/" + userID + ".json"); + actionBar.setDisplayHomeAsUpEnabled(true); + binding.buttonBack.setVisibility(View.GONE); + binding.buttonDelete.setVisibility(View.GONE); + binding.buttonSave.setVisibility(View.GONE); } - + + makeReadOnly(); + final UserSettings userSettings = this; + new LongRunningIORequest(new LongRunningIOCallback() { + @Override + public void displayErrorMessage(LongRunningIOTask task, String message) + { + userSettings.displayErrorMessage(task, message); + } + + @Override + public void processIOResult(LongRunningIOTask task, User user) + { + userSettings.user = user; + binding.setUser(user); + makeWritable(); + } + }, LongRunningIOTask.GET_USER, (config.userID != config.NO_USER_ID)? connection.getAPI().getUser(config.userID): connection.getAPI().getUserDefaults()); } @Override @@ -125,11 +127,14 @@ public boolean onOptionsItemSelected(final MenuItem item) switch (item.getItemId()) { case android.R.id.home: - goBack(); + finish(); break; case R.id.action_save: saveUser(); break; + case R.id.action_delete: + deleteUser(); + break; } return super.onOptionsItemSelected(item); } @@ -138,6 +143,9 @@ public boolean onOptionsItemSelected(final MenuItem item) public boolean onCreateOptionsMenu(final Menu menu) { getMenuInflater().inflate(R.menu.settings, menu); + boolean writable = this.writable.get(); + menu.findItem(R.id.action_save).setEnabled(writable); + menu.findItem(R.id.action_delete).setEnabled(writable); return true; } @@ -146,7 +154,7 @@ public boolean onKeyDown(final int keyCode, @NotNull final KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { - goBack(); + finish(); return true; } return super.onKeyDown(keyCode, event); @@ -154,29 +162,63 @@ public boolean onKeyDown(final int keyCode, @NotNull final KeyEvent event) private void makeReadOnly() { - usernameText.setEnabled(false); - emailText.setEnabled(false); - balanceText.setEnabled(false); + writable.set(false); + invalidateOptionsMenu(); setProgressBarIndeterminateVisibility(true); } private void makeWritable() { setProgressBarIndeterminateVisibility(false); - usernameText.setEnabled(true); - emailText.setEnabled(true); - balanceText.setEnabled(true); + writable.set(true); + invalidateOptionsMenu(); } - private void goBack() + private void deleteUser() { - if(userID == 0) //new user + if(config.userID == config.NO_USER_ID) //new user { - Utility.startActivity(this, PickUsername.class); + new AlertDialog.Builder(this) + .setMessage(R.string.user_settings_cant_delete_non_existing_user) + .setPositiveButton(android.R.string.ok, null) // Do nothing on click. + .create().show(); } else { - Utility.startActivity(this, BuyDrink.class); + final UserSettings userSettings = this; + new AlertDialog.Builder(this) + .setMessage(R.string.user_settings_confirm_delete_user) + .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() + { + @Override + public void onClick(DialogInterface dialog, int id) + { + // really delete the user + makeReadOnly(); + new LongRunningIORequest(new LongRunningIOCallback() { + @Override + public void displayErrorMessage(LongRunningIOTask task, String message) + { + userSettings.displayErrorMessage(task, message); + } + + @Override + public void processIOResult(LongRunningIOTask task, Void result) + { + config.userID = config.NO_USER_ID; + config.save(); + makeWritable(); + Utility.displayToastMessage(userSettings, getResources().getString(R.string.user_settings_deleted_user)); + Utility.startActivity(userSettings, PickUsername.class, Intent.FLAG_ACTIVITY_CLEAR_TOP); + finish(); + } + }, + LongRunningIOTask.DELETE_USER, + connection.getAPI().deleteUser(config.userID)); + } + }) + .setNegativeButton(android.R.string.cancel, null) // Do nothing on click. + .create().show(); } } @@ -184,7 +226,7 @@ private void saveUser() { makeReadOnly(); - final CharSequence username = usernameText.getText(); + final CharSequence username = binding.username.getText(); if (username == null || username.length() == 0) { Utility.displayToastMessage(this, getResources().getString(R.string.user_settings_empty_username)); @@ -192,16 +234,9 @@ private void saveUser() return; } - final CharSequence email = emailText.getText(); - String emailValue = ""; - if (email != null && email.length() > 0) - { - emailValue = email.toString(); - } - double balanceValue = 0; - final CharSequence balance = balanceText.getText(); - if (balance != null && balance.length() > 0) + final CharSequence balance = binding.balance.getText(); + if (balance != null) { try { @@ -214,66 +249,55 @@ private void saveUser() return; } } + + user.setBalance(balanceValue); - final User user = new User(userID, - username.toString(), - emailValue, - balanceValue, - new Date(), - new Date() - ); - - final String hostname = prefs.getString("hostname", null); - - if(userID == 0) //new user + final UserSettings userSettings = this; + if(config.userID == config.NO_USER_ID) //new user { - new LongRunningIOPost( - this, + new LongRunningIORequest(new LongRunningIOCallback() { + @Override + public void displayErrorMessage(LongRunningIOTask task, String message) + { + userSettings.displayErrorMessage(task, message); + } + + @Override + public void processIOResult(LongRunningIOTask task, User result) + { + Utility.startActivity(userSettings, PickUsername.class, Intent.FLAG_ACTIVITY_CLEAR_TOP); + finish(); + } + }, LongRunningIOTask.ADD_USER, - hostname + "users.json", - UserController.userToJSONPostParams(user) + connection.getAPI().createUser(user) ); } else { - new LongRunningIOPatch( - this, + new LongRunningIORequest(new LongRunningIOCallback() { + @Override + public void displayErrorMessage(LongRunningIOTask task, String message) + { + userSettings.displayErrorMessage(task, message); + } + + @Override + public void processIOResult(LongRunningIOTask task, Void result) + { + Utility.startActivity(userSettings, BuyDrink.class, Intent.FLAG_ACTIVITY_CLEAR_TOP); + finish(); + } + }, LongRunningIOTask.EDIT_USER, - hostname + "users/" + user.getId() + ".json", - UserController.userToJSONPostParams(user) + connection.getAPI().editUser(user.getId(), user) ); } } - @Override public void displayErrorMessage(final LongRunningIOTask task, final String message) { makeWritable(); Utility.displayToastMessage(this, message); } - - @Override - public void processIOResult(final LongRunningIOTask task, final String json) - { - final UserSettings usersettings = this; - if (json != null) - { - switch(task) - { - case ADD_USER: - Utility.startActivity(usersettings, PickUsername.class); - break; - case EDIT_USER: - Utility.startActivity(usersettings, BuyDrink.class); - break; - case GET_USER: - User user = UserController.parseUserFromJSON(json); - usernameText.setText(user.getName()); - emailText.setText(user.getEmail()); - balanceText.setText(DECIMAL_FORMAT.format(user.getBalance())); - makeWritable(); - break; - } - } - } } diff --git a/meteroid/src/main/java/de/chaosdorf/meteroid/controller/DrinkController.java b/meteroid/src/main/java/de/chaosdorf/meteroid/controller/DrinkController.java deleted file mode 100644 index ddb52e7..0000000 --- a/meteroid/src/main/java/de/chaosdorf/meteroid/controller/DrinkController.java +++ /dev/null @@ -1,90 +0,0 @@ -/******************************************************************************* - * The MIT License (MIT) - * - * Copyright (c) 2013-2016 Chaosdorf e.V. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - ******************************************************************************/ - -package de.chaosdorf.meteroid.controller; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import java.net.MalformedURLException; -import java.net.URL; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - -import de.chaosdorf.meteroid.model.BuyableItem; -import de.chaosdorf.meteroid.model.Drink; - -public class DrinkController -{ - public static List parseAllDrinksFromJSON(final String json, final String hostname) - { - final List list = new ArrayList(); - try - { - final URL baseUrl = new URL(hostname); - final JSONArray jsonArray = new JSONArray(json); - for (int i = 0; i < jsonArray.length(); i++) - { - final Drink drink = parseDrinkFromJSONObject(jsonArray.getJSONObject(i), baseUrl); - if (drink != null) - { - list.add(drink); - } - } - return list; - } - catch (JSONException ignored) - { - return null; - } - catch (MalformedURLException ignored) - { - return null; - } - } - - private static Drink parseDrinkFromJSONObject(final JSONObject jsonObject, final URL baseURL) - { - try - { - return new Drink( - jsonObject.getInt("id"), - jsonObject.getString("name"), - jsonObject.getString("logo_url"), - jsonObject.getDouble("bottle_size"), - jsonObject.getString("caffeine"), - jsonObject.getDouble("donation_recommendation"), - new Date(), - new Date(), - baseURL - ); - } - catch (JSONException ignored) - { - return null; - } - } -} diff --git a/meteroid/src/main/java/de/chaosdorf/meteroid/controller/MeteroidAdapter.java b/meteroid/src/main/java/de/chaosdorf/meteroid/controller/MeteroidAdapter.java new file mode 100644 index 0000000..7e071ba --- /dev/null +++ b/meteroid/src/main/java/de/chaosdorf/meteroid/controller/MeteroidAdapter.java @@ -0,0 +1,101 @@ +/******************************************************************************* + * The MIT License (MIT) + * + * Copyright (c) 2013-2016 Chaosdorf e.V. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +package de.chaosdorf.meteroid.controller; + +import android.content.Context; +import android.widget.ArrayAdapter; +import android.widget.SectionIndexer; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import de.chaosdorf.meteroid.model.MeteroidItem; + +public class MeteroidAdapter extends ArrayAdapter implements SectionIndexer { + private final List objects; + private final List sections; + private final Map indexForSection; + + public MeteroidAdapter(Context context, int resource, List objects) { + super(context, resource, objects); + this.objects = objects; + indexForSection = new HashMap(); + sections = new ArrayList(); + computeSections(); + } + + private void computeSections() { + for(int i = 0; i < objects.size(); i++) { + MeteroidItem item = objects.get(i); + if(!item.getActive()) { + if(!indexForSection.containsKey("-")) { + indexForSection.put("-", i); + sections.add("-"); + break; + } + } else { + String firstCharOfName = getFirstChar(item); + if(!indexForSection.containsKey(firstCharOfName)) { + indexForSection.put(firstCharOfName, i); + sections.add(firstCharOfName); + } + } + } + } + + private String getFirstChar(MeteroidItem item) { + return item.getName().substring(0, 1).toUpperCase(); + } + + public String[] getSections() { + return sections.toArray(new String[0]); + } + + public int getSectionForPosition(int position) { + try { + MeteroidItem item = objects.get(position); + if(!item.getActive()) { + return sections.size() - 1; // "-" + } + String firstCharOfName = getFirstChar(item); + return sections.indexOf(firstCharOfName); + } catch (IndexOutOfBoundsException exc) { + return 0; + } + } + + public int getPositionForSection(int sectionIndex) { + if(sectionIndex >= sections.size()) { + return objects.size() - 1; + } + if(sectionIndex < 0) { + return 0; + } + return indexForSection.get(sections.get(sectionIndex)); + } +} \ No newline at end of file diff --git a/meteroid/src/main/java/de/chaosdorf/meteroid/controller/MoneyController.java b/meteroid/src/main/java/de/chaosdorf/meteroid/controller/MoneyController.java index 83f59dd..f780680 100644 --- a/meteroid/src/main/java/de/chaosdorf/meteroid/controller/MoneyController.java +++ b/meteroid/src/main/java/de/chaosdorf/meteroid/controller/MoneyController.java @@ -33,6 +33,9 @@ public class MoneyController { public static void addMoney(List itemList) { + itemList.add(new Money("50 Cent", "euro_05", -0.5)); + itemList.add(new Money("1 Euro", "euro_1", -1)); + itemList.add(new Money("2 Euro", "euro_2", -2)); itemList.add(new Money("5 Euro", "euro_5", -5)); itemList.add(new Money("10 Euro", "euro_10", -10)); itemList.add(new Money("20 Euro", "euro_20", -20)); diff --git a/meteroid/src/main/java/de/chaosdorf/meteroid/controller/UserController.java b/meteroid/src/main/java/de/chaosdorf/meteroid/controller/UserController.java deleted file mode 100644 index 8ebb7b3..0000000 --- a/meteroid/src/main/java/de/chaosdorf/meteroid/controller/UserController.java +++ /dev/null @@ -1,106 +0,0 @@ -/******************************************************************************* - * The MIT License (MIT) - * - * Copyright (c) 2013-2016 Chaosdorf e.V. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - ******************************************************************************/ - -package de.chaosdorf.meteroid.controller; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - -import de.chaosdorf.meteroid.model.User; - -public class UserController -{ - public static List parseAllUsersFromJSON(final String json) - { - final List list = new ArrayList(); - try - { - final JSONArray jsonArray = new JSONArray(json); - for (int i = 0; i < jsonArray.length(); i++) - { - final User user = parseUserFromJSONObject(jsonArray.getJSONObject(i)); - if (user != null) - { - list.add(user); - } - } - return list; - } - catch (JSONException ignored) - { - return null; - } - } - - public static User parseUserFromJSON(final String json) - { - try - { - return parseUserFromJSONObject(new JSONObject(json)); - } - catch (JSONException e) - { - return null; - } - } - - private static User parseUserFromJSONObject(final JSONObject jsonObject) - { - try - { - return new User( - jsonObject.getInt("id"), - jsonObject.getString("name"), - jsonObject.getString("email"), - jsonObject.getDouble("balance"), - new Date(), - new Date() - ); - } - catch (JSONException ignored) - { - return null; - } - } - - public static String userToJSONPostParams(final User user) - { - final JSONObject jo = new JSONObject(); - try - { - JSONObject ujo = new JSONObject(); - ujo.put("name", user.getName()); - ujo.put("email", user.getEmail()); - ujo.put("balance", String.valueOf(user.getBalance())); - jo.put("user", ujo); - } - catch (JSONException e){} - return jo.toString(); - } -} diff --git a/meteroid/src/main/java/de/chaosdorf/meteroid/imageloader/FileCache.java b/meteroid/src/main/java/de/chaosdorf/meteroid/imageloader/FileCache.java deleted file mode 100644 index 71bcf6e..0000000 --- a/meteroid/src/main/java/de/chaosdorf/meteroid/imageloader/FileCache.java +++ /dev/null @@ -1,84 +0,0 @@ -/******************************************************************************* - * The MIT License (MIT) - * - * Copyright (c) 2013-2016 Chaosdorf e.V. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - ******************************************************************************/ - -package de.chaosdorf.meteroid.imageloader; - -import android.content.Context; - -import java.io.File; -import java.io.UnsupportedEncodingException; -import java.net.URL; -import java.net.URLEncoder; - -public class FileCache -{ - private final File cacheDir; - - public FileCache(final Context context) - { - cacheDir = context.getCacheDir(); - if (cacheDir != null && !cacheDir.exists()) - { - if (!cacheDir.mkdirs()) - { - throw new RuntimeException("Could not create cache directory!"); - } - } - } - - public void clear() - { - final File[] cachedFiles = cacheDir.listFiles(); - if (cachedFiles == null) - { - return; - } - for (File file : cachedFiles) - { - if (!file.delete()) - { - throw new RuntimeException("Could not delete cached file " + file.getName()); - } - } - } - - public File getFile(final URL url) - { - return new File(cacheDir, createFileNameFromURL(url)); - } - - private String createFileNameFromURL(final URL url) - { - try - { - final String filename = url.toString().replaceFirst(url.getProtocol(), "").substring(1); - return URLEncoder.encode(filename.startsWith("//") ? filename.substring(2) : filename, "UTF-8"); - } - catch (UnsupportedEncodingException e) - { - e.printStackTrace(); - } - return String.valueOf(url.hashCode()); - } -} diff --git a/meteroid/src/main/java/de/chaosdorf/meteroid/imageloader/ImageLoader.java b/meteroid/src/main/java/de/chaosdorf/meteroid/imageloader/ImageLoader.java deleted file mode 100644 index 5ecc96f..0000000 --- a/meteroid/src/main/java/de/chaosdorf/meteroid/imageloader/ImageLoader.java +++ /dev/null @@ -1,269 +0,0 @@ -/******************************************************************************* - * The MIT License (MIT) - * - * Copyright (c) 2013-2016 Chaosdorf e.V. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - ******************************************************************************/ - -package de.chaosdorf.meteroid.imageloader; - -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.media.ThumbnailUtils; -import android.os.Handler; -import android.widget.ImageView; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.HttpURLConnection; -import java.net.URL; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -public class ImageLoader -{ - private final int REQUIRED_SIZE; - - private final MemoryCache memoryCache; - private final FileCache fileCache; - private final ExecutorService executorService; - - private final Handler handler; - private final Map imageViews; - - public ImageLoader(final Context context, final int requiredSize) - { - REQUIRED_SIZE = requiredSize; - - memoryCache = new MemoryCache(); - fileCache = new FileCache(context); - executorService = Executors.newFixedThreadPool(5); - - handler = new Handler(); - imageViews = new ConcurrentHashMap(); - } - - public void displayImage(final URL url, final ImageView imageView, final Bitmap defaultBitmap) - { - if (url == null) - { - imageView.setImageBitmap(defaultBitmap); - return; - } - final Bitmap bitmap = memoryCache.get(url); - if (bitmap != null) - { - imageView.setImageBitmap(bitmap); - } - else - { - imageViews.put(imageView, url); - queuePhoto(url, imageView, defaultBitmap); - } - } - - public void clearCache() - { - memoryCache.clear(); - fileCache.clear(); - } - - private void queuePhoto(final URL url, final ImageView imageView, final Bitmap defaultBitmap) - { - final PhotoToLoad p = new PhotoToLoad(url, imageView, defaultBitmap); - executorService.submit(new PhotosLoader(p)); - } - - private Bitmap getBitmap(final URL url) - { - // Create file from URL - final File file = fileCache.getFile(url); - - // Read bitmap from SD - if (file.exists()) - { - final Bitmap bitmap = createThumbnailFromFile(file); - if (bitmap != null) - { - return bitmap; - } - } - - // Read bitmap from web - try - { - final HttpURLConnection conn = (HttpURLConnection) url.openConnection(); - conn.setConnectTimeout(30000); - conn.setReadTimeout(30000); - conn.setInstanceFollowRedirects(true); - conn.connect(); - if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) - { - return null; - } - final InputStream is = conn.getInputStream(); - final OutputStream os = new FileOutputStream(file); - copyStream(is, os); - os.close(); - is.close(); - conn.disconnect(); - return createThumbnailFromFile(file); - } - catch (Throwable t) - { - if (t instanceof OutOfMemoryError) - { - memoryCache.clear(); - fileCache.clear(); - } - t.printStackTrace(); - } - return null; - } - - private void copyStream(final InputStream is, final OutputStream os) - { - final int buffer_size = 1024; - final byte[] bytes = new byte[buffer_size]; - try - { - while (true) - { - final int count = is.read(bytes, 0, buffer_size); - if (count == -1) - { - break; - } - os.write(bytes, 0, count); - } - } - catch (Exception ignored) - { - } - } - - private Bitmap createThumbnailFromFile(final File file) - { - final Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath()); - if (bitmap == null) - { - return null; - } - final float ratio = (float)bitmap.getWidth() / (float)bitmap.getHeight(); - return ThumbnailUtils.extractThumbnail(bitmap, (int)(REQUIRED_SIZE * ratio), REQUIRED_SIZE, ThumbnailUtils.OPTIONS_RECYCLE_INPUT); - } - - private boolean imageViewReused(final PhotoToLoad photoToLoad) - { - final URL url = imageViews.get(photoToLoad.imageView); - return (url == null || !url.equals(photoToLoad.url)); - } - - // Task for the queue - private class PhotoToLoad - { - final public URL url; - final public ImageView imageView; - final public Bitmap defaultImage; - - public PhotoToLoad(final URL url, final ImageView imageView, final Bitmap defaultImage) - { - this.url = url; - this.imageView = imageView; - this.defaultImage = defaultImage; - } - } - - private class PhotosLoader implements Runnable - { - final PhotoToLoad photoToLoad; - - PhotosLoader(final PhotoToLoad photoToLoad) - { - this.photoToLoad = photoToLoad; - } - - @Override - public void run() - { - Bitmap bmp = null; - try - { - if (imageViewReused(photoToLoad)) - { - return; - } - bmp = getBitmap(photoToLoad.url); - } - catch (Throwable t) - { - t.printStackTrace(); - } - finally - { - if (bmp == null) - { - bmp = photoToLoad.defaultImage; - } - memoryCache.put(photoToLoad.url, bmp); - if (!imageViewReused(photoToLoad)) - { - handler.post(new BitmapDisplayer(bmp, photoToLoad)); - } - } - } - } - - // Used to display bitmap in the UI thread - private class BitmapDisplayer implements Runnable - { - final Bitmap bitmap; - final PhotoToLoad photoToLoad; - - public BitmapDisplayer(final Bitmap bitmap, final PhotoToLoad photoToLoad) - { - this.bitmap = bitmap; - this.photoToLoad = photoToLoad; - } - - public void run() - { - if (imageViewReused(photoToLoad)) - { - return; - } - if (bitmap != null) - { - photoToLoad.imageView.setImageBitmap(bitmap); - } - else - { - final int padding = (REQUIRED_SIZE - photoToLoad.defaultImage.getWidth()) / 2; - photoToLoad.imageView.setPadding(padding, 0, padding, 0); - photoToLoad.imageView.setImageBitmap(photoToLoad.defaultImage); - } - } - } -} diff --git a/meteroid/src/main/java/de/chaosdorf/meteroid/imageloader/ImageLoaderSingleton.java b/meteroid/src/main/java/de/chaosdorf/meteroid/imageloader/ImageLoaderSingleton.java deleted file mode 100644 index c9c4ec7..0000000 --- a/meteroid/src/main/java/de/chaosdorf/meteroid/imageloader/ImageLoaderSingleton.java +++ /dev/null @@ -1,93 +0,0 @@ -/******************************************************************************* - * The MIT License (MIT) - * - * Copyright (c) 2013-2016 Chaosdorf e.V. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - ******************************************************************************/ - -package de.chaosdorf.meteroid.imageloader; - -import android.app.Activity; -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.media.ThumbnailUtils; - -import java.util.concurrent.TimeUnit; - -import de.chaosdorf.meteroid.R; - -public class ImageLoaderSingleton -{ - private static final long REFRESH_INTERVAL = TimeUnit.MINUTES.toNanos(15); - - private static ImageLoader instance = null; - private static long refreshTimestamp = System.nanoTime(); - - private static Bitmap userDefaultImage = null; - private static Bitmap drinkDefaultImage = null; - - public static ImageLoader getInstance(final Activity activity) - { - if (instance == null) - { - ensureInstance(activity); - } - if (refreshTimestamp < System.nanoTime()) - { - refreshTimestamp = System.nanoTime() + REFRESH_INTERVAL; - instance.clearCache(); - } - return instance; - } - - public static Bitmap getUserDefaultImage() - { - return userDefaultImage; - } - - public static Bitmap getDrinkDefaultImage() - { - return drinkDefaultImage; - } - - private static synchronized void ensureInstance(final Activity activity) - { - if (instance == null) - { - if (activity == null) - { - return; - } - final Context context = activity.getApplicationContext(); - if (context == null) - { - return; - } - - // Set default images - userDefaultImage = BitmapFactory.decodeResource(context.getResources(), R.drawable.default_user); - drinkDefaultImage = BitmapFactory.decodeResource(context.getResources(), R.drawable.default_drink); - - // Set instance of ImageLoader - instance = new ImageLoader(context, 80); - } - } -} diff --git a/meteroid/src/main/java/de/chaosdorf/meteroid/imageloader/MemoryCache.java b/meteroid/src/main/java/de/chaosdorf/meteroid/imageloader/MemoryCache.java deleted file mode 100644 index 2526ad8..0000000 --- a/meteroid/src/main/java/de/chaosdorf/meteroid/imageloader/MemoryCache.java +++ /dev/null @@ -1,126 +0,0 @@ -/******************************************************************************* - * The MIT License (MIT) - * - * Copyright (c) 2013-2016 Chaosdorf e.V. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - ******************************************************************************/ - -package de.chaosdorf.meteroid.imageloader; - -import android.graphics.Bitmap; -import android.util.Log; - -import java.net.URL; -import java.util.Collections; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Map.Entry; - -public class MemoryCache -{ - private static final String TAG = "MemoryCache"; - - // Last argument true for LRU ordering - private final Map cache = Collections.synchronizedMap(new LinkedHashMap(10, 1.5f, true)); - - private long size = 0; // Current allocated size - private long limit = 1000000; // Max memory in bytes - - public MemoryCache() - { - // Use 25% of available heap size - setLimit(Runtime.getRuntime().maxMemory() / 4); - } - - public void setLimit(final long newLimit) - { - limit = newLimit; - Log.i(TAG, "MemoryCache will use up to " + (limit / 1024. / 1024.) + " MB"); - } - - public Bitmap get(final URL url) - { - return cache.get(url.toString()); - } - - public void put(final URL url, final Bitmap bitmap) - { - final String id = url.toString(); - try - { - if (cache.containsKey(id)) - { - size -= getSizeInBytes(cache.get(id)); - } - cache.put(id, bitmap); - size += getSizeInBytes(bitmap); - checkSize(); - } - catch (Throwable e) - { - e.printStackTrace(); - } - } - - public void clear() - { - try - { - // NullPointerException sometimes happen here http://code.google.com/p/osmdroid/issues/detail?id=78 - cache.clear(); - size = 0; - } - catch (NullPointerException e) - { - e.printStackTrace(); - } - } - - private void checkSize() - { - Log.i(TAG, "cache size=" + size + " length=" + cache.size()); - if (size > limit) - { - // Least recently accessed item will be the first one iterated - Iterator> iter = cache.entrySet().iterator(); - while (iter.hasNext()) - { - Entry entry = iter.next(); - size -= getSizeInBytes(entry.getValue()); - iter.remove(); - if (size <= limit) - { - break; - } - } - Log.i(TAG, "Clean cache. New size " + cache.size()); - } - } - - private long getSizeInBytes(final Bitmap bitmap) - { - if (bitmap == null) - { - return 0; - } - return bitmap.getRowBytes() * bitmap.getHeight(); - } -} \ No newline at end of file diff --git a/meteroid/src/main/java/de/chaosdorf/meteroid/longrunningio/LongRunningIOCallback.java b/meteroid/src/main/java/de/chaosdorf/meteroid/longrunningio/LongRunningIOCallback.java index ae302d1..ed7b31d 100644 --- a/meteroid/src/main/java/de/chaosdorf/meteroid/longrunningio/LongRunningIOCallback.java +++ b/meteroid/src/main/java/de/chaosdorf/meteroid/longrunningio/LongRunningIOCallback.java @@ -24,9 +24,9 @@ package de.chaosdorf.meteroid.longrunningio; -public interface LongRunningIOCallback +public interface LongRunningIOCallback { public void displayErrorMessage(final LongRunningIOTask task, final String message); - public void processIOResult(final LongRunningIOTask task, final String json); + public void processIOResult(final LongRunningIOTask task, final T response); } diff --git a/meteroid/src/main/java/de/chaosdorf/meteroid/longrunningio/LongRunningIOPatch.java b/meteroid/src/main/java/de/chaosdorf/meteroid/longrunningio/LongRunningIOPatch.java deleted file mode 100644 index 3d84d57..0000000 --- a/meteroid/src/main/java/de/chaosdorf/meteroid/longrunningio/LongRunningIOPatch.java +++ /dev/null @@ -1,49 +0,0 @@ -/******************************************************************************* - * The MIT License (MIT) - * - * Copyright (c) 2013-2016 Chaosdorf e.V. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - ******************************************************************************/ - -package de.chaosdorf.meteroid.longrunningio; - -import java.io.IOException; - -import okhttp3.MediaType; -import okhttp3.OkHttpClient; -import okhttp3.RequestBody; -import okhttp3.Request; -import okhttp3.Callback; -import okhttp3.Call; -import okhttp3.Response; - -import de.chaosdorf.meteroid.MeteroidNetworkActivity; - -public class LongRunningIOPatch extends LongRunningIOBase -{ - public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8"); - public LongRunningIOPatch(final MeteroidNetworkActivity callback, final LongRunningIOTask id, final String url, final String patchData) - { - super(); - RequestBody reqbody = RequestBody.create(JSON, patchData); - Request req = new Request.Builder().url(url).patch(reqbody).build(); - client.newCall(req).enqueue(newCallback(callback, id)); - } -} diff --git a/meteroid/src/main/java/de/chaosdorf/meteroid/longrunningio/LongRunningIOPost.java b/meteroid/src/main/java/de/chaosdorf/meteroid/longrunningio/LongRunningIOPost.java deleted file mode 100644 index 5c55eb7..0000000 --- a/meteroid/src/main/java/de/chaosdorf/meteroid/longrunningio/LongRunningIOPost.java +++ /dev/null @@ -1,49 +0,0 @@ -/******************************************************************************* - * The MIT License (MIT) - * - * Copyright (c) 2013-2016 Chaosdorf e.V. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - ******************************************************************************/ - -package de.chaosdorf.meteroid.longrunningio; - -import java.io.IOException; - -import okhttp3.MediaType; -import okhttp3.OkHttpClient; -import okhttp3.RequestBody; -import okhttp3.Request; -import okhttp3.Callback; -import okhttp3.Call; -import okhttp3.Response; - -import de.chaosdorf.meteroid.MeteroidNetworkActivity; - -public class LongRunningIOPost extends LongRunningIOBase -{ - public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8"); - public LongRunningIOPost(final MeteroidNetworkActivity callback, final LongRunningIOTask id, final String url, final String postData) - { - super(); - RequestBody reqbody = RequestBody.create(JSON, postData); - Request req = new Request.Builder().url(url).post(reqbody).build(); - client.newCall(req).enqueue(newCallback(callback, id)); - } -} diff --git a/meteroid/src/main/java/de/chaosdorf/meteroid/longrunningio/LongRunningIOBase.java b/meteroid/src/main/java/de/chaosdorf/meteroid/longrunningio/LongRunningIORequest.java similarity index 53% rename from meteroid/src/main/java/de/chaosdorf/meteroid/longrunningio/LongRunningIOBase.java rename to meteroid/src/main/java/de/chaosdorf/meteroid/longrunningio/LongRunningIORequest.java index b4238b8..70889f8 100644 --- a/meteroid/src/main/java/de/chaosdorf/meteroid/longrunningio/LongRunningIOBase.java +++ b/meteroid/src/main/java/de/chaosdorf/meteroid/longrunningio/LongRunningIORequest.java @@ -26,79 +26,53 @@ import java.io.IOException; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Callback; -import okhttp3.Call; -import okhttp3.Response; +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; -import de.chaosdorf.meteroid.MeteroidNetworkActivity; +import android.util.Log; -public class LongRunningIOBase +public class LongRunningIORequest { - protected OkHttpClient client; + private final static String TAG = "LongRunningIO"; - public LongRunningIOBase() + public LongRunningIORequest(final LongRunningIOCallback callback, final LongRunningIOTask id, final Call call) { - client = new OkHttpClient(); + Log.d(TAG, "Initiating call: " + call.request()); + call.enqueue(newCallback(callback, id)); } - - protected Callback newCallback(final MeteroidNetworkActivity callback, final LongRunningIOTask id) + + protected Callback newCallback(final LongRunningIOCallback callback, final LongRunningIOTask id) { - return new Callback() + return new Callback() { @Override - public void onFailure(final Call call, final IOException e) + public void onFailure(final Call call, final Throwable t) { - callback.runOnUiThread(new Runnable() - { - @Override - public void run() - { - callback.displayErrorMessage(id, e.getLocalizedMessage()); - } - }); + Log.d(TAG, "Handling failure: " + call.request()); + callback.displayErrorMessage(id, t.getLocalizedMessage()); } @Override - public void onResponse(final Call call, final Response resp) + public void onResponse(final Call call, final Response resp) { + Log.d(TAG, "Handling response: " + call.request()); + T responseBody = null; if(resp.isSuccessful()) { try { - final String response = resp.body().string(); - callback.runOnUiThread(new Runnable() - { - @Override - public void run() - { - callback.processIOResult(id, response); - } - }); + responseBody = resp.body(); } - catch(final IOException e) + catch(final Throwable t) { - callback.runOnUiThread(new Runnable() - { - @Override - public void run() - { - callback.displayErrorMessage(id, e.getLocalizedMessage()); - } - }); + callback.displayErrorMessage(id, t.getLocalizedMessage()); } + callback.processIOResult(id, responseBody); } else { - callback.runOnUiThread(new Runnable() - { - @Override - public void run() - { - callback.displayErrorMessage(id, resp.message()); - } - }); + callback.displayErrorMessage(id, resp.message()); } } }; diff --git a/meteroid/src/main/java/de/chaosdorf/meteroid/longrunningio/LongRunningIOTask.java b/meteroid/src/main/java/de/chaosdorf/meteroid/longrunningio/LongRunningIOTask.java index ccdf7d8..556b1e5 100644 --- a/meteroid/src/main/java/de/chaosdorf/meteroid/longrunningio/LongRunningIOTask.java +++ b/meteroid/src/main/java/de/chaosdorf/meteroid/longrunningio/LongRunningIOTask.java @@ -33,5 +33,6 @@ public enum LongRunningIOTask ADD_MONEY, ADD_USER, UPDATE_USER, - EDIT_USER + EDIT_USER, + DELETE_USER } diff --git a/meteroid/src/main/java/de/chaosdorf/meteroid/model/BuyableItem.java b/meteroid/src/main/java/de/chaosdorf/meteroid/model/BuyableItem.java index c6f9f35..d8720fa 100644 --- a/meteroid/src/main/java/de/chaosdorf/meteroid/model/BuyableItem.java +++ b/meteroid/src/main/java/de/chaosdorf/meteroid/model/BuyableItem.java @@ -24,13 +24,11 @@ package de.chaosdorf.meteroid.model; -public interface BuyableItem +public interface BuyableItem extends MeteroidItem { - public String getName(); + public String getLogoUrl(String hostname); - public String getLogoUrl(); - - public double getDonationRecommendation(); + public double getPrice(); public boolean isDrink(); } diff --git a/meteroid/src/main/java/de/chaosdorf/meteroid/model/Drink.java b/meteroid/src/main/java/de/chaosdorf/meteroid/model/Drink.java index d64afef..b460540 100644 --- a/meteroid/src/main/java/de/chaosdorf/meteroid/model/Drink.java +++ b/meteroid/src/main/java/de/chaosdorf/meteroid/model/Drink.java @@ -32,25 +32,24 @@ public class Drink implements BuyableItem { private final int id; private final String name; - private final String logoUrl; + private final String logo_url; - private final double bottleSize; + private final double bottle_size; + private final String barcode; private final String caffeine; - private final double donationRecommendation; + private final double price; + private final boolean active; - private final Date createdAt; - private final Date updatedAt; - - public Drink(final int id, final String name, final String logoUrl, final double bottleSize, final String caffeine, final double donationRecommendation, final Date created_at, final Date updated_at, final URL baseURL) + public Drink(final int id, final String name, final String logo_url, final double bottle_size, final String barcode, final String caffeine, final double price, final boolean active) { this.id = id; this.name = name; - this.logoUrl = createLogoURL(logoUrl, baseURL); - this.bottleSize = bottleSize; + this.logo_url = logo_url; + this.bottle_size = bottle_size; + this.barcode = barcode; this.caffeine = caffeine; - this.donationRecommendation = donationRecommendation; - this.createdAt = created_at; - this.updatedAt = updated_at; + this.price = price; + this.active = active; } public int getId() @@ -63,54 +62,54 @@ public String getName() return name; } - public String getLogoUrl() + public String getLogoUrl(String hostname) { - return logoUrl; + try + { + return createLogoURL(logo_url, new URL(hostname)); + } + catch (MalformedURLException ignored) + { + return null; + } } public double getBottleSize() { - return bottleSize; - } - - public String getCaffeine() - { - return caffeine; + return bottle_size; } - - public double getDonationRecommendation() + + public String getBarcode() { - return donationRecommendation; + return barcode; } - public Date getCreatedAt() + public String getCaffeine() { - return createdAt; + return caffeine; } - public Date getUpdatedAt() + public double getPrice() { - return updatedAt; + return price; } public boolean isDrink() { return true; } + + public boolean getActive() + { + return active; + } - private String createLogoURL(final String logoUrl, final URL baseURL) + private String createLogoURL(final String logoUrl, final URL baseURL) throws MalformedURLException { if (logoUrl.isEmpty()) { return null; } - try - { - return new URL(baseURL, logoUrl).toString(); - } - catch (MalformedURLException ignored) - { - } - return null; + return new URL(baseURL, logoUrl).toString(); } } diff --git a/meteroid/src/main/java/de/chaosdorf/meteroid/longrunningio/LongRunningIOGet.java b/meteroid/src/main/java/de/chaosdorf/meteroid/model/MeteroidItem.java similarity index 70% rename from meteroid/src/main/java/de/chaosdorf/meteroid/longrunningio/LongRunningIOGet.java rename to meteroid/src/main/java/de/chaosdorf/meteroid/model/MeteroidItem.java index fabf1a0..c30cc09 100644 --- a/meteroid/src/main/java/de/chaosdorf/meteroid/longrunningio/LongRunningIOGet.java +++ b/meteroid/src/main/java/de/chaosdorf/meteroid/model/MeteroidItem.java @@ -22,24 +22,9 @@ * THE SOFTWARE. ******************************************************************************/ -package de.chaosdorf.meteroid.longrunningio; +package de.chaosdorf.meteroid.model; -import java.io.IOException; - -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Callback; -import okhttp3.Call; -import okhttp3.Response; - -import de.chaosdorf.meteroid.MeteroidNetworkActivity; - -public class LongRunningIOGet extends LongRunningIOBase -{ - public LongRunningIOGet(final MeteroidNetworkActivity callback, final LongRunningIOTask id, final String url) - { - super(); - Request req = new Request.Builder().url(url).build(); - client.newCall(req).enqueue(newCallback(callback, id)); - } -} +public interface MeteroidItem { + public String getName(); + public boolean getActive(); +} \ No newline at end of file diff --git a/meteroid/src/main/java/de/chaosdorf/meteroid/model/Money.java b/meteroid/src/main/java/de/chaosdorf/meteroid/model/Money.java index 9eb4262..298f647 100644 --- a/meteroid/src/main/java/de/chaosdorf/meteroid/model/Money.java +++ b/meteroid/src/main/java/de/chaosdorf/meteroid/model/Money.java @@ -29,13 +29,13 @@ public class Money implements BuyableItem private final String name; private final String logoUrl; - private final double donationRecommendation; + private final double price; - public Money(String name, String logoUrl, double donationRecommendation) + public Money(String name, String logoUrl, double price) { this.name = name; this.logoUrl = logoUrl; - this.donationRecommendation = donationRecommendation; + this.price = price; } @Override @@ -45,15 +45,15 @@ public String getName() } @Override - public String getLogoUrl() + public String getLogoUrl(String hostname) { return logoUrl; } @Override - public double getDonationRecommendation() + public double getPrice() { - return donationRecommendation; + return price; } @Override @@ -61,4 +61,10 @@ public boolean isDrink() { return false; } + + @Override + public boolean getActive() + { + return true; + } } diff --git a/meteroid/src/main/java/de/chaosdorf/meteroid/model/User.java b/meteroid/src/main/java/de/chaosdorf/meteroid/model/User.java index a29feeb..460d14f 100644 --- a/meteroid/src/main/java/de/chaosdorf/meteroid/model/User.java +++ b/meteroid/src/main/java/de/chaosdorf/meteroid/model/User.java @@ -24,25 +24,25 @@ package de.chaosdorf.meteroid.model; -import java.util.Date; - -public class User +public class User implements MeteroidItem { private final int id; - private final String name; - private final String email; - private final double balance; - private final Date createdAt; - private final Date updatedAt; + private String name; + private String email; + private double balance; + private boolean active; + private boolean audit; + private boolean redirect; - public User(final int id, final String name, final String email, final double balance, final Date createdAt, final Date updated_at) + public User(final int id, final String name, final String email, final double balance, final boolean active, final boolean audit, final boolean redirect) { this.id = id; this.name = name; this.email = email; this.balance = balance; - this.createdAt = createdAt; - this.updatedAt = updated_at; + this.active = active; + this.audit = audit; + this.redirect = redirect; } public int getId() @@ -54,24 +54,59 @@ public String getName() { return name; } + + public void setName(String name) + { + this.name = name; + } public String getEmail() { return email; } + + public void setEmail(String email) + { + this.email = email; + } public double getBalance() { return balance; } - - public Date getCreatedAt() + + public void setBalance(double balance) { - return createdAt; + this.balance = balance; } - - public Date getUpdatedAt() + + public boolean getActive() + { + return active; + } + + public void setActive(boolean active) + { + this.active = active; + } + + public boolean getAudit() + { + return audit; + } + + public void setAudit(boolean audit) + { + this.audit = audit; + } + + public boolean getRedirect() + { + return redirect; + } + + public void setRedirect(boolean redirect) { - return updatedAt; + this.redirect = redirect; } } diff --git a/meteroid/src/main/java/de/chaosdorf/meteroid/util/API.java b/meteroid/src/main/java/de/chaosdorf/meteroid/util/API.java new file mode 100644 index 0000000..b9663f2 --- /dev/null +++ b/meteroid/src/main/java/de/chaosdorf/meteroid/util/API.java @@ -0,0 +1,124 @@ +/******************************************************************************* + * The MIT License (MIT) + * + * Copyright (c) 2013-2016 Chaosdorf e.V. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +package de.chaosdorf.meteroid.util; + +import retrofit2.http.*; +import retrofit2.Call; + +import java.util.List; + +import de.chaosdorf.meteroid.model.*; + +public interface API +{ + + /*// list (some) audits + @GET("audits.json") + Call> listAudits( + @Query("start_date[year]") int fromYear, + @Query("start_date[month]") int fromMonth, + @Query("start_date[day]") int fromDay, + @Query("end_date[year]") int toYear, + @Query("end_date[month]") int toMonth, + @Query("end_date[day]") int toDay + );*/ + + // list all drinks + @GET("drinks.json") + Call> listDrinks(); + + // retrieves information about a drinks + @GET("drinks/{did}.json") + Call getDrink(@Path("did") int did); + + // TODO: create and modify a drink + + // deletes a drink + @DELETE("drinks/{did}.json") + Call deleteDrink(@Path("did") int did); + + // lists all users + @GET("users.json") + Call> listUsers(); + + // retrieves information about a user + @GET("users/{uid}.json") + Call getUser(@Path("uid") int uid); + + // returns the defaults for creating new users + @GET("users/new.json") + Call getUserDefaults(); + + // creates a new user + @POST("users.json") + Call createUser( + @Body User user + ); + + // modifys an existing user + @PATCH("users/{uid}.json") + Call editUser( + @Path("uid") int uid, + @Body User user + ); + + // deletes an existing user + @DELETE("users/{uid}.json") + Call deleteUser(@Path("uid") int uid); + + // deposits money + @GET("users/{uid}/deposit.json") + Call deposit( + @Path("uid") int uid, + @Query("amount") double amount + ); + + // removes money from the balance + @GET("users/{uid}/pay.json") + Call pay( + @Path("uid") int uid, + @Query("amount") double amount + ); + + // buys a drink + @GET("users/{uid}/buy.json") + Call buy( + @Path("uid") int uid, + @Query("drink") int did + ); + + // buys a drink by barcode + @FormUrlEncoded + @POST("users/{uid}/buy_barcode.json") + Call buy_barcode( + @Path("uid") int uid, + @Field("barcode") String barcode + ); + + /*// retrieves various statistics + @GET("users/stats.json") + Call getUsersStats();*/ + +} \ No newline at end of file diff --git a/meteroid/src/main/java/de/chaosdorf/meteroid/util/Config.java b/meteroid/src/main/java/de/chaosdorf/meteroid/util/Config.java new file mode 100644 index 0000000..04b87dd --- /dev/null +++ b/meteroid/src/main/java/de/chaosdorf/meteroid/util/Config.java @@ -0,0 +1,113 @@ +/******************************************************************************* + * The MIT License (MIT) + * + * Copyright (c) 2013-2016 Chaosdorf e.V. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +package de.chaosdorf.meteroid.util; + +import android.content.Context; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; +import android.util.Log; + +public class Config +{ + public static final int NO_USER_ID = -1; + private static final String TAG = "Config"; + + private SharedPreferences prefs; + public String hostname = null; + public String apiVersion = null; + public boolean multiUserMode = false; + public boolean useGridView = false; + public int userID = NO_USER_ID; + private int version = 0; + + private static volatile Config instance; + + private Config(Context context) + { + prefs = PreferenceManager.getDefaultSharedPreferences(context); + hostname = prefs.getString("hostname", hostname); + apiVersion = prefs.getString("api_version", apiVersion); + multiUserMode = prefs.getBoolean("multi_user_mode", multiUserMode); + useGridView = prefs.getBoolean("use_grid_view", useGridView); + userID = prefs.getInt("userid", userID); + version = prefs.getInt("config_version", version); + migrate(); + } + + public static Config getInstance(Context context) + { + if(instance == null) + { + instance = new Config(context); + } + return instance; + } + + public void save() + { + Log.d(TAG, "Saving config."); + SharedPreferences.Editor edit = prefs.edit(); + edit.putString("hostname", this.hostname); + edit.putString("api_version", this.apiVersion); + edit.putBoolean("multi_user_mode", this.multiUserMode); + edit.putBoolean("use_grid_view", this.useGridView); + edit.putInt("userid", this.userID); + edit.putInt("config_version", this.version); + edit.apply(); + } + + private void migrate() + { + if(version == 0) + { + Log.d(TAG, "Migrating config from v0 to v1: Adding version."); + version = 1; + save(); + } + if(version == 1) + { + Log.d(TAG, "Migrating config from v1 to v2: Setting userID to -1, if none was set."); + if(userID == 0) + { + userID = -1; + } + version = 2; + save(); + } + if(version == 2) + { + Log.d(TAG, "Migrating config from v2 to v3: Guessing apiVersion, if none was set."); + if(hostname != null) + { + if(apiVersion == null) + { + apiVersion = Utility.guessApiVersion(hostname); + } + } + version = 3; + save(); + } + } +} diff --git a/meteroid/src/main/java/de/chaosdorf/meteroid/util/Connection.java b/meteroid/src/main/java/de/chaosdorf/meteroid/util/Connection.java new file mode 100644 index 0000000..4ecdc1f --- /dev/null +++ b/meteroid/src/main/java/de/chaosdorf/meteroid/util/Connection.java @@ -0,0 +1,123 @@ +/******************************************************************************* + * The MIT License (MIT) + * + * Copyright (c) 2013-2016 Chaosdorf e.V. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +package de.chaosdorf.meteroid.util; + +import java.util.List; + +import android.util.Log; + +import retrofit2.Retrofit; +import retrofit2.converter.gson.GsonConverterFactory; + +import de.chaosdorf.meteroid.longrunningio.LongRunningIOCallback; +import de.chaosdorf.meteroid.longrunningio.LongRunningIORequest; +import de.chaosdorf.meteroid.longrunningio.LongRunningIOTask; +import de.chaosdorf.meteroid.model.User; + +public class Connection +{ + private static final String TAG = "Connection"; + private Config config; + private API api; + + private static volatile Connection instance; + + private Connection(Config config) + { + this.config = config; + upgradeAPIversion(); + api = initializeRetrofit(config.hostname); + } + + public static Connection getInstance(Config config) + { + if(instance == null) + { + instance = new Connection(config); + } + return instance; + } + + public void reset() + { + Log.d(TAG, "Resetting connection..."); + upgradeAPIversion(); + api = initializeRetrofit(config.hostname); + } + + public API getAPI() + { + return api; + } + + private void upgradeAPIversion() + { + if(config.apiVersion == null) + { + Log.w(TAG, "API version is null. This shouldn't happen."); + config.apiVersion = Utility.guessApiVersion(config.hostname); // TODO: Do this properly. + config.save(); + } + if(config.apiVersion.equals("legacy")) + { + Log.d(TAG, "Trying to upgrade the API version from 'legacy' to 'v1'..."); + if(config.hostname.contains("api/v1")) + { + Log.e(TAG, "API version is configured as 'legacy', but seems like 'v1'. This is most certainly a bug."); + } + final String test_hostname = config.hostname + "api/v1/"; + final API test_api = initializeRetrofit(test_hostname); + new LongRunningIORequest>(new LongRunningIOCallback>() + { + @Override + public void displayErrorMessage(LongRunningIOTask task, String message) + { + Log.w(TAG, "The server doesn't seem to support 'v1' (or the request failed): " + message); + api = initializeRetrofit(config.hostname); + } + + @Override + public void processIOResult(LongRunningIOTask task, List users) + { + Log.i(TAG, "The server seems to support the newer API version 'v1'. Upgrading."); + config.hostname = test_hostname; + config.apiVersion = "v1"; + config.save(); + api = test_api; + } + }, LongRunningIOTask.GET_USERS, test_api.listUsers()); + } + } + + private API initializeRetrofit(String url) + { + Log.d(TAG, "Opening connection to " + url + "..."); + return new Retrofit.Builder() + .baseUrl(url) + .addConverterFactory(GsonConverterFactory.create()) + .build() + .create(API.class); + } +} diff --git a/meteroid/src/main/java/de/chaosdorf/meteroid/util/MenuUtility.java b/meteroid/src/main/java/de/chaosdorf/meteroid/util/MenuUtility.java index e483ecb..4297a92 100644 --- a/meteroid/src/main/java/de/chaosdorf/meteroid/util/MenuUtility.java +++ b/meteroid/src/main/java/de/chaosdorf/meteroid/util/MenuUtility.java @@ -39,14 +39,4 @@ public static void setChecked(final Menu menu, final int itemID, final boolean s } } - public static boolean onClickMultiUserMode(final Activity activity, final MenuItem item) - { - final boolean multiUserMode = Utility.toggleMultiUserMode(activity); - item.setChecked(multiUserMode); - if (multiUserMode) - { - Utility.resetUsername(activity); - } - return multiUserMode; - } } diff --git a/meteroid/src/main/java/de/chaosdorf/meteroid/util/Utility.java b/meteroid/src/main/java/de/chaosdorf/meteroid/util/Utility.java index 295a65b..fac67dd 100644 --- a/meteroid/src/main/java/de/chaosdorf/meteroid/util/Utility.java +++ b/meteroid/src/main/java/de/chaosdorf/meteroid/util/Utility.java @@ -26,25 +26,39 @@ import android.app.Activity; import android.content.Intent; -import android.content.SharedPreferences; -import android.preference.PreferenceManager; +import android.graphics.Color; +import android.graphics.PorterDuff; +import android.net.Uri; import android.widget.ImageView; import android.widget.Toast; import java.io.UnsupportedEncodingException; +import java.math.BigInteger; import java.net.MalformedURLException; -import java.net.URL; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Locale; +import com.squareup.picasso.Picasso; + import de.chaosdorf.meteroid.R; -import de.chaosdorf.meteroid.imageloader.ImageLoaderSingleton; import de.chaosdorf.meteroid.model.BuyableItem; import de.chaosdorf.meteroid.model.User; public class Utility { + public static String guessApiVersion(final String hostname) + { + if(hostname.contains("api/v1")) + { + return "v1"; + } + else + { + return "legacy"; + } + } + public static void displayToastMessage(final Activity activity, final String message) { Toast.makeText(activity, message, Toast.LENGTH_LONG).show(); @@ -52,38 +66,49 @@ public static void displayToastMessage(final Activity activity, final String mes public static void startActivity(final Activity activity, Class classType) { - final Intent intent = new Intent(activity, classType); - activity.startActivity(intent); + startActivity(activity, classType, 0); } - public static void resetUsername(final Activity activity) + public static void startActivity(final Activity activity, Class classType, int flags) { - final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity); - prefs.edit().remove("userid").apply(); + final Intent intent = new Intent(activity, classType); + intent.addFlags(flags); + activity.startActivity(intent); } public static boolean toggleUseGridView(final Activity activity) { - return Utility.toogleBooleanSharedPreference(activity, "use_grid_view", false, R.string.menu_use_grid_view_enabled, R.string.menu_use_grid_view_disabled); + Config config = Config.getInstance(activity); + config.useGridView = !config.useGridView; + config.save(); + final boolean newState = config.useGridView; + displayToastMessage(activity, activity.getResources().getString(newState ? R.string.menu_use_grid_view_enabled : R.string.menu_use_grid_view_disabled)); + return newState; } public static boolean toggleMultiUserMode(final Activity activity) { - return Utility.toogleBooleanSharedPreference(activity, "multi_user_mode", false, R.string.menu_multi_user_mode_enabled, R.string.menu_multi_user_mode_disabled); + Config config = Config.getInstance(activity); + config.multiUserMode = !config.multiUserMode; + config.save(); + final boolean newState = config.multiUserMode; + displayToastMessage(activity, activity.getResources().getString(newState ? R.string.menu_multi_user_mode_enabled : R.string.menu_multi_user_mode_disabled)); + return newState; } - private static boolean toogleBooleanSharedPreference(final Activity activity, final String prefName, final boolean defaultValue, final int enabledMessageID, final int disabledMessageID) + public static void loadUserImage(final Activity activity, final ImageView icon, final User user) { - final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity); - final boolean newState = !prefs.getBoolean(prefName, defaultValue); - prefs.edit().putBoolean(prefName, newState).apply(); - displayToastMessage(activity, activity.getResources().getString(newState ? enabledMessageID : disabledMessageID)); - return newState; + loadGravatarImage(activity, icon, user); + if(!user.getActive()) + { + icon.setColorFilter(Color.GRAY, PorterDuff.Mode.MULTIPLY); + } } - public static void loadGravatarImage(final Activity activity, final ImageView icon, final User user) + private static void loadGravatarImage(final Activity activity, final ImageView icon, final User user) { String email = null; + Uri uri = null; if (user != null) { email = user.getEmail(); @@ -94,48 +119,47 @@ public static void loadGravatarImage(final Activity activity, final ImageView ic } if (email != null && email.length() > 0) { - try - { - final URL url = new URL("http://www.gravatar.com/avatar/" + md5Hex(email) + "?d=404"); - ImageLoaderSingleton.getInstance(activity).displayImage(url, icon, ImageLoaderSingleton.getUserDefaultImage()); - return; - } - catch (MalformedURLException ignored) - { - } + uri = new Uri.Builder() + .scheme("https") + .authority("www.gravatar.com") + .appendPath("avatar") + .appendPath(md5Hex(email)) + .appendQueryParameter("d", "404") + .build(); + Picasso.get().load(uri).placeholder(R.drawable.default_user).into(icon); + } + else + { + icon.setImageResource(R.drawable.default_user); } - icon.setImageBitmap(ImageLoaderSingleton.getUserDefaultImage()); } - public static void loadBuyableItemImage(final Activity activity, final ImageView icon, final BuyableItem buyableItem) + public static void loadBuyableItemImage(final Activity activity, final ImageView icon, final BuyableItem buyableItem, final String hostname) + { + loadBuyableItemImage_(activity, icon, buyableItem, hostname); + if(!buyableItem.getActive()) + { + icon.setColorFilter(Color.GRAY, PorterDuff.Mode.MULTIPLY); + } + } + + private static void loadBuyableItemImage_(final Activity activity, final ImageView icon, final BuyableItem buyableItem, final String hostname) { icon.setContentDescription(buyableItem.getName()); + Uri uri = null; if (buyableItem.isDrink()) { - final URL url; - try - { - url = new URL(buyableItem.getLogoUrl()); - ImageLoaderSingleton.getInstance(activity).displayImage(url, icon, ImageLoaderSingleton.getDrinkDefaultImage()); - } - catch (MalformedURLException ignored) - { - icon.setImageBitmap(ImageLoaderSingleton.getDrinkDefaultImage()); - } + uri = Uri.parse(buyableItem.getLogoUrl(hostname)); + Picasso.get().load(uri).error(R.drawable.default_drink).into(icon); return; } - final int iconID = activity.getResources().getIdentifier(buyableItem.getLogoUrl(), "drawable", activity.getPackageName()); + final int iconID = activity.getResources().getIdentifier(buyableItem.getLogoUrl(hostname), "drawable", activity.getPackageName()); icon.setImageResource(iconID > 0 ? iconID : R.drawable.euro_5); } private static String arrayToHex(final byte[] array) { - StringBuilder sb = new StringBuilder(); - for (final byte anArray : array) - { - sb.append(Integer.toHexString((anArray & 0xFF) | 0x100).substring(1, 3)); - } - return sb.toString(); + return String.format("%032x", new BigInteger(1, array)); } private static String md5Hex(final String message) @@ -144,12 +168,9 @@ private static String md5Hex(final String message) { return arrayToHex(MessageDigest.getInstance("MD5").digest(message.getBytes("CP1252"))); } - catch (NoSuchAlgorithmException ignored) - { - } - catch (UnsupportedEncodingException ignored) + catch (NoSuchAlgorithmException | UnsupportedEncodingException exc) { + throw new AssertionError("Can't happen: " + exc.toString(), exc); } - return null; } } diff --git a/meteroid/src/main/res/drawable-hdpi/button_barcode.png b/meteroid/src/main/res/drawable-hdpi/button_barcode.png new file mode 100644 index 0000000..61503f9 Binary files /dev/null and b/meteroid/src/main/res/drawable-hdpi/button_barcode.png differ diff --git a/meteroid/src/main/res/drawable-hdpi/empty_glass.png b/meteroid/src/main/res/drawable-hdpi/empty_glass.png new file mode 100644 index 0000000..3e05584 Binary files /dev/null and b/meteroid/src/main/res/drawable-hdpi/empty_glass.png differ diff --git a/meteroid/src/main/res/drawable-mdpi/button_barcode.png b/meteroid/src/main/res/drawable-mdpi/button_barcode.png new file mode 100644 index 0000000..9e96ff2 Binary files /dev/null and b/meteroid/src/main/res/drawable-mdpi/button_barcode.png differ diff --git a/meteroid/src/main/res/drawable-mdpi/empty_glass.png b/meteroid/src/main/res/drawable-mdpi/empty_glass.png new file mode 100644 index 0000000..963d092 Binary files /dev/null and b/meteroid/src/main/res/drawable-mdpi/empty_glass.png differ diff --git a/meteroid/src/main/res/drawable-xhdpi/button_barcode.png b/meteroid/src/main/res/drawable-xhdpi/button_barcode.png new file mode 100644 index 0000000..74c4e43 Binary files /dev/null and b/meteroid/src/main/res/drawable-xhdpi/button_barcode.png differ diff --git a/meteroid/src/main/res/drawable-xhdpi/empty_glass.png b/meteroid/src/main/res/drawable-xhdpi/empty_glass.png new file mode 100644 index 0000000..1367ce4 Binary files /dev/null and b/meteroid/src/main/res/drawable-xhdpi/empty_glass.png differ diff --git a/meteroid/src/main/res/drawable-xxhdpi/empty_glass.png b/meteroid/src/main/res/drawable-xxhdpi/empty_glass.png new file mode 100644 index 0000000..b17df41 Binary files /dev/null and b/meteroid/src/main/res/drawable-xxhdpi/empty_glass.png differ diff --git a/meteroid/src/main/res/drawable/euro_05.png b/meteroid/src/main/res/drawable/euro_05.png new file mode 100644 index 0000000..28afde7 Binary files /dev/null and b/meteroid/src/main/res/drawable/euro_05.png differ diff --git a/meteroid/src/main/res/drawable/euro_1.png b/meteroid/src/main/res/drawable/euro_1.png new file mode 100644 index 0000000..14112cd Binary files /dev/null and b/meteroid/src/main/res/drawable/euro_1.png differ diff --git a/meteroid/src/main/res/drawable/euro_2.png b/meteroid/src/main/res/drawable/euro_2.png new file mode 100644 index 0000000..7d9f7b1 Binary files /dev/null and b/meteroid/src/main/res/drawable/euro_2.png differ diff --git a/meteroid/src/main/res/layout/activity_about.xml b/meteroid/src/main/res/layout/activity_about.xml new file mode 100644 index 0000000..1d3324d --- /dev/null +++ b/meteroid/src/main/res/layout/activity_about.xml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/meteroid/src/main/res/layout/activity_buy_drink.xml b/meteroid/src/main/res/layout/activity_buy_drink.xml index 0e41a88..78ec6d5 100644 --- a/meteroid/src/main/res/layout/activity_buy_drink.xml +++ b/meteroid/src/main/res/layout/activity_buy_drink.xml @@ -24,117 +24,126 @@ ~ THE SOFTWARE. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~--> - + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + android:orientation="vertical" + android:layout_width="fill_parent" + android:layout_height="fill_parent"> + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - + android:layout_alignParentBottom="true" + android:layout_alignParentRight="true" + android:layout_margin="16dip" + android:src="@drawable/button_barcode" /> + + + diff --git a/meteroid/src/main/res/layout/activity_buy_drink_item_gridview.xml b/meteroid/src/main/res/layout/activity_buy_drink_item_gridview.xml index a9fa4a7..0beeb7b 100644 --- a/meteroid/src/main/res/layout/activity_buy_drink_item_gridview.xml +++ b/meteroid/src/main/res/layout/activity_buy_drink_item_gridview.xml @@ -27,8 +27,8 @@ @@ -40,6 +40,7 @@ android:layout_height="wrap_content" android:adjustViewBounds="true" android:layout_gravity="center_horizontal" + android:layout_weight="2" android:contentDescription="@string/app_name"/> - \ No newline at end of file + diff --git a/meteroid/src/main/res/layout/activity_pick_username.xml b/meteroid/src/main/res/layout/activity_pick_username.xml index 16315cc..15e9ca9 100644 --- a/meteroid/src/main/res/layout/activity_pick_username.xml +++ b/meteroid/src/main/res/layout/activity_pick_username.xml @@ -24,73 +24,73 @@ ~ THE SOFTWARE. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~--> - + - + - + - + + - + - + + + + + + + - - - - - - - - + + diff --git a/meteroid/src/main/res/layout/activity_set_hostname.xml b/meteroid/src/main/res/layout/activity_set_hostname.xml index bdbc37b..889a684 100644 --- a/meteroid/src/main/res/layout/activity_set_hostname.xml +++ b/meteroid/src/main/res/layout/activity_set_hostname.xml @@ -24,35 +24,39 @@ ~ THE SOFTWARE. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~--> - + - + - + -