diff --git a/.idea/gradle.xml b/.idea/gradle.xml
index 7ac24c7..e9c7d3f 100644
--- a/.idea/gradle.xml
+++ b/.idea/gradle.xml
@@ -3,8 +3,9 @@
-
+
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Resemblance b/Resemblance
deleted file mode 160000
index 5e3ed0e..0000000
--- a/Resemblance
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 5e3ed0e5679838494a8f8f425e313e7fa4a59145
diff --git a/app/build.gradle b/app/build.gradle
index 928412c..4d86b7b 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -6,10 +6,11 @@ android {
defaultConfig {
applicationId "ru.spbau.resemblance"
minSdkVersion 16
- targetSdkVersion 24
+ targetSdkVersion 21
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ vectorDrawables.useSupportLibrary = true
}
buildTypes {
release {
@@ -17,13 +18,18 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
+ productFlavors {
+ }
}
dependencies {
- compile fileTree(dir: 'libs', include: ['*.jar'])
+ compile fileTree(include: ['*.jar'], dir: 'libs')
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:24.2.1'
+ compile 'com.android.support:design:24.2.1'
+ compile 'com.android.support:support-v4:24.2.1'
+ compile 'com.android.support:support-vector-drawable:24.2.1'
testCompile 'junit:junit:4.12'
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 22ce2d9..8e89312 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -2,19 +2,85 @@
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/ru/spbau/resemblance/CardPickerActivity.java b/app/src/main/java/ru/spbau/resemblance/CardPickerActivity.java
new file mode 100644
index 0000000..0cc2885
--- /dev/null
+++ b/app/src/main/java/ru/spbau/resemblance/CardPickerActivity.java
@@ -0,0 +1,144 @@
+package ru.spbau.resemblance;
+
+import android.content.Intent;
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.GridView;
+import android.widget.ListAdapter;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Timer;
+import java.util.TimerTask;
+
+public class CardPickerActivity extends AppCompatActivity implements AdapterView.OnItemClickListener {
+ public static final String PICTURE_PARAM = "picture_id";
+ public static final String OUR_CARDS_PARAM = "our_cards";
+ public static final String SUGGESTION_PARAM = "suggestion";
+ public static final String TITLE_PARAM = "title";
+ public static final String EXPECTATION_TIME_PARAM = "expectation_time";
+ public static final String LEADING_PARAM = "leading";
+ private final static int COLUMNS_NUMBER = 2;
+ private final static int CARD_REQUEST = 1;
+ private final static int LEADING_CARD_REQUEST = 2;
+ private final static long SECOND = 1000;
+
+ private TextView timeView;
+ private Timer timer;
+ private long deadline;
+ private boolean leading;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_card_picker);
+
+ GridView grid = (GridView) findViewById(R.id.cardPickedGrid);
+ TextView suggestion = (TextView) findViewById(R.id.cardPickerText);
+ timeView = (TextView) findViewById(R.id.cardPickerTimeView);
+
+ Intent callingIntent = getIntent();
+
+ leading = callingIntent.getBooleanExtra(LEADING_PARAM, false);
+ suggestion.setText(callingIntent.getStringExtra(SUGGESTION_PARAM));
+ setTitle(callingIntent.getStringExtra(TITLE_PARAM));
+ long timeout = callingIntent.getLongExtra(EXPECTATION_TIME_PARAM, -1);
+
+ List cardIds = new ArrayList<>();
+ long[] cardsArr = callingIntent.getLongArrayExtra(OUR_CARDS_PARAM);
+ for(long card: cardsArr) {
+ cardIds.add(card);
+ }
+
+ ArrayList cardViews = new ArrayList<>();
+ for (int i = 0; i < cardIds.size(); i++) {
+ cardViews.add(ImageStorage.ImageWrapped.createById((int)(long)cardIds.get(i)));
+ }
+
+ ListAdapter cardsAdapter = new CardsAdapter(this, cardViews,
+ getResources().getDisplayMetrics().widthPixels / COLUMNS_NUMBER);
+ grid.setAdapter(cardsAdapter);
+ grid.setNumColumns(COLUMNS_NUMBER);
+
+ grid.setOnItemClickListener(this);
+
+ timer = new Timer();
+ deadline = System.currentTimeMillis() + timeout;
+ timer.scheduleAtFixedRate(new TimeUpdater(), 10, 200);
+ }
+
+ @Override
+ public void onItemClick(AdapterView> parent, View view, int position, long id) {
+ if (leading) {
+ Intent showPicture = new Intent(this, LeadingAssociationActivity.class);
+ showPicture.putExtra(LeadingAssociationActivity.IMAGE_PARAM, id);
+ startActivityForResult(showPicture, LEADING_CARD_REQUEST);
+ } else {
+ Intent showPicture = new Intent(this, CardViewerActivity.class);
+ showPicture.putExtra(LeadingAssociationActivity.IMAGE_PARAM, id);
+ startActivityForResult(showPicture, CARD_REQUEST);
+ }
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ long pictureId = data.getLongExtra(GameIntermediateActivity.PICTURE_PARAM, -1L);
+ if (pictureId >= 0) {
+ Intent ret = new Intent();
+ ret.putExtra(GameIntermediateActivity.PICTURE_PARAM, pictureId);
+ if (leading) {
+ ret.putExtra(GameIntermediateActivity.ASSOCIATION_PARAM,
+ data.getStringExtra(GameIntermediateActivity.ASSOCIATION_PARAM));
+ }
+ setResult(RESULT_OK, ret);
+ timer.cancel();
+ finish();
+ }
+ }
+
+ private class TimeUpdater extends TimerTask {
+ final int MINUTE = 60;
+
+ @Override
+ public void run() {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ if (System.currentTimeMillis() > deadline) {
+ Intent ret = new Intent();
+ ret.putExtra(GameIntermediateActivity.PICTURE_PARAM, -1L);
+ setResult(RESULT_OK, ret);
+ timer.cancel();
+ finish();
+ } else {
+ long rest = (deadline - System.currentTimeMillis()) / SECOND;
+ timeView.setText(String.format(getString(R.string.card_picker_time), rest / MINUTE + ":" + rest % MINUTE));
+ }
+ }
+ });
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.chat_menu, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ Intent chatIntent = new Intent(this, ChatActivity.class);
+ startActivity(chatIntent);
+ return true;
+ }
+
+ @Override
+ public void onBackPressed() {
+ //Doing nothing. Player should submit an association.
+ }
+}
diff --git a/app/src/main/java/ru/spbau/resemblance/CardSetsActivity.java b/app/src/main/java/ru/spbau/resemblance/CardSetsActivity.java
new file mode 100644
index 0000000..0ca71ac
--- /dev/null
+++ b/app/src/main/java/ru/spbau/resemblance/CardSetsActivity.java
@@ -0,0 +1,75 @@
+package ru.spbau.resemblance;
+
+import android.content.Intent;
+import android.net.Uri;
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.ListView;
+
+import java.util.ArrayList;
+
+//This is an activity for manipulations with cards and sets
+
+public class CardSetsActivity extends AppCompatActivity implements AdapterView.OnItemClickListener {
+ final private static int GALLERY_REQUEST = 1;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_card_sets);
+
+ ListView optionsList = (ListView)findViewById(R.id.cardSetsOptionsList);
+ ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1,
+ getResources().getStringArray(R.array.cards_control_options));
+
+ optionsList.setAdapter(adapter);
+ optionsList.setOnItemClickListener(this);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent imageReturnedIntent) {
+ super.onActivityResult(requestCode, resultCode, imageReturnedIntent);
+
+ ImageView imageView = (ImageView) new ImageView(this);
+
+ switch (requestCode) {
+ case GALLERY_REQUEST:
+ if (resultCode == RESULT_OK) {
+ Uri selectedImage = imageReturnedIntent.getData();
+ imageView.setImageURI(selectedImage);
+ ImageStorage.addImageByUri(selectedImage.toString(), this);
+ }
+ }
+ }
+
+ @Override
+ public void onItemClick(AdapterView> parent, View view, int position, long id) {
+ switch (position) {
+ case 0: {
+ Intent listOfSets = new Intent(this, ListOfSetsActivity.class);
+ startActivity(listOfSets);
+ break;
+ }
+ case 1: {
+ Intent showCards = new Intent(this, GalleryActivity.class);
+ ArrayList cards = ImageStorage.getAllImages();
+ long[] cardIds = new long[cards.size()];
+ for (int i = 0; i < cardIds.length; i++) {
+ cardIds[i] = cards.get(i).getIdImage();
+ }
+ showCards.putExtra(GalleryActivity.CARDS_PARAM, cardIds);
+ startActivity(showCards);
+ break;
+ }
+ case 2: {
+ Intent createSet = new Intent(this, CreateSetActivity.class);
+ startActivity(createSet);
+ break;
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/ru/spbau/resemblance/CardViewerActivity.java b/app/src/main/java/ru/spbau/resemblance/CardViewerActivity.java
new file mode 100644
index 0000000..4332c56
--- /dev/null
+++ b/app/src/main/java/ru/spbau/resemblance/CardViewerActivity.java
@@ -0,0 +1,40 @@
+package ru.spbau.resemblance;
+
+import android.content.Intent;
+import android.net.Uri;
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.ImageView;
+
+public class CardViewerActivity extends AppCompatActivity {
+ public final static String IMAGE_PARAM = "image";
+
+ private ImageStorage.ImageWrapped image;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_card_viewer);
+
+ ImageView imageView = (ImageView) findViewById(R.id.cardViewerImage);
+
+ image = ImageStorage.ImageWrapped.createById((int)getIntent().getLongExtra(IMAGE_PARAM, -1L));
+ imageView.setImageURI(Uri.parse(image.getUriImage()));
+ }
+
+ public void onDoneClick(View v) {
+ Intent ret = new Intent();
+ ret.putExtra(GameIntermediateActivity.PICTURE_PARAM, (long)image.getIdImage());
+ setResult(RESULT_OK, ret);
+ finish();
+ }
+
+ @Override
+ public void onBackPressed() {
+ Intent ret = new Intent();
+ ret.putExtra(GameIntermediateActivity.PICTURE_PARAM, -1L);
+ setResult(RESULT_OK, ret);
+ finish();
+ }
+}
diff --git a/app/src/main/java/ru/spbau/resemblance/CardsAdapter.java b/app/src/main/java/ru/spbau/resemblance/CardsAdapter.java
new file mode 100644
index 0000000..9800955
--- /dev/null
+++ b/app/src/main/java/ru/spbau/resemblance/CardsAdapter.java
@@ -0,0 +1,108 @@
+package ru.spbau.resemblance;
+
+import android.app.Activity;
+import android.content.Context;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.GridView;
+import android.widget.ImageView;
+
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+public class CardsAdapter extends BaseAdapter {
+ private final Activity activity;
+ private final List cards;
+ private final int cardSide;
+ private final List views;
+ private GridView.LayoutParams sizeLayoutParams;
+
+ public CardsAdapter(final Activity activity, final List cards, int cardSide) {
+ this.activity = activity;
+ this.cards = cards;
+ this.cardSide = cardSide;
+
+ //First we fill the grid with placeholders
+ ImageView placeholder = new ImageView(activity);
+ placeholder.setImageResource(android.R.drawable.ic_menu_report_image);
+ placeholder.setScaleType(ImageView.ScaleType.CENTER_CROP);
+ sizeLayoutParams = new GridView.LayoutParams(CardsAdapter.this.cardSide,
+ CardsAdapter.this.cardSide);
+ placeholder.setLayoutParams(sizeLayoutParams);
+ views = new ArrayList<>();
+ for (int i = 0; i < cards.size(); i++) {
+ views.add(placeholder);
+ }
+
+ //Then we replace them with real pictures
+ //This is made for performance improvements
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ for (int i = 0; i < cards.size(); i++) {
+ //Setting picture from file and its parameters
+ try {
+ ImageView view = cards.get(i).getPreview(CardsAdapter.this.activity,
+ CardsAdapter.this.cardSide);
+ view.setScaleType(ImageView.ScaleType.CENTER_CROP);
+ view.setLayoutParams(sizeLayoutParams);
+ views.set(i, view);
+ } catch (FileNotFoundException e) {
+ Log.d("CardsAdapter", "File not found: " + cards.get(i).getUriImage());
+ }
+ //Updating screen
+ activity.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ CardsAdapter.this.notifyDataSetChanged();
+ }
+ });
+ }
+ }
+ }).start();
+ }
+
+ @Override
+ public int getCount() {
+ return cards.size();
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return cards.get(position).getIdImage();
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return cards.get(position);
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ return views.get(position);
+ }
+
+ public void addImage(ImageStorage.ImageWrapped image) {
+ try{
+ cards.add(image);
+ ImageView view = image.getPreview(activity, cardSide);
+ view.setScaleType(ImageView.ScaleType.CENTER_CROP);
+ view.setLayoutParams(sizeLayoutParams);
+ views.add(view);
+
+ activity.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ CardsAdapter.this.notifyDataSetChanged();
+ }
+ });
+ } catch (FileNotFoundException e) {
+ Log.d("CardsAdapter", "File not found: " + image.getUriImage());
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ru/spbau/resemblance/Chat.java b/app/src/main/java/ru/spbau/resemblance/Chat.java
new file mode 100644
index 0000000..b1b6c70
--- /dev/null
+++ b/app/src/main/java/ru/spbau/resemblance/Chat.java
@@ -0,0 +1,160 @@
+package ru.spbau.resemblance;
+
+import android.app.Activity;
+import android.content.Context;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+public class Chat {
+ private static final ArrayList messages = new ArrayList();
+ private static volatile Activity activity;
+ private static final Lock activityLock = new ReentrantLock();
+
+ private Chat(){}
+
+ private static final BaseAdapter ADAPTER = new BaseAdapter() {
+ @Override
+ public int getCount() {
+ return messages.size();
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return messages.get(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return 0;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if (convertView != null) {
+ return convertView;
+ }
+
+ LayoutInflater inflater = (LayoutInflater) activity.getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+ ChatMessage message = messages.get(position);
+ View messageView = inflater.inflate(R.layout.message_list_item, parent, false);
+ TextView authorView = (TextView) messageView.findViewById(R.id.messageAuthor);
+ TextView timeView = (TextView) messageView.findViewById(R.id.messageTime);
+ TextView textView = (TextView) messageView.findViewById(R.id.messageText);
+ authorView.setText(message.getAuthor());
+ timeView.setText(message.getTime());
+ textView.setText(message.getText());
+ if (message.isOurs()) {
+ LinearLayout head = (LinearLayout) messageView.findViewById(R.id.messageHeadline);
+ head.setGravity(Gravity.END);
+ textView.setGravity(Gravity.RIGHT);
+ }
+ return messageView;
+ }
+ };
+
+ public static BaseAdapter setActivity(Activity newActivity) {
+ activityLock.lock();
+ try {
+ activity = newActivity;
+ } finally {
+ activityLock.unlock();
+ }
+ return ADAPTER;
+ }
+
+ public static void unsetActivity() {
+ activityLock.lock();
+ try {
+ activity = null;
+ } finally {
+ activityLock.unlock();
+ }
+ }
+
+ public static void receiveMessage(String author, String time, String text) {
+ messages.add(new ChatMessage(author, time, text));
+ activityLock.lock();
+ try {
+ if (activity != null) {
+ activity.runOnUiThread(updateScreen);
+ }
+ } finally {
+ activityLock.unlock();
+ }
+ }
+
+ public static void sendMessage(String ourName, String text) {
+ ChatMessage message = new ChatMessage(ourName, text);
+ messages.add(message);
+ activityLock.lock();
+ try {
+ if (activity != null) {
+ activity.runOnUiThread(updateScreen);
+ }
+ } finally {
+ activityLock.unlock();
+ }
+ Message.sendChatMessage(message);
+ }
+
+ public static void clear() {
+ messages.clear();
+ }
+
+ public static class ChatMessage {
+ private final String author;
+ private final String time;
+ private final String text;
+ private final boolean ours;
+
+ ChatMessage(String author, String time, String text) {
+ this.author = author;
+ this.time = time;
+ this.text = text;
+ ours = false;
+ }
+
+ ChatMessage(String ourName, String text) {
+ author = ourName;
+ Calendar calendar = Calendar.getInstance();
+ time = " " + calendar.get(Calendar.HOUR_OF_DAY) + ":" + calendar.get(Calendar.MINUTE) + ":" +
+ calendar.get(Calendar.SECOND);
+ this.text = text;
+ ours = true;
+ }
+
+ public String getAuthor() {
+ return author;
+ }
+
+ public String getTime() {
+ return time;
+ }
+
+ public String getText() {
+ return text;
+ }
+
+ public boolean isOurs() {
+ return ours;
+ }
+ }
+
+ private static Runnable updateScreen = new Runnable() {
+ @Override
+ public void run() {
+ ADAPTER.notifyDataSetChanged();
+ }
+ };
+}
diff --git a/app/src/main/java/ru/spbau/resemblance/ChatActivity.java b/app/src/main/java/ru/spbau/resemblance/ChatActivity.java
new file mode 100644
index 0000000..6062ad8
--- /dev/null
+++ b/app/src/main/java/ru/spbau/resemblance/ChatActivity.java
@@ -0,0 +1,45 @@
+package ru.spbau.resemblance;
+
+import android.content.SharedPreferences;
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+
+public class ChatActivity extends AppCompatActivity {
+ private EditText messageField;
+ private String ourName;
+ private ListView messagesList;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_chat);
+ messageField = (EditText) findViewById(R.id.chatNewMessageField);
+ messagesList = (ListView) findViewById(R.id.chatMessagesList);
+
+
+ SharedPreferences preferences = getSharedPreferences(SettingsActivity.PREFERENCES, MODE_PRIVATE);
+ ourName = preferences.getString(SettingsActivity.NICKNAME_PREF, "");
+ setTitle(R.string.chat_title);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ messagesList.setAdapter(Chat.setActivity(this));
+ }
+
+ public void onSendClick(View v) {
+ Chat.sendMessage(ourName, messageField.getText().toString());
+ messageField.setText("");
+ }
+
+ @Override
+ protected void onPause() {
+ Chat.unsetActivity();
+ super.onPause();
+ }
+}
diff --git a/app/src/main/java/ru/spbau/resemblance/CreateGameActivity.java b/app/src/main/java/ru/spbau/resemblance/CreateGameActivity.java
new file mode 100644
index 0000000..7ca691a
--- /dev/null
+++ b/app/src/main/java/ru/spbau/resemblance/CreateGameActivity.java
@@ -0,0 +1,105 @@
+package ru.spbau.resemblance;
+
+import android.content.Intent;
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.SeekBar;
+import android.widget.Spinner;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import java.util.ArrayList;
+
+public class CreateGameActivity extends AppCompatActivity implements SeekBar.OnSeekBarChangeListener,
+ AdapterView.OnItemSelectedListener {
+ private static final int MINUTE = 60;
+ private static final long SECOND = 1000;
+ private int roundsNumber = 3;
+ private TextView roundsText = null;
+ private SeekBar roundsBar;
+ private ArrayList sets = null;
+ private int chosenSet = -1;
+ private SeekBar timeBar;
+ private int expectationTimeSeconds = 60;
+ private TextView timeText;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ Spinner setPicker = null;
+ String[] setNames = null;
+
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_create_game);
+
+ roundsText = (TextView)findViewById(R.id.createGameRoundsText);
+ roundsBar = (SeekBar)findViewById(R.id.createGameRoundsSeekBar);
+ roundsBar.setOnSeekBarChangeListener(this);
+ timeText = (TextView)findViewById(R.id.createGameTimeText);
+ timeBar = (SeekBar)findViewById(R.id.createGameTimeBar);
+ timeBar.setOnSeekBarChangeListener(this);
+ timeBar.setProgress(1);
+
+ setPicker = (Spinner)findViewById(R.id.createGameSetPicker);
+ sets = ImageStorage.getAllSetsCards();
+ setNames = new String[sets.size()];
+ for (int i = 0; i < sets.size(); i++) {
+ setNames[i] = sets.get(i).getNameSetCards();
+ }
+ ArrayAdapter pickerAdapter = new ArrayAdapter(this, R.layout.support_simple_spinner_dropdown_item,
+ setNames);
+ setPicker.setAdapter(pickerAdapter);
+ setPicker.setOnItemSelectedListener(this);
+ setTitle(R.string.create_game_title);
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar bar) {
+
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar bar) {
+
+ }
+
+ @Override
+ public void onProgressChanged(SeekBar bar, int val, boolean byUser) {
+ if (bar.equals(roundsBar)) {
+ roundsNumber = val + 1;
+ roundsText.setText(String.format(getString(R.string.create_game_rounds), roundsNumber));
+ }
+ if (bar.equals(timeBar)) {
+ expectationTimeSeconds = (val + 1) * 30;
+ String time = "";
+ if (expectationTimeSeconds / MINUTE > 0) {
+ time += " " + expectationTimeSeconds / MINUTE + getString(R.string.minutes);
+ }
+ if (expectationTimeSeconds % MINUTE > 0) {
+ time += " " + expectationTimeSeconds % MINUTE + getString(R.string.seconds);
+ }
+ timeText.setText(String.format(getString(R.string.create_game_choice_time_text), time));
+ }
+ }
+
+ @Override
+ public void onItemSelected(AdapterView> parent, View view, int position, long id) {
+ chosenSet = position;
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView> parent) {}
+
+ public void onCreateClick(View v) {
+ if (chosenSet == -1) {
+ Toast.makeText(this, R.string.create_game_choose_card_set, Toast.LENGTH_SHORT).show();
+ } else {
+ Message.sendCreateGameMessage(roundsNumber, sets.get(chosenSet).getListOfCards(),
+ expectationTimeSeconds * SECOND, sets.get(chosenSet).getHash());
+ Intent prepareGame = new Intent(this, GamePreparationActivity.class);
+ startActivity(prepareGame);
+ }
+ }
+}
diff --git a/app/src/main/java/ru/spbau/resemblance/CreateSetActivity.java b/app/src/main/java/ru/spbau/resemblance/CreateSetActivity.java
new file mode 100644
index 0000000..82ec4ef
--- /dev/null
+++ b/app/src/main/java/ru/spbau/resemblance/CreateSetActivity.java
@@ -0,0 +1,67 @@
+package ru.spbau.resemblance;
+
+import android.content.Intent;
+import android.net.Uri;
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.GridView;
+import android.widget.ImageView;
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+public class CreateSetActivity extends AppCompatActivity {
+ private final static int GALLERY_REQUEST = 1;
+ private final ArrayList cards = new ArrayList<>();
+ private CardsAdapter adapter;
+ private final static int COLUMNS_NUMBER = 3;
+ private EditText nameField;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_create_set);
+
+ adapter = new CardsAdapter(this, new ArrayList(),
+ getResources().getDisplayMetrics().widthPixels / COLUMNS_NUMBER);
+ GridView cardsGrid = (GridView)findViewById(R.id.createSetCardsGrid);
+ cardsGrid.setNumColumns(COLUMNS_NUMBER);
+ cardsGrid.setAdapter(adapter);
+ nameField = (EditText)findViewById(R.id.createSetNameField);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent imageReturnedIntent) {
+ super.onActivityResult(requestCode, resultCode, imageReturnedIntent);
+
+ switch (requestCode) {
+ case GALLERY_REQUEST:
+ if (resultCode == RESULT_OK) {
+ Uri selectedImage = imageReturnedIntent.getData();
+ ImageStorage.ImageWrapped newCard =
+ ImageStorage.addImageByUri(selectedImage.toString(), this);
+ new ImageView(this).setImageURI(selectedImage);
+ cards.add(newCard);
+ adapter.addImage(newCard);
+ }
+ }
+ }
+
+ public void onAddCardClick(View v) {
+ Intent photoPickerIntent = new Intent(Intent.ACTION_PICK);
+ photoPickerIntent.setType("image/*");
+ startActivityForResult(photoPickerIntent, GALLERY_REQUEST);
+ }
+
+ public void onDoneClick(View v) {
+ ImageStorage.SetCardsWrapped setCards = new ImageStorage.SetCardsWrapped();
+ setCards.setName(nameField.getText().toString());
+ setCards.addSetCards();
+ for (int i = 0; i < cards.size(); i++) {
+ setCards.addCardToSet(cards.get(i));
+ }
+ finish();
+ }
+}
diff --git a/app/src/main/java/ru/spbau/resemblance/GalleryActivity.java b/app/src/main/java/ru/spbau/resemblance/GalleryActivity.java
new file mode 100644
index 0000000..0070c3c
--- /dev/null
+++ b/app/src/main/java/ru/spbau/resemblance/GalleryActivity.java
@@ -0,0 +1,53 @@
+package ru.spbau.resemblance;
+
+import android.content.Intent;
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.GridView;
+import android.widget.ListAdapter;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class GalleryActivity extends AppCompatActivity implements AdapterView.OnItemClickListener {
+ public static final String CARDS_PARAM = "our_cards";
+ private final static int COLUMNS_NUMBER = 3;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_gallery);
+
+ GridView grid = (GridView) findViewById(R.id.galleryGrid);
+
+ Intent callingIntent = getIntent();
+
+ List cardIds = new ArrayList<>();
+ long[] cardsArr = callingIntent.getLongArrayExtra(CARDS_PARAM);
+ for(long card: cardsArr) {
+ cardIds.add(card);
+ }
+
+ ArrayList cardViews = new ArrayList<>();
+ for (int i = 0; i < cardIds.size(); i++) {
+ cardViews.add(ImageStorage.ImageWrapped.createById((int)(long)cardIds.get(i)));
+ }
+
+ ListAdapter cardsAdapter = new CardsAdapter(this, cardViews,
+ getResources().getDisplayMetrics().widthPixels / COLUMNS_NUMBER);
+ grid.setAdapter(cardsAdapter);
+ grid.setNumColumns(COLUMNS_NUMBER);
+
+ grid.setOnItemClickListener(this);
+ }
+
+ @Override
+ public void onItemClick(AdapterView> parent, View view, int position, long id) {
+ Intent showPicture = new Intent(this, GalleryCardViewerActivity.class);
+ showPicture.putExtra(LeadingAssociationActivity.IMAGE_PARAM, id);
+ startActivity(showPicture);
+ }
+}
diff --git a/app/src/main/java/ru/spbau/resemblance/GalleryCardViewerActivity.java b/app/src/main/java/ru/spbau/resemblance/GalleryCardViewerActivity.java
new file mode 100644
index 0000000..4543e77
--- /dev/null
+++ b/app/src/main/java/ru/spbau/resemblance/GalleryCardViewerActivity.java
@@ -0,0 +1,24 @@
+package ru.spbau.resemblance;
+
+import android.net.Uri;
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+import android.widget.ImageView;
+
+public class GalleryCardViewerActivity extends AppCompatActivity {
+ public final static String IMAGE_PARAM = "image";
+
+ private ImageView imageView;
+ private ImageStorage.ImageWrapped image;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_gallery_card_ciewer);
+
+ imageView = (ImageView)findViewById(R.id.cardViewerImage);
+
+ image = ImageStorage.ImageWrapped.createById((int)getIntent().getLongExtra(IMAGE_PARAM, -1L));
+ imageView.setImageURI(Uri.parse(image.getUriImage()));
+ }
+}
diff --git a/app/src/main/java/ru/spbau/resemblance/GameExpectationActivity.java b/app/src/main/java/ru/spbau/resemblance/GameExpectationActivity.java
new file mode 100644
index 0000000..580227d
--- /dev/null
+++ b/app/src/main/java/ru/spbau/resemblance/GameExpectationActivity.java
@@ -0,0 +1,69 @@
+package ru.spbau.resemblance;
+
+import android.content.Intent;
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.Toast;
+
+import java.util.ArrayList;
+
+public class GameExpectationActivity extends AppCompatActivity implements
+ Message.GameExpectationMessageListener {
+ public static final String RANDOM_GAME_EXTRA = "random_game";
+
+ private boolean randomGame;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_game_expectation);
+
+ Message.setGameExpectationListener(this);
+ setTitle(R.string.game_expectation_title);
+
+ randomGame = getIntent().getBooleanExtra(RANDOM_GAME_EXTRA, false);
+ if (randomGame) {
+ //Ask server to put us in the waiting list
+ Message.sendJoinRandomGameMessage();
+ }
+ }
+
+ @Override
+ public void onStartGameMessage(int roundsNumber, int playersNumber,
+ ArrayList names, long expectationTime) {
+ Intent startGame = new Intent(this, GameIntermediateActivity.class);
+ startGame.putExtra(GameIntermediateActivity.ROUNDS_NUMBER_PARAM, roundsNumber);
+ startGame.putExtra(GameIntermediateActivity.PLAYERS_NUMBER_PARAM, playersNumber);
+ startGame.putStringArrayListExtra(GameIntermediateActivity.PLAYERS_NAMES_PARAM, names);
+ startGame.putExtra(GameIntermediateActivity.EXPECTATION_TIME_PARAM, expectationTime);
+ startActivity(startGame);
+ finish();
+ }
+
+ @Override
+ public void onGameCancelled() {
+ finish();
+ }
+
+ public void onCancelClick(View v) {
+ if (randomGame) {
+ Message.sendQuitRandomGameMessage();
+ } else {
+ Message.sendQuitFriendGameMessage();
+ }
+ finish();
+ }
+
+ @Override
+ public void onBackPressed() {
+ //Disabled
+ }
+
+ @Override
+ protected void onDestroy() {
+ Message.unSetGameExpectationListener();
+
+ super.onDestroy();
+ }
+}
diff --git a/app/src/main/java/ru/spbau/resemblance/GameFinishActivity.java b/app/src/main/java/ru/spbau/resemblance/GameFinishActivity.java
new file mode 100644
index 0000000..119d86e
--- /dev/null
+++ b/app/src/main/java/ru/spbau/resemblance/GameFinishActivity.java
@@ -0,0 +1,131 @@
+package ru.spbau.resemblance;
+
+import android.content.Context;
+import android.content.Intent;
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+
+public class GameFinishActivity extends AppCompatActivity {
+ protected static final String LAST_ANSWER_PARAM = "last_answer";
+ protected static final String FINAL_SCORES_PARAM = "final_scores";
+ protected static final String OLD_RATINGS_PARAM = "old_ratings";
+ protected static final String NEW_RATINGS_PARAM = "new_ratings";
+ protected static final String PLAYERS_NAMES_PARAM = "players_names";
+
+ private ListView resultsList;
+ private LayoutInflater inflater;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_game_finish);
+
+ Intent callingIntent = getIntent();
+ ArrayList playerNames = callingIntent.getStringArrayListExtra(PLAYERS_NAMES_PARAM);
+ ArrayList finalScores = callingIntent.getIntegerArrayListExtra(FINAL_SCORES_PARAM);
+ ArrayList oldRatings = callingIntent.getIntegerArrayListExtra(OLD_RATINGS_PARAM);
+ ArrayList newRatings = callingIntent.getIntegerArrayListExtra(NEW_RATINGS_PARAM);
+ long lastAnswer = callingIntent.getLongExtra(LAST_ANSWER_PARAM, -1);
+
+ inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ setTitle(R.string.game_finish_title);
+
+ View[] viewsArray = new View[playerNames.size() + 3];
+
+ showResults(viewsArray, preparePlayersData(playerNames, finalScores, oldRatings, newRatings));
+ showLastAssociation(viewsArray, (int)lastAnswer, playerNames.size() + 1);
+
+ resultsList = (ListView) findViewById(R.id.resultsList);
+ ViewArrayAdapter adapter = new ViewArrayAdapter(viewsArray);
+ resultsList.setAdapter(adapter);
+ }
+
+ private ArrayList preparePlayersData(ArrayList playerNames,
+ ArrayList finalScores,
+ ArrayList oldRatings,
+ ArrayList newRatings) {
+ ArrayList playerResults = new ArrayList<>();
+ for (int i = 0; i < playerNames.size(); i++) {
+ playerResults.add(new PlayerResult(playerNames.get(i), finalScores.get(i),
+ oldRatings.get(i), newRatings.get(i)));
+ }
+
+ Collections.sort(playerResults, new Comparator() {
+ @Override
+ public int compare(PlayerResult o1, PlayerResult o2) {
+ return o2.getScore() - o1.getScore();
+ }
+ });
+ return playerResults;
+ }
+
+ private void showResults(View[] viewsArray, ArrayList playerResults) {
+ viewsArray[0] = inflater.inflate(R.layout.results_header, resultsList);
+ for (int i = 0; i < playerResults.size(); i++) {
+ View playerResultView = inflater.inflate(R.layout.result_list_item, resultsList, false);
+ ((TextView)playerResultView.findViewById(R.id.player)).setText(playerResults.get(i).getName());
+ ((TextView)playerResultView.findViewById(R.id.finalScore))
+ .setText(playerResults.get(i).getScoreString());
+ ((TextView)playerResultView.findViewById(R.id.oldRating))
+ .setText(playerResults.get(i).getOldRatingString());
+ ((TextView)playerResultView.findViewById(R.id.newRating))
+ .setText(playerResults.get(i).getNewRatingString());
+ viewsArray[i + 1] = playerResultView;
+ }
+ }
+
+ private void showLastAssociation(View[] viewsArray, int answer, int pos) {
+ viewsArray[pos] = new TextView(this);
+ ((TextView)viewsArray[pos]).setText(R.string.game_finish_last_association);
+ ((TextView)viewsArray[pos]).setTextAppearance(this,
+ android.R.style.TextAppearance_DeviceDefault_Large);
+
+ viewsArray[pos + 1] = ImageStorage.ImageWrapped.createById(answer)
+ .getImageView(this);
+ ((ImageView)viewsArray[pos + 1]).setAdjustViewBounds(true);
+ }
+
+ private class PlayerResult {
+ private final String name;
+ private final int score;
+ private final int oldRating;
+ private final int newRating;
+
+ PlayerResult(String name, int score, int oldRating, int newRating) {
+ this.name = name;
+ this.score = score;
+ this.oldRating = oldRating;
+ this.newRating = newRating;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public int getScore() {
+ return score;
+ }
+
+ public String getScoreString() {
+ return String.valueOf(score);
+ }
+
+ public String getOldRatingString() {
+ return String.valueOf(oldRating);
+ }
+
+ public String getNewRatingString() {
+ return String.valueOf(newRating);
+ }
+ }
+}
diff --git a/app/src/main/java/ru/spbau/resemblance/GameIntermediateActivity.java b/app/src/main/java/ru/spbau/resemblance/GameIntermediateActivity.java
new file mode 100644
index 0000000..a462f15
--- /dev/null
+++ b/app/src/main/java/ru/spbau/resemblance/GameIntermediateActivity.java
@@ -0,0 +1,228 @@
+package ru.spbau.resemblance;
+
+import android.content.Context;
+import android.content.Intent;
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+
+public class GameIntermediateActivity extends AppCompatActivity implements Message.GameMessageListener {
+ public static final String ROUNDS_NUMBER_PARAM = "rounds_number";
+ public static final String PLAYERS_NUMBER_PARAM = "players_number";
+ public static final String PLAYERS_NAMES_PARAM = "players_names";
+ public static final String EXPECTATION_TIME_PARAM = "expectation_time";
+ public static final int LEADING_ASSOCIATION_REQUEST = 1;
+ public static final int CHOICE_REQUEST = 2;
+ public static final int VOTE_REQUEST = 3;
+ public static final String ASSOCIATION_PARAM = "association";
+ public static final String PICTURE_PARAM = "picture_id";
+
+ private final ArrayList cards = new ArrayList<>();
+ private int roundsNumber = -1;
+ private int currentRound = -1;
+ private int playersNumber = -1;
+ private ArrayList playersNames = null;
+ private int[] scores = null;
+ private long answer = -1;
+ private long expectationTime;
+ private LayoutInflater inflater;
+ private ListView stateList;
+ private ViewArrayAdapter adapter;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_game_intermediate);
+
+ Message.setGameListener(this);
+
+ Intent callingIntent = getIntent();
+ roundsNumber = callingIntent.getIntExtra(ROUNDS_NUMBER_PARAM, -1);
+ currentRound = 1;
+ playersNumber = callingIntent.getIntExtra(PLAYERS_NUMBER_PARAM, -1);
+ playersNames = callingIntent.getStringArrayListExtra(PLAYERS_NAMES_PARAM);
+ expectationTime = callingIntent.getLongExtra(EXPECTATION_TIME_PARAM, -1);
+ scores = new int[playersNumber];
+
+ inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ stateList = (ListView) findViewById(R.id.gameStateList);
+
+ View[] listViews = new View[playersNumber + 2];
+ updateInfo(listViews);
+
+ adapter = new ViewArrayAdapter(listViews);
+ stateList.setAdapter(adapter);
+ }
+
+ @Override
+ public void onLeadRequest() {
+ Intent lead = new Intent(this, CardPickerActivity.class);
+ lead.putExtra(CardPickerActivity.LEADING_PARAM, true);
+ lead.putExtra(CardPickerActivity.SUGGESTION_PARAM,
+ getString(R.string.leading_cards_grid_card_invitation));
+ lead.putExtra(CardPickerActivity.TITLE_PARAM, getString(R.string.leading_grid_title));
+ lead.putExtra(CardPickerActivity.OUR_CARDS_PARAM, getCardsArr());
+ lead.putExtra(CardPickerActivity.EXPECTATION_TIME_PARAM, expectationTime);
+ startActivityForResult(lead, LEADING_ASSOCIATION_REQUEST);
+ }
+
+ @Override
+ public void onChoiceRequest(String association) {
+ Intent choose = new Intent(this, CardPickerActivity.class);
+ choose.putExtra(CardPickerActivity.LEADING_PARAM, false);
+ choose.putExtra(CardPickerActivity.SUGGESTION_PARAM,
+ String.format(getString(R.string.game_association_suggestion), association));
+ choose.putExtra(CardPickerActivity.TITLE_PARAM, getString(R.string.choice_title));
+ choose.putExtra(CardPickerActivity.OUR_CARDS_PARAM, getCardsArr());
+ choose.putExtra(CardPickerActivity.EXPECTATION_TIME_PARAM, expectationTime);
+ startActivityForResult(choose, CHOICE_REQUEST);
+ }
+
+ @Override
+ public void onVoteRequest(String association, long[] candidates) {
+ Intent vote = new Intent(this, CardPickerActivity.class);
+ vote.putExtra(CardPickerActivity.SUGGESTION_PARAM,
+ String.format(getString(R.string.game_association_suggestion), association));
+ vote.putExtra(CardPickerActivity.TITLE_PARAM, getString(R.string.vote_title));
+ vote.putExtra(CardPickerActivity.OUR_CARDS_PARAM, candidates);
+ vote.putExtra(CardPickerActivity.EXPECTATION_TIME_PARAM, expectationTime);
+ startActivityForResult(vote, VOTE_REQUEST);
+ }
+
+ @Override
+ public void onSendCard(long card) {
+ synchronized (cards) {
+ cards.add(card);
+ }
+ }
+
+ @Override
+ public void onRoundEnd(long leadersAssociation, int[] scores){
+ this.scores = scores;
+ answer = leadersAssociation;
+ currentRound++;
+
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ updateScreen();
+ }
+ });
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ long pictureId = data.getLongExtra(PICTURE_PARAM, -1L);
+ if (pictureId >= 0) {
+ switch (requestCode) {
+ case LEADING_ASSOCIATION_REQUEST: {
+ //Now we need to send the association and remove the card fom our cards list.
+ String association = data.getStringExtra(ASSOCIATION_PARAM);
+
+ synchronized (cards) {
+ cards.remove(pictureId);
+ }
+
+ Message.sendLeadAssociationMessage(pictureId, association);
+ break;
+ }
+
+ case CHOICE_REQUEST: {
+ //Sending player's choice and removing the chosen card.
+ synchronized (cards) {
+ cards.remove(cards.indexOf(pictureId));
+ }
+
+ Message.sendChoiceMessage(pictureId);
+ break;
+ }
+
+ case VOTE_REQUEST: {
+ Message.sendVoteMessage(pictureId);
+ break;
+ }
+ }
+ }
+ }
+
+ private long[] getCardsArr() {
+ long[] cardsArr = new long[cards.size()];
+ synchronized (cards) {
+ for (int i = 0; i < cards.size(); i++) {
+ cardsArr[i] = cards.get(i);
+ }
+ }
+ return cardsArr;
+ }
+
+ private void updateScreen() {
+ View[] listViews = new View[playersNumber + 3];
+ updateInfo(listViews);
+ listViews[playersNumber + 2] = ImageStorage.ImageWrapped.createById((int)answer).getImageView(this);
+ ((ImageView)listViews[playersNumber + 2]).setScaleType(ImageView.ScaleType.CENTER);
+ ((ImageView)listViews[playersNumber + 2]).setAdjustViewBounds(true);
+ adapter.setArray(listViews);
+ adapter.notifyDataSetChanged();
+ }
+
+ private void updateInfo(View[] listViews) {
+ listViews[0] = new TextView(this);
+ ((TextView)listViews[0]).setTextAppearance(this, android.R.style.TextAppearance_DeviceDefault_Large);
+ ((TextView)listViews[0]).setText(String.format(getString(R.string.game_round_text),
+ currentRound + "/" + roundsNumber));
+
+ listViews[1] = new TextView(this);
+ ((TextView)listViews[1]).setTextAppearance(this, android.R.style.TextAppearance_DeviceDefault_Large);
+ ((TextView)listViews[1]).setText(R.string.game_score_prefix);
+
+ for (int i = 0; i < playersNumber; i++) {
+ View playerScoreView = inflater.inflate(R.layout.score_list_item, stateList, false);
+ ((TextView)playerScoreView.findViewById(R.id.player)).setText(playersNames.get(i));
+ ((TextView)playerScoreView.findViewById(R.id.score)).setText(String.valueOf(scores[i]));
+ listViews[i + 2] = playerScoreView;
+ }
+ }
+
+ @Override
+ protected void onDestroy() {
+ Message.unsetGameListener();
+
+ super.onDestroy();
+ }
+
+ @Override
+ public void onGameFinish(long lastAnswer, ArrayList finalScores,
+ ArrayList oldRatings, ArrayList newRatings) {
+ Chat.clear();
+ Intent showGameFinish = new Intent(this, GameFinishActivity.class);
+ showGameFinish.putExtra(GameFinishActivity.LAST_ANSWER_PARAM, lastAnswer);
+ showGameFinish.putIntegerArrayListExtra(GameFinishActivity.FINAL_SCORES_PARAM, finalScores);
+ showGameFinish.putIntegerArrayListExtra(GameFinishActivity.OLD_RATINGS_PARAM, oldRatings);
+ showGameFinish.putIntegerArrayListExtra(GameFinishActivity.NEW_RATINGS_PARAM, newRatings);
+ showGameFinish.putStringArrayListExtra(GameFinishActivity.PLAYERS_NAMES_PARAM, playersNames);
+ startActivity(showGameFinish);
+
+ finish();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.chat_menu, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ Intent chatIntent = new Intent(this, ChatActivity.class);
+ startActivity(chatIntent);
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ru/spbau/resemblance/GamePreparationActivity.java b/app/src/main/java/ru/spbau/resemblance/GamePreparationActivity.java
new file mode 100644
index 0000000..85d1781
--- /dev/null
+++ b/app/src/main/java/ru/spbau/resemblance/GamePreparationActivity.java
@@ -0,0 +1,134 @@
+package ru.spbau.resemblance;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.support.annotation.NonNull;
+import android.support.v4.app.DialogFragment;
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+import android.widget.Toast;
+
+import java.util.ArrayList;
+
+public class GamePreparationActivity extends AppCompatActivity implements
+ Message.FriendGamePreparationListener, AdapterView.OnItemClickListener {
+ private static final int MIN_PLAYERS_NUMBER = 3;
+ private ArrayAdapter playersAdapter;
+ private ArrayList players;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_game_preparation);
+
+ Message.setFriendGamePreparationListener(this);
+
+ players = new ArrayList<>();
+ ListView playersListView = (ListView) findViewById(R.id.preparationListOfPlayers);
+ playersAdapter = new ArrayAdapter<>(this,
+ R.layout.support_simple_spinner_dropdown_item, players);
+ playersListView.setAdapter(playersAdapter);
+ playersListView.setOnItemClickListener(this);
+ }
+
+ public void onStartClick(View v) {
+ if (players.size() + 1 >= MIN_PLAYERS_NUMBER) {
+ Message.sendStartFriendGameMessage();
+
+ Intent expectGame = new Intent(this, GameExpectationActivity.class);
+ startActivity(expectGame);
+ finish();
+ } else {
+ Toast.makeText(this, R.string.not_enough_players, Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ public void onCancelClick(View v) {
+ Message.sendCancelFriendGameMessage();
+ finish();
+ }
+
+ private void updateList() {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ playersAdapter.notifyDataSetChanged();
+ }
+ });
+ }
+
+ @Override
+ public void onNewPlayer(String name) {
+ players.add(name);
+ updateList();
+ }
+
+ @Override
+ public void onGonePlayer(String name) {
+ players.remove(name);
+ updateList();
+ }
+
+ @Override
+ public void onPause() {
+ Message.unSetFriendGamePreparationListener();
+
+ super.onPause();
+ }
+
+ @Override
+ public void onItemClick(AdapterView> parent, View view, int position, long id) {
+ DeletePlayerDialog dialog = new DeletePlayerDialog();
+ dialog.setPlayer(position);
+ dialog.setActivity(this);
+ dialog.show(getSupportFragmentManager(), "DeletePlayerDialog");
+ }
+
+ @Override
+ public void onBackPressed() {}
+
+ private void removePlayer(int playerIndex) {
+ Message.sendRemovePlayerMessage(players.get(playerIndex));
+ players.remove(playerIndex);
+ updateList();
+ }
+
+ public static class DeletePlayerDialog extends DialogFragment {
+ private int playerIndex;
+ private GamePreparationActivity activity;
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ builder.setMessage(R.string.delete_player)
+ .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ activity.removePlayer(playerIndex);
+ }
+ })
+ .setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+
+ }
+ });
+ return builder.create();
+ }
+
+ public void setPlayer(int playerIndex) {
+ this.playerIndex = playerIndex;
+ }
+
+ public void setActivity(GamePreparationActivity activity) {
+ this.activity = activity;
+ }
+ }
+}
diff --git a/app/src/main/java/ru/spbau/resemblance/ImageStorage.java b/app/src/main/java/ru/spbau/resemblance/ImageStorage.java
new file mode 100644
index 0000000..12432a8
--- /dev/null
+++ b/app/src/main/java/ru/spbau/resemblance/ImageStorage.java
@@ -0,0 +1,614 @@
+package ru.spbau.resemblance;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+import android.util.Log;
+import android.widget.ImageView;
+
+import java.io.BufferedInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+
+
+public class ImageStorage {
+ final private static String LOG_TAG = "DBLog";
+ final private static String IMAGE_TABLE = "imageTable";
+ final private static String SET_CARDS_TABLE = "setCardsTable";
+ final private static String MAP_TABLE = "mapSetAndImage";
+ final private static String DATA_BASE_NAME = "imageDB1";
+ final private static int CURRENT_VERSION = 1;
+ final private static long BASE_IN_HASH = 257;
+
+
+ private static byte[] imageBuffer = null;
+
+ private ImageStorage() {
+ }
+
+ private static ImageDB imageDB = null;
+
+ public static void createImageStorage(Context context) {
+ if (imageDB == null) {
+ imageDB = new ImageDB(context);
+ }
+ if (imageBuffer == null) {
+ imageBuffer = new byte[context.getResources().getInteger(R.integer.max_image_size)];
+ }
+
+ addTestSet(context, "Set1", "a", 45);
+ addTestSet(context, "Set2", "b", 50);
+
+ printToLogAll(IMAGE_TABLE);
+ printToLogAll(SET_CARDS_TABLE);
+ printToLogAll(MAP_TABLE);
+ }
+
+ private static void addTestSet(Context context, String nameSet, String prefCardName, int sizeSet) {
+ SetCardsWrapped setCards = new SetCardsWrapped();
+ setCards.setName(nameSet);
+ setCards.addSetCards();
+ for (int i = 1; i <= sizeSet; i++) {
+ int curId = context.getResources().getIdentifier(prefCardName + i, "drawable", context.getPackageName());
+ Resources resources = context.getResources();
+ ImageWrapped curImage = addImageByUri(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + resources.getResourcePackageName(curId) + '/' + resources.getResourceTypeName(curId) + '/' + resources.getResourceEntryName(curId), context);
+ setCards.addCardToSet(curImage);
+ }
+ }
+
+ private static void clearImageDB() {
+ imageDB.getWritableDatabase().delete(IMAGE_TABLE, null, null);
+ imageDB.getWritableDatabase().delete(SET_CARDS_TABLE, null, null);
+ imageDB.getWritableDatabase().delete(MAP_TABLE, null, null);
+ imageDB.close();
+ }
+
+ private static void printToLogCur(Cursor c) {
+ StringBuilder newLog = new StringBuilder();
+ for (String columnName : c.getColumnNames()) {
+ newLog.append(columnName);
+ newLog.append(" = ");
+ newLog.append(c.getString(c.getColumnIndex(columnName)));
+ newLog.append(" ; ");
+ }
+ Log.d(LOG_TAG, newLog.toString());
+ }
+
+ private static void printToLogAll(Cursor c) {
+ if (c.moveToFirst()) {
+ do {
+ printToLogCur(c);
+ } while (c.moveToNext());
+ c.close();
+ }
+ }
+ private static void printToLogAll(String nameTable) {
+ imageDB.close();
+ Log.d(LOG_TAG, " -- Print table " + nameTable + " -- ");
+ Cursor c = imageDB.getWritableDatabase().query(nameTable, null, null, null, null, null, null);
+ printToLogAll(c);
+ Log.d(LOG_TAG, " -- End to print table " + nameTable + " -- ");
+ imageDB.close();
+ }
+
+ private static long calcHashByUri(String uriImage, Context context) {
+ long hashImage = 0;
+ try {
+ Uri uri = Uri.parse(uriImage);
+ InputStream stream = new BufferedInputStream(context.getContentResolver().openInputStream(uri));
+ int len = stream.read(imageBuffer);
+ stream.close();
+ hashImage = uriImage.hashCode();
+ for (int i = 0; i < len; i++) {
+ hashImage *= BASE_IN_HASH;
+ hashImage += imageBuffer[i];
+ }
+ //TODO
+ }
+ catch (IOException e) {
+ e.printStackTrace();
+ return 0;
+ }
+
+ return hashImage;
+ }
+
+ private static class ImageDB extends SQLiteOpenHelper{
+
+ private ImageDB(Context context) {
+ super(context, DATA_BASE_NAME, null, CURRENT_VERSION);
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ Log.d(LOG_TAG, " --- Create DB --- ");
+ db.execSQL("create table " + IMAGE_TABLE + " (id integer primary key autoincrement, uri text, hash integer);");
+ db.execSQL("create table " + SET_CARDS_TABLE + " (id integer primary key autoincrement, hash integer, name text, size integer);");
+ db.execSQL("create table " + MAP_TABLE + " (idSet integer, idImage integer);");
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ //TODO
+ }
+ }
+
+ public static ImageWrapped addImageByUri(String uriImage, Context context) {
+ long hashImage = calcHashByUri(uriImage, context);
+ if (hashImage == 0) {
+ Log.d(LOG_TAG, " -- Bad Uri: " + uriImage + " -- ");
+ return null;
+ }
+ ImageWrapped newImage = new ImageWrapped(hashImage, uriImage);
+ newImage.addImage();
+ newImage.setNotEmpty();
+ return newImage;
+ }
+
+ private static boolean checkExistenceByHash(long hashObject, String nameTable) {
+ Cursor c = imageDB.getWritableDatabase().query(nameTable, null, "hash = " + hashObject, null, null, null,null);
+ boolean ans = c.moveToFirst();
+ c.close();
+ return ans;
+ }
+
+ private static boolean checkExistenceByStringField(String valueField, String nameField, String nameTable) {
+ if (valueField == null || nameField == null) {
+ return false;
+ }
+ return checkExistenceByStringField(nameField + " = '" + valueField + "'", nameTable);
+ }
+ private static boolean checkExistenceByStringField(String nameAndValueField, String nameTable ) {
+ if (nameAndValueField == null) {
+ return false;
+ }
+ Cursor c = imageDB.getWritableDatabase().query(nameTable, null, nameAndValueField, null, null, null,null);
+ boolean ans = c.moveToFirst();
+ c.close();
+ return ans;
+ }
+
+ private static Cursor getImageInfo(ImageWrapped curImage) {
+ // Log.d(LOG_TAG, " -- Get image info -- ");
+ Cursor c = imageDB.getWritableDatabase().query(IMAGE_TABLE, null, "hash = " + curImage.hashImage, null, null, null, null);
+ if (c.moveToFirst()) {
+ return c;
+ }
+ if (curImage.uriImage != null) {
+ c = imageDB.getWritableDatabase().query(IMAGE_TABLE, null, "uri = '" + curImage.uriImage + "'", null, null, null, null);
+ if (c.moveToFirst()) {
+ return c;
+ }
+ }
+ c = imageDB.getWritableDatabase().query(IMAGE_TABLE, null, "id = " + curImage.idImage, null, null, null, null);
+ if (c.moveToFirst()) {
+ return c;
+ }
+ return null;
+ }
+
+ private static Cursor getSetCardsInfo(SetCardsWrapped curSetCard) {
+ // Log.d(LOG_TAG, " -- Get set cards info -- ");
+ Cursor c = null;
+ if (curSetCard.nameSetCards != null) {
+ c = imageDB.getWritableDatabase().query(SET_CARDS_TABLE, null, "name = '" + curSetCard.nameSetCards + "'", null, null, null, null);
+ if (c.moveToFirst()) {
+ return c;
+ }
+ }
+ c = imageDB.getWritableDatabase().query(SET_CARDS_TABLE, null, "id = " + curSetCard.idSetCards, null, null, null, null);
+ if (c.moveToFirst()) {
+ return c;
+ }
+ c = imageDB.getWritableDatabase().query(SET_CARDS_TABLE, null, "hash = " + curSetCard.hashSetCards, null, null, null, null);
+ if (c.moveToFirst()) {
+ return c;
+ }
+ return null;
+ }
+
+ private static Cursor getIdCardsByIdSet(long idSetCards) {
+ return imageDB.getWritableDatabase().query(MAP_TABLE, null, "idSet = " + idSetCards, null, null, null, null);
+ }
+
+ private static Cursor getIdCardsByIdImage(long idImage) {
+ return imageDB.getWritableDatabase().query(MAP_TABLE, null, "idImage = " + idImage, null, null, null, null);
+ }
+
+ private static long getHashImageById(long idImage) {
+ Cursor c = imageDB.getWritableDatabase().query(IMAGE_TABLE, null, "id = " + idImage, null, null, null, null);
+ c.moveToFirst();
+ long hashImage = c.getLong(c.getColumnIndex("hash"));
+ c.close();
+ return hashImage;
+ }
+
+ public static class ImageWrapped {
+ private long hashImage = 0;
+ private String uriImage = null;
+ private long idImage = 0;
+ private boolean isEmptyImage = true;
+
+ ImageWrapped(){}
+
+ public ImageWrapped(long newHash) {
+ hashImage = newHash;
+ }
+
+ public ImageWrapped(long hash, String uri) {
+ hashImage = hash;
+ uriImage = uri;
+ }
+
+ private ImageWrapped(Cursor c) {
+ hashImage = c.getLong(c.getColumnIndex("hash"));
+ uriImage = c.getString(c.getColumnIndex("uri"));
+ idImage = c.getLong(c.getColumnIndex("id"));
+ isEmptyImage = false;
+ }
+
+ public void setIdImage(long id) {
+ idImage = id;
+ }
+
+ public void setNotEmpty() { isEmptyImage = false; }
+
+ public String getUriImage() { return uriImage; }
+ public int getIdImage() { return (int)idImage; }
+
+ public boolean exists() {
+ return checkExistenceByHash(hashImage, IMAGE_TABLE)
+ || checkExistenceByStringField(uriImage, "uri", IMAGE_TABLE)
+ || checkExistenceByStringField(String.valueOf(idImage), "id", IMAGE_TABLE);
+ }
+
+ public void setImageInfo() {
+ if (!isEmptyImage) {
+ return;
+ }
+ Cursor c = getImageInfo(this);
+ if (c != null && c.moveToFirst()) {
+ uriImage = c.getString(c.getColumnIndex("uri"));
+ idImage = c.getLong(c.getColumnIndex("id"));
+ c.close();
+ isEmptyImage = false;
+ }
+ else {
+ Log.d(LOG_TAG, "Wrong image");
+ }
+ imageDB.close();
+ }
+
+ public ImageView getViewImage(Context context) {
+ if (!exists()) {
+ return null;
+ }
+ if (isEmptyImage) {
+ setImageInfo();
+ }
+ ImageView imageView = new ImageView(context);
+ imageView.setImageURI(Uri.parse(uriImage));
+ return imageView;
+ }
+
+ public boolean addImage() {
+ if (exists()) {
+ setImageInfo();
+ return false;
+ }
+ ContentValues cv = new ContentValues();
+ cv.put("uri", uriImage);
+ cv.put("hash", hashImage);
+ setIdImage(imageDB.getWritableDatabase().insert(IMAGE_TABLE, null, cv));
+ imageDB.close();
+ return true;
+ }
+
+ public void deleteImage() {
+ if (!exists()) {
+ return;
+ }
+ if (isEmptyImage) {
+ setImageInfo();
+ }
+ imageDB.getWritableDatabase().delete(IMAGE_TABLE, "hash = " + hashImage, null);
+ imageDB.getWritableDatabase().delete(MAP_TABLE, "idImage = " + idImage, null);
+ imageDB.close();
+ }
+ public ImageView getImageView(Context context) {
+ if (isEmptyImage) {
+ setImageInfo();
+ }
+ ImageView imageView = new ImageView(context);
+ imageView.setImageURI(Uri.parse(getUriImage()));
+ return imageView;
+ }
+
+ public ImageView getPreview(Context context, int size) throws FileNotFoundException {
+ final double EPS = 0.50001;
+ if (isEmptyImage) {
+ setImageInfo();
+ }
+ InputStream fileStream = context.getContentResolver().openInputStream(Uri.parse(uriImage));
+
+ BitmapFactory.Options sizeOptions = new BitmapFactory.Options();
+ sizeOptions.inJustDecodeBounds = true;
+ BitmapFactory.decodeStream(fileStream, null, sizeOptions);
+ int realSize = Math.min(sizeOptions.outHeight, sizeOptions.outWidth);
+ int scale = (int)((double)realSize / size + EPS);
+
+ BitmapFactory.Options readOptions = new BitmapFactory.Options();
+ readOptions.inSampleSize = scale;
+ fileStream = context.getContentResolver().openInputStream(Uri.parse(uriImage));
+ Bitmap imageBitmap = BitmapFactory.decodeStream(fileStream, null, readOptions);
+ ImageView ret = new ImageView(context);
+ ret.setImageBitmap(imageBitmap);
+
+ return ret;
+ }
+
+ public static ImageWrapped createById(int idNewImage) {
+ ImageWrapped newImage = new ImageWrapped();
+ newImage.setIdImage(idNewImage);
+ newImage.setImageInfo();
+ return newImage;
+ }
+ }
+
+ public static class SetCardsWrapped{
+ private long idSetCards = 0;
+ private long hashSetCards = 0;
+ private String nameSetCards = null;
+ private int sizeSetCards = 0;
+ private boolean isEmptySetCards = true;
+
+ SetCardsWrapped(){}
+
+ private SetCardsWrapped(Cursor c) {
+ Log.d(LOG_TAG, "q");
+ hashSetCards = c.getLong(c.getColumnIndex("hash"));
+ idSetCards = c.getLong(c.getColumnIndex("id"));
+ nameSetCards = c.getString(c.getColumnIndex("name"));
+ sizeSetCards = c.getInt(c.getColumnIndex("size"));
+ isEmptySetCards = false;
+ }
+
+ public long getHash() {
+ return hashSetCards;
+ }
+
+ public void setIdSetCards(long id) {
+ idSetCards = id;
+ }
+
+ public void setName(String name) {
+ nameSetCards = name;
+ }
+
+ public void setNotEmpty() { isEmptySetCards = false; }
+
+ public String getNameSetCards() {
+ return nameSetCards;
+ }
+
+ public int getSizeSetCards() {
+ return sizeSetCards;
+ }
+
+ public boolean checkExistenceSet() {
+ return ImageStorage.checkExistenceByHash(hashSetCards, SET_CARDS_TABLE)
+ || checkExistenceByStringField(nameSetCards, "name", SET_CARDS_TABLE)
+ || checkExistenceByStringField(String.valueOf(idSetCards), "id", SET_CARDS_TABLE);
+ }
+
+ public static boolean checkExistenceByHash(long hash) {
+ return ImageStorage.checkExistenceByHash(hash, SET_CARDS_TABLE);
+ }
+
+ public void setSetCardsInfo() {
+ if (!isEmptySetCards) {
+ return;
+ }
+ Cursor c = getSetCardsInfo(this);
+ if (c != null && c.moveToFirst()) {
+ idSetCards = c.getLong(c.getColumnIndex("id"));
+ nameSetCards = c.getString(c.getColumnIndex("name"));
+ sizeSetCards = c.getInt(c.getColumnIndex("size"));
+ c.close();
+ isEmptySetCards = false;
+ }
+ else {
+ Log.d(LOG_TAG, "Wrong set cards hash " + hashSetCards);
+ }
+ imageDB.close();
+ }
+
+ boolean checkExistenceAllCardsOfSet() {
+ Log.d(LOG_TAG, " -- Calc set cards hash -- ");
+ if (!checkExistenceSet()){
+ Log.d(LOG_TAG, " -- Set is not found -- ");
+ return false;
+ }
+ if (isEmptySetCards) {
+ setSetCardsInfo();
+ }
+ Cursor c = getIdCardsByIdSet(idSetCards);
+ long sumImageHash = 0;
+ int cntFound = 0;
+ if (c.moveToFirst()) {
+ do {
+ long idImage = c.getLong(c.getColumnIndex("idImage"));
+ sumImageHash += getHashImageById(idImage);
+ ++cntFound;
+ } while (c.moveToNext());
+ c.close();
+ }
+ imageDB.close();
+ return sumImageHash == hashSetCards && cntFound == sizeSetCards;
+ }
+
+ public void updateCardsSet() {
+ setSetCardsInfo();
+ Cursor c = getIdCardsByIdSet(idSetCards);
+ long sumImageHash = 0;
+ int cntFound = 0;
+ if (c.moveToFirst()) {
+ do {
+ long idImage = c.getLong(c.getColumnIndex("idImage"));
+ sumImageHash += getHashImageById(idImage);
+ ++cntFound;
+ } while (c.moveToNext());
+ c.close();
+ }
+ hashSetCards = sumImageHash;
+ sizeSetCards = cntFound;
+ ContentValues cv = new ContentValues();
+ cv.put("hash", hashSetCards);
+ cv.put("size", sizeSetCards);
+ imageDB.getWritableDatabase().update(SET_CARDS_TABLE, cv, "id = " + idSetCards, null);
+ imageDB.close();
+ }
+
+ public boolean addSetCards() {
+ if (checkExistenceSet()) {
+ setSetCardsInfo();
+ return false;
+ }
+ setNotEmpty();
+ ContentValues cv = new ContentValues();
+ cv.put("hash", hashSetCards);
+ cv.put("name", nameSetCards);
+ cv.put("size", sizeSetCards);
+ setIdSetCards(imageDB.getWritableDatabase().insert(SET_CARDS_TABLE, null, cv));
+ imageDB.close();
+ return true;
+ }
+
+ public boolean addCardToSet(ImageWrapped newImage) {
+ if (!newImage.exists()) {
+ newImage.addImage();
+ }
+ else {
+ newImage.setImageInfo();
+ }
+ if (!checkExistenceByStringField("idSet = " + idSetCards + " and idImage = " + newImage.idImage, MAP_TABLE)) {
+ ContentValues cv = new ContentValues();
+ cv.put("idSet", idSetCards);
+ cv.put("idImage", newImage.idImage);
+ hashSetCards += newImage.hashImage;
+ sizeSetCards++;
+ imageDB.getWritableDatabase().insert(MAP_TABLE, null, cv);
+ cv.clear();
+ cv.put("hash", hashSetCards);
+ cv.put("size", sizeSetCards);
+ imageDB.getWritableDatabase().update(SET_CARDS_TABLE, cv, "id = " + idSetCards, null);
+ imageDB.close();
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+
+ public int addCardsToSet(Collection allNewCards) {
+ int cntAddedCard = 0;
+ for (ImageWrapped curCard : allNewCards) {
+ if (addCardToSet(curCard)) {
+ cntAddedCard++;
+ }
+ }
+ return cntAddedCard;
+ }
+
+ public void deleteCardFromSet(ImageWrapped deletedImage) {
+ if (checkExistenceByStringField("idSet = " + idSetCards + " and idImage = " + deletedImage.idImage, MAP_TABLE)) {
+ sizeSetCards--;
+ hashSetCards -= deletedImage.hashImage;
+ ContentValues cv = new ContentValues();
+ cv.put("hash", hashSetCards);
+ cv.put("size", sizeSetCards);
+ imageDB.getWritableDatabase().update(SET_CARDS_TABLE, cv, "id = " + idSetCards, null);
+ imageDB.getWritableDatabase().delete(MAP_TABLE, "idSet = " + idSetCards + " and idImage = " + deletedImage.idImage, null);
+ imageDB.close();
+ }
+ }
+
+ public void deleteCardFromSet(Collection allDeletedCards) {
+ for (ImageWrapped curCard : allDeletedCards) {
+ deleteCardFromSet(curCard);
+ }
+ }
+
+ public void deleteSetCards() {
+ if (!checkExistenceSet()) {
+ return;
+ }
+ if (isEmptySetCards) {
+ setSetCardsInfo();
+ }
+ imageDB.getWritableDatabase().delete(SET_CARDS_TABLE, "hash = " + hashSetCards, null);
+ imageDB.getWritableDatabase().delete(MAP_TABLE, "idSet = " + idSetCards, null);
+ imageDB.close();
+ }
+
+ public ArrayList getListOfCards() {
+ ArrayList ansList = new ArrayList();
+ Cursor c = getIdCardsByIdSet(idSetCards);
+ if (c.moveToFirst()) {
+ do {
+ Cursor curC = imageDB.getWritableDatabase().query(IMAGE_TABLE, null, "id = " + c.getLong(c.getColumnIndex("idImage")), null, null, null, null);
+ if (curC.moveToFirst()) {
+ ansList.add(new ImageWrapped(curC));
+ curC.close();
+ }
+ } while (c.moveToNext());
+ c.close();
+ }
+ imageDB.close();
+ return ansList;
+ }
+ public static SetCardsWrapped createById(int idSetCards) {
+ SetCardsWrapped newSetCards = new SetCardsWrapped();
+ newSetCards.setIdSetCards(idSetCards);
+ newSetCards.setSetCardsInfo();
+ return newSetCards;
+ }
+ }
+ public static ArrayList getAllSetsCards() {
+ ArrayList ansList = new ArrayList();
+ Cursor c = imageDB.getWritableDatabase().query(SET_CARDS_TABLE, null, null, null, null, null, null);
+ if (c.moveToFirst()) {
+ do {
+ SetCardsWrapped curSet = new SetCardsWrapped(c);
+ curSet.updateCardsSet();
+ ansList.add(curSet);
+ } while (c.moveToNext());
+ c.close();
+ }
+ imageDB.close();
+ return ansList;
+ }
+ public static ArrayList getAllImages() {
+ ArrayList ansList = new ArrayList<>();
+ Cursor c = imageDB.getWritableDatabase().query(IMAGE_TABLE, null, null, null, null, null, null);
+ if (c.moveToFirst()) {
+ do {
+ ImageWrapped curImage = new ImageWrapped(c);
+ ansList.add(curImage);
+ } while (c.moveToNext());
+ c.close();
+ }
+ imageDB.close();
+ return ansList;
+ }
+}
diff --git a/app/src/main/java/ru/spbau/resemblance/JoinGameActivity.java b/app/src/main/java/ru/spbau/resemblance/JoinGameActivity.java
new file mode 100644
index 0000000..03e1353
--- /dev/null
+++ b/app/src/main/java/ru/spbau/resemblance/JoinGameActivity.java
@@ -0,0 +1,25 @@
+package ru.spbau.resemblance;
+
+import android.content.Intent;
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.EditText;
+
+public class JoinGameActivity extends AppCompatActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_join_game);
+ }
+
+ public void onJoinPressed(View v) {
+ String gameCreatorName = ((EditText)findViewById(R.id.joinGameNameField)).getText().toString();
+ Message.sendJoinFriendGameMessage(gameCreatorName);
+
+ Intent waitForGame = new Intent(this, GameExpectationActivity.class);
+ waitForGame.putExtra(GameExpectationActivity.RANDOM_GAME_EXTRA, false);
+ startActivity(waitForGame);
+ }
+}
diff --git a/app/src/main/java/ru/spbau/resemblance/LeadingAssociationActivity.java b/app/src/main/java/ru/spbau/resemblance/LeadingAssociationActivity.java
new file mode 100644
index 0000000..49924d9
--- /dev/null
+++ b/app/src/main/java/ru/spbau/resemblance/LeadingAssociationActivity.java
@@ -0,0 +1,45 @@
+package ru.spbau.resemblance;
+
+import android.content.Intent;
+import android.net.Uri;
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.ImageView;
+
+public class LeadingAssociationActivity extends AppCompatActivity {
+ public final static String IMAGE_PARAM = "image";
+
+ private EditText associationField;
+ private ImageStorage.ImageWrapped image;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_leading_association);
+
+ associationField = (EditText)findViewById(R.id.leadingAssociationField);
+ ImageView imageView = (ImageView) findViewById(R.id.leadingAssociationImage);
+
+ image = ImageStorage.ImageWrapped.createById((int)getIntent().getLongExtra(IMAGE_PARAM, -1L));
+ imageView.setImageURI(Uri.parse(image.getUriImage()));
+ }
+
+ public void onDoneClick(View v) {
+ Intent ret = new Intent();
+ ret.putExtra(GameIntermediateActivity.PICTURE_PARAM, (long)image.getIdImage());
+ ret.putExtra(GameIntermediateActivity.ASSOCIATION_PARAM, associationField.getText().toString());
+ setResult(RESULT_OK, ret);
+ finish();
+ }
+
+ @Override
+ public void onBackPressed() {
+ Intent ret = new Intent();
+ ret.putExtra(GameIntermediateActivity.PICTURE_PARAM, -1L);
+ ret.putExtra(GameIntermediateActivity.ASSOCIATION_PARAM, (String)null);
+ setResult(RESULT_OK, ret);
+ finish();
+ }
+}
diff --git a/app/src/main/java/ru/spbau/resemblance/ListOfSetsActivity.java b/app/src/main/java/ru/spbau/resemblance/ListOfSetsActivity.java
new file mode 100644
index 0000000..e9ebb89
--- /dev/null
+++ b/app/src/main/java/ru/spbau/resemblance/ListOfSetsActivity.java
@@ -0,0 +1,46 @@
+package ru.spbau.resemblance;
+
+import android.content.Intent;
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+
+import java.util.ArrayList;
+
+public class ListOfSetsActivity extends AppCompatActivity implements AdapterView.OnItemClickListener {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_list_of_sets);
+
+ ListView setsView = (ListView)findViewById(R.id.setsList);
+
+ ArrayList sets = new ArrayList<>();
+ for (ImageStorage.SetCardsWrapped set: ImageStorage.getAllSetsCards()) {
+ sets.add(String.format(getString(R.string.list_of_sets_item), set.getNameSetCards(),
+ set.getSizeSetCards()));
+ }
+ ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, sets);
+ setsView.setAdapter(adapter);
+
+ setsView.setOnItemClickListener(this);
+
+ setTitle(R.string.sets_title);
+ }
+
+ @Override
+ public void onItemClick(AdapterView> parent, View view, int position, long id) {
+ Intent showCards = new Intent(ListOfSetsActivity.this, GalleryActivity.class);
+ ArrayList cards =
+ ImageStorage.getAllSetsCards().get(position).getListOfCards();
+ long[] cardIds = new long[cards.size()];
+ for (int i = 0; i < cardIds.length; i++) {
+ cardIds[i] = cards.get(i).getIdImage();
+ }
+ showCards.putExtra(GalleryActivity.CARDS_PARAM, cardIds);
+ startActivity(showCards);
+ }
+}
diff --git a/app/src/main/java/ru/spbau/resemblance/LoginActivity.java b/app/src/main/java/ru/spbau/resemblance/LoginActivity.java
new file mode 100644
index 0000000..16aa4cd
--- /dev/null
+++ b/app/src/main/java/ru/spbau/resemblance/LoginActivity.java
@@ -0,0 +1,109 @@
+package ru.spbau.resemblance;
+
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.Toast;
+
+import java.math.BigInteger;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+public class LoginActivity extends AppCompatActivity implements Message.LoginMessageListener {
+ public static final int NETWORK_ERROR = -1;
+ public static final int SUCCESSFUL_LOGIN = 0;
+ public static final int NICKNAME_ERROR = 1;
+ public static final int PASSWORD_ERROR = 2;
+
+ private EditText nicknameField = null;
+ private EditText passwordField = null;
+ private String passwordHash = null;
+ private String nickname = null;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_login);
+
+ nicknameField = (EditText)findViewById(R.id.loginNicknameField);
+ passwordField = (EditText)findViewById(R.id.loginPasswordField);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ Message.setLoginListener(this);
+ }
+
+ public void onLoginClick(View v) {
+ nickname = nicknameField.getText().toString();
+ String password = passwordField.getText().toString();
+
+ //Getting the hash of the password.
+ try {
+ MessageDigest messageDigest = MessageDigest.getInstance("MD5");
+ byte[] hash = messageDigest.digest(password.getBytes());
+ passwordHash = new BigInteger(1, hash).toString();
+ Message.sendLoginMessage(nickname, passwordHash);
+ } catch (NoSuchAlgorithmException e) {
+ Log.d("MD5", "MD5 Algorithm not found.");
+ }
+ }
+
+ public void onRegisterClick(View v) {
+ Intent register = new Intent(this, RegistrationActivity.class);
+ startActivity(register);
+ }
+
+ @Override
+ public void onLoginResponse(int code) {
+ switch (code) {
+ case SUCCESSFUL_LOGIN: {
+ SharedPreferences prefs = getSharedPreferences(SettingsActivity.PREFERENCES, MODE_PRIVATE);
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putString(SettingsActivity.NICKNAME_PREF, nickname);
+ editor.putString(SettingsActivity.PASSWORD_PREF, passwordHash);
+ editor.apply();
+
+ finish();
+ break;
+ }
+ case PASSWORD_ERROR: {
+ showToast(R.string.login_password_error);
+ break;
+ }
+ case NICKNAME_ERROR: {
+ showToast(R.string.login_nickname_error);
+ break;
+ }
+ case NETWORK_ERROR: {
+ showToast(R.string.login_network_error);
+ break;
+ }
+ }
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ }
+
+ void showToast(final int resId) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ Toast.makeText(LoginActivity.this, resId, Toast.LENGTH_SHORT).show();
+ }
+ });
+ }
+
+ public void onIPChangeClick(View v) {
+ SettingsActivity.SetServerIPDialog dialog = new SettingsActivity.SetServerIPDialog();
+ dialog.show(getSupportFragmentManager(), "DeletePlayerDialog");
+ }
+}
diff --git a/app/src/main/java/ru/spbau/resemblance/MainActivity.java b/app/src/main/java/ru/spbau/resemblance/MainActivity.java
index 5255233..80fb7e9 100644
--- a/app/src/main/java/ru/spbau/resemblance/MainActivity.java
+++ b/app/src/main/java/ru/spbau/resemblance/MainActivity.java
@@ -1,13 +1,106 @@
package ru.spbau.resemblance;
-import android.support.v7.app.AppCompatActivity;
+import android.content.Intent;
+import android.content.SharedPreferences;
import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.view.View;
+import android.widget.TextView;
+import android.widget.Toast;
-public class MainActivity extends AppCompatActivity {
+public class MainActivity extends AppCompatActivity implements Message.LoginMessageListener,
+ Message.RatingMessageListener {
+ private TextView nicknameText = null;
+ private TextView ratingText = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
+
+ nicknameText = (TextView)findViewById(R.id.mainNickname);
+ ratingText = (TextView)findViewById(R.id.mainRatingText);
+
+ ImageStorage.createImageStorage(this);
+ SendMessageModule.connectToServer(SendMessageModule.getServerIP(this));
+
+ Message.setRatingListener(this);
+
+ SharedPreferences preferences = getSharedPreferences(SettingsActivity.PREFERENCES, MODE_PRIVATE);
+ if (preferences.contains(SettingsActivity.NICKNAME_PREF)) {
+ Message.sendLoginMessage(preferences.getString(SettingsActivity.NICKNAME_PREF, null),
+ preferences.getString(SettingsActivity.PASSWORD_PREF, null));
+
+ Message.setLoginListener(this);
+ } else {
+ Intent login = new Intent(this, LoginActivity.class);
+ startActivity(login);
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ SharedPreferences preferences = getSharedPreferences(SettingsActivity.PREFERENCES, MODE_PRIVATE);
+ nicknameText.setText(preferences.getString(SettingsActivity.NICKNAME_PREF, ""));
+ ratingText.setText(String.format(getString(R.string.main_rating_text),
+ preferences.getInt(SettingsActivity.RATING_PREF, -1)));
+ }
+
+ public void onRandomGameClick(View v) {
+ Intent randomGame = new Intent(this, GameExpectationActivity.class);
+ randomGame.putExtra(GameExpectationActivity.RANDOM_GAME_EXTRA, true);
+ startActivity(randomGame);
+ }
+
+ public void onCreateGameClick(View v) {
+ Intent newGame = new Intent(this, CreateGameActivity.class);
+ startActivity(newGame);
+ }
+
+ public void onJoinGameClick(View v) {
+ Intent joinGame = new Intent(this, JoinGameActivity.class);
+ startActivity(joinGame);
+ }
+
+ public void onSettingsClick(View v) {
+ Intent settings = new Intent(this, SettingsActivity.class);
+ startActivity(settings);
+ }
+
+ public void onSetCardsClick(View v) {
+ Intent setCards = new Intent(this, CardSetsActivity.class);
+ startActivity(setCards);
+ }
+
+ @Override
+ public void onLoginResponse(int code) {
+ if (code != LoginActivity.SUCCESSFUL_LOGIN) {
+ Intent login = new Intent(this, LoginActivity.class);
+ startActivity(login);
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ Toast.makeText(MainActivity.this, R.string.authorisation_required,
+ Toast.LENGTH_SHORT).show();
+ }
+ });
+ }
+ }
+
+ @Override
+ public void onRatingMessage(final int rating) {
+ SharedPreferences prefs = getSharedPreferences(SettingsActivity.PREFERENCES, MODE_PRIVATE);
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putInt(SettingsActivity.RATING_PREF, rating);
+ editor.apply();
+
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ ratingText.setText(String.format(getString(R.string.main_rating_text), rating));
+ }
+ });
}
}
diff --git a/app/src/main/java/ru/spbau/resemblance/Message.java b/app/src/main/java/ru/spbau/resemblance/Message.java
new file mode 100644
index 0000000..7bfde14
--- /dev/null
+++ b/app/src/main/java/ru/spbau/resemblance/Message.java
@@ -0,0 +1,611 @@
+package ru.spbau.resemblance;
+
+import android.util.Log;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+
+public class Message {
+ final public static int TEST_TYPE = 0;
+ final public static int REGISTER_TYPE = 1;
+ final public static int LOGIN_TYPE = 2;
+ final public static int JOIN_RANDOM_GAME_TYPE = 3;
+ final public static int QUIT_RANDOM_GAME_TYPE = 4;
+ final public static int START_GAME_TYPE = 5;
+ final public static int SEND_CARD_TYPE = 6;
+ final public static int LEAD_REQUEST_TYPE = 7;
+ final public static int LEAD_ASSOCIATION_TYPE = 8;
+ final public static int CHOICE_REQUEST_TYPE = 9;
+ final public static int CHOICE_TYPE = 10;
+ final public static int VOTE_REQUEST_TYPE = 11;
+ final public static int VOTE_TYPE = 12;
+ final public static int ROUND_END_TYPE = 13;
+ final public static int RATING_TYPE = 14;
+ final public static int CREATE_FIEND_GAME_TYPE = 15;
+ final public static int JOIN_FRIEND_GAME_TYPE = 16;
+ final public static int FRIEND_GAME_PLAYER_TYPE = 17;
+ final public static int REMOVE_PLAYER_TYPE = 18;
+ final public static int CANCEL_FRIEND_GAME_TYPE = 19;
+ final public static int START_FRIEND_GAME_TYPE = 20;
+ final public static int GAME_FINISH_TYPE = 21;
+ final public static int GAME_CANCELED_TYPE = 22;
+ final public static int QUIT_FIEND_GAME_TYPE = 23;
+ final public static int PASSWORD_CHANGE_REQUEST_TYPE = 24;
+ final public static int PASSWORD_CHANGE_RESPONSE_TYPE = 25;
+ final public static int OUTGOING_CHAT_MESSAGE_TYPE = 26;
+ final public static int INGOING_CHAT_MESSAGE = 27;
+
+ private final String MESSAGE_LOG_TAG = "ChatMessage";
+
+ private static volatile LoginMessageListener loginListener = null;
+ private static volatile RegisterMessageListener registerListener = null;
+ private static volatile RatingMessageListener ratingListener = null;
+ private static volatile GameMessageListener gameListener = null;
+ private static volatile GameExpectationMessageListener gameExpectationListener = null;
+ private static volatile FriendGamePreparationListener preparationListener = null;
+ private static volatile PasswordChangeListener passwordChangeListener = null;
+
+ private int type = 0;
+
+ Message(int type) {
+ this.type = type;
+ }
+
+ public void readMessage(DataInputStream in) {
+ switch (type) {
+ case TEST_TYPE:
+ readTestMessage(in);
+ break;
+ case REGISTER_TYPE:
+ readRegisterMessage(in);
+ break;
+ case LOGIN_TYPE:
+ readLoginMessage(in);
+ break;
+ case START_GAME_TYPE:
+ readStartGameMessage(in);
+ break;
+ case SEND_CARD_TYPE:
+ readSendCardMessage(in);
+ break;
+ case LEAD_REQUEST_TYPE:
+ readLeadRequestMessage(in);
+ break;
+ case CHOICE_REQUEST_TYPE:
+ readChoiceRequestMessage(in);
+ break;
+ case VOTE_REQUEST_TYPE:
+ readVoteRequestMessage(in);
+ break;
+ case ROUND_END_TYPE:
+ readRoundEndMessage(in);
+ break;
+ case RATING_TYPE:
+ readRatingMessage(in);
+ break;
+ case FRIEND_GAME_PLAYER_TYPE:
+ readFriendGamePlayerMessage(in);
+ break;
+ case GAME_FINISH_TYPE:
+ readGameFinishMessage(in);
+ break;
+ case GAME_CANCELED_TYPE:
+ readGameCancelledMessage(in);
+ break;
+ case PASSWORD_CHANGE_RESPONSE_TYPE:
+ readPasswordChangeMessage(in);
+ break;
+ case INGOING_CHAT_MESSAGE:
+ readChatMessage(in);
+ break;
+ }
+ }
+
+ private void readTestMessage(DataInputStream in) {
+ String testMessage = null;
+ try {
+ synchronized (in) {
+ testMessage = in.readUTF();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ applyTest(testMessage);
+ }
+
+ private void readRegisterMessage(DataInputStream in) {
+ Log.d(MESSAGE_LOG_TAG, "Register message received.");
+ int resultCode = -1;
+ try {
+ synchronized (in) {
+ resultCode = in.readInt();
+ }
+ registerListener.onRegisterResponse(resultCode);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private void readLoginMessage(DataInputStream in) {
+ Log.d(MESSAGE_LOG_TAG, "Login message received.");
+ int resultCode = -1;
+ try {
+ synchronized (in) {
+ resultCode = in.readInt();
+ }
+ loginListener.onLoginResponse(resultCode);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private void readStartGameMessage(DataInputStream stream) {
+ Log.d(MESSAGE_LOG_TAG, "Start game message received.");
+ try {
+ int roundsNumber = stream.readInt();
+ int playersNumber = stream.readInt();
+ ArrayList names = new ArrayList<>();
+ for (int i = 0; i < playersNumber; i++) {
+ names.add(stream.readUTF());
+ }
+ while (gameExpectationListener == null) {
+ try {
+ Thread.sleep(5);
+ } catch (InterruptedException e) {}
+ }
+ long expectationTime = stream.readLong();
+ gameExpectationListener.onStartGameMessage(roundsNumber, playersNumber, names, expectationTime);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private void readSendCardMessage(DataInputStream stream) {
+ Log.d(MESSAGE_LOG_TAG, "Send card message received.");
+ try {
+ long card = stream.readLong();
+ while (gameListener == null) {
+ try {
+ Thread.sleep(10);
+ } catch (InterruptedException e) {}
+ }
+ gameListener.onSendCard(card);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private void readLeadRequestMessage(DataInputStream stream) {
+ Log.d(MESSAGE_LOG_TAG, "Lead message received.");
+ gameListener.onLeadRequest();
+ }
+
+ private void readChoiceRequestMessage(DataInputStream stream) {
+ Log.d(MESSAGE_LOG_TAG, "Choice message received.");
+ try {
+ String association = stream.readUTF();
+ gameListener.onChoiceRequest(association);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private void readVoteRequestMessage(DataInputStream stream) {
+ Log.d(MESSAGE_LOG_TAG, "Vote message received.");
+ try {
+ String association = stream.readUTF();
+ int cardsNumber = stream.readInt();
+ long[] candidates = new long[cardsNumber];
+ for (int i = 0; i < cardsNumber; i++) {
+ candidates[i] = stream.readLong();
+ }
+ gameListener.onVoteRequest(association, candidates);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void readRatingMessage(DataInputStream stream) {
+ Log.d(MESSAGE_LOG_TAG, "Rating message received.");
+ try {
+ int rating = stream.readInt();
+ ratingListener.onRatingMessage(rating);
+ } catch (IOException e) {}
+ }
+
+ public void readRoundEndMessage(DataInputStream stream) {
+ try {
+ long leaderAssociation = stream.readLong();
+ int[] scores = new int[stream.readInt()];
+ for (int i = 0; i < scores.length; i++) {
+ scores[i] = stream.readInt();
+ }
+ gameListener.onRoundEnd(leaderAssociation, scores);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private void readFriendGamePlayerMessage(DataInputStream stream) {
+ try {
+ boolean joined = stream.readBoolean();
+ String playerName = stream.readUTF();
+ Log.d(MESSAGE_LOG_TAG, "FriendGamePlayerMessage received: " + joined);
+ while (preparationListener == null) {
+ try {
+ Thread.sleep(5);
+ } catch (InterruptedException e) {}
+ }
+ if (joined) {
+ preparationListener.onNewPlayer(playerName);
+ } else {
+ preparationListener.onGonePlayer(playerName);
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private void readGameFinishMessage(DataInputStream stream) {
+ try {
+ long lastAnswer = stream.readLong();
+ int playersNumber = stream.readInt();
+ ArrayList finalScores = new ArrayList<>();
+ for (int i = 0; i < playersNumber; i++) {
+ finalScores.add(stream.readInt());
+ }
+ ArrayList oldRatings = new ArrayList<>();
+ for (int i = 0; i < playersNumber; i++) {
+ oldRatings.add(stream.readInt());
+ }
+ ArrayList newRatings = new ArrayList<>();
+ for (int i = 0; i < playersNumber; i++) {
+ newRatings.add(stream.readInt());
+ }
+ gameListener.onGameFinish(lastAnswer, finalScores, oldRatings, newRatings);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private void readGameCancelledMessage(DataInputStream stream) {
+ while (gameExpectationListener == null) {
+ try {
+ Thread.sleep(5);
+ } catch (InterruptedException e) {}
+ }
+ gameExpectationListener.onGameCancelled();
+ }
+
+ private void readPasswordChangeMessage(DataInputStream stream) {
+ try {
+ int code = stream.readInt();
+ passwordChangeListener.onPasswordChangeResponse(code);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private void readChatMessage(DataInputStream stream) {
+ Log.d(MESSAGE_LOG_TAG, "ChatMessage received.");
+ try {
+ String author = stream.readUTF();
+ String time = stream.readUTF();
+ String text = stream.readUTF();
+ Chat.receiveMessage(author, time, text);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ //----------------------------------------------------
+
+
+ private void applyTest(String textMessage) {
+ System.out.println(textMessage);
+ }
+
+ //----------------------------------------------------
+
+ public void sendTestMessage(String textMessage) {
+ ByteArrayOutputStream byteOS = new ByteArrayOutputStream(100);
+ DataOutputStream out = new DataOutputStream(byteOS);
+ try {
+ out.writeInt(TEST_TYPE);
+ out.writeUTF(textMessage);
+ out.flush();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ SendMessageModule.sendMessage(byteOS.toByteArray());
+ }
+
+ public static void sendJoinRandomGameMessage() {
+ ByteArrayOutputStream byteOS = new ByteArrayOutputStream(100);
+ DataOutputStream out = new DataOutputStream(byteOS);
+ try {
+ out.writeInt(JOIN_RANDOM_GAME_TYPE);
+ out.flush();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ SendMessageModule.sendMessage(byteOS.toByteArray());
+ }
+
+ public static void sendQuitRandomGameMessage() {
+ ByteArrayOutputStream byteOS = new ByteArrayOutputStream(100);
+ DataOutputStream out = new DataOutputStream(byteOS);
+ try {
+ out.writeInt(QUIT_RANDOM_GAME_TYPE);
+ out.flush();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ SendMessageModule.sendMessage(byteOS.toByteArray());
+ }
+
+ public static void sendLeadAssociationMessage(long card, String association) {
+ ByteArrayOutputStream byteOS = new ByteArrayOutputStream(100);
+ DataOutputStream out = new DataOutputStream(byteOS);
+ try {
+ out.writeInt(LEAD_ASSOCIATION_TYPE);
+ out.writeLong(card);
+ out.writeUTF(association);
+ out.flush();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ SendMessageModule.sendMessage(byteOS.toByteArray());
+ }
+
+ public static void sendChoiceMessage(long card) {
+ ByteArrayOutputStream byteOS = new ByteArrayOutputStream(100);
+ DataOutputStream out = new DataOutputStream(byteOS);
+ try {
+ out.writeInt(CHOICE_TYPE);
+ out.writeLong(card);
+ out.flush();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ SendMessageModule.sendMessage(byteOS.toByteArray());
+ }
+
+ public static void sendVoteMessage(long card) {
+ ByteArrayOutputStream byteOS = new ByteArrayOutputStream(100);
+ DataOutputStream out = new DataOutputStream(byteOS);
+ try {
+ out.writeInt(VOTE_TYPE);
+ out.writeLong(card);
+ out.flush();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ SendMessageModule.sendMessage(byteOS.toByteArray());
+ }
+
+ public static void sendLoginMessage(String nickname, String password) {
+ ByteArrayOutputStream byteOS = new ByteArrayOutputStream(150);
+ DataOutputStream out = new DataOutputStream(byteOS);
+ try {
+ out.writeInt(LOGIN_TYPE);
+ out.writeUTF(nickname);
+ out.writeUTF(password);
+ out.flush();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ SendMessageModule.sendMessage(byteOS.toByteArray());
+ }
+
+ public static void sendRegisterMessage(String nickname, String password) {
+ ByteArrayOutputStream byteOS = new ByteArrayOutputStream(150);
+ DataOutputStream out = new DataOutputStream(byteOS);
+ try {
+ out.writeInt(REGISTER_TYPE);
+ out.writeUTF(nickname);
+ out.writeUTF(password);
+ out.flush();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ SendMessageModule.sendMessage(byteOS.toByteArray());
+ }
+
+ public static void sendCreateGameMessage(int roundsNumber, ArrayList cards,
+ long expectationTime, long cardSetHash) {
+ ByteArrayOutputStream byteOS = new ByteArrayOutputStream(150);
+ DataOutputStream out = new DataOutputStream(byteOS);
+ try {
+ out.writeInt(CREATE_FIEND_GAME_TYPE);
+ out.writeInt(roundsNumber);
+ out.writeInt(cards.size());
+ for (ImageStorage.ImageWrapped image: cards) {
+ out.writeLong(image.getIdImage());
+ }
+ out.writeLong(expectationTime);
+ out.writeLong(cardSetHash);
+ out.flush();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ SendMessageModule.sendMessage(byteOS.toByteArray());
+ }
+
+ public static void sendJoinFriendGameMessage(String gameCreatorName) {
+ ByteArrayOutputStream byteOS = new ByteArrayOutputStream(150);
+ DataOutputStream out = new DataOutputStream(byteOS);
+ try {
+ out.writeInt(JOIN_FRIEND_GAME_TYPE);
+ out.writeUTF(gameCreatorName);
+ out.flush();
+ SendMessageModule.sendMessage(byteOS.toByteArray());
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public static void sendStartFriendGameMessage() {
+ ByteArrayOutputStream byteOS = new ByteArrayOutputStream(150);
+ DataOutputStream out = new DataOutputStream(byteOS);
+ try {
+ out.writeInt(START_FRIEND_GAME_TYPE);
+ out.flush();
+ SendMessageModule.sendMessage(byteOS.toByteArray());
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public static void sendCancelFriendGameMessage() {
+ ByteArrayOutputStream byteOS = new ByteArrayOutputStream(150);
+ DataOutputStream out = new DataOutputStream(byteOS);
+ try {
+ out.writeInt(CANCEL_FRIEND_GAME_TYPE);
+ out.flush();
+ SendMessageModule.sendMessage(byteOS.toByteArray());
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public static void sendRemovePlayerMessage(String playerName) {
+ ByteArrayOutputStream byteOS = new ByteArrayOutputStream(150);
+ DataOutputStream out = new DataOutputStream(byteOS);
+ try {
+ out.writeInt(REMOVE_PLAYER_TYPE);
+ out.writeUTF(playerName);
+ out.flush();
+ SendMessageModule.sendMessage(byteOS.toByteArray());
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public static void sendQuitFriendGameMessage() {
+ ByteArrayOutputStream byteOS = new ByteArrayOutputStream(150);
+ DataOutputStream out = new DataOutputStream(byteOS);
+ try {
+ out.writeInt(QUIT_FIEND_GAME_TYPE);
+ out.flush();
+ SendMessageModule.sendMessage(byteOS.toByteArray());
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public static void sendPasswordChangeMessage(String oldPassword, String newPassword) {
+ ByteArrayOutputStream byteOS = new ByteArrayOutputStream(150);
+ DataOutputStream out = new DataOutputStream(byteOS);
+ try {
+ out.writeInt(PASSWORD_CHANGE_REQUEST_TYPE);
+ out.writeUTF(oldPassword);
+ out.writeUTF(newPassword);
+ out.flush();
+ SendMessageModule.sendMessage(byteOS.toByteArray());
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public static void sendChatMessage(Chat.ChatMessage chatMessage) {
+ ByteArrayOutputStream byteOS = new ByteArrayOutputStream(200);
+ DataOutputStream out = new DataOutputStream(byteOS);
+ try {
+ out.writeInt(OUTGOING_CHAT_MESSAGE_TYPE);
+ out.writeUTF(chatMessage.getTime());
+ out.writeUTF(chatMessage.getText());
+ out.flush();
+ SendMessageModule.sendMessage(byteOS.toByteArray());
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ protected static void setLoginListener(LoginMessageListener listener) {
+ loginListener = listener;
+ }
+
+ protected static void setRegisterListener(RegisterMessageListener listener) {
+ registerListener = listener;
+ }
+
+ protected static void setRatingListener(RatingMessageListener listener) {
+ ratingListener = listener;
+ }
+
+ protected static void setGameListener(GameMessageListener listener) {
+ gameListener = listener;
+ }
+
+ protected static void setGameExpectationListener(GameExpectationMessageListener listener) {
+ gameExpectationListener = listener;
+ }
+
+ protected static void setFriendGamePreparationListener(FriendGamePreparationListener listener) {
+ preparationListener = listener;
+ }
+
+ protected static void setPasswordChangeListener(PasswordChangeListener listener) {
+ passwordChangeListener = listener;
+ }
+
+ protected static void unsetGameListener() {
+ gameListener = null;
+ }
+
+ protected static void unSetGameExpectationListener() {
+ gameExpectationListener = null;
+ }
+
+ protected static void unSetFriendGamePreparationListener() {
+ preparationListener = null;
+ }
+
+ protected interface LoginMessageListener {
+ void onLoginResponse(int code);
+ }
+
+ protected interface RegisterMessageListener {
+ void onRegisterResponse(int code);
+ }
+
+ protected interface RatingMessageListener {
+ void onRatingMessage(int rating);
+ }
+
+ protected interface GameMessageListener {
+ void onSendCard(long card);
+
+ void onLeadRequest();
+
+ void onChoiceRequest(String association);
+
+ void onVoteRequest(String association, long[] candidates);
+
+ void onRoundEnd(long leadersAssociation, int[] scores);
+
+ void onGameFinish(long lastAnswer, ArrayList finalScores,
+ ArrayList oldRatings, ArrayList newRatings);
+ }
+
+ protected interface GameExpectationMessageListener {
+ void onStartGameMessage(int roundsNumber, int playersNumber, ArrayList names,
+ long expectationTime);
+
+ void onGameCancelled();
+ }
+
+ protected interface FriendGamePreparationListener {
+ void onNewPlayer(String name);
+
+ void onGonePlayer(String name);
+ }
+
+ protected interface PasswordChangeListener {
+ void onPasswordChangeResponse(int code);
+ }
+}
diff --git a/app/src/main/java/ru/spbau/resemblance/PasswordChangeActivity.java b/app/src/main/java/ru/spbau/resemblance/PasswordChangeActivity.java
new file mode 100644
index 0000000..de144d5
--- /dev/null
+++ b/app/src/main/java/ru/spbau/resemblance/PasswordChangeActivity.java
@@ -0,0 +1,91 @@
+package ru.spbau.resemblance;
+
+import android.content.SharedPreferences;
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.Toast;
+
+import java.math.BigInteger;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+public class PasswordChangeActivity extends AppCompatActivity implements
+ Message.PasswordChangeListener {
+ private EditText oldPasswordField = null;
+ private EditText newPasswordField1 = null;
+ private EditText newPasswordField2 = null;
+ private String newPasswordHash = null;
+ private volatile boolean waiting = false;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_password_change);
+
+ oldPasswordField = (EditText)findViewById(R.id.changePasswordOldField);
+ newPasswordField1 = (EditText)findViewById(R.id.changePasswordNewField2);
+ newPasswordField2 = (EditText)findViewById(R.id.changePasswordNewField1);
+
+ Message.setPasswordChangeListener(this);
+ }
+
+ public void onChangePasswordClick(View v) {
+ if (!newPasswordField1.getText().toString().equals(newPasswordField2.getText().toString())) {
+ Toast.makeText(this, R.string.passwords_differ, Toast.LENGTH_LONG).show();
+ } else {
+ try {
+ MessageDigest messageDigest = MessageDigest.getInstance("MD5");
+ byte[] oldHash = messageDigest.digest(oldPasswordField.getText().toString().getBytes());
+ String oldPasswordHash = new BigInteger(1, oldHash).toString();
+ byte[] newHash = messageDigest.digest(newPasswordField1.getText().toString().getBytes());
+ newPasswordHash = new BigInteger(1, newHash).toString();
+
+ if (!waiting) {
+ Message.sendPasswordChangeMessage(oldPasswordHash, newPasswordHash);
+ waiting = true;
+ } else {
+ Toast.makeText(this, R.string.password_change_waiting, Toast.LENGTH_SHORT).show();
+ }
+ } catch (NoSuchAlgorithmException e) {
+ Log.d("MD5", "MD5 Algorithm not found.");
+ }
+ }
+ }
+
+ @Override
+ public void onPasswordChangeResponse(int code) {
+ final int PASSWORD_CHANGE_SUCCESS = 1;
+ final int WRONG_PASSWORD = 2;
+ switch (code) {
+ case PASSWORD_CHANGE_SUCCESS: {
+ SharedPreferences prefs = getSharedPreferences(SettingsActivity.PREFERENCES, MODE_PRIVATE);
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putString(SettingsActivity.PASSWORD_PREF, newPasswordHash);
+ editor.apply();
+ finish();
+ }
+ case WRONG_PASSWORD: {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ Toast.makeText(PasswordChangeActivity.this,
+ R.string.password_change_wrong_old_password, Toast.LENGTH_SHORT).show();
+ }
+ });
+ }
+ }
+ waiting = false;
+ }
+
+ @Override
+ public void onBackPressed() {
+ if (waiting) {
+ Toast.makeText(this, R.string.password_change_waiting, Toast.LENGTH_SHORT).show();
+ } else {
+ super.onBackPressed();
+ }
+ }
+}
diff --git a/app/src/main/java/ru/spbau/resemblance/RegistrationActivity.java b/app/src/main/java/ru/spbau/resemblance/RegistrationActivity.java
new file mode 100644
index 0000000..3d13f53
--- /dev/null
+++ b/app/src/main/java/ru/spbau/resemblance/RegistrationActivity.java
@@ -0,0 +1,80 @@
+package ru.spbau.resemblance;
+
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.Toast;
+
+import java.math.BigInteger;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+public class RegistrationActivity extends AppCompatActivity implements Message.RegisterMessageListener {
+ private EditText nicknameField = null;
+ private EditText passwordField1 = null;
+ private EditText passwordField2 = null;
+ private String passwordHash = null;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_registration);
+
+ nicknameField = (EditText)findViewById(R.id.registrationNickField);
+ passwordField1 = (EditText)findViewById(R.id.registrationPasswordField1);
+ passwordField2 = (EditText)findViewById(R.id.registrationPasswordField2);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ Message.setRegisterListener(this);
+ }
+
+ public void onRegisterTouch(View v) {
+ if (!(passwordField1.getText().toString().equals(passwordField2.getText().toString()))) {
+ Toast.makeText(this, R.string.passwords_differ, Toast.LENGTH_SHORT).show();
+ } else {
+ String nickname = nicknameField.getText().toString();
+ String password = passwordField1.getText().toString();
+ try {
+ MessageDigest messageDigest = MessageDigest.getInstance("MD5");
+ byte[] hash = messageDigest.digest(password.getBytes());
+ passwordHash = new BigInteger(1, hash).toString();
+ } catch (NoSuchAlgorithmException e) {
+ Log.d("MD5", "MD5 Algorithm not found.");
+ }
+
+ Message.sendRegisterMessage(nickname, passwordHash);
+ }
+ }
+
+ @Override
+ public void onRegisterResponse(int code) {
+ final int NETWORK_ERROR = -1;
+ final int SUCCESS = 0;
+ final int NICKNAME_ERROR = 1;
+ int messageId = R.string.registration_unknown_error;
+ switch (code) {
+ case SUCCESS: {
+ finish();
+ messageId = R.string.registration_success;
+ break;
+ }
+ case NICKNAME_ERROR: {
+ messageId = R.string.registration_nickname_used_error;
+ break;
+ }
+ }
+ final String messageToShow = getString(messageId);
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ Toast.makeText(RegistrationActivity.this, messageToShow, Toast.LENGTH_SHORT).show();
+ }
+ });
+ }
+}
diff --git a/app/src/main/java/ru/spbau/resemblance/SendMessageModule.java b/app/src/main/java/ru/spbau/resemblance/SendMessageModule.java
new file mode 100644
index 0000000..9c8fbe0
--- /dev/null
+++ b/app/src/main/java/ru/spbau/resemblance/SendMessageModule.java
@@ -0,0 +1,201 @@
+package ru.spbau.resemblance;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+import static android.content.Context.MODE_PRIVATE;
+
+public class SendMessageModule {
+ final private static String LOG_TAG = "Messenger log";
+ final private static int maxCntToReconnect = 20;
+
+ final private static String DEFAULT_IP = "192.168.1.180";
+ final private static int serverPort = 6662;
+
+ private final static int sleepTime = 1000;
+
+ private static DataInputStream in = null;
+ private static DataOutputStream out = null;
+
+ final private static int STATUS_NOT_OPEN = 0;
+ final private static int STATUS_IS_OPENING = 1;
+ final private static int STATUS_OPEN = 2;
+ final private static int STATUS_CLOSE = 3;
+
+ private static int connectionStatus = STATUS_NOT_OPEN;
+ private static final Lock STATUS_LOCK = new ReentrantLock();
+ private static final Executor messageWriter = Executors.newSingleThreadExecutor();
+
+ public static boolean isAlive() {
+ STATUS_LOCK.lock();
+ try {
+ return connectionStatus == STATUS_OPEN;
+ } finally {
+ STATUS_LOCK.unlock();
+ }
+ }
+
+ public static String getServerIP(Context context) {
+ SharedPreferences preferences = context.getSharedPreferences(SettingsActivity.PREFERENCES,
+ MODE_PRIVATE);
+ if (!preferences.contains(SettingsActivity.IP_PREF)) {
+ resetIP(context);
+ return DEFAULT_IP;
+ }
+ return preferences.getString(SettingsActivity.IP_PREF, DEFAULT_IP);
+ }
+
+ public static void resetIP(Context context) {
+ setIP(context, DEFAULT_IP);
+ }
+
+ public static void setIP(Context context, String newServerIP) {
+ SharedPreferences preferences = context.getSharedPreferences(SettingsActivity.PREFERENCES,
+ MODE_PRIVATE);
+ SharedPreferences.Editor editor = preferences.edit();
+ editor.putString(SettingsActivity.IP_PREF, newServerIP);
+ editor.apply();
+ }
+
+ private static Thread readThread = new Thread() {
+ @Override
+ public void run() {
+ while (true) {
+ while (true) {
+ STATUS_LOCK.lock();
+ try {
+ if (connectionStatus == STATUS_OPEN) {
+ break;
+ }
+ if (connectionStatus == STATUS_CLOSE) {
+ return;
+ }
+ } finally {
+ STATUS_LOCK.unlock();
+ }
+ }
+ int messageType = -1;
+ for (int i = 0; i < maxCntToReconnect; i++) {
+ try {
+ messageType = in.readInt();
+ Message newMessage = new Message(messageType);
+ newMessage.readMessage(in);
+ break;
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ if (messageType == -1) {
+ STATUS_LOCK.lock();
+ try {
+ connectionStatus = STATUS_CLOSE;
+ } finally {
+ STATUS_LOCK.unlock();
+ }
+ }
+ }
+ }
+ };
+
+ public static void connectToServer(final String serverIP) {
+ Thread initThread = new Thread() {
+ @Override
+ public void run() {
+ STATUS_LOCK.lock();
+ try {
+ connectionStatus = STATUS_IS_OPENING;
+ for (int i = 0; i < maxCntToReconnect; i++) {
+ if (tryToConnect(serverIP)) {
+ connectionStatus = STATUS_OPEN;
+ break;
+ }
+ try {
+ sleep(sleepTime);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ if (connectionStatus == STATUS_IS_OPENING) {
+ connectionStatus = STATUS_CLOSE;
+ }
+ else {
+ readThread.start();
+ }
+ } finally {
+ STATUS_LOCK.unlock();
+ }
+ }
+ };
+ initThread.start();
+ }
+
+ private static boolean tryToConnect(String serverIP) {
+ try {
+ InetAddress ipAddress = InetAddress.getByName(serverIP);
+ Socket socket = new Socket(ipAddress, serverPort);
+ in = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
+ out = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
+ return true;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return false;
+ }
+
+ private static boolean tryToSendMessage(byte[] message) {
+ try {
+ out.write(message);
+ out.flush();
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ }
+ return true;
+ }
+
+ public static void sendMessage(final byte[] message) {
+ messageWriter.execute(new Runnable() {
+ @Override
+ public void run() {
+ while (true) {
+ STATUS_LOCK.lock();
+ try {
+ if (connectionStatus == STATUS_OPEN) {
+ break;
+ }
+ if (connectionStatus == STATUS_CLOSE) {
+ return;
+ }
+ } finally {
+ STATUS_LOCK.unlock();
+ }
+ }
+ boolean messageIsSent = false;
+ for (int i = 0; i < maxCntToReconnect && !messageIsSent; i++) {
+ messageIsSent = tryToSendMessage(message);
+ }
+ if (!messageIsSent) {
+ STATUS_LOCK.lock();
+ try {
+ connectionStatus = STATUS_CLOSE;
+ } finally {
+ STATUS_LOCK.unlock();
+ }
+ }
+ }
+ });
+ }
+}
diff --git a/app/src/main/java/ru/spbau/resemblance/SettingsActivity.java b/app/src/main/java/ru/spbau/resemblance/SettingsActivity.java
new file mode 100644
index 0000000..881a7e6
--- /dev/null
+++ b/app/src/main/java/ru/spbau/resemblance/SettingsActivity.java
@@ -0,0 +1,111 @@
+package ru.spbau.resemblance;
+
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.support.annotation.NonNull;
+import android.support.v4.app.DialogFragment;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.EditText;
+import android.widget.ListView;
+import android.widget.Toast;
+
+import java.util.regex.Pattern;
+
+public class SettingsActivity extends AppCompatActivity implements ListView.OnItemClickListener {
+ public static final String PREFERENCES = "preferences.xml";
+ public static final String NICKNAME_PREF = "nickname";
+ public static final String PASSWORD_PREF = "password";
+ public static final String RATING_PREF = "rating";
+ public static final String IP_PREF = "ip";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_settings);
+
+ String[] options = getResources().getStringArray(R.array.settings_options);
+ ListView optionsList = (ListView)findViewById(R.id.settingsOptionsList);
+ ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, options);
+
+ optionsList.setAdapter(adapter);
+ optionsList.setOnItemClickListener(this);
+ }
+
+ @Override
+ public void onItemClick(AdapterView> parent, View view, int position, long id) {
+ switch (position) {
+ case 0: {
+ Intent changePassword = new Intent(this, PasswordChangeActivity.class);
+ startActivity(changePassword);
+ break;
+ }
+ case 1: {
+ SharedPreferences preferences = getSharedPreferences(PREFERENCES, MODE_PRIVATE);
+ SharedPreferences.Editor editor = preferences.edit();
+ editor.remove(NICKNAME_PREF);
+ editor.remove(PASSWORD_PREF);
+ editor.remove(RATING_PREF);
+ editor.apply();
+
+ Intent quit = new Intent(this, LoginActivity.class);
+ startActivity(quit);
+ break;
+ }
+ case 2: {
+ SetServerIPDialog dialog = new SetServerIPDialog();
+ dialog.show(getSupportFragmentManager(), "DeletePlayerDialog");
+ break;
+ }
+ }
+ }
+
+ public static class SetServerIPDialog extends DialogFragment {
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ LayoutInflater inflater = getActivity().getLayoutInflater();
+ final View field = inflater.inflate(R.layout.server_ip_change_dialog, null);
+ builder.setView(field)
+ .setTitle(R.string.new_ip)
+ .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+
+ }
+ })
+ .setNeutralButton(R.string.restore, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ SendMessageModule.resetIP(getActivity());
+ }
+ })
+ .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ final String IP_PATTERN =
+ "^(([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.){3}" +
+ "([01]?\\d\\d?|2[0-4]\\d|25[0-5])$";
+ Pattern pattern = Pattern.compile(IP_PATTERN);
+
+ EditText ipField = (EditText) field.findViewById(R.id.serverIPField);
+ String ip = ipField.getText().toString();
+ if (pattern.matcher(ip).matches()) {
+ SendMessageModule.setIP(getActivity(), ip);
+ } else {
+ Toast.makeText(getActivity(), "Непрвильный IP.", Toast.LENGTH_SHORT).show();
+ }
+ }
+ });
+ return builder.create();
+ }
+ }
+}
diff --git a/app/src/main/java/ru/spbau/resemblance/ViewArrayAdapter.java b/app/src/main/java/ru/spbau/resemblance/ViewArrayAdapter.java
new file mode 100644
index 0000000..74fa312
--- /dev/null
+++ b/app/src/main/java/ru/spbau/resemblance/ViewArrayAdapter.java
@@ -0,0 +1,37 @@
+package ru.spbau.resemblance;
+
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+
+class ViewArrayAdapter extends BaseAdapter {
+ private View[] views;
+
+ ViewArrayAdapter(View[] views) {
+ this.views = views;
+ }
+
+ public void setArray(View[] views) {
+ this.views = views;
+ }
+
+ @Override
+ public int getCount() {
+ return views.length;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ return views[position];
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return null;
+ }
+}
diff --git a/app/src/main/res/drawable/a1.jpg b/app/src/main/res/drawable/a1.jpg
new file mode 100644
index 0000000..5c36a11
Binary files /dev/null and b/app/src/main/res/drawable/a1.jpg differ
diff --git a/app/src/main/res/drawable/a10.jpg b/app/src/main/res/drawable/a10.jpg
new file mode 100644
index 0000000..25e2488
Binary files /dev/null and b/app/src/main/res/drawable/a10.jpg differ
diff --git a/app/src/main/res/drawable/a11.jpg b/app/src/main/res/drawable/a11.jpg
new file mode 100644
index 0000000..f06111b
Binary files /dev/null and b/app/src/main/res/drawable/a11.jpg differ
diff --git a/app/src/main/res/drawable/a12.jpg b/app/src/main/res/drawable/a12.jpg
new file mode 100644
index 0000000..b0825cb
Binary files /dev/null and b/app/src/main/res/drawable/a12.jpg differ
diff --git a/app/src/main/res/drawable/a13.jpg b/app/src/main/res/drawable/a13.jpg
new file mode 100644
index 0000000..b7dd1a8
Binary files /dev/null and b/app/src/main/res/drawable/a13.jpg differ
diff --git a/app/src/main/res/drawable/a14.jpg b/app/src/main/res/drawable/a14.jpg
new file mode 100644
index 0000000..19a3c4e
Binary files /dev/null and b/app/src/main/res/drawable/a14.jpg differ
diff --git a/app/src/main/res/drawable/a15.jpg b/app/src/main/res/drawable/a15.jpg
new file mode 100644
index 0000000..05cacc0
Binary files /dev/null and b/app/src/main/res/drawable/a15.jpg differ
diff --git a/app/src/main/res/drawable/a16.jpg b/app/src/main/res/drawable/a16.jpg
new file mode 100644
index 0000000..0e7a984
Binary files /dev/null and b/app/src/main/res/drawable/a16.jpg differ
diff --git a/app/src/main/res/drawable/a17.jpg b/app/src/main/res/drawable/a17.jpg
new file mode 100644
index 0000000..c61fbda
Binary files /dev/null and b/app/src/main/res/drawable/a17.jpg differ
diff --git a/app/src/main/res/drawable/a18.jpg b/app/src/main/res/drawable/a18.jpg
new file mode 100644
index 0000000..ad21767
Binary files /dev/null and b/app/src/main/res/drawable/a18.jpg differ
diff --git a/app/src/main/res/drawable/a19.jpg b/app/src/main/res/drawable/a19.jpg
new file mode 100644
index 0000000..b5ccc79
Binary files /dev/null and b/app/src/main/res/drawable/a19.jpg differ
diff --git a/app/src/main/res/drawable/a2.jpg b/app/src/main/res/drawable/a2.jpg
new file mode 100644
index 0000000..2fbfe12
Binary files /dev/null and b/app/src/main/res/drawable/a2.jpg differ
diff --git a/app/src/main/res/drawable/a20.jpg b/app/src/main/res/drawable/a20.jpg
new file mode 100644
index 0000000..22ae4d2
Binary files /dev/null and b/app/src/main/res/drawable/a20.jpg differ
diff --git a/app/src/main/res/drawable/a21.jpg b/app/src/main/res/drawable/a21.jpg
new file mode 100644
index 0000000..994b248
Binary files /dev/null and b/app/src/main/res/drawable/a21.jpg differ
diff --git a/app/src/main/res/drawable/a22.jpg b/app/src/main/res/drawable/a22.jpg
new file mode 100644
index 0000000..fbe0924
Binary files /dev/null and b/app/src/main/res/drawable/a22.jpg differ
diff --git a/app/src/main/res/drawable/a23.jpg b/app/src/main/res/drawable/a23.jpg
new file mode 100644
index 0000000..a91ee73
Binary files /dev/null and b/app/src/main/res/drawable/a23.jpg differ
diff --git a/app/src/main/res/drawable/a24.jpg b/app/src/main/res/drawable/a24.jpg
new file mode 100644
index 0000000..986a9ea
Binary files /dev/null and b/app/src/main/res/drawable/a24.jpg differ
diff --git a/app/src/main/res/drawable/a25.jpg b/app/src/main/res/drawable/a25.jpg
new file mode 100644
index 0000000..aea5337
Binary files /dev/null and b/app/src/main/res/drawable/a25.jpg differ
diff --git a/app/src/main/res/drawable/a26.jpg b/app/src/main/res/drawable/a26.jpg
new file mode 100644
index 0000000..6897c7f
Binary files /dev/null and b/app/src/main/res/drawable/a26.jpg differ
diff --git a/app/src/main/res/drawable/a27.jpg b/app/src/main/res/drawable/a27.jpg
new file mode 100644
index 0000000..3a8d291
Binary files /dev/null and b/app/src/main/res/drawable/a27.jpg differ
diff --git a/app/src/main/res/drawable/a28.jpg b/app/src/main/res/drawable/a28.jpg
new file mode 100644
index 0000000..be3e6f9
Binary files /dev/null and b/app/src/main/res/drawable/a28.jpg differ
diff --git a/app/src/main/res/drawable/a29.jpg b/app/src/main/res/drawable/a29.jpg
new file mode 100644
index 0000000..372895a
Binary files /dev/null and b/app/src/main/res/drawable/a29.jpg differ
diff --git a/app/src/main/res/drawable/a3.jpg b/app/src/main/res/drawable/a3.jpg
new file mode 100644
index 0000000..d06fe02
Binary files /dev/null and b/app/src/main/res/drawable/a3.jpg differ
diff --git a/app/src/main/res/drawable/a30.jpg b/app/src/main/res/drawable/a30.jpg
new file mode 100644
index 0000000..47822ee
Binary files /dev/null and b/app/src/main/res/drawable/a30.jpg differ
diff --git a/app/src/main/res/drawable/a31.jpg b/app/src/main/res/drawable/a31.jpg
new file mode 100644
index 0000000..57530e0
Binary files /dev/null and b/app/src/main/res/drawable/a31.jpg differ
diff --git a/app/src/main/res/drawable/a32.jpg b/app/src/main/res/drawable/a32.jpg
new file mode 100644
index 0000000..c8149e2
Binary files /dev/null and b/app/src/main/res/drawable/a32.jpg differ
diff --git a/app/src/main/res/drawable/a33.jpg b/app/src/main/res/drawable/a33.jpg
new file mode 100644
index 0000000..e6b6d42
Binary files /dev/null and b/app/src/main/res/drawable/a33.jpg differ
diff --git a/app/src/main/res/drawable/a34.jpg b/app/src/main/res/drawable/a34.jpg
new file mode 100644
index 0000000..e0398cc
Binary files /dev/null and b/app/src/main/res/drawable/a34.jpg differ
diff --git a/app/src/main/res/drawable/a35.jpg b/app/src/main/res/drawable/a35.jpg
new file mode 100644
index 0000000..b69453c
Binary files /dev/null and b/app/src/main/res/drawable/a35.jpg differ
diff --git a/app/src/main/res/drawable/a36.jpg b/app/src/main/res/drawable/a36.jpg
new file mode 100644
index 0000000..f9eb2d3
Binary files /dev/null and b/app/src/main/res/drawable/a36.jpg differ
diff --git a/app/src/main/res/drawable/a37.jpg b/app/src/main/res/drawable/a37.jpg
new file mode 100644
index 0000000..b3b6375
Binary files /dev/null and b/app/src/main/res/drawable/a37.jpg differ
diff --git a/app/src/main/res/drawable/a38.jpg b/app/src/main/res/drawable/a38.jpg
new file mode 100644
index 0000000..9f277c2
Binary files /dev/null and b/app/src/main/res/drawable/a38.jpg differ
diff --git a/app/src/main/res/drawable/a39.jpg b/app/src/main/res/drawable/a39.jpg
new file mode 100644
index 0000000..10efda3
Binary files /dev/null and b/app/src/main/res/drawable/a39.jpg differ
diff --git a/app/src/main/res/drawable/a4.jpg b/app/src/main/res/drawable/a4.jpg
new file mode 100644
index 0000000..2958473
Binary files /dev/null and b/app/src/main/res/drawable/a4.jpg differ
diff --git a/app/src/main/res/drawable/a40.jpg b/app/src/main/res/drawable/a40.jpg
new file mode 100644
index 0000000..e5885f0
Binary files /dev/null and b/app/src/main/res/drawable/a40.jpg differ
diff --git a/app/src/main/res/drawable/a41.jpg b/app/src/main/res/drawable/a41.jpg
new file mode 100644
index 0000000..d62a96e
Binary files /dev/null and b/app/src/main/res/drawable/a41.jpg differ
diff --git a/app/src/main/res/drawable/a42.jpg b/app/src/main/res/drawable/a42.jpg
new file mode 100644
index 0000000..bf9875f
Binary files /dev/null and b/app/src/main/res/drawable/a42.jpg differ
diff --git a/app/src/main/res/drawable/a43.jpg b/app/src/main/res/drawable/a43.jpg
new file mode 100644
index 0000000..e6c5557
Binary files /dev/null and b/app/src/main/res/drawable/a43.jpg differ
diff --git a/app/src/main/res/drawable/a44.jpg b/app/src/main/res/drawable/a44.jpg
new file mode 100644
index 0000000..98273ea
Binary files /dev/null and b/app/src/main/res/drawable/a44.jpg differ
diff --git a/app/src/main/res/drawable/a45.jpg b/app/src/main/res/drawable/a45.jpg
new file mode 100644
index 0000000..6a840fb
Binary files /dev/null and b/app/src/main/res/drawable/a45.jpg differ
diff --git a/app/src/main/res/drawable/a5.jpg b/app/src/main/res/drawable/a5.jpg
new file mode 100644
index 0000000..0125728
Binary files /dev/null and b/app/src/main/res/drawable/a5.jpg differ
diff --git a/app/src/main/res/drawable/a6.jpg b/app/src/main/res/drawable/a6.jpg
new file mode 100644
index 0000000..d6f7630
Binary files /dev/null and b/app/src/main/res/drawable/a6.jpg differ
diff --git a/app/src/main/res/drawable/a7.jpg b/app/src/main/res/drawable/a7.jpg
new file mode 100644
index 0000000..1d415ed
Binary files /dev/null and b/app/src/main/res/drawable/a7.jpg differ
diff --git a/app/src/main/res/drawable/a8.jpg b/app/src/main/res/drawable/a8.jpg
new file mode 100644
index 0000000..c2a65f0
Binary files /dev/null and b/app/src/main/res/drawable/a8.jpg differ
diff --git a/app/src/main/res/drawable/a9.jpg b/app/src/main/res/drawable/a9.jpg
new file mode 100644
index 0000000..a3926cd
Binary files /dev/null and b/app/src/main/res/drawable/a9.jpg differ
diff --git a/app/src/main/res/drawable/b1.jpg b/app/src/main/res/drawable/b1.jpg
new file mode 100644
index 0000000..839a60b
Binary files /dev/null and b/app/src/main/res/drawable/b1.jpg differ
diff --git a/app/src/main/res/drawable/b10.jpg b/app/src/main/res/drawable/b10.jpg
new file mode 100644
index 0000000..54eb3e4
Binary files /dev/null and b/app/src/main/res/drawable/b10.jpg differ
diff --git a/app/src/main/res/drawable/b11.jpg b/app/src/main/res/drawable/b11.jpg
new file mode 100644
index 0000000..aadb749
Binary files /dev/null and b/app/src/main/res/drawable/b11.jpg differ
diff --git a/app/src/main/res/drawable/b12.jpg b/app/src/main/res/drawable/b12.jpg
new file mode 100644
index 0000000..3fac2c1
Binary files /dev/null and b/app/src/main/res/drawable/b12.jpg differ
diff --git a/app/src/main/res/drawable/b13.jpg b/app/src/main/res/drawable/b13.jpg
new file mode 100644
index 0000000..32ca933
Binary files /dev/null and b/app/src/main/res/drawable/b13.jpg differ
diff --git a/app/src/main/res/drawable/b14.jpg b/app/src/main/res/drawable/b14.jpg
new file mode 100644
index 0000000..b6ff5cd
Binary files /dev/null and b/app/src/main/res/drawable/b14.jpg differ
diff --git a/app/src/main/res/drawable/b15.jpg b/app/src/main/res/drawable/b15.jpg
new file mode 100644
index 0000000..4a577bd
Binary files /dev/null and b/app/src/main/res/drawable/b15.jpg differ
diff --git a/app/src/main/res/drawable/b16.jpg b/app/src/main/res/drawable/b16.jpg
new file mode 100644
index 0000000..b2a1113
Binary files /dev/null and b/app/src/main/res/drawable/b16.jpg differ
diff --git a/app/src/main/res/drawable/b17.jpg b/app/src/main/res/drawable/b17.jpg
new file mode 100644
index 0000000..730802e
Binary files /dev/null and b/app/src/main/res/drawable/b17.jpg differ
diff --git a/app/src/main/res/drawable/b18.jpg b/app/src/main/res/drawable/b18.jpg
new file mode 100644
index 0000000..ee38fa3
Binary files /dev/null and b/app/src/main/res/drawable/b18.jpg differ
diff --git a/app/src/main/res/drawable/b19.jpg b/app/src/main/res/drawable/b19.jpg
new file mode 100644
index 0000000..4cae14c
Binary files /dev/null and b/app/src/main/res/drawable/b19.jpg differ
diff --git a/app/src/main/res/drawable/b2.jpg b/app/src/main/res/drawable/b2.jpg
new file mode 100644
index 0000000..d5a317b
Binary files /dev/null and b/app/src/main/res/drawable/b2.jpg differ
diff --git a/app/src/main/res/drawable/b20.jpg b/app/src/main/res/drawable/b20.jpg
new file mode 100644
index 0000000..f0e2512
Binary files /dev/null and b/app/src/main/res/drawable/b20.jpg differ
diff --git a/app/src/main/res/drawable/b21.jpg b/app/src/main/res/drawable/b21.jpg
new file mode 100644
index 0000000..354a683
Binary files /dev/null and b/app/src/main/res/drawable/b21.jpg differ
diff --git a/app/src/main/res/drawable/b22.jpg b/app/src/main/res/drawable/b22.jpg
new file mode 100644
index 0000000..c35e0ac
Binary files /dev/null and b/app/src/main/res/drawable/b22.jpg differ
diff --git a/app/src/main/res/drawable/b23.jpg b/app/src/main/res/drawable/b23.jpg
new file mode 100644
index 0000000..28dc3af
Binary files /dev/null and b/app/src/main/res/drawable/b23.jpg differ
diff --git a/app/src/main/res/drawable/b24.jpg b/app/src/main/res/drawable/b24.jpg
new file mode 100644
index 0000000..7e3e224
Binary files /dev/null and b/app/src/main/res/drawable/b24.jpg differ
diff --git a/app/src/main/res/drawable/b25.jpg b/app/src/main/res/drawable/b25.jpg
new file mode 100644
index 0000000..2702abf
Binary files /dev/null and b/app/src/main/res/drawable/b25.jpg differ
diff --git a/app/src/main/res/drawable/b26.jpg b/app/src/main/res/drawable/b26.jpg
new file mode 100644
index 0000000..15142e3
Binary files /dev/null and b/app/src/main/res/drawable/b26.jpg differ
diff --git a/app/src/main/res/drawable/b27.jpg b/app/src/main/res/drawable/b27.jpg
new file mode 100644
index 0000000..455522f
Binary files /dev/null and b/app/src/main/res/drawable/b27.jpg differ
diff --git a/app/src/main/res/drawable/b28.jpg b/app/src/main/res/drawable/b28.jpg
new file mode 100644
index 0000000..eae8e97
Binary files /dev/null and b/app/src/main/res/drawable/b28.jpg differ
diff --git a/app/src/main/res/drawable/b29.jpg b/app/src/main/res/drawable/b29.jpg
new file mode 100644
index 0000000..33159c3
Binary files /dev/null and b/app/src/main/res/drawable/b29.jpg differ
diff --git a/app/src/main/res/drawable/b3.jpg b/app/src/main/res/drawable/b3.jpg
new file mode 100644
index 0000000..a4191b3
Binary files /dev/null and b/app/src/main/res/drawable/b3.jpg differ
diff --git a/app/src/main/res/drawable/b30.jpg b/app/src/main/res/drawable/b30.jpg
new file mode 100644
index 0000000..87bad5d
Binary files /dev/null and b/app/src/main/res/drawable/b30.jpg differ
diff --git a/app/src/main/res/drawable/b31.jpg b/app/src/main/res/drawable/b31.jpg
new file mode 100644
index 0000000..57c0e1a
Binary files /dev/null and b/app/src/main/res/drawable/b31.jpg differ
diff --git a/app/src/main/res/drawable/b32.jpg b/app/src/main/res/drawable/b32.jpg
new file mode 100644
index 0000000..0107b73
Binary files /dev/null and b/app/src/main/res/drawable/b32.jpg differ
diff --git a/app/src/main/res/drawable/b33.jpg b/app/src/main/res/drawable/b33.jpg
new file mode 100644
index 0000000..e727c41
Binary files /dev/null and b/app/src/main/res/drawable/b33.jpg differ
diff --git a/app/src/main/res/drawable/b34.jpg b/app/src/main/res/drawable/b34.jpg
new file mode 100644
index 0000000..4d5ec7a
Binary files /dev/null and b/app/src/main/res/drawable/b34.jpg differ
diff --git a/app/src/main/res/drawable/b35.jpg b/app/src/main/res/drawable/b35.jpg
new file mode 100644
index 0000000..bc65296
Binary files /dev/null and b/app/src/main/res/drawable/b35.jpg differ
diff --git a/app/src/main/res/drawable/b36.jpg b/app/src/main/res/drawable/b36.jpg
new file mode 100644
index 0000000..25cd7b9
Binary files /dev/null and b/app/src/main/res/drawable/b36.jpg differ
diff --git a/app/src/main/res/drawable/b37.jpg b/app/src/main/res/drawable/b37.jpg
new file mode 100644
index 0000000..407dba4
Binary files /dev/null and b/app/src/main/res/drawable/b37.jpg differ
diff --git a/app/src/main/res/drawable/b38.jpg b/app/src/main/res/drawable/b38.jpg
new file mode 100644
index 0000000..2dc1fb7
Binary files /dev/null and b/app/src/main/res/drawable/b38.jpg differ
diff --git a/app/src/main/res/drawable/b39.jpg b/app/src/main/res/drawable/b39.jpg
new file mode 100644
index 0000000..45d2b15
Binary files /dev/null and b/app/src/main/res/drawable/b39.jpg differ
diff --git a/app/src/main/res/drawable/b4.jpg b/app/src/main/res/drawable/b4.jpg
new file mode 100644
index 0000000..9efd7ba
Binary files /dev/null and b/app/src/main/res/drawable/b4.jpg differ
diff --git a/app/src/main/res/drawable/b40.jpg b/app/src/main/res/drawable/b40.jpg
new file mode 100644
index 0000000..ef1845c
Binary files /dev/null and b/app/src/main/res/drawable/b40.jpg differ
diff --git a/app/src/main/res/drawable/b41.jpg b/app/src/main/res/drawable/b41.jpg
new file mode 100644
index 0000000..335b5fb
Binary files /dev/null and b/app/src/main/res/drawable/b41.jpg differ
diff --git a/app/src/main/res/drawable/b42.jpg b/app/src/main/res/drawable/b42.jpg
new file mode 100644
index 0000000..8cc9267
Binary files /dev/null and b/app/src/main/res/drawable/b42.jpg differ
diff --git a/app/src/main/res/drawable/b43.jpg b/app/src/main/res/drawable/b43.jpg
new file mode 100644
index 0000000..55ab8da
Binary files /dev/null and b/app/src/main/res/drawable/b43.jpg differ
diff --git a/app/src/main/res/drawable/b44.jpg b/app/src/main/res/drawable/b44.jpg
new file mode 100644
index 0000000..4ea4d7f
Binary files /dev/null and b/app/src/main/res/drawable/b44.jpg differ
diff --git a/app/src/main/res/drawable/b45.jpg b/app/src/main/res/drawable/b45.jpg
new file mode 100644
index 0000000..988a8dc
Binary files /dev/null and b/app/src/main/res/drawable/b45.jpg differ
diff --git a/app/src/main/res/drawable/b46.jpg b/app/src/main/res/drawable/b46.jpg
new file mode 100644
index 0000000..d779a83
Binary files /dev/null and b/app/src/main/res/drawable/b46.jpg differ
diff --git a/app/src/main/res/drawable/b47.jpg b/app/src/main/res/drawable/b47.jpg
new file mode 100644
index 0000000..0ce0c04
Binary files /dev/null and b/app/src/main/res/drawable/b47.jpg differ
diff --git a/app/src/main/res/drawable/b48.jpg b/app/src/main/res/drawable/b48.jpg
new file mode 100644
index 0000000..512eb74
Binary files /dev/null and b/app/src/main/res/drawable/b48.jpg differ
diff --git a/app/src/main/res/drawable/b49.jpg b/app/src/main/res/drawable/b49.jpg
new file mode 100644
index 0000000..ba9f6ed
Binary files /dev/null and b/app/src/main/res/drawable/b49.jpg differ
diff --git a/app/src/main/res/drawable/b5.jpg b/app/src/main/res/drawable/b5.jpg
new file mode 100644
index 0000000..76a1d27
Binary files /dev/null and b/app/src/main/res/drawable/b5.jpg differ
diff --git a/app/src/main/res/drawable/b50.jpg b/app/src/main/res/drawable/b50.jpg
new file mode 100644
index 0000000..736a400
Binary files /dev/null and b/app/src/main/res/drawable/b50.jpg differ
diff --git a/app/src/main/res/drawable/b6.jpg b/app/src/main/res/drawable/b6.jpg
new file mode 100644
index 0000000..4ea74b4
Binary files /dev/null and b/app/src/main/res/drawable/b6.jpg differ
diff --git a/app/src/main/res/drawable/b7.jpg b/app/src/main/res/drawable/b7.jpg
new file mode 100644
index 0000000..04563b1
Binary files /dev/null and b/app/src/main/res/drawable/b7.jpg differ
diff --git a/app/src/main/res/drawable/b8.jpg b/app/src/main/res/drawable/b8.jpg
new file mode 100644
index 0000000..30b5d22
Binary files /dev/null and b/app/src/main/res/drawable/b8.jpg differ
diff --git a/app/src/main/res/drawable/b9.jpg b/app/src/main/res/drawable/b9.jpg
new file mode 100644
index 0000000..4149d48
Binary files /dev/null and b/app/src/main/res/drawable/b9.jpg differ
diff --git a/app/src/main/res/drawable/ok.png b/app/src/main/res/drawable/ok.png
new file mode 100644
index 0000000..d379822
Binary files /dev/null and b/app/src/main/res/drawable/ok.png differ
diff --git a/app/src/main/res/layout/activity_card_picker.xml b/app/src/main/res/layout/activity_card_picker.xml
new file mode 100644
index 0000000..bd2ee97
--- /dev/null
+++ b/app/src/main/res/layout/activity_card_picker.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_card_sets.xml b/app/src/main/res/layout/activity_card_sets.xml
new file mode 100644
index 0000000..446249b
--- /dev/null
+++ b/app/src/main/res/layout/activity_card_sets.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_card_viewer.xml b/app/src/main/res/layout/activity_card_viewer.xml
new file mode 100644
index 0000000..17326e9
--- /dev/null
+++ b/app/src/main/res/layout/activity_card_viewer.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_chat.xml b/app/src/main/res/layout/activity_chat.xml
new file mode 100644
index 0000000..1a76066
--- /dev/null
+++ b/app/src/main/res/layout/activity_chat.xml
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_create_game.xml b/app/src/main/res/layout/activity_create_game.xml
new file mode 100644
index 0000000..32fa428
--- /dev/null
+++ b/app/src/main/res/layout/activity_create_game.xml
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_create_set.xml b/app/src/main/res/layout/activity_create_set.xml
new file mode 100644
index 0000000..5efac28
--- /dev/null
+++ b/app/src/main/res/layout/activity_create_set.xml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_gallery.xml b/app/src/main/res/layout/activity_gallery.xml
new file mode 100644
index 0000000..ce1bc13
--- /dev/null
+++ b/app/src/main/res/layout/activity_gallery.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_gallery_card_ciewer.xml b/app/src/main/res/layout/activity_gallery_card_ciewer.xml
new file mode 100644
index 0000000..60ecef4
--- /dev/null
+++ b/app/src/main/res/layout/activity_gallery_card_ciewer.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_game_expectation.xml b/app/src/main/res/layout/activity_game_expectation.xml
new file mode 100644
index 0000000..f118c53
--- /dev/null
+++ b/app/src/main/res/layout/activity_game_expectation.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_game_finish.xml b/app/src/main/res/layout/activity_game_finish.xml
new file mode 100644
index 0000000..c4d322d
--- /dev/null
+++ b/app/src/main/res/layout/activity_game_finish.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_game_intermediate.xml b/app/src/main/res/layout/activity_game_intermediate.xml
new file mode 100644
index 0000000..6017e9a
--- /dev/null
+++ b/app/src/main/res/layout/activity_game_intermediate.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_game_preparation.xml b/app/src/main/res/layout/activity_game_preparation.xml
new file mode 100644
index 0000000..f422c60
--- /dev/null
+++ b/app/src/main/res/layout/activity_game_preparation.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_join_game.xml b/app/src/main/res/layout/activity_join_game.xml
new file mode 100644
index 0000000..033463e
--- /dev/null
+++ b/app/src/main/res/layout/activity_join_game.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_leading_association.xml b/app/src/main/res/layout/activity_leading_association.xml
new file mode 100644
index 0000000..1c3c8dd
--- /dev/null
+++ b/app/src/main/res/layout/activity_leading_association.xml
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_list_of_sets.xml b/app/src/main/res/layout/activity_list_of_sets.xml
new file mode 100644
index 0000000..8e7ff4f
--- /dev/null
+++ b/app/src/main/res/layout/activity_list_of_sets.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml
new file mode 100644
index 0000000..f623caf
--- /dev/null
+++ b/app/src/main/res/layout/activity_login.xml
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 9156550..e4f1aa8 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -1,4 +1,5 @@
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_password_change.xml b/app/src/main/res/layout/activity_password_change.xml
new file mode 100644
index 0000000..9faf0d3
--- /dev/null
+++ b/app/src/main/res/layout/activity_password_change.xml
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_registration.xml b/app/src/main/res/layout/activity_registration.xml
new file mode 100644
index 0000000..0adb42c
--- /dev/null
+++ b/app/src/main/res/layout/activity_registration.xml
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml
new file mode 100644
index 0000000..2d0f94e
--- /dev/null
+++ b/app/src/main/res/layout/activity_settings.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/message_list_item.xml b/app/src/main/res/layout/message_list_item.xml
new file mode 100644
index 0000000..2762d5e
--- /dev/null
+++ b/app/src/main/res/layout/message_list_item.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/result_list_item.xml b/app/src/main/res/layout/result_list_item.xml
new file mode 100644
index 0000000..de60993
--- /dev/null
+++ b/app/src/main/res/layout/result_list_item.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/results_header.xml b/app/src/main/res/layout/results_header.xml
new file mode 100644
index 0000000..f081115
--- /dev/null
+++ b/app/src/main/res/layout/results_header.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/score_list_item.xml b/app/src/main/res/layout/score_list_item.xml
new file mode 100644
index 0000000..f8b7e4d
--- /dev/null
+++ b/app/src/main/res/layout/score_list_item.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/server_ip_change_dialog.xml b/app/src/main/res/layout/server_ip_change_dialog.xml
new file mode 100644
index 0000000..504ca3e
--- /dev/null
+++ b/app/src/main/res/layout/server_ip_change_dialog.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/chat_menu.xml b/app/src/main/res/menu/chat_menu.xml
new file mode 100644
index 0000000..9d09459
--- /dev/null
+++ b/app/src/main/res/menu/chat_menu.xml
@@ -0,0 +1,10 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png
index cde69bc..88b0242 100644
Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png
index c133a0c..2ec8ff6 100644
Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png
index bfa42f0..50e0699 100644
Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
index 324e72c..3a48b2e 100644
Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
index aee44e1..9d9e2f3 100644
Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index 2a12c47..58160af 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -1,6 +1,6 @@
- #3F51B5
- #303F9F
+ #581f80
+ #7b1fa2
#FF4081
diff --git a/app/src/main/res/values/integers.xml b/app/src/main/res/values/integers.xml
new file mode 100644
index 0000000..b46ad14
--- /dev/null
+++ b/app/src/main/res/values/integers.xml
@@ -0,0 +1,4 @@
+
+
+ 0x400000
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 2387ef9..ee7b0e5 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,3 +1,82 @@
Resemblance
+ Настройки
+ Простая игра
+ Создать игру
+ Присоединиться к игре
+ Наборы карт
+ Настройки
+ OK
+ Набор карт:
+ Число раундов: 3
+ Создать
+
+ - "Список наборов"
+ - "Список карт"
+ - "Добавить набор"
+
+ Отмена
+ Итоговый счёт:
+ Рейтинги:
+ Последняя ассоциация:
+ Счёт
+ Раунд 0/N
+ Играть
+ Имя создателя игры:
+ Присоединиться
+ Ваша ассоциация:
+ Готово
+ Ваша карта:
+ Войти
+ Регистрация
+ Выберите имя пользователя:
+ Пароль:
+ Повторите пароль:
+ Зарегистрироваться
+
+ - "Изменить пароль"
+ - "Выйти"
+ - "Изменить IP сервера."
+
+ "Неправильный пароль."
+ "Неправильный логин."
+ "Ошибка сети."
+ "Старый пароль введён неверно."
+ "Ожидание ответа."
+ "Первый и второй пароль не совпадают."
+ "Имя пользователя занято."
+ "Ошибка"
+ "Регистрация успешно завершена."
+ "Время: %1$s"
+ "Вы водите"
+ "Новая игра"
+ "Число раундов: %1$d"
+ "Выберите набор карт."
+ "Ожидание игры"
+ "Игра закончена."
+ "Рейтинг: %1$d"
+ "Ваша карта"
+ "Голосование"
+ "Ассоциация: %1$s"
+ "Счёт:"
+ "Раунд: %1$s"
+ "Удалить игрока?"
+ "Да"
+ "Нет"
+ "Необходимо авторизоваться."
+ "%1$s, %2$d карт(ы)"
+ "Время на выбор: %1$s"
+ "мин."
+ "сек."
+ "Чат"
+ "Наборы"
+ "Недостаточно игроков."
+ "Результаты:"
+ Игрок
+ Рейтинг
+ "IP-адрес сервера."
+ IP
+ "Сбросить"
+ Добавить карту
+ Имя набора
diff --git a/build.gradle b/build.gradle
index 2449eab..cfce2ca 100644
--- a/build.gradle
+++ b/build.gradle
@@ -5,7 +5,7 @@ buildscript {
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:2.2.2'
+ classpath 'com.android.tools.build:gradle:2.2.3'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files