From b5615d75917fbf055837db1b902f6d157402e661 Mon Sep 17 00:00:00 2001
From: Przemek Grondek <github@nerull7.info>
Date: Wed, 1 Oct 2014 11:40:22 +0200
Subject: [PATCH] SQL queries

 Full support
---
 .../mysqlbrowser/DatabaseFragment.java        |   6 +
 .../nerull7/mysqlbrowser/ListActivity.java    |   2 +-
 .../mysqlbrowser/SQLEntriesFragment.java      | 245 ++++++++++++++++++
 .../nerull7/mysqlbrowser/SQLFragment.java     |  24 +-
 .../info/nerull7/mysqlbrowser/Static.java     |   3 +-
 .../db/AsyncDatabaseConnector.java            |  35 ++-
 .../main/res/drawable-hdpi/ic_action_info.png | Bin 0 -> 696 bytes
 .../main/res/drawable-mdpi/ic_action_info.png | Bin 0 -> 469 bytes
 .../res/drawable-xhdpi/ic_action_info.png     | Bin 0 -> 891 bytes
 .../res/drawable-xxhdpi/ic_action_info.png    | Bin 0 -> 1577 bytes
 app/src/main/res/values/strings.xml           |   1 +
 11 files changed, 298 insertions(+), 18 deletions(-)
 create mode 100644 app/src/main/java/info/nerull7/mysqlbrowser/SQLEntriesFragment.java
 create mode 100644 app/src/main/res/drawable-hdpi/ic_action_info.png
 create mode 100644 app/src/main/res/drawable-mdpi/ic_action_info.png
 create mode 100644 app/src/main/res/drawable-xhdpi/ic_action_info.png
 create mode 100644 app/src/main/res/drawable-xxhdpi/ic_action_info.png

diff --git a/app/src/main/java/info/nerull7/mysqlbrowser/DatabaseFragment.java b/app/src/main/java/info/nerull7/mysqlbrowser/DatabaseFragment.java
index c70f172..91a6ff4 100644
--- a/app/src/main/java/info/nerull7/mysqlbrowser/DatabaseFragment.java
+++ b/app/src/main/java/info/nerull7/mysqlbrowser/DatabaseFragment.java
@@ -31,6 +31,12 @@ public class DatabaseFragment extends Fragment implements AdapterView.OnItemClic
     private ProgressBar progressBar;
     private List<String> databases;
 
+    @Override
+    public void onResume() {
+        super.onResume();
+        Static.asyncDatabaseConnector.setDatabaseInUse(null);
+    }
+
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
         //Inflate the layout for this fragment
diff --git a/app/src/main/java/info/nerull7/mysqlbrowser/ListActivity.java b/app/src/main/java/info/nerull7/mysqlbrowser/ListActivity.java
index c88c75f..3eeb667 100644
--- a/app/src/main/java/info/nerull7/mysqlbrowser/ListActivity.java
+++ b/app/src/main/java/info/nerull7/mysqlbrowser/ListActivity.java
@@ -48,7 +48,7 @@ public class ListActivity extends Activity {
             Static.startSettings(this);
             return true;
         } else if (id == R.id.action_sql){
-            Static.startSQL(this);
+            Static.startSQL(this, getIntent().getExtras().getString(Static.DATABASE_NAME_ARG));
         }
         return super.onOptionsItemSelected(item);
     }
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..4c1af95
--- /dev/null
+++ b/app/src/main/java/info/nerull7/mysqlbrowser/SQLEntriesFragment.java
@@ -0,0 +1,245 @@
+package info.nerull7.mysqlbrowser;
+
+import android.app.AlertDialog;
+import android.app.Fragment;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.graphics.Typeface;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+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;
+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;
+
+/**
+ * Created by nerull7 on 15.07.14.
+ *
+ * Fragment for showing elements
+ */
+public class SQLEntriesFragment extends Fragment implements AsyncDatabaseConnector.MatrixReturnListener, AsyncDatabaseConnector.ListReturnListener, AsyncDatabaseConnector.OnPostExecuteListener, AsyncDatabaseConnector.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
index 0ba7079..c67bc2e 100644
--- a/app/src/main/java/info/nerull7/mysqlbrowser/SQLFragment.java
+++ b/app/src/main/java/info/nerull7/mysqlbrowser/SQLFragment.java
@@ -2,6 +2,7 @@ package info.nerull7.mysqlbrowser;
 
 import android.app.Activity;
 import android.app.Fragment;
+import android.app.FragmentTransaction;
 import android.content.Context;
 import android.os.Bundle;
 import android.util.Log;
@@ -14,12 +15,16 @@ import android.view.ViewGroup;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.EditText;
 
+import info.nerull7.mysqlbrowser.db.AsyncDatabaseConnector;
+
 /**
  * Created by nerull7 on 30.09.14.
  */
-public class SQLFragment extends Fragment{
+public class SQLFragment extends Fragment implements AsyncDatabaseConnector.OnPostExecuteListener {
     private EditText sqlEditText;
     private InputMethodManager inputMethodManager;
+    private SQLEntriesFragment sqlEntriesFragment;
+    private FragmentTransaction fragmentTransaction;
 
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
@@ -66,7 +71,20 @@ public class SQLFragment extends Fragment{
         String sqlQuery = String.valueOf(sqlEditText.getText());
         Log.d("SQLQUERY", sqlQuery);
 
-        Static.asyncDatabaseConnector.setOnPostExecuteListener(null);
-        Static.asyncDatabaseConnector.executeSQL(null /*FIXME*/, sqlQuery);
+        fragmentTransaction = getFragmentManager().beginTransaction();
+        sqlEntriesFragment = new SQLEntriesFragment();
+        fragmentTransaction.replace(R.id.container, sqlEntriesFragment);
+        fragmentTransaction.commit();
+
+        Static.asyncDatabaseConnector.setStringReturnListener(sqlEntriesFragment);
+        Static.asyncDatabaseConnector.setListReturnListener(sqlEntriesFragment);
+        Static.asyncDatabaseConnector.setMatrixReturnListener(sqlEntriesFragment);
+        Static.asyncDatabaseConnector.setOnPostExecuteListener(this);
+        Static.asyncDatabaseConnector.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/Static.java b/app/src/main/java/info/nerull7/mysqlbrowser/Static.java
index 01d7d35..ca0281e 100644
--- a/app/src/main/java/info/nerull7/mysqlbrowser/Static.java
+++ b/app/src/main/java/info/nerull7/mysqlbrowser/Static.java
@@ -52,8 +52,9 @@ public class Static {
         builder.show();
     }
 
-    public static void startSQL(Context context) {
+    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/db/AsyncDatabaseConnector.java b/app/src/main/java/info/nerull7/mysqlbrowser/db/AsyncDatabaseConnector.java
index 42a8e04..0162ca8 100644
--- a/app/src/main/java/info/nerull7/mysqlbrowser/db/AsyncDatabaseConnector.java
+++ b/app/src/main/java/info/nerull7/mysqlbrowser/db/AsyncDatabaseConnector.java
@@ -360,20 +360,29 @@ public class AsyncDatabaseConnector {
             public void onFinished(String data, String error) {
                 String []response = data.split("\n");
 
-                List<String>headerList = null;
-                try {
-                    headerList = parseListFromJSON(response[1]);
-                } catch (JSONException e) { e.printStackTrace(); }
-                if(listReturnListener!=null) {
-                    listReturnListener.onListReturn(headerList);
-                }
+                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);
+                    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);
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 0000000000000000000000000000000000000000..75ecc1834177da3ec2c3baae4ede59d3bb423cbc
GIT binary patch
literal 696
zcmV;p0!RIcP)<h;3K|Lk000e1NJLTq001xm001xu1^@s6R|5Hm0007iNkl<ZXhZE;
zJt#&|82&!O;;(EhvKf>zkg}jClv!jo8Z8J5o6#yVD-?xAi9soYO%^u!U67yWse8Y!
z>)w0rJ@=fi`^u@OudjRFpXZ$Sea~M$C%ol_`=9$KpqN2-fWB<aw5b^=frE_>8t_{O
zQ~~9V<8SzV2JV0x;M&1wT4HGl)WCrbpbe-^=*$E9p8+SpV?x<NcU=ON(9jFC1Espg
z9k~zuo&x*8OHy4r1X^Ka5U5USOws=d-?xGDBzlt&V4M#CT}cdR^gV*i4iGCp;u0u>
z!cm}E!yz4!O9-t4?<(Oq1UT~JMReZvnIu+KGZBZtP!Yl=Y@iuVTT!#1BEW<lQB9-e
z?;BBx9YuhpauU$3HjH0)1*RS5kxpZES_T43Y!rcE2zBaMbmDf_lZ14B4q!g%v||Wu
z1__1{;0ikh=q^$$fzv($3n>V2v0DUwt=M4%`p~&YXQ&(*(*&sY0NC^U6hwg2%+XJ}
zxAE$>LEs(+mYnb`*a{-hgs&4RM&G9B3Y1@Y4FnNj=rDw`*mmL6p^Cs5I<#cbd-!w#
ztf>gF_SYLA;NOhtmQb%NxA?;~Au<P8{&<>7)95_QCQo8)Jeg5%S`s|fQEw;#=EOWu
zk;Z$-+NeTw=@pm(3TJG85{oB5mZn0;<Vx%YgffKK(L@s43ZYm@Y@&?XA`q*Lc4HR$
zGn&M)7BUlC30pG|Yb%k}%^WYnxTXHFK>o??G{ax~frcbRA1O&d{GpyCL|?18OM&Q%
zEs_v_wMY`8FL&Kg5`Dey-5QB~;P8?V{lw%YBKDEbzuOl3%&Iyzh<WH%5wZ4N^+IFK
euqnmj348<P?Wr+h%~m%60000<MNUMnLSTaSOF9z(

literal 0
HcmV?d00001

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 0000000000000000000000000000000000000000..944ab3d239f33c5ab2439a170e146d29744b8c10
GIT binary patch
literal 469
zcmV;`0V@89P)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F80004;Nkl<ZSVzT|
zKP*H+5XSFZiCl9vi718SBG*#Wi->46dKaBaC)8JIL`3w|v?3CslnAv(P!ivF_9bgG
z>&@<SnN7azyLYoQzn%U2>NVq1H;!M+1sDZ*3mSGE_(1&vy`^?21%LrN(3A@<#UjK#
zbmf3bc{Bj*<s#IEhDtsD7<@rD&>6(+L`xaKBwDUOBg(*`**jhwLl3d`e*w&((K6J?
z3|=(*#KQ@6EBT25u;=Ul5Zs42kTM|waLhL=q&=1}4#$2tCnSI+7|klu{tTN-XE%p>
zOwr~VHmAN_2*9P*g*3DM4t7V**(&O31prsa9>nF^FCoAJ-pwl<(If|e?Jl767{C_(
zU={{h*!x`xfZOZ#AR~LecktZbw*4WP!0(L&fLGW)Is0+c&4ji+*gl2;+~e00oaD{{
zhB$<HX8g*4IY8<VRLOui1gVp-3V=8XIV35|Fc%=Y3<+Sq5t(nr%!27*L;&;Sm=LBH
zl43J7y^<9WrkBzJ!t`41pG{^TCei_E@@bZEQtM62zKryqGA+LW6Sia_0)jgh00000
LNkvXXu0mjf`4r7(

literal 0
HcmV?d00001

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 0000000000000000000000000000000000000000..6e10fde30dd53259f71fc77743b17ffa6e375b8a
GIT binary patch
literal 891
zcmV->1BCpEP)<h;3K|Lk000e1NJLTq002M$002M;1^@s6s%dfF0009*Nkl<ZcwX(B
zOQ=mz7{~7o1CJ<=n+$Xdr5I7j9e9i|p%h9H0|q803=9km44B-B0fi`qQcN)N7!ZmP
z1EFpPl2?((0Oj|uyLH;mK6|Zo*4let-`Vx=w$J^(_4w`e{Px=Yea5eT<Mr?B0qC^=
z;{>Yg0#jf;8x5eTXfhf<%KsOCzo9Sa9eRttR`G070vJaH7NA9F9-8S{p%2)<LeJ0(
z^xM<l$vg!BJ=tYwIhyWNigKe*m^?&}Mq5g`hoePI0IK*ZG=wHN=I?9v6Nh`~ZY&V7
z04%0J8__i18U@;a#_t+>8tDJmty=&SNofmGKd<A`_;4Bh2+h130JF$+JDL-kc_hC#
zq;mni4@oZ+z+5uhjV6U;n!x8fX`Vr^ebWvBpvvz;8uXfUX&rIa7lJPUHT!)Hdh+=P
zpq6mLr#tun(15qM0e!x#)oDyPiJS&T9{@W^w64*<tkU`aYj!y2;^YcI^ZX8%1RG>?
zp0bp#6I=jju-lJfE^<al?_&<u4DJUb0@Pw@7-?`Uyj%cmz<YJXf+?ws=t(ZkCERa|
zoX;K7yWR}|J^e!{_S(KG0NOho?bHg&TEH5d*GGOTRki@-bpzclszWCL+Rq<Av9`3W
zWvHqJC{JyrkE6EngiZh!;-uwnimOThD*tKpyj?{)0oaU#f*SwBrC`bxUjPSj@C{=%
z6_&zl$9w=NfO)v7wFxZO5&ZrMv=()N`55mB%&4hurzqIVeBp`!ti*6rQ)?1fuB-Un
zcLlHx!?l4Kxwt*RXe4Ji$o&cz=RliV_}z2`ka`BFdKy%{+s*_<U0^E?O9M%}xYYsR
zF$KHi+5*f2Fy94I4}x`c0k1(Y^)y%q0Iz8<^+Ko)0A35B)Jx$y0C+8hHCv?I3{?%l
zdov^eDQAMJ0R(M_%?Cuxhs4wkOb0~;z<gL-5KO-q$y$NwS7U-;`sHZ0g*N?qToBA3
zkPrmZM`W_DVET|$D+II0<T41R4@zbbOdpkPjWC-%EZqvh^l|w-2$LU7<X^fdo}l2W
zx<@yk%;>D2-s+B-PiFEUvU_tMO%;J?)bC}-<Qi4j6ou&lNI`WhrD?te`~g~!#6e&I
RlA-_r002ovPDHLkV1ke}kFx*(

literal 0
HcmV?d00001

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 0000000000000000000000000000000000000000..6835e8f56a38d6298b74ec0316501d58e9c8af15
GIT binary patch
literal 1577
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
bD4V|rgY8#vZc9{$00000NkvXXu0mjfjmpNB

literal 0
HcmV?d00001

diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index d4c83cb..e431c16 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -58,5 +58,6 @@
     <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>