diff --git a/.travis.yml b/.travis.yml index e040132..1e6f6ed 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,5 @@ language: java -branches: - only: - - master before_install: - chmod +x mvnw jdk: diff --git a/README.md b/README.md index 94655bc..550960a 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,11 @@ AndroidScreencast ================= -[![Build Status Travis-CI][travis_badge]][travis] [![Dependency Status][versioneye_badge]][versioneye] [![Codacy Badge][codacy_badge]][codacy] [![Join the chat at https://gitter.im/AndroidScreencast/Lobby][gitter_badge]][gitter] - -[travis_badge]: https://travis-ci.org/xSAVIKx/AndroidScreencast.svg?branch=master -[travis]: https://travis-ci.org/xSAVIKx/AndroidScreencast - -[versioneye_badge]: https://www.versioneye.com/user/projects/588988221618a700318eafb5/badge.svg -[versioneye]: https://www.versioneye.com/user/projects/588988221618a700318eafb5 - -[codacy_badge]: https://api.codacy.com/project/badge/Grade/064bbd2582b544bb9659a01a2872317c -[codacy]: https://www.codacy.com/app/xSAVIKx/AndroidScreencast?utm_source=github.com&utm_medium=referral&utm_content=xSAVIKx/AndroidScreencast&utm_campaign=badger - - - -[gitter_badge]: https://badges.gitter.im/AndroidScreencast/Lobby.svg -[gitter]: https://gitter.im/AndroidScreencast/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge +[![Build Status Travis-CI][travis_badge]][travis]  +[![Dependency Status][versioneye_badge]][versioneye]  +[![Codacy Badge][codacy_badge]][codacy]  +[![Join the chat at https://gitter.im/AndroidScreencast/Lobby][gitter_badge]][gitter]  +[![Apache License][license_badge]][license] # Description @@ -62,7 +52,7 @@ The resulting artifacts will be created in the `target` subdirectory. You can run the executable jar via `java -jar target/androidscreencast-VERSION-executable.jar`, replacing VERSION with the current version. -For example, `java -jar target/androidscreencast-0.0.9s-executable.jar`. +For example, `java -jar target/androidscreencast-0.0.10s-executable.jar`. Additionally OS-packages would be created with ADB executables bundled: * `androidscreencast-VERSION-windows.zip` @@ -82,4 +72,27 @@ So, right now AndroidScreencast support all Android versions equal or greater th Also, to run AndroidScreencast you will need *adb* installed (or you can use bundled in OS bundles adb). +# Similar Projects + +* [Seven Square][seven_square] - QT implementation of Android Screencast (actively developed) +* [Droid@Screen][droid_at_screen] - implementation of Android Screencast in Java (fancy one, last release in 2013) +* [Android Screen Monitor][android_screen_monitor] - implementation of Android Screencast in Java (latest release in 2013) + [Android_4_1_1_Input]: http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.1.1_r1/com/android/commands/input/Input.java#Input +[seven_square]: https://github.com/yangh/sevensquare +[droid_at_screen]: http://droid-at-screen.org/droid-at-screen/ + +[travis_badge]: https://travis-ci.org/xSAVIKx/AndroidScreencast.svg?branch=master +[travis]: https://travis-ci.org/xSAVIKx/AndroidScreencast + +[versioneye_badge]: https://www.versioneye.com/user/projects/58a746d8b4d2a20055fcb887/badge.svg?style=flat +[versioneye]: https://www.versioneye.com/user/projects/58a746d8b4d2a20055fcb887 + +[codacy_badge]: https://api.codacy.com/project/badge/Grade/064bbd2582b544bb9659a01a2872317c +[codacy]: https://www.codacy.com/app/xSAVIKx/AndroidScreencast?utm_source=github.com&utm_medium=referral&utm_content=xSAVIKx/AndroidScreencast&utm_campaign=badger + +[license_badge]: https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg?style=flat +[license]: http://www.apache.org/licenses/LICENSE-2.0 + +[gitter_badge]: https://badges.gitter.im/AndroidScreencast/Lobby.svg?style=flat +[gitter]: https://gitter.im/AndroidScreencast/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge diff --git a/app.properties b/app.properties index 2d7c1f7..1175efc 100644 --- a/app.properties +++ b/app.properties @@ -1,3 +1,3 @@ -adb.path=adb/windows/adb.exe +adb.path=adb/windows/adb2.exe default.window.width=1024 default.window.height=768 \ No newline at end of file diff --git a/docs/jnlp/androidscreencast.jnlp b/docs/jnlp/androidscreencast.jnlp index c3ac0d2..d3c68dd 100644 --- a/docs/jnlp/androidscreencast.jnlp +++ b/docs/jnlp/androidscreencast.jnlp @@ -1,7 +1,7 @@ + version="0.0.10s"> Android Screencast Iurii Sergiichuk @@ -16,7 +16,7 @@ + href="https://github.com/xSAVIKx/AndroidScreencast/releases/download/0.0.10s/androidscreencast-0.0.10s-executable.jar"/> diff --git a/pom.xml b/pom.xml index 1802285..a004b8e 100644 --- a/pom.xml +++ b/pom.xml @@ -3,12 +3,14 @@ 4.0.0 com.github.xsavikx androidscreencast - 0.0.9s + 0.0.10s Android Screencast + jar - 4.3.5.RELEASE + 2.9 25.2.0 - 1.2.17 + 1.7.24 + 1.2.1 21.0 com.github.xsavikx.androidscreencast.Main 1.8 @@ -25,24 +27,31 @@ ${ddmlib.version} - log4j - log4j - ${log4j.version} + org.slf4j + slf4j-api + ${slf4j.version} - org.springframework - spring-core - ${spring.version} + ch.qos.logback + logback-classic + ${logback-classic.version} - org.springframework - spring-beans - ${spring.version} + org.slf4j + jcl-over-slf4j + ${slf4j.version} - org.springframework - spring-context - ${spring.version} + com.google.dagger + dagger + ${dagger2.version} + + + com.google.dagger + dagger-compiler + ${dagger2.version} + true + provided com.google.guava @@ -75,7 +84,7 @@ org.apache.maven.plugins maven-shade-plugin - 2.4.3 + 3.0.0 package diff --git a/src/main/java/com/github/xsavikx/androidscreencast/Main.java b/src/main/java/com/github/xsavikx/androidscreencast/Main.java index 32a10bb..cb5e810 100644 --- a/src/main/java/com/github/xsavikx/androidscreencast/Main.java +++ b/src/main/java/com/github/xsavikx/androidscreencast/Main.java @@ -1,24 +1,23 @@ package com.github.xsavikx.androidscreencast; -import com.github.xsavikx.androidscreencast.app.AndroidScreencastApplication; import com.github.xsavikx.androidscreencast.app.Application; -import com.github.xsavikx.androidscreencast.spring.config.ApplicationContextProvider; -import org.apache.log4j.Logger; +import com.github.xsavikx.androidscreencast.dagger.MainComponentProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.Arrays; public class Main { - private static final Logger LOGGER = Logger.getLogger(Main.class); + private static final Logger LOGGER = LoggerFactory.getLogger(Main.class); public static void main(String args[]) { - LOGGER.debug("main(String[] args=" + Arrays.toString(args) + ") - start"); - Application application; + LOGGER.debug("main(String[] args={}) - start", Arrays.toString(args)); try { - application = ApplicationContextProvider.getBean(AndroidScreencastApplication.class); + Application application = MainComponentProvider.mainComponent().application(); application.init(); application.start(); } finally { - LOGGER.debug("main(String[] args=" + Arrays.toString(args) + ") - end"); + LOGGER.debug("main(String[] args={}) - end", Arrays.toString(args)); } } diff --git a/src/main/java/com/github/xsavikx/androidscreencast/api/AndroidDeviceImpl.java b/src/main/java/com/github/xsavikx/androidscreencast/api/AndroidDeviceImpl.java index f19ac15..295d671 100644 --- a/src/main/java/com/github/xsavikx/androidscreencast/api/AndroidDeviceImpl.java +++ b/src/main/java/com/github/xsavikx/androidscreencast/api/AndroidDeviceImpl.java @@ -7,55 +7,56 @@ import com.github.xsavikx.androidscreencast.api.injector.OutputStreamShellOutputReceiver; import com.github.xsavikx.androidscreencast.exception.AndroidScreenCastRuntimeException; import com.github.xsavikx.androidscreencast.exception.ExecuteCommandException; -import org.apache.log4j.Logger; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import javax.inject.Inject; +import javax.inject.Singleton; import java.io.ByteArrayOutputStream; import java.io.File; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; -@Component +@Singleton public class AndroidDeviceImpl implements AndroidDevice { - private static final Logger logger = Logger.getLogger(AndroidDeviceImpl.class); + private static final Logger LOGGER = LoggerFactory.getLogger(AndroidDeviceImpl.class); private final IDevice device; - @Autowired - public AndroidDeviceImpl(IDevice device) { + @Inject + public AndroidDeviceImpl(final IDevice device) { this.device = device; } @Override - public String executeCommand(String cmd) { - if (logger.isDebugEnabled()) { - logger.debug("executeCommand(String) - start"); + public String executeCommand(final String cmd) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("executeCommand(String) - start"); } - try (ByteArrayOutputStream bos = new ByteArrayOutputStream();) { + try (final ByteArrayOutputStream bos = new ByteArrayOutputStream();) { device.executeShellCommand(cmd, new OutputStreamShellOutputReceiver(bos)); - String returnString = new String(bos.toByteArray(), "UTF-8"); - if (logger.isDebugEnabled()) { - logger.debug("executeCommand(String) - end"); + final String returnString = new String(bos.toByteArray(), "UTF-8"); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("executeCommand(String) - end"); } return returnString; - } catch (Exception ex) { - logger.error("executeCommand(String)", ex); + } catch (final Exception ex) { + LOGGER.error("executeCommand(String)", ex); throw new ExecuteCommandException(cmd); } } @Override - public List list(String path) { - if (logger.isDebugEnabled()) { - logger.debug("list(String) - start"); + public List list(final String path) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("list(String) - start"); } try { - String s = executeCommand("ls -l " + path); - String[] entries = s.split("\r\n"); - List fileInfos = new ArrayList<>(); - for (String entry : entries) { + final String s = executeCommand("ls -l " + path); + final String[] entries = s.split("\r\n"); + final List fileInfos = new ArrayList<>(); + for (final String entry : entries) { String[] data = entry.split(" "); if (data.length < 4) continue; @@ -63,7 +64,7 @@ public List list(String path) { boolean directory = attributes.charAt(0) == 'd'; String name = data[data.length - 1]; - FileInfo fi = new FileInfo(); + final FileInfo fi = new FileInfo(); fi.attribs = attributes; fi.directory = directory; fi.name = name; @@ -73,59 +74,59 @@ public List list(String path) { fileInfos.add(fi); } - if (logger.isDebugEnabled()) { - logger.debug("list(String) - end"); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("list(String) - end"); } return fileInfos; - } catch (Exception ex) { - logger.error("list(String)", ex); + } catch (final Exception ex) { + LOGGER.error("list(String)", ex); throw new AndroidScreenCastRuntimeException(ex); } } @Override - public void openUrl(String url) { - if (logger.isDebugEnabled()) { - logger.debug("openUrl(String) - start"); + public void openUrl(final String url) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("openUrl(String) - start"); } executeCommand("am start " + url); - if (logger.isDebugEnabled()) { - logger.debug("openUrl(String) - end"); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("openUrl(String) - end"); } } @Override - public void pullFile(String removeFrom, File localTo) { - if (logger.isDebugEnabled()) { - logger.debug("pullFile(String, File) - start"); + public void pullFile(final String removeFrom, final File localTo) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("pullFile(String, File) - start"); } // ugly hack to call the method without FileEntry try { if (device.getSyncService() == null) - throw new RuntimeException("SyncService is null, ADB crashed ?"); - Method m = device.getSyncService().getClass().getDeclaredMethod("doPullFile", String.class, String.class, + throw new AndroidScreenCastRuntimeException("SyncService is null, ADB crashed ?"); + final Method m = device.getSyncService().getClass().getDeclaredMethod("doPullFile", String.class, String.class, ISyncProgressMonitor.class); m.setAccessible(true); device.getSyncService(); m.invoke(device.getSyncService(), removeFrom, localTo.getAbsolutePath(), SyncService.getNullProgressMonitor()); - } catch (Exception ex) { - logger.error("pullFile(String, File)", ex); + } catch (final Exception ex) { + LOGGER.error("pullFile(String, File)", ex); throw new AndroidScreenCastRuntimeException(ex); } - if (logger.isDebugEnabled()) { - logger.debug("pullFile(String, File) - end"); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("pullFile(String, File) - end"); } } @Override - public void pushFile(File localFrom, String remoteTo) { - if (logger.isDebugEnabled()) { - logger.debug("pushFile(File, String) - start"); + public void pushFile(final File localFrom, final String remoteTo) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("pushFile(File, String) - start"); } try { @@ -134,14 +135,14 @@ public void pushFile(File localFrom, String remoteTo) { device.getSyncService().pushFile(localFrom.getAbsolutePath(), remoteTo, SyncService.getNullProgressMonitor()); - } catch (Exception ex) { - logger.error("pushFile(File, String)", ex); + } catch (final Exception ex) { + LOGGER.error("pushFile(File, String)", ex); throw new AndroidScreenCastRuntimeException(ex); } - if (logger.isDebugEnabled()) { - logger.debug("pushFile(File, String) - end"); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("pushFile(File, String) - end"); } } diff --git a/src/main/java/com/github/xsavikx/androidscreencast/api/adb/AndroidDebugBridgeWrapper.java b/src/main/java/com/github/xsavikx/androidscreencast/api/adb/AndroidDebugBridgeWrapper.java new file mode 100644 index 0000000..d9e9a90 --- /dev/null +++ b/src/main/java/com/github/xsavikx/androidscreencast/api/adb/AndroidDebugBridgeWrapper.java @@ -0,0 +1,71 @@ +package com.github.xsavikx.androidscreencast.api.adb; + +import com.android.ddmlib.AndroidDebugBridge; +import com.android.ddmlib.IDevice; +import com.github.xsavikx.androidscreencast.exception.IllegalAdbConfigurationException; +import com.github.xsavikx.androidscreencast.util.StringUtils; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; +import java.io.IOException; + +import static com.github.xsavikx.androidscreencast.configuration.ApplicationConfigurationPropertyKeys.ADB_PATH_KEY; + +@Singleton +public class AndroidDebugBridgeWrapper { + private final String adbPath; + private AndroidDebugBridge adb; + + @Inject + public AndroidDebugBridgeWrapper(@Named(ADB_PATH_KEY) String adbPath) { + this.adbPath = adbPath; + } + + public IDevice[] getDevices() { + return getAdb().getDevices(); + } + + public boolean hasInitialDeviceList() { + return getAdb().hasInitialDeviceList(); + } + + public void stop() { + AndroidDebugBridge.disconnectBridge(); + AndroidDebugBridge.terminate(); + } + + private AndroidDebugBridge getAdb() { + if (adb == null) { + init(); + } + return adb; + } + + private void init() { + if (adb != null) { + return; + } + try { + AndroidDebugBridge.initIfNeeded(false); + if (StringUtils.isNotEmpty(adbPath)) { + adb = AndroidDebugBridge.createBridge(adbPath, false); + } else { + adb = AndroidDebugBridge.createBridge(); + } + } catch (IllegalArgumentException e) { + if (hasAdbProcFailed(e)) { + throw new IllegalAdbConfigurationException(adbPath); + } + throw e; + } + } + + private boolean hasAdbProcFailed(IllegalArgumentException e) { + return e.getCause() != null + && e.getCause() instanceof IOException + && e.getCause().getMessage().contains("Cannot run program") + && e.getCause().getMessage().contains("adb"); + + } +} diff --git a/src/main/java/com/github/xsavikx/androidscreencast/api/command/executor/ShellCommandExecutor.java b/src/main/java/com/github/xsavikx/androidscreencast/api/command/executor/ShellCommandExecutor.java index 1327533..41a3830 100644 --- a/src/main/java/com/github/xsavikx/androidscreencast/api/command/executor/ShellCommandExecutor.java +++ b/src/main/java/com/github/xsavikx/androidscreencast/api/command/executor/ShellCommandExecutor.java @@ -7,41 +7,46 @@ import com.github.xsavikx.androidscreencast.api.command.Command; import com.github.xsavikx.androidscreencast.api.command.exception.AdbShellCommandExecutionException; import com.github.xsavikx.androidscreencast.api.injector.MultiLineReceiverPrinter; -import org.apache.log4j.Logger; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Service; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; import java.io.IOException; import java.util.concurrent.TimeUnit; -@Service +import static com.github.xsavikx.androidscreencast.configuration.ApplicationConfigurationPropertyKeys.ADB_COMMAND_TIMEOUT_KEY; + +@Singleton public class ShellCommandExecutor implements CommandExecutor { - private static final Logger LOGGER = Logger.getLogger(ShellCommandExecutor.class); + private static final Logger LOGGER = LoggerFactory.getLogger(ShellCommandExecutor.class); private final IDevice device; private final MultiLineReceiverPrinter multiLineReceiverPrinter; - @Value("${adb.command.timeout:5}") - private long adbCommandTimeout; + private final long adbCommandTimeout; - @Autowired - public ShellCommandExecutor(IDevice device, MultiLineReceiverPrinter multiLineReceiverPrinter) { + @Inject + public ShellCommandExecutor(final IDevice device, + final MultiLineReceiverPrinter multiLineReceiverPrinter, + @Named(ADB_COMMAND_TIMEOUT_KEY) long adbCommandTimeout) { this.device = device; this.multiLineReceiverPrinter = multiLineReceiverPrinter; + this.adbCommandTimeout = adbCommandTimeout; } @Override public void execute(Command command) { - LOGGER.debug("execute(Command command=" + command + ") - start"); + LOGGER.debug("execute(Command command={}) - start", command); try { device.executeShellCommand(command.getFormattedCommand(), multiLineReceiverPrinter, adbCommandTimeout, TimeUnit.SECONDS); } catch (TimeoutException | AdbCommandRejectedException | ShellCommandUnresponsiveException | IOException e) { - LOGGER.error("execute(Command)", e); + LOGGER.error("execute(Command command={})", command, e); throw new AdbShellCommandExecutionException(command, e); } - LOGGER.debug("execute(Command command=" + command + ") - end"); + LOGGER.debug("execute(Command command={}) - end", command); } } diff --git a/src/main/java/com/github/xsavikx/androidscreencast/api/command/factory/AdbInputCommandFactory.java b/src/main/java/com/github/xsavikx/androidscreencast/api/command/factory/AdbInputCommandFactory.java index 6682e3c..0fe03aa 100644 --- a/src/main/java/com/github/xsavikx/androidscreencast/api/command/factory/AdbInputCommandFactory.java +++ b/src/main/java/com/github/xsavikx/androidscreencast/api/command/factory/AdbInputCommandFactory.java @@ -4,53 +4,47 @@ import com.github.xsavikx.androidscreencast.api.command.SwipeCommand; import com.github.xsavikx.androidscreencast.api.command.TapCommand; import com.github.xsavikx.androidscreencast.api.injector.InputKeyEvent; -import org.apache.log4j.Logger; -import org.springframework.stereotype.Service; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -@Service -public final class AdbInputCommandFactory { - private static final Logger LOGGER = Logger.getLogger(AdbInputCommandFactory.class); +import javax.inject.Inject; +import javax.inject.Singleton; - public static KeyCommand getKeyCommand(int keyCode) { - KeyCommand returnKeyCommand = new KeyCommand(keyCode); - LOGGER.debug(returnKeyCommand); - return returnKeyCommand; - } - - public static KeyCommand getKeyCommand(InputKeyEvent inputKeyEvent) { - KeyCommand returnKeyCommand = new KeyCommand(inputKeyEvent); - LOGGER.debug(returnKeyCommand); - return returnKeyCommand; - } +@Singleton +public class AdbInputCommandFactory implements InputCommandFactory { + private static final Logger LOGGER = LoggerFactory.getLogger(AdbInputCommandFactory.class); + @Inject + public AdbInputCommandFactory() { - public static KeyCommand getKeyCommand(int keyCode, boolean longpress) { - KeyCommand returnKeyCommand = new KeyCommand(keyCode, longpress); - LOGGER.debug(returnKeyCommand); - return returnKeyCommand; } - public static KeyCommand getKeyCommand(InputKeyEvent inputKeyEvent, boolean longpress) { - KeyCommand returnKeyCommand = new KeyCommand(inputKeyEvent, longpress); - LOGGER.debug(returnKeyCommand); + @Override + public KeyCommand getKeyCommand(final int keyCode) { + final KeyCommand returnKeyCommand = new KeyCommand(keyCode); + LOGGER.debug(String.valueOf(returnKeyCommand)); return returnKeyCommand; } - public static SwipeCommand getSwipeCommand(int x1, int y1, int x2, int y2, long duration) { - SwipeCommand returnSwipeCommand = new SwipeCommand(x1, y1, x2, y2, duration); - LOGGER.debug(returnSwipeCommand); - return returnSwipeCommand; + @Override + public KeyCommand getKeyCommand(final InputKeyEvent inputKeyEvent, final boolean longpress) { + final KeyCommand returnKeyCommand = new KeyCommand(inputKeyEvent, longpress); + LOGGER.debug(String.valueOf(returnKeyCommand)); + return returnKeyCommand; } - public static SwipeCommand getSwipeCommand(int x1, int y1, int x2, int y2) { - SwipeCommand returnSwipeCommand = new SwipeCommand(x1, y1, x2, y2); - LOGGER.debug(returnSwipeCommand); + @Override + public SwipeCommand getSwipeCommand(final int x1, final int y1, final int x2, final int y2, final long duration) { + final SwipeCommand returnSwipeCommand = new SwipeCommand(x1, y1, x2, y2, duration); + LOGGER.debug(String.valueOf(returnSwipeCommand)); return returnSwipeCommand; } - public static TapCommand getTapCommand(int x, int y) { - TapCommand returnTapCommand = new TapCommand(x, y); - LOGGER.debug(returnTapCommand); + @Override + public TapCommand getTapCommand(final int x, final int y) { + final TapCommand returnTapCommand = new TapCommand(x, y); + LOGGER.debug(String.valueOf(returnTapCommand)); return returnTapCommand; } + } diff --git a/src/main/java/com/github/xsavikx/androidscreencast/api/command/factory/InputCommandFactory.java b/src/main/java/com/github/xsavikx/androidscreencast/api/command/factory/InputCommandFactory.java new file mode 100644 index 0000000..a8953d1 --- /dev/null +++ b/src/main/java/com/github/xsavikx/androidscreencast/api/command/factory/InputCommandFactory.java @@ -0,0 +1,16 @@ +package com.github.xsavikx.androidscreencast.api.command.factory; + +import com.github.xsavikx.androidscreencast.api.command.KeyCommand; +import com.github.xsavikx.androidscreencast.api.command.SwipeCommand; +import com.github.xsavikx.androidscreencast.api.command.TapCommand; +import com.github.xsavikx.androidscreencast.api.injector.InputKeyEvent; + +public interface InputCommandFactory { + KeyCommand getKeyCommand(int keyCode); + + KeyCommand getKeyCommand(InputKeyEvent inputKeyEvent, boolean longpress); + + SwipeCommand getSwipeCommand(int x1, int y1, int x2, int y2, long duration); + + TapCommand getTapCommand(int x, int y); +} diff --git a/src/main/java/com/github/xsavikx/androidscreencast/api/file/FileInfo.java b/src/main/java/com/github/xsavikx/androidscreencast/api/file/FileInfo.java index 007de7f..a49804c 100644 --- a/src/main/java/com/github/xsavikx/androidscreencast/api/file/FileInfo.java +++ b/src/main/java/com/github/xsavikx/androidscreencast/api/file/FileInfo.java @@ -2,12 +2,13 @@ import com.github.xsavikx.androidscreencast.api.AndroidDeviceImpl; import com.github.xsavikx.androidscreencast.exception.IORuntimeException; -import org.springframework.stereotype.Component; +import javax.inject.Inject; +import javax.inject.Singleton; import java.io.File; import java.io.IOException; -@Component +@Singleton public class FileInfo { public AndroidDeviceImpl device; public String path; @@ -15,6 +16,11 @@ public class FileInfo { public boolean directory; public String name; + @Inject + public FileInfo() { + + } + public File downloadTemporary() { try { File tempFile = File.createTempFile("android", name); diff --git a/src/main/java/com/github/xsavikx/androidscreencast/api/injector/Injector.java b/src/main/java/com/github/xsavikx/androidscreencast/api/injector/Injector.java index eed9306..13fde3b 100644 --- a/src/main/java/com/github/xsavikx/androidscreencast/api/injector/Injector.java +++ b/src/main/java/com/github/xsavikx/androidscreencast/api/injector/Injector.java @@ -1,19 +1,19 @@ package com.github.xsavikx.androidscreencast.api.injector; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - +import javax.inject.Inject; +import javax.inject.Singleton; import java.io.File; -@Service +@Singleton public class Injector { private final ScreenCaptureRunnable screenCaptureRunnable; private final Thread screenCaptureThread; - @Autowired - public Injector(ScreenCaptureRunnable screenCaptureRunnable) { + @Inject + public Injector(final ScreenCaptureRunnable screenCaptureRunnable) { this.screenCaptureRunnable = screenCaptureRunnable; this.screenCaptureThread = new Thread(screenCaptureRunnable, "Screen Capturer"); + this.screenCaptureThread.setDaemon(true); } public void stop() { @@ -24,11 +24,11 @@ public void start() { screenCaptureThread.start(); } - public void setScreenCaptureListener(ScreenCaptureRunnable.ScreenCaptureListener listener) { + public void setScreenCaptureListener(final ScreenCaptureRunnable.ScreenCaptureListener listener) { this.screenCaptureRunnable.setListener(listener); } - public void startRecording(File file) { + public void startRecording(final File file) { screenCaptureRunnable.startRecording(file); } diff --git a/src/main/java/com/github/xsavikx/androidscreencast/api/injector/InputKeyEvent.java b/src/main/java/com/github/xsavikx/androidscreencast/api/injector/InputKeyEvent.java index ddaa0f6..ddf8ceb 100644 --- a/src/main/java/com/github/xsavikx/androidscreencast/api/injector/InputKeyEvent.java +++ b/src/main/java/com/github/xsavikx/androidscreencast/api/injector/InputKeyEvent.java @@ -1,12 +1,10 @@ //@formatter:off package com.github.xsavikx.androidscreencast.api.injector; -import javax.annotation.Resource; import java.awt.event.KeyEvent; import java.util.EnumSet; import java.util.Set; -@Resource public enum InputKeyEvent { KEYCODE_UNKNOWN(0, "Key code constant: Unknown key code."), KEYCODE_SOFT_LEFT(1, "Key code constant: Soft Left key. Usually situated below the display on phones and used as a multi-function feature key for selecting a software defined function shown on the bottom left of the display."), diff --git a/src/main/java/com/github/xsavikx/androidscreencast/api/injector/KeyCodeConverter.java b/src/main/java/com/github/xsavikx/androidscreencast/api/injector/KeyCodeConverter.java index b9d8bd6..153b537 100644 --- a/src/main/java/com/github/xsavikx/androidscreencast/api/injector/KeyCodeConverter.java +++ b/src/main/java/com/github/xsavikx/androidscreencast/api/injector/KeyCodeConverter.java @@ -1,14 +1,15 @@ package com.github.xsavikx.androidscreencast.api.injector; -import org.apache.log4j.Logger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.awt.event.KeyEvent; public class KeyCodeConverter { - private static final Logger LOGGER = Logger.getLogger(KeyCodeConverter.class); + private static final Logger LOGGER = LoggerFactory.getLogger(KeyCodeConverter.class); public static int getKeyCode(KeyEvent e) { - LOGGER.debug("getKeyCode(KeyEvent e=" + e + ") - start"); + LOGGER.debug("getKeyCode(KeyEvent e={}) - start", e); int code = InputKeyEvent.KEYCODE_UNKNOWN.getCode(); char c = e.getKeyChar(); int keyCode = e.getKeyCode(); @@ -16,7 +17,7 @@ public static int getKeyCode(KeyEvent e) { if (inputKeyEvent != null) { code = inputKeyEvent.getCode(); } - LOGGER.debug(String.format("Received KeyEvent=%s. Produced KeyCode=%d", String.valueOf(e), code)); + LOGGER.debug("Received KeyEvent={}. Produced KeyCode={}", String.valueOf(e), code); return code; } diff --git a/src/main/java/com/github/xsavikx/androidscreencast/api/injector/MultiLineReceiverPrinter.java b/src/main/java/com/github/xsavikx/androidscreencast/api/injector/MultiLineReceiverPrinter.java index d33a9ce..0233842 100644 --- a/src/main/java/com/github/xsavikx/androidscreencast/api/injector/MultiLineReceiverPrinter.java +++ b/src/main/java/com/github/xsavikx/androidscreencast/api/injector/MultiLineReceiverPrinter.java @@ -1,10 +1,16 @@ package com.github.xsavikx.androidscreencast.api.injector; import com.android.ddmlib.MultiLineReceiver; -import org.springframework.stereotype.Component; -@Component +import javax.inject.Inject; +import javax.inject.Singleton; + +@Singleton public class MultiLineReceiverPrinter extends MultiLineReceiver { + @Inject + public MultiLineReceiverPrinter() { + + } @Override public boolean isCancelled() { diff --git a/src/main/java/com/github/xsavikx/androidscreencast/api/injector/ScreenCaptureRunnable.java b/src/main/java/com/github/xsavikx/androidscreencast/api/injector/ScreenCaptureRunnable.java index 7d2a16f..2fa4169 100644 --- a/src/main/java/com/github/xsavikx/androidscreencast/api/injector/ScreenCaptureRunnable.java +++ b/src/main/java/com/github/xsavikx/androidscreencast/api/injector/ScreenCaptureRunnable.java @@ -7,11 +7,12 @@ import com.github.xsavikx.androidscreencast.api.image.ImageUtils; import com.github.xsavikx.androidscreencast.api.recording.QuickTimeOutputStream; import com.github.xsavikx.androidscreencast.exception.IORuntimeException; -import org.apache.log4j.Logger; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; import javax.swing.*; import java.awt.*; import java.awt.image.BufferedImage; @@ -20,49 +21,51 @@ import java.nio.channels.ClosedByInterruptException; import java.util.concurrent.TimeUnit; -@Component +import static com.github.xsavikx.androidscreencast.configuration.ApplicationConfigurationPropertyKeys.ADB_COMMAND_TIMEOUT_KEY; + +@Singleton public class ScreenCaptureRunnable implements Runnable { - private static final Logger LOGGER = Logger.getLogger(ScreenCaptureRunnable.class); + private static final Logger LOGGER = LoggerFactory.getLogger(ScreenCaptureRunnable.class); private static final int MOV_FPS = 30; private static final float MOV_COMPRESSION_RATE = 1f; private static final int FRAME_DURATION = 10; private final IDevice device; + private final long defaultAdbCommandTimeout; private Dimension size; private QuickTimeOutputStream qos = null; private boolean landscape = false; private ScreenCaptureListener listener = null; - @Value("${adb.command.timeout:5}") - private long defaultAdbCommandTimeout; - @Value("${adb.command.timeout:5}") private long currentAdbCommandTimeout; private boolean isStopped = false; - @Autowired - public ScreenCaptureRunnable(IDevice device) { - size = new Dimension(); + @Inject + public ScreenCaptureRunnable(final IDevice device, @Named(ADB_COMMAND_TIMEOUT_KEY) long adbCommandTimeout) { + this.size = new Dimension(); this.device = device; + this.defaultAdbCommandTimeout = adbCommandTimeout; + this.currentAdbCommandTimeout = defaultAdbCommandTimeout; } @Override public void run() { - LOGGER.info("Starting ScreenCaptureRunnable"); + LOGGER.info("Starting screen capturing"); while (!isStopped) { try { - RawImage screenshot = getScreenshot(); + final RawImage screenshot = getScreenshot(); if (screenshot != null) { display(screenshot); } else { - LOGGER.info("Failed to get device screenshot."); + LOGGER.info("Failed to get device screenshot"); } - } catch (ClosedByInterruptException e) { + } catch (final ClosedByInterruptException e) { LOGGER.error("ADB Channel closed due to interrupted exception", e); break; - } catch (InterruptedException e) { + } catch (final InterruptedException e) { LOGGER.error("Execution of thread was interrupted. Shutting down thread.", e); break; } } - LOGGER.info("ScreenCaptureRunnable is stopped."); + LOGGER.info("Stopping screen capturing"); } private RawImage getScreenshot() throws InterruptedException, ClosedByInterruptException { @@ -74,7 +77,7 @@ private RawImage getScreenshot() throws InterruptedException, ClosedByInterruptE currentAdbCommandTimeout = defaultAdbCommandTimeout; } catch (TimeoutException e) { currentAdbCommandTimeout++; - LOGGER.warn(String.format("Adb command timeout happened. Timeout would be set to %d for the next try.", currentAdbCommandTimeout), e); + LOGGER.warn("Adb command timeout happened. Timeout would be set to {} for the next try.", currentAdbCommandTimeout, e); } catch (AdbCommandRejectedException e) { LOGGER.warn("ADB Command was rejected. Will try again in 100 ms."); Thread.sleep(100); @@ -87,9 +90,9 @@ private RawImage getScreenshot() throws InterruptedException, ClosedByInterruptE return rawImage; } - private void display(RawImage rawImage) { - RawImage imageToProcess = landscape ? rawImage.getRotated() : rawImage; - BufferedImage image = ImageUtils.convertImage(imageToProcess); + private void display(final RawImage rawImage) { + final RawImage imageToProcess = landscape ? rawImage.getRotated() : rawImage; + final BufferedImage image = ImageUtils.convertImage(imageToProcess); size.setSize(image.getWidth(), image.getHeight()); if (listener != null) { SwingUtilities.invokeLater(() -> listener.handleNewImage(size, image, landscape)); @@ -99,25 +102,25 @@ private void display(RawImage rawImage) { try { qos.writeFrame(image, FRAME_DURATION); } catch (IORuntimeException e) { - LOGGER.error(e); + LOGGER.error("IO exception during writing video frame happened", e); } }); } } - public void setListener(ScreenCaptureListener listener) { + public void setListener(final ScreenCaptureListener listener) { this.listener = listener; } - public void startRecording(File file) { + public void startRecording(final File file) { try { qos = new QuickTimeOutputStream(file, QuickTimeOutputStream.VideoFormat.JPG); - } catch (IOException e) { + qos.setVideoCompressionQuality(MOV_COMPRESSION_RATE); + qos.setTimeScale(MOV_FPS); + } catch (final IOException e) { throw new IORuntimeException(e); } - qos.setVideoCompressionQuality(MOV_COMPRESSION_RATE); - qos.setTimeScale(MOV_FPS); } public void stopRecording() { diff --git a/src/main/java/com/github/xsavikx/androidscreencast/api/recording/atom/DataAtom.java b/src/main/java/com/github/xsavikx/androidscreencast/api/recording/atom/DataAtom.java index f4927b2..759f3d8 100644 --- a/src/main/java/com/github/xsavikx/androidscreencast/api/recording/atom/DataAtom.java +++ b/src/main/java/com/github/xsavikx/androidscreencast/api/recording/atom/DataAtom.java @@ -5,6 +5,8 @@ import com.github.xsavikx.androidscreencast.api.recording.exception.MaximumAtomSizeExeededException; import com.github.xsavikx.androidscreencast.exception.IORuntimeException; import com.google.common.base.Preconditions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.imageio.stream.ImageOutputStream; import java.io.IOException; @@ -13,6 +15,7 @@ * Data Atom. */ public class DataAtom extends CommonAtom { + private static final Logger LOGGER = LoggerFactory.getLogger(DataAtom.class); private static final int HEADER_SIZE = 1; protected final DataAtomOutputStream data; @@ -49,7 +52,7 @@ public void finish() { finished = true; long sizeAfter = size(); if (sizeBefore != sizeAfter) { - System.err.println("size mismatch " + sizeBefore + ".." + sizeAfter); + LOGGER.warn("Size mismatch. sizeBefore={}, sizeAfter={}.", sizeBefore, sizeAfter); } } } catch (IOException e) { diff --git a/src/main/java/com/github/xsavikx/androidscreencast/app/AndroidScreencastApplication.java b/src/main/java/com/github/xsavikx/androidscreencast/app/AndroidScreencastApplication.java index c5c73ea..306b468 100644 --- a/src/main/java/com/github/xsavikx/androidscreencast/app/AndroidScreencastApplication.java +++ b/src/main/java/com/github/xsavikx/androidscreencast/app/AndroidScreencastApplication.java @@ -1,84 +1,67 @@ package com.github.xsavikx.androidscreencast.app; -import com.android.ddmlib.AndroidDebugBridge; import com.android.ddmlib.IDevice; +import com.github.xsavikx.androidscreencast.api.adb.AndroidDebugBridgeWrapper; import com.github.xsavikx.androidscreencast.api.injector.Injector; +import com.github.xsavikx.androidscreencast.configuration.ApplicationConfiguration; import com.github.xsavikx.androidscreencast.ui.JFrameMain; -import org.apache.log4j.Logger; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; -import org.springframework.util.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import javax.inject.Inject; +import javax.inject.Singleton; import javax.swing.*; import java.awt.*; -@Component +@Singleton public class AndroidScreencastApplication extends SwingApplication { - private static final Logger LOGGER = Logger.getLogger(AndroidScreencastApplication.class); + private static final Logger LOGGER = LoggerFactory.getLogger(AndroidScreencastApplication.class); private final JFrameMain jFrameMain; private final Injector injector; private final IDevice iDevice; - @Value("${adb.path}") - private String adbPath; + private final AndroidDebugBridgeWrapper wrapper; private transient boolean isStopped = false; - @Autowired - public AndroidScreencastApplication(Injector injector, IDevice iDevice, JFrameMain jFrameMain) { + @Inject + public AndroidScreencastApplication(final Injector injector, final IDevice iDevice, final JFrameMain jFrameMain, + final ApplicationConfiguration applicationConfiguration, AndroidDebugBridgeWrapper wrapper) { + super(applicationConfiguration); this.injector = injector; this.iDevice = iDevice; this.jFrameMain = jFrameMain; + this.wrapper = wrapper; } @Override public void stop() { - try { - LOGGER.debug("stop() - start"); - if (isStopped) { - LOGGER.debug("Application is already stopped."); - return; - } - if (injector != null) - injector.stop(); - - if (iDevice != null) { - synchronized (iDevice) { - if (hasFilledAdbPath()) - AndroidDebugBridge.disconnectBridge(); - AndroidDebugBridge.terminate(); - } - } - for (Frame frame : Frame.getFrames()) { - frame.dispose(); - } - isStopped = true; - } finally { - LOGGER.debug("stop() - end"); + LOGGER.info("Stopping application"); + if (isStopped) { + LOGGER.debug("Application is already stopped."); + return; } - + injector.stop(); + wrapper.stop(); + for (final Frame frame : Frame.getFrames()) { + frame.dispose(); + } + isStopped = true; } @Override public void start() { - LOGGER.debug("start() - start"); + LOGGER.info("Starting application"); if (iDevice == null) { LOGGER.warn("No valid device was chosen. Please try to chose correct one."); stop(); } SwingUtilities.invokeLater(() -> { + jFrameMain.initialize(); // Start showing the iDevice screen - jFrameMain.setTitle("" + iDevice); - + jFrameMain.setTitle(iDevice.getSerialNumber()); // Show window jFrameMain.setVisible(true); jFrameMain.launchInjector(); }); - LOGGER.debug("start() - end"); - } - - - private boolean hasFilledAdbPath() { - return !StringUtils.isEmpty(adbPath); } } diff --git a/src/main/java/com/github/xsavikx/androidscreencast/app/DeviceChooserApplication.java b/src/main/java/com/github/xsavikx/androidscreencast/app/DeviceChooserApplication.java index 1a34925..f2b89a9 100644 --- a/src/main/java/com/github/xsavikx/androidscreencast/app/DeviceChooserApplication.java +++ b/src/main/java/com/github/xsavikx/androidscreencast/app/DeviceChooserApplication.java @@ -1,87 +1,82 @@ package com.github.xsavikx.androidscreencast.app; -import com.android.ddmlib.AndroidDebugBridge; import com.android.ddmlib.IDevice; +import com.github.xsavikx.androidscreencast.api.adb.AndroidDebugBridgeWrapper; +import com.github.xsavikx.androidscreencast.configuration.ApplicationConfiguration; import com.github.xsavikx.androidscreencast.exception.NoDeviceChosenException; import com.github.xsavikx.androidscreencast.exception.WaitDeviceListTimeoutException; import com.github.xsavikx.androidscreencast.ui.JDialogDeviceList; -import org.apache.log4j.Logger; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -@Component +import javax.inject.Inject; +import javax.inject.Singleton; + +import static com.github.xsavikx.androidscreencast.configuration.ApplicationConfigurationProperty.ADB_DEVICE_TIMEOUT; + +@Singleton public class DeviceChooserApplication extends SwingApplication { - private static final Logger LOGGER = Logger.getLogger(DeviceChooserApplication.class); + private static final Logger LOGGER = LoggerFactory.getLogger(DeviceChooserApplication.class); private static final long WAIT_TIMEOUT = 100; - private final AndroidDebugBridge bridge; + private final AndroidDebugBridgeWrapper bridge; private final long adbWaitSleepCyclesAmount; - @Value("${adb.device.timeout:30}") - private long adbDeviceTimeout; private IDevice device; - @Autowired - public DeviceChooserApplication(AndroidDebugBridge bridge) { + @Inject + public DeviceChooserApplication(final AndroidDebugBridgeWrapper bridge, + final ApplicationConfiguration applicationConfiguration) { + super(applicationConfiguration); this.bridge = bridge; - this.adbWaitSleepCyclesAmount = adbDeviceTimeout * 10; + this.adbWaitSleepCyclesAmount = getAdbDeviceTimeout() * 10; + } + + private long getAdbDeviceTimeout() { + return Long.valueOf(applicationConfiguration.getProperty(ADB_DEVICE_TIMEOUT)); } @Override public void stop() { - // ignore + bridge.stop(); } @Override public void start() { - LOGGER.debug("start() - start"); - initialize(); - LOGGER.debug("start() - end"); - } - - private void waitDeviceList(AndroidDebugBridge bridge) { - LOGGER.debug("waitDeviceList(AndroidDebugBridge bridge=" + bridge + ") - start"); - - int count = 0; - while (!bridge.hasInitialDeviceList()) { - try { - Thread.sleep(WAIT_TIMEOUT); - count++; - } catch (InterruptedException e) { - LOGGER.warn("waitDeviceList(AndroidDebugBridge) - exception ignored", e); - - } - - if (count > adbWaitSleepCyclesAmount) { - throw new WaitDeviceListTimeoutException(); - } - } - - LOGGER.debug("waitDeviceList(AndroidDebugBridge bridge=" + bridge + ") - end"); - } - - private void initialize() { - LOGGER.debug("initialize() - start"); + LOGGER.info("Starting application"); waitDeviceList(bridge); - IDevice devices[] = bridge.getDevices(); + final IDevice devices[] = bridge.getDevices(); + // Let the user choose the device if (devices.length == 1) { device = devices[0]; + LOGGER.info("1 device was found by ADB"); } else { - JDialogDeviceList jd = new JDialogDeviceList(devices); + final JDialogDeviceList jd = new JDialogDeviceList(devices); jd.setVisible(true); - device = jd.getDevice(); - if (device == null) { - return; - } + LOGGER.info("{} devices were found by ADB", devices.length); } if (device == null) { throw new NoDeviceChosenException(); } - LOGGER.debug("initialize() - end"); + LOGGER.info("{} was chosen", device.getName()); + } + + private void waitDeviceList(final AndroidDebugBridgeWrapper bridge) { + int count = 0; + while (!bridge.hasInitialDeviceList()) { + try { + Thread.sleep(WAIT_TIMEOUT); + count++; + } catch (InterruptedException e) { + LOGGER.warn("waitDeviceList(AndroidDebugBridge) - exception ignored", e); + } + if (count > adbWaitSleepCyclesAmount) { + throw new WaitDeviceListTimeoutException(); + } + } } public IDevice getDevice() { diff --git a/src/main/java/com/github/xsavikx/androidscreencast/app/GUIApplication.java b/src/main/java/com/github/xsavikx/androidscreencast/app/GUIApplication.java index 19e6fc7..52d24cf 100644 --- a/src/main/java/com/github/xsavikx/androidscreencast/app/GUIApplication.java +++ b/src/main/java/com/github/xsavikx/androidscreencast/app/GUIApplication.java @@ -1,15 +1,18 @@ package com.github.xsavikx.androidscreencast.app; -public abstract class GUIApplication implements Application { +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; - public GUIApplication() { - Runtime.getRuntime().addShutdownHook(new Thread(GUIApplication.this::stop)); +abstract class GUIApplication implements Application { + private static final Logger LOGGER = LoggerFactory.getLogger(GUIApplication.class); + + GUIApplication() { + Runtime.getRuntime().addShutdownHook(new Thread(this::stop)); Thread.setDefaultUncaughtExceptionHandler((thread, ex) -> { try { handleException(thread, ex); - } catch (Exception ex2) { - // ignored - ex2.printStackTrace(); + } catch (final Exception ex2) { + LOGGER.error("Error occurred during exception handling.", ex2); } }); } diff --git a/src/main/java/com/github/xsavikx/androidscreencast/app/SwingApplication.java b/src/main/java/com/github/xsavikx/androidscreencast/app/SwingApplication.java index 1a19362..d3d80a3 100644 --- a/src/main/java/com/github/xsavikx/androidscreencast/app/SwingApplication.java +++ b/src/main/java/com/github/xsavikx/androidscreencast/app/SwingApplication.java @@ -1,20 +1,29 @@ package com.github.xsavikx.androidscreencast.app; +import com.github.xsavikx.androidscreencast.configuration.ApplicationConfiguration; import com.github.xsavikx.androidscreencast.exception.AndroidScreenCastRuntimeException; import com.github.xsavikx.androidscreencast.ui.JDialogError; -import org.springframework.beans.factory.annotation.Value; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.swing.*; import java.io.PrintWriter; import java.io.StringWriter; +import static com.github.xsavikx.androidscreencast.configuration.ApplicationConfigurationProperty.APP_NATIVE_LOOK; + public abstract class SwingApplication extends GUIApplication { + private static final Logger LOGGER = LoggerFactory.getLogger(DeviceChooserApplication.class); + private static final String SYNC_TREE_UI = "SynthTreeUI"; + protected final ApplicationConfiguration applicationConfiguration; private JDialogError jd = null; - @Value("${app.native.look:true}") - private boolean nativeLook; + + protected SwingApplication(final ApplicationConfiguration applicationConfiguration) { + this.applicationConfiguration = applicationConfiguration; + } private boolean useNativeLook() { - return nativeLook; + return Boolean.valueOf(applicationConfiguration.getProperty(APP_NATIVE_LOOK)); } @Override @@ -28,19 +37,21 @@ public void init() { } @Override - public void handleException(Thread thread, Throwable ex) { + public void handleException(final Thread thread, final Throwable ex) { try { - StringWriter sw = new StringWriter(); + final StringWriter sw = new StringWriter(); ex.printStackTrace(new PrintWriter(sw)); - if (sw.toString().contains("SynthTreeUI")) + if (sw.toString().contains(SYNC_TREE_UI)) return; - ex.printStackTrace(System.err); + LOGGER.error(ex.getClass().getSimpleName(), ex); if (jd != null && jd.isVisible()) return; jd = new JDialogError(ex); - SwingUtilities.invokeLater(() -> jd.setVisible(true)); - } catch (Exception ignored) { - // ignored + SwingUtilities.invokeLater(() -> { + jd.setVisible(true); + }); + } catch (Exception e) { + LOGGER.warn("Exception occurred during exception handling.", e); } } diff --git a/src/main/java/com/github/xsavikx/androidscreencast/configuration/ApplicationConfiguration.java b/src/main/java/com/github/xsavikx/androidscreencast/configuration/ApplicationConfiguration.java new file mode 100644 index 0000000..5449143 --- /dev/null +++ b/src/main/java/com/github/xsavikx/androidscreencast/configuration/ApplicationConfiguration.java @@ -0,0 +1,43 @@ +package com.github.xsavikx.androidscreencast.configuration; + +import com.github.xsavikx.androidscreencast.exception.AndroidScreenCastRuntimeException; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.Properties; + +@Singleton +public class ApplicationConfiguration { + private static final String PROPERTIES_LOCATION = "./app.properties"; + private final Properties properties; + + @Inject + public ApplicationConfiguration() { + properties = initProperties(); + } + + private Properties initProperties() { + final Properties properties = new Properties(); + loadProperties(properties, PROPERTIES_LOCATION); + return properties; + } + + private void loadProperties(final Properties properties, final String propertiesLocation) { + final File propertiesFile = new File(propertiesLocation); + if (propertiesFile.exists()) { + try (final FileInputStream fis = new FileInputStream(propertiesFile)) { + properties.load(fis); + } catch (final IOException e) { + throw new AndroidScreenCastRuntimeException(e); + } + } + } + + public String getProperty(final ApplicationConfigurationProperty configurationProperty) { + final String property = properties.getProperty(configurationProperty.getPropertyKey(), configurationProperty.getDefaultValue()); + return property; + } +} diff --git a/src/main/java/com/github/xsavikx/androidscreencast/configuration/ApplicationConfigurationProperty.java b/src/main/java/com/github/xsavikx/androidscreencast/configuration/ApplicationConfigurationProperty.java new file mode 100644 index 0000000..25e1fa1 --- /dev/null +++ b/src/main/java/com/github/xsavikx/androidscreencast/configuration/ApplicationConfigurationProperty.java @@ -0,0 +1,27 @@ +package com.github.xsavikx.androidscreencast.configuration; + +import static com.github.xsavikx.androidscreencast.configuration.ApplicationConfigurationPropertyKeys.*; + +public enum ApplicationConfigurationProperty { + ADB_PATH(ADB_PATH_KEY, ""), + ADB_DEVICE_TIMEOUT(ADB_DEVICE_TIMEOUT_KEY, "30"), + ADB_COMMAND_TIMEOUT(ADB_COMMAND_TIMEOUT_KEY, "5"), + APP_WINDOW_WIDTH(APP_WINDOW_WIDTH_KEY, "1024"), + APP_WINDOW_HEIGHT(APP_WINDOW_HEIGHT_KEY, "768"), + APP_NATIVE_LOOK(APP_NATIVE_LOOK_KEY, "true"); + private final String propertyKey; + private final String defaultValue; + + ApplicationConfigurationProperty(final String propertyKey, String defaultValue) { + this.propertyKey = propertyKey; + this.defaultValue = defaultValue; + } + + public String getPropertyKey() { + return propertyKey; + } + + public String getDefaultValue() { + return defaultValue; + } +} \ No newline at end of file diff --git a/src/main/java/com/github/xsavikx/androidscreencast/configuration/ApplicationConfigurationPropertyKeys.java b/src/main/java/com/github/xsavikx/androidscreencast/configuration/ApplicationConfigurationPropertyKeys.java new file mode 100644 index 0000000..32c489c --- /dev/null +++ b/src/main/java/com/github/xsavikx/androidscreencast/configuration/ApplicationConfigurationPropertyKeys.java @@ -0,0 +1,14 @@ +package com.github.xsavikx.androidscreencast.configuration; + +public final class ApplicationConfigurationPropertyKeys { + public static final String ADB_PATH_KEY = "adb.path"; + public static final String ADB_DEVICE_TIMEOUT_KEY = "adb.device.timeout"; + public static final String ADB_COMMAND_TIMEOUT_KEY = "adb.command.timeout"; + public static final String APP_WINDOW_WIDTH_KEY = "app.window.width"; + public static final String APP_WINDOW_HEIGHT_KEY = "app.window.height"; + public static final String APP_NATIVE_LOOK_KEY = "app.native.look"; + + private ApplicationConfigurationPropertyKeys() { + // + } +} diff --git a/src/main/java/com/github/xsavikx/androidscreencast/constant/Constants.java b/src/main/java/com/github/xsavikx/androidscreencast/constant/Constants.java deleted file mode 100644 index 99f06d3..0000000 --- a/src/main/java/com/github/xsavikx/androidscreencast/constant/Constants.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.github.xsavikx.androidscreencast.constant; - -import javax.annotation.Resource; - -@Resource -public final class Constants { - public static final String APP_NATIVE_LOOK_PROPERTY = "app.native.look"; - public static final String ADB_PATH_PROPERTY = "adb.path"; - public static final String DEFAULT_WINDOW_WIDTH = "default.window.width"; - public static final String DEFAULT_WINDOW_HEIGHT = "default.window.height"; -} diff --git a/src/main/java/com/github/xsavikx/androidscreencast/dagger/ApiModule.java b/src/main/java/com/github/xsavikx/androidscreencast/dagger/ApiModule.java new file mode 100644 index 0000000..4e88110 --- /dev/null +++ b/src/main/java/com/github/xsavikx/androidscreencast/dagger/ApiModule.java @@ -0,0 +1,60 @@ +package com.github.xsavikx.androidscreencast.dagger; + +import com.github.xsavikx.androidscreencast.api.AndroidDevice; +import com.github.xsavikx.androidscreencast.api.AndroidDeviceImpl; +import com.github.xsavikx.androidscreencast.api.command.executor.CommandExecutor; +import com.github.xsavikx.androidscreencast.api.command.executor.ShellCommandExecutor; +import com.github.xsavikx.androidscreencast.api.command.factory.AdbInputCommandFactory; +import com.github.xsavikx.androidscreencast.api.command.factory.InputCommandFactory; +import com.github.xsavikx.androidscreencast.configuration.ApplicationConfiguration; +import dagger.Module; +import dagger.Provides; + +import javax.inject.Named; +import javax.inject.Singleton; + +import static com.github.xsavikx.androidscreencast.configuration.ApplicationConfigurationProperty.*; +import static com.github.xsavikx.androidscreencast.configuration.ApplicationConfigurationPropertyKeys.*; + +@Singleton +@Module +public class ApiModule { + @Singleton + @Named(ADB_COMMAND_TIMEOUT_KEY) + @Provides + public static long adbCommandTimeout(ApplicationConfiguration applicationConfiguration) { + return Long.valueOf(applicationConfiguration.getProperty(ADB_COMMAND_TIMEOUT)); + } + + @Singleton + @Named(ADB_DEVICE_TIMEOUT_KEY) + @Provides + public static long adbDeviceTimeout(ApplicationConfiguration applicationConfiguration) { + return Long.valueOf(applicationConfiguration.getProperty(ADB_DEVICE_TIMEOUT)); + } + + @Singleton + @Named(ADB_PATH_KEY) + @Provides + public static String adbPath(ApplicationConfiguration applicationConfiguration) { + return applicationConfiguration.getProperty(ADB_PATH); + } + + @Singleton + @Provides + public static CommandExecutor commandExecutor(ShellCommandExecutor shellCommandExecutor) { + return shellCommandExecutor; + } + + @Singleton + @Provides + public static AndroidDevice androidDevice(AndroidDeviceImpl androidDevice) { + return androidDevice; + } + + @Singleton + @Provides + public static InputCommandFactory inputCommandFactory(AdbInputCommandFactory adbInputCommandFactory) { + return adbInputCommandFactory; + } +} diff --git a/src/main/java/com/github/xsavikx/androidscreencast/dagger/AppModule.java b/src/main/java/com/github/xsavikx/androidscreencast/dagger/AppModule.java new file mode 100644 index 0000000..0672b8d --- /dev/null +++ b/src/main/java/com/github/xsavikx/androidscreencast/dagger/AppModule.java @@ -0,0 +1,34 @@ +package com.github.xsavikx.androidscreencast.dagger; + +import com.android.ddmlib.IDevice; +import com.github.xsavikx.androidscreencast.app.AndroidScreencastApplication; +import com.github.xsavikx.androidscreencast.app.Application; +import com.github.xsavikx.androidscreencast.app.DeviceChooserApplication; +import dagger.Module; +import dagger.Provides; + +import javax.inject.Singleton; + +@Singleton +@Module +public class AppModule { + @Singleton + @Provides + public static Application application(AndroidScreencastApplication application) { + return application; + } + + @Singleton + @Provides + public static IDevice iDevice(final DeviceChooserApplication application) { + try { + application.init(); + application.start(); + IDevice device = application.getDevice(); + return device; + } catch (Throwable e) { + application.stop(); + throw e; + } + } +} diff --git a/src/main/java/com/github/xsavikx/androidscreencast/dagger/MainComponent.java b/src/main/java/com/github/xsavikx/androidscreencast/dagger/MainComponent.java new file mode 100644 index 0000000..b5ad021 --- /dev/null +++ b/src/main/java/com/github/xsavikx/androidscreencast/dagger/MainComponent.java @@ -0,0 +1,18 @@ +package com.github.xsavikx.androidscreencast.dagger; + +import com.github.xsavikx.androidscreencast.api.command.executor.CommandExecutor; +import com.github.xsavikx.androidscreencast.api.command.factory.InputCommandFactory; +import com.github.xsavikx.androidscreencast.app.Application; +import dagger.Component; + +import javax.inject.Singleton; + +@Singleton +@Component(modules = {ApiModule.class, AppModule.class, UiModule.class}) +public interface MainComponent { + Application application(); + + CommandExecutor commandExecutor(); + + InputCommandFactory inputCommandFactory(); +} diff --git a/src/main/java/com/github/xsavikx/androidscreencast/dagger/MainComponentProvider.java b/src/main/java/com/github/xsavikx/androidscreencast/dagger/MainComponentProvider.java new file mode 100644 index 0000000..27f42f1 --- /dev/null +++ b/src/main/java/com/github/xsavikx/androidscreencast/dagger/MainComponentProvider.java @@ -0,0 +1,16 @@ +package com.github.xsavikx.androidscreencast.dagger; + +public final class MainComponentProvider { + private static MainComponent INSTANCE; + + private MainComponentProvider() { + + } + + public static MainComponent mainComponent() { + if (INSTANCE == null) { + INSTANCE = DaggerMainComponent.create(); + } + return INSTANCE; + } +} diff --git a/src/main/java/com/github/xsavikx/androidscreencast/dagger/UiModule.java b/src/main/java/com/github/xsavikx/androidscreencast/dagger/UiModule.java new file mode 100644 index 0000000..f3f460b --- /dev/null +++ b/src/main/java/com/github/xsavikx/androidscreencast/dagger/UiModule.java @@ -0,0 +1,37 @@ +package com.github.xsavikx.androidscreencast.dagger; + +import com.github.xsavikx.androidscreencast.api.injector.InputKeyEvent; +import com.github.xsavikx.androidscreencast.configuration.ApplicationConfiguration; +import com.github.xsavikx.androidscreencast.configuration.ApplicationConfigurationProperty; +import dagger.Module; +import dagger.Provides; + +import javax.inject.Named; +import javax.inject.Singleton; + +import static com.github.xsavikx.androidscreencast.configuration.ApplicationConfigurationPropertyKeys.APP_WINDOW_HEIGHT_KEY; +import static com.github.xsavikx.androidscreencast.configuration.ApplicationConfigurationPropertyKeys.APP_WINDOW_WIDTH_KEY; + +@Singleton +@Module +public class UiModule { + @Singleton + @Named(APP_WINDOW_WIDTH_KEY) + @Provides + public static int appWindowHeight(final ApplicationConfiguration applicationConfiguration) { + return Integer.valueOf(applicationConfiguration.getProperty(ApplicationConfigurationProperty.APP_WINDOW_HEIGHT)); + } + + @Singleton + @Named(APP_WINDOW_HEIGHT_KEY) + @Provides + public static int appWindowWidth(final ApplicationConfiguration applicationConfiguration) { + return Integer.valueOf(applicationConfiguration.getProperty(ApplicationConfigurationProperty.APP_WINDOW_WIDTH)); + } + + @Singleton + @Provides + public static InputKeyEvent[] initialData() { + return InputKeyEvent.values(); + } +} diff --git a/src/main/java/com/github/xsavikx/androidscreencast/exception/IllegalAdbConfigurationException.java b/src/main/java/com/github/xsavikx/androidscreencast/exception/IllegalAdbConfigurationException.java new file mode 100644 index 0000000..7c13535 --- /dev/null +++ b/src/main/java/com/github/xsavikx/androidscreencast/exception/IllegalAdbConfigurationException.java @@ -0,0 +1,10 @@ +package com.github.xsavikx.androidscreencast.exception; + +/** + * Created by User on 16.02.2017. + */ +public class IllegalAdbConfigurationException extends AndroidScreenCastRuntimeException { + public IllegalAdbConfigurationException(String adbPath) { + super(String.format("Exception happened during running your ADB instance. Probably ADB path is misconfigured. ADB path='%s'", adbPath)); + } +} diff --git a/src/main/java/com/github/xsavikx/androidscreencast/spring/config/ApplicationConfiguration.java b/src/main/java/com/github/xsavikx/androidscreencast/spring/config/ApplicationConfiguration.java deleted file mode 100644 index 27d4f62..0000000 --- a/src/main/java/com/github/xsavikx/androidscreencast/spring/config/ApplicationConfiguration.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.github.xsavikx.androidscreencast.spring.config; - -import com.android.ddmlib.AndroidDebugBridge; -import com.android.ddmlib.IDevice; -import com.github.xsavikx.androidscreencast.app.DeviceChooserApplication; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.context.annotation.*; -import org.springframework.util.StringUtils; - -import javax.annotation.PreDestroy; - -@Configuration -@ComponentScan(basePackages = "com.github.xsavikx.androidscreencast") -@PropertySources(value = { - @PropertySource(value = "file:${user.dir}/app.properties", ignoreResourceNotFound = true) -}) -public class ApplicationConfiguration { - @Value("${adb.path}") - private String adbPath; - - @Bean - public AndroidDebugBridge initAndroidDebugBridge() { - AndroidDebugBridge.initIfNeeded(false); - if (!StringUtils.isEmpty(adbPath)) { - return AndroidDebugBridge.createBridge(adbPath, false); - } - return AndroidDebugBridge.createBridge(); - } - - @PreDestroy - public void preDestroy() { - AndroidDebugBridge.disconnectBridge(); - AndroidDebugBridge.terminate(); - } - - @Bean - public DefaultListableBeanFactory initBeanFactory() { - return new DefaultListableBeanFactory(); - } - - @Bean - @Autowired - public IDevice initDevice(DeviceChooserApplication application) { - application.init(); - application.start(); - application.stop(); - return application.getDevice(); - } - -} diff --git a/src/main/java/com/github/xsavikx/androidscreencast/spring/config/ApplicationContextProvider.java b/src/main/java/com/github/xsavikx/androidscreencast/spring/config/ApplicationContextProvider.java deleted file mode 100644 index 68c0c1c..0000000 --- a/src/main/java/com/github/xsavikx/androidscreencast/spring/config/ApplicationContextProvider.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.github.xsavikx.androidscreencast.spring.config; - -import org.springframework.beans.BeansException; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; - -public class ApplicationContextProvider implements ApplicationContextAware { - private static ApplicationContext applicationContext; - - private ApplicationContextProvider() { - // - } - - public static ApplicationContext getApplicationContext() { - if (applicationContext == null) - applicationContext = new AnnotationConfigApplicationContext(ApplicationConfiguration.class); - return applicationContext; - } - - @Override - public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { - ApplicationContextProvider.applicationContext = applicationContext; - } - - public static T getBean(Class clazz) throws BeansException { - return getApplicationContext().getBean(clazz); - } - -} diff --git a/src/main/java/com/github/xsavikx/androidscreencast/ui/JDialogError.java b/src/main/java/com/github/xsavikx/androidscreencast/ui/JDialogError.java index 171518c..8d5435a 100644 --- a/src/main/java/com/github/xsavikx/androidscreencast/ui/JDialogError.java +++ b/src/main/java/com/github/xsavikx/androidscreencast/ui/JDialogError.java @@ -2,6 +2,7 @@ import com.github.xsavikx.androidscreencast.exception.AndroidScreenCastRuntimeException; import com.github.xsavikx.androidscreencast.exception.IORuntimeException; +import com.github.xsavikx.androidscreencast.util.StringUtils; import javax.swing.*; import java.awt.*; @@ -53,10 +54,13 @@ private void setErrorDetails(Throwable e) { AndroidScreenCastRuntimeException realCause = getCause(ex); if (realCause != null) { errorDialogLabel.setText(realCause.getClass().getSimpleName()); - stringWriter.append(realCause.getMessage()).append('\n').append('\n'); - stringWriter.append(realCause.getAdditionalInformation()); + if (StringUtils.isNotEmpty(realCause.getMessage())) + stringWriter.append(realCause.getMessage()).append('\n').append('\n'); + if (StringUtils.isNotEmpty(realCause.getAdditionalInformation())) + stringWriter.append(realCause.getAdditionalInformation()); } else { - stringWriter.append(ex.getMessage()).append('\n').append('\n'); + if (StringUtils.isNotEmpty(ex.getMessage())) + stringWriter.append(ex.getMessage()).append('\n').append('\n'); ex.printStackTrace(new PrintWriter(stringWriter)); } errorDescription.setText(stringWriter.toString()); diff --git a/src/main/java/com/github/xsavikx/androidscreencast/ui/JDialogExecuteKeyEvent.java b/src/main/java/com/github/xsavikx/androidscreencast/ui/JDialogExecuteKeyEvent.java index d4eb5c5..80e348d 100644 --- a/src/main/java/com/github/xsavikx/androidscreencast/ui/JDialogExecuteKeyEvent.java +++ b/src/main/java/com/github/xsavikx/androidscreencast/ui/JDialogExecuteKeyEvent.java @@ -1,19 +1,18 @@ package com.github.xsavikx.androidscreencast.ui; +import com.github.xsavikx.androidscreencast.api.command.KeyCommand; import com.github.xsavikx.androidscreencast.api.command.executor.CommandExecutor; -import com.github.xsavikx.androidscreencast.api.command.factory.AdbInputCommandFactory; +import com.github.xsavikx.androidscreencast.api.command.factory.InputCommandFactory; import com.github.xsavikx.androidscreencast.api.injector.InputKeyEvent; import com.github.xsavikx.androidscreencast.ui.model.InputKeyEventTable; -import com.github.xsavikx.androidscreencast.ui.model.InputKeyEventTableModel; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; -import javax.annotation.PostConstruct; +import javax.inject.Inject; +import javax.inject.Singleton; import javax.swing.*; import javax.swing.border.TitledBorder; import java.awt.*; -@Component +@Singleton public class JDialogExecuteKeyEvent extends JDialog { private static final long serialVersionUID = -4152020879675916776L; private static final int HEIGHT = 600; @@ -22,7 +21,7 @@ public class JDialogExecuteKeyEvent extends JDialog { private static final int BUTTON_WIDTH = WIDTH >> 1 - 5; private static final int TITLE_COLUMN_INDEX = 1; - + private static final String TITLE = "Execute key event"; private static final String EXECUTE_BUTTON_TEXT = "Execute"; private static final String USE_LONG_PRESS_BUTTON_TEXT = "Long press"; private static final String CANCEL_BUTTON_TEXT = "Cancel"; @@ -31,20 +30,26 @@ public class JDialogExecuteKeyEvent extends JDialog { private static final String NO_COMMAND_CHOSEN_WARNING_DIALOG_TITLE = "Warning"; private final CommandExecutor commandExecutor; + private final InputKeyEventTable commandListTable; + private final InputCommandFactory inputCommandFactory; - @Autowired - public JDialogExecuteKeyEvent(CommandExecutor commandExecutor) { + @Inject + public JDialogExecuteKeyEvent(CommandExecutor commandExecutor, InputKeyEventTable commandListTable, InputCommandFactory inputCommandFactory) { this.commandExecutor = commandExecutor; + this.commandListTable = commandListTable; + this.inputCommandFactory = inputCommandFactory; + init(); } + public void open() { + setVisible(true); + } - @PostConstruct - private void initialize() { - setResizable(false); - setTitle("Execute key event"); + private void init() { setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); - final InputKeyEventTableModel commandList = new InputKeyEventTableModel(InputKeyEvent.values()); - final InputKeyEventTable commandListTable = new InputKeyEventTable(commandList); + setResizable(false); + setTitle(TITLE); + setAlwaysOnTop(true); final JCheckBoxMenuItem useLongPress = new JCheckBoxMenuItem(USE_LONG_PRESS_BUTTON_TEXT, false); JButton executeCommandButton = new JButton(EXECUTE_BUTTON_TEXT); @@ -52,9 +57,14 @@ private void initialize() { executeCommandButton.addActionListener(actionEvent -> { int rowIndex = commandListTable.getSelectedRow(); if (rowIndex > 0) { - final String title = (String) commandList.getValueAt(rowIndex, TITLE_COLUMN_INDEX); - SwingUtilities.invokeLater(() -> commandExecutor.execute(AdbInputCommandFactory.getKeyCommand(InputKeyEvent.valueOf(title), - useLongPress.getState()))); + + final String title = (String) commandListTable.getModel().getValueAt(rowIndex, TITLE_COLUMN_INDEX); + SwingUtilities.invokeLater(() -> { + final InputKeyEvent inputKeyEvent = InputKeyEvent.valueOf(title); + final boolean longPress = useLongPress.getState(); + final KeyCommand keyCommand = inputCommandFactory.getKeyCommand(inputKeyEvent, longPress); + commandExecutor.execute(keyCommand); + }); closeDialog(); } else { JOptionPane.showMessageDialog(null, NO_COMMAND_CHOSEN_WARNING_MESSAGE, NO_COMMAND_CHOSEN_WARNING_DIALOG_TITLE, diff --git a/src/main/java/com/github/xsavikx/androidscreencast/ui/JFrameMain.java b/src/main/java/com/github/xsavikx/androidscreencast/ui/JFrameMain.java index 4672886..768fd26 100644 --- a/src/main/java/com/github/xsavikx/androidscreencast/ui/JFrameMain.java +++ b/src/main/java/com/github/xsavikx/androidscreencast/ui/JFrameMain.java @@ -2,36 +2,38 @@ import com.github.xsavikx.androidscreencast.api.injector.Injector; import com.github.xsavikx.androidscreencast.api.injector.InputKeyEvent; -import com.github.xsavikx.androidscreencast.app.AndroidScreencastApplication; -import com.github.xsavikx.androidscreencast.constant.Constants; +import com.github.xsavikx.androidscreencast.dagger.MainComponentProvider; import com.github.xsavikx.androidscreencast.exception.IORuntimeException; -import com.github.xsavikx.androidscreencast.spring.config.ApplicationContextProvider; import com.github.xsavikx.androidscreencast.ui.explorer.JFrameExplorer; import com.github.xsavikx.androidscreencast.ui.interaction.KeyEventDispatcherFactory; import com.github.xsavikx.androidscreencast.ui.interaction.KeyboardActionListenerFactory; +import com.github.xsavikx.androidscreencast.ui.interaction.MouseActionAdapter; import com.google.common.io.Files; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.env.Environment; -import org.springframework.stereotype.Component; -import javax.annotation.PostConstruct; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; import javax.swing.*; import javax.swing.filechooser.FileNameExtensionFilter; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.awt.event.MouseAdapter; import java.io.File; import java.io.IOException; import java.nio.file.Path; -@Component +import static com.github.xsavikx.androidscreencast.configuration.ApplicationConfigurationPropertyKeys.APP_WINDOW_HEIGHT_KEY; +import static com.github.xsavikx.androidscreencast.configuration.ApplicationConfigurationPropertyKeys.APP_WINDOW_WIDTH_KEY; + +@Singleton public class JFrameMain extends JFrame { private static final long serialVersionUID = -2085909236767692371L; private final JPanelScreen jp; - private final MouseAdapter ma; + private final MouseActionAdapter ma; private final Injector injector; - private final Environment env; + private final Dimension windowSize; + private final JFrameExplorer frameExplorer; + private final JDialogExecuteKeyEvent dialogExecuteKeyEvent; private transient boolean isDisposed = false; private JToolBar jtb = new JToolBar(); private JToolBar jtbHardkeys = new JToolBar(); @@ -47,22 +49,16 @@ public class JFrameMain extends JFrame { private JButton jbRecord = new JButton("Start record"); private Dimension oldImageDimension; - @Autowired - public JFrameMain(JPanelScreen jp, Environment env, Injector injector, MouseAdapter ma) { + @Inject + public JFrameMain(JPanelScreen jp, Injector injector, MouseActionAdapter ma, + JFrameExplorer frameExplorer, JDialogExecuteKeyEvent dialogExecuteKeyEvent, @Named(APP_WINDOW_WIDTH_KEY) int width, + @Named(APP_WINDOW_HEIGHT_KEY) int height) { this.jp = jp; this.injector = injector; - this.env = env; this.ma = ma; - } - - private void setPreferredWindowSize() { - if (env.containsProperty(Constants.DEFAULT_WINDOW_HEIGHT) && env.containsProperty(Constants.DEFAULT_WINDOW_WIDTH)) { - Integer height = env.getProperty(Constants.DEFAULT_WINDOW_HEIGHT, Integer.class); - Integer width = env.getProperty(Constants.DEFAULT_WINDOW_WIDTH, Integer.class); - if (height != null && width != null) - getContentPane().setPreferredSize(new Dimension(width, height)); - } - pack(); + this.frameExplorer = frameExplorer; + this.dialogExecuteKeyEvent = dialogExecuteKeyEvent; + this.windowSize = new Dimension(width, height); } @Override @@ -72,10 +68,9 @@ public void dispose() { } isDisposed = true; super.dispose(); - ApplicationContextProvider.getBean(AndroidScreencastApplication.class).stop(); + MainComponentProvider.mainComponent().application().stop(); } - @PostConstruct public void initialize() { setDefaultCloseOperation(DISPOSE_ON_CLOSE); KeyboardFocusManager.getCurrentKeyboardFocusManager() @@ -107,7 +102,6 @@ public void initialize() { jtbHardkeys.add(jbKbPhoneOn); jtbHardkeys.add(jbKbPhoneOff); - setDefaultCloseOperation(DISPOSE_ON_CLOSE); setLayout(new BorderLayout()); add(jtb, BorderLayout.NORTH); add(jtbHardkeys, BorderLayout.SOUTH); @@ -123,23 +117,28 @@ public void initialize() { jp.addMouseWheelListener(ma); jbExplorer.addActionListener(actionEvent -> { - JFrameExplorer jf = ApplicationContextProvider.getBean(JFrameExplorer.class); - jf.setIconImage(getIconImage()); - jf.launch(); - jf.setVisible(true); + SwingUtilities.invokeLater(() -> { + JFrameExplorer jf = frameExplorer; + jf.setIconImage(getIconImage()); + jf.launch(); + jf.setVisible(true); + }); }); jtb.add(jbExplorer); jbExecuteKeyEvent.addActionListener(actionEvent -> { - JDialogExecuteKeyEvent jdExecuteKeyEvent = ApplicationContextProvider - .getBean(JDialogExecuteKeyEvent.class); - jdExecuteKeyEvent.setVisible(true); + SwingUtilities.invokeLater(dialogExecuteKeyEvent::open); }); jtb.add(jbExecuteKeyEvent); jtb.add(jbRecord); } + private void setPreferredWindowSize() { + getContentPane().setPreferredSize(windowSize); + pack(); + } + private ActionListener createRecordActionListener() { return new ActionListener() { private final Path tmpDir = Files.createTempDir().toPath(); diff --git a/src/main/java/com/github/xsavikx/androidscreencast/ui/JPanelScreen.java b/src/main/java/com/github/xsavikx/androidscreencast/ui/JPanelScreen.java index d6362a6..3006250 100644 --- a/src/main/java/com/github/xsavikx/androidscreencast/ui/JPanelScreen.java +++ b/src/main/java/com/github/xsavikx/androidscreencast/ui/JPanelScreen.java @@ -1,12 +1,12 @@ package com.github.xsavikx.androidscreencast.ui; -import org.springframework.stereotype.Component; - +import javax.inject.Inject; +import javax.inject.Singleton; import javax.swing.*; import java.awt.*; import java.awt.image.BufferedImage; -@Component +@Singleton public class JPanelScreen extends JPanel { private static final long serialVersionUID = -2034873107028503004L; private float coef = 1; @@ -15,37 +15,40 @@ public class JPanelScreen extends JPanel { private Dimension size = null; private BufferedImage image = null; + @Inject public JPanelScreen() { - this.setFocusable(true); + setFocusable(true); } - public Point getRawPoint(Point p1) { - Point p2 = new Point(); + public Point getRawPoint(final Point p1) { + final Point p2 = new Point(); p2.x = (int) ((p1.x - origX) / coef); p2.y = (int) ((p1.y - origY) / coef); return p2; } - public void handleNewImage(Dimension size, BufferedImage image) { + public void handleNewImage(final Dimension size, final BufferedImage image) { this.size = size; this.image = image; repaint(); } @Override - protected void paintComponent(Graphics g) { - if (size == null) - return; - if (size.height == 0) + protected void paintComponent(final Graphics g) { + if (isNotInitialized()) return; - Graphics2D g2 = (Graphics2D) g; + final Graphics2D g2 = (Graphics2D) g; g2.clearRect(0, 0, getWidth(), getHeight()); - double width = Math.min(getWidth(), size.width * getHeight() / size.height); + final double width = Math.min(getWidth(), size.width * getHeight() / size.height); coef = (float) width / size.width; - double height = width * size.height / size.width; + final double height = width * size.height / size.width; origX = (getWidth() - width) / 2; origY = (getHeight() - height) / 2; g2.drawImage(image, (int) origX, (int) origY, (int) width, (int) height, this); } + private boolean isNotInitialized() { + return size == null || size.height == 0 || size.width == 0; + } + } diff --git a/src/main/java/com/github/xsavikx/androidscreencast/ui/explorer/JFrameExplorer.java b/src/main/java/com/github/xsavikx/androidscreencast/ui/explorer/JFrameExplorer.java index 41a7f0f..f40b571 100644 --- a/src/main/java/com/github/xsavikx/androidscreencast/ui/explorer/JFrameExplorer.java +++ b/src/main/java/com/github/xsavikx/androidscreencast/ui/explorer/JFrameExplorer.java @@ -3,9 +3,8 @@ import com.github.xsavikx.androidscreencast.api.AndroidDevice; import com.github.xsavikx.androidscreencast.api.file.FileInfo; import com.github.xsavikx.androidscreencast.exception.AndroidScreenCastRuntimeException; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; +import javax.inject.Inject; import javax.swing.*; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; @@ -19,7 +18,6 @@ import java.util.List; import java.util.Map; -@Component public class JFrameExplorer extends JFrame { private static final long serialVersionUID = -5209265873286028854L; @@ -28,7 +26,7 @@ public class JFrameExplorer extends JFrame { private JList jListFichiers; private Map> cache = new LinkedHashMap<>(); - @Autowired + @Inject public JFrameExplorer(AndroidDevice androidDevice) { setTitle("Explorer"); diff --git a/src/main/java/com/github/xsavikx/androidscreencast/ui/interaction/KeyEventDispatcherImpl.java b/src/main/java/com/github/xsavikx/androidscreencast/ui/interaction/KeyEventDispatcherImpl.java index 48f0f07..d22443a 100644 --- a/src/main/java/com/github/xsavikx/androidscreencast/ui/interaction/KeyEventDispatcherImpl.java +++ b/src/main/java/com/github/xsavikx/androidscreencast/ui/interaction/KeyEventDispatcherImpl.java @@ -1,37 +1,48 @@ package com.github.xsavikx.androidscreencast.ui.interaction; +import com.github.xsavikx.androidscreencast.api.command.KeyCommand; import com.github.xsavikx.androidscreencast.api.command.executor.CommandExecutor; -import com.github.xsavikx.androidscreencast.api.command.factory.AdbInputCommandFactory; +import com.github.xsavikx.androidscreencast.api.command.factory.InputCommandFactory; import com.github.xsavikx.androidscreencast.api.injector.KeyCodeConverter; -import com.github.xsavikx.androidscreencast.spring.config.ApplicationContextProvider; +import com.github.xsavikx.androidscreencast.dagger.MainComponentProvider; import javax.swing.*; import java.awt.*; import java.awt.event.KeyEvent; public class KeyEventDispatcherImpl implements KeyEventDispatcher { + private final Window window; private CommandExecutor commandExecutor; - private Window window; + private InputCommandFactory inputCommandFactory; public KeyEventDispatcherImpl(Window frame) { this.window = frame; } @Override - public boolean dispatchKeyEvent(KeyEvent e) { + public boolean dispatchKeyEvent(final KeyEvent e) { if (!window.isActive()) return false; if (e.getID() == KeyEvent.KEY_TYPED) { final int code = KeyCodeConverter.getKeyCode(e); - SwingUtilities.invokeLater(() -> getCommandExecutor().execute(AdbInputCommandFactory.getKeyCommand(code))); - + SwingUtilities.invokeLater(() -> { + final KeyCommand command = getInputCommandFactory().getKeyCommand(code); + getCommandExecutor().execute(command); + }); } return false; } + private InputCommandFactory getInputCommandFactory() { + if (inputCommandFactory == null) { + inputCommandFactory = MainComponentProvider.mainComponent().inputCommandFactory(); + } + return inputCommandFactory; + } + private CommandExecutor getCommandExecutor() { if (commandExecutor == null) { - commandExecutor = ApplicationContextProvider.getBean(CommandExecutor.class); + commandExecutor = MainComponentProvider.mainComponent().commandExecutor(); } return commandExecutor; } diff --git a/src/main/java/com/github/xsavikx/androidscreencast/ui/interaction/KeyboardActionListener.java b/src/main/java/com/github/xsavikx/androidscreencast/ui/interaction/KeyboardActionListener.java index 401cafa..039657d 100644 --- a/src/main/java/com/github/xsavikx/androidscreencast/ui/interaction/KeyboardActionListener.java +++ b/src/main/java/com/github/xsavikx/androidscreencast/ui/interaction/KeyboardActionListener.java @@ -1,29 +1,41 @@ package com.github.xsavikx.androidscreencast.ui.interaction; +import com.github.xsavikx.androidscreencast.api.command.KeyCommand; import com.github.xsavikx.androidscreencast.api.command.executor.CommandExecutor; -import com.github.xsavikx.androidscreencast.api.command.factory.AdbInputCommandFactory; -import com.github.xsavikx.androidscreencast.spring.config.ApplicationContextProvider; +import com.github.xsavikx.androidscreencast.api.command.factory.InputCommandFactory; +import com.github.xsavikx.androidscreencast.dagger.MainComponentProvider; import javax.swing.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; public class KeyboardActionListener implements ActionListener { + private final int key; private CommandExecutor commandExecutor; - private int key; + private InputCommandFactory inputCommandFactory; public KeyboardActionListener(int key) { this.key = key; } @Override - public void actionPerformed(ActionEvent e) { - SwingUtilities.invokeLater(() -> getCommandExecutor().execute(AdbInputCommandFactory.getKeyCommand(key))); + public void actionPerformed(final ActionEvent e) { + SwingUtilities.invokeLater(() -> { + final KeyCommand command = getInputCommandFactory().getKeyCommand(key); + getCommandExecutor().execute(command); + }); + } + + private InputCommandFactory getInputCommandFactory() { + if (inputCommandFactory == null) { + inputCommandFactory = MainComponentProvider.mainComponent().inputCommandFactory(); + } + return inputCommandFactory; } private CommandExecutor getCommandExecutor() { if (commandExecutor == null) { - commandExecutor = ApplicationContextProvider.getBean(CommandExecutor.class); + commandExecutor = MainComponentProvider.mainComponent().commandExecutor(); } return commandExecutor; } diff --git a/src/main/java/com/github/xsavikx/androidscreencast/ui/interaction/MouseActionAdapter.java b/src/main/java/com/github/xsavikx/androidscreencast/ui/interaction/MouseActionAdapter.java index c97a1cb..4d6f663 100644 --- a/src/main/java/com/github/xsavikx/androidscreencast/ui/interaction/MouseActionAdapter.java +++ b/src/main/java/com/github/xsavikx/androidscreencast/ui/interaction/MouseActionAdapter.java @@ -1,38 +1,45 @@ package com.github.xsavikx.androidscreencast.ui.interaction; +import com.github.xsavikx.androidscreencast.api.command.SwipeCommand; +import com.github.xsavikx.androidscreencast.api.command.TapCommand; import com.github.xsavikx.androidscreencast.api.command.executor.CommandExecutor; -import com.github.xsavikx.androidscreencast.api.command.factory.AdbInputCommandFactory; +import com.github.xsavikx.androidscreencast.api.command.factory.InputCommandFactory; import com.github.xsavikx.androidscreencast.api.injector.Injector; import com.github.xsavikx.androidscreencast.ui.JPanelScreen; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; +import javax.inject.Inject; +import javax.inject.Singleton; import javax.swing.*; import java.awt.*; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseWheelEvent; -@Component +@Singleton public class MouseActionAdapter extends MouseAdapter { private final static long ONE_SECOND = 1000L; private final JPanelScreen jp; private final CommandExecutor commandExecutor; + private final InputCommandFactory inputCommandFactory; private final Injector injector; private int dragFromX = -1; private int dragFromY = -1; private long timeFromPress = -1; - @Autowired - public MouseActionAdapter(JPanelScreen jp, CommandExecutor commandExecutor, Injector injector) { + @Inject + public MouseActionAdapter(final JPanelScreen jp, + final CommandExecutor commandExecutor, + final InputCommandFactory inputCommandFactory, + final Injector injector) { this.jp = jp; this.commandExecutor = commandExecutor; + this.inputCommandFactory = inputCommandFactory; this.injector = injector; } @Override - public void mouseClicked(MouseEvent e) { + public void mouseClicked(final MouseEvent e) { if (injector != null && e.getButton() == MouseEvent.BUTTON3) { injector.toggleOrientation(); e.consume(); @@ -40,14 +47,17 @@ public void mouseClicked(MouseEvent e) { } final Point p2 = jp.getRawPoint(e.getPoint()); if (p2.x > 0 && p2.y > 0) { - SwingUtilities.invokeLater(() -> commandExecutor.execute(AdbInputCommandFactory.getTapCommand(p2.x, p2.y))); + SwingUtilities.invokeLater(() -> { + final TapCommand command = inputCommandFactory.getTapCommand(p2.x, p2.y); + commandExecutor.execute(command); + }); } } @Override - public void mouseDragged(MouseEvent e) { + public void mouseDragged(final MouseEvent e) { if (dragFromX == -1 && dragFromY == -1) { - Point p2 = jp.getRawPoint(e.getPoint()); + final Point p2 = jp.getRawPoint(e.getPoint()); dragFromX = p2.x; dragFromY = p2.y; timeFromPress = System.currentTimeMillis(); @@ -62,15 +72,22 @@ public void mouseReleased(MouseEvent e) { final int yFrom = dragFromY; final int xTo = p2.x; final int yTo = p2.y; - SwingUtilities.invokeLater(() -> commandExecutor.execute(AdbInputCommandFactory.getSwipeCommand(xFrom, yFrom, xTo, yTo, timeFromPress))); - dragFromX = -1; - dragFromY = -1; - timeFromPress = -1; + SwingUtilities.invokeLater(() -> { + final SwipeCommand command = inputCommandFactory.getSwipeCommand(xFrom, yFrom, xTo, yTo, timeFromPress); + commandExecutor.execute(command); + }); + clearState(); } } + private void clearState() { + dragFromX = -1; + dragFromY = -1; + timeFromPress = -1; + } + @Override - public void mouseWheelMoved(MouseWheelEvent arg0) { + public void mouseWheelMoved(final MouseWheelEvent arg0) { // if (JFrameMain.this.injector == null) // return; // JFrameMain.this.injector.injectTrackball(arg0.getWheelRotation() < 0 ? diff --git a/src/main/java/com/github/xsavikx/androidscreencast/ui/model/InputKeyEventTable.java b/src/main/java/com/github/xsavikx/androidscreencast/ui/model/InputKeyEventTable.java index 10cb824..eaa0155 100644 --- a/src/main/java/com/github/xsavikx/androidscreencast/ui/model/InputKeyEventTable.java +++ b/src/main/java/com/github/xsavikx/androidscreencast/ui/model/InputKeyEventTable.java @@ -1,19 +1,23 @@ package com.github.xsavikx.androidscreencast.ui.model; +import javax.inject.Inject; +import javax.inject.Singleton; import javax.swing.*; import javax.swing.table.TableCellRenderer; import javax.swing.table.TableColumn; import javax.swing.table.TableColumnModel; import java.awt.*; +@Singleton public class InputKeyEventTable extends JTable { private static final long serialVersionUID = 3978642864003531967L; private final static int MIN_COLUMN_WIDTH = 20; + @Inject public InputKeyEventTable(InputKeyEventTableModel tableModel) { super(tableModel); - setTableColumnsNames(tableModel.columnNames); + setTableColumnsNames(InputKeyEventTableModel.COLUMN_NAMES); setAutoResizeMode(JTable.AUTO_RESIZE_OFF); setTableColumnsPreferredSize(); setSelectionMode(ListSelectionModel.SINGLE_SELECTION); diff --git a/src/main/java/com/github/xsavikx/androidscreencast/ui/model/InputKeyEventTableModel.java b/src/main/java/com/github/xsavikx/androidscreencast/ui/model/InputKeyEventTableModel.java index 6d0098a..a8adc5d 100644 --- a/src/main/java/com/github/xsavikx/androidscreencast/ui/model/InputKeyEventTableModel.java +++ b/src/main/java/com/github/xsavikx/androidscreencast/ui/model/InputKeyEventTableModel.java @@ -2,21 +2,26 @@ import com.github.xsavikx.androidscreencast.api.injector.InputKeyEvent; +import javax.inject.Inject; +import javax.inject.Singleton; import javax.swing.table.AbstractTableModel; import java.util.ArrayList; import java.util.List; +@Singleton public class InputKeyEventTableModel extends AbstractTableModel { private static final long serialVersionUID = 1553313932570896541L; private final static String INDEX_COLUMN_NAME = "#"; private final static String TITLE_COLUMN_NAME = "title"; private final static String DESCRIPTION_COLUMN_NAME = "description"; - public final String[] columnNames = { + public static final String[] COLUMN_NAMES = { INDEX_COLUMN_NAME, TITLE_COLUMN_NAME, DESCRIPTION_COLUMN_NAME }; + private List> data = new ArrayList<>(); private int rowCount = 0; + @Inject public InputKeyEventTableModel(InputKeyEvent[] initialData) { initData(initialData); } @@ -42,7 +47,7 @@ public int getRowCount() { @Override public int getColumnCount() { - return columnNames.length; + return COLUMN_NAMES.length; } @Override diff --git a/src/main/java/com/github/xsavikx/androidscreencast/ui/worker/SwingWorker.java b/src/main/java/com/github/xsavikx/androidscreencast/ui/worker/SwingWorker.java index 736bca8..b83a5c5 100644 --- a/src/main/java/com/github/xsavikx/androidscreencast/ui/worker/SwingWorker.java +++ b/src/main/java/com/github/xsavikx/androidscreencast/ui/worker/SwingWorker.java @@ -372,6 +372,7 @@ public final boolean cancel(boolean mayInterruptIfRunning) { * @see #get */ protected void done() { + // } // Future methods START @@ -605,6 +606,7 @@ public final boolean isDone() { * @see #publish */ protected void process(List chunks) { + // } // PropertyChangeSupports methods END diff --git a/src/main/java/com/github/xsavikx/androidscreencast/util/StringUtils.java b/src/main/java/com/github/xsavikx/androidscreencast/util/StringUtils.java new file mode 100644 index 0000000..858f477 --- /dev/null +++ b/src/main/java/com/github/xsavikx/androidscreencast/util/StringUtils.java @@ -0,0 +1,11 @@ +package com.github.xsavikx.androidscreencast.util; + +public class StringUtils { + private StringUtils() { + // + } + + public static boolean isNotEmpty(final CharSequence charSequence) { + return charSequence != null && charSequence.length() > 0; + } +} diff --git a/src/main/resources/log4j.properties b/src/main/resources/log4j.properties deleted file mode 100644 index d0af363..0000000 --- a/src/main/resources/log4j.properties +++ /dev/null @@ -1,17 +0,0 @@ -# Root logger option -log4j.rootLogger=TRACE, stdout, file -# Direct log messages to a log file -log4j.appender.file=org.apache.log4j.RollingFileAppender -log4j.appender.file.File=AndroidScreencast.log -log4j.appender.file.MaxFileSize=1MB -log4j.appender.file.MaxBackupIndex=1 -log4j.appender.file.layout=org.apache.log4j.PatternLayout -log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%-5p] [%C{1}:%L] - %m%n -# Direct log messages to stdout -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.Target=System.out -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d{HH:mm:ss} [%-5p] [%C{1}] - %m%n -# Set levels for appenders -log4j.appender.file.Threshold=DEBUG -log4j.appender.stdout.Threshold=INFO diff --git a/src/main/resources/logback-test.xml b/src/main/resources/logback-test.xml new file mode 100644 index 0000000..633cd4c --- /dev/null +++ b/src/main/resources/logback-test.xml @@ -0,0 +1,28 @@ + + + + + %-30(%date{HH:mm:ss} [%thread]) %highlight(%-5level) %logger{0} - %msg%n + + + + AndroidScreencast.log + + %-45(%date{ISO8601} [%thread]) %highlight(%-5level) %logger - %msg%n [%file:%line] + + + 10 + AndroidScreencast.%i.log + + + 1MB + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml new file mode 100644 index 0000000..633cd4c --- /dev/null +++ b/src/main/resources/logback.xml @@ -0,0 +1,28 @@ + + + + + %-30(%date{HH:mm:ss} [%thread]) %highlight(%-5level) %logger{0} - %msg%n + + + + AndroidScreencast.log + + %-45(%date{ISO8601} [%thread]) %highlight(%-5level) %logger - %msg%n [%file:%line] + + + 10 + AndroidScreencast.%i.log + + + 1MB + + + + + + + + + + \ No newline at end of file