@@ -1,4 +1,5 @@ | |||
apply plugin: 'com.android.application' | |||
apply plugin: 'kotlin-android' | |||
android { | |||
compileSdkVersion 29 | |||
@@ -51,4 +52,6 @@ dependencies { | |||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' | |||
implementation 'com.google.android.material:material:1.1.0' | |||
implementation 'com.mapbox.mapboxsdk:mapbox-android-sdk:9.1.0' | |||
implementation "androidx.core:core-ktx:+" | |||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" | |||
} |
@@ -8,7 +8,13 @@ import androidx.test.platform.app.InstrumentationRegistry; | |||
import org.junit.Test; | |||
import org.junit.runner.RunWith; | |||
import java.util.Arrays; | |||
import app.pott.kaffeepott.androidclient.core.Badge; | |||
import app.pott.kaffeepott.androidclient.sort.BadgeComparator; | |||
import static org.junit.Assert.assertEquals; | |||
import static org.junit.Assert.assertNotNull; | |||
/** | |||
* Instrumented test, which will execute on an Android device. | |||
@@ -18,10 +24,16 @@ import static org.junit.Assert.assertEquals; | |||
@RunWith(AndroidJUnit4.class) | |||
public class ExampleInstrumentedTest { | |||
@Test | |||
public void useAppContext() { | |||
// Context of the app under test. | |||
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); | |||
public void testBadgeComparator() { | |||
Badge[] badges = {Badge.PARTNER, Badge.ORGANIC, Badge.SEATING, Badge.TAKEAWAY, Badge.SELF_SERVICE}; | |||
Arrays.sort(badges, new BadgeComparator()); | |||
assertEquals("app.pott.kaffeepott.androidclient.ui", appContext.getPackageName()); | |||
assertEquals(Badge.PARTNER, badges[0]); | |||
} | |||
@Test | |||
public void appContextTest() { | |||
assertNotNull(InstrumentationRegistry.getInstrumentation().getTargetContext().getApplicationContext().getApplicationContext()); | |||
} | |||
} |
@@ -16,7 +16,7 @@ public final class Constants { | |||
} | |||
public static final class LocalDBConstants { | |||
public static final int DB_VERSION = 12; // Increment this version if the database's | |||
public static final int DB_VERSION = 13; // Increment this version if the database's | |||
// tables are altered | |||
public static final String DB_NAME = "KAFFEEPOTT_SQLITE_DB"; | |||
public static final String DB_TIME_FORMAT = "HH:mm"; | |||
@@ -9,6 +9,7 @@ import app.pott.kaffeepott.androidclient.core.Cafe; | |||
import app.pott.kaffeepott.androidclient.core.PriceLevel; | |||
import app.pott.kaffeepott.androidclient.core.Product; | |||
import app.pott.kaffeepott.androidclient.core.WeekTimeRange; | |||
import app.pott.kaffeepott.androidclient.sort.BadgeComparator; | |||
import app.pott.kaffeepott.androidclient.sort.OpeningRangeComparator; | |||
/** | |||
@@ -33,9 +34,11 @@ public class CafeReadWriteTK { | |||
Location location = readHandler.onGetLocation(); | |||
double rating = readHandler.onGetRating(); | |||
int numberOfRatings = readHandler.onGetNumberOfRatings(); | |||
WeekTimeRange[] weekTimeRanges = readHandler.onGetWeekTimeRanges(); | |||
Arrays.sort(weekTimeRanges, new OpeningRangeComparator()); | |||
Badge[] badges = readHandler.onGetBadges(); | |||
Arrays.sort(badges, new BadgeComparator()); | |||
Product[] products = readHandler.onGetProducts(); | |||
return new Cafe(id, name, location, rating, numberOfRatings, weekTimeRanges, priceLevel, badges, products); | |||
@@ -58,6 +61,7 @@ public class CafeReadWriteTK { | |||
writeHandler.onSetRating(cafe.getRating()); | |||
writeHandler.onSetNumberOfRatings(cafe.getNumberOfRatings()); | |||
writeHandler.onShallowWriteFinished(); | |||
writeHandler.onSetWeekTimeRanges(cafe.getOpeningHours()); | |||
writeHandler.onSetBadges(cafe.getBadges()); | |||
writeHandler.onSetProducts(cafe.getProducts()); | |||
@@ -1,8 +1,19 @@ | |||
package app.pott.kaffeepott.androidclient.core; | |||
import android.content.Context; | |||
import android.view.LayoutInflater; | |||
import android.view.View; | |||
import android.view.ViewGroup; | |||
import android.widget.ImageView; | |||
import android.widget.TextView; | |||
import androidx.annotation.DrawableRes; | |||
import androidx.annotation.LayoutRes; | |||
import androidx.annotation.StringRes; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import app.pott.kaffeepott.androidclient.ui.R; | |||
/** | |||
@@ -57,19 +68,34 @@ public enum Badge { | |||
/* Not my favourite */ | |||
SMOKING(R.drawable.ic_smoking, R.string.badge_smoking), | |||
/** | |||
* Given if the café has outdoor seating facilities. | |||
*/ | |||
OUTDOOR_SEATING(R.drawable.ic_outdoor_seating, R.string.badge_outdoor_seating); | |||
/** | |||
* Given if the café has outdoor seating facilities. | |||
*/ | |||
OUTDOOR_SEATING(R.drawable.ic_outdoor_seating, R.string.badge_outdoor_seating), | |||
/** | |||
* Given if the cafe is a partner of KaffeePott, i.e. it cooperates with its developers by, | |||
* for instance, promoting the application. | |||
*/ | |||
PARTNER(R.drawable.ic_partner, R.string.badge_partner, false, R.layout.fragment_special_badge); | |||
private @DrawableRes | |||
int iconId; | |||
private @StringRes | |||
int displayTextId; | |||
private boolean addable; | |||
private @LayoutRes | |||
int uiRepresentation; | |||
Badge(int iconId, int displayTextId) { | |||
this(iconId, displayTextId, true, R.layout.fragment_badge); | |||
} | |||
Badge(int iconId, int displayTextId, boolean addable, @LayoutRes int uiRepresentation) { | |||
this.iconId = iconId; | |||
this.displayTextId = displayTextId; | |||
this.addable = addable; | |||
this.uiRepresentation = uiRepresentation; | |||
} | |||
public @DrawableRes | |||
@@ -81,4 +107,19 @@ public enum Badge { | |||
int getDisplayTextId() { | |||
return displayTextId; | |||
} | |||
public View getView(ViewGroup root, Context context) { | |||
View view = LayoutInflater.from(context).inflate(uiRepresentation, root, false); | |||
((ImageView) view.findViewById(R.id.badgeFragmentImageView)).setImageResource(iconId); | |||
((TextView) view.findViewById(R.id.badgeFragmentTextView)).setText(context.getString(displayTextId)); | |||
return view; | |||
} | |||
public static Badge[] addableValues() { | |||
List<Badge> retVal = new ArrayList<>(); | |||
for (Badge badge : values()) | |||
if (badge.addable) | |||
retVal.add(badge); | |||
return retVal.toArray(new Badge[0]); | |||
} | |||
} |
@@ -5,6 +5,7 @@ import android.location.Location; | |||
import com.mapbox.mapboxsdk.geometry.LatLng; | |||
import java.util.Arrays; | |||
import java.util.Objects; | |||
import app.pott.kaffeepott.androidclient.Constants; | |||
@@ -32,6 +33,7 @@ public class Cafe { | |||
private String name; | |||
private Location location; | |||
private boolean isPartner; // Not DRY, but more efficient | |||
private double rating; | |||
private int numberOfRatings; | |||
private long cafeId; | |||
@@ -57,6 +59,7 @@ public class Cafe { | |||
this.priceLevel = priceLevel; | |||
this.badges = badges; | |||
this.products = products; | |||
this.isPartner = Arrays.asList(badges).contains(Badge.PARTNER); | |||
} | |||
/** | |||
@@ -86,6 +89,13 @@ public class Cafe { | |||
return location; | |||
} | |||
/** | |||
* @return Whether the café is KaffeePott's partner. | |||
*/ | |||
public boolean isPartner() { | |||
return isPartner; | |||
} | |||
/** | |||
* Gets this café's average rating. | |||
* | |||
@@ -0,0 +1,18 @@ | |||
package app.pott.kaffeepott.androidclient.sort; | |||
import java.util.Comparator; | |||
import app.pott.kaffeepott.androidclient.core.Badge; | |||
public class BadgeComparator implements Comparator<Badge> { | |||
@Override | |||
public int compare(Badge o1, Badge o2) { | |||
if ((o1 != Badge.PARTNER && o2 != Badge.PARTNER) || o1 == o2) // o1 == o2 shouldn't happen as badges are distinct | |||
return 0; | |||
else if (o1 == Badge.PARTNER) | |||
return -1; | |||
else | |||
return 1; | |||
} | |||
} |
@@ -24,26 +24,34 @@ public class RelevanceCafeComparator extends CafeComparator { | |||
public int compare(PersonalizedCafe cafe1, PersonalizedCafe cafe2) { | |||
int favouriteDiff = favourizedCafeComparator.compare(cafe1, cafe2); | |||
if (favouriteDiff == 0) { | |||
if (cafe1.isOpen(false) == cafe2.isOpen(false)) { | |||
int distanceDiff = distanceCafeComparator.compare(cafe2, cafe1); | |||
if (distanceDiff == 0) { | |||
int priceDiff = priceCafeComparator.compare(cafe2, cafe1); | |||
if (priceDiff == 0) { | |||
return ratingCafeComparator.compare(cafe2, cafe1); | |||
if (cafe1.isPartner() == cafe2.isPartner()) { | |||
if (cafe1.isOpen(false) == cafe2.isOpen(false)) { | |||
int distanceDiff = distanceCafeComparator.compare(cafe2, cafe1); | |||
if (distanceDiff == 0) { | |||
int priceDiff = priceCafeComparator.compare(cafe2, cafe1); | |||
if (priceDiff == 0) { | |||
return ratingCafeComparator.compare(cafe2, cafe1); | |||
} else { | |||
return priceDiff; // priceDiff != 0 | |||
} | |||
} else { | |||
return distanceDiff; // distanceDiff != 0 | |||
} | |||
} else { | |||
return priceDiff; | |||
return compareBooleanUsingMode(cafe1.isOpen(false), cafe2.isOpen(false)); // cafe1.isOpen() != cafe2.isOpen() | |||
} | |||
} else { | |||
return distanceDiff; | |||
return compareBooleanUsingMode(cafe1.isPartner(), cafe2.isPartner()); // cafe1.isPartner() != cafe2.isPartner() | |||
} | |||
} else { | |||
if (getMode() == SortMode.ASCENDING) | |||
return Boolean.compare(cafe1.isOpen(), cafe2.isOpen()); | |||
else | |||
return Boolean.compare(cafe2.isOpen(), cafe1.isOpen()); | |||
} | |||
} else { | |||
return favouriteDiff; | |||
} | |||
} | |||
private int compareBooleanUsingMode(boolean first, boolean second) { | |||
if (getMode() == SortMode.ASCENDING) | |||
return Boolean.compare(first, second); | |||
else | |||
return Boolean.compare(second, first); | |||
} | |||
} |
@@ -58,16 +58,7 @@ public class BadgeFragment extends Fragment { | |||
@Override | |||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { | |||
root = inflater.inflate(R.layout.fragment_badge, container, false); | |||
Badge badge = Enum.valueOf(Badge.class, badgeName); | |||
ImageView badgeImageView = root.findViewById(R.id.badgeFragmentImageView); | |||
badgeImageView.setImageResource(badge.getIconId()); | |||
TextView badgeTextView = root.findViewById(R.id.badgeFragmentTextView); | |||
badgeTextView.setText(getString(badge.getDisplayTextId())); | |||
root = Badge.valueOf(badgeName).getView(container, getContext()); | |||
setCollapsed(collapsed); | |||
@@ -321,7 +321,7 @@ public class ReportActivity extends AppCompatActivity { | |||
public void addBadgeDialog() { | |||
List<Badge> missingBadges = new ArrayList<Badge>(); | |||
List<ReportListData> currentBadges = badgeCollection.get(getString(R.string.report_badges)); | |||
Badge[] allBadges = Badge.values(); | |||
Badge[] allBadges = Badge.addableValues(); | |||
for (Badge allBadge: allBadges) { | |||
boolean exists = false; | |||
for (ReportListData currentBadge: currentBadges) { | |||
@@ -0,0 +1,12 @@ | |||
<vector xmlns:android="http://schemas.android.com/apk/res/android" | |||
android:width="16dp" | |||
android:height="16dp" | |||
android:tint="@color/colorForeground" | |||
android:viewportWidth="24" | |||
android:viewportHeight="24" | |||
> | |||
<path | |||
android:fillColor="#000" | |||
android:pathData="M23,12L20.56,9.22L20.9,5.54L17.29,4.72L15.4,1.54L12,3L8.6,1.54L6.71,4.72L3.1,5.53L3.44,9.21L1,12L3.44,14.78L3.1,18.47L6.71,19.29L8.6,22.47L12,21L15.4,22.46L17.29,19.28L20.9,18.46L20.56,14.78L23,12M10,17L6,13L7.41,11.59L10,14.17L16.59,7.58L18,9L10,17Z" | |||
/> | |||
</vector> |
@@ -1,43 +1,39 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" | |||
xmlns:tools="http://schemas.android.com/tools" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
tools:context=".BadgeFragment" | |||
> | |||
<LinearLayout | |||
android:id="@+id/badgeFragmentLinearLayout" | |||
xmlns:tools="http://schemas.android.com/tools" | |||
android:layout_width="wrap_content" | |||
android:layout_height="match_parent" | |||
android:gravity="end" | |||
android:orientation="horizontal" | |||
android:padding="2dp" | |||
android:paddingStart="0dp" | |||
android:paddingEnd="4dp" | |||
> | |||
android:layout_height="wrap_content" | |||
tools:context=".BadgeFragment"> | |||
<ImageView | |||
android:id="@+id/badgeFragmentImageView" | |||
<LinearLayout | |||
android:id="@+id/badgeFragmentLinearLayout" | |||
android:layout_width="wrap_content" | |||
android:layout_height="match_parent" | |||
android:background="@drawable/rounded_corners_start_shape" | |||
android:padding="4dp" | |||
android:src="@drawable/ic_network_wifi" | |||
/> | |||
android:gravity="end" | |||
android:orientation="horizontal" | |||
android:padding="2dp" | |||
android:paddingStart="0dp" | |||
android:paddingEnd="4dp"> | |||
<ImageView | |||
android:id="@+id/badgeFragmentImageView" | |||
android:layout_width="wrap_content" | |||
android:layout_height="match_parent" | |||
android:background="@drawable/rounded_corners_start_shape" | |||
android:padding="4dp" | |||
android:src="@drawable/ic_network_wifi" /> | |||
<TextView | |||
android:id="@+id/badgeFragmentTextView" | |||
android:layout_width="wrap_content" | |||
android:layout_height="match_parent" | |||
android:background="@drawable/rounded_corners_end_shape" | |||
android:gravity="center_vertical" | |||
android:lines="1" | |||
android:paddingStart="0dp" | |||
android:paddingEnd="6dp" | |||
android:text="dfshdfgbs" | |||
android:textColor="@color/colorForeground" | |||
android:textSize="@dimen/h3" | |||
/> | |||
android:id="@+id/badgeFragmentTextView" | |||
android:layout_width="wrap_content" | |||
android:layout_height="match_parent" | |||
android:background="@drawable/rounded_corners_end_shape" | |||
android:gravity="center_vertical" | |||
android:lines="1" | |||
android:paddingStart="0dp" | |||
android:paddingEnd="6dp" | |||
android:text="dfshdfgbs" | |||
android:textColor="@color/colorForeground" | |||
android:textSize="@dimen/h3" /> | |||
</LinearLayout> | |||
</FrameLayout> |
@@ -138,25 +138,25 @@ | |||
</androidx.constraintlayout.widget.ConstraintLayout> | |||
<HorizontalScrollView | |||
android:layout_width="0dp" | |||
android:layout_height="wrap_content" | |||
android:layout_marginTop="8dp" | |||
android:layout_marginEnd="16dp" | |||
android:visibility="visible" | |||
app:layout_constraintEnd_toEndOf="parent" | |||
app:layout_constraintStart_toStartOf="@+id/cafeFragmentSecondLineCL" | |||
app:layout_constraintTop_toBottomOf="@+id/cafeFragmentSecondLineCL" | |||
> | |||
android:id="@+id/horizontalScrollView" | |||
android:layout_width="0dp" | |||
android:layout_height="wrap_content" | |||
android:layout_marginTop="8dp" | |||
android:layout_marginEnd="16dp" | |||
android:visibility="visible" | |||
app:layout_constraintEnd_toEndOf="parent" | |||
app:layout_constraintStart_toStartOf="@+id/cafeFragmentSecondLineCL" | |||
app:layout_constraintTop_toBottomOf="@+id/cafeFragmentSecondLineCL"> | |||
<FrameLayout | |||
android:id="@+id/cafeFragmentBadgeFragmentStandalone" | |||
android:layout_width="wrap_content" | |||
android:layout_height="28dp" | |||
app:layout_constraintBottom_toBottomOf="parent" | |||
app:layout_constraintEnd_toEndOf="parent" | |||
app:layout_constraintTop_toTopOf="parent" | |||
tools:layout="@layout/fragment_badges" | |||
/> | |||
android:id="@+id/cafeFragmentBadgeFragmentStandalone" | |||
android:layout_width="wrap_content" | |||
android:layout_height="28dp" | |||
app:layout_constraintBottom_toBottomOf="parent" | |||
app:layout_constraintEnd_toEndOf="parent" | |||
app:layout_constraintTop_toTopOf="parent" | |||
tools:layout="@layout/fragment_badges" /> | |||
</HorizontalScrollView> | |||
</androidx.constraintlayout.widget.ConstraintLayout> | |||
</androidx.constraintlayout.widget.ConstraintLayout> |
@@ -0,0 +1,49 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" | |||
xmlns:tools="http://schemas.android.com/tools" | |||
android:layout_width="wrap_content" | |||
android:layout_height="wrap_content" | |||
tools:context=".BadgeFragment" | |||
> | |||
<LinearLayout | |||
android:id="@+id/badgeFragmentLinearLayout" | |||
android:layout_width="wrap_content" | |||
android:layout_height="match_parent" | |||
android:gravity="end" | |||
android:orientation="horizontal" | |||
android:padding="2dp" | |||
android:paddingStart="0dp" | |||
android:paddingEnd="4dp" | |||
android:elevation="4dp" | |||
> | |||
<ImageView | |||
android:id="@+id/badgeFragmentImageView" | |||
android:layout_width="wrap_content" | |||
android:layout_height="match_parent" | |||
android:background="@drawable/rounded_corners_start_shape" | |||
android:backgroundTint="@color/colorSpecialBadge" | |||
android:padding="4dp" | |||
android:src="@drawable/ic_partner" | |||
android:tint="@color/colorPrimary" | |||
/> | |||
<TextView | |||
android:id="@+id/badgeFragmentTextView" | |||
android:layout_width="wrap_content" | |||
android:layout_height="match_parent" | |||
android:background="@drawable/rounded_corners_end_shape" | |||
android:backgroundTint="@color/colorSpecialBadge" | |||
android:fontFamily="@font/inter_bold" | |||
android:gravity="center_vertical" | |||
android:lines="1" | |||
android:paddingStart="0dp" | |||
android:paddingEnd="6dp" | |||
android:text="Partnered" | |||
android:textColor="@color/colorPrimary" | |||
android:textSize="@dimen/h3" | |||
android:textStyle="bold" | |||
/> | |||
</LinearLayout> | |||
</FrameLayout> |
@@ -191,6 +191,7 @@ | |||
<string name="no_product_fragment_text">Produke würden hier angezeigt, aber wir kennen die Karte des Cafés nicht. Klicke hier, um Produkte hinzuzufügen</string> | |||
<string name="settings_data_save_mode">Datensparmodus</string> | |||
<string name="data_save_mode_summary">"Sende Bewertungen und Tickets nur, wenn über WLAN verbunden "</string> | |||
<string name="badge_partner">Partner</string> | |||
</resources> | |||
@@ -23,5 +23,7 @@ | |||
<color name="colorPrimaryDark">#dddddd</color> | |||
<color name="colorAccent">@color/colorBronze</color> | |||
<color name="colorSpecialBadge">@color/colorBronze</color> | |||
<bool name="lightStatusBar">true</bool> | |||
</resources> |
@@ -33,5 +33,7 @@ | |||
<color name="colorPrimaryDark">#393939</color> | |||
<color name="colorAccent">@color/colorBronze</color> | |||
<color name="colorSpecialBadge">@color/colorBronzeLight</color> | |||
<bool name="lightStatusBar">false</bool> | |||
</resources> |
@@ -82,6 +82,7 @@ | |||
<string name="badge_self_service">Self-service</string> | |||
<string name="badge_takeaway">Takeaway</string> | |||
<string name="badge_smoking">Smoking allowed</string> | |||
<string name="badge_partner">Partnered</string> | |||
<!-- Button Labels --> | |||
<string name="search_area_button_label">Search this area</string> | |||
<string name="do_not_enable_location_button_label">No thanks</string> | |||
@@ -10,8 +10,5 @@ import static org.junit.Assert.assertEquals; | |||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a> | |||
*/ | |||
public class ExampleUnitTest { | |||
@Test | |||
public void addition_isCorrect() { | |||
assertEquals(4, 2 + 2); | |||
} | |||
} |
@@ -1,7 +1,8 @@ | |||
// Top-level build file where you can add configuration options common to all sub-projects/modules. | |||
buildscript { | |||
ext.kotlin_version = '1.3.72' | |||
repositories { | |||
google() | |||
jcenter() | |||
@@ -9,7 +10,8 @@ buildscript { | |||
} | |||
dependencies { | |||
classpath 'com.android.tools.build:gradle:4.0.0' | |||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" | |||
// NOTE: Do not place your application dependencies here; they belong | |||
// in the individual module build.gradle files | |||