Add Update procedure for app

Add app version
main
saad.siddiq 2025-04-10 10:45:05 +05:00
parent c4da9adde9
commit 938794cb7d
23 changed files with 553 additions and 9 deletions

View File

@ -25,6 +25,8 @@ android {
compileOptions { compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8 sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8
coreLibraryDesugaringEnabled true
} }
} }
@ -49,5 +51,8 @@ dependencies {
implementation 'com.journeyapps:zxing-android-embedded:4.1.0' implementation 'com.journeyapps:zxing-android-embedded:4.1.0'
implementation 'com.squareup.okhttp3:okhttp:4.9.3' implementation 'com.squareup.okhttp3:okhttp:4.9.3'
implementation 'com.github.MikeOrtiz:TouchImageView:3.6' implementation 'com.github.MikeOrtiz:TouchImageView:3.6'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.0.1'
implementation 'com.squareup.retrofit2:converter-jackson:2.9.0'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.3'
} }
} }

BIN
app/release/app-release.apk Normal file

Binary file not shown.

View File

@ -0,0 +1,20 @@
{
"version": 3,
"artifactType": {
"type": "APK",
"kind": "Directory"
},
"applicationId": "com.utopiaindustries.qualitychecker",
"variantName": "release",
"elements": [
{
"type": "SINGLE",
"filters": [],
"attributes": [],
"versionCode": 1,
"versionName": "1.0",
"outputFile": "app-release.apk"
}
],
"elementType": "File"
}

View File

@ -0,0 +1,28 @@
package com.utopiaindustries.qualitychecker.apiservice;
import okhttp3.OkHttpClient;
import retrofit2.Retrofit;
import retrofit2.converter.jackson.JacksonConverterFactory;
public class GiteaClient {
private static Retrofit retrofit;
private static final String BASE_URL = "https://git.utopiadeals.com/api/v1/repos/UIND/Quality-Checker-Android-Releases/";
/**
* get retrofit instance
*/
public static Retrofit getInstance() {
if ( retrofit == null ) {
try {
retrofit = new Retrofit.Builder()
.baseUrl( BASE_URL )
.addConverterFactory( JacksonConverterFactory.create() )
.client( SSLCheckHttpClient.getOkHttpClient() )
.build();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
return retrofit;
}
}

View File

@ -0,0 +1,16 @@
package com.utopiaindustries.qualitychecker.apiservice;
import com.utopiaindustries.qualitychecker.utils.Release;
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Query;
public interface GiteaRestService {
/**
* get latest release
*/
@GET( "releases/latest" )
Call<Release> getLatestRelease(@Query( "access_token" ) String accessToken );
}

View File

@ -19,8 +19,8 @@ import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory; import retrofit2.converter.gson.GsonConverterFactory;
public class RetrofitClient { public class RetrofitClient {
//private final static String BASE_URL = "https://portal.utopiaindustries.pk/uind/"; private final static String BASE_URL = "https://portal.utopiaindustries.pk/uind/";
private final static String BASE_URL = "http://192.168.91.44:8081/uind/";//"http://192.168.91.44:8081/uind/";//"http://192.168.90.27:8080/uind/";//"http://192.168.91.16:8080/uind/"; //private final static String BASE_URL = "http://192.168.91.44:8081/uind/";//"http://192.168.91.44:8081/uind/";//"http://192.168.90.27:8080/uind/";//"http://192.168.91.16:8080/uind/";
private static Retrofit retrofit; private static Retrofit retrofit;

View File

@ -6,13 +6,17 @@ import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.pm.ActivityInfo; import android.content.pm.ActivityInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager; import android.net.ConnectivityManager;
import android.net.NetworkInfo; import android.net.NetworkInfo;
import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log; import android.util.Log;
import android.view.View; import android.view.View;
import android.widget.Button; import android.widget.Button;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
@ -30,6 +34,8 @@ import com.google.android.material.tabs.TabLayout;
import com.utopiaindustries.qualitychecker.R; import com.utopiaindustries.qualitychecker.R;
import com.utopiaindustries.qualitychecker.apiservice.ApiService; import com.utopiaindustries.qualitychecker.apiservice.ApiService;
import com.utopiaindustries.qualitychecker.apiservice.ApiServiceFactory; import com.utopiaindustries.qualitychecker.apiservice.ApiServiceFactory;
import com.utopiaindustries.qualitychecker.apiservice.GiteaClient;
import com.utopiaindustries.qualitychecker.apiservice.GiteaRestService;
import com.utopiaindustries.qualitychecker.db.CheckpointRepository; import com.utopiaindustries.qualitychecker.db.CheckpointRepository;
import com.utopiaindustries.qualitychecker.db.DefectRepository; import com.utopiaindustries.qualitychecker.db.DefectRepository;
import com.utopiaindustries.qualitychecker.db.DimensionRepository; import com.utopiaindustries.qualitychecker.db.DimensionRepository;
@ -53,9 +59,13 @@ import com.utopiaindustries.qualitychecker.ui.activities.fragments.ReportsFragme
import com.utopiaindustries.qualitychecker.ui.adapter.PagerAdapter; import com.utopiaindustries.qualitychecker.ui.adapter.PagerAdapter;
import com.utopiaindustries.qualitychecker.ui.adapter.ReportAdapter; import com.utopiaindustries.qualitychecker.ui.adapter.ReportAdapter;
import com.utopiaindustries.qualitychecker.ui.fragments.FirstStepFragment; import com.utopiaindustries.qualitychecker.ui.fragments.FirstStepFragment;
import com.utopiaindustries.qualitychecker.utils.FileUtils;
import com.utopiaindustries.qualitychecker.utils.PropertyReader;
import com.utopiaindustries.qualitychecker.utils.Release;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Properties;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.Callback; import retrofit2.Callback;
@ -63,7 +73,7 @@ import retrofit2.Response;
public class HomeActivity extends AppCompatActivity implements View.OnClickListener { public class HomeActivity extends AppCompatActivity implements View.OnClickListener {
private Button createReportBtn,refreshReportsBtn,logoutBtn,fetchProductBtn; private Button createReportBtn,refreshReportsBtn,logoutBtn,fetchProductBtn, appUpdateBtn;
//private RecyclerView recyclerView; //private RecyclerView recyclerView;
private ApiService apiService; private ApiService apiService;
private TextView usernameTitle;//, emptyReportTextView; private TextView usernameTitle;//, emptyReportTextView;
@ -72,11 +82,13 @@ public class HomeActivity extends AppCompatActivity implements View.OnClickListe
private InspectionReportService inspectionReportService; private InspectionReportService inspectionReportService;
private NetworkReceiver networkReceiver; private NetworkReceiver networkReceiver;
private boolean isReceiverRegistered = false; private boolean isReceiverRegistered = false;
private ProgressBar progressBar;
TabLayout tabLayout; TabLayout tabLayout;
ViewPager viewPager; ViewPager viewPager;
PagerAdapter pagerAdapter; PagerAdapter pagerAdapter;
private String currentVersionName;
@SuppressLint("SourceLockedOrientationActivity") @SuppressLint("SourceLockedOrientationActivity")
@Override @Override
@ -102,6 +114,8 @@ public class HomeActivity extends AppCompatActivity implements View.OnClickListe
refreshReportsBtn = findViewById( R.id.refresh_btn ); refreshReportsBtn = findViewById( R.id.refresh_btn );
logoutBtn = findViewById( R.id.logout_btn ); logoutBtn = findViewById( R.id.logout_btn );
fetchProductBtn = findViewById( R.id.fetch_product_btn ); fetchProductBtn = findViewById( R.id.fetch_product_btn );
appUpdateBtn = findViewById(R.id.app_update_btn);
progressBar = findViewById(R.id.progressBar);
//recyclerView = findViewById( R.id.reports_recyclerview ); //recyclerView = findViewById( R.id.reports_recyclerview );
usernameTitle = findViewById( R.id.username_title ); usernameTitle = findViewById( R.id.username_title );
profileImage = findViewById( R.id.profile_image ); profileImage = findViewById( R.id.profile_image );
@ -123,6 +137,7 @@ public class HomeActivity extends AppCompatActivity implements View.OnClickListe
createReportBtn.setOnClickListener( this ); createReportBtn.setOnClickListener( this );
logoutBtn.setOnClickListener( this ); logoutBtn.setOnClickListener( this );
fetchProductBtn.setOnClickListener( this ); fetchProductBtn.setOnClickListener( this );
appUpdateBtn.setOnClickListener( this );
//recyclerView.setLayoutManager( new LinearLayoutManager( this ) ); //recyclerView.setLayoutManager( new LinearLayoutManager( this ) );
@ -202,8 +217,23 @@ public class HomeActivity extends AppCompatActivity implements View.OnClickListe
Toast.makeText( this, "network not available", Toast.LENGTH_LONG ).show(); Toast.makeText( this, "network not available", Toast.LENGTH_LONG ).show();
} }
} }
if ( id == R.id.app_update_btn) {
if (!FileUtils.isNetworkConnected(this)) {
Toast.makeText(this, "No Internet Connection", Toast.LENGTH_LONG).show();
}
else {
checkForUpdates();
}
}
} }
private int generateRandomNumber(int min, int max) {
return (int) (Math.random() * ((max - min) + 1)) + min;
}
private void updateProfileImage(){ private void updateProfileImage(){
SharedPreferences sharedPreferences = getSharedPreferences("login_prefs", Context.MODE_PRIVATE); SharedPreferences sharedPreferences = getSharedPreferences("login_prefs", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit(); SharedPreferences.Editor editor = sharedPreferences.edit();
@ -220,7 +250,7 @@ public class HomeActivity extends AppCompatActivity implements View.OnClickListe
editor.apply(); editor.apply();
usernameTitle.setText( response.body().getName() ); usernameTitle.setText( response.body().getName() );
store.setEmployeePhoto( response.body() ); store.setEmployeePhoto( response.body() );
Log.e("Profile-Image: ",""+store.getProfileImage()); //Log.e("Profile-Image: ",""+store.getProfileImage());
Glide.with( getBaseContext() ) Glide.with( getBaseContext() )
.load( store.getProfileImage( ) ) .load( store.getProfileImage( ) )
.into( profileImage ); .into( profileImage );
@ -466,4 +496,63 @@ public class HomeActivity extends AppCompatActivity implements View.OnClickListe
isReceiverRegistered = false; // Mark the receiver as unregistered isReceiverRegistered = false; // Mark the receiver as unregistered
} }
} }
/**
* check for updates
*/
private void checkForUpdates() {
Log.e("checkForUpdates: ","*****");
PackageManager packageManager = getPackageManager();
try {
PackageInfo info = packageManager.getPackageInfo( getPackageName(), 0 );
currentVersionName = info.versionName;
Log.e("currentVersionName: ",""+currentVersionName);
} catch ( Exception e ) {
Log.e("Version-Exception: ",""+e.getMessage());
e.printStackTrace();
}
// adjust views
progressBar.setVisibility( View.VISIBLE );
// get properties
Properties properties = PropertyReader.getProperties( HomeActivity.this );
String token = properties.getProperty( "gitea_access_token" );
// send request
GiteaRestService restService = GiteaClient.getInstance().create( GiteaRestService.class );
Call<Release> call = restService.getLatestRelease( token );
call.enqueue( new Callback<Release>() {
@Override
public void onResponse( Call<Release> call, Response<Release> response ) {
Log.e("Response: ",""+response.body());
if ( response.body() != null ) {
Release release = response.body();
try {
float releaseVersion = Float.parseFloat( release.getName().replace( "v", "" ) );
float currentVersion = Float.parseFloat( currentVersionName.replace( "v", "" ) );
if ( releaseVersion > currentVersion && !FileUtils.isNullOrEmpty( release.getDownloadUrl() ) ) {
String msg = String.format( "Updating to version %s", release.getName() );
Toast.makeText( HomeActivity.this, msg, Toast.LENGTH_LONG ).show();
Intent intent = new Intent( Intent.ACTION_VIEW, Uri.parse( release.getDownloadUrl() ) );
startActivity( intent );
//startApkDownloadAndPerformUpdate( release.getDownloadUrl(), release.getName() );
} else {
Toast.makeText( HomeActivity.this, "Latest version is already installed", Toast.LENGTH_LONG ).show();
}
} catch ( Exception e ) {
Log.e("Response-Exception: ",""+e.getMessage());
Toast.makeText( HomeActivity.this, e.getMessage(), Toast.LENGTH_LONG ).show();
e.printStackTrace();
}
}
progressBar.setVisibility( View.INVISIBLE );
}
@Override
public void onFailure( Call<Release> call, Throwable t ) {
progressBar.setVisibility( View.INVISIBLE );
Log.e("Failure: ",""+t.getMessage());
t.printStackTrace();
}
} );
}
} }

View File

@ -5,6 +5,8 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.pm.ActivityInfo; import android.content.pm.ActivityInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager; import android.net.ConnectivityManager;
import android.net.NetworkInfo; import android.net.NetworkInfo;
import android.os.Bundle; import android.os.Bundle;
@ -12,6 +14,7 @@ import android.util.Log;
import android.view.View; import android.view.View;
import android.widget.Button; import android.widget.Button;
import android.widget.ProgressBar; import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import androidx.activity.EdgeToEdge; import androidx.activity.EdgeToEdge;
@ -37,6 +40,7 @@ public class LoginActivity extends AppCompatActivity {
private ProgressBar progressBar; private ProgressBar progressBar;
private Button loginButton; private Button loginButton;
private InspectionReportService reportService; private InspectionReportService reportService;
TextView txtVersion;
@SuppressLint("SourceLockedOrientationActivity") @SuppressLint("SourceLockedOrientationActivity")
@Override @Override
@ -60,10 +64,13 @@ public class LoginActivity extends AppCompatActivity {
password = findViewById( R.id.password ); password = findViewById( R.id.password );
loginButton = findViewById( R.id.login_btn ); loginButton = findViewById( R.id.login_btn );
progressBar = findViewById( R.id.progressBar ); progressBar = findViewById( R.id.progressBar );
txtVersion = findViewById(R.id.txt_version);
SharedPreferences sharedPreferences = getSharedPreferences("login_prefs", Context.MODE_PRIVATE); SharedPreferences sharedPreferences = getSharedPreferences("login_prefs", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit(); SharedPreferences.Editor editor = sharedPreferences.edit();
setAppVersion();
loginButton.setOnClickListener(v -> { loginButton.setOnClickListener(v -> {
try { try {
progressBar.setVisibility( View.VISIBLE ); progressBar.setVisibility( View.VISIBLE );
@ -106,6 +113,17 @@ public class LoginActivity extends AppCompatActivity {
}); });
} }
public void setAppVersion() {
PackageManager packageManager = getPackageManager();
try {
PackageInfo info = packageManager.getPackageInfo( getPackageName(), 0 );
txtVersion.setText(String.format("Version: %s", info.versionName));
} catch ( Exception e ) {
e.printStackTrace();
}
}
private void isUserAlreadyLoginAndAuthorize() { private void isUserAlreadyLoginAndAuthorize() {
SharedPreferences sharedPreferences = getSharedPreferences("login_prefs", Context.MODE_PRIVATE); SharedPreferences sharedPreferences = getSharedPreferences("login_prefs", Context.MODE_PRIVATE);

View File

@ -0,0 +1,22 @@
package com.utopiaindustries.qualitychecker.utils;
import android.os.Build;
import androidx.annotation.RequiresApi;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class DateTimeUtils {
public static final String HTML5_DATETIME_INPUT_FORMAT_WITH_SECONDS = "yyyy-MM-dd'T'HH:mm:ss";
/**
* format localdatetime into a given format string
*/
@RequiresApi(api = Build.VERSION_CODES.O)
public static String getFormattedDateTimeString(LocalDateTime dateTime, String dateTimeFormat ) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( dateTimeFormat );
return dateTime.format( formatter );
}
}

View File

@ -4,6 +4,8 @@ import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri; import android.net.Uri;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
@ -204,4 +206,17 @@ public class FileUtils {
} }
}).start(); }).start();
} }
/**
* is null or empty
*/
public static boolean isNullOrEmpty( String str ) {
return str == null || str.isEmpty();
}
public static boolean isNetworkConnected(Context context) {
ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = connectivityManager.getActiveNetworkInfo();
return activeNetwork != null && activeNetwork.isConnectedOrConnecting();
}
} }

View File

@ -0,0 +1,25 @@
package com.utopiaindustries.qualitychecker.utils;
import android.content.Context;
import android.content.res.Resources;
import com.utopiaindustries.qualitychecker.R;
import java.io.InputStream;
import java.util.Properties;
public class PropertyReader {
public static Properties getProperties( Context context ) {
Properties properties = new Properties();
try {
Resources resources = context.getResources();
InputStream inputStream = resources.openRawResource( R.raw.application );
properties.load( inputStream );
} catch ( Exception e ) {
e.printStackTrace();
}
return properties;
}
}

View File

@ -0,0 +1,68 @@
package com.utopiaindustries.qualitychecker.utils;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.annotation.JsonNaming;
import java.util.List;
@JsonIgnoreProperties( ignoreUnknown = true )
@JsonNaming( PropertyNamingStrategy.SnakeCaseStrategy.class )
public class Release {
private String id;
private String tagName;
private String name;
private List<ReleaseAsset> assets;
public Release() {
}
public String getId() {
return id;
}
public void setId( String id ) {
this.id = id;
}
public String getTagName() {
return tagName;
}
public void setTagName( String tagName ) {
this.tagName = tagName;
}
public String getName() {
return name;
}
public void setName( String name ) {
this.name = name;
}
public List<ReleaseAsset> getAssets() {
return assets;
}
public void setAssets( List<ReleaseAsset> assets ) {
this.assets = assets;
}
public String getDownloadUrl() {
if ( this.assets == null || this.assets.isEmpty() ) {
return null;
}
return this.assets.get( 0 ).getBrowserDownloadUrl();
}
@Override
public String toString() {
return "Release{" +
"id='" + id + '\'' +
", tagName='" + tagName + '\'' +
", name='" + name + '\'' +
", assets=" + assets +
'}';
}
}

View File

@ -0,0 +1,95 @@
package com.utopiaindustries.qualitychecker.utils;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonNaming;
import com.utopiaindustries.qualitychecker.utils.jackson.ZonedDateTimeDeserializer;
import java.time.ZonedDateTime;
@JsonIgnoreProperties( ignoreUnknown = true )
@JsonNaming( PropertyNamingStrategy.SnakeCaseStrategy.class )
public class ReleaseAsset {
private String id;
private String name;
private long size;
private long downloadCount;
@JsonDeserialize( using = ZonedDateTimeDeserializer.class )
private ZonedDateTime createdAt;
private String uuid;
private String browserDownloadUrl;
public ReleaseAsset() {
}
public String getId() {
return id;
}
public void setId( String id ) {
this.id = id;
}
public String getName() {
return name;
}
public void setName( String name ) {
this.name = name;
}
public long getSize() {
return size;
}
public void setSize( long size ) {
this.size = size;
}
public long getDownloadCount() {
return downloadCount;
}
public void setDownloadCount( long downloadCount ) {
this.downloadCount = downloadCount;
}
public ZonedDateTime getCreatedAt() {
return createdAt;
}
public void setCreatedAt( ZonedDateTime createdAt ) {
this.createdAt = createdAt;
}
public String getUuid() {
return uuid;
}
public void setUuid( String uuid ) {
this.uuid = uuid;
}
public String getBrowserDownloadUrl() {
return browserDownloadUrl;
}
public void setBrowserDownloadUrl( String browserDownloadUrl ) {
this.browserDownloadUrl = browserDownloadUrl;
}
@Override
public String toString() {
return "ReleaseAsset{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", size=" + size +
", downloadCount=" + downloadCount +
", createdAt=" + createdAt +
", uuid='" + uuid + '\'' +
", browserDownloadUrl='" + browserDownloadUrl + '\'' +
'}';
}
}

View File

@ -0,0 +1,35 @@
package com.utopiaindustries.qualitychecker.utils.jackson;
import android.os.Build;
import androidx.annotation.RequiresApi;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.utopiaindustries.qualitychecker.utils.DateTimeUtils;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class LocalDateTimeDeserializer extends StdDeserializer<LocalDateTime> {
private static final long serialVersionUID = 1L;
@RequiresApi(api = Build.VERSION_CODES.O)
public LocalDateTimeDeserializer() {
super( LocalDateTime.class );
}
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
public LocalDateTime deserialize( JsonParser parser, DeserializationContext ctxt ) throws IOException, JsonProcessingException {
JsonNode node = parser.getCodec().readTree( parser );
String dateStr = node.asText();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( DateTimeUtils.HTML5_DATETIME_INPUT_FORMAT_WITH_SECONDS );
return LocalDateTime.parse( dateStr, formatter );
}
}

View File

@ -0,0 +1,25 @@
package com.utopiaindustries.qualitychecker.utils.jackson;
import android.os.Build;
import androidx.annotation.RequiresApi;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.utopiaindustries.qualitychecker.utils.DateTimeUtils;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
@RequiresApi(api = Build.VERSION_CODES.O)
public class LocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern( DateTimeUtils.HTML5_DATETIME_INPUT_FORMAT_WITH_SECONDS );
@Override
public void serialize( LocalDateTime value, JsonGenerator gen, SerializerProvider serializers ) throws IOException {
gen.writeString( value.format( FORMATTER ) );
}
}

View File

@ -0,0 +1,35 @@
package com.utopiaindustries.qualitychecker.utils.jackson;
import android.os.Build;
import androidx.annotation.RequiresApi;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
public class ZonedDateTimeDeserializer extends StdDeserializer<ZonedDateTime> {
private static final long serialVersionUID = 1L;
@RequiresApi(api = Build.VERSION_CODES.O)
public ZonedDateTimeDeserializer() {
super( LocalDateTime.class );
}
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
public ZonedDateTime deserialize( JsonParser parser, DeserializationContext ctxt ) throws IOException, JsonProcessingException {
JsonNode node = parser.getCodec().readTree( parser );
String dateStr = node.asText();
DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE_TIME;
return ZonedDateTime.parse( dateStr, formatter );
}
}

View File

@ -169,5 +169,25 @@
android:layout_toStartOf="@id/logout_btn" android:layout_toStartOf="@id/logout_btn"
android:text="Fetch" /> android:text="Fetch" />
<Button
android:id="@+id/app_update_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="@id/fetch_product_btn"
android:layout_marginEnd="16dp"
android:layout_toStartOf="@id/fetch_product_btn"
android:text="App Update" />
</RelativeLayout> </RelativeLayout>
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="invisible"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -120,4 +120,18 @@
app:layout_constraintTop_toBottomOf="@+id/imageView" app:layout_constraintTop_toBottomOf="@+id/imageView"
app:layout_constraintVertical_bias="0.079" /> app:layout_constraintVertical_bias="0.079" />
<TextView
android:id="@+id/txt_version"
android:layout_width="150dp"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_marginEnd="40dp"
android:padding="10dp"
android:gravity="right"
android:textSize="20sp"
android:textStyle="bold"
android:textColor="@color/black"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -198,6 +198,15 @@
</androidx.recyclerview.widget.RecyclerView> </androidx.recyclerview.widget.RecyclerView>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Dimension Images"
android:layout_marginStart="10dp"
android:textAlignment="center"
android:textColor="@color/black"
android:textStyle="bold" />
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="100dp" android:layout_height="100dp"
@ -351,7 +360,10 @@
android:layout_margin="5dp" android:layout_margin="5dp"
android:background="@drawable/box_border" android:background="@drawable/box_border"
android:orientation="vertical" android:orientation="vertical"
android:padding="10dp"> android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingTop="5dp"
android:paddingBottom="5dp">
<!-- Text Label --> <!-- Text Label -->
@ -428,7 +440,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" android:orientation="horizontal"
android:paddingLeft="3dp" android:paddingLeft="3dp"
android:paddingTop="10dp" android:paddingTop="5dp"
android:weightSum="3" android:weightSum="3"
android:paddingBottom="10dp"> android:paddingBottom="10dp">
@ -444,7 +456,7 @@
android:id="@+id/txt_major" android:id="@+id/txt_major"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="0.65"
android:text="Major" android:text="Major"
android:textSize="16sp" android:textSize="16sp"
android:textStyle="bold" /> android:textStyle="bold" />

View File

@ -0,0 +1 @@
gitea_access_token=3efaddad018362af36b515c091d66da34b97abe2

View File

@ -6,5 +6,6 @@
<domain includeSubdomains="true">192.168.91.44</domain> <domain includeSubdomains="true">192.168.91.44</domain>
<domain includeSubdomains="true">portal.utopiaindustries.pk</domain> <domain includeSubdomains="true">portal.utopiaindustries.pk</domain>
<domain includeSubdomains="true">utopiaindustries.pk</domain> <domain includeSubdomains="true">utopiaindustries.pk</domain>
<domain includeSubdomains="true">git.utopiadeals.com/api/v1/repos/UIND/Quality-Checker-Android-Releases/releases/39</domain>
</domain-config> </domain-config>
</network-security-config> </network-security-config>

View File

@ -1,4 +1,4 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules. // Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins { plugins {
alias(libs.plugins.androidApplication) apply false alias(libs.plugins.androidApplication) apply false
} }

View File

@ -1,5 +1,5 @@
[versions] [versions]
agp = "8.3.1" agp = "8.1.2"
junit = "4.13.2" junit = "4.13.2"
junitVersion = "1.1.5" junitVersion = "1.1.5"
espressoCore = "3.5.1" espressoCore = "3.5.1"