commit 8d1597618af8502f29b756aafd117a12a5db9d8d Author: Carlos Galindo Date: Tue May 16 21:46:12 2017 +0200 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e4d8cb8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,106 @@ +*.iml +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures +.externalNativeBuild +### Android template +# Built application files +*.apk +*.ap_ + +# Files for the ART/Dalvik VM +*.dex + +# Java class files +*.class + +# Generated files +bin/ +gen/ +out/ + +# Gradle files +.gradle/ +build/ + +# Local configuration file (sdk path, etc) +local.properties + +# Proguard folder generated by Eclipse +proguard/ + +# Log Files +*.log + +# Android Studio Navigation editor temp files +.navigation/ + +# Android Studio captures folder +captures/ + +# Intellij +.idea/workspace.xml +.idea/tasks.xml +.idea/gradle.xml +.idea/dictionaries +.idea/libraries + +# Keystore files +*.jks + +# External native build folder generated in Android Studio 2.2 and later + +# Google Services (e.g. APIs or Firebase) +google-services.json + +# Freeline +freeline.py +freeline/ +freeline_project_description.json +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff: +.idea/**/workspace.xml +.idea/**/tasks.xml + +# Sensitive or high-churn files: +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.xml +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml + +# Gradle: +.idea/**/gradle.xml +.idea/**/libraries + +# Mongo Explorer plugin: +.idea/**/mongoSettings.xml + +## File-based project format: +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..96cc43e --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml new file mode 100644 index 0000000..e7bedf3 --- /dev/null +++ b/.idea/copyright/profiles_settings.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..97626ba --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/markdown-navigator/profiles_settings.xml b/.idea/markdown-navigator/profiles_settings.xml new file mode 100644 index 0000000..57927c5 --- /dev/null +++ b/.idea/markdown-navigator/profiles_settings.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..25ce2cf --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..35400b3 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml new file mode 100644 index 0000000..7f68460 --- /dev/null +++ b/.idea/runConfigurations.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..6dfa63e --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,33 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 25 + buildToolsVersion "25.0.2" + defaultConfig { + applicationId "com.kauron.newssarcher" + minSdkVersion 16 + targetSdkVersion 25 + versionCode 1 + versionName "1.0" + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + vectorDrawables.useSupportLibrary = true + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar']) + 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:25.3.1' + compile 'com.android.support.constraint:constraint-layout:1.0.2' + compile 'com.android.support:support-v4:25.3.1' + compile 'com.android.support:support-vector-drawable:25.3.1' + testCompile 'junit:junit:4.12' +} diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..042e5ab --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,25 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /home/carlos/linux/Android/Sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/app/src/androidTest/java/com/kauron/newssarcher/ExampleInstrumentedTest.java b/app/src/androidTest/java/com/kauron/newssarcher/ExampleInstrumentedTest.java new file mode 100644 index 0000000..ecbefe3 --- /dev/null +++ b/app/src/androidTest/java/com/kauron/newssarcher/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.kauron.newssarcher; + +import android.content.Context; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumentation test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() throws Exception { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getTargetContext(); + + assertEquals("com.kauron.newssarcher", appContext.getPackageName()); + } +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..bfdef95 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/ic_launcher-web.png b/app/src/main/ic_launcher-web.png new file mode 100644 index 0000000..92152ad Binary files /dev/null and b/app/src/main/ic_launcher-web.png differ diff --git a/app/src/main/java/com/kauron/newssarcher/MainActivity.java b/app/src/main/java/com/kauron/newssarcher/MainActivity.java new file mode 100644 index 0000000..957b2f2 --- /dev/null +++ b/app/src/main/java/com/kauron/newssarcher/MainActivity.java @@ -0,0 +1,186 @@ +package com.kauron.newssarcher; + +import android.content.DialogInterface; +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.AsyncTask; +import android.os.Bundle; +import android.preference.PreferenceManager; +import android.support.v7.app.AlertDialog; +import android.support.v7.app.AppCompatActivity; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.inputmethod.EditorInfo; +import android.widget.AdapterView; +import android.widget.Button; +import android.widget.CheckBox; +import android.widget.EditText; +import android.widget.ListView; +import android.widget.TextView; +import android.widget.Toast; + +import java.util.ArrayList; + +public class MainActivity extends AppCompatActivity { + private EditText editText; + private CheckBox stemmingCheck, stopwordsCheck; + private QueryAdapter mAdapter; + private ArrayList queries; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + queries = new ArrayList<>(); + ListView listView = (ListView) findViewById(R.id.listView); + editText = (EditText) findViewById(R.id.editText); + stemmingCheck = (CheckBox) findViewById(R.id.stemmingCheck); + stopwordsCheck = (CheckBox) findViewById(R.id.stopwordsCheck); + + editText.setOnEditorActionListener(new TextView.OnEditorActionListener() { + @Override + public boolean onEditorAction(TextView textView, int i, KeyEvent keyEvent) { + if (i == EditorInfo.IME_ACTION_SEARCH) { + onButtonClick(null); + return true; + } + return false; + } + }); + + mAdapter = new QueryAdapter(this, queries); + listView.setAdapter(mAdapter); + listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView adapterView, View view, int i, long l) { + if (queries.get(i).hasAnswer()) { + Toast.makeText(adapterView.getContext(), R.string.no_answer_yet, + Toast.LENGTH_LONG).show(); + return; + } + + LayoutInflater inflater= LayoutInflater.from(adapterView.getContext()); + View mView=inflater.inflate(R.layout.my_dialog, null); + + TextView answerView = (TextView) mView.findViewById(R.id.answer_text); + TextView shortAnswerView = (TextView) mView.findViewById(R.id.shortanswer_text); + TextView optionsView = (TextView) mView.findViewById(R.id.options_text); + + answerView.setText(queries.get(i).getAnswer()); + shortAnswerView.setText(queries.get(i).getShortAnswer()); + optionsView.setText(queries.get(i).getOptions()); + + final int[] position = new int[] {i}; + + AlertDialog.Builder alertDialog = new AlertDialog.Builder(adapterView.getContext()); + alertDialog.setTitle(queries.get(i).getQuery()); + alertDialog.setView(mView); + alertDialog.setPositiveButton(R.string.close, null); + alertDialog.setNeutralButton(R.string.copy_query, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + editText.setText(""); + editText.append(queries.get(position[0]).getQuery()); + dialogInterface.dismiss(); + } + }); + AlertDialog alert = alertDialog.create(); + alert.show(); + } + }); + listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { + @Override + public boolean onItemLongClick(AdapterView adapterView, View view, int i, long l) { + editText.getText().clear(); + editText.append(queries.get(i).getQuery()); + return false; + } + }); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.menu, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle item selection + switch (item.getItemId()) { + case R.id.menu_settings: + Intent intent = new Intent(this, SettingsActivity.class); + startActivity(intent); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + public void onButtonClick(View view) { + // connect to the server + String s = editText.getText().toString(); + if (s.isEmpty()) + return; + SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this); + String endpoint = sharedPref.getString("pref_endpoint", "kauron.ddns.net"); + int port = Integer.valueOf(sharedPref.getString("pref_port", "2048")); + + Query q = new Query(s, endpoint, port, stopwordsCheck.isChecked(), stemmingCheck.isChecked(), this); + new QueryTask().execute(q); + queries.add(0, q); + mAdapter.notifyDataSetChanged(); + editText.setText(""); + editText.requestFocus(); + } + + public void onSymbolClick(View view) { + editText.append(((Button)view).getText().toString()); + } + + public void onBoolClick(View view) { + String s = ((Button)view).getText().toString() + ' '; + String text = editText.getText().toString(); + if (!text.isEmpty() && text.charAt(text.length() - 1) != ' ') + s = ' ' + s; + editText.append(s); + } + + public void onFieldClick(View view) { + String s = ((Button)view).getText().toString(); + String text = editText.getText().toString(); + if (!text.isEmpty() && text.charAt(text.length() - 1) != ' ') + s = ' ' + s; + editText.append(s); + } + + + private class QueryTask extends AsyncTask { + @Override + protected Void doInBackground(Query... queries) { + Query q = queries[0]; + String message = q.getQuery() + q.getShortOptions(); + + TCPClient mTcpClient = new TCPClient(message, q.getEndpoint(), new TCPClient.OnMessageReceived() { + @Override + //here the messageReceived method is implemented + public void messageReceived(String response) { + MainActivity.this.queries.get(0).setAnswer(response); + } + }); + mTcpClient.run(); + return null; + } + + @Override + protected void onPostExecute(Void aVoid) { + mAdapter.notifyDataSetChanged(); + } + } +} diff --git a/app/src/main/java/com/kauron/newssarcher/Query.java b/app/src/main/java/com/kauron/newssarcher/Query.java new file mode 100644 index 0000000..fd2453c --- /dev/null +++ b/app/src/main/java/com/kauron/newssarcher/Query.java @@ -0,0 +1,58 @@ +package com.kauron.newssarcher; + +import android.content.Context; + +class Query { + private final String query; + private String answer; + private final boolean noStopwords; + private final boolean stemming; + private final Context context; + private final String host; + private final int port; + + Query(String query, String host, int port, boolean noStopwords, boolean stemming, Context context) { + this.query = query; + this.noStopwords = noStopwords; + this.stemming = stemming; + this.context = context; + this.host = host; + this.port = port; + } + + String getEndpoint() { + return host + ":" + port; + } + + String getQuery() { + return query; + } + + String getAnswer() { + return answer; + } + + String getOptions() { + return String.format("Stemming (%s), Remove stopwords (%s)", stemming ? "yes" : "no", + noStopwords ? "yes" : "no"); + } + + String getShortOptions() { + return (stemming ? " -s" : "") + (noStopwords ? " -n" : ""); + } + + String getShortAnswer() { + if (answer == null) + return context.getString(R.string.no_answer_yet); + else + return answer.substring(answer.lastIndexOf('\n') + 1); + } + + void setAnswer(String answer) { + this.answer = answer; + } + + boolean hasAnswer() { + return answer == null; + } +} diff --git a/app/src/main/java/com/kauron/newssarcher/QueryAdapter.java b/app/src/main/java/com/kauron/newssarcher/QueryAdapter.java new file mode 100644 index 0000000..3198083 --- /dev/null +++ b/app/src/main/java/com/kauron/newssarcher/QueryAdapter.java @@ -0,0 +1,41 @@ +package com.kauron.newssarcher; + +import android.content.Context; +import android.support.annotation.NonNull; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.TextView; + +import java.util.ArrayList; + +class QueryAdapter extends ArrayAdapter { + + QueryAdapter(@NonNull Context context, @NonNull ArrayList objects) { + super(context, R.layout.query_list_item, objects); + } + + @NonNull + @Override + public View getView(int position, View convertView, @NonNull ViewGroup parent) { + // Get the data item for this position + Query q = getItem(position); + + LayoutInflater inflater = LayoutInflater.from(getContext()); + if (convertView == null) + convertView = inflater.inflate(R.layout.query_list_item, parent, false); + TextView query = (TextView) convertView.findViewById(R.id.query_text); + TextView options = (TextView) convertView.findViewById(R.id.options_text); + TextView result = (TextView) convertView.findViewById(R.id.result_text); + + if (q != null) { + query.setText(q.getQuery()); + options.setText(q.getOptions()); + result.setText(q.getShortAnswer()); + } + + // Return the completed view to render on screen + return convertView; + } +} diff --git a/app/src/main/java/com/kauron/newssarcher/SettingsActivity.java b/app/src/main/java/com/kauron/newssarcher/SettingsActivity.java new file mode 100644 index 0000000..cb69240 --- /dev/null +++ b/app/src/main/java/com/kauron/newssarcher/SettingsActivity.java @@ -0,0 +1,28 @@ +package com.kauron.newssarcher; + +import android.os.Bundle; +import android.preference.PreferenceActivity; +import android.preference.PreferenceFragment; + +public class SettingsActivity extends PreferenceActivity { + + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Display the fragment as the main content. + getFragmentManager().beginTransaction() + .replace(android.R.id.content, new SettingsFragment()) + .commit(); + } + + + public static class SettingsFragment extends PreferenceFragment { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Load the preferences from an XML resource + addPreferencesFromResource(R.xml.pref_general); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/kauron/newssarcher/TCPClient.java b/app/src/main/java/com/kauron/newssarcher/TCPClient.java new file mode 100644 index 0000000..33afab6 --- /dev/null +++ b/app/src/main/java/com/kauron/newssarcher/TCPClient.java @@ -0,0 +1,70 @@ +package com.kauron.newssarcher; + +import android.util.Log; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.net.Socket; + +class TCPClient { + + private String SERVER_DOMAIN = "kauron.ddns.net"; //your computer IP address + private int SERVER_PORT = 2048; + private OnMessageReceived mMessageListener = null; + private final String message; + + /** + * Constructor of the class. OnMessagedReceived listens for the messages received from server + */ + TCPClient(String message, String endpoint, OnMessageReceived listener) { + mMessageListener = listener; + this.message = message; + if (endpoint.contains(":")) { + String[] strings = endpoint.split(":"); + SERVER_DOMAIN = strings[0]; + SERVER_PORT = Integer.valueOf(strings[1]); + } + } + + void run() { + try { + Socket socket = new Socket(SERVER_DOMAIN, SERVER_PORT); + try { + //send the message to the server + PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true); + if (!out.checkError()) { + out.print(this.message); + out.flush(); + } + //receive the message which the server sends back + BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); + String text = "", line; + while ((line = in.readLine()) != null) { + text += line + '\n'; + } + text = text.substring(0, text.length() - 1); + + if (mMessageListener != null) { + mMessageListener.messageReceived(text); + } + } catch (Exception e) { + Log.e("TCP", "S: Error", e); + } finally { + //the socket must be closed. It is not possible to reconnect to this socket + // after it is closed, which means a new socket instance has to be created. + socket.close(); + } + } catch (Exception e) { + Log.e("TCP", "C: Error", e); + } + } + + //Declare the interface. The method messageReceived(String message) will must be implemented in the MyActivity + //class at on asynckTask doInBackground + interface OnMessageReceived { + void messageReceived(String message); + } +} diff --git a/app/src/main/res/drawable/ic_search_black_24dp.xml b/app/src/main/res/drawable/ic_search_black_24dp.xml new file mode 100644 index 0000000..affc7ba --- /dev/null +++ b/app/src/main/res/drawable/ic_search_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_settings_black_24dp.xml b/app/src/main/res/drawable/ic_settings_black_24dp.xml new file mode 100644 index 0000000..ce997a7 --- /dev/null +++ b/app/src/main/res/drawable/ic_settings_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..a19d7e2 --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,234 @@ + + + + + + + + + + + + + +