Commit e4849df6 by Nguyễn Thị Thúy

Merge branch 'phase_2_check_version' into 'dev_phase2'

Phase 2 check version

See merge request !5
parents e7ab62e4 901d9269
...@@ -132,8 +132,8 @@ android { ...@@ -132,8 +132,8 @@ android {
applicationId "com.dcv.invest" applicationId "com.dcv.invest"
minSdkVersion rootProject.ext.minSdkVersion minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 3 versionCode 2
versionName "1.3" versionName "1.2"
vectorDrawables.useSupportLibrary = true vectorDrawables.useSupportLibrary = true
} }
splits { splits {
...@@ -151,6 +151,12 @@ android { ...@@ -151,6 +151,12 @@ android {
keyAlias 'androiddebugkey' keyAlias 'androiddebugkey'
keyPassword 'android' keyPassword 'android'
} }
release {
storeFile file('KeyStore')
storePassword 'dcvinvest'
keyAlias 'dcvinvest'
keyPassword 'dcvinvest'
}
} }
buildTypes { buildTypes {
debug { debug {
...@@ -159,8 +165,9 @@ android { ...@@ -159,8 +165,9 @@ android {
release { release {
// Caution! In production, you need to generate your own keystore file. // Caution! In production, you need to generate your own keystore file.
// see https://facebook.github.io/react-native/docs/signed-apk-android. // see https://facebook.github.io/react-native/docs/signed-apk-android.
signingConfig signingConfigs.debug signingConfig signingConfigs.release
minifyEnabled enableProguardInReleaseBuilds minifyEnabled enableProguardInReleaseBuilds
debuggable true
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
} }
} }
...@@ -222,7 +229,8 @@ implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-alpha02' ...@@ -222,7 +229,8 @@ implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-alpha02'
implementation jscFlavor implementation jscFlavor
} }
implementation 'com.google.firebase:firebase-analytics:17.2.2' implementation 'com.google.firebase:firebase-analytics:17.2.2'
implementation 'com.google.android.play:core:1.7.3'
implementation 'com.google.android.material:material:1.0.0'
} }
// Run this once to be able to run the application with BUCK // Run this once to be able to run the application with BUCK
......
package com.dcv.invest;
import android.app.Activity;
import android.content.Intent;
import android.content.IntentSender;
import android.graphics.Color;
import androidx.annotation.NonNull;
import com.facebook.react.bridge.ActivityEventListener;
import com.facebook.react.bridge.BaseActivityEventListener;
import com.facebook.react.bridge.LifecycleEventListener;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.google.android.material.snackbar.Snackbar;
import com.google.android.play.core.appupdate.AppUpdateInfo;
import com.google.android.play.core.appupdate.AppUpdateManager;
import com.google.android.play.core.appupdate.AppUpdateManagerFactory;
import com.google.android.play.core.install.InstallState;
import com.google.android.play.core.install.InstallStateUpdatedListener;
import com.google.android.play.core.install.model.AppUpdateType;
import com.google.android.play.core.install.model.InstallStatus;
import com.google.android.play.core.install.model.UpdateAvailability;
import com.google.android.play.core.tasks.Task;
import java.util.Objects;
import static android.app.Activity.RESULT_OK;
public class InAppUpdateModule extends ReactContextBaseJavaModule implements InstallStateUpdatedListener, LifecycleEventListener {
private AppUpdateManager appUpdateManager;
private static ReactApplicationContext reactContext;
private static final int STALE_DAYS = 5;
private static final int MY_REQUEST_CODE = 0;
private final ActivityEventListener mActivityEventListener = new BaseActivityEventListener() {
@Override
public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent intent) {
if (requestCode == MY_REQUEST_CODE) {
if (resultCode != RESULT_OK) {
System.out.println("Update flow failed! Result code: " + resultCode);
// If the update is cancelled or fails,
// you can request to start the update again.
}
}
}
};
InAppUpdateModule(ReactApplicationContext context) {
super(context);
reactContext = context;
reactContext.addActivityEventListener(mActivityEventListener);
reactContext.addLifecycleEventListener(this);
}
@NonNull
@Override
public String getName() {
return "InAppUpdate";
}
@ReactMethod
public void checkUpdate() throws Exception {
appUpdateManager = AppUpdateManagerFactory.create(reactContext);
appUpdateManager.registerListener(this);
Task<AppUpdateInfo> appUpdateInfoTask = appUpdateManager.getAppUpdateInfo();
if (appUpdateInfoTask.getException() != null) throw appUpdateInfoTask.getException();
appUpdateInfoTask.addOnSuccessListener(appUpdateInfo -> {
if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
&& appUpdateInfo.clientVersionStalenessDays() != null
&& appUpdateInfo.clientVersionStalenessDays() > STALE_DAYS
&& appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)) {
try {
appUpdateManager.startUpdateFlowForResult(
appUpdateInfo,
AppUpdateType.IMMEDIATE,
reactContext.getCurrentActivity(),
MY_REQUEST_CODE);
} catch (IntentSender.SendIntentException e) {
e.printStackTrace();
}
} else {
if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
&& appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)) {
try {
appUpdateManager.startUpdateFlowForResult(
appUpdateInfo,
AppUpdateType.FLEXIBLE,
reactContext.getCurrentActivity(),
MY_REQUEST_CODE);
} catch (IntentSender.SendIntentException e) {
e.printStackTrace();
}
}
}
});
}
@Override
public void onStateUpdate(InstallState state) {
if (state.installStatus() == InstallStatus.DOWNLOADED) {
popupSnackbarForCompleteUpdate();
}
}
private void popupSnackbarForCompleteUpdate() {
Snackbar snackbar =
Snackbar.make(Objects.requireNonNull(reactContext.getCurrentActivity()).findViewById(android.R.id.content).getRootView(),
"An update has just been downloaded.",
Snackbar.LENGTH_INDEFINITE);
snackbar.setAction("RESTART", view -> appUpdateManager.completeUpdate());
snackbar.setActionTextColor(Color.GREEN);
snackbar.show();
}
@Override
public void onHostResume() {
if (appUpdateManager != null) {
appUpdateManager
.getAppUpdateInfo()
.addOnSuccessListener(
appUpdateInfo -> {
if (appUpdateInfo.installStatus() == InstallStatus.DOWNLOADED) {
popupSnackbarForCompleteUpdate();
}
if (appUpdateInfo.updateAvailability()
== UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS) {
try {
appUpdateManager.startUpdateFlowForResult(
appUpdateInfo,
AppUpdateType.IMMEDIATE,
reactContext.getCurrentActivity(),
MY_REQUEST_CODE);
} catch (IntentSender.SendIntentException e) {
e.printStackTrace();
}
}
});
}
}
@Override
public void onHostPause() {
}
@Override
public void onHostDestroy() {
if (appUpdateManager != null) {
appUpdateManager.unregisterListener(this);
}
}
}
package com.dcv.invest;
import androidx.annotation.NonNull;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class InAppUpdatePackage implements ReactPackage {
@NonNull
@Override
public List<NativeModule> createNativeModules(@NonNull ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
modules.add(new InAppUpdateModule(reactContext));
return modules;
}
@NonNull
@Override
public List<ViewManager> createViewManagers(@NonNull ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}
...@@ -29,6 +29,7 @@ public class MainApplication extends Application implements ReactApplication { ...@@ -29,6 +29,7 @@ public class MainApplication extends Application implements ReactApplication {
// Packages that cannot be autolinked yet can be added manually here, for example: // Packages that cannot be autolinked yet can be added manually here, for example:
// packages.add(new MyReactNativePackage()); // packages.add(new MyReactNativePackage());
// packages.add(new RNBootSplashPackage()); // packages.add(new RNBootSplashPackage());
packages.add(new InAppUpdatePackage());
return packages; return packages;
} }
......
...@@ -57,7 +57,7 @@ ...@@ -57,7 +57,7 @@
<key>NSCameraUsageDescription</key> <key>NSCameraUsageDescription</key>
<string>Accect connect camera</string> <string>Accect connect camera</string>
<key>NSLocationWhenInUseUsageDescription</key> <key>NSLocationWhenInUseUsageDescription</key>
<string></string> <string/>
<key>NSPhotoLibraryUsageDescription</key> <key>NSPhotoLibraryUsageDescription</key>
<string>To upload images</string> <string>To upload images</string>
<key>UIAppFonts</key> <key>UIAppFonts</key>
...@@ -92,5 +92,9 @@ ...@@ -92,5 +92,9 @@
</array> </array>
<key>UIViewControllerBasedStatusBarAppearance</key> <key>UIViewControllerBasedStatusBarAppearance</key>
<false/> <false/>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>itms-apps</string>
</array>
</dict> </dict>
</plist> </plist>
...@@ -8,17 +8,17 @@ import {SkypeIndicator} from 'react-native-indicators'; ...@@ -8,17 +8,17 @@ import {SkypeIndicator} from 'react-native-indicators';
import {enableScreens} from 'react-native-screens'; import {enableScreens} from 'react-native-screens';
import NoInternetComponent from './components/NoInternet'; import NoInternetComponent from './components/NoInternet';
import {getVersion} from 'react-native-device-info'; import DeviceInfo from 'react-native-device-info';
import VersionChecker from './Screens/VersionChecker';
enableScreens(); enableScreens();
const RootView = (props) => { const RootView = (props) => {
useEffect(() => { useEffect(() => {
checkVersion();
}, []); }, []);
const checkVersion = (props) => { const checkVersion = (props) => {
const verCurrent = getVersion(); const verCurrent = DeviceInfo.getVersion();
console.log('version current', verCurrent); console.log('version current', verCurrent);
}; };
...@@ -31,6 +31,7 @@ const RootView = (props) => { ...@@ -31,6 +31,7 @@ const RootView = (props) => {
<StackNavigation /> <StackNavigation />
</View> </View>
<VersionChecker/>
<NoInternetComponent /> <NoInternetComponent />
</> </>
); );
......
import React, {useEffect, useState} from 'react';
import {
View,
Text,
Modal,
TouchableOpacity,
StyleSheet,
Image,
TouchableWithoutFeedback,
Linking, Platform,
} from 'react-native';
import R from '../../assets/R';
import {getFontXD, getHeight, getWidth, HEIGHTXD, WIDTHXD} from '../../Config/Functions';
import I18n from '../../helper/i18/i18n';
import DeviceInfo from 'react-native-device-info';
import {getNewestVersionInfo} from '../../apis/Functions/users';
import InAppUpdate from '../../helper/InAppUpdate';
const VersionChecker = (props) => {
const [visible, setVisible] = useState(false);
const [isForceUpdate, setIsForceUpdate] = useState(false);
const [version, setVersion] = useState('1.0');
useEffect(() => {
checkVersion();
}, []);
const checkVersion = async () => {
const verCurrent = DeviceInfo.getVersion();
const res = await getNewestVersionInfo({
platform: Platform.OS,
});
if ((res.data.code = 200 && res.data.data)) {
console.log(res);
if (res.data.data[0].version_name !== verCurrent || res.data.data[0].build.toString() !== DeviceInfo.getBuildNumber) {
setVersion(res.data.data[0].version_name);
setVisible(true);
setIsForceUpdate(res.data.data[0].is_require_update == 0 ? false : true);
}
}
};
const setVisibleModal = (visible, version, isForceUpdate) => {
setVisible(visible);
setVersion(version);
setIsForceUpdate(isForceUpdate);
};
const _renderDivider = () => <View style={styles.dividerStyle}/>;
const onOutsidePressed = () => {
setVisible(false);
};
const onUpdatePressed = async () => {
try {
if (Platform.OS === 'ios') {
Linking.openURL('itms-apps://itunes.apple.com/us/app/dcv-invest/id1556621903?l=id');
} else {
// try {
// InAppUpdate.checkUpdate()
// } catch(e) {
// console.log(e)
// }
Linking.openURL('https://play.google.com/store/apps/details?id=com.dcv.invest');
}
} catch (error) {
}
};
const onRequestClose = () => null;
const renderBackdrop = () => {
return (
<View
style={{
backgroundColor: 'rgba(0,0,0,0.30)',
// backgroundColor: 'red',
position: 'absolute',
top: 0,
bottom: 0,
left: 0,
right: 0,
width: getWidth(),
height: getHeight(),
}}
/>
);
};
const cancelUpdate = () => {
setVisible(false);
};
return (
<Modal
onRequestClose={() => onRequestClose()}
transparent
animationType="fade"
style={{position: 'absolute'}}
visible={visible}
>
{renderBackdrop()}
<View pointerEvents="box-none" style={styles.containerStyle}>
<View style={styles.imageUpgradeContainer} zIndex={100}>
<Image
source={R.images.iconUpgrade}
style={[styles.imageUpgradeStyle, {tintColor: R.colors.main}]}/>
</View>
<View style={styles.contentContainerStyle}>
<Text style={styles.titleStyle}>{I18n.t('Update')}</Text>
<Text style={styles.versionLabelStyle}>
{I18n.t('Version')}
{': '}
{version}
</Text>
<Text style={styles.descStyle}>{I18n.t('UpdateDescription')}</Text>
{_renderDivider()}
{isForceUpdate ?
<TouchableOpacity onPress={() => onUpdatePressed()} style={styles.notNowContainerStyle}>
<Text
style={[styles.textNotNowStyle, {color: R.colors.main}]}>{I18n.t('Update')}</Text>
</TouchableOpacity>
:
<View style={[styles.notNowContainerStyle, {
flexDirection: 'row',
marginHorizontal: WIDTHXD(100),
}]}>
<TouchableOpacity onPress={() => cancelUpdate()}
style={[styles.btnButton, {paddingRight: WIDTHXD(90)}]}>
<Text
style={[styles.textNotNowStyle, {color: R.colors.color777}]}>{I18n.t('Cancel')}</Text>
</TouchableOpacity>
<View style={styles.dividerStyleVertical}></View>
<TouchableOpacity onPress={() => onUpdatePressed()}
style={[styles.btnButton, {paddingLeft: WIDTHXD(90)}]}>
<Text
style={[styles.textNotNowStyle, {color: R.colors.main}]}>{I18n.t('Update')}</Text>
</TouchableOpacity>
</View>
}
</View>
</View>
</Modal>
);
};
const styles = StyleSheet.create({
imageUpgradeStyle: {
width: 50,
height: 50,
tintColor: R.colors.primaryColor,
},
imageUpgradeContainer: {
width: 80,
height: 80,
borderRadius: 40,
backgroundColor: 'white',
alignItems: 'center',
justifyContent: 'center',
},
containerStyle: {
flex: 1,
width: getWidth() * 0.8,
alignSelf: 'center',
alignItems: 'center',
justifyContent: 'center',
},
contentContainerStyle: {
marginTop: -40,
paddingTop: 40,
width: getWidth() * 0.9,
backgroundColor: 'white',
alignItems: 'center',
justifyContent: 'center',
borderRadius: 15,
},
logoStyle: {
width: 65,
height: 65,
marginVertical: 20,
},
titleStyle: {
fontWeight: '600',
fontSize: 20,
color: 'black',
paddingHorizontal: 8,
textAlign: 'center',
},
versionLabelStyle: {
fontSize: 14,
color: R.colors.grey600,
marginTop: 5,
marginBottom: 15,
paddingHorizontal: 8,
textAlign: 'center',
},
descStyle: {
fontSize: getFontXD(46),
color: R.colors.grey900,
marginBottom: 20,
paddingHorizontal: 10,
textAlign: 'center',
},
notNowContainerStyle: {
height: HEIGHTXD(160),
width: '100%',
alignItems: 'center',
justifyContent: 'center',
},
btnButton: {
flex: 0,
},
textNotNowStyle: {
fontSize: getFontXD(46),
width: '100%',
textAlign: 'center',
color: R.colors.primaryColor,
},
starContainer: {
flexDirection: 'row',
width: '100%',
justifyContent: 'center',
alignItems: 'center',
height: 45,
},
dividerStyle: {
height: 0.5,
width: '100%',
backgroundColor: R.colors.borderC,
},
dividerStyleVertical: {
height: HEIGHTXD(160),
width: 0.5,
backgroundColor: R.colors.borderC,
},
});
export default VersionChecker;
...@@ -56,3 +56,8 @@ export const updateLangugeApi = async (body) => ...@@ -56,3 +56,8 @@ export const updateLangugeApi = async (body) =>
PostData(url.urlUpdateLanguage, body) PostData(url.urlUpdateLanguage, body)
.then((res) => res) .then((res) => res)
.catch((err) => err); .catch((err) => err);
export const getNewestVersionInfo = async (body) =>
GetData(url.urlGetNewestVersionInfo, body)
.then((res) => res)
.catch((err) => err);
...@@ -37,4 +37,5 @@ export default { ...@@ -37,4 +37,5 @@ export default {
urlUpdateInforUser: root + 'api/v1/customers/update-general-info', urlUpdateInforUser: root + 'api/v1/customers/update-general-info',
urlUpdateLanguage: root + 'api/v1/customers/update-language', urlUpdateLanguage: root + 'api/v1/customers/update-language',
urlGetNewestVersionInfo: root + 'api/v1/settings/version',
}; };
...@@ -85,6 +85,7 @@ const images = { ...@@ -85,6 +85,7 @@ const images = {
iconHistoryMenu: require('./images/iconHistoryMenu1.png'), iconHistoryMenu: require('./images/iconHistoryMenu1.png'),
iconProfileMenu: require('./images/iconProfileMenu.png'), iconProfileMenu: require('./images/iconProfileMenu.png'),
iconUpgrade: require('./images/iconUpgrade.png'),
}; };
export default images; export default images;
import {NativeModules} from 'react-native'
module.exports = NativeModules.InAppUpdate
...@@ -74,7 +74,7 @@ export default { ...@@ -74,7 +74,7 @@ export default {
Waiting: 'Waiting', Waiting: 'Waiting',
Success: 'Success', Success: 'Success',
Ok: 'Ok', Ok: 'Ok',
Can_not_get_data: "Can't get data", Can_not_get_data: 'Can\'t get data',
Search: 'Search', Search: 'Search',
NullDataSearch: 'Data not found', NullDataSearch: 'Data not found',
...@@ -215,4 +215,6 @@ export default { ...@@ -215,4 +215,6 @@ export default {
Price_List: 'Price list', Price_List: 'Price list',
Time_Transaction: 'Time transaction', Time_Transaction: 'Time transaction',
Due_Date: 'Due date', Due_Date: 'Due date',
Version: 'Version',
UpdateDescription: 'A new version of DCVInvest is available. Update now to continue using and experiencing the latest system features!',
}; };
...@@ -220,4 +220,6 @@ export default { ...@@ -220,4 +220,6 @@ export default {
Time_Transaction: 'Thời gian GD', Time_Transaction: 'Thời gian GD',
Due_Date: 'Lịch đáo hạn', Due_Date: 'Lịch đáo hạn',
Enter_Phone: 'Nhập số điện thoại', Enter_Phone: 'Nhập số điện thoại',
Version: 'Phiên bản',
UpdateDescription: 'Đã có phiên bản DCVInvest mới. Cập nhật ngay để tiếp tục sử dụng và trải nghiệm những tính năng mới nhất của hệ thống!',
}; };
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