diff --git a/app/build.gradle b/app/build.gradle index 119e93c..0f4029f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -9,8 +9,8 @@ android { applicationId 'com.rubenvandeven.emotion_hero' minSdkVersion 16 targetSdkVersion 24 - versionCode 2 - versionName '0.2' + versionCode 4 + versionName '0.4' testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" renderscriptTargetApi 16 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a7163e0..a6fe132 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -35,10 +35,6 @@ - - + + \ No newline at end of file diff --git a/app/src/main/java/com/rubenvandeven/emotionhero/Achievement.java b/app/src/main/java/com/rubenvandeven/emotionhero/Achievement.java index 4ff29c6..68d1179 100644 --- a/app/src/main/java/com/rubenvandeven/emotionhero/Achievement.java +++ b/app/src/main/java/com/rubenvandeven/emotionhero/Achievement.java @@ -1,6 +1,7 @@ package com.rubenvandeven.emotionhero; import android.graphics.Bitmap; +import android.widget.RelativeLayout; /** * Created by ruben on 10/09/16. @@ -15,14 +16,20 @@ public class Achievement { */ public String description; public Bitmap icon; + public AchievementListener achievementListener; - public Achievement(String title, String description, Bitmap icon){ + public Achievement(String title, Bitmap icon, AchievementListener lvlListener){ this.title = title; - this.description = description; this.icon = icon; + this.achievementListener = lvlListener; } public int getId() { return AchievementCollection.getInstance().getId(this); } + + public interface AchievementListener { + boolean hasGotAchievement(Player player, Game game); + void setDescription(Player player, Game game, RelativeLayout layout); + } } diff --git a/app/src/main/java/com/rubenvandeven/emotionhero/AchievementCollection.java b/app/src/main/java/com/rubenvandeven/emotionhero/AchievementCollection.java index b60a2fb..861d185 100644 --- a/app/src/main/java/com/rubenvandeven/emotionhero/AchievementCollection.java +++ b/app/src/main/java/com/rubenvandeven/emotionhero/AchievementCollection.java @@ -1,8 +1,15 @@ package com.rubenvandeven.emotionhero; -import android.support.v4.content.res.TypedArrayUtils; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.support.v7.widget.AppCompatTextView; +import android.view.View; +import android.widget.RelativeLayout; import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; /** * Created by ruben on 10/09/16. @@ -10,12 +17,482 @@ import java.util.ArrayList; public class AchievementCollection { private ArrayList achievements = new ArrayList(){{ // fill the collection :-) - add(new Achievement("Microexpressor", "...", null)); - add(new Achievement("\"In pursuit of happiness\"", "...", null)); - add(new Achievement("CEO", "...", null)); - add(new Achievement("Kuleshov", "...", null)); - add(new Achievement("Soap opera", "...", null)); - add(new Achievement("Stock photo model", "...", null)); + add(new Achievement( + "Microexpressor", + null, + new Achievement.AchievementListener() { + @Override + public boolean hasGotAchievement(Player player, Game game) { + // not showing intense enough? requested emo's only minisculy detected + //return false; + for(Hit hit: game.hits.values()) { + if(hit.target.value == 100) { + // if target was 100, but player only showed less + // then 13..s/he's playing with microexpressions ;-) + if(hit.emotions.get(hit.target.emotion) < 13) { + return true; + } + } + } + return false; + } + + @Override + public void setDescription(Player player, Game game, RelativeLayout descriptionLayout) { + Context c = descriptionLayout.getContext(); + AppCompatTextView textView = new AppCompatTextView(c); + textView.setText("Do you have something to hide? You are not showing all requested emotions intense enough. A microexpression is a brief, involuntary facial expression shown on the face of humans according to emotions experienced."); + descriptionLayout.addView(textView); + } + }) + ); + add(new Achievement( + "Emotional leakage", + null, + new Achievement.AchievementListener() { + @Override + public boolean hasGotAchievement(Player player, Game game) { + // picking up on emotions that are not requested. + + // check for all hits in the level... + for(Hit hit: game.hits.values()) { + for(Emotion emotion: Emotion.values()) { + // ... whether the emotions that are NOT scored + // still have a value + if( hit.target.emotion != emotion && hit.emotions.get(emotion) > 20) { + // if so, there is emo leakage!! + return true; + } + } + } + return false; + } + + @Override + public void setDescription(Player player, Game game, RelativeLayout descriptionLayout) { + Context c = descriptionLayout.getContext(); + AppCompatTextView textView = new AppCompatTextView(c); + textView.setText("There is no sign of deceit itself. There is no gesture, facial expression, nor muscle twitch that itself indicates that a person is lying. There are only clues that the person is poorly prepared and that the clues of emotions do not fit what the person is saying. These are what provide clues of leakage or deception. (Wikipedia)"); + descriptionLayout.addView(textView); + } + }) + ); + add(new Achievement( + "A conversation with your data double", + null, + new Achievement.AchievementListener() { + @Override + public boolean hasGotAchievement(Player player, Game game) { + // 2nd play is better than _PREVIOUS_ play! + Game previousGame = player.getGameOpenHelper().getPreviousGame(game); + if(previousGame == null) { + return false; + } + + if(previousGame.bonus+previousGame.score < game.bonus+game.score) { + return true; + } + + return false; + } + + @Override + public void setDescription(Player player, Game game, final RelativeLayout descriptionLayout) { + Context c = descriptionLayout.getContext(); + AppCompatTextView textView = new AppCompatTextView(c); + textView.setText("You improved your performance after a retry! The 'data double' is a term coined by Mina Ruckenstein to describe how our gathered information is be figuratively reassembled for the purposes of personal reflection and interaction."); + descriptionLayout.addView(textView); + + /*AppCompatTextView seeAlsoView = new AppCompatTextView(c); + seeAlsoView.setTextSize(12); + seeAlsoView.setText("See also: Ruckenstein, Mina (2014) Visualized and Interacted Life: Personal Analytics and Engagements with Data Doubles"); + seeAlsoView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + String url = "https://www.researchgate.net/publication/260792107_Visualized_and_Interacted_Life_Personal_Analytics_and_Engagements_with_Data_Doubles"; + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse(url)); + descriptionLayout.getContext().startActivity(i); + } + }); + descriptionLayout.addView(seeAlsoView);*/ + } + }) + ); + add(new Achievement( + "Named emotions", + null, + new Achievement.AchievementListener() { + @Override + public boolean hasGotAchievement(Player player, Game game) { + // have expressed all named emotions! + Map showedEmotions = new HashMap(); + for(Hit hit: game.hits.values()) { + if(hit.score > 50) { + showedEmotions.put(hit.target.emotion, true); + } + } + if(showedEmotions.size() == Emotion.values().length) { + return true; + } + return false; + } + + @Override + public void setDescription(Player player, Game game, RelativeLayout descriptionLayout) { + Context c = descriptionLayout.getContext(); + AppCompatTextView textView = new AppCompatTextView(c); + textView.setText("\"We do not have names for all the possible combinations of " + + "primary and background focuses. No one culture has a monopoly on emotions, and each culture may offer its own unique feelings.\" (Hochschild, 1983)"); + descriptionLayout.addView(textView); + } + }) + ); + add(new Achievement( + "Augustine", + null, + new Achievement.AchievementListener() { + @Override + public boolean hasGotAchievement(Player player, Game game) { + // all hit emotions are > 80% + boolean hasLowerItem = false; + for(Hit hit: game.hits.values()) { + if(hit.emotions.get(hit.target.emotion) <= 80) { + hasLowerItem = true; + } + } + return !hasLowerItem; // reverse + } + + @Override + public void setDescription(Player player, Game game, RelativeLayout descriptionLayout) { + Context c = descriptionLayout.getContext(); + AppCompatTextView textView = new AppCompatTextView(c); + textView.setText("You are HYSTERICAL!!! Augustine is one of Jean-Martin Charcot's proofs cases for hysteria. Read 'The Invention Of Hysteria' by George Didi-Huberman on how Charcot contributed to her performance of hysteria."); + descriptionLayout.addView(textView); + + AppCompatTextView seeAlsoView = new AppCompatTextView(c); +// seeAlsoView.setTextSize(12); + seeAlsoView.setText("Hysteria, in the colloquial use of the term, means ungovernable emotional excess. Generally, modern medical professionals have abandoned using the term \"hysteria\" to denote a diagnostic category, replacing it with more precisely defined categories. (Wikipedia)"); + descriptionLayout.addView(seeAlsoView); + } + }) + ); + add(new Achievement( + "Steward(ess)", //Emotional labour + null, + new Achievement.AchievementListener() { + @Override + public boolean hasGotAchievement(Player player, Game game) { + + return false; + } + + @Override + public void setDescription(Player player, Game game, RelativeLayout descriptionLayout) { + Context c = descriptionLayout.getContext(); + AppCompatTextView textView = new AppCompatTextView(c); + textView.setText("..."); + descriptionLayout.addView(textView); + } + }) + ); + add(new Achievement( + "Stuff", // trouble recognising + null, + new Achievement.AchievementListener() { + @Override + public boolean hasGotAchievement(Player player, Game game) { + // have 10 times in which another emotion is expressed most pronounced then the one that is requested + int timesWrong = 0; + for(Hit hit: game.hits.values()) { + Emotion mostPronouncedEmotion = null; + for(Emotion emotion: hit.emotions.keySet()) { + if(mostPronouncedEmotion == null || hit.emotions.get(mostPronouncedEmotion) < hit.emotions.get(emotion)) { + mostPronouncedEmotion = emotion; + } + } + if(mostPronouncedEmotion != hit.target.emotion) { + timesWrong++; + } + } + + return timesWrong >= 10; + } + + @Override + public void setDescription(Player player, Game game, RelativeLayout descriptionLayout) { + Context c = descriptionLayout.getContext(); + AppCompatTextView textView = new AppCompatTextView(c); + textView.setText("Have you been on drugs? You're making a mess of things!"); + descriptionLayout.addView(textView); + + AppCompatTextView seeAlsoView = new AppCompatTextView(c); + seeAlsoView.setText("\"[...] Participants were tested between 1 and 2h after treatment with oral cocaine (300mg) or placebo. Emotion recognition of low and high intensity expressions of basic emotions (fear, anger, disgust, sadness, and happiness) was tested. Findings show that cocaine impaired recognition of negative emotions; this was mediated by the intensity of the presented emotions.\" (Kuypers, 2015)"); + descriptionLayout.addView(seeAlsoView); + } + }) + ); + add(new Achievement( + "Perfect CEO", + null, + new Achievement.AchievementListener() { + @Override + public boolean hasGotAchievement(Player player, Game game) { + // you score anger, disgust, contempt on moments where it is not needed + for(Hit hit: game.hits.values()) { + if(hit.target.emotion == Emotion.ANGER || hit.target.emotion == Emotion.DISGUST|| hit.target.emotion == Emotion.FEAR){ + continue; + } + if(hit.emotions.get(Emotion.ANGER) > 20 || hit.emotions.get(Emotion.DISGUST) > 20 || hit.emotions.get(Emotion.FEAR) > 20) { + return true; + } + } + return false; + } + + @Override + public void setDescription(Player player, Game game, RelativeLayout descriptionLayout) { + Context c = descriptionLayout.getContext(); + AppCompatTextView textView = new AppCompatTextView(c); + textView.setText("You would be a great CEO! You'll be able to convice shareholders, even if the company is in chaos!"); + descriptionLayout.addView(textView); + + AppCompatTextView seeAlsoView = new AppCompatTextView(c); + seeAlsoView.setText("\"Although fear, anger, and disgust are negative emotions, Dr. Cicon found they correlated positively with financial performance. CEOs whose faces during a media interview showed disgust–as evidenced by lowered eyebrows and eyes, and a closed, pursed mouth–were associated with a 9.3% boost in overall profits in the following quarter. CEOs who expressed fear–raised eyebrows, widened eyes and mouth, and lips pulled in at the corners–saw their companies’ stock rise .4% in the following week.\" (The Wall Street Journal, 17 Feb 2016)"); + descriptionLayout.addView(seeAlsoView); + } + }) + ); + add(new Achievement( + "You've got the job", + null, + new Achievement.AchievementListener() { + @Override + public boolean hasGotAchievement(Player player, Game game) { + // ??? + return false; + } + + @Override + public void setDescription(Player player, Game game, RelativeLayout descriptionLayout) { + Context c = descriptionLayout.getContext(); + AppCompatTextView textView = new AppCompatTextView(c); + textView.setText("..."); + descriptionLayout.addView(textView); + } + }) + ); + add(new Achievement( + "Commercial", + null, + new Achievement.AchievementListener() { + @Override + public boolean hasGotAchievement(Player player, Game game) { + // ??? + return false; + } + + @Override + public void setDescription(Player player, Game game, RelativeLayout descriptionLayout) { + Context c = descriptionLayout.getContext(); + AppCompatTextView textView = new AppCompatTextView(c); + textView.setText("..."); + descriptionLayout.addView(textView); + } + }) + ); + add(new Achievement( + "Emotion work: suppression", + null, + new Achievement.AchievementListener() { + @Override + public boolean hasGotAchievement(Player player, Game game) { + // You score good on the surpressed (<70%) targets (bonus points there!) + int suppressedTotal=0; + int suppressedGood=0; + for(Hit hit: game.hits.values()) { + if(hit.target.value > 70) { + continue; + } + suppressedTotal++; + if(hit.bonus > 10) { + suppressedGood++; + } + } + if(suppressedTotal*0.8 <= suppressedGood) { + return true; + } + return false; + } + + @Override + public void setDescription(Player player, Game game, RelativeLayout descriptionLayout) { + Context c = descriptionLayout.getContext(); + AppCompatTextView textView = new AppCompatTextView(c); + textView.setText("..."); + descriptionLayout.addView(textView); + } + }) + ); + add(new Achievement( + "Emotion work: evocation", + null, + new Achievement.AchievementListener() { + @Override + public boolean hasGotAchievement(Player player, Game game) { + // You score good on the high ranked (>70%) targets (requires bonus points) + int evocedTotal=0; + int evocedGood=0; + for(Hit hit: game.hits.values()) { + if(hit.target.value <= 70) { + continue; + } + evocedTotal++; + if(hit.bonus > 10) { + evocedGood++; + } + } + if(evocedTotal*0.8 <= evocedGood) { + return true; + } + return false; + } + + @Override + public void setDescription(Player player, Game game, RelativeLayout descriptionLayout) { + Context c = descriptionLayout.getContext(); + AppCompatTextView textView = new AppCompatTextView(c); + textView.setText("..."); + descriptionLayout.addView(textView); + } + }) + ); + add(new Achievement( + "The game's on you", // ??(mixed feelings/complex emotions) + null, + new Achievement.AchievementListener() { + @Override + public boolean hasGotAchievement(Player player, Game game) { + // ???? + return false; + } + + @Override + public void setDescription(Player player, Game game, RelativeLayout descriptionLayout) { + Context c = descriptionLayout.getContext(); + AppCompatTextView textView = new AppCompatTextView(c); + textView.setText("..."); + descriptionLayout.addView(textView); + } + }) + ); + add(new Achievement( + "CV dazzler", + null, + new Achievement.AchievementListener() { + @Override + public boolean hasGotAchievement(Player player, Game game) { + // high lostFaceTime + if(game.lostFaceTime > 20) { + return true; + } + return false; + } + + @Override + public void setDescription(Player player, Game game, RelativeLayout descriptionLayout) { + Context c = descriptionLayout.getContext(); + AppCompatTextView textView = new AppCompatTextView(c); + textView.setText("\"CV Dazzle explores how fashion can be used as camouflage from face-detection technology, the first step in automated face recognition. "); + descriptionLayout.addView(textView); + // TODO: add image + AppCompatTextView seeAlsoView = new AppCompatTextView(c); + seeAlsoView.setText("\"The name is derived from a type of World War I naval camouflage called Dazzle, which used cubist-inspired designs to break apart the visual continuity of a battleship and conceal its orientation and size. Likewise, CV Dazzle uses avant-garde hairstyling and makeup designs to break apart the continuity of a face. Since facial-recognition algorithms rely on the identification and spatial relationship of key facial features, like symmetry and tonal contours, one can block detection by creating an “anti-face”.\" (cvdazzle.com)"); + descriptionLayout.addView(seeAlsoView); + // cvdazzle image (something on skincolor? + } + }) + ); + add(new Achievement( + "Kuleshov", + null, + new Achievement.AchievementListener() { + @Override + public boolean hasGotAchievement(Player player, Game game) { + boolean missedATarget = false; + // Add Kuleshov's target... person managers to do it + for(int target_id: game.scenario.kuleshovTargetIds) { + if(game.hits.get(target_id).bonus < 5) { // needs at least a little bonus on all + missedATarget = true; + } + } + return !missedATarget; + } + + @Override + public void setDescription(Player player, Game game, RelativeLayout descriptionLayout) { + Context c = descriptionLayout.getContext(); + AppCompatTextView textView = new AppCompatTextView(c); + textView.setText("In the beginning of the 20th century filmmaker Lev Kuleshov did an experiment\n" + + "for which he edited three sequences. Each sequence showed the same ‘neutral’ face\n" + + "of a man, followed by the image of either a dead man, a plate of soup or a woman.\n" + + "When these sequences were shown, the audience “raved about the acting”(Pudovkin,\n" + + "1974, p. 184), believing the man who ‘looked’ at either the dead man, the soup or\n" + + "the woman, was expressing grief, hunger or desire."); + // TODO: kuleshov image/video? + descriptionLayout.addView(textView); + } + }) + ); + add(new Achievement( + "Soap opera ", + null, + new Achievement.AchievementListener() { + @Override + public boolean hasGotAchievement(Player player, Game game) { + // Confirm exactly (many bonus points?) + return false; + } + + @Override + public void setDescription(Player player, Game game, RelativeLayout descriptionLayout) { + Context c = descriptionLayout.getContext(); + AppCompatTextView textView = new AppCompatTextView(c); + textView.setText("..."); + descriptionLayout.addView(textView); + } + }) + ); + add(new Achievement( + "Stock photo model", + null, + new Achievement.AchievementListener() { + @Override + public boolean hasGotAchievement(Player player, Game game) { + // on average an overexecaration?? + int niceOnes = 0; + for(Hit hit: game.hits.values()) { + if(hit.score >= 80) { + niceOnes ++; + } + } + if(niceOnes >= game.hits.size() * 0.8) { + return true; + } + return false; + } + + @Override + public void setDescription(Player player, Game game, RelativeLayout descriptionLayout) { + Context c = descriptionLayout.getContext(); + AppCompatTextView textView = new AppCompatTextView(c); + textView.setText("You are remarkably good at posing, just like this guy:"); + // TODO: ad MS image + descriptionLayout.addView(textView); + } + }) + ); }}; private static AchievementCollection ourInstance = new AchievementCollection(); diff --git a/app/src/main/java/com/rubenvandeven/emotionhero/ApiRestClient.java b/app/src/main/java/com/rubenvandeven/emotionhero/ApiRestClient.java index c928ded..c050baa 100644 --- a/app/src/main/java/com/rubenvandeven/emotionhero/ApiRestClient.java +++ b/app/src/main/java/com/rubenvandeven/emotionhero/ApiRestClient.java @@ -212,6 +212,7 @@ public class ApiRestClient { DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); df.setTimeZone(tz); j.put("time", df.format(game.time)); + j.put("lost_face_time", game.lostFaceTime); JSONArray jHits = new JSONArray(); j.put("hits", jHits); @@ -251,6 +252,13 @@ public class ApiRestClient { jHits.put(jHit); } + + JSONArray jAchievements = new JSONArray(); + j.put("achievements", jAchievements); + for(Achievement achievement: game.achievements) { + jAchievements.put(achievement.getId()); + } + } catch (JSONException e) { e.printStackTrace(); } @@ -278,7 +286,8 @@ public class ApiRestClient { hit.remoteId = hits.getString(Long.toString(hit.id)); gameHelper.saveRemoteId(hit); } - JSONArray jAchievements = response.getJSONArray("achievements"); + // Achievement checking now in-game. + /*JSONArray jAchievements = response.getJSONArray("achievements"); if(jAchievements.length() > 0){ ArrayList achievements = new ArrayList(jAchievements.length()); for (int ai = 0; ai < jAchievements .length(); ai++) { @@ -286,7 +295,7 @@ public class ApiRestClient { } game.achievements = achievements; gameHelper.saveAchievementsForGame(game); - } + }*/ } catch (JSONException e) { Log.e("API","Invalid data: " + response.toString()); diff --git a/app/src/main/java/com/rubenvandeven/emotionhero/CreditsActivity.java b/app/src/main/java/com/rubenvandeven/emotionhero/CreditsActivity.java index 8bae76e..af27a57 100644 --- a/app/src/main/java/com/rubenvandeven/emotionhero/CreditsActivity.java +++ b/app/src/main/java/com/rubenvandeven/emotionhero/CreditsActivity.java @@ -1,8 +1,14 @@ package com.rubenvandeven.emotionhero; +import android.content.Intent; +import android.graphics.Typeface; +import android.net.Uri; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; +import android.view.View; import android.view.WindowManager; +import android.widget.LinearLayout; +import android.widget.TextView; public class CreditsActivity extends AppCompatActivity { @@ -14,5 +20,64 @@ public class CreditsActivity extends AppCompatActivity { getSupportActionBar().hide(); } setContentView(R.layout.activity_credits); + + Typeface font = Typeface.createFromAsset(getAssets(), "unifont-9.0.02.ttf"); + TextView textView = (TextView) findViewById(R.id.textView); + TextView affectivaPower = (TextView) findViewById(R.id.affectivaPower); + TextView fontInfo = (TextView) findViewById(R.id.fontInfo); + TextView moreInfo = (TextView) findViewById(R.id.moreInfo); + TextView mySite = (TextView) findViewById(R.id.mySite); + textView.setTypeface(font); + affectivaPower.setTypeface(font); + fontInfo.setTypeface(font); + moreInfo.setTypeface(font); + mySite.setTypeface(font); + + findViewById(R.id.logoArquivo).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + String url = "http://www.arquivo237.com"; + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse(url)); + startActivity(i); + } + }); + findViewById(R.id.logoV2).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + String url = "http://www.v2.nl"; + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse(url)); + startActivity(i); + } + }); + findViewById(R.id.logoAffectiva).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + String url = "http://www.affectiva.com"; + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse(url)); + startActivity(i); + } + }); + + moreInfo.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + String url = "http://www.emotionhero.com"; + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse(url)); + startActivity(i); + } + }); + mySite.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + String url = "http://www.rubenvandeven.com"; + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse(url)); + startActivity(i); + } + }); } } diff --git a/app/src/main/java/com/rubenvandeven/emotionhero/EndingActivity.java b/app/src/main/java/com/rubenvandeven/emotionhero/EndingActivity.java new file mode 100644 index 0000000..5c8e150 --- /dev/null +++ b/app/src/main/java/com/rubenvandeven/emotionhero/EndingActivity.java @@ -0,0 +1,91 @@ +package com.rubenvandeven.emotionhero; + +import android.support.v7.app.AppCompatActivity; +import android.os.Bundle; +import android.support.v7.widget.Toolbar; +import android.util.Log; +import android.view.View; +import android.view.WindowManager; +import android.widget.ImageView; +import android.widget.ProgressBar; +import android.widget.TextView; +import android.widget.Toast; + +import com.loopj.android.http.JsonHttpResponseHandler; + +import org.json.JSONException; +import org.json.JSONObject; + +import cz.msebera.android.httpclient.Header; + +public class EndingActivity extends AppCompatActivity { + + Player player; + + TextView textLoading; + TextView textFailure; + TextView textSuccess; + TextView textRank; + TextView rank; + ImageView logoEmotionHero; + ProgressBar scoreProgressBar; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_ending); + + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); + setSupportActionBar(toolbar); + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + getSupportActionBar().setTitle("The End?"); + + textLoading = (TextView) findViewById(R.id.textLoading); + textFailure = (TextView) findViewById(R.id.textFailure); + textSuccess = (TextView) findViewById(R.id.textSuccess); + textRank = (TextView) findViewById(R.id.textRank); + rank = (TextView) findViewById(R.id.rank); + logoEmotionHero = (ImageView) findViewById(R.id.logoEmotionHero); + scoreProgressBar = (ProgressBar) findViewById(R.id.scoreProgressBar); + + player = Player.getInstance(this); + player.api.get("/me", null, new JsonHttpResponseHandler() { + @Override + public void onSuccess(int statusCode, Header[] headers, JSONObject response) { + try { + int rank = response.getInt("rank"); + + setRank(rank); + + } catch (JSONException e) { + e.printStackTrace(); + } + } + @Override + public void onFailure(int statusCode, Header[] headers, String response, Throwable throwable) { + throwable.printStackTrace(); + ApiRestClient.handleOnFailure(statusCode, headers, response, throwable); +// Log.e("API", response == null ? "NULL" : response); + Toast.makeText(getApplicationContext(), "Something went wrong when loading results", Toast.LENGTH_LONG).show(); + scoreProgressBar.setVisibility(View.GONE); + } + }); + } + + public void setRank(int rank) { + textLoading.setVisibility(View.GONE); + scoreProgressBar.setVisibility(View.GONE); + + if(rank == 1) { + textSuccess.setVisibility(View.VISIBLE); + logoEmotionHero.setVisibility(View.VISIBLE); + } else { + textFailure.setVisibility(View.VISIBLE); + } + + this.rank.setText(""+rank); + this.rank.setVisibility(View.VISIBLE); + this.textRank.setVisibility(View.VISIBLE); + } +} diff --git a/app/src/main/java/com/rubenvandeven/emotionhero/Game.java b/app/src/main/java/com/rubenvandeven/emotionhero/Game.java index 60ec414..644e59f 100644 --- a/app/src/main/java/com/rubenvandeven/emotionhero/Game.java +++ b/app/src/main/java/com/rubenvandeven/emotionhero/Game.java @@ -1,9 +1,5 @@ package com.rubenvandeven.emotionhero; -import com.loopj.android.http.JsonHttpResponseHandler; -import com.loopj.android.http.RequestParams; - -import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; @@ -24,6 +20,11 @@ public class Game { public String remoteId; public Map hits = new HashMap<>(); public ArrayList achievements = new ArrayList<>(); + /** + * How long the face has been 'lost' during playing the level + */ + public float lostFaceTime; + public Game(Long id, int lvl_id, float score, float bonus, Date time, String remoteId) { this(id, new Scenario(lvl_id, null), score, bonus, time, remoteId); @@ -59,4 +60,15 @@ public class Game { } return s; } + + /** + * Calculate which achievements fit the game + */ + public void checkAchievements(Player player) { + for(Achievement achievement: scenario.achievements) { + if(achievement.achievementListener.hasGotAchievement(player, this) && !achievements.contains(achievement)) { + achievements.add(achievement); + } + } + } } diff --git a/app/src/main/java/com/rubenvandeven/emotionhero/GameOpenHelper.java b/app/src/main/java/com/rubenvandeven/emotionhero/GameOpenHelper.java index a4ff100..e21196e 100644 --- a/app/src/main/java/com/rubenvandeven/emotionhero/GameOpenHelper.java +++ b/app/src/main/java/com/rubenvandeven/emotionhero/GameOpenHelper.java @@ -23,7 +23,7 @@ import java.util.Date; * adb pull /sdcard/emotionhero.db */ public class GameOpenHelper extends SQLiteOpenHelper { - private static final int DATABASE_VERSION = 5; + private static final int DATABASE_VERSION = 6; private static final String GAME_TABLE_NAME = "games"; private static final String GAME_TABLE_CREATE = "CREATE TABLE " + GAME_TABLE_NAME + " (" + @@ -32,6 +32,7 @@ public class GameOpenHelper extends SQLiteOpenHelper { "score VARCHAR(255) NOT NULL," + "bonus VARCHAR(255) NOT NULL," + "time INTEGER NOT NULL," + + "lost_face_time VARCHAR(255) NOT NULL," + "remoteId VARCHAR(255) DEFAULT NULL, " + "PRIMARY KEY(id));"; private static final String[] GAME_PROJECTION = { @@ -40,6 +41,7 @@ public class GameOpenHelper extends SQLiteOpenHelper { "score", "bonus", "time", + "lost_face_time", "remoteId" }; @@ -206,6 +208,9 @@ public class GameOpenHelper extends SQLiteOpenHelper { if(oldVersion == 4 && newVersion == 5) { db.execSQL(GAME_ACH_TABLE_CREATE); } + if(oldVersion == 5 && newVersion == 6) { + db.execSQL("ALTER TABLE games ADD lost_face_time VARCHAR(255);"); + } } @@ -236,6 +241,7 @@ public class GameOpenHelper extends SQLiteOpenHelper { values.put("score", game.score); values.put("bonus", game.bonus); values.put("time", game.time.getTime()); + values.put("lost_face_time", game.lostFaceTime); if(game.remoteId == null) { values.putNull("remoteId"); } else { @@ -249,6 +255,8 @@ public class GameOpenHelper extends SQLiteOpenHelper { for(Hit hit: game.hits.values()) { insertHit(hit); } + + saveAchievementsForGame(game); } public Game[] getGamesForLevel(int lvl_id) { @@ -277,6 +285,37 @@ public class GameOpenHelper extends SQLiteOpenHelper { return cursorToGames(c, s); } + /** + * Get the game that was played before this game + * @param game + * @return Game + */ + public Game getPreviousGame(Game game) { + SQLiteDatabase db = getReadableDatabase(); + + String selection = "lvl_id = ? AND time < ?"; + String[] selectionArgs = { Integer.toString(game.scenario.id), Long.toString(game.time.getTime()) }; + + String sortOrder = "time DESC"; + + Cursor c = db.query( + GAME_TABLE_NAME, // The table to query + GAME_PROJECTION, // The columns to return + selection, // The columns for the WHERE clause + selectionArgs, // The values for the WHERE clause + null, // don't group the rows + null, // don't filter by row groups + sortOrder, // The sort order + "1" // only last item + ); + + if(c.getCount() < 1) + return null; + + Game[] games = cursorToGames(c, game.scenario); + return games[0]; + } + public Game[] cursorToGames(Cursor c, Scenario s) { Game[] games = new Game[c.getCount()]; int i = 0; @@ -292,6 +331,7 @@ public class GameOpenHelper extends SQLiteOpenHelper { new Date(c.getLong(c.getColumnIndex("time"))), c.getString(c.getColumnIndex("remoteId")) ); + game.lostFaceTime = c.getFloat(c.getColumnIndex("lost_face_time")); games[i] = game; this.getHitsForGame(game); // hits are appended to game object game.achievements = getAchievementsOfGame(game); diff --git a/app/src/main/java/com/rubenvandeven/emotionhero/GamingActivity.java b/app/src/main/java/com/rubenvandeven/emotionhero/GamingActivity.java index 8074b9c..49f1390 100644 --- a/app/src/main/java/com/rubenvandeven/emotionhero/GamingActivity.java +++ b/app/src/main/java/com/rubenvandeven/emotionhero/GamingActivity.java @@ -307,6 +307,7 @@ public class GamingActivity extends AppCompatActivity implements Detector.ImageL public void onFaceDetectionStarted() { setText("START!"); + currentScenario.lostFace = false; if(isStarted) currentScenario.start(); @@ -316,6 +317,7 @@ public class GamingActivity extends AppCompatActivity implements Detector.ImageL public void onFaceDetectionStopped() { currentScenario.pause(); + currentScenario.lostFace = true; } public void setText(String text) @@ -380,7 +382,9 @@ public class GamingActivity extends AppCompatActivity implements Detector.ImageL // check whether this is the highest reached level by the player // ... used by 'continue' button in main menu - if(currentScenario.minimumScore < currentScenario.game.score) { + currentScenario.game.checkAchievements(player); + + if(currentScenario.minimumScore < currentScenario.game.score + currentScenario.game.bonus) { if(currentScenario.isFinalLevel()) { playerInfo.completedAll = true; } else { diff --git a/app/src/main/java/com/rubenvandeven/emotionhero/IntroductionActivity.java b/app/src/main/java/com/rubenvandeven/emotionhero/IntroductionActivity.java deleted file mode 100644 index 9eab55f..0000000 --- a/app/src/main/java/com/rubenvandeven/emotionhero/IntroductionActivity.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.rubenvandeven.emotionhero; - -import android.content.Intent; -import android.graphics.Typeface; -import android.support.v7.app.AppCompatActivity; -import android.os.Bundle; -import android.support.v7.widget.Toolbar; -import android.view.View; -import android.view.WindowManager; -import android.widget.TextView; - -/** - * Not to be confused with IntroActivity: - * this is the first game introduction (explaining the procedure etc.) - */ -public class IntroductionActivity extends AppCompatActivity { - - TextView nextButton; - TextView readyButton; - TextView text1; - TextView text2; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_introduction); - - - nextButton = (TextView) findViewById(R.id.nextButton); - readyButton = (TextView) findViewById(R.id.readyButton); - text1 = (TextView) findViewById(R.id.introText1); - text2 = (TextView) findViewById(R.id.introText2); - - Typeface font = Typeface.createFromAsset(getAssets(), "unifont-9.0.02.ttf"); - nextButton.setTypeface(font); - readyButton.setTypeface(font); - - nextButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - nextButton.setVisibility(View.GONE); - readyButton.setVisibility(View.VISIBLE); - text1.setVisibility(View.GONE); - text2.setVisibility(View.VISIBLE); - } - }); - - readyButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Intent intent = new Intent(IntroductionActivity.this, ProgressActivity.class); - finish(); - startActivity(intent); - } - }); - } -} diff --git a/app/src/main/java/com/rubenvandeven/emotionhero/MenuActivity.java b/app/src/main/java/com/rubenvandeven/emotionhero/MenuActivity.java deleted file mode 100644 index 311f84a..0000000 --- a/app/src/main/java/com/rubenvandeven/emotionhero/MenuActivity.java +++ /dev/null @@ -1,137 +0,0 @@ -package com.rubenvandeven.emotionhero; - -import android.animation.AnimatorSet; -import android.animation.ObjectAnimator; -import android.app.ProgressDialog; -import android.content.Intent; -import android.support.v7.app.AppCompatActivity; -import android.os.Bundle; -import android.view.Menu; -import android.view.View; -import android.view.WindowManager; -import android.view.animation.AlphaAnimation; -import android.view.animation.Animation; -import android.widget.Button; -import android.widget.ImageView; -import android.widget.Toast; - -/** - * @deprecated Now done using MirrorMenuActivity - */ -public class MenuActivity extends AppCompatActivity { - - Player player; - - ProgressDialog loadGameDialog; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); - if(getSupportActionBar() != null) { - getSupportActionBar().hide(); - } - setContentView(R.layout.activity_menu); - - player = Player.getInstance(getApplicationContext()); - - Button startButton = (Button) findViewById(R.id.startButton); - Button continueButton = (Button) findViewById(R.id.continueButton); - Button highscoreButton = (Button) findViewById(R.id.highscoresButton); - Button creditsButton = (Button) findViewById(R.id.creditsButton); - - - final ImageView logoEmotionHero = (ImageView) findViewById(R.id.logoEmotionHero); - - if(!player.getPlayerInfo().canContinueLevel()) { - continueButton.setVisibility(View.GONE); - } - - // prepare for animation - startButton.setAlpha(0f); - continueButton.setAlpha(0f); - highscoreButton.setAlpha(0f); - creditsButton.setAlpha(0f); - - ObjectAnimator fadeInEmotionHero = ObjectAnimator.ofFloat(logoEmotionHero, "alpha", 0f, 1f); - fadeInEmotionHero.setDuration(1000); - - ObjectAnimator fadeInstartButton = ObjectAnimator.ofFloat(startButton, "alpha", 0f, 1f); - fadeInstartButton.setDuration(1000); - ObjectAnimator fadeInContinueButton = ObjectAnimator.ofFloat(continueButton, "alpha", 0f, 1f); - fadeInContinueButton.setDuration(1000); - fadeInContinueButton.setStartDelay(300); - ObjectAnimator fadeInHighscoreButton = ObjectAnimator.ofFloat(highscoreButton, "alpha", 0f, 1f); - fadeInHighscoreButton.setDuration(1000); - fadeInHighscoreButton.setStartDelay(600); - ObjectAnimator fadeInCreditsButton = ObjectAnimator.ofFloat(creditsButton, "alpha", 0f, 1f); - fadeInCreditsButton.setDuration(1000); - fadeInCreditsButton.setStartDelay(1000); - - - Animation anim = new AlphaAnimation(0.0f, 1.0f); - anim.setDuration(1000); //You can manage the blinking time with this parameter - anim.setStartOffset(20); - anim.setRepeatMode(Animation.REVERSE); - anim.setRepeatCount(Animation.INFINITE); - logoEmotionHero.startAnimation(anim); - - AnimatorSet buttonAnimatorSet = new AnimatorSet(); - buttonAnimatorSet.playTogether(fadeInstartButton, - fadeInContinueButton, - fadeInHighscoreButton, - fadeInCreditsButton); - buttonAnimatorSet.start(); - - - - startButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - loadGameDialog = ProgressDialog.show(MenuActivity.this, "", - MenuActivity.this.getResources().getString(R.string.load_game_activity), true); - Intent intent = new Intent(MenuActivity.this, MirrorMenuActivity.class); - startActivity(intent); - - } - }); - - continueButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - loadGameDialog= ProgressDialog.show(MenuActivity.this, "", - MenuActivity.this.getResources().getString(R.string.load_game_activity), true); - Intent intent = new Intent(MenuActivity.this, GamingActivity.class); - intent.putExtra(GamingActivity.INTENT_EXTRA_SCENARIO, player.getPlayerInfo().reachedLevelId); - startActivity(intent); - } - }); - - highscoreButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Intent intent = new Intent(MenuActivity.this, HighscoreActivity.class); -// intent.putExtra(HighscoreActivity.INTENT_EXTRA_LEVEL, player.getPlayerInfo().reachedLevelId); - startActivity(intent); - } - }); - - creditsButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Intent intent = new Intent(MenuActivity.this, CreditsActivity.class); - startActivity(intent); - } - }); - - - - } - - @Override - protected void onResume() { - super.onResume(); - if(loadGameDialog != null) - loadGameDialog.dismiss(); - } -} diff --git a/app/src/main/java/com/rubenvandeven/emotionhero/ProgressActivity.java b/app/src/main/java/com/rubenvandeven/emotionhero/ProgressActivity.java index a6ab7b1..7ffcc42 100644 --- a/app/src/main/java/com/rubenvandeven/emotionhero/ProgressActivity.java +++ b/app/src/main/java/com/rubenvandeven/emotionhero/ProgressActivity.java @@ -179,7 +179,9 @@ public class ProgressActivity extends AppCompatActivity { outroText.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - // TODO: .. handle onclick!! + Intent intent = new Intent(getApplicationContext(), EndingActivity.class); + finish(); + startActivity(intent); } }); } diff --git a/app/src/main/java/com/rubenvandeven/emotionhero/ReviewAchievementsActivity.java b/app/src/main/java/com/rubenvandeven/emotionhero/ReviewAchievementsActivity.java index 7ef1866..698af47 100644 --- a/app/src/main/java/com/rubenvandeven/emotionhero/ReviewAchievementsActivity.java +++ b/app/src/main/java/com/rubenvandeven/emotionhero/ReviewAchievementsActivity.java @@ -8,6 +8,7 @@ import android.util.Log; import android.view.WindowManager; import android.widget.ImageView; import android.widget.LinearLayout; +import android.widget.RelativeLayout; import android.widget.TextView; public class ReviewAchievementsActivity extends AppCompatActivity { @@ -72,6 +73,7 @@ public class ReviewAchievementsActivity extends AppCompatActivity { public void addAchievement(Achievement achievement) { AppCompatTextView titleText = new AppCompatTextView(this); AppCompatTextView descriptionText = new AppCompatTextView(this); + RelativeLayout descriptionLayout = new RelativeLayout(this); // ImageView imageView = new ImageView(this); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); @@ -82,13 +84,13 @@ public class ReviewAchievementsActivity extends AppCompatActivity { titleText.setTextSize(getResources().getDimensionPixelSize(R.dimen.highscore_textsize)); titleText.setLayoutParams(params); + descriptionLayout.setLayoutParams(params); + descriptionLayout.setPadding(0,0,0, 20); + achievement.achievementListener.setDescription(player, game, descriptionLayout); - descriptionText.setText(achievement.description); - descriptionText.setTextColor(getResources().getColor(R.color.textPrimary)); - descriptionText.setLayoutParams(params); achievementsLayout.addView(titleText); - achievementsLayout.addView(descriptionText); + achievementsLayout.addView(descriptionLayout); } } diff --git a/app/src/main/java/com/rubenvandeven/emotionhero/ReviewActivity.java b/app/src/main/java/com/rubenvandeven/emotionhero/ReviewActivity.java index 9da8763..e8617e6 100644 --- a/app/src/main/java/com/rubenvandeven/emotionhero/ReviewActivity.java +++ b/app/src/main/java/com/rubenvandeven/emotionhero/ReviewActivity.java @@ -51,6 +51,7 @@ public class ReviewActivity extends AppCompatActivity { TextView achievementArrow; TextView achievementText; TextView improveArrow; + RelativeLayout improveLayout; TextView hintText; RelativeLayout retryLayout; TextView retryArrow; @@ -89,7 +90,7 @@ public class ReviewActivity extends AppCompatActivity { // TODO: pass the game to checkAchievements() to avoid crash on null error // means we have to load Player.getInstance form here and load the game // from given id to be save. - checkAchievements(); +// checkAchievements(); } catch (JSONException e) { e.printStackTrace(); Toast.makeText(getApplicationContext(), "Something went wrong when loading results", Toast.LENGTH_LONG).show(); @@ -139,7 +140,9 @@ public class ReviewActivity extends AppCompatActivity { } } else { // no game specified??? SHould not be possible so finish - finish(); + // This happens when comming back. + // apparently back button does not acknowledge intent?? +// finish(); // doesn't work? } lvlNameText = (TextView) findViewById(R.id.lvlNameText); @@ -153,7 +156,8 @@ public class ReviewActivity extends AppCompatActivity { achievementArrow = (TextView) findViewById(R.id.achievementArrow); achievementText = (TextView) findViewById(R.id.achievementText); - improveArrow = (TextView) findViewById(R.id.improveArrow); +// improveLayout = (RelativeLayout) findViewById(R.id.improveLayout); +// improveArrow = (TextView) findViewById(R.id.improveArrow); hintText = (TextView) findViewById(R.id.hintText); retryLayout = (RelativeLayout) findViewById(R.id.retryLayout); retryArrow = (TextView) findViewById(R.id.retryArrow); @@ -165,6 +169,9 @@ public class ReviewActivity extends AppCompatActivity { scoreProgressBar = (ProgressBar) findViewById(R.id.scoreProgressBar); + nextLvlText.setVisibility(View.GONE); + nextLvlLayout.setVisibility(View.GONE); + checkAchievements(); Typeface font = Typeface.createFromAsset(getAssets(), "unifont-9.0.02.ttf"); @@ -173,7 +180,7 @@ public class ReviewActivity extends AppCompatActivity { achievementArrow.setTypeface(font); achievementText.setTypeface(font); retryArrow.setTypeface(font); - improveArrow.setTypeface(font); +// improveArrow.setTypeface(font); nextLvlArrow.setTypeface(font); lvlNameText.setText("\""+game.scenario.toString()+"\""); @@ -196,23 +203,6 @@ public class ReviewActivity extends AppCompatActivity { } }); - if(player.getPlayerInfo().hasAccessToLevel(game.scenario.getNextLevelId())) { - nextLvlLayout.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Intent intent = new Intent(getApplicationContext(), GamingActivity.class); - intent.putExtra(GamingActivity.INTENT_EXTRA_SCENARIO, game.scenario.getNextLevelId()); - ProgressDialog dialog = ProgressDialog.show(ReviewActivity.this, "", - getApplicationContext().getResources().getString(R.string.load_game_activity), true); - finish(); - startActivity(intent); - } - }); - } else { - nextLvlText.setVisibility(View.GONE); - nextLvlLayout.setVisibility(View.GONE); - } - int rank = player.getGameOpenHelper().getLocalRankOfGame(game); String personalScoreJudgement = rank < 7 ? "nice work!" : "you can do better."; @@ -220,18 +210,51 @@ public class ReviewActivity extends AppCompatActivity { retryText.setText(retryString); } + public void setNextLvlMessage(boolean hasAccess) { + nextLvlText.setVisibility(View.VISIBLE); + nextLvlLayout.setVisibility(View.VISIBLE); + if(hasAccess) { + if(game.scenario.isFinalLevel()) { + nextLvlText.setText("This was the final level! Let's see what the ending brings..."); + nextLvlLayout.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent intent = new Intent(getApplicationContext(), EndingActivity.class); + finish(); + startActivity(intent); + } + }); + } else { + nextLvlLayout.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent intent = new Intent(getApplicationContext(), GamingActivity.class); + intent.putExtra(GamingActivity.INTENT_EXTRA_SCENARIO, game.scenario.getNextLevelId()); + ProgressDialog dialog = ProgressDialog.show(ReviewActivity.this, "", + getApplicationContext().getResources().getString(R.string.load_game_activity), true); + finish(); + startActivity(intent); + } + }); + } + } else { + nextLvlText.setText(String.format("You need at least %1$.0f points, or unlock %2$d achievements to get to the next level.", game.scenario.minimumScore, game.scenario.minimumAchievements)); + nextLvlArrow.setVisibility(View.GONE); + } + } + public void checkAchievements() { if(game.achievements != null && game.achievements.size() > 0) { achievementTitle.setVisibility(View.VISIBLE); achievementLayout.setVisibility(View.VISIBLE); - String achievementString = game.achievements.size() > 1 ? "You've obtained special achievements!\n" : "You've obtained the special achievement "; + String achievementString = game.achievements.size() > 1 ? "You've obtained special achievements!\n - " : "You've obtained the special achievement "; boolean firstAchievement = true; for(Achievement achievement: game.achievements) { if(firstAchievement ) { firstAchievement = false; } else { - achievementString += "\n"; + achievementString += "\n - "; } achievementString += String.format("%1$s", achievement.title); } @@ -248,6 +271,31 @@ public class ReviewActivity extends AppCompatActivity { achievementTitle.setVisibility(View.GONE); achievementLayout.setVisibility(View.GONE); } + + // only check after sync has been done (so achievements is [] rather than null)) + if(game.achievements != null) { + // check next level access, based on achievements: + int nextLevelId = game.scenario.getNextLevelId(); + PlayerInfo playerInfo = player.getPlayerInfo(); + // if s/he has access, there is nothing to check.. if not.. go ahead + if(!playerInfo.hasAccessToLevel(nextLevelId)) { + int lvlAchievementCount = player.getGameOpenHelper().countAchievementsForLevel(game.scenario.id); + if(lvlAchievementCount >= game.scenario.minimumAchievements) { + playerInfo.reachedLevelId = nextLevelId; + player.savePlayerInfo(playerInfo); + } else { + // only reveal message if there is results + setNextLvlMessage(false); + return; + } + } else { + if(game.scenario.isFinalLevel() && !playerInfo.completedAll) { + setNextLvlMessage(false); + } else{ + setNextLvlMessage(true); + } + } + } } /** diff --git a/app/src/main/java/com/rubenvandeven/emotionhero/Scenario.java b/app/src/main/java/com/rubenvandeven/emotionhero/Scenario.java index b101a1a..624be62 100644 --- a/app/src/main/java/com/rubenvandeven/emotionhero/Scenario.java +++ b/app/src/main/java/com/rubenvandeven/emotionhero/Scenario.java @@ -37,12 +37,22 @@ public class Scenario { public static final int LVL_SURPRISE = 3; public static final int LVL_SADDNESS= 4; + public static final int LVL_THESEVEN = 5; + public static final int LVL_SMILE = 6; + public static final int LVL_BUSINESS = 7; + public static final int LVL_REALLY = 8; + public static final int LVL_ACTNORMAL = 9; + + // used for the kuleshov targets + public int[] kuleshovTargetIds; + // the levels in the right order. public static final ArrayList SCENARIOS = new ArrayList() {{ - add(LVL_JOY); - add(LVL_ANGER); - add(LVL_SADDNESS); - add(LVL_SURPRISE); + add(LVL_THESEVEN); + add(LVL_SMILE); + add(LVL_BUSINESS); + add(LVL_REALLY); + add(LVL_ACTNORMAL); }}; static int DESIRED_FPS = 25; @@ -59,7 +69,7 @@ public class Scenario { * Number of achievements required to have, before being able to go to next level * (check minimumScore || minimumAchievements) */ - float minimumAchievements= 0; + int minimumAchievements= 0; /** * Achievements one get obtain in this level @@ -97,6 +107,14 @@ public class Scenario { protected Paint squarePaint; + protected int bitmapCount = 0; + + /** + * LostFace starts false, stays false when face is found, the becomes + * true when losing the face + */ + protected boolean lostFace = false; + /** * The scorres in this moment, as to draw them on the screen. * Indexes are Emotion ordinals @@ -154,8 +172,12 @@ public class Scenario { */ public void tick() { - if(!isRunning) + if(!isRunning){ + if(lostFace) { + game.lostFaceTime += 1.0f/DESIRED_FPS; + } return; + } runningTime += 1.0f/DESIRED_FPS; @@ -186,10 +208,11 @@ public class Scenario { Bitmap outputBitmap = null; - if(currentFrame != null) { + if(currentFrame != null && bitmapCount < 5) { // convert NV21 byteArrayFrame from camera to RGB bitmap. Frame.ByteArrayFrame byteArrayFrame = (Frame.ByteArrayFrame) currentFrame; outputBitmap = Nv21Image.nv21ToBitmap(rs, byteArrayFrame.getByteArray(), byteArrayFrame.getWidth(), byteArrayFrame.getHeight()); + bitmapCount++; Frame.ROTATE rotation = byteArrayFrame.getTargetRotation(); // ie BY_90_CCW -90.0 Log.v("Scenario", "frame rotation: " + rotation.toString() + " " + rotation.toDouble()); @@ -199,6 +222,7 @@ public class Scenario { matrix.postRotate((int) rotation.toDouble()); // int width = rotation == Frame.ROTATE.BY_180 ? outputBitmap.getWidth() : outputBitmap.getHeight(); // int height = rotation == Frame.ROTATE.BY_180 ? outputBitmap.getHeight() : outputBitmap.getWidth(); + // TODO: max 5 bitmaps outputBitmap = Bitmap.createBitmap(outputBitmap , 0, 0, outputBitmap .getWidth(), outputBitmap .getHeight(), matrix, true); } @@ -236,7 +260,7 @@ public class Scenario { * @param value * @param timestamp */ - public void setTarget(Emotion emotion, float value, float timestamp) + public int setTarget(Emotion emotion, float value, float timestamp) { // Log.e(GamingActivity.LOG_TAG, "Set target:" + Float.toString(timestamp) + " " + Float.toString(duration)); if((timestamp + 1) > duration) @@ -250,6 +274,8 @@ public class Scenario { target.index = targets.size() + 1; targets.add(target); maxScore = getMaxScore(); + + return targets.indexOf(target); } /** @@ -449,6 +475,270 @@ public class Scenario { setMinimumScoreFromPercentage(10); minimumAchievements = 2; break; + ////////////////// new levels: + case LVL_THESEVEN: + int i = 2; + setTarget(Emotion.JOY, 100, i++); + setTarget(Emotion.JOY, 100, i++); + setTarget(Emotion.JOY, 100, i++); + i++; + i++; + setTarget(Emotion.SADNESS, 100, i++); + setTarget(Emotion.SADNESS, 100, i++); + setTarget(Emotion.SADNESS, 100, i++); + i++; + i++; + setTarget(Emotion.ANGER, 100, i++); + setTarget(Emotion.ANGER, 100, i++); + setTarget(Emotion.ANGER, 100, i++); + i++; + i++; + setTarget(Emotion.SURPRISE, 100, i++); + setTarget(Emotion.SURPRISE, 100, i++); + setTarget(Emotion.SURPRISE, 100, i++); + i++; + i++; + setTarget(Emotion.DISGUST, 100, i++); + setTarget(Emotion.DISGUST, 100, i++); + setTarget(Emotion.DISGUST, 100, i++); + i++; + i++; + setTarget(Emotion.CONTEMPT, 100, i++); + setTarget(Emotion.CONTEMPT, 100, i++); + setTarget(Emotion.CONTEMPT, 100, i++); + i++; + i++; + setTarget(Emotion.FEAR, 100, i++); + setTarget(Emotion.FEAR, 100, i++); + setTarget(Emotion.FEAR, 100, i++); + + + setMinimumScoreFromPercentage(40); + minimumAchievements = 2; + achievements.add(achievementCollection.get(1)); + achievements.add(achievementCollection.get(2)); + achievements.add(achievementCollection.get(3)); + achievements.add(achievementCollection.get(4)); + break; + case LVL_SMILE: + int s = 2; + setTarget(Emotion.JOY, 100, s++); + setTarget(Emotion.JOY, 100, s++); + setTarget(Emotion.JOY, 50, s++); + setTarget(Emotion.JOY, 50, s++); + setTarget(Emotion.JOY, 30, s++); + setTarget(Emotion.JOY, 30, s++); + setTarget(Emotion.JOY, 50, s++); + setTarget(Emotion.JOY, 50, s++); + setTarget(Emotion.JOY, 100, s++); + setTarget(Emotion.JOY, 100, s++); + + s++; + s++; + + setTarget(Emotion.SURPRISE, 100, s++); + setTarget(Emotion.SURPRISE, 100, s++); + setTarget(Emotion.SURPRISE, 50, s++); + setTarget(Emotion.SURPRISE, 50, s++); + setTarget(Emotion.SURPRISE, 100, s++); + setTarget(Emotion.SURPRISE, 100, s++); + + s++; + s++; + s++; + setTarget(Emotion.JOY, 100, s++); + setTarget(Emotion.SURPRISE, 100, s); + setTarget(Emotion.JOY, 100, s++); + setTarget(Emotion.SURPRISE, 100, s); + setTarget(Emotion.JOY, 100, s++); + setTarget(Emotion.SURPRISE, 50, s); + setTarget(Emotion.SURPRISE, 30, s); + + s++; + s++; + setTarget(Emotion.JOY, 100, s++); + setTarget(Emotion.ANGER, 1, s); + setTarget(Emotion.CONTEMPT, 1, s); + setTarget(Emotion.DISGUST, 1, s); + + s++; + s++; + setTarget(Emotion.DISGUST, 1, s++); + setTarget(Emotion.ANGER, 1, s); + setTarget(Emotion.SURPRISE, 100, s); + + setMinimumScoreFromPercentage(40); + minimumAchievements = 2; + achievements.add(achievementCollection.get(5)); + achievements.add(achievementCollection.get(6)); + achievements.add(achievementCollection.get(7)); + break; + case LVL_BUSINESS: + int b = 2; + + setTarget(Emotion.ANGER, 100, b++); + setTarget(Emotion.ANGER, 100, b++); + setTarget(Emotion.ANGER, 50, b++); + setTarget(Emotion.ANGER, 50, b++); + setTarget(Emotion.ANGER, 30, b++); + setTarget(Emotion.ANGER, 30, b++); + b++; + setTarget(Emotion.DISGUST, 100, b++); + setTarget(Emotion.DISGUST, 50, b++); + setTarget(Emotion.DISGUST, 50, b++); + setTarget(Emotion.DISGUST, 30, b++); + b++; + setTarget(Emotion.CONTEMPT, 100, b++); + setTarget(Emotion.CONTEMPT, 50, b++); + setTarget(Emotion.CONTEMPT, 50, b++); + setTarget(Emotion.CONTEMPT, 30, b++); + b++; + setTarget(Emotion.SURPRISE, 100, b++); + + setTarget(Emotion.SURPRISE, 100, b++); + setTarget(Emotion.SADNESS, 50, b); + + setTarget(Emotion.JOY, 50, b++); + setTarget(Emotion.DISGUST, 50, b++); + setTarget(Emotion.CONTEMPT, 30, b++); + setTarget(Emotion.CONTEMPT, 100, b++); + setTarget(Emotion.JOY, 100, b++); + setTarget(Emotion.SURPRISE, 100, b++); + + b++; + setTarget(Emotion.SADNESS, 100, b++); + setTarget(Emotion.ANGER, 10, b++); + setTarget(Emotion.SADNESS, 100, b++); + setTarget(Emotion.ANGER, 10, b++); + setTarget(Emotion.SADNESS, 100, b++); + setTarget(Emotion.ANGER, 10, b++); + + + setMinimumScoreFromPercentage(40); + minimumAchievements = 2; + achievements.add(achievementCollection.get(8)); + achievements.add(achievementCollection.get(9)); + achievements.add(achievementCollection.get(10)); + break; + case LVL_REALLY: + int r = 2; + setTarget(Emotion.FEAR, 100, r++); + setTarget(Emotion.FEAR, 50, r++); + setTarget(Emotion.FEAR, 30, r++); + + r++; + setTarget(Emotion.SADNESS, 100, r++); + setTarget(Emotion.SADNESS, 50, r++); + setTarget(Emotion.SADNESS, 100, r++); + + r++; + setTarget(Emotion.DISGUST, 100, r++); + setTarget(Emotion.DISGUST, 100, r++); + setTarget(Emotion.DISGUST, 50, r++); + + r++; + setTarget(Emotion.DISGUST, 100, r++); + setTarget(Emotion.CONTEMPT, 30, r); + setTarget(Emotion.DISGUST, 50, r++); + setTarget(Emotion.CONTEMPT, 50, r); + setTarget(Emotion.DISGUST, 30, r++); + setTarget(Emotion.CONTEMPT, 100, r); + + r++; + r++; + setTarget(Emotion.SADNESS, 100, r++); + setTarget(Emotion.JOY, 50, r++); + setTarget(Emotion.SADNESS, 100, r++); + setTarget(Emotion.JOY, 50, r++); + setTarget(Emotion.SADNESS, 50, r++); + setTarget(Emotion.JOY, 100, r++); + setTarget(Emotion.SADNESS, 50, r++); + setTarget(Emotion.JOY, 100, r); + setTarget(Emotion.SADNESS, 100, r++); + setTarget(Emotion.JOY, 50, r); + + + setMinimumScoreFromPercentage(40); + minimumAchievements = 2; + achievements.add(achievementCollection.get(8)); + achievements.add(achievementCollection.get(9)); + achievements.add(achievementCollection.get(10)); + achievements.add(achievementCollection.get(11)); + + break; + case LVL_ACTNORMAL: + float a = 2; + setTarget(Emotion.ANGER, 100, a++); + setTarget(Emotion.DISGUST, 100, a++); + setTarget(Emotion.FEAR, 100, a++); + setTarget(Emotion.JOY, 100, a++); + setTarget(Emotion.SADNESS, 100, a++); + setTarget(Emotion.SURPRISE, 100, a++); + + a++; + setTarget(Emotion.ANGER, 30, a++); + setTarget(Emotion.DISGUST, 30, a+=0.5); + setTarget(Emotion.FEAR, 30, a+=0.5); + setTarget(Emotion.JOY, 30, a+=0.5); + setTarget(Emotion.SADNESS, 30, a+=0.5); + setTarget(Emotion.SURPRISE, 30, a+=0.5); + + a++; + setTarget(Emotion.ANGER, 30, a++); + setTarget(Emotion.DISGUST, 30, a); + setTarget(Emotion.ANGER, 30, a++); + setTarget(Emotion.DISGUST, 30, a); + + setTarget(Emotion.FEAR, 30, a++); + setTarget(Emotion.JOY, 30, a); + setTarget(Emotion.FEAR, 30, a++); + setTarget(Emotion.JOY, 30, a); + + setTarget(Emotion.SADNESS, 30, a++); + setTarget(Emotion.SURPRISE, 30, a); + setTarget(Emotion.SADNESS, 30, a++); + setTarget(Emotion.SURPRISE, 30, a); + + a++; + a++; + setTarget(Emotion.DISGUST, 20, a++); + setTarget(Emotion.CONTEMPT, 20, a); + setTarget(Emotion.SURPRISE, 20, a); + + setTarget(Emotion.DISGUST, 10, a++); + setTarget(Emotion.CONTEMPT, 10, a); + setTarget(Emotion.SURPRISE, 10, a); + + setTarget(Emotion.DISGUST, 5, a++); + setTarget(Emotion.CONTEMPT, 5, a); + setTarget(Emotion.SURPRISE, 5, a); + + // Kuleshov's target: + kuleshovTargetIds = new int[3]; + kuleshovTargetIds[0] = setTarget(Emotion.DISGUST, 1, a++); + kuleshovTargetIds[1] = setTarget(Emotion.CONTEMPT, 1, a); + kuleshovTargetIds[2] = setTarget(Emotion.SURPRISE, 1, a); + // END OF KULESHOV + + setTarget(Emotion.DISGUST, 1, a++); + setTarget(Emotion.CONTEMPT, 1, a); + setTarget(Emotion.SURPRISE, 1, a); + + + setTarget(Emotion.ANGER, 1, a++); + setTarget(Emotion.CONTEMPT, 1, a); + setTarget(Emotion.DISGUST, 1, a); + setTarget(Emotion.FEAR, 1, a); + setTarget(Emotion.JOY, 1, a); + setTarget(Emotion.SADNESS, 1, a); + setTarget(Emotion.SURPRISE, 1, a); + + setMinimumScoreFromPercentage(40); + minimumAchievements = 2; + achievements.add(achievementCollection.get(12)); + achievements.add(achievementCollection.get(13)); + achievements.add(achievementCollection.get(14)); + break; } } @@ -494,6 +784,17 @@ public class Scenario { return "\"Let's talk business\""; case LVL_SADDNESS: return "A sad sad situation"; + // new: + case LVL_THESEVEN: + return "Welcome to the seven"; + case LVL_SMILE: + return "\"Smile like you mean it\""; + case LVL_BUSINESS: + return "\"Let's talk business\""; + case LVL_REALLY: + return "Show me what you really feel"; + case LVL_ACTNORMAL: + return "Please act normally"; } return "..."; } diff --git a/app/src/main/java/com/rubenvandeven/emotionhero/ScenarioView.java b/app/src/main/java/com/rubenvandeven/emotionhero/ScenarioView.java index 8b452c0..7367627 100644 --- a/app/src/main/java/com/rubenvandeven/emotionhero/ScenarioView.java +++ b/app/src/main/java/com/rubenvandeven/emotionhero/ScenarioView.java @@ -43,6 +43,7 @@ public class ScenarioView extends SurfaceView implements SurfaceHolder.Callback private Paint attrScorePaint = new Paint(); private Paint attrScoreLinePaint = new Paint(); private Paint linePaint = new Paint(); + private Paint missedPaint = new Paint(); private Paint scorePaint = new Paint(); private Paint bonusScorePaint = new Paint(); private Paint scoreFinishedPaint = new Paint(); @@ -103,6 +104,8 @@ public class ScenarioView extends SurfaceView implements SurfaceHolder.Callback getHolder().addCallback(this); _scenario = s; + Typeface font = Typeface.createFromAsset(context.getAssets(), "unifont-9.0.02.ttf"); + scorePaint.setColor(Color.YELLOW); scorePaint.setTextSize(40); scorePaint.setTypeface(Typeface.DEFAULT_BOLD); @@ -124,6 +127,7 @@ public class ScenarioView extends SurfaceView implements SurfaceHolder.Callback linePaint.setColor(Color.GRAY); linePaint.setStrokeWidth(5); attrScorePaint.setColor(Color.GRAY); + missedPaint.setColor(Color.DKGRAY); attrScoreLinePaint.setColor(Color.DKGRAY); attrScoreLinePaint.setStrokeWidth(2); @@ -131,6 +135,7 @@ public class ScenarioView extends SurfaceView implements SurfaceHolder.Callback for(Emotion emotion: Emotion.values()) { Paint emoPaint = new Paint(); emoPaint.setTextSize(22); +// emoPaint.setTypeface(font);//gets ugly when transformed emoPaint.setColor(emotion.getColor()); // emoPaint.setShadowLayer(10,0,0,Color.BLACK); emoPaints.put(emotion, emoPaint); @@ -138,7 +143,7 @@ public class ScenarioView extends SurfaceView implements SurfaceHolder.Callback Paint emoPaintOutline = new Paint(); emoPaintOutline.setColor(emotion.getColor()); emoPaintOutline.setStyle(Paint.Style.STROKE); - emoPaintOutline.setStrokeWidth(3); + emoPaintOutline.setStrokeWidth(5); // emoPaintOutline.setShadowLayer(10,0,0,Color.BLACK); emoOutlinePaints.put(emotion, emoPaintOutline); @@ -241,7 +246,6 @@ public class ScenarioView extends SurfaceView implements SurfaceHolder.Callback continue; } - // Paint targetPaint = (target.hit == null) ? emoPaints.get(target.emotion) : emoScoredPaints.get(target.emotion); float cy = bottomline_height - (target.timestamp * sec_height) + diff_y; if(cy < (-1 * canvas.getHeight()) ) { @@ -258,19 +262,8 @@ public class ScenarioView extends SurfaceView implements SurfaceHolder.Callback canvas.drawCircle(cx, cy, max_ball_radius * hit.score/100, emoScoredPaints.get(target.emotion)); } - - // String target_text = target.emotion.toString() + " " + target.value + "%"; - // canvas.drawText(target_text, cx, y_pos + diff_y , emoPaint); } - // draw the hit middle bottom; -// Paint usedScorePaint = _scenario.isFinished() ? scoreFinishedPaint : scorePaint; -// String scoreText = String.format("Total: %1$.0f", _scenario.getHitTotalValue()); -// Rect scoreTextBounds = new Rect(); -// usedScorePaint.getTextBounds(scoreText, 0, scoreText.length(), scoreTextBounds); - -// canvas.drawText(scoreText, (width - scoreTextBounds.width()) / 2, height - 10, usedScorePaint); - canvas.restore(); float hitWidth = width*(_scenario.getHitPercentage()/100); @@ -297,7 +290,7 @@ public class ScenarioView extends SurfaceView implements SurfaceHolder.Callback canvas.drawLine(stepSize*i, height*0.95f, stepSize*i, height, attrScoreLinePaint); } canvas.drawRect(0, height*0.95f, hitWidth, height, emoPaints.get(Emotion.JOY)); // paint: yellow - canvas.drawRect(hitWidth, height*0.95f, hitWidth+misWidth, height, attrScorePaint); + canvas.drawRect(hitWidth, height*0.95f, hitWidth+misWidth, height, missedPaint); canvas.drawRect(0, height*0.95f, bonusWidth, height*0.975f, bonusBarPaint); diff --git a/app/src/main/res/drawable/affectiva_logo.png b/app/src/main/res/drawable/affectiva_logo.png index 298f182..88411f2 100644 Binary files a/app/src/main/res/drawable/affectiva_logo.png and b/app/src/main/res/drawable/affectiva_logo.png differ diff --git a/app/src/main/res/drawable/arquivo237_logo.png b/app/src/main/res/drawable/arquivo237_logo.png index 518693e..631539d 100644 Binary files a/app/src/main/res/drawable/arquivo237_logo.png and b/app/src/main/res/drawable/arquivo237_logo.png differ diff --git a/app/src/main/res/drawable/v2_logo.png b/app/src/main/res/drawable/v2_logo.png index 09fe05f..f3dcd12 100644 Binary files a/app/src/main/res/drawable/v2_logo.png and b/app/src/main/res/drawable/v2_logo.png differ diff --git a/app/src/main/res/layout/activity_credits.xml b/app/src/main/res/layout/activity_credits.xml index ba7b422..be93f57 100644 --- a/app/src/main/res/layout/activity_credits.xml +++ b/app/src/main/res/layout/activity_credits.xml @@ -5,12 +5,99 @@ android:id="@+id/activity_credits" android:layout_width="match_parent" android:layout_height="match_parent" - android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" - android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.rubenvandeven.emotionhero.CreditsActivity"> + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - diff --git a/app/src/main/res/layout/activity_ending.xml b/app/src/main/res/layout/activity_ending.xml new file mode 100644 index 0000000..e23f155 --- /dev/null +++ b/app/src/main/res/layout/activity_ending.xml @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_gaming.xml b/app/src/main/res/layout/activity_gaming.xml index 80133e7..e14d5e3 100644 --- a/app/src/main/res/layout/activity_gaming.xml +++ b/app/src/main/res/layout/activity_gaming.xml @@ -35,6 +35,7 @@ android:layout_height="wrap_content" android:id="@+id/titleText" tools:text='"A spectacular level"' - android:layout_margin="@dimen/fab_margin" /> + android:layout_margin="@dimen/fab_margin" + android:textSize="24sp" /> diff --git a/app/src/main/res/layout/activity_introduction.xml b/app/src/main/res/layout/activity_introduction.xml deleted file mode 100644 index 0165f33..0000000 --- a/app/src/main/res/layout/activity_introduction.xml +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - - - - - diff --git a/app/src/main/res/layout/activity_menu.xml b/app/src/main/res/layout/activity_menu.xml deleted file mode 100644 index 8feca6c..0000000 --- a/app/src/main/res/layout/activity_menu.xml +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - -