diff --git a/MyCity/app/build.gradle b/MyCity/app/build.gradle index 9e5ede1..22557ae 100644 --- a/MyCity/app/build.gradle +++ b/MyCity/app/build.gradle @@ -25,6 +25,8 @@ dependencies { implementation 'com.android.support:appcompat-v7:27.0.0' implementation 'com.android.support:design:27.0.0' implementation 'com.android.support.constraint:constraint-layout:1.1.3' + implementation 'com.android.support:support-v4:27.1.1' + implementation 'com.android.support:recyclerview-v7:27.1.1' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' diff --git a/MyCity/app/src/main/AndroidManifest.xml b/MyCity/app/src/main/AndroidManifest.xml index cc04a85..cfb4487 100644 --- a/MyCity/app/src/main/AndroidManifest.xml +++ b/MyCity/app/src/main/AndroidManifest.xml @@ -1,7 +1,7 @@ - + { + + private List listeners = new ArrayList<>(); + + + @Override + protected Bitmap doInBackground(String... URL) { + + String imageURL = URL[0]; + Log.d("[BOOKMARK]",imageURL); + + Bitmap bitmap = null; + try { + // Download Image from URL + InputStream input = new java.net.URL(imageURL).openStream(); + // Decode Bitmap + bitmap = BitmapFactory.decodeStream(input); + } catch (Exception e) { + e.printStackTrace(); + } + return bitmap; + } + + @Override + protected void onPostExecute(Bitmap result) { + for (imageListener hl : listeners) + hl.imageDownloaded(result); + } + + public void addListener(imageListener toAdd) { + listeners.add(toAdd); + } +} diff --git a/MyCity/app/src/main/java/gq/yigit/mycity/MainActivity.java b/MyCity/app/src/main/java/gq/yigit/mycity/MainActivity.java index 35fdf5b..22ad329 100644 --- a/MyCity/app/src/main/java/gq/yigit/mycity/MainActivity.java +++ b/MyCity/app/src/main/java/gq/yigit/mycity/MainActivity.java @@ -3,9 +3,10 @@ package gq.yigit.mycity; import android.content.Context; import android.content.DialogInterface; import android.os.Bundle; -import android.support.constraint.ConstraintLayout; import android.support.design.widget.FloatingActionButton; import android.support.design.widget.Snackbar; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentTransaction; import android.support.v7.app.AlertDialog; import android.util.Log; import android.view.*; @@ -16,13 +17,11 @@ import android.support.v7.app.ActionBarDrawerToggle; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.widget.EditText; -import android.widget.RelativeLayout; -import android.widget.ViewFlipper; - -import java.io.*; +import gq.yigit.mycity.vote.VotesContent; +import gq.yigit.mycity.vote.VotesFragment; public class MainActivity extends AppCompatActivity - implements NavigationView.OnNavigationItemSelectedListener { + implements NavigationView.OnNavigationItemSelectedListener, VotesFragment.OnListFragmentInteractionListener { public Context cntxt; @@ -71,23 +70,21 @@ public class MainActivity extends AppCompatActivity @Override public boolean onOptionsItemSelected(MenuItem item) { - // Handle action bar item clicks here. The action bar will - // automatically handle clicks on the Home/Up button, so long - // as you specify a parent activity in AndroidManifest.xml. + int id = item.getItemId(); //noinspection SimplifiableIfStatement if (id == R.id.action_settings) { AlertDialog.Builder alert = new AlertDialog.Builder(this); alert.setTitle("Set new server"); - alert.setMessage("Server IP:"); + alert.setMessage("Server URL:"); final EditText input = new EditText(this); alert.setView(input); alert.setPositiveButton("Ok", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { - fileActions file_manager = new fileActions(); - file_manager.writeToFile(input.getText().toString(),cntxt,"serverip.config"); + FileActions file_manager = new FileActions(); + file_manager.writeToFile(input.getText().toString(),cntxt,"server.config"); } }); @@ -109,14 +106,14 @@ public class MainActivity extends AppCompatActivity public boolean onNavigationItemSelected(MenuItem item) { // Handle navigation view item clicks here. int id = item.getItemId(); + FragmentManager fragmentManager = getSupportFragmentManager(); + FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); if (id == R.id.voting) { - ViewFlipper vf = (ViewFlipper)findViewById(R.id.page); - try { - vf.setDisplayedChild(1); - }catch (Exception e){ - Log.e("[ERROR]",e.toString()); - } + VotesFragment fragment = new VotesFragment(); + fragmentTransaction.add(R.id.app_bar_main, fragment); + fragmentTransaction.commit(); + } else if (id == R.id.parking) { } else if (id == R.id.transit) { @@ -134,4 +131,8 @@ public class MainActivity extends AppCompatActivity return true; } + public void onListFragmentInteraction(VotesContent.VoteItem vote){ + Log.i("[INFO]",vote.id); + } + } diff --git a/MyCity/app/src/main/java/gq/yigit/mycity/VotesActivity.java b/MyCity/app/src/main/java/gq/yigit/mycity/VotesActivity.java deleted file mode 100644 index a31b4a7..0000000 --- a/MyCity/app/src/main/java/gq/yigit/mycity/VotesActivity.java +++ /dev/null @@ -1,4 +0,0 @@ -package gq.yigit.mycity; - -public class VotesActivity { -} diff --git a/MyCity/app/src/main/java/gq/yigit/mycity/WebRequest.java b/MyCity/app/src/main/java/gq/yigit/mycity/WebRequest.java index 96a1b28..f446766 100644 --- a/MyCity/app/src/main/java/gq/yigit/mycity/WebRequest.java +++ b/MyCity/app/src/main/java/gq/yigit/mycity/WebRequest.java @@ -27,7 +27,8 @@ public class WebRequest extends AsyncTask { private List listeners = new ArrayList<>(); - WebRequest(String url, boolean request_type, HashMap request_content){ + public WebRequest(String url, boolean request_type, HashMap request_content){ + //request_type=true:get else post this.url = url; this.request_content = request_content; diff --git a/MyCity/app/src/main/java/gq/yigit/mycity/imageListener.java b/MyCity/app/src/main/java/gq/yigit/mycity/imageListener.java new file mode 100644 index 0000000..005ffd9 --- /dev/null +++ b/MyCity/app/src/main/java/gq/yigit/mycity/imageListener.java @@ -0,0 +1,7 @@ +package gq.yigit.mycity; + +import android.graphics.Bitmap; + +public interface imageListener { + public void imageDownloaded(Bitmap img); +} diff --git a/MyCity/app/src/main/java/gq/yigit/mycity/vote/MyVotesRecyclerViewAdapter.java b/MyCity/app/src/main/java/gq/yigit/mycity/vote/MyVotesRecyclerViewAdapter.java new file mode 100644 index 0000000..7067e1e --- /dev/null +++ b/MyCity/app/src/main/java/gq/yigit/mycity/vote/MyVotesRecyclerViewAdapter.java @@ -0,0 +1,77 @@ +package gq.yigit.mycity.vote; + +import android.support.v7.widget.RecyclerView; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import gq.yigit.mycity.R; +import gq.yigit.mycity.vote.VotesFragment.OnListFragmentInteractionListener; +import gq.yigit.mycity.vote.VotesContent.VoteItem; + +import java.util.List; + +public class MyVotesRecyclerViewAdapter extends RecyclerView.Adapter { + + private final List mValues; + private final OnListFragmentInteractionListener mListener; + + public MyVotesRecyclerViewAdapter(List items, OnListFragmentInteractionListener listener) { + mValues = items; + mListener = listener; + } + + @Override + public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + View view = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.fragment_votes, parent, false); + return new ViewHolder(view); + } + + @Override + public void onBindViewHolder(final ViewHolder holder, int position) { + + holder.mItem = mValues.get(position); + holder.mIdView.setText(mValues.get(position).name); + holder.mContentView.setText(mValues.get(position).details); + holder.mImageView.setImageBitmap(mValues.get(position).img); + + holder.mView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (null != mListener) { + mListener.onListFragmentInteraction(holder.mItem); + } + } + }); + } + + @Override + public int getItemCount() { + return mValues.size(); + } + + public class ViewHolder extends RecyclerView.ViewHolder { + public final View mView; + public final TextView mIdView; + public final TextView mContentView; + public final ImageView mImageView; + public VoteItem mItem; + + public ViewHolder(View view) { + super(view); + mView = view; + mIdView = (TextView) view.findViewById(R.id.item_number); + mContentView = (TextView) view.findViewById(R.id.content); + mImageView = (ImageView) view.findViewById(R.id.vote_img); + } + + @Override + public String toString() { + return super.toString() + " '" + mContentView.getText() + "'"; + } + } +} diff --git a/MyCity/app/src/main/java/gq/yigit/mycity/vote/VotesContent.java b/MyCity/app/src/main/java/gq/yigit/mycity/vote/VotesContent.java new file mode 100644 index 0000000..66df6e0 --- /dev/null +++ b/MyCity/app/src/main/java/gq/yigit/mycity/vote/VotesContent.java @@ -0,0 +1,40 @@ +package gq.yigit.mycity.vote; + +import android.graphics.Bitmap; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class VotesContent { + + public static final List ITEMS = new ArrayList(); + + + public static final Map ITEM_MAP = new HashMap(); + public static void addItem(VoteItem item) { + ITEMS.add(item); + ITEM_MAP.put(item.id, item); + } + + + public static class VoteItem { + public final String id; + public final String name; + public final String details; + public final Bitmap img; + + public VoteItem(String id, String name, String details, Bitmap img) { + this.id = id; + this.name = name; + this.details = details; + this.img = img; + } + + @Override + public String toString() { + return name; + } + } +} diff --git a/MyCity/app/src/main/java/gq/yigit/mycity/vote/VotesFragment.java b/MyCity/app/src/main/java/gq/yigit/mycity/vote/VotesFragment.java new file mode 100644 index 0000000..7427a3a --- /dev/null +++ b/MyCity/app/src/main/java/gq/yigit/mycity/vote/VotesFragment.java @@ -0,0 +1,137 @@ +package gq.yigit.mycity.vote; + +import android.content.Context; +import android.graphics.Bitmap; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v7.widget.GridLayoutManager; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import gq.yigit.mycity.*; +import gq.yigit.mycity.vote.VotesContent.VoteItem; +import org.json.JSONArray; +import org.json.JSONObject; + +import java.util.HashMap; + + +public class VotesFragment extends Fragment implements responseListener, imageListener { + + private static final String ARG_COLUMN_COUNT = "column-count"; + private int mColumnCount = 1; + private OnListFragmentInteractionListener mListener; + public RecyclerView recyclerView; + public String url; + public int img_count = 0; + public JSONArray votes; + public ImageDownload img_downloader; + public int j = 0; + public VotesFragment() { + } + + public static VotesFragment newInstance(int columnCount) { + VotesFragment fragment = new VotesFragment(); + Bundle args = new Bundle(); + args.putInt(ARG_COLUMN_COUNT, columnCount); + fragment.setArguments(args); + return fragment; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + if (getArguments() != null) { + mColumnCount = getArguments().getInt(ARG_COLUMN_COUNT); + } + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_votes_list, container, false); + + // Set the adapter + if (view instanceof RecyclerView) { + Context context = view.getContext(); + recyclerView = (RecyclerView) view; + if (mColumnCount <= 1) { + recyclerView.setLayoutManager(new LinearLayoutManager(context)); + } else { + recyclerView.setLayoutManager(new GridLayoutManager(context, mColumnCount)); + } + FileActions file_manager = new FileActions(); + url = (file_manager.readFromFile(context,"server.config")).trim(); + WebRequest web_manager = new WebRequest(url + "/votings",true,new HashMap()); + web_manager.addListener(this); + web_manager.execute(); + } + return view; + } + + + @Override + public void onAttach(Context context) { + super.onAttach(context); + if (context instanceof OnListFragmentInteractionListener) { + mListener = (OnListFragmentInteractionListener) context; + } else { + throw new RuntimeException(context.toString() + + " must implement OnListFragmentInteractionListener"); + } + } + + @Override + public void onDetach() { + super.onDetach(); + mListener = null; + } + + public interface OnListFragmentInteractionListener { + void onListFragmentInteraction(VoteItem item); + } + + public void receivedResponse(boolean success, String response){ + + if(success) { + try { + votes = new JSONArray(response); + img_downloader = new ImageDownload(); + img_downloader.addListener(this); + img_downloader.execute(url+ ((JSONObject)votes.get(j)).getString("img")); + + }catch (Exception e){ + Log.e("[ERROR]:",e.getMessage()); + } + Log.i("[INFO]",VotesContent.ITEMS.toString()); + }else{ + Log.e("[ERROR]:",response); + } + } + + public void imageDownloaded(Bitmap img){ + try { + JSONObject vote = (JSONObject) votes.get(j); + vote.put("img",img); + j++; + if(j>votes.length()-1) { + for (int i = 0; i < votes.length(); i++) { + JSONObject poll = (JSONObject) votes.get(i); + VotesContent.addItem(new VoteItem(poll.get("id").toString(), poll.get("name").toString(), poll.get("desc").toString(), (Bitmap) poll.get("img"))); + } + recyclerView.setAdapter(new MyVotesRecyclerViewAdapter(VotesContent.ITEMS, mListener)); + }else{ + img_downloader = new ImageDownload(); + img_downloader.addListener(this); + img_downloader.execute(url+ ((JSONObject)votes.get(j)).getString("img")); + } + } catch (Exception e) { + Log.e("[ERROR]", e.toString()); + } + } + +} diff --git a/MyCity/app/src/main/res/drawable/bus.xml b/MyCity/app/src/main/res/drawable-v21/bus.xml similarity index 100% rename from MyCity/app/src/main/res/drawable/bus.xml rename to MyCity/app/src/main/res/drawable-v21/bus.xml diff --git a/MyCity/app/src/main/res/drawable/navigation.xml b/MyCity/app/src/main/res/drawable-v21/navigation.xml similarity index 100% rename from MyCity/app/src/main/res/drawable/navigation.xml rename to MyCity/app/src/main/res/drawable-v21/navigation.xml diff --git a/MyCity/app/src/main/res/drawable/parking.xml b/MyCity/app/src/main/res/drawable-v21/parking.xml similarity index 100% rename from MyCity/app/src/main/res/drawable/parking.xml rename to MyCity/app/src/main/res/drawable-v21/parking.xml diff --git a/MyCity/app/src/main/res/drawable/star.xml b/MyCity/app/src/main/res/drawable-v21/star.xml similarity index 100% rename from MyCity/app/src/main/res/drawable/star.xml rename to MyCity/app/src/main/res/drawable-v21/star.xml diff --git a/MyCity/app/src/main/res/drawable/utility.xml b/MyCity/app/src/main/res/drawable-v21/utility.xml similarity index 100% rename from MyCity/app/src/main/res/drawable/utility.xml rename to MyCity/app/src/main/res/drawable-v21/utility.xml diff --git a/MyCity/app/src/main/res/drawable/vote.xml b/MyCity/app/src/main/res/drawable-v21/vote.xml similarity index 100% rename from MyCity/app/src/main/res/drawable/vote.xml rename to MyCity/app/src/main/res/drawable-v21/vote.xml diff --git a/MyCity/app/src/main/res/drawable/vote_style.xml b/MyCity/app/src/main/res/drawable/vote_style.xml new file mode 100644 index 0000000..7580a99 --- /dev/null +++ b/MyCity/app/src/main/res/drawable/vote_style.xml @@ -0,0 +1,12 @@ + + + + + \ No newline at end of file diff --git a/MyCity/app/src/main/res/layout/app_bar_main.xml b/MyCity/app/src/main/res/layout/app_bar_main.xml index e7a93be..2583eba 100644 --- a/MyCity/app/src/main/res/layout/app_bar_main.xml +++ b/MyCity/app/src/main/res/layout/app_bar_main.xml @@ -5,6 +5,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" + android:id="@+id/app_bar_main" tools:context=".MainActivity"> - - - - - - + + + + + + + + + + diff --git a/MyCity/app/src/main/res/layout/fragment_votes_list.xml b/MyCity/app/src/main/res/layout/fragment_votes_list.xml new file mode 100644 index 0000000..f7e940c --- /dev/null +++ b/MyCity/app/src/main/res/layout/fragment_votes_list.xml @@ -0,0 +1,18 @@ + + \ No newline at end of file diff --git a/MyCity/app/src/main/res/layout/voting_content.xml b/MyCity/app/src/main/res/layout/voting_content.xml deleted file mode 100644 index 15984a8..0000000 --- a/MyCity/app/src/main/res/layout/voting_content.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - \ No newline at end of file diff --git a/MyCity/app/src/main/res/values/dimens.xml b/MyCity/app/src/main/res/values/dimens.xml index 32f2c1c..2b8eaf0 100644 --- a/MyCity/app/src/main/res/values/dimens.xml +++ b/MyCity/app/src/main/res/values/dimens.xml @@ -5,4 +5,5 @@ 8dp 176dp 16dp + 16dp \ No newline at end of file diff --git a/server_side/api/voting_system/app.py b/server_side/api/voting_system/app.py index 53c10e3..4f46742 100644 --- a/server_side/api/voting_system/app.py +++ b/server_side/api/voting_system/app.py @@ -1,84 +1,97 @@ import os -import ssl import json -from flask import Flask, send_from_directory -from flask_restful import Resource, Api, abort, reqparse +from flask import Flask, send_from_directory, request +from flask_restful import Resource, Api, abort app = Flask(__name__) api = Api(app) -parser = reqparse.RequestParser() -parser.add_argument('name', required=True) -parser.add_argument('desc') -parser.add_argument('img') -parser.add_argument('votes', required=True) - with open(os.path.join(app.root_path, 'votings.json'), 'r') as f: - votings = json.load(f) + votings = json.load(f) class Votings(Resource): - def get(self): - voting = [ - { - 'id' : v['id'], - 'name': v['name'], - 'desc': v['desc'], - 'img' : v['img'] - } - for v in votings - ] - return voting - - def post(self): - args = parser.parse_args() - voting_id = len(votings) + 1 - voting = { - 'id': voting_id, - 'name': args['name'], - 'desc': args['desc'], - 'img' : args['img'], - 'votes': [ - { - 'id': k, - 'name': vote['name'], - 'desc': vote['desc'], - 'votes': 0 - } - for k, vote in enumerate(json.loads(args['votes'])) - ] - } - - votings.append(voting) - - with open(os.path.join(app.root_path, 'votings.json'), 'w') as f: - json.dump(votings, f, indent=4) - - return voting - + def get(self): + voting = [ + { + 'id' : v['id'], + 'name': v['name'], + 'desc': v['desc'], + 'img' : v['img'] + } + for v in votings + ] + return voting + + def post(self): + """ + Example POST Data: + name=& + desc=& # OPTIONAL + img=& # OPTIONAL + votes=[ + { + "name": "", + "desc": "" # OPTIONAL + }, + (...) + ] + + """ + args = request.form + voting_id = len(votings) + 1 + voting = { + 'id': voting_id, + 'name': args['name'], + 'desc': args.get('desc'), + 'img' : args.get('img'), + 'votes': [ + { + 'id' : k, + 'name': vote['name'], + 'desc': vote.get('desc'), + 'votes': 0 + } + for k, vote in enumerate(json.loads(args['votes'])) + ] + } + + votings.append(voting) + + with open(os.path.join(app.root_path, 'votings.json'), 'w') as f: + json.dump(votings, f, indent=4) + + return voting + class Voting(Resource): - def get(self, voting_id): - try: - return votings[voting_id - 1] - except: - abort(404, error="Voting {} doesn't exist".format(voting_id)) + def get(self, voting_id): + try: + return votings[voting_id - 1] + except: + abort(404, error="Voting {} doesn't exist".format(voting_id)) class Vote(Resource): - def get(self, voting_id, vote_id): - votings[voting_id - 1]['votes'][vote_id - 1]['votes'] += 1 - with open(os.path.join(app.root_path, 'votings.json'), 'w') as f: - json.dump(votings, f, indent=4) - return votings[voting_id - 1] + def get(self): + """ + Example URL Query: + /vote?voting_id=&vote_id= + """ + voting_id = int(request.args['voting_id']) + vote_id = int(request.args['vote_id']) + votings[voting_id - 1]['votes'][vote_id - 1]['votes'] += 1 + with open(os.path.join(app.root_path, 'votings.json'), 'w') as f: + json.dump(votings, f, indent=4) + + return votings[voting_id - 1] api.add_resource(Votings, '/votings', '/votings/') api.add_resource(Voting, '/votings/') -api.add_resource(Vote, '/vote//') +api.add_resource(Vote, '/vote', '/vote/') @app.route('/img/') def send_img(path): - return send_from_directory('images', path) + return send_from_directory('images', path) if __name__ == '__main__': - app.run(host='0.0.0.0', port=5000, debug=True) - + app.run(host='0.0.0.0', port=5000)