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 @@ + + + + + + + + + + + + + + + + + +