Browse Source

feat: REPORT-70565 设计器环境监测(jar包异常、finedb、杀毒软件)

1-修改退出时的逻辑展示
2-添加 fatalError 的检测
3-工具栏效果
4-优化一下 MessageWithLink 的使用方式
5-补充超链接
feature/x
Harrison 3 years ago
parent
commit
58dc6d4031
  1. 20
      designer-base/src/main/java/com/fr/design/DesignerEnvManager.java
  2. 23
      designer-base/src/main/java/com/fr/design/components/notification/NotificationDialog.java
  3. 12
      designer-base/src/main/java/com/fr/design/dialog/link/MessageWithLink.java
  4. 3
      designer-base/src/main/java/com/fr/design/mainframe/toolbar/ToolBarMenuDock.java
  5. 4
      designer-base/src/main/java/com/fr/design/utils/DevDebugUtils.java
  6. 13
      designer-base/src/main/java/com/fr/design/utils/DevUtils.java
  7. 5
      designer-base/src/main/java/com/fr/env/detect/EnvPrepare.java
  8. 4
      designer-base/src/main/java/com/fr/env/detect/base/DetectorConstants.java
  9. 56
      designer-base/src/main/java/com/fr/exit/DesignerExiter.java
  10. 2
      designer-base/src/test/java/com/fr/design/components/notification/NotificationDialogTest.java
  11. 25
      designer-base/src/test/java/com/fr/env/detect/ui/DetectorErrorDialogTest.java
  12. 30
      designer-realize/src/main/java/com/fr/start/LifecycleFatalErrorHandler.java
  13. 2
      designer-realize/src/main/java/com/fr/start/MainDesigner.java

20
designer-base/src/main/java/com/fr/design/DesignerEnvManager.java

@ -29,6 +29,7 @@ import com.fr.design.style.color.ColorSelectConfigManager;
import com.fr.design.update.push.DesignerPushUpdateConfigManager; import com.fr.design.update.push.DesignerPushUpdateConfigManager;
import com.fr.design.utils.DesignUtils; import com.fr.design.utils.DesignUtils;
import com.fr.design.utils.DesignerPort; import com.fr.design.utils.DesignerPort;
import com.fr.env.detect.base.EnvDetectorConfig;
import com.fr.exit.DesignerExiter; import com.fr.exit.DesignerExiter;
import com.fr.file.FILEFactory; import com.fr.file.FILEFactory;
import com.fr.general.ComparatorUtils; import com.fr.general.ComparatorUtils;
@ -168,6 +169,12 @@ public class DesignerEnvManager implements XMLReadable, XMLWriter {
private boolean embedServerLazyStartup = false; private boolean embedServerLazyStartup = false;
//最近使用的颜色 //最近使用的颜色
private ColorSelectConfigManager configManager = new ColorSelectConfigManager(); private ColorSelectConfigManager configManager = new ColorSelectConfigManager();
/**
* 环境检测配置
*/
private EnvDetectorConfig envDetectorConfig = EnvDetectorConfig.getInstance();
/** /**
* alphafine * alphafine
*/ */
@ -1820,6 +1827,8 @@ public class DesignerEnvManager implements XMLReadable, XMLWriter {
readActiveStatus(reader); readActiveStatus(reader);
} else if (ComparatorUtils.equals(CAS_PARAS, name)) { } else if (ComparatorUtils.equals(CAS_PARAS, name)) {
readHttpsParas(reader); readHttpsParas(reader);
} else if (name.equals(EnvDetectorConfig.XML_TAG)) {
readEnvDetectorConfig(reader);
} else if (name.equals("AlphaFineConfigManager")) { } else if (name.equals("AlphaFineConfigManager")) {
readAlphaFineAttr(reader); readAlphaFineAttr(reader);
} else if (name.equals("RecentColors")) { } else if (name.equals("RecentColors")) {
@ -1857,6 +1866,10 @@ public class DesignerEnvManager implements XMLReadable, XMLWriter {
reader.readXMLObject(this.alphaFineConfigManager = AlphaFineConfigManager.getInstance()); reader.readXMLObject(this.alphaFineConfigManager = AlphaFineConfigManager.getInstance());
} }
private void readEnvDetectorConfig(XMLableReader reader) {
reader.readXMLObject(this.envDetectorConfig);
}
private void readHttpsParas(XMLableReader reader) { private void readHttpsParas(XMLableReader reader) {
String tempVal; String tempVal;
if ((tempVal = reader.getAttrAsString(CAS_CERTIFICATE_PATH, null)) != null) { if ((tempVal = reader.getAttrAsString(CAS_CERTIFICATE_PATH, null)) != null) {
@ -2070,6 +2083,7 @@ public class DesignerEnvManager implements XMLReadable, XMLWriter {
writeActiveStatus(writer); writeActiveStatus(writer);
writeHttpsParas(writer); writeHttpsParas(writer);
writeAlphaFineAttr(writer); writeAlphaFineAttr(writer);
writeEnvDetectorConfig(writer);
writeRecentColor(writer); writeRecentColor(writer);
writeOpenDebug(writer); writeOpenDebug(writer);
writeDesignerPushUpdateAttr(writer); writeDesignerPushUpdateAttr(writer);
@ -2114,6 +2128,12 @@ public class DesignerEnvManager implements XMLReadable, XMLWriter {
} }
} }
private void writeEnvDetectorConfig(XMLPrintWriter writer) {
if (this.envDetectorConfig != null) {
this.envDetectorConfig.writeXML(writer);
}
}
//写入uuid //写入uuid
private void writeUUID(XMLPrintWriter writer) { private void writeUUID(XMLPrintWriter writer) {
writer.startTAG("uuid"); writer.startTAG("uuid");

23
designer-base/src/main/java/com/fr/design/components/notification/NotificationDialog.java

@ -10,7 +10,8 @@ import com.fr.design.gui.ilable.UILabel;
import com.fr.design.i18n.Toolkit; import com.fr.design.i18n.Toolkit;
import com.fr.design.layout.FRGUIPaneFactory; import com.fr.design.layout.FRGUIPaneFactory;
import com.fr.design.layout.VerticalFlowLayout; import com.fr.design.layout.VerticalFlowLayout;
import com.fr.env.detect.base.ExceptionDetectorConfig; import com.fr.design.utils.LinkStrUtils;
import com.fr.env.detect.base.EnvDetectorConfig;
import javax.swing.BorderFactory; import javax.swing.BorderFactory;
import javax.swing.Icon; import javax.swing.Icon;
@ -135,6 +136,8 @@ public class NotificationDialog extends JDialog {
contentPanel.add(iconPanel, BorderLayout.WEST); contentPanel.add(iconPanel, BorderLayout.WEST);
JPanel centerPanel = FRGUIPaneFactory.createBorderLayout_S_Pane();
centerPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 5, 20));
NotificationMessage[] messages = model.getMessages(); NotificationMessage[] messages = model.getMessages();
List<? extends JComponent> messageComponents = Arrays.stream(messages) List<? extends JComponent> messageComponents = Arrays.stream(messages)
@ -145,7 +148,17 @@ public class NotificationDialog extends JDialog {
Desktop.getDesktop().browse(URI.create(linkMessage.getLink())); Desktop.getDesktop().browse(URI.create(linkMessage.getLink()));
})); }));
} }
return new UILabel(messageModel.format()); return new UILabel(LinkStrUtils.generateHtmlTag(messageModel.format()));
})
.peek((component) -> {
Dimension preferredSize = component.getPreferredSize();
double componentWidth = preferredSize.getWidth();
double componentHeight = preferredSize.getHeight();
double widthFactor = Math.ceil(componentWidth / 300);
double heightFactor = Math.ceil(componentHeight / 15);
int realHeight = (int) (heightFactor + widthFactor - 1) * 15;
component.setPreferredSize(new Dimension(300, realHeight));
}) })
.collect(Collectors.toList()); .collect(Collectors.toList());
@ -153,11 +166,9 @@ public class NotificationDialog extends JDialog {
JPanel messageSummaryPanel = FRGUIPaneFactory.createVerticalFlowLayout_Pane(true, VerticalFlowLayout.TOP, 0, 0); JPanel messageSummaryPanel = FRGUIPaneFactory.createVerticalFlowLayout_Pane(true, VerticalFlowLayout.TOP, 0, 0);
messageComponents.forEach(messageSummaryPanel::add); messageComponents.forEach(messageSummaryPanel::add);
JPanel centerPanel = FRGUIPaneFactory.createBorderLayout_L_Pane();
centerPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 5, 20));
JScrollPane jScrollPane = new JScrollPane(messageSummaryPanel, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); JScrollPane jScrollPane = new JScrollPane(messageSummaryPanel, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
jScrollPane.setBorder(BorderFactory.createEmptyBorder()); jScrollPane.setBorder(BorderFactory.createEmptyBorder());
centerPanel.add(jScrollPane, BorderLayout.CENTER); centerPanel.add(jScrollPane, BorderLayout.CENTER);
centerPanel.setPreferredSize(contentSize); centerPanel.setPreferredSize(contentSize);
@ -211,7 +222,7 @@ public class NotificationDialog extends JDialog {
@Override @Override
public void mouseClicked(MouseEvent e) { public void mouseClicked(MouseEvent e) {
// 配置处理 // 配置处理
ExceptionDetectorConfig.getInstance().setOpen(false); EnvDetectorConfig.getInstance().setEnabled(false);
// 点击事件 // 点击事件
destroy(); destroy();
} }

12
designer-base/src/main/java/com/fr/design/dialog/link/MessageWithLink.java

@ -23,10 +23,16 @@ import static com.fr.design.utils.LinkStrUtils.LABEL;
*/ */
public class MessageWithLink extends JEditorPane { public class MessageWithLink extends JEditorPane {
/**
* 直接放入 html 内容
* 如果有超链接标签 <a href=""></a> 的话将会自动点击匹配 url
*
* @param htmlText html内容
*/
public MessageWithLink(String htmlText) { public MessageWithLink(String htmlText) {
super("text/html", htmlText); super("text/html", htmlText);
initListener();
setEditable(false); setEditable(false);
setBorder(null); setBorder(null);
} }
@ -71,7 +77,7 @@ public class MessageWithLink extends JEditorPane {
setBorder(null); setBorder(null);
} }
public void initListener() { protected void initListener() {
addHyperlinkListener(hyperlinkEvent -> { addHyperlinkListener(hyperlinkEvent -> {
try { try {
@ -85,7 +91,7 @@ public class MessageWithLink extends JEditorPane {
}); });
} }
public void initListener(Runnable runnable) { protected void initListener(Runnable runnable) {
addHyperlinkListener(e -> { addHyperlinkListener(e -> {
if (e.getEventType().equals(HyperlinkEvent.EventType.ACTIVATED)) { if (e.getEventType().equals(HyperlinkEvent.EventType.ACTIVATED)) {

3
designer-base/src/main/java/com/fr/design/mainframe/toolbar/ToolBarMenuDock.java

@ -64,6 +64,7 @@ import com.fr.design.os.impl.SupportOSImpl;
import com.fr.design.remote.action.RemoteDesignAuthManagerAction; import com.fr.design.remote.action.RemoteDesignAuthManagerAction;
import com.fr.design.update.actions.SoftwareUpdateAction; import com.fr.design.update.actions.SoftwareUpdateAction;
import com.fr.design.utils.ThemeUtils; import com.fr.design.utils.ThemeUtils;
import com.fr.env.detect.ui.EnvDetectorAction;
import com.fr.general.ComparatorUtils; import com.fr.general.ComparatorUtils;
import com.fr.general.GeneralContext; import com.fr.general.GeneralContext;
import com.fr.general.locale.LocaleAction; import com.fr.general.locale.LocaleAction;
@ -573,6 +574,8 @@ public abstract class ToolBarMenuDock {
shortCuts.add(new AlphaFineAction()); shortCuts.add(new AlphaFineAction());
} }
shortCuts.add(new EnvDetectorAction());
shortCuts.add(SeparatorDef.DEFAULT); shortCuts.add(SeparatorDef.DEFAULT);
if (DesignerEnvManager.getEnvManager().isOpenDebug()) { if (DesignerEnvManager.getEnvManager().isOpenDebug()) {
OSSupportCenter.buildAction(objects -> shortCuts.add(new FineUIAction()), SupportOSImpl.FINEUI); OSSupportCenter.buildAction(objects -> shortCuts.add(new FineUIAction()), SupportOSImpl.FINEUI);

4
designer-base/src/main/java/com/fr/design/utils/DevDebugUtil.java → designer-base/src/main/java/com/fr/design/utils/DevDebugUtils.java

@ -3,10 +3,10 @@ package com.fr.design.utils;
/** /**
* created by Harrison on 2022/05/26 * created by Harrison on 2022/05/26
**/ **/
public class DevDebugUtil { public class DevDebugUtils {
public static void main(String[] args) { public static void main(String[] args) {
org.swingexplorer.Launcher.main(new String[]{"com.fr.design.utils.DevUtil"}); org.swingexplorer.Launcher.main(new String[]{"com.fr.design.utils.DevUtils"});
} }
} }

13
designer-base/src/main/java/com/fr/design/utils/DevUtils.java

@ -5,13 +5,6 @@ import com.fr.config.dao.impl.LocalClassHelperDao;
import com.fr.config.dao.impl.LocalEntityDao; import com.fr.config.dao.impl.LocalEntityDao;
import com.fr.config.dao.impl.LocalXmlEntityDao; import com.fr.config.dao.impl.LocalXmlEntityDao;
import com.fr.design.ui.util.UIUtil; import com.fr.design.ui.util.UIUtil;
import com.fr.env.detect.bean.DetectorResult;
import com.fr.env.detect.bean.DetectorType;
import com.fr.env.detect.bean.ExceptionLog;
import com.fr.env.detect.bean.ExceptionSolution;
import com.fr.env.detect.bean.ExceptionTips;
import com.fr.env.detect.ui.DetectorErrorDialog;
import com.fr.third.guava.collect.Lists;
import com.fr.transaction.Configurations; import com.fr.transaction.Configurations;
import com.fr.transaction.LocalConfigurationHelper; import com.fr.transaction.LocalConfigurationHelper;
@ -61,12 +54,6 @@ public class DevUtils {
DevUtils.show(new Consumer<Frame>() { DevUtils.show(new Consumer<Frame>() {
@Override @Override
public void accept(Frame frame) { public void accept(Frame frame) {
DetectorErrorDialog dialog = new DetectorErrorDialog(frame, Lists.newArrayList(DetectorResult.exception(DetectorType.FINE_DB_PERMISSION,
ExceptionTips.create("test"),
ExceptionSolution.create("111<a href=\"\">test</a>222", "www.baidu.com", null),
ExceptionLog.create("log"))));
dialog.setVisible(true);
} }
}); });
} }

5
designer-base/src/main/java/com/fr/env/detect/EnvPrepare.java vendored

@ -4,6 +4,11 @@ import com.fr.module.Activator;
/** /**
* 设计器环境准备 * 设计器环境准备
* 更多的是一些钩子需要在环境启动切换时进行处理
* 使用监听 {@link com.fr.workspace.WorkspaceEvent} 只能满足
* before -> stop -> start -> after
* 现在支持 =>
* before -> stop -> prepare -> start -> after
* *
* created by Harrison on 2022/05/29 * created by Harrison on 2022/05/29
**/ **/

4
designer-base/src/main/java/com/fr/env/detect/base/DetectorConstants.java vendored

@ -5,7 +5,7 @@ package com.fr.env.detect.base;
**/ **/
public class DetectorConstants { public class DetectorConstants {
public static final String JAR_HELP_LINK = ""; public static final String JAR_HELP_LINK = "https://help.fanruan.com/finereport/doc-view-4700.html?source=3";
public static final String FINE_DB_HELP_LINK = ""; public static final String FINE_DB_HELP_LINK = "https://help.fanruan.com/finereport/doc-view-4701.html?source=3";
} }

56
designer-base/src/main/java/com/fr/exit/DesignerExiter.java

@ -34,36 +34,46 @@ public class DesignerExiter {
public void exit(Throwable throwable) { public void exit(Throwable throwable) {
FineLoggerFactory.getLogger().error(throwable.getMessage(), throwable);
List<DetectorResult> results = EnvDetectorCenter.getInstance().terminate(throwable); List<DetectorResult> results = EnvDetectorCenter.getInstance().terminate(throwable);
if (Collections.isEmpty(results)) { if (Collections.isEmpty(results)) {
DesignerFrame designerFrame = DesignerContext.getDesignerFrame(); // 为空,则
DetectorErrorDialog errorDialog = new DetectorErrorDialog(designerFrame, results); showOldExitDialog(throwable);
errorDialog.setVisible(true);
} else { } else {
FineLoggerFactory.getLogger().error(throwable.getMessage(), throwable); showNewExitDialog(results);
StartErrorMessageCollector.getInstance().record(DesignerErrorMessage.UNEXCEPTED_START_FAILED.getId(),
DesignerErrorMessage.UNEXCEPTED_START_FAILED.getMessage(),
throwable.getMessage());
ErrorDialog dialog = new ErrorDialog(null, Toolkit.i18nText("Fine-Design_Error_Start_Apology_Message"),
Toolkit.i18nText("Fine-Design_Error_Start_Report"),
throwable.getMessage()) {
@Override
protected void okEvent() {
dispose();
DesignerExiter.getInstance().execute();
}
@Override
protected void restartEvent() {
dispose();
RestartHelper.restart();
}
};
dialog.setVisible(true);
} }
} }
private void showNewExitDialog(List<DetectorResult> results) {
DesignerFrame designerFrame = DesignerContext.getDesignerFrame();
DetectorErrorDialog errorDialog = new DetectorErrorDialog(designerFrame, results);
errorDialog.setVisible(true);
}
private void showOldExitDialog(Throwable throwable) {
StartErrorMessageCollector.getInstance().record(DesignerErrorMessage.UNEXCEPTED_START_FAILED.getId(),
DesignerErrorMessage.UNEXCEPTED_START_FAILED.getMessage(),
throwable.getMessage());
ErrorDialog dialog = new ErrorDialog(null, Toolkit.i18nText("Fine-Design_Error_Start_Apology_Message"),
Toolkit.i18nText("Fine-Design_Error_Start_Report"),
throwable.getMessage()) {
@Override
protected void okEvent() {
dispose();
DesignerExiter.getInstance().execute();
}
@Override
protected void restartEvent() {
dispose();
RestartHelper.restart();
}
};
dialog.setVisible(true);
}
public void execute() { public void execute() {
DesignerLifecycleMonitorContext.getMonitor().beforeStop(); DesignerLifecycleMonitorContext.getMonitor().beforeStop();
beforeExit(); beforeExit();

2
designer-base/src/test/java/com/fr/design/components/notification/NotificationDialogTest.java

@ -31,7 +31,7 @@ public class NotificationDialogTest {
public void run(Object... args) { public void run(Object... args) {
System.out.println("1111"); System.out.println("1111");
} }
}, new NotificationMessage.LinkMessage("display <a href='#'>model2</a> test", "abc")); }, new NotificationMessage.LinkMessage("1111 2222 33333333 4444 555 6666 66555 888 999 333 <br/> 3333", ""),new NotificationMessage.LinkMessage("display <a href='#'>model2</a> test", "abc"));
NotificationDialogProperties properties = new NotificationDialogProperties(frame, "test"); NotificationDialogProperties properties = new NotificationDialogProperties(frame, "test");
NotificationDialog dialog = new NotificationDialog(properties, Lists.newArrayList(model1, model2)); NotificationDialog dialog = new NotificationDialog(properties, Lists.newArrayList(model1, model2));

25
designer-base/src/test/java/com/fr/env/detect/ui/DetectorErrorDialogTest.java vendored

@ -1,8 +1,33 @@
package com.fr.env.detect.ui; package com.fr.env.detect.ui;
import com.fr.design.utils.DevUtils;
import com.fr.env.detect.bean.DetectorResult;
import com.fr.env.detect.bean.DetectorType;
import com.fr.env.detect.bean.ExceptionLog;
import com.fr.env.detect.bean.ExceptionSolution;
import com.fr.env.detect.bean.ExceptionTips;
import com.fr.third.guava.collect.Lists;
import java.awt.Frame;
import java.util.function.Consumer;
import static org.junit.Assert.*; import static org.junit.Assert.*;
public class DetectorErrorDialogTest { public class DetectorErrorDialogTest {
public static void main(String[] args) {
DevUtils.show(new Consumer<Frame>() {
@Override
public void accept(Frame frame) {
DetectorErrorDialog dialog = new DetectorErrorDialog(frame, Lists.newArrayList(DetectorResult.exception(DetectorType.FINE_DB_PERMISSION,
ExceptionTips.create("test"),
ExceptionSolution.create("111<a href=\"\">test</a>222", "www.baidu.com", null),
ExceptionLog.create("log"))));
dialog.setVisible(true);
}
});
}
} }

30
designer-realize/src/main/java/com/fr/start/LifecycleFatalErrorHandler.java

@ -116,30 +116,18 @@ public class LifecycleFatalErrorHandler {
// todo 其实这里的交互还是有问题, 为什么在锁住和没权限的场景下,要重置 FineDB 呢。 // todo 其实这里的交互还是有问题, 为什么在锁住和没权限的场景下,要重置 FineDB 呢。
DetectorResult detectorResult = DetectorBridge.getInstance().detect(DetectorType.FINE_DB_LOCKED, fatal); DetectorResult detectorResult = DetectorBridge.getInstance().detect(DetectorType.FINE_DB_LOCKED, fatal);
if (detectorResult.getStatus() == DetectorStatus.NORMAL) { if (detectorResult.getStatus() == DetectorStatus.EXCEPTION) {
detectorResult = DetectorBridge.getInstance().detect(DetectorType.FINE_DB_PERMISSION, fatal); return Toolkit.i18nText("Fine-Design_Error_Finedb_Locked_Backup_Reset");
} }
if (detectorResult.getStatus() == DetectorStatus.NORMAL) { detectorResult = DetectorBridge.getInstance().detect(DetectorType.FINE_DB_PERMISSION, fatal);
detectorResult = DetectorBridge.getInstance().detect(DetectorType.FINE_DB_DIRTY, fatal); if (detectorResult.getStatus() == DetectorStatus.EXCEPTION) {
return Toolkit.i18nText("Fine-Design_Error_Finedb_Permission_Backup_Reset");
} }
detectorResult = DetectorBridge.getInstance().detect(DetectorType.FINE_DB_DIRTY, fatal);
String message; if (detectorResult.getStatus() == DetectorStatus.EXCEPTION) {
return Toolkit.i18nText("Fine-Design_Error_Finedb_Dirty_Backup_Reset");
DetectorType type = detectorResult.getType();
switch (type) {
case FINE_DB_LOCKED:
message = Toolkit.i18nText("Fine-Design_Error_Finedb_Locked_Backup_Reset");
break;
case FINE_DB_PERMISSION:
message = Toolkit.i18nText("Fine-Design_Error_Finedb_Permission_Backup_Reset");
break;
case FINE_DB_DIRTY:
message = Toolkit.i18nText("Fine-Design_Error_Finedb_Dirty_Backup_Reset");
break;
default:
message = Toolkit.i18nText("Fine-Design_Error_Finedb_Backup_Reset");
} }
return message; return Toolkit.i18nText("Fine-Design_Error_Finedb_Backup_Reset");
} }
private void afterBackupFailed() { private void afterBackupFailed() {

2
designer-realize/src/main/java/com/fr/start/MainDesigner.java

@ -46,7 +46,6 @@ import com.fr.design.share.SharableManager;
import com.fr.design.ui.util.UIUtil; import com.fr.design.ui.util.UIUtil;
import com.fr.design.utils.concurrent.ThreadFactoryBuilder; import com.fr.design.utils.concurrent.ThreadFactoryBuilder;
import com.fr.design.utils.gui.GUICoreUtils; import com.fr.design.utils.gui.GUICoreUtils;
import com.fr.env.detect.EnvDetectorCenter;
import com.fr.env.utils.DesignerInteractionHistory; import com.fr.env.utils.DesignerInteractionHistory;
import com.fr.event.Event; import com.fr.event.Event;
import com.fr.event.EventDispatcher; import com.fr.event.EventDispatcher;
@ -111,7 +110,6 @@ public class MainDesigner extends BaseDesigner {
*/ */
public static void main(String[] args) { public static void main(String[] args) {
EnvDetectorCenter.getInstance().init();
showSplash(); showSplash();
DeepLinkManager.getInstance().start(args); DeepLinkManager.getInstance().start(args);
StopWatch watch = new StopWatch(); StopWatch watch = new StopWatch();

Loading…
Cancel
Save