app을 개발하다 보면 db파일에 실제로 저장되어 있는 내용을 보고싶을 때가 있다. 하지만 db파일의 내용을 직접 보려면 권한때문에 일단 db 파일을 폰에서 PC로 복사를 해야하고, 값을 변경해서 다시 폰에 저장하기도 상당히 귀찮은 과정이다. 그래서 앱 안에서 바로 db의 table을 선택해서 내용을 보고, 검색도 하고, 수정도 하고, 지우기도 할 수 있다면 개발과 테스트의 생산성이 높아진다.
그래서 구현한 DatabaseViewerActivity의 최종 모습은 이렇게 된다. 여기에 올려진 소스는 1주일 정도 공부하면서 다듬어진 소스이다.
요구사항:
- 목록의 첫번째 Row와 첫번째 Column은 고정되어, 좌우 상하 스크롤되어도 변경되지 않아야 한다.
- 처음에 테이블을 선택할 수 있고, Tables을 눌러서 다시 선택할 수 있다. 비동기 로드, 로드중에 프로그래스 다이얼로그 표시한다.
- 목록은 짝수, 홀수 Row 여부에 따라서 구별하기 싶도록 배경색이 달라야 한다.
- 타이틀을 Long click하면 Row를 삭제할 수 있다.
- 값 영역을 Long click하면 Copy value 혹은 Edit value할 수 있어야 한다.
- 목록은 30개 단위로 로드하고, MORE를 누르면 다음 30개를 로드한다. 더 로드할 것이 없다면 MORE는 disable된다. (Table의 View adapter 설정을 할 수 없기에 Row를 추가하는데 시간이 많이 걸림)
- SE를 누르면 검색할 Field를 선택하고, 검색어를 입력하여 검색을 할 수 있다.
일단 첫 번째 Column과 Row를 고정하기 위해서 외부에서 구현한 소스를 이용했다. 참고소스
클래스 이름을 TableExLayout으로 바꾸고, Row의 생성을 TableExRowHolder 인터페이스로 분리를 시켰다.
TableExRowHolder 소스
package com.mdiwebma.base.view;
import android.content.Context;
import android.view.View;
/**
* @author djkim
*/
public interface TableExRowHolder {
int getDataCount();
View getTitleView(Context context);
View getDataView(Context context, int index);
}
TableExLayout 소스
package com.mdiwebma.base.view;
import java.util.List;
import android.content.Context;
import android.graphics.Color;
import android.support.annotation.NonNull;
import android.util.Log;
import android.view.View;
import android.widget.HorizontalScrollView;
import android.widget.RelativeLayout;
import android.widget.ScrollView;
import android.widget.TableLayout;
import android.widget.TableRow;
// https://www.codeofaninja.com/2013/08/android-scroll-table-fixed-header-column.html
public class TableExLayout extends RelativeLayout {
public final String TAG = "TableMainLayout.java";
final TableExRowHolder headerObject;
final int headerCellsWidth[];
TableLayout tableA;
TableLayout tableB;
TableLayout tableC;
TableLayout tableD;
HorizontalScrollView horizontalScrollViewB;
HorizontalScrollView horizontalScrollViewD;
ScrollView scrollViewC;
ScrollView scrollViewD;
Context context;
public TableExLayout(Context context, @NonNull TableExRowHolder headerObject) {
super(context);
this.headerObject = headerObject;
headerCellsWidth = new int[headerObject.getDataCount() + 1];
this.context = context;
// initialize the main components (TableLayouts, HorizontalScrollView, ScrollView)
this.initComponents();
this.setComponentsId();
this.setScrollViewAndHorizontalScrollViewTag();
// no need to assemble component A, since it is just a table
this.horizontalScrollViewB.addView(this.tableB);
this.scrollViewC.addView(this.tableC);
this.scrollViewD.addView(this.horizontalScrollViewD);
this.horizontalScrollViewD.addView(this.tableD);
// add the components to be part of the main layout
this.addComponentToMainLayout();
this.setBackgroundColor(Color.WHITE);
// add some table rows
this.addTableRowToTableA();
this.addTableRowToTableB();
this.resizeHeaderHeight();
this.getTableRowHeaderCellWidth();
//this.appendDataRow();
//this.resizeBodyTableRowHeight();
}
// initalized components
private void initComponents() {
this.tableA = new TableLayout(this.context);
this.tableB = new TableLayout(this.context);
this.tableC = new TableLayout(this.context);
this.tableD = new TableLayout(this.context);
this.horizontalScrollViewB = new MyHorizontalScrollView(this.context);
this.horizontalScrollViewD = new MyHorizontalScrollView(this.context);
this.scrollViewC = new MyScrollView(this.context);
this.scrollViewD = new MyScrollView(this.context);
this.tableA.setBackgroundColor(Color.GREEN);
this.horizontalScrollViewB.setBackgroundColor(Color.LTGRAY);
}
// set essential component IDs
@SuppressWarnings("all")
private void setComponentsId() {
this.tableA.setId(1);
this.horizontalScrollViewB.setId(2);
this.scrollViewC.setId(3);
this.scrollViewD.setId(4);
}
// set tags for some horizontal and vertical scroll view
private void setScrollViewAndHorizontalScrollViewTag() {
this.horizontalScrollViewB.setTag("horizontal scroll view b");
this.horizontalScrollViewD.setTag("horizontal scroll view d");
this.scrollViewC.setTag("scroll view c");
this.scrollViewD.setTag("scroll view d");
}
// we add the components here in our TableMainLayout
private void addComponentToMainLayout() {
// RelativeLayout params were very useful here
// the addRule method is the key to arrange the components properly
RelativeLayout.LayoutParams componentB_Params = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
componentB_Params.addRule(RelativeLayout.RIGHT_OF, this.tableA.getId());
RelativeLayout.LayoutParams componentC_Params = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
componentC_Params.addRule(RelativeLayout.BELOW, this.tableA.getId());
RelativeLayout.LayoutParams componentD_Params = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
componentD_Params.addRule(RelativeLayout.RIGHT_OF, this.scrollViewC.getId());
componentD_Params.addRule(RelativeLayout.BELOW, this.horizontalScrollViewB.getId());
// 'this' is a relative layout,
// we extend this table layout as relative layout as seen during the creation of this class
this.addView(this.tableA);
this.addView(this.horizontalScrollViewB, componentB_Params);
this.addView(this.scrollViewC, componentC_Params);
this.addView(this.scrollViewD, componentD_Params);
}
private void addTableRowToTableA() {
this.tableA.addView(this.componentATableRow());
}
private void addTableRowToTableB() {
this.tableB.addView(this.componentBTableRow());
}
// generate table row of table A
TableRow componentATableRow() {
TableRow componentATableRow = new TableRow(this.context);
View textView = headerObject.getTitleView(this.context);
componentATableRow.addView(textView);
return componentATableRow;
}
// generate table row of table B
TableRow componentBTableRow() {
TableRow componentBTableRow = new TableRow(this.context);
int dataCount = this.headerObject.getDataCount();
TableRow.LayoutParams params = new TableRow.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
params.setMargins(2, 0, 0, 0);
for (int index = 0; index < dataCount; index++) {
View textView = this.headerObject.getDataView(this.context, index);
textView.setLayoutParams(params);
componentBTableRow.addView(textView);
}
return componentBTableRow;
}
// generate table row of table C and table D
public void appendDataRow(List<TableExRowHolder> dataList) {
// just seeing some header cell width
for (int x = 0; x < this.headerCellsWidth.length; x++) {
Log.v("TableMainLayout.java", this.headerCellsWidth[x] + "");
}
for (TableExRowHolder tableExRowObject : dataList) {
TableRow tableRowForTableC = this.tableRowForTableC(tableExRowObject);
TableRow taleRowForTableD = this.taleRowForTableD(tableExRowObject);
tableRowForTableC.setBackgroundColor(Color.LTGRAY);
taleRowForTableD.setBackgroundColor(Color.LTGRAY);
this.tableC.addView(tableRowForTableC);
this.tableD.addView(taleRowForTableD);
}
this.resizeBodyTableRowHeight();
}
// a TableRow for table C
TableRow tableRowForTableC(TableExRowHolder tableExRowObject) {
TableRow.LayoutParams params = new TableRow.LayoutParams(this.headerCellsWidth[0], LayoutParams.MATCH_PARENT);
params.setMargins(0, 2, 0, 0);
TableRow tableRowForTableC = new TableRow(this.context);
View view = tableExRowObject.getTitleView(this.context);
tableRowForTableC.addView(view, params);
return tableRowForTableC;
}
TableRow taleRowForTableD(TableExRowHolder tableExRowObject) {
TableRow taleRowForTableD = new TableRow(this.context);
int loopCount = ((TableRow)this.tableB.getChildAt(0)).getChildCount();
for (int index = 0; index < loopCount; index++) {
TableRow.LayoutParams params = new TableRow.LayoutParams(headerCellsWidth[index
+ 1], LayoutParams.MATCH_PARENT);
params.setMargins(2, 2, 0, 0);
View view = tableExRowObject.getDataView(this.context, index);
taleRowForTableD.addView(view, params);
}
return taleRowForTableD;
}
// resizing TableRow height starts here
void resizeHeaderHeight() {
TableRow productNameHeaderTableRow = (TableRow)this.tableA.getChildAt(0);
TableRow productInfoTableRow = (TableRow)this.tableB.getChildAt(0);
int rowAHeight = this.viewHeight(productNameHeaderTableRow);
int rowBHeight = this.viewHeight(productInfoTableRow);
TableRow tableRow = rowAHeight < rowBHeight ? productNameHeaderTableRow : productInfoTableRow;
int finalHeight = rowAHeight > rowBHeight ? rowAHeight : rowBHeight;
this.matchLayoutHeight(tableRow, finalHeight);
}
void getTableRowHeaderCellWidth() {
int tableAChildCount = ((TableRow)this.tableA.getChildAt(0)).getChildCount();
int tableBChildCount = ((TableRow)this.tableB.getChildAt(0)).getChildCount();
for (int x = 0; x < (tableAChildCount + tableBChildCount); x++) {
if (x == 0) {
this.headerCellsWidth[x] = this.getViewWidth(((TableRow)this.tableA.getChildAt(0)).getChildAt(x));
} else {
this.headerCellsWidth[x] = this.getViewWidth(((TableRow)this.tableB.getChildAt(0)).getChildAt(x - 1));
}
}
}
// resize body table row height
void resizeBodyTableRowHeight() {
int tableC_ChildCount = this.tableC.getChildCount();
for (int x = 0; x < tableC_ChildCount; x++) {
TableRow productNameHeaderTableRow = (TableRow)this.tableC.getChildAt(x);
TableRow productInfoTableRow = (TableRow)this.tableD.getChildAt(x);
int rowAHeight = this.viewHeight(productNameHeaderTableRow);
int rowBHeight = this.viewHeight(productInfoTableRow);
TableRow tableRow = rowAHeight < rowBHeight ? productNameHeaderTableRow : productInfoTableRow;
int finalHeight = rowAHeight > rowBHeight ? rowAHeight : rowBHeight;
this.matchLayoutHeight(tableRow, finalHeight);
}
}
// match all height in a table row
// to make a standard TableRow height
private void matchLayoutHeight(TableRow tableRow, int height) {
int tableRowChildCount = tableRow.getChildCount();
// if a TableRow has only 1 child
if (tableRow.getChildCount() == 1) {
View view = tableRow.getChildAt(0);
TableRow.LayoutParams params = (TableRow.LayoutParams)view.getLayoutParams();
params.height = height - (params.bottomMargin + params.topMargin);
return;
}
// if a TableRow has more than 1 child
for (int x = 0; x < tableRowChildCount; x++) {
View view = tableRow.getChildAt(x);
TableRow.LayoutParams params = (TableRow.LayoutParams)view.getLayoutParams();
if (!isTheHeighestLayout(tableRow, x)) {
params.height = height - (params.bottomMargin + params.topMargin);
return;
}
}
}
// check if the view has the highest height in a TableRow
private boolean isTheHeighestLayout(TableRow tableRow, int layoutPosition) {
int tableRowChildCount = tableRow.getChildCount();
int heighestViewPosition = -1;
int viewHeight = 0;
for (int x = 0; x < tableRowChildCount; x++) {
View view = tableRow.getChildAt(x);
int height = this.viewHeight(view);
if (viewHeight < height) {
heighestViewPosition = x;
viewHeight = height;
}
}
return heighestViewPosition == layoutPosition;
}
// read a view's height
private int viewHeight(View view) {
view.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
return view.getMeasuredHeight();
}
// read a view's width
private int getViewWidth(View view) {
view.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
return view.getMeasuredWidth();
}
public void removeRowAt(int rowIndex) {
if (rowIndex < tableC.getChildCount() && rowIndex < tableD.getChildCount()) {
tableC.removeViewAt(rowIndex);
tableD.removeViewAt(rowIndex);
}
}
// horizontal scroll view custom class
class MyHorizontalScrollView extends HorizontalScrollView {
public MyHorizontalScrollView(Context context) {
super(context);
}
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
String tag = (String)this.getTag();
if (tag.equalsIgnoreCase("horizontal scroll view b")) {
horizontalScrollViewD.scrollTo(l, 0);
} else {
horizontalScrollViewB.scrollTo(l, 0);
}
}
}
// scroll view custom class
class MyScrollView extends ScrollView {
public MyScrollView(Context context) {
super(context);
}
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
String tag = (String)this.getTag();
if (tag.equalsIgnoreCase("scroll view c")) {
scrollViewD.scrollTo(0, t);
} else {
scrollViewC.scrollTo(0, t);
}
}
}
}
테이블 row 한 줄의 view를 생성하고 관리한다. 간단히 TextView로 구성했다.
DatabaseRowHolder 소스
package com.mdiwebma.base.activity;
import android.content.Context;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.view.Gravity;
import android.view.View;
import android.widget.TextView;
import com.mdiwebma.base.utils.DisplayUtils;
import com.mdiwebma.base.view.TableExRowHolder;
import com.mdiwebma.learninghabit.R;
public class DatabaseRowHolder implements TableExRowHolder {
public static interface DatabaseRowListener {
//void onClickTitle(DatabaseRowHolder rowHolder);
void onLongClickTitle(DatabaseRowHolder rowHolder);
//void onClickData(DatabaseRowHolder rowHolder, int index);
void onLongClickData(DatabaseRowHolder rowHolder, int index);
}
String title;
final String data[];
boolean isHeader = false;
DatabaseRowListener rowListener;
final TextView[] dataViews;
boolean isDataViewWhite;
final String rowid;
public DatabaseRowHolder(long rowid, @NonNull String title, @NonNull String[] data) {
this.rowid = String.valueOf(rowid);
this.title = title;
this.data = data;
this.dataViews = new TextView[data.length];
}
@Override
public int getDataCount() {
return data.length;
}
@Override
public View getTitleView(Context context) {
TextView textView = new TextView(context);
textView.setBackgroundColor(context.getResources().getColor(R.color.database_viewer_color_header));
textView.setText(title);
textView.setGravity(Gravity.CENTER);
textView.setPadding(5, 5, 5, 5);
textView.setMinWidth(DisplayUtils.toPixel(90.f));
if (rowListener != null) {
textView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
rowListener.onLongClickTitle(DatabaseRowHolder.this);
return true;
}
});
}
return textView;
}
@Override
public View getDataView(Context context, final int index) {
if (dataViews[index] != null) {
return dataViews[index];
}
TextView textView = new TextView(context);
textView.setText(data[index] == null ? "<null>" : data[index]);
textView.setGravity(Gravity.CENTER);
textView.setPadding(5, 5, 5, 5);
textView.setMinWidth(DisplayUtils.toPixel(90.f));
textView.setEllipsize(TextUtils.TruncateAt.END);
textView.setMaxLines(5);
if (isHeader) {
textView.setBackgroundColor(context.getResources().getColor(R.color.database_viewer_color_header));
} else {
textView.setBackgroundResource(isDataViewWhite ? R.drawable.selector_databaseviewer_bg_first
: R.drawable.selector_databaseviewer_bg_second);
}
textView.setClickable(true);
if (rowListener != null) {
textView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
rowListener.onLongClickData(DatabaseRowHolder.this, index);
return true;
}
});
}
dataViews[index] = textView;
return textView;
}
public void setHeaderRow() {
this.isHeader = true;
}
public void setRowListener(DatabaseRowListener rowListener) {
this.rowListener = rowListener;
}
public void updateValue(int index, String value) {
this.data[index] = value;
if (dataViews[index] != null) {
dataViews[index].setText(value);
}
}
public void setDataViewWhite(boolean isDataViewWhite) {
this.isDataViewWhite = isDataViewWhite;
}}
DatabaseViewerActivity 소스
package com.mdiwebma.base.activity;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import android.content.ClipboardManager;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.database.Cursor;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.text.InputType;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.Spinner;
import com.mdiwebma.base.ApplicationKeeper;
import com.mdiwebma.base.BaseActionBarActivity;
import com.mdiwebma.base.dialog.MessageDialog;
import com.mdiwebma.base.dialog.MessageDialogHelper;
import com.mdiwebma.base.helper.StringBuilderPool;
import com.mdiwebma.base.logging.Dlog;
import com.mdiwebma.base.settings.SettingDO;
import com.mdiwebma.base.task.ExAsyncTask;
import com.mdiwebma.base.utils.StringUtils;
import com.mdiwebma.base.utils.ToastUtils;
import com.mdiwebma.base.view.TableExLayout;
import com.mdiwebma.base.view.TableExRowHolder;
/**
* @author djkim
* <p/>
* // TODO
* // V select table / reload / async load / progress
* // V coloring
* // V paging
* // V delete row
* // V edit value
* // V copy value
* // V search value
*/
public class DatabaseViewerActivity extends BaseActionBarActivity {
static final HashMap<String, String> mapTableToTitleKey = new HashMap<String, String>() {
{
put(SettingDO.TABLE, SettingDO.COL_KEY);
}
};
static final int PAGE_SIZE = 30;
TableExLayout tableLayout;
String tableName;
DatabaseRowHolder headerRowHolder;
List<TableExRowHolder> tableExRowObjects;
int currentPage;
int searchColumnIndex;
String searchKeyword;
String where;
@NonNull
MenuItem moreMenu;
MenuItem searchMenu;
@Override
protected void onCreate(Bundle b) {
super.onCreate(b);
getSupportActionBar().setTitle("Database viewer");
//for test
//ApplicationKeeper.getDbHelper().delete_(SettingDO.TABLE, null, null);
for (int i = 0; i < 100; i++) {
//new SettingDO(String.format("test_key=%d", i), String.format("value = %d", i)).insert();
}
onTablesMenu();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
menu.add(0, 0, 0, "Tables")//.setIcon(R.drawable.ic_menu_recent_history)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
moreMenu = menu.add(0, 1, 0, "More");//.setIcon(R.drawable.ic_menu_manage);
moreMenu.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
moreMenu.setEnabled(false);
searchMenu = menu.add(0, 2, 0, "Se");//.setIcon(R.drawable.ic_menu_manage);
searchMenu.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
searchMenu.setEnabled(false);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == 0) {
onTablesMenu();
} else if (item.getItemId() == 1) {
loadTableData(tableName, where, true);
} else if (item.getItemId() == 2) {
onSearchMenu();
}
return true;
}
void onTablesMenu() {
ArrayList<String> tableList = new ArrayList<>();
Cursor cursor = ApplicationKeeper.getDbHelper().query_("SELECT name FROM sqlite_master WHERE type='table'");
try {
while (cursor.moveToNext()) {
String name = cursor.getString(0);
if ("android_metadata".equals(name) == false
&& "sqlite_sequence".equals(name) == false) {
tableList.add(name);
}
}
} catch (Exception ex) {
Dlog.notReached(ex);
} finally {
if (cursor != null) {
cursor.close();
}
}
if (tableList.isEmpty()) {
MessageDialogHelper.showConfirmDialog(context, "table not found", null);
} else {
final String[] tableNameList = tableList.toArray(new String[0]);
new MessageDialog.Builder(context).setItems(tableNameList, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface d, int which) {
loadTableData(tableNameList[which], null, false);
}
}).setTitle("Tables").show().setCanceledOnTouchOutside(true);
}
}
@SuppressWarnings("all")
void loadTableData(final String targetTableName, final String targetWhere, final boolean readMore) {
new ExAsyncTask<Void, Void, Void>() {
String fixedTitle;
String headers[];
List<TableExRowHolder> resultList;
boolean hasMore = true;
@Override
protected Void doInBackground(Void... params) {
String fixedColumnName = mapTableToTitleKey.get(targetTableName);
StringBuilder sb = StringBuilderPool.obtain();
sb.append("SELECT rowid, ");
if (StringUtils.isNotEmpty(fixedColumnName)) {
sb.append(fixedColumnName);
sb.append(", ");
}
sb.append("* from ");
sb.append(targetTableName);
if (StringUtils.isNotEmpty(targetWhere)) {
sb.append(" WHERE ");
sb.append(targetWhere);
}
if (currentPage == 0 || readMore == false) {
sb.append(" LIMIT ").append(PAGE_SIZE);
} else {
sb.append(" LIMIT ").append(currentPage * PAGE_SIZE).append(", ").append(PAGE_SIZE);
}
String sql = StringBuilderPool.recycle(sb);
resultList = new ArrayList<>();
Cursor cursor = ApplicationKeeper.getDbHelper().query_(sql);
try {
final int columnCount = cursor.getColumnCount();
int count = 0;
while (cursor.moveToNext()) {
if (headers == null) {
headers = new String[columnCount - 2];
fixedTitle = cursor.getColumnName(1);
for (int i = 2; i < columnCount; i++) {
headers[i - 2] = cursor.getColumnName(i);
}
}
long rowid = cursor.getLong(0);
String title = cursor.getString(1);
String data[] = new String[columnCount - 2];
for (int i = 2; i < columnCount; i++) {
data[i - 2] = cursor.getString(i);
}
DatabaseRowHolder rowHolder = new DatabaseRowHolder(rowid, title, data);
rowHolder.setRowListener(rowListener);
rowHolder.setDataViewWhite(count % 2 == 0);
count++;
resultList.add(rowHolder);
}
if (count < PAGE_SIZE) {
hasMore = false;
}
} catch (Exception ex) {
Dlog.notReached(ex);
} finally {
if (cursor != null) {
cursor.close();
}
}
return null;
}
@Override
protected void onPostExecute(Void v) {
if (resultList.isEmpty() == false) {
getSupportActionBar().setTitle(targetTableName);
if (readMore && targetTableName.equals(tableName) && tableLayout != null
&& tableExRowObjects != null) {
tableExRowObjects.addAll(resultList);
tableLayout.appendDataRow(resultList);
currentPage++;
ToastUtils.show(String.format("%d more item(s) added", resultList.size()));
} else {
if (targetTableName.equals(tableName) == false) {
searchColumnIndex = 0;
}
tableName = targetTableName;
where = targetWhere;
headerRowHolder = new DatabaseRowHolder(0, fixedTitle, headers);
headerRowHolder.setHeaderRow();
// Long time ~~~
tableLayout = new TableExLayout(context, headerRowHolder);
tableExRowObjects = resultList;
tableLayout.appendDataRow(tableExRowObjects);
setContentView(tableLayout);
currentPage = 1;
}
moreMenu.setEnabled(hasMore);
searchMenu.setEnabled(true);
if (StringUtils.isNotEmpty(where)) {
getSupportActionBar().setSubtitle(String.format("%d items by %s:\"%s\"", tableExRowObjects.size(), headerRowHolder.data[searchColumnIndex], searchKeyword));
} else {
getSupportActionBar().setSubtitle(String.format("%d items", tableExRowObjects.size()));
}
} else {
MessageDialogHelper.showConfirmDialog(context, (readMore ? "More " : "")
+ "data not found!", null).setTitle(targetTableName);
if (readMore) {
moreMenu.setEnabled(hasMore);
}
}
}
}.showProgressDialog(this).execute(null);
}
void onSearchMenu() {
LinearLayout layout = new LinearLayout(context);
layout.setOrientation(LinearLayout.VERTICAL);
layout.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
// Column spinner
Spinner spinner = new Spinner(context);
spinner.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> arg0, View view, int position, long rowId) {
searchColumnIndex = position;
}
@Override
public void onNothingSelected(AdapterView<?> arg0) {
}
});
spinner.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, headerRowHolder.data));
spinner.setSelection(searchColumnIndex);
// Set up the input
final EditText input = new EditText(context);
// Specify the type of input expected; this, for example, sets the input as a password, and will mask the text
input.setInputType(InputType.TYPE_CLASS_TEXT
| InputType.TYPE_TEXT_FLAG_MULTI_LINE/*| InputType.TYPE_TEXT_VARIATION_PASSWORD*/);
//input.setText(data);
input.setHint("Input keyword");
input.setMaxLines(5);
input.setHintTextColor(0xFFF0F0F0);
input.setText(searchKeyword);
input.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
layout.addView(spinner);
layout.addView(input);
MessageDialog dialog = MessageDialogHelper.showConfirmAndCancelDialog(context, null, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface d, int which) {
String text = input.getEditableText().toString();
if (StringUtils.isNotEmpty(text)) {
searchKeyword = text;
where = headerRowHolder.data[searchColumnIndex] + " LIKE \'%" + searchKeyword + "%\'";
loadTableData(tableName, where, false);
}
}
});
dialog.setTitle("Searching");
dialog.setView(layout);
dialog.setCanceledOnTouchOutside(true);
// avoid memory leak in Samsung phone
dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
input.setCursorVisible(false);
}
});
}
DatabaseRowHolder.DatabaseRowListener rowListener = new DatabaseRowHolder.DatabaseRowListener() {
@Override
public void onLongClickTitle(final DatabaseRowHolder rowHolder) {
MessageDialog dialog = MessageDialogHelper.showConfirmAndCancelDialog(context, "Do you want to delete?", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface d, int which) {
try {
int count = ApplicationKeeper.getDbHelper().delete_(tableName, "rowid=?", new String[]{
rowHolder.rowid});
if (count > 0) {
int rowIndex = tableExRowObjects.indexOf(rowHolder);
if (rowIndex >= 0) {
tableLayout.removeRowAt(rowIndex);
tableExRowObjects.remove(rowHolder);
Dlog.toast("Deleted~");
}
} else {
Dlog.toast("Deletion failed~");
}
} catch (Exception ex) {
Dlog.notReached(ex);
}
}
});
dialog.setTitle(rowHolder.title);
dialog.setCanceledOnTouchOutside(true);
}
@Override
public void onLongClickData(final DatabaseRowHolder rowHolder, final int index) {
final String data = rowHolder.data[index];
String[] menuItems = new String[]{
"Copy value",
"Edit value"
};
new MessageDialog.Builder(context).setItems(menuItems, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface d, int which) {
if (which == 0) {
if (StringUtils.isEmpty(data)) {
Dlog.toast("data is empty");
} else {
ClipboardManager clipboard = (ClipboardManager)getSystemService(Context.CLIPBOARD_SERVICE);
clipboard.setText(data);
Dlog.toast(data + "\n\ncopyed!");
}
} else {
// Set up the input
final EditText input = new EditText(context);
// Specify the type of input expected; this, for example, sets the input as a password, and will mask the text
input.setInputType(InputType.TYPE_CLASS_TEXT
| InputType.TYPE_TEXT_FLAG_MULTI_LINE/*| InputType.TYPE_TEXT_VARIATION_PASSWORD*/);
input.setText(data);
input.setMaxLines(5);
input.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
MessageDialog dialog = MessageDialogHelper.showConfirmAndCancelDialog(context, null, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface d, int which) {
ContentValues contentValues = new ContentValues();
String newData = input.getEditableText().toString();
contentValues.put(headerRowHolder.data[index], newData);
try {
int count = ApplicationKeeper.getDbHelper().update_(tableName, contentValues, "rowid=?", new String[]{
rowHolder.rowid});
if (count > 0) {
rowHolder.updateValue(index, newData);
Dlog.toast("Updated~");
} else {
Dlog.toast("Not updated!");
}
} catch (Exception ex) {
Dlog.notReached(ex);
}
}
});
dialog.setTitle(rowHolder.title);
dialog.setView(input);
dialog.setCanceledOnTouchOutside(true);
// avoid memory leak in Samsung phone
dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
input.setCursorVisible(false);
}
});
}
}
}).setTitle(rowHolder.title + " \"" + data + "\"").show().setCanceledOnTouchOutside(true);
}
};
}