Commit d4472e97 authored by YONG-LIN SU's avatar YONG-LIN SU

1. 刪除垃圾

2. 新增藍芽連線功能
3. 新增藍芽列印功能
4. 調整資料庫欄位
parent 24da35c7
......@@ -12,6 +12,6 @@
</deviceKey>
</Target>
</runningDeviceTargetSelectedWithDropDown>
<timeTargetWasSelectedWithDropDown value="2022-08-02T03:29:02.338878300Z" />
<timeTargetWasSelectedWithDropDown value="2022-08-03T07:12:30.738727600Z" />
</component>
</project>
\ No newline at end of file
......@@ -32,10 +32,13 @@
<entry key="..\:/Users/pp931/Desktop/RD/Projects/NewParkApp/app/src/main/res/layout/activity_t03_img_viewer.xml" value="0.34375" />
<entry key="..\:/Users/pp931/Desktop/RD/Projects/NewParkApp/app/src/main/res/layout/activity_t03_list_files.xml" value="0.34375" />
<entry key="..\:/Users/pp931/Desktop/RD/Projects/NewParkApp/app/src/main/res/layout/activity_t03_total.xml" value="0.34375" />
<entry key="..\:/Users/pp931/Desktop/RD/Projects/NewParkApp/app/src/main/res/layout/alert_dialog_progress_bar.xml" value="0.29957264957264956" />
<entry key="..\:/Users/pp931/Desktop/RD/Projects/NewParkApp/app/src/main/res/layout/alert_dialog_t02_device_list.xml" value="0.34375" />
<entry key="..\:/Users/pp931/Desktop/RD/Projects/NewParkApp/app/src/main/res/layout/alert_dialog_t03_cumulative_time.xml" value="0.34375" />
<entry key="..\:/Users/pp931/Desktop/RD/Projects/NewParkApp/app/src/main/res/layout/card_view_image_plate_number_item.xml" value="0.34375" />
<entry key="..\:/Users/pp931/Desktop/RD/Projects/NewParkApp/app/src/main/res/layout/card_view_list_files_item.xml" value="0.34375" />
<entry key="..\:/Users/pp931/Desktop/RD/Projects/NewParkApp/app/src/main/res/layout/content_t02_select_road.xml" value="0.1875" />
<entry key="..\:/Users/pp931/Desktop/RD/Projects/NewParkApp/app/src/main/res/layout/list_view_device_name_item.xml" value="0.34375" />
<entry key="..\:/Users/pp931/Desktop/RD/Projects/NewParkApp/app/src/main/res/layout/recycler_view_bulletin_board_item.xml" value="0.34375" />
<entry key="..\:/Users/pp931/Desktop/RD/Projects/NewParkApp/app/src/main/res/layout/recycler_view_db_table_item.xml" value="0.33" />
<entry key="..\:/Users/pp931/Desktop/RD/Projects/NewParkApp/app/src/main/res/layout/recycler_view_item.xml" value="0.34375" />
......
......@@ -8,7 +8,7 @@ android {
defaultConfig {
applicationId "ecom.android.newparkapp"
minSdk 27
targetSdk 32
targetSdk 30
versionCode 1
versionName "Dev Ver.0.3.0"
......@@ -45,6 +45,7 @@ dependencies {
implementation 'androidx.preference:preference:1.1.1'
implementation 'androidx.navigation:navigation-fragment:2.5.0'
implementation 'androidx.navigation:navigation-ui:2.5.0'
implementation 'com.google.android.gms:play-services-location:20.0.0'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
......
......@@ -9,7 +9,6 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import ecom.android.newparkapp.database.CaseDatabase;
import ecom.android.newparkapp.entity.Case;
public class CaseDaoTest {
......
......@@ -17,7 +17,6 @@ import org.junit.runner.RunWith;
import java.util.List;
import ecom.android.newparkapp.database.UserDatabase;
import ecom.android.newparkapp.entity.User;
@RunWith(AndroidJUnit4.class)
......
......@@ -4,6 +4,8 @@
package="ecom.android.newparkapp">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
......
package ecom.android.newparkapp;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import androidx.annotation.NonNull;
public class BlueToothComm {
private static final byte NUL = 0x00;//#Line feed
private static final byte LF = 0x0A;//#Line feed
private static final byte FF = 0x0C;//#Line feed
private static final byte SUB = 0x1A;
private static final byte ESC = 0x1B;
private static final byte GS = 0x1D;
private static final byte Z = 0x5A;
private static final byte two = 0x32;
private static final byte three = 0x33;
private static final byte at = 0x40;
private static final byte[] ESC_2 = {ESC, two};//Set default line spacing
/**
* 相關指令請參考P58CII指令集
*/
public static void InitPrinter(@NonNull OutputStream out) {
byte[] bInit = {ESC, at};//ESC @ #Clear data in buffer and reset modes
//byte[] font = {ESC, 0x4d,0x04};
try {
out.write(bInit);
//out.write(font);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void ToBlackMark(@NonNull OutputStream out) {
byte[] test = {SUB, FF, 0x01, 0x03, 0x28, NUL};
try {
out.write(test);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void PrintChangeLine(@NonNull OutputStream out, int lines, int dotPitch) {
byte[] ESC_3_n = {ESC, three, (byte) dotPitch};//Set default line spacing
try {
out.write(ESC_3_n);
for (int i = 0; i < lines; i++) {
out.write(LF);
}
out.write(ESC_2);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void PrintString(@NonNull OutputStream out, String sCode, int dotPitch, byte[] widthCode) {
byte[] ESC_3_n = {ESC, three, (byte) dotPitch};//Set line spacing
try {
if (widthCode != null) {
out.write(widthCode);
}
out.write(ESC_3_n);
out.write(sCode.getBytes("BIG5"));
out.write(LF);
if (widthCode != null) {
widthCode[2] = 0x00;
out.write(widthCode);
}
out.write(ESC_2);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void PrintString(@NonNull OutputStream out, String sCode, int dotPitch, boolean isDoubleW) {
byte[] ESC_3_n = {ESC, three, (byte) dotPitch};//Set line spacing
byte[] doubleWidth = {ESC, 0x21, 0x20};//ESC ! n
byte[] setEmphasized = {ESC, 0x45, 1};//ESC E n 強調字體
try {
if (isDoubleW) {
out.write(doubleWidth);
}
out.write(setEmphasized);
out.write(ESC_3_n);
out.write(sCode.getBytes("BIG5"));
out.write(LF);
if (isDoubleW) {
doubleWidth[2] = 0x00;
out.write(doubleWidth);
}
out.write(ESC_2);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void PrintBarCode(@NonNull OutputStream out, @NonNull String barCode, boolean bSecond) {
byte[] bCodeHRI = {GS, 0x48, 0x02};//GS H n #Select HRI character print position
byte[] bCodeWidth = {GS, 0x77, 2};//GS w n #Set bar code horizontal size
byte[] bCodeHigh = {GS, 0x68, 64};//GS h n #Set bar code height
byte[] bCodeType = {GS, 0x6B, 0x45};//GS k m … #Print bar code
try {
out.write(bCodeHRI);
out.write(bCodeHigh);
out.write(bCodeWidth);
out.write(bCodeType);
out.write(barCode.length());
out.write(barCode.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
public static void PrintBarCodeTest(@NonNull OutputStream out, @NonNull String barCode, boolean bSecond) {
byte[] A = {0x1b, 0x40, 0x1d, 0x48, 0x00, 0x1d, 0x68, 64, 0x1d, 0x77, 4};
byte[] B = {0x30, 0x0D, 0x0A};
byte[] C = {0x1d, 0x6b, 0x04, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 31};
try {
out.write(A);
out.write(B);
out.write(C);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void TestPrintStringUTF8(@NonNull OutputStream out, String sCode, int dotPitch, boolean isDoubleW) {
byte[] ESC_3_n = {ESC, three, (byte) dotPitch};//Set line spacing
byte[] doubleWidth = {ESC, 0x21, 0x20};//ESC ! n
byte[] setEmphasized = {ESC, 0x45, 1};//ESC E n
byte[] changeEncode = {ESC, 0x39, 1};//ESC @ #Clear data in buffer and reset modes
try {
if (isDoubleW) {
out.write(doubleWidth);
}
out.write(ESC_3_n);
out.write(setEmphasized);
out.write(changeEncode);
out.write(sCode.getBytes(StandardCharsets.UTF_8));
out.write(LF);
if (isDoubleW) {
doubleWidth[2] = 0x00;
out.write(doubleWidth);
}
out.write(ESC_2);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void TestPrintBarCodeHigh(@NonNull OutputStream out, @NonNull String barCode, boolean bSecond) {
byte[] bCodeHRI = {GS, 0x48, 0x02};//GS H n #Select HRI character print position
byte[] bCodeWidth = {GS, 0x77, 2};//GS w n #Set bar code horizontal size
byte[] bCodeHigh = {GS, 0x68, (byte) 128};//GS h n #Set bar code height
byte[] bCodeType = {GS, 0x6B, 0x45};//GS k m … #Print bar code
try {
out.write(bCodeHRI);
out.write(bCodeHigh);
out.write(bCodeWidth);
out.write(bCodeType);
out.write(barCode.length());
out.write(barCode.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
public static void PrintQRCodeTest(@NonNull OutputStream out, @NonNull String barCode) {
try {
int p = barCode.getBytes("BIG5").length;
int pL = 0, pH = 0;
//k(字串字元數量)=(pL+pH×256)-3
pL = (p + 3) % 256;
pH = (p + 3) / 256;
byte[] initPrinter = {ESC, at};
// GS ( k pL pH cn fn n
byte[] initQRCode = {GS, 0x28, 0x6b, 0x03, 0x00, 0x31, 0x43, 0x03};//设置QR码的模块类型
byte[] QRCodeECC = {GS, 0x28, 0x6b, 0x03, 0x00, 0x31, 0x45, 0x31};//设置QR码的错误校正水平误差
byte[] QRCodeBuff = {GS, 0x28, 0x6b, (byte) pL, (byte) pH, 0x31, 0x50, 0x30};//存储QR码的数据到QR码缓冲区
byte[] cc = {ESC, 0x61, 0x01};//置中對齊
byte[] initQRCode2 = {GS, 0x28, 0x6b, 0x03, 0x00, 0x31, 0x52, 0x30};//设置QR码的图形信息
byte[] printQRCode = {GS, 0x28, 0x6b, 0x03, 0x00, 0x31, 0x51, 0x30};//打印QR码
//System.err.println("asdasd "+"台中市大里區立新二街89號".getBytes("BIG5").length);
out.write(initPrinter);
out.write(initQRCode);
out.write(QRCodeECC);
out.write(QRCodeBuff);
out.write(barCode.getBytes("BIG5"));
out.write(cc);
out.write(initQRCode2);
out.write(printQRCode);
out.write(LF);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void InitPrinterOld(@NonNull OutputStream outPut) {
byte[] bInit = {ESC, at};
try {
outPut.write(bInit);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void PrintTitleOld(@NonNull OutputStream outPut, @NonNull String szTitle) {
byte[] bDbTypeOn = {ESC, Z, 1};
byte[] bDbTypeOff = {ESC, Z, 0};
try {
outPut.write(bDbTypeOn);
outPut.write(szTitle.getBytes("BIG5"));
outPut.write(LF);
outPut.write(bDbTypeOff);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void PrintBarCodeOld(@NonNull OutputStream outPut, String szBarcode, boolean bSecond) {
byte[] bStringW = {ESC, Z, 1};
byte[] bCodeHRI = {GS, 0x48, 0, LF};
byte[] bCodeWidth = {GS, 0x77, 1, LF};
byte[] bCodeHeigh = {GS, 0x68, 64, LF};
byte[] bCodeType = {GS, 0x6b, 0x45}; //code39
try {
outPut.write(bStringW);
if (bSecond) {
outPut.write(bCodeHRI);
}
outPut.write(bCodeHeigh);
outPut.write(bCodeWidth);
outPut.write(bCodeType);
outPut.write(szBarcode.length());
outPut.write(szBarcode.getBytes());
outPut.write(LF);
if (bSecond) {
int nLen = szBarcode.length();
String szTemp = szBarcode.substring(0, nLen - 2) + "-" + szBarcode.substring(nLen - 2, nLen);
outPut.write(szTemp.getBytes());
outPut.write(LF);
bCodeHRI[2] = 2;
outPut.write(bCodeHRI);
}
bStringW[2] = 0;
outPut.write(bStringW);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void PrintStringOld(@NonNull OutputStream outPut, @NonNull String szCode) {
byte[] bSetDB = {ESC, Z, 1};
try {
outPut.write(bSetDB);
outPut.write(szCode.getBytes("BIG5"));
outPut.write(LF);
bSetDB[2] = 0;
outPut.write(bSetDB);
outPut.write(LF);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void PrintBlankLineOld(OutputStream outPut, int nLines) {
try {
for (int i = 0; i < nLines; ++i) {
outPut.write(LF);
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void ToBlackMarkOld(@NonNull OutputStream outPut) {
byte[] bBlackMark = {FF};
try {
outPut.write(bBlackMark);
} catch (IOException e) {
e.printStackTrace();
}
}
}
\ No newline at end of file
package ecom.android.newparkapp.adapter;
import android.graphics.Color;
import android.widget.Button;
import android.widget.TextView;
......@@ -9,6 +10,7 @@ import java.text.SimpleDateFormat;
import java.util.Date;
import ecom.android.newparkapp.entity.Case;
import ecom.android.newparkapp.viewModel.BlueToothViewModel;
public class ConvertBindingAdapter {
......@@ -19,4 +21,31 @@ public class ConvertBindingAdapter {
textView.setText(dateString);
}
@BindingAdapter("blueToothStatus2String")
public static void BlueToothStatus2String(TextView textView, BlueToothViewModel.BlueToothStatus blueToothStatus){
String message = "";
int colorCode = Color.BLACK;
switch (blueToothStatus){
case INIT:
message = "尚未連接";
break;
case CONNECTING:
message = "連接中";
colorCode = Color.BLUE;
break;
case CONNECT_FAILED:
message = "連線失敗";
colorCode = Color.RED;
break;
case CONNECT_SUCCESS:
message = "連線正常";
colorCode = Color.GREEN;
break;
default:
return;
}
textView.setText(message);
textView.setTextColor(colorCode);
}
}
\ No newline at end of file
package ecom.android.newparkapp.entity;
package ecom.android.newparkapp.converter;
import android.location.Location;
......
package ecom.android.newparkapp.converter;
import static ecom.android.newparkapp.entity.Space.SpaceType.UNSET_SPACE;
import androidx.room.ProvidedTypeConverter;
import androidx.room.TypeConverter;
import ecom.android.newparkapp.entity.Space.SpaceType;
@ProvidedTypeConverter
public class SpaceTypeConverter {
@TypeConverter
public static SpaceType toSpaceType(int value) {
return value > SpaceType.values().length ? UNSET_SPACE:SpaceType.values()[value];
}
@TypeConverter
public static int fromSpaceType(SpaceType spaceType){
return spaceType.ordinal();
}
}
package ecom.android.newparkapp.entity;
package ecom.android.newparkapp.converter;
import androidx.room.TypeConverter;
......
package ecom.android.newparkapp.dao;
import androidx.lifecycle.LiveData;
import androidx.room.Dao;
import androidx.room.Delete;
import androidx.room.Insert;
import androidx.room.Query;
import java.util.List;
import ecom.android.newparkapp.entity.SpaceType;
@Dao
public interface SpaceTypeDao {
@Query("SELECT * FROM spacetype")
List<SpaceType> getAll();
@Query("SELECT * FROM spacetype")
LiveData<List<SpaceType>> getAllLiveData();
@Query("SELECT * FROM spacetype WHERE id IN (:Ids)")
List<SpaceType> loadAllByIds(int[] Ids);
@Query("SELECT * FROM spacetype WHERE name LIKE :first LIMIT 1")
SpaceType findByName(String first);
@Insert
void insertAll(SpaceType... spaceTypes);
@Delete
void delete(SpaceType spaceType);
@Query("DELETE FROM spacetype")
void deleteAll();
}
package ecom.android.newparkapp.database;
import android.content.Context;
import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;
import androidx.room.TypeConverters;
import ecom.android.newparkapp.dao.CaseDao;
import ecom.android.newparkapp.entity.Case;
import ecom.android.newparkapp.entity.CasePhoto;
import ecom.android.newparkapp.entity.LocationConverter;
import ecom.android.newparkapp.entity.TimestampConverter;
import ecom.android.newparkapp.R;
@Database(entities = {Case.class, CasePhoto.class}, version = 1)
@TypeConverters({LocationConverter.class, TimestampConverter.class})
public abstract class CaseDatabase extends RoomDatabase {
public abstract CaseDao caseDao();
private static CaseDatabase instance;
public static CaseDatabase getInstance(Context context) {
if (instance == null){
RoomDatabase.Builder<CaseDatabase> builder = Room.databaseBuilder(context.getApplicationContext(), CaseDatabase.class, context.getString(R.string.info_db));
instance = builder.build();
}
return instance;
}
}
......@@ -8,13 +8,13 @@ import androidx.room.RoomDatabase;
import androidx.room.TypeConverters;
import ecom.android.newparkapp.R;
import ecom.android.newparkapp.converter.SpaceTypeConverter;
import ecom.android.newparkapp.dao.CaseDao;
import ecom.android.newparkapp.dao.CasePhotoDao;
import ecom.android.newparkapp.dao.RoadDao;
import ecom.android.newparkapp.dao.SpaceDao;
import ecom.android.newparkapp.dao.SpaceRateDao;
import ecom.android.newparkapp.dao.SpaceStatusDao;
import ecom.android.newparkapp.dao.SpaceTypeDao;
import ecom.android.newparkapp.dao.UserDao;
import ecom.android.newparkapp.dao.UserPermissionDao;
import ecom.android.newparkapp.dao.VehicleBrandDao;
......@@ -22,13 +22,12 @@ import ecom.android.newparkapp.dao.VehicleColorDao;
import ecom.android.newparkapp.dao.VehicleTypeDao;
import ecom.android.newparkapp.entity.Case;
import ecom.android.newparkapp.entity.CasePhoto;
import ecom.android.newparkapp.entity.LocationConverter;
import ecom.android.newparkapp.converter.LocationConverter;
import ecom.android.newparkapp.entity.Road;
import ecom.android.newparkapp.entity.Space;
import ecom.android.newparkapp.entity.SpaceRate;
import ecom.android.newparkapp.entity.SpaceStatus;
import ecom.android.newparkapp.entity.SpaceType;
import ecom.android.newparkapp.entity.TimestampConverter;
import ecom.android.newparkapp.converter.TimestampConverter;
import ecom.android.newparkapp.entity.User;
import ecom.android.newparkapp.entity.UserPermission;
import ecom.android.newparkapp.entity.VehicleBrand;
......@@ -36,9 +35,9 @@ import ecom.android.newparkapp.entity.VehicleColor;
import ecom.android.newparkapp.entity.VehicleType;
// DataBase 一個資料庫一個,並涵蓋多個資料表
@Database(entities = {Case.class, CasePhoto.class, Road.class, Space.class, SpaceRate.class, SpaceStatus.class, SpaceType.class, User.class, UserPermission.class, VehicleBrand.class, VehicleColor.class, VehicleType.class},
@Database(entities = {Case.class, CasePhoto.class, Road.class, Space.class, SpaceRate.class, SpaceStatus.class, User.class, UserPermission.class, VehicleBrand.class, VehicleColor.class, VehicleType.class},
version = 1)
@TypeConverters({LocationConverter.class, TimestampConverter.class})
@TypeConverters({LocationConverter.class, TimestampConverter.class, SpaceTypeConverter.class})
public abstract class InfoDatabase extends RoomDatabase {
public abstract CaseDao caseDao();
public abstract CasePhotoDao casePhotoDao();
......@@ -46,7 +45,6 @@ public abstract class InfoDatabase extends RoomDatabase {
public abstract SpaceDao spaceDao();
public abstract SpaceRateDao spaceRateDao();
public abstract SpaceStatusDao spaceStatusDao();
public abstract SpaceTypeDao spaceTypeDao();
public abstract UserDao userDao();
public abstract VehicleBrandDao vehicleBrandDao();
public abstract VehicleColorDao vehicleColorDao();
......
package ecom.android.newparkapp.database;
import android.content.Context;
import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;
import ecom.android.newparkapp.dao.RoadDao;
import ecom.android.newparkapp.entity.Road;
import ecom.android.newparkapp.R;
@Database(entities = {Road.class},version = 1)
public abstract class RoadDatabase extends RoomDatabase{
public abstract RoadDao roadDao();
private static RoadDatabase instance;
public static RoadDatabase getInstance(Context context) {
if (instance == null){
RoomDatabase.Builder<RoadDatabase> builder = Room.databaseBuilder(context.getApplicationContext(), RoadDatabase.class, context.getString(R.string.info_db));
instance = builder.build();
}
return instance;
}
}
package ecom.android.newparkapp.database;
import android.content.Context;
import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;
import ecom.android.newparkapp.dao.SpaceDao;
import ecom.android.newparkapp.entity.Space;
import ecom.android.newparkapp.R;
@Database(entities = {Space.class}, version = 1)
public abstract class SpaceDatabase extends RoomDatabase {
public abstract SpaceDao spaceDao();
private static SpaceDatabase instance;
public static SpaceDatabase getInstance(Context context) {
if (instance == null){
RoomDatabase.Builder<SpaceDatabase> builder = Room.databaseBuilder(context.getApplicationContext(), SpaceDatabase.class, context.getString(R.string.info_db));
instance = builder.build();
}
return instance;
}
}
package ecom.android.newparkapp.database;
import android.content.Context;
import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;
import ecom.android.newparkapp.dao.SpaceRateDao;
import ecom.android.newparkapp.entity.SpaceRate;
import ecom.android.newparkapp.R;
@Database(entities = {SpaceRate.class}, version = 1)
public abstract class SpaceRateDatabase extends RoomDatabase {
public abstract SpaceRateDao spaceRateDao();
private static SpaceRateDatabase instance;
public static SpaceRateDatabase getInstance(Context context) {
if (instance == null){
RoomDatabase.Builder<SpaceRateDatabase> builder = Room.databaseBuilder(context.getApplicationContext(), SpaceRateDatabase.class, context.getString(R.string.info_db));
instance = builder.build();
}
return instance;
}
}
package ecom.android.newparkapp.database;
import android.content.Context;
import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;
import ecom.android.newparkapp.dao.SpaceStatusDao;
import ecom.android.newparkapp.entity.SpaceStatus;
import ecom.android.newparkapp.R;
@Database(entities = {SpaceStatus.class}, version = 1)
public abstract class SpaceStatusDatabase extends RoomDatabase {
public abstract SpaceStatusDao spaceStatusDao();
private static SpaceStatusDatabase instance;
public static SpaceStatusDatabase getInstance(Context context) {
if (instance == null){
RoomDatabase.Builder<SpaceStatusDatabase> builder = Room.databaseBuilder(context.getApplicationContext(), SpaceStatusDatabase.class, context.getString(R.string.info_db));
instance = builder.build();
}
return instance;
}
}
package ecom.android.newparkapp.database;
import android.content.Context;
import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;
import ecom.android.newparkapp.dao.SpaceTypeDao;
import ecom.android.newparkapp.entity.SpaceType;
import ecom.android.newparkapp.R;
@Database(entities = {SpaceType.class}, version = 1)
public abstract class SpaceTypeDatabase extends RoomDatabase {
public abstract SpaceTypeDao spaceTypeDao();
private static SpaceTypeDatabase instance;
public static SpaceTypeDatabase getInstance(Context context) {
if (instance == null){
RoomDatabase.Builder<SpaceTypeDatabase> builder = Room.databaseBuilder(context.getApplicationContext(), SpaceTypeDatabase.class, context.getString(R.string.info_db));
instance = builder.build();
}
return instance;
}
}
package ecom.android.newparkapp.database;
import android.content.Context;
import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;
import ecom.android.newparkapp.dao.UserDao;
import ecom.android.newparkapp.entity.User;
import ecom.android.newparkapp.R;
@Database(entities = {User.class}, version = 1)
public abstract class UserDatabase extends RoomDatabase {
public abstract UserDao userDao();
private static UserDatabase instance;
public static UserDatabase getInstance(Context context) {
if (instance == null){
RoomDatabase.Builder<UserDatabase> builder = Room.databaseBuilder(context.getApplicationContext(), UserDatabase.class, context.getString(R.string.info_db));
instance = builder.build();
}
return instance;
}
}
package ecom.android.newparkapp.database;
import android.content.Context;
import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;
import ecom.android.newparkapp.dao.VehicleBrandDao;
import ecom.android.newparkapp.entity.VehicleBrand;
import ecom.android.newparkapp.R;
@Database(entities = {VehicleBrand.class}, version = 1)
public abstract class VehicleBrandDatabase extends RoomDatabase {
public abstract VehicleBrandDao vehicleBrandDao();
private static VehicleBrandDatabase instance;
public static VehicleBrandDatabase getInstance(Context context) {
if (instance == null){
RoomDatabase.Builder<VehicleBrandDatabase> builder = Room.databaseBuilder(context.getApplicationContext(), VehicleBrandDatabase.class, context.getString(R.string.info_db));
instance = builder.build();
}
return instance;
}
}
package ecom.android.newparkapp.database;
import android.content.Context;
import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;
import ecom.android.newparkapp.dao.VehicleColorDao;
import ecom.android.newparkapp.entity.VehicleColor;
import ecom.android.newparkapp.R;
@Database(entities = {VehicleColor.class}, version = 1)
public abstract class VehicleColorDatabase extends RoomDatabase {
public abstract VehicleColorDao vehicleColorDao();
private static VehicleColorDatabase instance;
public static VehicleColorDatabase getInstance(Context context) {
if (instance == null){
RoomDatabase.Builder<VehicleColorDatabase> builder = Room.databaseBuilder(context.getApplicationContext(), VehicleColorDatabase.class, context.getString(R.string.info_db));
instance = builder.build();
}
return instance;
}
}
package ecom.android.newparkapp.database;
import android.content.Context;
import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;
import ecom.android.newparkapp.dao.VehicleTypeDao;
import ecom.android.newparkapp.entity.VehicleType;
import ecom.android.newparkapp.R;
@Database(entities = {VehicleType.class}, version = 1)
public abstract class VehicleTypeDatabase extends RoomDatabase {
public abstract VehicleTypeDao vehicleTypeDao();
private static VehicleTypeDatabase instance;
public static VehicleTypeDatabase getInstance(Context context) {
if (instance == null){
RoomDatabase.Builder<VehicleTypeDatabase> builder = Room.databaseBuilder(context.getApplicationContext(), VehicleTypeDatabase.class, context.getString(R.string.info_db));
instance = builder.build();
}
return instance;
}
}
package ecom.android.newparkapp.entity;
import android.bluetooth.BluetoothDevice;
import android.content.pm.PackageManager;
import android.location.Location;
import android.os.Build;
import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
import androidx.lifecycle.MutableLiveData;
import androidx.room.ColumnInfo;
import androidx.room.Embedded;
import androidx.room.Entity;
......@@ -11,8 +16,13 @@ import androidx.room.PrimaryKey;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.Objects;
import ecom.android.newparkapp.printer.CarPrinter_TPB250;
import ecom.android.newparkapp.printer.IBluetoothPrinter;
@Entity
public class Case {
......@@ -68,7 +78,7 @@ public class Case {
public Date finalTime; // 最後一次續單時間
@ColumnInfo(name = "auto_pay")
public Boolean autoPay; // 自動扣款
public Boolean autoPay = false; // 自動扣款
@ColumnInfo(name = "agency")
public int agency;
......@@ -143,9 +153,7 @@ public class Case {
// TODO: 2022/7/29 加入GPS座標功能後,修改該處假資料
//String locationString = String.format("Lat:%f Lon:%f", location.getLatitude(), location.getLongitude()); // Lat:24.1434911 Lon:120.7285887
String locationString = "Lat:24.1434911 Lon:120.7285887";
String locationString = String.format("Lat:%f Lon:%f", location.getLatitude(), location.getLongitude()); // Lat:24.1434911 Lon:120.7285887
return String.format(Locale.TAIWAN, "%s%s;%s;%s;%s;%s;%s;%s;%s;%s;%s;%.1f;%d;%s;%s;%d;\r\n",
getShift(), user.id,
......@@ -164,4 +172,77 @@ public class Case {
billStatus,
photoCount);
}
public Calendar getCaseCalendar() {
Calendar calendar = Calendar.getInstance();
calendar.setTime(this.caseTime);
return calendar;
}
public Calendar getTermCalendar() {
Calendar calendar = Calendar.getInstance();
calendar.setTime(this.terminateDate);
return calendar;
}
/**
* 傳入時間計算是否在開單員工作時段內
* @param mCalendar 時間
*/
public boolean isWorkTime(Calendar mCalendar) {
Calendar endTime = Calendar.getInstance();
Calendar startTime = Calendar.getInstance();
if (isMorningShift()) {//08:00-16:30
startTime.set(Calendar.HOUR_OF_DAY, 8);
startTime.set(Calendar.MINUTE, 0);
startTime.set(Calendar.SECOND, 0);
startTime.set(Calendar.MILLISECOND, 0);
endTime.set(Calendar.HOUR_OF_DAY, 16);
endTime.set(Calendar.MINUTE, 30);
endTime.set(Calendar.SECOND, 0);
endTime.set(Calendar.MILLISECOND, 0);
} else {//13:30-22:00
startTime.set(Calendar.HOUR_OF_DAY, 13);
startTime.set(Calendar.MINUTE, 30);
startTime.set(Calendar.SECOND, 0);
startTime.set(Calendar.MILLISECOND, 0);
endTime.set(Calendar.HOUR_OF_DAY, 22);
endTime.set(Calendar.MINUTE, 0);
endTime.set(Calendar.SECOND, 0);
endTime.set(Calendar.MILLISECOND, 0);
}
return mCalendar.after(startTime) && mCalendar.before(endTime);
}
/**
* @return 是否為早班
*/
private boolean isMorningShift(){
return true;
}
/**
* @return 分級停車金額
*/
public int getPricing() {
return this.space.fee;
}
public IBluetoothPrinter getCasePrinter(String bluetoothDeviceName) {
IBluetoothPrinter printer;
if (Objects.equals(bluetoothDeviceName.toUpperCase().trim(), "P58C")
|| Objects.equals(bluetoothDeviceName.toUpperCase().trim(), "YH-TPB250")) {
printer = new CarPrinter_TPB250();//汽車用TPB250型印表機版面
} else {
// TODO: 2022/8/3 實作 CarPrinter_PK105B 印表機功能
printer = null;
//printer = new CarPrinter_PK105B();//汽車用PK105B型印表機版面
}
// TODO: 2022/8/3 加入 CarPrinter_LK-P34L 印表機功能
return printer;
}
}
......@@ -20,7 +20,7 @@ public class Space implements Parcelable {
@Embedded(prefix = "road_")
public Road road;
@Embedded(prefix = "space_type_")
@ColumnInfo(name = "space_type")
public SpaceType spaceType;
@ColumnInfo(name = "fee")
......@@ -52,7 +52,6 @@ public class Space implements Parcelable {
protected Space(Parcel in) {
id = in.readString();
road = in.readParcelable(Road.class.getClassLoader());
spaceType = in.readParcelable(SpaceType.class.getClassLoader());
fee = in.readInt();
spaceRate = in.readParcelable(SpaceRate.class.getClassLoader());
latitude = in.readFloat();
......@@ -64,7 +63,6 @@ public class Space implements Parcelable {
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(id);
dest.writeParcelable(road, flags);
dest.writeParcelable(spaceType, flags);
dest.writeInt(fee);
dest.writeParcelable(spaceRate, flags);
dest.writeFloat(latitude);
......@@ -90,6 +88,24 @@ public class Space implements Parcelable {
};
public String getCombineInfo() {
return String.format("%s %s %s %s", id, spaceRate.id, spaceType.id, road.name);
return String.format("%s %s %s %s", id, spaceRate.id, spaceType, road.name);
}
/**
* 轉換開單週期單位,由小時轉換為分鐘
*
* @return 開單時間(單位 : 分鐘)
*/
public int getAddMinutes() {
return (int)(60 * this.spaceRate.perHours);
}
public enum SpaceType {
UNSET_SPACE,
MOTO_SPACE,
VEHICLE_SPACE,
HANDICAPPED_SPACE,
FRIENDLY_SPACE
}
}
package ecom.android.newparkapp.entity;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.PrimaryKey;
/**
* 停車格類型資料表
*/
@Entity
public class SpaceType implements Parcelable {
@PrimaryKey
public int id;
@ColumnInfo(name = "name")
public String name;
public SpaceType(int id, String name) {
this.id = id;
this.name = name;
}
protected SpaceType(Parcel in) {
id = in.readInt();
name = in.readString();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(id);
dest.writeString(name);
}
@Override
public int describeContents() {
return 0;
}
public static final Creator<SpaceType> CREATOR = new Creator<SpaceType>() {
@Override
public SpaceType createFromParcel(Parcel in) {
return new SpaceType(in);
}
@Override
public SpaceType[] newArray(int size) {
return new SpaceType[size];
}
};
}
package ecom.android.newparkapp.printer;
import static ecom.android.newparkapp.BlueToothComm.InitPrinter;
import static ecom.android.newparkapp.BlueToothComm.PrintBarCode;
import static ecom.android.newparkapp.BlueToothComm.PrintChangeLine;
import static ecom.android.newparkapp.BlueToothComm.PrintString;
import static ecom.android.newparkapp.BlueToothComm.ToBlackMark;
import static ecom.android.newparkapp.entity.Space.SpaceType.HANDICAPPED_SPACE;
import static ecom.android.newparkapp.printer.IBluetoothPrinter.ResultCode.NOT_CONNECT;
import static ecom.android.newparkapp.printer.IBluetoothPrinter.ResultCode.PRINT_SUCCESS;
import android.bluetooth.BluetoothSocket;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Calendar;
import java.util.Locale;
import ecom.android.newparkapp.Common;
import ecom.android.newparkapp.entity.Case;
/**
* 2021年新型印表機
*/
public class CarPrinter_TPB250 implements IBluetoothPrinter {
@Override
public ResultCode print(BluetoothSocket socketBT, Case thisCase) throws IOException {
OutputStream outPutBT = socketBT.getOutputStream();
boolean b60Min = false;
Calendar nowCalendar;
Case carCase = thisCase;
if (outPutBT == null || !socketBT.isConnected()) {
return NOT_CONNECT;
}
InitPrinter(outPutBT);
PrintChangeLine(outPutBT, 1, 76);
if (!carCase.autoPay) {
//第一段條碼//
PrintBarCode(outPutBT, carCase.billingNumber1, false);
} else {
PrintString(outPutBT, "※本車已約定金融、電信帳戶代繳,請勿重複繳費、並維持正常扣繳;如扣繳失敗請依背面說明補單繳款。", 32, false);
//PrintChangeLine(outPutBT, 1, 24);
}
PrintChangeLine(outPutBT, 1, 24);
//-------------------------------------------
//車牌
PrintString(outPutBT, " " + carCase.plateNumber, 52, true);
//-------------------------------------------
//路段、格號
PrintString(outPutBT, " " + carCase.space.id + " " + carCase.space.road.name, 52, true);
//-------------------------------------------
//日期
PrintString(outPutBT, " " + String.format("%s年%s月%s日",
Common.getDate(carCase.getCaseCalendar(), true).substring(0, 3),
Common.getDate(carCase.getCaseCalendar(), true).substring(3, 5),
Common.getDate(carCase.getCaseCalendar(), true).substring(5, 7)), 40, true);
//-------------------------------------------
//單號2
PrintBarCode(outPutBT, carCase.billingNumber2, false);
PrintChangeLine(outPutBT, 1, 34);
//-------------------------------------------
//繳費期限
PrintString(outPutBT, " " + String.format("%s %s %s",
Common.getDate(carCase.getTermCalendar(), true).substring(0, 3),
Common.getDate(carCase.getTermCalendar(), true).substring(3, 5),
Common.getDate(carCase.getTermCalendar(), true).substring(5, 7)), 90, false);
//-------------------------------------------
nowCalendar = (Calendar) carCase.getCaseCalendar().clone();
int nHour = carCase.getCaseCalendar().get(Calendar.HOUR_OF_DAY);
for (int i = 0; i < 4; i++) {//開單累加最多累積4次
if (!carCase.isWorkTime(nowCalendar)) {//若第i行時間已超過該班次區間,則停止列印(PS.列印長度太短會導致黑點定位查找距離不夠到下一個黑點,利用換行增加)
for (int j = 0; j < 4 - i; j++) {
PrintString(outPutBT, "", 52, false);
}
break;
}
String szList = (0 == i) ?
String.format(Locale.TAIWAN, " %02d:%02d %5d %s", nHour, nowCalendar.get(Calendar.MINUTE), carCase.getPricing(), carCase.user.name) :
String.format(Locale.TAIWAN, " %02d:%02d %5s %s", nHour + (b60Min ? 1 : 0), b60Min ? 0 : (nowCalendar.get(Calendar.MINUTE) + 1), " ", "________");
if (carCase.space.spaceType == HANDICAPPED_SPACE) {//身障車格
if (carCase.vehicleType.id == 12) {//身心障礙(府-2)
if (i == 0) {
szList = String.format(Locale.TAIWAN, " %02d:%02d %5d %s",
nHour,
nowCalendar.get(Calendar.MINUTE),
carCase.getPricing(),
carCase.user.name);
} else if (i == 1) {
szList = String.format(Locale.TAIWAN, " %02d:%02d %5s %s",
nHour + (b60Min ? 1 : 0),
b60Min ? 0 : (nowCalendar.get(Calendar.MINUTE) + 1),
carCase.getPricing(),
"________");
} else {
szList = String.format(Locale.TAIWAN, " %02d:%02d %5s %s",
nHour + (b60Min ? 1 : 0),
b60Min ? 0 : (nowCalendar.get(Calendar.MINUTE) + 1),
" ",
"________");
}
} else if (carCase.vehicleType.id == 13) {//身心障礙(社-4)
szList = (0 == i) ?
String.format(Locale.TAIWAN, " %02d:%02d %5d %s", nHour, nowCalendar.get(Calendar.MINUTE), carCase.getPricing(), carCase.user.name) :
String.format(Locale.TAIWAN, " %02d:%02d %5s %s", nHour + (b60Min ? 1 : 0), b60Min ? 0 : (nowCalendar.get(Calendar.MINUTE) + 1), carCase.getPricing(), "________");
}
}
nowCalendar.add(Calendar.MINUTE, carCase.space.getAddMinutes());
nHour = nowCalendar.get(Calendar.HOUR_OF_DAY);
b60Min = nowCalendar.get(Calendar.MINUTE) == 59;
PrintString(outPutBT, szList, 40, new byte[]{0x1D, 0x21, 0x01});
}
PrintChangeLine(outPutBT, 1, 120);
//PrintString(outPutBT, "※如已有申請停車費自動扣款或行動支付服務,請勿持本單重複繳費。", 26, false);
PrintChangeLine(outPutBT, 2, 80);//減少黑點定位查找距離
//-------------------------------------------
ToBlackMark(outPutBT);
InitPrinter(outPutBT);
return PRINT_SUCCESS;
}
}
package ecom.android.newparkapp.printer;
import android.bluetooth.BluetoothSocket;
import java.io.IOException;
import ecom.android.newparkapp.entity.Case;
/**
* 自訂列印的內容、行數
*/
public interface IBluetoothPrinter {
enum ResultCode {PRINT_SUCCESS, NOT_CONNECT, NOT_WORK_TIME}
ResultCode print(BluetoothSocket socketBT, Case thisCase) throws IOException;
}
......@@ -12,14 +12,12 @@ import ecom.android.newparkapp.dao.RoadDao;
import ecom.android.newparkapp.dao.SpaceDao;
import ecom.android.newparkapp.dao.SpaceRateDao;
import ecom.android.newparkapp.dao.SpaceStatusDao;
import ecom.android.newparkapp.dao.SpaceTypeDao;
import ecom.android.newparkapp.dao.UserDao;
import ecom.android.newparkapp.dao.UserPermissionDao;
import ecom.android.newparkapp.dao.VehicleBrandDao;
import ecom.android.newparkapp.dao.VehicleColorDao;
import ecom.android.newparkapp.dao.VehicleTypeDao;
import ecom.android.newparkapp.database.InfoDatabase;
import ecom.android.newparkapp.entity.Road;
public class InfoRepository {
private InfoDatabase infoDatabase;
......@@ -33,7 +31,6 @@ public class InfoRepository {
public UserDao userDao;
public UserPermissionDao userPermissionDao;
public SpaceTypeDao spaceTypeDao;
public SpaceStatusDao spaceStatusDao;
public SpaceRateDao spaceRateDao;
public RoadDao roadDao;
......@@ -51,7 +48,6 @@ public class InfoRepository {
userDao = infoDatabase.userDao();
userPermissionDao = infoDatabase.userPermissionDao();
spaceTypeDao = infoDatabase.spaceTypeDao();
spaceStatusDao = infoDatabase.spaceStatusDao();
spaceRateDao = infoDatabase.spaceRateDao();
roadDao = infoDatabase.roadDao();
......
......@@ -24,32 +24,31 @@ import java.util.Map;
import ecom.android.newparkapp.R;
import ecom.android.newparkapp.databinding.ActivityMainBinding;
import ecom.android.newparkapp.viewModel.UserViewModel;
import ecom.android.newparkapp.entity.User;
import ecom.android.newparkapp.viewModel.T01SettingViewModel;
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding dataBinding;
private ViewModelProvider viewModelProvider;
private UserViewModel userViewModel;
private T01SettingViewModel t01SettingViewModel;
private ActivityResultLauncher<String[]> requestPermissionLauncher;
private String[] permissions = new String[]{ WRITE_EXTERNAL_STORAGE, READ_EXTERNAL_STORAGE,
ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION, ACCESS_LOCATION_EXTRA_COMMANDS};
private MutableLiveData<Boolean> hasPermissions = new MutableLiveData<>();
private ActivityResultLauncher settingActivityResultLauncher;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
dataBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
viewModelProvider = new ViewModelProvider(this);
userViewModel = viewModelProvider.get(UserViewModel.class);
userViewModel.setCurrentUser();
dataBinding.setUserViewModel(userViewModel);
t01SettingViewModel = viewModelProvider.get(T01SettingViewModel.class);
initLayout();
// 註冊權限請求結果處理
resultLauncherRegister();
// 檢查權限
......@@ -59,6 +58,7 @@ public class MainActivity extends AppCompatActivity {
// 有權限再綁定按鈕功能
hasPermissions.observe(this, has -> {
if (has){
// 註冊事件綁定
eventBinding();
}
});
......@@ -79,6 +79,14 @@ public class MainActivity extends AppCompatActivity {
// 離開按鈕事件註冊
dataBinding.btnExitApp.setOnClickListener(view -> android.os.Process.killProcess(android.os.Process.myPid()));
// 當前使用者設置
User currentUser = t01SettingViewModel.getCurrentUser();
if ( currentUser != null){
dataBinding.tvCurrentUsr.setText(getString(R.string.current_user, currentUser.id + " " + currentUser.name));
}else {
dataBinding.tvCurrentUsr.setText("請先設定當前使用者");
}
}
/**
......@@ -123,6 +131,20 @@ public class MainActivity extends AppCompatActivity {
hasPermissions.setValue(true);
}
);
settingActivityResultLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
// 重新讀取setting
t01SettingViewModel.restorePrefs();
// 當前使用者設置
User currentUser = t01SettingViewModel.getCurrentUser();
if ( currentUser != null){
dataBinding.tvCurrentUsr.setText(getString(R.string.current_user, currentUser.id + " " + currentUser.name));
}else {
dataBinding.tvCurrentUsr.setText("請先設定當前使用者");
}
});
}
/**
......@@ -136,20 +158,20 @@ public class MainActivity extends AppCompatActivity {
private void btnStartOnClicked(){
if (userViewModel.getCurrentUser().getValue() == null){
if (t01SettingViewModel.getCurrentUser() == null || t01SettingViewModel.getCurrentUser().id == 999){
Toast.makeText(this,"請先設定使用者",Toast.LENGTH_LONG).show();
return;
}
Intent intent = new Intent();
intent.setClass(this, T02StartActivity.class);
intent.putExtra("CurrentUser", userViewModel.getCurrentUser().getValue());
intent.putExtra("CurrentUser", t01SettingViewModel.getCurrentUser());
startActivity(intent);
}
private void btnGoSettingOnClicked(){
Intent intent = new Intent();
intent.setClass(this, T01SettingActivity.class);
startActivity(intent);
settingActivityResultLauncher.launch(intent);
}
}
\ No newline at end of file
......@@ -13,9 +13,7 @@ import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.ViewModelProvider;
import androidx.preference.PreferenceFragmentCompat;
import java.util.ArrayList;
import java.util.List;
......@@ -23,9 +21,7 @@ import java.util.List;
import ecom.android.newparkapp.R;
import ecom.android.newparkapp.databinding.ActivityT01SettingLayoutBinding;
import ecom.android.newparkapp.entity.User;
import ecom.android.newparkapp.entity.VehicleType;
import ecom.android.newparkapp.viewModel.T01SettingViewModel;
import ecom.android.newparkapp.viewModel.UserViewModel;
public class T01SettingActivity extends AppCompatActivity {
......@@ -35,7 +31,7 @@ public class T01SettingActivity extends AppCompatActivity {
private T01SettingViewModel t01SettingViewModel;
private ActivityResultLauncher UserResultLauncher;
private List<User> Users;
private UserViewModel userViewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
......@@ -45,7 +41,6 @@ public class T01SettingActivity extends AppCompatActivity {
viewModelProvider = new ViewModelProvider(this);
t01SettingViewModel = viewModelProvider.get(T01SettingViewModel.class);
userViewModel = viewModelProvider.get(UserViewModel.class);
dataBinding.setT01SettingViewModel(t01SettingViewModel);
ActionBar actionBar = getSupportActionBar();
......@@ -63,7 +58,6 @@ public class T01SettingActivity extends AppCompatActivity {
protected void onPause() {
super.onPause();
t01SettingViewModel.savePrefs();
userViewModel.setCurrentUser(t01SettingViewModel.getCurrentUser());
}
// public static class SettingsFragment extends PreferenceFragmentCompat {
// @Override
......
package ecom.android.newparkapp.view;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.FileProvider;
import androidx.databinding.DataBindingUtil;
import androidx.lifecycle.ViewModelProvider;
import static android.Manifest.permission.BLUETOOTH_CONNECT;
import static android.Manifest.permission.BLUETOOTH_SCAN;
import android.bluetooth.BluetoothAdapter;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.DisplayMetrics;
import android.view.LayoutInflater;
import android.view.WindowManager;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import android.widget.Toast;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.FileProvider;
import androidx.databinding.DataBindingUtil;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModelProvider;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Map;
import ecom.android.newparkapp.R;
import ecom.android.newparkapp.databinding.ActivityT02StartBinding;
import ecom.android.newparkapp.databinding.AlertDialogT02DeviceListBinding;
import ecom.android.newparkapp.databinding.AlertDialogT03CumulativeTimeBinding;
import ecom.android.newparkapp.databinding.AlertDialogProgressBarBinding;
import ecom.android.newparkapp.entity.Case;
import ecom.android.newparkapp.entity.CaseStatus;
import ecom.android.newparkapp.entity.Space;
import ecom.android.newparkapp.entity.User;
import ecom.android.newparkapp.entity.VehicleType;
import ecom.android.newparkapp.viewModel.BlueToothViewModel;
import ecom.android.newparkapp.viewModel.FusedGpsViewModel;
import ecom.android.newparkapp.viewModel.T01SettingViewModel;
import ecom.android.newparkapp.viewModel.T02StartViewModel;
import ecom.android.newparkapp.databinding.ActivityT02StartBinding;
import ecom.android.newparkapp.databinding.AlertDialogT03CumulativeTimeBinding;
public class T02StartActivity extends AppCompatActivity {
private ActivityT02StartBinding dataBinding;
private ViewModelProvider viewModelProvider;
private T02StartViewModel t02StartViewModel;
private T01SettingViewModel t01SettingViewModel;
private FusedGpsViewModel fusedGpsViewModel;
private BlueToothViewModel blueToothViewModel;
//藍芽相關
private ArrayAdapter<String> deviceNameArrayAdapter;
private MutableLiveData<Boolean> hasPermissions = new MutableLiveData<>();
private String[] bluetoothPermissions = new String[]{BLUETOOTH_CONNECT, BLUETOOTH_SCAN};
private ActivityResultLauncher vehicleTypeResultLauncher;
private ActivityResultLauncher spaceResultLauncher;
......@@ -44,10 +71,15 @@ public class T02StartActivity extends AppCompatActivity {
private ActivityResultLauncher takePhotosActivityResultLauncher;
private ActivityResultLauncher cumulativeTimeActivityResultLauncher;
private ActivityResultLauncher inventoryActivityResultLauncher;
private ActivityResultLauncher requestMultiplePermissions;
private ActivityResultLauncher requestBluetooth;
private User currentUser;
private File photoFile;
private AlertDialog alertDialogProgress;
private AlertDialogProgressBarBinding alertDialogProgressBarBinding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
......@@ -56,23 +88,39 @@ public class T02StartActivity extends AppCompatActivity {
viewModelProvider = new ViewModelProvider(this);
t02StartViewModel = viewModelProvider.get(T02StartViewModel.class);
t01SettingViewModel = viewModelProvider.get(T01SettingViewModel.class);
fusedGpsViewModel = viewModelProvider.get(FusedGpsViewModel.class);
blueToothViewModel = viewModelProvider.get(BlueToothViewModel.class);
currentUser = t01SettingViewModel.getCurrentUser();
if (currentUser == null) {
finish();
}
t02StartViewModel.setCurrentUser(currentUser);
t02StartViewModel.initCurrentCase();
dataBinding.setT02StartViewModel(t02StartViewModel);
dataBinding.setBlueToothViewModel(blueToothViewModel);
eventBinding();
resultLauncherRegister();
observeBinding();
currentUser = getIntent().getParcelableExtra("CurrentUser");
t02StartViewModel.setCurrentUser(currentUser);
t02StartViewModel.initCurrentCase();
// 搜尋監聽藍芽裝置
hasPermissions.setValue(false);
initBlueTooth();
// progress alert dialog init
initAlertDialogProgress();
}
private void eventBinding(){
private void eventBinding() {
dataBinding.btnParkingSpace.setOnClickListener(v -> btnParkingSpaceOnClicked());
dataBinding.btnVehicleType.setOnClickListener(v -> btnVehicleTypeOnClicked());
dataBinding.btnTimeNow.setOnClickListener(v -> btnTimeNowOnClicked());
dataBinding.btnPlateNumber.setOnClickListener(v-> btnPlateNumberOnClicked());
dataBinding.btnPlateNumber.setOnClickListener(v -> btnPlateNumberOnClicked());
dataBinding.btnPhotograph.setOnClickListener(v -> btnPhotographOnClicked());
dataBinding.btnNewPage.setOnClickListener(v -> btnNewPageOnClicked());
dataBinding.btnPageDown.setOnClickListener(v -> btnPageDownOnClicked());
......@@ -80,16 +128,18 @@ public class T02StartActivity extends AppCompatActivity {
dataBinding.btnPhotoCount.setOnClickListener(v -> btnPhotoCountOnClicked());
dataBinding.btnCumulativeTime.setOnClickListener(v -> btnCumulativeTimeOnClicked());
dataBinding.btnInventory.setOnClickListener(v -> btnInventoryOnClicked());
dataBinding.btnMonthlyReport.setOnClickListener( v -> btnMonthlyReportOnClicked());
dataBinding.btnMonthlyReport.setOnClickListener(v -> btnMonthlyReportOnClicked());
dataBinding.btnPrint.setOnClickListener(v -> btnPrintOnClicked());
dataBinding.btnStartBack.setOnClickListener(v -> {finish();});
dataBinding.btnStartBack.setOnClickListener(v -> {
finish();
});
}
private void resultLauncherRegister(){
private void resultLauncherRegister() {
vehicleTypeResultLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
if (result.getResultCode() == RESULT_OK && result.getData()!=null){
if (result.getResultCode() == RESULT_OK && result.getData() != null) {
VehicleType selectedVehicleType = result.getData().getParcelableExtra("VehicleType");
Toast.makeText(this, selectedVehicleType.id + " " + selectedVehicleType.name, Toast.LENGTH_SHORT).show();
......@@ -98,7 +148,7 @@ public class T02StartActivity extends AppCompatActivity {
});
spaceResultLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
if (result.getResultCode() == RESULT_OK && result.getData() != null){
if (result.getResultCode() == RESULT_OK && result.getData() != null) {
Space selectedSpace = result.getData().getParcelableExtra("Space");
Toast.makeText(this, selectedSpace.road.name + " " + selectedSpace.id, Toast.LENGTH_SHORT).show();
......@@ -107,7 +157,7 @@ public class T02StartActivity extends AppCompatActivity {
});
plateNumberResultLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
if (result.getResultCode() == RESULT_OK && result.getData() != null){
if (result.getResultCode() == RESULT_OK && result.getData() != null) {
String plateNumber = result.getData().getStringExtra("PlateNumber");
t02StartViewModel.setPlateNumber(plateNumber);
......@@ -115,8 +165,8 @@ public class T02StartActivity extends AppCompatActivity {
});
takePhotosActivityResultLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
if (result.getResultCode() == RESULT_OK){
if ( photoFile == null){
if (result.getResultCode() == RESULT_OK) {
if (photoFile == null) {
return;
}
t02StartViewModel.takeNewPhoto(photoFile);
......@@ -124,8 +174,8 @@ public class T02StartActivity extends AppCompatActivity {
});
cumulativeTimeActivityResultLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
if (result.getResultCode() == RESULT_OK){
if ( photoFile == null){
if (result.getResultCode() == RESULT_OK) {
if (photoFile == null) {
return;
}
t02StartViewModel.cumulativeTime(photoFile);
......@@ -133,41 +183,79 @@ public class T02StartActivity extends AppCompatActivity {
});
inventoryActivityResultLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
if (result.getResultCode() == RESULT_OK && result.getData() != null){
if (result.getResultCode() == RESULT_OK && result.getData() != null) {
int position = result.getData().getIntExtra("Position", -1);
boolean hasCase = false;
if (position >= 0){
if (position >= 0) {
hasCase = t02StartViewModel.go2Page(position);
}
if (!hasCase){
if (!hasCase) {
Toast.makeText(this, "指標索引錯誤", Toast.LENGTH_SHORT).show();
}
}
});
requestMultiplePermissions = registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(),results -> {
for (Map.Entry<String, Boolean> result : results.entrySet()) {
if (!result.getValue()){
return;
}
}
hasPermissions.setValue(true);
});
requestBluetooth = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
if (result.getResultCode() == RESULT_OK) {
selectPrinterDialog();
}
});
}
private void observeBinding(){
private void observeBinding() {
t02StartViewModel.getCaseCursor().observe(this, caseCursor -> {
// 判斷當前,是否為第一筆
if (caseCursor == 0){
if (caseCursor == 0) {
if (dataBinding.btnPageUp.isEnabled()) dataBinding.btnPageUp.setEnabled(false);
}else {
if (!dataBinding.btnPageUp.isEnabled()) dataBinding.btnPageUp.setEnabled(true);
} else {
if (!dataBinding.btnPageUp.isEnabled()) dataBinding.btnPageUp.setEnabled(true);
}
// 判斷當前,是否為最後一起
if (t02StartViewModel.isLastCase()){
if (t02StartViewModel.isLastCase()) {
if (dataBinding.btnPageDown.isEnabled()) dataBinding.btnPageDown.setEnabled(false);
}else {
} else {
if (!dataBinding.btnPageDown.isEnabled()) dataBinding.btnPageDown.setEnabled(true);
}
});
fusedGpsViewModel.getLocation().observe(this, location -> {
if (t02StartViewModel.getCurrentCase().getValue() != null && t02StartViewModel.getCurrentCase().getValue().caseStatus == CaseStatus.NEW) {
t02StartViewModel.setLocation(location);
}
});
fusedGpsViewModel.getLastUpdateTime().observe(this, date -> {
if (t02StartViewModel.getCurrentCase().getValue() != null && t02StartViewModel.getCurrentCase().getValue().caseStatus == CaseStatus.NEW) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
dataBinding.lastUpdateTimeText.setText(getString(R.string.last_location_time_textView, simpleDateFormat.format(date)));
}
});
blueToothViewModel.getBlueToothStatus().observe(this, blueToothStatus -> {
if (blueToothStatus == BlueToothViewModel.BlueToothStatus.PRINTING){
setAlertDialogProgressMessage("列印中,請稍後");
alertDialogProgress.show();
}
if (blueToothStatus == BlueToothViewModel.BlueToothStatus.PRINT_COMPLETE){
alertDialogProgress.dismiss();
}
});
}
private void btnParkingSpaceOnClicked(){
private void btnParkingSpaceOnClicked() {
Intent intent = new Intent();
Bundle bundle = new Bundle();
intent.setClass(this, T02SelectSpaceActivity.class);
......@@ -176,7 +264,7 @@ public class T02StartActivity extends AppCompatActivity {
spaceResultLauncher.launch(intent);
}
private void btnVehicleTypeOnClicked(){
private void btnVehicleTypeOnClicked() {
Intent intent = new Intent();
Bundle bundle = new Bundle();
intent.setClass(this, T02SelectVehicleTypeActivity.class);
......@@ -184,7 +272,7 @@ public class T02StartActivity extends AppCompatActivity {
vehicleTypeResultLauncher.launch(intent);
}
private void btnTimeNowOnClicked(){
private void btnTimeNowOnClicked() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("時間修改");
builder.setMessage("是否更改為現在的時間");
......@@ -192,20 +280,20 @@ public class T02StartActivity extends AppCompatActivity {
builder.setPositiveButton("確認", (dialogInterface, i) -> {
t02StartViewModel.updateCaseDate();
});
builder.setNegativeButton("取消",(dialogInterface, i) -> {
builder.setNegativeButton("取消", (dialogInterface, i) -> {
dialogInterface.dismiss();
});
builder.create().show();
}
private void btnPlateNumberOnClicked(){
private void btnPlateNumberOnClicked() {
Intent intent = new Intent();
intent.setClass(this, T02KeyInPlateNumberActivity.class);
plateNumberResultLauncher.launch(intent);
}
private void btnPhotographOnClicked(){
private void btnPhotographOnClicked() {
takePhoto(takePhotosActivityResultLauncher);
}
......@@ -213,10 +301,10 @@ public class T02StartActivity extends AppCompatActivity {
* 拍照功能,傳入不同的 ActivityResultLauncher,來接收不同的拍照邏輯
* @param activityResultLauncher 不同的事件處理器
*/
private void takePhoto(ActivityResultLauncher activityResultLauncher){
private void takePhoto(ActivityResultLauncher activityResultLauncher) {
Case currentCase = t02StartViewModel.getCurrentCase().getValue();
if (currentCase == null){
if (currentCase == null) {
return;
}
......@@ -244,37 +332,37 @@ public class T02StartActivity extends AppCompatActivity {
activityResultLauncher.launch(takePhotoIntent);
}
private void btnNewPageOnClicked(){
if (!t02StartViewModel.caseDataConfirm()){
private void btnNewPageOnClicked() {
if (!t02StartViewModel.caseDataConfirm()) {
// 資料不完整
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("檔案保存");
builder.setMessage("資料不完整,請重新確認");
builder.setPositiveButton("清空",(dialogInterface, i) -> {
builder.setPositiveButton("清空", (dialogInterface, i) -> {
t02StartViewModel.initCurrentCase();
});
builder.setNegativeButton("返回",(dialogInterface, i) -> {
builder.setNegativeButton("返回", (dialogInterface, i) -> {
dialogInterface.dismiss();
});
builder.create().show();
}else {
} else {
// 資料完整
// 儲存
t02StartViewModel.saveCurrentCase();
}
}
private void btnPageDownOnClicked(){
private void btnPageDownOnClicked() {
t02StartViewModel.pageDown();
}
private void btnPageUpOnClicked(){
private void btnPageUpOnClicked() {
t02StartViewModel.pageUp();
}
private void btnPhotoCountOnClicked(){
private void btnPhotoCountOnClicked() {
Case tempCase = t02StartViewModel.getCurrentCase().getValue();
if (tempCase == null || tempCase.photoCount == 0 || tempCase.billingNumber2.trim().isEmpty()){
if (tempCase == null || tempCase.photoCount == 0 || tempCase.billingNumber2.trim().isEmpty()) {
return;
}
Intent intent = new Intent();
......@@ -284,11 +372,13 @@ public class T02StartActivity extends AppCompatActivity {
}
private void btnCumulativeTimeOnClicked(){
private void btnCumulativeTimeOnClicked() {
Case tempCase = t02StartViewModel.getCurrentCase().getValue();
if (tempCase == null){return;}
if (tempCase.caseStatus == CaseStatus.NEW){
if (tempCase == null) {
return;
}
if (tempCase.caseStatus == CaseStatus.NEW) {
Toast.makeText(this, "請先設定格位", Toast.LENGTH_SHORT).show();
return;
}
......@@ -310,7 +400,7 @@ public class T02StartActivity extends AppCompatActivity {
t03CumulativeTimeDataBinding.diaParkingHoursTextView.setText(String.valueOf(newPeriodHour));
// 顯示 累加時間 開單時間
SimpleDateFormat simpleDate = new SimpleDateFormat("HH:mm");
SimpleDateFormat simpleDate = new SimpleDateFormat("HH:mm");
t03CumulativeTimeDataBinding.diaBillingTimeTextView.setText(simpleDate.format(tempCase.caseTime));
t03CumulativeTimeDataBinding.diaCumulativeTimeTextView.setText(simpleDate.format(tempCase.finalTime));
......@@ -346,13 +436,13 @@ public class T02StartActivity extends AppCompatActivity {
}
private void btnInventoryOnClicked(){
private void btnInventoryOnClicked() {
Intent intent = new Intent();
intent.setClass(this, T03ListFilesActivity.class);
inventoryActivityResultLauncher.launch(intent);
}
private void btnMonthlyReportOnClicked(){
private void btnMonthlyReportOnClicked() {
Intent intent = new Intent();
intent.setClass(this, T03TotalActivity.class);
Bundle bundle = new Bundle();
......@@ -362,4 +452,127 @@ public class T02StartActivity extends AppCompatActivity {
intent.putExtras(bundle);
startActivity(intent);
}
/**
* 初始化藍芽連線
*
* @return 初始化藍芽是否成功
*/
private boolean initBlueTooth() {
deviceNameArrayAdapter = blueToothViewModel.getDeviceNameArrayAdapter();
if (blueToothViewModel.getBluetoothAdapter() == null) {
Toast.makeText(getApplication(), "Bluetooth is not available", Toast.LENGTH_LONG).show();
return false;
}
if (!blueToothViewModel.getBluetoothAdapter().isEnabled()) {
// TODO: 2022/8/3 Android 12 以上的版本 需額外請求藍芽權限
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
requestMultiplePermissions.launch(bluetoothPermissions);
}else {
Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
requestBluetooth.launch(enableIntent);
}
}
selectPrinterDialog();
return true;
}
private void selectPrinterDialog() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
// Android 12 額外檢查藍芽權限
for (String permission : bluetoothPermissions){
if(ActivityCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED){
return;
}
}
}
AlertDialogT02DeviceListBinding t02DeviceListDataBinding = AlertDialogT02DeviceListBinding.inflate(LayoutInflater.from(this));
AlertDialog.Builder alertDialog = new AlertDialog.Builder(this);
final AlertDialog tempDialog = alertDialog.create();
t02DeviceListDataBinding.lvPairedDevices.setAdapter(deviceNameArrayAdapter);
t02DeviceListDataBinding.lvPairedDevices.setOnItemClickListener((adapterView, view, i, l) -> {
// 藍芽裝置選取事件
blueToothViewModel.cancelDiscovery();
String info = ((TextView) view).getText().toString();
String address = info.substring(info.length() - 17);
// 進行藍芽連線
blueToothViewModel.connect(address);
tempDialog.dismiss();
});
// 搜尋功能綁定
t02DeviceListDataBinding.btnScan.setOnClickListener(view -> {
if (deviceNameArrayAdapter.getPosition(getString(R.string.none_paired)) >= 0) {
deviceNameArrayAdapter.remove(getString(R.string.none_paired));
}
if (!blueToothViewModel.getBluetoothAdapter().isDiscovering()) {
t02DeviceListDataBinding.btnScan.setText(R.string.button_stop_scan);
t02DeviceListDataBinding.titlePairedDevices.setText(getString(R.string.title_other_devices));
// 開始搜尋
blueToothViewModel.startDiscovery();
} else {
t02DeviceListDataBinding.btnScan.setText(R.string.button_scan);
// 停止搜尋
blueToothViewModel.cancelDiscovery();
}
});
// dialog 關閉
t02DeviceListDataBinding.dialogClose.setOnClickListener(view -> {
tempDialog.dismiss();
});
tempDialog.setCanceledOnTouchOutside(false);
tempDialog.setView(t02DeviceListDataBinding.getRoot());
tempDialog.show();
// 對話框大小調整
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
WindowManager.LayoutParams p = tempDialog.getWindow().getAttributes(); //获取对话框当前的参数值
p.height = (int) (metrics.heightPixels * 0.7); //高度設置为螢幕的0.7
p.width = (int) (metrics.widthPixels * 0.9); //寬度設置為螢幕的0.9
tempDialog.getWindow().setAttributes(p);
}
private void btnPrintOnClicked(){
Case tempCase = t02StartViewModel.getCurrentCase().getValue();
if (tempCase.caseStatus != CaseStatus.LIST){
Toast.makeText(this, "請先確認資料後再列印", Toast.LENGTH_SHORT).show();
return;
}
if (blueToothViewModel.getBlueToothStatus().getValue() != BlueToothViewModel.BlueToothStatus.CONNECT_SUCCESS){
Toast.makeText(this, "請先連結藍芽印表機裝置", Toast.LENGTH_SHORT).show();
return;
}
blueToothViewModel.printCase(tempCase);
}
private void initAlertDialogProgress(){
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setCancelable(false); // if you want user to wait for some process to finish,
alertDialogProgressBarBinding = AlertDialogProgressBarBinding.inflate(LayoutInflater.from(this));
builder.setView(alertDialogProgressBarBinding.getRoot());
alertDialogProgress = builder.create();
}
private void setAlertDialogProgressMessage(String message){
alertDialogProgressBarBinding.tvProgressBarMsg.setText(message);
}
}
\ No newline at end of file
package ecom.android.newparkapp.viewModel;
import static android.Manifest.permission.BLUETOOTH_CONNECT;
import static android.Manifest.permission.BLUETOOTH_SCAN;
import android.app.Application;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.Build;
import android.widget.ArrayAdapter;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import ecom.android.newparkapp.R;
import ecom.android.newparkapp.entity.Case;
import ecom.android.newparkapp.printer.IBluetoothPrinter;
import ecom.android.newparkapp.view.T02StartActivity;
public class BlueToothViewModel extends AndroidViewModel {
private ExecutorService executorService;
private BluetoothAdapter bluetoothAdapter;
private BluetoothSocket bluetoothSocket;
private BTDeviceReceiver btDeviceReceiver;
private OutputStream outputStream;
private ArrayAdapter<String> deviceNameArrayAdapter;
private String[] bluetoothPermissions = new String[]{};
private MutableLiveData<BluetoothDevice> bluetoothDevice = new MutableLiveData<>();
private MutableLiveData<BlueToothStatus> blueToothStatusMutableLiveData = new MutableLiveData<>();
public BlueToothViewModel(@NonNull Application application) {
super(application);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
// Android 12 額外檢查藍芽權限
for (String permission : bluetoothPermissions){
if(ActivityCompat.checkSelfPermission(application, permission) != PackageManager.PERMISSION_GRANTED){
return;
}
}
}
executorService = Executors.newFixedThreadPool(application.getResources().getInteger(R.integer.number_db_thread_pool));
blueToothStatusMutableLiveData.setValue(BlueToothStatus.INIT);
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
deviceNameArrayAdapter = new ArrayAdapter<String>(application, R.layout.list_view_device_name_item);
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothDevice.ACTION_FOUND);
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
btDeviceReceiver = new BTDeviceReceiver();
application.registerReceiver(btDeviceReceiver, filter);
//取得已綁定過的裝置
Set<BluetoothDevice> pairedDevices = bluetoothAdapter.getBondedDevices();
if (pairedDevices.size() > 0) {
for (BluetoothDevice device : pairedDevices) {
deviceNameArrayAdapter.add(device.getName() + "\n"
+ device.getAddress());
}
} else {
deviceNameArrayAdapter.add(application.getString(R.string.none_paired));
}
}
public LiveData<BlueToothStatus> getBlueToothStatus(){
return blueToothStatusMutableLiveData;
}
public LiveData<BluetoothDevice> getBluetoothDevice(){
return bluetoothDevice;
}
public ArrayAdapter<String> getDeviceNameArrayAdapter(){
return deviceNameArrayAdapter;
}
public BluetoothAdapter getBluetoothAdapter(){
return bluetoothAdapter;
}
/**
* 藍芽連線
* @param device 藍芽裝置
*/
private void connect(BluetoothDevice device) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
// Android 12 額外檢查藍芽權限
for (String permission : bluetoothPermissions){
if(ActivityCompat.checkSelfPermission(getApplication(), permission) != PackageManager.PERMISSION_GRANTED){
return;
}
}
}
if (device == null){
blueToothStatusMutableLiveData.setValue(BlueToothStatus.CONNECT_FAILED);
return;
}
bluetoothDevice.setValue(device);
executorService.execute(() -> {
blueToothStatusMutableLiveData.postValue(BlueToothStatus.CONNECTING);
try {
BluetoothDevice thisBluetoothDevice = bluetoothDevice.getValue();
if (thisBluetoothDevice == null){
blueToothStatusMutableLiveData.postValue(BlueToothStatus.CONNECT_FAILED);
return;
}
bluetoothSocket = thisBluetoothDevice.createRfcommSocketToServiceRecord(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));//SerialPortServiceClass_UUID
bluetoothSocket.connect();
outputStream = bluetoothSocket.getOutputStream();
blueToothStatusMutableLiveData.postValue(BlueToothStatus.CONNECT_SUCCESS);
} catch (IOException e) {
e.printStackTrace();
try {
if (bluetoothSocket != null) {
bluetoothSocket.close();
}
blueToothStatusMutableLiveData.postValue(BlueToothStatus.CONNECT_FAILED);
} catch (IOException ex) {
ex.printStackTrace();
}
}
});
}
public void connect(String address){
BluetoothDevice bluetoothDeviceFromAddress = bluetoothAdapter.getRemoteDevice(address);
connect(bluetoothDeviceFromAddress);
}
/**
* 中斷當前藍芽連線
*/
public void disconnect(){
}
/**
* 開始藍芽搜尋
*/
public void startDiscovery() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
// Android 12 額外檢查藍芽權限
for (String permission : bluetoothPermissions){
if(ActivityCompat.checkSelfPermission(getApplication(), permission) != PackageManager.PERMISSION_GRANTED){
return;
}
}
}
if (bluetoothAdapter != null && !bluetoothAdapter.isDiscovering()){
bluetoothAdapter.startDiscovery();
}
}
/**
* 停止藍芽搜尋
*/
public void cancelDiscovery() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
// Android 12 額外檢查藍芽權限
for (String permission : bluetoothPermissions){
if(ActivityCompat.checkSelfPermission(getApplication(), permission) != PackageManager.PERMISSION_GRANTED){
return;
}
}
}
if (bluetoothAdapter != null && bluetoothAdapter.isDiscovering()){
bluetoothAdapter.cancelDiscovery();
}
}
public void printCase(Case tempCase) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
// Android 12 額外檢查藍芽權限
for (String permission : bluetoothPermissions){
if(ActivityCompat.checkSelfPermission(getApplication(), permission) != PackageManager.PERMISSION_GRANTED){
return;
}
}
}
blueToothStatusMutableLiveData.setValue(BlueToothStatus.PRINTING);
executorService.execute(()->{
/*-----取得CasePrinter-----*/
IBluetoothPrinter printer = tempCase.getCasePrinter(bluetoothDevice.getValue().getName());
try {
printer.print(bluetoothSocket, tempCase);
} catch (IOException e) {
e.printStackTrace();
blueToothStatusMutableLiveData.postValue(BlueToothStatus.PRINT_FAILED);
return;
}
blueToothStatusMutableLiveData.postValue(BlueToothStatus.PRINT_COMPLETE);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
blueToothStatusMutableLiveData.postValue(BlueToothStatus.CONNECT_SUCCESS);
});
}
/**
* 監聽藍芽裝置廣播
*/
private class BTDeviceReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, @NonNull Intent intent) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
// Android 12 額外檢查藍芽權限
for (String permission : bluetoothPermissions){
if(ActivityCompat.checkSelfPermission(getApplication(), permission) != PackageManager.PERMISSION_GRANTED){
return;
}
}
}
String action = intent.getAction();
if (BluetoothDevice.ACTION_FOUND.equals(action)) {//如果藍芽掃瞄有裝置
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (device == null) {
return;
}
if (device.getBondState() != BluetoothDevice.BOND_BONDED
&& deviceNameArrayAdapter.getPosition(device.getName() + "\n" + device.getAddress()) < 0) {
deviceNameArrayAdapter.add(device.getName() + "\n" + device.getAddress());
}
} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
if (deviceNameArrayAdapter.getCount() == 0) {
String noDevices = getApplication().getString(R.string.none_found);
deviceNameArrayAdapter.add(noDevices);
}
}
}
}
public enum BlueToothStatus{
INIT,CONNECTING ,CONNECT_SUCCESS, CONNECT_FAILED,PRINTING,PRINT_COMPLETE, PRINT_FAILED
}
}
package ecom.android.newparkapp.viewModel;
import android.Manifest;
import android.app.Application;
import android.content.pm.PackageManager;
import android.location.Location;
import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import com.google.android.gms.location.FusedLocationProviderClient;
import com.google.android.gms.location.LocationCallback;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationResult;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.location.LocationSettingsRequest;
import com.google.android.gms.location.Priority;
import com.google.android.gms.location.SettingsClient;
import java.text.DateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import ecom.android.newparkapp.R;
public class FusedGpsViewModel extends AndroidViewModel {
private long UPDATE_INTERVAL_IN_MILLISECONDS = 100;
private long FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS = 50;
private FusedLocationProviderClient fusedLocationProviderClient;
private SettingsClient settingsClient;
private LocationRequest locationRequest;
private LocationSettingsRequest locationSettingsRequest;
private LocationCallback locationCallback;
private Boolean requestingLocationUpdates;
private MutableLiveData<Location> location = new MutableLiveData<>();
private MutableLiveData<Date> lastUpdateTime = new MutableLiveData<>();//最後定位時間
private ExecutorService executorService;
public FusedGpsViewModel(@NonNull Application application) {
super(application);
fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(application);
settingsClient = LocationServices.getSettingsClient(application);
executorService = Executors.newFixedThreadPool(application.getResources().getInteger(R.integer.number_db_thread_pool));
createLocationCallback();
createLocationRequest();
buildLocationSettingsRequest();
if (ActivityCompat.checkSelfPermission(application, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(application, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
return ;
}
fusedLocationProviderClient.requestLocationUpdates(locationRequest, executorService, locationCallback);
}
public LiveData<Location> getLocation(){
return location;
}
public LiveData<Date> getLastUpdateTime(){
return lastUpdateTime;
}
private void createLocationCallback() {
locationCallback = new LocationCallback() {
@Override
public void onLocationResult(@NonNull LocationResult locationResult) {
super.onLocationResult(locationResult);
location.postValue(locationResult.getLastLocation());
lastUpdateTime.postValue(new Date());
}
};
}
private void createLocationRequest() {
locationRequest = LocationRequest.create()
.setInterval(UPDATE_INTERVAL_IN_MILLISECONDS)
.setFastestInterval(FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS)
.setPriority(Priority.PRIORITY_HIGH_ACCURACY)
.setMaxWaitTime(0);
}
private void buildLocationSettingsRequest(){
LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder();
builder.addLocationRequest(locationRequest);
locationSettingsRequest = builder.build();
}
}
package ecom.android.newparkapp.viewModel;
import static ecom.android.newparkapp.converter.SpaceTypeConverter.toSpaceType;
import android.Manifest;
import android.app.Application;
import android.content.pm.PackageManager;
......@@ -19,9 +21,9 @@ import ecom.android.newparkapp.Common;
import ecom.android.newparkapp.R;
import ecom.android.newparkapp.entity.Road;
import ecom.android.newparkapp.entity.Space;
import ecom.android.newparkapp.entity.Space.SpaceType;
import ecom.android.newparkapp.entity.SpaceRate;
import ecom.android.newparkapp.entity.SpaceStatus;
import ecom.android.newparkapp.entity.SpaceType;
import ecom.android.newparkapp.entity.User;
import ecom.android.newparkapp.entity.UserPermission;
import ecom.android.newparkapp.entity.VehicleBrand;
......@@ -218,24 +220,6 @@ public class T01ImportDbViewModel extends AndroidViewModel {
}
});
break;
case SpaceType:
infoRepository.executorService.execute(()->{
infoRepository.spaceTypeDao.deleteAll();
for (int i = 0; i < lines.length; i++){
String[] columnString = lines[i].trim().split("[ ]");
int newId;
if (columnString.length != 2){ break; }
try {
newId = Integer.parseInt(columnString[0]);
}catch (NumberFormatException exception){
break;
}
String newName = columnString[1];
SpaceType newSpaceType = new SpaceType(newId, newName);
infoRepository.spaceTypeDao.insertAll(newSpaceType);
}
});
break;
case Road:
infoRepository.executorService.execute(()->{
infoRepository.roadDao.deleteAll();
......@@ -278,9 +262,7 @@ public class T01ImportDbViewModel extends AndroidViewModel {
if (spaceRateList.size() == 0 ){break;}
SpaceRate newSpaceRate = spaceRateList.get(0);
List<SpaceType> spaceTypeList = infoRepository.spaceTypeDao.loadAllByIds(new int[]{spaceTypeId});
if (spaceTypeList.size() == 0){break;}
SpaceType newSpaceType = spaceTypeList.get(0);
SpaceType newSpaceType = toSpaceType(spaceTypeId);
List<SpaceStatus> spaceStatusList = infoRepository.spaceStatusDao.loadAllByIds(new int[]{spaceStatusId});
if (spaceStatusList.size() == 0){break;}
......@@ -299,6 +281,6 @@ public class T01ImportDbViewModel extends AndroidViewModel {
}
public enum DBTableName {
UserPermission,User, VehicleBrand, VehicleColor, VehicleType,SpaceRate, SpaceStatus, SpaceType,Road,Space
UserPermission,User, VehicleBrand, VehicleColor, VehicleType,SpaceRate, SpaceStatus,Road,Space
}
}
\ No newline at end of file
......@@ -41,14 +41,17 @@ public class T01SettingViewModel extends AndroidViewModel {
super(application);
tempPREF = new PreferenceSettings();
restorePrefs(application, tempPREF);
PREF.setValue(tempPREF);
restorePrefs();
infoRepository = new InfoRepository(application);
Users = infoRepository.userDao.getAllLiveData();
}
public void restorePrefs(){
restorePrefs(getApplication(), tempPREF);
PREF.setValue(tempPREF);
}
private void restorePrefs(@NonNull Application application,PreferenceSettings PREF) {
settings = application.getSharedPreferences(Common.PREF, MODE_PRIVATE);
PREF.pref_user = settings.getString(Common.PREF_USER, "999 系統測試");
......
package ecom.android.newparkapp.viewModel;
import android.app.Application;
import android.location.Location;
import android.media.MediaScannerConnection;
import android.os.Environment;
import android.widget.Toast;
......@@ -452,4 +453,10 @@ public class T02StartViewModel extends AndroidViewModel {
// 寫入DB
saveCurrentCase();
}
public void setLocation(Location location) {
Case tempCase = currentCase.getValue();
tempCase.location = location;
currentCase.setValue(tempCase);
}
}
package ecom.android.newparkapp.viewModel;
import android.app.Application;
import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MediatorLiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.Transformations;
import java.util.List;
import ecom.android.newparkapp.entity.User;
import ecom.android.newparkapp.repository.InfoRepository;
public class UserViewModel extends AndroidViewModel {
private InfoRepository infoRepository;
private LiveData<List<User>> allUsers;
private LiveData<User> userLiveData;
private MutableLiveData<User> currentUser = new MutableLiveData<>();
public UserViewModel(@NonNull Application application) {
super(application);
infoRepository = new InfoRepository(application);
userLiveData = infoRepository.userDao.getLiveDataById(1);
allUsers = infoRepository.userDao.getAllLiveData();
}
public LiveData<List<User>> getAllUsers() {
return allUsers;
}
public LiveData<User> getCurrentUser() { return currentUser ;}
public void setCurrentUser(){
User cUser = infoRepository.userDao.findByName("Root");
currentUser.setValue(cUser);
}
public void setCurrentUser(User cUser){
currentUser.setValue(cUser);
}
public LiveData<User> getUserLiveData(){return userLiveData;}
}
......@@ -2,11 +2,6 @@
<layout xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="userViewModel"
type="ecom.android.newparkapp.viewModel.UserViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
......@@ -86,7 +81,7 @@
android:id="@+id/tv_current_usr"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="當前使用者"
android:text="@string/current_user"
android:textAlignment="center"
android:textSize="30sp"
app:layout_constraintBottom_toBottomOf="parent"
......
......@@ -8,10 +8,13 @@
<import type="ecom.android.newparkapp.entity.Road"/>
<import type="ecom.android.newparkapp.entity.SpaceStatus"/>
<import type="ecom.android.newparkapp.entity.SpaceRate"/>
<import type="ecom.android.newparkapp.entity.SpaceType"/>
<variable
name="t02StartViewModel"
type="ecom.android.newparkapp.viewModel.T02StartViewModel" />
<variable
name="blueToothViewModel"
type="ecom.android.newparkapp.viewModel.BlueToothViewModel" />
</data>
<androidx.core.widget.NestedScrollView
android:id="@+id/t02_start_border"
......@@ -50,7 +53,7 @@ tools:layout_editor_absoluteY="25dp">
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="YH-TB250"
android:text="@{blueToothViewModel.bluetoothDevice.name}"
android:textAlignment="center"
android:textSize="20sp" />
......@@ -59,10 +62,10 @@ tools:layout_editor_absoluteY="25dp">
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="連線正常"
android:textAlignment="center"
android:textColor="#8BC34A"
android:textSize="20sp" />
android:textSize="20sp"
app:blueToothStatus2String="@{blueToothViewModel.blueToothStatus}"/>
<LinearLayout
android:layout_width="wrap_content"
......@@ -582,7 +585,7 @@ tools:layout_editor_absoluteY="25dp">
android:layout_height="match_parent"
android:textSize="21sp"
android:layout_weight="1"
android:text="座標更新時間:" />
android:text="@string/last_location_time_textView" />
</LinearLayout>
<!-- Printer
<Button
......
<?xml version="1.0" encoding="utf-8"?>
<layout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="20dp">
<ProgressBar
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1" />
<TextView
android:id="@+id/tv_progress_bar_msg"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="4"
android:gravity="center"
android:text="Please wait! This may take a moment." />
</LinearLayout>
</layout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<layout>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/title_paired_devices"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="5dp"
android:background="@color/colorPrimaryDark"
android:gravity="center"
android:padding="5dp"
android:text="@string/title_paired_devices"
android:textColor="#fff"
android:textSize="30sp"
app:layout_constraintBottom_toTopOf="@+id/btn_scan"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageButton
android:id="@+id/dialog_close"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:background="@drawable/ic_baseline_cancel_24"
app:layout_constraintBottom_toBottomOf="@+id/title_paired_devices"
app:layout_constraintEnd_toEndOf="@+id/title_paired_devices"
app:layout_constraintTop_toTopOf="@+id/title_paired_devices" />
<Button
android:id="@+id/btn_scan"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_marginEnd="5dp"
android:text="@string/button_scan"
android:textSize="18sp"
app:layout_constraintBottom_toTopOf="@+id/lv_paired_devices"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/title_paired_devices" />
<ListView
android:id="@+id/lv_paired_devices"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_marginEnd="5dp"
android:stackFromBottom="false"
app:layout_constraintBottom_toTopOf="@+id/title_paired_devices"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btn_scan"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="5dp"
android:textSize="30sp" />
......@@ -6,7 +6,7 @@
<!-- 1050830 阿達:改時間 by JiaRong-->
<!--<string name="morning_button_text">早班 08:00~16:00</string>-->
<!--<string name="afternoon_button_text">晚班 14:00~22:00</string>-->
<string name="ecom_title">2021©版權所有\n昱通資訊事業股份有限公司</string>
<string name="ecom_title">2022©版權所有\n昱通資訊事業股份有限公司</string>
<string name="morning_button_text">早班 08:00~16:30</string>
<string name="afternoon_button_text">晚班 13:30~22:00</string>
<string name="label_setting">設定</string>
......@@ -78,4 +78,6 @@
<string name="hello_first_fragment">Hello first fragment</string>
<string name="hello_second_fragment">Hello second fragment. Arg: %1$s</string>
<string name="current_user">當前使用者: %s</string>
</resources>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment