Browse Source

Merge remote-tracking branch 'origin/master'

ifengouy 2 years ago
parent
commit
05892c9652

+ 2 - 0
app/build.gradle

@@ -88,4 +88,6 @@ dependencies {
 
 
     implementation 'com.liaoinstan.springview:library:1.5.1'
+
+    implementation 'net.sourceforge.jexcelapi:jxl:2.6.12'
 }

+ 12 - 2
app/src/main/java/com/sunwin/visitorapp/SplashActivity.java

@@ -79,9 +79,14 @@ public class SplashActivity extends BaseActivity {
 //        String cert = readExternal(CERT_PATH).trim();
         String cert = SharePrefenceUtils.getString(Constant.ISharePrefence.CERT, "");
         if (TextUtils.isEmpty(cert)) {
-            startAuthActivity();
+            if(BuildConfig.DEBUG){
+                openApplication();
+            }else{
+                startAuthActivity();
+            }
             return;
         }
+
         loadingDialog.showLoadingDialog("授权中,请等待...");
         authApi.authDevice(this.getApplicationContext(), cert, "", new AuthApi.AuthDeviceCallBack() {
             @Override
@@ -104,7 +109,12 @@ public class SplashActivity extends BaseActivity {
                         public void run() {
                             ToastUtils.showToast("Apply update: error. error code is: " + result.errorCode + " , error message: " + result.errorMessage);
                             loadingDialog.dismissLoadingDialog();
-                            startAuthActivity();
+                            if(BuildConfig.DEBUG){
+                                openApplication();
+                            }else{
+                                startAuthActivity();
+                            }
+
                         }
                     });
                 }

+ 3 - 1
app/src/main/java/com/sunwin/visitorapp/db/DatabaseManager.java

@@ -154,7 +154,9 @@ public class DatabaseManager {
         return liteOrm.update(t);
     }
 
-
+    public <T> List<T> query(QueryBuilder<T> cla) {
+        return liteOrm.query(cla);
+    }
     /**
      * 查询 某字段 等于 Value的值
      */

+ 19 - 7
app/src/main/java/com/sunwin/visitorapp/db/LogModel.java

@@ -1,5 +1,6 @@
 package com.sunwin.visitorapp.db;
 
+import com.litesuits.orm.db.annotation.Column;
 import com.litesuits.orm.db.annotation.PrimaryKey;
 import com.litesuits.orm.db.enums.AssignType;
 import com.sunwin.visitorapp.manage.RunDataManage;
@@ -9,28 +10,39 @@ import java.util.Date;
 public class LogModel {
     public enum Type{
 
-        UserLoginIn,UserLoginOut,
-        UserSearch,UserEdit,UserDelete,
-        BlackListSearch,BlackListEdit,BlackListDelete,
-        RoleSearch,RoleEdit,RoleDelete,
+        UserLoginIn("登录"),UserLoginOut("登出"),
+        UserSearch("用户查询"),UserEdit("用户编辑"),UserDelete("用户删除"),
+        BlackListSearch("黑名单查询"),BlackListEdit("黑名单编辑"),BlackListDelete("黑名单删除"),
+        RoleSearch("角色查询"),RoleEdit("角色编辑"),RoleDelete("角色删除");
 
+        private String name;
+        Type(String name){
+            this.name = name;
+        }
+        public String getName() {
+            return name;
+        }
 
     }
     @PrimaryKey(AssignType.AUTO_INCREMENT)
     public long id;
 
+    public final static String Column_Name = "name";
+    @Column(Column_Name)
     public String name;
 
 
     public long time;
 
-    public Type type;
+    public final static String Column_Type = "type";
+    @Column(Column_Type)
+    public String type;
 
     public static void AddLog(Type type){
         LogModel model = new LogModel();
-        model.name = RunDataManage.GetLoginUser()!=null?RunDataManage.GetLoginUser().name:"位置";
+        model.name = RunDataManage.GetLoginUser()!=null?RunDataManage.GetLoginUser().name:"未知";
         model.time = new Date().getTime();
-        model.type = type;
+        model.type = type.getName();
         DatabaseManager.getInstance().insertNew(model);
     }
 }

+ 6 - 0
app/src/main/java/com/sunwin/visitorapp/fragment/BlackListManageFr.java

@@ -31,6 +31,7 @@ import com.sunwin.visitorapp.adapter.BaseRecyclerAdapter;
 import com.sunwin.visitorapp.adapter.BaseViewHolder;
 import com.sunwin.visitorapp.db.BlackUserModel;
 import com.sunwin.visitorapp.db.DatabaseManager;
+import com.sunwin.visitorapp.db.LogModel;
 import com.sunwin.visitorapp.db.UserManagerModel;
 import com.sunwin.visitorapp.utils.FileUtil;
 import com.sunwin.visitorapp.utils.ToastUtils;
@@ -64,6 +65,7 @@ public class BlackListManageFr extends Fragment {
             holder.getView(R.id.ButtonDelete).setOnClickListener(v->{
                 new AlertDialog.Builder(context).setTitle("提示").setMessage("确定删除吗?").setPositiveButton("确定", (dialog, which) -> {
                     DatabaseManager.getInstance().delete(data);
+                    LogModel.AddLog(LogModel.Type.BlackListDelete);
                     refreshData();
                 }).setNegativeButton("取消",null).create().show();
             });
@@ -75,6 +77,7 @@ public class BlackListManageFr extends Fragment {
 
 
     private void refreshData(){
+        LogModel.AddLog(LogModel.Type.BlackListSearch);
         adapter.setList(DatabaseManager.getInstance().getQueryAll(BlackUserModel.class));
     }
     @SuppressLint("ResourceAsColor")
@@ -143,8 +146,11 @@ public class BlackListManageFr extends Fragment {
             }
             model.name = editName.getText().toString().trim();
             model.id_card_code = editICCode.getText().toString().trim();
+
+            if(image_uri!=null)
             model.photo = FileUtil.copyToFilesDir(root.getContext(), image_uri);
             DatabaseManager.getInstance().insert(model);
+            LogModel.AddLog(LogModel.Type.BlackListEdit);
             refreshData();
             dialog.dismiss();
         });

+ 94 - 0
app/src/main/java/com/sunwin/visitorapp/fragment/LogManageFr.java

@@ -1,25 +1,119 @@
 package com.sunwin.visitorapp.fragment;
 
+import android.annotation.SuppressLint;
+import android.content.Context;
 import android.os.Bundle;
+import android.os.Environment;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.widget.EditText;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.appcompat.app.AlertDialog;
 import androidx.fragment.app.Fragment;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
 
+import com.litesuits.orm.db.assit.QueryBuilder;
 import com.sunwin.visitorapp.R;
+import com.sunwin.visitorapp.adapter.BaseRecyclerAdapter;
+import com.sunwin.visitorapp.adapter.BaseViewHolder;
+import com.sunwin.visitorapp.db.DatabaseManager;
+import com.sunwin.visitorapp.db.LogModel;
+import com.sunwin.visitorapp.db.RoleModel;
+import com.sunwin.visitorapp.db.UserManagerModel;
+import com.sunwin.visitorapp.utils.ExcelUtil;
+import com.sunwin.visitorapp.utils.TimeUtils;
+import com.sunwin.visitorapp.utils.ToastUtils;
+
+import java.io.File;
+import java.util.Date;
 
 public class LogManageFr extends Fragment {
 
     private View root;
+    private Context context;
+    private BaseRecyclerAdapter<LogModel> adapter ;
     @Nullable
     @Override
     public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
 
         if(root==null)root = inflater.inflate(R.layout.fr_log_manage,container,false);
+        context = root.getContext();
+        RecyclerView RecyclerBlackList = root.findViewById(R.id.RecyclerLogList);
+        adapter = new BaseRecyclerAdapter<LogModel>() {
+            @Override
+            protected View getLayoutView(ViewGroup parent, int viewType) {
+                return LayoutInflater.from(parent.getContext()).inflate(R.layout.item_loglist,parent,false);
+            }
+
+            @SuppressLint("UseCompatLoadingForDrawables")
+            @Override
+            public void bind(int position, LogModel data, BaseViewHolder holder, int viewType) {
+
+                holder.setText(R.id.TextName,data.name);
+                holder.setText(R.id.TextType, data.type);
+                holder.setText(R.id.TextTime, TimeUtils.stampToTime(data.time));
+
+
+            }
+        };
+        RecyclerBlackList.setAdapter(adapter);
+        RecyclerBlackList.setLayoutManager(new LinearLayoutManager(context,LinearLayoutManager.VERTICAL,false));
+//        RecyclerBlackList.addItemDecoration(new RVLinearItemDecoration.Builder(root.getContext()).color(R.color.ColorGray).dashWidth(2).create());
+        refreshData();
 
+        EditText EditSearchKey = root.findViewById(R.id.EditSearchKey);
+        root.findViewById(R.id.ButtonSearch).setOnClickListener(v->{
+            search(EditSearchKey.getText().toString().trim());
+        });
+
+        root.findViewById(R.id.ButtonExcelOut).setOnClickListener(v->{
+            File sdcard = Environment.getExternalStorageDirectory();
+            String path = new File(sdcard.getAbsolutePath()+"/VisitorAppLog").getAbsolutePath();
+            String excelFileName = "/日志记录"+TimeUtils.stampToTime(new Date().getTime())+".xls";
+            new AlertDialog.Builder(context).setTitle("提示").setMessage("确定导出吗?将"+adapter.getList().size()+"条数据导出到\n"+path+excelFileName).setPositiveButton("确定", (dialog, which) -> {
+                OntExcel(path,excelFileName);
+            }).setNegativeButton("取消",null).create().show();
+
+        });
         return root;
     }
+
+
+    public void OntExcel(String path,String fileName) {
+
+        new Thread(() -> {
+            try {
+                File file = new File(path);
+                if (!file.exists()) {
+                    file.mkdirs();
+                }
+                String[] title = {"用户名称", "操作类型", "操作时间"};
+                String sheetName = "日志记录";
+                String filePaths = file.getAbsolutePath() + fileName;
+                ExcelUtil.initExcel(filePaths, sheetName, title);
+                ExcelUtil.writeObjListToExcel(adapter.getList(), filePaths, LogManageFr.this.requireActivity());
+                LogManageFr.this.requireActivity().runOnUiThread(() -> ToastUtils.showToast("导出成功"));
+                return;
+            }catch (Exception ig){
+                LogManageFr.this.requireActivity().runOnUiThread(() -> ToastUtils.showToast("导出失败"));
+            }
+
+        }).start();
+
+    }
+    private void refreshData(){
+        adapter.setList(DatabaseManager.getInstance().getQueryAll(LogModel.class));
+    }
+
+    private void search(String key){
+        QueryBuilder<LogModel> q =new QueryBuilder<>(LogModel.class);
+        q.where(LogModel.Column_Name + " like ?","%"+key+"%")
+        .whereAppendOr().where(LogModel.Column_Type+ " like ?","%"+key+"%");
+
+        adapter.setList(DatabaseManager.getInstance().query(q));
+    }
 }

+ 5 - 0
app/src/main/java/com/sunwin/visitorapp/fragment/RoleManageFr.java

@@ -27,6 +27,7 @@ import com.sunwin.visitorapp.adapter.BaseRecyclerAdapter;
 import com.sunwin.visitorapp.adapter.BaseViewHolder;
 import com.sunwin.visitorapp.db.BlackUserModel;
 import com.sunwin.visitorapp.db.DatabaseManager;
+import com.sunwin.visitorapp.db.LogModel;
 import com.sunwin.visitorapp.db.RoleModel;
 import com.sunwin.visitorapp.db.UserManagerModel;
 import com.sunwin.visitorapp.manage.RunDataManage;
@@ -62,6 +63,7 @@ public class RoleManageFr extends Fragment {
                 holder.getView(R.id.ButtonDelete).setOnClickListener(v->{
                     new AlertDialog.Builder(holder.itemView.getContext()).setTitle("提示").setMessage("确定删除吗?").setPositiveButton("确定", (dialog, which) -> {
                         DatabaseManager.getInstance().delete(data);
+                        LogModel.AddLog(LogModel.Type.RoleDelete);
                         refreshData();
                     }).setNegativeButton("取消",null).create().show();
                 });
@@ -85,9 +87,11 @@ public class RoleManageFr extends Fragment {
         return root;
     }
     private void refreshData(){
+        LogModel.AddLog(LogModel.Type.RoleSearch);
         adapter.setList(DatabaseManager.getInstance().getQueryAll(RoleModel.class));
     }
     private void search(String key){
+        LogModel.AddLog(LogModel.Type.RoleSearch);
         adapter.setList(DatabaseManager.getInstance().getQueryByWhere(RoleModel.class,"name",key));
     }
     private void createDialog(Context context, ViewGroup parent, RoleModel model){
@@ -128,6 +132,7 @@ public class RoleManageFr extends Fragment {
             model.p_visitor_sign_in = CheckVisitorSignIn.isChecked();
             model.p_visitor_sign_out = CheckVisitorSignOut.isChecked();
             DatabaseManager.getInstance().save(model);
+            LogModel.AddLog(LogModel.Type.RoleEdit);
             if(RunDataManage.GetLoginRole()!=null&&RunDataManage.GetLoginRole().id==model.id){
                 RunDataManage.RefreshLoginUser();
             }

+ 5 - 0
app/src/main/java/com/sunwin/visitorapp/fragment/UserManageFr.java

@@ -34,6 +34,7 @@ import com.sunwin.visitorapp.R;
 import com.sunwin.visitorapp.adapter.BaseRecyclerAdapter;
 import com.sunwin.visitorapp.adapter.BaseViewHolder;
 import com.sunwin.visitorapp.db.DatabaseManager;
+import com.sunwin.visitorapp.db.LogModel;
 import com.sunwin.visitorapp.db.RoleModel;
 import com.sunwin.visitorapp.db.UserManagerModel;
 import com.sunwin.visitorapp.manage.RunDataManage;
@@ -79,6 +80,7 @@ public class UserManageFr extends Fragment {
                 });
                 holder.getView(R.id.ButtonDelete).setOnClickListener(v->{
                     new AlertDialog.Builder(context).setTitle("提示").setMessage("确定删除吗?").setPositiveButton("确定", (dialog, which) -> {
+                        LogModel.AddLog(LogModel.Type.UserDelete);
                         DatabaseManager.getInstance().delete(data);
                         refreshData();
                     }).setNegativeButton("取消",null).create().show();
@@ -101,10 +103,12 @@ public class UserManageFr extends Fragment {
     }
 
     private void refreshData(){
+        LogModel.AddLog(LogModel.Type.UserSearch);
         adapter.setList(DatabaseManager.getInstance().getQueryAll(UserManagerModel.class));
     }
 
     private void search(String key){
+        LogModel.AddLog(LogModel.Type.UserSearch);
         adapter.setList(DatabaseManager.getInstance().getQueryByWhere(UserManagerModel.class,"name",key));
     }
 
@@ -238,6 +242,7 @@ public class UserManageFr extends Fragment {
 //            model.valid_time = TextValidTime.getText().toString().trim();
             Timber.e(GsonUtil.toJson(model));
             DatabaseManager.getInstance().save(model);
+            LogModel.AddLog(LogModel.Type.UserEdit);
             if(RunDataManage.GetLoginUser()!=null&&RunDataManage.GetLoginUser().id==model.id){
                 RunDataManage.RefreshLoginUser();
             }

+ 1 - 1
app/src/main/java/com/sunwin/visitorapp/manage/RunDataManage.java

@@ -8,7 +8,7 @@ import com.sunwin.visitorapp.utils.GsonUtil;
 import com.sunwin.visitorapp.utils.SharePrefenceUtils;
 
 public class RunDataManage {
-    private static  String Key_loginUser = "Key_loginUser";
+    private static final String Key_loginUser = "Key_loginUser";
     private static UserManagerModel loginUser;
     private static RoleModel roleModel;
 

+ 171 - 0
app/src/main/java/com/sunwin/visitorapp/utils/ExcelUtil.java

@@ -0,0 +1,171 @@
+package com.sunwin.visitorapp.utils;
+
+import android.content.Context;
+
+import com.sunwin.visitorapp.db.LogModel;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import jxl.Workbook;
+import jxl.WorkbookSettings;
+import jxl.format.Colour;
+import jxl.write.Label;
+import jxl.write.WritableCell;
+import jxl.write.WritableCellFormat;
+import jxl.write.WritableFont;
+import jxl.write.WritableSheet;
+import jxl.write.WritableWorkbook;
+import jxl.write.WriteException;
+
+public class ExcelUtil {
+    private static WritableFont arial14font = null;
+
+    private static WritableCellFormat arial14format = null;
+    private static WritableFont arial10font = null;
+    private static WritableCellFormat arial10format = null;
+    private static WritableFont arial12font = null;
+    private static WritableCellFormat arial12format = null;
+    private final static String UTF8_ENCODING = "UTF-8";
+
+
+    /**
+     * 单元格的格式设置 字体大小 颜色 对齐方式、背景颜色等...
+     */
+    private static void format() {
+        try {
+            arial14font = new WritableFont(WritableFont.ARIAL, 14, WritableFont.BOLD);
+            arial14font.setColour(Colour.LIGHT_BLUE);
+            arial14format = new WritableCellFormat(arial14font);
+            arial14format.setAlignment(jxl.format.Alignment.CENTRE);
+            arial14format.setBorder(jxl.format.Border.ALL, jxl.format.BorderLineStyle.THIN);
+            arial14format.setBackground(Colour.VERY_LIGHT_YELLOW);
+
+            arial10font = new WritableFont(WritableFont.ARIAL, 10, WritableFont.BOLD);
+            arial10format = new WritableCellFormat(arial10font);
+            arial10format.setAlignment(jxl.format.Alignment.CENTRE);
+            arial10format.setBorder(jxl.format.Border.ALL, jxl.format.BorderLineStyle.THIN);
+            arial10format.setBackground(Colour.GRAY_25);
+
+            arial12font = new WritableFont(WritableFont.ARIAL, 10);
+            arial12format = new WritableCellFormat(arial12font);
+            //对齐格式
+            arial10format.setAlignment(jxl.format.Alignment.CENTRE);
+            //设置边框
+            arial12format.setBorder(jxl.format.Border.ALL, jxl.format.BorderLineStyle.THIN);
+
+        } catch (WriteException e) {
+            e.printStackTrace();
+        }
+    }
+
+
+    /**
+     * 初始化Excel表格
+     *
+     * @param filePath  存放excel文件的路径(path/demo.xls)
+     * @param sheetName Excel表格的表名
+     * @param colName   excel中包含的列名(可以有多个)
+     */
+    public static void initExcel(String filePath, String sheetName, String[] colName) {
+        format();
+        WritableWorkbook workbook = null;
+        try {
+            File file = new File(filePath);
+            if (!file.exists()) {
+                file.createNewFile();
+            } else {
+                return;
+            }
+            workbook = Workbook.createWorkbook(file);
+            //设置表格的名字
+            WritableSheet sheet = workbook.createSheet(sheetName, 0);
+            //创建标题栏
+            sheet.addCell((WritableCell) new Label(0, 0, filePath, arial14format));
+            for (int col = 0; col < colName.length; col++) {
+                sheet.addCell(new Label(col, 0, colName[col], arial10format));
+            }
+            //设置行高
+            sheet.setRowView(0, 340);
+            workbook.write();
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            if (workbook != null) {
+                try {
+                    workbook.close();
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+
+    /**
+     * 将制定类型的List写入Excel中
+     *
+     */
+    @SuppressWarnings("unchecked")
+    public static  void writeObjListToExcel(List<LogModel> objList, String fileName, Context mContext) {
+        if (objList != null && objList.size() > 0) {
+            WritableWorkbook writebook = null;
+            InputStream in = null;
+            try {
+                WorkbookSettings setEncode = new WorkbookSettings();
+                setEncode.setEncoding(UTF8_ENCODING);
+
+                in = new FileInputStream(new File(fileName));
+                Workbook workbook = Workbook.getWorkbook(in);
+                writebook = Workbook.createWorkbook(new File(fileName), workbook);
+                WritableSheet sheet = writebook.getSheet(0);
+
+                for (int j = 0; j < objList.size(); j++) {
+                    LogModel demoBean = (LogModel) objList.get(j);
+                    List<String> list = new ArrayList<>();
+                    list.add(demoBean.name);
+                    list.add(demoBean.type);
+                    list.add(TimeUtils.stampToTime(demoBean.time));
+
+                    for (int i = 0; i < list.size(); i++) {
+                        sheet.addCell(new Label(i, j + 1, list.get(i), arial12format));
+                        if (list.get(i).length() <= 4) {
+                            //设置列宽
+                            sheet.setColumnView(i, list.get(i).length() + 8);
+                        } else {
+                            //设置列宽
+                            sheet.setColumnView(i, list.get(i).length() + 5);
+                        }
+                    }
+                    //设置行高
+                    sheet.setRowView(j + 1, 350);
+                }
+
+                writebook.write();
+                workbook.close();
+            } catch (Exception e) {
+                e.printStackTrace();
+            } finally {
+                if (writebook != null) {
+                    try {
+                        writebook.close();
+                    } catch (Exception e) {
+                        e.printStackTrace();
+                    }
+
+                }
+                if (in != null) {
+                    try {
+                        in.close();
+                    } catch (IOException e) {
+                        e.printStackTrace();
+                    }
+                }
+            }
+
+        }
+    }
+}

+ 35 - 0
app/src/main/res/layout/fr_log_manage.xml

@@ -11,6 +11,7 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content">
         <Button
+            android:id="@+id/ButtonExcelOut"
             style="@style/ButtonPrimaryLarge"
 
             app:layout_constraintStart_toStartOf="parent"
@@ -31,11 +32,13 @@
             android:gravity="center_vertical"
             android:layout_height="wrap_content">
             <EditText
+                android:id="@+id/EditSearchKey"
                 android:layout_width="230dp"
                 android:hint="用户名、IP、行为等"
                 style="@style/StyleInputContent"
                 android:layout_height="wrap_content"/>
             <EditText
+                android:id="@+id/EditSearchStartTime"
                 android:layout_width="150dp"
                 android:hint="开始时间"
                 style="@style/StyleInputContent"
@@ -46,11 +49,13 @@
                 android:text="至"
                 />
             <EditText
+                android:id="@+id/EditSearchEndTime"
                 android:layout_width="150dp"
                 android:hint="结束时间"
                 style="@style/StyleInputContent"
                 android:layout_height="wrap_content"/>
             <Button
+                android:id="@+id/ButtonSearch"
                 style="@style/ButtonPrimaryLarge"
                 android:layout_width="wrap_content"
                 android:textAlignment="center"
@@ -60,7 +65,37 @@
         </LinearLayout>
     </androidx.constraintlayout.widget.ConstraintLayout>
 
+    <LinearLayout
+        android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="center_vertical"
+        android:layout_margin="1dp"
+        android:padding="10dp">
+
+        <TextView
+            style="@style/StyleTextContent"
+            android:layout_width="0dp"
+            android:layout_weight="1"
+            android:text="用户名称"
+            />
+        <TextView
+            style="@style/StyleTextContent"
+            android:layout_width="0dp"
+            android:layout_weight="1"
+            android:text="操作类型"
+            />
+        <TextView
+            style="@style/StyleTextContent"
+            android:layout_width="0dp"
+            android:layout_weight="1"
+            android:text="操作时间"
+            />
+
+
+    </LinearLayout>
     <androidx.recyclerview.widget.RecyclerView
+        android:id="@+id/RecyclerLogList"
         android:layout_marginVertical="10dp"
         android:layout_width="match_parent"
         android:layout_height="0dp"

+ 34 - 0
app/src/main/res/layout/item_loglist.xml

@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:orientation="horizontal"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:gravity="center_vertical"
+    android:background="@drawable/border_gary"
+    android:layout_margin="1dp"
+    android:padding="10dp">
+
+    <TextView
+        android:id="@+id/TextName"
+        style="@style/StyleTextContent"
+        android:layout_width="0dp"
+        android:layout_weight="1"
+
+        />
+    <TextView
+        android:id="@+id/TextType"
+        style="@style/StyleTextContent"
+        android:layout_width="0dp"
+        android:layout_weight="1"
+
+        />
+    <TextView
+        android:id="@+id/TextTime"
+        style="@style/StyleTextContent"
+        android:layout_width="0dp"
+        android:layout_weight="1"
+
+        />
+
+</LinearLayout>