From d9ffa50c7e8d6b8c3157690aef8e2a70af1d1695 Mon Sep 17 00:00:00 2001
From: houxiao <houxiao@454eff88-639b-444f-9e54-f578c98de674>
Date: 星期三, 09 八月 2017 13:58:01 +0800
Subject: [PATCH] rtps server (not ok)
---
VisitFace/DemoForBsk/app/src/main/java/cn/com/basic/face/base/MainActivity.java | 301 ++++++++++++++++++++++++++++++++++++++++++-------
1 files changed, 257 insertions(+), 44 deletions(-)
diff --git a/VisitFace/DemoForBsk/app/src/main/java/cn/com/basic/face/base/MainActivity.java b/VisitFace/DemoForBsk/app/src/main/java/cn/com/basic/face/base/MainActivity.java
index 2f84b6d..1a1d374 100644
--- a/VisitFace/DemoForBsk/app/src/main/java/cn/com/basic/face/base/MainActivity.java
+++ b/VisitFace/DemoForBsk/app/src/main/java/cn/com/basic/face/base/MainActivity.java
@@ -3,30 +3,32 @@
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
-import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
+import android.database.sqlite.SQLiteDatabase;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
+import android.graphics.Paint.Align;
import android.graphics.drawable.ColorDrawable;
import android.hardware.Camera;
+import android.hardware.Camera.PreviewCallback;
+import android.hardware.Camera.Size;
+import android.os.Bundle;
import android.os.Handler;
import android.support.design.widget.TabLayout;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
-import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
-import android.view.ViewGroup;
-import android.view.Window;
+import android.view.ViewGroup.LayoutParams;
import android.view.WindowManager;
import android.widget.EditText;
import android.widget.ImageView;
@@ -34,39 +36,62 @@
import android.widget.TextView;
import android.widget.Toast;
+import com.awsle.aibatis.client.SqlMapClientBuilder;
import com.bsk.zhangbo.demoforbsk.R;
import com.luxand.FSDK;
+import com.luxand.FSDK.HTracker;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+import cn.com.basic.face.dialog.AttenderRegisterSelectPhotoDialog;
+import cn.com.basic.face.dialog.AttenderRegisterUploadPhotoDialog;
+import cn.com.basic.face.dialog.VisitorRegisterSelectPhotoDialog;
+import cn.com.basic.face.dialog.VisitorRegisterUploadPhotoDialog;
import cn.com.basic.face.discern.common.CommonVariables;
import cn.com.basic.face.discern.query.item.SurveillanceQueryItem;
-import cn.com.basic.face.fragment.SurveillanceFragment;
-import cn.com.basic.face.service.SurveillanceMng;
-import cn.com.basic.face.util.FileUtil;
-import cn.com.basic.face.util.NativeImg;
-import cn.com.basic.face.util.TabNameUtil;
import cn.com.basic.face.fragment.AttendanceFragment;
import cn.com.basic.face.fragment.CheckInFragment;
import cn.com.basic.face.fragment.DeviceFragment;
import cn.com.basic.face.fragment.PhoneCallFragment;
-import cn.com.basic.face.fragment.RegisterFragment;
+import cn.com.basic.face.fragment.SurveillanceFragment;
import cn.com.basic.face.fragment.VisitFragment;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.InputStream;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantLock;
+import cn.com.basic.face.fragment.VisitorRegisterFragment;
+import cn.com.basic.face.service.DeviceMng;
+import cn.com.basic.face.service.InternetAccessThread;
+import cn.com.basic.face.service.SurveillanceMng;
+import cn.com.basic.face.service.sqlite.SurveillanceDao;
+import cn.com.basic.face.service.sqlite.TestDao;
+import cn.com.basic.face.service.sync.up.BaseSync;
+import cn.com.basic.face.util.FileUtil;
+import cn.com.basic.face.util.NativeImg;
+import cn.com.basic.face.util.TabNameUtil;
public class MainActivity extends BaseActivity implements View.OnClickListener{
private static MainActivity instance;
private static TabLayout mTabLayout;
- private static ViewPager mViewPager;
+ public static ViewPager mViewPager;
+ public SQLiteDatabase db;
+ private int internetAccessible = CommonVariables.InternetAccess.INTERNET_NOT_INITIALIZE;
+
+ public int getInternetAccessible() {
+ return internetAccessible;
+ }
+
+ public void setInternetAccessible(int internetAccessible) {
+ this.internetAccessible = internetAccessible;
+ }
public static void selectPage(int pageNum) {
mTabLayout.getTabAt(pageNum).select();
@@ -83,15 +108,20 @@
mViewPager = (ViewPager) findViewById(R.id.viewpager);
mTabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
instance = this;
+ internetAccessThread.start();
}
+
+ public MyFragmentPagerAdapter myFragmentPagerAdapter;
@Override
public void initData() {
List<String> tabNamesList = Arrays.asList(TabNameUtil.tabNames);
int[] tabIcons = TabNameUtil.tabIcons;
- MyFragmentPagerAdapter myFragmentPagerAdapter = new MyFragmentPagerAdapter(getSupportFragmentManager(),MainActivity.this);
+ myFragmentPagerAdapter = new MyFragmentPagerAdapter(getSupportFragmentManager(),MainActivity.this);
myFragmentPagerAdapter.addFragment(SurveillanceFragment.getInstance(), tabNamesList.get(0),tabIcons[0]);
- myFragmentPagerAdapter.addFragment(RegisterFragment.getInstance(), tabNamesList.get(1),tabIcons[1]);
+ myFragmentPagerAdapter.addFragment(VisitorRegisterFragment.getInstance(), tabNamesList.get(1),tabIcons[1]);
+// myFragmentPagerAdapter.addFragment(AttenderRegisterFragment.getInstance(), tabNamesList.get(1),tabIcons[1]);
+// myFragmentPagerAdapter.addFragment(RegisterFragment.getInstance(), tabNamesList.get(1),tabIcons[1]);
myFragmentPagerAdapter.addFragment(CheckInFragment.getInstance(), tabNamesList.get(2),tabIcons[2]);
myFragmentPagerAdapter.addFragment(PhoneCallFragment.getInstance(), tabNamesList.get(3),tabIcons[3]);
myFragmentPagerAdapter.addFragment(VisitFragment.getInstance(), tabNamesList.get(4),tabIcons[4]);
@@ -109,9 +139,11 @@
tab.getCustomView().setSelected(true);
}
}
- myThread = new MyThread();
- myThread.start();
-
+ if (CommonVariables.ID_CARD) {
+ myThread = new MyThread();
+ myThread.start();
+ }
+// initSqlMap();
}
public static MainActivity getInstance() {
@@ -133,6 +165,12 @@
mFragments.add(fragment);
mFragmentTitles.add(title);
mFragmentIcons.add(drawable);
+ }
+
+ public void setFragment(int index, Fragment fragment) {
+ mFragments.remove(index);
+ mFragments.add(index, fragment);
+ notifyDataSetChanged();
}
@Override
@@ -250,7 +288,58 @@
}
return true;
}
+/////////////////////////////
+ /**
+ * 鍒濆鍖杝qlMap
+ */
+ private void initSqlMap() {
+ Config.sqlMap = SqlMapClientBuilder.buildSqlMapClient(this.getClass().getResourceAsStream("/assets/config/SqlMapConfig.xml")).init(this);
+ }
+ /**
+ * 鍒濆鍖栨暟鎹�
+ */
+ private void initDb() {
+ InputStream dbIn = getClass().getResourceAsStream(Config.DB);
+ FileOutputStream out = null;
+ File z = null;
+ try {
+ byte[] bytes = new byte[1024];
+ z = new File(Config.APPDBPATH);
+ if(!z.exists()){
+ z.mkdirs();
+ }
+ //鍒ゆ柇鏁版嵁搴撴槸鍚﹀瓨鍦�
+ z = new File(Config.APPDB);
+ if (!z.exists()) {
+ out = new FileOutputStream(z);
+ int len = 0;
+ while((len = dbIn.read(bytes))!= -1){
+ out.write(bytes,0,len);
+ }
+ }
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ if (out != null)
+ out.close();
+ if (dbIn != null)
+ dbIn.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ try {
+ //瀵规暟鎹簱鏂囦欢鏉冮檺
+ Runtime.getRuntime().exec("chmod 7755 " + z.toString());
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ //Log.d(Config.APPTAG, "鍒濆鍖栨暟鎹畬鎴愶紒");
+ }
////////////////////////////////////////////////////////
private boolean mIsFailed = false;
@@ -261,22 +350,37 @@
public static float sDensity = 1.0f;
+ InternetAccessThread internetAccessThread = new InternetAccessThread();
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ initDb();
+ db=openOrCreateDatabase("/data/data/com.bsk.zhangbo.demoforbsk/databases/aibatis_test_db", Context.MODE_PRIVATE, null);
+ TestDao.getInstance().test();
+ DeviceMng.getInstance().findDevice();
if (CommonVariables.FACE_SERVER) {
sDensity = getResources().getDisplayMetrics().scaledDensity;
- int res = 0;
+ String s = FileUtil.getStringFromFile("/system/a");
+ int res = FSDK.ActivateLibrary(s);
if (res != FSDK.FSDKE_OK) {
mIsFailed = true;
showErrorAndClose("failed", res);
} else {
FSDK.Initialize();
+
+ // Hide the window title (it is done in manifest too)
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
+ //requestWindowFeature(Window.FEATURE_NO_TITLE);
+
+ // Lock orientation
+ setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+
+ // Camera layer and drawing layer
mDraw = new ProcessImageAndDrawResults(this);
mPreview = new Preview(this, mDraw);
- mDraw.mTracker = new FSDK.HTracker();
+ mDraw.mTracker = new HTracker();
String templatePath = this.getApplicationInfo().dataDir + "/" + database;
if (FSDK.FSDKE_OK != FSDK.LoadTrackerMemoryFromFile(mDraw.mTracker, templatePath)) {
res = FSDK.CreateTracker(mDraw.mTracker);
@@ -284,7 +388,47 @@
showErrorAndClose("Error creating tracker", res);
}
}
+
resetTrackerParameters();
+
+ this.getWindow().setBackgroundDrawable(new ColorDrawable()); //black background
+
+ //setContentView(mPreview); //creates MainActivity contents
+ //addContentView(mDraw, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
+
+
+ // Menu
+// LayoutInflater inflater = (LayoutInflater)this.getSystemService( Context.LAYOUT_INFLATER_SERVICE );
+// View buttons = inflater.inflate(R.layout.bottom_menu, null );
+// buttons.findViewById(R.id.helpButton).setOnClickListener(this);
+// buttons.findViewById(R.id.clearButton).setOnClickListener(this);
+// addContentView(buttons, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
+
+
+// FSDK.Initialize();
+// getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
+// setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+// mDraw = new ProcessImageAndDrawResults(this);
+// mPreview = new Preview(this, mDraw);
+// mDraw.mTracker = new FSDK.HTracker();
+// String templatePath = this.getApplicationInfo().dataDir + "/" + database;
+// if (FSDK.FSDKE_OK != FSDK.LoadTrackerMemoryFromFile(mDraw.mTracker, templatePath)) {
+// res = FSDK.CreateTracker(mDraw.mTracker);
+// if (FSDK.FSDKE_OK != res) {
+// showErrorAndClose("Error creating tracker", res);
+// }
+// }
+// resetTrackerParameters();
+//
+// setContentView(mPreview); //creates MainActivity contents
+// addContentView(mDraw, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
+
+ // Menu
+// LayoutInflater inflater = (LayoutInflater)this.getSystemService( Context.LAYOUT_INFLATER_SERVICE );
+// View buttons = inflater.inflate(R.layout.bottom_menu, null );
+// buttons.findViewById(R.id.helpButton).setOnClickListener(this);
+// buttons.findViewById(R.id.clearButton).setOnClickListener(this);
+// addContentView(buttons, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
}
}
}
@@ -323,6 +467,10 @@
String templatePath = this.getApplicationInfo().dataDir + "/" + database;
FSDK.SaveTrackerMemoryToFile(mDraw.mTracker, templatePath);
}
+ VisitorRegisterUploadPhotoDialog.getInstance().hide();
+ VisitorRegisterSelectPhotoDialog.getInstance().hide();
+ AttenderRegisterSelectPhotoDialog.getInstance().hide();
+ AttenderRegisterUploadPhotoDialog.getInstance().hide();
}
@Override
@@ -381,6 +529,17 @@
mDraw.mStopped = 0;
mDraw.mStopping = 0;
}
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ VisitorRegisterUploadPhotoDialog.getInstance().hide();
+ VisitorRegisterSelectPhotoDialog.getInstance().hide();
+ AttenderRegisterSelectPhotoDialog.getInstance().hide();
+ AttenderRegisterUploadPhotoDialog.getInstance().hide();
+ //Config.sqlMap.getDb().close();
+// db.close();
+ }
}
@@ -389,8 +548,9 @@
public int x1, y1, x2, y2;
}
+// Draw graphics on top of the video
class ProcessImageAndDrawResults extends View {
- public FSDK.HTracker mTracker;
+ public HTracker mTracker;
final int MAX_FACES = 5;
final FaceRectangle[] mFacePositions = new FaceRectangle[MAX_FACES];
@@ -448,12 +608,12 @@
mPaintGreen.setStyle(Paint.Style.FILL);
mPaintGreen.setColor(Color.GREEN);
mPaintGreen.setTextSize(18 * MainActivity.sDensity);
- mPaintGreen.setTextAlign(Paint.Align.CENTER);
+ mPaintGreen.setTextAlign(Align.CENTER);
mPaintBlue = new Paint();
mPaintBlue.setStyle(Paint.Style.FILL);
mPaintBlue.setColor(Color.BLUE);
mPaintBlue.setTextSize(18 * MainActivity.sDensity);
- mPaintBlue.setTextAlign(Paint.Align.CENTER);
+ mPaintBlue.setTextAlign(Align.CENTER);
mPaintBlueTransparent = new Paint();
mPaintBlueTransparent.setStyle(Paint.Style.STROKE);
@@ -461,6 +621,7 @@
mPaintBlueTransparent.setColor(Color.BLUE);
mPaintBlueTransparent.setTextSize(25);
+ //mBitmap = null;
mYUVData = null;
mRGBData = null;
@@ -499,12 +660,29 @@
} else {
FSDK.CopyImage(Image, RotatedImage);
}
+
+// FSDK.TFacePosition fp11 = new FSDK.TFacePosition();
+// FSDK.DetectFace(RotatedImage, fp11);
+ FSDK.FSDK_FaceTemplate face = new FSDK.FSDK_FaceTemplate();
+ int result = FSDK.GetFaceTemplate(RotatedImage, face);
+ if (result == FSDK.FSDKE_OK) {
+
+ }
+
+ String surveillancePhotoName11 = "/storage/sdcard1/"+System.currentTimeMillis()+".jpg";
+ FSDK.SaveImageToFile(Image, surveillancePhotoName11);
+
+
FSDK.FreeImage(Image);
long IDs[] = new long[MAX_FACES];
long face_count[] = new long[1];
+
+
FSDK.FeedFrame(mTracker, 0, RotatedImage, face_count, IDs);
+
+ System.out.println("face_count="+face_count[0]+", mImageWidth="+mImageWidth+", mImageHeight="+mImageHeight);
faceLock.lock();
@@ -569,10 +747,15 @@
surveillanceQueryItem.setBitmap(bmp);
surveillanceQueryItem.setWidth("100");
surveillanceQueryItem.setHeight("100");
+ surveillanceQueryItem.setRegisterOrCheckIn(CommonVariables.Surveillance.OFFLINE_SURVEILLANCE_ITEM_TYPE);
+ surveillanceQueryItem.setCreateTime(new Date());
bitmapList.add(surveillanceQueryItem);
-
- SurveillanceMng.getInstance().addBitmap(bitmapList, nativeImg.image, 0, CommonVariables.Camera.IN+"", nativeImg.image);
+ if (BaseApplication.getInstance().networkAvailable()) {
+ SurveillanceMng.getInstance().addBitmap(bitmapList, nativeImg.image, 0, CommonVariables.Camera.IN+"", nativeImg.image);
+ } else {
+ SurveillanceMng.getInstance().addOfflineBitmap(bitmapList, nativeImg.image, CommonVariables.Camera.IN+"");
+ }
}
}
@@ -599,7 +782,7 @@
}
super.onDraw(canvas);
- }
+ } // end onDraw method
@Override
@@ -614,6 +797,10 @@
long IDs[] = new long[MAX_FACES];
for (int i=0; i<MAX_FACES; ++i) {
rects[i] = new FaceRectangle();
+ if(rects[i] == null) {
+ faceLock.unlock();
+ return true;
+ }
rects[i].x1 = mFacePositions[i].x1;
rects[i].y1 = mFacePositions[i].y1;
rects[i].x2 = mFacePositions[i].x2;
@@ -628,6 +815,7 @@
mTouchedIndex = i;
+ // requesting name on tapping the face
final EditText input = new EditText(mContext);
AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
builder.setMessage("Enter person's name" )
@@ -645,7 +833,7 @@
mTouchedIndex = -1;
}
})
- .setCancelable(false)
+ .setCancelable(false) // cancel with button only
.show();
break;
@@ -682,8 +870,12 @@
}
}
}
-}
+} // end of ProcessImageAndDrawResults class
+
+
+
+// Show video from camera and pass frames to ProcessImageAndDraw class
class Preview extends SurfaceView implements SurfaceHolder.Callback {
Context mContext;
SurfaceHolder mHolder;
@@ -696,19 +888,23 @@
mContext = context;
mDraw = draw;
+ //Install a SurfaceHolder.Callback so we get notified when the underlying surface is created and destroyed.
mHolder = getHolder();
mHolder.addCallback(this);
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
+ //SurfaceView callback
public void surfaceCreated(SurfaceHolder holder) {
mFinished = false;
+ // Find the ID of the camera
int cameraId = 0;
boolean frontCameraFound = false;
Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
for (int i = 0; i < Camera.getNumberOfCameras(); i++) {
Camera.getCameraInfo(i, cameraInfo);
+ //if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK)
if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
cameraId = i;
frontCameraFound = true;
@@ -724,12 +920,14 @@
try {
mCamera.setPreviewDisplay(holder);
- mCamera.setPreviewCallback(new Camera.PreviewCallback() {
+ // Preview callback used whenever new viewfinder frame is available
+ mCamera.setPreviewCallback(new PreviewCallback() {
public void onPreviewFrame(byte[] data, Camera camera) {
if ( (mDraw == null) || mFinished )
return;
if (mDraw.mYUVData == null) {
+ // Initialize the draw-on-top companion
Camera.Parameters params = camera.getParameters();
mDraw.mImageWidth = params.getPreviewSize().width;
mDraw.mImageHeight = params.getPreviewSize().height;
@@ -737,6 +935,7 @@
mDraw.mYUVData = new byte[data.length];
}
+ // Pass YUV data to draw-on-top companion
System.arraycopy(data, 0, mDraw.mYUVData, 0, data.length);
mDraw.invalidate();
}
@@ -759,7 +958,11 @@
}
}
+ //SurfaceView callback
public void surfaceDestroyed(SurfaceHolder holder) {
+ // Surface will be destroyed when we return, so stop the preview.
+ // Because the CameraDevice object is not a shared resource, it's very
+ // important to release it when the activity is paused.
mFinished = true;
if (mCamera != null) {
mCamera.setPreviewCallback(null);
@@ -769,23 +972,32 @@
}
}
+ //SurfaceView callback, configuring camera
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
if (mCamera == null) return;
+ // Now that the size is known, set up the camera parameters and begin
+ // the preview.
Camera.Parameters parameters = mCamera.getParameters();
+ //Keep uncommented to work correctly on phones:
+ //This is an undocumented although widely known feature
+ /**/
if (this.getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE) {
parameters.set("orientation", "portrait");
- mCamera.setDisplayOrientation(90);
+ mCamera.setDisplayOrientation(90); // For Android 2.2 and above
mDraw.rotated = true;
} else {
parameters.set("orientation", "landscape");
- mCamera.setDisplayOrientation(0);
+ mCamera.setDisplayOrientation(0); // For Android 2.2 and above
}
- List<Camera.Size> supportedSizes = parameters.getSupportedPreviewSizes();
+ /**/
+
+ // choose preview size closer to 640x480 for optimal performance
+ List<Size> supportedSizes = parameters.getSupportedPreviewSizes();
int width = 0;
int height = 0;
- for (Camera.Size s: supportedSizes) {
+ for (Size s: supportedSizes) {
if ((width - 640)*(width - 640) + (height - 480)*(height - 480) >
(s.width - 640)*(s.width - 640) + (s.height - 480)*(s.height - 480)) {
width = s.width;
@@ -793,10 +1005,12 @@
}
}
+ //try to set preferred parameters
try {
if (width*height > 0) {
parameters.setPreviewSize(width, height);
}
+ //parameters.setPreviewFrameRate(10);
parameters.setSceneMode(Camera.Parameters.SCENE_MODE_PORTRAIT);
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
mCamera.setParameters(parameters);
@@ -810,7 +1024,7 @@
}
private void makeResizeForCameraAspect(float cameraAspectRatio){
- ViewGroup.LayoutParams layoutParams = this.getLayoutParams();
+ LayoutParams layoutParams = this.getLayoutParams();
int matchParentWidth = this.getWidth();
int newHeight = (int)(matchParentWidth/cameraAspectRatio);
if (newHeight != layoutParams.height) {
@@ -820,5 +1034,4 @@
this.invalidate();
}
}
-}
-
+} // end of Preview class
--
Gitblit v1.8.0