diff --git a/app/app.iml b/app/app.iml index 219a04b..20413bf 100644 --- a/app/app.iml +++ b/app/app.iml @@ -56,7 +56,25 @@ <sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" /> <sourceFolder url="file://$MODULE_DIR$/src/androidTest/jni" isTestSource="true" /> <sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates" /> + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" /> + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/bundles" /> + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" /> + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/coverage-instrumented-classes" /> + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" /> + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex" /> + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex-cache" /> + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" /> + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/jacoco" /> + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/javaResources" /> + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/libs" /> + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/lint" /> + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" /> + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/ndk" /> + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/pre-dexed" /> + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/proguard" /> + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" /> + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" /> + <excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" /> <excludeFolder url="file://$MODULE_DIR$/build/outputs" /> </content> <orderEntry type="jdk" jdkName="Android API 19 Platform" jdkType="Android SDK" /> diff --git a/app/build.gradle b/app/build.gradle index 6dce2b5..06e72d4 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -2,11 +2,11 @@ apply plugin: 'com.android.application' android { compileSdkVersion 19 - buildToolsVersion '20.0.0' + buildToolsVersion '19.1.0' defaultConfig { applicationId "info.nerull7.mysqlbrowser" - minSdkVersion 15 + minSdkVersion 14 targetSdkVersion 19 versionCode 1 versionName "1.0" diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 199b74d..b31c3ff 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -3,13 +3,14 @@ package="info.nerull7.mysqlbrowser" > <application - android:allowBackup="true" + android:allowBackup="false" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name=".MainActivity" - android:label="@string/app_name" > + android:label="@string/app_name" + android:theme="@style/LoginTheme" > <intent-filter> <action android:name="android.intent.action.MAIN" /> @@ -17,25 +18,30 @@ </intent-filter> </activity> <activity - android:name=".DatabaseActivity" - android:label="@string/title_activity_database" > - </activity> - <activity - android:name=".TableActivity" - android:label="@string/title_activity_table" > + android:name=".ListActivity" + android:label="@string/title_activity_list" > </activity> <activity android:name=".EntriesActivity" - android:label="@string/title_activity_entries" - android:theme="@style/EntriesTheme" > + android:label="@string/title_activity_entries" > </activity> <activity android:name=".SettingsActivity" android:label="@string/title_activity_setting" - android:icon="@drawable/ic_action_settings"> + android:theme="@style/SettingsTheme" > + </activity> + <activity + android:name=".ElementActivity" + android:label="@string/title_activity_element" + android:windowSoftInputMode="adjustPan"> + </activity> + <activity + android:name=".SQLActivity" + android:label="@string/title_activity_sql" + android:windowSoftInputMode="adjustResize"> </activity> </application> <uses-permission android:name="android.permission.INTERNET" /> - + <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> </manifest> diff --git a/app/src/main/java/info/nerull7/mysqlbrowser/Crypto.java b/app/src/main/java/info/nerull7/mysqlbrowser/Crypto.java index e22105a..0ad320e 100644 --- a/app/src/main/java/info/nerull7/mysqlbrowser/Crypto.java +++ b/app/src/main/java/info/nerull7/mysqlbrowser/Crypto.java @@ -2,21 +2,17 @@ package info.nerull7.mysqlbrowser; import android.content.Context; import android.util.Base64; -import android.util.Log; import java.io.File; import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.nio.charset.Charset; import java.security.InvalidKeyException; -import java.security.Key; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; -import java.security.spec.KeySpec; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; @@ -24,10 +20,11 @@ import javax.crypto.IllegalBlockSizeException; import javax.crypto.KeyGenerator; import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; -import javax.crypto.spec.PBEKeySpec; /** * Created by nerull7 on 28.07.14. + * + * Class delegated to encrypt data */ public class Crypto { private static final String KEY_FILE = "null_file"; // to trick h4x0r5 @@ -49,23 +46,19 @@ public class Crypto { } } - private SecretKey generateKey() throws NoSuchAlgorithmException { + private void generateKey() throws NoSuchAlgorithmException { SecureRandom secureRandom = new SecureRandom(); KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM); keyGenerator.init(OUTPUT_KEY_LENGTH, secureRandom); - SecretKey secretKey = keyGenerator.generateKey(); - - return secretKey; + secretKey = keyGenerator.generateKey(); } private void getSecretKey() throws NoSuchAlgorithmException, IOException, ClassNotFoundException { - String key; - // First try to open file File keyFile = new File(context.getFilesDir(), KEY_FILE); if(!keyFile.exists()) { // new key - secretKey = generateKey(); + generateKey(); FileOutputStream fileOutputStream = new FileOutputStream(keyFile); ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream); objectOutputStream.writeObject(secretKey); @@ -100,7 +93,6 @@ public class Crypto { public String decryptBase64(String encodedString) throws IllegalBlockSizeException, InvalidKeyException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException { byte [] encryptedString = Base64.decode(encodedString, Base64.DEFAULT); - String decrypted = decrypt(encryptedString); - return decrypted; + return decrypt(encryptedString); } } diff --git a/app/src/main/java/info/nerull7/mysqlbrowser/CustomScrollView.java b/app/src/main/java/info/nerull7/mysqlbrowser/CustomScrollView.java index e59685e..13ab2d4 100644 --- a/app/src/main/java/info/nerull7/mysqlbrowser/CustomScrollView.java +++ b/app/src/main/java/info/nerull7/mysqlbrowser/CustomScrollView.java @@ -2,6 +2,7 @@ package info.nerull7.mysqlbrowser; import android.content.Context; import android.util.AttributeSet; +import android.view.MotionEvent; import android.widget.ScrollView; /** @@ -9,10 +10,18 @@ import android.widget.ScrollView; */ public class CustomScrollView extends ScrollView { - private OnScrollChangedListener listener; + private OnTouchEventListener onTouchEventListener; - public void setOnScrollChangedListener(OnScrollChangedListener listener){ - this.listener = listener; + @Override + public boolean onTouchEvent(MotionEvent ev) { + if(onTouchEventListener != null){ + onTouchEventListener.onTouchEvent(ev); + } + return super.onTouchEvent(ev); + } + + public void setOnTouchEventListener(OnTouchEventListener onTouchEventListener){ + this.onTouchEventListener = onTouchEventListener; } public CustomScrollView(Context context) { @@ -23,13 +32,7 @@ public class CustomScrollView extends ScrollView { super(context,attributeSet); } - @Override - protected void onScrollChanged(int l, int t, int oldl, int oldt) { - if(listener!=null) - listener.onScrollChanged(l,t,oldl,oldt); - } - - public interface OnScrollChangedListener{ - public void onScrollChanged(int l, int t, int oldl, int oldt); + public interface OnTouchEventListener { + public boolean onTouchEvent(MotionEvent ev); } } diff --git a/app/src/main/java/info/nerull7/mysqlbrowser/DatabaseFragment.java b/app/src/main/java/info/nerull7/mysqlbrowser/DatabaseFragment.java index 428cc0d..428afa3 100644 --- a/app/src/main/java/info/nerull7/mysqlbrowser/DatabaseFragment.java +++ b/app/src/main/java/info/nerull7/mysqlbrowser/DatabaseFragment.java @@ -17,20 +17,25 @@ import android.widget.TextView; import java.util.List; -import info.nerull7.mysqlbrowser.db.AsyncDatabaseConnector; +import info.nerull7.mysqlbrowser.db.DatabaseConnector; /** * Created by nerull7 on 14.07.14. * * Fragment for showing list of Available Databases for user */ -public class DatabaseFragment extends Fragment implements AdapterView.OnItemClickListener, AsyncDatabaseConnector.ListReturnListener { +public class DatabaseFragment extends Fragment implements AdapterView.OnItemClickListener, DatabaseConnector.ListReturnListener, DatabaseConnector.OnPostExecuteListener { private ListView databasesListView; private ListAdapter listAdapter; private RelativeLayout rootView; private ProgressBar progressBar; + private List<String> databases; - private String chosenDatabase; + @Override + public void onResume() { + super.onResume(); + Static.databaseConnector.setDatabaseInUse(null); + } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){ @@ -40,48 +45,46 @@ public class DatabaseFragment extends Fragment implements AdapterView.OnItemClic this.rootView = (RelativeLayout) rootView; progressBar = (ProgressBar) rootView.findViewById(R.id.loginProgressBar); -// Static.asyncDatabaseConnector.setListReturnListener(this); -// Static.asyncDatabaseConnector.getDatabases(); - - listAdapter = new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1, Static.databases); - databasesListView.setAdapter(listAdapter); - databasesListView.setOnItemClickListener(this); - progressBar.setVisibility(View.INVISIBLE); - + Static.databaseConnector.setListReturnListener(this); + Static.databaseConnector.setOnPostExecuteListener(this); + Static.databaseConnector.getDatabases(); return rootView; } @Override public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) { - chosenDatabase = (String) listAdapter.getItem(position); - listAdapter.getItem(position); - - progressBar.setVisibility(View.VISIBLE); - Static.asyncDatabaseConnector.setDatabaseInUse(chosenDatabase); - Static.asyncDatabaseConnector.setListReturnListener(this); - Static.asyncDatabaseConnector.getTables(); + if(Static.isNetworkConnected(getActivity())) { + String chosenDatabase = (String) listAdapter.getItem(position); + listAdapter.getItem(position); + Intent intent = new Intent(getActivity(), ListActivity.class); + intent.putExtra(Static.FRAGMENT_TO_START, Static.FRAGMENT_TABLE); + intent.putExtra(Static.DATABASE_NAME_ARG, chosenDatabase); + Static.databaseConnector.setDatabaseInUse(chosenDatabase); + startActivity(intent); + } else { + Static.showErrorAlert(getResources().getString(R.string.no_connection), getActivity()); + } } @Override - public void onListReturn(List<String> tables) { -// if(databases!= null) { -// listAdapter = new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1, databases); -// databasesListView.setAdapter(listAdapter); -// databasesListView.setOnItemClickListener(this); -// } else { -// TextView errorMessage = new TextView(getActivity()); -// errorMessage.setText(R.string.error_no_databases); -// errorMessage.setTypeface(null, Typeface.ITALIC); -// errorMessage.setClickable(false); -// rootView.addView(errorMessage); -// rootView.removeView(databasesListView); -// } -// progressBar.setVisibility(View.INVISIBLE); - if(tables!=null) { - Static.tables = tables; + public void onListReturn(List<String> databases) { + this.databases = databases; + } + + @Override + public void onPostExecute() { + if(databases!= null) { + listAdapter = new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1, databases); + databasesListView.setAdapter(listAdapter); + databasesListView.setOnItemClickListener(this); + } else { + TextView errorMessage = new TextView(getActivity()); + errorMessage.setText(R.string.error_no_databases); + errorMessage.setTypeface(null, Typeface.ITALIC); + errorMessage.setClickable(false); + rootView.addView(errorMessage); + rootView.removeView(databasesListView); } - Intent intent = new Intent(getActivity(), TableActivity.class); - intent.putExtra(Static.DATABASE_NAME_ARG, chosenDatabase); - startActivity(intent); + progressBar.setVisibility(View.INVISIBLE); } } diff --git a/app/src/main/java/info/nerull7/mysqlbrowser/ElementActivity.java b/app/src/main/java/info/nerull7/mysqlbrowser/ElementActivity.java new file mode 100644 index 0000000..2034810 --- /dev/null +++ b/app/src/main/java/info/nerull7/mysqlbrowser/ElementActivity.java @@ -0,0 +1,49 @@ +package info.nerull7.mysqlbrowser; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.os.Bundle; + +public class ElementActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_element); + Bundle bundle = getIntent().getExtras(); + String titleName = bundle.getString(Static.TABLE_NAME_ARG); + setTitle(titleName); + + ElementFragment elementFragment = new ElementFragment(); + elementFragment.setArguments(bundle); + + if (savedInstanceState == null) { + getFragmentManager().beginTransaction() + .add(R.id.container, elementFragment) + .commit(); + } + } + + @Override + public void onBackPressed() { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setMessage(getString(R.string.error_no_save)); + builder.setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + + } + }); + builder.setPositiveButton(getString(R.string.yes), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + finish(); + } + }); + builder.setTitle(R.string.warning); + builder.setIcon(R.drawable.ic_action_warning); + builder.create(); + builder.show(); + } +} diff --git a/app/src/main/java/info/nerull7/mysqlbrowser/ElementArrayAdapter.java b/app/src/main/java/info/nerull7/mysqlbrowser/ElementArrayAdapter.java new file mode 100644 index 0000000..32388b5 --- /dev/null +++ b/app/src/main/java/info/nerull7/mysqlbrowser/ElementArrayAdapter.java @@ -0,0 +1,79 @@ +package info.nerull7.mysqlbrowser; + +import android.content.Context; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.TextView; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by nerull7 on 2014-08-06. + */ +public class ElementArrayAdapter extends ArrayAdapter<String> { + private Context context; + private List<String> fields; + private List<String> values; + private int layout; + + public ElementArrayAdapter(Context context, int resource, List<String> fields) { + super(context, resource, fields); + init(context, resource, fields); + values = new ArrayList<String>(); + for(String field: fields){ + values.add(""); + } + } + + public ElementArrayAdapter(Context context, int resource, List<String> fields, List<String> values) { + super(context, resource, fields); + init(context, resource, fields); + this.values = new ArrayList<String>(); + if(values!=null) { + this.values.addAll(values); // Copy + } else { + for(int i=0;i<fields.size();i++){ + this.values.add(new String()); + } + } + } + + private void init(Context context, int resource, List<String> fields){ + this.context = context; + this.fields = fields; + layout = resource; + } + + @Override + public View getView(final int position, View convertView, ViewGroup parent) { + LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + final View rowView = layoutInflater.inflate(layout, parent, false); + + TextView textView = (TextView) rowView.findViewById(R.id.textFieldName); + textView.setText(fields.get(position)); + TextView textFieldName = (TextView) rowView.findViewById(R.id.editFieldValue); + textFieldName.setText(values.get(position)); + textFieldName.setOnFocusChangeListener(new View.OnFocusChangeListener() { + @Override + public void onFocusChange(View v, boolean hasFocus) { + if (!hasFocus) { + String tmp = String.valueOf(((TextView) v).getText()); + values.set(position, tmp); + } + } + }); + return rowView; + } + + public List<String> getFieldArray(){ + return fields; + } + + public List<String> getValues() { + return values; + } +} diff --git a/app/src/main/java/info/nerull7/mysqlbrowser/ElementFragment.java b/app/src/main/java/info/nerull7/mysqlbrowser/ElementFragment.java new file mode 100644 index 0000000..236c769 --- /dev/null +++ b/app/src/main/java/info/nerull7/mysqlbrowser/ElementFragment.java @@ -0,0 +1,202 @@ +package info.nerull7.mysqlbrowser; + +import android.app.AlertDialog; +import android.app.Fragment; +import android.content.Context; +import android.content.DialogInterface; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.view.inputmethod.InputMethodManager; +import android.widget.AbsListView; +import android.widget.ListView; +import android.widget.ProgressBar; + +import java.util.List; + +import info.nerull7.mysqlbrowser.db.DatabaseConnector; + +/** + * Created by nerull7 on 2014-08-06. + * + * Fragment for editing/adding elements + */ +public class ElementFragment extends Fragment implements DatabaseConnector.ListReturnListener, DatabaseConnector.StringReturnListener, DatabaseConnector.OnPostExecuteListener { + public static final String EDIT_ELEMENT = "edit_element"; + public static final String EDIT_LIST = "edit_element_list"; + + private static final int POST_EXECUTE_NONE = 0; + private static final int POST_EXECUTE_GET_FIELDS = 1; + private static final int POST_EXECUTE_UPDATE_ELEMENT = 2; + private static final int POST_EXECUTE_ADD_ELEMENT = 3; + + private String tableName; + private ElementArrayAdapter listAdapter; + + private ProgressBar progressBar; + private ListView listView; + + private List<String> values; + private String message; + private int postExecute; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + //Inflate the layout for this fragment + View rootView = inflater.inflate(R.layout.fragment_element, container, false); + progressBar = (ProgressBar) rootView.findViewById(R.id.progressBar); + listView = (ListView) rootView.findViewById(R.id.listView); + + listView.setOnScrollListener(new AbsListView.OnScrollListener() { + @Override + public void onScrollStateChanged(AbsListView view, int scrollState) { + if(scrollState==SCROLL_STATE_TOUCH_SCROLL) { + listView.requestFocus(); + InputMethodManager inputMethodManager = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE); + inputMethodManager.hideSoftInputFromWindow(listView.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); + } + } + + @Override + public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { + } + }); + listView.setItemsCanFocus(true); + initArguments(); + + postExecute = POST_EXECUTE_NONE; + Static.databaseConnector.setListReturnListener(this); + Static.databaseConnector.setOnPostExecuteListener(this); + Static.databaseConnector.getFields(tableName); + + return rootView; + } + + private void actionSave(){ + List<String> fields = listAdapter.getFieldArray(); + Static.databaseConnector.setStringReturnListener(this); + if(getArguments().getBoolean(EDIT_ELEMENT)) + Static.databaseConnector.updateElement(tableName, fields, values, listAdapter.getValues()); + else + Static.databaseConnector.addNewElement(tableName, fields, listAdapter.getValues()); + } + + private void actionRemove(){ + final List<String> fields = listAdapter.getFieldArray(); + Static.databaseConnector.setStringReturnListener(this); + + final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + builder.setMessage(R.string.error_remove); + builder.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + Static.databaseConnector.removeElement(tableName, fields, values); +// getActivity().finish(); + } + }); + builder.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + // Nothing to do just get back + } + }); + builder.setTitle(R.string.warning); + builder.setIcon(R.drawable.ic_action_warning); + builder.setCancelable(false); // There is no exit + builder.create(); + builder.show(); + + + + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + listView.requestFocus(); + switch (item.getItemId()){ + case R.id.action_save: + actionSave(); + break; + case R.id.action_remove: + actionRemove(); + break; + default: + return super.onOptionsItemSelected(item); + } + return true; + } + + private void initArguments() { + tableName = getArguments().getString(Static.TABLE_NAME_ARG); + if(getArguments().getBoolean(EDIT_ELEMENT)) + values = getArguments().getStringArrayList(EDIT_LIST); + else + values = null; + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + inflater.inflate(R.menu.element, menu); + if(!getArguments().getBoolean(EDIT_ELEMENT)) { + menu.findItem(R.id.action_remove).setVisible(false); + } + super.onCreateOptionsMenu(menu, inflater); + } + + @Override + public void onListReturn(List<String> fields) { + listAdapter = new ElementArrayAdapter(getActivity(), R.layout.list_item_element_simple, fields, values); + postExecute = POST_EXECUTE_GET_FIELDS; + + } + + @Override + public void onStringReturn(String data) { + message = data; + postExecute = POST_EXECUTE_ADD_ELEMENT; + } + + @Override + public void onPostExecute() { + switch (postExecute){ + case POST_EXECUTE_GET_FIELDS: + listView.setAdapter(listAdapter); + progressBar.setVisibility(View.INVISIBLE); + setHasOptionsMenu(true); + break; + case POST_EXECUTE_ADD_ELEMENT: + case POST_EXECUTE_UPDATE_ELEMENT: + showInfo(message); + break; + } + // Clean after execute + postExecute = POST_EXECUTE_NONE; + } + + private void showInfo(String info){ + final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + builder.setMessage(info); + builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + getActivity().finish(); + } + }); + /*builder.setNegativeButton(R.string.back, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + // Nothing to do just get back + } + });*/ + builder.setTitle(R.string.status); + builder.setIcon(R.drawable.ic_action_warning); + builder.setCancelable(false); // There is no exit + builder.create(); + builder.show(); + } + +} diff --git a/app/src/main/java/info/nerull7/mysqlbrowser/EntriesActivity.java b/app/src/main/java/info/nerull7/mysqlbrowser/EntriesActivity.java index db98696..c783710 100644 --- a/app/src/main/java/info/nerull7/mysqlbrowser/EntriesActivity.java +++ b/app/src/main/java/info/nerull7/mysqlbrowser/EntriesActivity.java @@ -2,11 +2,6 @@ package info.nerull7.mysqlbrowser; import android.app.Activity; import android.os.Bundle; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; - -import java.util.zip.Inflater; public class EntriesActivity extends Activity { diff --git a/app/src/main/java/info/nerull7/mysqlbrowser/EntriesFragment.java b/app/src/main/java/info/nerull7/mysqlbrowser/EntriesFragment.java index ddc5daa..3c8a5b9 100644 --- a/app/src/main/java/info/nerull7/mysqlbrowser/EntriesFragment.java +++ b/app/src/main/java/info/nerull7/mysqlbrowser/EntriesFragment.java @@ -1,6 +1,7 @@ package info.nerull7.mysqlbrowser; import android.app.Fragment; +import android.content.Intent; import android.graphics.Typeface; import android.os.Bundle; import android.preference.PreferenceManager; @@ -8,9 +9,11 @@ import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; +import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; +import android.widget.HorizontalScrollView; import android.widget.ProgressBar; import android.widget.RelativeLayout; import android.widget.ScrollView; @@ -18,14 +21,17 @@ import android.widget.TableLayout; import android.widget.TableRow; import android.widget.TextView; +import java.util.ArrayList; import java.util.List; -import info.nerull7.mysqlbrowser.db.AsyncDatabaseConnector; +import info.nerull7.mysqlbrowser.db.DatabaseConnector; /** * Created by nerull7 on 15.07.14. + * + * Fragment for showing elements */ -public class EntriesFragment extends Fragment implements AsyncDatabaseConnector.MatrixReturnListener, AsyncDatabaseConnector.ListReturnListener, AsyncDatabaseConnector.IntegerReturnListener { +public class EntriesFragment extends Fragment implements DatabaseConnector.MatrixReturnListener, DatabaseConnector.ListReturnListener, DatabaseConnector.IntegerReturnListener, View.OnClickListener, DatabaseConnector.OnPostExecuteListener { private static final int HEADER_PADDING_TOP = 15; private static final int HEADER_PADDING_BOTTOM = 15; private static final int HEADER_PADDING_LEFT = 15; @@ -36,23 +42,28 @@ public class EntriesFragment extends Fragment implements AsyncDatabaseConnector. private static final int ENTRIES_PADDING_RIGHT = 15; private TableLayout entriesTable; - private CustomScrollView entriesScrollView; + private ScrollView entriesScrollView; private FrameLayout headerFrame; - private RelativeLayout rootView; + private HorizontalScrollView horizontalScrollView; private TableRow.LayoutParams layoutParams; - private TableRow headerRow; private String databaseName; private String tableName; private int entriesLimit; private int page; private int pageCount; + private int rowCount; private ProgressBar progressBar; - private ScrollView fakeScrollView; + private CustomScrollView fakeScrollView; private View dummyView; - private MenuInflater menuInflater; + private int onPostExecuteListenerExecuted; + private Menu menu; + private TableRow headerRow; + private int[] maxWidth; + + private boolean isFirstCreate; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, @@ -61,63 +72,11 @@ public class EntriesFragment extends Fragment implements AsyncDatabaseConnector. initArguments(); initViewItems(rootView); + initListeners(); -// Static.asyncDatabaseConnector.setIntegerReturnListener(this); -// Static.asyncDatabaseConnector.setListReturnListener(this); -// Static.asyncDatabaseConnector.setMatrixReturnListener(this); -// Static.asyncDatabaseConnector.getFields(tableName); -// Static.asyncDatabaseConnector.getEntriesCount(tableName); - - pageCount = Static.pageCount/entriesLimit; - if( Static.pageCount%entriesLimit > 0) - pageCount++; - - if(pageCount>1) - setHasOptionsMenu(true); - - // First we need header - headerRow = new TableRow(getActivity()); - headerRow.setLayoutParams(new TableRow.LayoutParams(TableRow.LayoutParams.MATCH_PARENT, TableRow.LayoutParams.WRAP_CONTENT)); - for(int i =0;i<Static.header.size();i++){ - TextView textView = new TextView(getActivity()); - textView.setText(Static.header.get(i)); - textView.setTypeface(null, Typeface.BOLD); - textView.setLayoutParams(layoutParams); - textView.setBackgroundResource(R.drawable.background_header); - textView.setPadding(HEADER_PADDING_LEFT, HEADER_PADDING_TOP, HEADER_PADDING_RIGHT, HEADER_PADDING_BOTTOM); - headerRow.addView(textView); - } - headerFrame.addView(headerRow); - - // now data - if(Static.entries!=null) { - for (int i = 0; i < Static.entries.size(); i++) { - List<String> elements = Static.entries.get(i); - TableRow newRow = new TableRow(getActivity()); - for (int j = 0; j < elements.size(); j++) { // elements.size can be the same as in header so maybe some one number or not - TextView textView = new TextView(getActivity()); - textView.setText(elements.get(j)); - textView.setLayoutParams(layoutParams); - textView.setPadding(ENTRIES_PADDING_LEFT, ENTRIES_PADDING_TOP, ENTRIES_PADDING_RIGHT, ENTRIES_PADDING_BOTTOM); - textView.setBackgroundResource(R.drawable.background_element); - newRow.addView(textView); - } - entriesTable.addView(newRow); - - syncWidths(); - fakeScroll(); - } - } else { - TextView errorMessage = new TextView(getActivity()); - errorMessage.setText(R.string.error_no_entries); - errorMessage.setTypeface(null, Typeface.ITALIC); - errorMessage.setClickable(false); - entriesScrollView.removeView(entriesTable); - headerFrame.setVisibility(View.VISIBLE); - entriesScrollView.addView(errorMessage); - } - - setLoading(false); + onPostExecuteListenerExecuted = 0; + Static.databaseConnector.getFields(tableName); + Static.databaseConnector.getEntriesCount(tableName); return rootView; } @@ -126,39 +85,48 @@ public class EntriesFragment extends Fragment implements AsyncDatabaseConnector. databaseName = getArguments().getString(Static.DATABASE_NAME_ARG); tableName = getArguments().getString(Static.TABLE_NAME_ARG); page = 1; + isFirstCreate = true; entriesLimit = PreferenceManager.getDefaultSharedPreferences(getActivity()).getInt(SettingsFragment.ENTRIES_PAGE_LIMIT, SettingsFragment.ENTRIES_PAGE_LIMIT_DEF); } private void initViewItems(View rootView){ headerFrame = (FrameLayout) rootView.findViewById(R.id.headerFrame); - entriesTable = (TableLayout) rootView.findViewById(R.id.entriesTable); - entriesScrollView = (CustomScrollView) rootView.findViewById(R.id.entriesScrollView); - fakeScrollView = (ScrollView) rootView.findViewById(R.id.fakeScroll); + entriesScrollView = (ScrollView) rootView.findViewById(R.id.entriesScrollView); + fakeScrollView = (CustomScrollView) rootView.findViewById(R.id.fakeScroll); progressBar = (ProgressBar) rootView.findViewById(R.id.loginProgressBar); dummyView = rootView.findViewById(R.id.dummyView); + horizontalScrollView = (HorizontalScrollView) rootView.findViewById(R.id.horizontalScrollView); + entriesTable = new TableLayout(getActivity()); - this.rootView = (RelativeLayout) rootView; - - entriesScrollView.setOnScrollChangedListener(new CustomScrollView.OnScrollChangedListener() { + fakeScrollView.setOnTouchEventListener(new CustomScrollView.OnTouchEventListener() { @Override - public void onScrollChanged(int l, int t, int oldl, int oldt) { - fakeScrollView.scrollTo(0,t); + public boolean onTouchEvent(MotionEvent ev) { + ev.offsetLocation(0, headerFrame.getHeight()); + horizontalScrollView.dispatchTouchEvent(ev); + return true; } }); - layoutParams = new TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT, TableRow.LayoutParams.WRAP_CONTENT); - headerFrame.setVisibility(View.INVISIBLE); - entriesTable.setVisibility(View.INVISIBLE); + layoutParams = new TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT, TableRow.LayoutParams.MATCH_PARENT); + } + + private void initListeners(){ + Static.databaseConnector.setIntegerReturnListener(this); + Static.databaseConnector.setListReturnListener(this); + Static.databaseConnector.setMatrixReturnListener(this); + Static.databaseConnector.setOnPostExecuteListener(this); } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { inflater.inflate(R.menu.entries_activity_actions, menu); - menuInflater = inflater; // I think we need it later + menu.findItem(R.id.action_previous).setVisible(false); // hide previous + if(pageCount<=1) + menu.findItem(R.id.action_next).setVisible(false); // hide next if we don't have any more pages this.menu = menu; -// super.onCreateOptionsMenu(menu, inflater); + super.onCreateOptionsMenu(menu, inflater); } private void changeMenus(int page){ @@ -182,76 +150,97 @@ public class EntriesFragment extends Fragment implements AsyncDatabaseConnector. progressBar.setVisibility(isLoading ? View.VISIBLE : View.INVISIBLE); } - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()){ - case R.id.action_previous: - page--; - break; - case R.id.action_next: - page++; - break; - } + private void loadAnotherPage(){ changeMenus(page); - entriesTable.removeAllViews(); // clean table + entriesScrollView.removeAllViews(); // clean table + entriesTable = new TableLayout(getActivity()); + onPostExecuteListenerExecuted--; setLoading(true); - Static.asyncDatabaseConnector.getRows(tableName, entriesLimit, page); // get new entries + Static.databaseConnector.getRows(tableName, entriesLimit, page); // get new entries + } + private void addNewElement(){ + Intent intent = new Intent(getActivity(), ElementActivity.class); + intent.putExtra(Static.DATABASE_NAME_ARG,databaseName); + intent.putExtra(Static.TABLE_NAME_ARG,tableName); + startActivity(intent); + } + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (Static.isNetworkConnected(getActivity())) { + switch (item.getItemId()) { + case R.id.action_previous: + page--; + loadAnotherPage(); + break; + case R.id.action_next: + page++; + loadAnotherPage(); + break; + case R.id.action_add: + addNewElement(); + break; + } + } else { + Static.showErrorAlert(getResources().getString(R.string.no_connection), getActivity()); + } return super.onOptionsItemSelected(item); } @Override public void onMatrixReturn(List<List<String>> rows) { -// // Now we get Rows -// if(rows!=null) { -// for (int i = 0; i < rows.size(); i++) { -// List<String> elements = rows.get(i); -// TableRow newRow = new TableRow(getActivity()); -// for (int j = 0; j < elements.size(); j++) { // elements.size can be the same as in header so maybe some one number or not -// TextView textView = new TextView(getActivity()); -// textView.setText(elements.get(j)); -// textView.setLayoutParams(layoutParams); -// textView.setPadding(ENTRIES_PADDING_LEFT, ENTRIES_PADDING_TOP, ENTRIES_PADDING_RIGHT, ENTRIES_PADDING_BOTTOM); -// textView.setBackgroundResource(R.drawable.background_element); -// newRow.addView(textView); -// } -// entriesTable.addView(newRow); -// -// syncWidths(); -// fakeScroll(); -// } -// } else { -// TextView errorMessage = new TextView(getActivity()); -// errorMessage.setText(R.string.error_no_entries); -// errorMessage.setTypeface(null, Typeface.ITALIC); -// errorMessage.setClickable(false); -// entriesScrollView.removeView(entriesTable); -// headerFrame.setVisibility(View.VISIBLE); -// entriesScrollView.addView(errorMessage); -// } -// -// setLoading(false); + // Now we get Rows + if(rows!=null) { + int background; + for (int i = 0; i < rows.size(); i++) { + List<String> elements = rows.get(i); + TableRow newRow = new TableRow(getActivity()); + + if( i%2 == 0 ){ // Two backgrounds for lines for better visibility + background=R.drawable.entries_element_1; + } else { + background=R.drawable.entries_element_2; + } + + for (int j = 0; j < elements.size(); j++) { // elements.size can be the same as in header so maybe some one number or not + TextView textView = new TextView(getActivity()); + textView.setText(elements.get(j)); + textView.setLayoutParams(layoutParams); + textView.setPadding(ENTRIES_PADDING_LEFT, ENTRIES_PADDING_TOP, ENTRIES_PADDING_RIGHT, ENTRIES_PADDING_BOTTOM); + textView.setBackgroundResource(background); + textView.setId(j); + newRow.addView(textView); + } + newRow.setClickable(true); + newRow.setOnClickListener(this); + entriesTable.addView(newRow); + syncWidthsFirstStage(); + } + } else { + entriesTable = null; + } + } @Override public void onListReturn(List<String> fieldList) { -// // First we need header -// headerRow = new TableRow(getActivity()); -// headerRow.setLayoutParams(new TableRow.LayoutParams(TableRow.LayoutParams.MATCH_PARENT, TableRow.LayoutParams.WRAP_CONTENT)); -// for(int i =0;i<fieldList.size();i++){ -// TextView textView = new TextView(getActivity()); -// textView.setText(fieldList.get(i)); -// textView.setTypeface(null, Typeface.BOLD); -// textView.setLayoutParams(layoutParams); -// textView.setBackgroundResource(R.drawable.background_header); -// textView.setPadding(HEADER_PADDING_LEFT, HEADER_PADDING_TOP, HEADER_PADDING_RIGHT, HEADER_PADDING_BOTTOM); -// headerRow.addView(textView); -// } -// headerFrame.addView(headerRow); -// -// Static.asyncDatabaseConnector.getRows(tableName, entriesLimit, page); + // First we need header + headerRow = new TableRow(getActivity()); + headerRow.setLayoutParams(new TableRow.LayoutParams(TableRow.LayoutParams.MATCH_PARENT, TableRow.LayoutParams.WRAP_CONTENT)); + rowCount = fieldList.size(); + for (String aFieldList : fieldList) { + TextView textView = new TextView(getActivity()); + textView.setText(aFieldList); + textView.setTypeface(null, Typeface.BOLD); + textView.setLayoutParams(layoutParams); + textView.setBackgroundResource(R.drawable.background_header); + textView.setPadding(HEADER_PADDING_LEFT, HEADER_PADDING_TOP, HEADER_PADDING_RIGHT, HEADER_PADDING_BOTTOM); + headerRow.addView(textView); + } + + Static.databaseConnector.getRows(tableName, entriesLimit, page); } @Override @@ -259,31 +248,35 @@ public class EntriesFragment extends Fragment implements AsyncDatabaseConnector. pageCount = result/entriesLimit; if( result%entriesLimit > 0) pageCount++; - - if(pageCount>1) - setHasOptionsMenu(true); + getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + setHasOptionsMenu(true); + } + }); } - private void syncWidths(){ // TODO: Merge with adding columns maybe? Loops -= 3 should be quicker - TableRow headerRow = (TableRow) headerFrame.getChildAt(0); - int maxWidth[]= new int[headerRow.getChildCount()]; - for(int i=0;i<headerRow.getChildCount();i++){ + private void syncWidthsFirstStage() { // TODO: Merge with adding columns maybe? Loops -= 3 should be quicker + maxWidth = new int[headerRow.getChildCount()]; + for (int i = 0; i < headerRow.getChildCount(); i++) { TextView textView = (TextView) headerRow.getChildAt(i); textView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); maxWidth[i] = textView.getMeasuredWidth(); } - for(int i=0;i<entriesTable.getChildCount();i++){ + for (int i = 0; i < entriesTable.getChildCount(); i++) { TableRow tableRow = (TableRow) entriesTable.getChildAt(i); - for(int j=0;j<tableRow.getChildCount();j++){ + for (int j = 0; j < tableRow.getChildCount(); j++) { TextView textView = (TextView) tableRow.getChildAt(j); textView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); int width = textView.getMeasuredWidth(); - if(width>maxWidth[j]) - maxWidth[j]=width; + if (width > maxWidth[j]) + maxWidth[j] = width; } } + } + private void syncWidthsSecondStage() { for(int i=0;i<headerRow.getChildCount();i++){ TableRow entriesRow = (TableRow) entriesTable.getChildAt(0); @@ -293,16 +286,65 @@ public class EntriesFragment extends Fragment implements AsyncDatabaseConnector. tmpEntries.setWidth(maxWidth[i]); tmpHeader.setWidth(maxWidth[i]); } - - headerFrame.setVisibility(View.VISIBLE); - entriesTable.setVisibility(View.VISIBLE); } private void fakeScroll(){ entriesScrollView.measure(View.MeasureSpec.UNSPECIFIED,View.MeasureSpec.UNSPECIFIED); int height = entriesScrollView.getMeasuredHeight(); dummyView.setMinimumHeight(height); + + RelativeLayout.LayoutParams fakeScrollLayout = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); + headerFrame.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); + fakeScrollLayout.setMargins(0,headerFrame.getMeasuredHeight(),0,0); + + fakeScrollView.setLayoutParams(fakeScrollLayout); } + @Override + public void onClick(View view) { + ArrayList<String> values = new ArrayList<String>(); + for(int i=0;i<rowCount;i++){ + TextView element = (TextView)view.findViewById(i); + values.add(element.getText().toString()); +// element.setBackgroundResource(android.R.color.holo_blue_bright); + } + Intent intent = new Intent(getActivity(), ElementActivity.class); + intent.putExtra(Static.DATABASE_NAME_ARG,databaseName); + intent.putExtra(Static.TABLE_NAME_ARG,tableName); + intent.putExtra(ElementFragment.EDIT_ELEMENT, true); + intent.putStringArrayListExtra(ElementFragment.EDIT_LIST, values); + startActivity(intent); + } + + @Override + public void onResume() { + super.onResume(); + if(!isFirstCreate) { + initListeners(); // Could be overwritten + loadAnotherPage(); // This reloads entries + } else { + isFirstCreate = false; + } + } + + @Override + public void onPostExecute() { + if(++onPostExecuteListenerExecuted==3){ + if(headerFrame.getChildCount()==0) // You can have only one child + headerFrame.addView(headerRow); + if(entriesTable!=null) { + syncWidthsSecondStage(); + entriesScrollView.addView(entriesTable); + fakeScroll(); + } else { + TextView errorMessage = new TextView(getActivity()); + errorMessage.setText(R.string.error_no_entries); + errorMessage.setTypeface(null, Typeface.ITALIC); + errorMessage.setClickable(false); + entriesScrollView.addView(errorMessage); + } + setLoading(false); + } + } } diff --git a/app/src/main/java/info/nerull7/mysqlbrowser/DatabaseActivity.java b/app/src/main/java/info/nerull7/mysqlbrowser/ListActivity.java similarity index 51% rename from app/src/main/java/info/nerull7/mysqlbrowser/DatabaseActivity.java rename to app/src/main/java/info/nerull7/mysqlbrowser/ListActivity.java index 0449410..3eeb667 100644 --- a/app/src/main/java/info/nerull7/mysqlbrowser/DatabaseActivity.java +++ b/app/src/main/java/info/nerull7/mysqlbrowser/ListActivity.java @@ -1,19 +1,31 @@ package info.nerull7.mysqlbrowser; import android.app.Activity; +import android.app.Fragment; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; -public class DatabaseActivity extends Activity { +public class ListActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.activity_database); + setContentView(R.layout.activity_list); + Bundle extras = getIntent().getExtras(); + String fragmentName = extras.getString(Static.FRAGMENT_TO_START); + Fragment fragment = null; + if(fragmentName.compareTo(Static.FRAGMENT_DATABASE)==0){ + fragment = new DatabaseFragment(); + setTitle(R.string.title_fragment_database); + } else if (fragmentName.compareTo(Static.FRAGMENT_TABLE)==0) { + fragment = new TableFragment(); + setTitle(extras.getString(Static.DATABASE_NAME_ARG)); + } + fragment.setArguments(getIntent().getExtras()); if (savedInstanceState == null) { getFragmentManager().beginTransaction() - .add(R.id.container, new DatabaseFragment()) + .add(R.id.container, fragment) .commit(); } } @@ -21,7 +33,7 @@ public class DatabaseActivity extends Activity { @Override public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.main, menu); + getMenuInflater().inflate(R.menu.logged, menu); return true; } @@ -35,6 +47,8 @@ public class DatabaseActivity extends Activity { if (id == R.id.action_settings) { Static.startSettings(this); return true; + } else if (id == R.id.action_sql){ + Static.startSQL(this, getIntent().getExtras().getString(Static.DATABASE_NAME_ARG)); } return super.onOptionsItemSelected(item); } diff --git a/app/src/main/java/info/nerull7/mysqlbrowser/LoginFragment.java b/app/src/main/java/info/nerull7/mysqlbrowser/LoginFragment.java index 5946d31..324c7e4 100644 --- a/app/src/main/java/info/nerull7/mysqlbrowser/LoginFragment.java +++ b/app/src/main/java/info/nerull7/mysqlbrowser/LoginFragment.java @@ -1,46 +1,34 @@ package info.nerull7.mysqlbrowser; -import android.app.AlertDialog; import android.app.Fragment; -import android.content.Context; -import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; -import android.graphics.Typeface; import android.os.Bundle; -import android.preference.Preference; import android.preference.PreferenceManager; -import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.EditText; import android.widget.ProgressBar; -import android.widget.TextView; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.util.List; - -import javax.crypto.BadPaddingException; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.NoSuchPaddingException; - -import info.nerull7.mysqlbrowser.db.AsyncDatabaseConnector; +import info.nerull7.mysqlbrowser.db.DatabaseConnector; /** * Created by nerull7 on 07.07.14. + * + * Fragment for login */ -public class LoginFragment extends Fragment implements View.OnClickListener, AsyncDatabaseConnector.BooleanReturnListener, AsyncDatabaseConnector.ListReturnListener { +public class LoginFragment extends Fragment implements View.OnClickListener, DatabaseConnector.BooleanReturnListener, DatabaseConnector.OnPostExecuteListener { private EditText urlTextbox; private EditText loginTextbox; private EditText passwordTextbox; private ProgressBar progressBar; private Button loginButton; - AsyncDatabaseConnector asyncDatabaseConnector; + DatabaseConnector databaseConnector; + + private boolean result; public LoginFragment(){} @@ -61,6 +49,13 @@ public class LoginFragment extends Fragment implements View.OnClickListener, Asy return rootView; } + @Override + public void onResume() { + super.onResume(); + + processCredentials(); + } + private void processCredentials() { SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getActivity()); @@ -89,55 +84,38 @@ public class LoginFragment extends Fragment implements View.OnClickListener, Asy login = loginTextbox.getText().toString(); password = passwordTextbox.getText().toString(); url = urlTextbox.getText().toString(); - asyncDatabaseConnector = new AsyncDatabaseConnector(login, password, url); - asyncDatabaseConnector.setBooleanReturnListener(this); - asyncDatabaseConnector.checkLogin(); + + if(Static.isNetworkConnected(getActivity())) { + databaseConnector = new DatabaseConnector(login, password, url, getActivity().getResources()); + databaseConnector.setBooleanReturnListener(this); + databaseConnector.setOnPostExecuteListener(this); + databaseConnector.checkLogin(); + } else { + Static.showErrorAlert(getResources().getString(R.string.no_connection), getActivity()); + loginButton.setEnabled(true); // Now we can click button again + progressBar.setVisibility(View.INVISIBLE); + } } @Override public void onBooleanReturn(boolean result) { + this.result = result; + } + + + @Override + public void onPostExecute() { if(result) { - Static.asyncDatabaseConnector = asyncDatabaseConnector; - Static.asyncDatabaseConnector.setListReturnListener(this); - Static.asyncDatabaseConnector.getDatabases(); + Static.databaseConnector = databaseConnector; + Intent intent = new Intent(getActivity(), ListActivity.class); + intent.putExtra(Static.FRAGMENT_TO_START, Static.FRAGMENT_DATABASE); + startActivity(intent); } else { - final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); - builder.setMessage(Static.asyncDatabaseConnector.errorMsg); - builder.setNeutralButton(R.string.ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) { - // Nothing to do here - // Cleaning inputs is stupid - } - }); - builder.setTitle(R.string.error); - builder.setIcon(R.drawable.ic_action_warning); - builder.create(); - builder.show(); + Static.showErrorAlert(DatabaseConnector.errorMsg, getActivity()); } + loginButton.setEnabled(true); // Now we can click button again progressBar.setVisibility(View.INVISIBLE); } - - @Override - public void onListReturn(List<String> databases) { - if(databases!= null) { - Static.databases = databases; - - Intent intent = new Intent(getActivity(), DatabaseActivity.class); - startActivity(intent); - } -// listAdapter = new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1, databases); -// databasesListView.setAdapter(listAdapter); -// databasesListView.setOnItemClickListener(this); -// } else { -// TextView errorMessage = new TextView(getActivity()); -// errorMessage.setText(R.string.error_no_databases); -// errorMessage.setTypeface(null, Typeface.ITALIC); -// errorMessage.setClickable(false); -// rootView.addView(errorMessage); -// rootView.removeView(databasesListView); -// } - } } diff --git a/app/src/main/java/info/nerull7/mysqlbrowser/MainActivity.java b/app/src/main/java/info/nerull7/mysqlbrowser/MainActivity.java index f5c394c..d70a46f 100644 --- a/app/src/main/java/info/nerull7/mysqlbrowser/MainActivity.java +++ b/app/src/main/java/info/nerull7/mysqlbrowser/MainActivity.java @@ -1,7 +1,6 @@ package info.nerull7.mysqlbrowser; import android.app.Activity; -import android.content.Intent; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; diff --git a/app/src/main/java/info/nerull7/mysqlbrowser/NumberPickerDialog.java b/app/src/main/java/info/nerull7/mysqlbrowser/NumberPickerDialog.java deleted file mode 100644 index c4778df..0000000 --- a/app/src/main/java/info/nerull7/mysqlbrowser/NumberPickerDialog.java +++ /dev/null @@ -1,108 +0,0 @@ -package info.nerull7.mysqlbrowser; - -/** - * Created by nerull7 on 18.07.14. - */ - -import android.app.AlertDialog; -import android.content.Context; -import android.content.DialogInterface; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.NumberPicker; - -/** - * A dialog that prompts the user for the message deletion limits. - */ -public class NumberPickerDialog extends AlertDialog implements DialogInterface.OnClickListener { - private static final String NUMBER = "number"; - - /** - * The callback interface used to indicate the user is done filling in - * the time (they clicked on the 'Set' button). - */ - public interface OnNumberSetListener { - - /** - * @param number The number that was set. - */ - void onNumberSet(int number); - } - - private final NumberPicker mNumberPicker; - private final OnNumberSetListener mCallback; - - /** - * @param context Parent. - * @param callBack How parent is notified. - * @param number The initial number. - */ - public NumberPickerDialog(Context context, - OnNumberSetListener callBack, - int number, - int rangeMin, - int rangeMax, - int title) { - this(context, AlertDialog.THEME_HOLO_LIGHT, callBack, number, rangeMin, rangeMax, title); - } - - /** - * @param context Parent. - * @param theme the theme to apply to this dialog - * @param callBack How parent is notified. - * @param number The initial number. - */ - public NumberPickerDialog(Context context, - int theme, - OnNumberSetListener callBack, - int number, - int rangeMin, - int rangeMax, - int title) { - super(context, theme); - mCallback = callBack; - - setTitle(title); - - setButton(DialogInterface.BUTTON_POSITIVE, context.getText(R.string.set), this); - setButton(DialogInterface.BUTTON_NEGATIVE, context.getText(R.string.cancel), - (OnClickListener) null); - - LayoutInflater inflater = - (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - View view = inflater.inflate(R.layout.number_picker_dialog, null); - setView(view); - mNumberPicker = (NumberPicker) view.findViewById(R.id.number_picker); - - // initialize state - mNumberPicker.setMinValue(rangeMin); - mNumberPicker.setMaxValue(rangeMax); - mNumberPicker.setValue(number); - mNumberPicker.setOnLongPressUpdateInterval(100); // make the repeat rate three times as fast - // as normal since the range is so large. - mNumberPicker.setWrapSelectorWheel(false); // don't wrap from min->max - } - - public void onClick(DialogInterface dialog, int which) { - if (mCallback != null) { - mNumberPicker.clearFocus(); - mCallback.onNumberSet(mNumberPicker.getValue()); - dialog.dismiss(); - } - } - - @Override - public Bundle onSaveInstanceState() { - Bundle state = super.onSaveInstanceState(); - state.putInt(NUMBER, mNumberPicker.getValue()); - return state; - } - - @Override - public void onRestoreInstanceState(Bundle savedInstanceState) { - super.onRestoreInstanceState(savedInstanceState); - int number = savedInstanceState.getInt(NUMBER); - mNumberPicker.setValue(number); - } -} \ No newline at end of file diff --git a/app/src/main/java/info/nerull7/mysqlbrowser/SQLActivity.java b/app/src/main/java/info/nerull7/mysqlbrowser/SQLActivity.java new file mode 100644 index 0000000..396ce94 --- /dev/null +++ b/app/src/main/java/info/nerull7/mysqlbrowser/SQLActivity.java @@ -0,0 +1,22 @@ +package info.nerull7.mysqlbrowser; + +import android.app.Activity; +import android.os.Bundle; + +/** + * Created by nerull7 on 30.09.14. + */ +public class SQLActivity extends Activity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_sql); + + if (savedInstanceState == null) { + getFragmentManager().beginTransaction() + .add(R.id.container, new SQLFragment()) + .commit(); + } + } + +} diff --git a/app/src/main/java/info/nerull7/mysqlbrowser/SQLEntriesFragment.java b/app/src/main/java/info/nerull7/mysqlbrowser/SQLEntriesFragment.java new file mode 100644 index 0000000..610a66a --- /dev/null +++ b/app/src/main/java/info/nerull7/mysqlbrowser/SQLEntriesFragment.java @@ -0,0 +1,240 @@ +package info.nerull7.mysqlbrowser; + +import android.app.AlertDialog; +import android.app.Fragment; +import android.content.DialogInterface; +import android.graphics.Typeface; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.HorizontalScrollView; +import android.widget.ProgressBar; +import android.widget.RelativeLayout; +import android.widget.ScrollView; +import android.widget.TableLayout; +import android.widget.TableRow; +import android.widget.TextView; + +import java.util.List; + +import info.nerull7.mysqlbrowser.db.DatabaseConnector; + +/** + * Created by nerull7 on 15.07.14. + * + * Fragment for showing elements + */ +public class SQLEntriesFragment extends Fragment implements DatabaseConnector.MatrixReturnListener, DatabaseConnector.ListReturnListener, DatabaseConnector.OnPostExecuteListener, DatabaseConnector.StringReturnListener { + private static final int HEADER_PADDING_TOP = 15; + private static final int HEADER_PADDING_BOTTOM = 15; + private static final int HEADER_PADDING_LEFT = 15; + private static final int HEADER_PADDING_RIGHT = 15; + private static final int ENTRIES_PADDING_TOP = 30; + private static final int ENTRIES_PADDING_BOTTOM = 30; + private static final int ENTRIES_PADDING_LEFT = 15; + private static final int ENTRIES_PADDING_RIGHT = 15; + + private TableLayout entriesTable; + private ScrollView entriesScrollView; + private FrameLayout headerFrame; + private HorizontalScrollView horizontalScrollView; + private TableRow.LayoutParams layoutParams; + + private ProgressBar progressBar; + private CustomScrollView fakeScrollView; + private View dummyView; + private boolean showError; + + private Menu menu; + private TableRow headerRow; + private int[] maxWidth; + + private boolean isFirstCreate; + private String errorMessage; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View rootView = inflater.inflate(R.layout.fragment_entries, container, false); + initViewItems(rootView); + showError = false; + return rootView; + } + + private void initViewItems(View rootView){ + headerFrame = (FrameLayout) rootView.findViewById(R.id.headerFrame); + entriesScrollView = (ScrollView) rootView.findViewById(R.id.entriesScrollView); + fakeScrollView = (CustomScrollView) rootView.findViewById(R.id.fakeScroll); + progressBar = (ProgressBar) rootView.findViewById(R.id.loginProgressBar); + dummyView = rootView.findViewById(R.id.dummyView); + horizontalScrollView = (HorizontalScrollView) rootView.findViewById(R.id.horizontalScrollView); + entriesTable = new TableLayout(getActivity()); + + fakeScrollView.setOnTouchEventListener(new CustomScrollView.OnTouchEventListener() { + @Override + public boolean onTouchEvent(MotionEvent ev) { + ev.offsetLocation(0, headerFrame.getHeight()); + horizontalScrollView.dispatchTouchEvent(ev); + return true; + } + }); + + layoutParams = new TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT, TableRow.LayoutParams.MATCH_PARENT); + } + + private void setLoading(boolean isLoading){ + if(menu != null) { + menu.findItem(R.id.action_next).setEnabled(!isLoading); + menu.findItem(R.id.action_previous).setEnabled(!isLoading); + } + progressBar.setVisibility(isLoading ? View.VISIBLE : View.INVISIBLE); + } + + @Override + public void onMatrixReturn(List<List<String>> rows) { + // Now we get Rows + if(rows!=null) { + int background; + for (int i = 0; i < rows.size(); i++) { + List<String> elements = rows.get(i); + TableRow newRow = new TableRow(getActivity()); + + if( i%2 == 0 ){ // Two backgrounds for lines for better visibility + background=R.drawable.entries_element_1; + } else { + background=R.drawable.entries_element_2; + } + + for (int j = 0; j < elements.size(); j++) { // elements.size can be the same as in header so maybe some one number or not + TextView textView = new TextView(getActivity()); + textView.setText(elements.get(j)); + textView.setLayoutParams(layoutParams); + textView.setPadding(ENTRIES_PADDING_LEFT, ENTRIES_PADDING_TOP, ENTRIES_PADDING_RIGHT, ENTRIES_PADDING_BOTTOM); + textView.setBackgroundResource(background); + textView.setId(j); + newRow.addView(textView); + } + newRow.setClickable(false); + entriesTable.addView(newRow); + syncWidthsFirstStage(); + } + } else { + entriesTable = null; + } + + } + + @Override + public void onListReturn(List<String> fieldList) { + // First we need header + headerRow = new TableRow(getActivity()); + headerRow.setLayoutParams(new TableRow.LayoutParams(TableRow.LayoutParams.MATCH_PARENT, TableRow.LayoutParams.WRAP_CONTENT)); + for (String aFieldList : fieldList) { + TextView textView = new TextView(getActivity()); + textView.setText(aFieldList); + textView.setTypeface(null, Typeface.BOLD); + textView.setLayoutParams(layoutParams); + textView.setBackgroundResource(R.drawable.background_header); + textView.setPadding(HEADER_PADDING_LEFT, HEADER_PADDING_TOP, HEADER_PADDING_RIGHT, HEADER_PADDING_BOTTOM); + headerRow.addView(textView); + } + } + + private void syncWidthsFirstStage() { // TODO: Merge with adding columns maybe? Loops -= 3 should be quicker + maxWidth = new int[headerRow.getChildCount()]; + for (int i = 0; i < headerRow.getChildCount(); i++) { + TextView textView = (TextView) headerRow.getChildAt(i); + textView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); + maxWidth[i] = textView.getMeasuredWidth(); + } + + for (int i = 0; i < entriesTable.getChildCount(); i++) { + TableRow tableRow = (TableRow) entriesTable.getChildAt(i); + for (int j = 0; j < tableRow.getChildCount(); j++) { + TextView textView = (TextView) tableRow.getChildAt(j); + textView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); + int width = textView.getMeasuredWidth(); + if (width > maxWidth[j]) + maxWidth[j] = width; + } + } + } + + private void syncWidthsSecondStage() { + for(int i=0;i<headerRow.getChildCount();i++){ + TableRow entriesRow = (TableRow) entriesTable.getChildAt(0); + + TextView tmpEntries = (TextView) entriesRow.getChildAt(i); + TextView tmpHeader = (TextView) headerRow.getChildAt(i); + + tmpEntries.setWidth(maxWidth[i]); + tmpHeader.setWidth(maxWidth[i]); + } + } + + private void fakeScroll(){ + entriesScrollView.measure(View.MeasureSpec.UNSPECIFIED,View.MeasureSpec.UNSPECIFIED); + int height = entriesScrollView.getMeasuredHeight(); + dummyView.setMinimumHeight(height); + + RelativeLayout.LayoutParams fakeScrollLayout = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); + headerFrame.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); + fakeScrollLayout.setMargins(0,headerFrame.getMeasuredHeight(),0,0); + + fakeScrollView.setLayoutParams(fakeScrollLayout); + } + +// @Override +// public void onResume() { +// super.onResume(); +// if(!isFirstCreate) { +// getActivity().finish(); +// } else { +// isFirstCreate = false; +// } +// } + + @Override + public void onPostExecute() { + if(!showError) { + if (headerFrame.getChildCount() == 0) // You can have only one child + headerFrame.addView(headerRow); + if (entriesTable != null) { + syncWidthsSecondStage(); + entriesScrollView.addView(entriesTable); + fakeScroll(); + } else { + TextView errorMessage = new TextView(getActivity()); + errorMessage.setText(R.string.error_no_entries); + errorMessage.setTypeface(null, Typeface.ITALIC); + errorMessage.setClickable(false); + entriesScrollView.addView(errorMessage); + } + setLoading(false); + } else { + setLoading(false); + final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + builder.setMessage(errorMessage); + builder.setNeutralButton(R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + getActivity().finish(); + } + }); + builder.setTitle(R.string.info); + builder.setIcon(R.drawable.ic_action_info); + builder.create(); + builder.show(); + } + } + + @Override + public void onStringReturn(String data) { + errorMessage = data; + showError = true; + } +} diff --git a/app/src/main/java/info/nerull7/mysqlbrowser/SQLFragment.java b/app/src/main/java/info/nerull7/mysqlbrowser/SQLFragment.java new file mode 100644 index 0000000..0249a12 --- /dev/null +++ b/app/src/main/java/info/nerull7/mysqlbrowser/SQLFragment.java @@ -0,0 +1,89 @@ +package info.nerull7.mysqlbrowser; + +import android.app.Fragment; +import android.app.FragmentTransaction; +import android.content.Context; +import android.os.Bundle; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.view.inputmethod.InputMethodManager; +import android.widget.EditText; + +import info.nerull7.mysqlbrowser.db.DatabaseConnector; + +/** + * Created by nerull7 on 30.09.14. + */ +public class SQLFragment extends Fragment implements DatabaseConnector.OnPostExecuteListener { + private EditText sqlEditText; + private InputMethodManager inputMethodManager; + private SQLEntriesFragment sqlEntriesFragment; + private FragmentTransaction fragmentTransaction; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View rootView = inflater.inflate(R.layout.fragment_sql, container, false); + sqlEditText = (EditText) rootView.findViewById(R.id.sqlQueryText); + setHasOptionsMenu(true); + return rootView; + } + + @Override + public void onStart() { + super.onStart(); + inputMethodManager = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE); + inputMethodManager.showSoftInput(sqlEditText, InputMethodManager.SHOW_FORCED); + sqlEditText.requestFocus(); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()){ + case R.id.action_execute: + actionExecute(); + break; + case R.id.action_cancel: + actionCancel(); + break; + } + return super.onOptionsItemSelected(item); + } + + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + inflater.inflate(R.menu.sql, menu); + super.onCreateOptionsMenu(menu, inflater); + } + + private void actionCancel(){ + inputMethodManager.hideSoftInputFromWindow(sqlEditText.getWindowToken(), 0); + getActivity().finish(); + } + + private void actionExecute(){ + String sqlQuery = String.valueOf(sqlEditText.getText()); + Log.d("SQLQUERY", sqlQuery); + + fragmentTransaction = getFragmentManager().beginTransaction(); + sqlEntriesFragment = new SQLEntriesFragment(); + fragmentTransaction.replace(R.id.container, sqlEntriesFragment); + fragmentTransaction.commit(); + + Static.databaseConnector.setStringReturnListener(sqlEntriesFragment); + Static.databaseConnector.setListReturnListener(sqlEntriesFragment); + Static.databaseConnector.setMatrixReturnListener(sqlEntriesFragment); + Static.databaseConnector.setOnPostExecuteListener(this); + Static.databaseConnector.executeSQL(getActivity().getIntent().getExtras().getString(Static.DATABASE_NAME_ARG), sqlQuery); + } + + @Override + public void onPostExecute() { + sqlEntriesFragment.onPostExecute(); + } +} diff --git a/app/src/main/java/info/nerull7/mysqlbrowser/SettingsActivity.java b/app/src/main/java/info/nerull7/mysqlbrowser/SettingsActivity.java index 3e84b0e..d572c3f 100644 --- a/app/src/main/java/info/nerull7/mysqlbrowser/SettingsActivity.java +++ b/app/src/main/java/info/nerull7/mysqlbrowser/SettingsActivity.java @@ -1,7 +1,5 @@ package info.nerull7.mysqlbrowser; -import android.app.FragmentManager; -import android.app.FragmentTransaction; import android.os.Bundle; import android.preference.PreferenceActivity; diff --git a/app/src/main/java/info/nerull7/mysqlbrowser/SettingsFragment.java b/app/src/main/java/info/nerull7/mysqlbrowser/SettingsFragment.java index 612337a..dfff2bc 100644 --- a/app/src/main/java/info/nerull7/mysqlbrowser/SettingsFragment.java +++ b/app/src/main/java/info/nerull7/mysqlbrowser/SettingsFragment.java @@ -6,32 +6,26 @@ import android.preference.CheckBoxPreference; import android.preference.EditTextPreference; import android.preference.Preference; import android.preference.PreferenceFragment; -import android.preference.PreferenceScreen; +import android.preference.PreferenceManager; import android.util.Base64; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; - -import javax.crypto.BadPaddingException; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.NoSuchPaddingException; - /** * Created by nerull7 on 18.07.14. + * + * Fragment for Preferences/Settings */ -public class SettingsFragment extends PreferenceFragment implements NumberPickerDialog.OnNumberSetListener, Preference.OnPreferenceClickListener, Preference.OnPreferenceChangeListener { +public class SettingsFragment extends PreferenceFragment implements Preference.OnPreferenceClickListener, Preference.OnPreferenceChangeListener { public static final String ENTRIES_PAGE_LIMIT = "entries_limit"; + public static final String ENTRIES_PAGE_LIMIT_STRING = "entries_limit_string"; public static final String SAVE_SERVER_CREDENTIALS = "save_credentials_enabled"; public static final String URL_CREDENTIALS = "url"; public static final String LOGIN_CREDENTIALS = "login"; public static final String PASSWORD_CREDENTIALS = "password"; public static final int ENTRIES_PAGE_LIMIT_DEF = 20; - public static final int ENTRIES_MIN_PAGE = 20; - public static final int ENTRIES_MAX_PAGE = 100; private SharedPreferences preferences; - private Preference mEntriesLimit; + private EditTextPreference mEntriesLimit; private CheckBoxPreference saveCredentials; private EditTextPreference connectorUrlCredentials; private EditTextPreference loginCredentials; @@ -42,7 +36,8 @@ public class SettingsFragment extends PreferenceFragment implements NumberPicker @Override public void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); - preferences = getPreferenceManager().getDefaultSharedPreferences(getActivity()); + getPreferenceManager(); + preferences = PreferenceManager.getDefaultSharedPreferences(getActivity()); crypto = new Crypto(getActivity()); loadPrefs(); @@ -52,17 +47,18 @@ public class SettingsFragment extends PreferenceFragment implements NumberPicker addPreferencesFromResource(R.xml.settings); // Getting fields - mEntriesLimit = findPreference(ENTRIES_PAGE_LIMIT); + mEntriesLimit = (EditTextPreference) findPreference(ENTRIES_PAGE_LIMIT_STRING); saveCredentials = (CheckBoxPreference) findPreference(SAVE_SERVER_CREDENTIALS); connectorUrlCredentials = (EditTextPreference) findPreference(URL_CREDENTIALS); loginCredentials = (EditTextPreference) findPreference(LOGIN_CREDENTIALS); passwordCredentials = (EditTextPreference) findPreference(PASSWORD_CREDENTIALS); // TODO: Some encryption // Settings fields - setEntriesPageLimitSummary(); + setEntriesPageLimit(); setPasswordCredentials(); // Settings Listener + mEntriesLimit.setOnPreferenceChangeListener(this); saveCredentials.setOnPreferenceClickListener(this); passwordCredentials.setOnPreferenceChangeListener(this); } @@ -77,7 +73,6 @@ public class SettingsFragment extends PreferenceFragment implements NumberPicker } editor.putBoolean(SAVE_SERVER_CREDENTIALS, isEnabled); editor.apply(); - editor.commit(); if(!isEnabled) reloadLoginPrefsView(); @@ -87,7 +82,7 @@ public class SettingsFragment extends PreferenceFragment implements NumberPicker saveCredentials.setChecked(preferences.getBoolean(SAVE_SERVER_CREDENTIALS, false)); connectorUrlCredentials.setText(preferences.getString(URL_CREDENTIALS, null)); loginCredentials.setText(preferences.getString(LOGIN_CREDENTIALS, null)); - passwordCredentials.setText(preferences.getString(PASSWORD_CREDENTIALS, null));; + passwordCredentials.setText(preferences.getString(PASSWORD_CREDENTIALS, null)); } private int getEntriesPageLimit(){ @@ -98,6 +93,11 @@ public class SettingsFragment extends PreferenceFragment implements NumberPicker mEntriesLimit.setSummary(getString(R.string.entries_summary, getEntriesPageLimit())); } + private void setEntriesPageLimit(){ + mEntriesLimit.setText(String.valueOf(getEntriesPageLimit())); + setEntriesPageLimitSummary(); + } + private void setPasswordCredentials(){ String password; password = preferences.getString(PASSWORD_CREDENTIALS, null); @@ -118,23 +118,6 @@ public class SettingsFragment extends PreferenceFragment implements NumberPicker } catch (Exception e) { e.printStackTrace(); } // TODO: Something useful } - @Override - public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { - if(preference == mEntriesLimit){ - new NumberPickerDialog(getActivity(), this, getEntriesPageLimit(), ENTRIES_MIN_PAGE, ENTRIES_MAX_PAGE, R.string.entries_limit).show(); - } - - return super.onPreferenceTreeClick(preferenceScreen, preference); - } - - @Override - public void onNumberSet(int number) { - SharedPreferences.Editor editor = preferences.edit(); - editor.putInt(ENTRIES_PAGE_LIMIT, number); - editor.apply(); - setEntriesPageLimitSummary(); - } - @Override public boolean onPreferenceClick(Preference preference) { if(preference==saveCredentials){ @@ -150,7 +133,16 @@ public class SettingsFragment extends PreferenceFragment implements NumberPicker public boolean onPreferenceChange(Preference preference, Object newValue) { if(preference == passwordCredentials){ savePassword((String) newValue); + } else if (preference == mEntriesLimit) { + saveEntriesLimit((String) newValue); } return false; } + + private void saveEntriesLimit(String newValue) { + SharedPreferences.Editor editor = preferences.edit(); + editor.putInt(ENTRIES_PAGE_LIMIT, Integer.parseInt(newValue)); + editor.apply(); + setEntriesPageLimit(); + } } diff --git a/app/src/main/java/info/nerull7/mysqlbrowser/Static.java b/app/src/main/java/info/nerull7/mysqlbrowser/Static.java index d271055..abf0d41 100644 --- a/app/src/main/java/info/nerull7/mysqlbrowser/Static.java +++ b/app/src/main/java/info/nerull7/mysqlbrowser/Static.java @@ -1,11 +1,13 @@ package info.nerull7.mysqlbrowser; +import android.app.AlertDialog; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; -import java.util.List; - -import info.nerull7.mysqlbrowser.db.AsyncDatabaseConnector; +import info.nerull7.mysqlbrowser.db.DatabaseConnector; /** * Created by nerull7 on 14.07.14. @@ -14,15 +16,45 @@ public class Static { public static final String DATABASE_NAME_ARG = "DatabaseName"; public static final String TABLE_NAME_ARG = "TableName"; - public static AsyncDatabaseConnector asyncDatabaseConnector = null; - static List<String> tables; - static List<String> databases; - static List<String> header; - static List<List<String>> entries; - static int pageCount; + public static final String FRAGMENT_TO_START = "FragmentStarter"; + public static final String FRAGMENT_DATABASE = "DatabaseFragment"; + public static final String FRAGMENT_TABLE = "TableFragment"; + + public static DatabaseConnector databaseConnector = null; public static void startSettings(Context context){ Intent intent = new Intent(context, SettingsActivity.class); context.startActivity(intent); } + + public static boolean isNetworkConnected(Context context){ + ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo(); + if(networkInfo!=null && networkInfo.isConnected()){ + return true; + } else + return false; + } + + public static void showErrorAlert(String errorMessage, Context context){ + final AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setMessage(errorMessage); + builder.setNeutralButton(R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + // Nothing to do here + // Cleaning inputs is stupid + } + }); + builder.setTitle(R.string.error); + builder.setIcon(R.drawable.ic_action_warning); + builder.create(); + builder.show(); + } + + public static void startSQL(Context context, String database) { + Intent intent = new Intent(context, SQLActivity.class); + intent.putExtra(Static.DATABASE_NAME_ARG, database); + context.startActivity(intent); + } } diff --git a/app/src/main/java/info/nerull7/mysqlbrowser/TableActivity.java b/app/src/main/java/info/nerull7/mysqlbrowser/TableActivity.java deleted file mode 100644 index e4c8256..0000000 --- a/app/src/main/java/info/nerull7/mysqlbrowser/TableActivity.java +++ /dev/null @@ -1,46 +0,0 @@ -package info.nerull7.mysqlbrowser; - -import android.app.Activity; -import android.os.Bundle; -import android.view.Menu; -import android.view.MenuItem; - -public class TableActivity extends Activity { - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_table); - setTitle(getIntent().getStringExtra(Static.DATABASE_NAME_ARG)); - - TableFragment tableFragment = new TableFragment(); - tableFragment.setArguments(getIntent().getExtras()); - if (savedInstanceState == null) { - getFragmentManager().beginTransaction() - .add(R.id.container, tableFragment) - .commit(); - } - } - - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.main, menu); - return true; - } - - @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(); - if (id == R.id.action_settings) { - Static.startSettings(this); - return true; - } - return super.onOptionsItemSelected(item); - } - -} diff --git a/app/src/main/java/info/nerull7/mysqlbrowser/TableFragment.java b/app/src/main/java/info/nerull7/mysqlbrowser/TableFragment.java index 62af04b..25dbedc 100644 --- a/app/src/main/java/info/nerull7/mysqlbrowser/TableFragment.java +++ b/app/src/main/java/info/nerull7/mysqlbrowser/TableFragment.java @@ -4,11 +4,9 @@ import android.app.Fragment; import android.content.Intent; import android.graphics.Typeface; import android.os.Bundle; -import android.preference.PreferenceManager; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.view.ViewParent; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.ListAdapter; @@ -19,20 +17,18 @@ import android.widget.TextView; import java.util.List; -import info.nerull7.mysqlbrowser.db.AsyncDatabaseConnector; +import info.nerull7.mysqlbrowser.db.DatabaseConnector; /** * Created by nerull7 on 14.07.14. */ -public class TableFragment extends Fragment implements AdapterView.OnItemClickListener, AsyncDatabaseConnector.ListReturnListener, AsyncDatabaseConnector.IntegerReturnListener, AsyncDatabaseConnector.MatrixReturnListener { +public class TableFragment extends Fragment implements AdapterView.OnItemClickListener, DatabaseConnector.ListReturnListener, DatabaseConnector.OnPostExecuteListener { private String databaseName; private ListView tablesList; private ListAdapter listAdapter; private RelativeLayout rootView; private ProgressBar progressBar; - - private int listenerCalled; - private String chosenTable; + private List<String> tables; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, @@ -42,12 +38,36 @@ public class TableFragment extends Fragment implements AdapterView.OnItemClickLi tablesList = (ListView) rootView.findViewById(R.id.tableList); this.rootView = (RelativeLayout) rootView; progressBar = (ProgressBar) rootView.findViewById(R.id.loginProgressBar); - listenerCalled = 0; -// Static.asyncDatabaseConnector.setListReturnListener(this); -// Static.asyncDatabaseConnector.getTables(); + Static.databaseConnector.setListReturnListener(this); + Static.databaseConnector.setOnPostExecuteListener(this); + Static.databaseConnector.getTables(); + return rootView; + } - if(Static.tables != null) { - listAdapter = new ArrayAdapter<String>(getActivity(),android.R.layout.simple_list_item_1, Static.tables); + @Override + public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) { + if(Static.isNetworkConnected(getActivity())) { + String chosenTable = (String) listAdapter.getItem(position); + listAdapter.getItem(position); + Intent intent = new Intent(getActivity(), EntriesActivity.class); + intent.putExtra(Static.DATABASE_NAME_ARG,databaseName); + intent.putExtra(Static.TABLE_NAME_ARG,chosenTable); + startActivity(intent); + } else { + Static.showErrorAlert(getResources().getString(R.string.no_connection), getActivity()); + } + + } + + @Override + public void onListReturn(List<String> tables) { + this.tables = tables; + } + + @Override + public void onPostExecute() { + if(tables != null) { + listAdapter = new ArrayAdapter<String>(getActivity(),android.R.layout.simple_list_item_1, tables); tablesList.setAdapter(listAdapter); tablesList.setOnItemClickListener(this); } else { @@ -55,73 +75,9 @@ public class TableFragment extends Fragment implements AdapterView.OnItemClickLi errorMessage.setText(R.string.error_no_tables); errorMessage.setTypeface(null, Typeface.ITALIC); errorMessage.setClickable(false); - this.rootView.addView(errorMessage); - this.rootView.removeView(tablesList); + rootView.addView(errorMessage); + rootView.removeView(tablesList); } progressBar.setVisibility(View.INVISIBLE); - - return rootView; - } - - @Override - public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) { - chosenTable = (String) listAdapter.getItem(position); - listAdapter.getItem(position); - - progressBar.setVisibility(View.VISIBLE); - Static.asyncDatabaseConnector.setIntegerReturnListener(this); - Static.asyncDatabaseConnector.setListReturnListener(this); - Static.asyncDatabaseConnector.setMatrixReturnListener(this); - - int entriesLimit = PreferenceManager.getDefaultSharedPreferences(getActivity()).getInt(SettingsFragment.ENTRIES_PAGE_LIMIT, SettingsFragment.ENTRIES_PAGE_LIMIT_DEF); - - Static.asyncDatabaseConnector.getRows(chosenTable, entriesLimit, 1); - Static.asyncDatabaseConnector.getFields(chosenTable); - Static.asyncDatabaseConnector.getEntriesCount(chosenTable); - } - - @Override - public void onListReturn(List<String> header) { -// if(tables != null) { -// listAdapter = new ArrayAdapter<String>(getActivity(),android.R.layout.simple_list_item_1, tables); -// tablesList.setAdapter(listAdapter); -// tablesList.setOnItemClickListener(this); -// } else { -// TextView errorMessage = new TextView(getActivity()); -// errorMessage.setText(R.string.error_no_tables); -// errorMessage.setTypeface(null, Typeface.ITALIC); -// errorMessage.setClickable(false); -// rootView.addView(errorMessage); -// rootView.removeView(tablesList); -// } -// progressBar.setVisibility(View.INVISIBLE); - Static.header = header; - listenerCalled++; - if(listenerCalled==3) - startEntriesActivity(); - } - - @Override - public void onIntegerReturn(int result) { - Static.pageCount = result; - listenerCalled++; - if(listenerCalled==3) - startEntriesActivity(); - } - - @Override - public void onMatrixReturn(List<List<String>> data) { - Static.entries = data; - listenerCalled++; - if(listenerCalled==3) - startEntriesActivity(); - } - - - private void startEntriesActivity() { - Intent intent = new Intent(getActivity(), EntriesActivity.class); - intent.putExtra(Static.DATABASE_NAME_ARG,databaseName); - intent.putExtra(Static.TABLE_NAME_ARG,chosenTable); - startActivity(intent); } } diff --git a/app/src/main/java/info/nerull7/mysqlbrowser/db/AsyncDatabaseConnector.java b/app/src/main/java/info/nerull7/mysqlbrowser/db/AsyncDatabaseConnector.java deleted file mode 100644 index bab7b30..0000000 --- a/app/src/main/java/info/nerull7/mysqlbrowser/db/AsyncDatabaseConnector.java +++ /dev/null @@ -1,289 +0,0 @@ -package info.nerull7.mysqlbrowser.db; - -import android.os.AsyncTask; -import android.util.Log; - -import org.json.JSONArray; -import org.json.JSONException; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.net.HttpURLConnection; -import java.net.URL; -import java.util.ArrayList; -import java.util.List; - -/** - * Created by nerull7 on 07.07.14. - */ -public class AsyncDatabaseConnector { - public static final String ACTION_LOGIN = "login"; - public static final String ACTION_DATABASE_LIST = "dblist"; - public static final String ACTION_TABLE_LIST = "tablelist"; - public static final String ACTION_FIELD_LIST = "fieldlist"; - public static final String ACTION_ENTRIES_COUNT = "numrows"; - public static final String ACTION_DATA_MATRIX = "getrows"; - - private String login; - private String password; - private String url; - - private String database; - - private BooleanReturnListener booleanReturnListener; - private IntegerReturnListener integerReturnListener; - private StringReturnListener stringReturnListener; - private ListReturnListener listReturnListener; - private MatrixReturnListener matrixReturnListener; - - public static String errorMsg; - - public AsyncDatabaseConnector(String login, String password, String url){ - this.login = login; - this.password = password; - this.url = url; - - booleanReturnListener=null; - stringReturnListener=null; - listReturnListener=null; - matrixReturnListener=null; - } - - private String actionUrlBuilder(String action){ // TODO Better UrlBuilder this is shit only for use - String urlBuilder = url; - urlBuilder += "?u="+login; - urlBuilder += "&p="+password; - urlBuilder += "&a="+action; -// Log.d("URLBuilder", urlBuilder); - return urlBuilder; - } - - public void setDatabaseInUse(String database){ - this.database = database; - } - - private void getList(String urlQuery){ - Downloader downloader = new Downloader(new Downloader.OnFinishedListener() { - @Override - public void onFinished(String data, String error) { - List<String>list = null; - try { - list = parseListFromJSON(data); - } catch (JSONException e) { e.printStackTrace(); } - if(listReturnListener!=null) - listReturnListener.onListReturn(list); - } - }); - downloader.execute(urlQuery); - } - - private void getMatrix(String urlQuery){ - Downloader downloader = new Downloader(new Downloader.OnFinishedListener() { - @Override - public void onFinished(String data, String error) { - List<List<String>> list = null; - try { - list = parseMatrixFromJSON(data); - } catch (JSONException e) { e.printStackTrace(); } - if(matrixReturnListener!=null) - matrixReturnListener.onMatrixReturn(list); - } - }); - downloader.execute(urlQuery); - } - - private List<String> parseListFromJSON(String jsonListString) throws JSONException { - JSONArray jsonArray = new JSONArray(jsonListString); - List<String> databaseStringList = new ArrayList<String>(); - for(int i=0;i<jsonArray.length();i++){ - databaseStringList.add(jsonArray.getString(i)); - } - return databaseStringList; - } - - private List<List<String>> parseMatrixFromJSON(String jsonMatrixString) throws JSONException { - JSONArray jsonMatrix = new JSONArray(jsonMatrixString); - List<List<String>> matrix = new ArrayList<List<String>>(); - for(int i=0;i<jsonMatrix.length();i++){ - JSONArray jsonArray = jsonMatrix.getJSONArray(i); - List<String> list = new ArrayList<String>(); - for(int j=0;j<jsonArray.length();j++){ - list.add(jsonArray.getString(j)); - } - matrix.add(list); - } - return matrix; - } - - public boolean checkLogin(){ - Downloader downloader = new Downloader(new Downloader.OnFinishedListener() { - @Override - public void onFinished(String data, String error) { - List<String>list = null; - boolean listenerData; - if(data==null) { - listenerData = false; - errorMsg = error; - } - else if(booleanReturnListener!=null) - listenerData = (data.compareTo("OK")==0); - else { - errorMsg = data; - listenerData = false; - } - booleanReturnListener.onBooleanReturn(listenerData); - } - }); - downloader.execute(actionUrlBuilder(ACTION_LOGIN)); - return false; - } - - public void getDatabases(){ - getList(actionUrlBuilder(ACTION_DATABASE_LIST)); - } - - public void getTables(){ - getList(actionUrlBuilder(ACTION_TABLE_LIST)+"&d="+database); - } - - public void getFields(String table){ - getList(actionUrlBuilder(ACTION_FIELD_LIST)+"&d="+database+"&t="+table); - } - - public void getRows(String table, int count, int page){ - int limitStart = (page-1) * count; - getMatrix(actionUrlBuilder(ACTION_DATA_MATRIX)+"&d="+database+"&t="+table+"&s="+limitStart+"&l="+count); - } - - public void getEntriesCount(String table){ - String urlQuery = actionUrlBuilder(ACTION_ENTRIES_COUNT)+"&d="+database+"&t="+table; - Downloader downloader = new Downloader(new Downloader.OnFinishedListener() { - @Override - public void onFinished(String data, String error) { - if(integerReturnListener!=null) - integerReturnListener.onIntegerReturn(Integer.parseInt(data)); - } - }); - downloader.execute(urlQuery); - } - - public void setBooleanReturnListener(BooleanReturnListener booleanReturnListener){ - this.booleanReturnListener = booleanReturnListener; - } - - public void setStringReturnListener(StringReturnListener stringReturnListener) { - this.stringReturnListener = stringReturnListener; - } - - public void setIntegerReturnListener(IntegerReturnListener integerReturnListener){ - this.integerReturnListener = integerReturnListener; - } - - public void setListReturnListener(ListReturnListener listReturnListener) { - this.listReturnListener = listReturnListener; - } - - public void setMatrixReturnListener(MatrixReturnListener matrixReturnListener) { - this.matrixReturnListener = matrixReturnListener; - } - - public static interface BooleanReturnListener{ - public void onBooleanReturn(boolean result); - } - - public static interface IntegerReturnListener{ - public void onIntegerReturn(int result); - } - - public static interface StringReturnListener{ - public void onStringReturn(String data); - } - - public static interface ListReturnListener { - public void onListReturn(List<String> data); - } - - public static interface MatrixReturnListener{ - public void onMatrixReturn(List<List<String>> data); - } - - private static class Downloader extends AsyncTask<String, Void, String> { - private OnFinishedListener onFinishedListener; - private String errorString; - - public static final String CONNECTION_REQUEST_METHOD = "GET"; - public static final int CONNECTION_TIMEOUT = 15000; - public static final int READ_TIMEOUT = 10000; - - Downloader(OnFinishedListener onFinishedListener){ - this.onFinishedListener = onFinishedListener; - errorString = null; - } - - private String httpRequest(String urlRequest) throws IOException { - URL url = new URL(urlRequest); - InputStream inputStream = null; - String response; - - HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); // TODO Handling no connection - urlConnection.setReadTimeout(READ_TIMEOUT); - urlConnection.setConnectTimeout(CONNECTION_TIMEOUT); - urlConnection.setRequestMethod(CONNECTION_REQUEST_METHOD); - urlConnection.connect(); - - if(urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) { - try { - inputStream = urlConnection.getInputStream(); - response = readStream(inputStream); - } finally { - if(inputStream!=null) - inputStream.close(); - urlConnection.disconnect(); - } - return response; - } - else { - errorString = "ERROR "+urlConnection.getResponseCode()+": "+urlConnection.getResponseMessage(); - return null; - } - } - - private String readStream(InputStream in) throws IOException { - String streamOutput = ""; - BufferedReader reader = null; - try { - reader = new BufferedReader(new InputStreamReader(in)); - String line = ""; - while ((line = reader.readLine()) != null) { - streamOutput += line; - } - } finally { - if (reader != null) { - reader.close(); - } - } - return streamOutput; - } - - @Override - protected String doInBackground(String... strings) { - try { - return httpRequest(strings[0]); - } catch (IOException e) { - e.printStackTrace(); - } - return null; - } - - @Override - protected void onPostExecute(String data) { - onFinishedListener.onFinished(data, errorString); // Can't be null cos we demand listener in constructor - } - - private static interface OnFinishedListener { - void onFinished(String data, String error); - } - } -} diff --git a/app/src/main/java/info/nerull7/mysqlbrowser/db/DatabaseConnector.java b/app/src/main/java/info/nerull7/mysqlbrowser/db/DatabaseConnector.java new file mode 100644 index 0000000..c4f98ad --- /dev/null +++ b/app/src/main/java/info/nerull7/mysqlbrowser/db/DatabaseConnector.java @@ -0,0 +1,565 @@ +package info.nerull7.mysqlbrowser.db; + +import android.content.res.Resources; +import android.os.AsyncTask; +import android.util.Log; + +import org.json.JSONArray; +import org.json.JSONException; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.UnsupportedEncodingException; +import java.net.HttpURLConnection; +import java.net.SocketException; +import java.net.SocketTimeoutException; +import java.net.URL; +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.List; + +import info.nerull7.mysqlbrowser.R; + +/** + * Created by nerull7 on 07.07.14. + * Database connector using Async calls + */ +public class DatabaseConnector { + public static final String ACTION_LOGIN = "login"; + public static final String ACTION_DATABASE_LIST = "dblist"; + public static final String ACTION_TABLE_LIST = "tablelist"; + public static final String ACTION_FIELD_LIST = "fieldlist"; + public static final String ACTION_ENTRIES_COUNT = "numrows"; + public static final String ACTION_DATA_MATRIX = "getrows"; + public static final String ACTION_ADD_ELEMENT = "addelement"; + public static final String ACTION_UPDATE_ELEMENT = "updateelement"; + public static final String ACTION_REMOVE_ELEMENT = "removeelement"; + public static final String ACTION_SQL_QUERY = "query"; + + private String login; + private String password; + private String url; + + private String database; + + private final Resources resources; + + private BooleanReturnListener booleanReturnListener; + private IntegerReturnListener integerReturnListener; + private StringReturnListener stringReturnListener; + private ListReturnListener listReturnListener; + private MatrixReturnListener matrixReturnListener; + + public static String errorMsg; + private OnPostExecuteListener onPostExecuteListener; + + public DatabaseConnector(String login, String password, String url, Resources resources){ + this.login = login; + this.password = password; + this.url = url; + this.resources = resources; + + booleanReturnListener=null; + stringReturnListener=null; + listReturnListener=null; + matrixReturnListener=null; + } + + private Request requestBuilder(String action){ + Request request = new Request(url); + String urlData = "u="+login + + "&p="+password + + "&a="+action; + request.data = urlData; + + return request; + } + + private Request actionUrlBuilder(String action, String argument, String value){ + ArrayList<String> arguments = new ArrayList<String>(); + ArrayList<String> values = new ArrayList<String>(); + arguments.add(argument); + values.add(value); + return this.actionUrlBuilder(action, arguments, values); + } + + private Request actionUrlBuilder(String action, List<String> arguments, List<String> values){ // TODO Better UrlBuilder this is shit only for use + Request urlBuilder = requestBuilder(action); + for (int i = 0; i < arguments.size(); i++) { + try { + urlBuilder.data += "&" + arguments.get(i) + "=" + URLEncoder.encode(values.get(i), "UTF-8"); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + } +// Log.d("URLBuilder", urlBuilder); + return urlBuilder; + } + + public void setDatabaseInUse(String database){ + this.database = database; + } + + private void getList(Request urlQuery){ + Downloader downloader = new Downloader(new Downloader.OnFinishedListener() { + @Override + public void onFinished(String data, String error) { + List<String>list = null; + try { + list = parseListFromJSON(data); + } catch (JSONException e) { e.printStackTrace(); } + if(listReturnListener!=null) { + listReturnListener.onListReturn(list); + } + errorMsg = error; + } + }, onPostExecuteListener, resources); + downloader.execute(urlQuery); + } + + private void getMatrix(Request urlQuery){ + Downloader downloader = new Downloader(new Downloader.OnFinishedListener() { + @Override + public void onFinished(String data, String error) { + List<List<String>> list = null; + try { + list = parseMatrixFromJSON(data); + } catch (JSONException e) { e.printStackTrace(); } + if(matrixReturnListener!=null) + matrixReturnListener.onMatrixReturn(list); + errorMsg = error; + } + }, onPostExecuteListener, resources); + downloader.execute(urlQuery); + } + + private List<String> parseListFromJSON(String jsonListString) throws JSONException { + JSONArray jsonArray = new JSONArray(jsonListString); + List<String> databaseStringList = new ArrayList<String>(); + for(int i=0;i<jsonArray.length();i++){ + databaseStringList.add(jsonArray.getString(i)); + } + return databaseStringList; + } + + private List<List<String>> parseMatrixFromJSON(String jsonMatrixString) throws JSONException { + JSONArray jsonMatrix = new JSONArray(jsonMatrixString); + List<List<String>> matrix = new ArrayList<List<String>>(); + for(int i=0;i<jsonMatrix.length();i++){ + JSONArray jsonArray = jsonMatrix.getJSONArray(i); + List<String> list = new ArrayList<String>(); + for(int j=0;j<jsonArray.length();j++){ + list.add(jsonArray.getString(j)); + } + matrix.add(list); + } + return matrix; + } + + public boolean checkLogin(){ + Downloader downloader = new Downloader(new Downloader.OnFinishedListener() { + @Override + public void onFinished(String data, String error) { + boolean listenerData; + if(data == null) { + listenerData = false; + errorMsg = error; + } else if( data.compareTo("OK") == 0){ + listenerData = true; + } else { + errorMsg = data; + listenerData = false; + } + booleanReturnListener.onBooleanReturn(listenerData); + } + }, onPostExecuteListener, resources); + downloader.execute(requestBuilder(ACTION_LOGIN)); + return false; + } + + public void getDatabases(){ + getList(requestBuilder(ACTION_DATABASE_LIST)); + } + + public void getTables(){ + getList(actionUrlBuilder(ACTION_TABLE_LIST, "d", database)); + } + + public void getFields(String table){ + ArrayList<String> args = new ArrayList<String>(); + ArrayList<String> values = new ArrayList<String>(); + + args.add("d"); + values.add(database); + + args.add("t"); + values.add(table); + + getList(actionUrlBuilder(ACTION_FIELD_LIST, args, values)); + } + + public void getRows(String table, int count, int page){ + int limitStart = (page-1) * count; + ArrayList<String> args = new ArrayList<String>(); + ArrayList<String> values = new ArrayList<String>(); + + args.add("d"); + values.add(database); + + args.add("t"); + values.add(table); + + args.add("s"); + values.add(String.valueOf(limitStart)); + + args.add("l"); + values.add(String.valueOf(count)); + + getMatrix(actionUrlBuilder(ACTION_DATA_MATRIX, args, values)); + } + + public void getEntriesCount(String table){ + ArrayList<String> args = new ArrayList<String>(); + ArrayList<String> values = new ArrayList<String>(); + + args.add("d"); + values.add(database); + + args.add("t"); + values.add(table); + + Request urlQuery = actionUrlBuilder(ACTION_ENTRIES_COUNT, args, values); + Downloader downloader = new Downloader(new Downloader.OnFinishedListener() { + @Override + public void onFinished(String data, String error) { + if(integerReturnListener!=null) + integerReturnListener.onIntegerReturn(Integer.parseInt(data)); + errorMsg = error; + } + }, onPostExecuteListener, resources); + downloader.execute(urlQuery); + } + + public void addNewElement(String table, List<String> header, List<String> values) { + this.updateElement(table, header, null, values); + } + + public void updateElement(String table, List<String> header, List<String> oldValues, List<String> newValues){ + JSONArray headerJSON = new JSONArray(); + JSONArray newValuesJSON = new JSONArray(); + Request request; + + ArrayList<String> args = new ArrayList<String>(); + ArrayList<String> values = new ArrayList<String>(); + + args.add("d"); + values.add(database); + + args.add("t"); + values.add(table); + + for (String aHeader : header) { + headerJSON.put(aHeader); + } + + for (String newValue : newValues) { + newValuesJSON.put(newValue); + } + + args.add("h"); + values.add(headerJSON.toString()); + + args.add("v"); + values.add(newValuesJSON.toString()); + + if(oldValues!=null){ + JSONArray oldValuesJSON = new JSONArray(); + for(int i=0;i<newValues.size();i++){ + oldValuesJSON.put(oldValues.get(i)); + } + args.add("o"); + values.add(oldValuesJSON.toString()); + request = actionUrlBuilder(ACTION_UPDATE_ELEMENT, args, values); + } else + request = actionUrlBuilder(ACTION_ADD_ELEMENT, args, values); + + Downloader downloader = new Downloader(new Downloader.OnFinishedListener() { + @Override + public void onFinished(String data, String error) { + if(stringReturnListener!=null){ + stringReturnListener.onStringReturn(data); + } + errorMsg = error; + } + }, onPostExecuteListener, resources); + downloader.execute(request); + } + + public void removeElement(String table, List<String> header, List<String> values) { + JSONArray headerJSON = new JSONArray(); + JSONArray valuesJSON = new JSONArray(); + Request request; + + ArrayList<String> args = new ArrayList<String>(); + ArrayList<String> argValues = new ArrayList<String>(); + + args.add("d"); + argValues.add(database); + + args.add("t"); + argValues.add(table); + + for (String aHeader : header) { + headerJSON.put(aHeader); + } + + for (String value : values) { + valuesJSON.put(value); + } + + args.add("h"); + argValues.add(headerJSON.toString()); + + args.add("v"); + argValues.add(valuesJSON.toString()); + + request = actionUrlBuilder(ACTION_REMOVE_ELEMENT, args, argValues); + + Downloader downloader = new Downloader(new Downloader.OnFinishedListener() { + @Override + public void onFinished(String data, String error) { + if(stringReturnListener!=null){ + stringReturnListener.onStringReturn(data); + } + errorMsg = error; + } + }, onPostExecuteListener, resources); + downloader.execute(request); + } + + public void executeSQL(String database, String query){ + ArrayList<String> args = new ArrayList(); + ArrayList<String> values = new ArrayList(); + final Request request; + + if(database!=null){ + args.add("d"); + values.add(database); + } + args.add("q"); + values.add(query); + + request = actionUrlBuilder(ACTION_SQL_QUERY, args, values); + + Downloader downloader = new Downloader(new Downloader.OnFinishedListener() { + @Override + public void onFinished(String data, String error) { + String []response = data.split("\n"); + + if(response[0].equals("OK")) { + List<String> headerList = null; + try { + headerList = parseListFromJSON(response[1]); + } catch (JSONException e) { + e.printStackTrace(); + } + if (listReturnListener != null) { + listReturnListener.onListReturn(headerList); + } + + List<List<String>> dataMatrix = null; + try { + dataMatrix = parseMatrixFromJSON(response[2]); + } catch (JSONException e) { + e.printStackTrace(); + } + if (matrixReturnListener != null) + matrixReturnListener.onMatrixReturn(dataMatrix); + } else { + if(stringReturnListener!=null) + stringReturnListener.onStringReturn(data); + } + } + }, onPostExecuteListener, resources); + downloader.execute(request); + } + + public void setBooleanReturnListener(BooleanReturnListener booleanReturnListener){ + this.booleanReturnListener = booleanReturnListener; + } + + public void setStringReturnListener(StringReturnListener stringReturnListener) { + this.stringReturnListener = stringReturnListener; + } + + public void setIntegerReturnListener(IntegerReturnListener integerReturnListener){ + this.integerReturnListener = integerReturnListener; + } + + public void setListReturnListener(ListReturnListener listReturnListener) { + this.listReturnListener = listReturnListener; + } + + public void setMatrixReturnListener(MatrixReturnListener matrixReturnListener) { + this.matrixReturnListener = matrixReturnListener; + } + + public void setOnPostExecuteListener(OnPostExecuteListener onPostExecuteListener){ + this.onPostExecuteListener = onPostExecuteListener; + } + + public static interface BooleanReturnListener{ + public void onBooleanReturn(boolean result); + } + + public static interface IntegerReturnListener{ + public void onIntegerReturn(int result); + } + + public static interface StringReturnListener{ + public void onStringReturn(String data); + } + + public static interface ListReturnListener { + public void onListReturn(List<String> data); + } + + public static interface MatrixReturnListener{ + public void onMatrixReturn(List<List<String>> data); + } + + public static interface OnPostExecuteListener { + void onPostExecute(); + } + + private static class Downloader extends AsyncTask<Request, Void, String> { + private OnFinishedListener onFinishedListener; + private OnPostExecuteListener onPostExecuteListener; + private String errorString; + private Resources resources; + + public static final String CONNECTION_REQUEST_METHOD = "POST"; + public static final int CONNECTION_TIMEOUT = 15000; // ms + public static final int READ_TIMEOUT = 10000; // ms + + private Downloader(OnFinishedListener onFinishedListener, OnPostExecuteListener onPostExecuteListener, Resources resources){ + this.onFinishedListener = onFinishedListener; + this.onPostExecuteListener = onPostExecuteListener; + this.resources = resources; + errorString = null; + } + + private String httpRequest(Request urlRequest) throws IOException { + URL url = new URL(urlRequest.url); + InputStream inputStream = null; + String response; + +// Log.d("URL REQUEST", urlRequest); + + HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); // TODO Handling no connection + urlConnection.setReadTimeout(READ_TIMEOUT); + urlConnection.setConnectTimeout(CONNECTION_TIMEOUT); + urlConnection.setRequestMethod(CONNECTION_REQUEST_METHOD); + + OutputStream outputStream = urlConnection.getOutputStream(); + OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream); + outputStreamWriter.write(urlRequest.data); + outputStreamWriter.flush(); + outputStreamWriter.close(); + outputStream.close(); + + try { + urlConnection.connect(); + + if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) { + try { + inputStream = urlConnection.getInputStream(); + response = readStream(inputStream); + } finally { + if (inputStream != null) + inputStream.close(); + urlConnection.disconnect(); + } + return response; + } else { + errorString = "ERROR " + urlConnection.getResponseCode() + ": " + urlConnection.getResponseMessage(); + } + } catch (Exception e) { + parseException(e); + } + return null; + } + + private void parseException(Exception e){ + if(e instanceof SocketException){ + if(e.getMessage().contains("ECONNREFUSED")) { + errorString = resources.getString(R.string.error_connection_refused); + } else if(e.getMessage().contains("EHOSTUNREACH")) { + errorString = resources.getString(R.string.error_connection_unreachable); + } else { + errorString = "Exception: " + e.getClass(); + } + } else if (e instanceof SocketTimeoutException){ + errorString = resources.getString(R.string.error_connection_timeout); + } else { + errorString = "Exception: " + e.getClass(); + } + } + + private String readStream(InputStream in) throws IOException { + String streamOutput = ""; + BufferedReader reader = null; + try { + reader = new BufferedReader(new InputStreamReader(in)); + String line; + while ((line = reader.readLine()) != null) { + streamOutput += line + '\n'; + } + } finally { + if (reader != null) { + reader.close(); + } + } + return streamOutput.substring(0, streamOutput.length()-1); // Remove last \n + } + + @Override + protected String doInBackground(Request... requests) { + try { + String data = httpRequest(requests[0]); + onFinishedListener.onFinished(data, errorString); // Can't be null cos we demand listener in constructor + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + + @Override + protected void onPostExecute(String data) { + if (onPostExecuteListener!=null) + onPostExecuteListener.onPostExecute(); + } + + public void setOnPostExecuteListener(OnPostExecuteListener onPostExecuteListener) { + this.onPostExecuteListener = onPostExecuteListener; + } + + private static interface OnFinishedListener { + void onFinished(String data, String error); + } + + } + + class Request{ + String url; + String data; + + Request(String url){ + this.url = url; + } + } +} diff --git a/app/src/main/res/drawable-hdpi/ic_action_add_circle.png b/app/src/main/res/drawable-hdpi/ic_action_add_circle.png new file mode 100644 index 0000000..5f68a01 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_action_add_circle.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_action_delete.png b/app/src/main/res/drawable-hdpi/ic_action_delete.png new file mode 100644 index 0000000..49e4513 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_action_delete.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_action_file_upload.png b/app/src/main/res/drawable-hdpi/ic_action_file_upload.png new file mode 100644 index 0000000..4910c3a Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_action_file_upload.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_action_info.png b/app/src/main/res/drawable-hdpi/ic_action_info.png new file mode 100644 index 0000000..75ecc18 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_action_info.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_action_save.png b/app/src/main/res/drawable-hdpi/ic_action_save.png new file mode 100644 index 0000000..107c11e Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_action_save.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_revert.png b/app/src/main/res/drawable-hdpi/ic_revert.png new file mode 100644 index 0000000..f0f7b38 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_revert.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_action_add_circle.png b/app/src/main/res/drawable-mdpi/ic_action_add_circle.png new file mode 100644 index 0000000..649d671 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_action_add_circle.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_action_delete.png b/app/src/main/res/drawable-mdpi/ic_action_delete.png new file mode 100644 index 0000000..2706550 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_action_delete.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_action_file_upload.png b/app/src/main/res/drawable-mdpi/ic_action_file_upload.png new file mode 100644 index 0000000..8374b8d Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_action_file_upload.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_action_info.png b/app/src/main/res/drawable-mdpi/ic_action_info.png new file mode 100644 index 0000000..944ab3d Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_action_info.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_action_save.png b/app/src/main/res/drawable-mdpi/ic_action_save.png new file mode 100644 index 0000000..0235a4b Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_action_save.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_revert.png b/app/src/main/res/drawable-mdpi/ic_revert.png new file mode 100644 index 0000000..a110afd Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_revert.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_action_add_circle.png b/app/src/main/res/drawable-xhdpi/ic_action_add_circle.png new file mode 100644 index 0000000..adbda7f Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_action_add_circle.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_action_delete.png b/app/src/main/res/drawable-xhdpi/ic_action_delete.png new file mode 100644 index 0000000..1487306 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_action_delete.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_action_file_upload.png b/app/src/main/res/drawable-xhdpi/ic_action_file_upload.png new file mode 100644 index 0000000..702ea4e Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_action_file_upload.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_action_info.png b/app/src/main/res/drawable-xhdpi/ic_action_info.png new file mode 100644 index 0000000..6e10fde Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_action_info.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_action_save.png b/app/src/main/res/drawable-xhdpi/ic_action_save.png new file mode 100644 index 0000000..1692720 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_action_save.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_revert.png b/app/src/main/res/drawable-xhdpi/ic_revert.png new file mode 100644 index 0000000..67956bc Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_revert.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_add_circle.png b/app/src/main/res/drawable-xxhdpi/ic_action_add_circle.png new file mode 100644 index 0000000..73e6e67 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_action_add_circle.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_delete.png b/app/src/main/res/drawable-xxhdpi/ic_action_delete.png new file mode 100644 index 0000000..251f91d Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_action_delete.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_file_upload.png b/app/src/main/res/drawable-xxhdpi/ic_action_file_upload.png new file mode 100644 index 0000000..8e78556 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_action_file_upload.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_info.png b/app/src/main/res/drawable-xxhdpi/ic_action_info.png new file mode 100644 index 0000000..6835e8f Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_action_info.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_save.png b/app/src/main/res/drawable-xxhdpi/ic_action_save.png new file mode 100644 index 0000000..42bf596 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_action_save.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_revert.png b/app/src/main/res/drawable-xxhdpi/ic_revert.png new file mode 100644 index 0000000..8f0fbc0 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_revert.png differ diff --git a/app/src/main/res/drawable/background_element.xml b/app/src/main/res/drawable/entries_element_1.xml similarity index 69% rename from app/src/main/res/drawable/background_element.xml rename to app/src/main/res/drawable/entries_element_1.xml index a682ad2..4149979 100644 --- a/app/src/main/res/drawable/background_element.xml +++ b/app/src/main/res/drawable/entries_element_1.xml @@ -1,8 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> -<shape xmlns:android="http://schemas.android.com/apk/res/android" - android:shape="rectangle"> - <!--<solid android:color="#0fff"/>--> +<shape xmlns:android="http://schemas.android.com/apk/res/android"> + <solid android:color="@color/entries_background_color_1"/> <stroke android:width="1dp" android:color="@color/border"/> - </shape> \ No newline at end of file diff --git a/app/src/main/res/drawable/entries_element_2.xml b/app/src/main/res/drawable/entries_element_2.xml new file mode 100644 index 0000000..0bd0f94 --- /dev/null +++ b/app/src/main/res/drawable/entries_element_2.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> + +<shape xmlns:android="http://schemas.android.com/apk/res/android"> + <solid android:color="@color/entries_background_color_2"/> + <stroke android:width="1dp" android:color="@color/border"/> +</shape> \ No newline at end of file diff --git a/app/src/main/res/layout/activity_database.xml b/app/src/main/res/layout/activity_element.xml similarity index 81% rename from app/src/main/res/layout/activity_database.xml rename to app/src/main/res/layout/activity_element.xml index c4d324d..12fee7b 100644 --- a/app/src/main/res/layout/activity_database.xml +++ b/app/src/main/res/layout/activity_element.xml @@ -3,5 +3,5 @@ android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent" - tools:context="info.nerull7.mysqlbrowser.DatabaseActivity" + tools:context="info.nerull7.mysqlbrowser.ElementActivity" tools:ignore="MergeRootFrame" /> diff --git a/app/src/main/res/layout/activity_table.xml b/app/src/main/res/layout/activity_list.xml similarity index 81% rename from app/src/main/res/layout/activity_table.xml rename to app/src/main/res/layout/activity_list.xml index e80cef3..43ab1db 100644 --- a/app/src/main/res/layout/activity_table.xml +++ b/app/src/main/res/layout/activity_list.xml @@ -3,5 +3,5 @@ android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent" - tools:context="info.nerull7.mysqlbrowser.TableActivity" + tools:context="info.nerull7.mysqlbrowser.ListActivity" tools:ignore="MergeRootFrame" /> diff --git a/app/src/main/res/layout/activity_sql.xml b/app/src/main/res/layout/activity_sql.xml new file mode 100644 index 0000000..43ab1db --- /dev/null +++ b/app/src/main/res/layout/activity_sql.xml @@ -0,0 +1,7 @@ +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/container" + android:layout_width="match_parent" + android:layout_height="match_parent" + tools:context="info.nerull7.mysqlbrowser.ListActivity" + tools:ignore="MergeRootFrame" /> diff --git a/app/src/main/res/layout/fragment_element.xml b/app/src/main/res/layout/fragment_element.xml new file mode 100644 index 0000000..c57f312 --- /dev/null +++ b/app/src/main/res/layout/fragment_element.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> + +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" android:layout_height="match_parent"> + <ProgressBar + style="?android:attr/progressBarStyleHorizontal" + android:indeterminate="true" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:id="@+id/progressBar" + android:layout_centerHorizontal="true" + android:layout_alignParentTop="false" + android:layout_marginTop="-7dp" /> + + <ListView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/listView" + android:layout_below="@+id/progressBar" + android:layout_alignParentLeft="true" + android:layout_alignParentStart="true" + android:descendantFocusability="afterDescendants"/> + +</RelativeLayout> \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_entries.xml b/app/src/main/res/layout/fragment_entries.xml index a913904..55f3cb8 100644 --- a/app/src/main/res/layout/fragment_entries.xml +++ b/app/src/main/res/layout/fragment_entries.xml @@ -4,22 +4,9 @@ android:layout_height="match_parent" tools:context="info.nerull7.mysqlbrowser.EntriesFragment"> - <ScrollView - android:layout_width="wrap_content" - android:layout_height="fill_parent" - android:layout_below="@+id/loginProgressBar" - android:layout_alignParentRight="true" - android:foregroundGravity="right" - android:id="@+id/fakeScroll"> - <View - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:id="@+id/dummyView"/> - </ScrollView> - <HorizontalScrollView - android:layout_width="wrap_content" - android:layout_height="wrap_content" + android:layout_width="fill_parent" + android:layout_height="fill_parent" android:id="@+id/horizontalScrollView" > <RelativeLayout @@ -33,18 +20,18 @@ android:id="@+id/headerFrame" ></FrameLayout> - <info.nerull7.mysqlbrowser.CustomScrollView + <ScrollView android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/entriesScrollView" android:layout_below="@+id/headerFrame" android:scrollbars="none"> - <TableLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:id="@+id/entriesTable"> - </TableLayout> - </info.nerull7.mysqlbrowser.CustomScrollView> + <!--<TableLayout--> + <!--android:layout_width="wrap_content"--> + <!--android:layout_height="wrap_content"--> + <!--android:id="@+id/entriesTable">--> + <!--</TableLayout>--> + </ScrollView> </RelativeLayout> </HorizontalScrollView> @@ -58,4 +45,17 @@ android:layout_alignParentTop="false" android:layout_marginTop="-7dp" /> + <info.nerull7.mysqlbrowser.CustomScrollView + android:layout_width="wrap_content" + android:layout_height="fill_parent" + android:layout_alignParentRight="true" + android:foregroundGravity="right" + android:id="@+id/fakeScroll" + android:overScrollMode="never"> + <View + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/dummyView"/> + </info.nerull7.mysqlbrowser.CustomScrollView> + </RelativeLayout> diff --git a/app/src/main/res/layout/fragment_sql.xml b/app/src/main/res/layout/fragment_sql.xml new file mode 100644 index 0000000..1298338 --- /dev/null +++ b/app/src/main/res/layout/fragment_sql.xml @@ -0,0 +1,24 @@ +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + tools:context="info.nerull7.mysqlbrowser.DatabaseFragment"> + + + <EditText + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:inputType="textMultiLine" + android:ems="10" + android:id="@+id/sqlQueryText" + android:layout_alignParentTop="true" + android:layout_alignParentLeft="true" + android:layout_alignParentStart="true" + android:layout_alignParentBottom="true" + android:layout_alignParentRight="true" + android:layout_alignParentEnd="true" + android:gravity="left|top" + android:hint="@string/hint_sql_query" > + <requestFocus/> + </EditText> +</RelativeLayout> diff --git a/app/src/main/res/layout/list_item_element_simple.xml b/app/src/main/res/layout/list_item_element_simple.xml new file mode 100644 index 0000000..3e1dcc0 --- /dev/null +++ b/app/src/main/res/layout/list_item_element_simple.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> + +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" android:layout_height="match_parent"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="New Text" + android:id="@+id/textFieldName" + android:layout_alignParentTop="true" + android:layout_alignParentLeft="true" + android:layout_alignParentStart="true" /> + + <EditText + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:id="@+id/editFieldValue" + android:layout_below="@+id/textFieldName" + android:layout_alignParentLeft="true" + android:layout_alignParentStart="true" /> + +</RelativeLayout> \ No newline at end of file diff --git a/app/src/main/res/menu/element.xml b/app/src/main/res/menu/element.xml new file mode 100644 index 0000000..f3262ad --- /dev/null +++ b/app/src/main/res/menu/element.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8"?> + +<menu xmlns:android="http://schemas.android.com/apk/res/android" > + + <item android:id="@+id/action_save" + android:title="@string/action_save" + android:showAsAction="always" + android:icon="@drawable/ic_action_save"/> + + <item android:id="@+id/action_remove" + android:title="@string/action_remove" + android:showAsAction="always" + android:icon="@drawable/ic_action_delete"/> + +</menu> diff --git a/app/src/main/res/menu/entries_activity_actions.xml b/app/src/main/res/menu/entries_activity_actions.xml index 43bef46..d4eca48 100644 --- a/app/src/main/res/menu/entries_activity_actions.xml +++ b/app/src/main/res/menu/entries_activity_actions.xml @@ -1,6 +1,12 @@ <?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item + android:id="@+id/action_add" + android:icon="@drawable/ic_action_add_circle" + android:title="@string/action_add" + android:showAsAction="ifRoom" /> + <item android:id="@+id/action_previous" android:icon="@drawable/ic_action_arrow_back" diff --git a/app/src/main/res/menu/logged.xml b/app/src/main/res/menu/logged.xml new file mode 100644 index 0000000..5d31f08 --- /dev/null +++ b/app/src/main/res/menu/logged.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> + +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@+id/action_settings" + android:title="@string/action_settings" + android:orderInCategory="100" + android:showAsAction="never" /> + <item android:id="@+id/action_sql" + android:title="@string/sql_query" + android:orderInCategory="200" + android:showAsAction="never" /> +</menu> \ No newline at end of file diff --git a/app/src/main/res/menu/sql.xml b/app/src/main/res/menu/sql.xml new file mode 100644 index 0000000..5bc3b39 --- /dev/null +++ b/app/src/main/res/menu/sql.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8"?> + +<menu xmlns:android="http://schemas.android.com/apk/res/android" > + + <item android:id="@+id/action_execute" + android:title="@string/action_execute" + android:showAsAction="always" + android:icon="@drawable/ic_action_file_upload"/> + + <item android:id="@+id/action_cancel" + android:title="@string/action_cancel" + android:showAsAction="always" + android:icon="@drawable/ic_action_delete"/> + +</menu> \ No newline at end of file diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml new file mode 100644 index 0000000..44d7f9e --- /dev/null +++ b/app/src/main/res/values-pl/strings.xml @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="app_name">Przeglądarka Mysql</string> + + <string name="action_settings">Ustawienia</string> + <string name="action_add">Dodaj nowy</string> + <string name="action_remove">Usuń</string> + <string name="action_previous">Poprzedni</string> + <string name="action_next">Następny</string> + + <string name="title_fragment_database">Dostępne bazy</string> + + <string name="login">Zaloguj</string> + <string name="password">Hasło</string> + <string name="username">Użytkownik</string> + + <string name="yes">Tak</string> + <string name="no">Nie</string> + <string name="ok">OK</string> + <string name="cancel">Anuluj</string> + <string name="action_save">Zapisz</string> + <string name="set">Ustaw</string> + <string name="back">Cofnij</string> + + <string name="status">Info</string> + <string name="warning">Ostrzeżenie</string> + <string name="error">Błąd</string> + + <string name="login_error">Zły login/hasło</string> + + <string name="general_category">Ogólne</string> + <string name="entries_limit">Limit wpisów na stronę</string> + <string name="entries_summary">%s na stronę</string> <!-- <xliff:g id="count"></xliff:g> --> + + <string name="error_no_tables">Brak tabel w tej bazie</string> + <string name="error_no_entries">Brak wpisów w tej bazie</string> + <string name="error_no_databases">Brak dostępnych baz</string> + <string name="error_no_save">Twoje dane <b>NIE</b> zostaną zapisane! Czy chciałbyś kontynuować?</string> + <string name="error_remove">Ten wpis zostanie usunięty z bazy. Tej akcji <b>NIE MOŻNA</b> cofnąć! Czy chciałbyś kontynuować?</string> + <string name="error_connection_timeout">Przekroczono czas połączenia</string> + <string name="error_connection_reset">Połączenie zostało zrestartowane</string> + <string name="error_connection_unreachable">No route to host</string> + + <string name="pref_entries_per_page">Ustaw limit wpisów na stronę</string> + <string name="login_settings">Poświadczenia logowania</string> + <string name="save_credentials">Zapamiętaj poświadczenia</string> + <string name="connector_url">URL Wtyczki php</string> + <string name="no_connection">Brak połączenia internetowego</string> + +</resources> diff --git a/app/src/main/res/values-v21/styles.xml b/app/src/main/res/values-v21/styles.xml deleted file mode 100644 index 65a325d..0000000 --- a/app/src/main/res/values-v21/styles.xml +++ /dev/null @@ -1,5 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<resources> - <style name="AppTheme" parent="android:Theme.Holo.Light"> - </style> -</resources> diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 86784b1..d9ecfa8 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -1,5 +1,8 @@ <?xml version="1.0" encoding="utf-8"?> <resources> <color name="border">@android:color/tab_indicator_text</color> - <color name="header_background">@android:color/holo_orange_light</color> + <color name="header_background">#ffaa22</color> + <color name="header_login_background">@android:color/holo_blue_dark</color> + <color name="entries_background_color_1">@android:color/background_light</color> + <color name="entries_background_color_2">#ffcccccc</color> </resources> \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ff22c47..e431c16 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,34 +1,63 @@ <?xml version="1.0" encoding="utf-8"?> <resources> - <string name="app_name">Mysql Browser</string> + <string name="action_settings">Settings</string> + <string name="action_add">Add new</string> + <string name="action_remove">Remove</string> + <string name="action_previous">Previous</string> + <string name="action_next">Next</string> + + <string name="title_activity_main">MainActivity</string> + <string name="title_activity_entries">EntriesActivity</string> + <string name="title_activity_setting">Settings</string> + <string name="title_activity_element">ElementActivity</string> + <string name="title_activity_list">ListActivity</string> + <string name="title_fragment_database">Available Databases</string> + <string name="login">Log in</string> <string name="password">Password</string> <string name="username">Username</string> <string name="hint_url">URL ex: https://example.com/c.php</string> - <string name="title_activity_main">MainActivity</string> - <string name="title_activity_database">DatabaseActivity</string> - <string name="hello_world">Hello world!</string> - <string name="login_error">Wrong login/password</string> + + <string name="yes">Yes</string> + <string name="no">No</string> <string name="ok">OK</string> + <string name="cancel">Cancel</string> + <string name="action_save">Save</string> + <string name="set">Set</string> + <string name="back">Back</string> + + <string name="status">Status</string> + <string name="warning">Warning</string> <string name="error">Error</string> - <string name="title_activity_table">TableActivity</string> - <string name="title_activity_entries">EntriesActivity</string> + + <string name="login_error">Wrong login/password</string> + <string name="general_category">General</string> <string name="entries_limit">Entries per page limit</string> - <string name="entries_summary">%s entries per page</string><!--<xliff:g id="count"></xliff:g>--> - <string name="title_activity_setting">Settings</string> - <string name="cancel">Cancel</string> - <string name="set">Set</string> - <string name="pref_entries_per_page">Set number of entries per page</string> + <string name="entries_summary">%s entries per page</string> <!-- <xliff:g id="count"></xliff:g> --> + <string name="error_no_tables">No tables in this database</string> <string name="error_no_entries">No entries in this table</string> <string name="error_no_databases">No available databases</string> - <string name="action_previous">Previous</string> - <string name="action_next">Next</string> + <string name="error_no_save">Your data will <b>NOT</b> be saved! Would you like to continue?</string> + <string name="error_remove">This entry will be removed from database. This action <b>CAN NOT</b> be reversed! Would you like to continue?</string> + <string name="error_connection_timeout">Connection timeout</string> + <string name="error_connection_reset">Connection reset by peer</string> + <string name="error_connection_refused">Connection refused</string> + <string name="error_connection_unreachable">No route to host</string> + + <string name="pref_entries_per_page">Set number of entries per page</string> <string name="login_settings">Login credentials</string> <string name="save_credentials">Save credentials</string> <string name="connector_url">Connector URL</string> + <string name="no_connection">No Internet Connection</string> + <string name="sql_query">SQL Query</string> + <string name="title_activity_sql">SQL Query</string> + <string name="hint_sql_query">SQL Query</string> + <string name="action_cancel">Cancel</string> + <string name="action_execute">Execute</string> + <string name="info">Information</string> </resources> diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 292e457..14ef368 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -1,14 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> <resources> - - <!-- Base application theme. --> - <style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar"> - <!-- Customize your theme here. --> + <!--Action Bar styles--> + <style name="DefaultActionBar" + parent="android:Widget.Holo.Light.ActionBar.Solid" > + <item name="android:background">@color/header_background</item> + <item name="android:icon">@drawable/ic_revert</item> </style> - <style name="TableTheme"> - <item name="android:layout_width">1dip</item> - <item name="android:layout_height">match_parent</item> - <item name="android:background">@android:color/white</item> + <style name="LoginActionBar" + parent="DefaultActionBar" > + <item name="android:background">@color/header_login_background</item> + <item name="android:icon">@drawable/ic_launcher</item> </style> + <style name="SettingsActionBar" + parent="DefaultActionBar"> + <item name="android:icon">@drawable/ic_action_settings</item> + </style> </resources> diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index c9512aa..984a41c 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -1,15 +1,19 @@ <?xml version="1.0" encoding="utf-8"?> <resources> - <!--Activites Themes--> - <style name="EntriesTheme" + <!-- Base application theme. --> + <style name="AppTheme" parent="android:Theme.Holo.Light"> - <item name="android:actionBarStyle">@style/EntriesActionBar</item> - </style> + <item name="android:actionBarStyle">@style/DefaultActionBar</item> + </style> - <!--Action Bar styles--> - <style name="EntriesActionBar" - parent="android:Widget.Holo.Light.ActionBar.Solid" > - <item name="android:background">@color/header_background</item> + <style name="LoginTheme" + parent="AppTheme"> + <item name="android:actionBarStyle">@style/LoginActionBar</item> + </style> + + <style name="SettingsTheme" + parent="AppTheme"> + <item name="android:actionBarStyle">@style/SettingsActionBar</item> </style> </resources> \ No newline at end of file diff --git a/app/src/main/res/xml/settings.xml b/app/src/main/res/xml/settings.xml index 3cd5391..f310eb7 100644 --- a/app/src/main/res/xml/settings.xml +++ b/app/src/main/res/xml/settings.xml @@ -3,8 +3,9 @@ <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> <PreferenceCategory android:title="@string/general_category"> - <Preference - android:key="entries_limit" + <EditTextPreference + android:key="entries_limit_string" + android:numeric="integer" android:title="@string/entries_limit" android:summary="@string/entries_summary" />